chore: clean up config 6ff7b44a
Steve · 2026-08-20 18:04 26 file(s) · +17 −1192
oxwm/config.lua +17 −5
28 28
29 29
-- Spawn one of those scripts. They resolve their own siblings relative to
30 30
-- themselves, so an absolute path is all they need — PATH is not involved.
31 -
local function script(name)
32 -
    return oxwm.spawn({ scripts .. "/" .. name })
31 +
-- Extra arguments are passed straight through: script("txtcliphist.sh", "sel").
32 +
local function script(name, ...)
33 +
    local cmd = { scripts .. "/" .. name }
34 +
    for _, arg in ipairs({ ... }) do
35 +
        cmd[#cmd + 1] = arg
36 +
    end
37 +
    return oxwm.spawn(cmd)
33 38
end
34 39
35 40
-- Color palette - customize these to match your theme
283 288
-- Straight to the wifi picker, skipping the menu
284 289
oxwm.key.bind({ modkey, "Control", "Shift" }, "N", script("dm-wifi-connect"))
285 290
291 +
-- Clipboard history, kept as plain text in ~/.cache/cliphist.
292 +
-- Mod+Shift+V pushes the current primary selection (whatever is highlighted)
293 +
-- into the clipboard and appends it to the history file; Mod+V picks an
294 +
-- earlier entry back out through dmenu and recopies it.
295 +
oxwm.key.bind({ modkey }, "P", script("txtcliphist.sh", "add"))
296 +
oxwm.key.bind({ modkey, "Shift" }, "P", script("txtcliphist.sh", "sel"))
297 +
286 298
-- Keybind overlay - Shows important keybindings on screen
287 299
oxwm.key.bind({ modkey, "Shift" }, "Slash", oxwm.show_keybinds())
288 300
292 304
293 305
-- Layout management
294 306
oxwm.key.bind({ modkey }, "F", oxwm.layout.set("normie"))
295 -
oxwm.key.bind({ modkey }, "C", oxwm.layout.set("tiling"))
307 +
--oxwm.key.bind({ modkey }, "C", oxwm.layout.set("tiling"))
296 308
-- Cycle through layouts
297 309
oxwm.key.bind({ modkey }, "N", oxwm.layout.cycle())
298 310
304 316
-- Enable tiled resize mode: Mod+RMB drag adjusts mfact instead of floating
305 317
-- oxwm.tiled_resize_mode(true)
306 318
-- Increment/Decrement number of master windows
307 -
oxwm.key.bind({ modkey }, "I", oxwm.inc_num_master(1))
308 -
oxwm.key.bind({ modkey }, "P", oxwm.inc_num_master(-1))
319 +
--oxwm.key.bind({ modkey }, "I", oxwm.inc_num_master(1))
320 +
--oxwm.key.bind({ modkey }, "P", oxwm.inc_num_master(-1))
309 321
310 322
-- Gaps toggle
311 323
oxwm.key.bind({ modkey }, "A", oxwm.toggle_gaps())
picom/picom.conf (deleted) +0 −33
1 -
# backend = "glx";
2 -
# glx-copy-from-front = false;
3 -
#
4 -
# active-opacity = 1;
5 -
# inactive-opacity = 1;
6 -
# frame-opacity = 1;
7 -
# inactive-opacity-override = false;
8 -
# blur-background = true;
9 -
# blur-background-exclude = [
10 -
#   "window_type = 'dock'",
11 -
# ];
12 -
#
13 -
# blur-method = "dual_kawase";
14 -
# blur-strength = 8;
15 -
16 -
fading = true;
17 -
fade-delta = 2;
18 -
no-fading-openclose = false;
19 -
20 -
fade-exclude = [ ];
21 -
22 -
wintypes:
23 -
{
24 -
  dock = {
25 -
    shadow = false;
26 -
  };
27 -
};
28 -
29 -
# opacity-rule = [
30 -
#   "90:class_g = 'URxvt'",
31 -
#   "90:class_g = 'Alacritty' && focused",
32 -
#   "70:class_g = 'Alacritty' && !focused"
33 -
# ];
scripts/audio-menu (deleted) +0 −38
1 -
#!/bin/sh
2 -
# Requires: wireplumber (wpctl), gum
3 -
# One menu over the vol-* / audio-output scripts.
4 -
set -u
5 -
6 -
DIR=$(dirname "$0")
7 -
8 -
CHOICE=$(gum choose \
9 -
    "Volume up" \
10 -
    "Volume down" \
11 -
    "Set volume" \
12 -
    "Toggle mute" \
13 -
    "Toggle mic mute" \
14 -
    "Select output" \
15 -
    "Show status" \
16 -
    "Quit") || exit 0
17 -
18 -
case "$CHOICE" in
19 -
    "Volume up")       "$DIR/vol-up" ;;
20 -
    "Volume down")     "$DIR/vol-down" ;;
21 -
    "Set volume")
22 -
        PCT=$(gum input --placeholder "0-100" --prompt "Volume % ") || exit 0
23 -
        case "$PCT" in
24 -
            ""|*[!0-9]*) gum style --foreground 214 "Not a number: $PCT"; exit 1 ;;
25 -
        esac
26 -
        [ "$PCT" -gt 100 ] && PCT=100
27 -
        wpctl set-volume @DEFAULT_AUDIO_SINK@ "${PCT}%" && "$DIR/vol-status" -n
28 -
        ;;
29 -
    "Toggle mute")     "$DIR/vol-mute" ;;
30 -
    "Toggle mic mute") "$DIR/vol-mute" -s ;;
31 -
    "Select output")   "$DIR/audio-output" ;;
32 -
    "Show status")
33 -
        gum style --border normal --padding "1 2" --border-foreground 212 \
34 -
            "Volume: $("$DIR/vol-status")" \
35 -
            "$("$DIR/audio-output" --list)"
36 -
        ;;
37 -
    *) exit 0 ;;
38 -
esac
scripts/audio-output (deleted) +0 −83
1 -
#!/bin/sh
2 -
# Requires: wireplumber (wpctl), gum
3 -
# Pick which sink audio plays out of.
4 -
#   audio-output           interactive picker
5 -
#   audio-output --list    print sinks (* marks the current default)
6 -
#   audio-output --auto    clear the pin, let wireplumber pick by priority
7 -
#   audio-output ID|NAME   select by wpctl id, or by a substring of the name
8 -
set -u
9 -
10 -
# wpctl has no machine-readable listing, so carve the Sinks block out of `status`.
11 -
# Output: "<*|space>\t<id>\t<name>"
12 -
sinks() {
13 -
    wpctl status 2>/dev/null | awk '
14 -
        /^Audio/                                { audio = 1; next }
15 -
        /^[A-Z]/                                { audio = 0 }
16 -
        audio && /Sinks:/ && !/endpoints:/      { insec = 1; next }
17 -
        audio && /Devices:|Sources:|Streams:|endpoints:|Filters:/ { insec = 0; next }
18 -
        insec {
19 -
            line = $0
20 -
            prefix = line; sub(/[0-9].*$/, "", prefix)
21 -
            def = (prefix ~ /\*/) ? "*" : " "
22 -
            sub(/^[^0-9]*/, "", line)
23 -
            if (line !~ /^[0-9]+\./) next
24 -
            id = line;   sub(/\..*$/, "", id)
25 -
            name = line; sub(/^[0-9]+\.[[:space:]]*/, "", name)
26 -
            sub(/[[:space:]]*\[vol:.*$/, "", name)
27 -
            sub(/[[:space:]]+$/, "", name)
28 -
            print def "\t" id "\t" name
29 -
        }
30 -
    '
31 -
}
32 -
33 -
name_of() {
34 -
    printf '%s\n' "$SINKS" | awk -F'\t' -v id="$1" '$2 == id { print $3; exit }'
35 -
}
36 -
37 -
SINKS=$(sinks)
38 -
if [ -z "$SINKS" ]; then
39 -
    echo "audio-output: no sinks found (is pipewire/wireplumber running?)" >&2
40 -
    exit 1
41 -
fi
42 -
43 -
use_auto() {
44 -
    wpctl clear-default || exit 1
45 -
    gum style --foreground 212 "Output: automatic (wireplumber picks)"
46 -
    exit 0
47 -
}
48 -
49 -
use_sink() {
50 -
    wpctl set-default "$1" || exit 1
51 -
    gum style --foreground 212 "Output: $(name_of "$1")"
52 -
    exit 0
53 -
}
54 -
55 -
case "${1:-}" in
56 -
    --list|-l)
