srdusr
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2025-12-03 22:52:00 +0200
committersrdusr <[email protected]>2025-12-03 22:52:00 +0200
commit393f886eb9e275c3b7a9e3ef396092ba71297dfb (patch)
treee84786279ff2216b1fa54c9fdad6ad57d8f73118
parent96aff79ae2df2ade1a29a68c1a8108d258596bc3 (diff)
downloadsrdwm-393f886eb9e275c3b7a9e3ef396092ba71297dfb.tar.gz
srdwm-393f886eb9e275c3b7a9e3ef396092ba71297dfb.zip
Stop giving tiled windows a shadow that lands on their neighbour
Diagnosed by a peer session (dotfiles-1a): SHADOW_SIZE is 24px, and a tiling layout with a small gap_inner (as little as 1px live) leaves the shadow nowhere to fall except onto the adjacent tile, darkening it by up to SHADOW_MAX_ALPHA (~35%) on whichever side is unfocused. Not a content tint or an opacity rule - verified against the actual rasteriser and the live rule set before accepting the diagnosis. A drop shadow separates a window from what's behind it; tiled windows are coplanar and adjacent by construction, with nothing behind them to separate from. redraw_decoration_buffer's shadow gate now requires w.floating in addition to the existing !maximized/!fullscreen checks. DecorationSignature gained a floating field so toggling floating on its own invalidates the decoration cache instead of waiting for an unrelated field to force a rebuild. Live-verified in a nested compositor: two tiled windows show a clean shared edge with no gradient bleeding across; floating a window still detaches it from the tile group with its shadow intact; shadows still toggle globally both ways.
-rw-r--r--crates/wayland/src/state/lifecycle.rs16
-rw-r--r--crates/wayland/src/state/mod.rs9
-rw-r--r--docs/TODO.md10
3 files changed, 34 insertions, 1 deletions
diff --git a/crates/wayland/src/state/lifecycle.rs b/crates/wayland/src/state/lifecycle.rs
index 1afa738..112c64e 100644
--- a/crates/wayland/src/state/lifecycle.rs
+++ b/crates/wayland/src/state/lifecycle.rs
@@ -157,6 +157,7 @@ impl CompState {
maximized: w.maximized,
fullscreen: w.fullscreen,
shadows_enabled: self.wm.borrow().shadows_enabled,
+ floating: w.floating,
hovered_button,
title_centered: theme.title_centered,
buttons_left: theme.buttons_left,
@@ -250,8 +251,21 @@ impl CompState {
// as a shadow the window doesn't visually need. Matches the
// Hyprland/GNOME convention `MISSING.md` measures this compositor
// against.
+ //
+ // No shadow for a TILED window either - a real, reported bug, not
+ // a style choice made up front: a drop shadow exists to separate a
+ // window from whatever is visually *behind* it, but tiled windows
+ // are coplanar and adjacent by construction, with nothing behind
+ // them to separate from. `SHADOW_SIZE` pixels of shadow with only
+ // `gap_inner` pixels of real gap to fall into (as little as 1px)
+ // has nowhere to land except on the neighbouring tile, darkening
+ // it by up to `SHADOW_MAX_ALPHA` - reported live as "some windows
+ // are dark tinted." Floating windows keep their shadow: they
+ // genuinely do sit above other windows, which is exactly where a
+ // shadow does its job, and it's also the one case `visible_border_
+ // fragments`' own occluder clipping already handles correctly.
let shadows_enabled = self.wm.borrow().shadows_enabled;
- if shadows_enabled && !w.maximized && !w.fullscreen {
+ if shadows_enabled && w.floating && !w.maximized && !w.fullscreen {
// A decorated window's corners are *always* rounded (the
// titlebar/border strips round to `corner_radius` regardless of
// this setting - see their own call sites); an undecorated
diff --git a/crates/wayland/src/state/mod.rs b/crates/wayland/src/state/mod.rs
index f39edcc..e07dc1d 100644
--- a/crates/wayland/src/state/mod.rs
+++ b/crates/wayland/src/state/mod.rs
@@ -114,6 +114,15 @@ pub(crate) struct DecorationSignature {
pub(crate) maximized: bool,
pub(crate) fullscreen: bool,
pub(crate) shadows_enabled: bool,
+ /// A tiled window gets no shadow at all (see `redraw_decoration_
+ /// buffer`'s own gate) - included here for the same "one signature,
+ /// every real input" reason as `maximized`/`fullscreen` just above:
+ /// toggling floating on its own (`srd.window.toggle_floating()`,
+ /// switching layouts) changes nothing else this signature already
+ /// tracks, so without this the cache would keep serving whichever
+ /// shadow state happened to be cached until some unrelated field
+ /// (a resize, a focus change) forced a rebuild anyway.
+ pub(crate) floating: bool,
/// Which of *this* window's own titlebar buttons (if any) is currently
/// hovered, and the glyph-reveal animation's current progress (0..=255)
/// - see `CompState::hovered_titlebar_button`'s own doc comment.
diff --git a/docs/TODO.md b/docs/TODO.md
index c212993..2cace23 100644
--- a/docs/TODO.md
+++ b/docs/TODO.md
@@ -1,5 +1,15 @@
# TODO / planned features - master checklist
+## Root cause found and fixed: tiled windows tinted dark along a shared edge (2026-08-28)
+
+Reported live as "some windows are dark tinted" (via a peer session, `dotfiles-1a`, who diagnosed the actual cause and handed over a concrete fix rather than a symptom). Root cause verified by reading the rasteriser before touching anything: `shadow_bitmap` never tints a window's own interior, and no rule sets `opacity` on the affected windows - the tint was never a content property. It was a shadow-versus-gap-size mismatch. `SHADOW_SIZE` is 24px; `gap_inner` on this session's live config is 1px. `redraw_decoration_buffer`'s shadow gate (`crates/wayland/src/state/lifecycle.rs`) only excluded a maximized or fullscreen window, so every *tiled* window got the same 24px shadow too - with only 1px of real gap for it to fall into, it landed almost entirely on the neighbouring tile instead, darkening it by up to `SHADOW_MAX_ALPHA` (~35%). The focused window is raised above its neighbours, so this showed up as the *unfocused* side of a shared tile edge reading tinted.
+
+Fixed by gating the shadow on `w.floating` as well: a drop shadow exists to separate a window from whatever is genuinely *behind* it, and tiled windows are coplanar and adjacent by construction - there's nothing behind them to separate from. Floating windows keep their shadow unchanged (`SHADOW_SIZE`/`SHADOW_MAX_ALPHA` untouched, no new config key) - they do sit above other windows, which is exactly where a shadow does its job, and it's also the one case `visible_border_fragments`'s own occluder clipping already handles correctly. `DecorationSignature` gained a `floating` field alongside `maximized`/`fullscreen` - without it, toggling floating on its own (`srd.window.toggle_floating()`, a layout switch) changes nothing else the signature already tracks, so the shadow cache would have kept serving whichever state it last computed until some unrelated field forced a rebuild anyway.
+
+Live-verified in a nested compositor, not just read: two tiled Alacritty windows with `gap_inner`/`gap_outer` at 8px each (this test's own config value; the live report was against 1px, an even more visible case of the same bug) showed a clean shared edge with no gradient bleeding across, confirmed via `grim` screenshot. Floating a window (`{"cmd":"toggle_floating"}`) correctly detached it from the tiling group and left the remaining tiled window filling the tile area alone, confirming the gate change didn't disturb ordinary floating behaviour; `{"cmd":"set","key":"shadows",...}` still toggles the global setting both ways. Full workspace build/test/clippy clean (152 wayland tests, unchanged in count - no new pure function to isolate here, the gate is a one-line boolean condition already covered by this same live test).
+
+**Your running srdwm is stale as of this fix.** A peer session found `/proc/<pid>/exe` on the live process resolves to `/home/srdusr/.local/share/cargo/bin/srdwm (deleted)` - it was started 04:23 today, before every rebuild since (lock screen, window-sizing fix, and now this one). None of today's work is live in your actual session. Not restarted here, per this file's own standing rule never to touch the live session without being asked - restart whenever you're ready to pick any of it up.
+
## Root cause found and fixed: every new window forced to the same guessed size, never its own (2026-08-28)
Asked directly why windows "spawn small and as a square" and not centred, on top of the already-fixed "don't remember placement" bug. Read the actual code path rather than guessing, and found the real cause: `new_managed_window` hardcodes a brand-new toplevel's `Window::geometry` to `800x632` (800x600 plus the titlebar band) *before* the client has said anything about its own size, `WindowManager::add_window` feeds that same guessed number into `SmartPlacement` as if it were real, and - the actual bug - `sync_geometry` then forces that guessed size onto the client's very first `xdg_toplevel::configure` via `state.size = Some(size.into())`, unconditionally, on every single new window. Per the xdg-shell protocol, `size: None` on that first configure is the standard way every mainstream compositor (Mutter, KWin, Hyprland, sway, niri) lets a client pick its own natural size; this compositor never did, so every app - a tiny dialog and a browser alike - was flattened onto the exact same placeholder rectangle regardless of what it would have chosen for itself. That is why windows read as "the same size, small, square" rather than each app looking like itself.