Skip to main content

Module cursor

Module cursor 

Source
Expand description

Out-of-band X11 cursor delivery, the X11 counterpart of the Wayland compositor’s cursor callback: one process-wide monitor thread (the X pointer is global, however many captures run) parks in wait_for_event on its own connection for XFixes DisplayCursorNotify and hands each new cursor to the registered CursorSink — the same payload the Wayland backend sends, so a consumer needs one handler for both. Keeping the cursor out of the framebuffer means pointer motion over static content never dirties the damage hash or re-encodes video; capture_cursor compositing stays available as the opt-in alternative.

The thread blocks — no polling. Stop and replay requests wake it by sending a ClientMessage to a private InputOnly window from a short-lived connection, which also keeps every request on the monitor’s own connection single-threaded. The SeqCst pairs on stop/wake_win close the startup race where a stop lands before the wake window exists: whichever side loses the ordering still observes the other’s store.

Structs§

Monitor 🔒
Slot 🔒
The monitor and the number of running X11 captures that keep it alive.

Statics§

CALLBACK 🔒
The cursor sink from set_cursor_callback, shared by every X11 capture.
REPLAY 🔒
Deliver the current cursor on the next wake (a callback registered mid-run).
SIZE_CAP 🔒
Longest delivered cursor edge; larger images are downscaled (<= 0 = uncapped). 128 is what a browser shows as a cursor, so an application’s own sprite travels whole.
SLOT 🔒

Functions§

acquire
An X11 capture started: the first one in spawns the monitor.
cursor_to_png 🔒
XFixes ARGB image -> callback payload: cropped to the visible bounding box (hotspot re-based to it), downscaled so the longest edge fits cap, un-premultiplied, and PNG-encoded. Scaling runs in premultiplied space — per-channel filtering is only linear there, and fully transparent texels cannot bleed dark fringes into edges — and straight alpha is produced last, since that is what PNG carries. A fully transparent image is an intentional pointer hide.
deliver 🔒
Hand a payload to the sink (skips quietly with no sink, no payload, or a consumer that is gone).
fetch_payload 🔒
Fetch and convert the current cursor. None on a fetch failure (XFixes returns an error while the cursor is not displayed, e.g. a blanked screen) or an encode failure — mirroring the Wayland gate where only real payloads or an explicit hide go downstream.
monitor_thread 🔒
release
An X11 capture stopped: the last one out stops and joins the monitor. A caller holding the GIL runs it detached — the thread may be blocked delivering a cursor to a sink that needs it — and the join is bounded: a thread that misses the wake (X server wedged mid-shutdown) is abandoned with a warning rather than hanging the caller; it exits on its own next event. A stop issued from inside the cursor sink runs on the monitor thread itself, where a join can only time out: it signals and detaches instead, and the loop observes the stop flag once the sink returns.
set_callback
Register/replace the callback and re-deliver the current cursor to it. None withdraws it, releasing what the callback holds: the slot is process-wide and outlives the capture that set it, so a consumer that is going away has no other way to let go.
set_size_cap
setup 🔒
shutdown
Consumer teardown: drop the sink and signal the thread without joining — SHUTDOWN already gates it off the consumer, and a normal capture stop reaps it via release.
wake 🔒
Wake the parked monitor: a ClientMessage to its private window (SendEvent with an empty mask delivers to the window’s creator) from a short-lived connection. The send is round-tripped (check) so a failure has a name, and the whole wake is retried once. A zero window means setup hasn’t finished; the thread re-checks its flags before first parking, so the wake can be skipped.

Type Aliases§

Payload 🔒
The last payload handed to the sink, retained so a replay (late callback registration) re-sends it without touching the server — mirroring the Wayland backend, and immune to XFixes’ transient “cursor not displayed” fetch errors.