57 -
        printf '%s\n' "$SINKS" | awk -F'\t' '{ printf "%s %-6s %s\n", $1, $2, $3 }'
58 -
        exit 0
59 -
        ;;
60 -
    --auto|-a) use_auto ;;
61 -
    "") ;;
62 -
    *)
63 -
        ID=$(printf '%s\n' "$SINKS" | awk -F'\t' -v q="$1" '
64 -
            $2 == q                         { print $2; found = 1; exit }
65 -
            index(tolower($3), tolower(q))  { print $2; found = 1; exit }')
66 -
        if [ -z "$ID" ]; then
67 -
            echo "audio-output: no sink matching '$1'" >&2
68 -
            exit 1
69 -
        fi
70 -
        use_sink "$ID"
71 -
        ;;
72 -
esac
73 -
74 -
CHOICE=$(printf '%s\n' "$SINKS" \
75 -
    | awk -F'\t' '{ printf "%s %-6s %s\n", $1, $2, $3 }' \
76 -
    | { cat; echo "  auto   (let wireplumber choose)"; } \
77 -
    | gum choose --header "Select audio output") || exit 0
78 -
79 -
[ -n "$CHOICE" ] || exit 0
80 -
81 -
ID=$(printf '%s\n' "$CHOICE" | awk '{ print $2 }')
82 -
[ "$ID" = "auto" ] && use_auto
83 -
use_sink "$ID"
scripts/dm-eth0-connect (deleted) +0 −41
1 -
#!/bin/sh
2 -
# Requires: dmenu, libnotify, ifupdown-ng, openrc, doas
3 -
# dmenu counterpart of eth0-connect. The confirm prompt runs as the calling
4 -
# user; only the switch itself is handed to doas.
5 -
set -u
6 -
7 -
IFACE="${1:-eth0}"
8 -
WIFI="${2:-wlan0}"
9 -
NOTIFY_ID=7303
10 -
11 -
notify() {
12 -
    if command -v dunstify >/dev/null 2>&1; then
13 -
        dunstify -r "$NOTIFY_ID" ${2:+-u "$2"} "Network" "$1"
14 -
    else
15 -
        notify-send ${2:+-u "$2"} "Network" "$1"
16 -
    fi
17 -
}
18 -
19 -
fail() { notify "$1" critical; exit 1; }
20 -
21 -
doas -n true 2>/dev/null || fail "doas needs a password; add a nopass rule for this user."
22 -
23 -
[ "$(printf 'No\nYes\n' | dmenu -c -i -l 2 -p "Switch to $IFACE and disable $WIFI?")" = "Yes" ] || exit 0
24 -
25 -
notify "󰈀  Switching to wired..."
26 -
27 -
doas sh -c '
28 -
    ifdown "$2" 2>/dev/null
29 -
    ip link set "$2" down 2>/dev/null
30 -
    # Same reason as dm-wifi-disable: the supervisor respawns the daemon
31 -
    # after a bare kill, so stop the service instead of pkill-ing it.
32 -
    rc-service -s -q wpa_supplicant stop 2>/dev/null
33 -
    ifup "$1"
34 -
' _ "$IFACE" "$WIFI"
35 -
36 -
if ip addr show "$IFACE" | grep -q "inet "; then
37 -
    IP=$(ip -4 addr show "$IFACE" | awk '/inet /{print $2; exit}')
38 -
    notify "󰈀  $IFACE up — $IP"
39 -
else
40 -
    fail "󰲛  $IFACE failed to come up"
41 -
fi
scripts/dm-eth0-disable (deleted) +0 −30
1 -
#!/bin/sh
2 -
# Requires: dmenu, libnotify, ifupdown-ng, doas
3 -
# dmenu counterpart of eth0-disable.
4 -
set -u
5 -
6 -
IFACE="${1:-eth0}"
7 -
NOTIFY_ID=7304
8 -
9 -
notify() {
10 -
    if command -v dunstify >/dev/null 2>&1; then
11 -
        dunstify -r "$NOTIFY_ID" ${2:+-u "$2"} "Network" "$1"
12 -
    else
13 -
        notify-send ${2:+-u "$2"} "Network" "$1"
14 -
    fi
15 -
}
16 -
17 -
fail() { notify "$1" critical; exit 1; }
18 -
19 -
doas -n true 2>/dev/null || fail "doas needs a password; add a nopass rule for this user."
20 -
21 -
[ "$(printf 'No\nYes\n' | dmenu -c -i -l 2 -p "Disable $IFACE?")" = "Yes" ] || exit 0
22 -
23 -
notify "󰲛  Disabling $IFACE..."
24 -
25 -
doas sh -c '
26 -
    ifdown "$1" 2>/dev/null
27 -
    ip link set "$1" down
28 -
' _ "$IFACE"
29 -
30 -
notify "󰲛  $IFACE disabled"
scripts/dm-net-menu (deleted) +0 −70
1 -
#!/bin/sh
2 -
# Requires: dmenu, libnotify
3 -
# dmenu counterpart of net-menu. Everything here runs as the calling user;
4 -
# the individual scripts escalate with doas only for the parts that need it.
5 -
set -u
6 -
7 -
# Launched from a keybinding, PATH may not include this directory — resolve
8 -
# the sibling scripts relative to this one instead.
9 -
DIR=$(dirname "$0")
10 -
11 -
menu() { dmenu -c -i "$@"; }
12 -
13 -
# BusyBox's `ip` has no `-br`, so build the brief view by hand. Each line is
14 -
# prefixed with a Nerd Font icon for what that interface is and how it is
15 -
# doing: 󰤨/󰤫/󰤭 wireless connected/no lease/down, 󰈀/󰲛 wired up/down.
16 -
net_status() {
17 -
    for path in /sys/class/net/*; do
18 -
        iface=${path##*/}
19 -
        [ "$iface" = "lo" ] && continue
20 -
21 -
        state=$(cat "$path/operstate" 2>/dev/null || echo unknown)
22 -
        addrs=$(ip -4 addr show "$iface" 2>/dev/null |
23 -
            awk '/inet /{ printf "%s%s", (n++ ? " " : ""), $2 }')
24 -
25 -
        # Container veths and other virtual links with no address are noise;
26 -
        # a virtual interface that does carry one (tailscale0, docker0) is not.
27 -
        [ -z "$addrs" ] && [ ! -e "$path/device" ] && continue
28 -
29 -
        # Tunnels sit at operstate "unknown" for their whole life, so an
30 -
        # address is what actually says the link is carrying traffic.
31 -
        up=0
32 -
        case "$state" in
33 -
            up) up=1 ;;
34 -
            unknown) [ -n "$addrs" ] && up=1 ;;
35 -
        esac
36 -
37 -
        if [ -d "$path/wireless" ] || [ -e "$path/phy80211" ]; then
38 -
            if   [ "$up" -eq 0 ];  then icon="󰤭"
39 -
            elif [ -z "$addrs" ];  then icon="󰤫"
40 -
            else                        icon="󰤨"
41 -
            fi
42 -
        else
43 -
            if [ "$up" -eq 1 ]; then icon="󰈀"; else icon="󰲛"; fi
44 -
        fi
45 -
46 -
        printf '%s  %-12s %-8s %s\n' "$icon" "$iface" "$state" "$addrs"
47 -
    done
