TheDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/smithay/wayland-rs/llms.txt
Use this file to discover all available pages before exploring further.
wayland-cursor crate gives Wayland clients a pure-Rust way to load and display cursor images from system XCursor themes. Rather than delegating to the C libwayland-cursor library, it parses XCursor files directly using the xcursor crate and allocates shared memory with rustix, writing pixel data into a wl_shm_pool that the compositor can read. The resulting [CursorImageBuffer] values dereference to [WlBuffer], so you attach them to a wl_surface just like any other buffer. Animated cursors are supported through frame-timing helpers that tell you which frame to show and for how long.
Adding the dependency
wayland-cursor requires no system libraries beyond a working Wayland connection — it does not link against libwayland-cursor.so. Shared memory is allocated via memfd_create on Linux and Android, falling back to POSIX shm_open on other platforms.
Loading a cursor theme
[
CursorTheme::load()] is the simplest entry point. It reads the XCURSOR_THEME and XCURSOR_SIZE environment variables (falling back to "default" and the supplied size if they are absent) and allocates a shared-memory pool large enough to hold the cursor images.use wayland_cursor::CursorTheme;
use wayland_client::{Connection, protocol::wl_shm::WlShm};
fn setup_cursor_theme(conn: &Connection, shm: WlShm) -> CursorTheme {
// Reads XCURSOR_THEME / XCURSOR_SIZE from the environment.
// Falls back to theme name "default" and size 32.
CursorTheme::load(conn, shm, 32)
.expect("Could not load cursor theme")
}
// Use "Adwaita" if XCURSOR_THEME is not set.
let theme = CursorTheme::load_or(conn, shm, "Adwaita", 24)?;
// Always use "Breeze" at 48 px regardless of environment.
let theme = CursorTheme::load_from_name(conn, shm, "Breeze", 48)?;
Call [
CursorTheme::get_cursor()] with an XCursor name such as "default", "text", or "wait". The method loads the cursor from the system theme on first call and caches it for subsequent calls. It returns None if the cursor is not provided by the theme or any of its parents.If the system theme does not provide a cursor you need, register a fallback closure with [
CursorTheme::set_fallback()]. The closure receives the cursor name and size and must return raw XCursor file bytes or None.Displaying cursor frames
A [Cursor] can contain multiple images when the cursor is animated. Use [Cursor::image_count()] to query how many frames are available, and index the cursor with cursor[i] to obtain a [CursorImageBuffer] for each frame.
Animating cursors
[Cursor::frame_and_duration()] takes elapsed milliseconds and returns a [FrameAndDuration] value containing:
frame_index— which image to displayframe_duration— how many more milliseconds that frame should remain on screen
CursorImageBuffer metadata
Each [CursorImageBuffer] carries the image dimensions and the hotspot position — the pixel coordinate within the image that represents the actual pointer tip.
Pass the hotspot coordinates to
wl_pointer.set_cursor so the compositor places the image correctly relative to the pointer position.Complete usage example
API summary
| Type / Method | Description |
|---|---|
CursorTheme::load(conn, shm, size) | Load the system theme; respects XCURSOR_THEME / XCURSOR_SIZE |
CursorTheme::load_or(conn, shm, name, size) | Load with a named fallback theme |
CursorTheme::load_from_name(conn, shm, name, size) | Load a specific theme, ignoring env vars |
CursorTheme::get_cursor(name) | Retrieve a Cursor by XCursor name |
CursorTheme::set_fallback(fn) | Provide raw XCursor bytes for missing cursors |
Cursor::frame_and_duration(millis) | Get the current frame index and remaining display time |
Cursor::image_count() | Total number of animation frames |
cursor[i] | Index into a CursorImageBuffer |
CursorImageBuffer::dimensions() | (width, height) in pixels |
CursorImageBuffer::hotspot() | (x, y) pointer hotspot |
CursorImageBuffer::delay() | Frame display duration in milliseconds |
Deref<Target = WlBuffer> | Attach the buffer to a wl_surface directly |