|  |  | 
|  | /* | 
|  | *  Virtual Frame Buffer Export | 
|  | * | 
|  | *  (C) Copyright 2024 Glider bv | 
|  | * | 
|  | *  This file is subject to the terms and conditions of the GNU General Public | 
|  | *  License. See the file COPYING in the main directory of this archive for | 
|  | *  more details. | 
|  | */ | 
|  |  | 
|  | #define _GNU_SOURCE | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "types.h" | 
|  | #include "export.h" | 
|  | #include "fb.h" | 
|  | #include "util.h" | 
|  |  | 
|  |  | 
|  | static const char *export_prefix; | 
|  | static unsigned int export_xres = DEFAULT_EXPORT_XRES; | 
|  | static unsigned int export_yres = DEFAULT_EXPORT_YRES; | 
|  |  | 
|  | static void export_setup(void) | 
|  | { | 
|  | const char *param = Opt_Export; | 
|  | char *end; | 
|  |  | 
|  | /* Prefix */ | 
|  | end = strchr(param, ','); | 
|  | if (end) | 
|  | *end++ = '\0'; | 
|  |  | 
|  | export_prefix = param; | 
|  | Debug("export_prefix = %s\n", export_prefix); | 
|  |  | 
|  | /* Optional horizontal resolution */ | 
|  | param = end; | 
|  | if (!param) | 
|  | return; | 
|  |  | 
|  | end = strchr(param, ','); | 
|  | if (end) | 
|  | *end++ = '\0'; | 
|  |  | 
|  | export_xres = atoi(param); | 
|  | Debug("export_xres = %u\n", export_xres); | 
|  |  | 
|  | /* Optional vertical resolution */ | 
|  | param = end; | 
|  | if (!param) | 
|  | return; | 
|  |  | 
|  | end = strchr(param, ','); | 
|  | if (end) | 
|  | *end++ = '\0'; | 
|  |  | 
|  | export_yres = atoi(param); | 
|  | Debug("export_yres = %u\n", export_yres); | 
|  | } | 
|  |  | 
|  | void export_init(void) | 
|  | { | 
|  | export_setup(); | 
|  |  | 
|  | fb_var.xres = export_xres; | 
|  | fb_var.yres = export_yres; | 
|  | fb_var.xres_virtual = export_xres; | 
|  | fb_var.yres_virtual = export_yres; | 
|  | fb_var.xoffset = 0; | 
|  | fb_var.yoffset = 0; | 
|  | fb_var.bits_per_pixel = 32; | 
|  | fb_var.grayscale = 0; | 
|  | fb_var.red.offset = 16; | 
|  | fb_var.red.length = 8; | 
|  | fb_var.red.msb_right = 0; | 
|  | fb_var.green.offset = 8; | 
|  | fb_var.green.length = 8; | 
|  | fb_var.green.msb_right = 0; | 
|  | fb_var.blue.offset = 0; | 
|  | fb_var.blue.length = 8; | 
|  | fb_var.blue.msb_right = 0; | 
|  | fb_var.transp.offset = 0; | 
|  | fb_var.transp.length = 0; | 
|  | fb_var.transp.msb_right = 0; | 
|  | fb_var.nonstd = 0; | 
|  | fb_var.activate = 0; | 
|  | fb_var.height = fb_var.xres / 4; | 
|  | fb_var.width = fb_var.yres / 4; | 
|  | fb_var.accel_flags = 0; | 
|  | fb_var.pixclock = 0; | 
|  | fb_var.left_margin = 0; | 
|  | fb_var.right_margin = 0; | 
|  | fb_var.upper_margin = 0; | 
|  | fb_var.lower_margin = 0; | 
|  | fb_var.hsync_len = 0; | 
|  | fb_var.vsync_len = 0; | 
|  | fb_var.sync = 0; | 
|  | fb_var.vmode = FB_VMODE_NONINTERLACED; | 
|  | fb_var.rotate = 0; | 
|  | fb_var.colorspace = 0; | 
|  | fb_var.reserved[0] = 0; | 
|  | fb_var.reserved[1] = 0; | 
|  | fb_var.reserved[2] = 0; | 
|  | fb_var.reserved[3] = 0; | 
|  |  | 
|  | strcpy(fb_fix.id, "fbtest"); | 
|  | fb_fix.smem_start = 0; | 
|  | fb_fix.smem_len = fb_var.xres * fb_var.yres * 4; | 
|  | fb_fix.type = FB_TYPE_PACKED_PIXELS; | 
|  | fb_fix.type_aux = 0; | 
|  | fb_fix.visual = FB_VISUAL_TRUECOLOR; | 
|  | fb_fix.xpanstep = 0; | 
|  | fb_fix.ypanstep = 0; | 
|  | fb_fix.ywrapstep = 0; | 
|  | fb_fix.line_length = fb_var.xres * 4; | 
|  | fb_fix.mmio_start = 0; | 
|  | fb_fix.mmio_len = 0; | 
|  | fb_fix.accel = FB_ACCEL_NONE; | 
|  | fb_fix.capabilities = 0; | 
|  | fb_fix.reserved[0] = 0; | 
|  | fb_fix.reserved[1] = 0; | 
|  |  | 
|  | fb = malloc(fb_fix.smem_len); | 
|  | if (!fb) | 
|  | Fatal("malloc %u: %s\n", fb_fix.smem_len, strerror(errno)); | 
|  | } | 
|  |  | 
|  | void export_fb(const char *name) | 
|  | { | 
|  | unsigned int x, y; | 
|  | u32 *src, pixel; | 
|  | char *filename; | 
|  | u8 *line, *dst; | 
|  | FILE *stream; | 
|  | int res; | 
|  |  | 
|  | res = asprintf(&filename, "%s%s.ppm", export_prefix, name); | 
|  | if (res < 0) | 
|  | Fatal("asprintf: %s\n", strerror(errno)); | 
|  |  | 
|  | line = malloc(fb_var.xres * 3); | 
|  | if (!line) | 
|  | Fatal("malloc %u: %s\n", fb_var.xres * 3, strerror(errno)); | 
|  |  | 
|  | Debug("Exporting to %s\n", filename); | 
|  | stream = fopen(filename, "w"); | 
|  | if (!stream) | 
|  | Fatal("fopen %s: %s\n", filename, strerror(errno)); | 
|  |  | 
|  | fputs("P6\n", stream); | 
|  | fprintf(stream, "%u %u 255\n", fb_var.xres, fb_var.yres); | 
|  |  | 
|  | src = (u32 *)fb; | 
|  | for (y = 0; y < fb_var.yres; y++) { | 
|  | dst = line; | 
|  | for (x = 0; x < fb_var.xres; x++) { | 
|  | pixel = *src++; | 
|  | *dst++ = (pixel >> fb_var.red.offset) & 0xff; | 
|  | *dst++ = (pixel >> fb_var.green.offset) & 0xff; | 
|  | *dst++ = (pixel >> fb_var.blue.offset) & 0xff; | 
|  | } | 
|  | res = fwrite(line, 3, fb_var.xres, stream); | 
|  | if (res < fb_var.xres) | 
|  | Fatal("fwrite: %s\n", strerror(errno)); | 
|  | } | 
|  |  | 
|  | fclose(stream); | 
|  | free(line); | 
|  | free(filename); | 
|  | } |