windows: Fix main thread blocking when resizing or moving window (#10758)

张小白 created

Connection: Fix #10703 


https://github.com/zed-industries/zed/assets/14981363/59abfab7-ebb2-4da7-ad13-0a9e42f9c1d3




Release Notes:

- N/A

Change summary

crates/gpui/src/platform/windows/platform.rs | 12 +++++-
crates/gpui/src/platform/windows/window.rs   | 34 +++++++++++++++++++++
2 files changed, 42 insertions(+), 4 deletions(-)

Detailed changes

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

@@ -78,6 +78,13 @@ impl WindowsPlatformInner {
             .find(|entry| *entry == &hwnd)
             .and_then(|hwnd| try_get_window_inner(*hwnd))
     }
+
+    #[inline]
+    pub fn run_foreground_tasks(&self) {
+        for runnable in self.main_receiver.drain() {
+            runnable.run();
+        }
+    }
 }
 
 #[derive(Default)]
@@ -182,10 +189,9 @@ impl WindowsPlatform {
         Self { inner }
     }
 
+    #[inline]
     fn run_foreground_tasks(&self) {
-        for runnable in self.inner.main_receiver.drain() {
-            runnable.run();
-        }
+        self.inner.run_foreground_tasks();
     }
 
     fn redraw_all(&self) {

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

@@ -253,6 +253,9 @@ impl WindowsWindowInner {
             WM_CREATE => self.handle_create_msg(lparam),
             WM_MOVE => self.handle_move_msg(lparam),
             WM_SIZE => self.handle_size_msg(lparam),
+            WM_ENTERSIZEMOVE | WM_ENTERMENULOOP => self.handle_size_move_loop(),
+            WM_EXITSIZEMOVE | WM_EXITMENULOOP => self.handle_size_move_loop_exit(),
+            WM_TIMER => self.handle_timer_msg(wparam),
             WM_NCCALCSIZE => self.handle_calc_client_size(wparam, lparam),
             WM_DPICHANGED => self.handle_dpi_changed_msg(wparam, lparam),
             WM_NCHITTEST => self.handle_hit_test_msg(msg, wparam, lparam),
@@ -342,10 +345,38 @@ impl WindowsWindowInner {
             let logical_size = logical_size(new_physical_size, scale_factor);
             callback(logical_size, scale_factor);
         }
-        self.invalidate_client_area();
         Some(0)
     }
 
+    fn handle_size_move_loop(&self) -> Option<isize> {
+        unsafe {
+            let ret = SetTimer(self.hwnd, SIZE_MOVE_LOOP_TIMER_ID, USER_TIMER_MINIMUM, None);
+            if ret == 0 {
+                log::error!(
+                    "unable to create timer: {}",
+                    std::io::Error::last_os_error()
+                );
+            }
+        }
+        None
+    }
+
+    fn handle_size_move_loop_exit(&self) -> Option<isize> {
+        unsafe {
+            KillTimer(self.hwnd, SIZE_MOVE_LOOP_TIMER_ID).log_err();
+        }
+        None
+    }
+
+    fn handle_timer_msg(&self, wparam: WPARAM) -> Option<isize> {
+        if wparam.0 == SIZE_MOVE_LOOP_TIMER_ID {
+            self.platform_inner.run_foreground_tasks();
+            self.handle_paint_msg();
+            return Some(0);
+        }
+        None
+    }
+
     fn handle_paint_msg(&self) -> Option<isize> {
         let mut paint_struct = PAINTSTRUCT::default();
         let _hdc = unsafe { BeginPaint(self.hwnd, &mut paint_struct) };
@@ -1883,6 +1914,7 @@ const DRAGDROP_GET_FILES_COUNT: u32 = 0xFFFFFFFF;
 const DOUBLE_CLICK_INTERVAL: Duration = Duration::from_millis(500);
 // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmetrics
 const DOUBLE_CLICK_SPATIAL_TOLERANCE: i32 = 4;
+const SIZE_MOVE_LOOP_TIMER_ID: usize = 1;
 
 #[cfg(test)]
 mod tests {