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 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}