From c251f2a2d4f655b3380b532b74231e214398230f Mon Sep 17 00:00:00 2001 From: Kamal Ahmad Date: Mon, 21 Jul 2025 19:48:01 +0500 Subject: [PATCH] gpui: Throttle interactive resize events on Wayland (#34760) Wayland compositors can potentially generate thousands of resize requests when drag-resizing a window, which Zed is unable to keep up with. This commit changes the behavior to only resize once per vblank cycle, which helps significantly when resizing the window with a high poll-rate mouse (1000Hz is common these days) Here is an example of the behavior pre and post this commit, with some print-debugging added for tracking resize calls. I have a wireless mouse with a 2000Hz polling rate and a 165Hz display: Before: https://github.com/user-attachments/assets/4c657f90-5fd2-4809-97ef-363fd48e81b8 After: https://github.com/user-attachments/assets/4a0f5fbd-c3c4-40a1-9f71-3b4358c827cf Closes #20660 Release Notes: - Improved: Make resizing smoother on Wayland/Linux --- crates/gpui/src/platform/linux/wayland/window.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crates/gpui/src/platform/linux/wayland/window.rs b/crates/gpui/src/platform/linux/wayland/window.rs index 36e070b0b0..255ae9c372 100644 --- a/crates/gpui/src/platform/linux/wayland/window.rs +++ b/crates/gpui/src/platform/linux/wayland/window.rs @@ -76,6 +76,7 @@ struct InProgressConfigure { size: Option>, fullscreen: bool, maximized: bool, + resizing: bool, tiling: Tiling, } @@ -107,6 +108,7 @@ pub struct WaylandWindowState { active: bool, hovered: bool, in_progress_configure: Option, + resize_throttle: bool, in_progress_window_controls: Option, window_controls: WindowControls, inset: Option, @@ -176,6 +178,7 @@ impl WaylandWindowState { tiling: Tiling::default(), window_bounds: options.bounds, in_progress_configure: None, + resize_throttle: false, client, appearance, handle, @@ -335,6 +338,7 @@ impl WaylandWindowStatePtr { pub fn frame(&self) { let mut state = self.state.borrow_mut(); state.surface.frame(&state.globals.qh, state.surface.id()); + state.resize_throttle = false; drop(state); let mut cb = self.callbacks.borrow_mut(); @@ -366,6 +370,12 @@ impl WaylandWindowStatePtr { state.fullscreen = configure.fullscreen; state.maximized = configure.maximized; state.tiling = configure.tiling; + // Limit interactive resizes to once per vblank + if configure.resizing && state.resize_throttle { + return; + } else if configure.resizing { + state.resize_throttle = true; + } if !configure.fullscreen && !configure.maximized { configure.size = if got_unmaximized { Some(state.window_bounds.size) @@ -472,6 +482,7 @@ impl WaylandWindowStatePtr { let mut tiling = Tiling::default(); let mut fullscreen = false; let mut maximized = false; + let mut resizing = false; for state in states { match state { @@ -481,6 +492,7 @@ impl WaylandWindowStatePtr { xdg_toplevel::State::Fullscreen => { fullscreen = true; } + xdg_toplevel::State::Resizing => resizing = true, xdg_toplevel::State::TiledTop => { tiling.top = true; } @@ -508,6 +520,7 @@ impl WaylandWindowStatePtr { size, fullscreen, maximized, + resizing, tiling, });