rich_sdl2_rust/video/window/
cursor.rs

1//! Cursor control on a window.
2
3use static_assertions::assert_not_impl_all;
4use std::marker::PhantomData;
5use std::ptr::NonNull;
6
7use crate::{bind, geo::Point, surface::Surface, Result, Sdl, SdlError};
8
9use super::Window;
10
11/// A kind of the system cursor.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13#[non_exhaustive]
14pub enum SystemCursorKind {
15    /// The arrow cursor.
16    Arrow,
17    /// The I beam cursor.
18    IBeam,
19    /// The waiting cursor.
20    Wait,
21    /// The crosshair cursor.
22    Crosshair,
23    /// The waiting cursor with arrow.
24    WaitArrow,
25    /// The resizing cursor between north west and south east.
26    SizeNwse,
27    /// The resizing cursor between north east and south west.
28    SizeNesw,
29    /// The resizing cursor between east and west.
30    SizeWe,
31    /// The resizing cursor between north and south.
32    SizeNs,
33    /// The resizing cursor for all directions.
34    SizeAll,
35    /// The prohibiting cursor.
36    No,
37    /// The hand cursor.
38    Hand,
39}
40
41impl SystemCursorKind {
42    pub(crate) fn as_raw(self) -> bind::SDL_SystemCursor {
43        match self {
44            SystemCursorKind::Arrow => bind::SDL_SYSTEM_CURSOR_ARROW,
45            SystemCursorKind::IBeam => bind::SDL_SYSTEM_CURSOR_IBEAM,
46            SystemCursorKind::Wait => bind::SDL_SYSTEM_CURSOR_WAIT,
47            SystemCursorKind::Crosshair => bind::SDL_SYSTEM_CURSOR_CROSSHAIR,
48            SystemCursorKind::WaitArrow => bind::SDL_SYSTEM_CURSOR_WAITARROW,
49            SystemCursorKind::SizeNwse => bind::SDL_SYSTEM_CURSOR_SIZENWSE,
50            SystemCursorKind::SizeNesw => bind::SDL_SYSTEM_CURSOR_SIZENESW,
51            SystemCursorKind::SizeWe => bind::SDL_SYSTEM_CURSOR_SIZEWE,
52            SystemCursorKind::SizeNs => bind::SDL_SYSTEM_CURSOR_SIZENS,
53            SystemCursorKind::SizeAll => bind::SDL_SYSTEM_CURSOR_SIZEALL,
54            SystemCursorKind::No => bind::SDL_SYSTEM_CURSOR_NO,
55            SystemCursorKind::Hand => bind::SDL_SYSTEM_CURSOR_HAND,
56        }
57    }
58}
59
60/// A system cursor controller.
61pub struct Cursor<'window> {
62    cursor: NonNull<bind::SDL_Cursor>,
63    window: PhantomData<&'window ()>,
64}
65
66impl std::fmt::Debug for Cursor<'_> {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        f.debug_struct("Cursor").finish_non_exhaustive()
69    }
70}
71
72assert_not_impl_all!(Cursor: Send, Sync);
73
74impl<'window> Cursor<'window> {
75    /// Constructs a system cursor from `kind`.
76    ///
77    /// # Errors
78    ///
79    /// Returns `Err` if the feature of cursor is unsupported.
80    pub fn system(_: &'window Window, kind: SystemCursorKind) -> Result<Self> {
81        let cursor = unsafe { bind::SDL_CreateSystemCursor(kind.as_raw()) };
82        let cursor = NonNull::new(cursor).ok_or(SdlError::UnsupportedFeature)?;
83        Ok(Self {
84            cursor,
85            window: PhantomData,
86        })
87    }
88
89    /// Constructs a colored cursor from surface and hot spot point.
90    ///
91    /// # Errors
92    ///
93    /// Returns `Err` if coloring a cursor is unsupported.
94    pub fn colored(_: &'window Window, surface: &impl Surface, hot_spot: Point) -> Result<Self> {
95        let cursor = unsafe {
96            bind::SDL_CreateColorCursor(surface.as_ptr().as_ptr(), hot_spot.x, hot_spot.y)
97        };
98        let cursor = NonNull::new(cursor).ok_or_else(|| SdlError::Others { msg: Sdl::error() })?;
99        Ok(Self {
100            cursor,
101            window: PhantomData,
102        })
103    }
104
105    /// Constructs a completely customized color from data, mask and hot spot point.
106    ///
107    /// # Errors
108    ///
109    /// Returns `Err` if failed to create a custom cursor.
110    pub fn customized(
111        _: &'window Window,
112        data: &[u8],
113        mask: &[u8],
114        hot_spot: Point,
115    ) -> Result<Self> {
116        debug_assert_eq!(data.len(), mask.len());
117        let width_height = (data.len() / 4) as i32;
118        let cursor = unsafe {
119            bind::SDL_CreateCursor(
120                data.as_ptr(),
121                mask.as_ptr(),
122                width_height,
123                width_height,
124                hot_spot.x,
125                hot_spot.y,
126            )
127        };
128        let cursor = NonNull::new(cursor).ok_or_else(|| SdlError::Others { msg: Sdl::error() })?;
129        Ok(Self {
130            cursor,
131            window: PhantomData,
132        })
133    }
134
135    /// Constructs a default cursor, or `None` if unavailable.
136    #[must_use]
137    pub fn default(_: &'window Window) -> Option<Self> {
138        NonNull::new(unsafe { bind::SDL_GetDefaultCursor() }).map(|cursor| Self {
139            cursor,
140            window: PhantomData,
141        })
142    }
143
144    /// Sets the cursor to the current.
145    pub fn set(&self) {
146        unsafe { bind::SDL_SetCursor(self.cursor.as_ptr()) }
147    }
148
149    /// Redraws a cursor.
150    pub fn redraw(&self) {
151        unsafe { bind::SDL_SetCursor(std::ptr::null_mut()) }
152    }
153}
154
155impl Drop for Cursor<'_> {
156    fn drop(&mut self) {
157        unsafe { bind::SDL_FreeCursor(self.cursor.as_ptr()) }
158    }
159}