ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

Commit: b25cf020007042c20ef92a810eaa5514878c986d
Parent: 023e3a3aaf9cbc286e0115cd6f2b1455db38ce5a
Author: Randy Palamar
Date:   Wed, 17 Jun 2026 08:48:36 -0600

platform: make window creation a required API

multiple windows are not really possible while raylib is in use
but they will be added after raylib is removed.

Diffstat:
Mbeamformer.c | 11++++-------
Mbeamformer.h | 21++++++++++++++++++++-
Mbeamformer_core.c | 13++++++++-----
Mbeamformer_internal.h | 3+++
Mexternal/rcore_extended.c | 2+-
Mmain_linux.c | 222+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mmain_w32.c | 219++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mui.c | 12++++++++++++
Mutil_os_ui.c | 17+++++++++++++++++
9 files changed, 397 insertions(+), 123 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -175,10 +175,13 @@ beamformer_init(BeamformerInput *input) BeamformerCtx *ctx = push_struct(&memory, BeamformerCtx); + str8 window_title = str8("VK Beamformer"); + ctx->main_window = os_window_create(window_title.data, window_title.length, 1280, 840); + ctx->window_size = (iv2){{1280, 840}}; + Arena scratch = {.beg = memory.end - 4096L, .end = memory.end}; memory.end = scratch.beg; - ctx->window_size = (iv2){{1280, 840}}; ctx->error_stream = error; ctx->ui_backing_store = ui_arena; ctx->compute_worker.arena = compute_arena; @@ -242,12 +245,6 @@ beamformer_init(BeamformerInput *input) beamformer_load_cuda_library(ctx, input->cuda_library_handle, memory); - SetConfigFlags(FLAG_VSYNC_HINT|FLAG_WINDOW_ALWAYS_RUN); - InitWindow(ctx->window_size.w, ctx->window_size.h, "OGL Beamformer"); - /* NOTE: do this after initing so that the window starts out floating in tiling wm */ - SetWindowState(FLAG_WINDOW_RESIZABLE); - SetWindowMinSize(840, ctx->window_size.h); - load_gl(&ctx->error_stream); ctx->shared_memory = input->shared_memory; diff --git a/beamformer.h b/beamformer.h @@ -69,6 +69,7 @@ typedef struct { uint64_t value[1]; } OSBarrier; typedef struct { uint64_t value[1]; } OSHandle; typedef struct { uint64_t value[1]; } OSLibrary; typedef struct { uint64_t value[1]; } OSThread; +typedef struct { uint64_t value[1]; } OSWindow; typedef struct { uint64_t value[1]; } OSW32Semaphore; typedef uint64_t os_thread_entry_point_fn(void *user_context); @@ -102,6 +103,12 @@ BEAMFORMER_IMPORT void * os_lookup_symbol(OSLibrary library, const char BEAMFORMER_IMPORT uint32_t os_wait_on_address(int32_t *lock, int32_t current, uint32_t timeout_ms); BEAMFORMER_IMPORT void os_wake_all_waiters(int32_t *lock); +// NOTE(rnp): currently beamformer will only create one window. +// once raylib is removed it may request multiple +BEAMFORMER_IMPORT OSWindow os_window_create(uint8_t *title, int64_t title_length, int32_t width, int32_t height); +//BEAMFORMER_IMPORT void os_window_title(OSWindow window, uint8_t *title, int64_t title_length); +//BEAMFORMER_IMPORT void os_window_destroy(OSWindow window); + // NOTE(rnp): eventually logging will just be done internally BEAMFORMER_IMPORT void os_console_log(uint8_t *data, int64_t length); BEAMFORMER_IMPORT void os_fatal(uint8_t *data, int64_t length); @@ -125,6 +132,8 @@ BEAMFORMER_IMPORT void os_w32_semaphore_release(OSW32Semaphore, int32_ typedef enum { BeamformerInputEventKind_ButtonPress, BeamformerInputEventKind_ButtonRelease, + BeamformerInputEventKind_MouseScroll, + BeamformerInputEventKind_WindowResize, BeamformerInputEventKind_ExecutableReload, BeamformerInputEventKind_FileEvent, } BeamformerInputEventKind; @@ -154,13 +163,23 @@ typedef enum { BeamformerInputModifier_Control = BeamformerInputModifier_LeftControl|BeamformerInputModifier_RightControl, BeamformerInputModifier_Shift = BeamformerInputModifier_LeftShift|BeamformerInputModifier_RightShift, BeamformerInputModifier_Meta = BeamformerInputModifier_LeftMeta|BeamformerInputModifier_RightMeta, + BeamformerInputModifier_Any = BeamformerInputModifier_Alt| + BeamformerInputModifier_Control| + BeamformerInputModifier_Shift| + BeamformerInputModifier_Meta, } BeamformerInputModifiers; typedef struct { BeamformerInputEventKind kind; union { BeamformerButtonID button_id; - void * file_watch_user_context; + + struct { + uint32_t width, height; + OSWindow window; + } window_resize; + + void *file_watch_user_context; }; } BeamformerInputEvent; diff --git a/beamformer_core.c b/beamformer_core.c @@ -1,5 +1,6 @@ /* See LICENSE for license details. */ /* TODO(rnp): + * [ ]: backtrace dumping on SIGSEGV * [ ]: bug? HERCULES might be broken, we may need to to chunk on transmits instead of channels * [ ]: refactor: do_compute should build its own "command graph" which tracks * dependencies better. It is very important that unnecessary barriers are @@ -1652,6 +1653,13 @@ beamformer_process_input_events(BeamformerCtx *ctx, BeamformerInput *input, BeamformerInputEvent *event = events + index; switch (event->kind) { + // NOTE(rnp): ui will handle these + case BeamformerInputEventKind_ButtonPress: + case BeamformerInputEventKind_ButtonRelease: + case BeamformerInputEventKind_MouseScroll: + case BeamformerInputEventKind_WindowResize: + {}break; + case BeamformerInputEventKind_ExecutableReload:{ ui_init(ctx, ctx->ui_backing_store); @@ -1710,11 +1718,6 @@ beamformer_frame_step(BeamformerInput *input) dt_for_frame = (f64)(current_time - ctx->frame_timestamp) / os_system_info()->timer_frequency; ctx->frame_timestamp = current_time; - if (IsWindowResized()) { - ctx->window_size.h = GetScreenHeight(); - ctx->window_size.w = GetScreenWidth(); - } - coalesce_timing_table(ctx->compute_timing_table, ctx->compute_shader_stats); beamformer_process_input_events(ctx, input, input->event_queue, input->event_count); diff --git a/beamformer_internal.h b/beamformer_internal.h @@ -493,6 +493,9 @@ typedef struct { ComputeTimingTable compute_timing_table[1]; BeamformWorkQueue beamform_work_queue[1]; + + // TODO(rnp): this should go to the UI eventually + OSWindow main_window; } BeamformerCtx; #define BeamformerContextMemory(m) (BeamformerCtx *)align_pointer_up((m), alignof(BeamformerCtx)); diff --git a/external/rcore_extended.c b/external/rcore_extended.c @@ -1,7 +1,7 @@ #include "raylib/src/rcore.c" // NOTE(rnp): hacky function to get the GLFWwindow handle -void * +RLAPI void * GetPlatformWindowHandle(void) { return (void *)platform.handle; diff --git a/main_linux.c b/main_linux.c @@ -29,28 +29,54 @@ #include <dlfcn.h> -typedef enum { - OSLinux_FileWatchKindPlatform, - OSLinux_FileWatchKindUser, -} OSLinux_FileWatchKind; - +typedef struct OSLinuxEntity OSLinuxEntity; typedef struct { - OSLinux_FileWatchKind kind; + void *handle; + OSLinuxEntity *prev, *next; +} OSLinuxWindow; + +typedef enum { + OSLinuxFileWatchKind_Platform, + OSLinuxFileWatchKind_User, +} OSLinuxFileWatchKind; + +typedef struct OSLinuxFileWatchDirectory OSLinuxFileWatchDirectory; +typedef struct OSLinuxFileWatch OSLinuxFileWatch; +struct OSLinuxFileWatch { + OSLinuxFileWatchKind kind; u64 hash; u64 update_time; - void * user_context; -} OSLinux_FileWatch; + void *user_context; -typedef struct { + OSLinuxFileWatchDirectory *parent; + OSLinuxFileWatch *prev, *next; +}; + +struct OSLinuxFileWatchDirectory { u64 hash; i64 handle; str8 name; - OSLinux_FileWatch *data; - da_count count; - da_count capacity; -} OSLinux_FileWatchDirectory; -DA_STRUCT(OSLinux_FileWatchDirectory, OSLinux_FileWatchDirectory); + OSLinuxFileWatch *first_child; + OSLinuxFileWatch *last_child; + OSLinuxFileWatchDirectory *prev, *next; +}; + +typedef enum { + OSLinuxEntityKind_Window, + OSLinuxEntityKind_FileWatch, + OSLinuxEntityKind_FileWatchDirectory, +} OSLinuxEntityKind; + +struct OSLinuxEntity { + OSLinuxEntityKind kind; + union { + OSLinuxFileWatch file_watch; + OSLinuxFileWatchDirectory file_watch_directory; + OSLinuxWindow window; + } as; + OSLinuxEntity *next; +}; typedef struct { Arena arena; @@ -58,12 +84,39 @@ typedef struct { i32 inotify_handle; - OSLinux_FileWatchDirectoryList file_watch_list; + BeamformerInput *input; OSSystemInfo system_info; + + struct { + OSLinuxFileWatchDirectory *first; + OSLinuxFileWatchDirectory *last; + } file_watch_directories; + + struct { + OSLinuxEntity *first; + OSLinuxEntity *last; + } windows; + + OSLinuxEntity *entity_freelist; } OSLinux_Context; global OSLinux_Context os_linux_context; +function OSLinuxEntity * +os_entity_allocate(OSLinuxEntityKind kind) +{ + OSLinuxEntity *result = 0; + DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock)) + { + result = SLLPopFreelist(os_linux_context.entity_freelist); + if (!result) result = push_struct_no_zero(&os_linux_context.arena, OSLinuxEntity); + } + + zero_struct(result); + result->kind = kind; + return result; +} + BEAMFORMER_IMPORT OSSystemInfo * os_system_info(void) { @@ -148,47 +201,106 @@ allocate_shared_memory(char *name, iz requested_capacity, u64 *capacity) return result; } -function OSLinux_FileWatchDirectory * -os_lookup_file_watch_directory(OSLinux_FileWatchDirectoryList *ctx, u64 hash) +function OSLinuxFileWatchDirectory * +os_lookup_file_watch_directory(u64 hash) { - OSLinux_FileWatchDirectory *result = 0; - for (da_count i = 0; !result && i < ctx->count; i++) - if (ctx->data[i].hash == hash) - result = ctx->data + i; + OSLinuxFileWatchDirectory *result = 0; + for (OSLinuxFileWatchDirectory *fwd = os_linux_context.file_watch_directories.first; fwd; fwd = fwd->next) { + if (fwd->hash == hash) { + result = fwd; + break; + } + } return result; } function void -os_linux_add_file_watch(str8 path, void *user_context, OSLinux_FileWatchKind kind) +os_linux_add_file_watch(str8 path, void *user_context, OSLinuxFileWatchKind kind) { str8 directory = path; directory.length = str8_scan_backwards(path, '/'); assert(directory.length > 0); - OSLinux_FileWatchDirectoryList *fwctx = &os_linux_context.file_watch_list; - u64 hash = u64_hash_from_str8(directory); - OSLinux_FileWatchDirectory *dir = os_lookup_file_watch_directory(fwctx, hash); + OSLinuxFileWatchDirectory *dir = os_lookup_file_watch_directory(hash); if (!dir) { assert(path.data[directory.length] == '/'); - dir = da_push(&os_linux_context.arena, fwctx); + OSLinuxEntity *fwd = os_entity_allocate(OSLinuxEntityKind_FileWatchDirectory); + dir = &fwd->as.file_watch_directory; + DLLInsert(0, os_linux_context.file_watch_directories.first, + os_linux_context.file_watch_directories.last, dir, next, prev); + dir->hash = hash; dir->name = push_str8(&os_linux_context.arena, directory); u32 mask = IN_MOVED_TO|IN_CLOSE_WRITE; dir->handle = inotify_add_watch(os_linux_context.inotify_handle, (c8 *)dir->name.data, mask); } - OSLinux_FileWatch *fw = da_push(&os_linux_context.arena, dir); + OSLinuxEntity *fwe = os_entity_allocate(OSLinuxEntityKind_FileWatch); + OSLinuxFileWatch *fw = &fwe->as.file_watch; + DLLInsert(0, dir->first_child, dir->last_child, fw, next, prev); fw->user_context = user_context; fw->hash = u64_hash_from_str8(str8_cut_head(path, dir->name.length + 1)); fw->kind = kind; + fw->parent = dir; } BEAMFORMER_IMPORT void os_add_file_watch(const char *path, int64_t path_length, void *user_context) { str8 path_str = {.data = (u8 *)path, .length = path_length}; - os_linux_add_file_watch(path_str, user_context, OSLinux_FileWatchKindUser); + os_linux_add_file_watch(path_str, user_context, OSLinuxFileWatchKind_User); +} + +function void +os_window_resize_callback(void *window, i32 width, i32 height) +{ + OSWindow event_window = {0}; + for (OSLinuxEntity *we = os_linux_context.windows.first; we; we = we->as.window.next) { + if (we->as.window.handle == window) { + event_window.value[0] = (u64)we; + break; + } + } + + os_push_input_event(beamformer_input, (BeamformerInputEvent){ + .kind = BeamformerInputEventKind_WindowResize, + .window_resize = { + .width = (u32)width, .height = (u32)height, + .window = event_window, + }, + }); + + raylib_window_resize(window, width, height); +} + +BEAMFORMER_IMPORT OSWindow +os_window_create(u8 *title, i64 title_length, i32 width, i32 height) +{ + OSLinuxEntity *we = os_entity_allocate(OSLinuxEntityKind_Window); + OSWindow result = {(u64)we}; + DLLInsert(0, os_linux_context.windows.first, os_linux_context.windows.last, we, as.window.next, as.window.prev); + + SetConfigFlags(FLAG_VSYNC_HINT|FLAG_WINDOW_ALWAYS_RUN); + + str8 name = {.data = title, .length = title_length}; + DeferLoop(take_lock(&os_linux_context.arena_lock, -1), release_lock(&os_linux_context.arena_lock)) + { + Arena scratch = os_linux_context.arena; + name.length = Min(name.length, arena_capacity(&scratch, u8) - 1); + str8 title_string = push_str8(&scratch, name); + InitWindow(width, height, (char *)title_string.data); + } + + we->as.window.handle = GetPlatformWindowHandle(); + os_window_equip_common(os_linux_context.input, we->as.window.handle); + raylib_window_resize = glfwSetWindowSizeCallback(we->as.window.handle, os_window_resize_callback); + + /* NOTE: do this after initing so that the window starts out floating in tiling wm */ + SetWindowState(FLAG_WINDOW_RESIZABLE); + SetWindowMinSize(320, 240); + + return result; } function OSLibrary @@ -226,6 +338,8 @@ debug_library_reload(BeamformerInput *input) beamformer_library_handle = new_handle; } } +#else +#define debug_library_reload(a) (void)(a) #endif /* BEAMFORMER_DEBUG */ function void @@ -234,7 +348,7 @@ load_platform_libraries(BeamformerInput *input) #if BEAMFORMER_DEBUG debug_library_reload(input); os_linux_add_file_watch(str8(OS_DEBUG_LIB_NAME), (void *)BeamformerInputEventKind_ExecutableReload, - OSLinux_FileWatchKindPlatform); + OSLinuxFileWatchKind_Platform); #endif input->vulkan_library_handle = (OSLibrary){OSInvalidHandleValue}; @@ -259,46 +373,39 @@ load_platform_libraries(BeamformerInput *input) function void dispatch_file_watch_events(BeamformerInput *input) { - OSLinux_FileWatchDirectoryList *fwctx = &os_linux_context.file_watch_list; Arena arena = os_linux_context.arena; u8 *mem = arena_alloc(&arena, .size = 4096, .align = 16); struct inotify_event *event; u64 current_time = os_timer_count(); - iz rlen; + i64 rlen; while ((rlen = read(os_linux_context.inotify_handle, mem, 4096)) > 0) { for (u8 *data = mem; data < mem + rlen; data += sizeof(*event) + event->len) { event = (struct inotify_event *)data; - for (da_count i = 0; i < fwctx->count; i++) { - OSLinux_FileWatchDirectory *dir = fwctx->data + i; + for (OSLinuxFileWatchDirectory *dir = os_linux_context.file_watch_directories.first; dir; dir = dir->next) { if (event->wd != dir->handle) continue; str8 file = str8_from_c_str(event->name); u64 hash = u64_hash_from_str8(file); - for (da_count j = 0; j < dir->count; j++) { - OSLinux_FileWatch *fw = dir->data + j; - if (fw->hash == hash) { - // NOTE(rnp): avoid multiple updates in a single frame - if (fw->update_time < current_time) { - BeamformerInputEvent input_event = {0}; - if (fw->kind == OSLinux_FileWatchKindPlatform) { - assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload); - #if BEAMFORMER_DEBUG - if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload) - debug_library_reload(input); - #endif - input_event.kind = (u64)fw->user_context; - } else { - input_event.kind = BeamformerInputEventKind_FileEvent; - input_event.file_watch_user_context = fw->user_context; - } - os_push_input_event(input, input_event); + for (OSLinuxFileWatch *fw = dir->first_child; fw; fw = fw->next) if (fw->hash == hash) { + // NOTE(rnp): avoid multiple updates in a single frame + if (fw->update_time < current_time) { + BeamformerInputEvent input_event = {0}; + if (fw->kind == OSLinuxFileWatchKind_Platform) { + assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload); + if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload) + debug_library_reload(input); + input_event.kind = (u64)fw->user_context; + } else { + input_event.kind = BeamformerInputEventKind_FileEvent; + input_event.file_watch_user_context = fw->user_context; } - fw->update_time = current_time; - break; + os_push_input_event(input, input_event); } + fw->update_time = current_time; + break; } } } @@ -313,12 +420,13 @@ main(void) os_linux_context.system_info.page_size = ARCH_X64? KB(4) : getauxval(AT_PAGESZ); os_linux_context.system_info.path_separator_byte = '/'; - Arena program_memory = os_alloc_arena(MB(16) + KB(16)); + Arena program_memory = os_alloc_arena(MB(16) + MB(1)); - os_linux_context.arena = sub_arena(&program_memory, KB(16), KB(4)); + os_linux_context.arena = sub_arena(&program_memory, MB(1), KB(4)); os_linux_context.inotify_handle = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); BeamformerInput *input = push_struct(&program_memory, BeamformerInput); + os_linux_context.input = input; input->memory = program_memory.beg; input->memory_size = program_memory.end - program_memory.beg; input->shared_memory = allocate_shared_memory(OS_SHARED_MEMORY_NAME, OS_SHARED_MEMORY_SIZE, @@ -350,7 +458,9 @@ main(void) beamformer_frame_step(input); // NOTE(rnp): this must happen at the end of frame to allow the pre loop events through - input->event_count = 0; + // TODO(rnp): hack: until raylib is removed this happens in ui since raylib will cause + // glfw to call the input callbacks in during EndDrawing() + //input->event_count = 0; } beamformer_terminate(input); diff --git a/main_w32.c b/main_w32.c @@ -59,46 +59,100 @@ W32(i32) SetThreadDescription(u64, u16 *); X("vulkan-1.dll") \ enum {OSW32_FileWatchDirectoryBufferSize = KB(4)}; -typedef enum { - OSW32_FileWatchKindPlatform, - OSW32_FileWatchKindUser, -} OSW32_FileWatchKind; +typedef struct OSW32Entity OSW32Entity; typedef struct { - OSW32_FileWatchKind kind; + void *handle; + OSW32Entity *prev, *next; +} OSW32Window; + +typedef enum { + OSW32FileWatchKind_Platform, + OSW32FileWatchKind_User, +} OSW32FileWatchKind; + +typedef struct OSW32FileWatchDirectory OSW32FileWatchDirectory; +typedef struct OSW32FileWatch OSW32FileWatch; +struct OSW32FileWatch { + OSW32FileWatchKind kind; u64 hash; u64 update_time; - void * user_context; -} OSW32_FileWatch; + void *user_context; -typedef struct { + OSW32FileWatchDirectory *parent; + OSW32FileWatch *prev, *next; +}; + +struct OSW32FileWatchDirectory { u64 hash; i64 handle; str8 name; - OSW32_FileWatch *data; - da_count count; - da_count capacity; + OSW32FileWatch *first_child; + OSW32FileWatch *last_child; + OSW32FileWatchDirectory *prev, *next; w32_overlapped overlapped; w32_io_completion_event event; void *buffer; -} OSW32_FileWatchDirectory; -DA_STRUCT(OSW32_FileWatchDirectory, OSW32_FileWatchDirectory); +}; + +typedef enum { + OSW32EntityKind_Window, + OSW32EntityKind_FileWatch, + OSW32EntityKind_FileWatchDirectory, +} OSW32EntityKind; + +struct OSW32Entity { + OSW32EntityKind kind; + union { + OSW32FileWatch file_watch; + OSW32FileWatchDirectory file_watch_directory; + OSW32Window window; + } as; + OSW32Entity *next; +}; typedef struct { Arena arena; i32 arena_lock; - iptr error_handle; - iptr io_completion_handle; + i64 error_handle; + i64 io_completion_handle; + + BeamformerInput *input; + + struct { + OSW32FileWatchDirectory *first; + OSW32FileWatchDirectory *last; + } file_watch_directories; - OSW32_FileWatchDirectoryList file_watch_list; + struct { + OSW32Entity *first; + OSW32Entity *last; + } windows; + + OSW32Entity *entity_freelist; OSSystemInfo system_info; } OSW32_Context; global OSW32_Context os_w32_context; +function OSW32Entity * +os_entity_allocate(OSW32EntityKind kind) +{ + OSW32Entity *result = 0; + DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) + { + result = SLLPopFreelist(os_w32_context.entity_freelist); + if (!result) push_struct_no_zero(&os_w32_context.arena, OSW32Entity); + } + + zero_struct(result); + result->kind = kind; + return result; +} + BEAMFORMER_IMPORT OSSystemInfo * os_system_info(void) { @@ -177,31 +231,36 @@ allocate_shared_memory(char *name, iz requested_capacity, u64 *capacity) return result; } -function OSW32_FileWatchDirectory * -os_lookup_file_watch_directory(OSW32_FileWatchDirectoryList *ctx, u64 hash) +function OSW32FileWatchDirectory * +os_lookup_file_watch_directory(u64 hash) { - OSW32_FileWatchDirectory *result = 0; - for (da_count i = 0; !result && i < ctx->count; i++) - if (ctx->data[i].hash == hash) - result = ctx->data + i; + OSW32FileWatchDirectory *result = 0; + for (OSW32FileWatchDirectory *fwd = os_w32_context.file_watch_directories.first; fwd; fwd = fwd->next) { + if (fwd->hash == hash) { + result = fwd; + break; + } + } return result; } function void -os_w32_add_file_watch(str8 path, void *user_context, OSW32_FileWatchKind kind) +os_w32_add_file_watch(str8 path, void *user_context, OSW32FileWatchKind kind) { str8 directory = path; directory.length = str8_scan_backwards(path, '\\'); assert(directory.length > 0); - OSW32_FileWatchDirectoryList *fwctx = &os_w32_context.file_watch_list; - u64 hash = u64_hash_from_str8(directory); - OSW32_FileWatchDirectory *dir = os_lookup_file_watch_directory(fwctx, hash); + OSW32FileWatchDirectory *dir = os_lookup_file_watch_directory(hash); if (!dir) { assert(path.data[directory.length] == '\\'); - dir = da_push(&os_w32_context.arena, fwctx); + OSW32Entity *fwd = os_entity_allocate(OSW32EntityKind_FileWatchDirectory); + dir = &fwd->as.file_watch_directory; + DLLInsert(0, os_w32_context.file_watch_directories.first, + os_w32_context.file_watch_directories.last, dir, next, prev); + dir->hash = hash; dir->name = push_str8(&os_w32_context.arena, directory); dir->handle = CreateFileA((c8 *)dir->name.data, GENERIC_READ, FILE_SHARE_READ, 0, @@ -217,17 +276,71 @@ os_w32_add_file_watch(str8 path, void *user_context, OSW32_FileWatchKind kind) FILE_NOTIFY_CHANGE_LAST_WRITE, 0, &dir->overlapped, 0); } - OSW32_FileWatch *fw = da_push(&os_w32_context.arena, dir); + OSW32Entity *fwe = os_entity_allocate(OSW32EntityKind_FileWatch); + OSW32FileWatch *fw = &fwe->as.file_watch; + DLLInsert(0, dir->first_child, dir->last_child, fw, next, prev); fw->user_context = user_context; fw->hash = u64_hash_from_str8(str8_cut_head(path, dir->name.length + 1)); fw->kind = kind; + fw->parent = dir; } BEAMFORMER_IMPORT void os_add_file_watch(const char *path, int64_t path_length, void *user_context) { str8 path_str = {.data = (u8 *)path, .length = path_length}; - os_w32_add_file_watch(path_str, user_context, OSW32_FileWatchKindUser); + os_w32_add_file_watch(path_str, user_context, OSW32FileWatchKind_User); +} + +function void +os_window_resize_callback(void *window, i32 width, i32 height) +{ + OSWindow event_window = {0}; + for (OSW32Entity *we = os_w32_context.windows.first; we; we = we->as.window.next) { + if (we->as.window.handle == window) { + event_window.value[0] = (u64)we; + break; + } + } + + os_push_input_event(beamformer_input, (BeamformerInputEvent){ + .kind = BeamformerInputEventKind_WindowResize, + .window_resize = { + .width = (u32)width, .height = (u32)height, + .window = event_window, + }, + }); + + raylib_window_resize(window, width, height); +} + +BEAMFORMER_IMPORT OSWindow +os_window_create(u8 *title, i64 title_length, i32 width, i32 height) +{ + OSW32Entity *we = os_entity_allocate(OSW32EntityKind_Window); + OSWindow result = {(u64)we}; + DLLInsert(0, os_w32_context.windows.first, os_w32_context.windows.last, we, as.window.next, as.window.prev); + + SetConfigFlags(FLAG_VSYNC_HINT|FLAG_WINDOW_ALWAYS_RUN); + + str8 name = {.data = title, .length = title_length}; + DeferLoop(take_lock(&os_w32_context.arena_lock, -1), release_lock(&os_w32_context.arena_lock)) + { + Arena scratch = os_w32_context.arena; + name.length = Min(name.length, arena_capacity(&scratch, u8) - 1); + str8 title_string = push_str8(&scratch, name); + InitWindow(width, height, (char *)title_string.data); + } + + we->as.window.handle = GetPlatformWindowHandle(); + os_window_equip_common(os_w32_context.input, we->as.window.handle); + raylib_window_resize = glfwSetWindowSizeCallback(we->as.window.handle, os_window_resize_callback); + + /* NOTE: do this after initing so that the window starts out floating in tiling wm */ + SetWindowState(FLAG_WINDOW_RESIZABLE); + SetWindowMinSize(320, 240); + + return result; } #if BEAMFORMER_RENDERDOC_HOOKS @@ -275,6 +388,8 @@ debug_library_reload(BeamformerInput *input) beamformer_library_handle = new_handle; } } +#else +#define debug_library_reload(a) (void)(a) #endif /* BEAMFORMER_DEBUG */ function void @@ -283,7 +398,7 @@ load_platform_libraries(BeamformerInput *input) #if BEAMFORMER_DEBUG debug_library_reload(input); os_w32_add_file_watch(str8(OS_DEBUG_LIB_NAME), (void *)BeamformerInputEventKind_ExecutableReload, - OSW32_FileWatchKindPlatform); + OSW32FileWatchKind_Platform); #endif input->vulkan_library_handle = (OSLibrary){OSInvalidHandleValue}; @@ -306,7 +421,7 @@ load_platform_libraries(BeamformerInput *input) } function void -dispatch_file_watch(BeamformerInput *input, Arena arena, u64 current_time, OSW32_FileWatchDirectory *fw_dir) +dispatch_file_watch(BeamformerInput *input, Arena arena, u64 current_time, OSW32FileWatchDirectory *fw_dir) { TempArena save_point = {0}; i64 offset = 0; @@ -328,28 +443,23 @@ dispatch_file_watch(BeamformerInput *input, Arena arena, u64 current_time, OSW32 str8 file_name = str8_from_str16(&arena, (str16){.data = fni->filename, .length = fni->filename_size / 2}); u64 hash = u64_hash_from_str8(file_name); - for (da_count i = 0; i < fw_dir->count; i++) { - OSW32_FileWatch *fw = fw_dir->data + i; - if (fw->hash == hash) { - // NOTE(rnp): avoid multiple updates in a single frame - if (fw->update_time < current_time) { - BeamformerInputEvent input_event = {0}; - if (fw->kind == OSW32_FileWatchKindPlatform) { - assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload); - #if BEAMFORMER_DEBUG - if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload) - debug_library_reload(input); - #endif - input_event.kind = (u64)fw->user_context; - } else { - input_event.kind = BeamformerInputEventKind_FileEvent; - input_event.file_watch_user_context = fw->user_context; - } - os_push_input_event(input, input_event); + for (OSW32FileWatch *fw = fw_dir->first_child; fw; fw = fw->next) if (fw->hash == hash) { + // NOTE(rnp): avoid multiple updates in a single frame + if (fw->update_time < current_time) { + BeamformerInputEvent input_event = {0}; + if (fw->kind == OSW32FileWatchKind_Platform) { + assert((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload); + if ((u64)fw->user_context == BeamformerInputEventKind_ExecutableReload) + debug_library_reload(input); + input_event.kind = (u64)fw->user_context; + } else { + input_event.kind = BeamformerInputEventKind_FileEvent; + input_event.file_watch_user_context = fw->user_context; } - fw->update_time = current_time; - break; + os_push_input_event(input, input_event); } + fw->update_time = current_time; + break; } offset = fni->next_entry_offset; @@ -371,7 +481,7 @@ clear_io_queue(BeamformerInput *input, Arena arena) w32_io_completion_event *event = (w32_io_completion_event *)user_data; switch (event->tag) { case W32IOEvent_FileWatch:{ - OSW32_FileWatchDirectory *dir = (OSW32_FileWatchDirectory *)event->context; + OSW32FileWatchDirectory *dir = (OSW32FileWatchDirectory *)event->context; dispatch_file_watch(input, arena, current_time, dir); zero_struct(&dir->overlapped); ReadDirectoryChangesW(dir->handle, dir->buffer, OSW32_FileWatchDirectoryBufferSize, 0, @@ -400,6 +510,7 @@ main(void) os_w32_context.arena = sub_arena(&program_memory, MB(2), os_w32_context.system_info.page_size); BeamformerInput *input = push_struct(&program_memory, BeamformerInput); + os_w32_context.input = input; input->memory = program_memory.beg; input->memory_size = program_memory.end - program_memory.beg; input->shared_memory = allocate_shared_memory(OS_SHARED_MEMORY_NAME, OS_SHARED_MEMORY_SIZE, @@ -428,7 +539,9 @@ main(void) beamformer_frame_step(input); // NOTE(rnp): this must happen at the end of frame to allow the pre loop events through - input->event_count = 0; + // TODO(rnp): hack: until raylib is removed this happens in ui since raylib will cause + // glfw to call the input callbacks in during EndDrawing() + //input->event_count = 0; } beamformer_terminate(input); diff --git a/ui.c b/ui.c @@ -4224,6 +4224,14 @@ draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformerFrame *frame_to_dr ui->latest_plane_valid[frame_plane] = 0; } + for EachIndex(input->event_count, it) { + if (input->event_queue[it].kind == BeamformerInputEventKind_WindowResize) { + // TODO(rnp): match window against window list + ctx->window_size.w = input->event_queue[it].window_resize.width; + ctx->window_size.h = input->event_queue[it].window_resize.height; + } + } + asan_poison_region(ui->arena.beg, ui->arena.end - ui->arena.beg); u32 selected_block = ui->selected_parameter_block % BeamformerMaxParameterBlocks; @@ -4339,6 +4347,10 @@ draw_ui(BeamformerCtx *ctx, BeamformerInput *input, BeamformerFrame *frame_to_dr draw_ui_regions(ui, window_rect, mouse); draw_floating_widgets(ui, window_rect, mouse); + + // TODO(rnp): hack: until raylib is removed this happens in ui since raylib will cause + // glfw to call the input callbacks during EndDrawing() + input->event_count = 0; EndDrawing(); ui->last_mouse = (v2){{input->mouse_x, input->mouse_y}}; diff --git a/util_os_ui.c b/util_os_ui.c @@ -2,6 +2,17 @@ // NOTE(rnp): functions which require platform layer support but // otherwise share implementation +// TODO(rnp): replace all this with platform specific functions + +void *GetPlatformWindowHandle(void); + +// see: external/raylib/src/external/glfw/include/GLFW/glfw3.h +typedef void glfw_window_resize_fn(void *window, i32 width, i32 height); + +glfw_window_resize_fn *glfwSetWindowSizeCallback(void *window, glfw_window_resize_fn *callback); + +global BeamformerInput *beamformer_input; +global glfw_window_resize_fn *raylib_window_resize; function void os_push_input_event(BeamformerInput *input, BeamformerInputEvent event) @@ -12,6 +23,12 @@ os_push_input_event(BeamformerInput *input, BeamformerInputEvent event) } function void +os_window_equip_common(BeamformerInput *input, void *window) +{ + beamformer_input = input; +} + +function void os_build_frame_input(BeamformerInput *input) { Vector2 new_mouse = {-1, -1};