48 -
}
49 -
50 -
CHOICE=$(printf '%s\n' \
51 -
    "Connect to WiFi" \
52 -
    "Disable WiFi" \
53 -
    "Connect eth0" \
54 -
    "Disable eth0" \
55 -
    "Show status" \
56 -
    "Quit" | menu -l 6 -p "Network:")
57 -
58 -
case "$CHOICE" in
59 -
    "Connect to WiFi") "$DIR/dm-wifi-connect" ;;
60 -
    "Disable WiFi")    "$DIR/dm-wifi-disable" ;;
61 -
    "Connect eth0")    "$DIR/dm-eth0-connect" ;;
62 -
    "Disable eth0")    "$DIR/dm-eth0-disable" ;;
63 -
    "Show status")
64 -
        # dmenu is the only display surface we have; the selection is ignored.
65 -
        STATUS=$(net_status)
66 -
        LINES=$(printf '%s\n' "$STATUS" | grep -c .)
67 -
        printf '%s\n' "$STATUS" | menu -l "$LINES" -p "Status:" >/dev/null
68 -
        ;;
69 -
    *) exit 0 ;;
70 -
esac
scripts/dm-wifi-connect (deleted) +0 −127
1 -
#!/bin/sh
2 -
# Requires: dmenu, libnotify, iw, wpa_supplicant, udhcpc, openrc, doas
3 -
# dmenu counterpart of wifi-connect. All prompting happens as the calling
4 -
# user so dmenu keeps the X session and notifications keep the user's bus;
5 -
# only scanning, the config write and the bring-up are handed to doas.
6 -
set -u
7 -
8 -
CONF=/etc/wpa_supplicant/wpa_supplicant.conf
9 -
NOTIFY_ID=7301
10 -
11 -
menu() { dmenu -c -i "$@"; }
12 -
13 -
# Typed text with no dmenu-visible input still has to be hidden, and this
14 -
# build has no password patch — paint the prompt's foreground onto its
15 -
# background so the passphrase never renders.
16 -
menu_secret() {
17 -
    dmenu -c -p "$1" -nb '#000000' -nf '#000000' -sb '#000000' -sf '#000000' </dev/null
18 -
}
19 -
20 -
notify() {
21 -
    # dunstify can replace an earlier notification in place, so the progress
22 -
    # line becomes the result line instead of stacking up.
23 -
    if command -v dunstify >/dev/null 2>&1; then
24 -
        dunstify -r "$NOTIFY_ID" ${2:+-u "$2"} "WiFi" "$1"
25 -
    else
26 -
        notify-send ${2:+-u "$2"} "WiFi" "$1"
27 -
    fi
28 -
}
29 -
30 -
fail() { notify "$1" critical; exit 1; }
31 -
32 -
# doas asks for a passphrase on a tty, and launched from a keybinding there
33 -
# is none — fail with something readable instead of hanging invisibly.
34 -
doas -n true 2>/dev/null || fail "doas needs a password; add a nopass rule for this user."
35 -
36 -
# Ask the kernel which interfaces are wireless rather than assuming wlan0.
37 -
IFACES=$(iw dev | awk '$1 == "Interface" { print $2 }')
38 -
39 -
case $(printf '%s' "$IFACES" | grep -c .) in
40 -
    0) fail "No wireless interfaces found." ;;
41 -
    1) IFACE=$IFACES ;;
42 -
    *) IFACE=$(printf '%s\n' "$IFACES" | menu -l 5 -p "Interface:") ;;
43 -
esac
44 -
45 -
[ -n "$IFACE" ] || exit 1
46 -
47 -
doas ip link set "$IFACE" up 2>/dev/null
48 -
49 -
notify "󰤬  Scanning for networks on $IFACE..."
50 -
51 -
# `scan dump` reuses cached results if the card is busy (already associated),
52 -
# which is when a live scan tends to fail.
53 -
SCAN=$(doas iw dev "$IFACE" scan 2>/dev/null || doas iw dev "$IFACE" scan dump 2>/dev/null)
54 -
55 -
# Only real SSID lines: bare "SSID: " is a hidden network, and "SSID List"
56 -
# under extended capabilities is not a network at all.
57 -
SSIDS=$(printf '%s\n' "$SCAN" | sed -n 's/^[[:space:]]*SSID: \(..*\)$/\1/p' | sort -u)
58 -
59 -
if [ -n "$SSIDS" ]; then
60 -
    # dmenu returns whatever was typed when nothing matches, so a hidden or
61 -
    # out-of-range network can just be typed into the same prompt.
62 -
    SSID=$(printf '%s\n' "$SSIDS" | menu -l 10 -p "Network:")
63 -
else
64 -
    notify "No networks found on $IFACE — type one in."
65 -
    SSID=$(menu -p "Network:" </dev/null)
66 -
fi
67 -
68 -
[ -n "$SSID" ] || exit 1
69 -
70 -
PASS=$(menu_secret "Password (empty = open):")
71 -
72 -
# Build the new config before touching anything: a rejected passphrase must
73 -
# not blank out the config that is currently working. The PSK never lands on
74 -
# disk unprivileged — it goes to the root shell over a pipe.
75 -
if [ -z "$PASS" ]; then
76 -
    NEW=$(printf 'network={\n\tssid="%s"\n\tkey_mgmt=NONE\n}\n' "$SSID")
77 -
else
78 -
    NEW=$(wpa_passphrase "$SSID" "$PASS" 2>/dev/null | grep -v '^[[:space:]]*#psk=') ||
79 -
        fail "Rejected by wpa_passphrase — password must be 8-63 characters."
80 -
fi
81 -
82 -
[ -n "$NEW" ] || fail "Could not generate a config for $SSID."
83 -
84 -
printf '%s\n' "$NEW" | doas sh -c '
85 -
    [ -f "$1" ] && cp "$1" "$1.bak"
86 -
    cat > "$1"
87 -
    chmod 600 "$1"
88 -
' _ "$CONF" || fail "Could not write $CONF."
89 -
90 -
notify "󰤨  Connecting to $SSID..."
91 -
92 -
doas sh -c '
93 -
    # wpa_supplicant is a supervised OpenRC service on this host and will not
94 -
    # reread its config on its own, so restart it rather than signalling it.
95 -
    # This also covers the case where dm-wifi-disable stopped it outright.
96 -
    # ifup is no help here: wlan0 has no stanza in /etc/network/interfaces.
97 -
    rc-service wpa_supplicant restart >/dev/null 2>&1
98 -
99 -
    # Give the supplicant time to associate before asking for a lease — a
100 -
    # DHCP request sent before association just burns its whole timeout.
101 -
    i=0
102 -
    while [ "$i" -lt 20 ]; do
103 -
        iw dev "$1" link 2>/dev/null | grep -q "^Connected to" && break
104 -
        sleep 1
105 -
        i=$((i + 1))
106 -
    done
107 -
108 -
    # Replace any DHCP client left over from a previous connect; without this
109 -
    # they stack up, one per run, all renewing the same lease.
110 -
    PIDFILE=/var/run/udhcpc.$1.pid
111 -
    if [ -f "$PIDFILE" ]; then
112 -
        kill "$(cat "$PIDFILE")" 2>/dev/null
113 -
        rm -f "$PIDFILE"
114 -
    fi
115 -
    ip addr flush dev "$1" 2>/dev/null
116 -
117 -
    # Same invocation ifupdown-ng uses for a dhcp stanza. udhcpc daemonises
118 -
    # once it has the lease, so this returns with the address already set.
119 -
    udhcpc -b -R -p "$PIDFILE" -i "$1" -x "hostname:$(hostname)" >/dev/null 2>&1
120 -
' _ "$IFACE"
121 -
122 -
if ip addr show "$IFACE" | grep -q "inet "; then
123 -
    IP=$(ip -4 addr show "$IFACE" | awk '/inet /{print $2; exit}')
124 -
    notify "$("$(dirname "$0")/wifi-status" "$IFACE")  on $IFACE — $IP"
125 -
else
126 -
    fail "󰤮  Failed to connect to $SSID"
