// WARNING: CURRENTLY UNMAINTAINED IN FAVOR OF THE GL PLATFORM // // This platform is very restrictive and has issues with shearing. It is more // pure than OpenGL, but it has some undesired artifacts such as shearing. #include #include #include #include #include #include #include "common.h" #include "teensy.h" #include "platform.h" #define SEC2NANO ((uint64_t)1000000000) struct x11_platform { Display *dis; Window root; Window win; GC gc; int scr; Atom wm_delete_window; bool should_close; uint64_t initial_time; }; struct x11_platform p; static int err_handler(Display *dis, XErrorEvent *ev) { char msg[1024]; XGetErrorText(dis, ev->error_code, msg, 1024); ty_log_fatal( TY_PLATFORM_ERR, "X Error (0x%x): %s", ev->error_code, msg ); return 0; } void ty_platform_init(struct ty_ctx *ctx) { XSetErrorHandler(err_handler); struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); p.initial_time = (uint64_t)ts.tv_sec * SEC2NANO + (uint64_t)ts.tv_nsec; p.dis = XOpenDisplay(0); p.root = XDefaultRootWindow(p.dis); p.scr = DefaultScreen(p.dis); XSetWindowAttributes attributes = {}; attributes.background_pixel = 0x00000000; attributes.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask; p.win = XCreateWindow( p.dis, p.root, 0, 0, ctx->creation_hints.win_width, ctx->creation_hints.win_height, 0, DefaultDepth(p.dis, p.scr), CopyFromParent, DefaultVisual(p.dis, p.scr), CWBackPixel | CWEventMask, &attributes ); p.gc = XCreateGC(p.dis, p.win, 0, NULL); XMapWindow(p.dis, p.win); p.wm_delete_window = XInternAtom( p.dis, "WM_DELETE_WINDOW", false ); p.should_close = false; } void ty_platform_deinit(void) { XUnmapWindow(p.dis, p.win); XFreeGC(p.dis, p.gc); XDestroyWindow(p.dis, p.win); XCloseDisplay(p.dis); } static void display_image(struct ty_image img) { size_t size = img.width * img.height; // Don't use ty_alloc cause it will be owned and freed by Xlib uint32_t *xdata = malloc(sizeof(uint32_t) * size); for (int i = 0; i < size; i++) { uint8_t r = img.data[i].r; uint8_t g = img.data[i].g; uint8_t b = img.data[i].b; xdata[i] = ((uint32_t)r << 16) | ((uint32_t)g << 8) | (uint32_t)b; } XImage *image = XCreateImage( p.dis, DefaultVisual(p.dis, p.scr), DefaultDepth(p.dis, p.scr), ZPixmap, 0, (char*)xdata, img.width, img.height, 32, 0 ); XPutImage( p.dis, p.win, p.gc, image, 0, 0, 0, 0, img.width, img.height ); XFlush(p.dis); XDestroyImage(image); } static void handle_keypress(XEvent ev) { XKeyPressedEvent *kp_ev = (XKeyPressedEvent*)&ev; if (kp_ev->keycode == XKeysymToKeycode(p.dis, XK_Escape)) { p.should_close = true; } } static void handle_client_msg(XEvent ev) { if ((Atom)ev.xclient.data.l[0] == p.wm_delete_window) p.should_close = true; } void ty_platform_frame(struct ty_image img) { XPending(p.dis); while (QLength(p.dis)) { XEvent ev; XNextEvent(p.dis, &ev); switch (ev.type) { case KeyPress: case KeyRelease: handle_keypress(ev); break; case ClientMessage: handle_client_msg(ev); break; } } display_image(img); } bool ty_platform_os_wants_quit(void) { return p.should_close; } // Thanks GLFW, ily // https://github.com/glfw/glfw/blob/master/src/posix_time.c#L52 double ty_platform_get_time(void) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); uint64_t timensec = (uint64_t)ts.tv_sec * SEC2NANO + (uint64_t)ts.tv_nsec; return (double)(timensec - p.initial_time) / SEC2NANO; }