From 11646cafcf1329ba4ce7caaedf1ac173ce8c460e Mon Sep 17 00:00:00 2001 From: server2 Date: Tue, 13 Oct 2026 06:15:37 +0200 Subject: [PATCH] Reserve the top of every monitor for the bar edge::spacer: a full-width, empty, click-through Background layer as tall as the pills reach (TOP_GAP + COMPACT_H = 44 px) with Zone::Reserve. Hyprland keeps tiled and maximised windows below it and lets fullscreen windows cover it, so space is reserved exactly where the bar stays. Verified: reserved 0 44 0 0 on all monitors, a tiled window moved from y=636 to 680, a fullscreen one still covers 1920x1080, idle 0 frames. --- README.md | 3 ++- src/edge.rs | 19 +++++++++++++++++++ src/main.rs | 1 + 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fe8a1e4..3be60cd 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ A Dynamic-Island-style media widget for Amane (Rust Wayland shell library), runn - **System tray:** right of the workspace pill, a pill with every tray app's icon (StatusNotifierItem). Left click activates the app (or opens its menu if that's all it offers), right click opens its menu, drawn in the island's look (checkmarks, separators, submenus with a way back, disabled entries dim), middle click is the app's secondary action, scrolling its scroll. Nothing on this desktop ran a tray watcher, so the shell owns `org.kde.StatusNotifierWatcher` on the session bus and serves it (if another bar already does, the shell shows no tray). Icons come from the app's icon name (its own theme folder, gtk's icon theme, hicolor, AdwaitaLegacy, Adwaita, `/usr/share/pixmaps`) or from the pixels it sends, written as PNGs to `$XDG_RUNTIME_DIR/amane-tray/`. An app that quits takes its icon with it (`src/tray.rs`). - **Control centre:** top right on every monitor, a pill with Ethernet, Wi-Fi (or the hotspot), VPN, Bluetooth and the battery. Click it and it opens, in the island's way, into a panel like macOS's Control Center. See [Control centre](#control-centre). - **Out of the way of fullscreen windows:** on a monitor whose workspace shows a fullscreen window (Hyprland fullscreen 2 or 3; only maximised keeps the bar), the island, the workspace pill (with the tray) and the control centre's pill slide out of sight. Resting the pointer on that monitor's top edge for 0.3 s brings them back until it has been off them and the edge for 0.7 s; an open card or panel stays. Everywhere else the bar is always there. Hyprland's event socket says when windows go fullscreen or workspaces change, and its command socket answers `monitors` and `clients`; nothing polls. The edge strip is a one-pixel Overlay layer, there only on fullscreen monitors (it takes their top pixel row). While hidden the bar draws nothing at all, a playing player included (`src/edge.rs`). +- **Room for the bar:** windows that aren't fullscreen stay below the bar. An empty, click-through strip as tall as the pills reach (44 px) reserves the top of every monitor (a layer-shell exclusive zone, namespace `magic-reserve`); Hyprland keeps tiled and maximised windows out of it, plus `gaps_out`, while fullscreen windows cover it, just where the bar hides. The reservation goes away with the shell. The bar's own windows ignore it, so they stay at the top edge. - **Peeks:** some events bring one part in for a moment over a fullscreen window: the workspace pill alone (without the tray) for 1.6 s when the workspace changes (with the disc sliding over), the island for 3.5 s when the song changes or a player appears, on the focused monitor. The island also pops (it widens and glows, then lets go) on those events when it's visible anyway. The control centre never peeks. ## Control centre @@ -131,7 +132,7 @@ src/workspaces.rs the workspace pill, top left on every monitor src/control.rs the control centre: status pill top right, its panel and menus, `control` IPC src/map.rs the Wi-Fi and Bluetooth menus' map: this desktop in the middle, what it can connect to around it src/prompt.rs the control centre's popup: passwords, PINs, codes, saved passwords, why a connection failed; joins through iwctl, pairs through bluetoothctl -src/edge.rs hides the bar over fullscreen windows (Hyprland's sockets), the edge strip, peeks, the workspace slide +src/edge.rs hides the bar over fullscreen windows (Hyprland's sockets), the edge strip, peeks, the workspace slide, the space reserved for the bar src/tray.rs the system tray: StatusNotifierWatcher and host, icon lookup, dbusmenu menus src/png.rs a stdlib png writer, for tray icons sent as pixels src/system.rs what the control centre shows: iwd, wired cards, openvpn, power, the external GPU diff --git a/src/edge.rs b/src/edge.rs index bbeca13..0bd0272 100644 --- a/src/edge.rs +++ b/src/edge.rs @@ -410,3 +410,22 @@ pub fn view(monitor: &Monitor) -> LayerWindow { window.child(Rectangle::new().width(Parent).height(Parent).on_hover(at_edge(monitor.name.clone()))) } + +/* + * keeps windows out from under the bar: an empty strip as tall as the pills reach, + * reserving its height on every monitor. Hyprland lets tiled and maximised windows + * respect it and fullscreen ones cover it, which is just when the bar stays and when + * it hides; it goes with the shell. the bar's own windows ignore what's reserved, so + * they stay at the top edge. reads nothing, so it never redraws + */ +pub fn spacer(_: &Monitor) -> LayerWindow { + LayerWindow::new() + .width(Full) + .height(TOP_GAP + COMPACT_H) + .anchor_vertical(Vertical::Top) + .layer(Layer::Background) + .space(Zone::Reserve) + .namespace("magic-reserve") + .click_through() + .child(Rectangle::new().width(0.0).height(0.0)) +} diff --git a/src/main.rs b/src/main.rs index 0f9978b..42bb8d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,6 +28,7 @@ fn main() { .ipc("island", island::command) .ipc("control", control::command) .ipc("tray", tray::command) + .window_per_monitor(edge::spacer) .window_per_monitor(edge::view) .window_per_monitor(island::view) .window_per_monitor(workspaces::view)