From 8542a7cb147522091d30bd6fcea9fe7aa99f9b80 Mon Sep 17 00:00:00 2001 From: IllusionMan1212 Date: Mon, 11 Dec 2023 22:42:19 +0200 Subject: [PATCH] finish refactor --- Makefile | 4 +- assets/sudoku-grid.png | Bin 4611 -> 0 bytes core.c | 33 ++ core.h | 126 ++++++ cudoku.c | 51 ++- cudoku.h | 5 +- main.c | 121 ++--- shader.c | 7 +- text.c | 54 ++- text.h | 8 +- timer.c | 7 +- timer.h | 10 +- todo.txt | 33 -- ui.c | 50 +-- ui.h | 3 - x11.c | 186 -------- x11.h | 12 - zephr.c | 987 +++++++++++++++++++++++++++++++++++++++-- zephr.h | 372 +++++++++++++++- zephr_math.c | 28 +- zephr_math.h | 3 - 21 files changed, 1622 insertions(+), 478 deletions(-) delete mode 100644 assets/sudoku-grid.png create mode 100644 core.c create mode 100644 core.h delete mode 100644 todo.txt delete mode 100644 x11.c delete mode 100644 x11.h diff --git a/Makefile b/Makefile index 268952a..07b9825 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ BIN=cudoku CC=gcc -CFLAGS=-Wall -Wextra -O3 -pedantic -g `pkg-config --cflags freetype2` -I3rdparty/glad/include -I3rdparty/stb -I3rdparty/fmod/include -OBJ=main.o x11.o cudoku.o shader.o text.o audio.o timer.o ui.o zephr.o zephr_math.o 3rdparty/glad/src/gl.o 3rdparty/glad/src/glx.o 3rdparty/stb/stb.o +CFLAGS=-Wall -Wextra -Werror -Wfloat-conversion -Wimplicit-fallthrough -pedantic -g `pkg-config --cflags freetype2` -I3rdparty/glad/include -I3rdparty/stb -I3rdparty/fmod/include +OBJ=main.o cudoku.o core.o shader.o text.o audio.o timer.o ui.o zephr.o zephr_math.o 3rdparty/glad/src/gl.o 3rdparty/glad/src/glx.o 3rdparty/stb/stb.o LDFLAGS=`pkg-config --libs x11 freetype2` -lm -L3rdparty/fmod/lib -Wl,-rpath=3rdparty/fmod/lib -lfmod DEPS=3rdparty/glad/include/glad/gl.h 3rdparty/glad/include/glad/glx.h 3rdparty/stb/stb_image.h 3rdparty/stb/stb_ds.h 3rdparty/fmod/include/fmod.h diff --git a/assets/sudoku-grid.png b/assets/sudoku-grid.png deleted file mode 100644 index 3755586d46b498895ce639027fdd1f207523b492..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4611 zcmeAS@N?(olHy`uVBq!ia0y~yU~U0n4kn;TnNCIy0|R4Arn7T^r?ay{K~a8MW=<*t zgT}<#iMAex9b}Hi2OnLf%U32)cp^adNYjdr#bFCMM4i@NSk-*;tMDun6-~XqrtE_U z`;V?_?%uqPZ(WnZ55`BY9xZvHr20=^b4$m^2Rr27zq5UJkI`xGnUKxNG6ANiJ&q+O zS#8!T+q+1b@rzPR;LMr7=BTJXu-y0V;p5k_Q_?Hv|1NIXWI379Zeqo>mu;?l9BuYy zzMS*D^Y9vt^v?60$9Z-sd{n+JI{m(dxQtju+6@oY=beX-BrWWnYL@2etU6DrFD111 zQ|p$~k>{f4TrHYv`u=3R#FByoP4yge49)U(3Dc+w%R}`LYGbtE&hXaKqOI#yLobz*YQ}ap~oQqNuOHxx5$}>wc6x=<1 z1Hv2m#2Fa)-+H<@hE&{odt+ngS_g)^FtQ^4y+4OEV z_(q)OW?6iiMO)+aoXK~%C;#pFVEEI@RI*^P;+u(=H|qMb>~r)uZZFKh@L|fMA1im= z6cfCCY5xD*)YYq+}A#Ii;a1l-u2k*XKNyE?J*5^-5SA(5)3c&;T1KkHZ3t6pw=f7+F2Y z!yrB-G$`G|0*+LgoOHkb>FX;IjPFcZY>TY6E$uvD&hhdCFf+l@?}6N|$ID-(MZ4s9 z*ZHfpupJJXqF;(a2dEJv<5ViOm83X4K4; z(mpV^ZG*et@ogr8KiCvyk>hiRZ{eSiyE)w*xi8QE-?r&$=)yi3sk5*M3mT0uJUSN?8@)&e!`a?&c};7jZYWR<7Lxbo&PhIySWM1rRv_01K$0SZ2qa xzP@rJ)16F?>L(>R-olOf0+$|wOTRzt)=av36VJZ__R$#_JYD@<);T3K0RSs|HIV=S diff --git a/core.c b/core.c new file mode 100644 index 0000000..fbc37f8 --- /dev/null +++ b/core.c @@ -0,0 +1,33 @@ +#include +#include +#include + +#include "core.h" + +void _core_assert_failed(const char* cond, const char* file, int line, const char* message, ...) { + fprintf(stderr, "assertion failed: %s\nmessage: ", cond); + + va_list va_args; + va_start(va_args, message); + vfprintf(stderr, message, va_args); + va_end(va_args); + + fprintf(stderr, "\nfile: %s:%u\n", file, line); + /* char* stacktrace = core_get_stacktrace(); */ + /* fprintf(stderr, "stacktrace:\n%s\n\n", stacktrace); */ + abort(); +} + +_Noreturn uintptr_t _core_abort(const char* file, int line, const char* message, ...) { + fprintf(stderr, "abort: "); + + va_list va_args; + va_start(va_args, message); + vfprintf(stderr, message, va_args); + va_end(va_args); + + fprintf(stderr, "\nfile: %s:%u\n", file, line); + /* char* stacktrace = core_get_stacktrace(); */ + /* fprintf(stderr, "stacktrace:\n%s\n\n", stacktrace); */ + abort(); +} diff --git a/core.h b/core.h new file mode 100644 index 0000000..8b098c7 --- /dev/null +++ b/core.h @@ -0,0 +1,126 @@ +#pragma once + +#include +#include + +#ifdef __GNUC__ +#define CORE_LIKELY(expr) __builtin_expect((expr), 1) +#define CORE_UNLIKELY(expr) __builtin_expect((expr), 0) +#else +#define CORE_LIKELY(expr) expr +#define CORE_UNLIKELY(expr) expr +#endif + +/////////////////////////// +// +// +// Number and Pointer Utilities +// +// +/////////////////////////// + +#define CORE_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define CORE_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define CORE_CLAMP(v, min, max) (((v) > (max)) ? (max) : ((v) < (min)) ? : (min) : (v)) +#define CORE_UNUSED(expr) ((void)(expr)) + +// align must be a power of 2 +#define CORE_INT_ROUND_UP_ALIGN(i, align) (((i) + ((align) - )) & ~((align) - 1)) +// align must be a power of 2 +#define CORE_INT_ROUND_DOWN_ALIGN(i, align) ((i) & ~((align) - 1)) + +#define CORE_ARRAY_COUNT(array) (sizeof(array) / sizeof(*(array))) +#define CORE_IS_POWER_OF_TWO_OR_ZERO(v) (((v) & ((v) - 1)) == 0) +#define CORE_IS_POWER_OF_TWO(v) (((v) != 0) && (((v) & ((v) - 1)) == 0)) +#define CORE_PTR_ADD(ptr, by) (void*)((uintptr_t)(ptr) + (uintptr_t)(by)) +#define CORE_PTR_SUB(ptr, by) (void*)((uintptr_t)(ptr) - (uintptr_t)(by)) +#define CORE_PTR_DIFF(to, from) ((char*)(to) - (char*)(from)) +// aling must be a power of 2 +#define CORE_PTR_ROUND_UP_ALIGN(ptr, align) ((void*)CORE_INT_ROUND_UP_ALIGN((uintptr_t)(ptr), (uintptr_t)(align))) +// aling must be a power of 2 +#define CORE_PTR_ROUND_DOWN_ALIGN(ptr, align) ((void*)CORE_INT_ROUND_DOWN_ALIGN((uintptr_t)(ptr), (uintptr_t)(align))) +#define CORE_ZERO_ELMT(ptr) memset(ptr, 0, sizeof(*(ptr))) +#define CORE_ONE_ELMT(ptr) memset(ptr, 0xff, sizeof(*(ptr))) +#define CORE_ZERO_ELMT_MANY(ptr, elmts_count) memset(ptr, 0, sizeof(*(ptr)) * (elmts_count)) +#define CORE_ONE_ELMT_MANY(ptr, elmts_count) memset(ptr, 0xff, sizeof(*(ptr)) * (elmts_count)) +#define CORE_ZERO_ARRAY(array) memset(array, 0, sizeof(array)) +#define CORE_ONE_ARRAY(array) memset(array, 0xff, sizeof(array)) +#define CORE_COPY_ARRAY(dst, src) memcpy(dst, src, sizeof(dst)) +#define CORE_COPY_ELMT_MANY(dst, src, elmts_count) memcpy(dst, src, sizeof(*(dst)) * (elmts_count)) +#define CORE_COPY_OVERLAP_ELMT_MANY(dst, src, elmts_count) memmove(dst, src, sizeof(*(dst)) * (elmts_count)) +#define CORE_CMP_ARRAY(a, b) (memcmp(a, b, sizeof(a)) == 0) +#define CORE_CMP_ELMT(a, b) (memcmp(a, b, sizeof(*(a))) == 0) +#define CORE_CMP_ELMT_MANY(a, b, elmts_count) (memcmp(a, b, sizeof(*(a)) * elmts_count) == 0) + +#define CORE_DIV_ROUND_UP(a, b) (((a) / (b)) + ((a) % (b) != 0)) + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +typedef uint64_t uptr; +typedef int64_t sptr; + +typedef float f32; +typedef double f64; + +#define U8_MAX UINT8_MAX +#define U16_MAX UINT16_MAX +#define U32_MAX UINT32_MAX +#define U64_MAX UINT64_MAX +#define UPTR_MAX UINT64_MAX + +#define I8_MIN INT8_MIN +#define I8_MAX INT8_MAX +#define I16_MIN INT16_MIN +#define I16_MAX INT16_MAX +#define I32_MIN INT32_MIN +#define I32_MAX INT32_MAX +#define I64_MIN INT64_MIN +#define I64_MAX INT64_MAX + +/////////////////////////// +// +// +// Abort and Assertions +// +// +/////////////////////////// + +#ifndef CORE_ENABLE_DEBUG_ASSERTIONS +#define CORE_ENABLE_DEBUG_ASSERTIONS 1 +#endif +#ifndef CORE_ABORT +#define CORE_ABORT(...) _core_abort(__FILE__, __LINE__, __VA_ARGS__) +#endif + +#ifndef CORE_ASSERT +#define CORE_ASSERT(cond, ...) if (CORE_UNLIKELY(!(cond))) _core_assert_failed(#cond, __FILE__, __LINE__, __VA_ARGS__) +#endif + +#if CORE_ENABLE_DEBUG_ASSERTIONS +#define CORE_DEBUG_ASSERT CORE_ASSERT +#else +#define CORE_DEBUG_ASSERT(cond, ...) (void)(cond) +#endif + +#define CORE_DEBUG_ASSERT_NON_ZERO(value) CORE_DEBUG_ASSERT(value, "'%s' must be a non zero value", #value) +#define CORE_DEBUG_ASSERT_ARRAY_BOUNDS(idx, count) (((idx) < (count)) ? (idx) : CORE_ABORT("idx %d out of bounds for array of size %d", idx, count)) +#define CORE_DEBUG_ASSERT_ARRAY_RESIZE(count, cap) CORE_DEBUG_ASSERT((count) <= (cap), "cannot resize array to count '%zu' when it has a capacity of '%zu'", count, cap) +#define CORE_DEBUG_ASSERT_POWER_OF_TWO(v) CORE_DEBUG_ASSERT(CORE_IS_POWER_OF_TWO(v), #v "must be a power of two but got '%zu'", v) + +#define CORE_BREAKPOINT() asm ("int $3") // x86 specific breakpoint instruction + +void _core_assert_failed(const char* cond, const char* file, int line, const char* message, ...); +_Noreturn uintptr_t _core_abort(const char* file, int line, const char* message, ...); + +/* static inline bool core_bitset_is_set(u64* bitset, uptr bit_idx) { */ +/* return (bool)(bitset[bit_idx >> 6] & (1 << (bit_idx & 63))); */ +/* } */ + diff --git a/cudoku.c b/cudoku.c index 62d6987..22e9c6b 100644 --- a/cudoku.c +++ b/cudoku.c @@ -4,9 +4,7 @@ #include #include -#include -#include "3rdparty/stb/stb_image.h" - +#include "core.h" #include "cudoku.h" #include "audio.h" #include "ui.h" @@ -43,7 +41,7 @@ void draw_win(Cudoku *game) { char time_text[64]; - float time_elapsed = game->win_time - game->timer.start + game->timer.elapsed; + double time_elapsed = game->win_time - game->timer.start + game->timer.elapsed; snprintf(time_text, 64, "Solved in: %02dm%02ds", (int)time_elapsed / 60, (int)time_elapsed % 60); UIConstraints constraints = {0}; @@ -203,8 +201,8 @@ void toggle_check(Cudoku *game) { void do_selection(Cudoku *game, int x, int y) { if (game->has_won || game->timer.state == TIMER_PAUSED) return; - int cell_x = floor(x / 100.f); - int cell_y = floor(y / 100.f); + int cell_x = (int)floor(x / 100.f); + int cell_y = (int)floor(y / 100.f); if (cell_x < 0 || cell_x > 8 || cell_y < 0 || cell_y > 8) { game->should_draw_selection = false; @@ -306,8 +304,8 @@ void get_candidates(Cell board[9][9], int row, int col, int candidates[], int *s } // check box - int box_row = floor(row / 3.f) * 3; - int box_col = floor(col / 3.f) * 3; + int box_row = (int)floor(row / 3.f) * 3; + int box_col = (int)floor(col / 3.f) * 3; for (int i = box_row; i < box_row + 3; i++) { for (int j = box_col; j < box_col + 3; j++) { if (board[i][j].value != 0) { @@ -360,8 +358,8 @@ bool backtracker(Cudoku *game, int start_row, int start_col) { // try every candidate until we find one that uniquely solves the board. while (num_candidates) { - uint rand_idx = rand() % num_candidates; - uint candidate = candidates[rand_idx]; + u32 rand_idx = rand() % num_candidates; + u32 candidate = candidates[rand_idx]; remove_arr_element(candidates, rand_idx, num_candidates); num_candidates--; game->board[start_row][start_col].value = candidate; @@ -433,7 +431,7 @@ void remove_numbers(Cudoku *game) { int rand_idx = rand() % filled_cells_size; int cell = filled_cells[rand_idx]; remove_arr_element(filled_cells, rand_idx, filled_cells_size--); - int removed_row = floor(cell / 9.f); + int removed_row = (int)floor(cell / 9.f); int removed_col = cell % 9; Cell removed_cell = game->board[removed_row][removed_col]; @@ -481,7 +479,7 @@ void generate_random_board(Cudoku *game) { size = 9; } - const int k = floor(row / 3.f) * 3; + const int k = (int)floor(row / 3.f) * 3; for (int j = k; j < k + 3; j++) { int rand_idx = rand() % size; int num = digits[rand_idx]; @@ -527,8 +525,8 @@ bool toggle_help(Cudoku *game) { void draw_help(Timer *timer) { int const text_padding = 10; - float const help_font_size = 24.f; - float const closing_in_font_size = 18.f; + int const help_font_size = 24; + int const closing_in_font_size = 18; Color const bg_color = { .r = 0.f, .g = 0.f, @@ -548,16 +546,16 @@ void draw_help(Timer *timer) { .a = 255.0f, }; - int overlay_height = 0; - int overlay_width = 0; - int total_help_texts_height = 10; + float overlay_height = 0; + float overlay_width = 0; + float total_help_texts_height = 10; for (int i = 0; i < HELP_TEXT_SIZE; i++) { Sizef text_size = calculate_text_size(help_texts[i], help_font_size); overlay_height += text_size.height + text_padding; - overlay_width = max(overlay_width, text_size.width + text_padding * 2); + overlay_width = CORE_MAX(overlay_width, text_size.width + text_padding * 2); } - Size size = {.width = overlay_width, .height = overlay_height + text_padding * 2}; + Sizef size = {.width = overlay_width, .height = overlay_height + text_padding * 2}; UIConstraints constraints = {0}; set_width_constraint(&constraints, size.width, UI_CONSTRAINT_FIXED); set_height_constraint(&constraints, size.height, UI_CONSTRAINT_FIXED); @@ -592,30 +590,31 @@ void draw_help(Timer *timer) { void draw_timer(Timer *timer) { if (timer->state == TIMER_STOPPED) return; - char timer_text[64]; - Color text_color = { + const int font_size = 32; + const Color text_color = { .r = 66.0f, .g = 92.0f, .b = 124.0f, .a = 255.0f, }; - Color bg_color = { + const Color bg_color = { .r = 0.0f, .g = 0.0f, .b = 0.0f, .a = 25.0f, }; - float font_size = 32.f; - Alignment alignment = ALIGN_BOTTOM_RIGHT; + const Alignment alignment = ALIGN_BOTTOM_RIGHT; - float elapsed = timer_elapsed(timer); + char timer_text[64]; + + double elapsed = timer_elapsed(timer); if (timer->state == TIMER_PAUSED) { // if the timer is paused, we want to show the time at which it was paused elapsed = timer->elapsed; } - int minutes = elapsed / 60; + int minutes = (int)elapsed / 60; int seconds = (int)elapsed % 60; snprintf(timer_text, sizeof(timer_text), "Time: %02d:%02d", minutes, seconds); diff --git a/cudoku.h b/cudoku.h index 9b4a9c7..cb5f9ed 100644 --- a/cudoku.h +++ b/cudoku.h @@ -2,9 +2,8 @@ #include -#include "shader.h" #include "timer.h" -#include "ui.h" +#include "zephr_math.h" typedef struct Cell { int value; @@ -21,7 +20,7 @@ typedef struct Cudoku { bool should_draw_help; Timer help_timer; Timer timer; - float win_time; + double win_time; } Cudoku; void draw_selection_box(int x, int y, const Color color); diff --git a/main.c b/main.c index 7228627..a05984c 100644 --- a/main.c +++ b/main.c @@ -1,34 +1,39 @@ -#include +#include #include +#include #include #include -#include -#include +#include -#include "cudoku.h" #include "audio.h" +#include "cudoku.h" +#include "timer.h" #include "zephr.h" +#include "zephr_math.h" #define DEBUG 1 -extern Display *display; const char *font_path = "assets/fonts/Rubik/Rubik-VariableFont_wght.ttf"; const char *title = "Cudoku"; void usage() { printf("Usage: cudoku [OPTION]\n\n"); - printf(" %-20s%-20s", "-h, --help", "prints this help message\n"); - printf(" %-20s%-20s", "-f, --font FONT", "use a custom font file to render text\n"); + printf(" %-30s%-20s", "-h, --help", "prints this help message\n"); + printf(" %-30s%-20s", "-f, --font ", "use a custom font file to render text\n"); } -void handle_keypress(XEvent xev, Cudoku *game) { - if (XLookupKeysym(&xev.xkey, 0) == XK_r) { +void handle_keypress(ZephrEvent e, Cudoku *game) { + if (e.key.code == ZEPHR_KEYCODE_R) { reset_board(game); } else if ( - xev.xkey.state & ControlMask && - xev.xkey.state & Mod1Mask && - XLookupKeysym(&xev.xkey, 0) == XK_w) { + e.key.code == ZEPHR_KEYCODE_ESCAPE || + ((e.key.mods & ZEPHR_KEY_MOD_CTRL) && e.key.code == ZEPHR_KEYCODE_Q) + ) { + zephr_quit(); + } else if ( + e.key.mods & (ZEPHR_KEY_MOD_CTRL | ZEPHR_KEY_MOD_SHIFT) && + e.key.code == ZEPHR_KEYCODE_W) { #if DEBUG printf("DEBUG: Ctrl + Shift + W pressed. Winning game\n"); @@ -41,42 +46,44 @@ void handle_keypress(XEvent xev, Cudoku *game) { audio_play_win(); #endif - } else if (XLookupKeysym(&xev.xkey, 0) >= XK_0 && XLookupKeysym(&xev.xkey, 0) <= XK_9) { - set_selected_number(game, XLookupKeysym(&xev.xkey, 0) - XK_0); - } else if (XLookupKeysym(&xev.xkey, 0) == XK_BackSpace || - XLookupKeysym(&xev.xkey, 0) == XK_Delete) { + } else if (e.key.code >= ZEPHR_KEYCODE_1 && e.key.code <= ZEPHR_KEYCODE_9) { + set_selected_number(game, e.key.code - ZEPHR_KEYCODE_1 + 1); + } else if (e.key.code == ZEPHR_KEYCODE_0) { + set_selected_number(game, 0); + } else if (e.key.code == ZEPHR_KEYCODE_BACKSPACE || + e.key.code == ZEPHR_KEYCODE_DELETE) { set_selected_number(game, 0); } else if ( - XLookupKeysym(&xev.xkey, 0) == XK_Left || - XLookupKeysym(&xev.xkey, 0) == XK_a || - XLookupKeysym(&xev.xkey, 0) == XK_h) { + e.key.code == ZEPHR_KEYCODE_LEFT || + e.key.code == ZEPHR_KEYCODE_A || + e.key.code == ZEPHR_KEYCODE_H) { move_selection(game, -1, 0); } else if ( - XLookupKeysym(&xev.xkey, 0) == XK_Right || - XLookupKeysym(&xev.xkey, 0) == XK_d || - XLookupKeysym(&xev.xkey, 0) == XK_l) { + e.key.code == ZEPHR_KEYCODE_RIGHT || + e.key.code == ZEPHR_KEYCODE_D || + e.key.code == ZEPHR_KEYCODE_L) { move_selection(game, 1, 0); } else if ( - XLookupKeysym(&xev.xkey, 0) == XK_Up || - XLookupKeysym(&xev.xkey, 0) == XK_w || - XLookupKeysym(&xev.xkey, 0) == XK_k) { + e.key.code == ZEPHR_KEYCODE_UP || + e.key.code == ZEPHR_KEYCODE_W || + e.key.code == ZEPHR_KEYCODE_K) { move_selection(game, 0, -1); } else if ( - XLookupKeysym(&xev.xkey, 0) == XK_Down || - XLookupKeysym(&xev.xkey, 0) == XK_s || - XLookupKeysym(&xev.xkey, 0) == XK_j) { + e.key.code == ZEPHR_KEYCODE_DOWN || + e.key.code == ZEPHR_KEYCODE_S || + e.key.code == ZEPHR_KEYCODE_J) { move_selection(game, 0, 1); } else if ( - XLookupKeysym(&xev.xkey, 0) == XK_Return || - XLookupKeysym(&xev.xkey, 0) == XK_space) { + e.key.code == ZEPHR_KEYCODE_ENTER || + e.key.code == ZEPHR_KEYCODE_SPACE) { toggle_selection(game); - } else if (XLookupKeysym(&xev.xkey, 0) == XK_n) { + } else if (e.key.code == ZEPHR_KEYCODE_N) { generate_random_board(game); - } else if (XLookupKeysym(&xev.xkey, 0) == XK_c) { + } else if (e.key.code == ZEPHR_KEYCODE_C) { toggle_check(game); - } else if (XLookupKeysym(&xev.xkey, 0) == XK_p) { + } else if (e.key.code == ZEPHR_KEYCODE_P) { pause_game(game); - } else if (XLookupKeysym(&xev.xkey, 0) == XK_F1) { + } else if (e.key.code == ZEPHR_KEYCODE_F1) { if (!toggle_help(game)) { timer_stop(&game->help_timer); } @@ -92,9 +99,9 @@ int main(int argc, char *argv[]) { } for (int i = 1; i < argc; i++) { - char *flag = argv[i]; + char *option = argv[i]; - if (strcmp(flag, "-f") == 0 || strcmp(flag, "--font") == 0) { + if (strcmp(option, "-f") == 0 || strcmp(option, "--font") == 0) { if (i + 1 < argc) { font_path = argv[i + 1]; i++; @@ -105,9 +112,8 @@ int main(int argc, char *argv[]) { } } - Color clear_color = {0.0f, 0.0f, 0.0f, 1.f}; Size window_size = {900, 900}; - int res = init_zephr(font_path, title, window_size, &clear_color); + int res = init_zephr(font_path, title, window_size); if (res != 0) { printf("[ERROR]: could not initialize zephr\n"); return 1; @@ -124,32 +130,31 @@ int main(int argc, char *argv[]) { timer_start(&game.timer, 0.0f); while (!zephr_should_quit()) { - while (XPending(display)) { - XEvent xev; - XNextEvent(display, &xev); + ZephrEvent event; - if (xev.type == KeyPress) { - if (XLookupKeysym(&xev.xkey, 0) == XK_Escape || - (XLookupKeysym(&xev.xkey, 0) == XK_q && xev.xkey.state & ControlMask)) { + while (zephr_iter_events(&event)) { + switch (event.type) { + case ZEPHR_EVENT_UNKNOWN: + printf("[WARN]: Unknown event\n"); + break; + case ZEPHR_EVENT_KEY_PRESSED: + handle_keypress(event, &game); + break; + case ZEPHR_EVENT_MOUSE_BUTTON_PRESSED: + if (event.mouse.button == ZEPHR_MOUSE_BUTTON_LEFT) { + do_selection(&game, event.mouse.position.x, event.mouse.position.y); + } + break; + case ZEPHR_EVENT_WINDOW_CLOSED: zephr_quit(); break; - } else { - handle_keypress(xev, &game); + default: + case ZEPHR_EVENT_WINDOW_RESIZED: + case ZEPHR_EVENT_KEY_RELEASED: + break; } - } else if (xev.type == ButtonPress) { - if (xev.xbutton.button == Button1) { - int x = xev.xbutton.x; - int y = xev.xbutton.y; - do_selection(&game, x, y); - } - } } - // TODO: how do I want the input handling to look like - //zephr_poll_events();? - //for event in zephr_events()?? - // - draw_board(&game, window_size); draw_timer(&game.timer); diff --git a/shader.c b/shader.c index 527cfb4..3a4cac3 100644 --- a/shader.c +++ b/shader.c @@ -3,6 +3,7 @@ #include +#include "core.h" #include "shader.h" void read_shader_file(const char *path, char **buf) { @@ -14,13 +15,15 @@ void read_shader_file(const char *path, char **buf) { } fseek(fp, 0, SEEK_END); - long filesize = ftell(fp); + u64 filesize = ftell(fp); fseek(fp, 0, SEEK_SET); *buf = malloc(filesize + 1); (*buf)[filesize] = '\0'; - fread(*buf, filesize, 1, fp); + u64 read = fread(*buf, filesize, 1, fp); + + CORE_UNUSED(read); fclose(fp); } diff --git a/text.c b/text.c index b1fb043..f8745bb 100644 --- a/text.c +++ b/text.c @@ -4,16 +4,14 @@ #include #include FT_FREETYPE_H -#include #include -#include "zephr.h" #include "shader.h" #include "text.h" +#include "zephr.h" #define FONT_PIXEL_SIZE 64 -extern Context zephr_context; Shader font_shader; unsigned int font_vao; unsigned int font_instance_vbo; @@ -33,7 +31,7 @@ void extend_glyph_instance_list(GlyphInstanceList *dest, GlyphInstanceList *src) dest->size += src->size; } -void new_glyph_instance_list(GlyphInstanceList *list, uint capacity) { +void new_glyph_instance_list(GlyphInstanceList *list, u32 capacity) { list->size = 0; list->capacity = capacity; list->data = malloc(list->capacity * sizeof(GlyphInstance)); @@ -89,8 +87,8 @@ int init_freetype(const char* font_path) { /* FT_UInt glyph_idx; */ /* FT_ULong c = FT_Get_First_Char(face, &glyph_idx); */ - uint tex_width = 0, tex_height = 0; - for (uint i = 32; i < 128; i++) { + u32 tex_width = 0, tex_height = 0; + for (u32 i = 32; i < 128; i++) { if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { printf("[ERROR]: failed to load glyph for char '0x%x'\n", i); } @@ -98,12 +96,12 @@ int init_freetype(const char* font_path) { /* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */ tex_width += face->glyph->bitmap.width + 1; - tex_height = max(tex_height, face->glyph->bitmap.rows); + tex_height = CORE_MAX(tex_height, face->glyph->bitmap.rows); } char *pixels = (char *)calloc(tex_width * tex_height, 1); - for (uint i = 32; i < 128; i++) { + for (u32 i = 32; i < 128; i++) { /* while (glyph_idx) { */ if (FT_Load_Char(face, i, FT_LOAD_RENDER)) { printf("[ERROR]: failed to load glyph for char '0x%x'\n", i); @@ -118,8 +116,8 @@ int init_freetype(const char* font_path) { pen_y += (1 + (face->size->metrics.height >> 6)); } - for (uint row = 0; row < bmp->rows; row++) { - for (uint col = 0; col < bmp->width; col++) { + for (u32 row = 0; row < bmp->rows; row++) { + for (u32 col = 0; col < bmp->width; col++) { int x = pen_x + col; int y = pen_y + row; pixels[y * tex_width + x] = bmp->buffer[row * bmp->pitch + col]; @@ -146,7 +144,7 @@ int init_freetype(const char* font_path) { character.size = (Size){ .width = face->glyph->bitmap.width, .height = face->glyph->bitmap.rows }; character.bearing = (Size){ .width = face->glyph->bitmap_left, .height = face->glyph->bitmap_top }; - zephr_context.font.characters[i] = character; + zephr_ctx.font.characters[i] = character; pen_x += bmp->width + 1; @@ -165,7 +163,7 @@ int init_freetype(const char* font_path) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - zephr_context.font.atlas_texture_id = texture; + zephr_ctx.font.atlas_texture_id = texture; glBindTexture(GL_TEXTURE_2D, 0); @@ -178,8 +176,8 @@ int init_freetype(const char* font_path) { } int init_fonts(const char *font_path) { - uint font_vbo; - uint font_ebo; + u32 font_vbo; + u32 font_ebo; int res = init_freetype(font_path); if (res != 0) { @@ -247,7 +245,7 @@ int init_fonts(const char *font_path) { for (int j = 0; j < 4; j++) { char text[32]; snprintf(text, 32, "texcoords[%d]", (i - 32) * 4 + j); - set_vec2f(font_shader, text, zephr_context.font.characters[i].tex_coords[j].x, zephr_context.font.characters[i].tex_coords[j].y); + set_vec2f(font_shader, text, zephr_ctx.font.characters[i].tex_coords[j].x, zephr_ctx.font.characters[i].tex_coords[j].y); } } @@ -266,12 +264,12 @@ Sizef calculate_text_size(const char *text, int font_size) { // NOTE: I don't like looping through the characters twice, but it's fine for now for (int i = 0; text[i] != '\0'; i++) { - Character ch = zephr_context.font.characters[(uint)text[i]]; - max_bearing_h = max(max_bearing_h, ch.bearing.height); + Character ch = zephr_ctx.font.characters[(u32)text[i]]; + max_bearing_h = CORE_MAX(max_bearing_h, ch.bearing.height); } - for (int i = 0; text[i] != '\0'; i++) { - Character ch = zephr_context.font.characters[(uint)text[i]]; + for (u64 i = 0; text[i] != '\0'; i++) { + Character ch = zephr_ctx.font.characters[(u32)text[i]]; w += ch.advance / 64; // remove bearing of first character @@ -289,7 +287,7 @@ Sizef calculate_text_size(const char *text, int font_size) { w -= (ch.bearing.width); } - h = max(h, max_bearing_h - ch.bearing.height + ch.size.height); + h = CORE_MAX(h, max_bearing_h - ch.bearing.height + ch.size.height); } size.width = (float)w * scale; size.height = (float)h * scale; @@ -302,7 +300,7 @@ GlyphInstanceList get_glyph_instance_list_from_text(const char *text, int font_s if (color) { text_color = (Color){ color->r / 255.f, color->g / 255.f, color->b / 255.f, color->a / 255.f }; } - set_mat4f(font_shader, "projection", (float *)zephr_context.projection.m); + set_mat4f(font_shader, "projection", (float *)zephr_ctx.projection.m); Vec2f pos = { 0.f, 0.f }; Sizef size = {0}; @@ -326,11 +324,11 @@ GlyphInstanceList get_glyph_instance_list_from_text(const char *text, int font_s int max_bearing_h = 0; for (int i = 0; (i < text[i]) != '\0'; i++) { - Character ch = zephr_context.font.characters[(int)text[i]]; - max_bearing_h = max(max_bearing_h, ch.bearing.height); + Character ch = zephr_ctx.font.characters[(int)text[i]]; + max_bearing_h = CORE_MAX(max_bearing_h, ch.bearing.height); } - float first_char_bearing_w = zephr_context.font.characters[(int)text[0]].bearing.width; + float first_char_bearing_w = zephr_ctx.font.characters[(int)text[0]].bearing.width; GlyphInstanceList glyph_instance_list; new_glyph_instance_list(&glyph_instance_list, 16); @@ -342,7 +340,7 @@ GlyphInstanceList get_glyph_instance_list_from_text(const char *text, int font_s int c = 0; int x = 0; while (text[c] != '\0') { - Character ch = zephr_context.font.characters[(int)text[c]]; + Character ch = zephr_ctx.font.characters[(int)text[c]]; // subtract the bearing width of the first character to remove the extra space // at the start of the text and move every char to the left by that width float xpos = (x + (ch.bearing.width - first_char_bearing_w)); @@ -352,7 +350,7 @@ GlyphInstanceList get_glyph_instance_list_from_text(const char *text, int font_s .position = (Vec4f){xpos, ypos, ch.size.width, ch.size.height}, .tex_coords_index = (int)text[c] - 32, .color = text_color, - .model = {0}, + .model = {{0}}, }; memcpy(instance.model, model.m, sizeof(float[4][4])); @@ -370,7 +368,7 @@ void draw_text(const char* text, int font_size, UIConstraints constraints, const GlyphInstanceList glyph_instance_list = get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment); glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, zephr_context.font.atlas_texture_id); + glBindTexture(GL_TEXTURE_2D, zephr_ctx.font.atlas_texture_id); glBindVertexArray(font_vao); glBindBuffer(GL_ARRAY_BUFFER, font_instance_vbo); @@ -386,7 +384,7 @@ void draw_text(const char* text, int font_size, UIConstraints constraints, const void draw_text_batch(GlyphInstanceList *batch) { glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, zephr_context.font.atlas_texture_id); + glBindTexture(GL_TEXTURE_2D, zephr_ctx.font.atlas_texture_id); glBindVertexArray(font_vao); glBindBuffer(GL_ARRAY_BUFFER, font_instance_vbo); diff --git a/text.h b/text.h index 1bc5543..6e86bbf 100644 --- a/text.h +++ b/text.h @@ -1,6 +1,8 @@ #pragma once +#include "core.h" #include "ui.h" +#include "zephr_math.h" typedef struct Character { Size size; @@ -9,10 +11,10 @@ typedef struct Character { Vec2f tex_coords[4]; // the 4 corners of the character quad } Character; -typedef struct ZephFont { +typedef struct ZephrFont { Character characters[128]; unsigned int atlas_texture_id; -} ZephFont; +} ZephrFont; typedef struct TextInstance { Vec4f position; @@ -27,7 +29,7 @@ typedef struct GlyphInstanceList { int capacity; } GlyphInstanceList; -void new_glyph_instance_list(GlyphInstanceList *list, uint capacity); +void new_glyph_instance_list(GlyphInstanceList *list, u32 capacity); int init_fonts(const char *font_path); Sizef calculate_text_size(const char *text, int font_size); void draw_text(const char* text, int font_size, UIConstraints constraints, const Color *color, Alignment alignment); diff --git a/timer.c b/timer.c index e81da47..37d1eb4 100644 --- a/timer.c +++ b/timer.c @@ -1,3 +1,6 @@ +#include +#include + #include "timer.h" struct timeval start_time; @@ -36,11 +39,11 @@ void timer_reset(Timer *timer) { timer->state = TIMER_RUNNING; } -float timer_remaining(Timer *timer) { +double timer_remaining(Timer *timer) { return timer->duration - (get_time() - timer->start); } -float timer_elapsed(Timer *timer) { +double timer_elapsed(Timer *timer) { return get_time() - timer->start + timer->elapsed; } diff --git a/timer.h b/timer.h index cb64e3f..75b3b35 100644 --- a/timer.h +++ b/timer.h @@ -1,8 +1,6 @@ #pragma once #include -#include -#include typedef enum TimerState { TIMER_RUNNING, @@ -11,8 +9,8 @@ typedef enum TimerState { } TimerState; typedef struct Timer { - float start; - float elapsed; + double start; + double elapsed; float duration; TimerState state; } Timer; @@ -23,7 +21,7 @@ bool timer_ended(Timer *timer); void timer_start(Timer *timer, float duration); void timer_stop(Timer *timer); void timer_reset(Timer *timer); -float timer_remaining(Timer *timer); -float timer_elapsed(Timer *timer); +double timer_remaining(Timer *timer); +double timer_elapsed(Timer *timer); void timer_pause(Timer *timer); void timer_resume(Timer *timer); diff --git a/todo.txt b/todo.txt deleted file mode 100644 index 30b4f4e..0000000 --- a/todo.txt +++ /dev/null @@ -1,33 +0,0 @@ -zephr stuff -- "premade" (but also customizable) ui elements (buttons, text boxes, etc) with instanced rendering -- border decorations for shapes (maybe. or maybe only for ui elements) -- some kind of ui layout/hierarchy system (for later, when we actually need it) -- texture func (draw_image? or draw_texture? func) -- input handling (mouse, keyboard, gamepad?) with an event system - Watch youtu.be/watch?v=oheNg0k_WDc and related videos to get ideas for the keyboard and gamepad handling - -text rendering improvements - - [ ] unicode support (allows for non-ascii characters) - - unicode support removed for the foreseeable future - - [x] atlas texture for text glyphs (this means we only load in one texture for all text) - - [x] better estimation of atlas width and height (currently the generated atlas texture - has a lot of empty space (over 50% of the texture's height)) - - ended up just looping over the ascii glyphs and finding the max width and height - - - SDF generation allowing for better text scaling (slower but visually better) - - SDF is too slow if we want to support unicode. it's acceptably fast for ascii only though. - - An alternative is to use MSDF at a much lower pixel height (e.g 16) which results in - better scaling than SDF (almost perfect) and is fast enough to include unicode. - - Another alternative is using a pre-baked SDF texture atlas. This is the fastest - but it's also the least flexible because we can't load arbitrary fonts - - Maybe also rendering text as vector using bezier curves. Sounds like a lot of work though. - - Will probably end up using MSDF (if I can get it to work with C lol) - - - [x] instanced rendering for text (this means we only have to do one draw call for all text) - running into problems. we got the positions to instance correctly but the texture coordinates - are more difficult to work with because we can't just send a texcoord per vertex per instance - - Ended up passing hardcoded texcoords of all glyphs to the shader using a uniform array which allows us to render infinite - instances of text in one draw call without exceeding any buffer limits. Of course this does mean that if we were to support - unicode we'd have to make the uniform array bigger and its size dynamic. So unicode is dropped for now - diff --git a/ui.c b/ui.c index 2f5734d..b8aa99a 100644 --- a/ui.c +++ b/ui.c @@ -1,23 +1,17 @@ #include -#include -#include #include -#include "cudoku.h" #include "shader.h" -#include "ui.h" -#include "x11.h" #include "zephr.h" -extern Context zephr_context; Shader ui_shader; unsigned int ui_vao; unsigned int ui_vbo; int init_ui(const char* font_path, Size window_size) { - zephr_context.window.size = window_size; - zephr_context.projection = orthographic_projection_2d(0.f, window_size.width, window_size.height, 0.f); + zephr_ctx.window.size = window_size; + zephr_ctx.projection = orthographic_projection_2d(0.f, window_size.width, window_size.height, 0.f); int res = init_fonts(font_path); if (res == -1) { @@ -54,7 +48,7 @@ void set_x_constraint(UIConstraints *constraints, float value, UIConstraint type constraints->x = value; break; case UI_CONSTRAINT_RELATIVE: - constraints->x = (value * zephr_context.window.size.width); + constraints->x = (value * zephr_ctx.window.size.width); break; } } @@ -68,7 +62,7 @@ void set_y_constraint(UIConstraints *constraints, float value, UIConstraint type constraints->y = value; break; case UI_CONSTRAINT_RELATIVE: - constraints->y = (value * zephr_context.window.size.height); + constraints->y = (value * zephr_ctx.window.size.height); break; } } @@ -79,10 +73,10 @@ void set_width_constraint(UIConstraints *constraints, float value, UIConstraint constraints->width = value; break; case UI_CONSTRAINT_RELATIVE: - constraints->width = (value * zephr_context.window.size.width); + constraints->width = (value * zephr_ctx.window.size.width); break; case UI_CONSTRAINT_RELATIVE_PIXELS: - constraints->width = zephr_context.window.size.width / (float)zephr_context.screen_size.width * value; + constraints->width = zephr_ctx.window.size.width / (float)zephr_ctx.screen_size.width * value; break; case UI_CONSTRAINT_ASPECT_RATIO: constraints->width = constraints->height * value; @@ -96,10 +90,10 @@ void set_height_constraint(UIConstraints *constraints, float value, UIConstraint constraints->height = value; break; case UI_CONSTRAINT_RELATIVE: - constraints->height = (value * zephr_context.window.size.height); + constraints->height = (value * zephr_ctx.window.size.height); break; case UI_CONSTRAINT_RELATIVE_PIXELS: - constraints->height = zephr_context.window.size.height / (float)zephr_context.screen_size.height * value; + constraints->height = zephr_ctx.window.size.height / (float)zephr_ctx.screen_size.height * value; break; case UI_CONSTRAINT_ASPECT_RATIO: constraints->height = constraints->width * value; @@ -123,36 +117,36 @@ void apply_alignment(Alignment align, Vec2f *pos, Sizef size) { pos->y = pos->y; break; case ALIGN_TOP_CENTER: - pos->x += zephr_context.window.size.width / 2.f - size.width / 2.f; + pos->x += zephr_ctx.window.size.width / 2.f - size.width / 2.f; pos->y = pos->y; break; case ALIGN_TOP_RIGHT: - pos->x += zephr_context.window.size.width - size.width; + pos->x += zephr_ctx.window.size.width - size.width; pos->y = pos->y; break; case ALIGN_BOTTOM_LEFT: pos->x = pos->x; - pos->y += zephr_context.window.size.height - size.height; + pos->y += zephr_ctx.window.size.height - size.height; break; case ALIGN_BOTTOM_CENTER: - pos->x += zephr_context.window.size.width / 2.f - size.width / 2.f; - pos->y += zephr_context.window.size.height - size.height; + pos->x += zephr_ctx.window.size.width / 2.f - size.width / 2.f; + pos->y += zephr_ctx.window.size.height - size.height; break; case ALIGN_BOTTOM_RIGHT: - pos->x += zephr_context.window.size.width - size.width; - pos->y += zephr_context.window.size.height - size.height; + pos->x += zephr_ctx.window.size.width - size.width; + pos->y += zephr_ctx.window.size.height - size.height; break; case ALIGN_LEFT_CENTER: pos->x = pos->x; - pos->y += zephr_context.window.size.height / 2.f - size.height / 2.f; + pos->y += zephr_ctx.window.size.height / 2.f - size.height / 2.f; break; case ALIGN_RIGHT_CENTER: - pos->x += zephr_context.window.size.width - size.width; - pos->y += zephr_context.window.size.height / 2.f - size.height / 2.f; + pos->x += zephr_ctx.window.size.width - size.width; + pos->y += zephr_ctx.window.size.height / 2.f - size.height / 2.f; break; case ALIGN_CENTER: - pos->x += zephr_context.window.size.width / 2.f - size.width / 2.f; - pos->y += zephr_context.window.size.height / 2.f - size.height / 2.f; + pos->x += zephr_ctx.window.size.width / 2.f - size.width / 2.f; + pos->y += zephr_ctx.window.size.height / 2.f - size.height / 2.f; break; } } @@ -169,7 +163,7 @@ void draw_quad(UIConstraints constraints, const Color *color, float border_radiu set_float(ui_shader, "uiWidth", constraints.width); set_float(ui_shader, "uiHeight", constraints.height); set_float(ui_shader, "borderRadius", border_radius); - set_mat4f(ui_shader, "projection", (float *)zephr_context.projection.m); + set_mat4f(ui_shader, "projection", (float *)zephr_ctx.projection.m); Vec2f pos = { 0.f, 0.f }; Sizef size = { 0.f, 0.f }; @@ -228,7 +222,7 @@ void draw_triangle(UIConstraints constraints, const Color *color, Alignment alig } else { set_vec4f(ui_shader, "aColor", 0.f, 0.f, 0.f, 1.f); } - set_mat4f(ui_shader, "projection", (float *)zephr_context.projection.m); + set_mat4f(ui_shader, "projection", (float *)zephr_ctx.projection.m); Vec2f pos = { 0.f, 0.f }; Sizef size = { 0.f, 0.f }; diff --git a/ui.h b/ui.h index 5b9dcaf..dfdb0c3 100644 --- a/ui.h +++ b/ui.h @@ -1,8 +1,5 @@ #pragma once -#include -#include - #include "zephr_math.h" typedef enum Alignment { diff --git a/x11.c b/x11.c deleted file mode 100644 index aaf70f7..0000000 --- a/x11.c +++ /dev/null @@ -1,186 +0,0 @@ -#include -#include -#include - -#include -#include - -Colormap colormap; -GLXContext glx_context; - -void x11_resize_window(Display *display, Window window) { - XWindowAttributes win_attrs; - XGetWindowAttributes(display, window, &win_attrs); - glViewport(0, 0, win_attrs.width, win_attrs.height); -} - -int x11_init(Display **display, Window *window, const char* title, int window_width, int window_height) { - *display = XOpenDisplay(NULL); - - if (display == NULL) { - fprintf(stderr, "Cannot open display\n"); - return 1; - } - - int screen = DefaultScreen(*display); - Window root = RootWindow(*display, screen); - Visual *visual = DefaultVisual(*display, screen); - - colormap = XCreateColormap(*display, root, visual, AllocNone); - - XSetWindowAttributes attributes; - attributes.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | - StructureNotifyMask | ButtonPressMask; - attributes.colormap = colormap; - - *window = XCreateWindow(*display, root, 0, 0, window_width, window_height, 0, - DefaultDepth(*display, screen), InputOutput, visual, - CWColormap | CWEventMask, &attributes); - - // hints to the WM that the window is a dialog window which makes it a floating - // window in titling WMs (only tested on DWM though) - Atom net_wm_window_type = XInternAtom(*display, "_NET_WM_WINDOW_TYPE", False); - Atom net_wm_window_type_dialog = XInternAtom(*display, "_NET_WM_WINDOW_TYPE_DIALOG", False); - XChangeProperty(*display, *window, net_wm_window_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&net_wm_window_type_dialog, 1); - - XMapWindow(*display, *window); - XStoreName(*display, *window, title); - - if (!window) { - printf("Failed to create window\n"); - return 1; - } - - int glx_version = gladLoaderLoadGLX(*display, screen); - - if (!glx_version) { - printf("Failed to load GLX\n"); - return 1; - } - printf("Loaded GLX %d.%d\n", - GLAD_VERSION_MAJOR(glx_version), GLAD_VERSION_MINOR(glx_version)); - - GLint visual_attributes[] = { - GLX_RENDER_TYPE, GLX_RGBA_BIT, - GLX_DEPTH_SIZE, 24, - GLX_DOUBLEBUFFER, 1, - GLX_SAMPLES, 4, // MSAA - None - }; - - int num_fbc = 0; - GLXFBConfig *fbc = - glXChooseFBConfig(*display, screen, visual_attributes, &num_fbc); - - GLint context_attributes[] = { - GLX_CONTEXT_MAJOR_VERSION_ARB, 3, - GLX_CONTEXT_MINOR_VERSION_ARB, 3, - GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, - None - }; - - glx_context = glXCreateContextAttribsARB(*display, fbc[0], NULL, 1, - context_attributes); - - if (!glx_context) { - printf("Failed to create GLX context\n"); - return 1; - } - - glXMakeCurrent(*display, *window, glx_context); - - int gl_version = gladLoaderLoadGL(); - - if (!gl_version) { - printf("Failed to load GL\n"); - return 1; - } - printf("Loaded GL %d.%d\n", - GLAD_VERSION_MAJOR(gl_version), GLAD_VERSION_MINOR(gl_version)); - - XWindowAttributes win_attrs; - XGetWindowAttributes(*display, *window, &win_attrs); - - glad_glXSwapIntervalEXT(*display, *window, 1); - // we enable blending for text - glEnable(GL_BLEND); - glEnable(GL_MULTISAMPLE); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glViewport(0, 0, win_attrs.width, win_attrs.height); - x11_resize_window(*display, *window); - - XFree(fbc); - - return 0; -} - -void x11_close(Display **display, Window *window) { - glXMakeCurrent(*display, 0, 0); - glXDestroyContext(*display, glx_context); - - XDestroyWindow(*display, *window); - XFreeColormap(*display, colormap); - XCloseDisplay(*display); - - gladLoaderUnloadGLX(); -} - -void go_fullscreen(Display *display, Window window) -{ - XEvent xev; - Atom wm_state = XInternAtom(display, "_NET_WM_STATE", False); - Atom fullscreen = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False); - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD - xev.xclient.data.l[1] = fullscreen; - xev.xclient.data.l[2] = 0; - XSendEvent(display, DefaultRootWindow(display), False, - SubstructureNotifyMask | SubstructureRedirectMask, &xev); -} - -void return_fullscreen(Display *display, Window window) -{ - XEvent xev; - Atom wm_state = XInternAtom(display, "_NET_WM_STATE", False); - Atom fullscreen = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False); - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = 0; // _NET_WM_STATE_REMOVE - xev.xclient.data.l[1] = fullscreen; - xev.xclient.data.l[2] = 0; - XSendEvent(display, DefaultRootWindow(display), False, - SubstructureNotifyMask | SubstructureRedirectMask, &xev); -} - -void x11_toggle_fullscreen(bool fullscreen, Display *display, Window window) { - if (fullscreen) { - return_fullscreen(display, window); - } else { - go_fullscreen(display, window); - } -} - -void x11_get_screen_size(Display *display, int *width, int *height) { - Screen *screen = DefaultScreenOfDisplay(display); - - *width = screen->width; - *height = screen->height; -} - -void x11_make_window_non_resizable(Display *display, Window window, int width, int height) { - XSizeHints *size_hints = XAllocSizeHints(); - size_hints->flags = PMinSize | PMaxSize; - size_hints->min_width = width; - size_hints->min_height = height; - size_hints->max_width = width; - size_hints->max_height = height; - XSetWMNormalHints(display, window, size_hints); - XFree(size_hints); -} diff --git a/x11.h b/x11.h deleted file mode 100644 index 36961fb..0000000 --- a/x11.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -#include - -int x11_init(Display **display, Window *window, const char* title, int window_width, int window_height); -void x11_close(Display **display, Window *window); -void x11_resize_window(Display *display, Window window); -void x11_toggle_fullscreen(bool fullscreen, Display *display, Window window); -void x11_get_screen_size(Display *display, int *width, int *height); -void x11_make_window_non_resizable(Display *display, Window window, int width, int height); diff --git a/zephr.c b/zephr.c index 6021dc0..b5b0f9f 100644 --- a/zephr.c +++ b/zephr.c @@ -1,27 +1,720 @@ #include +#include -#include +#include #include -#include "zephr.h" #include "audio.h" -#include "x11.h" -#include "ui.h" +#include "core.h" #include "timer.h" +#include "ui.h" +#include "zephr.h" -Display *display; -Window window; +Display *x11_display; +Window x11_window; +Colormap x11_colormap; +GLXContext glx_context; +/* XIC x11_xic; */ -Context zephr_context = {0}; +Context zephr_ctx = {0}; -int init_zephr(const char* font_path, const char* window_title, Size window_size, Color *clear_color) { +/////////////////////////// +// +// Common +// +/////////////////////////// + +ZephrKeycode zephr_evdev_scancode_to_zephr_scancode_map[] = { + ZEPHR_KEYCODE_NULL, + ZEPHR_KEYCODE_ESCAPE, + ZEPHR_KEYCODE_1, + ZEPHR_KEYCODE_2, + ZEPHR_KEYCODE_3, + ZEPHR_KEYCODE_4, + ZEPHR_KEYCODE_5, + ZEPHR_KEYCODE_6, + ZEPHR_KEYCODE_7, + ZEPHR_KEYCODE_8, + ZEPHR_KEYCODE_9, + ZEPHR_KEYCODE_0, + ZEPHR_KEYCODE_MINUS, + ZEPHR_KEYCODE_EQUALS, + ZEPHR_KEYCODE_BACKSPACE, + ZEPHR_KEYCODE_TAB, + ZEPHR_KEYCODE_Q, + ZEPHR_KEYCODE_W, + ZEPHR_KEYCODE_E, + ZEPHR_KEYCODE_R, + ZEPHR_KEYCODE_T, + ZEPHR_KEYCODE_Y, + ZEPHR_KEYCODE_U, + ZEPHR_KEYCODE_I, + ZEPHR_KEYCODE_O, + ZEPHR_KEYCODE_P, + ZEPHR_KEYCODE_LEFT_BRACKET, + ZEPHR_KEYCODE_RIGHT_BRACKET, + ZEPHR_KEYCODE_ENTER, + ZEPHR_KEYCODE_LEFT_CTRL, + ZEPHR_KEYCODE_A, + ZEPHR_KEYCODE_S, + ZEPHR_KEYCODE_D, + ZEPHR_KEYCODE_F, + ZEPHR_KEYCODE_G, + ZEPHR_KEYCODE_H, + ZEPHR_KEYCODE_J, + ZEPHR_KEYCODE_K, + ZEPHR_KEYCODE_L, + ZEPHR_KEYCODE_SEMICOLON, + ZEPHR_KEYCODE_APOSTROPHE, + ZEPHR_KEYCODE_GRAVE, + ZEPHR_KEYCODE_LEFT_SHIFT, + ZEPHR_KEYCODE_BACKSLASH, + ZEPHR_KEYCODE_Z, + ZEPHR_KEYCODE_X, + ZEPHR_KEYCODE_C, + ZEPHR_KEYCODE_V, + ZEPHR_KEYCODE_B, + ZEPHR_KEYCODE_N, + ZEPHR_KEYCODE_M, + ZEPHR_KEYCODE_COMMA, + ZEPHR_KEYCODE_PERIOD, + ZEPHR_KEYCODE_SLASH, + ZEPHR_KEYCODE_RIGHT_SHIFT, + ZEPHR_KEYCODE_KP_MULTIPLY, + ZEPHR_KEYCODE_LEFT_ALT, + ZEPHR_KEYCODE_SPACE, + ZEPHR_KEYCODE_CAPS_LOCK, + ZEPHR_KEYCODE_F1, + ZEPHR_KEYCODE_F2, + ZEPHR_KEYCODE_F3, + ZEPHR_KEYCODE_F4, + ZEPHR_KEYCODE_F5, + ZEPHR_KEYCODE_F6, + ZEPHR_KEYCODE_F7, + ZEPHR_KEYCODE_F8, + ZEPHR_KEYCODE_F9, + ZEPHR_KEYCODE_F10, + ZEPHR_KEYCODE_NUM_LOCK_OR_CLEAR, + ZEPHR_KEYCODE_SCROLL_LOCK, + ZEPHR_KEYCODE_KP_7, + ZEPHR_KEYCODE_KP_8, + ZEPHR_KEYCODE_KP_9, + ZEPHR_KEYCODE_KP_MINUS, + ZEPHR_KEYCODE_KP_4, + ZEPHR_KEYCODE_KP_5, + ZEPHR_KEYCODE_KP_6, + ZEPHR_KEYCODE_KP_PLUS, + ZEPHR_KEYCODE_KP_1, + ZEPHR_KEYCODE_KP_2, + ZEPHR_KEYCODE_KP_3, + ZEPHR_KEYCODE_KP_0, + ZEPHR_KEYCODE_KP_PERIOD, + [85] = ZEPHR_KEYCODE_LANG5, // KEY_ZENKAKUHANKAKU + ZEPHR_KEYCODE_NON_US_BACKSLASH, // KEY_102ND + ZEPHR_KEYCODE_F11, + ZEPHR_KEYCODE_F12, + ZEPHR_KEYCODE_INTERNATIONAL1, // KEY_RO, + ZEPHR_KEYCODE_LANG3, // KEY_KATAKANA + ZEPHR_KEYCODE_LANG4, // KEY_HIRAGANA + ZEPHR_KEYCODE_INTERNATIONAL4, // KEY_HENKAN + ZEPHR_KEYCODE_INTERNATIONAL2, // KEY_KATAKANAHIRAGANA + ZEPHR_KEYCODE_INTERNATIONAL5, // KEY_MUHENKAN + ZEPHR_KEYCODE_INTERNATIONAL5, // KEY_KPJOCOMMA + ZEPHR_KEYCODE_KP_ENTER, + ZEPHR_KEYCODE_RIGHT_CTRL, + ZEPHR_KEYCODE_KP_DIVIDE, + ZEPHR_KEYCODE_SYSREQ, + ZEPHR_KEYCODE_RIGHT_ALT, + ZEPHR_KEYCODE_NULL, // KEY_LINEFEED + ZEPHR_KEYCODE_HOME, + ZEPHR_KEYCODE_UP, + ZEPHR_KEYCODE_PAGE_UP, + ZEPHR_KEYCODE_LEFT, + ZEPHR_KEYCODE_RIGHT, + ZEPHR_KEYCODE_END, + ZEPHR_KEYCODE_DOWN, + ZEPHR_KEYCODE_PAGE_DOWN, + ZEPHR_KEYCODE_INSERT, + ZEPHR_KEYCODE_DELETE, + ZEPHR_KEYCODE_NULL, // KEY_MACRO + ZEPHR_KEYCODE_MUTE, + ZEPHR_KEYCODE_VOLUME_DOWN, + ZEPHR_KEYCODE_VOLUME_UP, + ZEPHR_KEYCODE_POWER, + ZEPHR_KEYCODE_KP_EQUALS, + ZEPHR_KEYCODE_KP_PLUS_MINUS, + ZEPHR_KEYCODE_PAUSE, + [121] = ZEPHR_KEYCODE_KP_COMMA, + ZEPHR_KEYCODE_LANG1, // KEY_HANGUEL + ZEPHR_KEYCODE_LANG2, // KEY_HANJA + ZEPHR_KEYCODE_INTERNATIONAL3, // KEY_YEN + ZEPHR_KEYCODE_LEFT_META, + ZEPHR_KEYCODE_RIGHT_META, + ZEPHR_KEYCODE_APPLICATION, // KEY_COMPOSE + ZEPHR_KEYCODE_STOP, + ZEPHR_KEYCODE_AGAIN, + ZEPHR_KEYCODE_NULL, // KEY_PROPS + ZEPHR_KEYCODE_UNDO, + ZEPHR_KEYCODE_NULL, // KEY_FRONT + ZEPHR_KEYCODE_COPY, + ZEPHR_KEYCODE_NULL, // KEY_OPEN + ZEPHR_KEYCODE_PASTE, + ZEPHR_KEYCODE_FIND, + ZEPHR_KEYCODE_CUT, + ZEPHR_KEYCODE_HELP, + ZEPHR_KEYCODE_MENU, + ZEPHR_KEYCODE_NULL, // CALCULATOR + ZEPHR_KEYCODE_NULL, // KEY_SETUP + ZEPHR_KEYCODE_NULL, // SLEEP + ZEPHR_KEYCODE_NULL, // KEY_WAKEUP + ZEPHR_KEYCODE_NULL, // KEY_FILE + ZEPHR_KEYCODE_NULL, // KEY_SENDFILE + ZEPHR_KEYCODE_NULL, // KEY_DELETEFILE + ZEPHR_KEYCODE_NULL, // KEY_XFER + ZEPHR_KEYCODE_NULL, // KEY_PROG1 + ZEPHR_KEYCODE_NULL, // KEY_PROG2 + ZEPHR_KEYCODE_NULL, // WWW + ZEPHR_KEYCODE_NULL, // KEY_MSDOS + ZEPHR_KEYCODE_NULL, // KEY_COFFEE + ZEPHR_KEYCODE_NULL, // KEY_DIRECTION + ZEPHR_KEYCODE_NULL, // KEY_CYCLEWINDOWS + ZEPHR_KEYCODE_NULL, // MAIL + ZEPHR_KEYCODE_NULL, // AC_BOOKMARKS + ZEPHR_KEYCODE_NULL, // COMPUTER + ZEPHR_KEYCODE_NULL, // AC_BACK + ZEPHR_KEYCODE_NULL, // AC_FORWARD + ZEPHR_KEYCODE_NULL, // KEY_CLOSECD + ZEPHR_KEYCODE_NULL, // EJECT + ZEPHR_KEYCODE_NULL, // KEY_EJECTCLOSECD + ZEPHR_KEYCODE_NULL, // AUDIO_NEXT + ZEPHR_KEYCODE_NULL, // AUDIO_PLAY + ZEPHR_KEYCODE_NULL, // AUDIO_PREV + ZEPHR_KEYCODE_NULL, // AUDIO_STOP + ZEPHR_KEYCODE_NULL, // KEY_RECORD + ZEPHR_KEYCODE_NULL, // AUDIO_REWIND + ZEPHR_KEYCODE_NULL, // KEY_PHONE + ZEPHR_KEYCODE_NULL, // KEY_ISO + ZEPHR_KEYCODE_NULL, // KEY_CONFIG + ZEPHR_KEYCODE_NULL, // AC_HOME + ZEPHR_KEYCODE_NULL, // AC_REFRESH + ZEPHR_KEYCODE_NULL, // KEY_EXIT + ZEPHR_KEYCODE_NULL, // KEY_MOVE + ZEPHR_KEYCODE_NULL, // KEY_EDIT + ZEPHR_KEYCODE_NULL, // KEY_SCROLLUP + ZEPHR_KEYCODE_NULL, // KEY_SCROLLDOWN + ZEPHR_KEYCODE_KP_LEFT_PAREN, + ZEPHR_KEYCODE_KP_RIGHT_PAREN, + ZEPHR_KEYCODE_NULL, // KEY_NEW + ZEPHR_KEYCODE_NULL, // KEY_REDO + ZEPHR_KEYCODE_F13, + ZEPHR_KEYCODE_F14, + ZEPHR_KEYCODE_F15, + ZEPHR_KEYCODE_F16, + ZEPHR_KEYCODE_F17, + ZEPHR_KEYCODE_F18, + ZEPHR_KEYCODE_F19, + ZEPHR_KEYCODE_F20, + ZEPHR_KEYCODE_F21, + ZEPHR_KEYCODE_F22, + ZEPHR_KEYCODE_F23, + ZEPHR_KEYCODE_F24, + [200] = ZEPHR_KEYCODE_NULL, // KEY_PLAYCD + ZEPHR_KEYCODE_NULL, // KEY_PAUSECD + ZEPHR_KEYCODE_NULL, // KEY_PROG3 + ZEPHR_KEYCODE_NULL, // KEY_PROG4 + [205] = ZEPHR_KEYCODE_NULL, // KEY_SUSPEND + ZEPHR_KEYCODE_NULL, // KEY_CLOSE + ZEPHR_KEYCODE_NULL, // KEY_PLAY + ZEPHR_KEYCODE_NULL, // AUDIO_FASTFORWARD + ZEPHR_KEYCODE_NULL, // KEY_BASSBOOST + ZEPHR_KEYCODE_NULL, // KEY_PRINT + ZEPHR_KEYCODE_NULL, // KEY_HP + ZEPHR_KEYCODE_NULL, // KEY_CAMERA + ZEPHR_KEYCODE_NULL, // KEY_SOUND + ZEPHR_KEYCODE_NULL, // KEY_QUESTION + ZEPHR_KEYCODE_NULL, // KEY_EMAIL + ZEPHR_KEYCODE_NULL, // KEY_CHAT + ZEPHR_KEYCODE_NULL, // AC_SEARCH + ZEPHR_KEYCODE_NULL, // KEY_CONNECT + ZEPHR_KEYCODE_NULL, // KEY_FINANCE + ZEPHR_KEYCODE_NULL, // KEY_SPORT + ZEPHR_KEYCODE_NULL, // KEY_SHOP + ZEPHR_KEYCODE_ALT_ERASE, + ZEPHR_KEYCODE_CANCEL, + ZEPHR_KEYCODE_NULL, // BRIGHTNESS_DOWN + ZEPHR_KEYCODE_NULL, // BRIGHNESS_UP + ZEPHR_KEYCODE_NULL, // KEY_MEDIA + ZEPHR_KEYCODE_NULL, // DISPLAY_SWITCH + ZEPHR_KEYCODE_NULL, // KBD_ILLUM_TOGGLE + ZEPHR_KEYCODE_NULL, // KBD_ILLUM_DOWN + ZEPHR_KEYCODE_NULL, // KBD_ILLUM_UP + ZEPHR_KEYCODE_NULL, // KEY_SEND + ZEPHR_KEYCODE_NULL, // KEY_REPLY + ZEPHR_KEYCODE_NULL, // KEY_FORWARDEMAIL + ZEPHR_KEYCODE_NULL, // KEY_SAVE + ZEPHR_KEYCODE_NULL, // KEY_DOCUMENTS + ZEPHR_KEYCODE_NULL, // KEY_BATTERY +}; + +ZephrKeycode zephr_x11_fn_keysym_to_zephr_keycode_map[] = { + [0x08] = ZEPHR_KEYCODE_BACKSPACE, + ZEPHR_KEYCODE_TAB, + + [0x0b] = ZEPHR_KEYCODE_CLEAR, + [0x0d] = ZEPHR_KEYCODE_ENTER, + [0x13] = ZEPHR_KEYCODE_PAUSE, + ZEPHR_KEYCODE_SCROLL_LOCK, + ZEPHR_KEYCODE_SYSREQ, + [0x1b] = ZEPHR_KEYCODE_ESCAPE, + + [0x50] = ZEPHR_KEYCODE_HOME, + ZEPHR_KEYCODE_LEFT, + ZEPHR_KEYCODE_UP, + ZEPHR_KEYCODE_RIGHT, + ZEPHR_KEYCODE_DOWN, + ZEPHR_KEYCODE_PAGE_UP, + ZEPHR_KEYCODE_PAGE_DOWN, + ZEPHR_KEYCODE_END, + + [0x60] = ZEPHR_KEYCODE_SELECT, + ZEPHR_KEYCODE_PRINT_SCREEN, + ZEPHR_KEYCODE_EXECUTE, + ZEPHR_KEYCODE_INSERT, + + [0x65] = ZEPHR_KEYCODE_UNDO, + [0x67] = ZEPHR_KEYCODE_MENU, + ZEPHR_KEYCODE_FIND, + ZEPHR_KEYCODE_CANCEL, + ZEPHR_KEYCODE_HELP, + + [0x7f] = ZEPHR_KEYCODE_NUM_LOCK_OR_CLEAR, + [0x80] = ZEPHR_KEYCODE_KP_SPACE, + [0x89] = ZEPHR_KEYCODE_KP_TAB, + [0x8d] = ZEPHR_KEYCODE_KP_ENTER, + [0xbd] = ZEPHR_KEYCODE_KP_EQUALS, + + [0xaa] = ZEPHR_KEYCODE_KP_MULTIPLY, + ZEPHR_KEYCODE_KP_PLUS, + + [0xad] = ZEPHR_KEYCODE_KP_MINUS, + ZEPHR_KEYCODE_KP_DECIMAL, + ZEPHR_KEYCODE_KP_DIVIDE, + + [0xb0] = ZEPHR_KEYCODE_KP_0, + ZEPHR_KEYCODE_KP_1, + ZEPHR_KEYCODE_KP_2, + ZEPHR_KEYCODE_KP_3, + ZEPHR_KEYCODE_KP_4, + ZEPHR_KEYCODE_KP_5, + ZEPHR_KEYCODE_KP_6, + ZEPHR_KEYCODE_KP_7, + ZEPHR_KEYCODE_KP_8, + ZEPHR_KEYCODE_KP_9, + + [0xbe] = ZEPHR_KEYCODE_F1, + ZEPHR_KEYCODE_F2, + ZEPHR_KEYCODE_F3, + ZEPHR_KEYCODE_F4, + ZEPHR_KEYCODE_F5, + ZEPHR_KEYCODE_F6, + ZEPHR_KEYCODE_F7, + ZEPHR_KEYCODE_F8, + ZEPHR_KEYCODE_F9, + ZEPHR_KEYCODE_F10, + ZEPHR_KEYCODE_F11, + ZEPHR_KEYCODE_F12, + ZEPHR_KEYCODE_F13, + ZEPHR_KEYCODE_F14, + ZEPHR_KEYCODE_F15, + ZEPHR_KEYCODE_F16, + ZEPHR_KEYCODE_F17, + ZEPHR_KEYCODE_F18, + ZEPHR_KEYCODE_F19, + ZEPHR_KEYCODE_F20, + ZEPHR_KEYCODE_F21, + ZEPHR_KEYCODE_F22, + ZEPHR_KEYCODE_F23, + ZEPHR_KEYCODE_F24, + + [0xe1] = ZEPHR_KEYCODE_LEFT_SHIFT, + ZEPHR_KEYCODE_RIGHT_SHIFT, + ZEPHR_KEYCODE_LEFT_CTRL, + ZEPHR_KEYCODE_RIGHT_CTRL, + ZEPHR_KEYCODE_CAPS_LOCK, + ZEPHR_KEYCODE_CAPS_LOCK, // TODO: ??? + ZEPHR_KEYCODE_LEFT_META, + ZEPHR_KEYCODE_RIGHT_META, + ZEPHR_KEYCODE_LEFT_ALT, + ZEPHR_KEYCODE_RIGHT_ALT, + ZEPHR_KEYCODE_LEFT_META, // TODO: ??? + ZEPHR_KEYCODE_RIGHT_META, // TODO: ??? + + [0xff] = ZEPHR_KEYCODE_DELETE, +}; + +ZephrKeycode zephr_x11_fn_ex_keysym_to_zephr_keycode_map[] = { + [0x03] = ZEPHR_KEYCODE_RIGHT_ALT, + [0xff] = ZEPHR_KEYCODE_NULL, +}; + +ZephrKeycode zephr_x11_latin1_keysym_to_zephr_keycode_map[] = { + [0x20] = ZEPHR_KEYCODE_SPACE, + [0x27] = ZEPHR_KEYCODE_APOSTROPHE, + [0x2c] = ZEPHR_KEYCODE_COMMA, + ZEPHR_KEYCODE_MINUS, + ZEPHR_KEYCODE_PERIOD, + ZEPHR_KEYCODE_SLASH, + ZEPHR_KEYCODE_0, + ZEPHR_KEYCODE_1, + ZEPHR_KEYCODE_2, + ZEPHR_KEYCODE_3, + ZEPHR_KEYCODE_4, + ZEPHR_KEYCODE_5, + ZEPHR_KEYCODE_6, + ZEPHR_KEYCODE_7, + ZEPHR_KEYCODE_8, + ZEPHR_KEYCODE_9, + + [0x3b] = ZEPHR_KEYCODE_SEMICOLON, + [0x3d] = ZEPHR_KEYCODE_EQUALS, + + [0x5b] = ZEPHR_KEYCODE_LEFT_BRACKET, + ZEPHR_KEYCODE_BACKSLASH, + ZEPHR_KEYCODE_RIGHT_BRACKET, + [0x60] = ZEPHR_KEYCODE_GRAVE, + ZEPHR_KEYCODE_A, + ZEPHR_KEYCODE_B, + ZEPHR_KEYCODE_C, + ZEPHR_KEYCODE_D, + ZEPHR_KEYCODE_E, + ZEPHR_KEYCODE_F, + ZEPHR_KEYCODE_G, + ZEPHR_KEYCODE_H, + ZEPHR_KEYCODE_I, + ZEPHR_KEYCODE_J, + ZEPHR_KEYCODE_K, + ZEPHR_KEYCODE_L, + ZEPHR_KEYCODE_M, + ZEPHR_KEYCODE_N, + ZEPHR_KEYCODE_O, + ZEPHR_KEYCODE_P, + ZEPHR_KEYCODE_Q, + ZEPHR_KEYCODE_R, + ZEPHR_KEYCODE_S, + ZEPHR_KEYCODE_T, + ZEPHR_KEYCODE_U, + ZEPHR_KEYCODE_V, + ZEPHR_KEYCODE_W, + ZEPHR_KEYCODE_X, + ZEPHR_KEYCODE_Y, + ZEPHR_KEYCODE_Z, + + [0xff] = ZEPHR_KEYCODE_NULL, +}; + +/////////////////////////// +// +// X11 +// +/////////////////////////// + +ZephrKeycode x11_keysym_to_zephr_keycode_map(KeySym sym) { + if (sym >= 0xff00 && sym <= 0xffff) { + return zephr_x11_fn_keysym_to_zephr_keycode_map[sym - 0xff00]; + } else if (sym >= 0xfe00 && sym <= 0xfeff) { + return zephr_x11_fn_ex_keysym_to_zephr_keycode_map[sym - 0xfe00]; + } else if (sym >= 0x100 && sym <= 0x1ff) { + return ZEPHR_KEYCODE_NULL; + /* return zephr_x11_latin2_keysym_to_zephr_keycode_map[sym - 0x100]; */ + } else if (sym <= 0xff) { + return zephr_x11_latin1_keysym_to_zephr_keycode_map[sym]; + } else { + return ZEPHR_KEYCODE_NULL; + } +} + +// update internal keyboard map when the keyboard layout changes during runtime +/* void x11_keyboard_map_update() { */ +/* // reset the keycodes to map directly to the scancodes */ +/* for (int kc = 0; kc < ZEPHR_KEYCODE_COUNT; kc++) { */ +/* zephr_ctx.keyboard.scancode_to_keycode[kc] = kc; */ +/* zephr_ctx.keyboard.keycode_to_scancode[kc] = kc; */ +/* } */ + +/* XkbStateRec state; */ +/* XkbGetUpdatedMap(x11_display, XkbAllClientInfoMask, zephr_ctx.xkb); */ + +/* unsigned int group = 0; */ +/* if (XkbGetState(x11_display, XkbUseCoreKbd, &state) == Success) { */ +/* group = state.group; */ +/* } */ + +/* int first_keycode = zephr_ctx.xkb->min_key_code; */ +/* int last_keycode = zephr_ctx.xkb->max_key_code; */ +/* // we don't map any more keys in evdev after 236 */ +/* if (last_keycode > 236) { */ +/* last_keycode = 236; */ +/* } */ + +/* // some x11 magic: https://youtu.be/Y7yIwWIaD-Q?t=12993 */ +/* // last_keycode - first_keycode exclusive because the first keycode is a NULL keycode */ +/* // evdev scancodes are 8 less than x11 keycodes */ +/* XkbGetKeySyms(x11_display, first_keycode, last_keycode - first_keycode, zephr_ctx.xkb); */ +/* XkbClientMapRec *client_map = zephr_ctx.xkb->map; */ +/* for (int x11keycode = 8; x11keycode < last_keycode; x11keycode++) { */ +/* XkbSymMapRec *sym_map = &client_map->key_sym_map[x11keycode]; */ +/* int sym_start_idx = sym_map->offset; */ +/* // since we do not want any key modifiers, our shift is 0 */ +/* int shift = 0; */ +/* int sym_idx = sym_start_idx + (sym_map->width * group) + shift; */ +/* KeySym sym = client_map->syms[sym_idx]; */ + +/* ZephrScancode scancode = zephr_evdev_scancode_to_zephr_scancode_map[x11keycode]; */ +/* ZephrKeycode keycode = x11_keysym_to_zephr_keycode_map(sym); */ + +/* zephr_ctx.keyboard.scancode_to_keycode[scancode] = keycode; */ +/* zephr_ctx.keyboard.keycode_to_scancode[keycode] = scancode; */ +/* } */ +/* } */ + +void x11_resize_window() { + XWindowAttributes win_attrs; + XGetWindowAttributes(x11_display, x11_window, &win_attrs); + glViewport(0, 0, win_attrs.width, win_attrs.height); +} + +int x11_create_window(const char* title, int window_width, int window_height) { + x11_display = XOpenDisplay(NULL); + CORE_ASSERT(x11_display, "Cannot open x11 display connection\n"); + + int screen = DefaultScreen(x11_display); + Window root = RootWindow(x11_display, screen); + Visual *visual = DefaultVisual(x11_display, screen); + + x11_colormap = XCreateColormap(x11_display, root, visual, AllocNone); + + XSetWindowAttributes attributes; + attributes.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | + StructureNotifyMask | ButtonPressMask | ButtonReleaseMask; + attributes.colormap = x11_colormap; + + x11_window = XCreateWindow(x11_display, root, 0, 0, window_width, window_height, 0, + DefaultDepth(x11_display, screen), InputOutput, visual, + CWColormap | CWEventMask, &attributes); + + // Hints to the WM that the window is a dialog window which makes it a floating + // window in tiling WMs (only tested on DWM though) + // Of course this is only a hint and the WM can ignore it + Atom net_wm_window_type = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False); + Atom net_wm_window_type_dialog = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_DIALOG", False); + XChangeProperty(x11_display, x11_window, net_wm_window_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&net_wm_window_type_dialog, 1); + + Atom wm_delete_window = XInternAtom(x11_display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(x11_display, x11_window, &wm_delete_window, 1); + zephr_ctx.window_delete_atom = wm_delete_window; + + XMapWindow(x11_display, x11_window); + XStoreName(x11_display, x11_window, title); + + if (!x11_window) { + printf("[FATAL] Failed to create window\n"); + return 1; + } + + int glx_version = gladLoaderLoadGLX(x11_display, screen); + + if (!glx_version) { + printf("[FATAL] Failed to load GLX\n"); + return 1; + } + printf("[INFO] Loaded GLX %d.%d\n", + GLAD_VERSION_MAJOR(glx_version), GLAD_VERSION_MINOR(glx_version)); + + GLint visual_attributes[] = { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DEPTH_SIZE, 24, + GLX_DOUBLEBUFFER, 1, + GLX_SAMPLES, 4, // MSAA + None + }; + + int num_fbc = 0; + GLXFBConfig *fbc = + glXChooseFBConfig(x11_display, screen, visual_attributes, &num_fbc); + + GLint context_attributes[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 3, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + None + }; + + glx_context = glXCreateContextAttribsARB(x11_display, fbc[0], NULL, 1, + context_attributes); + + if (!glx_context) { + printf("[FATAL] Failed to create GLX context\n"); + return 1; + } + + glXMakeCurrent(x11_display, x11_window, glx_context); + + int gl_version = gladLoaderLoadGL(); + + if (!gl_version) { + printf("[FATAL] Failed to load GL\n"); + return 1; + } + printf("[INFO] Loaded GL %d.%d\n", + GLAD_VERSION_MAJOR(gl_version), GLAD_VERSION_MINOR(gl_version)); + + XWindowAttributes win_attrs; + XGetWindowAttributes(x11_display, x11_window, &win_attrs); + + glad_glXSwapIntervalEXT(x11_display, x11_window, 1); + // we enable blending for text + glEnable(GL_BLEND); + glEnable(GL_MULTISAMPLE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glViewport(0, 0, win_attrs.width, win_attrs.height); + x11_resize_window(); + + XFree(fbc); + + return 0; +} + +int x11_init(const char* title, int window_width, int window_height) { + int res = x11_create_window(title, window_width, window_height); + + /* // loads the XMODIFIERS environment variable to see what IME to use */ + /* XSetLocaleModifiers(""); */ + /* XIM xim = XOpenIM(x11_display, 0, 0, 0); */ + /* if(!xim){ */ + /* // fallback to internal input method */ + /* XSetLocaleModifiers("@im=none"); */ + /* xim = XOpenIM(x11_display, 0, 0, 0); */ + /* } */ + /* zephr_ctx.xim = xim; */ + + /* // XIC */ + /* // create an input context that is used by the whole window */ + /* // you are supposed to have one of these per text field, but this should do */ + /* x11_xic = XCreateIC(zephr_ctx.xim, */ + /* XNInputStyle, XIMPreeditNothing | XIMStatusNothing, */ + /* XNClientWindow, x11_window, XNFocusWindow, x11_window, */ + /* NULL); */ + + /* XSetICFocus(x11_xic); */ + + /* // TODO: do we need to flush?? */ + /* XFlush(x11_display); */ + + // XKB + /* { */ + /* XAutoRepeatOn(x11_display); */ + /* int major = 1, minor = 0; */ + /* CORE_ASSERT(XkbQueryExtension(x11_display, NULL, NULL, NULL, &major, &minor), "XKB not supported"); */ + + /* zephr_ctx.xkb = XkbGetMap(x11_display, XkbAllClientInfoMask, XkbUseCoreKbd); */ + + /* int repeat = 0; */ + /* XkbSetDetectableAutoRepeat(x11_display, True, &repeat); */ + + /* XkbSelectEvents(x11_display, XkbUseCoreKbd, XkbMapNotifyMask, XkbMapNotifyMask); */ + + /* x11_keyboard_map_update(); */ + /* } */ + + return res; +} + +void x11_close() { + glXMakeCurrent(x11_display, 0, 0); + glXDestroyContext(x11_display, glx_context); + + XDestroyWindow(x11_display, x11_window); + XFreeColormap(x11_display, x11_colormap); + XCloseDisplay(x11_display); + + gladLoaderUnloadGLX(); +} + +void x11_go_fullscreen() +{ + XEvent xev; + Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False); + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = x11_window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD + xev.xclient.data.l[1] = fullscreen; + xev.xclient.data.l[2] = 0; + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, + SubstructureNotifyMask | SubstructureRedirectMask, &xev); +} + +void x11_return_fullscreen() +{ + XEvent xev; + Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False); + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = x11_window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = 0; // _NET_WM_STATE_REMOVE + xev.xclient.data.l[1] = fullscreen; + xev.xclient.data.l[2] = 0; + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, + SubstructureNotifyMask | SubstructureRedirectMask, &xev); +} + +void x11_toggle_fullscreen(bool fullscreen) { + if (fullscreen) { + x11_return_fullscreen(); + } else { + x11_go_fullscreen(); + } +} + +void x11_get_screen_size(int *width, int *height) { + Screen *screen = DefaultScreenOfDisplay(x11_display); + + *width = screen->width; + *height = screen->height; +} + +void x11_make_window_non_resizable(int width, int height) { + XSizeHints *size_hints = XAllocSizeHints(); + size_hints->flags = PMinSize | PMaxSize; + size_hints->min_width = width; + size_hints->min_height = height; + size_hints->max_width = width; + size_hints->max_height = height; + XSetWMNormalHints(x11_display, x11_window, size_hints); + XFree(size_hints); +} + +//////////////////////////// +// +// Zephr +// +/////////////////////////// + + +u32 init_zephr(const char* font_path, const char* window_title, Size window_size) { int res = audio_init(); if (res != 0) { printf("[ERROR]: failed to initialize audio\n"); return 1; } - res = x11_init(&display, &window, window_title, window_size.width, window_size.height); + res = x11_init(window_title, window_size.width, window_size.height); if (res < 0) return 1; res = init_ui(font_path, (Size){ .width = window_size.width, .height = window_size.height }); @@ -30,74 +723,274 @@ int init_zephr(const char* font_path, const char* window_title, Size window_size return 1; } - if (clear_color) { - zephr_context.clear_color = *clear_color; - } else { - zephr_context.clear_color = (Color){0.0, 0.0, 0.0, 1.0}; - } - - x11_get_screen_size(display, &zephr_context.screen_size.width, &zephr_context.screen_size.height); + x11_get_screen_size(&zephr_ctx.screen_size.width, &zephr_ctx.screen_size.height); start_internal_timer(); return 0; } void deinit_zephr() { - x11_close(&display, &window); + x11_close(); audio_close(); } bool zephr_should_quit() { - // TODO: input events - // TODO: window events - - while (XPending(display)) { - XEvent xev; - XNextEvent(display, &xev); - - if (xev.type == ConfigureNotify) { - XConfigureEvent xce = xev.xconfigure; - - if (xce.width != zephr_context.window.size.width || xce.height != zephr_context.window.size.height) { - zephr_context.window.size = (Size){ .width = xce.width, .height = xce.height }; - zephr_context.projection = orthographic_projection_2d(0.f, xce.width, xce.height, 0.f); - x11_resize_window(display, window); - } - } - } - - float r = zephr_context.clear_color.r; - float g = zephr_context.clear_color.g; - float b = zephr_context.clear_color.b; - float a = zephr_context.clear_color.a; - glClearColor(r, g, b, a); + glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); audio_update(); - return zephr_context.should_quit; + return zephr_ctx.should_quit; } // This MUST be called at the end of the frame void zephr_swap_buffers() { - glXSwapBuffers(display, window); + glXSwapBuffers(x11_display, x11_window); } Size zephr_get_window_size() { - return zephr_context.window.size; + return zephr_ctx.window.size; } // This MUST be called after calling init_zephr() void zephr_make_window_non_resizable() { - x11_make_window_non_resizable(display, window, zephr_context.window.size.width, zephr_context.window.size.height); + x11_make_window_non_resizable(zephr_ctx.window.size.width, zephr_ctx.window.size.height); } void zephr_toggle_fullscreen() { - x11_toggle_fullscreen(zephr_context.window.is_fullscreen, display, window); + x11_toggle_fullscreen(zephr_ctx.window.is_fullscreen); - zephr_context.window.is_fullscreen = !zephr_context.window.is_fullscreen; + zephr_ctx.window.is_fullscreen = !zephr_ctx.window.is_fullscreen; } void zephr_quit() { - zephr_context.should_quit = true; + zephr_ctx.should_quit = true; } + +ZephrKeyMod zephr_x11_mods_to_zephr_mods(XKeyEvent xkey) { + ZephrKeyMod zephr_mods = 0; + + if (xkey.state & ShiftMask) { + if (XLookupKeysym(&xkey, 0) == XK_Shift_L) { + zephr_mods |= ZEPHR_KEY_MOD_LEFT_SHIFT; + } else if (XLookupKeysym(&xkey, 0) == XK_Shift_R) { + zephr_mods |= ZEPHR_KEY_MOD_RIGHT_SHIFT; + } else { + zephr_mods |= ZEPHR_KEY_MOD_SHIFT; + } + } + if (xkey.state & ControlMask){ + if (XLookupKeysym(&xkey, 0) == XK_Control_L) { + zephr_mods |= ZEPHR_KEY_MOD_LEFT_CTRL; + } else if (XLookupKeysym(&xkey, 0) == XK_Control_R) { + zephr_mods |= ZEPHR_KEY_MOD_RIGHT_CTRL; + } else { + zephr_mods |= ZEPHR_KEY_MOD_CTRL; + } + } + if (xkey.state & Mod1Mask) { + if (XLookupKeysym(&xkey, 0) == XK_Alt_L) { + zephr_mods |= ZEPHR_KEY_MOD_LEFT_ALT; + } else if (XLookupKeysym(&xkey, 0) == XK_Alt_R) { + zephr_mods |= ZEPHR_KEY_MOD_RIGHT_ALT; + } else { + zephr_mods |= ZEPHR_KEY_MOD_ALT; + } + } + if (xkey.state & Mod4Mask) { + if (XLookupKeysym(&xkey, 0) == XK_Super_L) { + zephr_mods |= ZEPHR_KEY_MOD_LEFT_META; + } else if (XLookupKeysym(&xkey, 0) == XK_Super_R) { + zephr_mods |= ZEPHR_KEY_MOD_RIGHT_META; + } else { + zephr_mods |= ZEPHR_KEY_MOD_META; + } + } + if (xkey.state & LockMask) { + zephr_mods |= ZEPHR_KEY_MOD_CAPS_LOCK; + } + if (xkey.state & Mod2Mask) { + zephr_mods |= ZEPHR_KEY_MOD_NUM_LOCK; + } + + return zephr_mods; +} + +bool zephr_iter_events(ZephrEvent *event_out) { + XEvent xev; + + while (XPending(x11_display)) { + XNextEvent(x11_display, &xev); + + if (xev.type == ConfigureNotify) { + XConfigureEvent xce = xev.xconfigure; + + if (xce.width != zephr_ctx.window.size.width || xce.height != zephr_ctx.window.size.height) { + zephr_ctx.window.size = (Size){ .width = xce.width, .height = xce.height }; + zephr_ctx.projection = orthographic_projection_2d(0.f, xce.width, xce.height, 0.f); + x11_resize_window(); + + event_out->type = ZEPHR_EVENT_WINDOW_RESIZED; + event_out->window.width = xce.width; + event_out->window.height = xce.height; + + return true; + } + } else if (xev.type == DestroyNotify) { + // window destroy event + event_out->type = ZEPHR_EVENT_WINDOW_CLOSED; + return true; + } else if (xev.type == ClientMessage) { + // window close event + if ((Atom)xev.xclient.data.l[0] == zephr_ctx.window_delete_atom) { + event_out->type = ZEPHR_EVENT_WINDOW_CLOSED; + return true; + } + } else if (xev.type == KeyPress) { + XKeyEvent xke = xev.xkey; + + u32 evdev_keycode = xke.keycode - 8; + ZephrScancode scancode = zephr_evdev_scancode_to_zephr_scancode_map[evdev_keycode]; + + event_out->type = ZEPHR_EVENT_KEY_PRESSED; + event_out->key.code = scancode; + event_out->key.mods = zephr_x11_mods_to_zephr_mods(xke); + + /* { */ + /* // remove the control modifier as it causes control codes to be returned */ + /* xev.xkey.state &= ~ControlMask; */ + + /* char string[4] = {0}; */ + /* KeySym keysym = 0; */ + /* u8 string_length = Xutf8LookupString(x11_xic, &xev.xkey, string, sizeof(string), &keysym, NULL); */ + + /* // do not send any keys like ctrl, shift, function, arrow, escape, return, backspace. */ + /* // instead, send regular key events. */ + /* if (string_length && !(keysym >= 0xfd00 && keysym <= 0xffff)) { */ + /* os_event_queue_virt_key_input_utf8(string, string_length); */ + /* } */ + /* } */ + + /* // an X11 keycode is conceptually the same as our keycode. */ + /* // they are both used to represent a physical key. */ + /* // map evdev enumeration to our keycode */ + /* u32 evdev_keycode = xev.xkey.keycode - 8; */ + /* ZephrKeycode keycode; */ + /* if (evdev_keycode < CORE_ARRAY_COUNT(os_linux_keyboard_evdev_keycode_to_keycode_map)) { */ + /* keycode = os_linux_keyboard_evdev_keycode_to_keycode_map[evdev_keycode]; */ + /* } else { */ + /* keycode = ZEPHR_KEYCODE_NULL; */ + /* } */ + /* os_event_queue_virt_key_changed(xev.type == KeyPress, keycode); */ + + return true; + } else if (xev.type == KeyRelease) { + XKeyEvent xke = xev.xkey; + + unsigned int evdev_keycode = xke.keycode - 8; + ZephrScancode scancode = zephr_evdev_scancode_to_zephr_scancode_map[evdev_keycode]; + + event_out->type = ZEPHR_EVENT_KEY_RELEASED; + event_out->key.code = scancode; + event_out->key.mods = zephr_x11_mods_to_zephr_mods(xke); + + return true; + + } else if (xev.type == ButtonPress) { + event_out->type = ZEPHR_EVENT_MOUSE_BUTTON_PRESSED; + event_out->mouse.position = (Vec2){ .x = xev.xbutton.x, .y = xev.xbutton.y }; + + switch (xev.xbutton.button) { + case Button1: + event_out->mouse.button = ZEPHR_MOUSE_BUTTON_LEFT; + break; + case Button2: + event_out->mouse.button = ZEPHR_MOUSE_BUTTON_MIDDLE; + break; + case Button3: + event_out->mouse.button = ZEPHR_MOUSE_BUTTON_RIGHT; + break; + case Button4: + event_out->type = ZEPHR_EVENT_MOUSE_SCROLL; + event_out->mouse.scroll_direction = ZEPHR_MOUSE_SCROLL_UP; + break; + case Button5: + event_out->type = ZEPHR_EVENT_MOUSE_SCROLL; + event_out->mouse.scroll_direction = ZEPHR_MOUSE_SCROLL_DOWN; + break; + case 8: // Back + event_out->mouse.button = ZEPHR_MOUSE_BUTTON_BACK; + break; + case 9: // Forward + event_out->mouse.button = ZEPHR_MOUSE_BUTTON_FORWARD; + break; + default: + printf("[WARN] Unknown mouse button pressed: %d\n", xev.xbutton.button); + break; + } + + return true; + } else if (xev.type == ButtonRelease) { + event_out->type = ZEPHR_EVENT_MOUSE_BUTTON_RELEASED; + event_out->mouse.position = (Vec2){ .x = xev.xbutton.x, .y = xev.xbutton.y }; + + switch (xev.xbutton.button) { + case Button1: + event_out->mouse.button = ZEPHR_MOUSE_BUTTON_LEFT; + break; + case Button2: + event_out->mouse.button = ZEPHR_MOUSE_BUTTON_MIDDLE; + break; + case Button3: + event_out->mouse.button = ZEPHR_MOUSE_BUTTON_RIGHT; + break; + } + + return true; + } else if (xev.type == MappingNotify) { + // input device mapping changed + if (xev.xmapping.request != MappingKeyboard) { + break; + } + XRefreshKeyboardMapping(&xev.xmapping); + /* x11_keyboard_map_update(); */ + break; + } + } + + return false; +} + +/* bool zephr_keyboard_scancode_is_pressed(ZephrScancode scancode) { */ +/* return core_bitset_is_set(zephr_ctx.keyboard.scancode_is_pressed_bitset, scancode); */ +/* } */ + +/* bool zephr_keyboard_scancode_has_been_pressed(ZephrScancode scancode) { */ +/* return core_bitset_is_set(zephr_ctx.keyboard.scancode_has_been_pressed_bitset, scancode); */ +/* } */ + +/* bool zephr_keyboard_scancode_has_been_released(ZephrScancode scancode) { */ +/* return core_bitset_is_set(zephr_ctx.keyboard.scancode_has_been_released_bitset, scancode); */ +/* } */ + + +/* bool zephr_keyboard_keycode_is_pressed(ZephrKeycode keycode) { */ +/* return core_bitset_is_set(zephr_ctx.keyboard.keycode_is_pressed_bitset, keycode); */ +/* } */ + +/* bool zephr_keyboard_keycode_has_been_pressed(ZephrKeycode keycode) { */ +/* return core_bitset_is_set(zephr_ctx.keyboard.keycode_has_been_pressed_bitset, keycode); */ +/* } */ + +/* bool zephr_keyboard_keycode_has_been_released(ZephrKeycode keycode) { */ +/* return core_bitset_is_set(zephr_ctx.keyboard.keycode_has_been_released_bitset, keycode); */ +/* } */ + + +/* ZephrScancode zephr_keyboard_keycode_to_scancode(ZephrKeycode keycode) { */ +/* return zephr_ctx.keyboard.keycode_to_scancode[keycode]; */ +/* } */ + +/* ZephrKeycode zephr_keyboard_scancode_to_keycode(ZephrScancode scancode) { */ +/* return zephr_ctx.keyboard.scancode_to_keycode[scancode]; */ +/* } */ diff --git a/zephr.h b/zephr.h index 361ad31..0e8d98d 100644 --- a/zephr.h +++ b/zephr.h @@ -1,29 +1,375 @@ #pragma once #include +#include + +#include #include "text.h" +#include "zephr_math.h" -typedef struct ZephWindow { +typedef struct ZephrWindow { Size size; bool is_fullscreen; -} ZephWindow; +} ZephrWindow; + +typedef enum ZephrEventType { + ZEPHR_EVENT_UNKNOWN, + ZEPHR_EVENT_KEY_PRESSED, + ZEPHR_EVENT_KEY_RELEASED, + ZEPHR_EVENT_MOUSE_BUTTON_PRESSED, + ZEPHR_EVENT_MOUSE_BUTTON_RELEASED, + ZEPHR_EVENT_MOUSE_SCROLL, + ZEPHR_EVENT_WINDOW_RESIZED, + ZEPHR_EVENT_WINDOW_CLOSED +} ZephrEventType; + +typedef enum ZephrMouseScrollDirection { + ZEPHR_MOUSE_SCROLL_UP, + ZEPHR_MOUSE_SCROLL_DOWN, +} ZephrMouseScrollDirection; + +typedef enum ZephrMouseButton { + ZEPHR_MOUSE_BUTTON_LEFT, + ZEPHR_MOUSE_BUTTON_RIGHT, + ZEPHR_MOUSE_BUTTON_MIDDLE, + ZEPHR_MOUSE_BUTTON_BACK, + ZEPHR_MOUSE_BUTTON_FORWARD, + ZEPHR_MOUSE_BUTTON_3, + ZEPHR_MOUSE_BUTTON_4, + ZEPHR_MOUSE_BUTTON_5, + ZEPHR_MOUSE_BUTTON_6, + ZEPHR_MOUSE_BUTTON_7, +} ZephrMouseButton; + + +typedef u16 ZephrScancode; +typedef u16 ZephrKeycode; + +// these are actually scancodes as documented by the USB HID Usage Tables +// https://usb.org/sites/default/files/documents/hut1_12v2.pdf +enum { + ZEPHR_KEYCODE_NULL = 0, + + ZEPHR_KEYCODE_A = 4, + ZEPHR_KEYCODE_B = 5, + ZEPHR_KEYCODE_C = 6, + ZEPHR_KEYCODE_D = 7, + ZEPHR_KEYCODE_E = 8, + ZEPHR_KEYCODE_F = 9, + ZEPHR_KEYCODE_G = 10, + ZEPHR_KEYCODE_H = 11, + ZEPHR_KEYCODE_I = 12, + ZEPHR_KEYCODE_J = 13, + ZEPHR_KEYCODE_K = 14, + ZEPHR_KEYCODE_L = 15, + ZEPHR_KEYCODE_M = 16, + ZEPHR_KEYCODE_N = 17, + ZEPHR_KEYCODE_O = 18, + ZEPHR_KEYCODE_P = 19, + ZEPHR_KEYCODE_Q = 20, + ZEPHR_KEYCODE_R = 21, + ZEPHR_KEYCODE_S = 22, + ZEPHR_KEYCODE_T = 23, + ZEPHR_KEYCODE_U = 24, + ZEPHR_KEYCODE_V = 25, + ZEPHR_KEYCODE_W = 26, + ZEPHR_KEYCODE_X = 27, + ZEPHR_KEYCODE_Y = 28, + ZEPHR_KEYCODE_Z = 29, + + ZEPHR_KEYCODE_1 = 30, + ZEPHR_KEYCODE_2 = 31, + ZEPHR_KEYCODE_3 = 32, + ZEPHR_KEYCODE_4 = 33, + ZEPHR_KEYCODE_5 = 34, + ZEPHR_KEYCODE_6 = 35, + ZEPHR_KEYCODE_7 = 36, + ZEPHR_KEYCODE_8 = 37, + ZEPHR_KEYCODE_9 = 38, + ZEPHR_KEYCODE_0 = 39, + + ZEPHR_KEYCODE_ENTER = 40, + ZEPHR_KEYCODE_ESCAPE = 41, + ZEPHR_KEYCODE_BACKSPACE = 42, + ZEPHR_KEYCODE_TAB = 43, + ZEPHR_KEYCODE_SPACE = 44, + + ZEPHR_KEYCODE_MINUS = 45, + ZEPHR_KEYCODE_EQUALS = 46, + ZEPHR_KEYCODE_LEFT_BRACKET = 47, + ZEPHR_KEYCODE_RIGHT_BRACKET = 48, + ZEPHR_KEYCODE_BACKSLASH = 49, + ZEPHR_KEYCODE_NON_US_HASH = 50, + ZEPHR_KEYCODE_SEMICOLON = 51, + ZEPHR_KEYCODE_APOSTROPHE = 52, + ZEPHR_KEYCODE_GRAVE = 53, + ZEPHR_KEYCODE_COMMA = 54, + ZEPHR_KEYCODE_PERIOD = 55, + ZEPHR_KEYCODE_SLASH = 56, + + ZEPHR_KEYCODE_CAPS_LOCK = 57, + + ZEPHR_KEYCODE_F1 = 58, + ZEPHR_KEYCODE_F2 = 59, + ZEPHR_KEYCODE_F3 = 60, + ZEPHR_KEYCODE_F4 = 61, + ZEPHR_KEYCODE_F5 = 62, + ZEPHR_KEYCODE_F6 = 63, + ZEPHR_KEYCODE_F7 = 64, + ZEPHR_KEYCODE_F8 = 65, + ZEPHR_KEYCODE_F9 = 66, + ZEPHR_KEYCODE_F10 = 67, + ZEPHR_KEYCODE_F11 = 68, + ZEPHR_KEYCODE_F12 = 69, + + ZEPHR_KEYCODE_PRINT_SCREEN = 70, + ZEPHR_KEYCODE_SCROLL_LOCK = 71, + ZEPHR_KEYCODE_PAUSE = 72, + ZEPHR_KEYCODE_INSERT = 73, + ZEPHR_KEYCODE_HOME = 74, + ZEPHR_KEYCODE_PAGE_UP = 75, + ZEPHR_KEYCODE_DELETE = 76, + ZEPHR_KEYCODE_END = 77, + ZEPHR_KEYCODE_PAGE_DOWN = 78, + ZEPHR_KEYCODE_RIGHT = 79, + ZEPHR_KEYCODE_LEFT = 80, + ZEPHR_KEYCODE_DOWN = 81, + ZEPHR_KEYCODE_UP = 82, + + ZEPHR_KEYCODE_NUM_LOCK_OR_CLEAR = 83, + ZEPHR_KEYCODE_KP_DIVIDE = 84, + ZEPHR_KEYCODE_KP_MULTIPLY = 85, + ZEPHR_KEYCODE_KP_MINUS = 86, + ZEPHR_KEYCODE_KP_PLUS = 87, + ZEPHR_KEYCODE_KP_ENTER = 88, + ZEPHR_KEYCODE_KP_1 = 89, + ZEPHR_KEYCODE_KP_2 = 90, + ZEPHR_KEYCODE_KP_3 = 91, + ZEPHR_KEYCODE_KP_4 = 92, + ZEPHR_KEYCODE_KP_5 = 93, + ZEPHR_KEYCODE_KP_6 = 94, + ZEPHR_KEYCODE_KP_7 = 95, + ZEPHR_KEYCODE_KP_8 = 96, + ZEPHR_KEYCODE_KP_9 = 97, + ZEPHR_KEYCODE_KP_0 = 98, + ZEPHR_KEYCODE_KP_PERIOD = 99, + + ZEPHR_KEYCODE_NON_US_BACKSLASH = 100, + ZEPHR_KEYCODE_APPLICATION = 101, + ZEPHR_KEYCODE_POWER = 102, + ZEPHR_KEYCODE_KP_EQUALS = 103, + ZEPHR_KEYCODE_F13 = 104, + ZEPHR_KEYCODE_F14 = 105, + ZEPHR_KEYCODE_F15 = 106, + ZEPHR_KEYCODE_F16 = 107, + ZEPHR_KEYCODE_F17 = 108, + ZEPHR_KEYCODE_F18 = 109, + ZEPHR_KEYCODE_F19 = 110, + ZEPHR_KEYCODE_F20 = 111, + ZEPHR_KEYCODE_F21 = 112, + ZEPHR_KEYCODE_F22 = 113, + ZEPHR_KEYCODE_F23 = 114, + ZEPHR_KEYCODE_F24 = 115, + ZEPHR_KEYCODE_EXECUTE = 116, + ZEPHR_KEYCODE_HELP = 117, + ZEPHR_KEYCODE_MENU = 118, + ZEPHR_KEYCODE_SELECT = 119, + ZEPHR_KEYCODE_STOP = 120, + ZEPHR_KEYCODE_AGAIN = 121, + ZEPHR_KEYCODE_UNDO = 122, + ZEPHR_KEYCODE_CUT = 123, + ZEPHR_KEYCODE_COPY = 124, + ZEPHR_KEYCODE_PASTE = 125, + ZEPHR_KEYCODE_FIND = 126, + ZEPHR_KEYCODE_MUTE = 127, + ZEPHR_KEYCODE_VOLUME_UP = 128, + ZEPHR_KEYCODE_VOLUME_DOWN = 129, + ZEPHR_KEYCODE_KP_COMMA = 133, + ZEPHR_KEYCODE_KP_EQUALSAS400 = 134, + + ZEPHR_KEYCODE_INTERNATIONAL1 = 135, + ZEPHR_KEYCODE_INTERNATIONAL2 = 136, + ZEPHR_KEYCODE_INTERNATIONAL3 = 137, + ZEPHR_KEYCODE_INTERNATIONAL4 = 138, + ZEPHR_KEYCODE_INTERNATIONAL5 = 139, + ZEPHR_KEYCODE_INTERNATIONAL6 = 140, + ZEPHR_KEYCODE_INTERNATIONAL7 = 141, + ZEPHR_KEYCODE_INTERNATIONAL8 = 142, + ZEPHR_KEYCODE_INTERNATIONAL9 = 143, + ZEPHR_KEYCODE_LANG1 = 144, + ZEPHR_KEYCODE_LANG2 = 145, + ZEPHR_KEYCODE_LANG3 = 146, + ZEPHR_KEYCODE_LANG4 = 147, + ZEPHR_KEYCODE_LANG5 = 148, + ZEPHR_KEYCODE_LANG6 = 149, + ZEPHR_KEYCODE_LANG7 = 150, + ZEPHR_KEYCODE_LANG8 = 151, + ZEPHR_KEYCODE_LANG9 = 152, + + ZEPHR_KEYCODE_ALT_ERASE = 153, + ZEPHR_KEYCODE_SYSREQ = 154, + ZEPHR_KEYCODE_CANCEL = 155, + ZEPHR_KEYCODE_CLEAR = 156, + ZEPHR_KEYCODE_PRIOR = 157, + ZEPHR_KEYCODE_ENTER_2 = 158, + ZEPHR_KEYCODE_SEPARATOR = 159, + ZEPHR_KEYCODE_OUT = 160, + ZEPHR_KEYCODE_OPER = 161, + ZEPHR_KEYCODE_CLEARAGAIN = 162, + ZEPHR_KEYCODE_CRSEL = 163, + ZEPHR_KEYCODE_EXSEL = 164, + + ZEPHR_KEYCODE_KP_00 = 176, + ZEPHR_KEYCODE_KP_000 = 177, + ZEPHR_KEYCODE_THOUSANDSSEPARATOR = 178, + ZEPHR_KEYCODE_DECIMALSEPARATOR = 179, + ZEPHR_KEYCODE_CURRENCYUNIT = 180, + ZEPHR_KEYCODE_CURRENCYSUBUNIT = 181, + ZEPHR_KEYCODE_KP_LEFT_PAREN = 182, + ZEPHR_KEYCODE_KP_RIGHT_PAREN = 183, + ZEPHR_KEYCODE_KP_LEFT_BRACE = 184, + ZEPHR_KEYCODE_KP_RIGHT_BRACE = 185, + ZEPHR_KEYCODE_KP_TAB = 186, + ZEPHR_KEYCODE_KP_BACKSPACE = 187, + ZEPHR_KEYCODE_KP_A = 188, + ZEPHR_KEYCODE_KP_B = 189, + ZEPHR_KEYCODE_KP_C = 190, + ZEPHR_KEYCODE_KP_D = 191, + ZEPHR_KEYCODE_KP_E = 192, + ZEPHR_KEYCODE_KP_F = 193, + ZEPHR_KEYCODE_KP_XOR = 194, + ZEPHR_KEYCODE_KP_POWER = 195, + ZEPHR_KEYCODE_KP_PERCENT = 196, + ZEPHR_KEYCODE_KP_LESS = 197, + ZEPHR_KEYCODE_KP_GREATER = 198, + ZEPHR_KEYCODE_KP_AMPERSAND = 199, + ZEPHR_KEYCODE_KP_DBLAMPERSAND = 200, + ZEPHR_KEYCODE_KP_VERTICALBAR = 201, + ZEPHR_KEYCODE_KP_DBLVERTICALBAR = 202, + ZEPHR_KEYCODE_KP_COLON = 203, + ZEPHR_KEYCODE_KP_HASH = 204, + ZEPHR_KEYCODE_KP_SPACE = 205, + ZEPHR_KEYCODE_KP_AT = 206, + ZEPHR_KEYCODE_KP_EXCLAM = 207, + ZEPHR_KEYCODE_KP_MEMSTORE = 208, + ZEPHR_KEYCODE_KP_MEMRECALL = 209, + ZEPHR_KEYCODE_KP_MEMCLEAR = 210, + ZEPHR_KEYCODE_KP_MEMADD = 211, + ZEPHR_KEYCODE_KP_MEMSUBTRACT = 212, + ZEPHR_KEYCODE_KP_MEMMULTIPLY = 213, + ZEPHR_KEYCODE_KP_MEMDIVIDE = 214, + ZEPHR_KEYCODE_KP_PLUS_MINUS = 215, + ZEPHR_KEYCODE_KP_CLEAR = 216, + ZEPHR_KEYCODE_KP_CLEARENTRY = 217, + ZEPHR_KEYCODE_KP_BINARY = 218, + ZEPHR_KEYCODE_KP_OCTAL = 219, + ZEPHR_KEYCODE_KP_DECIMAL = 220, + ZEPHR_KEYCODE_KP_HEXADECIMAL = 221, + + ZEPHR_KEYCODE_LEFT_CTRL = 224, + ZEPHR_KEYCODE_LEFT_SHIFT = 225, + ZEPHR_KEYCODE_LEFT_ALT = 226, + ZEPHR_KEYCODE_LEFT_META = 227, + ZEPHR_KEYCODE_RIGHT_CTRL = 228, + ZEPHR_KEYCODE_RIGHT_SHIFT = 229, + ZEPHR_KEYCODE_RIGHT_ALT = 230, + ZEPHR_KEYCODE_RIGHT_META = 231, + + /** Not a key. Marks the number of scancodes. */ + ZEPHR_KEYCODE_COUNT = 512, +}; + +typedef enum ZephrKeyMod { + ZEPHR_KEY_MOD_NONE = 0x0000, + + ZEPHR_KEY_MOD_LEFT_SHIFT = 0x0001, + ZEPHR_KEY_MOD_RIGHT_SHIFT = 0x0002, + ZEPHR_KEY_MOD_SHIFT = 0x0003, + + ZEPHR_KEY_MOD_LEFT_CTRL = 0x0040, + ZEPHR_KEY_MOD_RIGHT_CTRL = 0x0080, + ZEPHR_KEY_MOD_CTRL = 0x00C0, + + ZEPHR_KEY_MOD_LEFT_ALT = 0x0100, + ZEPHR_KEY_MOD_RIGHT_ALT = 0x0200, + ZEPHR_KEY_MOD_ALT = 0x0300, + + ZEPHR_KEY_MOD_LEFT_META = 0x0400, + ZEPHR_KEY_MOD_RIGHT_META = 0x0800, + ZEPHR_KEY_MOD_META = 0x0C00, + + ZEPHR_KEY_MOD_CAPS_LOCK = 0x1000, + ZEPHR_KEY_MOD_NUM_LOCK = 0x2000, +} ZephrKeyMod; + +typedef struct ZephrKeyboard { + ZephrKeycode scancode_to_keycode[ZEPHR_KEYCODE_COUNT]; + ZephrScancode keycode_to_scancode[ZEPHR_KEYCODE_COUNT]; + /* u64 scancode_is_pressed_bitset[CORE_DIV_ROUND_UP(ZEPHR_KEYCODE_COUNT, 64)]; */ + /* u64 scancode_has_been_pressed_bitset[CORE_DIV_ROUND_UP(ZEPHR_KEYCODE_COUNT, 64)]; */ + /* u64 scancode_has_been_released_bitset[CORE_DIV_ROUND_UP(ZEPHR_KEYCODE_COUNT, 64)]; */ + /* u64 keycode_is_pressed_bitset[CORE_DIV_ROUND_UP(ZEPHR_KEYCODE_COUNT, 64)]; */ + /* u64 keycode_has_been_pressed_bitset[CORE_DIV_ROUND_UP(ZEPHR_KEYCODE_COUNT, 64)]; */ + /* u64 keycode_has_been_released_bitset[CORE_DIV_ROUND_UP(ZEPHR_KEYCODE_COUNT, 64)]; */ +} ZephrKeyboard; + +typedef struct ZephrEvent { + ZephrEventType type; + + union { + struct { + bool is_pressed; + bool is_repeat; + ZephrScancode scancode; + ZephrKeycode code; + ZephrKeyMod mods; + } key; + + struct { + ZephrMouseButton button; + Vec2 position; + ZephrMouseScrollDirection scroll_direction; + } mouse; + + struct { + u32 width; + u32 height; + } window; + }; +} ZephrEvent; +/* CORE_DEFINE_STACK(ZephrEvent); */ typedef struct Context { + Atom window_delete_atom; bool should_quit; - Color clear_color; Size screen_size; - ZephWindow window; - ZephFont font; + ZephrWindow window; + ZephrFont font; + /* ZephrKeyboard keyboard; */ + /* XkbDescPtr xkb; */ + /* XIM xim; */ + /* CoreStack(ZephrEvent) event_queue; */ + /* u32 event_queue_cursor; */ Matrix4x4 projection; } Context; -int init_zephr(const char* font_path, const char* window_title, Size window_size, Color *clear_color); -void deinit_zephr(); -bool zephr_should_quit(); -void zephr_swap_buffers(); -Size zephr_get_window_size(); -void zephr_make_window_non_resizable(); -void zephr_toggle_fullscreen(); -void zephr_quit(); +u32 init_zephr(const char* font_path, const char* window_title, Size window_size); +void deinit_zephr(void); +bool zephr_should_quit(void); +void zephr_swap_buffers(void); +Size zephr_get_window_size(void); +void zephr_make_window_non_resizable(void); +void zephr_toggle_fullscreen(void); +void zephr_quit(void); +bool zephr_iter_events(ZephrEvent *event_out); + +/* bool zephr_keyboard_keycode_is_pressed(ZephrKeycode keycode); */ +/* bool zephr_keyboard_keycode_has_been_pressed(ZephrKeycode keycode); */ +/* bool zephr_keyboard_keycode_has_been_released(ZephrKeycode keycode); */ + +/* ZephrScancode zephr_keyboard_keycode_to_scancode(ZephrKeycode keycode); */ +/* ZephrKeycode zephr_keyboard_scancode_to_keycode(ZephrScancode scancode); */ + +extern Context zephr_ctx; diff --git a/zephr_math.c b/zephr_math.c index b92d8c7..740f8bc 100644 --- a/zephr_math.c +++ b/zephr_math.c @@ -2,26 +2,8 @@ #include "zephr_math.h" -int max(int a, int b) { - return a > b ? a : b; -} - -float maxf(float a, float b) { - return a > b ? a : b; -} - -float clampf(float value, float min, float max) { - if (value < min) { - return min; - } else if (value > max) { - return max; - } - - return value; -} - float to_radians(float degrees) { - return degrees * (M_PI / 180.f); + return degrees * ((float)M_PI / 180.f); } Matrix4x4 orthographic_projection_2d(float left, float right, float bottom, float top) { @@ -75,10 +57,10 @@ void apply_scale(Matrix4x4 *matrix, Sizef scale) { void apply_rotation(Matrix4x4 *model, float angle) { Matrix4x4 temp = identity(); - temp.m[0][0] = cos(angle); - temp.m[0][1] = sin(angle); - temp.m[1][0] = -sin(angle); - temp.m[1][1] = cos(angle); + temp.m[0][0] = (float)cos(angle); + temp.m[0][1] = (float)sin(angle); + temp.m[1][0] = -(float)sin(angle); + temp.m[1][1] = (float)cos(angle); mult_4x4(model, &temp); } diff --git a/zephr_math.h b/zephr_math.h index 118faaa..5f69654 100644 --- a/zephr_math.h +++ b/zephr_math.h @@ -38,9 +38,6 @@ typedef struct Color { float a; } Color; -int max(int a, int b); -float maxf(float a, float b); -float clampf(float value, float min, float max); float to_radians(float degrees); Matrix4x4 identity(); void mult_4x4(Matrix4x4 *multiplicant, Matrix4x4 *multiplier);