#include "screen.h" #undef Screen #include #include #include #include #include #include #define XLIB_ILLEGAL_ACCESS #include #include #include static Display* dpy; static int scr; static Window root; static int count = 0; static size_t bit_depth = sizeof(uint32_t) * 8; static int key_count; static bool* key_state; static bool* key_checked; typedef struct { Window window; GC graphics_ctx; Pixmap pixel_map; XImage* image; int width, height; } WindowState; static void init_x() { if ((dpy = XOpenDisplay(NULL)) == NULL) { printf("error: failed to open x11 display\n"); exit(EXIT_FAILURE); } scr = DefaultScreen(dpy); root = RootWindow(dpy, scr); key_count = dpy->max_keycode - dpy->min_keycode; key_state = malloc(sizeof(bool) * key_count); key_checked = malloc(sizeof(bool) * key_count); memset(key_state, 0, sizeof(bool) * key_count); memset(key_checked, 0, sizeof(bool) * key_count); } static Window create_window(int x, int y, int w, int h, int d, const char* t) { XVisualInfo vis_info; if(!XMatchVisualInfo(dpy, scr, d, TrueColor, &vis_info)) { printf("error: %d depth not supported\n", d); exit(EXIT_FAILURE); } Visual* visual = vis_info.visual; XSetWindowAttributes xwa; xwa.background_pixel = 0; xwa.border_pixel = 0; xwa.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask; xwa.colormap = XCreateColormap(dpy, root, visual, AllocNone); long wm = CWBackPixel | CWColormap | CWBorderPixel | CWEventMask; Window window = XCreateWindow(dpy, root, x, y, w, h, 0, d, InputOutput, visual, wm, &xwa); XStoreName(dpy, window, t); XSetIconName(dpy, window, t); return window; } static XImage* create_image(int width, int height, void* data) { return XCreateImage( dpy, CopyFromParent, bit_depth, ZPixmap, 0, data, width, height, bit_depth, 0 ); } void init_screen(struct Screen* screen, uint16_t width, uint16_t height, const char* title) { if (dpy == NULL) { init_x(); } screen->width = width; screen->height = height; screen->delta = 0; size_t pixel_count = screen->width * screen->height; screen->pixels = malloc(pixel_count * bit_depth / 8); memset(screen->pixels, 0, pixel_count * bit_depth / 8); Window window = create_window(0, 0, width, height, bit_depth, title); XMapWindow(dpy, window); Pixmap pixel_map = XCreatePixmap(dpy, window, width, height, bit_depth); GC gc = XCreateGC(dpy, pixel_map, 0, NULL); WindowState* state = malloc(sizeof(WindowState)); state->window = window; state->graphics_ctx = gc; state->pixel_map = pixel_map; XWindowAttributes xwa; XGetWindowAttributes(dpy, window, &xwa); state->width = xwa.width; state->height = xwa.height; screen->internal = state; XImage* image = create_image(state->width, state->height, screen->pixels); state->image = image; count++; } static long get_time() { struct timespec spec; clock_gettime(CLOCK_MONOTONIC, &spec); return round(spec.tv_nsec / 1.0e6) + spec.tv_sec * 1000; } static long last = 0; static void update_delta(struct Screen* screen) { long now = get_time(); if (last == 0) { screen->delta = 0; } else { screen->delta = (now - last) / 1000.0; } last = now; } static void draw_screen(struct Screen* screen) { WindowState* state = (WindowState*) screen->internal; XPutImage( dpy, state->window, state->graphics_ctx, state->image, 0, 0, 0, 0, state->width, state->height ); } static void handle_event(WindowState* state) { XEvent event; XNextEvent(dpy, &event); switch(event.type) { case KeyPress: { XKeyEvent xkpe = event.xkey; uint32_t code = xkpe.keycode - dpy->min_keycode; key_state[code] = true; key_checked[code] = false; break; } case KeyRelease: { XKeyEvent xkre = event.xkey; uint32_t code = xkre.keycode - dpy->min_keycode; key_state[code] = false; break; } case ConfigureNotify: { XConfigureEvent xce = event.xconfigure; state->width = xce.width; state->height = xce.height; break; } default: break; } } bool poll_screen(struct Screen* screen) { draw_screen(screen); WindowState* state = (WindowState*) screen->internal; while (XPending(dpy)) handle_event(state); update_delta(screen); return true; } void free_screen(struct Screen* screen) { WindowState* state = (WindowState*) screen->internal; XUnmapWindow(dpy, state->window); XFreePixmap(dpy, state->pixel_map); XDestroyImage(state->image); XFreeGC(dpy, state->graphics_ctx); XDestroyWindow(dpy, state->window); free(screen->internal); count--; if(count == 0) { XCloseDisplay(dpy); free(key_state); free(key_checked); } } bool key_pressed(int keycode) { if (key_checked[keycode]) return false; if (!key_state[keycode]) return false; key_checked[keycode] = true; return true; } bool key_down(int keycode) { return key_state[keycode]; }