diff --git a/arc/platforms.md b/arc/platforms.md
index 52d167b6a3235559e31cdba73976fa27e4a09960..a47bd0d46ecd439a3b812d92c3b8c90864a3ef86 100644
--- a/arc/platforms.md
+++ b/arc/platforms.md
@@ -63,16 +63,20 @@ keyboard, the toolbar and the macOS menu bar all run commands from it.
- X11 and Wayland. Title bars come from the window manager, or on Wayland from
the compositor where it offers server-side decorations, and the toolbar's
row lies beneath them, as in Dolphin and Kate. Elsewhere on Wayland, as on
- GNOME, the row is the title bar, as a GTK 4 header bar holding tools: winit's
- Adwaita frame keeps its shadow, corners and resize edges but has no header,
- and the row ends in the window's buttons in the order GNOME's `button-layout`
- lists them. Under that frame on GNOME the window erases its bottom
- corners to transparent pixels and the frame draws libadwaita's window edge
- round it: radius, shadows and outline. KWin rounds Breeze's corners itself.
+ GNOME, the row is the title bar, as a GTK 4 header bar holding tools:
+ winit's Adwaita frame keeps its shadow, corners and resize edges but has no
+ header, and the window's buttons sit at the row's ends as GNOME's
+ `button-layout` places them, kept current through the settings portal. On
+ GNOME they are libadwaita's, sized and coloured as the release that came
+ with the running GNOME Shell draws them; elsewhere they are the toolbar's
+ own buttons, not an imitation of a theme Snowbound can't read. Under that
+ frame on GNOME the window erases its bottom corners to transparent pixels
+ and the frame draws libadwaita's window edge round it: radius, shadows and
+ outline. KWin rounds Breeze's corners itself.
- The toolbar and the rest of the chrome continue the title bar's fill,
focused and not: on KDE the colour scheme's header colours from
- `kdeglobals`, as KWin paints its title bars, and on GNOME winit's Adwaita
- frame's. A settings portal signal re-reads them when the scheme changes.
+ `kdeglobals`, as KWin paints its title bars, and on GNOME libadwaita's
+ header bar's. A settings portal signal re-reads them when the scheme changes.
- Menus and other popups take the desktop's look and motion: libadwaita's
popover menus on GNOME, shown and hidden at once as GTK 4 does, and Breeze's
on KDE, faded as KWin fades popups and scaled by Plasma's animation speed.
diff --git a/crates/snowbound/assets/adwaita/window-close.svg b/crates/snowbound/assets/adwaita/window-close.svg
new file mode 100644
index 0000000000000000000000000000000000000000..9e4cde45123b8068a16c2e24640c162967ca6998
--- /dev/null
+++ b/crates/snowbound/assets/adwaita/window-close.svg
@@ -0,0 +1,3 @@
+
diff --git a/crates/snowbound/assets/adwaita/window-maximize.svg b/crates/snowbound/assets/adwaita/window-maximize.svg
new file mode 100644
index 0000000000000000000000000000000000000000..56dedbc9d89f646578e98ba6764028a115f6ab6b
--- /dev/null
+++ b/crates/snowbound/assets/adwaita/window-maximize.svg
@@ -0,0 +1,3 @@
+
diff --git a/crates/snowbound/assets/adwaita/window-minimize.svg b/crates/snowbound/assets/adwaita/window-minimize.svg
new file mode 100644
index 0000000000000000000000000000000000000000..702a692dfbdd060e96a21276518bb5307121997a
--- /dev/null
+++ b/crates/snowbound/assets/adwaita/window-minimize.svg
@@ -0,0 +1,3 @@
+
diff --git a/crates/snowbound/assets/adwaita/window-restore.svg b/crates/snowbound/assets/adwaita/window-restore.svg
new file mode 100644
index 0000000000000000000000000000000000000000..57a2dc1a021bbf3f9e22c24c0eeea9936b705487
--- /dev/null
+++ b/crates/snowbound/assets/adwaita/window-restore.svg
@@ -0,0 +1,3 @@
+
diff --git a/crates/snowbound/src/linux.rs b/crates/snowbound/src/linux.rs
index 84cce0188c05e08e6e35fdfcea9ec8663ff9e69a..0d411b87f3bc381f123c740e75e87346cb17d8ac 100644
--- a/crates/snowbound/src/linux.rs
+++ b/crates/snowbound/src/linux.rs
@@ -14,7 +14,7 @@ use std::{
path::PathBuf,
process::Command,
sync::{
- OnceLock,
+ Mutex, OnceLock,
atomic::{AtomicBool, AtomicU32, Ordering},
},
time::Duration,
@@ -30,10 +30,9 @@ use winit::{
};
use zbus::zvariant::{OwnedObjectPath, OwnedValue, Value};
-/// The title bar's leading margin; the window controls sit at its trailing end.
-pub const LEADING: f32 = 8.0;
-/// The margin past the window controls.
-pub const TRAILING: f32 = 8.0;
+/// The title bar's margins at its leading and trailing ends, libadwaita's header bar's.
+pub const LEADING: f32 = 7.0;
+pub const TRAILING: f32 = 7.0;
/// How far inside the window's edges a press resizes it, and how far along them a corner
/// reaches, in logical pixels.
const EDGE: f32 = 5.0;
@@ -41,6 +40,14 @@ const CORNER: f32 = 16.0;
const MINIMIZE: &[&str] = &[include_str!("../assets/icons/window-minimize.svg")];
const MAXIMIZE: &[&str] = &[include_str!("../assets/icons/window-maximize.svg")];
const RESTORE: &[&str] = &[include_str!("../assets/icons/window-restore.svg")];
+/// Adwaita's `window-*-symbolic` glyphs, which GTK's window controls show.
+const ADWAITA_MINIMIZE: &[&str] = &[include_str!("../assets/adwaita/window-minimize.svg")];
+const ADWAITA_MAXIMIZE: &[&str] = &[include_str!("../assets/adwaita/window-maximize.svg")];
+const ADWAITA_RESTORE: &[&str] = &[include_str!("../assets/adwaita/window-restore.svg")];
+const ADWAITA_CLOSE: &[&str] = &[include_str!("../assets/adwaita/window-close.svg")];
+/// GNOME's `button-layout`, as GTK reads it: the window's buttons at the title bar's start,
+/// a colon, then those at its end. Read once, then kept by the settings portal's signal.
+static BUTTON_LAYOUT: Mutex