aboutsummaryrefslogtreecommitdiff
path: root/plat.c
diff options
context:
space:
mode:
Diffstat (limited to 'plat.c')
-rw-r--r--plat.c429
1 files changed, 429 insertions, 0 deletions
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 <fcntl.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdarg.h>
+#include <stdio.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) {
+ 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 <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+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