ensure app is idle

Junkui Zhang created

Change summary

crates/gpui/src/platform.rs                |  2 +-
crates/gpui/src/platform/windows/events.rs | 17 ++++++++++++++---
crates/gpui/src/platform/windows/window.rs |  6 +++---
crates/gpui/src/window.rs                  |  6 +++++-
4 files changed, 23 insertions(+), 8 deletions(-)

Detailed changes

crates/gpui/src/platform.rs 🔗

@@ -479,7 +479,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
     fn zoom(&self);
     fn toggle_fullscreen(&self);
     fn is_fullscreen(&self) -> bool;
-    fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>);
+    fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions) -> bool>);
     fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> DispatchEventResult>);
     fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
     fn on_hover_status_change(&self, callback: Box<dyn FnMut(bool)>);

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

@@ -237,12 +237,23 @@ fn handle_timer_msg(
 
 fn handle_paint_msg(handle: HWND, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
     let mut lock = state_ptr.state.borrow_mut();
-    if let Some(mut request_frame) = lock.callbacks.request_frame.take() {
+    let is_idle = if let Some(mut request_frame) = lock.callbacks.request_frame.take() {
         drop(lock);
-        request_frame(Default::default());
+        // request_frame(RequestFrameOptions {
+        //     require_presentation: true,
+        // });
+        let is_idle = request_frame(Default::default());
         state_ptr.state.borrow_mut().callbacks.request_frame = Some(request_frame);
-    }
+        is_idle
+    } else {
+        false
+    };
     unsafe { ValidateRect(Some(handle), None).ok().log_err() };
+    if is_idle {
+        unsafe {
+            MsgWaitForMultipleObjects(None, false, 100, QS_ALLINPUT);
+        }
+    }
     Some(0)
 }
 

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

@@ -38,7 +38,7 @@ pub struct WindowsWindowState {
     pub border_offset: WindowBorderOffset,
     pub appearance: WindowAppearance,
     pub scale_factor: f32,
-    pub restore_from_minimized: Option<Box<dyn FnMut(RequestFrameOptions)>>,
+    pub restore_from_minimized: Option<Box<dyn FnMut(RequestFrameOptions) -> bool>>,
 
     pub callbacks: Callbacks,
     pub input_handler: Option<PlatformInputHandler>,
@@ -312,7 +312,7 @@ impl WindowsWindowStatePtr {
 
 #[derive(Default)]
 pub(crate) struct Callbacks {
-    pub(crate) request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,
+    pub(crate) request_frame: Option<Box<dyn FnMut(RequestFrameOptions) -> bool>>,
     pub(crate) input: Option<Box<dyn FnMut(crate::PlatformInput) -> DispatchEventResult>>,
     pub(crate) active_status_change: Option<Box<dyn FnMut(bool)>>,
     pub(crate) hovered_status_change: Option<Box<dyn FnMut(bool)>>,
@@ -734,7 +734,7 @@ impl PlatformWindow for WindowsWindow {
         self.0.state.borrow().is_fullscreen()
     }
 
-    fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>) {
+    fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions) -> bool>) {
         self.0.state.borrow_mut().callbacks.request_frame = Some(callback);
     }
 

crates/gpui/src/window.rs 🔗

@@ -1012,6 +1012,7 @@ impl Window {
                         .log_err();
                 }
 
+                let invalidator_is_dirty = invalidator.is_dirty();
                 // Keep presenting the current scene for 1 extra second since the
                 // last input to prevent the display from underclocking the refresh rate.
                 let needs_present = request_frame_options.require_presentation
@@ -1019,7 +1020,7 @@ impl Window {
                     || (active.get()
                         && last_input_timestamp.get().elapsed() < Duration::from_secs(1));
 
-                if invalidator.is_dirty() {
+                if invalidator_is_dirty {
                     measure("frame duration", || {
                         handle
                             .update(&mut cx, |_, window, cx| {
@@ -1041,6 +1042,9 @@ impl Window {
                         window.complete_frame();
                     })
                     .log_err();
+
+                // Return true if app is idle
+                !(invalidator_is_dirty || needs_present)
             }
         }));
         platform_window.on_resize(Box::new({