1use std::ffi::CString;
2use std::iter;
3
4use x11rb::connection::Connection;
5
6use crate::window::CursorIcon;
7
8use super::*;
9
10impl XConnection {
11 pub fn set_cursor_icon(&self, window: xproto::Window, cursor: Option<CursorIcon>) {
12 let cursor = *self
13 .cursor_cache
14 .lock()
15 .unwrap()
16 .entry(cursor)
17 .or_insert_with(|| self.get_cursor(cursor));
18
19 self.update_cursor(window, cursor)
20 .expect("Failed to set cursor");
21 }
22
23 fn create_empty_cursor(&self) -> ffi::Cursor {
24 let data = 0;
25 let pixmap = unsafe {
26 let screen = (self.xlib.XDefaultScreen)(self.display);
27 let window = (self.xlib.XRootWindow)(self.display, screen);
28 (self.xlib.XCreateBitmapFromData)(self.display, window, &data, 1, 1)
29 };
30
31 if pixmap == 0 {
32 panic!("failed to allocate pixmap for cursor");
33 }
34
35 unsafe {
36 // We don't care about this color, since it only fills bytes
37 // in the pixmap which are not 0 in the mask.
38 let mut dummy_color = MaybeUninit::uninit();
39 let cursor = (self.xlib.XCreatePixmapCursor)(
40 self.display,
41 pixmap,
42 pixmap,
43 dummy_color.as_mut_ptr(),
44 dummy_color.as_mut_ptr(),
45 0,
46 0,
47 );
48 (self.xlib.XFreePixmap)(self.display, pixmap);
49
50 cursor
51 }
52 }
53
54 fn get_cursor(&self, cursor: Option<CursorIcon>) -> ffi::Cursor {
55 let cursor = match cursor {
56 Some(cursor) => cursor,
57 None => return self.create_empty_cursor(),
58 };
59
60 let mut xcursor = 0;
61 for &name in iter::once(&cursor.name()).chain(cursor.alt_names().iter()) {
62 let name = CString::new(name).unwrap();
63 xcursor = unsafe {
64 (self.xcursor.XcursorLibraryLoadCursor)(
65 self.display,
66 name.as_ptr() as *const c_char,
67 )
68 };
69
70 if xcursor != 0 {
71 break;
72 }
73 }
74
75 xcursor
76 }
77
78 fn update_cursor(&self, window: xproto::Window, cursor: ffi::Cursor) -> Result<(), X11Error> {
79 self.xcb_connection()
80 .change_window_attributes(
81 window,
82 &xproto::ChangeWindowAttributesAux::new().cursor(cursor as xproto::Cursor),
83 )?
84 .ignore_error();
85
86 self.xcb_connection().flush()?;
87 Ok(())
88 }
89}
90