127 -
fi
scripts/dm-wifi-disable (deleted) +0 −51
1 -
#!/bin/sh
2 -
# Requires: dmenu, libnotify, openrc, ifupdown-ng, doas
3 -
# dmenu counterpart of wifi-disable. The confirm prompt runs as the calling
4 -
# user; only the teardown is handed to doas.
5 -
set -u
6 -
7 -
IFACE="${1:-wlan0}"
8 -
NOTIFY_ID=7302
9 -
10 -
notify() {
11 -
    if command -v dunstify >/dev/null 2>&1; then
12 -
        dunstify -r "$NOTIFY_ID" ${2:+-u "$2"} "WiFi" "$1"
13 -
    else
14 -
        notify-send ${2:+-u "$2"} "WiFi" "$1"
15 -
    fi
16 -
}
17 -
18 -
fail() { notify "$1" critical; exit 1; }
19 -
20 -
# doas asks for a passphrase on a tty, and launched from a keybinding there
21 -
# is none — fail with something readable instead of hanging invisibly.
22 -
doas -n true 2>/dev/null || fail "doas needs a password; add a nopass rule for this user."
23 -
24 -
# Default to No so a stray Enter never takes the link down.
25 -
[ "$(printf 'No\nYes\n' | dmenu -c -i -l 2 -p "Disable WiFi on $IFACE?")" = "Yes" ] || exit 0
26 -
27 -
notify "󰤭  Disabling $IFACE..."
28 -
29 -
doas sh -c '
30 -
    # wpa_supplicant runs under supervise-daemon (supervisor= in
31 -
    # /etc/init.d/wpa_supplicant), which respawns the daemon a couple of
32 -
    # seconds after any kill. Stopping the service is what takes the
33 -
    # supervisor down with it; pkill only ever kills the child.
34 -
    rc-service -s -q wpa_supplicant stop
35 -
36 -
    # wlan0 has no stanza in /etc/network/interfaces, so ifdown is a no-op on
37 -
    # this host and leaves the DHCP client behind. Try it anyway for hosts
38 -
    # where the interface *is* configured, then clean up by pidfile.
39 -
    ifdown "$1" 2>/dev/null
40 -
41 -
    PIDFILE=/var/run/udhcpc.$1.pid
42 -
    if [ -f "$PIDFILE" ]; then
43 -
        kill "$(cat "$PIDFILE")" 2>/dev/null
44 -
        rm -f "$PIDFILE"
45 -
    fi
46 -
47 -
    ip addr flush dev "$1" 2>/dev/null
48 -
    ip link set "$1" down
49 -
' _ "$IFACE"
50 -
51 -
notify "󰤭  $IFACE disabled"
scripts/eth0-connect (deleted) +0 −27
1 -
#!/bin/sh
2 -
# Requires: gum, ifupdown-ng
3 -
# Bringing interfaces up/down needs root.
4 -
set -u
5 -
6 -
IFACE="${1:-eth0}"
7 -
WIFI="${2:-wlan0}"
8 -
9 -
if [ "$(id -u)" -ne 0 ]; then
10 -
    exec doas "$0" "$@"
11 -
fi
12 -
13 -
gum confirm "Switch to $IFACE and disable $WIFI?" || exit 0
14 -
15 -
gum spin --spinner dot --title "Switching to wired..." -- sh -c '
16 -
    ifdown "$2" 2>/dev/null
17 -
    ip link set "$2" down 2>/dev/null
18 -
    pkill wpa_supplicant 2>/dev/null
19 -
    ifup "$1"
20 -
' _ "$IFACE" "$WIFI"
21 -
22 -
if ip addr show "$IFACE" | grep -q "inet "; then
23 -
    IP=$(ip -4 addr show "$IFACE" | grep inet | awk '{print $2}')
24 -
    gum style --foreground 46 --bold "󰈀  $IFACE up — $IP"
25 -
else
26 -
    gum style --foreground 196 --bold "󰲛  $IFACE failed to come up"
27 -
fi
scripts/eth0-disable (deleted) +0 −19
1 -
#!/bin/sh
2 -
# Requires: gum, ifupdown-ng
3 -
# Bringing interfaces down needs root.
4 -
set -u
5 -
6 -
IFACE="${1:-eth0}"
7 -
8 -
if [ "$(id -u)" -ne 0 ]; then
9 -
    exec doas "$0" "$@"
10 -
fi
11 -
12 -
gum confirm "Disable $IFACE?" || exit 0
13 -
14 -
gum spin --spinner dot --title "Disabling $IFACE..." -- sh -c '
15 -
    ifdown "$1" 2>/dev/null
16 -
    ip link set "$1" down
17 -
' _ "$IFACE"
18 -
19 -
gum style --foreground 214 "󰲛  $IFACE disabled"
scripts/get-forcast-url.sh (deleted) +0 −8
1 -
#!/usr/bin/env bash
2 -
3 -
LOCATION=$1
4 -
5 -
# data=$(curl -sSL https://api.weather.gov/points/$LOCATION)
6 -
# echo $data
7 -
8 -
curl -sSL https://api.weather.gov/points/$LOCATION | jq -r '.properties.forecast'
scripts/net-menu (deleted) +0 −54
1 -
#!/bin/sh
2 -
# Resolve the sibling scripts relative to this one rather than through PATH.
3 -
DIR=$(dirname "$0")
4 -
5 -
# BusyBox's `ip` has no `-br`, so build the brief view by hand. Each line is
6 -
# prefixed with a Nerd Font icon for what that interface is and how it is
7 -
# doing: 󰤨/󰤫/󰤭 wireless connected/no lease/down, 󰈀/󰲛 wired up/down.
8 -
net_status() {
9 -
    for path in /sys/class/net/*; do
10 -
        iface=${path##*/}
11 -
        [ "$iface" = "lo" ] && continue
12 -
13 -
        state=$(cat "$path/operstate" 2>/dev/null || echo unknown)
14 -
        addrs=$(ip -4 addr show "$iface" 2>/dev/null |
15 -
            awk '/inet /{ printf "%s%s", (n++ ? " " : ""), $2 }')
16 -
17 -
        # Container veths and other virtual links with no address are noise;
18 -
        # a virtual interface that does carry one (tailscale0, docker0) is not.
19 -
        [ -z "$addrs" ] && [ ! -e "$path/device" ] && continue
20 -
21 -
        # Tunnels sit at operstate "unknown" for their whole life, so an
22 -
        # address is what actually says the link is carrying traffic.
23 -
        up=0
24 -
        case "$state" in
25 -
            up) up=1 ;;
26 -
            unknown) [ -n "$addrs" ] && up=1 ;;
27 -
        esac
28 -
29 -
        if [ -d "$path/wireless" ] || [ -e "$path/phy80211" ]; then
30 -
            if   [ "$up" -eq 0 ];  then icon="󰤭"
31 -
            elif [ -z "$addrs" ];  then icon="󰤫"
32 -
            else                        icon="󰤨"
33 -
            fi
34 -
        else
35 -
            if [ "$up" -eq 1 ]; then icon="󰈀"; else icon="󰲛"; fi
36 -
        fi
37 -
38 -
        printf '%s  %-12s %-8s %s\n' "$icon" "$iface" "$state" "$addrs"
39 -
    done
40 -
}
41 -
42 -
CHOICE=$(gum choose "Connect to WiFi" "Disable WiFi" "Connect eth0" "Disable eth0" "Show status" "Quit")
43 -
44 -
case "$CHOICE" in
45 -
    "Connect to WiFi") "$DIR/wifi-connect" ;;
46 -
    "Disable WiFi")    "$DIR/wifi-disable" ;;
47 -
    "Connect eth0")    "$DIR/eth0-connect" ;;
48 -
    "Disable eth0")    "$DIR/eth0-disable" ;;
49 -
    "Show status")
50 -
        gum style --border normal --padding "1 2" --border-foreground 212 \
51 -
            "$(net_status)"
52 -
        ;;
53 -
    *) exit 0 ;;
