gpui: Fix window display on Windows (#18705)

张小白 created

- 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

Change summary

crates/gpui/src/platform/windows/window.rs | 90 +++++++++++++++--------
1 file changed, 58 insertions(+), 32 deletions(-)

Detailed changes

crates/gpui/src/platform/windows/window.rs 🔗

@@ -52,6 +52,7 @@ pub struct WindowsWindowState {
 
     pub display: WindowsDisplay,
     fullscreen: Option<StyleAndBounds>,
+    initial_placement: Option<WINDOWPLACEMENT>,
     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::<WINDOWPLACEMENT>() 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<Pixels>,
+    scale_factor: f32,
+    border_offset: WindowBorderOffset,
+) -> Result<WINDOWPLACEMENT> {
+    let mut placement = WINDOWPLACEMENT {
+        length: std::mem::size_of::<WINDOWPLACEMENT>() 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};