From 234e3ed44052e43ac21ddafe57321d5647d4e323 Mon Sep 17 00:00:00 2001 From: Charlie Malmqvist Date: Fri, 26 Jul 2024 21:33:04 +0200 Subject: [PATCH] Mouse pointers & text rendering bug --- TODO | 15 +- build.bat | 2 +- build.c | 4 +- changelog.txt | 14 ++ oogabooga/cpu.c | 8 +- oogabooga/drawing.c | 1 - oogabooga/examples/renderer_stress_test.c | 7 +- oogabooga/font.c | 4 +- oogabooga/gfx_impl_d3d11.c | 79 ++++++--- oogabooga/oogabooga.c | 3 +- oogabooga/os_impl_windows.c | 190 +++++++++++++++++++++- oogabooga/os_interface.c | 47 +++++- 12 files changed, 329 insertions(+), 45 deletions(-) diff --git a/TODO b/TODO index acba88d..5995d2f 100644 --- a/TODO +++ b/TODO @@ -12,15 +12,13 @@ - Optimize - Spam simd - Concurrent jobs for players? - - Mega buffer for contiguous intermediate buffers - We definitely also want a limit to how much memory we want allocated to intermediate buffers. + - Pool of intermediate buffers (2^) - Bugs: - Small fadeout on pause is slightly noisy - Setting time stamp/progression causes noise (need fade transition like on pause/play) - End of clip also causes noise if audio clip does not end smoothly - Setting audio source to a format which differs from audio output format in both channels and bit_width at the same time will produce pure loud noise. - 24-Bit audio conversion doesn't really work - - Converting 24-bit audio files doesn't really work - General bugs & issues - Release freeze in run_tests @@ -30,6 +28,10 @@ - Renderer - API to pass constant values to shader (codegen #define's) + - Draw_Frame instances + - Or draw to a custom Quad_Buffer, then draw quad buffers ? + - draw_deferred ? + - Draw_Frame Render_Image - Fonts - Atlases are way too big, render atlases with size depending on font_height (say, 128 codepoints per atlas) @@ -38,9 +40,16 @@ - Window::bool is_minimized - don't set window.width & window.height to 0 - Sockets recv, send + - Mouse pointer + - Hide mouse pointer - Arenas + +- Examples/Guides: + - Scaling text for pixel perfect rendering + - Z sorting + - Scissor boxing - Needs testing: - Audio format channel conversions diff --git a/build.bat b/build.bat index ed21dba..8df5b8a 100644 --- a/build.bat +++ b/build.bat @@ -6,6 +6,6 @@ mkdir build pushd build -clang -g -o cgame.exe ../build.c -O0 -std=c11 -D_CRT_SECURE_NO_WARNINGS -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -lkernel32 -lgdi32 -luser32 -lruntimeobject -lwinmm -ld3d11 -ldxguid -ld3dcompiler -lshlwapi -lole32 -lavrt -lksuser -ldbghelp -femit-all-decls +clang -g -fuse-ld=lld -o cgame.exe ../build.c -O0 -std=c11 -D_CRT_SECURE_NO_WARNINGS -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -lkernel32 -lgdi32 -luser32 -lruntimeobject -lwinmm -ld3d11 -ldxguid -ld3dcompiler -lshlwapi -lole32 -lavrt -lksuser -ldbghelp -femit-all-decls popd \ No newline at end of file diff --git a/build.c b/build.c index fbb44aa..9e16373 100644 --- a/build.c +++ b/build.c @@ -37,11 +37,11 @@ typedef struct Context_Extra { // #include "oogabooga/examples/text_rendering.c" // #include "oogabooga/examples/custom_logger.c" -// #include "oogabooga/examples/renderer_stress_test.c" +#include "oogabooga/examples/renderer_stress_test.c" // #include "oogabooga/examples/tile_game.c" // #include "oogabooga/examples/audio_test.c" // #include "oogabooga/examples/custom_shader.c" -#include "oogabooga/examples/growing_array_example.c" +// #include "oogabooga/examples/growing_array_example.c" // This is where you swap in your own project! // #include "entry_yourepicgamename.c" diff --git a/changelog.txt b/changelog.txt index c73d40c..0b531ee 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,18 @@ +## v0.01.003 - + - Os layer + - Implemented setting of mouse pointers, either to system standard pointers or a custom image + - Ignore SETCURSOR events unless window resize + - Store SYSTEM_INFO in init + - os_get_number_of_logical_processors() + + - Renderer + - Fix bad uv sampling bug when uneven window dimensions + + - Misc + - + + ## v0.01.002 - Flexible build options, Hotloading, growing array - Build system - We can now compile with: diff --git a/oogabooga/cpu.c b/oogabooga/cpu.c index fc1ad99..087ebd4 100644 --- a/oogabooga/cpu.c +++ b/oogabooga/cpu.c @@ -28,9 +28,10 @@ typedef struct Cpu_Capabilities { #if COMPILER_MVSC #define inline __forceinline #define alignat(x) __declspec(align(x)) + #define noreturn __declspec(noreturn) #define COMPILER_HAS_MEMCPY_INTRINSICS 1 inline void - crash() { + crash() noreturn { __debugbreak(); volatile int *a = 0; *a = 5; @@ -113,10 +114,11 @@ typedef struct Cpu_Capabilities { #elif COMPILER_GCC || COMPILER_CLANG #define inline __attribute__((always_inline)) inline - #define alignat(x) __attribute__((aligned(x))) + #define alignat(x) __attribute__((aligned(x))) + #define noreturn __attribute__((noreturn)) #define COMPILER_HAS_MEMCPY_INTRINSICS 1 - inline void __attribute__((noreturn)) + inline void noreturn crash() { __builtin_trap(); volatile int *a = 0; diff --git a/oogabooga/drawing.c b/oogabooga/drawing.c index 8008d22..239ebe7 100644 --- a/oogabooga/drawing.c +++ b/oogabooga/drawing.c @@ -14,7 +14,6 @@ Draw_Quad *draw_quad_projected(Draw_Quad quad, Matrix4 world_to_clip); Draw_Quad *draw_quad(Draw_Quad quad); Draw_Quad *draw_quad_xform(Draw_Quad quad, Matrix4 xform); - bool draw_text_callback(Gfx_Glyph glyph, Gfx_Font_Atlas *atlas, float glyph_x, float glyph_y, void *ud); void draw_text_xform(Gfx_Font *font, string text, u32 raster_height, Matrix4 xform, Vector2 scale, Vector4 color); void draw_text(Gfx_Font *font, string text, u32 raster_height, Vector2 position, Vector2 scale, Vector4 color); Gfx_Text_Metrics draw_text_and_measure(Gfx_Font *font, string text, u32 raster_height, Vector2 position, Vector2 scale, Vector4 color); diff --git a/oogabooga/examples/renderer_stress_test.c b/oogabooga/examples/renderer_stress_test.c index ee7b36d..4fbc465 100644 --- a/oogabooga/examples/renderer_stress_test.c +++ b/oogabooga/examples/renderer_stress_test.c @@ -15,6 +15,11 @@ int entry(int argc, char **argv) { Gfx_Image *hammer_image = load_image_from_disk(STR("oogabooga/examples/hammer.png"), get_heap_allocator()); assert(hammer_image, "Failed loading hammer.png"); + Custom_Mouse_Pointer hammer_pointer + = os_make_custom_mouse_pointer_from_file(STR("oogabooga/examples/hammer.png"), 16, 16, get_heap_allocator()); + assert(hammer_pointer != 0, "Could not load hammer pointer"); + + void *my_data = alloc(get_heap_allocator(), 32*32*4); memset(my_data, 0xffffffff, 32*32*4); Gfx_Image *my_image = make_image(32, 32, 4, my_data, get_heap_allocator()); @@ -54,7 +59,7 @@ int entry(int argc, char **argv) { if (is_key_just_released(KEY_ESCAPE)) { window.should_close = true; } - + os_set_mouse_pointer_custom(hammer_pointer); if (is_key_just_pressed(KEY_ARROW_LEFT)) { window.x -= 10; } diff --git a/oogabooga/font.c b/oogabooga/font.c index 19f1bca..42a6b4f 100644 --- a/oogabooga/font.c +++ b/oogabooga/font.c @@ -244,8 +244,8 @@ void font_atlas_init(Gfx_Font_Atlas *atlas, Gfx_Font_Variation *variation, u32 f glyph->advance = (float)advance*variation->scale; //glyph->xoffset += (float)left_side_bearing*variation->scale; - glyph->uv.x1 = (float)cursor_x/(float)FONT_ATLAS_WIDTH; - glyph->uv.y1 = (float)cursor_y/(float)FONT_ATLAS_HEIGHT; + glyph->uv.x1 = ((float)cursor_x)/(float)FONT_ATLAS_WIDTH; + glyph->uv.y1 = ((float)cursor_y)/(float)FONT_ATLAS_HEIGHT; glyph->uv.x2 = ((float)cursor_x+glyph->width)/(float)FONT_ATLAS_WIDTH; glyph->uv.y2 = ((float)cursor_y+glyph->height)/(float)FONT_ATLAS_HEIGHT; diff --git a/oogabooga/gfx_impl_d3d11.c b/oogabooga/gfx_impl_d3d11.c index e81c9cd..4514a9f 100644 --- a/oogabooga/gfx_impl_d3d11.c +++ b/oogabooga/gfx_impl_d3d11.c @@ -674,8 +674,21 @@ void d3d11_process_draw_frame() { } if (q->type == QUAD_TYPE_TEXT) { + + // This is meant to fix the annoying artifacts that shows up when sampling text from an atlas + // presumably for floating point precision issues or something. + + // #Incomplete + // If we want to animate text with small movements then it will look wonky. + // This should be optional probably. + // Also, we might want to do this on non-text if rendering with linear filtering + // from a large texture atlas. + float pixel_width = 2.0/(float)window.width; float pixel_height = 2.0/(float)window.height; + + bool xeven = window.width % 2 == 0; + bool yeven = window.height % 2 == 0; q->bottom_left.x = round(q->bottom_left.x / pixel_width) * pixel_width; q->bottom_left.y = round(q->bottom_left.y / pixel_height) * pixel_height; @@ -703,10 +716,50 @@ void d3d11_process_draw_frame() { TR->position = v4(q->top_right.x, q->top_right.y, 0, 1); BR->position = v4(q->bottom_right.x, q->bottom_right.y, 0, 1); - BL->uv = v2(q->uv.x1, q->uv.y1); - TL->uv = v2(q->uv.x1, q->uv.y2); - TR->uv = v2(q->uv.x2, q->uv.y2); - BR->uv = v2(q->uv.x2, q->uv.y1); + + if (q->image) { + + BL->uv = v2(q->uv.x1, q->uv.y1); + TL->uv = v2(q->uv.x1, q->uv.y2); + TR->uv = v2(q->uv.x2, q->uv.y2); + BR->uv = v2(q->uv.x2, q->uv.y1); + // #Hack #Bug #Cleanup + // When a window dimension is uneven it slightly under/oversamples on an axis by a + // seemingly arbitrary amount. The 0.25 is a magic value I got from trial and error. + // (It undersamples by a fourth of the atlas texture?) + // Anything > 0.25 < will slightly over/undersample on my machine. + // I have no idea about #Portability here. + // - Charlie M 26th July 2024 + if (window.width % 2 != 0) { + BL->uv.x += (2.0/(float)q->image->width)*0.25; + TL->uv.x += (2.0/(float)q->image->width)*0.25; + TR->uv.x += (2.0/(float)q->image->width)*0.25; + BR->uv.x += (2.0/(float)q->image->width)*0.25; + } + if (window.height % 2 != 0) { + BL->uv.y -= (2.0/(float)q->image->height)*0.25; + TL->uv.y -= (2.0/(float)q->image->height)*0.25; + TR->uv.y -= (2.0/(float)q->image->height)*0.25; + BR->uv.y -= (2.0/(float)q->image->height)*0.25; + } + + u8 sampler = -1; + if (q->image_min_filter == GFX_FILTER_MODE_NEAREST + && q->image_mag_filter == GFX_FILTER_MODE_NEAREST) + sampler = 0; + if (q->image_min_filter == GFX_FILTER_MODE_LINEAR + && q->image_mag_filter == GFX_FILTER_MODE_LINEAR) + sampler = 1; + if (q->image_min_filter == GFX_FILTER_MODE_LINEAR + && q->image_mag_filter == GFX_FILTER_MODE_NEAREST) + sampler = 2; + if (q->image_min_filter == GFX_FILTER_MODE_NEAREST + && q->image_mag_filter == GFX_FILTER_MODE_LINEAR) + sampler = 3; + BL->sampler=TL->sampler=TR->sampler=BR->sampler = (u8)sampler; + + } + BL->texture_index=TL->texture_index=TR->texture_index=BR->texture_index = texture_index; BL->self_uv = v2(0, 0); TL->self_uv = v2(0, 1); @@ -721,7 +774,6 @@ void d3d11_process_draw_frame() { BL->color = TL->color = TR->color = BR->color = q->color; - BL->texture_index=TL->texture_index=TR->texture_index=BR->texture_index = texture_index; BL->type=TL->type=TR->type=BR->type = (u8)q->type; float t = q->scissor.y1; @@ -734,23 +786,6 @@ void d3d11_process_draw_frame() { BL->has_scissor=TL->has_scissor=TR->has_scissor=BR->has_scissor = q->has_scissor; BL->scissor=TL->scissor=TR->scissor=BR->scissor = q->scissor; - u8 sampler = -1; - if (q->image_min_filter == GFX_FILTER_MODE_NEAREST - && q->image_mag_filter == GFX_FILTER_MODE_NEAREST) - sampler = 0; - if (q->image_min_filter == GFX_FILTER_MODE_LINEAR - && q->image_mag_filter == GFX_FILTER_MODE_LINEAR) - sampler = 1; - if (q->image_min_filter == GFX_FILTER_MODE_LINEAR - && q->image_mag_filter == GFX_FILTER_MODE_NEAREST) - sampler = 2; - if (q->image_min_filter == GFX_FILTER_MODE_NEAREST - && q->image_mag_filter == GFX_FILTER_MODE_LINEAR) - sampler = 3; - - BL->type=TL->type=TR->type=BR->type = (u8)q->type; - BL->sampler=TL->sampler=TR->sampler=BR->sampler = (u8)sampler; - *BL2 = *BL; *TR2 = *TR; diff --git a/oogabooga/oogabooga.c b/oogabooga/oogabooga.c index 171288c..975a8c2 100644 --- a/oogabooga/oogabooga.c +++ b/oogabooga/oogabooga.c @@ -118,7 +118,7 @@ #define OGB_VERSION_MAJOR 0 #define OGB_VERSION_MINOR 1 -#define OGB_VERSION_PATCH 2 +#define OGB_VERSION_PATCH 3 #define OGB_VERSION (OGB_VERSION_MAJOR*1000000+OGB_VERSION_MINOR*1000+OGB_VERSION_PATCH) @@ -228,6 +228,7 @@ typedef u8 bool; #ifdef _WIN32 #define COBJMACROS + #undef noreturn #include #if CONFIGURATION == DEBUG #include diff --git a/oogabooga/os_impl_windows.c b/oogabooga/os_impl_windows.c index 14c023c..efc9eb3 100644 --- a/oogabooga/os_impl_windows.c +++ b/oogabooga/os_impl_windows.c @@ -41,6 +41,12 @@ void win32_check_hr_impl(HRESULT hr, u32 line, const char* file_name) { } } +// #Global +bool win32_want_override_mouse_pointer = false; +HCURSOR win32_shadowed_mouse_pointer = 0; +bool win32_did_override_user_mouse_pointer = false; +SYSTEM_INFO win32_system_info; + #ifndef OOGABOOGA_HEADLESS // Persistent @@ -86,6 +92,8 @@ void win32_handle_key_repeat(Input_Key_Code code) { win32_send_key_event(code, win32_key_states[code]); } + + LRESULT CALLBACK win32_window_proc(HWND passed_window, UINT message, WPARAM wparam, LPARAM lparam) { if (window._initialized) { @@ -161,6 +169,32 @@ LRESULT CALLBACK win32_window_proc(HWND passed_window, UINT message, WPARAM wpar goto DEFAULT_HANDLE; } + case WM_SETCURSOR: { + + WORD hit_test = LOWORD(lparam); + WORD mouse_message = HIWORD(lparam); + + if (hit_test == HTLEFT || hit_test == HTRIGHT || hit_test == HTTOP || + hit_test == HTBOTTOM || hit_test == HTTOPLEFT || hit_test == HTTOPRIGHT || + hit_test == HTBOTTOMLEFT || hit_test == HTBOTTOMRIGHT) { + + // We are hovering the borders, let windows decide the pointer + win32_want_override_mouse_pointer = true; + + goto DEFAULT_HANDLE; + } else { + if (win32_want_override_mouse_pointer) { + win32_want_override_mouse_pointer = false; + if (win32_did_override_user_mouse_pointer) { + win32_did_override_user_mouse_pointer = false; + SetCursor(win32_shadowed_mouse_pointer); + } else { + goto DEFAULT_HANDLE; + } + } + } + break; + } default: DEFAULT_HANDLE: @@ -255,10 +289,11 @@ void os_init(u64 program_memory_size) { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - SYSTEM_INFO si; - GetSystemInfo(&si); - os.granularity = cast(u64)si.dwAllocationGranularity; - os.page_size = cast(u64)si.dwPageSize; + os_set_mouse_pointer_standard(MOUSE_POINTER_DEFAULT); + + GetSystemInfo(&win32_system_info); + os.granularity = cast(u64)win32_system_info.dwAllocationGranularity; + os.page_size = cast(u64)win32_system_info.dwPageSize; os.static_memory_start = 0; os.static_memory_end = 0; @@ -1031,18 +1066,25 @@ void fprintf(File f, const char* fmt, ...) { /// /// -// Memory +// Queries /// -void* os_get_stack_base() { +void* +os_get_stack_base() { NT_TIB* tib = (NT_TIB*)NtCurrentTeb(); return tib->StackBase; } -void* os_get_stack_limit() { +void* +os_get_stack_limit() { NT_TIB* tib = (NT_TIB*)NtCurrentTeb(); return tib->StackLimit; } +u64 +os_get_number_of_logical_processors() { + return (u64)win32_system_info.dwNumberOfProcessors; +} + /// /// // Debug @@ -1144,8 +1186,140 @@ os_get_stack_trace(u64 *trace_count, Allocator allocator) { #endif // NOT DEBUG } +/// +/// +// Mouse pointer -#ifndef OOGABOOGA_HEADLESS +LPCSTR +win32_mouse_pointer_kind_to_win32(Mouse_Pointer_Kind k) { + switch (k) { + case MOUSE_POINTER_DEFAULT: return IDC_ARROW; + case MOUSE_POINTER_TEXT_SELECT: return IDC_IBEAM; + case MOUSE_POINTER_BUSY: return IDC_WAIT; + case MOUSE_POINTER_BUSY_BACKGROUND: return IDC_APPSTARTING; + case MOUSE_POINTER_CROSS: return IDC_CROSS; + case MOUSE_POINTER_ARROW_N: return IDC_UPARROW; + case MOUSE_POINTER_ARROWS_NW_SE: return IDC_SIZENWSE; + case MOUSE_POINTER_ARROWS_NE_SW: return IDC_SIZENESW; + case MOUSE_POINTER_ARROWS_HORIZONTAL: return IDC_SIZEWE; + case MOUSE_POINTER_ARROWS_VERTICAL: return IDC_SIZENS; + case MOUSE_POINTER_ARROWS_ALL: return IDC_SIZEALL; + case MOUSE_POINTER_NO: return IDC_NO; + case MOUSE_POINTER_POINT: return IDC_HAND; + default: break; + } + panic("Unhandled Mouse_Pointer_Kind"); +} + +void ogb_instance +os_set_mouse_pointer_standard(Mouse_Pointer_Kind kind) { + thread_local local_persist HCURSOR loaded_pointers[MOUSE_POINTER_MAX] = {0}; + + if (loaded_pointers[kind] == 0) { + loaded_pointers[kind] = LoadCursor(0, win32_mouse_pointer_kind_to_win32(kind)); + } + + if (win32_want_override_mouse_pointer) { + win32_shadowed_mouse_pointer = loaded_pointers[kind]; + win32_did_override_user_mouse_pointer = true; + } else { + SetCursor(loaded_pointers[kind]); + } +} +void ogb_instance +os_set_mouse_pointer_custom(Custom_Mouse_Pointer p) { + if (win32_want_override_mouse_pointer) { + win32_shadowed_mouse_pointer = (HCURSOR)p; + win32_did_override_user_mouse_pointer = true; + } else { + SetCursor((HCURSOR)p); + } +} + +// Expects 32-bit rgba +Custom_Mouse_Pointer ogb_instance +os_make_custom_mouse_pointer(void *image, int width, int height, int hotspot_x, int hotspot_y) { + HICON icon = NULL; + HBITMAP bitmap = NULL; + ICONINFO icon_info = { 0 }; + + BITMAPINFO bmi = { 0 }; + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = width; + bmi.bmiHeader.biHeight = height; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + + BYTE* bits = NULL; + HDC hdc = GetDC(NULL); + bitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); + ReleaseDC(NULL, hdc); + if (!bitmap) { + assert(false, "Failed to create DIB section"); + return NULL; + } + + memcpy(bits, image, width * height * 4); + + icon_info.fIcon = FALSE; // Cursor, not icon + icon_info.xHotspot = hotspot_x; + icon_info.yHotspot = height-hotspot_y; + icon_info.hbmMask = bitmap; + icon_info.hbmColor = bitmap; + + icon = CreateIconIndirect(&icon_info); + if (!icon) { + assert(false, "Failed to create icon from bitmap"); + DeleteObject(bitmap); + return NULL; + } + + DeleteObject(bitmap); + + return icon; +} + +Custom_Mouse_Pointer ogb_instance +os_make_custom_mouse_pointer_from_file(string path, int hotspot_x, int hotspot_y, Allocator allocator) { + int width, height, channels; + stbi_set_flip_vertically_on_load(1); + third_party_allocator = allocator; + + string png; + bool ok = os_read_entire_file(path, &png, allocator); + + if (!ok) return 0; + + unsigned char* stb_data = stbi_load_from_memory( + png.data, + png.count, + &width, + &height, + &channels, + STBI_rgb_alpha + ); + + if (!stb_data) { + dealloc_string(allocator, png); + return 0; + } + + Custom_Mouse_Pointer p = os_make_custom_mouse_pointer(stb_data, width, height, hotspot_x, hotspot_y); + + dealloc_string(allocator, png); + stbi_image_free(stb_data); + third_party_allocator = ZERO(Allocator); + + return p; + +} + + + + + +#ifndef OOGABOOGA_HEADLESS // No audio in headless // Actually fuck you bill gates const GUID CLSID_MMDeviceEnumerator = {0xbcde0395, 0xe52f, 0x467c, {0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e}}; diff --git a/oogabooga/os_interface.c b/oogabooga/os_interface.c index e2d36e5..41948c0 100644 --- a/oogabooga/os_interface.c +++ b/oogabooga/os_interface.c @@ -200,6 +200,7 @@ typedef enum Os_Io_Open_Flags { // To append, pass WRITE flag without CREATE flag } Os_Io_Open_Flags; +// Returns OS_INVALID_FILE on fail File ogb_instance os_file_open_s(string path, Os_Io_Open_Flags flags); @@ -376,13 +377,16 @@ void fprint_va_list_buffered(File f, const string fmt, va_list args) { /// /// -// Memory +// Queries /// ogb_instance void* os_get_stack_base(); ogb_instance void* os_get_stack_limit(); +ogb_instance u64 +os_get_number_of_logical_processors(); + /// /// @@ -401,6 +405,47 @@ void dump_stack_trace() { } } +/// +/// +// Mouse pointer + +typedef enum Mouse_Pointer_Kind { + MOUSE_POINTER_DEFAULT = 0, // https://learn.microsoft.com/en-us/windows/win32/menurc/images/idc_arrow.png + MOUSE_POINTER_TEXT_SELECT = 10, // https://learn.microsoft.com/en-us/windows/win32/menurc/images/idc_ibeam.png + MOUSE_POINTER_BUSY = 20, // https://learn.microsoft.com/en-us/windows/win32/menurc/images/idc_wait.png + MOUSE_POINTER_BUSY_BACKGROUND = 30, // https://learn.microsoft.com/en-us/windows/win32/menurc/images/idc_appstarting.png + MOUSE_POINTER_CROSS = 40, // https://learn.microsoft.com/en-us/windows/win32/menurc/images/idc_cross.png + MOUSE_POINTER_ARROW_N = 50, // https://learn.microsoft.com/en-us/windows/win32/menurc/images/idc_uparrow.png + MOUSE_POINTER_ARROWS_NW_SE = 60, // https://learn.microsoft.com/en-us/windows/win32/menurc/images/idc_sizenwse.png + MOUSE_POINTER_ARROWS_NE_SW = 70, // https://learn.microsoft.com/en-us/windows/win32/menurc/images/idc_sizenesw.png + MOUSE_POINTER_ARROWS_HORIZONTAL = 80, // https://learn.microsoft.com/en-us/windows/win32/menurc/images/idc_sizewe.png + MOUSE_POINTER_ARROWS_VERTICAL = 90, // https://learn.microsoft.com/en-us/windows/win32/menurc/images/idc_sizens.png + MOUSE_POINTER_ARROWS_ALL = 100, // https://learn.microsoft.com/en-us/windows/win32/menurc/images/idc_sizeall.png + MOUSE_POINTER_NO = 110, // https://learn.microsoft.com/en-us/windows/win32/menurc/images/idc_no.png + MOUSE_POINTER_POINT = 120, // https://learn.microsoft.com/en-us/windows/win32/menurc/images/idc_hand.png + + MOUSE_POINTER_MAX, +} Mouse_Pointer_Kind; + +typedef void* Custom_Mouse_Pointer; + +void ogb_instance +os_set_mouse_pointer_standard(Mouse_Pointer_Kind kind); +void ogb_instance +os_set_mouse_pointer_custom(Custom_Mouse_Pointer p); + +// Expects 32-bit rgba +// Returns 0 on fail +Custom_Mouse_Pointer ogb_instance +os_make_custom_mouse_pointer(void *image, int width, int height, int hotspot_x, int hotspot_y); + +// Returns 0 on fail +// Will free everything that's allocated, passing temp allocator should be fine as long as the image is small +Custom_Mouse_Pointer ogb_instance +os_make_custom_mouse_pointer_from_file(string path, int hotspot_x, int hotspot_y, Allocator allocator); + + +/////////////////////////////////////////////// void ogb_instance os_init(u64 program_memory_size);