From d1717b6a01f4bcc11bd5129183fa76b7db757c43 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Wed, 17 Jan 2024 18:17:49 +0100 Subject: [PATCH] X11: cache custom cursors (#3366) --- src/cursor.rs | 5 +- src/platform_impl/linux/mod.rs | 24 +++++++- src/platform_impl/linux/wayland/mod.rs | 1 + src/platform_impl/linux/wayland/window/mod.rs | 4 +- .../linux/wayland/window/state.rs | 21 +++++-- src/platform_impl/linux/x11/mod.rs | 1 + src/platform_impl/linux/x11/util/cursor.rs | 59 ++++++++++++------- src/platform_impl/linux/x11/window.rs | 23 +++++--- 8 files changed, 100 insertions(+), 38 deletions(-) diff --git a/src/cursor.rs b/src/cursor.rs index 5090af5d68..e1ed4622dc 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -213,7 +213,10 @@ impl Eq for OnlyCursorImage {} #[allow(dead_code)] impl OnlyCursorImage { - fn build(builder: OnlyCursorImageBuilder, _: &platform_impl::EventLoopWindowTarget) -> Self { + pub(crate) fn build( + builder: OnlyCursorImageBuilder, + _: &platform_impl::EventLoopWindowTarget, + ) -> Self { Self(Arc::new(builder.0)) } } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 4c6e0404ce..c19a2df23c 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -35,7 +35,6 @@ use crate::{ }; pub(crate) use self::common::keymap::{physicalkey_to_scancode, scancode_to_physicalkey}; -pub(crate) use crate::cursor::OnlyCursorImage as PlatformCustomCursor; pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder; pub(crate) use crate::icon::RgbaIcon as PlatformIcon; pub(crate) use crate::platform_impl::Fullscreen; @@ -639,6 +638,29 @@ pub struct KeyEventExtra { pub key_without_modifiers: Key, } +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub(crate) enum PlatformCustomCursor { + #[cfg(wayland_platform)] + Wayland(wayland::CustomCursor), + #[cfg(x11_platform)] + X(x11::CustomCursor), +} +impl PlatformCustomCursor { + pub(crate) fn build( + builder: PlatformCustomCursorBuilder, + p: &EventLoopWindowTarget, + ) -> PlatformCustomCursor { + match p { + #[cfg(wayland_platform)] + EventLoopWindowTarget::Wayland(_) => { + Self::Wayland(wayland::CustomCursor::build(builder, p)) + } + #[cfg(x11_platform)] + EventLoopWindowTarget::X(p) => Self::X(x11::CustomCursor::build(builder, p)), + } + } +} + /// Hooks for X11 errors. #[cfg(x11_platform)] pub(crate) static mut XLIB_ERROR_HOOKS: Lazy>> = diff --git a/src/platform_impl/linux/wayland/mod.rs b/src/platform_impl/linux/wayland/mod.rs index 4a86aff5f3..2c0d6b5d3d 100644 --- a/src/platform_impl/linux/wayland/mod.rs +++ b/src/platform_impl/linux/wayland/mod.rs @@ -9,6 +9,7 @@ use sctk::reexports::client::globals::{BindError, GlobalError}; use sctk::reexports::client::protocol::wl_surface::WlSurface; use sctk::reexports::client::{self, ConnectError, DispatchError, Proxy}; +pub(super) use crate::cursor::OnlyCursorImage as CustomCursor; use crate::dpi::{LogicalSize, PhysicalSize}; pub use crate::platform_impl::platform::{OsError, WindowId}; pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}; diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 5097591980..cd4c6dd260 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -171,7 +171,7 @@ impl Window { match attributes.cursor { Cursor::Icon(icon) => window_state.set_cursor(icon), - Cursor::Custom(cursor) => window_state.set_custom_cursor(&cursor.inner.0), + Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor), } // Activate the window when the token is passed. @@ -514,7 +514,7 @@ impl Window { match cursor { Cursor::Icon(icon) => window_state.set_cursor(icon), - Cursor::Custom(cursor) => window_state.set_custom_cursor(&cursor.inner.0), + Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor), } } diff --git a/src/platform_impl/linux/wayland/window/state.rs b/src/platform_impl/linux/wayland/window/state.rs index b540a8d2e0..e08d2bd900 100644 --- a/src/platform_impl/linux/wayland/window/state.rs +++ b/src/platform_impl/linux/wayland/window/state.rs @@ -28,7 +28,7 @@ use sctk::shm::Shm; use sctk::subcompositor::SubcompositorState; use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur; -use crate::cursor::CursorImage; +use crate::cursor::CustomCursor as RootCustomCursor; use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size}; use crate::error::{ExternalError, NotSupportedError}; use crate::event::WindowEvent; @@ -36,7 +36,7 @@ use crate::platform_impl::wayland::event_loop::sink::EventSink; use crate::platform_impl::wayland::types::cursor::{CustomCursor, SelectedCursor}; use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager; use crate::platform_impl::wayland::{logical_to_physical_rounded, make_wid}; -use crate::platform_impl::WindowId; +use crate::platform_impl::{PlatformCustomCursor, WindowId}; use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme}; use crate::platform_impl::wayland::seat::{ @@ -726,10 +726,23 @@ impl WindowState { } /// Set the custom cursor icon. - pub(crate) fn set_custom_cursor(&mut self, cursor: &CursorImage) { + pub(crate) fn set_custom_cursor(&mut self, cursor: RootCustomCursor) { + let cursor = match cursor { + RootCustomCursor { + inner: PlatformCustomCursor::Wayland(cursor), + } => cursor.0, + #[cfg(x11_platform)] + RootCustomCursor { + inner: PlatformCustomCursor::X(_), + } => { + log::error!("passed a X11 cursor to Wayland backend"); + return; + } + }; + let cursor = { let mut pool = self.custom_cursor_pool.lock().unwrap(); - CustomCursor::new(&mut pool, cursor) + CustomCursor::new(&mut pool, &cursor) }; if self.cursor_visible { diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 81ebd0924e..f78f997d28 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -60,6 +60,7 @@ use x11rb::{ xcb_ffi::ReplyOrIdError, }; +pub(super) use self::util::CustomCursor; use self::{ dnd::{Dnd, DndState}, event_processor::EventProcessor, diff --git a/src/platform_impl/linux/x11/util/cursor.rs b/src/platform_impl/linux/x11/util/cursor.rs index 5650aadcf3..027d236420 100644 --- a/src/platform_impl/linux/x11/util/cursor.rs +++ b/src/platform_impl/linux/x11/util/cursor.rs @@ -1,9 +1,15 @@ -use std::{ffi::CString, iter, slice, sync::Arc}; +use std::{ + ffi::CString, + hash::{Hash, Hasher}, + iter, slice, + sync::Arc, +}; use x11rb::connection::Connection; -use crate::{cursor::CursorImage, window::CursorIcon}; +use crate::{platform_impl::PlatformCustomCursorBuilder, window::CursorIcon}; +use super::super::EventLoopWindowTarget; use super::*; impl XConnection { @@ -98,36 +104,55 @@ pub enum SelectedCursor { Named(CursorIcon), } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone)] pub struct CustomCursor { inner: Arc, } +impl Hash for CustomCursor { + fn hash(&self, state: &mut H) { + Arc::as_ptr(&self.inner).hash(state); + } +} + +impl PartialEq for CustomCursor { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.inner, &other.inner) + } +} + +impl Eq for CustomCursor {} + impl CustomCursor { - pub(crate) unsafe fn new(xconn: &Arc, image: &CursorImage) -> Self { + pub(crate) fn build( + builder: PlatformCustomCursorBuilder, + p: &EventLoopWindowTarget, + ) -> CustomCursor { unsafe { - let ximage = - (xconn.xcursor.XcursorImageCreate)(image.width as i32, image.height as i32); + let ximage = (p.xconn.xcursor.XcursorImageCreate)( + builder.0.width as i32, + builder.0.height as i32, + ); if ximage.is_null() { panic!("failed to allocate cursor image"); } - (*ximage).xhot = image.hotspot_x as u32; - (*ximage).yhot = image.hotspot_y as u32; + (*ximage).xhot = builder.0.hotspot_x as u32; + (*ximage).yhot = builder.0.hotspot_y as u32; (*ximage).delay = 0; - let dst = slice::from_raw_parts_mut((*ximage).pixels, image.rgba.len() / 4); - for (dst, chunk) in dst.iter_mut().zip(image.rgba.chunks_exact(4)) { + let dst = slice::from_raw_parts_mut((*ximage).pixels, builder.0.rgba.len() / 4); + for (dst, chunk) in dst.iter_mut().zip(builder.0.rgba.chunks_exact(4)) { *dst = (chunk[0] as u32) << 16 | (chunk[1] as u32) << 8 | (chunk[2] as u32) | (chunk[3] as u32) << 24; } - let cursor = (xconn.xcursor.XcursorImageLoadCursor)(xconn.display, ximage); - (xconn.xcursor.XcursorImageDestroy)(ximage); + let cursor = (p.xconn.xcursor.XcursorImageLoadCursor)(p.xconn.display, ximage); + (p.xconn.xcursor.XcursorImageDestroy)(ximage); Self { inner: Arc::new(CustomCursorInner { - xconn: xconn.clone(), + xconn: p.xconn.clone(), cursor, }), } @@ -149,14 +174,6 @@ impl Drop for CustomCursorInner { } } -impl PartialEq for CustomCursorInner { - fn eq(&self, other: &Self) -> bool { - self.cursor == other.cursor - } -} - -impl Eq for CustomCursorInner {} - impl Default for SelectedCursor { fn default() -> Self { SelectedCursor::Named(Default::default()) diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 4829b001ba..a2c7137dd3 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -21,7 +21,7 @@ use x11rb::{ }; use crate::{ - cursor::Cursor, + cursor::{Cursor, CustomCursor as RootCustomCursor}, dpi::{PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, event::{Event, InnerSizeWriter, WindowEvent}, @@ -32,8 +32,9 @@ use crate::{ atoms::*, xinput_fp1616_to_float, MonitorHandle as X11MonitorHandle, WakeSender, X11Error, }, - Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon, - PlatformSpecificWindowBuilderAttributes, VideoModeHandle as PlatformVideoModeHandle, + Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformCustomCursor, + PlatformIcon, PlatformSpecificWindowBuilderAttributes, + VideoModeHandle as PlatformVideoModeHandle, }, window::{ CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, WindowAttributes, @@ -43,7 +44,7 @@ use crate::{ use super::{ ffi, - util::{self, CustomCursor, SelectedCursor}, + util::{self, SelectedCursor}, CookieResultExt, EventLoopWindowTarget, ImeRequest, ImeSender, VoidCookie, WindowId, XConnection, }; @@ -1552,16 +1553,20 @@ impl UnownedWindow { self.xconn.set_cursor_icon(self.xwindow, Some(icon)); } } - Cursor::Custom(cursor) => { - let new_cursor = unsafe { CustomCursor::new(&self.xconn, &cursor.inner.0) }; - + Cursor::Custom(RootCustomCursor { + inner: PlatformCustomCursor::X(cursor), + }) => { #[allow(clippy::mutex_atomic)] if *self.cursor_visible.lock().unwrap() { - self.xconn.set_custom_cursor(self.xwindow, &new_cursor); + self.xconn.set_custom_cursor(self.xwindow, &cursor); } - *self.selected_cursor.lock().unwrap() = SelectedCursor::Custom(new_cursor); + *self.selected_cursor.lock().unwrap() = SelectedCursor::Custom(cursor); } + #[cfg(wayland_platform)] + Cursor::Custom(RootCustomCursor { + inner: PlatformCustomCursor::Wayland(_), + }) => log::error!("passed a Wayland cursor to X11 backend"), } }