srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/state
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2026-01-30 09:39:00 +0200
committersrdusr <[email protected]>2026-01-30 09:39:00 +0200
commit2a51fb86e2a123ff9e810a0b4105c9fe5f85e3ae (patch)
tree7a1fc6b2b87cd0bf82bc8953ba0a2a9e65c9e7cf /crates/wayland/src/state
parentd16f7dab25bd9508271017bc6dc15024c30cbf71 (diff)
downloadsrdwm-2a51fb86e2a123ff9e810a0b4105c9fe5f85e3ae.tar.gz
srdwm-2a51fb86e2a123ff9e810a0b4105c9fe5f85e3ae.zip
Fix a real regression: dynamic-mode windows lost their shadow via toggle_floating
The tiled-shadow-tint fix earlier today gated the shadow on Window::floating alone. arrange_workspace only reads floating under the "tiling" layout, so every window on this project's own default "dynamic" layout starts, and stays, floating: false - the gate misread that as "tiled, no shadow" regardless of which layout was actually running, so shadows silently vanished under dynamic mode entirely, recoverable only by pressing Super+S (toggle_floating), which then looked like that key toggles a tint rather than floating. Fixed by checking the workspace's own layout name first: a window is only "currently tiled" when its workspace runs "tiling" AND it hasn't opted out via floating. DecorationSignature's floating field is now currently_tiled, since a layout switch changes this for every window on a workspace without touching any of their own floating fields. Also disabled general.shadows in the user's own config per direct request - never asked for, on by default, and a real problem for color-accuracy work regardless of how correctly it renders otherwise. Also fixed both context menus (titlebar and desktop) silently truncating labels past a fixed 170px width with no indication - widened dynamically to each menu's own real widest label via a new measure_text_width helper.
Diffstat (limited to 'crates/wayland/src/state')
-rw-r--r--crates/wayland/src/state/desktop_icons.rs11
-rw-r--r--crates/wayland/src/state/lifecycle.rs49
-rw-r--r--crates/wayland/src/state/menu.rs18
-rw-r--r--crates/wayland/src/state/mod.rs21
4 files changed, 77 insertions, 22 deletions
diff --git a/crates/wayland/src/state/desktop_icons.rs b/crates/wayland/src/state/desktop_icons.rs
index 9d2dd91..05bd85a 100644
--- a/crates/wayland/src/state/desktop_icons.rs
+++ b/crates/wayland/src/state/desktop_icons.rs
@@ -591,8 +591,17 @@ impl CompState {
self.build_desktop_menu_buffer(menu);
}
- fn build_desktop_menu_buffer(&mut self, menu: DesktopMenu) {
+ fn build_desktop_menu_buffer(&mut self, mut menu: DesktopMenu) {
let theme = self.wm.borrow().theme;
+ // Same fixed-width-guess bug the titlebar menu was reported for
+ // ("text goes out of view") - "Open in File Manager"/"New Text
+ // Document" both run past a fixed 170px guess. Widened to this
+ // menu's own widest real label, same measurement `open_context_
+ // menu` (`state/menu.rs`) uses.
+ let font = decoration::find_system_font();
+ let widest_label = menu.items.iter().map(|(label, _)| decoration::measure_text_width(&font, label, decoration::FONT_PIXELS)).fold(0.0_f32, f32::max);
+ let content_width = (widest_label + decoration::TEXT_LEFT_PADDING * 2.0).ceil() as u32;
+ menu.width = menu.width.max(content_width);
// Not redesigned the way the titlebar menu was (`srdwm_core::
// context_menu`'s own module doc comment) - this menu's rows are
// still all one uniform height, `Separator` included, matching
diff --git a/crates/wayland/src/state/lifecycle.rs b/crates/wayland/src/state/lifecycle.rs
index 112c64e..51297bb 100644
--- a/crates/wayland/src/state/lifecycle.rs
+++ b/crates/wayland/src/state/lifecycle.rs
@@ -145,6 +145,17 @@ impl CompState {
// nothing this function reads has actually changed since the last
// call turns those redundant calls into a cheap signature
// comparison instead of a full re-rasterization.
+ // Whether this window is *actually* occupying a tiled slot right
+ // now - see the shadow gate further down for the full reasoning.
+ // Computed once, here, and reused there rather than recomputed:
+ // it depends on the workspace's own layout name, not just this
+ // window's own `floating` flag, so it has to be part of the
+ // signature too - switching a workspace's layout (`Super+Shift+
+ // t`/`s`) changes this for every window on it without touching
+ // any of their own `floating` fields, and a signature that didn't
+ // track it would keep serving whichever shadow state was cached
+ // before the switch.
+ let currently_tiled = self.wm.borrow().workspace(w.workspace).is_some_and(|ws| ws.layout == "tiling") && !w.floating;
let signature = DecorationSignature {
width: frame.width,
height: frame.height,
@@ -157,7 +168,7 @@ impl CompState {
maximized: w.maximized,
fullscreen: w.fullscreen,
shadows_enabled: self.wm.borrow().shadows_enabled,
- floating: w.floating,
+ currently_tiled,
hovered_button,
title_centered: theme.title_centered,
buttons_left: theme.buttons_left,
@@ -252,20 +263,36 @@ impl CompState {
// 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
+ // No shadow for a window that is *actually being tiled right now*
+ // - 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 a tiled window is coplanar and
+ // adjacent to its neighbours by construction, with nothing behind
+ // it 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.
+ // are dark tinted."
+ //
+ // Gating this on `w.floating` alone (the first version of this
+ // fix) was a real regression, caught live: `arrange_workspace`
+ // only reads `floating` under the `"tiling"` layout, so every
+ // window on this project's own default `"dynamic"` layout starts
+ // - and stays - `floating: false` unless something explicitly
+ // flips it. That first version read `floating: false` as "this
+ // window is tiled, no shadow" regardless of which layout was
+ // actually running, so *every* window under dynamic/floating mode
+ // silently lost its shadow, recoverable only by toggling `Super+S`
+ // (`srd.window.toggle_floating()`) - which then looked like that
+ // key toggles a "tint", not floating, since floating itself does
+ // nothing visible under a layout that never tiles anyone. `Window::
+ // floating` only ever means "opted out of tiling" *within* a
+ // workspace that tiles at all - checking the workspace's own
+ // layout name first is what this needed instead: a window is only
+ // "currently tiled" when both are true. `currently_tiled` itself
+ // was already computed above, alongside the signature.
let shadows_enabled = self.wm.borrow().shadows_enabled;
- if shadows_enabled && w.floating && !w.maximized && !w.fullscreen {
+ if shadows_enabled && !currently_tiled && !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/menu.rs b/crates/wayland/src/state/menu.rs
index b0b0a6e..82e4434 100644
--- a/crates/wayland/src/state/menu.rs
+++ b/crates/wayland/src/state/menu.rs
@@ -7,12 +7,28 @@ impl CompState {
/// and caches the rasterised buffer once here rather than per frame --
/// same reasoning as `redraw_decoration_buffer`.
pub(crate) fn open_context_menu(&mut self, window: WindowId, pos: (i32, i32)) {
- let Some(menu) = ({
+ let Some(mut menu) = ({
let wm = self.wm.borrow();
crate::context_menu::ContextMenu::open(&wm, window, pos)
}) else {
return;
};
+ // `ContextMenu::width` is a backend-agnostic placeholder - `core`
+ // has no font of its own to measure real text against, so it can
+ // only ever pick a fixed guess. Widened here to whatever this
+ // menu's own widest real label actually needs, or reported live
+ // as "text goes out of view": the old fixed width comfortably fit
+ // every label back when this menu only listed short ones
+ // ("Minimize", "Close"), but a longer one added since ("Button
+ // Style: Traffic Lights", or a user-configurable workspace name)
+ // just ran past the panel's own right edge, silently cut off
+ // mid-character by `render_context_menu`'s own overflow guard.
+ // Only ever grows the width, never shrinks it below the built-in
+ // minimum `ContextMenu::open` already picked.
+ let font = decoration::find_system_font();
+ let widest_label = menu.items.iter().map(|&(label, _)| decoration::measure_text_width(&font, label, decoration::FONT_PIXELS)).fold(0.0_f32, f32::max);
+ let content_width = (widest_label + decoration::TEXT_LEFT_PADDING * 2.0).ceil() as u32;
+ menu.width = menu.width.max(content_width);
let theme = self.wm.borrow().theme;
let rows: Vec<(&str, bool, u32, decoration::MenuRowKind)> = menu
.items
diff --git a/crates/wayland/src/state/mod.rs b/crates/wayland/src/state/mod.rs
index e07dc1d..6396692 100644
--- a/crates/wayland/src/state/mod.rs
+++ b/crates/wayland/src/state/mod.rs
@@ -114,15 +114,18 @@ 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,
+ /// A window actually occupying a tiled slot right now (its workspace
+ /// is running the `"tiling"` layout, and it hasn't opted out via
+ /// `Window::floating`) gets no shadow at all - see `redraw_
+ /// decoration_buffer`'s own gate. Deliberately *not* just `Window::
+ /// floating` on its own: a workspace's layout name changing (`Super+
+ /// Shift+t`/`s`) affects every window on it without touching any of
+ /// their own `floating` fields, and toggling `floating` alone
+ /// (`srd.window.toggle_floating()`) changes nothing else this
+ /// signature already tracks - without this field, either change
+ /// would leave the cache serving whichever shadow state it last
+ /// computed until some unrelated field forced a rebuild anyway.
+ pub(crate) currently_tiled: 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.