1 use crate::window::CursorIcon;
2 
3 use super::*;
4 
5 impl XConnection {
set_cursor_icon(&self, window: ffi::Window, cursor: Option<CursorIcon>)6     pub fn set_cursor_icon(&self, window: ffi::Window, cursor: Option<CursorIcon>) {
7         let cursor = *self
8             .cursor_cache
9             .lock()
10             .entry(cursor)
11             .or_insert_with(|| self.get_cursor(cursor));
12 
13         self.update_cursor(window, cursor);
14     }
15 
create_empty_cursor(&self) -> ffi::Cursor16     fn create_empty_cursor(&self) -> ffi::Cursor {
17         let data = 0;
18         let pixmap = unsafe {
19             let screen = (self.xlib.XDefaultScreen)(self.display);
20             let window = (self.xlib.XRootWindow)(self.display, screen);
21             (self.xlib.XCreateBitmapFromData)(self.display, window, &data, 1, 1)
22         };
23 
24         if pixmap == 0 {
25             panic!("failed to allocate pixmap for cursor");
26         }
27 
28         unsafe {
29             // We don't care about this color, since it only fills bytes
30             // in the pixmap which are not 0 in the mask.
31             let mut dummy_color = MaybeUninit::uninit();
32             let cursor = (self.xlib.XCreatePixmapCursor)(
33                 self.display,
34                 pixmap,
35                 pixmap,
36                 dummy_color.as_mut_ptr(),
37                 dummy_color.as_mut_ptr(),
38                 0,
39                 0,
40             );
41             (self.xlib.XFreePixmap)(self.display, pixmap);
42 
43             cursor
44         }
45     }
46 
load_cursor(&self, name: &[u8]) -> ffi::Cursor47     fn load_cursor(&self, name: &[u8]) -> ffi::Cursor {
48         unsafe {
49             (self.xcursor.XcursorLibraryLoadCursor)(self.display, name.as_ptr() as *const c_char)
50         }
51     }
52 
load_first_existing_cursor(&self, names: &[&[u8]]) -> ffi::Cursor53     fn load_first_existing_cursor(&self, names: &[&[u8]]) -> ffi::Cursor {
54         for name in names.iter() {
55             let xcursor = self.load_cursor(name);
56             if xcursor != 0 {
57                 return xcursor;
58             }
59         }
60         0
61     }
62 
get_cursor(&self, cursor: Option<CursorIcon>) -> ffi::Cursor63     fn get_cursor(&self, cursor: Option<CursorIcon>) -> ffi::Cursor {
64         let cursor = match cursor {
65             Some(cursor) => cursor,
66             None => return self.create_empty_cursor(),
67         };
68 
69         let load = |name: &[u8]| self.load_cursor(name);
70 
71         let loadn = |names: &[&[u8]]| self.load_first_existing_cursor(names);
72 
73         // Try multiple names in some cases where the name
74         // differs on the desktop environments or themes.
75         //
76         // Try the better looking (or more suiting) names first.
77         match cursor {
78             CursorIcon::Alias => load(b"link\0"),
79             CursorIcon::Arrow => load(b"arrow\0"),
80             CursorIcon::Cell => load(b"plus\0"),
81             CursorIcon::Copy => load(b"copy\0"),
82             CursorIcon::Crosshair => load(b"crosshair\0"),
83             CursorIcon::Default => load(b"left_ptr\0"),
84             CursorIcon::Hand => loadn(&[b"hand2\0", b"hand1\0"]),
85             CursorIcon::Help => load(b"question_arrow\0"),
86             CursorIcon::Move => load(b"move\0"),
87             CursorIcon::Grab => loadn(&[b"openhand\0", b"grab\0"]),
88             CursorIcon::Grabbing => loadn(&[b"closedhand\0", b"grabbing\0"]),
89             CursorIcon::Progress => load(b"left_ptr_watch\0"),
90             CursorIcon::AllScroll => load(b"all-scroll\0"),
91             CursorIcon::ContextMenu => load(b"context-menu\0"),
92 
93             CursorIcon::NoDrop => loadn(&[b"no-drop\0", b"circle\0"]),
94             CursorIcon::NotAllowed => load(b"crossed_circle\0"),
95 
96             // Resize cursors
97             CursorIcon::EResize => load(b"right_side\0"),
98             CursorIcon::NResize => load(b"top_side\0"),
99             CursorIcon::NeResize => load(b"top_right_corner\0"),
100             CursorIcon::NwResize => load(b"top_left_corner\0"),
101             CursorIcon::SResize => load(b"bottom_side\0"),
102             CursorIcon::SeResize => load(b"bottom_right_corner\0"),
103             CursorIcon::SwResize => load(b"bottom_left_corner\0"),
104             CursorIcon::WResize => load(b"left_side\0"),
105             CursorIcon::EwResize => load(b"h_double_arrow\0"),
106             CursorIcon::NsResize => load(b"v_double_arrow\0"),
107             CursorIcon::NwseResize => loadn(&[b"bd_double_arrow\0", b"size_bdiag\0"]),
108             CursorIcon::NeswResize => loadn(&[b"fd_double_arrow\0", b"size_fdiag\0"]),
109             CursorIcon::ColResize => loadn(&[b"split_h\0", b"h_double_arrow\0"]),
110             CursorIcon::RowResize => loadn(&[b"split_v\0", b"v_double_arrow\0"]),
111 
112             CursorIcon::Text => loadn(&[b"text\0", b"xterm\0"]),
113             CursorIcon::VerticalText => load(b"vertical-text\0"),
114 
115             CursorIcon::Wait => load(b"watch\0"),
116 
117             CursorIcon::ZoomIn => load(b"zoom-in\0"),
118             CursorIcon::ZoomOut => load(b"zoom-out\0"),
119         }
120     }
121 
update_cursor(&self, window: ffi::Window, cursor: ffi::Cursor)122     fn update_cursor(&self, window: ffi::Window, cursor: ffi::Cursor) {
123         unsafe {
124             (self.xlib.XDefineCursor)(self.display, window, cursor);
125 
126             self.flush_requests().expect("Failed to set the cursor");
127         }
128     }
129 }
130