1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
#! /bin/bash
# One dot per defined VM, coloured by state.
# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
# Licensed under the GNU General Public License v2 only.
#
# dependencies: libvirt (virsh), jq
#
# The colours come from the palette, not from here: install.sh writes
# ~/.config/waybar-udt/dots.colors from the selected udt scheme, and this
# sources it. The defaults below are only what a missing file falls back to,
# so the bar still draws if the theme has not been installed yet.
set -euo pipefail
JQ_BIN="${JQ:-jq}"
# Palette. Sourced, with fallbacks, so this script works standalone.
DOT_SUCCESS="#a6da95"
DOT_WARNING="#eed49f"
DOT_CRITICAL="#ed8796"
DOT_INFO="#8bd5ca"
DOT_ACCENT="#b7bdf8"
DOT_FAINT="#6e738d"
colors="$HOME/.config/waybar-udt/dots.colors"
# shellcheck source=/dev/null
[[ -r "$colors" ]] && . "$colors"
# Require virsh
if ! command -v virsh >/dev/null 2>&1; then
$JQ_BIN -cn '{text: "", tooltip: "virsh not found", class: "vmdot"}'
exit 0
fi
# Collect all defined VMs and their states.
# virsh list --all columns: Id Name State (state may be multi-word, e.g. "shut off")
declare -A vm_states
while IFS= read -r line; do
# Skip header lines and separator lines
[[ "$line" =~ ^[[:space:]]*Id[[:space:]] ]] && continue
[[ "$line" =~ ^[-[:space:]]+$ ]] && continue
[[ -z "${line// }" ]] && continue
# Fields: id (may be "-"), name, state (rest of line)
read -r _id name state_rest <<< "$line"
vm_states["$name"]="$state_rest"
done < <(virsh list --all 2>/dev/null)
if [[ ${#vm_states[@]} -eq 0 ]]; then
$JQ_BIN -cn '{text: "", tooltip: "No VMs defined", class: "vmdot"}'
exit 0
fi
# libvirt state → palette role.
declare -A state_color=(
["running"]="$DOT_SUCCESS"
["idle"]="$DOT_WARNING" # running but idle
["paused"]="$DOT_WARNING"
["pmsuspended"]="$DOT_ACCENT" # PM sleep / ACPI S3
["saved"]="$DOT_INFO" # managed save
["crashed"]="$DOT_CRITICAL"
["shut off"]="$DOT_FAINT"
)
fallback_color="$DOT_FAINT" # any unknown future state
dots=()
tooltip_parts=()
classes="vmdot"
# Sort VM names for stable ordering
mapfile -t sorted_names < <(printf '%s\n' "${!vm_states[@]}" | sort)
for name in "${sorted_names[@]}"; do
state="${vm_states[$name]}"
color="${state_color[$state]:-$fallback_color}"
# Derive a CSS-safe class token (spaces → hyphens)
state_class="${state// /-}"
dots+=("<span foreground=\"${color}\">●</span>")
classes="$classes vm-${state_class}"
tooltip_parts+=("${name}: ${state}")
done
text="${dots[*]}"
tooltip="$(IFS=' | '; echo "${tooltip_parts[*]}")"
$JQ_BIN -cn \
--arg text "$text" \
--arg tooltip "$tooltip" \
--arg class "$classes" \
'{text: $text, tooltip: $tooltip, class: $class}'
|