Expand description
Per-app custom mouse cursors, no toolkit fork required.
UI toolkits (gpui included) ask the OS for named cursors — +[NSCursor arrowCursor] on macOS, LoadCursorW(IDC_ARROW) on Windows, a freedesktop
name like "default" on Linux. This crate changes what those answers look
like for the current process only, from below the toolkit:
- macOS — the
NSCursorclass methods are swizzled (supported objc runtime API) to return cursors built from your images. Callinstallon the main thread. - Windows — a thread-scoped
WH_CALLWNDPROCREThook watchesWM_SETCURSOR; when the toolkit sets a standardIDC_*cursor, it is swapped for yours. Windows collapses several logical cursors onto one handle (all horizontal resizes areIDC_SIZEWE, hands areIDC_HAND), so only the cursors in that granularity install — the rest returnfalse. Callinstallon the UI thread. - Linux — the platform already does per-process theming:
libXcursor/libwayland-cursor read
XCURSOR_THEME/XCURSOR_PATH/XCURSOR_SIZE.use_xcursor_themesets them; per-imageinstallis a no-op. Must run before the display connection (i.e. first thing inmain). Caveat: a Wayland client using thecursor-shape-v1protocol delegates drawing to the compositor and cannot be themed — gpui’s current backend does not, but check yours.
The pack currency is the XCursor theme directory — the standard Linux
cursor-theme format, so every existing theme is drop-in content.
xcursor parses and writes the binary files, pure Rust, no deps.
Modules§
- xcursor
- The XCursor binary format — parse and write, pure Rust.
Structs§
- Image
- One cursor image (a single size — one XCursor frame).
Enums§
- Cursor
- The named cursors an app can replace — the intersection AppKit vends and
gpui requests.
ResizeColumn/ResizeRowdon’t appear: every platform aliases them ontoResizeLeftRight/ResizeUpDown.
Functions§
- best_
image - From a parsed set of frames, the best single frame for a target pixel
size: the smallest nominal size ≥
target_px, else the largest available. (First frame wins among animation siblings of one size.) - install
- Replace
cursorwith the best-fitting frame fromimages. - reset
- Remove every installed cursor; the native ones return.
- use_
xcursor_ theme - Linux: point this process at an XCursor theme —
themes_dir/name/cursors/must exist. SetsXCURSOR_PATH(prepended),XCURSOR_THEME, andXCURSOR_SIZE, which libXcursor and libwayland-cursor read when the UI first connects — so this must run before any windowing/display setup and before other threads exist (environment mutation is process-global). No-opfalseon macOS/Windows — useinstall.