cursor: refactor CustomCursor to be dyn

cursor: refactor `CustomCursor` to be `dyn`

Same as for `MonitorHandle`, the source was changed to support
all kinds of sources.
This commit is contained in:
Kirill Chibisov 2025-03-13 17:18:37 +03:00 committed by GitHub
parent a0464ae83b
commit ae28eea406
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 329 additions and 333 deletions

View file

@ -9,7 +9,6 @@ use std::time::Duration;
pub(crate) use self::common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey};
use crate::application::ApplicationHandler;
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
#[cfg(x11_platform)]
use crate::dpi::Size;
use crate::error::{EventLoopError, NotSupportedError};
@ -120,14 +119,6 @@ macro_rules! x11_or_wayland {
};
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub(crate) enum PlatformCustomCursor {
#[cfg(wayland_platform)]
Wayland(wayland::CustomCursor),
#[cfg(x11_platform)]
X(x11::CustomCursor),
}
#[derive(Debug)]
pub enum EventLoop {
#[cfg(wayland_platform)]

View file

@ -12,9 +12,8 @@ use sctk::reexports::calloop_wayland_source::WaylandSource;
use sctk::reexports::client::{globals, Connection, QueueHandle};
use crate::application::ApplicationHandler;
use crate::cursor::OnlyCursorImage;
use crate::dpi::LogicalSize;
use crate::error::{EventLoopError, OsError, RequestError};
use crate::error::{EventLoopError, NotSupportedError, OsError, RequestError};
use crate::event::{DeviceEvent, StartCause, SurfaceSizeWriter, WindowEvent};
use crate::event_loop::{
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
@ -23,8 +22,8 @@ use crate::event_loop::{
use crate::monitor::MonitorHandle as CoreMonitorHandle;
use crate::platform::pump_events::PumpStatus;
use crate::platform_impl::platform::min_timeout;
use crate::platform_impl::PlatformCustomCursor;
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme};
use crate::platform_impl::wayland::types::cursor::WaylandCustomCursor;
use crate::window::{CustomCursor as CoreCustomCursor, CustomCursorSource, Theme};
mod proxy;
pub mod sink;
@ -604,10 +603,15 @@ impl RootActiveEventLoop for ActiveEventLoop {
fn create_custom_cursor(
&self,
cursor: CustomCursorSource,
) -> Result<RootCustomCursor, RequestError> {
Ok(RootCustomCursor {
inner: PlatformCustomCursor::Wayland(OnlyCursorImage(Arc::from(cursor.inner.0))),
})
) -> Result<CoreCustomCursor, RequestError> {
let cursor_image = match cursor {
CustomCursorSource::Image(cursor_image) => cursor_image,
CustomCursorSource::Animation { .. } | CustomCursorSource::Url { .. } => {
return Err(NotSupportedError::new("unsupported cursor kind").into())
},
};
Ok(CoreCustomCursor(Arc::new(WaylandCustomCursor(cursor_image))))
}
#[inline]

View file

@ -3,7 +3,6 @@
use sctk::reexports::client::protocol::wl_surface::WlSurface;
use sctk::reexports::client::Proxy;
pub(super) use crate::cursor::OnlyCursorImage as CustomCursor;
use crate::dpi::{LogicalSize, PhysicalSize};
use crate::window::WindowId;

View file

@ -2,7 +2,16 @@ use cursor_icon::CursorIcon;
use sctk::reexports::client::protocol::wl_shm::Format;
use sctk::shm::slot::{Buffer, SlotPool};
use crate::cursor::CursorImage;
use crate::cursor::{CursorImage, CustomCursorProvider};
// Wrap in our own type to not impl trait on global type.
#[derive(Debug)]
pub struct WaylandCustomCursor(pub(crate) CursorImage);
impl CustomCursorProvider for WaylandCustomCursor {
fn is_animated(&self) -> bool {
false
}
}
#[derive(Debug)]
pub enum SelectedCursor {
@ -26,7 +35,8 @@ pub struct CustomCursor {
}
impl CustomCursor {
pub(crate) fn new(pool: &mut SlotPool, image: &CursorImage) -> Self {
pub(crate) fn new(pool: &mut SlotPool, image: &WaylandCustomCursor) -> Self {
let image = &image.0;
let (buffer, canvas) = pool
.create_buffer(
image.width as i32,

View file

@ -28,7 +28,7 @@ use sctk::subcompositor::SubcompositorState;
use tracing::{info, warn};
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
use crate::cursor::CustomCursor as RootCustomCursor;
use crate::cursor::CustomCursor as CoreCustomCursor;
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::platform_impl::wayland::event_loop::OwnedDisplayHandle;
@ -37,9 +37,10 @@ use crate::platform_impl::wayland::seat::{
PointerConstraintsState, WinitPointerData, WinitPointerDataExt, ZwpTextInputV3Ext,
};
use crate::platform_impl::wayland::state::{WindowCompositorUpdate, WinitState};
use crate::platform_impl::wayland::types::cursor::{CustomCursor, SelectedCursor};
use crate::platform_impl::wayland::types::cursor::{
CustomCursor, SelectedCursor, WaylandCustomCursor,
};
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
use crate::platform_impl::PlatformCustomCursor;
use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, WindowId};
#[cfg(feature = "sctk-adwaita")]
@ -702,19 +703,18 @@ impl WindowState {
}
/// Set the custom cursor icon.
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(_) } => {
tracing::error!("passed a X11 cursor to Wayland backend");
pub(crate) fn set_custom_cursor(&mut self, cursor: CoreCustomCursor) {
let cursor = match cursor.cast_ref::<WaylandCustomCursor>() {
Some(cursor) => cursor,
None => {
tracing::error!("unrecognized cursor passed 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 {

View file

@ -36,10 +36,9 @@ use crate::platform::x11::XlibErrorHook;
use crate::platform_impl::common::xkb::Context;
use crate::platform_impl::platform::min_timeout;
use crate::platform_impl::x11::window::Window;
use crate::platform_impl::PlatformCustomCursor;
use crate::utils::Lazy;
use crate::window::{
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as CoreWindow,
CustomCursor as CoreCustomCursor, CustomCursorSource, Theme, Window as CoreWindow,
WindowAttributes, WindowId,
};
@ -731,10 +730,8 @@ impl RootActiveEventLoop for ActiveEventLoop {
fn create_custom_cursor(
&self,
custom_cursor: CustomCursorSource,
) -> Result<RootCustomCursor, RequestError> {
Ok(RootCustomCursor {
inner: PlatformCustomCursor::X(CustomCursor::new(self, custom_cursor.inner)?),
})
) -> Result<CoreCustomCursor, RequestError> {
Ok(CoreCustomCursor(Arc::new(CustomCursor::new(self, custom_cursor)?)))
}
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {

View file

@ -9,8 +9,8 @@ use x11rb::protocol::xproto;
use super::super::ActiveEventLoop;
use super::*;
use crate::error::RequestError;
use crate::platform_impl::PlatformCustomCursorSource;
use crate::cursor::{CustomCursorProvider, CustomCursorSource};
use crate::error::{NotSupportedError, RequestError};
use crate::window::CursorIcon;
impl XConnection {
@ -36,7 +36,7 @@ impl XConnection {
window: xproto::Window,
cursor: &CustomCursor,
) -> Result<(), X11Error> {
self.update_cursor(window, cursor.inner.cursor)
self.update_cursor(window, cursor.cursor)
}
/// Create a cursor from an image.
@ -170,32 +170,45 @@ pub enum SelectedCursor {
Named(CursorIcon),
}
impl Default for SelectedCursor {
fn default() -> Self {
SelectedCursor::Named(Default::default())
}
}
#[derive(Debug, Clone)]
pub struct CustomCursor {
inner: Arc<CustomCursorInner>,
xconn: Arc<XConnection>,
cursor: xproto::Cursor,
}
impl Hash for CustomCursor {
fn hash<H: Hasher>(&self, state: &mut H) {
Arc::as_ptr(&self.inner).hash(state);
self.cursor.hash(state);
}
}
impl PartialEq for CustomCursor {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.inner, &other.inner)
self.cursor == other.cursor
}
}
impl Eq for CustomCursor {}
impl CustomCursor {
pub(crate) fn new(
event_loop: &ActiveEventLoop,
mut cursor: PlatformCustomCursorSource,
cursor: CustomCursorSource,
) -> Result<CustomCursor, RequestError> {
let mut cursor = match cursor {
CustomCursorSource::Image(cursor_image) => cursor_image,
CustomCursorSource::Animation { .. } | CustomCursorSource::Url { .. } => {
return Err(NotSupportedError::new("unsupported cursor kind").into())
},
};
// Reverse RGBA order to BGRA.
cursor.0.rgba.chunks_mut(4).for_each(|chunk| {
cursor.rgba.chunks_mut(4).for_each(|chunk| {
let chunk: &mut [u8; 4] = chunk.try_into().unwrap();
chunk[0..3].reverse();
@ -209,32 +222,26 @@ impl CustomCursor {
let cursor = event_loop
.xconn
.create_cursor_from_image(
cursor.0.width,
cursor.0.height,
cursor.0.hotspot_x,
cursor.0.hotspot_y,
&cursor.0.rgba,
cursor.width,
cursor.height,
cursor.hotspot_x,
cursor.hotspot_y,
&cursor.rgba,
)
.map_err(|err| os_error!(err))?;
Ok(Self { inner: Arc::new(CustomCursorInner { xconn: event_loop.xconn.clone(), cursor }) })
Ok(Self { xconn: event_loop.xconn.clone(), cursor })
}
}
#[derive(Debug)]
struct CustomCursorInner {
xconn: Arc<XConnection>,
cursor: xproto::Cursor,
}
impl Drop for CustomCursorInner {
impl Drop for CustomCursor {
fn drop(&mut self) {
self.xconn.xcb_connection().free_cursor(self.cursor).map(|r| r.ignore_error()).ok();
}
}
impl Default for SelectedCursor {
fn default() -> Self {
SelectedCursor::Named(Default::default())
impl CustomCursorProvider for CustomCursor {
fn is_animated(&self) -> bool {
false
}
}

View file

@ -19,10 +19,11 @@ use x11rb::protocol::{randr, xinput};
use super::util::{self, SelectedCursor};
use super::{
ffi, ActiveEventLoop, CookieResultExt, ImeRequest, ImeSender, VoidCookie, XConnection,
ffi, ActiveEventLoop, CookieResultExt, CustomCursor, ImeRequest, ImeSender, VoidCookie,
XConnection,
};
use crate::application::ApplicationHandler;
use crate::cursor::{Cursor, CustomCursor as RootCustomCursor};
use crate::cursor::Cursor;
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{NotSupportedError, RequestError};
use crate::event::{SurfaceSizeWriter, WindowEvent};
@ -35,7 +36,7 @@ use crate::platform_impl::x11::atoms::*;
use crate::platform_impl::x11::{
xinput_fp1616_to_float, MonitorHandle as X11MonitorHandle, WakeSender, X11Error,
};
use crate::platform_impl::{common, PlatformCustomCursor, PlatformIcon};
use crate::platform_impl::{common, PlatformIcon};
use crate::window::{
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow,
WindowAttributes, WindowButtons, WindowId, WindowLevel,
@ -1808,19 +1809,23 @@ impl UnownedWindow {
}
}
},
Cursor::Custom(RootCustomCursor { inner: PlatformCustomCursor::X(cursor) }) => {
Cursor::Custom(cursor) => {
let cursor = match cursor.cast_ref::<CustomCursor>() {
Some(cursor) => cursor,
None => {
tracing::error!("unrecognized cursor passed to X11 backend");
return;
},
};
#[allow(clippy::mutex_atomic)]
if *self.cursor_visible.lock().unwrap() {
if let Err(err) = self.xconn.set_custom_cursor(self.xwindow, &cursor) {
if let Err(err) = self.xconn.set_custom_cursor(self.xwindow, cursor) {
tracing::error!("failed to set window icon: {err}");
}
}
*self.selected_cursor.lock().unwrap() = SelectedCursor::Custom(cursor);
},
#[cfg(wayland_platform)]
Cursor::Custom(RootCustomCursor { inner: PlatformCustomCursor::Wayland(_) }) => {
tracing::error!("passed a Wayland cursor to X11 backend")
*self.selected_cursor.lock().unwrap() = SelectedCursor::Custom(cursor.clone());
},
}
}