From 7c306a5a0e20811d1c75668516aa7f600444ce7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=B0=8F=E7=99=BD?= <364772080@qq.com> Date: Thu, 10 Oct 2024 19:09:50 +0800 Subject: [PATCH] gpui: Fix window display on Windows (#18705) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Closes #18610 This PR addresses the same issue as PR #18578. After a full day of research and testing, I believe I’ve found the best solution to resolve this issue. With this PR, the window creation behavior on Windows becomes more consistent with macOS: - When `params.show` is `true`: The window is created and immediately displayed. - When `params.show` is `false`: The window is created but remains hidden until the first call to `activate_window`. As I mentioned in #18578, `winit` creates hidden windows by setting the window's `exstyle` to `WS_EX_NOACTIVATE | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOOLWINDOW`, which is different from the method used in this PR. Here, the window is created with normal parameters, but we do not call `ShowWindow` so the window is not shown. I'm not sure why `winit` doesn't use a smilliar approach like this PR to create hidden windows. My guess is that `winit` is creating this hidden window to function as a "DispatchWindow" — serving a purpose similar to `WindowsPlatform` in `zed`. To ensure the window stays hidden even if `ShowWindow` is called, they use the `exstyle` approach. With the method used in this PR, my initial tests haven't revealed any issues. Release Notes: - N/A --- crates/gpui/src/platform/windows/window.rs | 90 ++++++++++++++-------- 1 file changed, 58 insertions(+), 32 deletions(-) diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index d7b9a469b7d4e551b634d5439718201b02dfeabf..3faaa6a8d2472bebb9c802cef0fc2545a46c4e57 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -52,6 +52,7 @@ pub struct WindowsWindowState { pub display: WindowsDisplay, fullscreen: Option, + initial_placement: Option, hwnd: HWND, } @@ -97,6 +98,7 @@ impl WindowsWindowState { let system_settings = WindowsSystemSettings::new(display); let nc_button_pressed = None; let fullscreen = None; + let initial_placement = None; Ok(Self { origin, @@ -114,6 +116,7 @@ impl WindowsWindowState { nc_button_pressed, display, fullscreen, + initial_placement, hwnd, }) } @@ -231,6 +234,14 @@ impl WindowsWindowStatePtr { main_receiver: context.main_receiver.clone(), })) } + + fn set_window_placement(&self) -> Result<()> { + let Some(placement) = self.state.borrow_mut().initial_placement.take() else { + return Ok(()); + }; + unsafe { SetWindowPlacement(self.hwnd, &placement)? }; + Ok(()) + } } #[derive(Default)] @@ -295,9 +306,6 @@ impl WindowsWindow { WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, ) }; - if !params.show { - dwstyle |= WS_MINIMIZE; - } let hinstance = get_module_handle(); let display = if let Some(display_id) = params.display_id { @@ -336,36 +344,24 @@ impl WindowsWindow { lpparam, ) }; - // We should call `?` on state_ptr first, then call `?` on raw_hwnd. + // We should call `?` on state_ptr first, then call `?` on hwnd. // Or, we will lose the error info reported by `WindowsWindowState::new` let state_ptr = context.inner.take().unwrap()?; - let raw_hwnd = creation_result?; + let hwnd = creation_result?; register_drag_drop(state_ptr.clone())?; - unsafe { - let mut placement = WINDOWPLACEMENT { - length: std::mem::size_of::() as u32, - ..Default::default() - }; - GetWindowPlacement(raw_hwnd, &mut placement)?; - // the bounds may be not inside the display - let bounds = if display.check_given_bounds(params.bounds) { - params.bounds - } else { - display.default_bounds() - }; - let mut lock = state_ptr.state.borrow_mut(); - let bounds = bounds.to_device_pixels(lock.scale_factor); - lock.border_offset.update(raw_hwnd)?; - placement.rcNormalPosition = calculate_window_rect(bounds, lock.border_offset); - drop(lock); - SetWindowPlacement(raw_hwnd, &placement)?; - } - + state_ptr.state.borrow_mut().border_offset.update(hwnd)?; + let placement = retrieve_window_placement( + hwnd, + display, + params.bounds, + state_ptr.state.borrow().scale_factor, + state_ptr.state.borrow().border_offset, + )?; if params.show { - unsafe { ShowWindow(raw_hwnd, SW_SHOW).ok()? }; + unsafe { SetWindowPlacement(hwnd, &placement)? }; } else { - unsafe { ShowWindow(raw_hwnd, SW_HIDE).ok()? }; + state_ptr.state.borrow_mut().initial_placement = Some(placement); } Ok(Self(state_ptr)) @@ -541,11 +537,18 @@ impl PlatformWindow for WindowsWindow { fn activate(&self) { let hwnd = self.0.hwnd; - unsafe { SetActiveWindow(hwnd).log_err() }; - unsafe { SetFocus(hwnd).log_err() }; - // todo(windows) - // crate `windows 0.56` reports true as Err - unsafe { SetForegroundWindow(hwnd).as_bool() }; + let this = self.0.clone(); + self.0 + .executor + .spawn(async move { + this.set_window_placement().log_err(); + unsafe { SetActiveWindow(hwnd).log_err() }; + unsafe { SetFocus(hwnd).log_err() }; + // todo(windows) + // crate `windows 0.56` reports true as Err + unsafe { SetForegroundWindow(hwnd).as_bool() }; + }) + .detach(); } fn is_active(&self) -> bool { @@ -1066,6 +1069,29 @@ fn calculate_client_rect( } } +fn retrieve_window_placement( + hwnd: HWND, + display: WindowsDisplay, + initial_bounds: Bounds, + scale_factor: f32, + border_offset: WindowBorderOffset, +) -> Result { + let mut placement = WINDOWPLACEMENT { + length: std::mem::size_of::() as u32, + ..Default::default() + }; + unsafe { GetWindowPlacement(hwnd, &mut placement)? }; + // the bounds may be not inside the display + let bounds = if display.check_given_bounds(initial_bounds) { + initial_bounds + } else { + display.default_bounds() + }; + let bounds = bounds.to_device_pixels(scale_factor); + placement.rcNormalPosition = calculate_window_rect(bounds, border_offset); + Ok(placement) +} + mod windows_renderer { use std::{num::NonZeroIsize, sync::Arc};