From 6fccde1c53dd059af9c3592bc5dcf9b4b4b3b080 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Sat, 3 Aug 2024 20:33:28 +0200 Subject: Add code --- reduced_system_layer.c | 536 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 536 insertions(+) create mode 100755 reduced_system_layer.c (limited to 'reduced_system_layer.c') diff --git a/reduced_system_layer.c b/reduced_system_layer.c new file mode 100755 index 0000000..01a39e5 --- /dev/null +++ b/reduced_system_layer.c @@ -0,0 +1,536 @@ +#if 0 /* +#/ ================================================================ +#/ +#/ reduced_system_layer.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); + +// 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}; + +// ================================================================ +// +// WRITE YOUR CODE HERE +// +// ================================================================ + +i32 main(i32 argc, c8 **argv) { + (void) argc; + (void) argv; + + platform = (Platform) { + .title = "Reduced System Layer", + .frame_width = 960, + .frame_height = 720, + }; + + p_init(); + + while (!platform.done) { + p_handle_events(); + } + + 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 +// +// ================================================================ + +#include +#include + +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) { + XEvent ev; + XWindowAttributes attrs; + + XGetWindowAttributes(_display, _window, &attrs); + + if (attrs.width == platform.frame_width && attrs.height == platform.frame_height) + XPutImage(_display, _window, _gc, &_image, 0, 0, 0, 0, attrs.width, attrs.height); + + XFlush(_display); + + 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; + + 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:; + } + } + + 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; +} + +#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