Skip to content

Fix KDE portal Freeze and Zoom capture on mixed-scale displays - #400

Merged
devmobasa merged 10 commits into
mainfrom
fix/kde-portal-capture-398
Sep 29, 2026
Merged

devmobasa merged 10 commits into
mainfrom
fix/kde-portal-capture-398

Conversation

@devmobasa

@devmobasa devmobasa commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Summary

Related to #398.

KDE captures the entire desktop at the highest output scale. Wayscriber expected a raster assembled from each monitor's native pixel density, causing valid screenshots to fail geometry validation.

  • Share verified portal crop geometry between Freeze and Zoom, and resample the active monitor's crop to its native dimensions.
  • Preserve output identity, raster bounds, layout generation, topology, and dimension checks.
  • Retry once after a confirmed layout change, retaining the original request and repeating the GTK and main-surface transparency barriers.
  • Wait for complete output metadata and 100 ms without another portal layout change before retry admission, bounded to one second.
  • Log screenshot bounds, crop coordinates, output identity, layout generations, and retry phases.
  • Document the Plasma/KWin screenshot-portal requirement in the support matrix.

Review follow-up

Seven focused commits address the accepted review findings:

  • Check live Freeze topology before interpreting a worker crop error, including the interval before new_output refreshes geometry.
  • Preserve buffer-release and frame-cap pacing instead of repeatedly waking dispatch with a zero timeout while no buffer is available.
  • Separate portal desktop generations from active-output source generations, so another monitor's metadata change does not invalidate direct captures or installed screen-modal sources.
  • Add bounded layout settling before the one allowed retry.
  • Remove the obsolete activation-time desktop crop path and its cropped flag; validate and install the worker's completed crop.
  • Split activation, direct capture resources, and retry coordination into focused modules; update scoped guides and logical blank-line grouping.
  • Cover Freeze activation/cancel/output-switch/stable-error behavior, coordinator failure branches, retry deadlines, rotated crop pixels, and Zoom terminal categories.

Validation

  • ./tools/lint-and-test.sh: passed at 8b124b76, including strict workspace Clippy and all-feature/no-default-feature builds and tests.
  • Root unit harnesses: 5221 all-feature and 5037 no-default-feature tests passed; 10 intentionally ignored in each. Configurator: 511 / 510 passed.
  • ./tools/test-gtk-widgets.sh: 84 parent-harness tests passed in private headless Weston, with both actual GTK execution markers.
  • Follow-up KWin 6.7.5/KDE portal 6.7.5 acceptance: 64 Wayscriber portal requests passed with builtin and GTK toolbars. Covers mixed/equal/fractional scales, both initial monitors, successful retries, retry exhaustion, two-batch layout updates, and two-buffer capture with VSync on/off and a 10 FPS cap when off.
  • Pixel observers and regressions verify the correct monitor region and resampling. Four regressions were demonstrated failing before repair and passing afterward.
  • Follow-up diff and working-tree whitespace checks passed. The unrelated local docs/CONFIG.md edit is excluded.

The KWin lab binary was built immediately before a final whitespace-only amend; its code behavior matches the final head. The full local and GTK gates used the final head.

Limitations

The request stays bound to its original wl_output. Recreating that object remains terminal: connector name/make/model fields are not a guaranteed unique physical identity for safely redirecting a pending capture. The reporter's physical Samsung EDID behavior and specific GTK popover timeout were not reproduced. The 100 ms quiet period is bounded; a later update during the retry barrier remains terminal.

A separate GTK output-cycling failure and the restricted KWin ScreenShot2 backend remain outside this change. The lab pacing cases use normal compositor buffer releases; forced withholding of all buffers is covered by the wakeup policy regression and source guard, not a runtime CPU measurement. Portal chooser dismissal is controller-tested; lab acceptance uses a private consent store. No host desktop was used.

SCTK can advertise a new output before the geometry refresh callback runs. Freeze now checks the live count before accepting a worker result or interpreting its raster error, matching Zoom. The regression failed before this repair and now retains the original acquisition for one retry.
Do not request an immediate dispatch wake while every swapchain slot is owned by the compositor. A buffer release already wakes Wayland dispatch. When a slot is available, honor the no-vsync frame cap before the hidden frame attempt. The pacing regression failed on the unconditional zero timeout and now passes.
Use a full desktop generation for portal preflight, worker results and pending portal images. Direct capture and installed modal sources retain an active viewport generation, so another monitor cannot invalidate their pixels. Preflight retains its backend to select the correct guard; Zoom now names that backend explicitly. Regression coverage checks direct Freeze activation and installed Zoom source validity while portal admission goes stale.
Keep the original request suppressed while waiting for complete geometry and 100 ms without a desktop change, bounded to one second. Pace incomplete metadata checks at 50 ms instead of polling with a zero timeout. Share this admission and deadline policy across Freeze and Zoom. Extract runtime retry coordination from barrier.rs so the presentation barrier stays below its size review threshold; controller tests cover lost suppression, output switches, settling failure, activation-time retry and exactly one completion.
Portal workers already return a completed output crop. Remove the raw desktop setter, cropped flag and test-only activation crop branch; retain the installation guards and move raster acceptance coverage to portal_raster. Add marked-pixel checks for rotated output dimensions and unknown multi-output bounds. Split activation and direct attempt resources from state.rs; frozen state and capture barrier now remain below 500 production lines. Restore logical blank-line groups in the touched raster and retry functions.
Give Plasma/KWin an explicit support-matrix entry explaining that Freeze and Zoom require its screenshot portal. Update scoped guides for portal raster validation, separate generations, retry settling, image activation, and direct capture resources.
Return typed retry admission errors so an output switch remains StaleLayout for Zoom rather than becoming a generic failure. The coordinator regression failed on that category change and now passes. Limit the removed Zoom wrapper imports to tests so strict production builds remain warning-free.
- Replace string-matched capture terminals with CapturePreflightError.
  Stale layouts and layouts that never settle end as StaleLayout in both
  Freeze and Zoom, so region capture does not fall back silently.
- Share active-output/desktop generation bookkeeping. Board PDF desktop
  captures use the full-desktop generation again.
- Abandon unowned Freeze preflight failures and clear pending retries.
- Move Zoom request, waiter and terminal ownership to zoom/state/source.rs
  and select the Zoom backend with ZoomCaptureBackend.
- Compute the frame-cap delay once for render and capture wakes.
- Cover buffer availability, packed mixed-density crops, desktop PDF
  contexts and typed terminal categories.
- Update capture invalidation docs and module guides.
@devmobasa
devmobasa merged commit 65b6410 into main Sep 29, 2026
3 checks passed
@devmobasa
devmobasa deleted the fix/kde-portal-capture-398 branch October 2, 2026 20:02
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.

1 participant