eww QoL update and vpn toggle
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
59
eww/.config/eww/scripts/bar-shot
Executable file
59
eww/.config/eww/scripts/bar-shot
Executable file
@@ -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)"
|
||||
120
eww/.config/eww/scripts/net-info
Executable file
120
eww/.config/eww/scripts/net-info
Executable file
@@ -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 "device<TAB>connection".
|
||||
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
|
||||
20
eww/.config/eww/scripts/vpn-toggle
Executable file
20
eww/.config/eww/scripts/vpn-toggle
Executable file
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user