From b204f057653f13ed2a578fec70a23ae333641513 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Mon, 12 Aug 2024 14:37:58 +0200 Subject: Update code --- examples/gravity.c | 890 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 673 insertions(+), 217 deletions(-) (limited to 'examples/gravity.c') diff --git a/examples/gravity.c b/examples/gravity.c index 824558b..7c6faf0 100755 --- a/examples/gravity.c +++ b/examples/gravity.c @@ -3,6 +3,31 @@ #/ #/ gravity.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 @@ -16,6 +41,7 @@ gcc \ -Wno-unused-variable \ -Wno-unused-but-set-variable \ -Wno-unused-parameter \ + -Wno-overlength-strings \ -O3 \ -fsanitize=undefined,address,leak -mshstk \ -lX11 -lm \ @@ -40,6 +66,7 @@ typedef unsigned short u16; typedef unsigned u32; typedef unsigned long long u64; typedef char c8; +typedef int c32; typedef signed char b8; typedef float f32; typedef double f64; @@ -51,28 +78,117 @@ typedef double f64; // ================================================================ 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, + MAX_NUM_PIXELS = 10 * 1024 * 1024, + MAX_INPUT_SIZE = 256, + MAX_CLIPBOARD_SIZE = 10 * 1024 * 1024, + MAX_NUM_AUDIO_SAMPLES = 0, + MAX_NUM_SOCKETS = 0, + + AUDIO_NUM_CHANNELS = 2, + AUDIO_SAMPLE_RATE = 44100, + + IPv4 = 1, + IPv6 = 2, + + KEY_LEFT = 128, + 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, + BUTTON_LEFT, + BUTTON_MIDDLE, + BUTTON_RIGHT, + MOD_CTRL, + MOD_SHIFT, + MOD_ALT, + MOD_CAPS, + MOD_NUM, + MOD_SCROLL, + KEY_F_, + KEY_KP_ = KEY_F_ + 64, }; 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]; + b8 ctrl : 1; + b8 shift : 1; + b8 alt : 1; + b8 caps : 1; + b8 num : 1; + b8 scroll : 1; + u16 key; + c32 c; +} Input_Key; + +typedef struct { + c8 * title; + i32 frame_width; + i32 frame_height; + u32 * pixels; + i64 input_size; + Input_Key *input; + i64 clipboard_size; + c8 * clipboard; + 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; -Platform platform = {0}; +typedef struct { + u16 type; + u16 port; + union { + u8 v4_address[4]; + u8 v6_address[16]; + }; +} IP_Address; + +typedef i64 (*Thread_Proc)(void *user_data); +// UTF-8 +i32 utf8_size(c32 c); +c32 utf8_read(i64 len, c8 *s); +i32 utf8_write(c32 c, c8 *buffer); + +// Window void p_init(void); void p_cleanup(void); i32 p_handle_events(void); +i32 p_wait_events(void); +void p_render_frame(void); + +// Clipboard +void p_clipboard_write(i64 size, c8 *data); + +// 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}; // ================================================================ // @@ -123,6 +239,9 @@ u32 u32_from_rgb(f32 red, f32 green, f32 blue) { } i32 main(i32 argc, c8 **argv) { + (void) argc; + (void) argv; + u32 background = u32_from_rgb(.0f, .0f, .08f); platform = (Platform) { @@ -139,7 +258,7 @@ i32 main(i32 argc, c8 **argv) { i64 time_0 = time_milliseconds(); while (!platform.done) { - i32 num_events = p_handle_events(); + p_handle_events(); i64 time_elapsed = time_milliseconds() - time_0; time_0 += time_elapsed; @@ -225,6 +344,8 @@ i32 main(i32 argc, c8 **argv) { } } } + + p_render_frame(); } p_cleanup(); @@ -233,197 +354,331 @@ i32 main(i32 argc, c8 **argv) { // ================================================================ // -// LINUX X11 PLATFORM IMPLEMENTATION +// PLATFORM IMPLEMENTATION +// +// ---------------------------------------------------------------- +// +// TODO +// - Sound +// - Sockets +// - Clipboard daemon +// +// ALSA +// https://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_min_8c-example.html +// +// ================================================================ + +#include +#include + +c32 utf8_read(i64 len, c8 *s) { + if (len >= 1 && + (s[0] & 0x80) == 0) + return s[0]; + if (len >= 2 && + (s[0] & 0xe0) == 0xc0 && + (s[1] & 0xc0) == 0x80) + return (s[1] & 0x3f) + | ((s[0] & 0x1f) << 6); + if (len >= 3 && + (s[0] & 0xf0) == 0xe0 && + (s[1] & 0xc0) == 0x80 && + (s[2] & 0xc0) == 0x80) + return (s[2] & 0x3f) + | ((s[1] & 0x3f) << 6) + | ((s[0] & 0x0f) << 12); + if (len >= 4 && + (s[0] & 0xf8) == 0xf0 && + (s[1] & 0xc0) == 0x80 && + (s[2] & 0xc0) == 0x80 && + (s[3] & 0xc0) == 0x80) + return (s[3] & 0x3f) + | ((s[2] & 0x3f) << 6) + | ((s[1] & 0x3f) << 12) + | ((s[0] & 0x07) << 18); + return 0; +} + +i32 utf8_size(c32 c) { + if ((c & 0x00007f) == c) return 1; + if ((c & 0x0007ff) == c) return 2; + if ((c & 0x00ffff) == c) return 3; + if ((c & 0x1fffff) == c) return 4; + return 0; +} + +i32 utf8_write(c32 c, c8 *buffer) { + if ((c & 0x7f) == c) { + buffer[0] = (c8) c; + return 1; + } + + if ((c & 0x7ff) == c) { + buffer[0] = 0xc0 | ((c >> 6) & 0x1f); + buffer[1] = 0x80 | ( c & 0x3f); + return 2; + } + + if ((c & 0xffff) == c) { + buffer[0] = 0xc0 | ((c >> 12) & 0x0f); + buffer[1] = 0x80 | ((c >> 6) & 0x3f); + buffer[2] = 0x80 | ( c & 0x3f); + return 3; + } + + if ((c & 0x1fffff) == c) { + buffer[0] = 0xc0 | ((c >> 18) & 0x07); + buffer[1] = 0x80 | ((c >> 12) & 0x3f); + buffer[2] = 0x80 | ((c >> 6) & 0x3f); + buffer[3] = 0x80 | ( c & 0x3f); + return 4; + } + + return 0; +} + +// ================================================================ +// +// 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 +#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; +static i16 _key_table[512] = {0}; +static u32 _buffer[MAX_NUM_PIXELS] = {0}; +static Input_Key _input[MAX_INPUT_SIZE] = {0}; +static c8 _clipboard_buffer[MAX_CLIPBOARD_SIZE] = {0}; +static XImage _image = {0}; +static Display * _display = NULL; +static GC _gc = NULL; +static XIM _im = NULL; +static XIC _ic = NULL; +static Window _window = 0; +static Atom _wm_delete_window = 0; +static Atom _clipboard = 0; +static Atom _targets = 0; +static Atom _utf8_string = 0; +static Atom _target = None; 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); + _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_minus)] = '-'; + _key_table[XKeysymToKeycode(_display, XK_equal)] = '='; + _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); + assert(_gc != NULL); + + 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, }); + + _im = XOpenIM(_display, NULL, NULL, NULL); + assert(_im != NULL); + + _ic = XCreateIC(_im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, _window, NULL); + assert(_ic != NULL); + + platform.pixels = _buffer; + platform.input = _input; + platform.clipboard = _clipboard_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); + _clipboard = XInternAtom(_display, "CLIPBOARD", False); + _targets = XInternAtom(_display, "TARGETS", False); + _utf8_string = XInternAtom(_display, "UTF8_STRING", False); + + XSetICFocus(_ic); + 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); + XDestroyWindow(_display, _window); + XCloseDisplay (_display); } i32 p_handle_events(void) { - XGetWindowAttributes(display, window, &attrs); + i32 num_events = 0; - 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); - } + memset(platform.key_pressed, 0, sizeof platform.key_pressed); - XFlush(display); + platform.input_size = 0; + platform.cursor_dx = 0; + platform.cursor_dy = 0; + platform.wheel_dy = 0; - i32 num_events = XEventsQueued(display, QueuedAlready); + XEvent ev; - memset(platform.key_pressed, 0, sizeof platform.key_pressed); + b8 requested_clipboard = 0; + + while (XEventsQueued(_display, QueuedAlready) > 0) { + ++num_events; - for (i32 i = 0; i < num_events; ++i) { - XNextEvent(display, &ev); + XNextEvent(_display, &ev); + XFilterEvent(&ev, _window); switch (ev.type) { case DestroyNotify: @@ -431,8 +686,10 @@ i32 p_handle_events(void) { break; case MotionNotify: - platform.cursor_x = ev.xmotion.x; - platform.cursor_y = ev.xmotion.y; + 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: @@ -441,26 +698,24 @@ i32 p_handle_events(void) { switch (ev.xbutton.button) { case Button1: platform.key_down[BUTTON_LEFT] = 1; - platform.key_pressed[BUTTON_LEFT]++; + ++platform.key_pressed[BUTTON_LEFT]; break; case Button2: platform.key_down[BUTTON_MIDDLE] = 1; - platform.key_pressed[BUTTON_MIDDLE]++; + ++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]++; + ++platform.key_pressed[BUTTON_RIGHT]; break; + case Button4: ++platform.wheel_dy; break; + case Button5: --platform.wheel_dy; break; default:; } + if (!requested_clipboard) { + XConvertSelection(_display, _clipboard, _targets, _clipboard, _window, CurrentTime); + requested_clipboard = 1; + } break; case ButtonRelease: @@ -470,33 +725,178 @@ i32 p_handle_events(void) { 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: + case KeyPress: { + i16 k = _key_table[ev.xkey.keycode]; 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; + platform.key_down [k] = 1; + platform.key_pressed[k]++; + platform.key_down[MOD_CTRL] = !!(ev.xkey.state & ControlMask); + platform.key_down[MOD_SHIFT] = !!(ev.xkey.state & ShiftMask); + platform.key_down[MOD_ALT] = !!(ev.xkey.state & Mod1Mask); + platform.key_down[MOD_CAPS] = !!(ev.xkey.state & LockMask); + platform.key_down[MOD_NUM] = !!(ev.xkey.state & Mod2Mask); + platform.key_down[MOD_SCROLL] = !!(ev.xkey.state & Mod3Mask); + if (platform.input_size < MAX_INPUT_SIZE) { + if (k < 32 || k >= 128) + platform.input[platform.input_size++] = (Input_Key) { + .ctrl = !!(ev.xkey.state & ControlMask), + .shift = !!(ev.xkey.state & ShiftMask), + .alt = !!(ev.xkey.state & Mod1Mask), + .caps = !!(ev.xkey.state & LockMask), + .num = !!(ev.xkey.state & Mod2Mask), + .scroll = !!(ev.xkey.state & Mod3Mask), + .key = k, + .c = 0, + }; + else { + c8 buf[16]; + i32 len = Xutf8LookupString(_ic, &ev.xkey, buf, sizeof buf - 1, NULL, NULL); + if (len > 0) + platform.input[platform.input_size++] = (Input_Key) { + .ctrl = !!(ev.xkey.state & ControlMask), + .shift = !!(ev.xkey.state & ShiftMask), + .alt = !!(ev.xkey.state & Mod1Mask), + .caps = !!(ev.xkey.state & LockMask), + .num = !!(ev.xkey.state & Mod2Mask), + .scroll = !!(ev.xkey.state & Mod3Mask), + .key = k, + .c = utf8_read(len, buf), + }; + } + } + if (!requested_clipboard) { + XConvertSelection(_display, _clipboard, _targets, _clipboard, _window, CurrentTime); + requested_clipboard = 1; + } + } break; - case KeyRelease: + case KeyRelease: { + i16 k = _key_table[ev.xkey.keycode]; 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]]--; + platform.key_down [k] = 0; + platform.key_pressed[k]--; + platform.key_down[MOD_CTRL] = !!(ev.xkey.state & ControlMask); + platform.key_down[MOD_SHIFT] = !!(ev.xkey.state & ShiftMask); + platform.key_down[MOD_ALT] = !!(ev.xkey.state & Mod1Mask); + platform.key_down[MOD_CAPS] = !!(ev.xkey.state & LockMask); + platform.key_down[MOD_NUM] = !!(ev.xkey.state & Mod2Mask); + platform.key_down[MOD_SCROLL] = !!(ev.xkey.state & Mod3Mask); + } break; + + case SelectionRequest: + if (ev.xselectionrequest.requestor != _window && + ev.xselectionrequest.selection == _clipboard && + XGetSelectionOwner(_display, _clipboard) == _window) { + if (ev.xselectionrequest.property != None) { + if (ev.xselectionrequest.target == _targets) + XChangeProperty( + ev.xselectionrequest.display, + ev.xselectionrequest.requestor, + ev.xselectionrequest.property, + XA_ATOM, + 32, + PropModeReplace, + (u8 *) &_utf8_string, + 1 + ); + else if (ev.xselectionrequest.target == _utf8_string) + XChangeProperty( + ev.xselectionrequest.display, + ev.xselectionrequest.requestor, + ev.xselectionrequest.property, + ev.xselectionrequest.target, + 8, + PropModeReplace, + (u8 *) platform.clipboard, + platform.clipboard_size + ); + } + + XSendEvent(_display, ev.xselectionrequest.requestor, 0, 0, (XEvent *) &(XSelectionEvent) { + .type = SelectionNotify, + .serial = ev.xselectionrequest.serial, + .send_event = ev.xselectionrequest.send_event, + .display = ev.xselectionrequest.display, + .requestor = ev.xselectionrequest.requestor, + .selection = ev.xselectionrequest.selection, + .target = ev.xselectionrequest.target, + .property = ev.xselectionrequest.property, + .time = ev.xselectionrequest.time, + }); + } + break; + + case SelectionNotify: + if (ev.xselection.property != None) { + i64 len; + u8 *data; + + XGetWindowProperty( + _display, + _window, + _clipboard, + 0, + MAX_CLIPBOARD_SIZE, + False, + AnyPropertyType, + &(Atom) {0}, + &(int) {0}, + (unsigned long *) &len, + &(unsigned long) {0}, + &data + ); + + if (ev.xselection.target == _targets) { + _target = None; + Atom *list = (Atom *) data; + + for (i64 i = 0; i < len; i++) + if (list[i] == XA_STRING) + _target = XA_STRING; + else if (list[i] == _utf8_string) { + _target = _utf8_string; + break; + } + + if (_target != None) + XConvertSelection(_display, _clipboard, _target, _clipboard, _window, CurrentTime); + } else if (ev.xselection.target == _target) { + if (len > MAX_CLIPBOARD_SIZE) + len = MAX_CLIPBOARD_SIZE; + platform.clipboard_size = len; + if (len > 0) + memcpy(platform.clipboard, data, len); + } + + if (data) + XFree(data); + } 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 FocusIn: + platform.has_focus = 1; + if (!requested_clipboard) { + XConvertSelection(_display, _clipboard, _targets, _clipboard, _window, CurrentTime); + requested_clipboard = 1; + } + break; + + case MappingNotify: + XRefreshKeyboardMapping(&ev.xmapping); + break; + case ClientMessage: - if ((Atom) ev.xclient.data.l[0] == wm_delete_window) + if ((Atom) ev.xclient.data.l[0] == _wm_delete_window) platform.done = 1; break; @@ -504,14 +904,17 @@ i32 p_handle_events(void) { } } - 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; + for (i64 k = 0; k < (i64) (sizeof platform.key_pressed / sizeof *platform.key_pressed); ++k) + platform.key_pressed[k] = platform.key_pressed[k] > 0 ? 1 : 0; + + XWindowAttributes attrs; + XGetWindowAttributes(_display, _window, &attrs); - XFreePixmap(display, pixmap); - pixmap = XCreatePixmap(display, window, attrs.width, attrs.height, depth); + 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; @@ -520,3 +923,56 @@ i32 p_handle_events(void) { 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); +} + +void p_clipboard_write(i64 size, c8 *data) { + assert(size <= MAX_CLIPBOARD_SIZE); + + XSetSelectionOwner(_display, _clipboard, _window, CurrentTime); + + platform.clipboard_size = size < MAX_CLIPBOARD_SIZE ? size : MAX_CLIPBOARD_SIZE; + if (platform.clipboard_size > 0) + memcpy(platform.clipboard, data, platform.clipboard_size); +} + +#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