diff options
Diffstat (limited to 'plat.c')
-rw-r--r-- | plat.c | 413 |
1 files changed, 413 insertions, 0 deletions
@@ -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 |