Skip to content

Commit

Permalink
Fix window drawing when switching X11 workspaces by presenting when e…
Browse files Browse the repository at this point in the history
…xpose events occur (#20535)

Closes #18184

Release Notes:

- Fix window drawing when switching X11 workspaces, particularly for tiling window managers such as i3wm and XMonad.
  • Loading branch information
mgsloan authored Nov 12, 2024
1 parent ad3171d commit aad3ed7
Show file tree
Hide file tree
Showing 9 changed files with 40 additions and 32 deletions.
7 changes: 6 additions & 1 deletion crates/gpui/src/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Pixels>;
fn is_maximized(&self) -> bool;
Expand Down Expand Up @@ -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<dyn FnMut()>);
fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>);
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)>);
Expand Down
12 changes: 6 additions & 6 deletions crates/gpui/src/platform/linux/wayland/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Box<dyn FnMut()>>,
request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,
input: Option<Box<dyn FnMut(crate::PlatformInput) -> crate::DispatchEventResult>>,
active_status_change: Option<Box<dyn FnMut(bool)>>,
hover_status_change: Option<Box<dyn FnMut(bool)>>,
Expand Down Expand Up @@ -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());
}
}

Expand Down Expand Up @@ -902,7 +902,7 @@ impl PlatformWindow for WaylandWindow {
self.borrow().fullscreen
}

fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>) {
self.0.callbacks.borrow_mut().request_frame = Some(callback);
}

Expand Down
10 changes: 6 additions & 4 deletions crates/gpui/src/platform/linux/x11/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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,
});
}
}

Expand Down Expand Up @@ -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
};
Expand Down
14 changes: 7 additions & 7 deletions crates/gpui/src/platform/linux/x11/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -227,7 +227,7 @@ struct RawWindow {

#[derive(Default)]
pub struct Callbacks {
request_frame: Option<Box<dyn FnMut()>>,
request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,
input: Option<Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>>,
active_status_change: Option<Box<dyn FnMut(bool)>>,
hovered_status_change: Option<Box<dyn FnMut(bool)>>,
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -1204,7 +1204,7 @@ impl PlatformWindow for X11Window {
self.0.state.borrow().fullscreen
}

fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>) {
self.0.callbacks.borrow_mut().request_frame = Some(callback);
}

Expand Down
12 changes: 6 additions & 6 deletions crates/gpui/src/platform/mac/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -316,7 +316,7 @@ struct MacWindowState {
native_view: NonNull<Object>,
display_link: Option<DisplayLink>,
renderer: renderer::Renderer,
request_frame_callback: Option<Box<dyn FnMut()>>,
request_frame_callback: Option<Box<dyn FnMut(RequestFrameOptions)>>,
event_callback: Option<Box<dyn FnMut(PlatformInput) -> crate::DispatchEventResult>>,
activate_callback: Option<Box<dyn FnMut(bool)>>,
resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
Expand Down Expand Up @@ -1060,7 +1060,7 @@ impl PlatformWindow for MacWindow {
}
}

fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>) {
self.0.as_ref().lock().request_frame_callback = Some(callback);
}

Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
}
Expand Down
6 changes: 3 additions & 3 deletions crates/gpui/src/platform/test/window.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -221,7 +221,7 @@ impl PlatformWindow for TestWindow {
self.0.lock().is_fullscreen
}

fn on_request_frame(&self, _callback: Box<dyn FnMut()>) {}
fn on_request_frame(&self, _callback: Box<dyn FnMut(RequestFrameOptions)>) {}

fn on_input(&self, callback: Box<dyn FnMut(crate::PlatformInput) -> DispatchEventResult>) {
self.0.lock().input_callback = Some(callback)
Expand Down
2 changes: 1 addition & 1 deletion crates/gpui/src/platform/windows/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ fn handle_paint_msg(handle: HWND, state_ptr: Rc<WindowsWindowStatePtr>) -> 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() };
Expand Down
4 changes: 2 additions & 2 deletions crates/gpui/src/platform/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ impl WindowsWindowStatePtr {

#[derive(Default)]
pub(crate) struct Callbacks {
pub(crate) request_frame: Option<Box<dyn FnMut()>>,
pub(crate) request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,
pub(crate) input: Option<Box<dyn FnMut(crate::PlatformInput) -> DispatchEventResult>>,
pub(crate) active_status_change: Option<Box<dyn FnMut(bool)>>,
pub(crate) resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
Expand Down Expand Up @@ -680,7 +680,7 @@ impl PlatformWindow for WindowsWindow {
self.0.state.borrow().is_fullscreen()
}

fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
fn on_request_frame(&self, callback: Box<dyn FnMut(RequestFrameOptions)>) {
self.0.state.borrow_mut().callbacks.request_frame = Some(callback);
}

Expand Down
5 changes: 3 additions & 2 deletions crates/gpui/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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));

Expand Down

0 comments on commit aad3ed7

Please sign in to comment.