Skip to content

feat: support ext-image-copy-capture-v1 (SHM target)#14

Draft
Ca-moes wants to merge 1 commit into
linuxserver:masterfrom
Ca-moes:feature/image-copy-capture
Draft

feat: support ext-image-copy-capture-v1 (SHM target)#14
Ca-moes wants to merge 1 commit into
linuxserver:masterfrom
Ca-moes:feature/image-copy-capture

Conversation

@Ca-moes
Copy link
Copy Markdown
Contributor

@Ca-moes Ca-moes commented Apr 28, 2026

What

Adds compositor-side support for ext-image-copy-capture-v1, the modern replacement for wlr-screencopy, so any standards-compliant Wayland client can capture pixelflux's output via the canonical screen-capture protocol.

Why

Smithay shipped ext-image-copy-capture-v1 support in PR #1902. Pixelflux is a natural place to expose it — the rest of pixelflux's protocol surface already targets compositor-aware clients, and the new handler traits map cleanly onto pixelflux's existing pixman backbuffer.

What's in this PR

  • Bumps the smithay rev to pull in PR #1902
  • Adds four AppState fields (3 protocol-state structs + 1 Vec<Session> helper)
  • Implements ImageCaptureSourceHandler, OutputCaptureSourceHandler, ImageCopyCaptureHandler
  • Wires the three corresponding delegate_*! macros
  • Implements the SHM-target frame copy: row-by-row memcpy from the pixman backbuffer to the client's mmap'd SHM region, advertising a single Argb8888 format that matches the backbuffer's pixel layout

Scope — SHM-only first cut

This PR handles SHM target buffers. DMA-BUF target buffers are rejected with FailureReason::BufferConstraints. Adding the GLES path (mirroring the existing cursor-readback at pixelflux_wayland/src/wayland/frontend.rs:486-673: renderer.bindcopy_framebuffermap_texture) is straightforward and can land as a follow-up — splitting it keeps this PR focused on the SHM core and the protocol wiring.

Happy to fold DMA-BUF into this PR pre-merge if maintainers prefer a single-shot landing.

Validation

cargo check and cargo build --release clean against the new smithay rev.

wayland-info against a running pixelflux Smithay reports the new globals at version 1, alongside the pre-existing pixelflux protocols — no regressions:

$ wayland-info | grep -E "image_copy_capture|image_capture_source"
interface: 'ext_output_image_capture_source_manager_v1', version: 1, name: 21
interface: 'ext_image_copy_capture_manager_v1',          version: 1, name: 22

$ wayland-info | grep -c "interface:"
24    # 22 pre-existing pixelflux globals + 2 new ones

Validation gap I'd appreciate help closing: the SHM frame() body itself wasn't exercised end-to-end locally — wl-screenrec (the obvious off-the-shelf consumer) is DMA-BUF-only and the dev environment had no GPU, so I couldn't drive a real capture round-trip. Code review plus Smithay's own test contract is the current floor. A reviewer with GPU access (or willing to run a small SHM test client) would close the gap definitively.

Smithay PR #1902 (merged 2026-02-03) added compositor-side support for
ext-image-copy-capture-v1, the modern replacement for wlr-screencopy.
This change advances pixelflux's smithay rev past that PR and wires the
three new handler traits so any compositor client can capture the
pixelflux output via the standard protocol.

Implementation:
- Bump smithay rev to 5de53056 to pull in PR #1902
- Add four AppState fields: ImageCaptureSourceState,
  OutputCaptureSourceState, ImageCopyCaptureState, and a Vec<Session>
  helper that tracks live capture sessions
- Implement ImageCaptureSourceHandler (source_destroyed stub),
  OutputCaptureSourceHandler (stores WeakOutput in user_data on
  output_source_created), and ImageCopyCaptureHandler
- ImageCopyCaptureHandler::capture_constraints advertises a single SHM
  format (Argb8888, matching the pixman backbuffer); dma is None for
  this first cut
