From da0df79afc05dd1b1cd49072084333f67f3c98b9 Mon Sep 17 00:00:00 2001 From: Jonas H Date: Tue, 4 Aug 2026 11:36:51 +0200 Subject: [PATCH] eww QoL update and vpn toggle --- eww/.config/eww/eww.scss | 280 ++++++++++++---------- eww/.config/eww/eww.yuck | 178 +++++++++++--- eww/.config/eww/scripts/bar-shot | 59 +++++ eww/.config/eww/scripts/net-info | 120 ++++++++++ eww/.config/eww/scripts/vpn-toggle | 20 ++ eww/.config/eww/scripts/wifi | 13 - eww/.config/eww/scripts/wifi-monitor | 13 - eww/.config/eww/scripts/wifi-name-monitor | 13 - 8 files changed, 499 insertions(+), 197 deletions(-) create mode 100755 eww/.config/eww/scripts/bar-shot create mode 100755 eww/.config/eww/scripts/net-info create mode 100755 eww/.config/eww/scripts/vpn-toggle delete mode 100644 eww/.config/eww/scripts/wifi delete mode 100644 eww/.config/eww/scripts/wifi-monitor delete mode 100644 eww/.config/eww/scripts/wifi-name-monitor diff --git a/eww/.config/eww/eww.scss b/eww/.config/eww/eww.scss index eac4d46..b642d16 100644 --- a/eww/.config/eww/eww.scss +++ b/eww/.config/eww/eww.scss @@ -1,201 +1,219 @@ - -*{ +* { all: unset; } +// --------------------------------------------------------------------------- +// Tokens +// --------------------------------------------------------------------------- + +$font-family: "JetBrainsMono Nerd Font", monospace; + +$bg-bar: #000000; +$bg-popup: #0d0d0d; +$border: #2a2a2a; +$track: #3a3a3a; +$accent: #E0AF68; +$urgent: #F7768E; +$fg: #ffffff; +$fg-dim: #9a9a9a; + +// Legacy aliases +$background: $bg-bar; +$foreground: $fg; + +// Spacing. Widget-to-widget gaps live in eww.yuck (`gap` / `gap-tight`). +// Only edge padding and slider sizes are set here. +$bar-edge: 12px; + +// Nerd Font glyphs draw wider than their advance width, so a label sized to +// the advance clips the ink. Every bar icon gets this much breathing room on +// both sides. It is subtracted from `gap` / `gap-tight` in eww.yuck, so the +// gaps you see stay 14px / 6px. +$icon-pad: 3px; +$slider-width: 44px; +$slider-height: 6px; + window { - font-family: "JetBrainsMono Nerd Font", monospace; - background-color: #000000; - color: #FFFFFF; + font-family: $font-family; + background-color: $bg-bar; + color: $fg; } -/* Global normalization - no widget should add spacing */ +// No widget adds its own spacing. Gaps come from box :spacing only. eventbox, box, label, scale { padding: 0; margin: 0; } -$font-family: "JetBrainsMono Nerd Font", monospace; +// --------------------------------------------------------------------------- +// Bar +// --------------------------------------------------------------------------- -// Global colors (sync with ~/.config/colors) -$bg-bar: #000000; -$track: #3a3a3a; -$accent: #E0AF68; -$urgent: #F7768E; -$fg: #ffffff; +.bar { + background-color: $bg-bar; +} -// Legacy -$background: $bg-bar; -$foreground: $fg; +// The last icon already carries $icon-pad, so subtract it to keep the visible +// edge gap equal to $bar-edge. +.system { + margin-right: $bar-edge - $icon-pad; +} + +.workspaces-widget { + margin-left: $bar-edge - 6px; +} .workspace-entry { - /* Styles for all workspace entries */ - padding: 0 0.5em; - font-family: $font-family; - color : $foreground; + padding: 0 6px; + color: $fg; } .workspace-entry.current { - /* Styles for the current workspace */ color: $accent; font-weight: bold; } .workspace-entry.urgent { - /* Styles for urgent workspaces */ color: $urgent; font-weight: bold; } -.time-label { - font-family: $font-family; - color : $foreground; - font-weight: bold; - margin-right: 5px; -} - -.notification-eventbox { - padding: 0; - min-width: 0; - min-height: 0; -} +// --------------------------------------------------------------------------- +// Bar items +// --------------------------------------------------------------------------- +// Every bar glyph and value shares one look. +.signal-icon, +.net-icon, +.brightness-icon, +.volume-icon, +.battery-icon, +.battery-percent, +.time-label, .notification-icon { - font-family: $font-family; - color : $foreground; - font-weight: bold; - margin-right: 5px; -} - -.battery-percent { - font-family: $font-family; - color : $foreground; - font-weight: bold; - margin-right: 5px; -} - -.battery-icon { - font-family: $font-family; - color : $foreground; + color: $fg; font-weight: bold; + padding: 0 $icon-pad; } -.bar { - background-color: $background; -} +.net-vpn-on { color: $accent; } +.net-offline { color: $urgent; } -.wifi-eventbox { - padding: 0; +.signal-eventbox, +.net-eventbox, +.notification-eventbox { min-width: 0; min-height: 0; } -.wifi-icon{ - color : $foreground; - background-color: $background; - font-weight: bold; -} - -.brightness-icon { - color: $foreground; - font-weight: bold; - margin-right: 3px; -} - -.volume-icon { - color: $foreground; - font-weight: bold; - margin-right: 3px; -} - -.volume-bar { - min-width: 10px; - min-height: 6px; - padding: 0; - margin: 0; - margin-right: 10px; - border: none; - outline: none; -} - -.volume-bar trough { - background-color: $track; - border-radius: 3px; - min-height: 6px; - min-width: 10px; - padding: 0; - margin: 0; - border: none; - outline: none; -} - -.volume-bar highlight { - background-color: $fg; - border-radius: 3px; - padding: 0; - margin: 0; -} - -.volume-bar slider { - all: unset; - padding: 0; - margin: 0; - min-width: 0; - min-height: 0; -} +// --------------------------------------------------------------------------- +// Sliders +// --------------------------------------------------------------------------- +// Sliders have no ink overflow, so they carry $icon-pad as a margin instead. +// This keeps the icon-to-slider gap equal to every other tight gap. +.volume-bar, .brightness-bar { - min-width: 10px; - min-height: 6px; - padding: 0; - margin: 0; - margin-right: 10px; + min-width: $slider-width; + min-height: $slider-height; + margin: 0 $icon-pad; border: none; outline: none; } +.volume-bar trough, .brightness-bar trough { background-color: $track; border-radius: 3px; - min-height: 6px; - min-width: 10px; - padding: 0; - margin: 0; + min-width: $slider-width; + min-height: $slider-height; border: none; outline: none; } +.volume-bar highlight, .brightness-bar highlight { background-color: $fg; border-radius: 3px; - padding: 0; - margin: 0; } +.volume-bar slider, .brightness-bar slider { all: unset; - padding: 0; - margin: 0; min-width: 0; min-height: 0; } -.signal-eventbox { - padding: 0; - min-width: 0; - min-height: 0; +// --------------------------------------------------------------------------- +// Network popup +// --------------------------------------------------------------------------- + +.net-popup { + background-color: $bg-popup; + border: 1px solid $border; + border-radius: 6px; + padding: 12px 14px; } -.signal-icon { - color: $foreground; +.net-popup-header { + color: $fg-dim; + font-size: 11px; font-weight: bold; - margin-right: 5px; } +.net-row-icon { + color: $fg; + font-size: 18px; +} + +.net-row-icon.net-vpn-off { + color: $track; +} + +.net-row-title { + color: $fg; + font-weight: bold; +} + +.net-row-subtitle { + color: $fg-dim; + font-size: 11px; +} + +.net-separator { + background-color: $border; + min-height: 1px; +} + +.net-vpn-eventbox { + min-height: 0; +} + +.net-pill { + font-size: 11px; + font-weight: bold; + padding: 2px 8px; + border-radius: 10px; +} + +.net-pill-on { + background-color: $accent; + color: $bg-bar; +} + +.net-pill-off { + background-color: $track; + color: $fg; +} + +// --------------------------------------------------------------------------- +// Tooltips +// --------------------------------------------------------------------------- + tooltip { background-color: $bg-bar; color: $fg; - border: none; - border-radius: 0; - font-family: "JetBrainsMono Nerd Font", monospace; + border: 1px solid $border; + border-radius: 4px; + font-family: $font-family; } diff --git a/eww/.config/eww/eww.yuck b/eww/.config/eww/eww.yuck index 8d7e3f8..816f8e4 100644 --- a/eww/.config/eww/eww.yuck +++ b/eww/.config/eww/eww.yuck @@ -1,10 +1,31 @@ +;; --------------------------------------------------------------------------- +;; Layout constants +;; --------------------------------------------------------------------------- +;; gap = space between two widget groups in the bar +;; gap-tight = space inside a group (icon + its value) +;; Keep every spacing in the bar on one of these two values. +;; +;; Each bar icon carries $icon-pad (3px per side) in eww.scss, so Nerd Font +;; glyphs are not clipped. That padding sits inside the gap, so these numbers +;; are the gap you want to see, minus 2 * $icon-pad: +;; groups: 21px visible = 15 + 3 + 3 +;; tight : 10px visible = 4 + 3 + 3 + +(defvar gap 15) +(defvar gap-tight 4) + +;; --------------------------------------------------------------------------- +;; Workspaces +;; --------------------------------------------------------------------------- + (deflisten workspaces :initial "[]" "bash ~/.config/eww/scripts/get-workspaces") (deflisten current_workspace :initial "1" "bash ~/.config/eww/scripts/get-active-workspace") (defwidget workspaces [monitor] - (eventbox :onscroll "bash ~/.config/eww/scripts/change-active-workspace {} ${current_workspace}" :class "workspaces-widget" - (box :spacing 10 :orientation "h" :halign "start" :space-evenly false + (eventbox :onscroll "bash ~/.config/eww/scripts/change-active-workspace {} ${current_workspace}" + :class "workspaces-widget" + (box :spacing 0 :orientation "h" :halign "start" :space-evenly false (label :text "${workspaces}${current_workspace}" :visible false) (for workspace in workspaces (box @@ -13,6 +34,9 @@ (box :class "workspace-entry ${workspace.focused ? 'current' : ''} ${workspace.urgent ? 'urgent' : ''}" (label :text "${workspace.id}")))))))) +;; --------------------------------------------------------------------------- +;; Clock +;; --------------------------------------------------------------------------- (defpoll time :interval "1s" "date '+%H:%M'") @@ -20,40 +44,107 @@ (defpoll time_tooltip :interval "1s" "date '+%A, %B %d, %Y'") - (defwidget time-widget [] (label :text {time} :tooltip time_tooltip :class "time-label")) +;; --------------------------------------------------------------------------- +;; Notifications +;; --------------------------------------------------------------------------- + (defwidget notification-widget [] - (eventbox :class "notification-eventbox" :onclick "bash ~/.config/eww/scripts/notification-history-toggle" + (eventbox :class "notification-eventbox" + :onclick "bash ~/.config/eww/scripts/notification-history-toggle" :tooltip "Toggle notification history" (label :text "" :class "notification-icon"))) +;; --------------------------------------------------------------------------- +;; Battery +;; --------------------------------------------------------------------------- + (deflisten BATPERC :initial "" "bash ~/.config/eww/scripts/battery-perc-monitor") (deflisten BATICON :initial "" "bash ~/.config/eww/scripts/battery-icon-monitor") (deflisten BATTIME :initial "" "bash ~/.config/eww/scripts/battery-time-monitor") (defwidget battery-widget [] - (box :orientation "horizontal" :spacing 0 :tooltip "Time remaining: ${BATTIME}" + (box :orientation "h" :space-evenly false :spacing gap-tight + :tooltip "Time remaining: ${BATTIME}" (label :text {BATICON} :class "battery-icon") (label :text {BATPERC} :class "battery-percent"))) -(defwidget internet [] - (label :text wifi-icon - :tooltip wifi-name - :class "wifi-icon")) -(deflisten wifi-icon :initial "󰪎" "bash ~/.config/eww/scripts/wifi-monitor") -(deflisten wifi-name :initial "" "bash ~/.config/eww/scripts/wifi-name-monitor") +;; --------------------------------------------------------------------------- +;; Network (wifi / wired / vpn) +;; --------------------------------------------------------------------------- + +(deflisten net + :initial '{"icon":"󰪎","vpn":false,"vpnname":"asahi","vpnstate":"missing","wifi":false,"ssid":"","wifidev":"","signal":0,"wired":false,"wireddev":"","wiredconn":"","online":false,"tooltip":"No connection"}' + "bash ~/.config/eww/scripts/net-info") + +;; Bar entry. Click opens the network popup for this monitor. +(defwidget internet [popup] + (eventbox :class "net-eventbox" + :onclick "eww open --toggle ${popup}" + :tooltip {net.tooltip} + (label :text {net.icon} + :class "net-icon ${net.vpn ? 'net-vpn-on' : (net.online ? 'net-online' : 'net-offline')}"))) + +;; One connection row inside the popup. +(defwidget net-row [icon title subtitle] + (box :orientation "h" :space-evenly false :spacing 10 :halign "start" :class "net-row" + (label :text icon :class "net-row-icon") + (box :orientation "v" :space-evenly false :halign "start" + (label :text title :class "net-row-title" :halign "start" :limit-width 24) + (label :text subtitle :class "net-row-subtitle" :halign "start" :limit-width 28)))) + +(defwidget net-popup-content [] + (box :class "net-popup" :orientation "v" :space-evenly false :spacing 12 + (label :text "Network" :class "net-popup-header" :halign "start") + + ;; --- active connections --- + (box :orientation "v" :space-evenly false :spacing 10 + (box :visible {net.wired} :space-evenly false + (net-row :icon "󰈀" + :title {net.wiredconn} + :subtitle "Wired · ${net.wireddev}")) + (box :visible {net.wifi} :space-evenly false + (net-row :icon "󰖩" + :title {net.ssid} + :subtitle "Wi-Fi · ${net.wifidev} · ${net.signal}%")) + (box :visible {!net.online} :space-evenly false + (net-row :icon "󰪎" + :title "Not connected" + :subtitle "No wired or Wi-Fi link"))) + + (box :class "net-separator" :height 1) + + ;; --- vpn toggle --- + (eventbox :class "net-vpn-eventbox" + :cursor {net.vpnstate == "missing" ? "not-allowed" : "pointer"} + :onclick {net.vpnstate == "missing" ? "" : "bash ~/.config/eww/scripts/vpn-toggle"} + :tooltip {net.vpnstate == "missing" + ? "Import it first: sudo nmcli connection import type wireguard file /etc/wireguard/${net.vpnname}.conf" + : "Click to toggle the ${net.vpnname} VPN"} + (box :orientation "h" :space-evenly false :spacing 10 + (label :text "󱁍" :class "net-row-icon ${net.vpn ? 'net-vpn-on' : 'net-vpn-off'}") + (box :orientation "v" :space-evenly false :halign "start" :hexpand true + (label :text "VPN · ${net.vpnname}" :class "net-row-title" :halign "start") + (label :text {net.vpnstate} :class "net-row-subtitle" :halign "start")) + (label :text {net.vpn ? "ON" : "OFF"} + :halign "end" + :class "net-pill ${net.vpn ? 'net-pill-on' : 'net-pill-off'}"))))) + +;; --------------------------------------------------------------------------- +;; Brightness +;; --------------------------------------------------------------------------- (deflisten brightness :initial "50" "bash ~/.config/eww/scripts/brightness") (defwidget brightness-widget [] (eventbox :onscroll "[ {} = up ] && brightnessctl set 10%+ || brightnessctl set 10%-" :tooltip "Brightness: ${brightness}%" - (box :orientation "h" :spacing 0 + (box :orientation "h" :space-evenly false :spacing gap-tight (label :text "󰌵" :class "brightness-icon") (scale :min 0 :max 100 @@ -61,44 +152,55 @@ :class "brightness-bar" :onchange "brightnessctl set {}%")))) +;; --------------------------------------------------------------------------- +;; Volume +;; --------------------------------------------------------------------------- + (deflisten volume :initial "50" "bash ~/.config/eww/scripts/volume") (deflisten volume-icon :initial "" "bash ~/.config/eww/scripts/volume-icon-monitor") (defwidget volume-widget [] - (eventbox :onscroll "[ {} = up ] && pactl set-sink-volume @DEFAULT_SINK@ +10% || pactl set-sink-volume @DEFAULT_SINK@ -10%" - :tooltip "Volume: ${volume}%" - (box :orientation "h" :spacing 0 + (eventbox :onscroll "[ {} = up ] && pactl set-sink-volume @DEFAULT_SINK@ +10% || pactl set-sink-volume @DEFAULT_SINK@ -10%" + :tooltip "Volume: ${volume}%" + (box :orientation "h" :space-evenly false :spacing gap-tight (label :text volume-icon :class "volume-icon") (scale :min 0 :max 100 :value volume :class "volume-bar" - :onchange "pactl set-sink-volume @DEFAULT_SINK@ {}%")))) + :onchange "pactl set-sink-volume @DEFAULT_SINK@ {}%")))) + +;; --------------------------------------------------------------------------- +;; Signal +;; --------------------------------------------------------------------------- (defwidget signal-widget [] (eventbox :class "signal-eventbox" :onclick "bash ~/.config/eww/scripts/signal-toggle" :tooltip "Signal" (label :text "󰭹" :class "signal-icon"))) -(defwidget system [] - (box :orientation "h" :halign "end" :spacing 0 +;; --------------------------------------------------------------------------- +;; Bar +;; --------------------------------------------------------------------------- + +(defwidget system [popup] + (box :class "system" :orientation "h" :halign "end" :space-evenly false :spacing gap (signal-widget) - (internet) + (internet :popup popup) (brightness-widget) (volume-widget) (battery-widget) (time-widget) (notification-widget))) - -(defwidget bar [monitor] - (box :orientation "h" :space-evenly false +(defwidget bar [monitor popup] + (box :class "bar" :orientation "h" :space-evenly false (workspaces :monitor monitor) (box :hexpand true) - (system))) + (system :popup popup))) (defwindow bar0 - :monitor 0 + :monitor "HDMI-A-1" :geometry (geometry :x "0%" :y "0px" :width "100%" @@ -109,10 +211,10 @@ :reserve (struts :distance "40px" :side "top") :windowtype "dock" :wm-ignore false - (bar :monitor "HDMI-A-1")) + (bar :monitor "HDMI-A-1" :popup "net-popup0")) (defwindow bar1 - :monitor 1 + :monitor "eDP-1" :geometry (geometry :x "0%" :y "0px" :width "100%" @@ -123,6 +225,28 @@ :reserve (struts :distance "40px" :side "top") :windowtype "dock" :wm-ignore false - (bar :monitor "eDP-1")) + (bar :monitor "eDP-1" :popup "net-popup1")) +;; Popups sit under the bar on the same monitor. The bar reserves 40px at the +;; top, so anchoring at "top right" already lands below it. +(defwindow net-popup0 + :monitor "HDMI-A-1" + :geometry (geometry :x "10px" + :y "0px" + :width "330px" + :anchor "top right") + :stacking "overlay" + :exclusive false + :focusable false + (net-popup-content)) +(defwindow net-popup1 + :monitor "eDP-1" + :geometry (geometry :x "10px" + :y "0px" + :width "330px" + :anchor "top right") + :stacking "overlay" + :exclusive false + :focusable false + (net-popup-content)) diff --git a/eww/.config/eww/scripts/bar-shot b/eww/.config/eww/scripts/bar-shot new file mode 100755 index 0000000..07a9423 --- /dev/null +++ b/eww/.config/eww/scripts/bar-shot @@ -0,0 +1,59 @@ +#!/bin/bash +# Capture the eww bar (and optionally the area under it) with grim, for visual +# debugging of spacing and icons. +# +# Usage: +# bar-shot # right end of the bar on the focused output +# bar-shot -o eDP-1 # pick the output +# bar-shot -f # full bar width +# bar-shot -p # bar + 300px below it, to see popups +# bar-shot -w 900 # width of the right-end crop (default 780) +# bar-shot -O /tmp/x.png # output path (default /tmp/bar-shot.png) +# +# Scale factor 2 is used so glyph spacing stays readable when zoomed. + +set -euo pipefail + +OUT="" +FULL=0 +POPUP=0 +WIDTH=780 +FILE=/tmp/bar-shot.png +BAR_H=30 +POPUP_H=320 + +while getopts "o:fpw:O:h" opt; do + case "$opt" in + o) OUT=$OPTARG ;; + f) FULL=1 ;; + p) POPUP=1 ;; + w) WIDTH=$OPTARG ;; + O) FILE=$OPTARG ;; + h) sed -n '2,16p' "$0"; exit 0 ;; + *) exit 2 ;; + esac +done + +if [ -z "$OUT" ]; then + OUT=$(swaymsg -t get_outputs -r | jq -r '.[] | select(.focused) | .name') + [ -n "$OUT" ] || OUT=$(swaymsg -t get_outputs -r | jq -r '[.[] | select(.active)][0].name') +fi + +read -r X Y W _H < <( + swaymsg -t get_outputs -r | + jq -r --arg o "$OUT" '.[] | select(.name == $o) | "\(.rect.x) \(.rect.y) \(.rect.width) \(.rect.height)"' +) + +height=$BAR_H +[ "$POPUP" -eq 1 ] && height=$POPUP_H + +if [ "$FULL" -eq 1 ]; then + geom="$X,$Y ${W}x${height}" +else + crop=$WIDTH + [ "$crop" -gt "$W" ] && crop=$W + geom="$((X + W - crop)),$Y ${crop}x${height}" +fi + +grim -g "$geom" -s 2 "$FILE" +echo "$FILE ($OUT $geom)" diff --git a/eww/.config/eww/scripts/net-info b/eww/.config/eww/scripts/net-info new file mode 100755 index 0000000..8434fbe --- /dev/null +++ b/eww/.config/eww/scripts/net-info @@ -0,0 +1,120 @@ +#!/bin/bash +# Emit the full network state as one JSON line for eww. +# Replaces wifi-monitor, wifi-name-monitor and vpn-monitor. +# +# Output shape: +# { +# "icon": "f", # bar glyph: vpn / online / offline +# "vpn": true, # vpn connection is active +# "vpnname": "asahi", +# "vpnstate": "connected", # connected | disconnected | missing +# "wifi": true, +# "ssid": "Mosenet", +# "wifidev": "wld0", +# "signal": 68, +# "wired": false, +# "wireddev": "", +# "online": true, +# "tooltip": "Wi-Fi: Mosenet" +# } + +VPN_CONN="${VPN_CONN:-asahi}" + +ICON_VPN="󱁍" # vpn active +ICON_ONLINE="󰖟" # connected, no vpn +ICON_OFFLINE="󰪎" # no connection + +vpn_state() { + if nmcli -t -f NAME connection show --active 2>/dev/null | grep -qx "$VPN_CONN"; then + echo connected + elif nmcli -t -f NAME connection show 2>/dev/null | grep -qx "$VPN_CONN"; then + echo disconnected + else + echo missing + fi +} + +# First connected device of the given nmcli type. Prints "deviceconnection". +connected_device() { + nmcli -t -f DEVICE,TYPE,STATE,CONNECTION device 2>/dev/null | + awk -F: -v want="$1" '$2 == want && $3 == "connected" { print $1 "\t" $4; exit }' +} + +wifi_signal() { + nmcli -t -f IN-USE,SIGNAL device wifi list --rescan no 2>/dev/null | + awk -F: '/^\*/ { print $2; exit }' +} + +emit_state() { + local vpnstate vpn wifi_row wired_row + local wifidev="" ssid="" signal=0 wireddev="" wiredconn="" + local wifi=false wired=false online=false icon tooltip + + vpnstate=$(vpn_state) + [ "$vpnstate" = connected ] && vpn=true || vpn=false + + wifi_row=$(connected_device wifi) + if [ -n "$wifi_row" ]; then + wifi=true + wifidev=${wifi_row%%$'\t'*} + ssid=${wifi_row#*$'\t'} + signal=$(wifi_signal) + [ -n "$signal" ] || signal=0 + fi + + wired_row=$(connected_device ethernet) + if [ -n "$wired_row" ]; then + wired=true + wireddev=${wired_row%%$'\t'*} + wiredconn=${wired_row#*$'\t'} + fi + + if [ "$wifi" = true ] || [ "$wired" = true ]; then + online=true + fi + + if [ "$vpn" = true ]; then + icon="$ICON_VPN" + elif [ "$online" = true ]; then + icon="$ICON_ONLINE" + else + icon="$ICON_OFFLINE" + fi + + if [ "$wired" = true ]; then + tooltip="Wired: $wiredconn" + elif [ "$wifi" = true ]; then + tooltip="Wi-Fi: $ssid" + else + tooltip="No connection" + fi + [ "$vpn" = true ] && tooltip="$tooltip | VPN $VPN_CONN up" + + jq -nc \ + --arg icon "$icon" \ + --argjson vpn "$vpn" \ + --arg vpnname "$VPN_CONN" \ + --arg vpnstate "$vpnstate" \ + --argjson wifi "$wifi" \ + --arg ssid "$ssid" \ + --arg wifidev "$wifidev" \ + --argjson signal "${signal:-0}" \ + --argjson wired "$wired" \ + --arg wireddev "$wireddev" \ + --arg wiredconn "$wiredconn" \ + --argjson online "$online" \ + --arg tooltip "$tooltip" \ + '$ARGS.named' +} + +last=$(emit_state) +printf '%s\n' "$last" + +# Report only real changes, so eww does not redraw on unrelated network events. +nmcli monitor | while read -r _; do + current=$(emit_state) + if [ "$current" != "$last" ]; then + printf '%s\n' "$current" + last=$current + fi +done diff --git a/eww/.config/eww/scripts/vpn-toggle b/eww/.config/eww/scripts/vpn-toggle new file mode 100755 index 0000000..aab8b4b --- /dev/null +++ b/eww/.config/eww/scripts/vpn-toggle @@ -0,0 +1,20 @@ +#!/bin/bash +# Toggle the "asahi" WireGuard connection in NetworkManager. +# Usage: vpn-toggle [toggle|up|down|status] +# Requires the connection to be imported first: +# sudo nmcli connection import type wireguard file /etc/wireguard/asahi.conf + +CONN="asahi" + +is_up() { + nmcli -t -f NAME connection show --active 2>/dev/null | grep -qx "$CONN" +} + +case "${1:-toggle}" in + up) nmcli connection up "$CONN" >/dev/null 2>&1 ;; + down) nmcli connection down "$CONN" >/dev/null 2>&1 ;; + status) is_up && echo up || echo down ;; + toggle) is_up && nmcli connection down "$CONN" >/dev/null 2>&1 \ + || nmcli connection up "$CONN" >/dev/null 2>&1 ;; + *) echo "Usage: ${0##*/} [toggle|up|down|status]" >&2; exit 2 ;; +esac diff --git a/eww/.config/eww/scripts/wifi b/eww/.config/eww/scripts/wifi deleted file mode 100644 index 40240ae..0000000 --- a/eww/.config/eww/scripts/wifi +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -symbol() { -[ $(cat /sys/class/net/w*/operstate) = down ] && echo 󰪎 && exit -echo 󰖟 -} - -name() { -nmcli | grep "^wlp" | sed 's/\ connected\ to\ /Connected to /g' | cut -d ':' -f2 -} - -[ "$1" = "icon" ] && symbol && exit -[ "$1" = "name" ] && name && exit diff --git a/eww/.config/eww/scripts/wifi-monitor b/eww/.config/eww/scripts/wifi-monitor deleted file mode 100644 index 62bc24c..0000000 --- a/eww/.config/eww/scripts/wifi-monitor +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -get_icon() { - [ $(cat /sys/class/net/w*/operstate 2>/dev/null) = down ] && echo "󰪎" || echo "󰖟" -} - -# Output initial state -get_icon - -# Monitor network changes -nmcli monitor | while read -r line; do - get_icon -done diff --git a/eww/.config/eww/scripts/wifi-name-monitor b/eww/.config/eww/scripts/wifi-name-monitor deleted file mode 100644 index 9513bc7..0000000 --- a/eww/.config/eww/scripts/wifi-name-monitor +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -get_name() { - nmcli | grep "^wlp" | sed 's/\ connected\ to\ /Connected to /g' | cut -d ':' -f2 -} - -# Output initial state -get_name - -# Monitor network changes -nmcli monitor | while read -r line; do - get_name -done