From d0e272efaefd904022bb27af90d9f2a3a3db6a30 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Sat, 10 Aug 2024 14:35:30 +0200 Subject: Add bit font --- examples/bitfont.inl.c | 1 + examples/gravity.c | 522 ++++++++++++++++++++++++++++++++++++ examples/landscape.c | 12 +- examples/phys.c | 522 ------------------------------------ examples/ui.c | 708 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1237 insertions(+), 528 deletions(-) create mode 100644 examples/bitfont.inl.c create mode 100755 examples/gravity.c delete mode 100755 examples/phys.c create mode 100755 examples/ui.c (limited to 'examples') diff --git a/examples/bitfont.inl.c b/examples/bitfont.inl.c new file mode 100644 index 0000000..16ddabd --- /dev/null +++ b/examples/bitfont.inl.c @@ -0,0 +1 @@ +0x1f111111f111111f, 0x111f111111f11111, 0x11111f111111f111, 0xf111111f111111f1, 0x11f111111f111111, 0x1111f111111f1111, 0x111111f111111f11, 0x1f111111f111111f, 0x111f111111f11111, 0x11111f111111f111, 0xf111111f111111f1, 0x11f111111f111111, 0x1111f111111f1111, 0x111111f111111f11, 0x1f01011110000000, 0x111f111111f11111, 0x11111f111111f111, 0xf111111f111111f1, 0x11f111111f111111, 0x1111f12000001111, 0x111111f010000011, 0x1f111111f111111f, 0x111f111111f11111, 0x11111f111111f111, 0xf111111f111111f1, 0x11f111111f111111, 0x1111f111111f1111, 0x111111f111111f11, 0x1f111111f111111f, 0x111f111111f11111, 0x11111f111111f111, 0xf0999f99111111f1, 0x11f111111f111111, 0x1111f111111f1111, 0x111111f111111f11, 0x1f111111f111111f, 0x111f069861611111, 0x11111f111111f111, 0xf111111f111111f1, 0x11f111111f111111, 0x1111f111111f1111, 0x111111f111111f11, 0x1f069f860111111f, 0x111f111111f11111, 0x11111f061f960111, 0x111111f111111f1, 0x11f111111f072302, 0x1111f02111111111, 0x699960111111f11, 0x50111111f111111f, 0x111f111111f01113, 0x11111f111111f111, 0xf111111f111111f1, 0x11f111111f111111, 0x1111f111111f1111, 0x111111f111111f11, diff --git a/examples/gravity.c b/examples/gravity.c new file mode 100755 index 0000000..824558b --- /dev/null +++ b/examples/gravity.c @@ -0,0 +1,522 @@ +#if 0 /* +#/ ================================================================ +#/ +#/ gravity.c +#/ +#/ ================================================================ +#/ +#/ Self-compilation shell script +#/ +SRC=${0##*./} +BIN=${SRC%.*} +gcc \ + -Wall -Wextra -Werror -pedantic \ + -Wno-old-style-declaration \ + -Wno-missing-braces \ + -Wno-unused-variable \ + -Wno-unused-but-set-variable \ + -Wno-unused-parameter \ + -O3 \ + -fsanitize=undefined,address,leak -mshstk \ + -lX11 -lm \ + -o $BIN $SRC && \ + ./$BIN $@ && rm $BIN +exit $? # */ +#endif +// ================================================================ +// +// Basic declarations +// +// ================================================================ + +#define _GNU_SOURCE + +typedef signed char i8; +typedef signed short i16; +typedef signed i32; +typedef signed long long i64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned u32; +typedef unsigned long long u64; +typedef char c8; +typedef signed char b8; +typedef float f32; +typedef double f64; + +// ================================================================ +// +// PLATFORM API +// +// ================================================================ + +enum { + KEY_NONE, BUTTON_LEFT, BUTTON_MIDDLE, BUTTON_RIGHT, BUTTON_X1, BUTTON_X2, KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_SPACE, KEY_BRACE_LEFT, KEY_BRACE_RIGHT, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_TILDE, KEY_BACKSLASH, KEY_COMMA, KEY_PERIOD, KEY_SLASH, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_LCTRL, KEY_RCTRL, KEY_LSHIFT, KEY_RSHIFT, KEY_LALT, KEY_RALT, KEY_ESCAPE, KEY_BACKSPACE, KEY_TAB, KEY_ENTER, KEY_PRINTSCREEN, KEY_DELETE, KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_END, KEY_PAGEUP, KEY_PAGEDOWN, KEY_KP_0, KEY_KP_1, KEY_KP_2, KEY_KP_3, KEY_KP_4, KEY_KP_5, KEY_KP_6, KEY_KP_7, KEY_KP_8, KEY_KP_9, KEY_KP_ENTER, KEY_KP_DIVIDE, KEY_KP_MULTIPLY, KEY_KP_PLUS, KEY_KP_MINUS, KEY_KP_DECIMAL, KEY_KP_SEPARATOR, +}; + +typedef struct { + c8 *title; + i32 frame_width; + i32 frame_height; + u32 *pixels; + b8 done; + b8 has_focus; + b8 has_cursor; + i32 cursor_x; + i32 cursor_y; + b8 key_down[512]; + b8 key_pressed[512]; +} Platform; + +Platform platform = {0}; + +void p_init(void); +void p_cleanup(void); +i32 p_handle_events(void); + +// ================================================================ +// +// GAME +// +// ================================================================ + +#include +#include +#include +#include +#include +#include + +typedef struct { + f64 x; + f64 y; + f64 vx; + f64 vy; +} Entity; + +typedef struct { + i64 time; + i64 num_entities; + Entity entities[10 * 1024 * 1024]; +} World; + +i64 time_milliseconds() { + struct timespec t; + timespec_get(&t, TIME_UTC); + + return (i64) t.tv_sec * 1000ll + (i64) t.tv_nsec / 1000000ll; +} + +u32 u32_from_rgb(f32 red, f32 green, f32 blue) { + i32 r = (i32) floor(red * 255.f); + i32 g = (i32) floor(green * 255.f); + i32 b = (i32) floor(blue * 255.f); + + if (r < 0) r = 0; + if (r > 255) r = 255; + if (g < 0) g = 0; + if (g > 255) g = 255; + if (b < 0) b = 0; + if (b > 255) b = 255; + + return (r << 16) | (g << 8) | b; +} + +i32 main(i32 argc, c8 **argv) { + u32 background = u32_from_rgb(.0f, .0f, .08f); + + platform = (Platform) { + .title = "Gravity", + .frame_width = 960, + .frame_height = 720, + }; + + p_init(); + + srand(time(0)); + + static World world = {0}; + i64 time_0 = time_milliseconds(); + + while (!platform.done) { + i32 num_events = p_handle_events(); + + i64 time_elapsed = time_milliseconds() - time_0; + time_0 += time_elapsed; + + if (platform.key_pressed[BUTTON_LEFT]) { + i64 n = world.num_entities++; + + world.entities[n] = (Entity) { + .x = (f64) platform.cursor_x - (f64) platform.frame_width / 2, + .y = -((f64) platform.cursor_y - (f64) platform.frame_height / 2), + }; + } + + if (time_elapsed > 0) { + if (platform.key_down[BUTTON_RIGHT]) { + for (i64 n = 0; n < time_elapsed; ++n) + world.entities[world.num_entities + n] = (Entity) { + .x = (f64) platform.cursor_x - (f64) platform.frame_width / 2, + .y = -((f64) platform.cursor_y - (f64) platform.frame_height / 2), + }; + + world.num_entities += time_elapsed; + } + + for (i64 n = 0; n < world.num_entities; ++n) { + Entity *e = &world.entities[n]; + + for (i64 k = 0; k < n; ++k) { + Entity *u = &world.entities[k]; + + f64 dx = e->x - u->x; + f64 dy = e->y - u->y; + f64 d = dx * dx + dy * dy; + if (d < 1000.) continue; + + f64 r = sqrt(d); + dx /= r; + dy /= r; + + f64 a = (1. / d) * time_elapsed; + + if (d < 5000.) + a = -a; + + e->vx -= dx * a; + e->vy -= dy * a; + u->vx += dx * a; + u->vy += dy * a; + } + } + + for (i64 n = 0; n < world.num_entities; ++n) { + Entity *e = &world.entities[n]; + + e->x += e->vx * time_elapsed; + e->y += e->vy * time_elapsed; + + f64 z = (M_PI * 2.0) * (rand() % 10000) * .0001; + e->vx += .0001 * cos(z); + e->vy += .0001 * sin(z); + } + + world.time += time_elapsed; + } + + for (i32 j = 0; j < platform.frame_height; ++j) + for (i32 i = 0; i < platform.frame_width; ++i) + platform.pixels[j * platform.frame_width + i] = background; + + for (i64 n = 0; n < world.num_entities; ++n) { + Entity *e = &world.entities[n]; + + i32 x = platform.frame_width / 2 + (i32) floor(e->x + .5); + i32 y = platform.frame_height / 2 - (i32) floor(e->y + .5); + + for (i32 j = y - 10; j <= y + 10; ++j) { + if (j < 0 || j >= platform.frame_height) continue; + for (i32 i = x - 10; i <= x + 10; ++i) { + if (i < 0 || i >= platform.frame_width) continue; + if ((i - x) * (i - x) + (j - y) * (j - y) > 100) continue; + f64 v = (e->vx * e->vx + e->vy * e->vy) * 8.; + platform.pixels[j * platform.frame_width + i] = u32_from_rgb(-.2 + v * 2., .1 + v * .7, 1. - v); + } + } + } + } + + p_cleanup(); + return 0; +} + +// ================================================================ +// +// LINUX X11 PLATFORM IMPLEMENTATION +// +// ================================================================ + +#include +#include + +static i16 key_table[512]; +static u32 buffer[10 * 1024 * 1024]; + +static XImage image = { + .xoffset = 0, + .format = ZPixmap, + .data = (c8 *) buffer, + .byte_order = LSBFirst, + .bitmap_unit = 32, + .bitmap_bit_order = LSBFirst, + .bitmap_pad = 32, + .bits_per_pixel = 32, + .red_mask = 0xff0000, + .green_mask = 0x00ff00, + .blue_mask = 0x0000ff, +}; + +static Display * display; +static i32 screen; +static i32 depth; +static Visual * visual; +static GC gc; +static Window window; +static Pixmap pixmap; +static Atom wm_delete_window; +static XEvent ev; +static XWindowAttributes attrs; + +void p_init(void) { + display = XOpenDisplay(NULL); + assert(display != NULL); + + key_table[XKeysymToKeycode(display, XK_Left)] = KEY_LEFT; + key_table[XKeysymToKeycode(display, XK_Right)] = KEY_RIGHT; + key_table[XKeysymToKeycode(display, XK_Up)] = KEY_UP; + key_table[XKeysymToKeycode(display, XK_Down)] = KEY_DOWN; + key_table[XKeysymToKeycode(display, XK_1)] = KEY_1; + key_table[XKeysymToKeycode(display, XK_2)] = KEY_2; + key_table[XKeysymToKeycode(display, XK_3)] = KEY_3; + key_table[XKeysymToKeycode(display, XK_4)] = KEY_4; + key_table[XKeysymToKeycode(display, XK_5)] = KEY_5; + key_table[XKeysymToKeycode(display, XK_6)] = KEY_6; + key_table[XKeysymToKeycode(display, XK_7)] = KEY_7; + key_table[XKeysymToKeycode(display, XK_8)] = KEY_8; + key_table[XKeysymToKeycode(display, XK_9)] = KEY_9; + key_table[XKeysymToKeycode(display, XK_0)] = KEY_0; + key_table[XKeysymToKeycode(display, XK_A)] = KEY_A; + key_table[XKeysymToKeycode(display, XK_B)] = KEY_B; + key_table[XKeysymToKeycode(display, XK_C)] = KEY_C; + key_table[XKeysymToKeycode(display, XK_D)] = KEY_D; + key_table[XKeysymToKeycode(display, XK_E)] = KEY_E; + key_table[XKeysymToKeycode(display, XK_F)] = KEY_F; + key_table[XKeysymToKeycode(display, XK_G)] = KEY_G; + key_table[XKeysymToKeycode(display, XK_H)] = KEY_H; + key_table[XKeysymToKeycode(display, XK_I)] = KEY_I; + key_table[XKeysymToKeycode(display, XK_J)] = KEY_J; + key_table[XKeysymToKeycode(display, XK_K)] = KEY_K; + key_table[XKeysymToKeycode(display, XK_L)] = KEY_L; + key_table[XKeysymToKeycode(display, XK_M)] = KEY_M; + key_table[XKeysymToKeycode(display, XK_N)] = KEY_N; + key_table[XKeysymToKeycode(display, XK_O)] = KEY_O; + key_table[XKeysymToKeycode(display, XK_P)] = KEY_P; + key_table[XKeysymToKeycode(display, XK_Q)] = KEY_Q; + key_table[XKeysymToKeycode(display, XK_R)] = KEY_R; + key_table[XKeysymToKeycode(display, XK_S)] = KEY_S; + key_table[XKeysymToKeycode(display, XK_T)] = KEY_T; + key_table[XKeysymToKeycode(display, XK_V)] = KEY_V; + key_table[XKeysymToKeycode(display, XK_W)] = KEY_W; + key_table[XKeysymToKeycode(display, XK_X)] = KEY_X; + key_table[XKeysymToKeycode(display, XK_Y)] = KEY_Y; + key_table[XKeysymToKeycode(display, XK_Z)] = KEY_Z; + key_table[XKeysymToKeycode(display, XK_space)] = KEY_SPACE; + key_table[XKeysymToKeycode(display, XK_braceleft)] = KEY_BRACE_LEFT; + key_table[XKeysymToKeycode(display, XK_braceright)] = KEY_BRACE_RIGHT; + key_table[XKeysymToKeycode(display, XK_colon)] = KEY_SEMICOLON; + key_table[XKeysymToKeycode(display, XK_quotedbl)] = KEY_APOSTROPHE; + key_table[XKeysymToKeycode(display, XK_asciitilde)] = KEY_TILDE; + key_table[XKeysymToKeycode(display, XK_backslash)] = KEY_BACKSLASH; + key_table[XKeysymToKeycode(display, XK_comma)] = KEY_COMMA; + key_table[XKeysymToKeycode(display, XK_greater)] = KEY_PERIOD; + key_table[XKeysymToKeycode(display, XK_question)] = KEY_SLASH; + key_table[XKeysymToKeycode(display, XK_F1)] = KEY_F1; + key_table[XKeysymToKeycode(display, XK_F2)] = KEY_F2; + key_table[XKeysymToKeycode(display, XK_F3)] = KEY_F3; + key_table[XKeysymToKeycode(display, XK_F4)] = KEY_F4; + key_table[XKeysymToKeycode(display, XK_F5)] = KEY_F5; + key_table[XKeysymToKeycode(display, XK_F6)] = KEY_F6; + key_table[XKeysymToKeycode(display, XK_F7)] = KEY_F7; + key_table[XKeysymToKeycode(display, XK_F8)] = KEY_F8; + key_table[XKeysymToKeycode(display, XK_F9)] = KEY_F9; + key_table[XKeysymToKeycode(display, XK_F10)] = KEY_F10; + key_table[XKeysymToKeycode(display, XK_F11)] = KEY_F11; + key_table[XKeysymToKeycode(display, XK_F12)] = KEY_F12; + key_table[XKeysymToKeycode(display, XK_Control_L)] = KEY_LCTRL; + key_table[XKeysymToKeycode(display, XK_Control_R)] = KEY_RCTRL; + key_table[XKeysymToKeycode(display, XK_Shift_L)] = KEY_LSHIFT; + key_table[XKeysymToKeycode(display, XK_Shift_R)] = KEY_RSHIFT; + key_table[XKeysymToKeycode(display, XK_Alt_L)] = KEY_LALT; + key_table[XKeysymToKeycode(display, XK_Alt_R)] = KEY_RALT; + key_table[XKeysymToKeycode(display, XK_Escape)] = KEY_ESCAPE; + key_table[XKeysymToKeycode(display, XK_BackSpace)] = KEY_BACKSPACE; + key_table[XKeysymToKeycode(display, XK_Tab)] = KEY_TAB; + key_table[XKeysymToKeycode(display, XK_Return)] = KEY_ENTER; + key_table[XKeysymToKeycode(display, XK_Print)] = KEY_PRINTSCREEN; + key_table[XKeysymToKeycode(display, XK_Delete)] = KEY_DELETE; + key_table[XKeysymToKeycode(display, XK_Pause)] = KEY_PAUSE; + key_table[XKeysymToKeycode(display, XK_Insert)] = KEY_INSERT; + key_table[XKeysymToKeycode(display, XK_Home)] = KEY_HOME; + key_table[XKeysymToKeycode(display, XK_End)] = KEY_END; + key_table[XKeysymToKeycode(display, XK_Page_Up)] = KEY_PAGEUP; + key_table[XKeysymToKeycode(display, XK_Page_Down)] = KEY_PAGEDOWN; + key_table[XKeysymToKeycode(display, XK_KP_0)] = KEY_KP_0; + key_table[XKeysymToKeycode(display, XK_KP_1)] = KEY_KP_1; + key_table[XKeysymToKeycode(display, XK_KP_2)] = KEY_KP_2; + key_table[XKeysymToKeycode(display, XK_KP_3)] = KEY_KP_3; + key_table[XKeysymToKeycode(display, XK_KP_4)] = KEY_KP_4; + key_table[XKeysymToKeycode(display, XK_KP_5)] = KEY_KP_5; + key_table[XKeysymToKeycode(display, XK_KP_6)] = KEY_KP_6; + key_table[XKeysymToKeycode(display, XK_KP_7)] = KEY_KP_7; + key_table[XKeysymToKeycode(display, XK_KP_8)] = KEY_KP_8; + key_table[XKeysymToKeycode(display, XK_KP_9)] = KEY_KP_9; + key_table[XKeysymToKeycode(display, XK_KP_Enter)] = KEY_KP_ENTER; + key_table[XKeysymToKeycode(display, XK_KP_Divide)] = KEY_KP_DIVIDE; + key_table[XKeysymToKeycode(display, XK_KP_Multiply)] = KEY_KP_MULTIPLY; + key_table[XKeysymToKeycode(display, XK_KP_Add)] = KEY_KP_PLUS; + key_table[XKeysymToKeycode(display, XK_KP_Subtract)] = KEY_KP_MINUS; + key_table[XKeysymToKeycode(display, XK_KP_Decimal)] = KEY_KP_DECIMAL; + key_table[XKeysymToKeycode(display, XK_KP_Separator)] = KEY_KP_SEPARATOR; + + screen = DefaultScreen(display); + depth = DefaultDepth (display, screen); + visual = DefaultVisual(display, screen); + gc = DefaultGC (display, screen); + + XSetGraphicsExposures(display, gc, False); + + i32 display_width = DisplayWidth (display, screen); + i32 display_height = DisplayHeight(display, screen); + + i32 x = (display_width - platform.frame_width) / 2; + i32 y = (display_height - platform.frame_height) / 2; + + window = XCreateWindow(display, XDefaultRootWindow(display), x, y, platform.frame_width, platform.frame_height, 0, depth, InputOutput, visual, CWEventMask, &(XSetWindowAttributes) { .event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | VisibilityChangeMask | FocusChangeMask | StructureNotifyMask | SubstructureNotifyMask, }); + + platform.pixels = buffer; + + image.width = platform.frame_width; + image.height = platform.frame_height; + image.depth = depth; + image.bytes_per_line = 4 * platform.frame_width; + + XInitImage(&image); + + pixmap = XCreatePixmap(display, window, platform.frame_width, platform.frame_height, depth); + wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False); + + XSetWMProtocols(display, window, &wm_delete_window, 1); + + XStoreName(display, window, platform.title); + XMapWindow(display, window); +} + +void p_cleanup(void) { + XFreePixmap (display, pixmap); + XDestroyWindow(display, window); + XCloseDisplay (display); +} + +i32 p_handle_events(void) { + XGetWindowAttributes(display, window, &attrs); + + if (attrs.width == platform.frame_width && attrs.height == platform.frame_height) { + XPutImage(display, pixmap, gc, &image, 0, 0, 0, 0, attrs.width, attrs.height); + XCopyArea(display, pixmap, window, gc, 0, 0, attrs.width, attrs.height, 0, 0); + } + + XFlush(display); + + i32 num_events = XEventsQueued(display, QueuedAlready); + + memset(platform.key_pressed, 0, sizeof platform.key_pressed); + + for (i32 i = 0; i < num_events; ++i) { + XNextEvent(display, &ev); + + switch (ev.type) { + case DestroyNotify: + platform.done = 1; + break; + + case MotionNotify: + platform.cursor_x = ev.xmotion.x; + platform.cursor_y = ev.xmotion.y; + break; + + case ButtonPress: + platform.cursor_x = ev.xbutton.x; + platform.cursor_y = ev.xbutton.y; + switch (ev.xbutton.button) { + case Button1: + platform.key_down[BUTTON_LEFT] = 1; + platform.key_pressed[BUTTON_LEFT]++; + break; + case Button2: + platform.key_down[BUTTON_MIDDLE] = 1; + platform.key_pressed[BUTTON_MIDDLE]++; + break; + case Button3: + platform.key_down[BUTTON_RIGHT] = 1; + platform.key_pressed[BUTTON_RIGHT]++; + break; + case Button4: + platform.key_down[BUTTON_X1] = 1; + platform.key_pressed[BUTTON_X1]++; + break; + case Button5: + platform.key_down[BUTTON_X2] = 1; + platform.key_pressed[BUTTON_X2]++; + break; + default:; + } + break; + + case ButtonRelease: + platform.cursor_x = ev.xbutton.x; + platform.cursor_y = ev.xbutton.y; + switch (ev.xbutton.button) { + case Button1: platform.key_down[BUTTON_LEFT] = 0; break; + case Button2: platform.key_down[BUTTON_MIDDLE] = 0; break; + case Button3: platform.key_down[BUTTON_RIGHT] = 0; break; + case Button4: platform.key_down[BUTTON_X1] = 0; break; + case Button5: platform.key_down[BUTTON_X2] = 0; break; + default:; + } + break; + + case KeyPress: + platform.cursor_x = ev.xkey.x; + platform.cursor_y = ev.xkey.y; + platform.key_down [key_table[ev.xkey.keycode]] = 1; + platform.key_pressed[key_table[ev.xkey.keycode]]++; + break; + + case KeyRelease: + platform.cursor_x = ev.xkey.x; + platform.cursor_y = ev.xkey.y; + platform.key_down [key_table[ev.xkey.keycode]] = 0; + platform.key_pressed[key_table[ev.xkey.keycode]]--; + break; + + case EnterNotify: platform.has_cursor = 1; break; + case LeaveNotify: platform.has_cursor = 0; break; + case FocusIn: platform.has_focus = 1; break; + case FocusOut: platform.has_focus = 0; break; + + case ClientMessage: + if ((Atom) ev.xclient.data.l[0] == wm_delete_window) + platform.done = 1; + break; + + default:; + } + } + + if ((platform.frame_width != attrs.width || platform.frame_height != attrs.height) && attrs.width * attrs.height * 4 <= (i32) sizeof buffer) { + if (attrs.width > 0 && attrs.height > 0) { + image.width = attrs.width; + image.height = attrs.height; + image.bytes_per_line = 4 * attrs.width; + + XFreePixmap(display, pixmap); + pixmap = XCreatePixmap(display, window, attrs.width, attrs.height, depth); + } + + platform.frame_width = attrs.width; + platform.frame_height = attrs.height; + } + + return num_events; +} diff --git a/examples/landscape.c b/examples/landscape.c index 018242b..a3de2cf 100755 --- a/examples/landscape.c +++ b/examples/landscape.c @@ -87,9 +87,6 @@ enum { IPv4 = 1, IPv6 = 2, - BUTTON_LEFT = 256, - BUTTON_MIDDLE, - BUTTON_RIGHT, KEY_LEFT, KEY_RIGHT, KEY_UP, @@ -109,8 +106,11 @@ enum { KEY_END, KEY_PAGEUP, KEY_PAGEDOWN, - KEY_F_ = 256 * 2, - KEY_KP_ = 256 * 3, + KEY_KP_ = 128, + KEY_F_ = 256, + BUTTON_LEFT = KEY_F_ + 64, + BUTTON_MIDDLE, + BUTTON_RIGHT, }; typedef struct { @@ -388,7 +388,7 @@ i32 main(i32 argc, c8 **argv) { u32 background = u32_from_rgb(.03f, .08f, .01f); platform = (Platform) { - .title = "Game", + .title = "Landscape", .frame_width = 960, .frame_height = 720, }; diff --git a/examples/phys.c b/examples/phys.c deleted file mode 100755 index 19db4c8..0000000 --- a/examples/phys.c +++ /dev/null @@ -1,522 +0,0 @@ -#if 0 /* -#/ ================================================================ -#/ -#/ phys.c -#/ -#/ ================================================================ -#/ -#/ Self-compilation shell script -#/ -SRC=${0##*./} -BIN=${SRC%.*} -gcc \ - -Wall -Wextra -Werror -pedantic \ - -Wno-old-style-declaration \ - -Wno-missing-braces \ - -Wno-unused-variable \ - -Wno-unused-but-set-variable \ - -Wno-unused-parameter \ - -O3 \ - -fsanitize=undefined,address,leak -mshstk \ - -lX11 -lm \ - -o $BIN $SRC && \ - ./$BIN $@ && rm $BIN -exit $? # */ -#endif -// ================================================================ -// -// Basic declarations -// -// ================================================================ - -#define _GNU_SOURCE - -typedef signed char i8; -typedef signed short i16; -typedef signed i32; -typedef signed long long i64; -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned u32; -typedef unsigned long long u64; -typedef char c8; -typedef signed char b8; -typedef float f32; -typedef double f64; - -// ================================================================ -// -// PLATFORM API -// -// ================================================================ - -enum { - KEY_NONE, BUTTON_LEFT, BUTTON_MIDDLE, BUTTON_RIGHT, BUTTON_X1, BUTTON_X2, KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_SPACE, KEY_BRACE_LEFT, KEY_BRACE_RIGHT, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_TILDE, KEY_BACKSLASH, KEY_COMMA, KEY_PERIOD, KEY_SLASH, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_LCTRL, KEY_RCTRL, KEY_LSHIFT, KEY_RSHIFT, KEY_LALT, KEY_RALT, KEY_ESCAPE, KEY_BACKSPACE, KEY_TAB, KEY_ENTER, KEY_PRINTSCREEN, KEY_DELETE, KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_END, KEY_PAGEUP, KEY_PAGEDOWN, KEY_KP_0, KEY_KP_1, KEY_KP_2, KEY_KP_3, KEY_KP_4, KEY_KP_5, KEY_KP_6, KEY_KP_7, KEY_KP_8, KEY_KP_9, KEY_KP_ENTER, KEY_KP_DIVIDE, KEY_KP_MULTIPLY, KEY_KP_PLUS, KEY_KP_MINUS, KEY_KP_DECIMAL, KEY_KP_SEPARATOR, -}; - -typedef struct { - c8 *title; - i32 frame_width; - i32 frame_height; - u32 *pixels; - b8 done; - b8 has_focus; - b8 has_cursor; - i32 cursor_x; - i32 cursor_y; - b8 key_down[512]; - b8 key_pressed[512]; -} Platform; - -Platform platform = {0}; - -void p_init(void); -void p_cleanup(void); -i32 p_handle_events(void); - -// ================================================================ -// -// GAME -// -// ================================================================ - -#include -#include -#include -#include -#include -#include - -typedef struct { - f64 x; - f64 y; - f64 vx; - f64 vy; -} Entity; - -typedef struct { - i64 time; - i64 num_entities; - Entity entities[10 * 1024 * 1024]; -} World; - -i64 time_milliseconds() { - struct timespec t; - timespec_get(&t, TIME_UTC); - - return (i64) t.tv_sec * 1000ll + (i64) t.tv_nsec / 1000000ll; -} - -u32 u32_from_rgb(f32 red, f32 green, f32 blue) { - i32 r = (i32) floor(red * 255.f); - i32 g = (i32) floor(green * 255.f); - i32 b = (i32) floor(blue * 255.f); - - if (r < 0) r = 0; - if (r > 255) r = 255; - if (g < 0) g = 0; - if (g > 255) g = 255; - if (b < 0) b = 0; - if (b > 255) b = 255; - - return (r << 16) | (g << 8) | b; -} - -i32 main(i32 argc, c8 **argv) { - u32 background = u32_from_rgb(.0f, .0f, .08f); - - platform = (Platform) { - .title = "Game", - .frame_width = 960, - .frame_height = 720, - }; - - p_init(); - - srand(time(0)); - - static World world = {0}; - i64 time_0 = time_milliseconds(); - - while (!platform.done) { - i32 num_events = p_handle_events(); - - i64 time_elapsed = time_milliseconds() - time_0; - time_0 += time_elapsed; - - if (platform.key_pressed[BUTTON_LEFT]) { - i64 n = world.num_entities++; - - world.entities[n] = (Entity) { - .x = (f64) platform.cursor_x - (f64) platform.frame_width / 2, - .y = -((f64) platform.cursor_y - (f64) platform.frame_height / 2), - }; - } - - if (time_elapsed > 0) { - if (platform.key_down[BUTTON_RIGHT]) { - for (i64 n = 0; n < time_elapsed; ++n) - world.entities[world.num_entities + n] = (Entity) { - .x = (f64) platform.cursor_x - (f64) platform.frame_width / 2, - .y = -((f64) platform.cursor_y - (f64) platform.frame_height / 2), - }; - - world.num_entities += time_elapsed; - } - - for (i64 n = 0; n < world.num_entities; ++n) { - Entity *e = &world.entities[n]; - - for (i64 k = 0; k < n; ++k) { - Entity *u = &world.entities[k]; - - f64 dx = e->x - u->x; - f64 dy = e->y - u->y; - f64 d = dx * dx + dy * dy; - if (d < 1000.) continue; - - f64 r = sqrt(d); - dx /= r; - dy /= r; - - f64 a = (1. / d) * time_elapsed; - - if (d < 5000.) - a = -a; - - e->vx -= dx * a; - e->vy -= dy * a; - u->vx += dx * a; - u->vy += dy * a; - } - } - - for (i64 n = 0; n < world.num_entities; ++n) { - Entity *e = &world.entities[n]; - - e->x += e->vx * time_elapsed; - e->y += e->vy * time_elapsed; - - f64 z = (M_PI * 2.0) * (rand() % 10000) * .0001; - e->vx += .0001 * cos(z); - e->vy += .0001 * sin(z); - } - - world.time += time_elapsed; - } - - for (i32 j = 0; j < platform.frame_height; ++j) - for (i32 i = 0; i < platform.frame_width; ++i) - platform.pixels[j * platform.frame_width + i] = background; - - for (i64 n = 0; n < world.num_entities; ++n) { - Entity *e = &world.entities[n]; - - i32 x = platform.frame_width / 2 + (i32) floor(e->x + .5); - i32 y = platform.frame_height / 2 - (i32) floor(e->y + .5); - - for (i32 j = y - 10; j <= y + 10; ++j) { - if (j < 0 || j >= platform.frame_height) continue; - for (i32 i = x - 10; i <= x + 10; ++i) { - if (i < 0 || i >= platform.frame_width) continue; - if ((i - x) * (i - x) + (j - y) * (j - y) > 100) continue; - f64 v = (e->vx * e->vx + e->vy * e->vy) * 8.; - platform.pixels[j * platform.frame_width + i] = u32_from_rgb(-.2 + v * 2., .1 + v * .7, 1. - v); - } - } - } - } - - p_cleanup(); - return 0; -} - -// ================================================================ -// -// LINUX X11 PLATFORM IMPLEMENTATION -// -// ================================================================ - -#include -#include - -static i16 key_table[512]; -static u32 buffer[10 * 1024 * 1024]; - -static XImage image = { - .xoffset = 0, - .format = ZPixmap, - .data = (c8 *) buffer, - .byte_order = LSBFirst, - .bitmap_unit = 32, - .bitmap_bit_order = LSBFirst, - .bitmap_pad = 32, - .bits_per_pixel = 32, - .red_mask = 0xff0000, - .green_mask = 0x00ff00, - .blue_mask = 0x0000ff, -}; - -static Display * display; -static i32 screen; -static i32 depth; -static Visual * visual; -static GC gc; -static Window window; -static Pixmap pixmap; -static Atom wm_delete_window; -static XEvent ev; -static XWindowAttributes attrs; - -void p_init(void) { - display = XOpenDisplay(NULL); - assert(display != NULL); - - key_table[XKeysymToKeycode(display, XK_Left)] = KEY_LEFT; - key_table[XKeysymToKeycode(display, XK_Right)] = KEY_RIGHT; - key_table[XKeysymToKeycode(display, XK_Up)] = KEY_UP; - key_table[XKeysymToKeycode(display, XK_Down)] = KEY_DOWN; - key_table[XKeysymToKeycode(display, XK_1)] = KEY_1; - key_table[XKeysymToKeycode(display, XK_2)] = KEY_2; - key_table[XKeysymToKeycode(display, XK_3)] = KEY_3; - key_table[XKeysymToKeycode(display, XK_4)] = KEY_4; - key_table[XKeysymToKeycode(display, XK_5)] = KEY_5; - key_table[XKeysymToKeycode(display, XK_6)] = KEY_6; - key_table[XKeysymToKeycode(display, XK_7)] = KEY_7; - key_table[XKeysymToKeycode(display, XK_8)] = KEY_8; - key_table[XKeysymToKeycode(display, XK_9)] = KEY_9; - key_table[XKeysymToKeycode(display, XK_0)] = KEY_0; - key_table[XKeysymToKeycode(display, XK_A)] = KEY_A; - key_table[XKeysymToKeycode(display, XK_B)] = KEY_B; - key_table[XKeysymToKeycode(display, XK_C)] = KEY_C; - key_table[XKeysymToKeycode(display, XK_D)] = KEY_D; - key_table[XKeysymToKeycode(display, XK_E)] = KEY_E; - key_table[XKeysymToKeycode(display, XK_F)] = KEY_F; - key_table[XKeysymToKeycode(display, XK_G)] = KEY_G; - key_table[XKeysymToKeycode(display, XK_H)] = KEY_H; - key_table[XKeysymToKeycode(display, XK_I)] = KEY_I; - key_table[XKeysymToKeycode(display, XK_J)] = KEY_J; - key_table[XKeysymToKeycode(display, XK_K)] = KEY_K; - key_table[XKeysymToKeycode(display, XK_L)] = KEY_L; - key_table[XKeysymToKeycode(display, XK_M)] = KEY_M; - key_table[XKeysymToKeycode(display, XK_N)] = KEY_N; - key_table[XKeysymToKeycode(display, XK_O)] = KEY_O; - key_table[XKeysymToKeycode(display, XK_P)] = KEY_P; - key_table[XKeysymToKeycode(display, XK_Q)] = KEY_Q; - key_table[XKeysymToKeycode(display, XK_R)] = KEY_R; - key_table[XKeysymToKeycode(display, XK_S)] = KEY_S; - key_table[XKeysymToKeycode(display, XK_T)] = KEY_T; - key_table[XKeysymToKeycode(display, XK_V)] = KEY_V; - key_table[XKeysymToKeycode(display, XK_W)] = KEY_W; - key_table[XKeysymToKeycode(display, XK_X)] = KEY_X; - key_table[XKeysymToKeycode(display, XK_Y)] = KEY_Y; - key_table[XKeysymToKeycode(display, XK_Z)] = KEY_Z; - key_table[XKeysymToKeycode(display, XK_space)] = KEY_SPACE; - key_table[XKeysymToKeycode(display, XK_braceleft)] = KEY_BRACE_LEFT; - key_table[XKeysymToKeycode(display, XK_braceright)] = KEY_BRACE_RIGHT; - key_table[XKeysymToKeycode(display, XK_colon)] = KEY_SEMICOLON; - key_table[XKeysymToKeycode(display, XK_quotedbl)] = KEY_APOSTROPHE; - key_table[XKeysymToKeycode(display, XK_asciitilde)] = KEY_TILDE; - key_table[XKeysymToKeycode(display, XK_backslash)] = KEY_BACKSLASH; - key_table[XKeysymToKeycode(display, XK_comma)] = KEY_COMMA; - key_table[XKeysymToKeycode(display, XK_greater)] = KEY_PERIOD; - key_table[XKeysymToKeycode(display, XK_question)] = KEY_SLASH; - key_table[XKeysymToKeycode(display, XK_F1)] = KEY_F1; - key_table[XKeysymToKeycode(display, XK_F2)] = KEY_F2; - key_table[XKeysymToKeycode(display, XK_F3)] = KEY_F3; - key_table[XKeysymToKeycode(display, XK_F4)] = KEY_F4; - key_table[XKeysymToKeycode(display, XK_F5)] = KEY_F5; - key_table[XKeysymToKeycode(display, XK_F6)] = KEY_F6; - key_table[XKeysymToKeycode(display, XK_F7)] = KEY_F7; - key_table[XKeysymToKeycode(display, XK_F8)] = KEY_F8; - key_table[XKeysymToKeycode(display, XK_F9)] = KEY_F9; - key_table[XKeysymToKeycode(display, XK_F10)] = KEY_F10; - key_table[XKeysymToKeycode(display, XK_F11)] = KEY_F11; - key_table[XKeysymToKeycode(display, XK_F12)] = KEY_F12; - key_table[XKeysymToKeycode(display, XK_Control_L)] = KEY_LCTRL; - key_table[XKeysymToKeycode(display, XK_Control_R)] = KEY_RCTRL; - key_table[XKeysymToKeycode(display, XK_Shift_L)] = KEY_LSHIFT; - key_table[XKeysymToKeycode(display, XK_Shift_R)] = KEY_RSHIFT; - key_table[XKeysymToKeycode(display, XK_Alt_L)] = KEY_LALT; - key_table[XKeysymToKeycode(display, XK_Alt_R)] = KEY_RALT; - key_table[XKeysymToKeycode(display, XK_Escape)] = KEY_ESCAPE; - key_table[XKeysymToKeycode(display, XK_BackSpace)] = KEY_BACKSPACE; - key_table[XKeysymToKeycode(display, XK_Tab)] = KEY_TAB; - key_table[XKeysymToKeycode(display, XK_Return)] = KEY_ENTER; - key_table[XKeysymToKeycode(display, XK_Print)] = KEY_PRINTSCREEN; - key_table[XKeysymToKeycode(display, XK_Delete)] = KEY_DELETE; - key_table[XKeysymToKeycode(display, XK_Pause)] = KEY_PAUSE; - key_table[XKeysymToKeycode(display, XK_Insert)] = KEY_INSERT; - key_table[XKeysymToKeycode(display, XK_Home)] = KEY_HOME; - key_table[XKeysymToKeycode(display, XK_End)] = KEY_END; - key_table[XKeysymToKeycode(display, XK_Page_Up)] = KEY_PAGEUP; - key_table[XKeysymToKeycode(display, XK_Page_Down)] = KEY_PAGEDOWN; - key_table[XKeysymToKeycode(display, XK_KP_0)] = KEY_KP_0; - key_table[XKeysymToKeycode(display, XK_KP_1)] = KEY_KP_1; - key_table[XKeysymToKeycode(display, XK_KP_2)] = KEY_KP_2; - key_table[XKeysymToKeycode(display, XK_KP_3)] = KEY_KP_3; - key_table[XKeysymToKeycode(display, XK_KP_4)] = KEY_KP_4; - key_table[XKeysymToKeycode(display, XK_KP_5)] = KEY_KP_5; - key_table[XKeysymToKeycode(display, XK_KP_6)] = KEY_KP_6; - key_table[XKeysymToKeycode(display, XK_KP_7)] = KEY_KP_7; - key_table[XKeysymToKeycode(display, XK_KP_8)] = KEY_KP_8; - key_table[XKeysymToKeycode(display, XK_KP_9)] = KEY_KP_9; - key_table[XKeysymToKeycode(display, XK_KP_Enter)] = KEY_KP_ENTER; - key_table[XKeysymToKeycode(display, XK_KP_Divide)] = KEY_KP_DIVIDE; - key_table[XKeysymToKeycode(display, XK_KP_Multiply)] = KEY_KP_MULTIPLY; - key_table[XKeysymToKeycode(display, XK_KP_Add)] = KEY_KP_PLUS; - key_table[XKeysymToKeycode(display, XK_KP_Subtract)] = KEY_KP_MINUS; - key_table[XKeysymToKeycode(display, XK_KP_Decimal)] = KEY_KP_DECIMAL; - key_table[XKeysymToKeycode(display, XK_KP_Separator)] = KEY_KP_SEPARATOR; - - screen = DefaultScreen(display); - depth = DefaultDepth (display, screen); - visual = DefaultVisual(display, screen); - gc = DefaultGC (display, screen); - - XSetGraphicsExposures(display, gc, False); - - i32 display_width = DisplayWidth (display, screen); - i32 display_height = DisplayHeight(display, screen); - - i32 x = (display_width - platform.frame_width) / 2; - i32 y = (display_height - platform.frame_height) / 2; - - window = XCreateWindow(display, XDefaultRootWindow(display), x, y, platform.frame_width, platform.frame_height, 0, depth, InputOutput, visual, CWEventMask, &(XSetWindowAttributes) { .event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | VisibilityChangeMask | FocusChangeMask | StructureNotifyMask | SubstructureNotifyMask, }); - - platform.pixels = buffer; - - image.width = platform.frame_width; - image.height = platform.frame_height; - image.depth = depth; - image.bytes_per_line = 4 * platform.frame_width; - - XInitImage(&image); - - pixmap = XCreatePixmap(display, window, platform.frame_width, platform.frame_height, depth); - wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False); - - XSetWMProtocols(display, window, &wm_delete_window, 1); - - XStoreName(display, window, platform.title); - XMapWindow(display, window); -} - -void p_cleanup(void) { - XFreePixmap (display, pixmap); - XDestroyWindow(display, window); - XCloseDisplay (display); -} - -i32 p_handle_events(void) { - XGetWindowAttributes(display, window, &attrs); - - if (attrs.width == platform.frame_width && attrs.height == platform.frame_height) { - XPutImage(display, pixmap, gc, &image, 0, 0, 0, 0, attrs.width, attrs.height); - XCopyArea(display, pixmap, window, gc, 0, 0, attrs.width, attrs.height, 0, 0); - } - - XFlush(display); - - i32 num_events = XEventsQueued(display, QueuedAlready); - - memset(platform.key_pressed, 0, sizeof platform.key_pressed); - - for (i32 i = 0; i < num_events; ++i) { - XNextEvent(display, &ev); - - switch (ev.type) { - case DestroyNotify: - platform.done = 1; - break; - - case MotionNotify: - platform.cursor_x = ev.xmotion.x; - platform.cursor_y = ev.xmotion.y; - break; - - case ButtonPress: - platform.cursor_x = ev.xbutton.x; - platform.cursor_y = ev.xbutton.y; - switch (ev.xbutton.button) { - case Button1: - platform.key_down[BUTTON_LEFT] = 1; - platform.key_pressed[BUTTON_LEFT]++; - break; - case Button2: - platform.key_down[BUTTON_MIDDLE] = 1; - platform.key_pressed[BUTTON_MIDDLE]++; - break; - case Button3: - platform.key_down[BUTTON_RIGHT] = 1; - platform.key_pressed[BUTTON_RIGHT]++; - break; - case Button4: - platform.key_down[BUTTON_X1] = 1; - platform.key_pressed[BUTTON_X1]++; - break; - case Button5: - platform.key_down[BUTTON_X2] = 1; - platform.key_pressed[BUTTON_X2]++; - break; - default:; - } - break; - - case ButtonRelease: - platform.cursor_x = ev.xbutton.x; - platform.cursor_y = ev.xbutton.y; - switch (ev.xbutton.button) { - case Button1: platform.key_down[BUTTON_LEFT] = 0; break; - case Button2: platform.key_down[BUTTON_MIDDLE] = 0; break; - case Button3: platform.key_down[BUTTON_RIGHT] = 0; break; - case Button4: platform.key_down[BUTTON_X1] = 0; break; - case Button5: platform.key_down[BUTTON_X2] = 0; break; - default:; - } - break; - - case KeyPress: - platform.cursor_x = ev.xkey.x; - platform.cursor_y = ev.xkey.y; - platform.key_down [key_table[ev.xkey.keycode]] = 1; - platform.key_pressed[key_table[ev.xkey.keycode]]++; - break; - - case KeyRelease: - platform.cursor_x = ev.xkey.x; - platform.cursor_y = ev.xkey.y; - platform.key_down [key_table[ev.xkey.keycode]] = 0; - platform.key_pressed[key_table[ev.xkey.keycode]]--; - break; - - case EnterNotify: platform.has_cursor = 1; break; - case LeaveNotify: platform.has_cursor = 0; break; - case FocusIn: platform.has_focus = 1; break; - case FocusOut: platform.has_focus = 0; break; - - case ClientMessage: - if ((Atom) ev.xclient.data.l[0] == wm_delete_window) - platform.done = 1; - break; - - default:; - } - } - - if ((platform.frame_width != attrs.width || platform.frame_height != attrs.height) && attrs.width * attrs.height * 4 <= (i32) sizeof buffer) { - if (attrs.width > 0 && attrs.height > 0) { - image.width = attrs.width; - image.height = attrs.height; - image.bytes_per_line = 4 * attrs.width; - - XFreePixmap(display, pixmap); - pixmap = XCreatePixmap(display, window, attrs.width, attrs.height, depth); - } - - platform.frame_width = attrs.width; - platform.frame_height = attrs.height; - } - - return num_events; -} diff --git a/examples/ui.c b/examples/ui.c new file mode 100755 index 0000000..a116074 --- /dev/null +++ b/examples/ui.c @@ -0,0 +1,708 @@ +#if 0 /* +#/ ================================================================ +#/ +#/ ui.c +#/ +#/ This is a reduced system layer. +#/ It allows you to create a window, draw graphics in it, handle +#/ input events. +#/ +#/ ---------------------------------------------------------------- +#/ +#/ DESIGN PRINCIPLES +#/ +#/ - Minimalistic feature set. For graphics, you have access to the +#/ pixel buffer, and that's it. +#/ +#/ - No implicit control flow. No callbacks. You write your own +#/ main and call everything explicitly. But the number of things +#/ you have to call to do something is as little as possible. +#/ +#/ - Optimized to use in a single source file. +#/ Installation process? Ctrl+C, Ctrl+V, done. +#/ +#/ If you have an idea how to reduce the feature set further, +#/ let me know! +#/ +#/ ---------------------------------------------------------------- +#/ +#/ (C) 2024 Mitya Selivanov , MIT License +#/ +#/ ================================================================ +#/ +#/ Self-compilation shell script +#/ +SRC=${0##*./} +BIN=${SRC%.*} +gcc \ + -Wall -Wextra -Werror -pedantic \ + -Wno-old-style-declaration \ + -Wno-missing-braces \ + -Wno-unused-variable \ + -Wno-unused-but-set-variable \ + -Wno-unused-parameter \ + -Wno-overlength-strings \ + -O3 \ + -fsanitize=undefined,address,leak -mshstk \ + -lX11 -lm \ + -o $BIN $SRC && \ + ./$BIN $@ && rm $BIN +exit $? # */ +#endif +// ================================================================ +// +// Basic declarations +// +// ================================================================ + +#define _GNU_SOURCE + +typedef signed char i8; +typedef signed short i16; +typedef signed i32; +typedef signed long long i64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned u32; +typedef unsigned long long u64; +typedef char c8; +typedef signed char b8; +typedef float f32; +typedef double f64; + +// ================================================================ +// +// PLATFORM API +// +// ================================================================ + +enum { + MAX_NUM_PIXELS = 4 * 1024 * 1024, + MAX_CLIPBOARD_SIZE = 0, + MAX_NUM_AUDIO_SAMPLES = 0, + MAX_NUM_SOCKETS = 0, + + AUDIO_NUM_CHANNELS = 2, + AUDIO_SAMPLE_RATE = 44100, + + IPv4 = 1, + IPv6 = 2, + + KEY_LEFT, + KEY_RIGHT, + KEY_UP, + KEY_DOWN, + KEY_LCTRL, + KEY_RCTRL, + KEY_LSHIFT, + KEY_RSHIFT, + KEY_LALT, + KEY_RALT, + KEY_ESCAPE, + KEY_PRINTSCREEN, + KEY_DELETE, + KEY_PAUSE, + KEY_INSERT, + KEY_HOME, + KEY_END, + KEY_PAGEUP, + KEY_PAGEDOWN, + KEY_KP_ = 128, + KEY_F_ = 256, + BUTTON_LEFT = KEY_F_ + 64, + BUTTON_MIDDLE, + BUTTON_RIGHT, +}; + +typedef struct { + c8 *title; + i32 frame_width; + i32 frame_height; + u32 *pixels; + i64 clipboard_size; + c8 *clipboard; // TODO + b8 done; + b8 has_focus; + b8 has_cursor; + i32 cursor_x; + i32 cursor_y; + i32 cursor_dx; + i32 cursor_dy; + i64 wheel_dy; + b8 key_down[512]; + b8 key_pressed[512]; +} Platform; + +typedef struct { + u16 type; + u16 port; + union { + u8 v4_address[4]; + u8 v6_address[16]; + }; +} IP_Address; + +typedef i64 (*Thread_Proc)(void *user_data); + +// Window +void p_init(void); +void p_cleanup(void); +i32 p_handle_events(void); +i32 p_wait_events(void); +void p_render_frame(void); + +// Sound +void p_handle_audio(i64 time_elapsed); +void p_queue_sound(i64 delay, i64 num_samples, f32 *samples); + +// UDP sockets +i64 p_recv(u16 slot, IP_Address address, i64 size, u8 *data, IP_Address *remote_address); +i64 p_send(u16 slot, IP_Address address, i64 size, u8 *data); + +Platform platform = {0}; + +// ================================================================ +// +// UI +// +// ================================================================ + +#include +#include +#include +#include + +i64 char_num_bits_x = 4; +i64 char_num_bits_y = 7; +u64 bitfont[] = { +#include "bitfont.inl.c" +}; + +void print_text(u32 color, i32 x, i32 y, f64 width, f64 height, i64 num_chars, c8 *text) { + assert(text != NULL); + + f64 dx = x; + i64 w = (i64) floor(width + .5); + + for (i64 n = 0; n < num_chars; ++n) { + i64 x0 = (i64) floor(dx + .5); + i64 y1 = (i64) floor(y + height + .5); + + i64 num_cols = 0; + + for (i64 i = x0; i < x0 + w; ++i, ++num_cols) { + b8 empty_col = 1; + + for (i64 j = y; j < y1; ++j) { + if (i < 0 || i >= platform.frame_width || j < 0 || j >= platform.frame_height) + continue; + i64 char_x = ((i - x0) * (char_num_bits_x + 1)) / w; + if (char_x >= char_num_bits_x) + continue; + i64 char_y = ((j - y ) * char_num_bits_y ) / (y1 - y ); + i64 bit_index = text[n] * char_num_bits_x * char_num_bits_y + char_y * char_num_bits_x + char_x; + i64 qword_index = bit_index / 64; + if (qword_index >= (i64) (sizeof bitfont / sizeof *bitfont)) + continue; + u64 mask = 1ull << (bit_index % 64); + if (!!(bitfont[qword_index] & mask)) { + platform.pixels[j * platform.frame_width + i] = color; + empty_col = 0; + } + } + + if (empty_col) + break; + } + + if (text[n] == ' ') + dx += width; + else + dx += num_cols + width / char_num_bits_x; + } +} + +i32 main(i32 argc, c8 **argv) { + c8 static data[] = + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + " ! xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxx xxxx" + " ! x x x x x x x x x x x x " + " ! x x x x x x x x x x x x " + " ! x x x x x x x x x x x x " + " x x x x x x x x x x x x " + " ! x x x x x x x x x x , x . x " + " x x x x x x x x x x , x x " + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x x " + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxH Hxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "x x x x x x x x H Hx x x x x x x " + "x x x x x x x x HHHHx x x x x x x " + "x x x x x x x x H Hx x x x x x x " + "x x x x x x x x H Hx x x x x x x " + "x x x x x x x x H Hx x x x x x x " + "x x x x x x x x x x x x x x x " + "xxxxxxxxxxxx SS xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "x x x S x x x x x x x x x x x x " + "x x x SS x x x x x x x x x x x x " + "x x x Sx x x x x x x x x x x x " + "x x x S Sx x x x x x x x x x x x " + "x x x SS x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x " + "xxxx xxxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxl xxxxxxxx " + "x aa x x x ee x x x i x x l x x oo " + "x ax x x e ex x x x x l x x o o" + "x aaaax x x eeeex x x ii x x l x x o o" + "x a ax x x e x x x i x x l x x o o" + "x aa x x x ee x x x iii x x l x x oo " + "x x x x x x x x x x x " + "xxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "x x r r x x x x x x x x x x x x x " + "x x rr x x x x x x x x x x x x x " + "x x r x x x x x x x x x x x x x " + "x x r x x x x x x x x x x x x x " + "x x r x x x x x x x x x x x x x " + "x x x x x x x x x x x x x x x " + ; + + u64 bits[(16 * 8 * 7 * 4) / 64] = {0}; + + for (i64 j = 0; j < 8; ++j) + for (i64 i = 0; i < 16; ++i) + for (i64 y = 0; y < char_num_bits_y; ++y) + for (i64 x = 0; x < char_num_bits_x; ++x) { + i64 num_char = j * 16 + i; + i64 num_bit = y * char_num_bits_x + x; + i64 bit_index = num_char * char_num_bits_x * char_num_bits_y + num_bit; + i64 qword_index = bit_index / 64; + u64 mask = 1ull << (bit_index % 64); + i64 dx = i * char_num_bits_x + x; + i64 dy = j * char_num_bits_y + y; + i64 index = dy * char_num_bits_x * 16 + dx; + if (index < (i64) (sizeof data / sizeof *data) && data[index] != ' ') + bits[qword_index] |= mask; + } + + FILE *f = fopen("bitfont.inl.c", "wb"); + for (i64 i = 0; i < (i64) (sizeof bits / sizeof *bits); ++i) + fprintf(f, "0x%llx,%s", bits[i], i + 1 < (i64) (sizeof bits / sizeof *bits) ? " " : "\n"); + fclose(f); + + (void) argc; + (void) argv; + + platform = (Platform) { + .title = "UI", + .frame_width = 960, + .frame_height = 720, + }; + + p_init(); + + while (!platform.done) { + p_wait_events(); + + for (i32 j = 0; j < platform.frame_height; ++j) + for (i32 i = 0; i < platform.frame_width; ++i) + platform.pixels[j * platform.frame_width + i] = 0x302000; + + i64 color = 0xff7f7f; + + if (platform.cursor_x >= 80 && platform.cursor_x < 800 && + platform.cursor_y >= 80 && platform.cursor_y < 180) + color = 0xffffff; + + print_text(color, 80, 80, 60., 100., 14, "Hello, Sailor!"); + + p_render_frame(); + } + + p_cleanup(); + return 0; +} + +// ================================================================ +// +// PLATFORM IMPLEMENTATION +// +// ---------------------------------------------------------------- +// +// TODO +// - Clipboard +// - Sound +// - Sockets +// +// X11 clipboard +// https://handmade.network/forums/articles/t/8544-implementing_copy_paste_in_x11 +// +// ALSA +// https://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_min_8c-example.html +// +// ================================================================ + +#include +#include + +// ================================================================ +// +// UDP sockets +// +// ================================================================ + +i64 p_recv(u16 slot, IP_Address address, i64 size, u8 *data, IP_Address *remote_address) { + (void) slot; + (void) address; + (void) size; + (void) data; + (void) remote_address; + assert(0); + return 0; +} + +i64 p_send(u16 slot, IP_Address address, i64 size, u8 *data) { + (void) slot; + (void) address; + (void) size; + (void) data; + assert(0); + return 0; +} + +// ================================================================ +// +// X11 +// +// ================================================================ + +#ifdef __linux__ + +#include +#include +#include +#include + +static i16 _key_table[512]; +static u32 _buffer[MAX_NUM_PIXELS]; +static XImage _image; +static Display *_display; +static GC _gc; +static Window _window; +static Atom _wm_delete_window; + +void p_init(void) { + _display = XOpenDisplay(NULL); + assert(_display != NULL); + + _key_table[XKeysymToKeycode(_display, XK_Left)] = KEY_LEFT; + _key_table[XKeysymToKeycode(_display, XK_Right)] = KEY_RIGHT; + _key_table[XKeysymToKeycode(_display, XK_Up)] = KEY_UP; + _key_table[XKeysymToKeycode(_display, XK_Down)] = KEY_DOWN; + _key_table[XKeysymToKeycode(_display, XK_1)] = '1'; + _key_table[XKeysymToKeycode(_display, XK_2)] = '2'; + _key_table[XKeysymToKeycode(_display, XK_3)] = '3'; + _key_table[XKeysymToKeycode(_display, XK_4)] = '4'; + _key_table[XKeysymToKeycode(_display, XK_5)] = '5'; + _key_table[XKeysymToKeycode(_display, XK_6)] = '6'; + _key_table[XKeysymToKeycode(_display, XK_7)] = '7'; + _key_table[XKeysymToKeycode(_display, XK_8)] = '8'; + _key_table[XKeysymToKeycode(_display, XK_9)] = '9'; + _key_table[XKeysymToKeycode(_display, XK_0)] = '0'; + _key_table[XKeysymToKeycode(_display, XK_A)] = 'a'; + _key_table[XKeysymToKeycode(_display, XK_B)] = 'b'; + _key_table[XKeysymToKeycode(_display, XK_C)] = 'c'; + _key_table[XKeysymToKeycode(_display, XK_D)] = 'd'; + _key_table[XKeysymToKeycode(_display, XK_E)] = 'e'; + _key_table[XKeysymToKeycode(_display, XK_F)] = 'f'; + _key_table[XKeysymToKeycode(_display, XK_G)] = 'g'; + _key_table[XKeysymToKeycode(_display, XK_H)] = 'h'; + _key_table[XKeysymToKeycode(_display, XK_I)] = 'i'; + _key_table[XKeysymToKeycode(_display, XK_J)] = 'j'; + _key_table[XKeysymToKeycode(_display, XK_K)] = 'k'; + _key_table[XKeysymToKeycode(_display, XK_L)] = 'l'; + _key_table[XKeysymToKeycode(_display, XK_M)] = 'm'; + _key_table[XKeysymToKeycode(_display, XK_N)] = 'n'; + _key_table[XKeysymToKeycode(_display, XK_O)] = 'o'; + _key_table[XKeysymToKeycode(_display, XK_P)] = 'p'; + _key_table[XKeysymToKeycode(_display, XK_Q)] = 'q'; + _key_table[XKeysymToKeycode(_display, XK_R)] = 'r'; + _key_table[XKeysymToKeycode(_display, XK_S)] = 's'; + _key_table[XKeysymToKeycode(_display, XK_T)] = 't'; + _key_table[XKeysymToKeycode(_display, XK_U)] = 'u'; + _key_table[XKeysymToKeycode(_display, XK_V)] = 'v'; + _key_table[XKeysymToKeycode(_display, XK_W)] = 'w'; + _key_table[XKeysymToKeycode(_display, XK_X)] = 'x'; + _key_table[XKeysymToKeycode(_display, XK_Y)] = 'y'; + _key_table[XKeysymToKeycode(_display, XK_Z)] = 'z'; + _key_table[XKeysymToKeycode(_display, XK_space)] = ' '; + _key_table[XKeysymToKeycode(_display, XK_braceleft)] = '['; + _key_table[XKeysymToKeycode(_display, XK_braceright)] = ']'; + _key_table[XKeysymToKeycode(_display, XK_colon)] = ';'; + _key_table[XKeysymToKeycode(_display, XK_quotedbl)] = '\''; + _key_table[XKeysymToKeycode(_display, XK_asciitilde)] = '`'; + _key_table[XKeysymToKeycode(_display, XK_backslash)] = '\\'; + _key_table[XKeysymToKeycode(_display, XK_comma)] = ','; + _key_table[XKeysymToKeycode(_display, XK_greater)] = '.'; + _key_table[XKeysymToKeycode(_display, XK_question)] = '/'; + _key_table[XKeysymToKeycode(_display, XK_F1)] = KEY_F_ + 1; + _key_table[XKeysymToKeycode(_display, XK_F2)] = KEY_F_ + 2; + _key_table[XKeysymToKeycode(_display, XK_F3)] = KEY_F_ + 3; + _key_table[XKeysymToKeycode(_display, XK_F4)] = KEY_F_ + 4; + _key_table[XKeysymToKeycode(_display, XK_F5)] = KEY_F_ + 5; + _key_table[XKeysymToKeycode(_display, XK_F6)] = KEY_F_ + 6; + _key_table[XKeysymToKeycode(_display, XK_F7)] = KEY_F_ + 7; + _key_table[XKeysymToKeycode(_display, XK_F8)] = KEY_F_ + 8; + _key_table[XKeysymToKeycode(_display, XK_F9)] = KEY_F_ + 9; + _key_table[XKeysymToKeycode(_display, XK_F10)] = KEY_F_ + 10; + _key_table[XKeysymToKeycode(_display, XK_F11)] = KEY_F_ + 11; + _key_table[XKeysymToKeycode(_display, XK_F12)] = KEY_F_ + 12; + _key_table[XKeysymToKeycode(_display, XK_Control_L)] = KEY_LCTRL; + _key_table[XKeysymToKeycode(_display, XK_Control_R)] = KEY_RCTRL; + _key_table[XKeysymToKeycode(_display, XK_Shift_L)] = KEY_LSHIFT; + _key_table[XKeysymToKeycode(_display, XK_Shift_R)] = KEY_RSHIFT; + _key_table[XKeysymToKeycode(_display, XK_Alt_L)] = KEY_LALT; + _key_table[XKeysymToKeycode(_display, XK_Alt_R)] = KEY_RALT; + _key_table[XKeysymToKeycode(_display, XK_Escape)] = KEY_ESCAPE; + _key_table[XKeysymToKeycode(_display, XK_BackSpace)] = '\b'; + _key_table[XKeysymToKeycode(_display, XK_Tab)] = '\t'; + _key_table[XKeysymToKeycode(_display, XK_Return)] = '\n'; + _key_table[XKeysymToKeycode(_display, XK_Print)] = KEY_PRINTSCREEN; + _key_table[XKeysymToKeycode(_display, XK_Delete)] = KEY_DELETE; + _key_table[XKeysymToKeycode(_display, XK_Pause)] = KEY_PAUSE; + _key_table[XKeysymToKeycode(_display, XK_Insert)] = KEY_INSERT; + _key_table[XKeysymToKeycode(_display, XK_Home)] = KEY_HOME; + _key_table[XKeysymToKeycode(_display, XK_End)] = KEY_END; + _key_table[XKeysymToKeycode(_display, XK_Page_Up)] = KEY_PAGEUP; + _key_table[XKeysymToKeycode(_display, XK_Page_Down)] = KEY_PAGEDOWN; + _key_table[XKeysymToKeycode(_display, XK_KP_0)] = KEY_KP_ + '0'; + _key_table[XKeysymToKeycode(_display, XK_KP_1)] = KEY_KP_ + '1'; + _key_table[XKeysymToKeycode(_display, XK_KP_2)] = KEY_KP_ + '2'; + _key_table[XKeysymToKeycode(_display, XK_KP_3)] = KEY_KP_ + '3'; + _key_table[XKeysymToKeycode(_display, XK_KP_4)] = KEY_KP_ + '4'; + _key_table[XKeysymToKeycode(_display, XK_KP_5)] = KEY_KP_ + '5'; + _key_table[XKeysymToKeycode(_display, XK_KP_6)] = KEY_KP_ + '6'; + _key_table[XKeysymToKeycode(_display, XK_KP_7)] = KEY_KP_ + '7'; + _key_table[XKeysymToKeycode(_display, XK_KP_8)] = KEY_KP_ + '8'; + _key_table[XKeysymToKeycode(_display, XK_KP_9)] = KEY_KP_ + '9'; + _key_table[XKeysymToKeycode(_display, XK_KP_Enter)] = KEY_KP_ + '\n'; + _key_table[XKeysymToKeycode(_display, XK_KP_Divide)] = KEY_KP_ + '/'; + _key_table[XKeysymToKeycode(_display, XK_KP_Multiply)] = KEY_KP_ + '*'; + _key_table[XKeysymToKeycode(_display, XK_KP_Add)] = KEY_KP_ + '+'; + _key_table[XKeysymToKeycode(_display, XK_KP_Subtract)] = KEY_KP_ + '-'; + _key_table[XKeysymToKeycode(_display, XK_KP_Decimal)] = KEY_KP_ + '.'; + _key_table[XKeysymToKeycode(_display, XK_KP_Separator)] = KEY_KP_ + ','; + + i32 screen = DefaultScreen(_display); + i32 depth = DefaultDepth (_display, screen); + Visual *visual = DefaultVisual(_display, screen); + + _gc = DefaultGC(_display, screen); + + XSetGraphicsExposures(_display, _gc, False); + + i32 _display_width = DisplayWidth (_display, screen); + i32 _display_height = DisplayHeight(_display, screen); + + i32 x = (_display_width - platform.frame_width) / 2; + i32 y = (_display_height - platform.frame_height) / 2; + + _window = XCreateWindow(_display, XDefaultRootWindow(_display), x, y, platform.frame_width, platform.frame_height, 0, depth, InputOutput, visual, CWEventMask, &(XSetWindowAttributes) { .event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | VisibilityChangeMask | FocusChangeMask | StructureNotifyMask | SubstructureNotifyMask, }); + + platform.pixels = _buffer; + + _image = (XImage) { + .width = platform.frame_width, + .height = platform.frame_height, + .depth = depth, + .xoffset = 0, + .format = ZPixmap, + .data = (c8 *) _buffer, + .byte_order = LSBFirst, + .bitmap_unit = 32, + .bitmap_bit_order = LSBFirst, + .bitmap_pad = 32, + .bits_per_pixel = 32, + .bytes_per_line = 4 * platform.frame_width, + .red_mask = 0xff0000, + .green_mask = 0x00ff00, + .blue_mask = 0x0000ff, + }; + + + XInitImage(&_image); + + _wm_delete_window = XInternAtom(_display, "WM_DELETE_WINDOW", False); + + XSetWMProtocols(_display, _window, &_wm_delete_window, 1); + + XStoreName(_display, _window, platform.title); + XMapWindow(_display, _window); +} + +void p_cleanup(void) { + XDestroyWindow(_display, _window); + XCloseDisplay (_display); +} + +i32 p_handle_events(void) { + i32 num_events = XEventsQueued(_display, QueuedAlready); + + memset(platform.key_pressed, 0, sizeof platform.key_pressed); + + platform.cursor_dx = 0; + platform.cursor_dy = 0; + platform.wheel_dy = 0; + + XEvent ev; + + for (i32 i = 0; i < num_events; ++i) { + XNextEvent(_display, &ev); + + switch (ev.type) { + case DestroyNotify: + platform.done = 1; + break; + + case MotionNotify: + platform.cursor_dx += ev.xmotion.x - platform.cursor_x; + platform.cursor_dy += ev.xmotion.y - platform.cursor_y; + platform.cursor_x = ev.xmotion.x; + platform.cursor_y = ev.xmotion.y; + break; + + case ButtonPress: + platform.cursor_x = ev.xbutton.x; + platform.cursor_y = ev.xbutton.y; + switch (ev.xbutton.button) { + case Button1: + platform.key_down[BUTTON_LEFT] = 1; + ++platform.key_pressed[BUTTON_LEFT]; + break; + case Button2: + platform.key_down[BUTTON_MIDDLE] = 1; + ++platform.key_pressed[BUTTON_MIDDLE]; + break; + case Button3: + platform.key_down[BUTTON_RIGHT] = 1; + ++platform.key_pressed[BUTTON_RIGHT]; + break; + case Button4: ++platform.wheel_dy; break; + case Button5: --platform.wheel_dy; break; + default:; + } + break; + + case ButtonRelease: + platform.cursor_x = ev.xbutton.x; + platform.cursor_y = ev.xbutton.y; + switch (ev.xbutton.button) { + case Button1: platform.key_down[BUTTON_LEFT] = 0; break; + case Button2: platform.key_down[BUTTON_MIDDLE] = 0; break; + case Button3: platform.key_down[BUTTON_RIGHT] = 0; break; + default:; + } + break; + + case KeyPress: + platform.cursor_x = ev.xkey.x; + platform.cursor_y = ev.xkey.y; + platform.key_down [_key_table[ev.xkey.keycode]] = 1; + platform.key_pressed[_key_table[ev.xkey.keycode]]++; + break; + + case KeyRelease: + platform.cursor_x = ev.xkey.x; + platform.cursor_y = ev.xkey.y; + platform.key_down [_key_table[ev.xkey.keycode]] = 0; + platform.key_pressed[_key_table[ev.xkey.keycode]]--; + break; + + case EnterNotify: platform.has_cursor = 1; break; + case LeaveNotify: platform.has_cursor = 0; break; + case FocusIn: platform.has_focus = 1; break; + case FocusOut: platform.has_focus = 0; break; + + case ClientMessage: + if ((Atom) ev.xclient.data.l[0] == _wm_delete_window) + platform.done = 1; + break; + + default:; + } + } + + XWindowAttributes attrs; + XGetWindowAttributes(_display, _window, &attrs); + + if ((platform.frame_width != attrs.width || platform.frame_height != attrs.height) && attrs.width * attrs.height * 4 <= (i32) sizeof _buffer) { + if (attrs.width > 0 && attrs.height > 0) { + _image.width = attrs.width; + _image.height = attrs.height; + _image.bytes_per_line = 4 * attrs.width; + } + + platform.frame_width = attrs.width; + platform.frame_height = attrs.height; + } + + return num_events; +} + +i32 p_wait_events(void) { + i32 num_events = 0; + + do { + num_events = p_handle_events(); + sched_yield(); + } while (num_events == 0); + + return num_events; +} + +void p_render_frame(void) { + if (platform.done) + return; + + XPutImage(_display, _window, _gc, &_image, 0, 0, 0, 0, platform.frame_width, platform.frame_height); + XFlush(_display); +} + +#endif + +// ================================================================ +// +// ALSA +// +// ================================================================ + +#ifdef __linux__ + +void p_handle_audio(i64 time_elapsed) { + (void) time_elapsed; + assert(0); +} + +void p_queue_sound(i64 delay, i64 num_samples, f32 *samples) { + (void) delay; + (void) num_samples; + (void) samples; + assert(0); +} + +#endif -- cgit v1.2.3