1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
//! Safe [libxcb-cursor](https://gitlab.freedesktop.org/xorg/lib/libxcb-cursor) bindings for rust.
//! You will need [xcb](https://crates.io/crates/xcb).
//!
//! # Example
//!
//! ```
//! use xcb_util_cursor::{Cursor, CursorContext};
//!
//! let (connection, _) = xcb::Connection::connect(None).unwrap();
//! let setup = connection.get_setup();
//! let screen = setup.roots().next().unwrap();
//!
//! let cursor_context = CursorContext::new(&connection, screen).unwrap();
//!
//! let left_ptr = cursor_context.load_cursor(Cursor::LeftPtr);
//! ```

#![deny(missing_docs)]
#![doc(html_root_url = "https://docs.rs/xcb-util-cursor/")]

use std::{ffi, fmt, ptr};

use xcb::{x, Xid, XidNew};
use xcb_util_cursor_sys as sys;

/// This enum provides all possible cursors. [reference](https://www.oreilly.com/library/view/x-window-system/9780937175149/ChapterD.html)
pub enum Cursor {
    /// X_cursor
    XCursor,
    /// arrow
    Arrow,
    /// based_arrow_down
    BaseArrowDown,
    /// based_arrow_up
    BasedArrowUp,
    /// boat
    Boat,
    /// bogosity
    Bogosity,
    /// bottom_left_corner
    BottomLeftCorner,
    /// bottom_right_corner
    BottomRightCorner,
    /// bottom_side
    BottomSide,
    /// bottom_tee
    BottomTee,
    /// box_spiral
    BoxSpiral,
    /// center_ptr
    CenterPtr,
    /// circle
    Circle,
    /// clock
    Clock,
    /// coffee_mug
    CoffeeMug,
    /// cross
    Cross,
    /// cross_reverse
    CrossReverse,
    /// crosshair
    Crosshair,
    /// diamond_cross
    DiamongCross,
    /// dot
    Dot,
    /// dotbox
    Dotbox,
    /// double_arrow
    DoubleArrow,
    /// draft_large
    DraftLarge,
    /// draft_small
    DrawftSmall,
    /// draped_box
    DrapedBox,
    /// exchange
    Exchange,
    /// fleur
    Fleur,
    /// gobbler
    Gobbler,
    /// gumby
    Gumby,
    /// hand1
    Hand1,
    /// hand2
    Hand2,
    /// heart
    Heart,
    /// icon
    Icon,
    /// iron_cross
    IronCross,
    /// left_ptr
    LeftPtr,
    /// left_side
    LeftSide,
    /// left_tee
    LeftTee,
    /// leftbutton
    Leftbutton,
    /// ll_angle
    LlAngle,
    /// lr_angle
    LrAngle,
    /// man
    Man,
    /// middlebutton
    Middlebutton,
    /// mouse
    Mouse,
    /// pencil
    Pencil,
    /// pirate
    Pirate,
    /// plus
    Plus,
    /// question_arrow
    QuestionArrow,
    /// right_ptr
    RightPtr,
    /// right_side
    RightSide,
    /// right_tee
    RightTee,
    /// rightbutton
    Rightbutton,
    /// rtl_logo
    RtlLogo,
    /// sailboat
    Sailboat,
    /// sb_down_arrow
    SbDownArrow,
    /// sb_h_double_arrow
    SbHDoubleArrow,
    /// sb_left_arrow
    SbLeftArrow,
    /// sb_right_arrow
    SbRightArrow,
    /// sb_up_arrow
    SbUpArrow,
    /// sb_v_double_arrow
    SbVDoubleArrow,
    /// shuttle
    Shuttle,
    /// sizing
    Sizing,
    /// spider
    Spider,
    /// spraycan
    Spraycan,
    /// star
    Star,
    /// target
    Target,
    /// tcross
    Tcross,
    /// top_left_arrow
    TopLeftArrow,
    /// top_left_corner
    TopLeftCorner,
    /// top_right_corner
    TopRightCorner,
    /// top_side
    TopSide,
    /// top_tee
    TopTee,
    /// trek
    Trek,
    /// ul_angle
    UlAngle,
    /// umbrella
    Umbrella,
    /// ur_angle
    UrAngle,
    /// watch
    Watch,
    /// xterm
    Xterm,
    /// Custom cursor name
    Custom(String),
}

