#include "plat.h" #include "config.h" extern void prog_init(void*); extern void prog_update(void); extern void prog_deinit(void); #ifdef plat_posix #define _POSIX_SOURCE #define _GNU_SOURCE #include "std_printers.c" #include #include #include #include #include #include #include #include #include #include #include static clockid_t global_clock; static unsigned long global_freq; void init_timer(void) { struct timespec ts; global_clock = CLOCK_REALTIME; global_freq = 1000000000; #if defined(_POSIX_MONOTONIC_CLOCK) if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { global_clock = CLOCK_MONOTONIC; } #endif } static unsigned long get_timer() { struct timespec ts; clock_gettime(global_clock, &ts); return (unsigned long)ts.tv_sec * global_freq + (unsigned long)ts.tv_nsec; } unsigned long get_current_time() { return get_timer(); } void sleep_ns(unsigned long ns) { struct timespec t = { 0 }; t.tv_nsec = ns; nanosleep(&t, &t); } unsigned char* load_binary(const char* n, int* size) { FILE* f; int s; void* b; f = fopen(n, "r"); if (!f) { print_err("Failed to open %s.\n", n); pbreak(error_file_not_found); return 0; } fseek(f, 0, SEEK_END); s = ftell(f); rewind(f); b = malloc(s); s = fread(b, 1, s, f); if (size) *size = s; return b; } void free_file(void* p) { free(p); } void iter_dir(const char* path, Dir_Iter fn, void* u) { struct dirent* de; int l; DIR* di; char buf[256]; char* p; di = opendir(path); if (!di) return; strcpy(buf, path); l = strlen(buf); if (!l) return; if (buf[l - 1] != '/') { strcat(buf, "/"); l++; } p = buf + l; while ((de = readdir(di))) { if (de->d_name[0] == '.') continue; strcpy(p, de->d_name); if (de->d_type == DT_DIR) iter_dir(buf, fn, u); else fn(u, buf); } } #endif #ifdef plat_x11 #define Font RX11Font #include #include #include #undef RX11Font #include "rcache.h" #define txt_buf_max 32 struct { int w, h, run; Display* d; Window wi; GC gc; XImage* bb; Colour* bp, * fb; Atom wm_p, wm_d; char txt_buf[txt_buf_max]; char raw_buf[txt_buf_max]; int txt_len; int mx, my, scrx, scry; } app; static MBtn mheld_btns[mbtn_count]; static MBtn mpressed_btns[mbtn_count]; static MBtn mreleased_btns[mbtn_count]; static void reset_keys(void) { int i; for (i = 0; i < mbtn_count; i++) { mpressed_btns[i] = 0; mreleased_btns[i] = 0; } } void init_app_render(const XWindowAttributes* wa) { Colour* p, * fb; Display* d; XImage* bb; int w, h; d = app.d; w = app.w; h = app.h; p = (Colour*)malloc(sizeof *p * w * h); fb = (Colour*)malloc(sizeof *fb * w * h); bb = app.bb; if (bb) { XDestroyImage(bb); } if (!p) { print_err("Out of memory.\n"); pbreak(error_out_of_memory); } bb = XCreateImage( d, wa->visual, wa->depth, ZPixmap, 0, (char*)p, w, h, 32, w * sizeof *p ); if (!bb) { print_err("Failed to create X11 backbuffer.\n"); pbreak(error_platform_error); } app.bp = p; app.fb = fb; app.bb = bb; } void init_app(void) { Window w, r; Display* d; GC gc; unsigned rm, bm; XWindowAttributes wa; app.run = 0; d = XOpenDisplay(0); if (!d) { print_err("Failed to open X11 display.\n"); pbreak(error_platform_error); return; } r = DefaultRootWindow(d); app.wm_p = XInternAtom( d, "WM_PROTOCOLS", 0 ); app.wm_d = XInternAtom( d, "WM_DELETE_WINDOW", 0 ); w = XCreateSimpleWindow( d, r, 0, 0, default_window_w, default_window_h, 0, WhitePixel(d, 0), BlackPixel(d, 0) ); XSetWMProtocols(d, w, &app.wm_d, 1); XStoreName(d, w, app_name); XSelectInput( d, w, ExposureMask | KeyPressMask | KeyReleaseMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask ); XClearWindow(d, w); XMapRaised(d, w); gc = XCreateGC(d, w, 0, 0); if (!gc) { print_err("Failed to create graphics context.\n"); pbreak(error_platform_error); return; } XGetWindowAttributes(d, w, &wa); if (wa.depth != 24 && wa.depth != 32) { print_err("Only true colour displays are supported.\n"); pbreak(error_platform_error); } rm = wa.visual->red_mask & 0x1000000; bm = wa.visual->blue_mask & 0xffffff; if ((rm == 0xff && bm == 0xff0000)) { print_war("Detected BGR. Colours will look fucked.\n"); } app.d = d; app.gc = gc; app.w = wa.width; app.h = wa.height; app.run = 1; app.wi = w; app.bb = 0; init_app_render(&wa); } void deinit_app(void) { XDestroyWindow(app.d, app.wi); XDestroyImage(app.bb); XCloseDisplay(app.d); } void update_events(void) { XWindowAttributes wa; Display* d; Window w; XEvent e; Rectangle r; int mb; d = app.d; w = app.wi; reset_keys(); app.scrx = app.scry = 0; while (XPending(d)) { XNextEvent(d, &e); switch (e.type) { case ClientMessage: if ( (Atom)e.xclient.data.l[0] == app.wm_d ) { app.run = 0; } break; case Expose: XGetWindowAttributes(d, w, &wa); if ( wa.width != app.w || wa.height != app.h ) { app.w = wa.width; app.h = wa.height; init_app_render(&wa); } r.x = 0; r.y = 0; r.w = app.w; r.h = app.h; rc_invalidate(&r); break; case MotionNotify: app.mx = e.xmotion.x; app.my = e.xmotion.y; break; case ButtonPress: switch (e.xbutton.button) { case 1: case 2: case 3: mb = e.xbutton.button - 1; mheld_btns[mb] = 1; mpressed_btns[mb] = 1; break; } break; case ButtonRelease: switch (e.xbutton.button) { case 1: case 2: case 3: mb = e.xbutton.button - 1; mheld_btns[mb] = 0; mreleased_btns[mb] = 1; break; case 4: app.scry--; break; case 5: app.scry++; break; case 6: app.scrx--; break; case 7: app.scrx++; break; } break; default: break; } } } int main(int argc, const char** argv) { void* mem; unsigned long now, next; long ts; (void)argc; (void)argv; init_timer(); mem = malloc(memory_size); if (!mem) { print_err("Out of memory.\n"); return error_out_of_memory; } init_app(); next = get_current_time(); prog_init(mem); while (app.run) { now = get_current_time(); while (now >= next) { update_events(); prog_update(); next += ms_per_frame * 1000000; } ts = next - now; if (ts > 0) sleep_ns(ts); } prog_deinit(); deinit_app(); free(mem); return 0; } void plat_present( int x, int y, int w, int h ) { const Colour* src; Colour* dst; int i, j; int ex, ey; int s; ex = x + w; ey = y + h; dst = app.bp + x + y * app.w; src = app.fb + x + y * app.w; s = app.w - w; for (j = y; j < ey; j++) { for (i = x; i < ex; i++) { dst->r = src->b; dst->g = src->g; dst->b = src->r; dst->a = src->a; dst++; src++; } dst += s; src += s; } XPutImage( app.d, app.wi, app.gc, app.bb, x, y, x, y, w, h ); } Colour* get_fb(void) { return app.fb; } int get_render_w(void) { return app.w; } int get_render_h(void) { return app.h; } int get_mouse_x(void) { return app.mx; } int get_mouse_y(void) { return app.my; } int get_mscroll_x(void) { return app.scrx; } int get_mscroll_y(void) { return app.scry; } int mbtn_pressed(MBtn btn) { return mheld_btns[btn]; } int mbtn_just_pressed(MBtn btn) { return mpressed_btns[btn]; } int mbtn_just_released(MBtn btn) { return mreleased_btns[btn]; } #endif