From b7921693c4260c17653fa2bb48f7d2f4a90085bf Mon Sep 17 00:00:00 2001 From: Charlie Malmqvist Date: Wed, 21 Aug 2024 20:17:05 +0200 Subject: [PATCH] Os monitors query --- build.bat | 2 +- build.c | 4 +- changelog.txt | 3 + oogabooga/base.c | 2 +- oogabooga/examples/minimal_game_loop.c | 4 +- oogabooga/examples/renderer_stress_test.c | 5 +- oogabooga/input.c | 2 + oogabooga/memory.c | 75 +++++++++++++++++++++- oogabooga/oogabooga.c | 21 ++++--- oogabooga/os_impl_windows.c | 77 ++++++++++++++++++++++- oogabooga/os_interface.c | 23 +++++-- oogabooga/string.c | 4 ++ oogabooga/string_format.c | 43 ++++++++++++- 13 files changed, 241 insertions(+), 24 deletions(-) diff --git a/build.bat b/build.bat index 8df5b8a..efa8428 100644 --- a/build.bat +++ b/build.bat @@ -6,6 +6,6 @@ mkdir build pushd build -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 +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 -lshcore -lavrt -lksuser -ldbghelp -femit-all-decls popd \ No newline at end of file diff --git a/build.c b/build.c index 2101fec..946da24 100644 --- a/build.c +++ b/build.c @@ -33,9 +33,9 @@ typedef struct Context_Extra { // // This is a minimal starting point for new projects. Copy & rename to get started -// #include "oogabooga/examples/minimal_game_loop.c" +#include "oogabooga/examples/minimal_game_loop.c" -#include "oogabooga/examples/text_rendering.c" +// #include "oogabooga/examples/text_rendering.c" // #include "oogabooga/examples/custom_logger.c" // #include "oogabooga/examples/renderer_stress_test.c" // #include "oogabooga/examples/tile_game.c" diff --git a/changelog.txt b/changelog.txt index f00dd9d..ab6bb91 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,6 @@ +## v0.01.005 + + ## v0.01.004 - Gamepad input, text wrapping, bug fixes - Input - Added Gamepad support diff --git a/oogabooga/base.c b/oogabooga/base.c index 563054a..e4dc70c 100644 --- a/oogabooga/base.c +++ b/oogabooga/base.c @@ -17,7 +17,7 @@ dump_stack_trace(); #define ASSERT_STR_HELPER(x) #x #define ASSERT_STR(x) ASSERT_STR_HELPER(x) -#define assert_line(line, cond, ...) {if(!(cond)) { printf("Assertion failed in file " __FILE__ " on line " ASSERT_STR(line) "\nFailed Condition: " #cond ". Message: " __VA_ARGS__); dump_stack_trace(); crash(); }} +#define assert_line(line, cond, ...) {if(!(cond)) { printf("\nAssertion failed in file " __FILE__ " on line " ASSERT_STR(line) "\n\nFailed Condition: " #cond ". Message: " __VA_ARGS__); printf("\n"); dump_stack_trace(); crash(); }} #define assert(cond, ...) {assert_line(__LINE__, cond, __VA_ARGS__)} #define DEFER(start, end) for(int _i_ = ((start), 0); _i_ == 0; _i_ += 1, (end)) diff --git a/oogabooga/examples/minimal_game_loop.c b/oogabooga/examples/minimal_game_loop.c index 672ca73..df11635 100644 --- a/oogabooga/examples/minimal_game_loop.c +++ b/oogabooga/examples/minimal_game_loop.c @@ -10,12 +10,12 @@ int entry(int argc, char **argv) { float64 last_time = os_get_elapsed_seconds(); while (!window.should_close) { + reset_temporary_storage(); + float64 now = os_get_elapsed_seconds(); if ((int)now != (int)last_time) log("%.2f FPS\n%.2fms", 1.0/(now-last_time), (now-last_time)*1000); last_time = now; - reset_temporary_storage(); - Matrix4 rect_xform = m4_scalar(1.0); rect_xform = m4_rotate_z(rect_xform, (f32)now); rect_xform = m4_translate(rect_xform, v3(-.25f, -.25f, 0)); diff --git a/oogabooga/examples/renderer_stress_test.c b/oogabooga/examples/renderer_stress_test.c index e75922b..e6ea322 100644 --- a/oogabooga/examples/renderer_stress_test.c +++ b/oogabooga/examples/renderer_stress_test.c @@ -31,7 +31,10 @@ int entry(int argc, char **argv) { Gfx_Font *font = load_font_from_disk(STR("C:/windows/fonts/arial.ttf"), get_heap_allocator()); assert(font, "Failed loading arial.ttf, %d", GetLastError()); - render_atlas_if_not_yet_rendered(font, 32, 'A'); + // This makes sure atlas is rendered for ascii. + // You might want to do this if your game lags the first time you render text because it renders + // the atlas on the fly. + render_atlas_if_not_yet_rendered(font, 32, 'A'); seed_for_random = rdtsc(); diff --git a/oogabooga/input.c b/oogabooga/input.c index 140d52a..79c3642 100644 --- a/oogabooga/input.c +++ b/oogabooga/input.c @@ -26,6 +26,8 @@ Gamepad input: + (!! Only Xbox gamepads are natively supported. Steam will map playstation controllers to xbox, but you need third party software to map playstation controllers to xbox. See DS4Windows. !!) + Vector2 input_frame.left_stick Vector2 input_frame.right_stick float32 input_frame.left_trigger diff --git a/oogabooga/memory.c b/oogabooga/memory.c index 6ed0d94..20908fb 100644 --- a/oogabooga/memory.c +++ b/oogabooga/memory.c @@ -619,6 +619,7 @@ void* temp_allocator_proc(u64 size, void *p, Allocator_Message message, void* da return 0; } case ALLOCATOR_REALLOCATE: { + panic("Temporary allocator cannot 'reallocate'"); return 0; } } @@ -662,4 +663,76 @@ void reset_temporary_storage() { has_warned_temporary_storage_overflow = false; } -#endif // NOT OOGABOOGA_LINK_EXTERNAL_INSTANCE \ No newline at end of file +#endif // NOT OOGABOOGA_LINK_EXTERNAL_INSTANCE + + +typedef struct Arena { + void *start; + void *next; + u64 size; +} Arena; + +void* arena_allocator_proc(u64 size, void *p, Allocator_Message message, void* data) { + if (size > 8) size = align_next(size, 8); + Arena *arena = (Arena*)data; + switch (message) { + case ALLOCATOR_ALLOCATE: { + void *p = arena->next; + + arena->next = (u8*)arena->next + size; + break; + } + case ALLOCATOR_DEALLOCATE: { + return 0; + } + case ALLOCATOR_REALLOCATE: { + panic("Arena allocator cannot 'reallocate'"); + return 0; + } + } + return 0; +} + +// Allocates arena from heap +Arena make_arena(u64 size) { + size = align_next(size, 8); + Arena arena; + + arena.start = alloc(get_heap_allocator(), size); + arena.next = arena.start; + arena.size = size; + + return arena; +} + +// Allocates arena from heap +Allocator make_arena_allocator(u64 size) { + void *mem = alloc(get_heap_allocator(), size + sizeof(Arena)); + + Arena *arena = (Arena*)mem; + + arena->start = (u8*)mem + sizeof(Arena); + arena->next = arena->start; + arena->size = size; + + Allocator allocator; + allocator.data = arena; + allocator.proc = arena_allocator_proc; + + return allocator; +} +Allocator make_arena_allocator_with_memory(u64 size, void *p) { + void *mem = alloc(get_heap_allocator(), size + sizeof(Arena)); + + Arena *arena = (Arena*)alloc(get_heap_allocator(), sizeof(Arena)); + + arena->start = p; + arena->next = arena->start; + arena->size = size; + + Allocator allocator; + allocator.data = arena; + allocator.proc = arena_allocator_proc; + + return allocator; +} diff --git a/oogabooga/oogabooga.c b/oogabooga/oogabooga.c index 964aba0..f135470 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 4 +#define OGB_VERSION_PATCH 5 #define OGB_VERSION (OGB_VERSION_MAJOR*1000000+OGB_VERSION_MINOR*1000+OGB_VERSION_PATCH) @@ -391,15 +391,18 @@ void oogabooga_init(u64 program_memory_size) { #else log_info("Headless mode on"); #endif - log_verbose("CPU has sse1: %cs", features.sse1 ? "true" : "false"); - log_verbose("CPU has sse2: %cs", features.sse2 ? "true" : "false"); - log_verbose("CPU has sse3: %cs", features.sse3 ? "true" : "false"); - log_verbose("CPU has ssse3: %cs", features.ssse3 ? "true" : "false"); - log_verbose("CPU has sse41: %cs", features.sse41 ? "true" : "false"); - log_verbose("CPU has sse42: %cs", features.sse42 ? "true" : "false"); - log_verbose("CPU has avx: %cs", features.avx ? "true" : "false"); - log_verbose("CPU has avx2: %cs", features.avx2 ? "true" : "false"); + log_verbose("CPU has sse1: %cs", features.sse1 ? "true" : "false"); + log_verbose("CPU has sse2: %cs", features.sse2 ? "true" : "false"); + log_verbose("CPU has sse3: %cs", features.sse3 ? "true" : "false"); + log_verbose("CPU has ssse3: %cs", features.ssse3 ? "true" : "false"); + log_verbose("CPU has sse41: %cs", features.sse41 ? "true" : "false"); + log_verbose("CPU has sse42: %cs", features.sse42 ? "true" : "false"); + log_verbose("CPU has avx: %cs", features.avx ? "true" : "false"); + log_verbose("CPU has avx2: %cs", features.avx2 ? "true" : "false"); log_verbose("CPU has avx512: %cs", features.avx512 ? "true" : "false"); + + Os_Monitor *m = os.primary_monitor; + log_verbose("Primary Monitor:\n\t%s\n\t%dhz\n\t%dx%d\n\tdpi: %d", m->name, m->refresh_rate, m->resolution_x, m->resolution_y, m->dpi); } #endif diff --git a/oogabooga/os_impl_windows.c b/oogabooga/os_impl_windows.c index a74fd80..41a5c56 100644 --- a/oogabooga/os_impl_windows.c +++ b/oogabooga/os_impl_windows.c @@ -7,6 +7,7 @@ #include #include #include +#include #define VIRTUAL_MEMORY_BASE ((void*)0x0000690000000000ULL) @@ -163,6 +164,7 @@ void win32_handle_key_repeat(Input_Key_Code code, s64 gamepad_index) { win32_send_key_event(code, win32_key_states[code], gamepad_index); } +void win32_query_monitors(); LRESULT CALLBACK win32_window_proc(HWND passed_window, UINT message, WPARAM wparam, LPARAM lparam) { @@ -265,6 +267,12 @@ LRESULT CALLBACK win32_window_proc(HWND passed_window, UINT message, WPARAM wpar } break; } + case WM_DISPLAYCHANGE: { + + win32_query_monitors(); + + goto DEFAULT_HANDLE; + } default: DEFAULT_HANDLE: @@ -360,7 +368,6 @@ void os_init(u64 program_memory_capacity) { context.thread_id = GetCurrentThreadId(); - #if CONFIGURATION == RELEASE // #Configurable #Copypaste SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); @@ -379,9 +386,9 @@ void os_init(u64 program_memory_capacity) { os.static_memory_start = 0; os.static_memory_end = 0; + MEMORY_BASIC_INFORMATION mbi; - unsigned char* addr = 0; while (VirtualQuery(addr, &mbi, sizeof(mbi))) { if (mbi.Type == MEM_IMAGE) { @@ -433,6 +440,57 @@ void os_init(u64 program_memory_capacity) { + win32_query_monitors(); +} + +BOOL win32_query_monitors_callback(HMONITOR monitor_handle, HDC dc, LPRECT rect, LPARAM param) { + MONITORINFOEX info = ZERO(MONITORINFOEX); + info.cbSize = sizeof(MONITORINFOEX); + BOOL ok = GetMonitorInfo(monitor_handle, (MONITORINFO*)&info); + assert(ok, "GetMonitorInfo failed"); + + string monitor_id; + monitor_id.count = strlen(info.szDevice); + monitor_id.data = (u8*)info.szDevice; + + u16 *monitor_id_wide = temp_win32_fixed_utf8_to_null_terminated_wide(monitor_id); + + DEVMODEW more_info = ZERO(DEVMODEW); + u16 *name_wide = temp_win32_fixed_utf8_to_null_terminated_wide(monitor_id); + ok = EnumDisplaySettingsW(name_wide, ENUM_CURRENT_SETTINGS, &more_info); + assert(ok, "EnumDisplaySettingsW failed"); + + DISPLAY_DEVICEW even_more_info = ZERO(DISPLAY_DEVICEW); + even_more_info.cb = sizeof(DISPLAY_DEVICE); + bool display_device_found = false; + for (DWORD i = 0; EnumDisplayDevicesW(NULL, i, &even_more_info, 0); ++i) { + if (wcscmp(even_more_info.DeviceName, monitor_id_wide) == 0) { + display_device_found = TRUE; + break; + } + } + assert(display_device_found, "DISPLAY_DEVICE not found"); + + Os_Monitor *monitor = (Os_Monitor*)growing_array_add_empty((void**)&os.monitors); + memset(monitor, 0, sizeof(Os_Monitor)); + if (info.dwFlags & MONITORINFOF_PRIMARY) os.primary_monitor = monitor; + + monitor->name = temp_win32_null_terminated_wide_to_fixed_utf8(even_more_info.DeviceString); + monitor->refresh_rate = more_info.dmDisplayFrequency; + monitor->resolution_x = info.rcMonitor.right - info.rcMonitor.left; + monitor->resolution_y = info.rcMonitor.bottom - info.rcMonitor.top; + + GetDpiForMonitor(monitor_handle, MDT_EFFECTIVE_DPI, (UINT*)&monitor->dpi, (UINT*)&monitor->dpi_y); + + return TRUE; +} +void win32_query_monitors() { + if (os.monitors) growing_array_clear((void**)&os.monitors); + else growing_array_init((void**)&os.monitors, sizeof(Os_Monitor), get_heap_allocator()); + + EnumDisplayMonitors(0, 0, win32_query_monitors_callback, 0); + + os.number_of_connected_monitors = growing_array_get_valid_count(os.monitors); } void s64_to_null_terminated_string_reverse(char str[], int length) @@ -1043,6 +1101,8 @@ bool os_do_paths_match(string a, string b) { return false; } +// #Cleanup +// These are not os-specific, why are they here? void fprints(File f, string fmt, ...) { va_list args; va_start(args, fmt); @@ -1059,7 +1119,20 @@ void fprintf(File f, const char* fmt, ...) { va_end(args); } +void os_wait_and_read_stdin(string *result, u64 max_count, Allocator allocator) { + char *buffer = talloc(max_count); + + DWORD read; + BOOL ok = ReadConsole(GetStdHandle(STD_INPUT_HANDLE), buffer, max_count, &read, 0); + + if (!ok) { + *result = string_copy(STR("STDIN is not available"), allocator); + } else { + *result = alloc_string(allocator, read); + memcpy(result->data, buffer, read); + } +} diff --git a/oogabooga/os_interface.c b/oogabooga/os_interface.c index 33bb936..ba5344e 100644 --- a/oogabooga/os_interface.c +++ b/oogabooga/os_interface.c @@ -37,17 +37,31 @@ typedef int (__cdecl *Crt_Vsnprintf_Proc) (char*, size_t, const char*, va_list); -typedef struct Os_Info { +typedef struct Os_Monitor { + u64 refresh_rate; + u64 resolution_x; + u64 resolution_y; + u64 dpi; + u64 dpi_y; + string name; +} Os_Monitor; + +typedef struct Os_Context { u64 page_size; u64 granularity; Dynamic_Library_Handle crt; Crt_Vsnprintf_Proc crt_vsnprintf; + + u64 number_of_connected_monitors; + Os_Monitor *monitors; + Os_Monitor *primary_monitor; + // These are not correct, but they probably do include static memory void *static_memory_start, *static_memory_end; -} Os_Info; +} Os_Context; typedef struct Os_Window { @@ -72,11 +86,11 @@ typedef struct Os_Window { // #Global ogb_instance Os_Window window; -ogb_instance Os_Info os; +ogb_instance Os_Context os; #if !OOGABOOGA_LINK_EXTERNAL_INSTANCE -Os_Info os; +Os_Context os; Os_Window window; #endif // NOT OOGABOOGA_LINK_EXTERNAL_INSTANCE @@ -378,6 +392,7 @@ void fprint_va_list_buffered(File f, const string fmt, va_list args) { } } +void os_wait_and_read_stdin(string *result, u64 max_count, Allocator allocator); /// /// diff --git a/oogabooga/string.c b/oogabooga/string.c index 5917695..349bfe9 100644 --- a/oogabooga/string.c +++ b/oogabooga/string.c @@ -173,6 +173,10 @@ string_builder_init(String_Builder *b, Allocator allocator) { string_builder_init_reserve(b, 128, allocator); } void +string_builder_deinit(String_Builder *b) { + dealloc(b->allocator, b->buffer); +} +void string_builder_append(String_Builder *b, string s) { assert(b->allocator.proc, "String_Builder is missing allocator"); string_builder_reserve(b, b->count+s.count); diff --git a/oogabooga/string_format.c b/oogabooga/string_format.c index 2b437aa..2648ee0 100644 --- a/oogabooga/string_format.c +++ b/oogabooga/string_format.c @@ -1,4 +1,33 @@ +/* + + Format a string and print to stdout: + print(string fmt, ...) + + Allocate a new string and format into it: + sprint(Allocator allocator, string fmt, ...) + + Allocate a new string with the temporary allocator and format into it: + tprint(string fmt, ...) + + Example: + print("Int: %d, Float: %f, String: %s", my_int, my_float, my_string); + + Format specifiers: + %d, %i: Any SIGNED integer + %u : Any UNSIGNED integer + %f : Float32 or float64 + %s : string + %b : bool + %c : Character + %v2 : Vector2 + %v3 : Vector3 + %v4 : Vector4 + + Also includes all of the standard C printf-like format specifiers: + https://www.geeksforgeeks.org/format-specifiers-in-c/ +*/ + ogb_instance void os_write_string_to_stdout(string s); inline int crt_sprintf(char *str, const char *format, ...); int vsnprintf(char* buffer, size_t n, const char* fmt, va_list args); @@ -69,6 +98,18 @@ u64 format_string_to_buffer(char* buffer, u64 count, const char* fmt, va_list ar len += 1; assert(len < (1024ULL*1024ULL*1024ULL*1ULL), "The argument passed to %%cs is either way too big, missing null-termination or simply not a char*."); } + } else if (*p == 'b') { + p += 1; + int data = va_arg(args, int); + bool val = !(data == 0); + + char *result = (val ? "true" : "false"); + + if (buffer) { + memcpy(bufp, result, strlen(result)); + } + bufp += strlen(result); + } else if (*p == 'v' && *(p+1) == '2') { p += 2; @@ -131,7 +172,7 @@ u64 format_string_to_buffer(char* buffer, u64 count, const char* fmt, va_list ar case 'n': va_arg(args, int*); break; default: break; } - + if (temp_len < 0) { return -1; // Error in formatting }