srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/crates/wayland/src/protocols/xdg_decoration.rs
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2025-02-15 14:56:00 +0200
committersrdusr <[email protected]>2025-02-15 14:56:00 +0200
commit0a4c4b5941fe982ccb3d3175e26d9d83f0025ffd (patch)
tree7672d0af277664f457c6c9462925c0005fe35dcf /crates/wayland/src/protocols/xdg_decoration.rs
parent413daa7ba2ea0ebd1424c024fd0566423aaea3f8 (diff)
downloadsrdwm-0a4c4b5941fe982ccb3d3175e26d9d83f0025ffd.tar.gz
srdwm-0a4c4b5941fe982ccb3d3175e26d9d83f0025ffd.zip
Checkpoint: preserve all uncommitted rust-rewrite worktree work
Safety commit before reconciling this worktree with main, which has diverged with its own separate fixes today. Nothing here is reviewed or curated yet - this exists purely so none of this work can be lost to a git operation, disk issue, or worktree cleanup while that reconciliation happens.
Diffstat (limited to 'crates/wayland/src/protocols/xdg_decoration.rs')
-rw-r--r--crates/wayland/src/protocols/xdg_decoration.rs63
1 files changed, 63 insertions, 0 deletions
diff --git a/crates/wayland/src/protocols/xdg_decoration.rs b/crates/wayland/src/protocols/xdg_decoration.rs
new file mode 100644
index 0000000..25973c0
--- /dev/null
+++ b/crates/wayland/src/protocols/xdg_decoration.rs
@@ -0,0 +1,63 @@
+//! `zxdg_decoration_manager_v1`: negotiates whether a toplevel draws its own
+//! (client-side) chrome or lets us draw it (server-side).
+
+use smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1::Mode as DecorationMode;
+use smithay::wayland::shell::xdg::decoration::XdgDecorationHandler;
+use smithay::wayland::shell::xdg::ToplevelSurface;
+
+use crate::state::CompState;
+
+impl XdgDecorationHandler for CompState {
+ /// Offers whichever mode `theme.decorations.default_mode`/`srd set
+ /// decoration_mode` currently prefers - a client with a real opinion
+ /// of its own still overrides this via `request_mode` below regardless
+ /// of what's offered here; this only decides what a client with *no*
+ /// preference ends up with. See `srdwm_core::ThemeConfig::
+ /// default_decorated`'s own doc comment for why this is configurable
+ /// rather than hardcoded to one mode.
+ fn new_decoration(&mut self, toplevel: ToplevelSurface) {
+ let offer = if self.wm.borrow().theme.default_decorated { DecorationMode::ServerSide } else { DecorationMode::ClientSide };
+ toplevel.with_pending_state(|state| {
+ state.decoration_mode = Some(offer);
+ });
+ }
+
+ /// Honors whichever mode the client actually asked for, rather than
+ /// always forcing server-side - and mirrors the result into our own
+ /// `Window.decorated`, so a client drawing its own titlebar doesn't
+ /// *also* get one drawn on top of it by us.
+ ///
+ /// Always forcing `ServerSide` (what this used to do) is why some
+ /// clients ended up with two sets of window buttons: Firefox requests
+ /// client-side decoration when its own "use system titlebar" setting
+ /// is off, and draws its own close/minimize/maximize row regardless of
+ /// what the compositor grants - so forcing server-side just added
+ /// srdwm's row on top of the one Firefox was drawing anyway, instead
+ /// of preventing it. Respecting the request means srdwm steps out of
+ /// 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.
+ fn request_mode(&mut self, toplevel: ToplevelSurface, mode: DecorationMode) {
+ toplevel.with_pending_state(|state| {
+ state.decoration_mode = Some(mode);
+ });
+ toplevel.send_configure();
+ self.set_decorated_from_mode(toplevel.wl_surface(), mode == DecorationMode::ServerSide);
+ }
+
+ /// The client dropped its decoration-mode preference. `new_decoration`
+ /// already offers the configured default as the mode the next
+ /// configure will carry, so mirror that same default here rather than
+ /// leaving whatever mode was negotiated before this - otherwise a
+ /// client that requests one mode, then later unsets it expecting the
+ /// default back, would stay stuck in that mode forever.
+ fn unset_mode(&mut self, toplevel: ToplevelSurface) {
+ let default_decorated = self.wm.borrow().theme.default_decorated;
+ let mode = if default_decorated { DecorationMode::ServerSide } else { DecorationMode::ClientSide };
+ toplevel.with_pending_state(|state| {
+ state.decoration_mode = Some(mode);
+ });
+ toplevel.send_configure();
+ self.set_decorated_from_mode(toplevel.wl_surface(), default_decorated);
+ }
+}