Disable extra frames for ProMotion when screen is not active. (#7410)

Mikayla Maki , max , nathan , and antonio created

This was causing an issue where windows about 1/10 of the way across the
display would hang for a fully second after being deactivated.

Release Notes:

- N/A

Co-authored-by: max <max@zed.dev>
Co-authored-by: nathan <nathan@zed.dev>
Co-authored-by: antonio <antonio@zed.dev>

Change summary

crates/gpui/src/platform/mac/window.rs |  1 +
crates/gpui/src/window.rs              | 23 ++++++++++++++---------
2 files changed, 15 insertions(+), 9 deletions(-)

Detailed changes

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

@@ -1530,6 +1530,7 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
 extern "C" fn step(this: &Object, _: Sel, display_link: id) {
     let window_state = unsafe { get_window_state(this) };
     let mut lock = window_state.lock();
+
     if lock.display_link == display_link {
         if let Some(mut callback) = lock.request_frame_callback.take() {
             drop(lock);

crates/gpui/src/window.rs 🔗

@@ -271,7 +271,7 @@ pub struct Window {
     bounds_observers: SubscriberSet<(), AnyObserver>,
     appearance: WindowAppearance,
     appearance_observers: SubscriberSet<(), AnyObserver>,
-    active: bool,
+    active: Rc<Cell<bool>>,
     pub(crate) dirty: Rc<Cell<bool>>,
     pub(crate) last_input_timestamp: Rc<Cell<Instant>>,
     pub(crate) refreshing: bool,
@@ -337,11 +337,13 @@ impl Window {
         let appearance = platform_window.appearance();
         let text_system = Arc::new(WindowTextSystem::new(cx.text_system().clone()));
         let dirty = Rc::new(Cell::new(true));
+        let active = Rc::new(Cell::new(false));
         let last_input_timestamp = Rc::new(Cell::new(Instant::now()));
 
         platform_window.on_request_frame(Box::new({
             let mut cx = cx.to_async();
             let dirty = dirty.clone();
+            let active = active.clone();
             let last_input_timestamp = last_input_timestamp.clone();
             move || {
                 if dirty.get() {
@@ -353,9 +355,12 @@ impl Window {
                             })
                             .log_err();
                     })
-                } else if last_input_timestamp.get().elapsed() < Duration::from_secs(1) {
-                    // Keep presenting the current scene for 1 extra second since the
-                    // last input to prevent the display from underclocking the refresh rate.
+                }
+                // Keep presenting the current scene for 1 extra second since the
+                // last input to prevent the display from underclocking the refresh rate.
+                else if active.get()
+                    && last_input_timestamp.get().elapsed() < Duration::from_secs(1)
+                {
                     handle.update(&mut cx, |_, cx| cx.present()).log_err();
                 }
             }
@@ -389,7 +394,7 @@ impl Window {
             move |active| {
                 handle
                     .update(&mut cx, |_, cx| {
-                        cx.window.active = active;
+                        cx.window.active.set(active);
                         cx.window
                             .activation_observers
                             .clone()
@@ -435,7 +440,7 @@ impl Window {
             bounds_observers: SubscriberSet::new(),
             appearance,
             appearance_observers: SubscriberSet::new(),
-            active: false,
+            active,
             dirty,
             last_input_timestamp,
             refreshing: false,
@@ -777,7 +782,7 @@ impl<'a> WindowContext<'a> {
 
     /// Returns whether this window is focused by the operating system (receiving key events).
     pub fn is_window_active(&self) -> bool {
-        self.window.active
+        self.window.active.get()
     }
 
     /// Toggle zoom on the window.
@@ -1033,7 +1038,7 @@ impl<'a> WindowContext<'a> {
                 self.window.focus,
             );
         self.window.next_frame.focus = self.window.focus;
-        self.window.next_frame.window_active = self.window.active;
+        self.window.next_frame.window_active = self.window.active.get();
         self.window.root_view = Some(root_view);
 
         // Set the cursor only if we're the active window.
@@ -2553,7 +2558,7 @@ impl<V: 'static + Render> WindowHandle<V> {
     pub fn is_active(&self, cx: &AppContext) -> Option<bool> {
         cx.windows
             .get(self.id)
-            .and_then(|window| window.as_ref().map(|window| window.active))
+            .and_then(|window| window.as_ref().map(|window| window.active.get()))
     }
 }