feat(kvm): DRM/KMS scanout capture via gbm readback (VA-104 part 2) #108

Merged
David merged 2 commits from fix/VA-104-drm-egl-readback into main 2026-07-04 03:08:10 +02:00
Owner

Second increment of #VA-104: the capture half. Together with the merged input backend (#106), this is the complete consent-free, unattended Wayland desktop for KWin / GNOME.

What

drm_kms.rs + drm_gbm.rs: read the active CRTC's scanout framebuffer straight off /dev/dri as root (drmModeGetFB2 -> DMA-BUF export), then read the pixels back through libgbm (gbm_bo_import + gbm_bo_map), which has the GPU driver blit the VRAM/tiled buffer into a CPU-visible linear region. No portal, no consent, no compositor cooperation - the path grim (wlr-screencopy) and the portal can't provide on KWin/GNOME.

  • Handles 8-bit RGBX/BGRX and 10-bit 2:10:10:10 scanout formats.
  • Wired as the primary backend in wayland::capture_rgb, ahead of grim then the portal, with a process-wide unavailable latch for non-root / headless hosts.
  • libgbm is dlopen'd (like libpipewire) so the binary carries no hard link and still runs headless; libloading is now a base crate dependency.
  • A drm_probe example + just drm-probe / just deploy-local recipes for the on-host dev loop.

Verified live

On an AMD Ryzen iGPU + KWin Wayland host (the dev machine): the probe captures a correct 3840x2160 ABGR2101010 screenshot with no consent prompt (verified real content, not garbage - 249/256 distinct byte values, varied regions). The full agent builds, deploys, and runs with this path selected. Unit tests cover the pixel-format conversion; just pre-commit green.

Result

  • Capture: DRM/KMS scanout, consent-free, works on KWin (this PR).
  • Input: uinput pointer+keyboard, consent-free (#106, merged).

A root system agent now drives a logged-in KWin Wayland desktop end-to-end with zero interactive consent - the requirement that motivated VA-104.

Second increment of #VA-104: the **capture** half. Together with the merged input backend (#106), this is the complete consent-free, unattended Wayland desktop for KWin / GNOME. ## What `drm_kms.rs` + `drm_gbm.rs`: read the active CRTC's scanout framebuffer straight off `/dev/dri` as root (`drmModeGetFB2` -> DMA-BUF export), then read the pixels back through **libgbm** (`gbm_bo_import` + `gbm_bo_map`), which has the GPU driver blit the VRAM/tiled buffer into a CPU-visible linear region. No portal, no consent, no compositor cooperation - the path grim (wlr-screencopy) and the portal can't provide on KWin/GNOME. - Handles 8-bit RGBX/BGRX and 10-bit `2:10:10:10` scanout formats. - Wired as the **primary** backend in `wayland::capture_rgb`, ahead of grim then the portal, with a process-wide unavailable latch for non-root / headless hosts. - `libgbm` is `dlopen`'d (like libpipewire) so the binary carries no hard link and still runs headless; `libloading` is now a base crate dependency. - A `drm_probe` example + `just drm-probe` / `just deploy-local` recipes for the on-host dev loop. ## Verified live On an AMD Ryzen iGPU + KWin Wayland host (the dev machine): the probe captures a **correct 3840x2160 ABGR2101010 screenshot with no consent prompt** (verified real content, not garbage - 249/256 distinct byte values, varied regions). The full agent builds, deploys, and runs with this path selected. Unit tests cover the pixel-format conversion; `just pre-commit` green. ## Result - **Capture**: DRM/KMS scanout, consent-free, works on KWin (this PR). - **Input**: uinput pointer+keyboard, consent-free (#106, merged). A root system agent now drives a logged-in KWin Wayland desktop end-to-end with zero interactive consent - the requirement that motivated VA-104.
feat(kvm): DRM/KMS scanout capture via gbm readback (VA-104)
Some checks failed
Check / fmt + clippy + tests (pull_request) Failing after 16m0s
92b1f07d63
Complete the consent-free Wayland capture path: read the active CRTC's scanout DMA-BUF back through libgbm (dlopen'd), which has the driver blit the VRAM/tiled buffer into a CPU-visible linear region. This is the piece that makes KWin/GNOME capture work unattended, where a plain mmap of the DMA-BUF is denied (AMD scanout lives in VRAM) or would read tiled garbage.

Handles 8-bit RGBX/BGRX and 10-bit 2:10:10:10 scanout formats. Wires DRM/KMS in as the primary backend in wayland::capture_rgb, ahead of grim and the portal, with a process-wide unavailable latch for non-root / headless hosts. libgbm is dlopen'd (like libpipewire) so the binary carries no hard link and still runs headless; libloading is now a base dependency of the crate.

Verified live on an AMD Ryzen iGPU + KWin Wayland host: the probe captures a correct 3840x2160 ABGR2101010 screenshot with no consent prompt. Unit-tests the pixel-format conversion.

#VA-104

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(kvm): split uinput into separate pointer and keyboard devices
All checks were successful
Check / fmt + clippy + tests (pull_request) Successful in 5m24s
Create release / Create release from merged PR (pull_request) Has been skipped
e26fd95452
libinput ignores a single uinput device that mixes ABS_X/Y with keyboard keys and buttons - it reads as an unclassifiable absolute / joystick device - and then drops it, which silently killed both mouse and keyboard on KWin / GNOME (the device was created but no events reached the compositor). Register two plain, unambiguous devices instead: a relative pointer (REL_X/Y, wheels, BTN_LEFT/RIGHT/MIDDLE/SIDE/EXTRA) and a keyboard (KEY_* only), which libinput always accepts. The browser's absolute coordinates are converted to relative deltas.

#VA-104

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
David scheduled this pull request to auto merge when all checks succeed 2026-07-04 03:05:02 +02:00
David merged commit a67900d67f into main 2026-07-04 03:08:10 +02:00
David deleted branch fix/VA-104-drm-egl-readback 2026-07-04 03:08:10 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/vervain-agent!108
No description provided.