Tray, fullscreen-only hiding, peeks, Ethernet details, wider maps

- tray.rs: the shell serves org.kde.StatusNotifierWatcher (only when no other
  watcher runs) and is its host; icons from the theme or from IconPixmap
  (png.rs, a stdlib png writer); left/middle/right click and scroll; dbusmenu
  menus drawn in the island's look; tray IPC verbs for testing
- edge.rs: the bar hides only on monitors showing a fullscreen window, driven
  by Hyprland's event socket; peeks of the workspaces (with the disc sliding)
  and the island (song change, new player), with a pop on the island
- prompt.rs: Ethernet details popup (addresses, gateway, DNS, MAC copyable)
- map.rs: 1100x560 maps, every other node a ring further in
- tools/fake_tray.py: a fake tray app, by bus name or by path
This commit is contained in:
server2
2026-10-13 04:58:08 +02:00
parent b14df3a435
commit 1e99d7264c
13 changed files with 1963 additions and 126 deletions
+15 -7
View File
@@ -11,9 +11,11 @@ A Dynamic-Island-style media widget for Amane (Rust Wayland shell library), runn
- **Background:** a diagonal gradient in the wallpaper's colours (see Colours); optionally the open card takes the song cover's colours.
- **Opening animation:** the pill fades out, the empty shape grows, then the card fades in. Closing runs backwards. Moving the pointer off the open card closes it.
- **Every monitor:** each monitor shows the pill. The card opens only on the monitor you click (a keybind opens it on the focused one), and only the focused monitor's pill shows live bars; the others show still bars, which keeps the cost at about one island's.
- **Workspace pill:** top left on every monitor, that monitor's Hyprland workspaces as round numbered slots in the island's look. The focused workspace takes the accent colour, the one shown on another monitor a softer accent, empty ones dim. Click one to switch to it.
- **Workspace pill:** top left on every monitor, that monitor's Hyprland workspaces as round numbered slots in the island's look. The focused workspace sits on an accent disc, which slides over (stretching a little on the way) when the workspace changes; the one shown on another monitor has a softer accent, empty ones are dim. Click one to switch to it.
- **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).
- **Hidden until wanted:** the island, the workspace pill and the control centre's pill stay out of sight until the pointer rests on a monitor's top edge for 0.3 s; then they slide down on that monitor, and slide away 0.7 s after the pointer has left them and the edge. An open card or panel stays. A one-pixel strip along each monitor's top notices the pointer (nothing polls); it's an Overlay layer like the bar, so this works over fullscreen windows too, at the cost of their top pixel row. While hidden the bar draws nothing at all, a playing player included (`src/edge.rs`).
- **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`).
- **Peeks:** some events bring one part in for a moment over a fullscreen window: the workspace pill 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
@@ -26,9 +28,9 @@ The panel has:
- *Bluetooth* (BlueZ, through Amane's `Bluetooth`): the switch powers the adapter; with bluez installed but stopped it runs `rc-service bluetooth start`. The menu is the same map with the devices bluez knows or finds (it scans while open): connected ones closest, then paired, then the rest. Click to connect, disconnect, or pair (then trust and connect). Pairing and connecting go through a `bluetoothctl` session with its own agent: a PIN or passkey it needs, a code to confirm or to type on the device are asked in a popup, and a failure shows why. Click the bubble to search for devices (`bluetoothctl --timeout 15 scan on`).
- *VPN*: every openrc service `/etc/init.d/openvpn.*` (here `openvpn.swiss`), each with a switch. Connecting while openrc has it *inactive*, connected once its `up.sh` marks it *started*. A switch runs `rc-service openvpn.<name> start|stop`.
- *Hotspot* (iwd access point): see [Hotspot setup](#hotspot-setup).
- *Ethernet*: every real wired card in `/sys/class/net` (a device behind it, not wireless): connected or no cable, and the link speed it negotiated (`speed`, as 1 Gb/s). Shows only.
- *Ethernet*: every real wired card in `/sys/class/net` (a device behind it, not wireless): connected or no cable, and the link speed it negotiated (`speed`, as 1 Gb/s). A click opens a popup beside the panel with the interface and driver, status (speed, duplex), every IPv4 and IPv6 address, gateway, DNS servers, MAC, MTU and traffic since boot (`ip -o addr`, `ip -o route`, `/etc/resolv.conf`, sysfs). Addresses, gateway, DNS and MAC have a Copy chip; a click anywhere on such a row copies it (addresses without their prefix).
- **Popups:** questions (a password, PIN or passkey), a code to confirm, a saved password or why something failed, in a small card over the menu. Only a popup with a field takes the keyboard (Enter answers, Escape cancels); the others leave keys to other windows until clicked.
- **Menus:** one shape the whole way: the panel fades its content out and takes the menu's size (the Wi-Fi and Bluetooth maps are 820 px wide, more than twice the panel), then the menu's content fades in over it from a window of its own (so the maps' animation redraws only that window). The back button returns to the panel. Moving the pointer off both closes the menu where it is: its content fades out and it shrinks straight into the pill, without the panel growing back first.
- **Menus:** one shape the whole way: the panel fades its content out and takes the menu's size (the Wi-Fi and Bluetooth maps are 1100 × 560 px, three times the panel's width, with every other node a ring further in so neighbouring labels don't meet), then the menu's content fades in over it from a window of its own (so the maps' animation redraws only that window). The back button returns to the panel. Moving the pointer off both closes the menu where it is: its content fades out and it shrinks straight into the pill, without the panel growing back first.
- **Sound:** the default output's volume (drag or scroll the slider); the value next to the title is the mute button. With `extra_output` in `shell.conf` a second slider follows for the output whose name contains it (here `NR515`: the `[TX-NR515]` receiver on HDMI 1). Clicking the block elsewhere opens the Sound menu: every output with its own slider and mute; click a name to make it the default output.
- **Display:** the laptop screen's brightness and the keyboard light (`tpacpi::kbd_backlight`: Off, Low, High), written straight to their sysfs files (a udev rule makes them writable by everyone on this laptop), else through logind, which refuses a shell without a seat (one started over ssh). For external monitors see [External monitor brightness](#external-monitor-brightness).
- **Performance:** the firmware profiles in `/sys/firmware/acpi/platform_profile_choices` (Low power, Balanced, Performance), and the battery's state and time left. Picking one sets the platform profile and the CPU governor and energy preference with it: Low power = `powersave` + `power`, Balanced = `powersave` + `balance_performance`, Performance = the `performance` governor.
@@ -43,6 +45,10 @@ amane ipc call control status # expanded=<bool> menu=<name|none> wifi=<state>
amane ipc call control wifi join <SSID> # as a click on it in the map; also: wifi password <SSID>, wifi search
amane ipc call control bluetooth pair <ADDRESS> # also: bluetooth connect <ADDRESS>, bluetooth search
amane ipc call control prompt answer <text> # answers the popup; also: prompt cancel
amane ipc call control ethernet # as a click on the Ethernet tile
amane ipc call tray list # every tray item: key, title, icon file
amane ipc call tray open <n> # the n-th icon's menu, as a right click; also: activate <n>
amane ipc call tray menu # the open menu's entries; then: choose <id>, enter <id>, back, close
```
### Hotspot setup
@@ -125,7 +131,9 @@ 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 until the pointer rests on a monitor's top edge
src/edge.rs hides the bar over fullscreen windows (Hyprland's sockets), the edge strip, peeks, the workspace slide
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
src/glyph.rs every drawn icon, as a Canvas or as shapes for a shared one
src/theme.rs the shell's colours: the matugen theme file, shell.conf, the cross-fade
@@ -133,7 +141,7 @@ src/parse.rs pure text parsing with unit tests, no amane dependency
matugen/ the matugen template (amane.colors), rendered by skwd-wall into ./theme
shell.conf settings: cover_colors, transition_ms, hotspot, extra_output
island-eq saved equalizer curves (a snapshot of ~/.config/amane/island-eq)
tools/ remote test driver and fake MPRIS players, see AGENTS.md
tools/ remote test driver, fake MPRIS players, a fake tray app (`fake_tray.py name|path`), see AGENTS.md
```
## Requirements
@@ -144,7 +152,7 @@ On the desktop that runs the shell:
- PipeWire with `pipewire-pulse`: `pactl`, `pw-record`, `pw-cli`, `pipewire` (filter-chain module)
- `cava`, `curl`
- the Fira Code font
- for the control centre: `iwd`, openrc `openvpn.<name>` services, `pciutils` (`lspci`), `wl-clipboard` (`wl-copy`, for copying a saved password), optionally `bluez` (with `bluetoothctl`), and a doas rule without a password for `rc-service`, `sh` and `cat` (see [Control centre](#control-centre))
- for the control centre: `iwd`, openrc `openvpn.<name>` services, `pciutils` (`lspci`), `wl-clipboard` (`wl-copy`, for copying a saved password or an address), `iproute2` (`ip`, for the Ethernet details), optionally `bluez` (with `bluetoothctl`), and a doas rule without a password for `rc-service`, `sh` and `cat` (see [Control centre](#control-centre))
## Install and run