Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow custom cursor caching #3276

Merged
merged 3 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ features = [
'FocusEvent',
'HtmlCanvasElement',
'HtmlElement',
'HtmlImageElement',
'ImageBitmap',
'ImageBitmapOptions',
'ImageBitmapRenderingContext',
Expand Down
12 changes: 7 additions & 5 deletions examples/custom_cursors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@
use simple_logger::SimpleLogger;
use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
event_loop::{EventLoop, EventLoopWindowTarget},
keyboard::Key,
window::{CustomCursor, WindowBuilder},
};

fn decode_cursor(bytes: &[u8]) -> CustomCursor {
fn decode_cursor<T>(bytes: &[u8], window_target: &EventLoopWindowTarget<T>) -> CustomCursor {
let img = image::load_from_memory(bytes).unwrap().to_rgba8();
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()
let builder = CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap();

builder.build(window_target)
}

#[cfg(not(wasm_platform))]
Expand Down Expand Up @@ -43,8 +45,8 @@ fn main() -> Result<(), impl std::error::Error> {
let mut cursor_visible = true;

let custom_cursors = [
decode_cursor(include_bytes!("data/cross.png")),
decode_cursor(include_bytes!("data/cross2.png")),
decode_cursor(include_bytes!("data/cross.png"), &event_loop),
decode_cursor(include_bytes!("data/cross2.png"), &event_loop),
];

event_loop.run(move |event, _elwt| match event {
Expand Down
77 changes: 65 additions & 12 deletions src/cursor.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use core::fmt;
use std::{error::Error, sync::Arc};
use std::hash::Hasher;
use std::sync::Arc;
use std::{error::Error, hash::Hash};

use crate::platform_impl::PlatformCustomCursor;
use crate::event_loop::EventLoopWindowTarget;
use crate::platform_impl::{self, PlatformCustomCursor, PlatformCustomCursorBuilder};

/// The maximum width and height for a cursor when using [`CustomCursor::from_rgba`].
pub const MAX_CURSOR_SIZE: u16 = 2048;
Expand All @@ -16,25 +19,52 @@ const PIXEL_SIZE: usize = 4;
///
/// # Example
///
/// ```
/// use winit::window::CustomCursor;
/// ```no_run
/// use winit::{
/// event::{Event, WindowEvent},
/// event_loop::{ControlFlow, EventLoop},
/// window::{CustomCursor, Window},
/// };
///
/// let mut event_loop = EventLoop::new().unwrap();
///
/// let w = 10;
/// let h = 10;
/// let rgba = vec![255; (w * h * 4) as usize];
/// let custom_cursor = CustomCursor::from_rgba(rgba, w, h, w / 2, h / 2).unwrap();
///
/// #[cfg(not(target_family = "wasm"))]
/// let builder = CustomCursor::from_rgba(rgba, w, h, w / 2, h / 2).unwrap();
///
/// #[cfg(target_family = "wasm")]
/// let custom_cursor_url = {
/// let builder = {
/// use winit::platform::web::CustomCursorExtWebSys;
/// CustomCursor::from_url("http://localhost:3000/cursor.png", 0, 0).unwrap()
/// CustomCursor::from_url(String::from("http://localhost:3000/cursor.png"), 0, 0)
/// };
///
/// let custom_cursor = builder.build(&event_loop);
///
/// let window = Window::new(&event_loop).unwrap();
/// window.set_custom_cursor(&custom_cursor);
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Clone, Debug)]
pub struct CustomCursor {
pub(crate) inner: Arc<PlatformCustomCursor>,
}

impl Hash for CustomCursor {
fn hash<H: Hasher>(&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.
///
Expand All @@ -48,20 +78,35 @@ impl CustomCursor {
height: u16,
hotspot_x: u16,
hotspot_y: u16,
) -> Result<Self, BadImage> {
Ok(Self {
) -> Result<CustomCursorBuilder, BadImage> {
Ok(CustomCursorBuilder {
inner: PlatformCustomCursor::from_rgba(
rgba.into(),
width,
height,
hotspot_x,
hotspot_y,
)?
.into(),
)?,
})
}
}

/// Builds a [`CustomCursor`].
///
/// See [`CustomCursor`] for more details.
#[derive(Debug)]
pub struct CustomCursorBuilder {
pub(crate) inner: PlatformCustomCursorBuilder,
}

impl CustomCursorBuilder {
pub fn build<T>(self, window_target: &EventLoopWindowTarget<T>) -> CustomCursor {
CustomCursor {
inner: self.inner.build(&window_target.p),
}
}
}

/// An error produced when using [`CustomCursor::from_rgba`] with invalid arguments.
#[derive(Debug, Clone)]
pub enum BadImage {
Expand Down Expand Up @@ -177,6 +222,10 @@ impl CursorImage {
hotspot_y,
})
}

fn build<T>(self, _: &platform_impl::EventLoopWindowTarget<T>) -> Arc<CursorImage> {
Arc::new(self)
}
}

// Platforms that don't support cursors will export this as `PlatformCustomCursor`.
Expand All @@ -195,4 +244,8 @@ impl NoCustomCursor {
CursorImage::from_rgba(rgba, width, height, hotspot_x, hotspot_y)?;
Ok(Self)
}

fn build<T>(self, _: &platform_impl::EventLoopWindowTarget<T>) -> Arc<NoCustomCursor> {
Arc::new(self)
}
}
16 changes: 8 additions & 8 deletions src/platform/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@
//! [`border`]: https://developer.mozilla.org/en-US/docs/Web/CSS/border
//! [`padding`]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding

use crate::cursor::CustomCursor;
use crate::cursor::CustomCursorBuilder;
use crate::event::Event;
use crate::event_loop::EventLoop;
use crate::event_loop::EventLoopWindowTarget;
use crate::platform_impl::PlatformCustomCursor;
use crate::platform_impl::PlatformCustomCursorBuilder;
use crate::window::CustomCursor;
use crate::window::{Window, WindowBuilder};
use crate::SendSyncWrapper;

Expand Down Expand Up @@ -209,18 +210,17 @@ pub trait CustomCursorExtWebSys {
/// 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) -> Self;
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder;
}

impl CustomCursorExtWebSys for CustomCursor {
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> Self {
Self {
inner: PlatformCustomCursor::Url {
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder {
CustomCursorBuilder {
inner: PlatformCustomCursorBuilder::Url {
url,
hotspot_x,
hotspot_y,
}
.into(),
},
}
}
}
4 changes: 2 additions & 2 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use android_activity::{
use once_cell::sync::Lazy;

use crate::{
cursor::CustomCursor,
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error,
event::{self, Force, InnerSizeWriter, StartCause},
Expand Down Expand Up @@ -907,7 +906,7 @@ impl Window {

pub fn set_cursor_icon(&self, _: window::CursorIcon) {}

pub fn set_custom_cursor(&self, _: CustomCursor) {}
pub(crate) fn set_custom_cursor(&self, _: Arc<PlatformCustomCursor>) {}

pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
Expand Down Expand Up @@ -1035,6 +1034,7 @@ impl Display for OsError {
}

pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorBuilder;
pub(crate) use crate::icon::NoIcon as PlatformIcon;

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Expand Down
1 change: 1 addition & 0 deletions src/platform_impl/ios/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ pub(crate) use self::{

use self::uikit::UIScreen;
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorBuilder;
pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;

Expand Down
6 changes: 3 additions & 3 deletions src/platform_impl/ios/window.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![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;
Expand All @@ -11,14 +12,13 @@ use super::app_state::EventWrapper;
use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation};
use super::view::{WinitUIWindow, WinitView, WinitViewController};
use crate::{
cursor::CustomCursor,
dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
event::{Event, WindowEvent},
icon::Icon,
platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations},
platform_impl::platform::{
app_state, monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle,
app_state, monitor, EventLoopWindowTarget, Fullscreen, MonitorHandle, PlatformCustomCursor,
},
window::{
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
Expand Down Expand Up @@ -178,7 +178,7 @@ impl Inner {
debug!("`Window::set_cursor_icon` ignored on iOS")
}

pub fn set_custom_cursor(&self, _: CustomCursor) {
pub(crate) fn set_custom_cursor(&self, _: Arc<PlatformCustomCursor>) {
debug!("`Window::set_custom_cursor` ignored on iOS")
}

Expand Down
4 changes: 2 additions & 2 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Mutex};
use once_cell::sync::Lazy;
use smol_str::SmolStr;

use crate::cursor::CustomCursor;
#[cfg(x11_platform)]
use crate::platform::x11::XlibErrorHook;
use crate::{
Expand All @@ -41,6 +40,7 @@ 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::icon::RgbaIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;
Expand Down Expand Up @@ -427,7 +427,7 @@ impl Window {
}

#[inline]
pub fn set_custom_cursor(&self, cursor: CustomCursor) {
pub(crate) fn set_custom_cursor(&self, cursor: Arc<PlatformCustomCursor>) {
x11_or_wayland!(match self; Window(w) => w.set_custom_cursor(cursor))
}

Expand Down
9 changes: 4 additions & 5 deletions src/platform_impl/linux/wayland/window/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@ use sctk::shell::xdg::window::Window as SctkWindow;
use sctk::shell::xdg::window::WindowDecorations;
use sctk::shell::WaylandSurface;

use crate::cursor::CustomCursor;
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
use crate::event::{Ime, WindowEvent};
use crate::event_loop::AsyncRequestSerial;
use crate::platform_impl::{
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon,
PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformCustomCursor,
PlatformIcon, PlatformSpecificWindowBuilderAttributes as PlatformAttributes,
};
use crate::window::{
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
Expand Down Expand Up @@ -508,8 +507,8 @@ impl Window {
}

#[inline]
pub fn set_custom_cursor(&self, cursor: CustomCursor) {
self.window_state.lock().unwrap().set_custom_cursor(cursor);
pub(crate) fn set_custom_cursor(&self, cursor: Arc<PlatformCustomCursor>) {
self.window_state.lock().unwrap().set_custom_cursor(&cursor);
}

#[inline]
Expand Down
6 changes: 3 additions & 3 deletions src/platform_impl/linux/wayland/window/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::CustomCursor as RootCustomCursor;
use crate::cursor::CursorImage;
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size};
use crate::error::{ExternalError, NotSupportedError};
use crate::event::WindowEvent;
Expand Down Expand Up @@ -726,10 +726,10 @@ impl WindowState {
}

/// Set the custom cursor icon.
pub fn set_custom_cursor(&mut self, cursor: RootCustomCursor) {
pub fn set_custom_cursor(&mut self, cursor: &CursorImage) {
let cursor = {
let mut pool = self.custom_cursor_pool.lock().unwrap();
CustomCursor::new(&mut pool, &cursor.inner)
CustomCursor::new(&mut pool, cursor)
};

if self.cursor_visible {
Expand Down
10 changes: 4 additions & 6 deletions src/platform_impl/linux/x11/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ use std::{
sync::{Arc, Mutex, MutexGuard},
};

use crate::cursor::CustomCursor as RootCustomCursor;

use cursor_icon::CursorIcon;
use x11rb::{
connection::Connection,
Expand All @@ -32,8 +30,8 @@ use crate::{
atoms::*, xinput_fp1616_to_float, MonitorHandle as X11MonitorHandle, WakeSender,
X11Error,
},
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon,
PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode,
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformCustomCursor,
PlatformIcon, PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode,
},
window::{
CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
Expand Down Expand Up @@ -1552,8 +1550,8 @@ impl UnownedWindow {
}

#[inline]
pub fn set_custom_cursor(&self, cursor: RootCustomCursor) {
let new_cursor = unsafe { CustomCursor::new(&self.xconn, &cursor.inner) };
pub(crate) fn set_custom_cursor(&self, cursor: Arc<PlatformCustomCursor>) {
let new_cursor = unsafe { CustomCursor::new(&self.xconn, &cursor) };

#[allow(clippy::mutex_atomic)]
if *self.cursor_visible.lock().unwrap() {
Expand Down
1 change: 1 addition & 0 deletions src/platform_impl/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ 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::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;

Expand Down
Loading
Loading