finish refactor

This commit is contained in:
2023-12-11 22:42:19 +02:00
parent 9e25f16b5a
commit 8542a7cb14
21 changed files with 1622 additions and 478 deletions
+2 -2
View File
@@ -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
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

+33
View File
@@ -0,0 +1,33 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#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();
}
+126
View File
@@ -0,0 +1,126 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#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))); */
/* } */
+25 -26
View File
@@ -4,9 +4,7 @@
#include <math.h>
#include <string.h>
#include <glad/gl.h>
#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);
+2 -3
View File
@@ -2,9 +2,8 @@
#include <stdbool.h>
#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);
+63 -58
View File
@@ -1,34 +1,39 @@
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <glad/glx.h>
#include <stb_image.h>
#include <X11/Xlib.h>
#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 <path_to_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,31 +130,30 @@ 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);
}
} 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);
default:
case ZEPHR_EVENT_WINDOW_RESIZED:
case ZEPHR_EVENT_KEY_RELEASED:
break;
}
}
}
// 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);
+5 -2
View File
@@ -3,6 +3,7 @@
#include <glad/gl.h>
#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);
}
+26 -28
View File
@@ -4,16 +4,14 @@
#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype/ftmm.h>
#include <glad/glx.h>
#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);
+5 -3
View File
@@ -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);
+5 -2
View File
@@ -1,3 +1,6 @@
#include <sys/time.h>
#include <stdlib.h>
#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;
}
+4 -6
View File
@@ -1,8 +1,6 @@
#pragma once
#include <stdbool.h>
#include <stdio.h>
#include <sys/time.h>
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);
-33
View File
@@ -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
+22 -28
View File
@@ -1,23 +1,17 @@
#include <stdio.h>
#include <math.h>
#include <X11/Xlib.h>
#include <glad/glx.h>
#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 };
-3
View File
@@ -1,8 +1,5 @@
#pragma once
#include <stdbool.h>
#include <stdlib.h>
#include "zephr_math.h"
typedef enum Alignment {
-186
View File
@@ -1,186 +0,0 @@
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xatom.h>
#include <glad/glx.h>
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);
}
-12
View File
@@ -1,12 +0,0 @@
#pragma once
#include <stdbool.h>
#include <X11/Xlib.h>
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);
+940 -47
View File
File diff suppressed because it is too large Load Diff
+359 -13
View File
@@ -1,29 +1,375 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <X11/XKBlib.h>
#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;
+5 -23
View File
@@ -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);
}
-3
View File
@@ -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);