srdusr
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crates/core/src/theme.rs23
-rw-r--r--crates/srdwm/src/main.rs1
-rw-r--r--crates/wayland/src/protocols/xdg_decoration.rs5
-rw-r--r--docs/DEFAULTS.md74
-rw-r--r--docs/TODO.md58
5 files changed, 161 insertions, 0 deletions
diff --git a/crates/core/src/theme.rs b/crates/core/src/theme.rs
index 026688a..649ec64 100644
--- a/crates/core/src/theme.rs
+++ b/crates/core/src/theme.rs
@@ -175,6 +175,28 @@ pub struct ThemeConfig {
/// Asked for as titlebars able to use "decorations/buttons of the
/// program/dynamic".
pub dynamic_buttons: bool,
+ /// Enforce server-side decoration on every client that negotiates it,
+ /// instead of honouring what the client asked for.
+ ///
+ /// `xdg-decoration` explicitly allows this: "The compositor can decide
+ /// not to use the client's mode and enforce a different mode instead",
+ /// and "the specified mode must be obeyed by the client". So a toolkit
+ /// that asks for client-side - winit does, measured - can be given
+ /// srdwm's titlebar anyway, which is what makes every negotiating
+ /// window look the same.
+ ///
+ /// Off by default, because it cannot help the case it looks like it
+ /// should. A client that never creates a decoration object at all is
+ /// outside the protocol: "if compositor and client do not negotiate
+ /// the use of a server-side decoration ... clients continue to
+ /// self-decorate as they see fit". GTK is exactly that client - it
+ /// creates no decoration object, measured directly - so forcing this
+ /// on cannot move a single GTK button, while it *can* stack srdwm's
+ /// titlebar on top of a client that draws its own regardless (the
+ /// Firefox case this project already hit once). Turn it on to make
+ /// negotiating toolkits uniform; use `rules.lua`'s `decorated = false`
+ /// for the ones that draw their own anyway.
+ pub force_server_side: bool,
}
impl Default for ThemeConfig {
@@ -194,6 +216,7 @@ impl Default for ThemeConfig {
button_glyph_always: false,
traffic_light_buttons: true,
dynamic_buttons: true,
+ force_server_side: false,
}
}
}
diff --git a/crates/srdwm/src/main.rs b/crates/srdwm/src/main.rs
index 6c2f04b..9772e65 100644
--- a/crates/srdwm/src/main.rs
+++ b/crates/srdwm/src/main.rs
@@ -321,6 +321,7 @@ fn apply_general_settings(engine: &Engine, wm: &Rc<RefCell<WindowManager>>) {
// "dynamic" (default: only the buttons the window can actually use) or
// "fixed" (always the full set) - see `ThemeConfig::dynamic_buttons`.
theme.dynamic_buttons = engine.get_string("theme.decorations.title_bar.button_mode", "dynamic") != "fixed";
+ theme.force_server_side = engine.get_bool("theme.decorations.force_server_side", false);
let border_width = engine.get_f64("theme.decorations.border.width", 2.0).max(0.0) as u32;
theme.default_border_width = border_width;
// 12, not the original 6: matches real macOS's own ~0.36 radius-to-
diff --git a/crates/wayland/src/protocols/xdg_decoration.rs b/crates/wayland/src/protocols/xdg_decoration.rs
index 25973c0..c3c7f0b 100644
--- a/crates/wayland/src/protocols/xdg_decoration.rs
+++ b/crates/wayland/src/protocols/xdg_decoration.rs
@@ -37,7 +37,12 @@ impl XdgDecorationHandler for CompState {
/// the way for exactly those clients, while everything that accepts
/// (or has no preference and gets offered) server-side still gets our
/// titlebar as before.
+ /// `theme.decorations.force_server_side` overrides the client's request
+ /// - see `ThemeConfig::force_server_side` for why that is allowed and
+ /// why it is off by default.
fn request_mode(&mut self, toplevel: ToplevelSurface, mode: DecorationMode) {
+ let forced = self.wm.borrow().theme.force_server_side;
+ let mode = if forced { DecorationMode::ServerSide } else { mode };
toplevel.with_pending_state(|state| {
state.decoration_mode = Some(mode);
});
diff --git a/docs/DEFAULTS.md b/docs/DEFAULTS.md
index daa65d8..fd10861 100644
--- a/docs/DEFAULTS.md
+++ b/docs/DEFAULTS.md
@@ -971,3 +971,77 @@ is not intercepted until the next startup.
Combos are reported in canonical order (`Ctrl+Mod4+l`), which is what the
compositor matches a real keypress against - not necessarily how the combo
was written in the config.
+
+## Making client-side decorated apps match srdwm's titlebar
+
+A window's chrome is drawn by one of two parties, and which one is decided
+by the `xdg-decoration` protocol. Three behaviours were measured directly
+against a nested srdwm, not assumed:
+
+| toolkit | creates a decoration object | asks for |
+|---|---|---|
+| Qt / KDE (Dolphin) | yes | server-side |
+| winit (Alacritty) | yes | **client-side** |
+| GTK 3 / GTK 4 (Nemo) | **no, never** | - |
+
+That table is the whole problem. The protocol says "the compositor can
+decide not to use the client's mode and enforce a different mode instead",
+and "the specified mode must be obeyed by the client" - so the first two
+rows are srdwm's to control. But it also says "if compositor and client do
+not negotiate the use of a server-side decoration ... clients continue to
+self-decorate as they see fit", and GTK never negotiates at all. No
+compositor setting can reach a GTK window's buttons, because GTK is not
+having the conversation.
+
+### For toolkits that negotiate: force server-side
+
+```lua
+srd.set("theme.decorations.force_server_side", true)
+```
+
+Off by default. It makes every negotiating client take srdwm's titlebar,
+including one that asked for client-side. Verified: with it off, Alacritty
+draws its own content straight to the window's top edge; with it on, the
+same window gets srdwm's titlebar band.
+
+The reason it is not the default is that it cannot help the case it looks
+like it should, and can hurt: a client that draws its own chrome regardless
+of what it negotiated (Firefox, historically) ends up with srdwm's titlebar
+stacked on top of its own. Use `rules.lua`'s `decorated = false` for those.
+
+### For GTK: set the desktop's button layout
+
+GTK reads its button layout from the desktop, not from the compositor. GTK 4
+on Wayland reads it from `xdg-desktop-portal`
+(`org.freedesktop.portal.Settings`, key `org.gnome.desktop.wm.preferences`
+`button-layout`); GTK 3 reads the `gtk-decoration-layout` GtkSettings
+property. Both resolve to the same string on a normal setup.
+
+```sh
+gsettings set org.gnome.desktop.wm.preferences button-layout ':minimize,maximize,close'
+```
+
+The colon separates the left corner from the right, so a leading colon puts
+every button on the right. Match it to srdwm's own `button_side`:
+
+| srdwm `button_side` | button-layout |
+|---|---|
+| `"right"` (default) | `:minimize,maximize,close` |
+| `"left"` | `close,minimize,maximize:` |
+
+This is exactly how KDE does it - `kde-gtk-config` exists to write GTK's
+setting to match KWin's own decoration. The value persists in dconf, so it
+survives a reboot without any startup script.
+
+Verified with a screenshot A/B on one Nemo window, changing only this
+setting: buttons moved from x=25/49/73 (left) to x=817/841/865 (right),
+landing in the same place and the same order as srdwm's own titlebar buttons
+directly above them.
+
+### What still cannot match
+
+The button *style*. srdwm's `button_style` draws srdwm's own titlebar; a
+GTK app draws its buttons with its GTK theme. Position and order can be made
+identical, as above; whether they are flat glyphs or coloured dots is the
+GTK theme's decision. On WhiteSur-Dark they are macOS-style dots by design.
+Changing that means changing the GTK theme.
diff --git a/docs/TODO.md b/docs/TODO.md
index d6e0cad..971d0cd 100644
--- a/docs/TODO.md
+++ b/docs/TODO.md
@@ -1,5 +1,63 @@
# TODO / planned features - master checklist
+## Deep dive: can every window use the same decorations, client- or server-side (2026-08-28)
+
+Asked after being told "srdwm can only control its own titlebar" - correctly
+pushed back on, because that answer was too quick. It is half wrong, and the
+half that is right is right for a different reason than given.
+
+**Measured first, against a nested srdwm, one client at a time:**
+
+ Qt/KDE (Dolphin) creates a decoration object, asks for server-side
+ winit (Alacritty) creates a decoration object, asks for CLIENT-side
+ GTK3/4 (Nemo) never creates a decoration object at all
+
+**The spec, read rather than remembered** (`xdg-decoration-unstable-v1`):
+"The compositor can decide not to use the client's mode and enforce a
+different mode instead", and "the specified mode must be obeyed by the
+client". So rows one and two are entirely srdwm's to decide - it had simply
+been choosing to defer. The same spec closes the door on row three: "if
+compositor and client do not negotiate the use of a server-side decoration
+... clients continue to self-decorate as they see fit". GTK is not having
+the conversation, so no compositor setting can reach its buttons.
+
+**What the other desktops actually do.** KWin defaults to server-side on
+Wayland and offers a per-window rule ("No titlebar and frame", Force/No) to
+push CSD clients back to server-side - the same override this protocol text
+allows. For GTK it does not use the protocol at all: `kde-gtk-config` exists
+purely to write GTK's own setting so GTK draws its buttons where KWin would
+have. GNOME goes the other way: GTK reads the layout from the desktop, and
+on Wayland GTK4 takes it from `xdg-desktop-portal`'s
+`org.freedesktop.portal.Settings`, key `org.gnome.desktop.wm.preferences`
+`button-layout`.
+
+**Both halves built and verified.**
+
+1. `theme.decorations.force_server_side` (default off). With it off,
+ Alacritty draws its own content straight to the window's top edge; with
+ it on, the same window gets srdwm's titlebar - sampled at three rows,
+ `(0,0,0)` content versus `(46,52,64)` titlebar. Off by default because it
+ cannot move a GTK button and *can* stack srdwm's titlebar on a client
+ that draws its own regardless, which is the Firefox case this project
+ already hit.
+
+2. The GTK half needs no srdwm code, only the right desktop setting. The
+ portal on this machine was serving `close,minimize,maximize:` - buttons
+ on the left - while srdwm's own `button_side` was `right`. That single
+ contradiction is the whole "some windows are still using traffic lights
+ on the wrong side" report. Setting
+ `org.gnome.desktop.wm.preferences button-layout` to
+ `:minimize,maximize,close` moved Nemo's own buttons from x=25/49/73 to
+ x=817/841/865 - the same position and order as srdwm's own buttons
+ directly above them, screenshotted before and after with nothing else
+ changed.
+
+**What genuinely cannot match:** button *style*. Position and order can be
+made identical; whether the buttons are flat glyphs or coloured dots is the
+GTK theme's decision, and on WhiteSur-Dark they are macOS dots by design.
+
+275 core tests, 525 total, clippy clean.
+
## Five reports after the second restart: a fix that landed on the wrong branch, and a config that fought itself (2026-08-28)
**The maximize border was still there because the fix landed on the wrong