- ImageCopyCaptureHandler::new_session pushes constraints immediately
  on bind so clients don't hang waiting for a buffer_size event
- ImageCopyCaptureHandler::frame implements the SHM scanout copy:
  validates buffer kind/format/dimensions/stride, performs row-by-row
  memcpy from the pixman backbuffer, and calls frame.success with the
  monotonic clock. Every error path calls frame.fail() explicitly to
  avoid Smithay's Frame::Drop fallback.
- Add delegate_image_copy_capture!, delegate_image_capture_source!,
  and delegate_output_capture_source! macros

DMA-BUF target buffer support (and the GLES copy_framebuffer/
map_texture path mirroring the cursor readback at frontend.rs:486-673)
is deferred to a follow-up PR.

Verified:
- cargo check / cargo build --release clean
- wayland-info reports both new globals
  (ext_output_image_capture_source_manager_v1,
  ext_image_copy_capture_manager_v1) at version 1, alongside the 22
  pre-existing pixelflux globals — no regressions
@thelamer
Copy link
Copy Markdown
Member

While I see your why section and explainers, I need a human reason for this as it is bumping Smithay and will need a good bit of testing to get into my downstream images. I am not trying to hold this project back to please my needs but they are interconnected and we have just recently reached a high level of stability across our platform.

What do you specifically need this for how are you using it?

@thelamer
Copy link
Copy Markdown
Member

thelamer commented May 4, 2026

Also I need a testing method for this, wayvnc was the only thing I found as a tool to hook into this proto and it just shows a gray screen for me.

@Ca-moes
Copy link
Copy Markdown
Contributor Author

Ca-moes commented May 5, 2026

Hey @thelamer — fair questions, sorry for the slow reply. Both of yours converge on the same answer, so let me lay the cards out honestly.

What we needed this for — the PR was the protocol-side half of a session-recording prototype. The plan: advertise ext-image-copy-capture-v1 from pixelflux's Smithay so an off-the-shelf recorder (specifically wl-screenrec) could attach as a sidecar and write rotating segments to disk. Two findings killed that plan for our deployment:

  1. wl-screenrec is DMA-BUF-only — --no-hw only switches the encoder to libx264, it doesn't switch the capture-buffer transport off DMA-BUF.
  2. Pixelflux only registers zwp_linux_dmabuf when GPU init succeeds (the if use_gpu block in lib.rs::run_wayland_thread). Our deployment runs without a GPU and uses PixmanRenderer end-to-end.

Net: in a no-GPU deployment, no DMA-BUF global → wl-screenrec exits immediately. And there's no off-the-shelf SHM-capable recorder for the new protocol that I could find — wayvnc isn't a file recorder, wayshot does single frames, wluma is a backlight controller.

We pivoted to the encoded-stripe tap path (PR #15, which you merged). That's what we're actually using; this PR is a spec-driven contribution that turned out not to be load-bearing for us.

On wayvnc showing gray — this is the more honest blocker. The PR body already flagged that I couldn't drive the SHM frame() body end-to-end locally (no GPU dev environment, and wl-screenrec was the only off-the-shelf consumer I had on hand). Your gray-screen result is consistent with a bug in the SHM scanout copy itself — most likely a format or stride mismatch between the advertised Argb8888 and what the pixman backbuffer actually emits in memory order (BGRA). I don't currently have an environment where I can productively iterate on it.

Given (a) there's no downstream consumer driving this anymore, (b) it bumps your Smithay rev for stability you've worked hard to land, and (c) the SHM path doesn't actually round-trip yet — I don't want to push you to merge this. Happy to convert it to draft and leave the branch open as a starting point for any future contributor with a GPU-equipped test setup, or close it outright if you'd rather not keep stale work in the PR queue. Your call.

@thelamer thelamer marked this pull request as draft May 5, 2026 12:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants