diff options
| author | srdusr <[email protected]> | 2024-06-20 16:08:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2024-06-20 16:08:00 +0200 |
| commit | ab0916391532031376817ed4ab6ab234428559a2 (patch) | |
| tree | 169e7799fbb6dd5d225b01ed645b1fa5cbf179de /crates/wayland/src/state.rs | |
| parent | 20cdd47b39f09aa19317d9d29fef6511b98c1233 (diff) | |
| download | srdwm-ab0916391532031376817ed4ab6ab234428559a2.tar.gz srdwm-ab0916391532031376817ed4ab6ab234428559a2.zip | |
Add drop shadows and shrink the resize grab margin that was eating content clicks
Two independent daily-driving gaps closed in one pass, both from MISSING.md
and live user feedback:
Drop shadows (general.shadows, default true). Reuses the exact "bitmap
drawn outside geometry, cached like the border" technique border_strips/
render_border_top already established - decoration::shadow_bitmap rasterizes
a linear alpha falloff (Chebyshev/square-ring distance, not a true blur --
no blur primitive exists without a GPU shader, and the udev backend's
PixmanRenderer is software-only) from SHADOW_MAX_ALPHA (90/255, deliberately
subtle) at the window's own edge down to fully transparent SHADOW_SIZE (12px)
out. Cached in CompState::shadow_buffers, rebuilt at the same trigger points
as border_top_decorations (redraw_decoration_buffer), for the identical
damage-tracking reason: a fresh Id every frame means OutputDamageTracker
never finds a previous-frame match. No shadow for a maximized or fullscreen
window, matching the Hyprland/GNOME convention MISSING.md measures against.
Resize grab margin: 10px -> 6px (general.resize_margin, now configurable,
same call-site-count-preserving change as threading a new parameter through
one indirection point: ResizeEdge::hit_test's only production caller is
WindowManager::hit_test, so this didn't need touching every backend despite
hit_test being shared verbatim across X11/Wayland/Windows/macOS). Reported
live: ordinary clicks near any window edge - a link near a browser's edge,
a button near a panel's edge - regularly registered as a resize-edge grab
instead of reaching the client, not just an occasional near-miss, because
the 10px band was measured inward from the client's own content rect. 6px
stays comfortably grabbable while giving content back most of its edge.
Verified: cargo build --workspace (all 9 crates including the windows/macos
stub backends), cargo clippy --workspace (0 new warnings), cargo test across
core/wayland/config/x11 (188 tests, 0 failed). Shadow rendering verified at
the render-element level live in a nested session (correct geometry, alpha,
buffer contents) - grim/screencopy itself turned out to route through
winit.rs's separate capture_offscreen path, which only ever drew
`decorations` (titlebars), never borders or shadows, so screenshots taken
this way have never shown either; a real gap, not fixed in this pass.
Diffstat (limited to 'crates/wayland/src/state.rs')
| -rw-r--r-- | crates/wayland/src/state.rs | 30 |
1 files changed, 30 insertions, 0 deletions
diff --git a/crates/wayland/src/state.rs b/crates/wayland/src/state.rs index 1964d99..8d04d20 100644 --- a/crates/wayland/src/state.rs +++ b/crates/wayland/src/state.rs @@ -236,6 +236,18 @@ pub(crate) struct CompState { /// `elements::border_side_render_element`'s doc comment for the damage- /// tracking reason a per-frame rebuild was wrong in the first place. pub(crate) border_top_decorations: HashMap<WindowId, MemoryRenderBuffer>, + /// A window's drop-shadow bitmap (`decoration::shadow_bitmap`), cached + /// the same way and at the same trigger points as `border_top_decorations` + /// - rebuilt only on creation or a real size change, not per frame, for + /// the identical damage-tracking reason (a fresh `Id` every frame means + /// `OutputDamageTracker` never finds a previous-frame match, so the + /// shadow - like the border strips before this caching existed - would + /// mark itself fully damaged forever, keeping the output page-flipping + /// on an otherwise fully static screen). `None` for a maximized or + /// fullscreen window, or with `general.shadows` off - see the render + /// call site for why those don't get a shadow at all rather than a + /// zero-alpha one. + pub(crate) shadow_buffers: HashMap<WindowId, MemoryRenderBuffer>, /// Persistent solid-colour buffers backing a window's other three /// border strips (bottom, left, right - `decoration::border_strips`' /// order past index 0), reused by position every frame rather than @@ -610,6 +622,23 @@ impl CompState { } else { self.border_top_decorations.remove(&id); } + // No shadow for a maximized/fullscreen window: it already reaches + // (or, for fullscreen, exceeds) the monitor's own edge, so there is + // nowhere for `SHADOW_SIZE` pixels of shadow to actually fall, and + // a shadow drawn there would either be clipped to nothing useful or + // - for a maximized window short of the true monitor edge - read + // as a shadow the window doesn't visually need. Matches the + // Hyprland/GNOME convention `MISSING.md` measures this compositor + // against. + let shadows_enabled = self.wm.borrow().shadows_enabled; + if shadows_enabled && !w.maximized && !w.fullscreen { + let data = decoration::shadow_bitmap(w.geometry.width, w.geometry.height); + let rect = decoration::shadow_rect(w.geometry); + let buffer = MemoryRenderBuffer::from_slice(&data, Fourcc::Argb8888, (rect.width as i32, rect.height as i32), 1, Transform::Normal, None); + self.shadow_buffers.insert(id, buffer); + } else { + self.shadow_buffers.remove(&id); + } } pub(crate) fn remove_window(&mut self, surface: &WlSurface) { @@ -619,6 +648,7 @@ impl CompState { } self.decorations.remove(&id); self.border_top_decorations.remove(&id); + self.shadow_buffers.remove(&id); self.border_side_buffers.remove(&id); self.last_synced_size.remove(&id); // A window closing (crash, kill, or its own menu's "Close" action |