Expand description
§pixelflux
A high-performance screen capture and encoding pipeline: a Rust library, and with the
python feature the pixelflux Python extension module built on it (python). It
supports two independent backends — X11 (XShm + XFixes) and Wayland
(a headless Smithay compositor) — and a shared
encoding layer that dispatches to software (striped JPEG, H.264 through the build’s
software encoder — libx264 with the gpl feature, OpenH264 without — and the other
codecs through the software encoders the build links: x265 or kvazaar, libvpx, SVT-AV1) or
hardware (NVENC, VA-API) encoders based on the available GPU and operator settings.
§Crate structure
| Module | Purpose |
|---|---|
encoders | Encoder backends: software H.264 (libx264 or OpenH264) / JPEG, NVENC, VA-API, watermark overlay |
wayland | Headless Smithay compositor, cursor rendering |
x11 | X11/XShm capture loop, XFixes out-of-band cursor monitor, and stripe dispatch |
pipeline | Frame-processing policy shared by both backends (send/QP/keyframe decisions) |
recording_sink | Unix-socket H.264 fan-out for external recording |
recorder | Built-in MP4 recorder (fMP4 muxer + API/env/REST control surfaces) |
sink | What a capture hands its consumer, and the traits a consumer implements to receive it |
python | The Python bridge, with the python feature: the classes and functions import pixelflux sees |
computer_use | AI-agent desktop control (screenshots, input injection, the recorder’s commands) |
nvgpufilter | Multi-GPU NVENC device filtering via ioctl |
webcam | Virtual camera: client webcam uplink decoded into a V4L2 device (interposer ring, v4l2loopback, PipeWire node) |
§Data flow
Consumer ──► CaptureSettings ──► X11 / Wayland backend
│
frame pixels
│
┌────┴────┐
│ Encoder │ (NVENC / VAAPI / x264 or OpenH264 / JPEG)
└────┬────┘
│
EncodedStripe(s)
│
FrameSink (a Rust consumer, or the Python callback)Re-exports§
pub use error::Error;pub use encoders::nvenc;pub use encoders::software::StripeState;
Modules§
- computer_
use - The Anthropic Computer Use actions for AI agent desktop control. The Anthropic Computer Use actions on the captured desktop, and the recorder’s commands beside them, for whatever transport a host serves them over.
- encoders
- Encoder backends and the codec identities, wire framing, and rate-control policy they share. Encoder backends and what they share: the codec identities and wire framing, the rate-control policy, which software encoder a build resolves each codec to, and which hardware encoder a render node serves each with.
- error
- The one error type the public API returns.
The one error type the public API returns. Each variant names the part of pixelflux
that refused: the settings a consumer handed over, a capture, the compositor, or a
capability the session lacks (a compositor without the virtual-keyboard protocol). The
Python bridge maps them to its exceptions (
ValueErrorfor settings,VirtualKeyboardUnavailablefor a missing capability,RuntimeErrorotherwise). - ledger
- Per-block stillness and cleanliness of a region, for refining blocks while others move.
Where a region’s picture stands, block by block: the per-block counterpart of the cleanup
policy’s per-region bookkeeping (
pipeline::cleanup_due). - log
- The debug switch behind every backend’s tagged line. The verbosity switch behind every backend’s tagged line.
- nvgpufilter
- Multi-GPU NVENC device filtering via kernel ioctl.
Multi-GPU NVENC
GET_ATTACHED_IDS/GET_PROBED_IDSioctl filter — it exists so a container handed only a subset of the host’s GPUs can still open an NVENC session. - pace
- When a capture is due a frame, shared by the X11 and Wayland backends. The frame pacing every capture backend shares: when a display is due a frame, and how far a change that just happened may bring the next one forward.
- pipeline
- Frame-processing policy shared by the X11 and Wayland backends. Frame-processing policy shared by the two capture backends. It lives in its own module for one reason: the Wayland path (dmabuf, compositor damage) and the X11 path (host-ARGB, stripe-hash damage) capture pixels in completely different ways, but a viewer must never be able to tell which one produced a frame — a paint-over refresh or a recovery keyframe has to behave identically either way. Keeping the decision logic here, source-agnostic, is what guarantees it.
- pipewire
- Run-time libpipewire binding and SPA pod encoding shared by the webcam sink and host capture.
Run-time binding to
libpipewire-0.3and the SPA pod encoding shared by every PipeWire user in pixelflux: the webcam sink (aVideo/Sourcenode) and the portal screen capture (aVideo/Inputstream). The library is loaded with dlopen, so pixelflux keeps no build-time or load-time dependency on PipeWire and each user simply stays off where the library or the daemon is absent. Pods are built and read by hand against the SPA wire layout: 8-byte alignedsize, type, payloadcells, objects carryingkey, flags, cellproperties, and a choice cell whose first value is the default (and, once fixated, the only) value. - recorder
- Built-in MP4 recorder: independent capture-to-file with API/env/REST control. Built-in MP4 recorder: an independent pipeline that captures the desktop to a ready-to-play fragmented MP4 without needing any connected client.
- recording_
sink - Unix-socket H.264 recording fan-out for external capture tools. Unix-socket video fan-out for external recording.
- report
- What each capture streams and how it got there, as values a caller reads. What a capture streams and how it got there, for the caller to show its user: the capture path and whether it is zero-copy, the encoder and whether it is hardware, the GPU behind it, and the reason a faster path was declined. The same facts the tagged log lines state, kept as values so nobody has to read a log to learn them.
- sink
- What a capture hands its consumer, and the traits a consumer implements to receive it.
What a capture hands its consumer, and the traits a consumer implements to receive it:
the encoded frames of a capture ([
FrameSink]), the pointer’s image as it changes ([CursorSink]), the clipboard a client application copies to ([ClipboardSink]), and the selection of an application compositor watched from outside ([SelectionWatch]). - uinput
- Kernel uinput devices, the first rung of host-capture input injection.
Kernel input devices for host-capture injection, over
/dev/uinput. - wayland
- Headless Wayland compositor and cursor rendering. Wayland backend: a headless Smithay compositor that stands in for a real display server.
- webcam
- Virtual camera: the browser’s webcam, delivered to applications as a V4L2 capture device.
- wm
- The window policy interface a window manager implements for the compositor. The window policy interface: what a window manager decides for the compositor.
- x11
- X11/XShm capture loop, stripe dispatch, and per-stripe change detection.
X11 host capture: grab the root window into host memory as BGRA, composite the XFixes hardware
cursor and the watermark on the CPU, and feed each frame to
pipeline::X11Pipeline, which owns damage/stripe/encode. The grab goes through a shared-memory segment (XShm via x11rb) rather than a plainGetImagefor one reason: a full-screen frame is far too large to copy through the X protocol socket every tick, so XShm has the server write the pixels straight into memory this process already has mapped.
Structs§
- Capture
Settings - The full set of capture + encode parameters a consumer hands the backend.
- Card
Identity 🔒 - A DRM card’s identity as the kernel reports it, read from the device’s
sysfs
uevent(DRIVER=, PCI_ID=, OF_COMPATIBLE_n=) with per-file fallbacks (vendor,modalias, thedriversymlink). uevent is uniform across buses (PCI, platform/devicetree, USB) and readable in unprivileged containers. - Compositor
- The handle to the one long-lived compositor thread. Because calloop, EGL/GBM, and the
Wayland display are all thread-affine and the compositor has to keep running to serve its
clients even between captures, the backend cannot be a passive object that starts work on
demand: constructing it spawns that thread and it stays up for the process lifetime. The
struct itself is nothing but the command-channel sender used to drive that thread across
the boundary;
ensure_wayland_displayhands out the process-wide one. - Frame
Scene 🔒 - What an output’s frame is composed of, for
composition_elements. - GpuBring
Up 🔒 - What the compositor renders with on a GPU, all of it brought up before any client hears of
the GPU (
gpu_bring_up). - Live
Tunables - The per-frame decision/quality knobs every encoder re-reads from the settings on each
tick, so they retune a running capture: x264, x265, and libvpx reconfigure, NVENC and
VA-API retarget their constant quantizer, kvazaar and SVT-AV1 re-open, JPEG is stateless.
A changed reference depth (
video_reference_frames) re-opens an x264 session, on a key frame; a hardware session keeps the depth it opened with. Applied on the thread that owns the settings copy. Structural switches (encoder, chroma, RC mode, device) still need a capture restart. - ScState 🔒
- Mutable per-capture state behind
ScreenCapture’s mutex: the active backend, the live X11 controls and thread handle, and the capture / encode thread ids used to detect a re-entrant stop. - Screen
Capture - Screenshot
Readbacks 🔒 - The persistently mapped pixel-pack buffers GLES screenshots are read back into, so the
compositor only issues the transfer and the caller copies the pixels out once the GPU has
written them. Without
GL_EXT_buffer_storage, or withPIXELFLUX_SCREENSHOT_SYNCset, the compositor reads back synchronously into the caller’s buffer instead. Declared ahead of the renderer inAppState, so its drop forgets every mapping, waiting out a copy under way, before the GL context goes away. - Software
Cursor 🔒 - The software cursor an output’s frame draws, where it does: the theme that resolves a named cursor, what the client set, where the pointer is on the output, and the time for an animated cursor’s frame.
- Stream
Info - Unified capture handle, the one a consumer holds. Drives the X11 capture directly or delegates to the
shared Wayland backend, chosen at
start_capturetime. Exposes start_capture / stop_capture / request_idr_frame / update_* / is_capturing, plus the Wayland input-injection methods. What a capture streams and how it got there (ScreenCapture::stream_info): the report with the encoder’s GPU named, and the compositor’s renderer where the capture runs on Wayland. - Stream
Stats - A capture’s counters (
ScreenCapture::stream_stats): the totals since it started and the extremes since the previous read. - Wayland
GpuProbe - What
probe_wayland_gpufound: the render node tried, whether it renders in hardware, the GL renderer’s name, and why it was refused where it was. - Wayland
Thread 🔒Config - Startup inputs for the compositor thread: its two calloop channels, the sender it hands to computer-use, the initial geometry, and the GPU / cursor policy.
- WlEncode
Config 🔒 - Everything the Wayland encode thread needs, fixed for the life of one capture (a
StartCapture reconfigure tears the thread down and spawns a fresh one). Rate changes
flow through
controls; damage and the IDR request arrive per-frame inWlFrame. - WlEncode
Controls - Cross-thread controls for the Wayland encode thread, the X11
Controlsscheme: the UpdateRate handler stores the current values then flipsrate_dirtywith Release; the encode thread swaps it with Acquire and re-reads the payload, never seeing it half-applied.force_idris swapped just before each encode, so an on-demand keyframe lands on the frame ALREADY in flight instead of waiting one pipeline stage for the next publish.reference_reportsare what clients said of frames (lost, or held by every one of them), drained ahead of the same encode. - WlEncode
Stats - Shared capture stats: whichever thread owns the encoders counts frames/stripes and
composes
desc+n_stripes(the encoder half of the 1 s debug log line); the calloop log loads, prints, and resets the counters. - WlFrame
- One captured frame in flight from the calloop (render/readback) to the Wayland encode thread: the slot’s buffer (a readback’s packed rows, or the zero-copy session’s GPU target) plus the per-frame inputs of the encode dispatch (damage, overlay animation); the IDR request travels separately via the controls atomic.
- WlFrame
Pool - Render->encode handoff for the Wayland readback paths, mirroring the X11 FramePool’s
single-slot non-dropping design with one deliberate difference: the calloop thread is also
the compositor + input dispatcher, so it must NEVER block on the pool.
try_beginhands out a buffer only while the publish slot is empty, sopublishcannot block, and a saturated encoder throttles capture by SKIPPING ticks (compositor damage accumulates via buffer age, so nothing is lost). Every published frame is encoded, in order: the H.264 reference chain stays contiguous exactly as on X11. - WlPool
Inner 🔒 - Interior state of
WlFramePool: the free-buffer list plus the single publish slot. - Zero
Copy 🔒Session - A zero-copy hardware session (GLES render + same-GPU dmabuf encode), driven by the capture’s encode thread. The calloop opens it and takes it back at a restart to reuse in place; the thread holds the lock for one encode at a time, never across a delivery.
Enums§
- Composition
Elements - Shot
Pixels 🔒 - Where a screenshot’s pixels are once its output has rendered.
- Thread
Command - Control messages sent from the
Compositorhandle’s methods to the compositor thread.
Constants§
- DEFAULT_
FPS 🔒 - DEFAULT_
VIEW_ 🔒ID - The view every screen carries from the start, covering it: the display a consumer gets without asking for a layout at all.
- EGL_
LIBRARY 🔒 - The library smithay opens for its EGL calls, by the name it opens it by.
- GEOMETRY_
BARRIER_ 🔒TIMEOUT - Bound on the geometry read-back barrier. Behind a host-capture start the read is held
until the host has ruled on the requested mode, which the host’s control thread bounds
by
LAYOUT_DEADLINE, so this clears that with margin or a live-but-slow start would read as a timeout. - GL_
CLIENT_ 🔒STORAGE_ BIT_ EXT - GL_
MAP_ 🔒COHERENT_ BIT_ EXT - GL_
MAP_ 🔒PERSISTENT_ BIT_ EXT - GL_
MAP_ 🔒READ_ BIT - HOST_
MODE_ 🔒REFUSALS_ BEFORE_ HINT - Refused mode requests in a row for one display before the log says plainly what to do: a host that keeps its own mode is asked again on every client resize, and each refusal restarts the capture at the mode the host runs.
- HOST_
QUIET_ 🔒PERIODS - Periods without a fresh host frame before the timer treats the host as quiet and, in streaming mode, re-encodes the retained frame to hold the rate. A live host’s frames come about a period apart, so a tick landing a hair ahead of one must not publish a duplicate the fresh frame then waits behind.
- HW_
ERROR_ 🔒RECOVERY_ THRESHOLD - Consecutive encode failures before a hardware path recovers (~0.5s at 60fps): a hiccup outlasts it, anything longer starts to look like a dead session. Shared by every hardware encode path (Wayland zero-copy, Wayland readback, X11) so recovery timing matches.
- IDLE_
FRAME_ 🔒INTERVAL - How often frame callbacks go out while nothing is being captured. Fast enough that a client blocked on one keeps making progress, slow enough that an unwatched session is not drawing frames nobody asked for.
- MAX_
CAPTURE_ 🔒DIM - Upper bounds for consumer-supplied capture geometry: keeps a hostile or buggy setting from
turning into a multi-GB
vec![]allocation that would abort the process. 16384 covers every real display wall; the fps/scale bounds keep timing math finite and sane. - MAX_FPS 🔒
- MAX_
SCALE 🔒 - OF_
PREFIX_ 🔒ALIASES - Human name -> devicetree vendor prefix, only where they differ (a token equal to the prefix itself, e.g. “qcom” or “rockchip”, matches directly).
- READBACK_
SLOTS_ 🔒MAX - Pixel-pack buffers kept at once, as many screenshots as the process admits.
- READBACK_
SLOT_ 🔒IDLE - How long an unused pixel-pack buffer is kept for the next screenshot.
- RENDER_
FENCE_ 🔒TIMEOUT - Longest a render tick waits for the GPU to finish a frame before giving that frame up.
- SCROLL_
V120_ 🔒PER_ UNIT - Wheel v120 units per unit of injected scroll value: selkies sends 10 per notch, and one notch is 120, so both the seat’s v120 and the host virtual pointer’s discrete steps derive from the same value.
- SHM_
GONE 🔒 - The error
encode_shm_rowsreturns where the client’s pool no longer holds the frame. - STOP_
JOIN_ 🔒TIMEOUT - Best-effort nice boost for the calling capture/encode/delivery thread. These threads compete with the very workload being captured, so a scheduling edge keeps frame pacing steady under load. Requires CAP_SYS_NICE (or root); otherwise EPERM and silently a no-op. How long a stop waits for a capture or delivery thread to end before leaving it behind.
- TRIGGER_
FPS 🔒 - The frame rate
paint_over_trigger_framesis counted at. - UNDRAWN_
TICKS_ 🔒PER_ FRAME - Render ticks in a row that draw nothing before one closes an empty frame on its target. smithay binds a dmabuf target through a texture with a framebuffer made for that bind, and the NVIDIA driver keeps the state of each one never drawn to until a frame retires it (a fence or a flush does not): about 15 KB a tick while the screen stands still.
- VENDOR_
PCI_ 🔒IDS - Human vendor name -> PCI vendor IDs. The kernel has no such table (the grouping is conventional), and pci.ids/hwdata is often absent in containers, so this is the one mapping that must be embedded — kept to the names only.
- VERSION
- The crate version compiled in, for a host that names the pixelflux it links.
- WL_
CONTENT_ 🔒HOLD - How long a reconfigured output holds its frames while its clients answer the new size. It is measured from the start, which rebuilds the encode path before there is any frame to hold: the first one arrives roughly 300 ms later, so a window shorter than that expires before it can suppress anything and the hold does nothing at all. This one outlasts the rebuild and a client’s own repaint, and is the whole delay an output that no client ever draws on pays before its content reaches the viewer regardless.
- WL_
POOL_ 🔒SURFACES - Two buffers: one being encoded while the calloop fills the other. try_begin gates on the publish slot, so a deeper pool would only add latency (staler frames), never overlap.
- WL_
POOL_ 🔒WAKE_ QUANTUM - The encode thread’s bounded idle wait in
WlFramePool::take. - XWAYLAND_
VAR - The variable naming the Xwayland a caller ships, found ahead of the host’s
(
wayland::xwm::XWAYLAND_VAR).
Statics§
- CURSOR_
REGISTRATIONS 🔒 - Every capture’s own cursor callback, the latest registration last: the process-wide slots carry the latest one’s, so a capture withdrawing its own (a second display leaving) hands the cursor back to the capture registered before it.
- LIVE_
X11 🔒 - Registry of every live X11 capture’s
Controls, so the atexit sweep can flag them all to stop before the interpreter finalizes even after the owningScreenCapturePython handles have been dropped — without a central registry those captures would have no reachable stop switch. - NEXT_
CAPTURE_ 🔒ID - Hands each
ScreenCapturea unique, monotonic id — the tokenWAYLAND_OWNERScompares to decide which instance is allowed to stop a display’s capture. Starts at 1 so 0 can mean “no owner”. - PENDING_
CURSOR_ 🔒CALLBACK - Cursor callback registered before the backend exists (selkies registers it pre-start); applied when the backend is created, which is deferred to capture start so the real render node (not a placeholder) reaches the compositor.
- SHUTDOWN 🔒
- Consumer-gone gate, set by
shutdown: the detached compositor and delivery threads must never call a sink again, since the Python bridge runsshutdownfromatexitand a sink attaching to a finalizing interpreter aborts the process (pre-3.13). Cleared by a fresh capture start (only a live consumer can start one). - WAYLAND_
ACTIVE_ 🔒CODEC - Record (or clear, with
None) the last-start outcome fordisplay_id. - WAYLAND_
ALIVE_ 🔒DISPLAYS - Display ids whose capture pipeline is actually running (StartCapture inserts, StopCapture/DestroyOutput remove), so the Python-facing is_capturing() reports pipeline liveness, not merely which ScreenCapture owns the backend.
- WAYLAND_
BACKEND 🔒 - Process-wide Wayland backend: input and capture share ONE compositor (constructed lazily).
- WAYLAND_
CAPTURE_ 🔒ERR - Per-display outcome of the most recent capture start, recorded by the calloop thread
so the Python-facing
capture_statecan report it. A start that came up clean removes its entry; a hard failure (no output, …) or a caveat that still capturing (host connect failed -> local compositing, hardware encoder fell back to CPU) leaves the reason here. Distinct fromwayland_alive, which says only whether the pipeline runs. - WAYLAND_
OWNERS 🔒 - Per-display capture ownership: display id -> the ScreenCapture id that owns that display’s capture. Only the owner may stop it, so an input-only or stale instance can’t tear down a live capture.
- WAYLAND_
SOCKET_ 🔒CV - WAYLAND_
SOCKET_ 🔒NAME - The compositor’s auto-picked socket name (e.g. “wayland-1”), published by the compositor
thread once its listening socket exists.
ListeningSocketSource::new_autobinds the first FREE wayland-N, which need not match any configured index — consumers must read the real name from here instead of assuming one.
Traits§
- WlPayload 🔒
- What a Wayland encode thread encodes a frame from: the packed rows a readback filled, or the GPU buffer a zero-copy session encodes in place.
Functions§
- alloc_
render_ 🔒target - The buffer a display is rendered into and a hardware encoder reads in place: one plane, for rendering. Intel’s video processor, converting to 4:2:0, reads a linear surface at a pitch rounded up to 64 bytes whatever pitch its import declares, and Mesa gives a linear buffer allocated for rendering alone the pitch of its width, so a width that is no multiple of 16 reaches the encoder sheared. A buffer the display engine may scan out carries that engine’s aligned pitch, so a linear allocation whose pitch is not a multiple of 64 is made again for scanout as well, and that one is kept only where it came out aligned.
- alloc_
zero_ 🔒copy_ ring WL_POOL_SURFACESzero-copy render targets the size of the capture, orNonewhere the GPU refuses one.- announce_
scale_ 🔒to_ windows - Tell every window on an output the output’s scale: the surface’s preferred buffer scale and fractional scale, so a client draws at the new density rather than being stretched to it. The size a client keeps is the policy’s business where one runs, and the fullscreen configure’s otherwise.
- answer_
frame_ 🔒callbacks - Answer the frame callbacks of each captured display’s clients once a window, on its boundary:
the vblank a display gives them. A change renders as it lands (
serve_changes), and a client drawing on frame callbacks takes its clock from them, which the boundaries keep even. The frame timer wakes at every boundary while a display captures, and calls this each time. - answer_
geometry_ 🔒waiters - Answer geometry readers parked behind a host layout request with what
display_idcaptures now. - app_
socket_ 🔒path - The
pixelfluxPython module: registers the exported classes and functions, and hooks_stop_all_capturesintoatexitso every live capture is stopped before interpreter shutdown. Socket path for a Wayland display name, with the ABI methods’ error shape. - auto_
render_ 🔒node - The render node an
auto_gpurequest picks: None where the request is off or matches no node. - auto_
select_ 🔒render_ node - Resolve a usable
/dev/dri/renderD*node, optionally matching a vendor/driver token. - boost_
thread_ 🔒priority - Give the calling thread the nice level
nice, below the default.setprioritytakes it only with CAP_SYS_NICE or an RLIMIT_NICE that reaches it, which a session’s process rarely has; where it refuses, the request goes to rtkit over the system bus, which grants a level within the daemon’s own bounds to any process of the seat. A host without rtkit, or without a system bus at all as a container is, leaves the thread at the default, said once in the log. - bootstrap_
readback_ 🔒pool - Start (or in-place reconfigure) the capture bound to output
display_id: reprogram the output’s mode/scale/refresh, size the render targets, fullscreen the display’s windows at the new logical size, resolve the encode path (zero-copy vs readback), and spawn the delivery (and readback-mode encode) threads. The single-display behavior of the former global StartCapture is preserved exactly for display 0. Bring up the readback encode path (pixman readback → pool → encode thread) for a capture whose zero-copy session is absent or just died. Mirrors the start-capture bootstrap: u64::MAX content generations mark every pool slot stale so each one is read back before its first publish, whatever the damage says. - bootstrap_
zero_ 🔒copy_ pool - Start a zero-copy capture’s encode thread on
encoder, a session the calloop opened on the renderer’s EGL display, onring’s render targets. - build_
readback_ 🔒encoders - Build the readback-mode frame encoder on the thread that will own and drive it: the shared
ladder with host frames (
rgbafor a GLES readback, BGRA otherwise), with the GPU rungs skipped whentry_gpuis off (the recovery ladder’s demotion).Noneis the CPU path, whereencode_cpubuilds its own per-region state. A compatible NVENC session handed over from the previous encode thread is reconfigured in place rather than rebuilt, which would stall the stream. - cancel_
slots 🔒 - Hand back the pool reservations a render tick made and did not publish.
- capture_
period 🔒 - The frame period a capture renders at.
- card_
matches_ 🔒token - Does a card match the requested token? Accepted token forms, checked against the identity the kernel itself reports: a kernel DRIVER name (exact, no table), a raw PCI vendor ID (“0x10de”/“10de”), a devicetree vendor prefix (literal first segment of any compatible), or a human vendor name resolved through the small embedded alias maps above.
- compose_
host_ 🔒frame - Re-compose
src(the retained host frame) intotarget, with the watermark whereoverlayis given: the path a moving watermark needs, since re-drawing over the same retained buffer would leave trails. - composited_
target 🔒 - The GPU target the last render composited into:
source, a zero-copy capture’s target still on the calloop, or else the output’s own. - composition_
elements 🔒 - The render elements of an output’s frame, topmost first: the software cursor, a drag’s icon,
the watermark, the window manager’s overlays, the overlay and top wlr-layer layers, the windows
with their frames, and the bottom and background layers. One list for both renderers, read
by
scanout_candidatebefore anything is drawn. - configure_
windows_ 🔒for_ mode - copy_
rows_ 🔒into_ shm - Copy full rows from
src(tightwidth*4stride) into an Argb8888/Xrgb8888 shm buffer, honoring the destination’s own offset and stride. - create_
dmabuf_ 🔒from_ bo - Export the offscreen GBM render target as a Dmabuf so the very same GPU pixels can be both rendered into and encoded with no intervening copy — the linchpin of the zero-copy capture path. The returned dmabuf is the one handle the GLES renderer binds as its framebuffer AND a hardware encoder (NVENC through CUDA, or VAAPI) imports to read those pixels directly, which only works if the buffer is described precisely enough (fd, stride, DRM modifier) for the importer to interpret it. The render targets and host capture slots have one plane; retain the BO’s format so an opaque host slot is not imported as a buffer with alpha.
- create_
output_ 🔒on - Create an additional output mapped into the layout at
(x, y). Fails (false) on a duplicate id, non-positive geometry/scale, a rectangle overlapping a live output, or a GPU render-target allocation failure. Only Create/Reposition placements are validated: a capture reconfigure (StartCapture on an existing output) resizes UNVALIDATED, so keeping a multi-step relayout overlap-free at every step is the caller’s ordering responsibility. - create_
view_ 🔒on - Add a view: a display over a rectangle of
owner’s output. - cursor_
surface_ 🔒hotspot - Hotspot of a surface-backed cursor, in the surface’s logical coordinates (zero when the client set none). The sprite is placed at pointer - hotspot, like named and X11 cursors.
- desktop_
view - The desktop as the window policy installed on the running compositor holds it;
Nonewhere no compositor runs or none has a policy. - destroy_
output_ 🔒on - Destroy a secondary output: end its capture, relocate its windows onto the primary output, unmap it from the space, and retract its global. The primary (id 0) is refused.
- detach_
retained_ 🔒gpu HostSession::detach_retainedfor a GPU frame, which only a zero-copy session takes: the renderer copies it into a buffer of the capture’s own, which re-encodes then read, and the stream’s buffer goes back once no frame in flight reads it.- draw_
host_ 🔒watermark - One output’s render + capture tick: composite the elements overlapping this output
(positions made output-local by subtracting its layout origin), track damage, feed the
display’s own encode path, and answer a pending screenshot on the primary. Returns true
when the tick was skipped because this display’s encode pool was still busy (the caller
then retries shortly instead of waiting a full frame interval).
Stamp the watermark element onto
targetin place — no clear, so the captured content underneath stays — returning the draw’s sync point for the encoder to wait on. - driver_
selects_ 🔒nvenc - Whether an encode node’s driver name routes to NVENC: NVIDIA, or unknown (no readable render-node sysfs entry, the usual shape of an NVIDIA container without /dev/dri), so the NVENC attempt runs before the CPU fallback. Any other named driver routes to VA-API. Shared by the X11 and Wayland paths so both pick the same encoder for the same node.
- drives_
screen 🔒 - Views share one output, so its clients are driven once a frame, by the fastest display capturing that screen (ties go to the lowest number) – a client asked to draw once per view would render as many times a frame as there are displays over it, one asked by nobody (the screen itself is often captured by views alone) would freeze on the frame it first painted, and one driven by a slower sibling would stream stale frames on the faster display.
- egl_
display 🔒 - An EGL display on
native, or an error where libEGL is not installed. - egl_
library 🔒 - Whether
EGL_LIBRARYopens. It is opened once and kept open, so smithay’s own open of it finds it loaded. - encode_
shm_ 🔒rows - Run
encodeon the rows of a client’s shm buffer a frame names (WlFrame::shm),heightrows ofwidthpixels packed four bytes each: the pool’s mapping is valid only inside this call, so the encode runs inside it.SHM_GONEwhere the pool was resized or destroyed under the frame. - encoder_
desc 🔒 - Compose the encoder half of the 1 s debug log line (backend, colorspace, rate control) for whichever thread owns the encoders.
- end_
zero_ 🔒copy - Stop a capture’s zero-copy session: its encode thread ends after the frame in flight, and its render targets go. False where none ran.
- ensure_
compositor - Get-or-create the singleton Wayland backend (idempotent: the first dimensions and render node win, and a later capture just resizes).
- ensure_
wayland_ display - Bring up the process-wide compositor (
ensure_compositor) and return its socket name, so a consumer can placeWAYLAND_DISPLAYfor the clients it starts; empty where the compositor published no socket within five seconds. - find_
output_ 🔒overlap - The first live screen (excluding
skip_id; views stand behind their owner’s rectangle) whose rectangle overlaps a candidate placement. Both rectangle flavors are checked — logical (Space layout, scale-divided) and physical (mode pixels at the same origin) — because input injection and cursor compositing key off the physical rects while window layout keys off the logical ones, and neither may overlap. - first_
render_ 🔒node_ index - The index of the first render node present, which a capture that names none encodes on: renderD128 need not exist, as in a container handed one GPU of several.
- frame_
period 🔒 - The frame period for a target rate. Settings are sanitized at the API boundary; a rate
outside the sane range takes the default here so a non-finite one never reaches
Duration::from_secs_f64. - get_
gpu_ 🔒driver - Read the kernel driver bound to a render node for encoder routing.
- get_
process_ 🔒rss_ bytes - get_
shm_ 🔒usage_ bytes - gl_
renderer_ 🔒name GL_RENDERERof a live renderer — the driver that actually answered, which is the only thing that separates a GPU from Mesa’s software fallback on the same node. Empty when the context cannot be made current or the string is unavailable.- gpu_
bring_ 🔒up - The GL renderer on
device_path, the dmabuf formats it imports, and its offscreen render target atwidthbyheight. It touches no Wayland global: the caller advertises the GPU only once this succeeded, since a dmabuf global left behind by a failed start (a render node whose EGL comes up but whose allocator refuses the target, as on a virtual GPU) offered the GL renderer’s formats to a compositor rendering in Pixman, which refused every buffer, and a nested kwin_wayland died of the protocol error. - gpu_
exposed 🔒 - A GPU is exposed to this container or machine.
- gpu_
render_ 🔒init - Bring the hardware renderer up on a DRM render node: the GBM device, the EGL display and context it backs, the GLES renderer, and the allocator the render targets come from.
- hardware_
encoders - The hardware encoders of the encode node
encode_node_indexnames (-1for none, below-1for theauto_gpupick or the first render node), by codec: the backend’s name and the formats it codes the codec in. - host_
layout_ 🔒resolution - What the mode a host announced means for a capture configured at
want, once the host has ruled on the layout request:Some(size)when the capture must follow a different mode the host runs,Nonewhen nothing changes — the host runs the requested size, or its mode is unknown and the capture keeps gating on the size it asked for. The verdict itself is not consulted: a host may answersucceededfor a mode it did not take (Hyprland acknowledges a custom mode it rejected), so the announced size decides. It has arrived by the time any verdict does, both riding the one host connection in order. - install_
window_ policy - Install a window policy on the running compositor; an error where none runs.
- join_
within 🔒 - Join
handleif it ends withinSTOP_JOIN_TIMEOUT, else leave the thread behind with one line saying so: a thread held inside a driver call that never returns must not hold the stop, and with it every later capture of the session, with nothing said. - live_
x11 🔒 - log_
stream_ 🔒settings - One-shot “Stream settings active” line, printed by the thread that owns the encoders once the selection is final (calloop for zero-copy, encode thread for readback, the X11 zero-copy backends at their start), tagged for the backend that prints it.
- log_
stream_ 🔒settings_ of - The “Stream settings active” line for a backend named outright,
(name, hardware), where the session is not aFrameEncoder(NvFBC’s own NVENC session);Noneis the CPU path,fixed_rateis the backend’s one rate control where it has one (FrameEncoder::fixed_rate_control),holdssays whether the session holds a cleanup at the quantizer asked for (FrameEncoder::holds_quantizer), andcappedwhether a constant quality runs under the peak ofvideo_bitrate_kbps(encoders::session_capped). - memfd_
create 🔒 memfd_create(2)through the raw system call: glibc wraps it only from 2.27, and the wheels hold to the glibc 2.17 manylinux floor. The descriptor, or -1 witherrnoset.- node_
gpu_ 🔒name - The name the GL driver gives the GPU behind render node
node_index, asked once per node and remembered: what a VA-API session, which names no device, is reported to run on. Empty where no GL context comes up on the node. - note_
direct 🔒 - Say, at debug, that a display’s frames moved between compositing and a client’s buffer, or are composited for a new reason: the choice is made every frame, so only its change is worth a line.
- parse_
auto_ 🔒gpu - Parse an auto-GPU request (the CaptureSettings
auto_gpufield, which selkies fills from –auto-gpu / SELKIES_AUTO_GPU).None= disabled;Some(None)= pick the first GPU overall (“true”);Some(Some(token))= pick the first GPU whose kernel identity matches the case-insensitive token (vendor name, kernel driver name, devicetree vendor prefix, or raw PCI vendor id). - probe_
wayland_ gpu - Probe the render node the compositor would run on:
render_nodeexplicitly, else theauto_gpupick. Allocates one GBM buffer, exports it, and opens a GL context on it, so a deployment learns before starting whether the compositor renders in hardware. - publish_
socket_ 🔒name - push_
ghost_ 🔒elements - Push the ghosts of closing windows the policy animates, over the windows: each its last picture and frame scaled and blended through its effect.
- push_
layer_ 🔒elements - Push an output’s wlr-layer surfaces on
target_layerintoelements, top-most first, the orderlayer_map.layers()reversed gives, each with its popups and subsurfaces. Shared by both renderers and by both of a frame’s layer passes, which differ only in the layers they name. - push_
window_ 🔒elements - Push a window’s elements for an output at
originandscale: through the window policy’s effect where one runs on it (the surface tree scaled into the effect’s rectangle and blended at its alpha, the frame parts scaled and blended with it), else as the window stands with its frame parts beside it. - read_
back_ 🔒rgba - Reads
targetback as RGBA intodst. The read runs in a frame of the target: a dmabuf bound as a texture gets a framebuffer of its own on each bind, which only a frame makes current, so a bare read on a tick that drew nothing would read another framebuffer. - read_
back_ 🔒rgba_ async - Start reading the bound target back into a pixel-pack buffer and return the readback the
caller waits for, or
Nonewhere the buffers are unavailable. - read_
back_ 🔒rgba_ result - Screenshot reads also check GL errors before any pixels can be handed to the caller.
- read_
card_ 🔒identity - Read a DRM card’s
CardIdentityfrom sysfs. - realized_
geometry 🔒 - The geometry
get_realized_geometryreports fordisplay_id: the live capture’s size and scale, else the output’s current mode, else zeros for an unknown display. - reap_
dead_ 🔒host - Drop a host-capture session whose compositor connection has ended, stopping every
capture it fed: their displays leave the alive set, so
is_capturingturns false and the consumer rebuilds them (the next start reconnects), and no input is written to a dead connection. The same signal X11 gives when its capture thread ends. - reconcile_
host_ 🔒layouts - Settle the host layout requests answered since the last tick, against the mode each host announced. A host already running the requested size needs nothing: the capture was configured for it and its frames flow. A host running anything else — a request it refused, acknowledged without applying, or never answered by the deadline, and a host with no layout management at all — has the capture re-sized to that mode through the same in-place reconfigure a resize takes, so it never gates on a size the host will not produce, and the mismatch becomes the capture’s caveat. Geometry readers parked behind a request answer once it is settled, with the size actually captured.
- recording_
status - The recording in progress or last finished,
Nonewhere none was started. - rects_
overlap 🔒 - True when the rectangles
(x, y, w, h)overlap: strict interior intersection, so touching edges do not count and empty (non-positive-dimension) rectangles never overlap anything. Arithmetic is widened so extreme coordinates cannot wrap. - release_
host_ 🔒inflight - Hand back to the host the frames its compositor blitted that a zero-copy encode no longer
reads: each once the encode thread is done with every frame published from it, or all of
them (
all) where the capture is going away. - release_
scanout_ 🔒inflight - Let go of the client buffers encoded as they stood that no frame in flight reads anymore
(
WlCapture::scanout_inflight), or all of them (all) where the capture or its zero-copy session is going away: the last hold on one releases it to its client. - render_
display 🔒 - One display’s render tick, for a trigger that concerns that display alone.
- render_
node_ 🔒index - The index
encode_node_indexcounts a render node path by (/dev/dri/renderD{128 + idx}). - render_
node_ 🔒tick - One display’s frame: decide whether this tick produces one, reserve a pool buffer, take the host’s frame or compose the output’s own, publish it to the encode thread, and answer the clients’ frame callbacks and presentation feedback. True where the encoder still holds every pool buffer and the tick is skipped.
- render_
pass 🔒 - Render every output that is due for
trigger, and say whether a readback pool was still busy. The nodes are taken out of the state so each per-output render can borrow the shared renderer/space alongside its own damage tracker and buffers. - reposition_
output_ 🔒on - Move an existing output (the primary included) to layout offset
(x, y). The output’s advertised position, its Space mapping, and the windows placed on it (mapped at the output’s origin — window positions are output-relative under forced fullscreen) all follow, so absolute input injection and cursor compositing — both keyed offnode.pos— resolve against the new layout immediately. A destination overlapping another live output is refused (false). As withCreateOutput, only the placement itself is validated: a capture reconfigure (StartCapture on an existing output) resizes UNVALIDATED, so keeping a multi-step relayout overlap-free at every step is the caller’s ordering responsibility. - resize_
output_ 🔒on - retain_
host_ 🔒frame - Keep
frameas the display’s host content. Under a zero-copy session the frame it replaces goes back to the host only once no frame in flight to the encode thread reads it. - run_
wayland_ 🔒thread - The main execution loop of the Wayland backend.
- running_
compositor - The live Wayland backend, if any — never creates one. The pre-capture entry points (input injection, cursor/config setters) use this so they can’t lock in a backend with a placeholder render node.
- scanout_
candidate 🔒 - The surface whose buffer is an output’s frame as it stands, or why there is none.
- scanout_
dmabuf 🔒 - A client’s dmabuf the zero-copy session encodes as it stands, with its description for the
log, and a hold on the buffer, or why the session cannot take it: both hardware sessions
import one of the four packed 8-bit RGB formats at their own width and height, so only a
buffer of the capture’s size qualifies, and of no more planes than the session’s encoder
imports (
planes: VA-API maps a compressed layout’s metadata plane, NVENC one plane). What the driver then makes of its modifier and pitch only the import says, and a refusal there ends the direct path for the capture (WlEncodeControls::scanout_refused). - scanout_
shm 🔒 - A client’s shm buffer the readback path encodes where it lies, with its description for
the log, or why not: the rows have to be what the pool’s are, B,G,R,A (
ARGB8888orXRGB8888) packed at the capture’s width, since the encoders read them at that pitch. - screenshot_
len 🔒 - The byte length of one
widthxheightRGBA frame,Nonefor an empty or overflowing one. - screenshot_
pixels 🔒 - Copy exactly one complete frame into a caller’s buffer, excluding any scratch-buffer capacity from an older size.
- screenshot_
png - A PNG of display
displayas the capture backend sees it now. - send_
cursor_ 🔒frame - Answer the frame callbacks of a surface-backed cursor. Xwayland and libwayland-cursor clients wait for the cursor surface’s frame callback before attaching their next sprite, so a cursor surface that never gets one stops updating after its first sprite.
- serve_
changes 🔒 - Render each capture a change waits on (
WlCapture::change_pending: a client’s commit, input, a host frame) once its period is open: a change takes its period’s frame as it lands, the X11 captures’ schedule, rather than waiting for the frame timer. A capture whose period a frame already took keeps waiting, and the timer wakes as the next one opens. - service_
copy_ 🔒frames - Complete parked ext-image-copy-capture frames for this output from the frame the
render pass just produced. A dmabuf target is filled by one GPU blit from the
composited buffer (
composited_target); an shm target by one readback (GLES) or one row copy (pixman). With no damage, a session that has already received content keeps its frame parked, so a static screen costs capture clients nothing. - set_
wayland_ 🔒active_ codec - set_
wayland_ 🔒capture_ err - shm_
usage_ 🔒in - Blocks actually allocated, not apparent size, so a sparse file is not reported as memory the process took. Feeds the debug log line only.
- shutdown
- Stop every live capture ahead of the process’s exit, and raise the consumer-gone gate first so no detached thread calls a sink again.
- start_
capture_ 🔒on_ display - start_
recording - Start the built-in MP4 recorder on
path: withsettings, a recorder-owned H.264 capture of the display and backend they name (any other codec is refused); without, the capture thePIXELFLUX_RECORD_*environment describes.audio_socketnames pcmflux’s recording socket, empty for none. - start_
xwayland - Start Xwayland on the running compositor;
notifyis told the display number. - stop_
capture_ 🔒on_ display - Stop the capture bound to
display_id, leaving the output (and every other display’s capture) running. - stop_
recording - Finalize the recording in progress and report what it wrote.
- stream_
settings_ 🔒line - The text of the “Stream settings active” line, which says what the session applies rather
than what it was asked for (
encoders::rate_desc,encoders::paint_over_desc). - teardown_
capture 🔒 - Tear down a capture’s encode/delivery threads and pools, returning both join handles as
(deliver, encode)for the caller to place. NEITHER is joined here: this runs on the calloop thread, which also dispatches Wayland clients and input, and either thread can be parked behind a consumer still inside Python. A restart hands each handle to its successor thread, which joins it off the event loop — the encode successor inheriting the readback hardware session, the deliver successor keeping stripes in capture order across the restart — and a plain stop hands both to the reaper. The zero-copy session (if any) is left on the capture for the caller to reuse or drop, and so are the host frames its thread may still be reading (release_host_inflight). - unpremultiply_
rgba 🔒 - Convert premultiplied-alpha RGBA pixels to the straight alpha PNG expects. Every cursor
source feeding the callback stores premultiplied color (XFixes and Xcursor by format
definition, wl_shm/dmabuf by Wayland convention); encoding those values as straight
alpha double-darkens antialiased edges. Rounds as
(c * 255 + a/2) / a, clamped — selkies’ python seed path mirrors this exact integer math so both sources hash a cursor to the same content handle. - validate_
host_ 🔒screenshot - Reject truncated host mappings instead of retaining pixels from an earlier frame.
- wait_
render_ 🔒fence - Wait, bounded, for a render’s fence to signal before the tick reuses or releases the buffers it sampled.
- wait_
socket_ 🔒name - Wait (bounded) for the compositor thread to publish its socket name.
- warn_
once_ 🔒host_ watermark - wayland_
active_ 🔒codec - The codec each Wayland display’s capture streams, as
Codec::id, read byScreenCapture::active_codecwithout a round trip to the compositor thread. - wayland_
alive 🔒 - wayland_
capture_ 🔒err - wayland_
capture_ 🔒state (state, last_error)fordisplay_id’s Wayland capture, from the shared liveness and outcome maps (no command round-trip).- wayland_
display_ name - The socket name of the running compositor,
Nonewithout one or where it published none within two seconds. - wayland_
encode_ 🔒loop - Encode-thread body for the Wayland captures: drain published frames and run the full encode dispatch, driving the encoders for the life of the capture.
- wayland_
owners 🔒 - wayland_
update_ 🔒link_ rate - Forward the rate a display’s path carries to the shared Wayland backend (no-op if none is running).
- wayland_
update_ 🔒rate - Forward a live rate change to the shared Wayland backend (no-op if none is running).
- wayland_
update_ 🔒tunables - Forward live per-frame tunables to the shared Wayland backend (no-op if none is running).
- window_
request - Ask the window policy installed on the running compositor to act on a window; false where no compositor runs or none has a policy.
- windows_
on_ 🔒display - Resize a screen’s output, keeping its position, so the displays cut out of it can be laid out over a different desk.
Type Aliases§
- Buffer
Storage 🔒Fn glBufferStorageEXT, which GLES has only throughGL_EXT_buffer_storage.- Output
Desc - One live output as
(id, x, y, width, height, scale, capturing). Sizes are physical pixels and(x, y)is the layout offset. - Output
Overlap 🔒 - An overlapping output as
(id, flavor, rect), where flavor names which rectangle pair matched and rect is(x, y, width, height). - Window
Desc - One mapped window as
(window_id, title, app_id, output_id, parked). A parked window has no output yet — a nested session’s spare screens.