diff options
| author | srdusr <[email protected]> | 2026-07-27 01:33:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2026-07-27 01:33:00 +0200 |
| commit | 3c47912ae0a2b81d06e4e9101b5b4e2f1e2997d9 (patch) | |
| tree | be0aa3740beb1ec18dfccbd68a4fcf16faa8638b /crates/wayland/src/protocols | |
| parent | e20d49b0ee3dbd83499445d61eb2d65904d74311 (diff) | |
| download | srdwm-3c47912ae0a2b81d06e4e9101b5b4e2f1e2997d9.tar.gz srdwm-3c47912ae0a2b81d06e4e9101b5b4e2f1e2997d9.zip | |
Reach GTK4's window buttons too, and speak the decoration protocol GTK knows
Two findings from reading what other projects do, and then measuring this
machine rather than trusting the reading.
GTK has never implemented xdg-decoration, so a compositor that advertises
only that protocol is invisible to every GTK client on the decoration
question. What GTK does implement is KDE's older
org_kde_kwin_server_decoration - confirmed by reading libgtk-4's own symbol
strings, where the manager, the mode enum and the default-mode handler are
all present, and absent from libgtk-3. That is the channel a KDE session
uses. srdwm now advertises it alongside xdg-decoration, with both answering
from the same policy (theme.default_decorated, theme.force_server_side) so a
client is told the same thing whichever it asks through.
Measured what that actually buys, with WAYLAND_DEBUG on a real GTK4 client:
it binds the manager and receives default_mode(2) = Server, and then never
creates a decoration object for its window. So it changes nothing for a GTK
application's own header bar, and it is kept because it is the correct thing
to advertise and because clients that do honour it - Qt and KDE's own --
now get server-side decoration from srdwm instead of nothing.
The second finding is the one that fixes what was reported. GTK3 and GTK4
put their window buttons under different CSS selectors, and the generated
stylesheet named only GTK3's. A diagnostic rule proved it both ways: a flat
colour reached Nemo (GTK3) through `headerbar button.titlebutton` and
gnome-calculator (GTK4) through `windowcontrols button`, and neither
selector reached the other toolkit. Every style is now written for both, so
a GTK4 application is styled rather than silently skipped. `headerbar`
itself works in both, so the titlebar block needed no split.
Verified on screen: gnome-calculator, a GTK4/libadwaita application, now
draws its header bar in srdwm's own titlebar colour with srdwm's text
colour, where before it kept its theme's.
Also worth writing down, because it bounds what any of this can achieve: an
application's header bar is its own widget. No protocol removes it. GTK_CSD=0
does not, the KDE protocol does not, and neither does forcing server-side
decoration - that only adds a second titlebar above the first. What a
compositor can do is make the two look like one, which is what this does.
Diffstat (limited to 'crates/wayland/src/protocols')
| -rw-r--r-- | crates/wayland/src/protocols/kde_decoration.rs | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/crates/wayland/src/protocols/kde_decoration.rs b/crates/wayland/src/protocols/kde_decoration.rs new file mode 100644 index 0000000..2448a39 --- /dev/null +++ b/crates/wayland/src/protocols/kde_decoration.rs @@ -0,0 +1,74 @@ +//! `org_kde_kwin_server_decoration`: KDE's older decoration protocol, and +//! the only decoration protocol GTK actually speaks. +//! +//! GTK has never implemented `xdg-decoration`. It does implement this one: +//! `org_kde_kwin_server_decoration_manager` is present in libgtk-4 on this +//! machine (confirmed by reading the library's own symbol strings), and it +//! is how a KDE session gets GTK applications to stop drawing their own +//! frame. A compositor that advertises only `xdg-decoration` is invisible +//! to those clients, which is why "set the decoration once and every +//! application follows" stopped at srdwm's own titlebars. +//! +//! Both protocols are advertised, and both answer with the same policy +//! (`theme.default_decorated`, `theme.force_server_side`), so a client is +//! told the same thing whichever one it asks through. +//! +//! The protocol's own warning applies here: a client may ignore the mode +//! the compositor suggests and ask for its own. That is honoured the same +//! way `xdg_decoration.rs` honours it, and for the same reason - a client +//! that draws its own titlebar regardless (Firefox with its system-titlebar +//! setting off) would otherwise get srdwm's row on top of its own. + +use smithay::reexports::wayland_protocols_misc::server_decoration::server::org_kde_kwin_server_decoration::{ + Mode, OrgKdeKwinServerDecoration, +}; +use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface; +use smithay::reexports::wayland_server::WEnum; +use smithay::wayland::shell::kde::decoration::{KdeDecorationHandler, KdeDecorationState}; + +use crate::state::CompState; + +impl KdeDecorationHandler for CompState { + fn kde_decoration_state(&self) -> &KdeDecorationState { + &self.kde_decoration_state + } + + /// Tells a client, the moment it asks, which mode this compositor + /// wants - the same answer `XdgDecorationHandler::new_decoration` + /// gives through the other protocol. + /// + /// The manager's own default mode (set once, at startup) is what a + /// client sees before it creates a decoration object at all; this is + /// what it sees afterward, and it has to agree, or a client that reads + /// both ends up with two different answers. + fn new_decoration(&mut self, surface: &WlSurface, decoration: &OrgKdeKwinServerDecoration) { + let server = self.wm.borrow().theme.default_decorated; + decoration.mode(if server { Mode::Server } else { Mode::Client }); + self.set_decorated_from_mode(surface, server); + } + + /// Honours what the client asked for, unless `force_server_side` says + /// otherwise - identical policy to the xdg-decoration path, and the + /// mode is echoed back either way because the protocol requires the + /// compositor to confirm what it decided. + fn request_mode(&mut self, surface: &WlSurface, decoration: &OrgKdeKwinServerDecoration, mode: WEnum<Mode>) { + let WEnum::Value(requested) = mode else { return }; + let forced = self.wm.borrow().theme.force_server_side; + // `Mode::None` means no decoration at all, which for srdwm's + // purposes is a client saying it wants nothing drawn around it -- + // treated as client-side, the same as `Mode::Client`, rather than + // as a request for a titlebar. + let server = forced || requested == Mode::Server; + let granted = if server { Mode::Server } else { requested }; + decoration.mode(granted); + self.set_decorated_from_mode(surface, server); + } + + /// The client is going away, or has dropped its decoration object. + /// Nothing to undo: `remove_window` already clears everything keyed on + /// this surface, and a surface with no decoration object keeps + /// whatever mode it last negotiated, exactly as before. + fn release(&mut self, _decoration: &OrgKdeKwinServerDecoration, _surface: &WlSurface) {} +} + +smithay::delegate_kde_decoration!(CompState); |