tile: stop the fullscreen backdrop from masking window translucency#4034
tile: stop the fullscreen backdrop from masking window translucency#4034joshsymonds wants to merge 8 commits into
Conversation
Pins the contract of the geometry helper that the next commit will introduce for the fullscreen-backdrop transparency fix. Tests cover the standard decomposition cases + corner-radius routing. The helper takes the tile rect, window rect, and tile_corner_radius; returns up-to-4 (Rectangle, CornerRadius) pairs covering tile-minus- window. Per-rect CornerRadius is non-zero only on corners that coincide with tile-outer-corners — the L-shape backdrop keeps animated rounded outer corners, inner edges next to the window stay sharp.
Replace single full-tile SolidColorRenderElement with up-to-4 from backdrop_clip_rects(tile, window, 0). At fullscreen_progress=1.0 the backdrop no longer covers the window's geometry — translucent windows (e.g., kitty with background_opacity<1.0) now show the wallpaper through their transparency. Aspect-ratio padding bars unchanged. Adds SolidColorRenderElement::from_buffer_at, a sibling of from_buffer that takes an explicit geometry instead of deriving it from the buffer's natural size — needed so we can paint multiple sub-rects from one backing buffer. Animation path (BorderRenderElement, fullscreen_progress < 1.0) is the next task and untouched here.
Same fix as the steady-state path (commit 18c1dfb) applied to the BorderRenderElement animation branch. Each L-shape strip is its own BorderRenderElement with a per-corner CornerRadius — non-zero only on tile-outer-corners, zero on inner edges next to the window. The animated rounded-corner shrink is preserved end-to-end; backdrop never paints under the window's geometry at any animation frame. Also includes cargo fmt cleanup (one long-comment wrap in the test module that fmt --check flagged).
Behavior fix (C1): top/bottom middle strips now own tile-outer corners when the corresponding L/R bar is absent. Previously the strips unconditionally emitted CornerRadius::default(), so a letterbox-shaped window (full width, partial height) would render the four tile-outer corners sharp during the fullscreen animation — wrong, since at that point those corners ARE on the top/bottom strips. New routing checks window_left <= tile_left / window_right >= tile_right per strip and inherits the relevant corner radii from tile_corner_radius. Test additions: - backdrop_clip_rects_window_flush_one_edge_returns_3_strips: the 3-strip case from the epic spec, previously missing. Asserts geometry AND corner radii on right bar + top middle + bottom middle for a window flush-left, partial elsewhere. - backdrop_clip_rects_letterbox_top_bottom_own_all_tile_outer_corners: the C1 regression test; full-width partial-height window expects both top middle strip's top corners AND bottom middle's bottom corners to inherit tile_corner_radius. - backdrop_clip_rects_window_outside_tile_returns_whole_tile: covers the early-return at the top of the helper when window doesn't overlap tile at all. Test cleanup: - Renamed backdrop_clip_rects_window_flush_left_no_left_bar to backdrop_clip_rects_window_flush_top_and_bottom_returns_2_strips (the old name was mislabeled per its own apologetic comment). - Removed backdrop_clip_rects_zero_radius_yields_zero_radii — its assertion was already covered inline by backdrop_clip_rects_window_centered_returns_4_strips. Style + perf: - Vec::with_capacity(4) at the helper's allocation site (per-frame on fullscreen tiles; up to 4 strips means cap-0 → cap-4 wastes 1-2 reallocations per frame). - 0.0/16.0 → 0./16. in CornerRadius literals, matching the rest of tile.rs's preferred f32-literal style. - `let (geo, _radius)` → `let (geo, _)` in the steady-state branch, since CornerRadius::default() is passed and the radius is unused.
|
If I understand them correctly, the Wayland protocol linked to in the docs you reference say that you should also draw behind the window itself?
|
The fullscreen-backdrop clip diverges from xdg-shell, which says a non-opaque fullscreen surface must not let other screen content show through. Per upstream feedback, make this opt-in instead of unconditional: - Default (rule absent or false): restore upstream behavior — single full-tile SolidColor element steady-state, single full-tile Border element during the un/fullscreen animation. - Rule true: the existing 4-strip tile-minus-window clip (steady-state SolidColor strips, animation BorderRenderElement strips with per-strip corner-radius routing). The geometry helper backdrop_clip_rects and its 8 tests are unchanged. niri-config: - Add `clip-fullscreen-backdrop-to-window` Option<bool> to WindowRule. src/window/mod.rs: - Add the same field to ResolvedWindowRules with merge logic mirroring clip_to_geometry. src/layout/tile.rs: - Read rules.clip_fullscreen_backdrop_to_window and branch in both the animation (border-element) and steady-state (solid-color) paths. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- josh/fullscreen-backdrop-clip: now gates behavior on opt-in clip-fullscreen-backdrop-to-window window rule (PR niri-wm#4034 review feedback). - Re-adds the josh/instrument-ffm-debug entry that was dropped when the tooling commit was cherry-picked from main during re-derivation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@Uzlkav fair point — you're right that the protocol clause covers the non-opaque case too, and a blanket clip diverges from spec. I've updated the PR to make this opt-in: a new The 4-strip decomposition + its 8 geometry tests are unchanged; both render paths (steady-state SolidColor and the un/fullscreen-animation BorderRenderElement) now branch on If people want it they can opt-in; if maintainers absolutely don't want toggles for non-spec Wayland features, no harm no foul, close this PR and I'll just keep it on my local fork. It seems obvious to me that if someone adds alpha to their full screen window they mean it, but maybe that's wrong. |
Adds clip-fullscreen-backdrop-to-window true to the existing window-rule in the parse() snapshot test and asserts it resolves to Some(true). Mirrors the coverage other Option<bool> rule fields (e.g. open-maximized, open-focused) get and proves the knuffel plumbing for the new field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- josh/fullscreen-backdrop-clip: now gates behavior on opt-in clip-fullscreen-backdrop-to-window window rule (PR niri-wm#4034 review feedback). - Re-adds the josh/instrument-ffm-debug entry that gets dropped when the tooling commit is cherry-picked from main during re-derivation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps niri-flake.inputs.niri-unstable to josh/integration@39c2331c, which adds the new clip-fullscreen-backdrop-to-window window rule (default off, spec-compliant). Opts kitty in via niri/extras.kdl so a translucent fullscreen kitty (background_opacity<1.0) composes against the wallpaper instead of the opaque black backdrop. See niri-wm/niri#4034. Also pulls niri-flake itself (sodiboo/niri-flake) and the stable nixpkgs along with it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Closing this — keeping it on my fork. @Uzlkav, thanks for the actual review. Your point on the xdg-shell non-opaque clause was right and made the patch better. |
Summary
The fullscreen backdrop is currently painted as a single rect covering the entire tile, including the area under the window. This silently overrides user-set window transparency: a fullscreen kitty with
background_opacity 0.85(or mpv with--alpha=yes, etc.) composites its alpha against the opaque backdrop instead of what's behind the tile, so the user's transparency setting becomes invisible.This PR clips the backdrop to
tile − window, preserving its documented role of covering screen-size padding when the window is smaller than the tile (e.g. fullscreening a fixed-size dialog) while no longer masking the window's own alpha channel. Opaque fullscreen windows render identically — the change is opt-in by content alpha, so no config knob is needed.Implementation
backdrop_clip_rects(tile, window, tile_corner_radius)decomposestile − windowinto up to 4 axis-aligned rects (left bar, right bar, top middle, bottom middle), each with aCornerRadiuswhose non-zero corners coincide with tile-outer corners and zero corners butt against the window. Zero-area strips are dropped.fullscreen_progress >= 1.0) emits oneSolidColorRenderElementper strip via a newfrom_buffer_athelper that takes an explicit geometry instead of using the buffer's natural size.fullscreen_progress < 1.0) emits oneBorderRenderElementper strip. The rounded-corner shrink animation works the same; the backdrop just doesn't paint under the window at any frame.Test plan
cargo test -p niri --lib backdrop_clip— 8 geometry tests covering the standard 4-strip decomposition, 2-strip / 3-strip degenerate cases, window-equals-tile, window-outside-tile, and corner-radius routing including the letterbox case.cargo +nightly fmt --all -- --checkandcargo clippy --all --all-targetsclean.