54 -
esac
scripts/notes.sh (deleted) +0 −40
1 -
#!/usr/bin/env bash
2 -
3 -
folder="$HOME/notes"
4 -
5 -
mkdir -p "$folder"
6 -
7 -
# BusyBox setsid has no -f; background with & instead.
8 -
launch () {
9 -
  setsid "$term" -e nvim "$1" >/dev/null 2>&1 &
10 -
}
11 -
12 -
: "${TERMINAL:=st}"
13 -
term="$(command -v "$TERMINAL")" || {
14 -
  echo "notes: terminal '$TERMINAL' not found" >&2
15 -
  exit 1
16 -
}
17 -
18 -
newnote () { \
19 -
  dir="$(find "$folder" -type d | sort | dmenu -c -l 5 -i -p 'Choose directory: ')" || exit 0
20 -
  : "${dir:=$folder}"
21 -
  name="$(echo "" | dmenu -c -sb "#a3be8c" -nf "#d8dee9" -p "Enter a name: " <&-)" || exit 0
22 -
  : "${name:=$(date +%F_%H-%M-%S)}"
23 -
  launch "${dir%/}/${name}.md"
24 -
}
25 -
26 -
selected () { \
27 -
  choice=$(
28 -
    { echo "New"
29 -
      find "$folder" -type f -name '*.md' -exec stat -c '%Y %n' {} + 2>/dev/null \
30 -
        | sort -nr | cut -d' ' -f2- | sed "s|^$folder/||"
31 -
    } | dmenu -c -l 5 -i -p "Choose note or create new: "
32 -
  )
33 -
  case $choice in
34 -
    New) newnote ;;
35 -
    *.md) launch "$folder/$choice" ;;
36 -
    *) exit ;;
37 -
  esac
38 -
}
39 -
40 -
selected
scripts/stats.sh (deleted) +0 −40
1 -
#!/usr/bin/env bash
2 -
# basic stats checker script mostly adapted from statusbar, may need changes to work on your system
3 -
4 -
CPU_TEMP=$(sensors | awk '
5 -
    /^Tdie|^Tctl|^Package id|^Core 0|^CPU|^temp1/ {
6 -
        gsub(/[+°C]/, "");
7 -
        for (i=1; i<=NF; i++) {
8 -
            if ($i ~ /^[0-9]+(\.[0-9]+)?$/) {
9 -
                gsub(/\..*/, "", $i);
10 -
                print $i;
11 -
                exit;
12 -
            }
13 -
        }
14 -
    }')
15 -
16 -
MEM_USE=$(free -m | awk '/^Mem/ {print ($3)/1024}'| cut -c-4)
17 -
CPU_USE=$(iostat -c | awk 'NR>=4 && NR <=4' | awk '{print "User CPU: "$1"%\nSystem CPU: ",$3"%"}')
18 -
GPU_TEMP=$(sensors | awk '/^edge/ {print substr($2,2)}')
19 -
20 -
21 -
DISK1=$(df -h ~/ | awk 'NR==2 {print $4}')
22 -
DISK2=$(df -h / | awk 'NR==2 {print $4}')
23 -
24 -
# [[ -n $(amixer get Capture | awk '/\[on\]/') ]] && MIC="on" || MIC="off"
25 -
UPDATE=$(stat -c %y /lib/apk/db/installed | awk '{print $1}')
26 -
KERNEL=$(uname -r)
27 -
28 -
echo -e "Stats $CPU_USE
29 -
30 -
GPU temp: $GPU_TEMP
31 -
CPU temp: $CPU_TEMP°C
32 -
33 -
Memory use: ${MEM_USE}g
34 -
35 -
Home remaining: $DISK1
36 -
Root remaining: $DISK2
37 -
38 -
Last update: $UPDATE       $KERNEL" \
39 -
| bat -l cpuinfo --style=grid
40 -
scripts/sys.sh (deleted) +0 −9
1 -
#!/usr/bin/env bash
2 -
3 -
case "$(printf "kill\nzzz\nreboot\nshutdown" | dmenu -i -c -l 4)" in
4 -
	kill) ps -u "$USER" -o pid,comm,%cpu,%mem | dmenu -i -c -l 10 -p Kill: | awk '{print $1}' | xargs -r kill ;;
5 -
	zzz) slock systemctl suspend -i ;;
6 -
	reboot) systemctl reboot -i ;;
7 -
	shutdown) shutdown now ;;
8 -
	*) exit 1 ;;
9 -
esac
scripts/txtcliphist.sh (deleted) +0 −30
1 -
#!/usr/bin/env bash
2 -
3 -
histfile="$HOME/.cache/cliphist"
4 -
placeholder="<NEWLINE>"
5 -
6 -
highlight() {
7 -
  clip=$(xclip -o -selection primary | xclip -i -f -selection clipboard 2>/dev/null) ;}
8 -
9 -
output() {
10 -
  clip=$(xclip -i -f -selection clipboard 2>/dev/null) ;}
11 -
12 -
write() {
13 -
  [ -f "$histfile" ] || notify-send "Creating $histfile"; touch $histfile
14 -
  [ -z "$clip" ] && exit 0
15 -
  multiline=$(echo "$clip" | sed ':a;N;$!ba;s/\n/'"$placeholder"'/g')
16 -
  grep -Fxq "$multiline" "$histfile" || echo "$multiline" >> "$histfile"
17 -
  notification=$(echo \"$multiline\") ;}
18 -
19 -
sel() {
20 -
  selection=$(tac "$histfile" | dmenu -b -l 5 -i -p "Clipboard history:")
21 -
  [ -n "$selection" ] && echo "$selection" | sed "s/$placeholder/\n/g" | xclip -i -selection clipboard && notification="Copied to clipboard!" ;}
22 -
23 -
case "$1" in
24 -
  add) highlight && write ;;
25 -
  out) output && write ;;
26 -
  sel) sel ;;
27 -
  *) printf "$0 | File: $histfile\n\nadd - copies primary selection to clipboard, and adds to history file\nout - pipe commands to copy output to clipboard, and add to history file\nsel - select from history file with dmenu and recopy!\n" ; exit 0 ;;
28 -
esac
29 -
30 -
[ -z "$notification" ] || notify-send "$notification"
scripts/vol-down (deleted) +0 −9
1 -
#!/bin/sh
2 -
# Requires: wireplumber (wpctl)
3 -
# Lower the default sink. Usage: vol-down [STEP_PERCENT]   (default 5)
4 -
set -u
5 -
6 -
STEP="${1:-5}"
7 -
8 -
wpctl set-volume @DEFAULT_AUDIO_SINK@ "${STEP}%-" || exit 1
9 -
exec "$(dirname "$0")/vol-status" -n
scripts/vol-mute (deleted) +0 −27
1 -
#!/bin/sh
2 -
# Requires: wireplumber (wpctl)
3 -
# Mute the default sink. Usage: vol-mute [toggle|on|off]   (default toggle)
4 -
# Pass -s / --source to act on the default microphone instead.
5 -
set -u
6 -
7 -
TARGET="@DEFAULT_AUDIO_SINK@"
8 -
case "${1:-}" in
9 -
    -s|--source) TARGET="@DEFAULT_AUDIO_SOURCE@"; shift ;;
10 -
esac
11 -
12 -
case "${1:-toggle}" in
13 -
    toggle|"") ARG=toggle ;;
14 -
    on|1|mute)     ARG=1 ;;
15 -
    off|0|unmute)  ARG=0 ;;
16 -
    *) echo "usage: vol-mute [-s] [toggle|on|off]" >&2; exit 2 ;;
17 -
esac
18 -
19 -
wpctl set-mute "$TARGET" "$ARG" || exit 1
20 -
21 -
# vol-status carries the icon logic for both devices, so hand the reporting
22 -
# (and the notification) off to it either way.
23 -
if [ "$TARGET" = "@DEFAULT_AUDIO_SOURCE@" ]; then
24 -
    exec "$(dirname "$0")/vol-status" -s -n
