Skip to content

os-cursors API

The complete public API of os-cursors — every exported item, with its signature, contract, and platform behavior. For the what-and-why (interception techniques, quick start), see the README.

ItemKindSignaturePurpose
Cursorenum19 unit variantsThe named cursors an app can replace
Cursor::freedesktop_namemethodfn freedesktop_name(self) -> &'static strThe theme-file name for this cursor
Cursor::allmethodfn all() -> &'static [Cursor]Every replaceable cursor
Imagestructpublic fieldsOne cursor frame: pixels + hotspot + nominal size
best_imagefnfn best_image(&[Image], target_px: u32) -> Option<&Image>Pick the frame for a target pixel size
installfnfn install(Cursor, &[Image], points: f32) -> boolReplace one cursor (macOS / Windows)
resetfnfn reset()Remove every installed cursor
use_xcursor_themefnfn use_xcursor_theme(&Path, name: &str, size_px: u32) -> boolPoint the process at a theme (Linux)
xcursor::parsefnfn parse(&[u8]) -> Option<Vec<Image>>Decode an XCursor file
xcursor::writefnfn write(&[Image]) -> Vec<u8>Encode an XCursor file

pub enum Cursor {
Arrow, IBeam, Crosshair, ClosedHand, OpenHand, PointingHand,
ResizeLeft, ResizeRight, ResizeLeftRight, ResizeUp, ResizeDown,
ResizeUpDown, ResizeUpLeftDownRight, ResizeUpRightDownLeft,
IBeamVertical, OperationNotAllowed, DragLink, DragCopy, ContextualMenu,
}

Copy, Clone, Debug, PartialEq, Eq, Hash.

The named cursors an app can replace — the intersection of what AppKit vends and what toolkits request. There is no ResizeColumn/ResizeRow: every platform aliases them onto ResizeLeftRight/ResizeUpDown.

pub fn freedesktop_name(self) -> &'static str

The freedesktop cursor-file name — the file inside an XCursor theme’s cursors/ directory — for this cursor: Arrow"default", IBeam"text", ResizeLeftRight"ew-resize", DragLink"alias", etc. Use it to look up pack files by Cursor.

pub fn all() -> &'static [Cursor]

Every replaceable cursor, for iterating a pack install.


pub struct Image {
pub size: u32, // nominal size (XCursor TOC subtype)
pub width: u32,
pub height: u32,
pub hotspot: (u32, u32), // pixels, from the top-left
pub delay: u32, // animation frame delay in ms; 0 = static
pub bgra: Vec<u8>, // premultiplied BGRA, width * height * 4
}

Clone, Debug, PartialEq, Eq.

One cursor frame. Pixels are premultiplied BGRA — exactly the XCursor on-disk pixel format (little-endian ARGB), which is also what Windows alpha cursors take; macOS swaps to RGBA on install. delay is preserved so animated cursors round-trip through xcursor::write; install shows the first frame only.


pub fn best_image(images: &[Image], target_px: u32) -> Option<&Image>

The best single frame for a target pixel size: the smallest nominal size >= target_px, else the largest available. Among animation siblings of one size, the first frame wins. None only for an empty slice.


pub fn install(cursor: Cursor, images: &[Image], points: f32) -> bool

Replace cursor with the best-fitting frame from images. Call on the UI thread. Installing again replaces the previous image; mixing install calls builds up a pack one cursor at a time.

  • macOS — honors points (the native arrow is ≈17pt; 20pt with 64px frames renders Retina-crisp). Prefers a frame ≥3× points for headroom. All 19 cursors work; the themed cursor applies process-wide, and the system cursor returns outside your windows.
  • Windowspoints is ignored; cursors show at pixel size, so the frame nearest GetSystemMetrics(SM_CXCURSOR) is used. Only the nine cursors with their own standard handle install (see the README table); the aliased rest return false.
  • Linux — always false; use use_xcursor_theme.

Returns false when the cursor can’t be replaced on this platform, the slice is empty, or an image is malformed (undersized pixel buffer).


pub fn reset()

Remove every installed cursor — the native ones return. The interception hooks stay armed (harmless), so a later install is cheap. Does not undo use_xcursor_theme’s environment on Linux.


pub fn use_xcursor_theme(themes_dir: &Path, name: &str, size_px: u32) -> bool

Linux: point this process at an XCursor theme — themes_dir/name/cursors/ must exist, or false. Sets XCURSOR_PATH (prepending themes_dir, keeping the inherited search path for inherits= fallbacks), XCURSOR_THEME, and XCURSOR_SIZE.

libXcursor and libwayland-cursor read these when the display connection loads cursors, so this must run before any windowing setup and before other threads exist (environment mutation is process-global) — first thing in main. Works on X11 and Wayland alike; see the README for the cursor-shape-v1 caveat.

macOS / Windows: no-op, returns false — use install.


The XCursor binary format, pure Rust. An XCursor file holds one logical cursor at several nominal sizes (plus animation frames); a theme is a directory of them under cursors/, named by Cursor::freedesktop_name.

pub fn parse(data: &[u8]) -> Option<Vec<Image>>

Every image frame in an XCursor file, in TOC order (sizes ascending, animation frames consecutive). None on anything malformed — bad magic, truncated chunks, absurd dimensions (> 1024px). Non-image chunks (comments) are skipped.

pub fn write(images: &[Image]) -> Vec<u8>

Encode frames as an XCursor file. Give them in TOC order — sizes ascending; parse’s output order round-trips exactly. Useful for stripping themes to one size or generating packs (e.g. from rasterized SVGs).