pub fn run_capture<F>(
settings: RustCaptureSettings,
controls: Arc<Controls>,
encode_tid_tx: Sender<ThreadId>,
on_frame: F,
) -> Result<(), String>Expand description
Run the X11 capture pipeline until stop is set, splitting capture and encode across two
threads that overlap for throughput.
This (the caller’s) thread performs setup and then the capture loop; a spawned encode thread runs
encode_loop and invokes on_frame(stripes) once per encoded frame. The split lets capture and
encode overlap, and because frames are throttled/dropped as RAW frames before encode, the delivered
H.264 stays a valid contiguous reference chain.
- Setup: connect to X (a private connection) and require a 32-bpp BGRA root — the Z-pixmap
byte depth must be 4, which modern servers use for depth 24/32 — then negotiate XShm and
XFixes. XFixes is always negotiated (one round-trip) so the cursor overlay can be toggled on
live even when capture started without it. Initial dimensions and origin are resolved from the
settings and the live root geometry, and a
FramePoolofPOOL_N= 3 shm surfaces is allocated — the working set (one in-capture, one in-slot, one in-encode) that a one-frame-ahead demand-driven capture needs, which also keeps the memory cost (3 * W*H*4, significant at 4K) bounded. - Encode thread: spawned with a raised scheduling priority; it reports its thread id back
through
encode_tid_txso the caller can detect a re-entrant stop issued from inside the delivery callback. - Capture loop (until
stop): fps is re-read each iteration for live pacing — sleep to the next frame deadline, oryield_nowwhen already behind instead of busy-spinning.- Live region change (
region_dirty): re-target the grab origin immediately (an x/y pan needs no surface work); a size change reuses the drain/recreate path below. - Auto-adjust: on a root geometry change, drain in-flight frames then recreate the surfaces
and bump the generation (which the encode thread turns into a reshape or rebuild). The geometry
is re-resolved AFTER the drain, because a slow drain can outlast another geometry change (a
fast flap) and recreating at a stale size would make the next
shm_get_imageexceed the root; a fully-reverted flap keeps its surfaces as-is. A stop that races the drain breaks out to teardown. - Grab: acquire a pooled surface (blocks until the encoder frees one), then
shm_get_imagethe region into it synchronously (reply()waits). - Overlays: composite the XFixes cursor (drawn at its hotspot-offset origin; live-toggleable) and the watermark onto the BGRA pixels on the CPU.
- Publish: hand the finished frame to the encode thread (blocks until the slot is free; never drops). A stop observed while waiting in acquire/publish exits the loop.
- Live region change (
- Teardown: stop and JOIN the encode thread BEFORE destroying the shm surfaces it may still be reading, preserving the resize-safety guarantee.
Blocking; intended to run on a dedicated thread. The X connection and shm surfaces live on this thread and the encoder lives on the encode thread — nothing X-related crosses the boundary.