summaryrefslogtreecommitdiff
path: root/plat.c
diff options
context:
space:
mode:
authorquou <quou@disroot.org>2024-06-30 18:24:01 +1000
committerquou <quou@disroot.org>2024-06-30 18:27:11 +1000
commit39100e7292d3ee12d387fddfa0f0d7b712e31e1c (patch)
tree28f26b19de857868aeb9ecf23a7fecfc170addf9 /plat.c
initial commit.
Diffstat (limited to 'plat.c')
-rw-r--r--plat.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/plat.c b/plat.c
new file mode 100644
index 0000000..e0d72be
--- /dev/null
+++ b/plat.c
@@ -0,0 +1,413 @@
+#include "config.h"
+#include "error.h"
+#include "memory.h"
+#include "plat.h"
+
+#ifdef plat_posix
+
+#define _POSIX_SOURCE
+#define _GNU_SOURCE
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+
+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) {
+ global_clock = CLOCK_REALTIME;
+ global_freq = 1000000000;
+#if defined(_POSIX_MONOTONIC_CLOCK)
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+ global_clock = CLOCK_MONOTONIC;
+ }
+#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);
+}
+
+#include <stdlib.h>
+
+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 <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <stdlib.h>
+
+typedef struct {
+ Display* d;
+ Window wi;
+ GC gc;
+ int w, h;
+ 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, * fb;
+ int w = i->w = wa->width;
+ int h = i->h = wa->height;
+ int tw = (w + a->s) / a->s;
+ int th = (h + a->s) / a->s;
+ a->w = tw;
+ a->h = th;
+ if (i->bb) {
+ heap_free(a->heap, a->fb);
+ XDestroyImage(i->bb);
+ }
+ p = malloc(sizeof *p * w * h);
+ fb = heap_alloc(a->heap, sizeof *p * tw * th);
+ if (!p || !fb) {
+ print_err("Out of memory.\n");
+ pbreak(error_out_of_memory);
+ }
+ a->fb = fb;
+ 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,
+ a->w * a->s,
+ a->h * a->s,
+ 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);
+}
+
+App* new_app(Heap* mem, int w, int h, 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;
+ a->w = w;
+ a->h = h;
+ i->d = XOpenDisplay(0);
+ 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;
+ i->begin = get_timer();
+ while (XPending(d)) {
+ XEvent e;
+ XWindowAttributes wa;
+ int nw, nh;
+ 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);
+ nw = wa.width;
+ nh = wa.height;
+ if (
+ !i->bb ||
+ nw != i->w ||
+ nh != i->h
+ ) {
+ init_rendering(a, i, &wa);
+ }
+ break;
+ case MotionNotify:
+ a->mx = e.xmotion.x / a->s;
+ a->my = e.xmotion.y / a->s;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void app_rencpy(
+ App* a,
+ App_Internal* i,
+ int x,
+ int y,
+ int w,
+ int h
+) {
+ int rat_x, rat_y;
+ int rrat_x, rrat_y;
+ int sx, sy;
+ int ix, iy, sw, sh, dw, dh;
+ int dsx, dsy;
+ int dex, dey;
+ int pidx, fbits = 9;
+ Colour* dst;
+ const Colour* src;
+ unsigned char t;
+ sw = a->w;
+ sh = a->h;
+ dw = i->w;
+ dh = i->h;
+ rat_x = (sw << fbits * 2) / (dw << fbits);
+ rat_y = (sh << fbits * 2) / (dh << fbits);
+ rrat_x = (dw << fbits * 2) / (sw << fbits);
+ rrat_y = (dh << fbits * 2) / (sh << fbits);
+ dsx = (x * rrat_x) >> fbits;
+ dsy = (y * rrat_y) >> fbits;
+ dex = ((x + w) * rrat_x) >> fbits;
+ dey = ((y + h) * rrat_y) >> fbits;
+ dst = i->bbp;
+ src = a->fb;
+ for (iy = dsy; iy < dey; iy++) {
+ sy = (rat_y * (iy << fbits)) >> (fbits * 2);
+ for (ix = dsx; ix < dex; ix++) {
+ sx = (rat_x * (ix << fbits)) >> (fbits * 2);
+ pidx = sx + sy * sw;
+ dst = &i->bbp[ix + iy * dw];
+ *dst = src[pidx];
+ t = dst->r;
+ dst->r = dst->b;
+ dst->b = t;
+ }
+ }
+ XPutImage(
+ i->d,
+ i->wi,
+ i->gc,
+ i->bb,
+ dsx, dsy,
+ dsx, dsy,
+ dex - dsx, dey - dsy
+ );
+}
+
+void app_end(App* a) {
+ App_Internal* i = (App_Internal*)(&a[1]);
+ app_rencpy(a, i, 0, 0, a->w, a->h);
+ i->end = get_timer();
+ a->fps = 1000000000 / (i->end - i->begin);
+}
+
+#endif