nixpkgs wraps waybar, so the running comm is `.waybar-wrapped`: `pkill -x waybar` (state-sync restart/SIGUSR2 branches, tz-watch, updates) matched nothing — a theme switch neither restarted nor reloaded the bar, which then kept the gtk.css of whatever theme it started under (the stale workspace-digit colors reported 2026-07-18; companion gtk fix in the next commit). The unanchored pokes (recording, airplane, theme-shot) had the opposite bug: `waybar` substring-matches the supervisor's `nomarchy-waybar` comm too, and an unhandled RTMIN terminates its bash — the pokes were killing the crash guard. All seven sites now use `pkill … -x 'waybar|\.waybar-wrapped'`. state-sync 0.5.1 → 0.5.2. Verified live on Newton (V3-grade): new pattern restarts the bar under the supervisor (fresh pid, supervisor alive); RTMIN+10 poke reaches the bar, supervisor survives. V0 `nix flake check --no-build` + py_compile; V1 HM generation + system toplevel build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
128 lines
3.9 KiB
Nix
128 lines
3.9 KiB
Nix
{ lib
|
|
, writeShellScriptBin
|
|
, networkmanager
|
|
, util-linux
|
|
, bluez
|
|
, coreutils
|
|
, gnugrep
|
|
, gawk
|
|
, libnotify
|
|
, procps
|
|
}:
|
|
|
|
# Runtime Wi-Fi + Bluetooth airplane mode (#104). Session-scoped state under XDG
|
|
# $XDG_RUNTIME_DIR; Waybar status self-hides when off (signal 11).
|
|
writeShellScriptBin "nomarchy-airplane" ''
|
|
set -euo pipefail
|
|
rt="''${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
|
|
state_file="$rt/nomarchy-airplane.state"
|
|
# Prefer an existing PATH prefix (test mocks, user wrappers) then the
|
|
# closed store tools so a headless check can inject fake nmcli/rfkill.
|
|
PATH=$PATH:${lib.makeBinPath [
|
|
networkmanager util-linux bluez coreutils gnugrep gawk libnotify procps
|
|
]}
|
|
|
|
# -x, both comm names: nixpkgs wraps the binary as `.waybar-wrapped`,
|
|
# and an unanchored `waybar` also matches (and kills) the
|
|
# nomarchy-waybar supervisor, whose bash dies on an unhandled RTMIN.
|
|
poke_bar() {
|
|
pkill -RTMIN+11 -x 'waybar|\.waybar-wrapped' 2>/dev/null || true
|
|
}
|
|
|
|
wifi_enabled() {
|
|
[ "$(nmcli -t -f WIFI g 2>/dev/null || echo disabled)" = "enabled" ]
|
|
}
|
|
|
|
# Soft-blocked means "we (or something) turned it off in software".
|
|
# Hard-blocked is a laptop kill-switch — we never try to fight that.
|
|
bt_soft_unblocked() {
|
|
rfkill list bluetooth 2>/dev/null | grep -q 'Soft blocked: no'
|
|
}
|
|
|
|
bt_powered() {
|
|
bluetoothctl show 2>/dev/null | grep -q 'Powered: yes'
|
|
}
|
|
|
|
# "Bluetooth was usable" = adapter present and not soft-blocked (or
|
|
# already powered). Desktops without BT leave this false.
|
|
bt_was_on() {
|
|
ls /sys/class/bluetooth/hci* >/dev/null 2>&1 || return 1
|
|
bt_powered || bt_soft_unblocked
|
|
}
|
|
|
|
engaged() { [ -f "$state_file" ]; }
|
|
|
|
engage() {
|
|
if engaged; then
|
|
notify-send -a Nomarchy "Airplane mode" "Already on." 2>/dev/null || true
|
|
exit 0
|
|
fi
|
|
wifi_prior=0
|
|
bt_prior=0
|
|
wifi_enabled && wifi_prior=1
|
|
bt_was_on && bt_prior=1
|
|
|
|
# Wi-Fi via NetworkManager so nm-applet/waybar stay consistent;
|
|
# fall back to rfkill if nmcli is missing (shouldn't happen on
|
|
# a Nomarchy desktop).
|
|
if command -v nmcli >/dev/null 2>&1; then
|
|
nmcli radio wifi off 2>/dev/null || true
|
|
else
|
|
rfkill block wlan 2>/dev/null || true
|
|
fi
|
|
# Bluetooth: soft-block the radio (covers adapters that ignore
|
|
# bluetoothctl) and ask the daemon to power off when present.
|
|
rfkill block bluetooth 2>/dev/null || true
|
|
bluetoothctl power off >/dev/null 2>&1 || true
|
|
|
|
printf 'wifi_prior=%s\nbt_prior=%s\n' "$wifi_prior" "$bt_prior" > "$state_file"
|
|
poke_bar
|
|
notify-send -a Nomarchy "Airplane mode" "On — Wi-Fi and Bluetooth off." 2>/dev/null || true
|
|
}
|
|
|
|
disengage() {
|
|
if ! engaged; then
|
|
notify-send -a Nomarchy "Airplane mode" "Already off." 2>/dev/null || true
|
|
exit 0
|
|
fi
|
|
# shellcheck disable=SC1090
|
|
. "$state_file"
|
|
wifi_prior=''${wifi_prior:-0}
|
|
bt_prior=''${bt_prior:-0}
|
|
|
|
if [ "$wifi_prior" = 1 ]; then
|
|
if command -v nmcli >/dev/null 2>&1; then
|
|
nmcli radio wifi on 2>/dev/null || true
|
|
else
|
|
rfkill unblock wlan 2>/dev/null || true
|
|
fi
|
|
fi
|
|
if [ "$bt_prior" = 1 ]; then
|
|
rfkill unblock bluetooth 2>/dev/null || true
|
|
bluetoothctl power on >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
rm -f "$state_file"
|
|
poke_bar
|
|
notify-send -a Nomarchy "Airplane mode" "Off — prior radio state restored." 2>/dev/null || true
|
|
}
|
|
|
|
case "''${1:-toggle}" in
|
|
status)
|
|
# Waybar: plane glyph only while engaged; empty → self-hide.
|
|
if engaged; then
|
|
printf '{"text":"","tooltip":"Airplane mode on — Wi-Fi and Bluetooth off (click to restore)","class":"on"}\n'
|
|
fi
|
|
exit 0 ;;
|
|
is-active)
|
|
if engaged; then echo on; else echo off; fi ;;
|
|
on) engage ;;
|
|
off) disengage ;;
|
|
toggle)
|
|
if engaged; then disengage; else engage; fi ;;
|
|
*)
|
|
echo "usage: nomarchy-airplane [toggle|status|on|off|is-active]" >&2
|
|
exit 64 ;;
|
|
esac
|
|
''
|