#pragma comment(lib, "gdi32") #define OS_W32_GraphicalWindowClassName L"ApplicationWindowClass" root_function OS_InitGfxReceipt OS_gfx_init(OS_InitReceipt os_init_receipt) { if (is_main_thread() && os_g_w32_gfx_state == 0) { { // Global state Arena *arena = m_make_arena_reserve(Gigabytes(1)); os_g_w32_gfx_state = PushArray(arena, OS_W32_Gfx_State, 1); os_g_w32_gfx_state->arena = arena; os_g_w32_gfx_state->window_arena = m_make_arena_reserve(Gigabytes(1)); } // TODO(antonl) DPI awareness // Register window class { /* WNDCLASSW window_class = { 0 }; */ /* window_class.style = CS_HREDRAW | CS_VREDRAW; */ /* window_class.lpfnWndProc = OS_W32_WindowProc; */ /* window_class.hInstance = g_os_w32_hinstance; */ /* window_class.lpszClassName = OS_W32_GraphicalWindowClassName; */ /* window_class.hCursor = LoadCursor(0, IDC_ARROW); */ /* RegisterClassW(&window_class); */ WNDCLASSEXW window_class = {0}; window_class.cbSize = sizeof(WNDCLASSEXW); window_class.lpfnWndProc = OS_W32_window_proc; window_class.hInstance = os_g_w32_hinstance; window_class.lpszClassName = OS_W32_GraphicalWindowClassName; if(!RegisterClassExW(&window_class)) { break_debugger(); } } // Rjf makes a "global invisible window", but why? { os_g_w32_gfx_state->global_hwnd = CreateWindowExW(0, OS_W32_GraphicalWindowClassName, L"", WS_OVERLAPPEDWINDOW, 100,100, 0,0, 0,0, os_g_w32_hinstance, 0); os_g_w32_gfx_state->global_hdc = GetDC(os_g_w32_gfx_state->global_hwnd); } } OS_InitGfxReceipt out; out.u64[0] = 1; return out; } root_function OS_Handle OS_W32_handle_from_window(OS_W32_Window *window) { OS_Handle handle = { 0 }; handle.u64[0] = (U64)window; return handle; } root_function OS_W32_Window* OS_W32_window_from_handle(OS_Handle handle) { OS_W32_Window *window = (OS_W32_Window *)handle.u64[0]; return window; } root_function OS_Handle OS_window_open(OS_Window_Flags flags, Vec2_S64 size, String8 title) { OS_Handle handle = { 0 }; { // Window allocation OS_W32_Window *window = os_g_w32_gfx_state->free_window; { // Windows are stored in a stack on the gfx state if (window != 0) { StackPop(os_g_w32_gfx_state->free_window); } else { window = PushArray(os_g_w32_gfx_state->window_arena, OS_W32_Window, 1); } MemoryZeroStruct(window); DLLPushBack(os_g_w32_gfx_state->first_window, os_g_w32_gfx_state->last_window, window); } // Open window HWND hwnd = 0; HDC hdc = 0; { ArenaTemp scratch = scratch_get(0, 0); String16 title16 = str16_from8(scratch.arena, title); hwnd = CreateWindowExW(0, OS_W32_GraphicalWindowClassName, (LPCWSTR)title16.str, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, size.x, size.y, 0, 0, os_g_w32_hinstance, 0); hdc = GetDC(hwnd); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)window); scratch_release(scratch); } { window->hwnd = hwnd; window->hdc = hdc; } handle = OS_W32_handle_from_window(window); } return handle; } function LRESULT OS_W32_window_proc(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param) { LRESULT result = 0; OS_Event *event = 0; OS_W32_Window *window = (OS_W32_Window *)GetWindowLongPtr(hwnd, GWLP_USERDATA); OS_Handle window_handle = OS_W32_handle_from_window(window); ArenaTemp scratch = scratch_get(&os_w32_tl_events_arena, 1); OS_EventList fallback_event_list = {0}; if(os_w32_tl_events_arena == 0) { os_w32_tl_events_arena = scratch.arena; os_w32_tl_events_list = &fallback_event_list; } B32 is_release = 0; Axis2 scroll_axis = Axis2_Y; switch(message) { default: { result = DefWindowProcW(hwnd, message, w_param, l_param); } break; //- General window events case WM_CLOSE: { event = PushArray(os_w32_tl_events_arena, OS_Event, 1); event->kind = OS_EventKind_WindowClose; event->window = window_handle; } break; //- Mouse buttons case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: { ReleaseCapture(); is_release = 1; } fallthrough; case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: { if(is_release == 0) { SetCapture(hwnd); } OS_EventKind kind = is_release ? OS_EventKind_Release : OS_EventKind_Press; OS_Key key = OS_Key_MouseLeft; switch(message) { case WM_MBUTTONUP: case WM_MBUTTONDOWN: key = OS_Key_MouseMiddle; break; case WM_RBUTTONUP: case WM_RBUTTONDOWN: key = OS_Key_MouseRight; break; } event = PushArray(os_w32_tl_events_arena, OS_Event, 1); event->kind = kind; event->window = window_handle; event->key = key; event->position = OS_mouse_from_window(window_handle); } break; //- Keyboard events case WM_SYSKEYDOWN: case WM_SYSKEYUP: { result = DefWindowProcW(hwnd, message, w_param, l_param); } fallthrough; case WM_KEYDOWN: case WM_KEYUP: { // TODO(anton): Just check this thing with was down, is down.., WINAPI crap B32 was_down = !!(l_param & (1 << 30)); B32 is_down = !(l_param & (1 << 31)); OS_EventKind kind = is_down ? OS_EventKind_Press : OS_EventKind_Release; // TODO(anton): Here we use statics but maybe we should not... // Could just move this out and pre-init or generate it and include or whatever... // probably should just be in some meta header. local_persist OS_Key key_table[256] = {0}; local_persist B32 key_table_initialised = 0; if(!key_table_initialised) { key_table_initialised = 1; for (U32 i = 'A', j = OS_Key_A; i <= 'Z'; i += 1, j += 1) { key_table[i] = (OS_Key)j; } for (U32 i = '0', j = OS_Key_0; i <= '9'; i += 1, j += 1) { key_table[i] = (OS_Key)j; } for (U32 i = VK_F1, j = OS_Key_F1; i <= VK_F24; i += 1, j += 1) { key_table[i] = (OS_Key)j; } key_table[VK_ESCAPE] = OS_Key_Esc; key_table[VK_OEM_3] = OS_Key_GraveAccent; key_table[VK_OEM_MINUS] = OS_Key_Minus; key_table[VK_OEM_PLUS] = OS_Key_Equal; key_table[VK_BACK] = OS_Key_Backspace; key_table[VK_TAB] = OS_Key_Tab; key_table[VK_SPACE] = OS_Key_Space; key_table[VK_RETURN] = OS_Key_Enter; key_table[VK_CONTROL] = OS_Key_Ctrl; key_table[VK_SHIFT] = OS_Key_Shift; key_table[VK_MENU] = OS_Key_Alt; key_table[VK_UP] = OS_Key_Up; key_table[VK_LEFT] = OS_Key_Left; key_table[VK_DOWN] = OS_Key_Down; key_table[VK_RIGHT] = OS_Key_Right; key_table[VK_DELETE] = OS_Key_Delete; key_table[VK_PRIOR] = OS_Key_PageUp; key_table[VK_NEXT] = OS_Key_PageDown; key_table[VK_HOME] = OS_Key_Home; key_table[VK_END] = OS_Key_End; key_table[VK_OEM_2] = OS_Key_ForwardSlash; key_table[VK_OEM_PERIOD] = OS_Key_Period; key_table[VK_OEM_COMMA] = OS_Key_Comma; key_table[VK_OEM_7] = OS_Key_Quote; key_table[VK_OEM_4] = OS_Key_LeftBracket; key_table[VK_OEM_6] = OS_Key_RightBracket; key_table[VK_INSERT] = OS_Key_Insert; key_table[VK_OEM_1] = OS_Key_Semicolon; } OS_Key key = OS_Key_Null; if(w_param < ArrayCount(key_table)) { key = key_table[w_param]; } event = PushArray(os_w32_tl_events_arena, OS_Event, 1); event->kind = kind; event->window = window_handle; event->key = key; } break; } // If we registered an event we push it to the event list. if(event) { DLLPushBack(os_w32_tl_events_list->first, os_w32_tl_events_list->last, event); os_w32_tl_events_list->count += 1; } scratch_release(scratch); return result; } root_function Vec2_F32 OS_mouse_from_window(OS_Handle handle) { Vec2_F32 result = vec2_F32(-100, -100); OS_W32_Window *window = OS_W32_window_from_handle(handle); if(window != 0) { POINT point; if(GetCursorPos(&point)) { if(ScreenToClient(window->hwnd, &point)) { result = vec2_F32(point.x, point.y); } } } return result; } root_function OS_EventList OS_get_events(Arena* arena) { OS_EventList list = {0}; os_w32_tl_events_arena = arena; os_w32_tl_events_list = &list; for(MSG message; PeekMessage(&message, 0, 0, 0, PM_REMOVE);) { TranslateMessage(&message); DispatchMessage(&message); } os_w32_tl_events_arena = 0; os_w32_tl_events_list = 0; return list; } root_function void OS_consume_event(OS_EventList *events, OS_Event *event) { DLLRemove(events->first, events->last, event); events->count -= 1; event->kind = OS_EventKind_Null; } root_function void OS_window_first_paint(OS_Handle handle) { ArenaTemp scratch = scratch_get(0,0); OS_W32_Window *window = OS_W32_window_from_handle(handle); ShowWindow(window->hwnd, SW_SHOW); UpdateWindow(window->hwnd); scratch_release(scratch); }