diff --git a/build_file.c b/build_file.c index 20266bc..f31570a 100644 --- a/build_file.c +++ b/build_file.c @@ -9,16 +9,51 @@ #include "src/app/app.meta.c" +void list_files_recursive(sb8_t *sb, s8_t path) { + for (OS_FileIter iter = OS_IterateFiles(&Perm, path); OS_IsValid(iter); OS_Advance(&iter)) { + if (iter.is_directory) { + list_files_recursive(sb, iter.absolute_path); + } else { + sb8_append(sb, iter.absolute_path); + } + } +} + int main(int argc, char **argv) { int ok = 0; ma_arena_t *arena = ma_create(ma_default_reserve_size); meta_app(arena); + bool execute_python_snippets = true; // make sure to not abuse just for quick maths bool run_server = false; + bool core_test_target = false; - bool wasm_target = true; - bool win32_target = false; + bool wasm_target = false; + bool win32_target = true; + + if (execute_python_snippets) { + sb8_t *sb = sb8_serial_begin(arena); + list_files_recursive(sb, s8_lit("..")); + for (sb8_node_t *it = sb->first; it; it = it->next) { + s8_t abs = it->string; + + bool is_c_file = s8_ends_with(abs, s8_lit(".c"), true) || s8_ends_with(abs, s8_lit(".cpp"), true) || s8_ends_with(abs, s8_lit(".h"), true) || s8_ends_with(abs, s8_lit(".hpp"), true); + if (!is_c_file) { + continue; + } + bool is_build_file = s8_ends_with(abs, s8_lit("build_file.c"), true); + if (is_build_file) { + continue; + } + + s8_t file = OS_ReadFile(&Perm, abs); + i64 idx = s8_find(file, s8_lit("/*#"), 0); + if (idx != -1) { + os_systemf("\"D:\\dev\\apps\\bin\\pyorun.bat\" %s", abs.str); + } + } + } if (run_server) { os_systemf("start /D ..\\package ..\\package\\run_server.bat"); diff --git a/src/app/app.gen.c b/src/app/app.gen.c index cf0198a..bd08a40 100644 --- a/src/app/app.gen.c +++ b/src/app/app.gen.c @@ -255,6 +255,7 @@ type_t type__app_event_kind_t = { type_kind_enum, s8_const_lit("app_event_kind_t }; type_t type__app_event_t = { type_kind_struct, s8_const_lit("app_event_t"), sizeof(app_event_t), .members = (type_member_t[]){ + {.name = s8_const_lit("next"), .type = &(type_t){type_kind_pointer, s8_const_lit("app_event_t*"), sizeof(void *), .base = &type__app_event_t}, .offset = offsetof(app_event_t, next)}, {.name = s8_const_lit("kind"), .type = &type__app_event_kind_t, .offset = offsetof(app_event_t, kind)}, {.name = s8_const_lit("mouse_button"), .type = &type__app_mouse_button_t, .offset = offsetof(app_event_t, mouse_button)}, {.name = s8_const_lit("key"), .type = &type__app_key_t, .offset = offsetof(app_event_t, key)}, @@ -268,5 +269,13 @@ type_t type__app_event_t = { type_kind_struct, s8_const_lit("app_event_t"), size {.name = s8_const_lit("window_size"), .type = &type__v2f64_t, .offset = offsetof(app_event_t, window_size)}, {.name = s8_const_lit("mouse_pos"), .type = &type__v2f64_t, .offset = offsetof(app_event_t, mouse_pos)}, }, - .count = 12, + .count = 13, +}; +type_t type__app_event_list_t = { type_kind_struct, s8_const_lit("app_event_list_t"), sizeof(app_event_list_t), + .members = (type_member_t[]){ + {.name = s8_const_lit("first"), .type = &(type_t){type_kind_pointer, s8_const_lit("app_event_t*"), sizeof(void *), .base = &type__app_event_t}, .offset = offsetof(app_event_list_t, first)}, + {.name = s8_const_lit("last"), .type = &(type_t){type_kind_pointer, s8_const_lit("app_event_t*"), sizeof(void *), .base = &type__app_event_t}, .offset = offsetof(app_event_list_t, last)}, + {.name = s8_const_lit("len"), .type = &type__i32, .offset = offsetof(app_event_list_t, len)}, + }, + .count = 3, }; \ No newline at end of file diff --git a/src/app/app.gen.h b/src/app/app.gen.h index e0f5387..5512a15 100644 --- a/src/app/app.gen.h +++ b/src/app/app.gen.h @@ -93,6 +93,7 @@ typedef enum { typedef struct app_event_t app_event_t; struct app_event_t { + app_event_t* next; app_event_kind_t kind; app_mouse_button_t mouse_button; app_key_t key; @@ -105,4 +106,11 @@ struct app_event_t { f64 dpr; v2f64_t window_size; v2f64_t mouse_pos; +}; + +typedef struct app_event_list_t app_event_list_t; +struct app_event_list_t { + app_event_t* first; + app_event_t* last; + i32 len; }; \ No newline at end of file diff --git a/src/app/app.meta.c b/src/app/app.meta.c index 3054fdc..0c122c4 100644 --- a/src/app/app.meta.c +++ b/src/app/app.meta.c @@ -159,12 +159,15 @@ void meta_app(ma_arena_t *arena) { } app_event_kind_t; struct app_event_t { + app_event_t *next; app_event_kind_t kind; // data present only during events app_mouse_button_t mouse_button; // @mouse_down @mouse_up app_key_t key; // @key_down @key_up s8_t text; // @text + + // @todo: test why xyz in js???????????? v3f64_t mouse_wheel_delta; // @mouse_wheel // always present data @@ -177,6 +180,12 @@ void meta_app(ma_arena_t *arena) { v2f64_t window_size; v2f64_t mouse_pos; }; + + struct app_event_list_t { + app_event_t *first; + app_event_t *last; + i32 len; + }; )); sb8_serial_ast_to_code(h, decls); diff --git a/src/app/app_wasm.c b/src/app/app_wasm.c index 8f8ebbe..a9d14d2 100644 --- a/src/app/app_wasm.c +++ b/src/app/app_wasm.c @@ -1,20 +1,25 @@ /* .doesn't miss events (always processes all key strokes and buttons etc.) -.replayable / deterministic (that is you can serialize all the events, replay them back and get the same results) -..rendering and update decoupled .sleeps properly when nothing is happening -.animations probably then should be in the rendering part +.replayable (that is you can serialize all the events, replay them back and get the same results) +..rendering and update decoupled +..animations probably then should be in the rendering part void app_update(app_event_t *events, i32 event_count) { - loop (events) update(it); + update_result_t *state = NULL; // contains event + loop (events) { + state = update(it); + } { - app_event_t *ev = events + event_count - 1; + app_event_t *ev = state->ev; + + f64 delta_time = app_anim_get_delta_time(); f64 time = app_anim_get_time(); - animate(ev, delta_time, time); - render(ev); + animate(state, delta_time, time); + render(state); } } @@ -37,10 +42,10 @@ glb_wasm_export i32 wasm_temp_buff1_len = 127; glb_wasm_export char wasm_temp_buff2[128] = {[127] = 0x13}; glb_wasm_export i32 wasm_temp_buff2_len = 127; -global f64 wasm_dpr; -global ma_arena_t *wasm_input_text_arena; -global STACK(app_event_t, 64) wasm_events; -global b32 wasm_event_failed_to_queue; +// @todo: event list +global f64 wasm_dpr; +global STACK(app_event_t, 256) wasm_events; +global b32 wasm_event_failed_to_queue; global f64 wasm_delta_time; global f64 wasm_time; @@ -53,7 +58,7 @@ struct wasm_cached_t { b8 ctrl, alt, meta, shift; } wasm_cached; -fn void app_update(app_event_t *events, i32 event_count); +fn void app_update(void); fn void app_init(void); fn void wasm_add_event(app_event_t event) { @@ -147,7 +152,7 @@ fn_wasm_export void wasm_key_down(char *key, b32 ctrl, b32 shift, b32 alt, b32 m return; } - s8_t text = s8_copy(wasm_input_text_arena, key8); + s8_t text = s8_copy(tcx.temp, key8); wasm_add_event((app_event_t){ .kind = app_event_kind_text, .mouse_pos = wasm_cached.mouse_pos, @@ -180,7 +185,7 @@ fn_wasm_export void wasm_key_up(char *key, b32 ctrl, b32 shift, b32 alt, b32 met } fn_wasm_export void wasm_update(f64 width, f64 height, f64 dpr) { - wasm_time = os_get_milliseconds(); + wasm_time = os_milliseconds_now(); wasm_delta_time = wasm_time - wasm_last_time_milliseconds; v2f64_t window_size = (v2f64_t){width / dpr, height / dpr}; @@ -202,18 +207,32 @@ fn_wasm_export void wasm_update(f64 width, f64 height, f64 dpr) { wasm_events.data[i].window_size = window_size; } - app_update(wasm_events.data, wasm_events.len); + app_update(); wasm_events.len = 0; wasm_last_time_milliseconds = wasm_time; - ma_set0(wasm_input_text_arena); } fn_wasm_export void wasm_init(void) { core_init(); - wasm_input_text_arena = ma_push_arena(&tcx.perm, kib(1)); app_init(); - wasm_app_init_time_milliseconds = os_get_milliseconds(); + wasm_app_init_time_milliseconds = os_milliseconds_now(); +} + +fn app_event_t *app_iterate_events(void) { + return wasm_events.data; +} + +fn b32 app_is_event_valid(app_event_t *event) { + return event < (wasm_events.data + wasm_events.len); +} + +fn void app_next_event(app_event_t **event) { + event[0] += 1; +} + +fn app_event_t *app_get_last_event(void) { + return wasm_events.data + wasm_events.len - 1; } fn f64 app_get_anim_time(void) { diff --git a/src/app/app_win32.c b/src/app/app_win32.c index d3b37ab..ea9c39e 100644 --- a/src/app/app_win32.c +++ b/src/app/app_win32.c @@ -1,8 +1,11 @@ -#include "core/core.h" +#include "core/core_inc.h" #include "app.gen.h" -#include "core/core.c" +#include "core/core_inc.c" #include "app.gen.c" +#include "app_win32_opengl.c" + +#include #pragma comment(linker, "/subsystem:windows") #pragma comment(lib, "gdi32.lib") @@ -10,120 +13,361 @@ #pragma comment(lib, "winmm.lib") -b32 w32_good_scheduling = false; +b32 w32_good_scheduling; WNDCLASSW w32_wc; HWND w32_window_handle; HDC w32_dc; +b32 w32_quit_app; -LRESULT CALLBACK w32_window_proc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) { +app_event_list_t w32_event_list; +ma_arena_t *w32_event_arena; + +fn v2f64_t w32_get_window_size(HWND window) { + RECT window_rect; + GetClientRect(window, &window_rect); + f64 x = window_rect.right - window_rect.left; + f64 y = window_rect.bottom - window_rect.top; + return (v2f64_t){x, y}; +} + +fn v2f64_t w32_get_mouse_pos(HWND window) { + POINT p; + GetCursorPos(&p); + ScreenToClient(window, &p); + return (v2f64_t){p.x, p.y}; +} + +fn f64 w32_get_dpr(HWND window_handle) { + UINT dpi = GetDpiForWindow(window_handle); + if (dpi == 0) { + return 1.0; + } + f64 result = (f64)dpi / 96.0; + return result; +} + +fn void w32_push_event(app_event_t event) { + app_event_t *ev = ma_push_type(w32_event_arena, app_event_t); + *ev = event; + + if (GetKeyState(VK_CONTROL) & 0x8000) ev->ctrl = true; + if (GetKeyState(VK_SHIFT) & 0x8000) ev->shift = true; + if (GetKeyState(VK_MENU) & 0x8000) ev->alt = true; + ev->window_size = w32_get_window_size(w32_window_handle); + ev->mouse_pos = w32_get_mouse_pos(w32_window_handle); + ev->dpr = w32_get_dpr(w32_window_handle); + + SLLQ_APPEND(w32_event_list.first, w32_event_list.last, ev); + w32_event_list.len += 1; +} + +fn LRESULT CALLBACK w32_window_proc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { - case WM_CLOSE: PostQuitMessage(0); break; - case WM_KEYUP: { - app_key_t key = w32_map_wparam_to_app_key(wparam); + case WM_CLOSE: { + ExitProcess(0); } break; + case WM_KEYDOWN: { - app_key_t key = w32_map_wparam_to_app_key(wparam); + w32_push_event((app_event_t){ + .kind = app_event_kind_key_down, + .key = w32_map_wparam_to_app_key(wparam) + }); } break; + case WM_KEYUP: { + w32_push_event((app_event_t){ + .kind = app_event_kind_key_up, + .key = w32_map_wparam_to_app_key(wparam) + }); + } break; + + + case WM_LBUTTONDOWN: { + SetCapture(wnd); + w32_push_event((app_event_t){ + .kind = app_event_kind_mouse_down, + .mouse_button = app_mouse_button_left, + }); + } break; + case WM_LBUTTONUP: { + ReleaseCapture(); + w32_push_event((app_event_t){ + .kind = app_event_kind_mouse_up, + .mouse_button = app_mouse_button_left, + }); + } break; + + + case WM_RBUTTONDOWN: { + w32_push_event((app_event_t){ + .kind = app_event_kind_mouse_down, + .mouse_button = app_mouse_button_right, + }); + } break; + case WM_RBUTTONUP: { + w32_push_event((app_event_t){ + .kind = app_event_kind_mouse_up, + .mouse_button = app_mouse_button_right, + }); + } break; + + + case WM_MBUTTONDOWN: { + SetCapture(wnd); + w32_push_event((app_event_t){ + .kind = app_event_kind_mouse_down, + .mouse_button = app_mouse_button_middle, + }); + } break; + case WM_MBUTTONUP: { + ReleaseCapture(); + w32_push_event((app_event_t){ + .kind = app_event_kind_mouse_up, + .mouse_button = app_mouse_button_middle, + }); + } break; + + case WM_MOUSEWHEEL: { + int zDelta = GET_WHEEL_DELTA_WPARAM(wparam); + w32_push_event((app_event_t){ + .kind = app_event_kind_mouse_wheel, + .mouse_wheel_delta = (v3f64_t){0, zDelta}, + }); + } break; + +case WM_CHAR: { + //https://handmade.network/forums/t/2011-keyboard_inputs_-_scancodes,_raw_input,_text_input,_key_names + // WM_UNICHAR + /* +I looked closer to how you process WM_CHAR in example code and I believe it is very wrong. wParam doesn't contain two ushorts joined in one 32-bit value. It is always one UTF-16 value. That means 16-bits. If Windows wants to send you unicode codepoint with value >0xFFFF, then it sends two UTF-16 WM_CHAR messages. Each with one part of UTF-16 surrogate pair. So your first WM_CHAR needs to remember it, and second one construct full unicode codepoint. For example, this is mentioned here: http://www.catch22.net/tuts/unicode-text-editing + */ +} break + + default: return DefWindowProcW(wnd, msg, wparam, lparam); } return 0; - } -int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { - typedef enum W32_PROCESS_DPI_AWARENESS { - W32_PROCESS_DPI_UNAWARE = 0, - W32_PROCESS_SYSTEM_DPI_AWARE = 1, - W32_PROCESS_PER_MONITOR_DPI_AWARE = 2 - } W32_PROCESS_DPI_AWARENESS; +app_event_list_t w32_get_events(ma_arena_t *arena) { + w32_event_arena = arena; + memory_zero(&w32_event_list, sizeof(w32_event_list)); - typedef unsigned MU_TimeBeginPeriod(unsigned); - typedef HRESULT MU_SetProcessDpiAwareness(W32_PROCESS_DPI_AWARENESS); - - HMODULE shcore = LoadLibraryA("Shcore.dll"); - if (shcore) { - MU_SetProcessDpiAwareness *set_dpi_awr = (MU_SetProcessDpiAwareness *)GetProcAddress(shcore, "SetProcessDpiAwareness"); - if (set_dpi_awr) { - HRESULT hr = set_dpi_awr(W32_PROCESS_PER_MONITOR_DPI_AWARE); - assert(SUCCEEDED(hr) && "Failed to set dpi awareness"); - } + MSG msg; + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); } - HMODULE winmm = LoadLibraryA("winmm.dll"); - if (winmm) { - MU_TimeBeginPeriod *timeBeginPeriod = (MU_TimeBeginPeriod *)GetProcAddress(winmm, "timeBeginPeriod"); - if (timeBeginPeriod) { - if (timeBeginPeriod(1) == 0) { - w32_good_scheduling = true; - } - } - } + return w32_event_list; +} - WNDCLASSW wc = {0}; - { - wc.lpfnWndProc = w32_window_proc; - wc.hInstance = GetModuleHandleW(NULL); - wc.lpszClassName = L"HelloClassName"; - wc.hCursor = LoadCursor(0, IDC_ARROW); - wc.hIcon = NULL; // LoadIcon(wc.hInstance, IDI_APPLICATION); - wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; - ATOM result = RegisterClassW(&wc); - assert(result != 0); - w32_wc = wc; - } +/////////////////////////////// +// canvas functions - RECT window_rect = {0}; - { - window_rect.left = 0; - window_rect.right = 1280; - window_rect.bottom = 732; - window_rect.top = 12; - AdjustWindowRectEx(&window_rect, WS_OVERLAPPEDWINDOW, false, 0); - } - w32_window_handle = CreateWindowW(w32_wc.lpszClassName, L"Zzz... Window, hello!", WS_OVERLAPPEDWINDOW, window_rect.left, window_rect.top, window_rect.right - window_rect.left, window_rect.bottom - window_rect.top, NULL, NULL, hInstance, NULL); - assert(w32_window_handle); +typedef struct w32_canvas_t w32_canvas_t; +struct w32_canvas_t { + u32 *memory; + HWND window_handle; + HBITMAP dib; + HDC dib_dc; + v2f64_t window_size; +}; - w32_dc = GetDC(w32_window_handle); - assert(w32_dc); - ShowWindow(w32_window_handle, SW_SHOW); - - // @todo: rebuild on resize - // Create a writable backbuffer bitmap - uint32_t *mem = 0; +w32_canvas_t w32_create_canvas(HWND window_handle) { + v2f64_t window_size = w32_get_window_size(window_handle); + w32_canvas_t result = {.window_handle = window_handle, .window_size = window_size}; + HDC window_dc = GetDC(window_handle); BITMAPINFO bminfo = {0}; { bminfo.bmiHeader.biSize = sizeof(bminfo.bmiHeader); - bminfo.bmiHeader.biWidth = (LONG)1280; - bminfo.bmiHeader.biHeight = (LONG)720; + bminfo.bmiHeader.biWidth = (LONG)window_size.x; + bminfo.bmiHeader.biHeight = (LONG)window_size.y; bminfo.bmiHeader.biPlanes = 1; bminfo.bmiHeader.biBitCount = 32; bminfo.bmiHeader.biCompression = BI_RGB; // AA RR GG BB bminfo.bmiHeader.biXPelsPerMeter = 1; bminfo.bmiHeader.biYPelsPerMeter = 1; } - HBITMAP dib = CreateDIBSection(w32_dc, &bminfo, DIB_RGB_COLORS, (void **)&mem, 0, 0); - HDC dib_dc = CreateCompatibleDC(w32_dc); + result.dib = CreateDIBSection(window_dc, &bminfo, DIB_RGB_COLORS, (void **)&result.memory, 0, 0); + result.dib_dc = CreateCompatibleDC(window_dc); + return result; +} +void w32_destroy_canvas(w32_canvas_t *canvas) { + if (canvas->memory) { + DeleteDC(canvas->dib_dc); + DeleteObject(canvas->dib); + memory_zero(canvas, sizeof(*canvas)); + } +} + +void w32_present_canvas(w32_canvas_t *canvas) { + if (canvas->memory) { + SelectObject(canvas->dib_dc, canvas->dib); + HDC window_dc = GetDC(canvas->window_handle); + BitBlt(window_dc, 0, 0, (int)canvas->window_size.x, (int)canvas->window_size.y, canvas->dib_dc, 0, 0, SRCCOPY); + } +} + +void w32_try_resizing_canvas(w32_canvas_t *canvas, HWND window_handle) { + v2f64_t window_size = w32_get_window_size(window_handle); + if (canvas->window_size.x != window_size.x || canvas->window_size.y != window_size.y) { + w32_destroy_canvas(canvas); + *canvas = w32_create_canvas(window_handle); + } +} + +int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { + tcx.temp = ma_create(ma_default_reserve_size); + + // Set DPI aware, @todo: verify / new way to do this | @todo: get dpi ratio + { + typedef enum W32_PROCESS_DPI_AWARENESS { + W32_PROCESS_DPI_UNAWARE = 0, + W32_PROCESS_SYSTEM_DPI_AWARE = 1, + W32_PROCESS_PER_MONITOR_DPI_AWARE = 2 + } W32_PROCESS_DPI_AWARENESS; + typedef HRESULT MU_SetProcessDpiAwareness(W32_PROCESS_DPI_AWARENESS); + + HMODULE shcore = LoadLibraryA("Shcore.dll"); + if (shcore) { + MU_SetProcessDpiAwareness *set_dpi_awr = (MU_SetProcessDpiAwareness *)GetProcAddress(shcore, "SetProcessDpiAwareness"); + if (set_dpi_awr) { + HRESULT hr = set_dpi_awr(W32_PROCESS_PER_MONITOR_DPI_AWARE); + assert(SUCCEEDED(hr) && "Failed to set dpi awareness"); + } + } + } + + // setup better scheduling, @todo: should we do this? + { + typedef unsigned MU_TimeBeginPeriod(unsigned); + + HMODULE winmm = LoadLibraryA("winmm.dll"); + if (winmm) { + MU_TimeBeginPeriod *timeBeginPeriod = (MU_TimeBeginPeriod *)GetProcAddress(winmm, "timeBeginPeriod"); + if (timeBeginPeriod) { + if (timeBeginPeriod(1) == 0) { + w32_good_scheduling = true; + } + } + } + } + + // create window + { + WNDCLASSW wc = {0}; + { + wc.lpfnWndProc = w32_window_proc; + wc.hInstance = GetModuleHandleW(NULL); + wc.lpszClassName = L"HelloClassName"; + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = NULL; // LoadIcon(wc.hInstance, IDI_APPLICATION); + wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; + ATOM result = RegisterClassW(&wc); + assert(result != 0); + w32_wc = wc; + } + + f64 primary_monitor_size_x = (f64)GetSystemMetrics(SM_CXSCREEN); + f64 primary_monitor_size_y = (f64)GetSystemMetrics(SM_CYSCREEN); + RECT window_rect = {0}; + { + window_rect.left = 0 + 0; + window_rect.right = 0 + (int)(primary_monitor_size_x * 0.8); + window_rect.bottom = 30 + (int)(primary_monitor_size_y * 0.8); + window_rect.top = 30 + 0; + AdjustWindowRectEx(&window_rect, WS_OVERLAPPEDWINDOW, false, 0); + } + w32_window_handle = CreateWindowW(w32_wc.lpszClassName, L"Zzz... Window, hello!", WS_OVERLAPPEDWINDOW, window_rect.left, window_rect.top, window_rect.right - window_rect.left, window_rect.bottom - window_rect.top, NULL, NULL, hInstance, NULL); + assert(w32_window_handle); + + w32_dc = GetDC(w32_window_handle); + assert(w32_dc); + + b32 ok = w32_load_wgl_fns(); + assert(ok); + + ok = w32_create_opengl_context(w32_dc, 4, 5); + assert(ok); + + ShowWindow(w32_window_handle, SW_SHOW); + } + + // w32_canvas_t canvas = w32_create_canvas(w32_window_handle); + + f64 time_frame_start = os_seconds_now(); + f64 time_delta = 0.01666666666666; + f64 time_total = 0.0; + f64 time_update = 0.0; + + u64 consecutive_missed_frames = 0; + u64 missed_frames = 0; + u64 frame = 0; for (;;) { - MSG msg; - if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { - if (msg.message == WM_QUIT) { - break; - } - TranslateMessage(&msg); - DispatchMessageW(&msg); - continue; - } + app_event_list_t event_list = w32_get_events(tcx.temp); - for (i32 y = 0; y < 720; y++) { - for (i32 x = 0; x < 1280; x++) { - mem[x + y * 1280] = 0xFFFF0000; + for (app_event_t *ev = event_list.first; ev; ev = ev->next) { + if (ev->kind == app_event_kind_key_down && ev->key == app_key_escape) { + ExitProcess(0); } } - SelectObject(dib_dc, dib); - BitBlt(w32_dc, 0, 0, 1280, 720, dib_dc, 0, 0, SRCCOPY); - Sleep(10); + // w32_try_resizing_canvas(&canvas, w32_window_handle); + // debugf("time_update: %f", time_update); + +#if 0 + for (i32 y = 0; y < canvas.window_size.y; y++) { + for (i32 x = 0; x < canvas.window_size.x; x++) { + canvas.memory[x + y * (int)canvas.window_size.x] = 0xFFFF0000; + } + } +#endif + + // w32_present_canvas(&canvas); + + /////////////////////////////// + // end of frame timings + + ma_set0(tcx.temp); + + f64 time_update_partial = os_seconds_now() - time_frame_start; + time_update = time_update_partial; + if (time_update < time_delta) { + consecutive_missed_frames = 0; + + // @todo: we are currently busy looping when we don't get the good schduling + // is that actually a good tactic?? + if (w32_good_scheduling) { + f64 time_to_sleep = time_delta - time_update; + f64 time_to_sleep_in_ms = (time_to_sleep * 1000.0) - 1.0; + if (time_to_sleep > 0.0) { + DWORD ms = (DWORD)time_to_sleep_in_ms; + if (ms) { + Sleep(ms); + } + } + } + + // busy loop if we dont have good scheduling + // or we woke up early + time_update = os_seconds_now() - time_frame_start; + while (time_update < time_delta) { + time_update = os_seconds_now() - time_frame_start; + } + } else { + missed_frames += 1; + consecutive_missed_frames += 1; + } + + frame += 1; + + // @todo: + // should this be time_delta or time_update ???? + // probably want the locked frame rate and total should reflect that, so choosing + // time_delta seems the correct choice but not really sure what is correct. + time_total += time_delta; + time_frame_start = os_seconds_now(); } return 0; diff --git a/src/app/app_win32_opengl.c b/src/app/app_win32_opengl.c new file mode 100644 index 0000000..571c1b0 --- /dev/null +++ b/src/app/app_win32_opengl.c @@ -0,0 +1,270 @@ +// first load the opengl loading functions +fn b32 w32_load_wgl_fns(void); + +// then create opengl context for window +fn b32 w32_create_opengl_context(HDC window_handle_dc, i32 opengl_major, i32 opengl_minor); + +// then you can use this to load opengl functions - +// either pass this to glad or load them manually +fn void *w32_load_opengl_fn(const char *proc); + +// compile time options +#define W32_OPENGL_DEBUG 1 +#define W32_ENABLE_MULTISAMPLING 1 + +// Symbols taken from GLFW +// +// Executables (but not DLLs) exporting this symbol with this value will be +// automatically directed to the high-performance GPU on Nvidia Optimus systems +// with up-to-date drivers +// +__declspec(dllexport) DWORD NvOptimusEnablement = 1; + +// Executables (but not DLLs) exporting this symbol with this value will be +// automatically directed to the high-performance GPU on AMD PowerXpress systems +// with up-to-date drivers +// +__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; + +typedef HGLRC MU_wglCreateContext(HDC unnamedParam1); +typedef BOOL MU_wglMakeCurrent(HDC unnamedParam1, HGLRC unnamedParam2); +typedef BOOL MU_wglDeleteContext(HGLRC unnamedParam1); +typedef void *MU_glGetProcAddress(const char *); +typedef const char *MU_wglGetExtensionsStringARB(HDC hdc); +typedef BOOL MU_wglChoosePixelFormatARB(HDC hdc, const int *piAttribIList, const float *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +typedef HGLRC MU_wglCreateContextAttribsARB(HDC hDC, HGLRC hshareContext, const int *attribList); +typedef BOOL MU_wglSwapIntervalEXT(int interval); + +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 + +global MU_wglChoosePixelFormatARB *wglChoosePixelFormatARB; +global MU_wglCreateContextAttribsARB *wglCreateContextAttribsARB; +global MU_wglSwapIntervalEXT *wglSwapIntervalEXT; + +global MU_glGetProcAddress *wgl_get_proc_address; +global void *(*gl_get_proc_address)(const char *str); +global HMODULE opengl_hmodule; + +global HGLRC(*mu_wglCreateContext)(HDC unnamedParam1); +global BOOL(*mu_wglMakeCurrent)(HDC unnamedParam1, HGLRC unnamedParam2); +global BOOL(*mu_wglDeleteContext)(HGLRC unnamedParam1); + +// compares src string with dstlen characters from dst, returns 1 if they are equal, 0 if not +fn int w32_are_strings_equal(const char *src, const char *dst, size_t dstlen) { + while (*src && dstlen-- && *dst) { + if (*src++ != *dst++) { + return 0; + } + } + return (dstlen && *src == *dst) || (!dstlen && *src == 0); +} + +fn void *w32_load_opengl_fn(const char *proc) { + void *func = wgl_get_proc_address(proc); + if (!func) { + func = GetProcAddress(opengl_hmodule, proc); + } + return func; +} + +fn b32 w32_load_wgl_fns(void) { + HMODULE opengl32 = LoadLibraryA("opengl32"); + assert(opengl32); + if (opengl32) { + opengl_hmodule = opengl32; + wgl_get_proc_address = (MU_glGetProcAddress *)GetProcAddress(opengl32, "wglGetProcAddress"); + gl_get_proc_address = w32_load_opengl_fn; + mu_wglCreateContext = (MU_wglCreateContext *)GetProcAddress(opengl32, "wglCreateContext"); + mu_wglMakeCurrent = (MU_wglMakeCurrent *)GetProcAddress(opengl32, "wglMakeCurrent"); + mu_wglDeleteContext = (MU_wglDeleteContext *)GetProcAddress(opengl32, "wglDeleteContext"); + } + if (opengl32 == NULL || mu_wglCreateContext == NULL || gl_get_proc_address == NULL || mu_wglMakeCurrent == NULL || mu_wglDeleteContext == NULL) { + assert(!"Failed to load Opengl wgl functions from opengl32.lib"); + } + + // to get WGL functions we need valid GL context, so create dummy window for dummy GL contetx + HWND dummy = CreateWindowExW( + 0, L"STATIC", L"DummyWindow", WS_OVERLAPPED, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, NULL, NULL); + assert(dummy && "Failed to create dummy window"); + + HDC dc = GetDC(dummy); + assert(dc && "Failed to get device context for dummy window"); + + PIXELFORMATDESCRIPTOR desc = {0}; + { + desc.nSize = sizeof(desc); + desc.nVersion = 1; + desc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + desc.iPixelType = PFD_TYPE_RGBA; + desc.cColorBits = 24; + }; + + int format = ChoosePixelFormat(dc, &desc); + if (!format) { + assert(!"Cannot choose OpenGL pixel format for dummy window!"); + } + + int ok = DescribePixelFormat(dc, format, sizeof(desc), &desc); + assert(ok && "Failed to describe OpenGL pixel format"); + + // reason to create dummy window is that SetPixelFormat can be called only once for the window + if (!SetPixelFormat(dc, format, &desc)) { + assert(!"Cannot set OpenGL pixel format for dummy window!"); + } + + HGLRC rc = mu_wglCreateContext(dc); + assert(rc && "Failed to create OpenGL context for dummy window"); + + ok = mu_wglMakeCurrent(dc, rc); + assert(ok && "Failed to make current OpenGL context for dummy window"); + + // https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_extensions_string.txt + MU_wglGetExtensionsStringARB *wglGetExtensionsStringARB = (MU_wglGetExtensionsStringARB *)gl_get_proc_address("wglGetExtensionsStringARB"); + if (!wglGetExtensionsStringARB) { + assert(!"OpenGL does not support WGL_ARB_extensions_string extension!"); + } + + const char *ext = wglGetExtensionsStringARB(dc); + assert(ext && "Failed to get OpenGL WGL extension string"); + + const char *start = ext; + for (;;) { + while (*ext != 0 && *ext != ' ') { + ext++; + } + size_t length = ext - start; + if (w32_are_strings_equal("WGL_ARB_pixel_format", start, length)) { + // https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_pixel_format.txt + wglChoosePixelFormatARB = (MU_wglChoosePixelFormatARB *)gl_get_proc_address("wglChoosePixelFormatARB"); + } + else if (w32_are_strings_equal("WGL_ARB_create_context", start, length)) { + // https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_create_context.txt + wglCreateContextAttribsARB = (MU_wglCreateContextAttribsARB *)gl_get_proc_address("wglCreateContextAttribsARB"); + } + else if (w32_are_strings_equal("WGL_EXT_swap_control", start, length)) { + // https://www.khronos.org/registry/OpenGL/extensions/EXT/WGL_EXT_swap_control.txt + wglSwapIntervalEXT = (MU_wglSwapIntervalEXT *)gl_get_proc_address("wglSwapIntervalEXT"); + } + + if (*ext == 0) { + break; + } + + ext++; + start = ext; + } + + if (!wglChoosePixelFormatARB || !wglCreateContextAttribsARB || !wglSwapIntervalEXT) { + assert(!"OpenGL does not support required WGL extensions for modern context!"); + } + + BOOL ok_b = mu_wglMakeCurrent(NULL, NULL); + assert(ok_b); + + ok_b = mu_wglDeleteContext(rc); + assert(ok_b); + + ok = ReleaseDC(dummy, dc); + assert(ok); + + ok_b = DestroyWindow(dummy); + assert(ok_b); + + return true; +} + +fn b32 w32_create_opengl_context(HDC window_handle_dc, i32 opengl_major, i32 opengl_minor) { + // set pixel format for OpenGL context + int attrib[] = + { + WGL_DRAW_TO_WINDOW_ARB, + true, + WGL_SUPPORT_OPENGL_ARB, + true, + WGL_DOUBLE_BUFFER_ARB, + true, + WGL_PIXEL_TYPE_ARB, + WGL_TYPE_RGBA_ARB, + WGL_COLOR_BITS_ARB, + 32, + WGL_DEPTH_BITS_ARB, + 24, + WGL_STENCIL_BITS_ARB, + 8, + + // uncomment for sRGB framebuffer, from WGL_ARB_framebuffer_sRGB extension + // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_framebuffer_sRGB.txt + // WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, GL_TRUE, + + // uncomment for multisampeld framebuffer, from WGL_ARB_multisample extension + // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_multisample.txt +#if W32_ENABLE_MULTISAMPLING + WGL_SAMPLE_BUFFERS_ARB, + 1, + WGL_SAMPLES_ARB, + 4, // 4x MSAA +#endif + + 0, + }; + + int format; + UINT formats; + if (!wglChoosePixelFormatARB(window_handle_dc, attrib, 0, 1, &format, &formats) || formats == 0) { + assert(!"OpenGL does not support required pixel format!"); + } + + PIXELFORMATDESCRIPTOR desc = {0}; + desc.nSize = sizeof(desc); + int ok = DescribePixelFormat(window_handle_dc, format, sizeof(desc), &desc); + assert(ok && "Failed to describe OpenGL pixel format"); + + if (!SetPixelFormat(window_handle_dc, format, &desc)) { + assert(!"Cannot set OpenGL selected pixel format!"); + } + + // create modern OpenGL context + { + int attrib[] = + { + WGL_CONTEXT_MAJOR_VERSION_ARB, + opengl_major, + WGL_CONTEXT_MINOR_VERSION_ARB, + opengl_minor, + WGL_CONTEXT_PROFILE_MASK_ARB, + WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + +#if W32_OPENGL_DEBUG + WGL_CONTEXT_FLAGS_ARB, + WGL_CONTEXT_DEBUG_BIT_ARB, +#endif + + 0, + }; + + HGLRC rc = wglCreateContextAttribsARB(window_handle_dc, 0, attrib); + assert(rc && "Cannot create modern OpenGL context! OpenGL version not supported?"); + + BOOL ok = mu_wglMakeCurrent(window_handle_dc, rc); + assert(ok && "Failed to make current OpenGL context"); + } + + return true; +} \ No newline at end of file diff --git a/src/core/core_ctx.h b/src/core/core_ctx.h index 9cb523d..d5b49e2 100644 --- a/src/core/core_ctx.h +++ b/src/core/core_ctx.h @@ -1,8 +1,15 @@ typedef struct thread_ctx_t thread_ctx_t; struct thread_ctx_t { ma_arena_t scratch[3]; - ma_arena_t perm; ma_arena_t *temp; // application specific arena + + // I probably want to discourage using it implicitly like: tcx.perm, instead just pass it around + // to functions that allocate pernament memory. temp and scratch very nicely create reusable code + // perm is basically global state so it would be nice to annotate which functions are reusable + // and which functions have state + ma_arena_t _perm; + + logger_t log; }; diff --git a/src/core/core_log.h b/src/core/core_log.h index ff6d47c..dd8ac57 100644 --- a/src/core/core_log.h +++ b/src/core/core_log.h @@ -63,7 +63,7 @@ fn void default_log_proc(log_event_t ev); #define program_version "---" #if PLATFORM_DEBUG_ASSERT -#define assert(x) (!(x) && (os_error_box(FILE_AND_LINE ": internal program error! assertion failed, program version: " program_version "\n"), debug_break())) +#define assert(x) (!(x) && (os_error_box(FILE_AND_LINE ": internal program error! assertion failed: " #x ", program version: " program_version "\n"), debug_break())) #else #define assert(x) (void)(x) #endif diff --git a/src/core/core_platform.h b/src/core/core_platform.h index 1cffa48..2527812 100644 --- a/src/core/core_platform.h +++ b/src/core/core_platform.h @@ -9,9 +9,27 @@ struct date_t { u16 year; }; +#if 0 +typedef struct core_funcs_t core_funcs_t; +struct core_funcs_t { + void (*error_box)(char *str); + void (*console_log)(char *str); + + date_t (*date_now)(void); + f64 (*milliseconds_now)(void); + + void *(*reserve)(usize size); + b32 (*commit)(void *p, usize size); + b32 (*release)(void *p); + b32 (*decommit)(void *p, usize size); +}; +#endif + + fn void os_error_box(char *str); fn void os_console_log(char *str); fn date_t os_date_now(void); +fn f64 os_milliseconds_now(void); fn void *os_vmem_reserve(usize size); fn b32 os_vmem_commit(void *p, usize size); diff --git a/src/core/core_platform_unix.c b/src/core/core_platform_unix.c index 46179eb..7c30b68 100644 --- a/src/core/core_platform_unix.c +++ b/src/core/core_platform_unix.c @@ -39,7 +39,7 @@ fn u64 os_get_microseconds(void) { return result; } -fn f64 os_get_milliseconds(void) { +fn f64 os_milliseconds_now(void) { u64 micros = os_get_microseconds(); f64 result = (f64)micros / 1000.0; return result; diff --git a/src/core/core_platform_wasm.c b/src/core/core_platform_wasm.c index b072dd6..cae703b 100644 --- a/src/core/core_platform_wasm.c +++ b/src/core/core_platform_wasm.c @@ -39,7 +39,7 @@ fn double strtod(const char *str, char **end_unused) { return wasm_parse_float((isize)str, str_len((char *)str)); } -fn f64 os_get_milliseconds(void) { +fn f64 os_milliseconds_now(void) { return wasm_get_milliseconds(); } @@ -48,10 +48,10 @@ fn void core_init(void) { isize page_count = __builtin_wasm_memory_size(0); u8 *memory = (u8 *)&__heap_base; usize memory_size = page_count * (page_size) - (isize)memory; - tcx.perm.data = memory; - tcx.perm.commit = tcx.perm.reserve = memory_size; + tcx._perm.data = memory; + tcx._perm.commit = tcx._perm.reserve = memory_size; - ma_push_arena_ex(&tcx.perm, &tcx.scratch[0], mib(1)); - ma_push_arena_ex(&tcx.perm, &tcx.scratch[1], kib(256)); - ma_push_arena_ex(&tcx.perm, &tcx.scratch[2], kib(64)); + ma_push_arena_ex(&tcx._perm, &tcx.scratch[0], mib(1)); + ma_push_arena_ex(&tcx._perm, &tcx.scratch[1], kib(256)); + ma_push_arena_ex(&tcx._perm, &tcx.scratch[2], kib(64)); } \ No newline at end of file diff --git a/src/core/core_platform_win32.c b/src/core/core_platform_win32.c index 3ba301c..8a2b60b 100644 --- a/src/core/core_platform_win32.c +++ b/src/core/core_platform_win32.c @@ -32,28 +32,23 @@ fn date_t os_date_now(void) { return result; } -global LARGE_INTEGER win32__performance_frequency; -global u64 win32_performance_frequency = 1; - -fn u64 os_get_microseconds(void) { - if (win32__performance_frequency.QuadPart == 0) { - win32__performance_frequency.QuadPart = 1; // don't query more then one time - if(QueryPerformanceFrequency(&win32__performance_frequency)) { - win32_performance_frequency = win32__performance_frequency.QuadPart; - } +fn f64 os_seconds_now(void) { + static int64_t counts_per_second; + if (counts_per_second == 0) { + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + counts_per_second = freq.QuadPart; } - u64 result = 0; - LARGE_INTEGER n; - if (QueryPerformanceCounter(&n)) { - result = (n.QuadPart*million(1))/win32_performance_frequency; - } + LARGE_INTEGER time; + QueryPerformanceCounter(&time); + f64 result = (f64)time.QuadPart / (f64)counts_per_second; return result; } -fn f64 os_get_milliseconds(void) { - u64 micros = os_get_microseconds(); - f64 result = (f64)micros / 1000.0; +fn f64 os_milliseconds_now(void) { + f64 secs = os_seconds_now(); + f64 result = secs * 1000; return result; } diff --git a/src/gfx2d/gfx2d.c b/src/gfx2d/gfx2d.c index 8590621..9ed711c 100644 --- a/src/gfx2d/gfx2d.c +++ b/src/gfx2d/gfx2d.c @@ -43,45 +43,45 @@ fn void draw_rect(r2f64_t rect, v4f32_t color) { } typedef enum { - gfx2d_kind_null, - gfx2d_kind_clear, - gfx2d_kind_draw_rect, - gfx2d_kind_draw_text, - gfx2d_kind_set_clip, -} gfx2d_kind_t; + gfx_kind_null, + gfx_kind_clear, + gfx_kind_draw_rect, + gfx_kind_draw_text, + gfx_kind_set_clip, +} gfx_kind_t; -typedef struct gfx2d_cmd_t gfx2d_cmd_t; -struct gfx2d_cmd_t { - gfx2d_cmd_t *next; - gfx2d_kind_t kind; +typedef struct gfx_cmd_t gfx_cmd_t; +struct gfx_cmd_t { + gfx_cmd_t *next; + gfx_kind_t kind; r2f64_t rect; v4f32_t color; s8_t text; }; -typedef struct gfx2d_t gfx2d_t; -struct gfx2d_t { - gfx2d_cmd_t *first; - gfx2d_cmd_t *last; +typedef struct gfx_t gfx_t; +struct gfx_t { + gfx_cmd_t *first; + gfx_cmd_t *last; app_event_t *ev; }; -void gfx2d_begin(gfx2d_t *gfx, app_event_t *ev) { +void gfx_begin(gfx_t *gfx, app_event_t *ev) { gfx->ev = ev; } -void gfx2d_end(gfx2d_t *gfx) { +void gfx_end(gfx_t *gfx) { app_event_t *ev = gfx->ev; r2f64_t window = (r2f64_t){0, 0, ev->window_size.x, ev->window_size.y}; - for (gfx2d_cmd_t *cmd = gfx->first; cmd; cmd = cmd->next) { - if (cmd->kind == gfx2d_kind_clear) { + for (gfx_cmd_t *cmd = gfx->first; cmd; cmd = cmd->next) { + if (cmd->kind == gfx_kind_clear) { wasm_clear(); draw_rect(window, cmd->color); - } else if (cmd->kind == gfx2d_kind_draw_rect) { + } else if (cmd->kind == gfx_kind_draw_rect) { draw_rect(cmd->rect, cmd->color); - } else if (cmd->kind == gfx2d_kind_draw_text) { + } else if (cmd->kind == gfx_kind_draw_text) { draw_text(cmd->rect.min, cmd->color, cmd->text); - } else if (cmd->kind == gfx2d_kind_set_clip) { + } else if (cmd->kind == gfx_kind_set_clip) { set_clip(cmd->rect); } else { invalid_codepath; @@ -91,46 +91,46 @@ void gfx2d_end(gfx2d_t *gfx) { gfx->first = gfx->last = NULL; } -void gfx2d_add_cmd(gfx2d_t *gfx, gfx2d_cmd_t cmd) { - gfx2d_cmd_t *c = ma_push_type(tcx.temp, gfx2d_cmd_t); +void gfx_add_cmd(gfx_t *gfx, gfx_cmd_t cmd) { + gfx_cmd_t *c = ma_push_type(tcx.temp, gfx_cmd_t); *c = cmd; SLLQ_APPEND(gfx->first, gfx->last, c); } -void gfx2d_clear(gfx2d_t *gfx, v4f32_t color) { - gfx2d_add_cmd(gfx, (gfx2d_cmd_t){ - .kind = gfx2d_kind_clear, +void gfx_clear(gfx_t *gfx, v4f32_t color) { + gfx_add_cmd(gfx, (gfx_cmd_t){ + .kind = gfx_kind_clear, .color = color, }); } -void gfx2d_rect(gfx2d_t *gfx, r2f64_t rect, v4f32_t color) { - gfx2d_add_cmd(gfx, (gfx2d_cmd_t){ - .kind = gfx2d_kind_draw_rect, +void gfx_rect(gfx_t *gfx, r2f64_t rect, v4f32_t color) { + gfx_add_cmd(gfx, (gfx_cmd_t){ + .kind = gfx_kind_draw_rect, .color = color, .rect = rect, }); } -void gfx2d_set_clip(gfx2d_t *gfx, r2f64_t rect) { - gfx2d_add_cmd(gfx, (gfx2d_cmd_t){ - .kind = gfx2d_kind_set_clip, +void gfx_set_clip(gfx_t *gfx, r2f64_t rect) { + gfx_add_cmd(gfx, (gfx_cmd_t){ + .kind = gfx_kind_set_clip, .rect = rect, }); } -void gfx2d_text(gfx2d_t *gfx, v2f64_t pos, v4f32_t color, s8_t string) { - gfx2d_add_cmd(gfx, (gfx2d_cmd_t){ - .kind = gfx2d_kind_draw_text, +void gfx_text(gfx_t *gfx, v2f64_t pos, v4f32_t color, s8_t string) { + gfx_add_cmd(gfx, (gfx_cmd_t){ + .kind = gfx_kind_draw_text, .text = s8_copy(tcx.temp, string), .color = color, .rect.min = pos, }); } -void gfx2d_textf(gfx2d_t *gfx, v2f64_t pos, v4f32_t color, char *str, ...) { +void gfx_textf(gfx_t *gfx, v2f64_t pos, v4f32_t color, char *str, ...) { S8_FMT(tcx.temp, str, text); - gfx2d_add_cmd(gfx, (gfx2d_cmd_t){ - .kind = gfx2d_kind_draw_text, + gfx_add_cmd(gfx, (gfx_cmd_t){ + .kind = gfx_kind_draw_text, .text = text, .color = color, .rect.min = pos, diff --git a/src/wasm_app/main.c b/src/wasm_app/main.c index 1ddd96f..8e4fa96 100644 --- a/src/wasm_app/main.c +++ b/src/wasm_app/main.c @@ -5,32 +5,33 @@ #include "gfx2d/gfx2d.c" // #include "ui.c" -gfx2d_t *gfx2d = NULL; +gfx_t *gfx = NULL; void app_init(void) { - tcx.temp = ma_push_arena(&tcx.perm, mib(1)); - gfx2d = ma_push_type(&tcx.perm, gfx2d_t); + ma_arena_t *perm = &tcx._perm; + tcx.temp = ma_push_arena(perm, mib(1)); + gfx = ma_push_type(perm, gfx_t); } -void app_update(app_event_t *events, i32 events_len) { +void app_update(void) { ma_set0(tcx.temp); - for (app_event_t *ev = events; ev < (events + events_len); ev += 1) { - // update(ev) + for (app_event_t *ev = app_iterate_events(); app_is_event_valid(ev); app_next_event(&ev)) { + // update } // These steps should be totally optional!! { - app_event_t *ev = events + events_len - 1; + app_event_t *ev = app_get_last_event(); f64 delta = app_get_anim_delta_time(); f64 time = app_get_anim_time(); // animate // render - gfx2d_begin(gfx2d, ev); - gfx2d_clear(gfx2d, white_color_global); - gfx2d_textf(gfx2d, (v2f64_t){0,0}, black_color_global, "delta: %f, time: %f", delta, time); - gfx2d_end(gfx2d); + gfx_begin(gfx, ev); + gfx_clear(gfx, white_color_global); + gfx_textf(gfx, (v2f64_t){0,0}, black_color_global, "delta: %f, time: %f", delta, time); + gfx_end(gfx); } } diff --git a/src/wasm_app/ui.c b/src/wasm_app/ui.c index 5c51aaf..6a0cc71 100644 --- a/src/wasm_app/ui.c +++ b/src/wasm_app/ui.c @@ -29,7 +29,7 @@ ui_signal_t ui_interact(app_event_t *ev, r2f64_t rect) { return sig; } -b32 ui_button(gfx2d_t *gfx, app_event_t *ev, r2f64_t rect, char *title) { +b32 ui_button(gfx_t *gfx, app_event_t *ev, r2f64_t rect, char *title) { v2f64_t text_pos = ui_calc_text_pos(rect, title); ui_signal_t sig = ui_interact(ev, rect); @@ -39,15 +39,15 @@ b32 ui_button(gfx2d_t *gfx, app_event_t *ev, r2f64_t rect, char *title) { rect_color = primary_color_global; } - gfx2d_set_clip(gfx, rect); - gfx2d_rect(gfx, rect, rect_color); - gfx2d_text(gfx, text_pos, text_color, s8_from_char(title)); - gfx2d_set_clip(gfx, r2f64(-1000, -1000, 1000000, 1000000)); + gfx_set_clip(gfx, rect); + gfx_rect(gfx, rect, rect_color); + gfx_text(gfx, text_pos, text_color, s8_from_char(title)); + gfx_set_clip(gfx, r2f64(-1000, -1000, 1000000, 1000000)); return sig.pressed; } -b32 ui_checkbox(gfx2d_t *gfx, app_event_t *ev, r2f64_t rect, b32 *value, char *title) { +b32 ui_checkbox(gfx_t *gfx, app_event_t *ev, r2f64_t rect, b32 *value, char *title) { v2f64_t text_pos = ui_calc_text_pos(rect, title); ui_signal_t sig = ui_interact(ev, rect); if (sig.pressed) *value = !*value; @@ -61,10 +61,10 @@ b32 ui_checkbox(gfx2d_t *gfx, app_event_t *ev, r2f64_t rect, b32 *value, char *t rect_color = v4f32_lerp(rect_color, accent2_color_global, 0.5); } - gfx2d_set_clip(gfx, rect); - gfx2d_rect(gfx, rect, rect_color); - gfx2d_text(gfx, text_pos, text_color, s8_from_char(title)); - gfx2d_set_clip(gfx, r2f64(-1000, -1000, 1000000, 1000000)); + gfx_set_clip(gfx, rect); + gfx_rect(gfx, rect, rect_color); + gfx_text(gfx, text_pos, text_color, s8_from_char(title)); + gfx_set_clip(gfx, r2f64(-1000, -1000, 1000000, 1000000)); return *value; } @@ -84,7 +84,7 @@ ui_id_t ui_string_to_id(const char *string, i32 len) { // FNV HASH (1a?) #define ui_location_id() ui_string_to_id(FILE_AND_LINE, sizeof(FILE_AND_LINE) - 1) ui_id_t ui_active_element = {0}; -void ui_slider(gfx2d_t *gfx, app_event_t *ev, r2f64_t rect, ui_id_t id, f64 *value, char *title) { +void ui_slider(gfx_t *gfx, app_event_t *ev, r2f64_t rect, ui_id_t id, f64 *value, char *title) { v2f64_t text_pos = ui_calc_text_pos(rect, title); ui_signal_t sig = ui_interact(ev, rect); b32 interacting = false; @@ -113,24 +113,24 @@ void ui_slider(gfx2d_t *gfx, app_event_t *ev, r2f64_t rect, ui_id_t id, f64 *val if (sig.overlapping) slider_color = v4f32_lerp(slider_color, accent2_color_global, 0.2); v4f32_t text_color = black_color_global; - gfx2d_set_clip(gfx, rect); - gfx2d_rect(gfx, rect, rect_color); - gfx2d_rect(gfx, slider_rect, slider_color); - gfx2d_text(gfx, text_pos, text_color, s8_from_char(title)); - gfx2d_set_clip(gfx, r2f64(-1000, -1000, 1000000, 1000000)); + gfx_set_clip(gfx, rect); + gfx_rect(gfx, rect, rect_color); + gfx_rect(gfx, slider_rect, slider_color); + gfx_text(gfx, text_pos, text_color, s8_from_char(title)); + gfx_set_clip(gfx, r2f64(-1000, -1000, 1000000, 1000000)); } -void ui_demo(gfx2d_t *gfx, app_event_t *ev) { +void ui_demo(gfx_t *gfx, app_event_t *ev) { if (ev->kind == app_event_kind_mouse_up) { ui_active_element = (ui_id_t){0}; } r2f64_t window_rect = (r2f64_t){(v2f64_t){0}, ev->window_size}; r2f64_t top_bar_rect = r2f64_cut_top(&window_rect, get_font_height() + 20); - gfx2d_rect(gfx, window_rect, primary_color_global); + gfx_rect(gfx, window_rect, primary_color_global); f64 padding = 50; - gfx2d_rect(gfx, top_bar_rect, secondary_color_global); + gfx_rect(gfx, top_bar_rect, secondary_color_global); static b32 open_file_panel; f64 open_file_panel_xsize = 0; @@ -143,7 +143,7 @@ void ui_demo(gfx2d_t *gfx, app_event_t *ev) { if (ui_checkbox(gfx, ev, rect, &open_file_panel, title)) { } r2f64_t gap_rect = r2f64_cut_left(&top_bar_rect, 1); - gfx2d_rect(gfx, gap_rect, black_color_global); + gfx_rect(gfx, gap_rect, black_color_global); } if (open_file_panel) { @@ -180,7 +180,7 @@ void ui_demo(gfx2d_t *gfx, app_event_t *ev) { if (ui_button(gfx, ev, rect, title)) { } r2f64_t gap_rect = r2f64_cut_left(&top_bar_rect, 1); - gfx2d_rect(gfx, gap_rect, black_color_global); + gfx_rect(gfx, gap_rect, black_color_global); } { @@ -189,7 +189,7 @@ void ui_demo(gfx2d_t *gfx, app_event_t *ev) { if (ui_button(gfx, ev, rect, title)) { } r2f64_t gap_rect = r2f64_cut_left(&top_bar_rect, 1); - gfx2d_rect(gfx, gap_rect, black_color_global); + gfx_rect(gfx, gap_rect, black_color_global); } } \ No newline at end of file diff --git a/todo.txt b/todo.txt index 46abe50..20d7cea 100644 --- a/todo.txt +++ b/todo.txt @@ -2,6 +2,8 @@ [ ] app [ ] ui [ ] event playback + [ ] update should produce a straight struct result for render + [ ] how to fix variable scroll? or do we not care? [ ] win32 [ ] hot reload / plugins [ ] tests using yield @@ -18,6 +20,10 @@ [ ] change name, hard to type [ ] core + [ ] add time stuff into core? app start time and stuff like that + [ ] meta + [ ] search for python snippets and execute meta.py script on that file + [ ] new simple format with tags [x] revisit api [ ] s8_bin [ ] bin_write_begin - serialize binary data (sb8_bin_write?) @@ -34,3 +40,14 @@ bin_read_end bin_read_... + +new format with tags: + +app_event_t: @struct { + kind: app_event_kind_t + key: app_key_t + button: app_mouse_button_t + delta_time: f64 + + next: *app_event_t @dont_serialize +} \ No newline at end of file