win32_app

This commit is contained in:
krzosa
2025-01-03 07:53:13 +01:00
parent 05d49eefe8
commit 6933566a86
17 changed files with 832 additions and 200 deletions

View File

@@ -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");

View File

@@ -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,
};

View File

@@ -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;
@@ -106,3 +107,10 @@ struct app_event_t {
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;
};

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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 <windowsx.h>
#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;

270
src/app/app_win32_opengl.c Normal file
View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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));
}

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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
}