diff --git a/lib/OS/OS_Win32.cpp b/lib/OS/OS_Win32.cpp index 0eae892..d6cfc11 100644 --- a/lib/OS/OS_Win32.cpp +++ b/lib/OS/OS_Win32.cpp @@ -34,6 +34,10 @@ struct OS_System_Info { u64 large_page_size; u64 allocation_granularity; string machine_name; + + // Monitor stuff: + b32 monitors_enumerated; + Array monitors; // Back with GPAllocator }; struct OS_Process_Info { @@ -44,6 +48,10 @@ struct OS_Process_Info { string user_program_data_path; Array module_load_paths; Array environment_paths; + + b32 window_class_initialized; + Arena* event_arena; + Array windows; }; struct OS_State_Win32 { @@ -53,11 +61,11 @@ struct OS_State_Win32 { OS_Process_Info process_info; }; -global OS_State_Win32 os_state_w32; -internal b32 win32_g_is_quiet = 0; // No console output +global OS_State_Win32 global_win32_state; +internal b32 global_win32_is_quiet = 0; // No console output internal LONG WINAPI Win32_Exception_Filter (EXCEPTION_POINTERS* exception_ptrs) { - if (win32_g_is_quiet) { ExitProcess(1); } + if (global_win32_is_quiet) { ExitProcess(1); } local_persist volatile LONG first = 0; if(InterlockedCompareExchange(&first, 1, 0) != 0) @@ -69,6 +77,8 @@ internal LONG WINAPI Win32_Exception_Filter (EXCEPTION_POINTERS* exception_ptrs) // #Exception handling code (TODO) + ExitProcess(1); + return 0; } @@ -102,18 +112,18 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) { push_arena(thread_context()->arena); - { OS_System_Info* info = &os_state_w32.system_info; + { OS_System_Info* info = &global_win32_state.system_info; info->logical_processor_count = (s32)sysinfo.dwNumberOfProcessors; info->page_size = sysinfo.dwPageSize; info->large_page_size = GetLargePageMinimum(); info->allocation_granularity = sysinfo.dwAllocationGranularity; } - { OS_Process_Info* info = &os_state_w32.process_info; + { OS_Process_Info* info = &global_win32_state.process_info; info->large_pages_allowed = false; info->process_id = GetCurrentProcessId(); } // #cpuid - /*{ OS_System_Info* info = &os_state_w32.system_info; + /*{ OS_System_Info* info = &global_win32_state.system_info; // [ ] Extract input args u32 length; GetLogicalProcessorInformationEx(RelationProcessorCore, nullptr, (PDWORD)&length); @@ -154,7 +164,8 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) { } // info->secondary_core_count = ; */ - { OS_System_Info* info = &os_state_w32.system_info; + { OS_System_Info* info = &global_win32_state.system_info; + info->monitors.allocator = GPAllocator(); u8 buffer[MAX_COMPUTERNAME_LENGTH + 1] = {0}; DWORD size = MAX_COMPUTERNAME_LENGTH + 1; if(GetComputerNameA((char*)buffer, &size)) { @@ -163,7 +174,8 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) { } } - { OS_Process_Info* info = &os_state_w32.process_info; + { OS_Process_Info* info = &global_win32_state.process_info; + info->windows.allocator = GPAllocator(); DWORD length = GetCurrentDirectoryW(0, 0); // This can be freed later when we call temp_reset(); u16* memory = NewArray(temp(), length + 1); @@ -172,6 +184,11 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) { Assert(is_valid(info->working_path)); } + // Setup event arena, allocators for Array<> types. + if (!global_win32_state.process_info.event_arena) { + global_win32_state.process_info.event_arena = next_arena(Arena_Reserve::Size_64K); + } + // [ ] Get Working directory (info->working_path) // [ ] GetEnvironmentStringsW temp_reset(); @@ -504,10 +521,211 @@ internal bool write_entire_file (string file_path, ArrayView file_data) { return write_entire_file(file_path, file_data.data, file_data.count); } +internal LRESULT // see os_w32_wnd_proc from raddebugger. +win32_wnd_proc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + LRESULT result = 0; + bool good = true; + + Arena* event_arena = global_win32_state.process_info.event_arena; + Assert(event_arena != nullptr); + + switch (uMsg) { + default: { + result = DefWindowProcW(hwnd, uMsg, wParam, lParam); + } break; + case WM_CLOSE: { + ExitProcess(0); // #temp. + } break; + } + + return result; +} + +internal BOOL +monitor_enum_proc (HMONITOR hMonitor, HDC hdc, RECT* rect, LPARAM data) { + Monitor monitor = {}; + monitor.left = rect->left; + monitor.top = rect->top; + monitor.right = rect->right; + monitor.bottom = rect->bottom; + + monitor.monitor_info.cbSize = sizeof(MONITORINFO); + + GetMonitorInfoW(hMonitor, &monitor.monitor_info); + monitor.primary = !!(monitor.monitor_info.dwFlags & 0x1); + monitor.present = true; + + array_add(global_win32_state.system_info.monitors, monitor); + + return true; +} + +// #TODO: how do I setup a callback if monitors configuration is changed? +// we handle WM_DISPLAYCHANGE or WM_DEVICECHANGE and call EnumDisplayMonitors +internal void os_enumerate_monitors () { + if (!global_win32_state.system_info.monitors_enumerated) { + // should reset array? + if (!EnumDisplayMonitors(nullptr, nullptr, monitor_enum_proc, 0)) { + log_error("Failed to enumerate monitors\n"); + Assert(false); // Failed to enumerate monitors + ExitProcess(1); + } + + global_win32_state.system_info.monitors_enumerated = true; + } +} + +Window_Dimensions platform_get_centered_window_dimensions (bool open_on_largest_monitor=false) { + os_enumerate_monitors(); + + Assert(global_win32_state.system_info.monitors.count > 0); // must have at least 1 monitor! + + Array monitors = global_win32_state.system_info.monitors; + Monitor monitor = monitors[0]; + + if (open_on_largest_monitor) { + s64 max_area = 0; + for (s64 i = 0; i < monitors.count; i += 1) { + s64 width = monitors[i].right - monitors[i].left; + s64 height = monitors[i].bottom - monitors[i].top; + s64 area = width * height; + + if (max_area < area) { + monitor = monitors[i]; + max_area = area; + } else if (max_area == area && monitors[i].primary) { + // if monitors are the same dimension, then just use primary monitor + monitor = monitors[i]; + } + } + } else { // Opens on whatever monitor is marked as "primary." + for (s64 i = 0; i < monitors.count; i += 1) { + if (monitors[i].primary) { + monitor = monitors[i]; + break; + } + } + } + + s64 monitor_width = monitor.right - monitor.left; + s64 monitor_height = monitor.bottom - monitor.top; + + s64 window_width = (s64)((f64)monitor_width / 1.125); + s64 window_height = (s64)((f64)monitor_height / 1.25); + s64 window_x = (monitor.left + (monitor_width / 2) - (window_width / 2)); + s64 window_y = (monitor.top + (monitor_height / 2) - (window_height / 2)); + + Window_Dimensions dimensions = { + (s32)window_x, + (s32)window_y, + (s32)window_width, + (s32)window_height, + }; + + return dimensions; +} + // #window_creation -> put API in OS_Win32.h -// void initialize_window_class(float background_color[3]) {} -Window_Type create_window (string new_window_name, bool center_window) { - return 0; +// Instead of returning WindowType, return the handle + other information. +bool os_create_window (string new_window_name, Window_Type parent, bool center_window, bool open_on_largest_monitor, bool display_window) { + const ArrayView background_color = array_view_from_values(0.0f, 0.0f, 0.0f); + + local_persist string class_name = "Win32_Window_Class"; + + if (!global_win32_state.process_info.window_class_initialized) { + global_win32_state.process_info.window_class_initialized = true; + + WNDCLASSEXW wc = {}; + wc.cbSize = sizeof(WNDCLASSEXW); + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = win32_wnd_proc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = nullptr; + wc.hIcon = (HICON)LoadImageW(nullptr, (LPCWSTR)utf8_to_wide("tmp.ico").data, IMAGE_ICON, 0, 0, LR_LOADFROMFILE); + wc.hCursor = LoadCursorW(nullptr, (LPCWSTR)IDC_ARROW); + wc.hbrBackground = nullptr; + wc.lpszMenuName = nullptr; + wc.lpszClassName = (LPCWSTR)utf8_to_wide(class_name).data; + + if (RegisterClassExW(&wc) == 0) { + log_error("RegisterClassExW Failed."); + return false; + } + } + + Window_Dimensions wd = { 100, 100, 640, 480 }; + + if (center_window) { + wd = platform_get_centered_window_dimensions(open_on_largest_monitor); + log("[Window_Dimensions] location: (%d, %d); size: %dx%d px", + wd.window_x, wd.window_y, wd.window_width, wd.window_height); + } + + HWND hwnd = CreateWindowExW( + WS_EX_APPWINDOW, + (LPCWSTR)utf8_to_wide(class_name).data, + (LPCWSTR)utf8_to_wide(new_window_name).data, + WS_OVERLAPPEDWINDOW, + wd.window_x, wd.window_y, wd.window_width, wd.window_height, + nullptr, nullptr, nullptr, nullptr + ); + + if (!hwnd) { + Assert(false); + DestroyWindow(hwnd); + return false; + } + + // Display the window: + if (display_window) { + UpdateWindow(hwnd); + ShowWindow(hwnd, SW_SHOW); + } + + // Save window to stack + Window_Info info = {}; + info.window = hwnd; + info.initial_dimensions = wd; + info.is_main_window = (parent == nullptr); + + // Should we mutex this? Seems very unlikely we'll ever need to call this + // from multiple threads at the same time. + array_add(global_win32_state.process_info.windows, info); + + return true; +} + +Window_Info get_main_window() { + Array windows = global_win32_state.process_info.windows; + Assert(windows.count > 0); + + for (s64 i = 0; i < windows.count; i += 1) { + if (windows[i].is_main_window) { + return windows[i]; + } + } + + return {}; +} + +bool get_window_dimensions(Window_Info* info, s32* width, s32* height) { + if (info == nullptr || width == nullptr || height == nullptr) return false; + Assert(width && height); + + RECT c = {}; + bool success = GetClientRect(info->window, &c); + + if (!success) { + (*width) = 0; + (*height) = 0; + return false; + } + + (*width) = (s32)(c.right - c.left); + (*height) = (s32)(c.bottom - c.top); + + return true; } // #TODO: #window_creation diff --git a/lib/OS/OS_Win32.h b/lib/OS/OS_Win32.h index a1cfa6d..52f7b54 100644 --- a/lib/OS/OS_Win32.h +++ b/lib/OS/OS_Win32.h @@ -1,12 +1,8 @@ +#pragma comment(lib, "user32") + f64 GetUnixTimestamp (); s64 GetUnixTimestampNanoseconds (); -// struct File_Contents { -// File file = {}; -// ArrayView file_data = {}; -// bool read_success = false; -// }; - enum class Wait_For_Result : s32 { SUCCESS = 0, TIMEOUT = 1, @@ -51,3 +47,41 @@ internal bool write_entire_file (string file_path, ArrayView file_data); // #window_creation typedef HWND Window_Type; + +struct Window_Dimensions { + s32 window_x; + s32 window_y; + s32 window_width; + s32 window_height; +}; + +struct Window_Info { + Window_Type window; + Window_Dimensions initial_dimensions; + + b32 is_main_window; +}; + +// #move: Monitor - platform-independent hardware monitor information: +struct Monitor { + s64 left; + s64 top; + s64 right; + s64 bottom; + bool primary; + bool present; +#if OS_WINDOWS + MONITORINFO monitor_info; +#endif +}; + + +bool os_create_window (string new_window_name, Window_Type parent=nullptr, bool center_window=true, bool open_on_largest_monitor=true, bool display_window=true); + +Window_Info get_main_window(); + +// struct File_Contents { +// File file = {}; +// ArrayView file_data = {}; +// bool read_success = false; +// }; diff --git a/lib/UI/Layout.h b/lib/UI/Layout.h new file mode 100644 index 0000000..ebd7bc3 --- /dev/null +++ b/lib/UI/Layout.h @@ -0,0 +1,15 @@ +struct Rect { + f32 x; f32 y; f32 w; f32 h; +}; + +force_inline bool operator==(const Rect& a, const Rect& b) { + return a.x == b.x && a.y == b.y && a.w == b.w && a.h == b.h; +} + +Rect make_rect_int (s64 x, s64 y, s64 w, s64 h) { + return {(f32)x, (f32)y, (f32)w, (f32)h}; +} + +Rect make_rect (f32 x, f32 y, f32 w, f32 h) { + return {x, y, w, h}; +} diff --git a/lib_main.cpp b/lib_main.cpp index 5132fdd..c34e3d0 100644 --- a/lib_main.cpp +++ b/lib_main.cpp @@ -24,6 +24,7 @@ #if OS_WINDOWS # include "lib/OS/OS_Win32.h" #endif +#include "lib/UI/Layout.h" #include "lib/Base/Logger.h" #include "lib/Base/String.cpp" diff --git a/src/Base_Entry_Point.cpp b/src/Base_Entry_Point.cpp index cdfe572..a7ff599 100644 --- a/src/Base_Entry_Point.cpp +++ b/src/Base_Entry_Point.cpp @@ -35,11 +35,25 @@ internal void Main_Entry_Point (int argc, WCHAR **argv) { run_post_setup_tests(); #endif - // 2. Setup Window - // 3. Initialize Graphics Backend (DX11 or OpenGL3) + // 2. Create Window + bool success = os_create_window("App Main Window", nullptr, true, true); + Assert(success); + Window_Info main_window = get_main_window(); + s32 window_width; s32 window_height; + success = get_window_dimensions(&main_window, &window_width, &window_height); + Assert(success); + // #TODO: Set in global state for convenience: + Rect main_screen = make_rect_int(0, 0, window_width, window_height); + // renderer.//set_render_target(main_window.window, main_screen); + + // get_render_dimensions + + // 3. Init Graphics (DX11 or OpenGL3) + // Maybe start with OpenGL3 // #TODO: #Main - `Main_Entry_Point` - // [ ] Setup Mouse and Keyboard Inputs - // [ ] Launch second thread + + // 4. [ ] Setup Mouse and Keyboard Inputs + // 5. [ ] Launch second thread; thread groups }