winit/platform_impl/linux/x11/util/
cursor.rs

1use std::ffi::CString;
2use std::hash::{Hash, Hasher};
3use std::sync::Arc;
4use std::{iter, slice};
5
6use x11rb::connection::Connection;
7
8use crate::platform_impl::PlatformCustomCursorSource;
9use crate::window::CursorIcon;
10
11use super::super::ActiveEventLoop;
12use super::*;
13
14impl XConnection {
15    pub fn set_cursor_icon(&self, window: xproto::Window, cursor: Option<CursorIcon>) {
16        let cursor = *self
17            .cursor_cache
18            .lock()
19            .unwrap()
20            .entry(cursor)
21            .or_insert_with(|| self.get_cursor(cursor));
22
23        self.update_cursor(window, cursor).expect("Failed to set cursor");
24    }
25
26    pub(crate) fn set_custom_cursor(&self, window: xproto::Window, cursor: &CustomCursor) {
27        self.update_cursor(window, cursor.inner.cursor).expect("Failed to set cursor");
28    }
29
30    fn create_empty_cursor(&self) -> ffi::Cursor {
31        let data = 0;
32        let pixmap = unsafe {
33            let screen = (self.xlib.XDefaultScreen)(self.display);
34            let window = (self.xlib.XRootWindow)(self.display, screen);
35            (self.xlib.XCreateBitmapFromData)(self.display, window, &data, 1, 1)
36        };
37
38        if pixmap == 0 {
39            panic!("failed to allocate pixmap for cursor");
40        }
41
42        unsafe {
43            // We don't care about this color, since it only fills bytes
44            // in the pixmap which are not 0 in the mask.
45            let mut dummy_color = MaybeUninit::uninit();
46            let cursor = (self.xlib.XCreatePixmapCursor)(
47                self.display,
48                pixmap,
49                pixmap,
50                dummy_color.as_mut_ptr(),
51                dummy_color.as_mut_ptr(),
52                0,
53                0,
54            );
55            (self.xlib.XFreePixmap)(self.display, pixmap);
56
57            cursor
58        }
59    }
60
61    fn get_cursor(&self, cursor: Option<CursorIcon>) -> ffi::Cursor {
62        let cursor = match cursor {
63            Some(cursor) => cursor,
64            None => return self.create_empty_cursor(),
65        };
66
67        let mut xcursor = 0;
68        for &name in iter::once(&cursor.name()).chain(cursor.alt_names().iter()) {
69            let name = CString::new(name).unwrap();
70            xcursor = unsafe {
71                (self.xcursor.XcursorLibraryLoadCursor)(
72                    self.display,
73                    name.as_ptr() as *const c_char,
74                )
75            };
76
77            if xcursor != 0 {
78                break;
79            }
80        }
81
82        xcursor
83    }
84
85    fn update_cursor(&self, window: xproto::Window, cursor: ffi::Cursor) -> Result<(), X11Error> {
86        self.xcb_connection()
87            .change_window_attributes(
88                window,
89                &xproto::ChangeWindowAttributesAux::new().cursor(cursor as xproto::Cursor),
90            )?
91            .ignore_error();
92
93        self.xcb_connection().flush()?;
94        Ok(())
95    }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
99pub enum SelectedCursor {
100    Custom(CustomCursor),
101    Named(CursorIcon),
102}
103
104#[derive(Debug, Clone)]
105pub struct CustomCursor {
106    inner: Arc<CustomCursorInner>,
107}
108
109impl Hash for CustomCursor {
110    fn hash<H: Hasher>(&self, state: &mut H) {
111        Arc::as_ptr(&self.inner).hash(state);
112    }
113}
114
115impl PartialEq for CustomCursor {
116    fn eq(&self, other: &Self) -> bool {
117        Arc::ptr_eq(&self.inner, &other.inner)
118    }
119}
120
121impl Eq for CustomCursor {}
122
123impl CustomCursor {
124    pub(crate) fn new(
125        event_loop: &ActiveEventLoop,
126        cursor: PlatformCustomCursorSource,
127    ) -> CustomCursor {
128        unsafe {
129            let ximage = (event_loop.xconn.xcursor.XcursorImageCreate)(
130                cursor.0.width as i32,
131                cursor.0.height as i32,
132            );
133            if ximage.is_null() {
134                panic!("failed to allocate cursor image");
135            }
136            (*ximage).xhot = cursor.0.hotspot_x as u32;
137            (*ximage).yhot = cursor.0.hotspot_y as u32;
138            (*ximage).delay = 0;
139
140            let dst = slice::from_raw_parts_mut((*ximage).pixels, cursor.0.rgba.len() / 4);
141            for (dst, chunk) in dst.iter_mut().zip(cursor.0.rgba.chunks_exact(4)) {
142                *dst = (chunk[0] as u32) << 16
143                    | (chunk[1] as u32) << 8
144                    | (chunk[2] as u32)
145                    | (chunk[3] as u32) << 24;
146            }
147
148            let cursor =
149                (event_loop.xconn.xcursor.XcursorImageLoadCursor)(event_loop.xconn.display, ximage);
150            (event_loop.xconn.xcursor.XcursorImageDestroy)(ximage);
151            Self { inner: Arc::new(CustomCursorInner { xconn: event_loop.xconn.clone(), cursor }) }
152        }
153    }
154}
155
156#[derive(Debug)]
157struct CustomCursorInner {
158    xconn: Arc<XConnection>,
159    cursor: ffi::Cursor,
160}
161
162impl Drop for CustomCursorInner {
163    fn drop(&mut self) {
164        unsafe {
165            (self.xconn.xlib.XFreeCursor)(self.xconn.display, self.cursor);
166        }
167    }
168}
169
170impl Default for SelectedCursor {
171    fn default() -> Self {
172        SelectedCursor::Named(Default::default())
173    }
174}