2023-12-16 22:02:17 +02:00
use core ::fmt ;
2023-12-22 22:20:41 +01:00
use std ::hash ::Hasher ;
use std ::sync ::Arc ;
use std ::{ error ::Error , hash ::Hash } ;
2023-12-16 22:02:17 +02:00
2023-12-25 07:20:52 +01:00
use cursor_icon ::CursorIcon ;
2023-12-22 22:20:41 +01:00
use crate ::event_loop ::EventLoopWindowTarget ;
use crate ::platform_impl ::{ self , PlatformCustomCursor , PlatformCustomCursorBuilder } ;
2023-12-16 22:02:17 +02:00
/// The maximum width and height for a cursor when using [`CustomCursor::from_rgba`].
pub const MAX_CURSOR_SIZE : u16 = 2048 ;
const PIXEL_SIZE : usize = 4 ;
2023-12-25 07:20:52 +01:00
/// See [`Window::set_cursor()`](crate::window::Window::set_cursor) for more details.
#[ derive(Clone, Debug, Eq, Hash, PartialEq) ]
pub enum Cursor {
Icon ( CursorIcon ) ,
Custom ( CustomCursor ) ,
}
impl Default for Cursor {
fn default ( ) -> Self {
Self ::Icon ( CursorIcon ::default ( ) )
}
}
impl From < CursorIcon > for Cursor {
fn from ( icon : CursorIcon ) -> Self {
Self ::Icon ( icon )
}
}
impl From < CustomCursor > for Cursor {
fn from ( custom : CustomCursor ) -> Self {
Self ::Custom ( custom )
}
}
2023-12-16 22:02:17 +02:00
/// Use a custom image as a cursor (mouse pointer).
///
2023-12-25 07:20:52 +01:00
/// Is guaranteed to be cheap to clone.
///
2023-12-16 22:02:17 +02:00
/// ## Platform-specific
///
/// **Web**: Some browsers have limits on cursor sizes usually at 128x128.
///
/// # Example
///
2023-12-22 22:20:41 +01:00
/// ```no_run
/// use winit::{
/// event::{Event, WindowEvent},
/// event_loop::{ControlFlow, EventLoop},
/// window::{CustomCursor, Window},
/// };
///
/// let mut event_loop = EventLoop::new().unwrap();
2023-12-16 22:02:17 +02:00
///
/// let w = 10;
/// let h = 10;
/// let rgba = vec![255; (w * h * 4) as usize];
2023-12-22 22:20:41 +01:00
///
/// #[cfg(not(target_family = "wasm"))]
/// let builder = CustomCursor::from_rgba(rgba, w, h, w / 2, h / 2).unwrap();
2023-12-16 22:02:17 +02:00
///
/// #[cfg(target_family = "wasm")]
2023-12-22 22:20:41 +01:00
/// let builder = {
2023-12-16 22:02:17 +02:00
/// use winit::platform::web::CustomCursorExtWebSys;
2023-12-22 22:20:41 +01:00
/// CustomCursor::from_url(String::from("http://localhost:3000/cursor.png"), 0, 0)
2023-12-16 22:02:17 +02:00
/// };
2023-12-22 22:20:41 +01:00
///
/// let custom_cursor = builder.build(&event_loop);
///
/// let window = Window::new(&event_loop).unwrap();
2023-12-25 07:20:52 +01:00
/// window.set_cursor(custom_cursor.clone());
2023-12-16 22:02:17 +02:00
/// ```
2023-12-23 16:12:29 +01:00
#[ derive(Clone, Debug, Eq, Hash, PartialEq) ]
2023-12-16 22:02:17 +02:00
pub struct CustomCursor {
2023-12-23 16:12:29 +01:00
/// Platforms should make sure this is cheap to clone.
pub ( crate ) inner : PlatformCustomCursor ,
2023-12-16 22:02:17 +02:00
}
impl CustomCursor {
/// Creates a new cursor from an rgba buffer.
pub fn from_rgba (
rgba : impl Into < Vec < u8 > > ,
width : u16 ,
height : u16 ,
hotspot_x : u16 ,
hotspot_y : u16 ,
2023-12-22 22:20:41 +01:00
) -> Result < CustomCursorBuilder , BadImage > {
Ok ( CustomCursorBuilder {
2023-12-23 16:12:29 +01:00
inner : PlatformCustomCursorBuilder ::from_rgba (
2023-12-16 22:02:17 +02:00
rgba . into ( ) ,
width ,
height ,
hotspot_x ,
hotspot_y ,
2023-12-22 22:20:41 +01:00
) ? ,
2023-12-16 22:02:17 +02:00
} )
}
}
2023-12-22 22:20:41 +01:00
/// 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 {
2023-12-23 16:12:29 +01:00
inner : PlatformCustomCursor ::build ( self . inner , & window_target . p ) ,
2023-12-22 22:20:41 +01:00
}
}
}
2023-12-16 22:02:17 +02:00
/// An error produced when using [`CustomCursor::from_rgba`] with invalid arguments.
#[ derive(Debug, Clone) ]
pub enum BadImage {
/// Produced when the image dimensions are larger than [`MAX_CURSOR_SIZE`]. This doesn't
/// guarantee that the cursor will work, but should avoid many platform and device specific
/// limits.
TooLarge { width : u16 , height : u16 } ,
/// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be
/// safely interpreted as 32bpp RGBA pixels.
ByteCountNotDivisibleBy4 { byte_count : usize } ,
/// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
/// At least one of your arguments is incorrect.
DimensionsVsPixelCount {
width : u16 ,
height : u16 ,
width_x_height : u64 ,
pixel_count : u64 ,
} ,
/// Produced when the hotspot is outside the image bounds
HotspotOutOfBounds {
width : u16 ,
height : u16 ,
hotspot_x : u16 ,
hotspot_y : u16 ,
} ,
}
impl fmt ::Display for BadImage {
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
match self {
BadImage ::TooLarge { width , height } = > write! ( f ,
" The specified dimensions ({width:?}x{height:?}) are too large. The maximum is {MAX_CURSOR_SIZE:?}x{MAX_CURSOR_SIZE:?}. " ,
) ,
BadImage ::ByteCountNotDivisibleBy4 { byte_count } = > write! ( f ,
" The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels. " ,
) ,
BadImage ::DimensionsVsPixelCount {
width ,
height ,
width_x_height ,
pixel_count ,
} = > write! ( f ,
" The specified dimensions ({width:?}x{height:?}) don't match the number of pixels supplied by the `rgba` argument ({pixel_count:?}). For those dimensions, the expected pixel count is {width_x_height:?}. " ,
) ,
BadImage ::HotspotOutOfBounds {
width ,
height ,
hotspot_x ,
hotspot_y ,
} = > write! ( f ,
" The specified hotspot ({hotspot_x:?}, {hotspot_y:?}) is outside the image bounds ({width:?}x{height:?}). " ,
) ,
}
}
}
impl Error for BadImage { }
2023-12-23 16:12:29 +01:00
/// 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 < u8 > ,
width : u16 ,
height : u16 ,
hotspot_x : u16 ,
hotspot_y : u16 ,
) -> Result < Self , BadImage > {
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 < CursorImage > ) ;
impl Hash for OnlyCursorImage {
fn hash < H : Hasher > ( & 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 < T > (
builder : OnlyCursorImageBuilder ,
_ : & platform_impl ::EventLoopWindowTarget < T > ,
) -> Self {
Self ( Arc ::new ( builder . 0 ) )
}
}
#[ derive(Debug) ]
#[ allow(dead_code) ]
pub ( crate ) struct CursorImage {
2023-12-16 22:02:17 +02:00
pub ( crate ) rgba : Vec < u8 > ,
pub ( crate ) width : u16 ,
pub ( crate ) height : u16 ,
pub ( crate ) hotspot_x : u16 ,
pub ( crate ) hotspot_y : u16 ,
}
impl CursorImage {
2023-12-23 16:12:29 +01:00
pub ( crate ) fn from_rgba (
2023-12-16 22:02:17 +02:00
rgba : Vec < u8 > ,
width : u16 ,
height : u16 ,
hotspot_x : u16 ,
hotspot_y : u16 ,
) -> Result < Self , BadImage > {
if width > MAX_CURSOR_SIZE | | height > MAX_CURSOR_SIZE {
return Err ( BadImage ::TooLarge { width , height } ) ;
}
if rgba . len ( ) % PIXEL_SIZE ! = 0 {
return Err ( BadImage ::ByteCountNotDivisibleBy4 {
byte_count : rgba . len ( ) ,
} ) ;
}
let pixel_count = ( rgba . len ( ) / PIXEL_SIZE ) as u64 ;
let width_x_height = width as u64 * height as u64 ;
if pixel_count ! = width_x_height {
return Err ( BadImage ::DimensionsVsPixelCount {
width ,
height ,
width_x_height ,
pixel_count ,
} ) ;
}
if hotspot_x > = width | | hotspot_y > = height {
return Err ( BadImage ::HotspotOutOfBounds {
width ,
height ,
hotspot_x ,
hotspot_y ,
} ) ;
}
Ok ( CursorImage {
rgba ,
width ,
height ,
hotspot_x ,
hotspot_y ,
} )
}
}
// Platforms that don't support cursors will export this as `PlatformCustomCursor`.
2023-12-23 16:12:29 +01:00
#[ derive(Debug, Clone, Hash, PartialEq, Eq) ]
2023-12-16 22:02:17 +02:00
pub ( crate ) struct NoCustomCursor ;
#[ allow(dead_code) ]
impl NoCustomCursor {
2023-12-23 16:12:29 +01:00
pub ( crate ) fn from_rgba (
2023-12-16 22:02:17 +02:00
rgba : Vec < u8 > ,
width : u16 ,
height : u16 ,
hotspot_x : u16 ,
hotspot_y : u16 ,
) -> Result < Self , BadImage > {
CursorImage ::from_rgba ( rgba , width , height , hotspot_x , hotspot_y ) ? ;
Ok ( Self )
}
2023-12-22 22:20:41 +01:00
2023-12-23 16:12:29 +01:00
fn build < T > ( self , _ : & platform_impl ::EventLoopWindowTarget < T > ) -> NoCustomCursor {
self
2023-12-22 22:20:41 +01:00
}
2023-12-16 22:02:17 +02:00
}