diff options
author | quou <quou@disroot.org> | 2024-06-01 12:19:16 +1000 |
---|---|---|
committer | quou <quou@disroot.org> | 2024-06-01 12:20:17 +1000 |
commit | ea7cd94f7aeb177618db3907a6c86b7252e018f0 (patch) | |
tree | e972f9cf590ef756c2e41f3eac5b03e16db08300 /plat.c |
Initial commit.
Diffstat (limited to 'plat.c')
-rw-r--r-- | plat.c | 462 |
1 files changed, 462 insertions, 0 deletions
@@ -0,0 +1,462 @@ +#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 <dirent.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +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 <stdlib.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#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 + |