eww QoL update and vpn toggle
This commit is contained in:
@@ -1,201 +1,219 @@
|
|||||||
|
* {
|
||||||
*{
|
|
||||||
all: unset;
|
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 {
|
window {
|
||||||
font-family: "JetBrainsMono Nerd Font", monospace;
|
font-family: $font-family;
|
||||||
background-color: #000000;
|
background-color: $bg-bar;
|
||||||
color: #FFFFFF;
|
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 {
|
eventbox, box, label, scale {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$font-family: "JetBrainsMono Nerd Font", monospace;
|
// ---------------------------------------------------------------------------
|
||||||
|
// Bar
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// Global colors (sync with ~/.config/colors)
|
.bar {
|
||||||
$bg-bar: #000000;
|
background-color: $bg-bar;
|
||||||
$track: #3a3a3a;
|
}
|
||||||
$accent: #E0AF68;
|
|
||||||
$urgent: #F7768E;
|
|
||||||
$fg: #ffffff;
|
|
||||||
|
|
||||||
// Legacy
|
// The last icon already carries $icon-pad, so subtract it to keep the visible
|
||||||
$background: $bg-bar;
|
// edge gap equal to $bar-edge.
|
||||||
$foreground: $fg;
|
.system {
|
||||||
|
margin-right: $bar-edge - $icon-pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspaces-widget {
|
||||||
|
margin-left: $bar-edge - 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.workspace-entry {
|
.workspace-entry {
|
||||||
/* Styles for all workspace entries */
|
padding: 0 6px;
|
||||||
padding: 0 0.5em;
|
color: $fg;
|
||||||
font-family: $font-family;
|
|
||||||
color : $foreground;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-entry.current {
|
.workspace-entry.current {
|
||||||
/* Styles for the current workspace */
|
|
||||||
color: $accent;
|
color: $accent;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-entry.urgent {
|
.workspace-entry.urgent {
|
||||||
/* Styles for urgent workspaces */
|
|
||||||
color: $urgent;
|
color: $urgent;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-label {
|
// ---------------------------------------------------------------------------
|
||||||
font-family: $font-family;
|
// Bar items
|
||||||
color : $foreground;
|
// ---------------------------------------------------------------------------
|
||||||
font-weight: bold;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification-eventbox {
|
|
||||||
padding: 0;
|
|
||||||
min-width: 0;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Every bar glyph and value shares one look.
|
||||||
|
.signal-icon,
|
||||||
|
.net-icon,
|
||||||
|
.brightness-icon,
|
||||||
|
.volume-icon,
|
||||||
|
.battery-icon,
|
||||||
|
.battery-percent,
|
||||||
|
.time-label,
|
||||||
.notification-icon {
|
.notification-icon {
|
||||||
font-family: $font-family;
|
color: $fg;
|
||||||
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;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
padding: 0 $icon-pad;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bar {
|
.net-vpn-on { color: $accent; }
|
||||||
background-color: $background;
|
.net-offline { color: $urgent; }
|
||||||
}
|
|
||||||
|
|
||||||
.wifi-eventbox {
|
.signal-eventbox,
|
||||||
padding: 0;
|
.net-eventbox,
|
||||||
|
.notification-eventbox {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wifi-icon{
|
// ---------------------------------------------------------------------------
|
||||||
color : $foreground;
|
// Sliders
|
||||||
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 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 {
|
.brightness-bar {
|
||||||
min-width: 10px;
|
min-width: $slider-width;
|
||||||
min-height: 6px;
|
min-height: $slider-height;
|
||||||
padding: 0;
|
margin: 0 $icon-pad;
|
||||||
margin: 0;
|
|
||||||
margin-right: 10px;
|
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.volume-bar trough,
|
||||||
.brightness-bar trough {
|
.brightness-bar trough {
|
||||||
background-color: $track;
|
background-color: $track;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
min-height: 6px;
|
min-width: $slider-width;
|
||||||
min-width: 10px;
|
min-height: $slider-height;
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.volume-bar highlight,
|
||||||
.brightness-bar highlight {
|
.brightness-bar highlight {
|
||||||
background-color: $fg;
|
background-color: $fg;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.volume-bar slider,
|
||||||
.brightness-bar slider {
|
.brightness-bar slider {
|
||||||
all: unset;
|
all: unset;
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.signal-eventbox {
|
// ---------------------------------------------------------------------------
|
||||||
padding: 0;
|
// Network popup
|
||||||
min-width: 0;
|
// ---------------------------------------------------------------------------
|
||||||
min-height: 0;
|
|
||||||
|
.net-popup {
|
||||||
|
background-color: $bg-popup;
|
||||||
|
border: 1px solid $border;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 12px 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.signal-icon {
|
.net-popup-header {
|
||||||
color: $foreground;
|
color: $fg-dim;
|
||||||
|
font-size: 11px;
|
||||||
font-weight: bold;
|
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 {
|
tooltip {
|
||||||
background-color: $bg-bar;
|
background-color: $bg-bar;
|
||||||
color: $fg;
|
color: $fg;
|
||||||
border: none;
|
border: 1px solid $border;
|
||||||
border-radius: 0;
|
border-radius: 4px;
|
||||||
font-family: "JetBrainsMono Nerd Font", monospace;
|
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 workspaces :initial "[]" "bash ~/.config/eww/scripts/get-workspaces")
|
||||||
|
|
||||||
(deflisten current_workspace :initial "1" "bash ~/.config/eww/scripts/get-active-workspace")
|
(deflisten current_workspace :initial "1" "bash ~/.config/eww/scripts/get-active-workspace")
|
||||||
|
|
||||||
(defwidget workspaces [monitor]
|
(defwidget workspaces [monitor]
|
||||||
(eventbox :onscroll "bash ~/.config/eww/scripts/change-active-workspace {} ${current_workspace}" :class "workspaces-widget"
|
(eventbox :onscroll "bash ~/.config/eww/scripts/change-active-workspace {} ${current_workspace}"
|
||||||
(box :spacing 10 :orientation "h" :halign "start" :space-evenly false
|
:class "workspaces-widget"
|
||||||
|
(box :spacing 0 :orientation "h" :halign "start" :space-evenly false
|
||||||
(label :text "${workspaces}${current_workspace}" :visible false)
|
(label :text "${workspaces}${current_workspace}" :visible false)
|
||||||
(for workspace in workspaces
|
(for workspace in workspaces
|
||||||
(box
|
(box
|
||||||
@@ -13,6 +34,9 @@
|
|||||||
(box :class "workspace-entry ${workspace.focused ? 'current' : ''} ${workspace.urgent ? 'urgent' : ''}"
|
(box :class "workspace-entry ${workspace.focused ? 'current' : ''} ${workspace.urgent ? 'urgent' : ''}"
|
||||||
(label :text "${workspace.id}"))))))))
|
(label :text "${workspace.id}"))))))))
|
||||||
|
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
;; Clock
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
(defpoll time :interval "1s"
|
(defpoll time :interval "1s"
|
||||||
"date '+%H:%M'")
|
"date '+%H:%M'")
|
||||||
@@ -20,40 +44,107 @@
|
|||||||
(defpoll time_tooltip :interval "1s"
|
(defpoll time_tooltip :interval "1s"
|
||||||
"date '+%A, %B %d, %Y'")
|
"date '+%A, %B %d, %Y'")
|
||||||
|
|
||||||
|
|
||||||
(defwidget time-widget []
|
(defwidget time-widget []
|
||||||
(label
|
(label
|
||||||
:text {time}
|
:text {time}
|
||||||
:tooltip time_tooltip
|
:tooltip time_tooltip
|
||||||
:class "time-label"))
|
:class "time-label"))
|
||||||
|
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
;; Notifications
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
(defwidget notification-widget []
|
(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"
|
:tooltip "Toggle notification history"
|
||||||
(label :text "" :class "notification-icon")))
|
(label :text "" :class "notification-icon")))
|
||||||
|
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
;; Battery
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
(deflisten BATPERC :initial "" "bash ~/.config/eww/scripts/battery-perc-monitor")
|
(deflisten BATPERC :initial "" "bash ~/.config/eww/scripts/battery-perc-monitor")
|
||||||
(deflisten BATICON :initial "" "bash ~/.config/eww/scripts/battery-icon-monitor")
|
(deflisten BATICON :initial "" "bash ~/.config/eww/scripts/battery-icon-monitor")
|
||||||
(deflisten BATTIME :initial "" "bash ~/.config/eww/scripts/battery-time-monitor")
|
(deflisten BATTIME :initial "" "bash ~/.config/eww/scripts/battery-time-monitor")
|
||||||
|
|
||||||
(defwidget battery-widget []
|
(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 {BATICON} :class "battery-icon")
|
||||||
(label :text {BATPERC} :class "battery-percent")))
|
(label :text {BATPERC} :class "battery-percent")))
|
||||||
|
|
||||||
(defwidget internet []
|
;; ---------------------------------------------------------------------------
|
||||||
(label :text wifi-icon
|
;; Network (wifi / wired / vpn)
|
||||||
:tooltip wifi-name
|
;; ---------------------------------------------------------------------------
|
||||||
:class "wifi-icon"))
|
|
||||||
(deflisten wifi-icon :initial "" "bash ~/.config/eww/scripts/wifi-monitor")
|
(deflisten net
|
||||||
(deflisten wifi-name :initial "" "bash ~/.config/eww/scripts/wifi-name-monitor")
|
: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")
|
(deflisten brightness :initial "50" "bash ~/.config/eww/scripts/brightness")
|
||||||
|
|
||||||
(defwidget brightness-widget []
|
(defwidget brightness-widget []
|
||||||
(eventbox :onscroll "[ {} = up ] && brightnessctl set 10%+ || brightnessctl set 10%-"
|
(eventbox :onscroll "[ {} = up ] && brightnessctl set 10%+ || brightnessctl set 10%-"
|
||||||
:tooltip "Brightness: ${brightness}%"
|
:tooltip "Brightness: ${brightness}%"
|
||||||
(box :orientation "h" :spacing 0
|
(box :orientation "h" :space-evenly false :spacing gap-tight
|
||||||
(label :text "" :class "brightness-icon")
|
(label :text "" :class "brightness-icon")
|
||||||
(scale :min 0
|
(scale :min 0
|
||||||
:max 100
|
:max 100
|
||||||
@@ -61,44 +152,55 @@
|
|||||||
:class "brightness-bar"
|
:class "brightness-bar"
|
||||||
:onchange "brightnessctl set {}%"))))
|
:onchange "brightnessctl set {}%"))))
|
||||||
|
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
;; Volume
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
(deflisten volume :initial "50" "bash ~/.config/eww/scripts/volume")
|
(deflisten volume :initial "50" "bash ~/.config/eww/scripts/volume")
|
||||||
(deflisten volume-icon :initial "" "bash ~/.config/eww/scripts/volume-icon-monitor")
|
(deflisten volume-icon :initial "" "bash ~/.config/eww/scripts/volume-icon-monitor")
|
||||||
|
|
||||||
(defwidget volume-widget []
|
(defwidget volume-widget []
|
||||||
(eventbox :onscroll "[ {} = up ] && pactl set-sink-volume @DEFAULT_SINK@ +10% || pactl set-sink-volume @DEFAULT_SINK@ -10%"
|
(eventbox :onscroll "[ {} = up ] && pactl set-sink-volume @DEFAULT_SINK@ +10% || pactl set-sink-volume @DEFAULT_SINK@ -10%"
|
||||||
:tooltip "Volume: ${volume}%"
|
:tooltip "Volume: ${volume}%"
|
||||||
(box :orientation "h" :spacing 0
|
(box :orientation "h" :space-evenly false :spacing gap-tight
|
||||||
(label :text volume-icon :class "volume-icon")
|
(label :text volume-icon :class "volume-icon")
|
||||||
(scale :min 0
|
(scale :min 0
|
||||||
:max 100
|
:max 100
|
||||||
:value volume
|
:value volume
|
||||||
:class "volume-bar"
|
:class "volume-bar"
|
||||||
:onchange "pactl set-sink-volume @DEFAULT_SINK@ {}%"))))
|
:onchange "pactl set-sink-volume @DEFAULT_SINK@ {}%"))))
|
||||||
|
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
;; Signal
|
||||||
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
(defwidget signal-widget []
|
(defwidget signal-widget []
|
||||||
(eventbox :class "signal-eventbox" :onclick "bash ~/.config/eww/scripts/signal-toggle"
|
(eventbox :class "signal-eventbox" :onclick "bash ~/.config/eww/scripts/signal-toggle"
|
||||||
:tooltip "Signal"
|
:tooltip "Signal"
|
||||||
(label :text "" :class "signal-icon")))
|
(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)
|
(signal-widget)
|
||||||
(internet)
|
(internet :popup popup)
|
||||||
(brightness-widget)
|
(brightness-widget)
|
||||||
(volume-widget)
|
(volume-widget)
|
||||||
(battery-widget)
|
(battery-widget)
|
||||||
(time-widget)
|
(time-widget)
|
||||||
(notification-widget)))
|
(notification-widget)))
|
||||||
|
|
||||||
|
(defwidget bar [monitor popup]
|
||||||
(defwidget bar [monitor]
|
(box :class "bar" :orientation "h" :space-evenly false
|
||||||
(box :orientation "h" :space-evenly false
|
|
||||||
(workspaces :monitor monitor)
|
(workspaces :monitor monitor)
|
||||||
(box :hexpand true)
|
(box :hexpand true)
|
||||||
(system)))
|
(system :popup popup)))
|
||||||
|
|
||||||
(defwindow bar0
|
(defwindow bar0
|
||||||
:monitor 0
|
:monitor "HDMI-A-1"
|
||||||
:geometry (geometry :x "0%"
|
:geometry (geometry :x "0%"
|
||||||
:y "0px"
|
:y "0px"
|
||||||
:width "100%"
|
:width "100%"
|
||||||
@@ -109,10 +211,10 @@
|
|||||||
:reserve (struts :distance "40px" :side "top")
|
:reserve (struts :distance "40px" :side "top")
|
||||||
:windowtype "dock"
|
:windowtype "dock"
|
||||||
:wm-ignore false
|
:wm-ignore false
|
||||||
(bar :monitor "HDMI-A-1"))
|
(bar :monitor "HDMI-A-1" :popup "net-popup0"))
|
||||||
|
|
||||||
(defwindow bar1
|
(defwindow bar1
|
||||||
:monitor 1
|
:monitor "eDP-1"
|
||||||
:geometry (geometry :x "0%"
|
:geometry (geometry :x "0%"
|
||||||
:y "0px"
|
:y "0px"
|
||||||
:width "100%"
|
:width "100%"
|
||||||
@@ -123,6 +225,28 @@
|
|||||||
:reserve (struts :distance "40px" :side "top")
|
:reserve (struts :distance "40px" :side "top")
|
||||||
:windowtype "dock"
|
:windowtype "dock"
|
||||||
:wm-ignore false
|
: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