#if 0 /* #/ ================================================================ #/ #/ landscape.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 \ -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, BUTTON_LEFT = 256, BUTTON_MIDDLE, BUTTON_RIGHT, 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_F_ = 256 * 2, KEY_KP_ = 256 * 3, }; 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); 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}; // ================================================================ // // GAME // // ================================================================ // // TODO // - Font // // ================================================================ #include #include #include #include #include #include #define EPS 1e-8 enum { F_ENABLED = 1, F_HIDDEN = 2, TILE_NONE = 0, TILE_GROUND, TILE_ROCK, TILE_GRASS, ENTITY_NONE = 0, ENTITY_STONE, ENTITY_TREE, ENTITY_CABIN, MAX_NUM_TILES = 10 * 1024 * 1024, MAX_NUM_ENTITIES = 10 * 1024 * 1024, }; typedef struct { i32 index; i32 version; } Handle; typedef union { struct { f64 x, y; }; struct { f64 v[2]; }; } Vec2; typedef union { struct { f64 x, y, z; }; struct { f64 r, g, b; }; struct { f64 v[3]; }; } Vec3; typedef union { struct { f64 x, y, z, w; }; struct { f64 r, g, b, a; }; struct { f64 v[4]; }; } Vec4; typedef struct { b8 visible; f64 x; f64 y; f64 size; } Screen_Area; typedef struct { u32 flags; u16 id; u16 variation; f64 height; f64 water; } Tile; typedef struct { u32 flags; i32 version; u16 type; u16 variation; f64 size; i64 time; Vec3 position; } Entity; typedef struct { i64 time; i32 map_num_x; i32 map_num_y; Tile map[MAX_NUM_TILES]; i32 num_entities; i32 next_entity; Entity entities[MAX_NUM_ENTITIES]; f64 screen_distance; f64 zoom; f64 tilt; Vec3 eye_position; Vec3 right; Vec3 up; Vec3 forward; Vec3 screen_x_axis; Vec3 screen_y_axis; Vec3 screen_z_axis; } World; #define CHECK(fail_return, condition) \ assert(condition); \ if (!(condition)) \ return fail_return; #define Y(flags, mask) (((flags) & (mask)) == (mask)) #define N(flags, mask) (((flags) & (mask)) != (mask)) World world = {0}; f64 dot(Vec3 a, Vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } Vec3 cross(Vec3 a, Vec3 b) { return (Vec3) { .x = a.y * b.z - a.z * b.y, .y = a.z * b.x - a.x * b.z, .z = a.x * b.y - a.y * b.x, }; } Vec3 neg(Vec3 a) { return (Vec3) { .x = -a.x, .y = -a.y, .z = -a.z, }; } Vec3 add(Vec3 a, Vec3 b) { return (Vec3) { .x = a.x + b.x, .y = a.y + b.y, .z = a.z + b.z, }; } Vec3 sub(Vec3 a, Vec3 b) { return (Vec3) { .x = a.x - b.x, .y = a.y - b.y, .z = a.z - b.z, }; } Vec3 mul(Vec3 a, f64 b) { return (Vec3) { .x = a.x * b, .y = a.y * b, .z = a.z * b, }; } Vec3 v_div(Vec3 a, f64 b) { if (b > -EPS && b < EPS) { assert(0); return (Vec3) { .x = 0., .y = 0., .z = 1., }; } return (Vec3) { .x = a.x / b, .y = a.y / b, .z = a.z / b, }; } Vec3 normal(Vec3 a) { return v_div(a, sqrt(dot(a, a))); } void resolve_axes(void) { world.right = (Vec3) { .x = 1., .y = 0., .z = 0., }; world.forward = (Vec3) { .x = 0., .y = 1., .z = 0., }; world.up = (Vec3) { .x = 0., .y = 0., .z = 1., }; if (world.tilt < 0.0) world.tilt += M_PI * 2.0 * floor(-world.tilt / (M_PI * 2.0) + 1.0); if (world.tilt >= M_PI * 2.0) world.tilt -= M_PI * 2.0 * floor(world.tilt / (M_PI * 2.0)); world.screen_z_axis = (Vec3) { .x = 0., .y = sin(world.tilt), .z = -cos(world.tilt), }; world.screen_y_axis = normal(cross(world.screen_z_axis, world.right)); world.screen_x_axis = normal(cross(world.screen_y_axis, world.screen_z_axis)); } Screen_Area world_to_screen(Vec3 p, f64 size) { Vec3 r = sub(p, world.eye_position); f64 l = dot(r, world.screen_z_axis); if (l < EPS) return (Screen_Area) { .visible = 0, }; f64 k = (world.screen_distance / l) * world.zoom; f64 x = k * dot(r, world.screen_x_axis); f64 y = k * dot(r, world.screen_y_axis); f64 hw = platform.frame_width * .5; f64 hh = platform.frame_height * .5; return (Screen_Area) { .visible = 1, .x = hw + x, .y = hh + y, .size = k * size, }; } Vec3 screen_to_world(i32 x, i32 y) { f64 fx = ((f64) x - platform.frame_width * .5) / world.zoom; f64 fy = ((f64) y - platform.frame_height * .5) / world.zoom; Vec3 r = add(add( mul(world.screen_x_axis, fx), mul(world.screen_y_axis, fy)), mul(world.screen_z_axis, world.screen_distance) ); if (r.z < -EPS || r.z > EPS) r = mul(r, -world.eye_position.z / r.z); return add(world.eye_position, r); } Handle e_create_in(i32 index, Entity data) { CHECK((Handle) {0}, index < MAX_NUM_ENTITIES); CHECK((Handle) {0}, N(world.entities[index].flags, F_ENABLED)); Handle h = { .index = index, .version = world.entities[index].version + 1, }; world.entities[index] = data; world.entities[index].version = h.version; world.entities[index].flags |= F_ENABLED; while (Y(world.entities[world.next_entity].flags, F_ENABLED)) { ++world.next_entity; CHECK(h, world.next_entity < MAX_NUM_ENTITIES); } if (world.num_entities <= index) world.num_entities = index + 1; return h; } Handle e_create(Entity data) { return e_create_in(world.next_entity, data); } void e_destroy(Handle id) { CHECK(, world.entities[id.index].version == id.version); CHECK(, Y(world.entities[id.index].flags, F_ENABLED)); world.entities[id.index].flags = 0; if (world.next_entity > id.index) world.next_entity = id.index; } void e_render(i32 index) { Entity *e = world.entities + index; CHECK(, Y(e->flags, F_ENABLED)); if (Y(e->flags, F_HIDDEN)) return; Screen_Area a = world_to_screen(e->position, e->size); i32 nx = (i32) floor(a.x + .5); i32 ny = (i32) floor(a.y + .5); i32 ns = (i32) floor(a.size + .5); if (!a.visible) return; for (i64 j = -ns; j <= ns; ++j) if (ny + j >= 0 && ny + j < platform.frame_height) for (i64 i = -ns; i <= ns; ++i) if (nx + i >= 0 && nx + i < platform.frame_width) platform.pixels[(ny + j) * platform.frame_width + (nx + i)] = 0x00ff00; } 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) { (void) argc; (void) argv; u32 background = u32_from_rgb(.03f, .08f, .01f); platform = (Platform) { .title = "Game", .frame_width = 960, .frame_height = 720, }; p_init(); world.screen_distance = 1.; world.zoom = 1e+3; world.eye_position = (Vec3) { .z = 10., }; i64 time_0 = time_milliseconds(); while (!platform.done) { p_handle_events(); i64 time_elapsed = time_milliseconds() - time_0; time_0 += time_elapsed; resolve_axes(); if (time_elapsed > 0) { if (platform.key_down['w']) world.tilt += .001 * time_elapsed; if (platform.key_down['s']) world.tilt -= .001 * time_elapsed; f64 k = world.eye_position.z; if (world.zoom > EPS) k /= world.zoom; if (platform.wheel_dy != 0) world.eye_position.z -= (f64) platform.wheel_dy; if (platform.key_down[BUTTON_RIGHT]) { world.eye_position.x -= platform.cursor_dx * k; world.eye_position.y += platform.cursor_dy * k; } if (platform.key_pressed[BUTTON_LEFT]) { e_create((Entity) { .size = .1, .position = screen_to_world(platform.cursor_x, platform.cursor_y), }); } if (platform.key_down[BUTTON_LEFT]) world.entities[world.num_entities - 1].position.z += .01 * time_elapsed; for (i32 k = 0; k < world.num_entities; ++k) world.entities[k].time += time_elapsed; 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 (i32 k = 0; k < world.num_entities; ++k) e_render(k); 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 // // ================================================================ // // 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 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; } 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