#![deny(missing_docs)]
#![doc(html_root_url = "https://docs.rs/xcb-util-cursor/")]
use std::{ffi, ptr};
use anyhow::Result;
use strum::Display;
use xcb::{x, Xid, XidNew};
use xcb_util_cursor_sys as sys;
#[derive(Display)]
pub enum Cursor {
#[strum(serialize = "X_cursor")]
XCursor,
#[strum(serialize = "arrow")]
Arrow,
#[strum(serialize = "based_arrow_down")]
BaseArrowDown,
#[strum(serialize = "based_arrow_up")]
BasedArrowUp,
#[strum(serialize = "boat")]
Boat,
#[strum(serialize = "bogosity")]
Bogosity,
#[strum(serialize = "bottom_left_corner")]
BottomLeftCorner,
#[strum(serialize = "bottom_right_corner")]
BottomRightCorner,
#[strum(serialize = "bottom_side")]
BottomSide,
#[strum(serialize = "bottom_tee")]
BottomTee,
#[strum(serialize = "box_spiral")]
BoxSpiral,
#[strum(serialize = "center_ptr")]
CenterPtr,
#[strum(serialize = "circle")]
Circle,
#[strum(serialize = "clock")]
Clock,
#[strum(serialize = "coffee_mug")]
CoffeeMug,
#[strum(serialize = "cross")]
Cross,
#[strum(serialize = "cross_reverse")]
CrossReverse,
#[strum(serialize = "crosshair")]
Crosshair,
#[strum(serialize = "diamond_cross")]
DiamongCross,
#[strum(serialize = "dot")]
Dot,
#[strum(serialize = "dotbox")]
Dotbox,
#[strum(serialize = "double_arrow")]
DoubleArrow,
#[strum(serialize = "draft_large")]
DraftLarge,
#[strum(serialize = "draft_small")]
DrawftSmall,
#[strum(serialize = "draped_box")]
DrapedBox,
#[strum(serialize = "exchange")]
Exchange,
#[strum(serialize = "fleur")]
Fleur,
#[strum(serialize = "gobbler")]
Gobbler,
#[strum(serialize = "gumby")]
Gumby,
#[strum(serialize = "hand1")]
Hand1,
#[strum(serialize = "hand2")]
Hand2,
#[strum(serialize = "heart")]
Heart,
#[strum(serialize = "icon")]
Icon,
#[strum(serialize = "iron_cross")]
IronCross,
#[strum(serialize = "left_ptr")]
LeftPtr,
#[strum(serialize = "left_side")]
LeftSide,
#[strum(serialize = "left_tee")]
LeftTee,
#[strum(serialize = "leftbutton")]
Leftbutton,
#[strum(serialize = "ll_angle")]
LlAngle,
#[strum(serialize = "lr_angle")]
LrAngle,
#[strum(serialize = "man")]
Man,
#[strum(serialize = "middlebutton")]
Middlebutton,
#[strum(serialize = "mouse")]
Mouse,
#[strum(serialize = "pencil")]
Pencil,
#[strum(serialize = "pirate")]
Pirate,
#[strum(serialize = "plus")]
Plus,
#[strum(serialize = "question_arrow")]
QuestionArrow,
#[strum(serialize = "right_ptr")]
RightPtr,
#[strum(serialize = "right_side")]
RightSide,
#[strum(serialize = "right_tee")]
RightTee,
#[strum(serialize = "rightbutton")]
Rightbutton,
#[strum(serialize = "rtl_logo")]
RtlLogo,
#[strum(serialize = "sailboat")]
Sailboat,
#[strum(serialize = "sb_down_arrow")]
SbDownArrow,
#[strum(serialize = "sb_h_double_arrow")]
SbHDoubleArrow,
#[strum(serialize = "sb_left_arrow")]
SbLeftArrow,
#[strum(serialize = "sb_right_arrow")]
SbRightArrow,
#[strum(serialize = "sb_up_arrow")]
SbUpArrow,
#[strum(serialize = "sb_v_double_arrow")]
SbVDoubleArrow,
#[strum(serialize = "shuttle")]
Shuttle,
#[strum(serialize = "sizing")]
Sizing,
#[strum(serialize = "spider")]
Spider,
#[strum(serialize = "spraycan")]
Spraycan,
#[strum(serialize = "star")]
Star,
#[strum(serialize = "target")]
Target,
#[strum(serialize = "tcross")]
Tcross,
#[strum(serialize = "top_left_arrow")]
TopLeftArrow,
#[strum(serialize = "top_left_corner")]
TopLeftCorner,
#[strum(serialize = "top_right_corner")]
TopRightCorner,
#[strum(serialize = "top_side")]
TopSide,
#[strum(serialize = "top_tee")]
TopTee,
#[strum(serialize = "trek")]
Trek,
#[strum(serialize = "ul_angle")]
UlAngle,
#[strum(serialize = "umbrella")]
Umbrella,
#[strum(serialize = "ur_angle")]
UrAngle,
#[strum(serialize = "watch")]
Watch,
#[strum(serialize = "xterm")]
Xterm,
}
pub struct CursorContext {
inner: ptr::NonNull<sys::xcb_cursor_context_t>,
}
impl CursorContext {
pub fn new(connection: &xcb::Connection, screen: &x::Screen) -> Result<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();
let res = unsafe {
sys::xcb_cursor_context_new(
connection.get_raw_conn() as *mut sys::xcb_connection_t,
&mut screen,
&mut ctx,
)
};
let Some(ctx) = ptr::NonNull::new(ctx) else {
anyhow::bail!(res);
};
Ok(Self { inner: ctx })
}
pub fn load_cursor(&self, cursor: Cursor) -> Result<x::Cursor> {
unsafe {
let c_str = ffi::CString::new(cursor.to_string())?;
let cursor = sys::xcb_cursor_load_cursor(self.inner.as_ptr(), c_str.as_ptr());
if cursor == x::CURSOR_NONE.resource_id() {
anyhow::bail!("Could not load {}", cursor);
}
Ok(x::Cursor::new(cursor))
}
}
}
impl Drop for CursorContext {
fn drop(&mut self) {
unsafe {
sys::xcb_cursor_context_free(self.inner.as_ptr());
}
}
}