From 259237fae792e8f19d9b7920b2354f75bc1789e2 Mon Sep 17 00:00:00 2001 From: quou Date: Mon, 23 Sep 2024 21:11:29 +1000 Subject: initial commit, basic platform and rendering code. --- plat.c | 429 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 429 insertions(+) create mode 100644 plat.c (limited to 'plat.c') diff --git a/plat.c b/plat.c new file mode 100644 index 0000000..2101910 --- /dev/null +++ b/plat.c @@ -0,0 +1,429 @@ +#include "config.h" +#include "maths.h" +#include "memory.h" +#include "plat.h" + +#ifdef plat_posix +#define _POSIX_SOURCE +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +extern int isatty(int); +extern int fileno(FILE*); + +int imp_assert( + int val, + const char* expr, + const char* file, + int line +) { + if (!val) { + print_err( + "%d:%s: Assertion failed: %s.\n", + line, + file, + expr + ); + pbreak(error_assertion_failed); + return 0; + } + return 1; +} + +void print(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); +} + +void print_err(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[31;31m"); + } + + vfprintf(stderr, fmt, args); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[0m"); + } + + va_end(args); +} + +void print_war(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[31;35m"); + } + + vfprintf(stderr, fmt, args); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[0m"); + } + + va_end(args); +} + +void pbreak(Error code) { +#if defined(DEBUG) && defined(plat_x86) + __asm__("int3;"); + (void)code; +#else + exit(code); +#endif +} + +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; + } +#else + (void)ts; +#endif +} + +unsigned long get_timer(void) { + struct timespec ts; + clock_gettime(global_clock, &ts); + return + (unsigned long)ts.tv_sec * global_freq + + (unsigned long)ts.tv_nsec; +} + +void sleep_ns(unsigned long ns) { + struct timespec t = { 0 }; + t.tv_nsec = ns; + nanosleep(&t, &t); +} + +void init_fps(FPS* f, int mpf) { + f->now = get_timer(); + f->next = f->now; + f->mpf = mpf; + f->pt = 0; + f->ct = -1; +} + +void fps_begin(FPS* f) { + f->now = get_timer(); +} + +void fps_end(FPS* f) { + unsigned long ts = f->next - f->now; + if (ts > 0) { + sleep_ns(ts); + } +} + +void fps_update(FPS* f) { + f->next += f->mpf * 1000000; + f->pt = f->ct; + f->ct = get_timer(); + f->fps = 1000000000 / (f->ct - f->pt); +} + +extern int entrypoint(int, const char**, Arena*); + +int main(int argc, const char** argv) { + Arena a; + void* mem = malloc(memory_size); + if (!mem) { + print_err("Out of memory.\n"); + return error_out_of_memory; + } + init_timer(); + init_arena( + &a, + mem, + memory_size + ); + return entrypoint(argc, argv, &a); +} + +#endif + + +#ifdef plat_x11 + +#include +#include + +typedef struct { + unsigned char r, g, b, a; +} Colour; + +typedef struct { + Display* d; + Window wi; + GC gc; + int w, h; + int rmx, rmy; + int omx, omy; + int ms; + unsigned long begin, end; + XImage* bb; + Colour* bbp; + Atom wm_delete; +} App_Internal; + +void init_rendering( + App* a, + App_Internal* i, + XWindowAttributes* wa +) { + Colour* p; + int w = max_pc_window_w; + int h = max_pc_window_h; + a->fb = heap_alloc( + a->heap, + (viewport_w * viewport_h + 32) / 32 * sizeof *a->fb + ); + p = malloc(sizeof *p * w * h); + if (!p || !a->fb) { + print_err("Out of memory.\n"); + pbreak(error_out_of_memory); + } + i->bb = XCreateImage( + i->d, + wa->visual, + wa->depth, + ZPixmap, + 0, + (char*)p, + w, + h, + 32, + w * sizeof *p + ); + if (!i->bb) { + print_err("Failed to create X11 backbuffer.\n"); + pbreak(error_platform_error); + } + i->bbp = p; +} + +void init_window(App* a, App_Internal* i, const char* n) { + Window root, w; + Display* d; + XWindowAttributes wa; + unsigned rm, bm; + i->bb = 0; + i->bbp = 0; + d = i->d; + root = DefaultRootWindow(d); + w = XCreateSimpleWindow( + d, + root, + 0, + 0, + viewport_w * default_scale, + viewport_h * default_scale, + 0, + WhitePixel(d, 0), + BlackPixel(d, 0) + ); + i->wi = w; + i->wm_delete = XInternAtom( + d, + "WM_DELETE_WINDOW", + 0 + ); + XSetWMProtocols(d, w, &i->wm_delete, 1); + XStoreName(d, w, n); + XSelectInput( + d, + w, + ExposureMask | + KeyPressMask | + KeyReleaseMask | + PointerMotionMask | + ButtonPressMask | + ButtonReleaseMask + ); + XMapRaised(d, w); + i->gc = XCreateGC(d, w, 0, 0); + if (!i->gc) { + print_err("Failed to create X graphics context.\n"); + pbreak(error_platform_error); + } + 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"); + } + init_rendering(a, i, &wa); + i->w = wa.width; + i->h = wa.height; +} + +Btn btn_from_xkey(unsigned key) { + switch (key) { + case XK_Up: return btn_up; + case XK_Down: return btn_down; + case XK_Left: return btn_left; + case XK_Right: return btn_right; + case 0x78: return btn_shoot; + case 0x7a: return btn_jump; + case XK_Return: return btn_start; + default: return btn_unknown; + } +} + +App* new_app(Heap* mem, const char* n) { + App* a; + App_Internal* i; + a = heap_alloc(mem, sizeof *a + sizeof *i); + i = (App_Internal*)(&a[1]); + a->heap = mem; + a->s = 2; + a->o = 0; + a->err = error_none; + i->d = XOpenDisplay(0); + i->omx = i->omy = 0; + i->ms = 1; + if (!i->d) { + print_err("Failed to open X11 display.\n"); + pbreak(error_platform_error); + } + init_window(a, i, n); + a->o = 1; + return a; +} + +void deinit_app(App* a) { + App_Internal* i = (App_Internal*)(&a[1]); + XDestroyImage(i->bb); + XFreeGC(i->d, i->gc); + XDestroyWindow(i->d, i->wi); + XCloseDisplay(i->d); + heap_free(a->heap, a); +} + +void app_begin(App* a) { + App_Internal* i = (App_Internal*)(&a[1]); + Display* d = i->d; + Window w = i->wi; + int j; + i->begin = get_timer(); + for (j = 0; j < btn_count; j++) + a->btn_states[j] &= ~( + btn_state_just_pressed | btn_state_just_released + ); + while (XPending(d)) { + XEvent e; + XWindowAttributes wa; + KeySym sym; + Btn btn; + XNextEvent(d, &e); + switch (e.type) { + case ClientMessage: + if ( + (Atom)e.xclient.data.l[0] == + i->wm_delete + ) { + a->o = 0; + } + break; + case Expose: + XGetWindowAttributes(d, w, &wa); + i->w = mini(wa.width, max_pc_window_w); + i->h = mini(wa.height, max_pc_window_h); + break; + case KeyPress: + sym = XLookupKeysym(&e.xkey, 0); + btn = btn_from_xkey(sym); + a->btn_states[btn] |= + btn_state_pressed | + btn_state_just_pressed; + break; + case KeyRelease: + sym = XLookupKeysym(&e.xkey, 0); + btn = btn_from_xkey(sym); + a->btn_states[btn] &= ~btn_state_pressed; + a->btn_states[btn] |= btn_state_just_released; + break; + default: + break; + } + } +} + +void app_rencpy( + App* a, + App_Internal* i, + int sx, + int sy, + int s +) { + const Colour white = { 0xff, 0xff, 0xff, 0xff }; + const Colour black = { 0x00, 0x00, 0x00, 0xff }; + int x, y, sj = 0; + int w = viewport_w * s; + int h = viewport_h * s; + int ex = sx + w; + int ey = sy + h; + for (y = sy; y < ey; y++, sj++) { + int sy = sj / s; + int si = 0; + for (x = sx; x < ex; x++, si++) { + int sx = si / s; + int idx = sx + sy * viewport_w; + int bit = 1 << (idx & 0x1f); + bit &= a->fb[idx >> 5]; + i->bbp[x + y * max_pc_window_w] = bit? white: black; + } + } + XPutImage( + i->d, + i->wi, + i->gc, + i->bb, + 0, 0, + sx, sy, + w, h + ); +} + +void app_end(App* a) { + int s = 1; + int m1, m2; + App_Internal* i = (App_Internal*)(&a[1]); + if (i->w < i->h) { + m1 = i->w; + m2 = viewport_w; + } else { + m1 = i->h; + m2 = viewport_h; + } + while (m2 * s < m1) s++; /* lol */ + app_rencpy(a, i, 0, 0, s); + i->end = get_timer(); + a->fps = 1000000000 / (i->end - i->begin); +} + +#endif -- cgit v1.2.3-54-g00ecf