diff --git a/src/cursor.rs b/src/cursor.rs index d53af7a52d..08ac912d53 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -46,25 +46,12 @@ const PIXEL_SIZE: usize = 4; /// let window = Window::new(&event_loop).unwrap(); /// window.set_custom_cursor(&custom_cursor); /// ``` -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct CustomCursor { - pub(crate) inner: Arc, + /// Platforms should make sure this is cheap to clone. + pub(crate) inner: PlatformCustomCursor, } -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 { /// Creates a new cursor from an rgba buffer. /// @@ -80,7 +67,7 @@ impl CustomCursor { hotspot_y: u16, ) -> Result { Ok(CustomCursorBuilder { - inner: PlatformCustomCursor::from_rgba( + inner: PlatformCustomCursorBuilder::from_rgba( rgba.into(), width, height, @@ -102,7 +89,7 @@ pub struct CustomCursorBuilder { impl CustomCursorBuilder { pub fn build(self, window_target: &EventLoopWindowTarget) -> CustomCursor { CustomCursor { - inner: self.inner.build(&window_target.p), + inner: PlatformCustomCursor::build(self.inner, &window_target.p), } } } @@ -165,9 +152,54 @@ impl fmt::Display for BadImage { impl Error for BadImage {} -/// Platforms export this directly as `PlatformCustomCursor` if they need to only work with images. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CursorImage { +/// Platforms export this directly as `PlatformCustomCursorBuilder` if they need to only work with images. +#[derive(Debug)] +pub(crate) struct OnlyCursorImageBuilder(pub(crate) CursorImage); + +#[allow(dead_code)] +impl OnlyCursorImageBuilder { + 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) + } +} + +/// Platforms export this directly as `PlatformCustomCursor` if they don't implement caching. +#[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 {} + +#[allow(dead_code)] +impl OnlyCursorImage { + fn build( + builder: OnlyCursorImageBuilder, + _: &platform_impl::EventLoopWindowTarget, + ) -> Self { + Self(Arc::new(builder.0)) + } +} + +#[derive(Debug)] +#[allow(dead_code)] +pub(crate) struct CursorImage { pub(crate) rgba: Vec, pub(crate) width: u16, pub(crate) height: u16, @@ -175,9 +207,8 @@ pub struct CursorImage { pub(crate) hotspot_y: u16, } -#[allow(dead_code)] impl CursorImage { - pub fn from_rgba( + pub(crate) fn from_rgba( rgba: Vec, width: u16, height: u16, @@ -222,19 +253,15 @@ impl CursorImage { hotspot_y, }) } - - fn build(self, _: &platform_impl::EventLoopWindowTarget) -> Arc { - Arc::new(self) - } } // Platforms that don't support cursors will export this as `PlatformCustomCursor`. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub(crate) struct NoCustomCursor; #[allow(dead_code)] impl NoCustomCursor { - pub fn from_rgba( + pub(crate) fn from_rgba( rgba: Vec, width: u16, height: u16, @@ -245,7 +272,7 @@ impl NoCustomCursor { Ok(Self) } - fn build(self, _: &platform_impl::EventLoopWindowTarget) -> Arc { - Arc::new(self) + fn build(self, _: &platform_impl::EventLoopWindowTarget) -> NoCustomCursor { + self } } diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index eee0fb0ac1..cf28c21bbe 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -906,7 +906,7 @@ impl Window { pub fn set_cursor_icon(&self, _: window::CursorIcon) {} - pub(crate) fn set_custom_cursor(&self, _: Arc) {} + pub(crate) fn set_custom_cursor(&self, _: PlatformCustomCursor) {} pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> { Err(error::ExternalError::NotSupported( diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 6358826687..2f0f8c0974 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -1,7 +1,6 @@ #![allow(clippy::unnecessary_cast)] use std::collections::VecDeque; -use std::sync::Arc; use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker}; use objc2::rc::Id; @@ -178,7 +177,7 @@ impl Inner { debug!("`Window::set_cursor_icon` ignored on iOS") } - pub(crate) fn set_custom_cursor(&self, _: Arc) { + pub(crate) fn set_custom_cursor(&self, _: PlatformCustomCursor) { debug!("`Window::set_custom_cursor` ignored on iOS") } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 0979b700f6..0cfd3cb8ba 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -40,8 +40,8 @@ pub use x11::XNotSupported; #[cfg(x11_platform)] use x11::{util::WindowType as XWindowType, X11Error, XConnection, XError}; -pub(crate) use crate::cursor::CursorImage as PlatformCustomCursorBuilder; -pub(crate) use crate::cursor::CursorImage as PlatformCustomCursor; +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; @@ -427,7 +427,7 @@ impl Window { } #[inline] - pub(crate) fn set_custom_cursor(&self, cursor: Arc) { + pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) { x11_or_wayland!(match self; Window(w) => w.set_custom_cursor(cursor)) } diff --git a/src/platform_impl/linux/wayland/types/cursor.rs b/src/platform_impl/linux/wayland/types/cursor.rs index 483486197f..4af996f6f3 100644 --- a/src/platform_impl/linux/wayland/types/cursor.rs +++ b/src/platform_impl/linux/wayland/types/cursor.rs @@ -27,7 +27,7 @@ pub struct CustomCursor { } impl CustomCursor { - pub fn new(pool: &mut SlotPool, image: &CursorImage) -> Self { + pub(crate) fn new(pool: &mut SlotPool, image: &CursorImage) -> Self { let (buffer, canvas) = pool .create_buffer( image.width as i32, diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index d2dbeb3611..74ff1baf11 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -507,8 +507,11 @@ impl Window { } #[inline] - pub(crate) fn set_custom_cursor(&self, cursor: Arc) { - self.window_state.lock().unwrap().set_custom_cursor(&cursor); + pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) { + self.window_state + .lock() + .unwrap() + .set_custom_cursor(&cursor.0); } #[inline] diff --git a/src/platform_impl/linux/wayland/window/state.rs b/src/platform_impl/linux/wayland/window/state.rs index 10bb246c4b..05747cf07c 100644 --- a/src/platform_impl/linux/wayland/window/state.rs +++ b/src/platform_impl/linux/wayland/window/state.rs @@ -726,7 +726,7 @@ impl WindowState { } /// Set the custom cursor icon. - pub fn set_custom_cursor(&mut self, cursor: &CursorImage) { + pub(crate) fn set_custom_cursor(&mut self, cursor: &CursorImage) { let cursor = { let mut pool = self.custom_cursor_pool.lock().unwrap(); CustomCursor::new(&mut pool, cursor) diff --git a/src/platform_impl/linux/x11/util/cursor.rs b/src/platform_impl/linux/x11/util/cursor.rs index e9b457d967..5650aadcf3 100644 --- a/src/platform_impl/linux/x11/util/cursor.rs +++ b/src/platform_impl/linux/x11/util/cursor.rs @@ -19,7 +19,7 @@ impl XConnection { .expect("Failed to set cursor"); } - pub fn set_custom_cursor(&self, window: xproto::Window, cursor: &CustomCursor) { + pub(crate) fn set_custom_cursor(&self, window: xproto::Window, cursor: &CustomCursor) { self.update_cursor(window, cursor.inner.cursor) .expect("Failed to set cursor"); } diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index f438616d15..b7ea8e1a12 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -1550,8 +1550,8 @@ impl UnownedWindow { } #[inline] - pub(crate) fn set_custom_cursor(&self, cursor: Arc) { - let new_cursor = unsafe { CustomCursor::new(&self.xconn, &cursor) }; + pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) { + let new_cursor = unsafe { CustomCursor::new(&self.xconn, &cursor.0) }; #[allow(clippy::mutex_atomic)] if *self.cursor_visible.lock().unwrap() { diff --git a/src/platform_impl/macos/mod.rs b/src/platform_impl/macos/mod.rs index 1f813b19fa..7feecb96b8 100644 --- a/src/platform_impl/macos/mod.rs +++ b/src/platform_impl/macos/mod.rs @@ -28,8 +28,8 @@ pub(crate) use self::{ use crate::event::DeviceId as RootDeviceId; pub(crate) use self::window::Window; -pub(crate) use crate::cursor::CursorImage as PlatformCustomCursor; -pub(crate) use crate::cursor::CursorImage as PlatformCustomCursorBuilder; +pub(crate) use crate::cursor::OnlyCursorImage as PlatformCustomCursor; +pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder; pub(crate) use crate::icon::NoIcon as PlatformIcon; pub(crate) use crate::platform_impl::Fullscreen; diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 4b204bb0ac..328ddc4ace 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -5,7 +5,6 @@ use std::f64; use std::ops; use std::os::raw::c_void; use std::ptr::NonNull; -use std::sync::Arc; use std::sync::{Mutex, MutexGuard}; use crate::{ @@ -848,9 +847,9 @@ impl WinitWindow { } #[inline] - pub(crate) fn set_custom_cursor(&self, cursor: Arc) { + pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) { let view = self.view(); - view.set_cursor_icon(NSCursor::from_image(&cursor)); + view.set_cursor_icon(NSCursor::from_image(&cursor.0)); self.invalidateCursorRectsForView(&view); } diff --git a/src/platform_impl/orbital/window.rs b/src/platform_impl/orbital/window.rs index b3edcdbf29..3a5c74b7e1 100644 --- a/src/platform_impl/orbital/window.rs +++ b/src/platform_impl/orbital/window.rs @@ -352,7 +352,7 @@ impl Window { #[inline] pub fn set_cursor_icon(&self, _: window::CursorIcon) {} - pub(crate) fn set_custom_cursor(&self, _: Arc) {} + pub(crate) fn set_custom_cursor(&self, _: PlatformCustomCursor) {} #[inline] pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> { diff --git a/src/platform_impl/web/cursor.rs b/src/platform_impl/web/cursor.rs index 04c566631a..cd8922ecc8 100644 --- a/src/platform_impl/web/cursor.rs +++ b/src/platform_impl/web/cursor.rs @@ -1,6 +1,8 @@ use std::{ cell::RefCell, - future, mem, + future, + hash::{Hash, Hasher}, + mem, ops::DerefMut, rc::{self, Rc}, sync::{self, Arc}, @@ -25,7 +27,7 @@ use self::thread_safe::ThreadSafe; use super::{backend::Style, r#async::AsyncSender, EventLoopWindowTarget}; #[derive(Debug)] -pub enum CustomCursorBuilder { +pub(crate) enum CustomCursorBuilder { Image(CursorImage), Url { url: String, @@ -35,26 +37,60 @@ pub enum CustomCursorBuilder { } impl CustomCursorBuilder { - pub fn build(self, window_target: &EventLoopWindowTarget) -> Arc { + pub fn from_rgba( + rgba: Vec, + width: u16, + height: u16, + hotspot_x: u16, + hotspot_y: u16, + ) -> Result { + Ok(CustomCursorBuilder::Image(CursorImage::from_rgba( + rgba, width, height, hotspot_x, hotspot_y, + )?)) + } +} + +#[derive(Clone, Debug)] +pub struct CustomCursor(Arc); + +impl Hash for CustomCursor { + fn hash(&self, state: &mut H) { + Arc::as_ptr(&self.0).hash(state); + } +} + +impl PartialEq for CustomCursor { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.0, &other.0) + } +} + +impl Eq for CustomCursor {} + +impl CustomCursor { + pub(crate) fn build( + builder: CustomCursorBuilder, + window_target: &EventLoopWindowTarget, + ) -> Self { Lazy::force(&DROP_HANDLER); - match self { - Self::Image(image) => ImageState::from_rgba( + Self(match builder { + CustomCursorBuilder::Image(image) => ImageState::from_rgba( window_target.runner.window(), window_target.runner.document().clone(), &image, ), - Self::Url { + CustomCursorBuilder::Url { url, hotspot_x, hotspot_y, } => ImageState::from_url(url, hotspot_x, hotspot_y), - } + }) } } #[derive(Debug)] -pub struct CustomCursor(Option>>); +struct Inner(Option>>); static DROP_HANDLER: Lazy>>> = Lazy::new(|| { let (sender, receiver) = r#async::channel(); @@ -63,21 +99,9 @@ static DROP_HANDLER: Lazy>>> = Lazy:: sender }); -impl CustomCursor { - pub fn from_rgba( - rgba: Vec, - width: u16, - height: u16, - hotspot_x: u16, - hotspot_y: u16, - ) -> Result { - Ok(CustomCursorBuilder::Image(CursorImage::from_rgba( - rgba, width, height, hotspot_x, hotspot_y, - )?)) - } - +impl Inner { fn new() -> Arc { - Arc::new(Self(Some(ThreadSafe::new(RefCell::new( + Arc::new(Inner(Some(ThreadSafe::new(RefCell::new( ImageState::Loading(None), ))))) } @@ -90,7 +114,7 @@ impl CustomCursor { } } -impl Drop for CustomCursor { +impl Drop for Inner { fn drop(&mut self) { let value = self .0 @@ -130,13 +154,13 @@ impl CursorState { this.set_style(); } - pub fn set_custom_cursor(&self, cursor: Arc) { + pub(crate) fn set_custom_cursor(&self, cursor: CustomCursor) { let mut this = self.0.borrow_mut(); - match cursor.get().borrow_mut().deref_mut() { + match cursor.0.get().borrow_mut().deref_mut() { ImageState::Loading(state) => { this.cursor = SelectedCursor::ImageLoading { - state: cursor.clone(), + state: cursor.0.clone(), previous: mem::take(&mut this.cursor).into(), }; *state = Some(Rc::downgrade(&self.0)); @@ -187,7 +211,7 @@ impl State { enum SelectedCursor { Named(CursorIcon), ImageLoading { - state: Arc, + state: Arc, previous: Previous, }, ImageReady(Rc), @@ -241,7 +265,7 @@ enum ImageState { } impl ImageState { - fn from_rgba(window: &Window, document: Document, image: &CursorImage) -> Arc { + fn from_rgba(window: &Window, document: Document, image: &CursorImage) -> Arc { // 1. Create an `ImageData` from the RGBA data. // 2. Create an `ImageBitmap` from the `ImageData`. // 3. Draw `ImageBitmap` on an `HTMLCanvasElement`. @@ -293,7 +317,7 @@ impl ImageState { .expect("unexpected exception in `createImageBitmap()`"), ); - let this = CustomCursor::new(); + let this = Inner::new(); wasm_bindgen_futures::spawn_local({ let weak = Arc::downgrade(&this); @@ -406,8 +430,8 @@ impl ImageState { this } - fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> Arc { - let this = CustomCursor::new(); + fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> Arc { + let this = Inner::new(); wasm_bindgen_futures::spawn_local(Self::decode( Arc::downgrade(&this), url, @@ -420,7 +444,7 @@ impl ImageState { } async fn decode( - weak: sync::Weak, + weak: sync::Weak, url: String, object: bool, hotspot_x: u16, diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index 8b7fb75949..f3845dc810 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -16,7 +16,6 @@ use web_sys::HtmlCanvasElement; use std::cell::RefCell; use std::collections::VecDeque; use std::rc::Rc; -use std::sync::Arc; pub struct Window { inner: Dispatcher, @@ -218,7 +217,7 @@ impl Inner { } #[inline] - pub(crate) fn set_custom_cursor(&self, cursor: Arc) { + pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) { self.cursor.set_custom_cursor(cursor) } diff --git a/src/platform_impl/windows/icon.rs b/src/platform_impl/windows/icon.rs index 275f7fb699..70ce7c1ac3 100644 --- a/src/platform_impl/windows/icon.rs +++ b/src/platform_impl/windows/icon.rs @@ -194,7 +194,7 @@ impl WinCursor { } } - pub fn new(image: &CursorImage) -> Result { + pub(crate) fn new(image: &CursorImage) -> Result { let mut bgra = image.rgba.clone(); bgra.chunks_exact_mut(4).for_each(|chunk| chunk.swap(0, 2)); diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index 3d2253bcec..13f4ecc6e0 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -16,8 +16,8 @@ pub(crate) use self::{ }; pub use self::icon::WinIcon as PlatformIcon; -pub(crate) use crate::cursor::CursorImage as PlatformCustomCursor; -pub(crate) use crate::cursor::CursorImage as PlatformCustomCursorBuilder; +pub(crate) use crate::cursor::OnlyCursorImage as PlatformCustomCursor; +pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder; use crate::platform_impl::Fullscreen; use crate::event::DeviceId as RootDeviceId; diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index ae1fb07d95..b794064859 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -405,8 +405,8 @@ impl Window { } #[inline] - pub(crate) fn set_custom_cursor(&self, cursor: Arc) { - let new_cursor = match WinCursor::new(&cursor) { + pub(crate) fn set_custom_cursor(&self, cursor: PlatformCustomCursor) { + let new_cursor = match WinCursor::new(&cursor.0) { Ok(cursor) => cursor, Err(err) => { warn!("Failed to create custom cursor: {err}"); diff --git a/src/window.rs b/src/window.rs index aacc5c4de5..8786435272 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,5 +1,5 @@ //! The [`Window`] struct and associated types. -use std::{fmt, sync::Arc}; +use std::fmt; use crate::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, @@ -1355,7 +1355,7 @@ impl Window { /// - **iOS / Android / Orbital:** Unsupported. #[inline] pub fn set_custom_cursor(&self, cursor: &CustomCursor) { - let cursor = Arc::clone(&cursor.inner); + let cursor = cursor.inner.clone(); self.window .maybe_queue_on_main(move |w| w.set_custom_cursor(cursor)) }