25 -
fi
26 -
27 -
exec "$(dirname "$0")/vol-status" -n
scripts/vol-status (deleted) +0 −75
1 -
#!/bin/sh
2 -
# Requires: wireplumber (wpctl), optionally libnotify
3 -
# Prints the default sink's volume with a Nerd Font icon for its level.
4 -
# Shared by vol-up / vol-down / vol-mute and the status bar.
5 -
# Usage: vol-status [-n] [-p] [-s]
6 -
#   -n  also fire a desktop notification
7 -
#   -p  plain output, no Nerd Font icon (for terminals without one)
8 -
#   -s  report the default source (microphone) instead of the sink
9 -
set -u
10 -
11 -
NOTIFY=0
12 -
PLAIN=0
13 -
TARGET="@DEFAULT_AUDIO_SINK@"
14 -
LABEL="Volume"
15 -
16 -
while [ $# -gt 0 ]; do
17 -
    case "$1" in
18 -
        -n|--notify) NOTIFY=1 ;;
19 -
        -p|--plain)  PLAIN=1 ;;
20 -
        -s|--source) TARGET="@DEFAULT_AUDIO_SOURCE@"; LABEL="Microphone" ;;
21 -
        *) echo "usage: vol-status [-n] [-p] [-s]" >&2; exit 2 ;;
22 -
    esac
23 -
    shift
24 -
done
25 -
26 -
RAW=$(wpctl get-volume "$TARGET" 2>/dev/null)
27 -
if [ -z "$RAW" ]; then
28 -
    echo "vol-status: no default device (is pipewire/wireplumber running?)" >&2
29 -
    exit 1
30 -
fi
31 -
32 -
# "Volume: 0.45" or "Volume: 0.45 [MUTED]"
33 -
PCT=$(printf '%s\n' "$RAW" | awk '{ printf "%d", $2 * 100 + 0.5 }')
34 -
case "$RAW" in
35 -
    *MUTED*) MUTED=1 ;;
36 -
    *)       MUTED=0 ;;
37 -
esac
38 -
39 -
# Two icon sets: speaker glyphs for the sink, mic glyphs for the source.
40 -
# ICON is the Nerd Font glyph in the bar; FDO is the freedesktop icon name
41 -
# the notification daemon looks up in the icon theme.
42 -
if [ "$TARGET" = "@DEFAULT_AUDIO_SOURCE@" ]; then
43 -
    if [ "$MUTED" -eq 1 ]; then
44 -
        ICON="󰍭"; FDO="microphone-sensitivity-muted"; TEXT="muted"
45 -
    else
46 -
        ICON="󰍬"; FDO="microphone-sensitivity-high";  TEXT="$PCT%"
47 -
    fi
48 -
elif [ "$MUTED" -eq 1 ]; then
49 -
    ICON="󰝟"; FDO="audio-volume-muted"; TEXT="muted ($PCT%)"
50 -
else
51 -
    TEXT="$PCT%"
52 -
    if   [ "$PCT" -ge 66 ]; then ICON="󰕾"; FDO="audio-volume-high"
53 -
    elif [ "$PCT" -ge 33 ]; then ICON="󰖀"; FDO="audio-volume-medium"
54 -
    elif [ "$PCT" -gt 0 ];  then ICON="󰕿"; FDO="audio-volume-low"
55 -
    else                         ICON="󰝟"; FDO="audio-volume-muted"
56 -
    fi
57 -
fi
58 -
59 -
if [ "$PLAIN" -eq 1 ]; then
60 -
    echo "$TEXT"
61 -
else
62 -
    echo "$ICON  $TEXT"
63 -
fi
64 -
65 -
# Only bother with notifications when there is a session to show them in.
66 -
[ "$NOTIFY" -eq 1 ] || exit 0
67 -
[ -n "${WAYLAND_DISPLAY:-}${DISPLAY:-}" ] || exit 0
68 -
command -v notify-send >/dev/null 2>&1 || exit 0
69 -
70 -
notify-send \
71 -
    -a volume \
72 -
    -i "$FDO" \
73 -
    -h string:x-canonical-private-synchronous:volume \
74 -
    -h "int:value:$PCT" \
75 -
    "$ICON  $LABEL" "$TEXT"
scripts/vol-up (deleted) +0 −11
1 -
#!/bin/sh
2 -
# Requires: wireplumber (wpctl)
3 -
# Raise the default sink. Usage: vol-up [STEP_PERCENT]   (default 5)
4 -
# VOL_MAX caps the ceiling (1.0 = 100%); raise it if you want overamplification.
5 -
set -u
6 -
7 -
STEP="${1:-5}"
8 -
MAX="${VOL_MAX:-1.0}"
9 -
10 -
wpctl set-volume -l "$MAX" @DEFAULT_AUDIO_SINK@ "${STEP}%+" || exit 1
11 -
exec "$(dirname "$0")/vol-status" -n
scripts/weather.sh (deleted) +0 −98
1 -
#!/usr/bin/env bash
2 -
3 -
set -euo pipefail
4 -
5 -
gridpoint=${WEATHER_GRIDPOINT:-MRX/23,5}
6 -
base_url="https://api.weather.gov/gridpoints/${gridpoint}/forecast"
7 -
8 -
cloudy=󰖐
9 -
fog=󰖑
10 -
hail=󰖒
11 -
hazy=󰼰
12 -
storm=󰖓
13 -
clear_night=󰖔
14 -
rain=󰖖
15 -
snow=󰖘
16 -
clear_day=󰖙
17 -
18 -
usage() {
19 -
	cat <<'USAGE'
20 -
usage: weather.sh [-h|--hourly] [-j|--json]
21 -
22 -
  -h, --hourly   use the hourly forecast instead of the daily one
23 -
  -j, --json     print { "text": ..., "tooltip": ... } (waybar friendly)
24 -
25 -
env:
26 -
  WEATHER_GRIDPOINT   NWS gridpoint, default MRX/23,5
27 -
USAGE
28 -
}
29 -
30 -
hourly=false
31 -
json=false
32 -
while (($#)); do
33 -
	case $1 in
34 -
	-h | --hourly) hourly=true ;;
35 -
	-j | --json) json=true ;;
36 -
	--help) usage && exit 0 ;;
37 -
	*) usage >&2 && exit 1 ;;
38 -
	esac
39 -
	shift
