#include "config.h" #include "error.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 #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 } struct File { int handle; int cursor; int size; }; File* file_open(const char* name, File_Flags flags) { File* file; int handle, f = 0; struct stat info; if (flags & file_flags_write) { f |= O_WRONLY | O_TRUNC; } if (flags & file_flags_read) { f |= O_RDONLY; } if (flags & file_flags_read_write) { f |= O_RDWR; } if (stat(name, &info) < 0) { info.st_size = 0; if (flags & file_flags_create) { f |= O_CREAT | S_IRUSR | S_IWUSR; } } handle = open(name, f); if (handle < 0) { return 0; } file = (File*)malloc(sizeof *file); file->handle = handle; file->cursor = 0; file->size = (int)info.st_size; return file; } void file_close(File* file) { close(file->handle); free(file); } void file_read(File* file, void* buffer, int size) { lseek(file->handle, (off_t)file->cursor, SEEK_SET); read(file->handle, buffer, size); file->cursor += size; } void file_write(File* file, const void* buffer, int size) { lseek(file->handle, (off_t)file->cursor, SEEK_SET); write(file->handle, buffer, size); file->cursor += size; } void file_seek(File* file, int offset, File_Whence whence) { switch (whence) { case file_whence_begin: file->cursor = offset; break; case file_whence_end: file->cursor = file->size - offset; break; case file_whence_cur: file->cursor += offset; break; } } int file_size(File* file) { return file->size; } int file_exists(const char* name) { struct stat info; return stat(name, &info) == 0; } 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); } #include 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 #include #include 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, * fb; int w = max_pc_window_w; int h = max_pc_window_h; int tw = max_vp_w; int th = max_vp_h; 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); a->w = (wa.width + a->s) / a->s; a->h = (wa.height + a->s) / a->s; i->w = wa.width; i->h = wa.height; } 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); i->omx = i->omy = 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; 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); a->w = (wa.width + a->s) / a->s; a->h = (wa.height + a->s) / a->s; a->w = mini(a->w, max_vp_w); a->h = mini(a->h, max_vp_w); i->w = mini(wa.width, max_pc_window_w); i->h = mini(wa.height, max_pc_window_h); break; case MotionNotify: if (i->ms) { a->mx = e.xmotion.x / a->s; a->my = e.xmotion.y / a->s; } break; case GenericEvent: if (!i->ms) { if ( XGetEventData(d, &e.xcookie) && e.xcookie.evtype == XI_RawMotion ) { XIRawEvent* re = e.xcookie.data; if (re->valuators.mask_len) { const double* values = re->raw_values; if (XIMaskIsSet(re->valuators.mask, 0)) { i->rmx += (int)values[0]; } if (XIMaskIsSet(re->valuators.mask, 1)) { i->rmy += (int)values[1]; } a->mx = i->rmx / a->s; a->my = i->rmy / a->s; } XFreeEventData(d, &e.xcookie); } } break; default: break; } } a->dmx = a->mx - i->omx; a->dmy = a->my - i->omy; i->omx = a->mx; i->omy = a->my; } 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; 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 * max_pc_window_w]; *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); } void cfg_mouse(App* a, int show) { App_Internal* i = (App_Internal*)(&a[1]); Display* d = i->d; Window w = i->wi; i->ms = show; if (show) { XUndefineCursor(d, w); XUngrabPointer(d, CurrentTime); } else { XColor c; char bd[1] = { 0 }; Pixmap b = XCreateBitmapFromData(d, w, bd, 1, 1); Cursor cur = XCreatePixmapCursor(d, b, b, &c, &c, 0, 0); XIEventMask em; unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; XDefineCursor(d, w, cur); XGrabPointer( d, w, 1, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, w, None, CurrentTime ); em.deviceid = XIAllMasterDevices; em.mask_len = sizeof(mask); em.mask = mask; XISetMask(mask, XI_RawMotion); XISelectEvents(d, DefaultRootWindow(d), &em, 1); i->rmx = a->mx; i->rmy = a->my; } } #endif