Skip to content

fix(cua-driver): fail closed on unproven Wayland window capture - #2964

Closed
injaneity wants to merge 1 commit into
mainfrom
codex/2962-wayland-fail-closed
Closed

fix(cua-driver): fail closed on unproven Wayland window capture#2964
injaneity wants to merge 1 commit into
mainfrom
codex/2962-wayland-fail-closed

Conversation

@injaneity

Copy link
Copy Markdown
Collaborator

What changed

  • stop treating Wayland output crops as identified window screenshots
  • return a typed surface_identity_unproven screenshot error while preserving the truthful accessibility tree from get_window_state
  • keep X11 on its existing scoped per-window capture path
  • apply the same fail-closed boundary to other Linux window-capture callers

Why

On opted-in Wayland, get_window_state captured the current output and cropped it using the requested window geometry. For an off-workspace XWayland window, those coordinates can select unrelated pixels from the active workspace and present them as the target window.

Output capture and geometry do not prove surface identity. Until a compositor-backed identified per-window capture path is available, omitting the screenshot is safer than returning plausible pixels from another application.

Fixes #2962.

User impact

Wayland window-scoped capture now omits unprovable pixels and reports:

{
  "screenshot_frame_valid": false,
  "screenshot_error": {
    "code": "surface_identity_unproven"
  }
}

The accessibility tree remains available. Explicit full-display screenshots remain available, and X11 per-window capture is unchanged.

Validation

Candidate: 4bdee4c2119780d43d9e60cf3f44afda9c0c91cb

  • cargo fmt --check --all
  • git diff --check
  • focused regression tests:
    • wayland_window_capture_fails_closed_without_using_x11_pixels
    • x11_window_capture_keeps_the_existing_per_window_path
    • surface_identity_failure_is_a_typed_screenshot_error
  • cargo test -p platform-linux --lib: 264 passed, 0 failed, 4 existing environment-dependent tests ignored

Runtime limitation

This host has no Hyprland binary/process, hyprctl, Wayland/X11 display variables, or desktop session bus. The exact Hyprland + XWayland off-workspace topology from #2962 could not be reproduced here, so this PR claims source-level and unit-level verification only. The regression test proves the unsafe output/X11 fallback is not invoked at the dispatch boundary; compositor runtime certification remains outstanding.

@injaneity
injaneity marked this pull request as ready for review August 10, 2026 09:57
@injaneity
injaneity requested a review from f-trycua as a code owner August 10, 2026 09:57
@RodriMora

Copy link
Copy Markdown

I’m validating the exact candidate 4bdee4c2119780d43d9e60cf3f44afda9c0c91cb on a real Arch/Omarchy Hyprland 0.56 mixed Wayland+XWayland session. Scope: reproduce the off-workspace XWayland topology from #2962 with repo-owned fixtures, verify get_window_state fails closed without returning unrelated pixels, and record source/runtime evidence here. I will not open a competing implementation.

@RodriMora

Copy link
Copy Markdown

Hyprland runtime result for exact candidate 4bdee4c2119780d43d9e60cf3f44afda9c0c91cb: the #2962 fail-closed behavior passes.

Environment: Arch/Omarchy, Hyprland 0.56, mixed Wayland+XWayland, two outputs at 1.25×/1.5×. I launched the repo-owned Electron fixture as a true XWayland window on inactive workspace 98 while workspace 1 remained active. get_window_state returned 109 AT-SPI elements / 13,557 tree characters, screenshot_frame_valid:false, and screenshot_error.code:"surface_identity_unproven"; it returned no image bytes, file, or dimensions, wrote no requested screenshot file, and did not change active workspaces. Focused unit tests and the 268-test platform-linux --features portal-input suite also pass.

Blocker before readiness: this patch refuses screenshots for every Wayland window, not only the unproven Hyprland/off-workspace case. That conflicts with the accepted Sway window/capture contract and the canonical matrix, whose PX rows require screenshot_width and whose evidence validator requires non-empty pre/post target images. The branch is also 57 commits behind current main.

