summaryrefslogtreecommitdiff
path: root/examples/gravity.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/gravity.c')
-rwxr-xr-xexamples/gravity.c890
1 files changed, 673 insertions, 217 deletions
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 <guattari.tech>, 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 <assert.h>
+#include <string.h>
+
+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 <X11/Xlib.h>
#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <sched.h>
+#include <time.h>
-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