WND_W32_SharedState WND_W32_shared_state = ZI; //////////////////////////////////////////////////////////// //~ @hookdef Startup void WND_Startup(void) { WND_W32_SharedState *g = &WND_W32_shared_state; //- Initialize btn table { ZeroArray(g->vk_to_btn); for (u32 i = 'A', j = Btn_A; i <= 'Z'; ++i, ++j) { g->vk_to_btn[i] = (Btn)j; } for (u32 i = '0', j = Btn_0; i <= '9'; ++i, ++j) { g->vk_to_btn[i] = (Btn)j; } for (u32 i = VK_F1, j = Btn_F1; i <= VK_F24; ++i, ++j) { g->vk_to_btn[i] = (Btn)j; } g->vk_to_btn[VK_ESCAPE] = Btn_Esc; g->vk_to_btn[VK_OEM_3] = Btn_GraveAccent; g->vk_to_btn[VK_OEM_MINUS] = Btn_Minus; g->vk_to_btn[VK_OEM_PLUS] = Btn_Equal; g->vk_to_btn[VK_BACK] = Btn_Backspace; g->vk_to_btn[VK_TAB] = Btn_Tab; g->vk_to_btn[VK_SPACE] = Btn_Space; g->vk_to_btn[VK_RETURN] = Btn_Enter; g->vk_to_btn[VK_CONTROL] = Btn_Ctrl; g->vk_to_btn[VK_SHIFT] = Btn_Shift; g->vk_to_btn[VK_MENU] = Btn_Alt; g->vk_to_btn[VK_UP] = Btn_Up; g->vk_to_btn[VK_LEFT] = Btn_Left; g->vk_to_btn[VK_DOWN] = Btn_Down; g->vk_to_btn[VK_RIGHT] = Btn_Right; g->vk_to_btn[VK_DELETE] = Btn_Delete; g->vk_to_btn[VK_PRIOR] = Btn_PageUp; g->vk_to_btn[VK_NEXT] = Btn_PageDown; g->vk_to_btn[VK_HOME] = Btn_Home; g->vk_to_btn[VK_END] = Btn_End; g->vk_to_btn[VK_OEM_2] = Btn_ForwardSlash; g->vk_to_btn[VK_OEM_PERIOD] = Btn_Period; g->vk_to_btn[VK_OEM_COMMA] = Btn_Comma; g->vk_to_btn[VK_OEM_7] = Btn_Quote; g->vk_to_btn[VK_OEM_4] = Btn_LeftBracket; g->vk_to_btn[VK_OEM_6] = Btn_RightBracket; g->vk_to_btn[VK_INSERT] = Btn_Insert; g->vk_to_btn[VK_OEM_1] = Btn_Semicolon; } //- Create window class { HMODULE instance = GetModuleHandle(0); /* Register the window class */ WNDCLASSEXW *wc = &g->window_class; wc->cbSize = sizeof(WNDCLASSEX); wc->lpszClassName = WND_W32_WindowClassName; wc->hCursor = LoadCursor(0, IDC_ARROW); wc->style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // wc->hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wc->lpfnWndProc = WND_W32_WindowProc; wc->hInstance = instance; /* Use first icon resource as window icon (same as explorer) */ wchar_t path[4096] = ZI; GetModuleFileNameW(instance, path, countof(path)); ExtractIconExW(path, 0, &wc->hIcon, &wc->hIconSm, 1); if (!RegisterClassExW(wc)) { Panic(Lit("Failed to register window class")); } } //- Register raw mouse input { RAWINPUTDEVICE rid = ZI; rid.usUsagePage = 0x01; /* HID_USAGE_PAGE_GENERIC */ rid.usUsage = 0x02; /* HID_USAGE_GENERIC_MOUSE */ RegisterRawInputDevices(&rid, 1, sizeof(rid)); } //- Start message processing job JobPoolId message_job_pool = InitJobPool(1, Lit("Win32 message loop"), JobPoolPriority_Graphics); RunJob(WND_W32_ProcessMessagesForever, .pool = message_job_pool); } //////////////////////////////////////////////////////////// //~ Window helpers WND_W32_Window *WND_W32_WindowFromHandle(WND_Handle handle) { return (WND_W32_Window *)handle.v; } //////////////////////////////////////////////////////////// //~ Initialization /* Win32 limitation: Window must be initialized on same thread that processes events */ JobDef(WND_W32_ProcessMessagesForever, sig, id) { WND_W32_SharedState *g = &WND_W32_shared_state; WND_W32_Window *window = &g->window; window->w2u_inputs_arena = AcquireArena(Gibi(64)); //- Init hwnd HWND hwnd = 0; { /* * From martins (https://gist.github.com/mmozeiko/5e727f845db182d468a34d524508ad5f#file-win32_d3d11-c-L66-L70): * WS_EX_NOREDIRECTIONBITMAP flag here is needed to fix ugly bug with Windows 10 * when window is resized and DXGI swap chain uses FLIP presentation model * DO NOT use it if you choose to use non-FLIP presentation model * read about the bug here: https://stackoverflow.com/q/63096226 and here: https://stackoverflow.com/q/53000291 */ DWORD exstyle = WS_EX_APPWINDOW | WS_EX_NOREDIRECTIONBITMAP; /* TODO: Check for hwnd success */ hwnd = CreateWindowExW( exstyle, g->window_class.lpszClassName, L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, g->window_class.hInstance, 0 ); /* Dark mode */ BOOL dark_mode = 1; DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, (LPCVOID)&dark_mode, sizeof(dark_mode)); /* Set window as userdata */ /* FIXME: Necessary? */ SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)window); } window->hwnd = hwnd; //- Begin processing messages Atomic32Set(&window->is_ready, 1); for (;;) { // SetFocus(window->hwnd); MSG msg = ZI; GetMessageW(&msg, 0, 0, 0); { __profn("Process window message"); TranslateMessage(&msg); DispatchMessageW(&msg); } } /* Destroy window */ DestroyWindow(window->hwnd); } //////////////////////////////////////////////////////////// //~ Message processing void WND_W32_PushInput(WND_W32_Window *window, Input input) { LockTicketMutex(&window->w2u_tm); { *PushStructNoZero(window->w2u_inputs_arena, Input) = input; } UnlockTicketMutex(&window->w2u_tm); } LRESULT CALLBACK WND_W32_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { WND_W32_SharedState *g = &WND_W32_shared_state; WND_W32_Window *window = &g->window; LRESULT result = 0; { switch(msg) { //- Default default: { result = DefWindowProcW(hwnd, msg, wparam, lparam); } break; //- Quit case WM_QUIT: case WM_CLOSE: case WM_DESTROY: { WND_W32_PushInput(window, (Input) { .kind = InputKind_Quit }); } break; //- Keyboard button case WM_SYSKEYUP: case WM_SYSKEYDOWN:; case WM_KEYUP: case WM_KEYDOWN: { WORD vk_code = LOWORD(wparam); Input input = ZI; if (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) { input.kind = InputKind_ButtonDown; input.is_repeat = (lparam & 0x40000000) != 0; } else if (msg == WM_KEYUP || msg == WM_SYSKEYUP) { input.kind = InputKind_ButtonUp; } if (vk_code < countof(g->vk_to_btn)) { input.button = g->vk_to_btn[vk_code]; } WND_W32_PushInput(window, input); if (msg == WM_SYSKEYUP || msg == WM_SYSKEYDOWN) { result = DefWindowProcW(hwnd, msg, wparam, lparam); } } break; //- Text case WM_SYSCHAR: case WM_CHAR: { u32 codepoint = 0; { u16 utf16_char = (u32)wparam; if (IsUtf16HighSurrogate(utf16_char)) { window->previous_utf16_high_surrogate = utf16_char; } else if (IsUtf16LowSurrogate(utf16_char)) { u16 high = window->previous_utf16_high_surrogate; u16 low = utf16_char; if (high) { u16 utf16_pair_bytes[2] = { high, low }; Utf16DecodeResult decoded = DecodeUtf16((String16) { .len = countof(utf16_pair_bytes), .text = utf16_pair_bytes }); if (decoded.advance16 == 2 && decoded.codepoint < U32Max) { codepoint = decoded.codepoint; } } window->previous_utf16_high_surrogate = 0; } else { window->previous_utf16_high_surrogate = 0; codepoint = utf16_char; } if (codepoint != 0) { if (codepoint == '\r') { codepoint = '\n'; /* Just treat all \r as newline */ } } } if ((codepoint >= 32 && codepoint != 127) || codepoint == '\t' || codepoint == '\n') { Input input = ZI; input.kind = InputKind_Text; input.text_codepoint = codepoint; WND_W32_PushInput(window, input); } } break; //- Mouse buttons case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: case WM_XBUTTONUP: case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_XBUTTONDOWN: { Input input = ZI; b32 is_down = msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN || msg == WM_RBUTTONDOWN || msg == WM_XBUTTONDOWN; if (is_down) { input.kind = InputKind_ButtonDown; SetCapture(hwnd); } else { input.kind = InputKind_ButtonUp; ReleaseCapture(); } switch (msg) { case WM_LBUTTONUP: case WM_LBUTTONDOWN: input.button = Btn_M1; break; case WM_RBUTTONUP: case WM_RBUTTONDOWN: input.button = Btn_M2; break; case WM_MBUTTONUP: case WM_MBUTTONDOWN: input.button = Btn_M3; break; case WM_XBUTTONUP: case WM_XBUTTONDOWN: { u32 wparam_xbutton = GET_XBUTTON_WPARAM(wparam); if (wparam_xbutton == XBUTTON1) { input.button = Btn_M4; } else if (wparam_xbutton == XBUTTON2) { input.button = Btn_M5; } } break; } if (input.button) { WND_W32_PushInput(window, input); } } break; //- Mouse wheel case WM_MOUSEWHEEL: { int delta = GET_WHEEL_DELTA_WPARAM(wparam); i32 dir = delta >= 0 ? 1 : -1; Btn btn = dir >= 0 ? Btn_MWheelUp : Btn_MWheelDown; for (i32 i = 0; i < (dir * delta); i += WHEEL_DELTA) { /* Send a button down & button up event simultaneously */ WND_W32_PushInput(window, (Input) { .kind = InputKind_ButtonDown, .button = btn }); WND_W32_PushInput(window, (Input) { .kind = InputKind_ButtonUp, .button = btn }); } } break; //- Cursor move case WM_MOUSEMOVE: { i32 x = GET_X_LPARAM(lparam); i32 y = GET_Y_LPARAM(lparam); Input input = ZI; input.kind = InputKind_CursorMove; input.cursor_pos = VEC2I32(x, y); WND_W32_PushInput(window, input); } break; //- Raw mouse move case WM_INPUT: { TempArena scratch = BeginScratchNoConflict(); { /* Read raw input buffer */ UINT buff_size; GetRawInputData((HRAWINPUT)lparam, RID_INPUT, 0, &buff_size, sizeof(RAWINPUTHEADER)); u8 *buff = PushStructs(scratch.arena, u8, buff_size); if (GetRawInputData((HRAWINPUT)lparam, RID_INPUT, buff, &buff_size, sizeof(RAWINPUTHEADER)) != buff_size) { P_LogErrorF("GetRawInputData did not return correct size"); break; } RAWINPUT raw = ZI; CopyBytes(&raw, buff, sizeof(RAWINPUT)); if (raw.header.dwType == RIM_TYPEMOUSE) { i32 x = raw.data.mouse.lLastX; i32 y = raw.data.mouse.lLastY; Input input = ZI; input.kind = InputKind_MouseMove; input.mouse_delta = VEC2I32(x, y); WND_W32_PushInput(window, input); } } EndScratch(scratch); } break; #if 0 //- Process command case WND_CmdMsgId: { P_LogWarningF("RAAAAH"); WND_Event user_event = ZI; WND_Cmd user_cmd = ZI; { LockTicketMutex(&window->u2w_tm); { user_event = window->u2w_update_end_event; user_cmd = window->u2w_update_end_cmd; } UnlockTicketMutex(&window->u2w_tm); } /* Determine old settings */ Vec2I32 old_draw_p0 = ZI; Vec2I32 old_draw_p1 = ZI; Vec2I32 old_restore_p0 = ZI; Vec2I32 old_restore_p1 = ZI; b32 old_is_visible = 0; b32 old_has_border = 0; { { RECT screen_rect = ZI; GetClientRect(hwnd, (LPRECT)&screen_rect); ClientToScreen(hwnd, (LPPOINT)&screen_rect.left); ClientToScreen(hwnd, (LPPOINT)&screen_rect.right); old_draw_p0 = VEC2I32(screen_rect.left, screen_rect.top); old_draw_p1 = VEC2I32(screen_rect.right, screen_rect.bottom); } { WINDOWPLACEMENT placement = { .length = sizeof(placement) }; GetWindowPlacement(hwnd, &placement); RECT placement_rect = placement.rcNormalPosition; old_restore_p0 = VEC2I32(placement_rect.left, placement_rect.top); old_restore_p1 = VEC2I32(placement_rect.right, placement_rect.bottom); } { DWORD style = (DWORD)GetWindowLongPtr(hwnd, GWL_STYLE); old_is_visible = !!(style & WS_VISIBLE); old_has_border = !(style & WS_POPUP); } } /* Determine new settings */ Vec2I32 new_draw_p0 = old_draw_p0; Vec2I32 new_draw_p1 = old_draw_p1; Vec2I32 new_restore_p0 = old_restore_p0; Vec2I32 new_restore_p1 = old_restore_p1; b32 new_is_visible = old_is_visible; b32 new_has_border = old_has_border; { WND_Settings desired_settings = user_cmd.desired_settings; if (desired_settings.flags & WND_Flag_Fullscreen) { new_has_border = 0; new_draw_p0 = user_event.monitor_p0; new_draw_p1 = user_event.monitor_p1; } else if (desired_settings.flags & WND_Flag_Maximized) { // show_cmd = SW_MAXIMIZE; } else if (desired_settings.flags & WND_Flag_Minimized) { // show_cmd = SW_MINIMIZE; } else { new_has_border = 1; new_restore_p0 = desired_settings.restore_p0; new_restore_p1 = desired_settings.restore_p1; } new_is_visible = 1; } /* Determine new style & placement */ b32 has_user_seen_os_settings = user_event.os_gen == (u64)Atomic64Fetch(&window->os_gen); if (has_user_seen_os_settings) { /* Calculate style */ b32 dirty_style = 0; DWORD new_style = (DWORD)GetWindowLongPtr(hwnd, GWL_STYLE); { if (new_is_visible != old_is_visible) { dirty_style = 1; if (new_is_visible) { new_style |= WS_VISIBLE; } else { new_style &= ~WS_VISIBLE; } } if (new_has_border != old_has_border) { dirty_style = 1; if (new_has_border) { new_style &= ~WS_POPUP; new_style |= WS_OVERLAPPEDWINDOW; } else { new_style &= ~WS_OVERLAPPEDWINDOW; new_style |= WS_POPUP; } } } /* Calculate placement */ b32 dirty_placement = 0; WINDOWPLACEMENT new_placement = { .length = sizeof(new_placement) }; GetWindowPlacement(hwnd, &new_placement); { if (!EqVec2I32(new_restore_p0, old_restore_p0) || !EqVec2I32(new_restore_p1, old_restore_p1)) { dirty_placement = 1; new_placement.rcNormalPosition.left = new_restore_p0.x; new_placement.rcNormalPosition.top = new_restore_p0.y; new_placement.rcNormalPosition.right = new_restore_p1.x; new_placement.rcNormalPosition.bottom = new_restore_p1.y; } } /* Calculate draw position */ b32 dirty_draw_rect = 0; RECT new_draw_rect = ZI; if (!EqVec2I32(new_draw_p0, old_draw_p0) || !EqVec2I32(new_draw_p1, old_draw_p1)) { dirty_draw_rect = 1; new_draw_rect.left = new_draw_p0.x; new_draw_rect.top = new_draw_p0.y; new_draw_rect.right = new_draw_p1.x; new_draw_rect.bottom = new_draw_p1.y; AdjustWindowRect(&new_draw_rect, new_style, 0); } /* Apply changes */ if (dirty_style || dirty_placement || dirty_draw_rect) { if (dirty_style) { ++window->cmd_depth; SetWindowLongPtrW(hwnd, GWL_STYLE, new_style); } if (dirty_placement) { ++window->cmd_depth; SetWindowPlacement(hwnd, &new_placement); } if (dirty_draw_rect) { u32 pflags = 0; Vec2I32 size = VEC2I32(new_draw_rect.right - new_draw_rect.left, new_draw_rect.bottom - new_draw_rect.top); ++window->cmd_depth; SetWindowPos(hwnd, 0, new_draw_rect.left, new_draw_rect.top, size.x, size.y, pflags); } } } /* Bring window to front on first show */ if (!window->first_shown) { SetForegroundWindow(hwnd); BringWindowToTop(hwnd); window->first_shown = 1; } } break; #endif } } return result; } //////////////////////////////////////////////////////////// //~ @hookdef Cmds void WND_PushCmd_(WND_Frame frame, WND_Cmd desc) { WND_W32_Window *window = WND_W32_WindowFromHandle(frame.window_handle); WND_W32_CmdNode *n = PushStruct(window->cmds_arena, WND_W32_CmdNode); n->cmd = desc; QueuePush(window->first_cmd, window->last_cmd, n); if (desc.kind == WND_CmdKind_Restore) { n->cmd.restore = PushString(window->cmds_arena, desc.restore); } } //////////////////////////////////////////////////////////// //~ @hookdef Frame WND_Frame WND_BeginFrame(Arena *arena) { WND_W32_SharedState *g = &WND_W32_shared_state; WND_W32_Window *window = &g->window; WND_Frame result = ZI; while (!Atomic32Fetch(&window->is_ready)) { _mm_pause(); } HWND hwnd = window->hwnd; result.window_handle.v = (u64)window; /* Reset cmds */ if (!window->cmds_arena) { window->cmds_arena = AcquireArena(Gibi(64)); } ResetArena(window->cmds_arena); window->first_cmd = 0; window->last_cmd = 0; /* Pop inputs */ { LockTicketMutex(&window->w2u_tm); { Input *src = (Input *)ArenaBase(window->w2u_inputs_arena); result.inputs_count = ArenaCount(window->w2u_inputs_arena, Input); result.inputs = PushStructsNoZero(arena, Input, result.inputs_count); CopyStructs(result.inputs, src, result.inputs_count); ResetArena(window->w2u_inputs_arena); } UnlockTicketMutex(&window->w2u_tm); } /* Grab monitor info */ RECT monitor_rect = ZI; { MONITORINFO monitor_info = { .cbSize = sizeof(monitor_info) }; GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &monitor_info); monitor_rect = monitor_info.rcMonitor; } /* Client rect */ RECT client_rect = ZI; GetClientRect(hwnd, (LPRECT)&client_rect); /* Screen rect */ RECT screen_rect = client_rect; ClientToScreen(hwnd, (LPPOINT)&screen_rect.left); ClientToScreen(hwnd, (LPPOINT)&screen_rect.right); result.draw_size = VEC2I32(screen_rect.right - screen_rect.left, screen_rect.bottom - screen_rect.top); /* Minimized / maximized / fullscreen */ DWORD style = (DWORD)GetWindowLongPtr(hwnd, GWL_STYLE); DWORD ex_style = (DWORD)GetWindowLongPtr(hwnd, GWL_EXSTYLE); WINDOWPLACEMENT placement = { .length = sizeof(placement) }; GetWindowPlacement(hwnd, &placement); { if (placement.showCmd == SW_MAXIMIZE) { result.maximized = 1; } if (placement.showCmd == SW_MINIMIZE) { result.minimized = 1; } if (screen_rect.left == monitor_rect.left && screen_rect.top == monitor_rect.top && screen_rect.right == monitor_rect.right && screen_rect.bottom == monitor_rect.bottom) { result.fullscreen = 1; } } result.forced_top = window->is_forced_top; result.fullscreen = window->is_fullscreen; /* Generate restore data */ { WND_W32_RestorableData restore = ZI; { restore.magic = WND_W32_RestoreMagic; restore.placement = placement; restore.style = style; restore.ex_style = ex_style; restore.is_forced_top = window->is_forced_top; restore.is_fullscreen = window->is_fullscreen; restore.fullscreen_restore_rect = window->fullscreen_restore_rect; if (IsWindowArranged(hwnd)) { restore.is_snapped = 1; restore.snapped_screen_rect = screen_rect; } } result.restore = PushString(window->cmds_arena, StringFromStruct(&restore)); } return result; } void WND_EndFrame(WND_Frame frame) { TempArena scratch = BeginScratchNoConflict(); WND_W32_SharedState *g = &WND_W32_shared_state; WND_W32_Window *window = WND_W32_WindowFromHandle(frame.window_handle); HWND hwnd = window->hwnd; /* Process cmds */ for (WND_W32_CmdNode *n = window->first_cmd; n; n = n->next) { WND_Cmd cmd = n->cmd; switch(cmd.kind) { //- Minimize case WND_CmdKind_SetMinimized: { ShowWindow(hwnd, SW_MINIMIZE); } break; //- Maximize case WND_CmdKind_SetMaximized: { ShowWindow(hwnd, SW_MAXIMIZE); } break; //- Fullscreen case WND_CmdKind_SetFullscreen: { if (cmd.v != window->is_fullscreen) { DWORD old_style = (DWORD)GetWindowLongPtr(hwnd, GWL_STYLE); RECT old_rect = ZI; { GetClientRect(hwnd, (LPRECT)&old_rect); ClientToScreen(hwnd, (LPPOINT)&old_rect.left); ClientToScreen(hwnd, (LPPOINT)&old_rect.right); AdjustWindowRect(&old_rect, old_style, 0); } RECT new_rect = ZI; HWND new_hwnd = 0; DWORD new_style = old_style; if (cmd.v) { /* Enter fullscreen */ { MONITORINFO monitor_info = { .cbSize = sizeof(monitor_info) }; GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &monitor_info); new_rect = monitor_info.rcMonitor; } new_style &= ~WS_OVERLAPPEDWINDOW; new_style |= WS_POPUP; window->fullscreen_restore_rect = old_rect; new_hwnd = HWND_TOP; } else { /* Exit fullscreen */ new_rect = window->fullscreen_restore_rect; new_style &= ~WS_POPUP; new_style |= WS_OVERLAPPEDWINDOW; new_hwnd = window->is_forced_top ? HWND_TOPMOST : HWND_NOTOPMOST; } window->is_fullscreen = cmd.v; SetWindowLongPtr(hwnd, GWL_STYLE, new_style); SetWindowPos(hwnd, new_hwnd, new_rect.left, new_rect.top, new_rect.right - new_rect.left, new_rect.bottom - new_rect.top, 0); } } break; //- Force top case WND_CmdKind_SetForcedTop: { window->is_forced_top = cmd.v; SetWindowPos(hwnd, window->is_forced_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } break; //- Restore case WND_CmdKind_Restore: { /* FIXME: Cap bounds */ String restore_str = cmd.restore; if (restore_str.len == sizeof(WND_W32_RestorableData)) { WND_W32_RestorableData *restore = PushStructNoZero(scratch.arena, WND_W32_RestorableData); CopyBytes(restore, restore_str.text, restore_str.len); if (restore->magic == WND_W32_RestoreMagic) { WINDOWPLACEMENT placement = restore->placement; SetWindowPlacement(hwnd, &placement); SetWindowLongPtr(hwnd, GWL_STYLE, restore->style); SetWindowLongPtr(hwnd, GWL_EXSTYLE, restore->ex_style); if (restore->is_fullscreen) { MONITORINFO monitor_info = { .cbSize = sizeof(monitor_info) }; GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY), &monitor_info); RECT rect = monitor_info.rcMonitor; window->is_fullscreen = 1; window->fullscreen_restore_rect = restore->fullscreen_restore_rect; SetWindowPos(hwnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 0); } if (restore->is_forced_top) { window->is_forced_top = 1; SetWindowPos(hwnd, window->is_forced_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } if (restore->is_snapped) { RECT rect = restore->snapped_screen_rect; HWND pos_hwnd = window->is_forced_top ? HWND_TOPMOST : HWND_NOTOPMOST; SetWindowPos(hwnd, pos_hwnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, 0); } } } } break; } } /* Bring window to front on first show */ if (!window->first_shown) { ShowWindow(hwnd, SW_SHOW); SetForegroundWindow(hwnd); BringWindowToTop(hwnd); window->first_shown = 1; } EndScratch(scratch); }