impl fmt::Display for Cursor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = match self {
            Cursor::XCursor => "X_cursor",
            Cursor::Arrow => "arrow",
            Cursor::BaseArrowDown => "based_arrow_down",
            Cursor::BasedArrowUp => "based_arrow_up",
            Cursor::Boat => "boat",
            Cursor::Bogosity => "bogosity",
            Cursor::BottomLeftCorner => "bottom_left_corner",
            Cursor::BottomRightCorner => "bottom_right_corner",
            Cursor::BottomSide => "bottom_side",
            Cursor::BottomTee => "bottom_tee",
            Cursor::BoxSpiral => "box_spiral",
            Cursor::CenterPtr => "center_ptr",
            Cursor::Circle => "circle",
            Cursor::Clock => "clock",
            Cursor::CoffeeMug => "coffee_mug",
            Cursor::Cross => "cross",
            Cursor::CrossReverse => "cross_reverse",
            Cursor::Crosshair => "crosshair",
            Cursor::DiamongCross => "diamond_cross",
            Cursor::Dot => "dot",
            Cursor::Dotbox => "dotbox",
            Cursor::DoubleArrow => "double_arrow",
            Cursor::DraftLarge => "draft_large",
            Cursor::DrawftSmall => "draft_small",
            Cursor::DrapedBox => "draped_box",
            Cursor::Exchange => "exchange",
            Cursor::Fleur => "fleur",
            Cursor::Gobbler => "gobbler",
            Cursor::Gumby => "gumby",
            Cursor::Hand1 => "hand1",
            Cursor::Hand2 => "hand2",
            Cursor::Heart => "heart",
            Cursor::Icon => "icon",
            Cursor::IronCross => "iron_cross",
            Cursor::LeftPtr => "left_ptr",
            Cursor::LeftSide => "left_side",
            Cursor::LeftTee => "left_tee",
            Cursor::Leftbutton => "leftbutton",
            Cursor::LlAngle => "ll_angle",
            Cursor::LrAngle => "lr_angle",
            Cursor::Man => "man",
            Cursor::Middlebutton => "middlebutton",
            Cursor::Mouse => "mouse",
            Cursor::Pencil => "pencil",
            Cursor::Pirate => "pirate",
            Cursor::Plus => "plus",
            Cursor::QuestionArrow => "question_arrow",
            Cursor::RightPtr => "right_ptr",
            Cursor::RightSide => "right_side",
            Cursor::RightTee => "right_tee",
            Cursor::Rightbutton => "rightbutton",
            Cursor::RtlLogo => "rtl_logo",
            Cursor::Sailboat => "sailboat",
            Cursor::SbDownArrow => "sb_down_arrow",
            Cursor::SbHDoubleArrow => "sb_h_double_arrow",
            Cursor::SbLeftArrow => "sb_left_arrow",
            Cursor::SbRightArrow => "sb_right_arrow",
            Cursor::SbUpArrow => "sb_up_arrow",
            Cursor::SbVDoubleArrow => "sb_v_double_arrow",
            Cursor::Shuttle => "shuttle",
            Cursor::Sizing => "sizing",
            Cursor::Spider => "spider",
            Cursor::Spraycan => "spraycan",
            Cursor::Star => "star",
            Cursor::Target => "target",
            Cursor::Tcross => "tcross",
            Cursor::TopLeftArrow => "top_left_arrow",
            Cursor::TopLeftCorner => "top_left_corner",
            Cursor::TopRightCorner => "top_right_corner",
            Cursor::TopSide => "top_side",
            Cursor::TopTee => "top_tee",
            Cursor::Trek => "trek",
            Cursor::UlAngle => "ul_angle",
            Cursor::Umbrella => "umbrella",
            Cursor::UrAngle => "ur_angle",
            Cursor::Watch => "watch",
            Cursor::Xterm => "xterm",
            Cursor::Custom(s) => s,
        };

        write!(f, "{s}")
    }
}

/// Wrapper sctruct for xcb_cursor_context_t that handles creation and freeing.
pub struct CursorContext {
    inner: ptr::NonNull<sys::xcb_cursor_context_t>,
}

impl CursorContext {
    /// Create a new cursor context.
    pub fn new(connection: &xcb::Connection, screen: &x::Screen) -> Option<Self> {
        let mut screen = sys::xcb_screen_t {
            root: screen.root().resource_id(),
            default_colormap: screen.default_colormap().resource_id(),
            white_pixel: screen.white_pixel(),
            black_pixel: screen.black_pixel(),
            current_input_masks: screen.current_input_masks().bits(),
            width_in_pixels: screen.width_in_pixels(),
            height_in_pixels: screen.height_in_pixels(),
            width_in_millimeters: screen.width_in_millimeters(),
            height_in_millimeters: screen.height_in_millimeters(),
            min_installed_maps: screen.min_installed_maps(),
            max_installed_maps: screen.max_installed_maps(),
            root_visual: screen.root_visual(),
            backing_stores: screen.backing_stores() as u8,
            save_unders: screen.save_unders() as u8,
            root_depth: screen.root_depth(),
            allowed_depths_len: screen.allowed_depths().count() as u8,
        };

        let mut ctx = ptr::null_mut();

        unsafe {
            sys::xcb_cursor_context_new(
                connection.get_raw_conn() as *mut sys::xcb_connection_t,
                &mut screen,
                &mut ctx,
            )
        };

        ptr::NonNull::new(ctx).map(|ctx| Self { inner: ctx })
    }

    /// Loads a cursor. Returns CURSOR_NONE on error.
    pub fn load_cursor(&self, cursor: Cursor) -> x::Cursor {
        let c_str = ffi::CString::new(cursor.to_string()).unwrap();

        unsafe {
            let cursor = sys::xcb_cursor_load_cursor(self.inner.as_ptr(), c_str.as_ptr());
            x::Cursor::new(cursor)
        }
    }
}

impl Drop for CursorContext {
    fn drop(&mut self) {
        unsafe {
            sys::xcb_cursor_context_free(self.inner.as_ptr());
        }
    }
}