From 2539d57ac76628deaefc7d621c63018cfcf1f993 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Mon, 16 Jun 2025 22:27:02 -0600 Subject: [PATCH] wayland: Avoid reloading cursor theme on every cursor style change (#32832) Release Notes: - N/A --- .../gpui/src/platform/linux/wayland/client.rs | 2 +- .../gpui/src/platform/linux/wayland/cursor.rs | 103 ++++++++++-------- 2 files changed, 58 insertions(+), 47 deletions(-) diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 62f1d091c78988d826695984f89a643c1001c835..77b8cf5ccaba4afb8625dc55456a1897591a373d 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -537,7 +537,7 @@ impl WaylandClient { XDPEvent::CursorTheme(theme) => { if let Some(client) = client.0.upgrade() { let mut client = client.borrow_mut(); - client.cursor.set_theme(theme.as_str()); + client.cursor.set_theme(theme); } } XDPEvent::CursorSize(size) => { diff --git a/crates/gpui/src/platform/linux/wayland/cursor.rs b/crates/gpui/src/platform/linux/wayland/cursor.rs index f15fb5dde23bf0bb729a0812624b5634cd120eec..2a24d0e1ba347fb718da126120bc809c65d93b33 100644 --- a/crates/gpui/src/platform/linux/wayland/cursor.rs +++ b/crates/gpui/src/platform/linux/wayland/cursor.rs @@ -1,6 +1,6 @@ use crate::Globals; use crate::platform::linux::{DEFAULT_CURSOR_ICON_NAME, log_cursor_icon_warning}; -use anyhow::anyhow; +use anyhow::{Context as _, anyhow}; use util::ResultExt; use wayland_client::Connection; @@ -9,75 +9,85 @@ use wayland_client::protocol::{wl_pointer::WlPointer, wl_shm::WlShm}; use wayland_cursor::{CursorImageBuffer, CursorTheme}; pub(crate) struct Cursor { - theme: Option, - theme_name: Option, - theme_size: u32, - surface: WlSurface, + loaded_theme: Option, size: u32, + scaled_size: u32, + surface: WlSurface, shm: WlShm, connection: Connection, } +pub(crate) struct LoadedTheme { + theme: CursorTheme, + name: Option, + scaled_size: u32, +} + impl Drop for Cursor { fn drop(&mut self) { - self.theme.take(); + self.loaded_theme.take(); self.surface.destroy(); } } impl Cursor { pub fn new(connection: &Connection, globals: &Globals, size: u32) -> Self { - Self { - theme: CursorTheme::load(&connection, globals.shm.clone(), size).log_err(), - theme_name: None, - theme_size: size, + let mut this = Self { + loaded_theme: None, + size, + scaled_size: size, surface: globals.compositor.create_surface(&globals.qh, ()), shm: globals.shm.clone(), connection: connection.clone(), - size, - } + }; + this.set_theme_internal(None); + this } - pub fn set_theme(&mut self, theme_name: &str) { - if let Some(theme) = CursorTheme::load_from_name( - &self.connection, - self.shm.clone(), - theme_name, - self.theme_size, - ) - .log_err() - { - self.theme = Some(theme); - self.theme_name = Some(theme_name.to_string()); - } else if let Some(theme) = - CursorTheme::load(&self.connection, self.shm.clone(), self.theme_size).log_err() + fn set_theme_internal(&mut self, theme_name: Option) { + if let Some(loaded_theme) = self.loaded_theme.as_ref() { + if loaded_theme.name == theme_name && loaded_theme.scaled_size == self.scaled_size { + return; + } + } + let result = if let Some(theme_name) = theme_name.as_ref() { + CursorTheme::load_from_name( + &self.connection, + self.shm.clone(), + theme_name, + self.scaled_size, + ) + } else { + CursorTheme::load(&self.connection, self.shm.clone(), self.scaled_size) + }; + if let Some(theme) = result + .context("Wayland: Failed to load cursor theme") + .log_err() { - self.theme = Some(theme); - self.theme_name = None; + self.loaded_theme = Some(LoadedTheme { + theme, + name: theme_name.map(|name| name.to_string()), + scaled_size: self.scaled_size, + }); } } - fn set_theme_size(&mut self, theme_size: u32) { - self.theme = self - .theme_name + pub fn set_theme(&mut self, theme_name: String) { + self.set_theme_internal(Some(theme_name)); + } + + fn set_scaled_size(&mut self, scaled_size: u32) { + self.scaled_size = scaled_size; + let theme_name = self + .loaded_theme .as_ref() - .and_then(|name| { - CursorTheme::load_from_name( - &self.connection, - self.shm.clone(), - name.as_str(), - theme_size, - ) - .log_err() - }) - .or_else(|| { - CursorTheme::load(&self.connection, self.shm.clone(), theme_size).log_err() - }); + .and_then(|loaded_theme| loaded_theme.name.clone()); + self.set_theme_internal(theme_name); } pub fn set_size(&mut self, size: u32) { self.size = size; - self.set_theme_size(size); + self.set_scaled_size(size); } pub fn set_icon( @@ -87,12 +97,13 @@ impl Cursor { mut cursor_icon_names: &[&str], scale: i32, ) { - self.set_theme_size(self.size * scale as u32); + self.set_scaled_size(self.size * scale as u32); - let Some(theme) = &mut self.theme else { + let Some(loaded_theme) = &mut self.loaded_theme else { log::warn!("Wayland: Unable to load cursor themes"); return; }; + let mut theme = &mut loaded_theme.theme; let mut buffer: &CursorImageBuffer; 'outer: { @@ -115,7 +126,7 @@ impl Cursor { log_cursor_icon_warning(anyhow!( "wayland: Unable to fallback on default cursor icon '{}' for theme '{}'", DEFAULT_CURSOR_ICON_NAME, - self.theme_name.as_deref().unwrap_or("default") + loaded_theme.name.as_deref().unwrap_or("default") )); return; }