diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index c9c672557f..bcab047fe4 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -95,8 +95,8 @@ type AnyObserver = Box bool + 'static>; type AnyWindowFocusListener = Box bool + 'static>; struct FocusEvent { - previous_focus_path: SmallVec<[FocusId; 8]>, - current_focus_path: SmallVec<[FocusId; 8]>, + old_focus_path: SmallVec<[FocusId; 8]>, + new_focus_path: SmallVec<[FocusId; 8]>, } slotmap::new_key_type! { @@ -344,24 +344,55 @@ impl Window { let last_input_timestamp = Rc::new(Cell::new(Instant::now())); platform_window.on_request_frame(Box::new({ - let mut cx = cx.to_async(); + let mut cx = AsyncWindowContext::new(cx.to_async(), handle); let dirty = dirty.clone(); let last_input_timestamp = last_input_timestamp.clone(); move || { - if dirty.get() { - measure("frame duration", || { - handle - .update(&mut cx, |_, cx| { - cx.draw(); - cx.present(); - }) - .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. - handle.update(&mut cx, |_, cx| cx.present()).log_err(); + fn handle_frame_request( + dirty: &Rc>, + last_input_timestamp: &Rc>, + cx: &mut AsyncWindowContext, + ) -> Result<()> { + if dirty.get() { + measure("frame duration", || { + let original_focus_path = + cx.update(|cx| cx.window.rendered_frame.focus_path())?; + + let mut draw_count = 0; + while dirty.get() { + cx.update(|cx| { + let old_window_active = cx.window.rendered_frame.focus_path(); + let old_focus_path = cx.window.rendered_frame.focus_path(); + + cx.draw(); + cx.notify_focus_listeners( + &original_focus_path, + old_focus_path, + old_window_active, + ); + })?; + draw_count += 1; + if draw_count > 2 { + log::warn!( + "redrew frame {} times due to focus changes", + draw_count + ); + } + } + + cx.update(|cx| cx.present())?; + Ok(()) + })?; + } 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. + cx.update(|cx| cx.present())?; + } + + Ok(()) } + + handle_frame_request(&dirty, &last_input_timestamp, &mut cx).log_err(); } })); platform_window.on_resize(Box::new({ @@ -1071,18 +1102,28 @@ impl<'a> WindowContext<'a> { } element_arena.clear(); }); - - let previous_focus_path = self.window.rendered_frame.focus_path(); - let previous_window_active = self.window.rendered_frame.window_active; mem::swap(&mut self.window.rendered_frame, &mut self.window.next_frame); self.window.next_frame.clear(); - let current_focus_path = self.window.rendered_frame.focus_path(); - let current_window_active = self.window.rendered_frame.window_active; + self.window.refreshing = false; + self.window.drawing = false; + } - if previous_focus_path != current_focus_path - || previous_window_active != current_window_active - { - if !previous_focus_path.is_empty() && current_focus_path.is_empty() { + fn notify_focus_listeners( + &mut self, + original_focus_path: &SmallVec<[FocusId; 8]>, + old_focus_path: SmallVec<[FocusId; 8]>, + old_window_active: bool, + ) { + let new_window_active = self.window.rendered_frame.window_active; + let new_focus_path = self.window.rendered_frame.focus_path(); + + if new_window_active == old_window_active && new_focus_path == *original_focus_path { + log::warn!("skipping focus notifications due to restoring focus to the original handle in a single frame"); + return; + } + + if new_focus_path != old_focus_path || new_window_active != old_window_active { + if !old_focus_path.is_empty() && new_focus_path.is_empty() { self.window .focus_lost_listeners .clone() @@ -1090,15 +1131,15 @@ impl<'a> WindowContext<'a> { } let event = FocusEvent { - previous_focus_path: if previous_window_active { - previous_focus_path + old_focus_path: if old_window_active { + old_focus_path } else { - Default::default() + SmallVec::new() }, - current_focus_path: if current_window_active { - current_focus_path + new_focus_path: if new_window_active { + new_focus_path } else { - Default::default() + SmallVec::new() }, }; self.window @@ -1106,8 +1147,6 @@ impl<'a> WindowContext<'a> { .clone() .retain(&(), |listener| listener(&event, self)); } - self.window.refreshing = false; - self.window.drawing = false; } fn present(&self) { @@ -2124,8 +2163,8 @@ impl<'a, V: 'static> ViewContext<'a, V> { (), Box::new(move |event, cx| { view.update(cx, |view, cx| { - if event.previous_focus_path.last() != Some(&focus_id) - && event.current_focus_path.last() == Some(&focus_id) + if event.old_focus_path.last() != Some(&focus_id) + && event.new_focus_path.last() == Some(&focus_id) { listener(view, cx) } @@ -2150,8 +2189,8 @@ impl<'a, V: 'static> ViewContext<'a, V> { (), Box::new(move |event, cx| { view.update(cx, |view, cx| { - if !event.previous_focus_path.contains(&focus_id) - && event.current_focus_path.contains(&focus_id) + if !event.old_focus_path.contains(&focus_id) + && event.new_focus_path.contains(&focus_id) { listener(view, cx) } @@ -2176,8 +2215,8 @@ impl<'a, V: 'static> ViewContext<'a, V> { (), Box::new(move |event, cx| { view.update(cx, |view, cx| { - if event.previous_focus_path.last() == Some(&focus_id) - && event.current_focus_path.last() != Some(&focus_id) + if event.old_focus_path.last() == Some(&focus_id) + && event.new_focus_path.last() != Some(&focus_id) { listener(view, cx) } @@ -2219,8 +2258,8 @@ impl<'a, V: 'static> ViewContext<'a, V> { (), Box::new(move |event, cx| { view.update(cx, |view, cx| { - if event.previous_focus_path.contains(&focus_id) - && !event.current_focus_path.contains(&focus_id) + if event.old_focus_path.contains(&focus_id) + && !event.new_focus_path.contains(&focus_id) { listener(view, cx) }