40 -
done
41 -
42 -
$hourly && weather_url="$base_url/hourly" || weather_url="$base_url"
43 -
44 -
data=$(curl -sSL --fail --max-time 10 -H 'Accept: application/geo+json' "$weather_url" |
45 -
	jq -r '.properties.periods[0] | [.icon, .temperature, .temperatureUnit, .shortForecast, .isDaytime] | @tsv') || {
46 -
	echo "weather: request failed" >&2
47 -
	exit 1
48 -
}
49 -
50 -
IFS=$'\t' read -r icon_url temp unit short is_day <<<"$data"
51 -
52 -
# https://api.weather.gov/icons/land/night/tsra_hi,20/bkn?size=small
53 -
#                             ^period      ^condition
54 -
icon_path=${icon_url%%\?*}      # drop the query string
55 -
icon_path=${icon_path#*/icons/} # land/night/tsra_hi,20/bkn
56 -
icon_path=${icon_path#*/}       # night/tsra_hi,20/bkn
57 -
period=${icon_path%%/*}         # day|night
58 -
condition=${icon_path#*/}
59 -
condition=${condition%%/*}      # first of the (up to two) conditions
60 -
condition=${condition%%,*}      # drop the ",20" precip chance
61 -
62 -
night=false
63 -
[[ $period == night || $is_day == false ]] && night=true
64 -
65 -
# NWS icon vocabulary -> glyph.
66 -
case $condition in
67 -
skc | few | hot | cold) $night && glyph=$clear_night || glyph=$clear_day ;;
68 -
sct | bkn | ovc | wind_skc | wind_few | wind_sct | wind_bkn | wind_ovc) glyph=$cloudy ;;
69 -
tsra | tsra_sct | tsra_hi | tornado | hurricane | tropical_storm) glyph=$storm ;;
70 -
rain | rain_showers | rain_showers_hi) glyph=$rain ;;
71 -
snow | blizzard | rain_snow | snow_sleet) glyph=$snow ;;
72 -
sleet | fzra | rain_fzra | snow_fzra | rain_sleet) glyph=$hail ;;
73 -
fog) glyph=$fog ;;
74 -
haze | smoke | dust) glyph=$hazy ;;
75 -
*) glyph="" ;;
76 -
esac
77 -
78 -
# Fall back to a keyword match on the forecast text, most specific first.
79 -
if [[ -z $glyph ]]; then
80 -
	case ${short,,} in
81 -
	*thunder* | *tstm* | *tornado* | *hurricane* | *tropical*) glyph=$storm ;;
82 -
	*hail* | *sleet* | *freezing* | *ice*) glyph=$hail ;;
83 -
	*snow* | *flurr* | *blizzard* | *wintry*) glyph=$snow ;;
84 -
	*rain* | *shower* | *drizzle*) glyph=$rain ;;
85 -
	*fog* | *mist*) glyph=$fog ;;
86 -
	*haze* | *hazy* | *smoke* | *dust*) glyph=$hazy ;;
87 -
	*cloud* | *overcast*) glyph=$cloudy ;;
88 -
	*clear* | *sunny* | *fair* | *hot*) $night && glyph=$clear_night || glyph=$clear_day ;;
89 -
	*) $night && glyph=$clear_night || glyph=$clear_day ;;
90 -
	esac
91 -
fi
92 -
93 -
if $json; then
94 -
	jq -cn --arg text "$glyph $temp°$unit" --arg tooltip "$short" \
95 -
		'{text: $text, tooltip: $tooltip}'
96 -
else
97 -
	printf '%s %s°%s\n' "$glyph" "$temp" "$unit"
98 -
fi
scripts/wifi-connect (deleted) +0 −116
1 -
#!/bin/sh
2 -
# Requires: gum, iw, wpa_supplicant, udhcpc, openrc
3 -
# Scanning and writing wpa_supplicant.conf both need root.
4 -
set -u
5 -
6 -
CONF=/etc/wpa_supplicant/wpa_supplicant.conf
7 -
8 -
if [ "$(id -u)" -ne 0 ]; then
9 -
    exec doas "$0" "$@"
10 -
fi
11 -
12 -
# Ask the kernel which interfaces are wireless rather than assuming wlan0.
13 -
IFACES=$(iw dev | awk '$1 == "Interface" { print $2 }')
14 -
15 -
case $(printf '%s' "$IFACES" | grep -c .) in
16 -
    0) gum style --foreground 196 "No wireless interfaces found."; exit 1 ;;
17 -
    1) IFACE=$IFACES ;;
18 -
    *) IFACE=$(printf '%s\n' "$IFACES" | gum choose --header "Interface:") ;;
19 -
esac
20 -
21 -
if [ -z "$IFACE" ]; then
22 -
    exit 1
23 -
fi
24 -
25 -
ip link set "$IFACE" up 2>/dev/null
26 -
27 -
SCAN=$(mktemp)
28 -
trap 'rm -f "$SCAN"' EXIT INT TERM
29 -
30 -
# The scan has to run inside the spinner, and into a file — gum spin owns
31 -
# stdout while it renders. `scan dump` reuses cached results if the card is
32 -
# busy (already associated), which is when a live scan tends to fail.
33 -
gum spin --spinner dot --title "Scanning for networks..." -- sh -c '
34 -
    iw dev "$1" scan > "$2" 2>/dev/null || iw dev "$1" scan dump > "$2" 2>/dev/null
35 -
' _ "$IFACE" "$SCAN"
36 -
37 -
# Only real SSID lines: bare "SSID: " is a hidden network, and "SSID List"
38 -
# under extended capabilities is not a network at all.
39 -
SSIDS=$(sed -n 's/^[[:space:]]*SSID: \(..*\)$/\1/p' "$SCAN" | sort -u)
40 -
41 -
SSID=""
42 -
if [ -n "$SSIDS" ]; then
43 -
    # Never pipe an empty list into gum filter: with no input it falls back to
44 -
    # listing the current directory, which looks like a network list but isn't.
45 -
    SSID=$(printf '%s\n' "$SSIDS" | gum filter --placeholder "Select a network...")
46 -
else
47 -
    gum style --foreground 214 "No networks found on $IFACE — enter one manually."
48 -
fi
49 -
50 -
if [ -z "$SSID" ]; then
51 -
    SSID=$(gum input --placeholder "SSID" --prompt "Network name: ")
52 -
fi
53 -
54 -
if [ -z "$SSID" ]; then
55 -
    gum style --foreground 196 "No SSID entered, aborting."
56 -
    exit 1
57 -
fi
58 -
59 -
PASS=$(gum input --password --placeholder "leave empty for an open network" --prompt "Password: ")
60 -
61 -
# Build the new config off to the side: a rejected passphrase must not blank
62 -
# out the config that is currently working.
63 -
NEW=$(mktemp)
64 -
trap 'rm -f "$SCAN" "$NEW"' EXIT INT TERM
65 -
66 -
if [ -z "$PASS" ]; then
67 -
    printf 'network={\n\tssid="%s"\n\tkey_mgmt=NONE\n}\n' "$SSID" > "$NEW"
68 -
elif ! wpa_passphrase "$SSID" "$PASS" 2>/dev/null | grep -v '^[[:space:]]*#psk=' > "$NEW"; then
69 -
    gum style --foreground 196 "Rejected by wpa_passphrase — password must be 8-63 characters."
70 -
    exit 1
71 -
fi
72 -
73 -
if [ ! -s "$NEW" ]; then
74 -
    gum style --foreground 196 "Could not generate a config for $SSID, aborting."
75 -
    exit 1
76 -
fi
77 -
78 -
[ -f "$CONF" ] && cp "$CONF" "$CONF.bak"
79 -
cat "$NEW" > "$CONF"
80 -
chmod 600 "$CONF"
81 -
82 -
gum spin --spinner dot --title "Connecting to $SSID..." -- sh -c '
83 -
    # wpa_supplicant is a supervised OpenRC service on this host and will not
84 -
    # reread its config on its own, so restart it rather than signalling it.
85 -
    # This also covers the case where wifi-disable stopped it outright.
86 -
    # ifup is no help here: wlan0 has no stanza in /etc/network/interfaces.
87 -
    rc-service wpa_supplicant restart >/dev/null 2>&1
88 -
89 -
    # Give the supplicant time to associate before asking for a lease — a
90 -
    # DHCP request sent before association just burns its whole timeout.
91 -
    i=0
92 -
    while [ "$i" -lt 20 ]; do
93 -
        iw dev "$1" link 2>/dev/null | grep -q "^Connected to" && break
94 -
        sleep 1
95 -
        i=$((i + 1))
96 -
    done
97 -
98 -
    # Replace any DHCP client left over from a previous connect; without this
99 -
    # they stack up, one per run, all renewing the same lease.
100 -
    PIDFILE=/var/run/udhcpc.$1.pid
101 -
    if [ -f "$PIDFILE" ]; then
102 -
        kill "$(cat "$PIDFILE")" 2>/dev/null
103 -
        rm -f "$PIDFILE"
104 -
    fi
105 -
    ip addr flush dev "$1" 2>/dev/null
106 -
107 -
    # Same invocation ifupdown-ng uses for a dhcp stanza. udhcpc daemonises
108 -
    # once it has the lease, so this returns with the address already set.
109 -
    udhcpc -b -R -p "$PIDFILE" -i "$1" -x "hostname:$(hostname)" >/dev/null 2>&1
110 -
' _ "$IFACE"
111 -
112 -
if ip addr show "$IFACE" | grep -q "inet "; then
113 -
    gum style --foreground 46 --bold "$("$(dirname "$0")/wifi-status" "$IFACE")  connected on $IFACE"
114 -
else
115 -
    gum style --foreground 196 --bold "󰤮  Failed to connect to $SSID"
116 -
fi
scripts/wifi-disable (deleted) +0 −36
1 -
#!/bin/sh
2 -
# Requires: gum, openrc, ifupdown-ng
3 -
# Stopping the wpa_supplicant service and downing the link both need root.
4 -
set -u
5 -
6 -
IFACE="${1:-wlan0}"
7 -
8 -
if [ "$(id -u)" -ne 0 ]; then
9 -
    exec doas "$0" "$@"
10 -
fi
11 -
12 -
gum confirm "Disable WiFi on $IFACE?" || exit 0
13 -
14 -
gum spin --spinner dot --title "Disabling $IFACE..." -- sh -c '
15 -
    # wpa_supplicant runs under supervise-daemon (supervisor= in
16 -
    # /etc/init.d/wpa_supplicant), which respawns the daemon a couple of
17 -
    # seconds after any kill. Stopping the service is what takes the
18 -
    # supervisor down with it; pkill only ever kills the child.
19 -
    rc-service -s -q wpa_supplicant stop
20 -
21 -
    # wlan0 has no stanza in /etc/network/interfaces, so ifdown is a no-op on
22 -
    # this host and leaves the DHCP client behind. Try it anyway for hosts
23 -
    # where the interface *is* configured, then clean up by pidfile.
24 -
    ifdown "$1" 2>/dev/null
25 -
26 -
    PIDFILE=/var/run/udhcpc.$1.pid
27 -
    if [ -f "$PIDFILE" ]; then
28 -
        kill "$(cat "$PIDFILE")" 2>/dev/null
29 -
        rm -f "$PIDFILE"
30 -
    fi
31 -
32 -
    ip addr flush dev "$1" 2>/dev/null
33 -
    ip link set "$1" down
34 -
' _ "$IFACE"
35 -
36 -
gum style --foreground 214 "󰤭  $IFACE disabled"
scripts/wifi-status (deleted) +0 −113
1 -
#!/bin/sh
2 -
# Requires: iw
3 -
# Prints the link state with a Nerd Font icon, for the status bar and menus.
4 -
# Wireless is the subject: the icon tracks signal strength while connected,
5 -
# so it moves as the link does. When wifi is not carrying traffic it falls
6 -
# back to reporting a live wired link rather than saying "off" while the
7 -
# machine is plainly online.
8 -
# Usage: wifi-status [-p|--plain] [-w|--wifi-only] [IFACE]
9 -
#   -p  plain output, no Nerd Font icon (for terminals without one)
10 -
#   -w  never fall back to wired, even with no wifi
11 -
#   IFACE  wireless interface to report (default: the first one the kernel
12 -
#          lists); naming one implies -w, since the question was about it
13 -
set -u
14 -
15 -
PLAIN=0
16 -
WIFI_ONLY=0
17 -
18 -
while [ $# -gt 0 ]; do
19 -
    case "$1" in
20 -
        -p|--plain)     PLAIN=1 ;;
21 -
        -w|--wifi-only) WIFI_ONLY=1 ;;
22 -
        -*) echo "usage: wifi-status [-p] [-w] [IFACE]" >&2; exit 2 ;;
23 -
        *) break ;;
24 -
    esac
25 -
    shift
26 -
done
27 -
28 -
# Every exit path prints through this, so -p only has to be handled once.
29 -
say() {
30 -
    if [ "$PLAIN" -eq 1 ]; then
31 -
        echo "$2"
32 -
    else
33 -
        echo "$1  $2"
34 -
    fi
35 -
}
36 -
37 -
# A wired interface counts only if it is a real NIC (a `device` symlink rules
38 -
# out docker0, tailscale0 and the veths), is up, and holds a v4 address —
39 -
# that last part is what says it can actually carry traffic.
40 -
wired_iface() {
41 -
    for path in /sys/class/net/*; do
42 -
        iface=${path##*/}
43 -
        [ "$iface" = "lo" ] && continue
44 -
        [ -d "$path/wireless" ] || [ -e "$path/phy80211" ] && continue
45 -
        [ -e "$path/device" ] || continue
46 -
        [ "$(cat "$path/operstate" 2>/dev/null)" = "up" ] || continue
47 -
        ip addr show "$iface" 2>/dev/null | grep -q "inet " || continue
48 -
        echo "$iface"
49 -
        return 0
50 -
    done
51 -
    return 1
52 -
}
53 -
54 -
# 󰈀 wired link carrying traffic. Only consulted once wifi has come up empty,
55 -
# so a working wifi connection always wins the block.
56 -
fallback() {
57 -
    [ "$WIFI_ONLY" -eq 1 ] && return 1
58 -
    WIRED=$(wired_iface) || return 1
59 -
    say "󰈀" "$WIRED"
60 -
    exit 0
61 -
}
62 -
63 -
if [ -n "${1:-}" ]; then
64 -
    # An explicitly named interface is the whole question — no wired fallback,
65 -
    # so callers like wifi-connect report on the link they just brought up.
66 -
    IFACE="$1"
67 -
    WIFI_ONLY=1
68 -
else
69 -
    # Ask the kernel which interfaces are wireless rather than assuming wlan0.
70 -
    IFACE=$(iw dev 2>/dev/null | awk '$1 == "Interface" { print $2; exit }')
71 -
fi
72 -
73 -
# 󰤮 no wireless hardware at all, 󰤭 card present but down/unassociated,
74 -
# 󰤫 associated without a lease, 󰤟󰤢󰤥󰤨 connected at rising signal strength.
75 -
if [ -z "$IFACE" ]; then
76 -
    fallback
77 -
    say "󰤮" "none"
78 -
    exit 0
79 -
fi
80 -
81 -
# `iw link` prints "Not connected." while the card is up but unassociated, and
82 -
# fails outright once wifi-disable has downed the link.
83 -
LINK=$(iw dev "$IFACE" link 2>/dev/null) || { fallback; say "󰤭" "off"; exit 0; }
84 -
85 -
case "$LINK" in
86 -
    "Connected to"*) ;;
