xcb_util_cursor/
lib.rs

1//! Safe [libxcb-cursor](https://gitlab.freedesktop.org/xorg/lib/libxcb-cursor) bindings for rust.
2//! You will need [xcb](https://crates.io/crates/xcb).
3//!
4//! # Example
5//!
6//! ```
7//! use xcb_util_cursor::{Cursor, CursorContext};
8//!
9//! let (connection, _) = xcb::Connection::connect(None).unwrap();
10//! let setup = connection.get_setup();
11//! let screen = setup.roots().next().unwrap();
12//!
13//! let cursor_context = CursorContext::new(&connection, screen).unwrap();
14//!
15//! let left_ptr = cursor_context.load_cursor(Cursor::LeftPtr);
16//! ```
17
18#![deny(missing_docs)]
19#![doc(html_root_url = "https://docs.rs/xcb-util-cursor/")]
20
21use std::{ffi::CString, fmt, marker, ptr};
22
23use xcb::{x, Xid, XidNew};
24use xcb_util_cursor_sys as ffi;
25
26/// This enum provides all possible cursors. [reference](https://www.oreilly.com/library/view/x-window-system/9780937175149/ChapterD.html)
27pub enum Cursor {
28    /// X_cursor
29    XCursor,
30    /// arrow
31    Arrow,
32    /// based_arrow_down
33    BaseArrowDown,
34    /// based_arrow_up
35    BasedArrowUp,
36    /// boat
37    Boat,
38    /// bogosity
39    Bogosity,
40    /// bottom_left_corner
41    BottomLeftCorner,
42    /// bottom_right_corner
43    BottomRightCorner,
44    /// bottom_side
45    BottomSide,
46    /// bottom_tee
47    BottomTee,
48    /// box_spiral
49    BoxSpiral,
50    /// center_ptr
51    CenterPtr,
52    /// circle
53    Circle,
54    /// clock
55    Clock,
56    /// coffee_mug
57    CoffeeMug,
58    /// cross
59    Cross,
60    /// cross_reverse
61    CrossReverse,
62    /// crosshair
63    Crosshair,
64    /// diamond_cross
65    DiamongCross,
66    /// dot
67    Dot,
68    /// dotbox
69    Dotbox,
70    /// double_arrow
71    DoubleArrow,
72    /// draft_large
73    DraftLarge,
74    /// draft_small
75    DrawftSmall,
76    /// draped_box
77    DrapedBox,
78    /// exchange
79    Exchange,
80    /// fleur
81    Fleur,
82    /// gobbler
83    Gobbler,
84    /// gumby
85    Gumby,
86    /// hand1
87    Hand1,
88    /// hand2
89    Hand2,
90    /// heart
91    Heart,
92    /// icon
93    Icon,
94    /// iron_cross
95    IronCross,
96    /// left_ptr
97    LeftPtr,
98    /// left_side
99    LeftSide,
100    /// left_tee
101    LeftTee,
102    /// leftbutton
103    Leftbutton,
104    /// ll_angle
105    LlAngle,
106    /// lr_angle
107    LrAngle,
108    /// man
109    Man,
110    /// middlebutton
111    Middlebutton,
112    /// mouse
113    Mouse,
114    /// pencil
115    Pencil,
116    /// pirate
117    Pirate,
118    /// plus
119    Plus,
120    /// question_arrow
121    QuestionArrow,
122    /// right_ptr
123    RightPtr,
124    /// right_side
125    RightSide,
126    /// right_tee
127    RightTee,
128    /// rightbutton
129    Rightbutton,
130    /// rtl_logo
131    RtlLogo,
132    /// sailboat
133    Sailboat,
134    /// sb_down_arrow
135    SbDownArrow,
136    /// sb_h_double_arrow
137    SbHDoubleArrow,
138    /// sb_left_arrow
139    SbLeftArrow,
140    /// sb_right_arrow
141    SbRightArrow,
142    /// sb_up_arrow
143    SbUpArrow,
144    /// sb_v_double_arrow
145    SbVDoubleArrow,
146    /// shuttle
147    Shuttle,
148    /// sizing
149    Sizing,
150    /// spider
151    Spider,
152    /// spraycan
153    Spraycan,
154    /// star
155    Star,
156    /// target
157    Target,
158    /// tcross
159    Tcross,
160    /// top_left_arrow
161    TopLeftArrow,
162    /// top_left_corner
163    TopLeftCorner,
164    /// top_right_corner
165    TopRightCorner,
166    /// top_side
167    TopSide,
168    /// top_tee
169    TopTee,
170    /// trek
171    Trek,
172    /// ul_angle
173    UlAngle,
174    /// umbrella
175    Umbrella,
176    /// ur_angle
177    UrAngle,
178    /// watch
179    Watch,
180    /// xterm
181    Xterm,
182    /// Custom cursor name
183    Custom(String),
184}
185
186impl fmt::Display for Cursor {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        let s = match self {
189            Cursor::XCursor => "X_cursor",
190            Cursor::Arrow => "arrow",
191            Cursor::BaseArrowDown => "based_arrow_down",
192            Cursor::BasedArrowUp => "based_arrow_up",
193            Cursor::Boat => "boat",
194            Cursor::Bogosity => "bogosity",
195            Cursor::BottomLeftCorner => "bottom_left_corner",
196            Cursor::BottomRightCorner => "bottom_right_corner",
197            Cursor::BottomSide => "bottom_side",
198            Cursor::BottomTee => "bottom_tee",
199            Cursor::BoxSpiral => "box_spiral",
200            Cursor::CenterPtr => "center_ptr",
201            Cursor::Circle => "circle",
202            Cursor::Clock => "clock",
203            Cursor::CoffeeMug => "coffee_mug",
204            Cursor::Cross => "cross",
205            Cursor::CrossReverse => "cross_reverse",
206            Cursor::Crosshair => "crosshair",
207            Cursor::DiamongCross => "diamond_cross",
208            Cursor::Dot => "dot",
209            Cursor::Dotbox => "dotbox",
210            Cursor::DoubleArrow => "double_arrow",
211            Cursor::DraftLarge => "draft_large",
212            Cursor::DrawftSmall => "draft_small",
213            Cursor::DrapedBox => "draped_box",
214            Cursor::Exchange => "exchange",
215            Cursor::Fleur => "fleur",
216            Cursor::Gobbler => "gobbler",
217            Cursor::Gumby => "gumby",
218            Cursor::Hand1 => "hand1",
219            Cursor::Hand2 => "hand2",
220            Cursor::Heart => "heart",
221            Cursor::Icon => "icon",
222            Cursor::IronCross => "iron_cross",
223            Cursor::LeftPtr => "left_ptr",
224            Cursor::LeftSide => "left_side",
225            Cursor::LeftTee => "left_tee",
226            Cursor::Leftbutton => "leftbutton",
227            Cursor::LlAngle => "ll_angle",
228            Cursor::LrAngle => "lr_angle",
229            Cursor::Man => "man",
230            Cursor::Middlebutton => "middlebutton",
231            Cursor::Mouse => "mouse",
232            Cursor::Pencil => "pencil",
233            Cursor::Pirate => "pirate",
234            Cursor::Plus => "plus",
235            Cursor::QuestionArrow => "question_arrow",
236            Cursor::RightPtr => "right_ptr",
237            Cursor::RightSide => "right_side",
238            Cursor::RightTee => "right_tee",
239            Cursor::Rightbutton => "rightbutton",
240            Cursor::RtlLogo => "rtl_logo",
241            Cursor::Sailboat => "sailboat",
242            Cursor::SbDownArrow => "sb_down_arrow",
243            Cursor::SbHDoubleArrow => "sb_h_double_arrow",
244            Cursor::SbLeftArrow => "sb_left_arrow",
245            Cursor::SbRightArrow => "sb_right_arrow",
246            Cursor::SbUpArrow => "sb_up_arrow",
247            Cursor::SbVDoubleArrow => "sb_v_double_arrow",
248            Cursor::Shuttle => "shuttle",
249            Cursor::Sizing => "sizing",
250            Cursor::Spider => "spider",
251            Cursor::Spraycan => "spraycan",
252            Cursor::Star => "star",
253            Cursor::Target => "target",
254            Cursor::Tcross => "tcross",
255            Cursor::TopLeftArrow => "top_left_arrow",
256            Cursor::TopLeftCorner => "top_left_corner",
257            Cursor::TopRightCorner => "top_right_corner",
258            Cursor::TopSide => "top_side",
259            Cursor::TopTee => "top_tee",
260            Cursor::Trek => "trek",
261            Cursor::UlAngle => "ul_angle",
262            Cursor::Umbrella => "umbrella",
263            Cursor::UrAngle => "ur_angle",
264            Cursor::Watch => "watch",
265            Cursor::Xterm => "xterm",
266            Cursor::Custom(s) => s,
267        };
268
269        write!(f, "{s}")
270    }
271}
272
273/// Wrapper sctruct for xcb_cursor_context_t that handles creation and freeing.
274pub struct CursorContext<'a> {
275    ctx: *mut ffi::xcb_cursor_context_t,
276    phantom: marker::PhantomData<&'a xcb::Connection>,
277}
278
279impl<'a> CursorContext<'a> {
280    /// Create a new cursor context.
281    pub fn new(connection: &'a xcb::Connection, screen: &x::Screen) -> Option<Self> {
282        let mut screen = ffi::xcb_screen_t {
283            root: screen.root().resource_id(),
284            default_colormap: screen.default_colormap().resource_id(),
285            white_pixel: screen.white_pixel(),
286            black_pixel: screen.black_pixel(),
287            current_input_masks: screen.current_input_masks().bits(),
288            width_in_pixels: screen.width_in_pixels(),
289            height_in_pixels: screen.height_in_pixels(),
290            width_in_millimeters: screen.width_in_millimeters(),
291            height_in_millimeters: screen.height_in_millimeters(),
292            min_installed_maps: screen.min_installed_maps(),
293            max_installed_maps: screen.max_installed_maps(),
294            root_visual: screen.root_visual(),
295            backing_stores: screen.backing_stores() as u8,
296            save_unders: screen.save_unders() as u8,
297            root_depth: screen.root_depth(),
298            allowed_depths_len: screen.allowed_depths().count() as u8,
299        };
300
301        let mut ctx = ptr::null_mut();
302
303        let res = unsafe {
304            ffi::xcb_cursor_context_new(connection.get_raw_conn(), &mut screen, &mut ctx)
305        };
306
307        if res != 0 {
308            None
309        } else {
310            Some(Self {
311                ctx,
312                phantom: marker::PhantomData,
313            })
314        }
315    }
316
317    /// Loads a cursor. Returns CURSOR_NONE on error.
318    pub fn load_cursor(&self, cursor: Cursor) -> x::Cursor {
319        let c_str = CString::new(cursor.to_string()).unwrap();
320
321        unsafe {
322            let cursor = ffi::xcb_cursor_load_cursor(self.ctx, c_str.as_ptr());
323            x::Cursor::new(cursor)
324        }
325    }
326}
327
328impl<'a> Drop for CursorContext<'a> {
329    fn drop(&mut self) {
330        unsafe {
331            ffi::xcb_cursor_context_free(self.ctx);
332        }
333    }
334}