/* xshmu implementation on the Linux framebuffer. * * This is pretty lame, so far just enough to run Tetris and Munching * Squares. It doesn’t have any mouse handling or arrow keys. But it * seems clear that xshmu_fd has to go! */ #include #include #include #include #include #include #include #include #include #include #include #include "xshmu.h" struct xshmu_event { xshmu_key_event key_event; }; struct _xshmu { xshmu_pic back_buffer; xshmu_pic front_buffer; size_t buf_size; int fb_fd; xshmu_event event; char buf[2]; }; void ignoresystem(char *cmd) { int result = system(cmd); result = result; } #define FRAMEBUFFER "/dev/fb0" xshmu xshmu_open(const char *name, int width, int height, const char *options) { char errmsg[100]; char *fb_buf = 0; xshmu w = (xshmu)malloc(sizeof *w); memset(w, 0, sizeof(*w)); w->fb_fd = open(FRAMEBUFFER, O_RDWR); if (w->fb_fd < 0) { sprintf(errmsg, FRAMEBUFFER ": %s", strerror(errno)); goto fail; } struct fb_fix_screeninfo fix = {0}; struct fb_var_screeninfo var = {0}; if (ioctl(w->fb_fd, FBIOGET_FSCREENINFO, &fix) < 0) { sprintf(errmsg, FRAMEBUFFER ": FBIOGET_FSCREENINFO: %s", strerror(errno)); goto fail; } if (ioctl(w->fb_fd, FBIOGET_VSCREENINFO, &var) < 0) { sprintf(errmsg, FRAMEBUFFER ": FBIOGET_VSCREENINFO: %s", strerror(errno)); goto fail; } int pagesize = sysconf(_SC_PAGESIZE); if (fix.smem_start & (pagesize-1)) { sprintf(errmsg, FRAMEBUFFER ": smem_start %x isn’t %d-aligned", (int)fix.smem_start, pagesize); goto fail; } if (fix.type != FB_TYPE_PACKED_PIXELS) { sprintf(errmsg, FRAMEBUFFER ": type %u isn’t FB_TYPE_PACKED_PIXELS", (unsigned)fix.type); goto fail; } if (fix.visual != FB_VISUAL_TRUECOLOR) { sprintf(errmsg, FRAMEBUFFER ": visual %u isn’t FB_VISUAL_TRUECOLOR", (unsigned)fix.visual); goto fail; } if (var.bits_per_pixel != 32) { sprintf(errmsg, FRAMEBUFFER ": bits_per_pixel is %d", var.bits_per_pixel); goto fail; } char layout[50]; sprintf(layout, "R %d(%d) G %d(%d) B %d(%d)", var.red.offset, var.red.length, var.green.offset, var.green.length, var.blue.offset, var.blue.length); if (0 != strcmp(layout, "R 16(8) G 8(8) B 0(8)")) { sprintf(errmsg, FRAMEBUFFER ": pixel format %s", layout); goto fail; } w->buf_size = fix.smem_len; fb_buf = mmap(NULL, w->buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, w->fb_fd, 0); if (!fb_buf || (intptr_t)fb_buf == -1) { fb_buf = 0; sprintf(errmsg, FRAMEBUFFER ": mmap: %s", strerror(errno)); goto fail; } xshmu_pic front_buffer = { .p = (uint32_t*)fb_buf, .w = var.xres_virtual, .stride = var.xres_virtual, .h = var.yres, }; w->front_buffer = front_buffer; w->back_buffer = xshmu_canvas(var.xres_virtual, var.yres_virtual); ignoresystem("stty cbreak -echo"); return w; fail: if (w->front_buffer.p) free(w->front_buffer.p); if (fb_buf) munmap(fb_buf, w->buf_size); if (w->fb_fd) close(w->fb_fd); xshmu_error_handler(errmsg); return 0; } xshmu_pic xshmu_framebuffer(xshmu w) { return w->back_buffer; } void xshmu_flush(xshmu w) { xshmu_copy(w->front_buffer, w->back_buffer); } int xshmu_fd(xshmu w) { return 0; } /* XXX this lacks the event-swallowing semantics of the normal implementation and will hang if you don’t xshmu_get_event */ void xshmu_wait(xshmu w) { fd_set read_fds; FD_ZERO(&read_fds); FD_SET(0, &read_fds); select(1, &read_fds, NULL, NULL, NULL); } xshmu_event *xshmu_get_event(xshmu w) { struct timeval zero = {.tv_sec = 0, .tv_usec = 0}; fd_set read_fds; FD_ZERO(&read_fds); FD_SET(0, &read_fds); if (select(1, &read_fds, NULL, NULL, &zero)) { if (read(0, &w->buf, 1) != 1) return NULL; w->buf[1] = 'E'; xshmu_key_event ev = { .s = w->buf, .keysym = w->buf[0], .down = 1, .len = 1, }; w->event.key_event = ev; return &w->event; } return NULL; } xshmu_key_event *xshmu_as_key_event(xshmu_event *ev) { return &ev->key_event; /* All events are key events so far */ } xshmu_die_event xshmu_as_die_event(xshmu_event *ev) { /* TODO */ return 0; } xshmu_mouse_event *xshmu_as_mouse_event(xshmu_event *ev) { /* TODO */ return 0; } void xshmu_close(xshmu w) { if (w->front_buffer.p) munmap(w->front_buffer.p, w->buf_size); if (w->back_buffer.p) free(w->back_buffer.p); if (w->fb_fd) close(w->fb_fd); free(w); ignoresystem("stty sane"); } /* xshmu_subpic, xshmu_copy, and xshmu_canvas here are copied from xshmu.c, which suggests that they really need to be factored out. */ xshmu_pic xshmu_subpic(xshmu_pic start, int x, int y, int w, int h) { if (x < 0) x = 0; if (y < 0) y = 0; if (w > start.w - x) w = start.w - x; if (h > start.h - y) h = start.h - y; if (w < 0) w = 0; if (h < 0) h = 0; xshmu_pic result = { start.p + y*start.stride + x, w, start.stride, h }; return result; } void xshmu_copy(xshmu_pic dest, xshmu_pic src) { int w = src.w, h = src.h; if (w > dest.w) w = dest.w; if (h > dest.h) h = dest.h; if ((size_t)((uintptr_t)src.p - (uintptr_t)dest.p) <= src.stride * (src.h + 1)) { // Possible vertical overlap requires copying upwards for (int y = h-1; y >= 0; y--) { memmove(&dest.p[y * dest.stride], &src.p[y * src.stride], w * 4); } } else { for (int y = 0; y < h; y++) { memmove(&dest.p[y * dest.stride], &src.p[y * src.stride], w * 4); } } } xshmu_pic xshmu_canvas(int w, int h) { xshmu_pic result = { malloc(w*h*4), w, w, h }; if (!result.p) xshmu_error_handler("canvas malloc"); return result; } /* The error handling stuff below is also mostly C&P from xshmu.c, but I’m not sure it should be factored out. */ static void default_error_handler(const char *msg) { fprintf(stderr, "xshμ: %s\n", msg); ignoresystem("stty sane"); abort(); } xshmu_error_handler_t xshmu_error_handler = default_error_handler;