1 | use std::ffi::CString; |
2 | use std::iter; |
3 | |
4 | use x11rb::connection::Connection; |
5 | |
6 | use crate::window::CursorIcon; |
7 | |
8 | use super::*; |
9 | |
10 | impl 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 | |