From ae28eea406d16cb991f95eeff2df4278c250f4fe Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Thu, 13 Mar 2025 17:18:37 +0300 Subject: [PATCH] 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. --- examples/application.rs | 19 +- src/changelog/unreleased.md | 6 +- src/cursor.rs | 212 ++++++++++-------- src/platform/web.rs | 73 +----- src/platform_impl/android/mod.rs | 6 +- src/platform_impl/apple/appkit/cursor.rs | 21 +- src/platform_impl/apple/appkit/event_loop.rs | 6 +- src/platform_impl/apple/appkit/mod.rs | 2 - .../apple/appkit/window_delegate.rs | 10 +- src/platform_impl/apple/uikit/mod.rs | 3 - src/platform_impl/linux/mod.rs | 9 - .../linux/wayland/event_loop/mod.rs | 20 +- src/platform_impl/linux/wayland/mod.rs | 1 - .../linux/wayland/types/cursor.rs | 14 +- .../linux/wayland/window/state.rs | 20 +- src/platform_impl/linux/x11/mod.rs | 9 +- src/platform_impl/linux/x11/util/cursor.rs | 57 ++--- src/platform_impl/linux/x11/window.rs | 25 ++- src/platform_impl/orbital/mod.rs | 6 +- src/platform_impl/web/cursor.rs | 93 ++++---- .../web/event_loop/window_target.rs | 8 +- src/platform_impl/web/mod.rs | 5 +- src/platform_impl/windows/event_loop.rs | 15 +- src/platform_impl/windows/icon.rs | 8 +- src/platform_impl/windows/mod.rs | 4 +- src/platform_impl/windows/window.rs | 10 +- 26 files changed, 329 insertions(+), 333 deletions(-) diff --git a/examples/application.rs b/examples/application.rs index 9addc5b9..4c58d4c1 100644 --- a/examples/application.rs +++ b/examples/application.rs @@ -31,7 +31,7 @@ use winit::platform::startup_notify::{ self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify, }; #[cfg(web_platform)] -use winit::platform::web::{ActiveEventLoopExtWeb, CustomCursorExtWeb, WindowAttributesExtWeb}; +use winit::platform::web::{ActiveEventLoopExtWeb, WindowAttributesExtWeb}; #[cfg(x11_platform)] use winit::platform::x11::WindowAttributesExtX11; use winit::window::{ @@ -836,7 +836,7 @@ impl WindowState { custom_cursors[1].clone(), event_loop.create_custom_cursor(url_custom_cursor())?, ]; - let cursor = CustomCursor::from_animation(Duration::from_secs(3), cursors).unwrap(); + let cursor = CustomCursorSource::from_animation(Duration::from_secs(3), cursors).unwrap(); let cursor = event_loop.create_custom_cursor(cursor)?; self.window.set_cursor(cursor.into()); @@ -1097,7 +1097,7 @@ fn decode_cursor(bytes: &[u8]) -> CustomCursorSource { let samples = img.into_flat_samples(); let (_, w, h) = samples.extents(); let (w, h) = (w as u16, h as u16); - CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap() + CustomCursorSource::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap() } #[cfg(web_platform)] @@ -1106,11 +1106,14 @@ fn url_custom_cursor() -> CustomCursorSource { static URL_COUNTER: AtomicU64 = AtomicU64::new(0); - CustomCursor::from_url( - format!("https://picsum.photos/128?random={}", URL_COUNTER.fetch_add(1, Ordering::Relaxed)), - 64, - 64, - ) + CustomCursorSource::Url { + hotspot_x: 64, + hotspot_y: 64, + url: format!( + "https://picsum.photos/128?random={}", + URL_COUNTER.fetch_add(1, Ordering::Relaxed) + ), + } } fn load_icon(bytes: &[u8]) -> Icon { diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 7c30116c..d1ce8b80 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -74,6 +74,8 @@ changelog entry. - On Windows, add `IconExtWindows::from_resource_name`. - Implement `MonitorHandleProvider` for `MonitorHandle` to access common monitor API. - On X11, set an "area" attribute on XIM input connection to convey the cursor area. +- Implement `CustomCursorProvider` for `CustomCursor` to access cursor API. +- Add `CustomCursorSource::Url`, `CustomCursorSource::from_animation`. ### Changed @@ -223,7 +225,9 @@ changelog entry. - Remove `WindowEvent::Touch` and `Touch` in favor of the new `PointerKind`, `PointerSource` and `ButtonSource` as part of the new pointer event overhaul. - Remove `Force::altitude_angle`. -- Removed `Window::inner_position`, use the new `Window::surface_position` instead. +- Remove `Window::inner_position`, use the new `Window::surface_position` instead. +- Remove `CustomCursorExtWeb`, use the `CustomCursorSource`. +- Remove `CustomCursor::from_rgba`, use `CustomCursorSource` instead. ### Fixed diff --git a/src/cursor.rs b/src/cursor.rs index d9130b82..69dba15f 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -1,13 +1,15 @@ use core::fmt; use std::error::Error; -use std::hash::{Hash, Hasher}; +use std::hash::Hash; +use std::ops::Deref; use std::sync::Arc; +use std::time::Duration; use cursor_icon::CursorIcon; -use crate::platform_impl::{PlatformCustomCursor, PlatformCustomCursorSource}; +use crate::utils::{impl_dyn_casting, AsAny}; -/// The maximum width and height for a cursor when using [`CustomCursor::from_rgba`]. +/// The maximum width and height for a cursor when using [`CustomCursorSource::from_rgba`]. pub const MAX_CURSOR_SIZE: u16 = 2048; const PIXEL_SIZE: usize = 4; @@ -51,19 +53,20 @@ impl From for Cursor { /// # use winit::event_loop::ActiveEventLoop; /// # use winit::window::Window; /// # fn scope(event_loop: &dyn ActiveEventLoop, window: &dyn Window) { -/// use winit::window::CustomCursor; +/// use winit::window::CustomCursorSource; /// /// let w = 10; /// let h = 10; /// let rgba = vec![255; (w * h * 4) as usize]; /// /// #[cfg(not(target_family = "wasm"))] -/// let source = CustomCursor::from_rgba(rgba, w, h, w / 2, h / 2).unwrap(); +/// let source = CustomCursorSource::from_rgba(rgba, w, h, w / 2, h / 2).unwrap(); /// /// #[cfg(target_family = "wasm")] -/// let source = { -/// use winit::platform::web::CustomCursorExtWeb; -/// CustomCursor::from_url(String::from("http://localhost:3000/cursor.png"), 0, 0) +/// let source = CustomCursorSource::Url { +/// url: String::from("http://localhost:3000/cursor.png"), +/// hotspot_x: 0, +/// hotspot_y: 0, /// }; /// /// if let Ok(custom_cursor) = event_loop.create_custom_cursor(source) { @@ -71,50 +74,96 @@ impl From for Cursor { /// } /// # } /// ``` -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct CustomCursor { - /// Platforms should make sure this is cheap to clone. - pub(crate) inner: PlatformCustomCursor, +#[derive(Clone, Debug)] +pub struct CustomCursor(pub(crate) Arc); + +pub trait CustomCursorProvider: AsAny + fmt::Debug + Send + Sync { + /// Whether a cursor was backed by animation. + fn is_animated(&self) -> bool; } -impl CustomCursor { - /// Creates a new cursor from an rgba buffer. - /// - /// The alpha channel is assumed to be **not** premultiplied. - pub fn from_rgba( - rgba: impl Into>, - width: u16, - height: u16, - hotspot_x: u16, - hotspot_y: u16, - ) -> Result { - let _span = - tracing::debug_span!("winit::Cursor::from_rgba", width, height, hotspot_x, hotspot_y) - .entered(); - - Ok(CustomCursorSource { - inner: PlatformCustomCursorSource::from_rgba( - rgba.into(), - width, - height, - hotspot_x, - hotspot_y, - )?, - }) +impl PartialEq for CustomCursor { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.0, &other.0) } } +impl Eq for CustomCursor {} + +impl Hash for CustomCursor { + fn hash(&self, state: &mut H) { + Arc::as_ptr(&self.0).hash(state); + } +} + +impl Deref for CustomCursor { + type Target = dyn CustomCursorProvider; + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +impl_dyn_casting!(CustomCursorProvider); + /// Source for [`CustomCursor`]. /// /// See [`CustomCursor`] for more details. #[derive(Debug, Clone, Eq, Hash, PartialEq)] -pub struct CustomCursorSource { - // Some platforms don't support custom cursors. - #[allow(dead_code)] - pub(crate) inner: PlatformCustomCursorSource, +pub enum CustomCursorSource { + /// Cursor that is backed by RGBA image. + /// + /// See [CustomCursorSource::from_rgba] for more. + /// + /// ## Platform-specific + /// + /// - **iOS / Android / Orbital:** Unsupported + Image(CursorImage), + /// Animated cursor. + /// + /// See [CustomCursorSource::from_animation] for more. + /// + /// ## Platform-specific + /// + /// - **iOS / Android / Wayland / Windows / X11 / macOS / Orbital:** Unsupported + Animation(CursorAnimation), + /// Creates a new cursor from a URL pointing to an image. + /// It uses the [url css function](https://developer.mozilla.org/en-US/docs/Web/CSS/url), + /// but browser support for image formats is inconsistent. Using [PNG] is recommended. + /// + /// [PNG]: https://en.wikipedia.org/wiki/PNG + /// + /// ## Platform-specific + /// + /// - **iOS / Android / Wayland / Windows / X11 / macOS / Orbital:** Unsupported + Url { hotspot_x: u16, hotspot_y: u16, url: String }, } -/// An error produced when using [`CustomCursor::from_rgba`] with invalid arguments. +impl CustomCursorSource { + /// Creates a new cursor from an rgba buffer. + /// + /// The alpha channel is assumed to be **not** premultiplied. + pub fn from_rgba( + rgba: Vec, + width: u16, + height: u16, + hotspot_x: u16, + hotspot_y: u16, + ) -> Result { + CursorImage::from_rgba(rgba, width, height, hotspot_x, hotspot_y).map(Self::Image) + } + + /// Crates a new animated cursor from multiple [`CustomCursor`]s + /// Supplied `cursors` can't be empty or other animations. + pub fn from_animation( + duration: Duration, + cursors: Vec, + ) -> Result { + CursorAnimation::new(duration, cursors).map(Self::Animation) + } +} + +/// An error produced when using [`CustomCursorSource::from_rgba`] with invalid arguments. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum BadImage { @@ -164,47 +213,30 @@ impl fmt::Display for BadImage { impl Error for BadImage {} -/// Platforms export this directly as `PlatformCustomCursorSource` if they need to only work with -/// images. -#[allow(dead_code)] -#[derive(Debug, Clone, Eq, Hash, PartialEq)] -pub(crate) struct OnlyCursorImageSource(pub(crate) CursorImage); +/// An error produced when using [`CustomCursorSource::from_animation`] with invalid arguments. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum BadAnimation { + /// Produced when no cursors were supplied. + Empty, + /// Produced when a supplied cursor is an animation. + Animation, +} -#[allow(dead_code)] -impl OnlyCursorImageSource { - pub(crate) fn from_rgba( - rgba: Vec, - width: u16, - height: u16, - hotspot_x: u16, - hotspot_y: u16, - ) -> Result { - CursorImage::from_rgba(rgba, width, height, hotspot_x, hotspot_y).map(Self) +impl fmt::Display for BadAnimation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Empty => write!(f, "No cursors supplied"), + Self::Animation => write!(f, "A supplied cursor is an animation"), + } } } -/// Platforms export this directly as `PlatformCustomCursor` if they don't implement caching. -#[allow(dead_code)] -#[derive(Debug, Clone)] -pub(crate) struct OnlyCursorImage(pub(crate) Arc); - -impl Hash for OnlyCursorImage { - fn hash(&self, state: &mut H) { - Arc::as_ptr(&self.0).hash(state); - } -} - -impl PartialEq for OnlyCursorImage { - fn eq(&self, other: &Self) -> bool { - Arc::ptr_eq(&self.0, &other.0) - } -} - -impl Eq for OnlyCursorImage {} +impl Error for BadAnimation {} #[derive(Debug, Clone, Eq, Hash, PartialEq)] #[allow(dead_code)] -pub(crate) struct CursorImage { +pub struct CursorImage { pub(crate) rgba: Vec, pub(crate) width: u16, pub(crate) height: u16, @@ -247,20 +279,22 @@ impl CursorImage { } } -// Platforms that don't support cursors will export this as `PlatformCustomCursor`. -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub(crate) struct NoCustomCursor; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CursorAnimation { + pub(crate) duration: Duration, + pub(crate) cursors: Vec, +} -#[allow(dead_code)] -impl NoCustomCursor { - pub(crate) fn from_rgba( - rgba: Vec, - width: u16, - height: u16, - hotspot_x: u16, - hotspot_y: u16, - ) -> Result { - CursorImage::from_rgba(rgba, width, height, hotspot_x, hotspot_y)?; - Ok(Self) +impl CursorAnimation { + pub fn new(duration: Duration, cursors: Vec) -> Result { + if cursors.is_empty() { + return Err(BadAnimation::Empty); + } + + if cursors.iter().any(|cursor| cursor.is_animated()) { + return Err(BadAnimation::Animation); + } + + Ok(Self { duration, cursors }) } } diff --git a/src/platform/web.rs b/src/platform/web.rs index 4f23a47f..7fc181f1 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -46,8 +46,8 @@ use std::error::Error; use std::fmt::{self, Display, Formatter}; use std::future::Future; use std::pin::Pin; +use std::sync::Arc; use std::task::{Context, Poll}; -use std::time::Duration; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -59,6 +59,7 @@ use crate::cursor::CustomCursorSource; use crate::error::NotSupportedError; use crate::event_loop::{ActiveEventLoop, EventLoop}; use crate::monitor::MonitorHandleProvider; +use crate::platform_impl::MonitorHandle as WebMonitorHandle; #[cfg(web_platform)] use crate::platform_impl::{ CustomCursorFuture as PlatformCustomCursorFuture, @@ -66,7 +67,6 @@ use crate::platform_impl::{ MonitorPermissionFuture as PlatformMonitorPermissionFuture, OrientationLockFuture as PlatformOrientationLockFuture, }; -use crate::platform_impl::{MonitorHandle as WebMonitorHandle, PlatformCustomCursorSource}; use crate::window::{CustomCursor, Window, WindowAttributes}; #[cfg(not(web_platform))] @@ -486,73 +486,6 @@ pub enum WaitUntilStrategy { Worker, } -pub trait CustomCursorExtWeb { - /// Returns if this cursor is an animation. - fn is_animation(&self) -> bool; - - /// Creates a new cursor from a URL pointing to an image. - /// It uses the [url css function](https://developer.mozilla.org/en-US/docs/Web/CSS/url), - /// but browser support for image formats is inconsistent. Using [PNG] is recommended. - /// - /// [PNG]: https://en.wikipedia.org/wiki/PNG - fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorSource; - - /// Crates a new animated cursor from multiple [`CustomCursor`]s. - /// Supplied `cursors` can't be empty or other animations. - fn from_animation( - duration: Duration, - cursors: Vec, - ) -> Result; -} - -impl CustomCursorExtWeb for CustomCursor { - fn is_animation(&self) -> bool { - self.inner.animation - } - - fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorSource { - CustomCursorSource { inner: PlatformCustomCursorSource::Url { url, hotspot_x, hotspot_y } } - } - - fn from_animation( - duration: Duration, - cursors: Vec, - ) -> Result { - if cursors.is_empty() { - return Err(BadAnimation::Empty); - } - - if cursors.iter().any(CustomCursor::is_animation) { - return Err(BadAnimation::Animation); - } - - Ok(CustomCursorSource { - inner: PlatformCustomCursorSource::Animation { duration, cursors }, - }) - } -} - -/// An error produced when using [`CustomCursor::from_animation`] with invalid arguments. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum BadAnimation { - /// Produced when no cursors were supplied. - Empty, - /// Produced when a supplied cursor is an animation. - Animation, -} - -impl fmt::Display for BadAnimation { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Empty => write!(f, "No cursors supplied"), - Self::Animation => write!(f, "A supplied cursor is an animation"), - } - } -} - -impl Error for BadAnimation {} - #[cfg(not(web_platform))] struct PlatformCustomCursorFuture; @@ -563,7 +496,7 @@ impl Future for CustomCursorFuture { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - Pin::new(&mut self.0).poll(cx).map_ok(|cursor| CustomCursor { inner: cursor }) + Pin::new(&mut self.0).poll(cx).map_ok(|cursor| CustomCursor(Arc::new(cursor))) } } diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index fe43dc5c..49b99e67 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -20,6 +20,7 @@ use crate::event_loop::{ EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider, OwnedDisplayHandle as CoreOwnedDisplayHandle, }; +pub(crate) use crate::icon::NoIcon as PlatformIcon; use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; use crate::platform::pump_events::PumpStatus; use crate::window::{ @@ -29,11 +30,6 @@ use crate::window::{ mod keycodes; -pub(crate) use crate::cursor::{ - NoCustomCursor as PlatformCustomCursor, NoCustomCursor as PlatformCustomCursorSource, -}; -pub(crate) use crate::icon::NoIcon as PlatformIcon; - static HAS_FOCUS: AtomicBool = AtomicBool::new(true); /// Returns the minimum `Option`, taking into account that `None` diff --git a/src/platform_impl/apple/appkit/cursor.rs b/src/platform_impl/apple/appkit/cursor.rs index 807f6b31..0a965a41 100644 --- a/src/platform_impl/apple/appkit/cursor.rs +++ b/src/platform_impl/apple/appkit/cursor.rs @@ -10,21 +10,34 @@ use objc2_foundation::{ ns_string, NSData, NSDictionary, NSNumber, NSObject, NSPoint, NSSize, NSString, }; -use crate::cursor::{CursorImage, OnlyCursorImageSource}; -use crate::error::RequestError; +use crate::cursor::{CursorImage, CustomCursorProvider, CustomCursorSource}; +use crate::error::{NotSupportedError, RequestError}; use crate::window::CursorIcon; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct CustomCursor(pub(crate) Retained); +impl CustomCursorProvider for CustomCursor { + fn is_animated(&self) -> bool { + false + } +} + // SAFETY: NSCursor is immutable and thread-safe // TODO(madsmtm): Put this logic in objc2-app-kit itself unsafe impl Send for CustomCursor {} unsafe impl Sync for CustomCursor {} impl CustomCursor { - pub(crate) fn new(cursor: OnlyCursorImageSource) -> Result { - cursor_from_image(&cursor.0).map(Self) + pub(crate) fn new(cursor: CustomCursorSource) -> Result { + let cursor = match cursor { + CustomCursorSource::Image(cursor_image) => cursor_image, + CustomCursorSource::Animation { .. } | CustomCursorSource::Url { .. } => { + return Err(NotSupportedError::new("unsupported cursor kind").into()) + }, + }; + + cursor_from_image(&cursor).map(Self) } } diff --git a/src/platform_impl/apple/appkit/event_loop.rs b/src/platform_impl/apple/appkit/event_loop.rs index 0ddf61b2..3b0a03ea 100644 --- a/src/platform_impl/apple/appkit/event_loop.rs +++ b/src/platform_impl/apple/appkit/event_loop.rs @@ -33,7 +33,7 @@ use crate::monitor::MonitorHandle as CoreMonitorHandle; use crate::platform::macos::ActivationPolicy; use crate::platform::pump_events::PumpStatus; use crate::platform_impl::Window; -use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme}; +use crate::window::{CustomCursor as CoreCustomCursor, CustomCursorSource, Theme}; #[derive(Default)] pub struct PanicInfo { @@ -110,8 +110,8 @@ impl RootActiveEventLoop for ActiveEventLoop { fn create_custom_cursor( &self, source: CustomCursorSource, - ) -> Result { - Ok(RootCustomCursor { inner: CustomCursor::new(source.inner)? }) + ) -> Result { + Ok(CoreCustomCursor(Arc::new(CustomCursor::new(source)?))) } fn available_monitors(&self) -> Box> { diff --git a/src/platform_impl/apple/appkit/mod.rs b/src/platform_impl/apple/appkit/mod.rs index 19cb782c..03f3c18b 100644 --- a/src/platform_impl/apple/appkit/mod.rs +++ b/src/platform_impl/apple/appkit/mod.rs @@ -14,7 +14,6 @@ mod view; mod window; mod window_delegate; -pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor; pub(crate) use self::event::{physicalkey_to_scancode, scancode_to_physicalkey}; pub(crate) use self::event_loop::{ ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes, @@ -22,5 +21,4 @@ pub(crate) use self::event_loop::{ pub(crate) use self::monitor::MonitorHandle; pub(crate) use self::window::Window; pub(crate) use self::window_delegate::PlatformSpecificWindowAttributes; -pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource; pub(crate) use crate::icon::NoIcon as PlatformIcon; diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index 1bd1ac5f..86011d08 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -36,7 +36,7 @@ use objc2_foundation::{ use tracing::{trace, warn}; use super::app_state::AppState; -use super::cursor::cursor_from_icon; +use super::cursor::{cursor_from_icon, CustomCursor}; use super::monitor::{self, flip_window_screen_coordinates, get_display_id}; use super::observer::RunLoop; use super::util::cgerr; @@ -1249,7 +1249,13 @@ impl WindowDelegate { let cursor = match cursor { Cursor::Icon(icon) => cursor_from_icon(icon), - Cursor::Custom(cursor) => cursor.inner.0, + Cursor::Custom(cursor) => match cursor.cast_ref::() { + Some(cursor) => cursor.0.clone(), + None => { + tracing::error!("unrecognized cursor passed to macOS backend"); + return; + }, + }, }; if view.cursor_icon() == cursor { diff --git a/src/platform_impl/apple/uikit/mod.rs b/src/platform_impl/apple/uikit/mod.rs index 392507cb..8a1bb167 100644 --- a/src/platform_impl/apple/uikit/mod.rs +++ b/src/platform_impl/apple/uikit/mod.rs @@ -14,9 +14,6 @@ pub(crate) use self::event_loop::{ }; pub(crate) use self::monitor::MonitorHandle; pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window}; -pub(crate) use crate::cursor::{ - NoCustomCursor as PlatformCustomCursor, NoCustomCursor as PlatformCustomCursorSource, -}; pub(crate) use crate::icon::NoIcon as PlatformIcon; #[derive(Debug)] diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index de58deac..9749e9b2 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -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)] diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index 4c6e1d6a..061ff3c3 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -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 { - Ok(RootCustomCursor { - inner: PlatformCustomCursor::Wayland(OnlyCursorImage(Arc::from(cursor.inner.0))), - }) + ) -> Result { + 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] diff --git a/src/platform_impl/linux/wayland/mod.rs b/src/platform_impl/linux/wayland/mod.rs index 6276a870..bb96844e 100644 --- a/src/platform_impl/linux/wayland/mod.rs +++ b/src/platform_impl/linux/wayland/mod.rs @@ -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; diff --git a/src/platform_impl/linux/wayland/types/cursor.rs b/src/platform_impl/linux/wayland/types/cursor.rs index 83eeb47c..682d959a 100644 --- a/src/platform_impl/linux/wayland/types/cursor.rs +++ b/src/platform_impl/linux/wayland/types/cursor.rs @@ -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, diff --git a/src/platform_impl/linux/wayland/window/state.rs b/src/platform_impl/linux/wayland/window/state.rs index fdbeb04c..5e74f7c8 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::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::() { + 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 { diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index ab86c304..33b5060b 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -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 { - Ok(RootCustomCursor { - inner: PlatformCustomCursor::X(CustomCursor::new(self, custom_cursor.inner)?), - }) + ) -> Result { + Ok(CoreCustomCursor(Arc::new(CustomCursor::new(self, custom_cursor)?))) } fn available_monitors(&self) -> Box> { diff --git a/src/platform_impl/linux/x11/util/cursor.rs b/src/platform_impl/linux/x11/util/cursor.rs index bd4adace..ecef41f7 100644 --- a/src/platform_impl/linux/x11/util/cursor.rs +++ b/src/platform_impl/linux/x11/util/cursor.rs @@ -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, + xconn: Arc, + cursor: xproto::Cursor, } impl Hash for CustomCursor { fn hash(&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 { + 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, - 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 } } diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index f9a39c7c..97dcb207 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -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::() { + 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()); }, } } diff --git a/src/platform_impl/orbital/mod.rs b/src/platform_impl/orbital/mod.rs index 89b63988..3b7f62df 100644 --- a/src/platform_impl/orbital/mod.rs +++ b/src/platform_impl/orbital/mod.rs @@ -4,15 +4,11 @@ use std::{fmt, str}; pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop}; pub use self::window::Window; +pub(crate) use crate::icon::NoIcon as PlatformIcon; mod event_loop; mod window; -pub(crate) use crate::cursor::{ - NoCustomCursor as PlatformCustomCursor, NoCustomCursor as PlatformCustomCursorSource, -}; -pub(crate) use crate::icon::NoIcon as PlatformIcon; - #[derive(Debug)] struct RedoxSocket { fd: usize, diff --git a/src/platform_impl/web/cursor.rs b/src/platform_impl/web/cursor.rs index 3f4d7b65..e67f0860 100644 --- a/src/platform_impl/web/cursor.rs +++ b/src/platform_impl/web/cursor.rs @@ -24,50 +24,17 @@ use super::backend::Style; use super::main_thread::{MainThreadMarker, MainThreadSafe}; use super::r#async::{AbortHandle, Abortable, DropAbortHandle, Notified, Notifier}; use super::ActiveEventLoop; -use crate::cursor::{BadImage, Cursor, CursorImage, CustomCursor as RootCustomCursor}; +use crate::cursor::{ + Cursor, CursorAnimation, CursorImage, CustomCursorProvider, CustomCursorSource, +}; use crate::platform::web::CustomCursorError; -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub(crate) enum CustomCursorSource { - Image(CursorImage), - Url { url: String, hotspot_x: u16, hotspot_y: u16 }, - Animation { duration: Duration, cursors: Vec }, -} - -impl CustomCursorSource { - pub fn from_rgba( - rgba: Vec, - width: u16, - height: u16, - hotspot_x: u16, - hotspot_y: u16, - ) -> Result { - Ok(CustomCursorSource::Image(CursorImage::from_rgba( - rgba, width, height, hotspot_x, hotspot_y, - )?)) - } -} - #[derive(Clone, Debug)] pub struct CustomCursor { pub(crate) animation: bool, state: Arc>>, } -impl Hash for CustomCursor { - fn hash(&self, state: &mut H) { - Arc::as_ptr(&self.state).hash(state); - } -} - -impl PartialEq for CustomCursor { - fn eq(&self, other: &Self) -> bool { - Arc::ptr_eq(&self.state, &other.state) - } -} - -impl Eq for CustomCursor {} - impl CustomCursor { pub(crate) fn new(event_loop: &ActiveEventLoop, source: CustomCursorSource) -> Self { match source { @@ -81,15 +48,13 @@ impl CustomCursor { from_url(UrlType::Plain(url), hotspot_x, hotspot_y), false, ), - CustomCursorSource::Animation { duration, cursors } => Self::build_spawn( - event_loop, - from_animation( - event_loop.runner.main_thread(), - duration, - cursors.into_iter().map(|cursor| cursor.inner), - ), - true, - ), + CustomCursorSource::Animation(CursorAnimation { duration, cursors }) => { + Self::build_spawn( + event_loop, + from_animation(event_loop.runner.main_thread(), duration, cursors.into_iter()), + true, + ) + }, } } @@ -163,6 +128,26 @@ impl CustomCursor { } } +impl CustomCursorProvider for CustomCursor { + fn is_animated(&self) -> bool { + self.animation + } +} + +impl Hash for CustomCursor { + fn hash(&self, state: &mut H) { + Arc::as_ptr(&self.state).hash(state); + } +} + +impl PartialEq for CustomCursor { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.state, &other.state) + } +} + +impl Eq for CustomCursor {} + #[derive(Debug)] pub struct CustomCursorFuture { notified: Notified>, @@ -230,13 +215,16 @@ impl CursorHandler { this.set_style(); }, Cursor::Custom(cursor) => { - let cursor = cursor.inner; + let cursor = match cursor.cast_ref::() { + Some(cursor) => cursor, + None => todo!(), + }; if let SelectedCursor::Loading { cursor: old_cursor, .. } | SelectedCursor::Image(old_cursor) | SelectedCursor::Animation { cursor: old_cursor, .. } = &this.cursor { - if *old_cursor == cursor { + if old_cursor == cursor { return; } } @@ -263,7 +251,7 @@ impl CursorHandler { drop(state); this.cursor = SelectedCursor::Loading { - cursor, + cursor: cursor.clone(), previous: mem::take(&mut this.cursor).into(), _handle: handle, }; @@ -275,7 +263,7 @@ impl CursorHandler { }, ImageState::Image(_) => { drop(state); - this.cursor = SelectedCursor::Image(cursor); + this.cursor = SelectedCursor::Image(cursor.clone()); this.set_style(); }, ImageState::Animation(animation) => { @@ -292,7 +280,7 @@ impl CursorHandler { this.cursor = SelectedCursor::Animation { animation: AnimationDropper(animation), - cursor, + cursor: cursor.clone(), }; this.set_style(); }, @@ -650,12 +638,13 @@ async fn from_url( async fn from_animation( main_thread: MainThreadMarker, duration: Duration, - cursors: impl ExactSizeIterator, + cursors: impl ExactSizeIterator, ) -> Result { let keyframes = Array::new(); let mut images = Vec::with_capacity(cursors.len()); for cursor in cursors { + let cursor = cursor.cast_ref::().unwrap(); let state = cursor.state.get(main_thread).borrow(); match state.deref() { @@ -680,7 +669,7 @@ async fn from_animation( keyframes.push(&keyframe); drop(state); - images.push(cursor); + images.push(cursor.clone()); } keyframes.push(&keyframes.get(0)); diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 7a766248..098dac30 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -23,7 +23,7 @@ use crate::platform::web::{CustomCursorFuture, PollStrategy, WaitUntilStrategy}; use crate::platform_impl::platform::cursor::CustomCursor; use crate::platform_impl::web::event_loop::proxy::EventLoopProxy; use crate::platform_impl::Window; -use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId}; +use crate::window::{CustomCursor as CoreCustomCursor, CustomCursorSource, Theme, WindowId}; #[derive(Default, Debug)] struct ModifiersShared(Rc>); @@ -65,7 +65,7 @@ impl ActiveEventLoop { } pub fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture { - CustomCursorFuture(CustomCursor::new_async(self, source.inner)) + CustomCursorFuture(CustomCursor::new_async(self, source)) } pub fn register(&self, canvas: &Rc, window_id: WindowId) { @@ -498,8 +498,8 @@ impl RootActiveEventLoop for ActiveEventLoop { fn create_custom_cursor( &self, source: CustomCursorSource, - ) -> Result { - Ok(RootCustomCursor { inner: CustomCursor::new(self, source.inner) }) + ) -> Result { + Ok(CoreCustomCursor(Arc::new(CustomCursor::new(self, source)))) } fn available_monitors(&self) -> Box> { diff --git a/src/platform_impl/web/mod.rs b/src/platform_impl/web/mod.rs index c5cc2d7f..a644e77b 100644 --- a/src/platform_impl/web/mod.rs +++ b/src/platform_impl/web/mod.rs @@ -32,10 +32,7 @@ mod monitor; mod web_sys; mod window; -pub(crate) use cursor::{ - CustomCursor as PlatformCustomCursor, CustomCursorFuture, - CustomCursorSource as PlatformCustomCursorSource, -}; +pub(crate) use cursor::CustomCursorFuture; pub(crate) use self::event_loop::{ ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes, diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 4e3272f7..fb843d09 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -64,7 +64,7 @@ use super::window::set_skip_taskbar; use super::SelectedCursor; use crate::application::ApplicationHandler; use crate::dpi::{PhysicalPosition, PhysicalSize}; -use crate::error::{EventLoopError, RequestError}; +use crate::error::{EventLoopError, NotSupportedError, RequestError}; use crate::event::{ DeviceEvent, DeviceId, FingerId, Force, Ime, RawKeyEvent, SurfaceSizeWriter, TouchPhase, WindowEvent, @@ -93,7 +93,7 @@ use crate::platform_impl::platform::{raw_input, util, wrap_device_id}; use crate::platform_impl::Window; 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, }; @@ -421,8 +421,15 @@ impl RootActiveEventLoop for ActiveEventLoop { fn create_custom_cursor( &self, source: CustomCursorSource, - ) -> Result { - Ok(RootCustomCursor { inner: WinCursor::new(&source.inner.0)? }) + ) -> Result { + let cursor = match source { + CustomCursorSource::Image(cursor) => cursor, + CustomCursorSource::Animation { .. } | CustomCursorSource::Url { .. } => { + return Err(NotSupportedError::new("unsupported cursor kind").into()) + }, + }; + + Ok(CoreCustomCursor(Arc::new(WinCursor::new(&cursor)?))) } fn available_monitors(&self) -> Box> { diff --git a/src/platform_impl/windows/icon.rs b/src/platform_impl/windows/icon.rs index f966a0f5..10360f01 100644 --- a/src/platform_impl/windows/icon.rs +++ b/src/platform_impl/windows/icon.rs @@ -15,7 +15,7 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ }; use super::util; -use crate::cursor::CursorImage; +use crate::cursor::{CursorImage, CustomCursorProvider}; use crate::dpi::PhysicalSize; use crate::error::RequestError; use crate::icon::*; @@ -196,6 +196,12 @@ impl Default for SelectedCursor { #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct WinCursor(pub(super) Arc); +impl CustomCursorProvider for WinCursor { + fn is_animated(&self) -> bool { + false + } +} + impl WinCursor { pub(crate) fn new(image: &CursorImage) -> Result { let mut bgra = image.rgba.clone(); diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index b7dc2b2d..a23dabca 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -2,12 +2,10 @@ use windows_sys::Win32::Foundation::HWND; use windows_sys::Win32::UI::WindowsAndMessaging::{HMENU, WINDOW_LONG_PTR_INDEX}; pub(crate) use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes}; -pub use self::icon::WinIcon as PlatformIcon; -pub(crate) use self::icon::{SelectedCursor, WinCursor as PlatformCustomCursor, WinIcon}; +pub(crate) use self::icon::{SelectedCursor, WinIcon as PlatformIcon, WinIcon}; pub(crate) use self::keyboard::{physicalkey_to_scancode, scancode_to_physicalkey}; pub(crate) use self::monitor::MonitorHandle; pub(crate) use self::window::Window; -pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource; use crate::event::DeviceId; use crate::icon::Icon; use crate::platform::windows::{BackdropType, Color, CornerPreference}; diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index e6aeec4a..accf84f8 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -47,6 +47,7 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ WDA_EXCLUDEFROMCAPTURE, WDA_NONE, WM_NCLBUTTONDOWN, WM_SYSCOMMAND, WNDCLASSEXW, }; +use super::icon::WinCursor; use super::MonitorHandle; use crate::cursor::Cursor; use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}; @@ -597,10 +598,15 @@ impl CoreWindow for Window { }); }, Cursor::Custom(cursor) => { + let cursor = match cursor.cast_ref::() { + Some(cursor) => cursor, + None => return, + }; self.window_state_lock().mouse.selected_cursor = - SelectedCursor::Custom(cursor.inner.0.clone()); + SelectedCursor::Custom(cursor.0.clone()); + let handle = cursor.0.clone(); self.thread_executor.execute_in_thread(move || unsafe { - SetCursor(cursor.inner.0.as_raw_handle()); + SetCursor(handle.as_raw_handle()); }); }, }