From aad3ed7f9135aac72527c10cf568fc13966bcb9b Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Tue, 12 Nov 2024 01:20:25 -0700 Subject: [PATCH] Fix window drawing when switching X11 workspaces by presenting when expose events occur (#20535) Closes #18184 Release Notes: - Fix window drawing when switching X11 workspaces, particularly for tiling window managers such as i3wm and XMonad. --- crates/gpui/src/platform.rs | 7 ++++++- crates/gpui/src/platform/linux/wayland/window.rs | 12 ++++++------ crates/gpui/src/platform/linux/x11/client.rs | 10 ++++++---- crates/gpui/src/platform/linux/x11/window.rs | 14 +++++++------- crates/gpui/src/platform/mac/window.rs | 12 ++++++------ crates/gpui/src/platform/test/window.rs | 6 +++--- crates/gpui/src/platform/windows/events.rs | 2 +- crates/gpui/src/platform/windows/window.rs | 4 ++-- crates/gpui/src/window.rs | 5 +++-- 9 files changed, 40 insertions(+), 32 deletions(-) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index fd87792146eb2261ad75fb151bd97c2ecf9bd3eb..a8424d197a76221c06c6b33422c2d79a72f01487 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -339,6 +339,11 @@ impl Tiling { } } +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] +pub(crate) struct RequestFrameOptions { + pub(crate) require_presentation: bool, +} + pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { fn bounds(&self) -> Bounds; fn is_maximized(&self) -> bool; @@ -367,7 +372,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); + fn on_request_frame(&self, callback: Box); fn on_input(&self, callback: Box DispatchEventResult>); fn on_active_status_change(&self, callback: Box); fn on_hover_status_change(&self, callback: Box); diff --git a/crates/gpui/src/platform/linux/wayland/window.rs b/crates/gpui/src/platform/linux/wayland/window.rs index 1ca358edecd0af9513666bf2d9ce1ab04c70b5ff..8d4516b3f37b72dbf601e173475777bc260c2f15 100644 --- a/crates/gpui/src/platform/linux/wayland/window.rs +++ b/crates/gpui/src/platform/linux/wayland/window.rs @@ -26,14 +26,14 @@ use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow}; use crate::scene::Scene; use crate::{ px, size, AnyWindowHandle, Bounds, Decorations, GPUSpecs, Globals, Modifiers, Output, Pixels, - PlatformDisplay, PlatformInput, Point, PromptLevel, ResizeEdge, ScaledPixels, Size, Tiling, - WaylandClientStatePtr, WindowAppearance, WindowBackgroundAppearance, WindowBounds, - WindowControls, WindowDecorations, WindowParams, + PlatformDisplay, PlatformInput, Point, PromptLevel, RequestFrameOptions, ResizeEdge, + ScaledPixels, Size, Tiling, WaylandClientStatePtr, WindowAppearance, + WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations, WindowParams, }; #[derive(Default)] pub(crate) struct Callbacks { - request_frame: Option>, + request_frame: Option>, input: Option crate::DispatchEventResult>>, active_status_change: Option>, hover_status_change: Option>, @@ -323,7 +323,7 @@ impl WaylandWindowStatePtr { let mut cb = self.callbacks.borrow_mut(); if let Some(fun) = cb.request_frame.as_mut() { - fun(); + fun(Default::default()); } } @@ -902,7 +902,7 @@ impl PlatformWindow for WaylandWindow { self.borrow().fullscreen } - fn on_request_frame(&self, callback: Box) { + fn on_request_frame(&self, callback: Box) { self.0.callbacks.borrow_mut().request_frame = Some(callback); } diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index d18bc4b2777280720243c1db9c21dcf13541492c..82ef39fc6b2a775ac3df61dd85d42fa766ec2d4b 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -38,8 +38,8 @@ use crate::platform::{LinuxCommon, PlatformWindow}; use crate::{ modifiers_from_xinput_info, point, px, AnyWindowHandle, Bounds, ClipboardItem, CursorStyle, DisplayId, FileDropEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, Pixels, - Platform, PlatformDisplay, PlatformInput, Point, ScaledPixels, ScrollDelta, Size, TouchPhase, - WindowParams, X11Window, + Platform, PlatformDisplay, PlatformInput, Point, RequestFrameOptions, ScaledPixels, + ScrollDelta, Size, TouchPhase, WindowParams, X11Window, }; use super::{ @@ -531,7 +531,9 @@ impl X11Client { for window in windows_to_refresh.into_iter() { if let Some(window) = self.get_window(window) { - window.refresh(); + window.refresh(RequestFrameOptions { + require_presentation: true, + }); } } @@ -1356,7 +1358,7 @@ impl LinuxClient for X11Client { if let Some(window) = state.windows.get(&x_window) { let window = window.window.clone(); drop(state); - window.refresh(); + window.refresh(Default::default()); } xcb_connection }; diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index 2884c7ea91a51d15e819266424c536601727e084..15712233c2ffef000be8dbff392cdfc9ae81d01b 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -4,9 +4,9 @@ use crate::{ platform::blade::{BladeRenderer, BladeSurfaceConfig}, px, size, AnyWindowHandle, Bounds, Decorations, DevicePixels, ForegroundExecutor, GPUSpecs, Modifiers, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, - PlatformWindow, Point, PromptLevel, ResizeEdge, ScaledPixels, Scene, Size, Tiling, - WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind, - WindowParams, X11ClientStatePtr, + PlatformWindow, Point, PromptLevel, RequestFrameOptions, ResizeEdge, ScaledPixels, Scene, Size, + Tiling, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowDecorations, + WindowKind, WindowParams, X11ClientStatePtr, }; use blade_graphics as gpu; @@ -227,7 +227,7 @@ struct RawWindow { #[derive(Default)] pub struct Callbacks { - request_frame: Option>, + request_frame: Option>, input: Option crate::DispatchEventResult>>, active_status_change: Option>, hovered_status_change: Option>, @@ -830,10 +830,10 @@ impl X11WindowStatePtr { } } - pub fn refresh(&self) { + pub fn refresh(&self, request_frame_options: RequestFrameOptions) { let mut cb = self.callbacks.borrow_mut(); if let Some(ref mut fun) = cb.request_frame { - fun(); + fun(request_frame_options); } } @@ -1204,7 +1204,7 @@ impl PlatformWindow for X11Window { self.0.state.borrow().fullscreen } - fn on_request_frame(&self, callback: Box) { + fn on_request_frame(&self, callback: Box) { self.0.callbacks.borrow_mut().request_frame = Some(callback); } diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index dba8942164c8985be18f0199ba5e2ac02c38aefd..f0a80a220769e0d051f2c1e5f2e0055a6594def7 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -4,8 +4,8 @@ use crate::{ ExternalPaths, FileDropEvent, ForegroundExecutor, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point, PromptLevel, - ScaledPixels, Size, Timer, WindowAppearance, WindowBackgroundAppearance, WindowBounds, - WindowKind, WindowParams, + RequestFrameOptions, ScaledPixels, Size, Timer, WindowAppearance, WindowBackgroundAppearance, + WindowBounds, WindowKind, WindowParams, }; use block::ConcreteBlock; use cocoa::{ @@ -316,7 +316,7 @@ struct MacWindowState { native_view: NonNull, display_link: Option, renderer: renderer::Renderer, - request_frame_callback: Option>, + request_frame_callback: Option>, event_callback: Option crate::DispatchEventResult>>, activate_callback: Option>, resize_callback: Option, f32)>>, @@ -1060,7 +1060,7 @@ impl PlatformWindow for MacWindow { } } - fn on_request_frame(&self, callback: Box) { + fn on_request_frame(&self, callback: Box) { self.0.as_ref().lock().request_frame_callback = Some(callback); } @@ -1617,7 +1617,7 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) { lock.renderer.set_presents_with_transaction(true); lock.stop_display_link(); drop(lock); - callback(); + callback(Default::default()); let mut lock = window_state.lock(); lock.request_frame_callback = Some(callback); @@ -1634,7 +1634,7 @@ unsafe extern "C" fn step(view: *mut c_void) { if let Some(mut callback) = lock.request_frame_callback.take() { drop(lock); - callback(); + callback(Default::default()); window_state.lock().request_frame_callback = Some(callback); } } diff --git a/crates/gpui/src/platform/test/window.rs b/crates/gpui/src/platform/test/window.rs index ebbf6ecc12b466a448735c3c6eb984a42ab3feda..d8ec6a718b0d21b6e9c72d18b7f770ae0f798786 100644 --- a/crates/gpui/src/platform/test/window.rs +++ b/crates/gpui/src/platform/test/window.rs @@ -1,8 +1,8 @@ use crate::{ AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DispatchEventResult, GPUSpecs, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, - Point, ScaledPixels, Size, TestPlatform, TileId, WindowAppearance, WindowBackgroundAppearance, - WindowBounds, WindowParams, + Point, RequestFrameOptions, ScaledPixels, Size, TestPlatform, TileId, WindowAppearance, + WindowBackgroundAppearance, WindowBounds, WindowParams, }; use collections::HashMap; use parking_lot::Mutex; @@ -221,7 +221,7 @@ impl PlatformWindow for TestWindow { self.0.lock().is_fullscreen } - fn on_request_frame(&self, _callback: Box) {} + fn on_request_frame(&self, _callback: Box) {} fn on_input(&self, callback: Box DispatchEventResult>) { self.0.lock().input_callback = Some(callback) diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index b62f51f6d9b94e90e1af583e2508eb4fa126e69e..1f501f3341e3e055baac77310e4bd7172771d057 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -190,7 +190,7 @@ fn handle_paint_msg(handle: HWND, state_ptr: Rc) -> Optio let mut lock = state_ptr.state.borrow_mut(); if let Some(mut request_frame) = lock.callbacks.request_frame.take() { drop(lock); - request_frame(); + request_frame(Default::default()); state_ptr.state.borrow_mut().callbacks.request_frame = Some(request_frame); } unsafe { ValidateRect(handle, None).ok().log_err() }; diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index a363745f9baf0abe3114966c779d1fa1de6f0c5c..46b54e689d6ad75abae12cd561382a81f78694fd 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -323,7 +323,7 @@ impl WindowsWindowStatePtr { #[derive(Default)] pub(crate) struct Callbacks { - pub(crate) request_frame: Option>, + pub(crate) request_frame: Option>, pub(crate) input: Option DispatchEventResult>>, pub(crate) active_status_change: Option>, pub(crate) resize: Option, f32)>>, @@ -680,7 +680,7 @@ impl PlatformWindow for WindowsWindow { self.0.state.borrow().is_fullscreen() } - fn on_request_frame(&self, callback: Box) { + fn on_request_frame(&self, callback: Box) { self.0.state.borrow_mut().callbacks.request_frame = Some(callback); } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index b5aa39a9bb0b6b3d96ea768183ef39f36b20a95c..ba974567a6ce518db1b57a32cd69250d4f3b4a06 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -681,7 +681,7 @@ impl Window { let needs_present = needs_present.clone(); let next_frame_callbacks = next_frame_callbacks.clone(); let last_input_timestamp = last_input_timestamp.clone(); - move || { + move |request_frame_options| { let next_frame_callbacks = next_frame_callbacks.take(); if !next_frame_callbacks.is_empty() { handle @@ -695,7 +695,8 @@ impl Window { // 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 = needs_present.get() + let needs_present = request_frame_options.require_presentation + || needs_present.get() || (active.get() && last_input_timestamp.get().elapsed() < Duration::from_secs(1));