I’m adapting this existing PR rather than opening a competitor: rebase onto current main, use compositor-identified hyprland-toplevel-export-v1 capture when Hyprland can bind the requested window, retain the typed fail-closed result when identity/capture cannot be proven, and preserve the existing non-Hyprland Wayland behavior pending its own contract decision. Material protocol/capture design will retain credit from closed source PR #1876 (shuv1337). Acceptance will include occluded/off-workspace Hyprland fixture pixels, fail-closed negative cases, focused tests, and the affected Linux harness evidence.

@RodriMora

Copy link
Copy Markdown

I could not push directly to this repository branch (GitHub returned 403), so the reviewed adaptation is available as draft stacked PR #3052, whose base is this PR branch—not main. It preserves this PR’s original author commit, rebases it onto current main, and adds the PID/title/app-id-bound Hyprland toplevel-export path with #1876 attribution. Please merge/cherry-pick #3052 into this branch; it is not intended as a competing landing PR.

@RodriMora

Copy link
Copy Markdown

Updated stacked adaptation #3052 to exact candidate 1bcb4b59148d1bbe82f41717010ee45c4c749039. It now includes the representative Hyprland harness, mixed-scale geometry/input normalization, embedded-browser and private-worker binding, and truthful WebKitGTK limitations in addition to identity-bound toplevel capture. The complete Hyprland desktop report is green: 129/129 contracts (79 delivered, 50 expected typed refusals, 0 failed/skipped), with required videos and exact-source evidence. #3052 remains based on this branch and draft because it should be merged/cherry-picked here rather than landed independently; its description records validation and remaining integration boundaries.

f-trycua added a commit that referenced this pull request Aug 16, 2026
Preserve visible compositor-attested Wayland capture while refusing off-workspace or otherwise unproven surfaces before output or X11 pixels are read. Add a source-built Sway/XWayland public-interface regression row with workspace, focus, fixture-state, file-absence, and protocol oracles.

Salvaged from #2964.

Co-authored-by: injaneity <44902825+injaneity@users.noreply.github.com>
f-trycua pushed a commit that referenced this pull request Aug 16, 2026
f-trycua added a commit that referenced this pull request Aug 16, 2026
Preserve visible compositor-attested Wayland capture while refusing off-workspace or otherwise unproven surfaces before output or X11 pixels are read. Add a source-built Sway/XWayland public-interface regression row with workspace, focus, fixture-state, file-absence, and protocol oracles.

Salvaged from #2964.

Co-authored-by: injaneity <44902825+injaneity@users.noreply.github.com>
f-trycua added a commit that referenced this pull request Aug 16, 2026
Fail closed when Wayland window-scoped capture cannot prove that output pixels belong to the requested surface. Preserve truthful accessibility output, explicit display capture, visible compositor-attested capture, and X11 per-window capture. Add a representative Sway/XWayland off-workspace regression row.

Salvaged from #2964.

Co-authored-by: injaneity <44902825+injaneity@users.noreply.github.com>
@f-trycua

Copy link
Copy Markdown
Collaborator

This fix has shipped in #3200 (merge commit c78c1d873b5c091a951716559d2ccc1dc2f7a0e6) after passing the representative Sway/XWayland off-workspace regression row and the full pull-request checks.

The landed history preserves @injaneity’s contribution: it was based on 4bdee4c2119780d43d9e60cf3f44afda9c0c91cb, records Salvaged from #2964, and includes Co-authored-by: injaneity <44902825+injaneity@users.noreply.github.com>. Thank you for identifying and fixing the unsafe capture behavior.

@f-trycua f-trycua closed this Aug 16, 2026
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.

Linux/Hyprland: get_window_state returns current-workspace pixels for an off-workspace XWayland window

3 participants