87 -
    *) fallback; say "󰤭" "off"; exit 0 ;;
88 -
esac
89 -
90 -
SSID=$(printf '%s\n' "$LINK" | sed -n 's/^[[:space:]]*SSID: \(..*\)$/\1/p')
91 -
[ -n "$SSID" ] || SSID="unknown"
92 -
93 -
# Associated but without a lease carries no traffic, so wired wins here too;
94 -
# without one, name the network but say the lease is missing rather than
95 -
# showing an SSID that looks connected.
96 -
if ! ip addr show "$IFACE" 2>/dev/null | grep -q "inet "; then
97 -
    fallback
98 -
    say "󰤫" "$SSID (no ip)"
99 -
    exit 0
100 -
fi
101 -
102 -
# "signal: -47 dBm" — the thresholds are the usual rule of thumb: better than
103 -
# -50 is excellent, -70 is the edge of usable.
104 -
DBM=$(printf '%s\n' "$LINK" | awk '/signal:/ { printf "%d", $2; exit }')
105 -
106 -
if   [ -z "$DBM" ];      then ICON="󰤨"   # no signal line (some drivers) — assume good
107 -
elif [ "$DBM" -ge -50 ]; then ICON="󰤨"
108 -
elif [ "$DBM" -ge -60 ]; then ICON="󰤥"
109 -
elif [ "$DBM" -ge -70 ]; then ICON="󰤢"
110 -
else                          ICON="󰤟"
111 -
fi
112 -
113 -
say "$ICON" "$SSID"
zsh/dot-zshrc +0 −2
39 39
export CLICOLOR=1
40 40
41 41
# Go
42 -
export GOROOT="/usr/local/go"
43 42
export GOPATH="$HOME/.local/share/go"
44 43
export GOMODCACHE="$HOME/.local/share/go-mod-cache"
45 -
export GOPRIVATE="*gitlab*"
46 44
47 45
# bat
48 46
export BAT_THEME="ansi"