aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-29 11:42:09 +0200
committerDanilo M. <danix@danix.xyz>2026-09-29 11:42:09 +0200
commitff7dcf3ef2338a0aef1856a33da06d1a92c2434c (patch)
tree65f48abed7b214cd27492afadd1aeb7cad90d254
parent8164e17276bb37f5f24c9f84df5923c090efed79 (diff)
downloadunified-desktop-theme-ff7dcf3ef2338a0aef1856a33da06d1a92c2434c.tar.gz
unified-desktop-theme-ff7dcf3ef2338a0aef1856a33da06d1a92c2434c.zip
feat(grub): rebuild on every wallpaper change via udt-grub
GRUB's background is a wallpaper snapshot, but only install.sh rebuilt it, so a wallpaper change left the /boot copy stale with no hint. udt-grub now holds the build-and-print logic; install.sh runs it in the foreground and udt-accent starts it detached next to udt-homepage. A stale installed theme gets a notification carrying the root copy command. A flock serialises the two runs install.sh triggers, since build swaps dist/udt in by rename. AGENTS.md: udt-accent writes seven outputs (SDDM was missing), and names the two detached helpers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--AGENTS.md18
-rwxr-xr-xbin/udt-accent9
-rwxr-xr-xbin/udt-grub50
-rwxr-xr-xinstall.sh35
4 files changed, 74 insertions, 38 deletions
diff --git a/AGENTS.md b/AGENTS.md
index e6b0960..6218fb6 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -24,9 +24,11 @@ Sibling checkouts are wired from here, each skipped when not cloned:
`~/.config/conky/`) and `hypr-theme`, which is the whole of `~/.config/hypr` as
one symlink. A real `~/.config/hypr` directory is left alone with a warning.
`grub-theme-udt` is SDDM's shape: udt-palette renders its `theme.txt.in`,
-`install.sh` runs its `build` as the user, then prints the root copy. A copy,
-not a link, because GRUB reads `/boot` before `/home` is mounted; it needs
-rerunning after a scheme or wallpaper change.
+`bin/udt-grub` runs its `build` as the user, then prints the root copy. A copy,
+not a link, because GRUB reads `/boot` before `/home` is mounted, so it goes
+stale on a scheme or wallpaper change. `install.sh` runs `udt-grub` in the
+foreground and `udt-accent` starts it detached, and when an installed theme
+differs from the new build the root command arrives as a notification.
Its panel title is `PRETTY_HOSTNAME` from `/etc/machine-info`, which
`bin/udt-machine-name` writes (root, run by the user; there is no
hostnamectl without systemd). It rewrites that one key and keeps the rest.
@@ -166,12 +168,16 @@ One colour moves: `@accent`. `bin/udt-accent` reads the current wallpaper,
extracts its signature colour, and snaps it to the nearest of ten Catppuccin
accents by perceptual hue in CIELAB. `wallp` calls it on every wallpaper change.
-It writes six things: `~/.cache/udt/udt-accent.rasi` (rofi),
+It writes seven things: `~/.cache/udt/udt-accent.rasi` (rofi),
`~/.cache/udt/udt-border.lua` (Hyprland), `~/.cache/udt/hyprlock-border.conf`
(hyprlock, the same three hues as the window border, as a hyprlang gradient),
`~/.cache/wal/colors.json` (Firefox, via pywalfox),
-`~/.cache/udt/udt-palette.qml` (quickshell), and the accent substitution in
-Sublime's installed `Packages/User/udt.sublime-color-scheme`.
+`~/.cache/udt/udt-palette.qml` (quickshell),
+`~/.local/share/udt/sddm-theme.conf` (the SDDM greeter, palette plus wallpaper,
+read through a root-made `theme.conf.user` symlink), and the accent
+substitution in Sublime's installed `Packages/User/udt.sublime-color-scheme`.
+It then starts `udt-homepage` and `udt-grub` detached, for the two outputs
+that need a root step on another filesystem or host.
**Sublime's accent is replaced, not substituted into a placeholder.** Its
colour scheme cannot cascade, so the accent has to live in the one generated
diff --git a/bin/udt-accent b/bin/udt-accent
index 72d3a64..1027fab 100755
--- a/bin/udt-accent
+++ b/bin/udt-accent
@@ -477,9 +477,12 @@ def main(image):
# The dashboard host. Detached, because wallp runs this synchronously and
# an unreachable host would hold the wallpaper change for the scp timeout.
- subprocess.Popen([Path(__file__).resolve().parent / "udt-homepage"],
- stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
- stderr=subprocess.DEVNULL, start_new_session=True)
+ # GRUB's background is a wallpaper snapshot. Detached for the same reason;
+ # it only rebuilds and notifies, since the copy into /boot needs root.
+ for helper in ("udt-homepage", "udt-grub"):
+ subprocess.Popen([Path(__file__).resolve().parent / helper],
+ stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL, start_new_session=True)
before, after = hue_neighbours(name)
print(f"{name} {ACCENTS[name]} (border: {before} .. {name} .. {after})")
diff --git a/bin/udt-grub b/bin/udt-grub
new file mode 100755
index 0000000..20fd296
--- /dev/null
+++ b/bin/udt-grub
@@ -0,0 +1,50 @@
+#!/bin/bash
+# udt-grub: rebuild the GRUB theme and say what root has to copy into /boot.
+# Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+# Licensed under the GNU General Public License v2 only.
+#
+# Built, not installed: GRUB reads /boot before /home is mounted, so the theme
+# is a root-owned copy of grub-theme-udt's dist/udt/, and this script only
+# prints the command that refreshes it. Its background is a snapshot of the
+# wallpaper and its colours come from the scheme, so both a wallpaper change
+# and a scheme switch leave the copy stale.
+#
+# udt-accent starts this detached on every run; install.sh runs it in the
+# foreground. A notification goes out only when an installed theme is stale:
+# a machine that never installed it has nothing to refresh.
+
+repo="$(dirname "$(readlink -f "$0")")/.."
+grub_repo="$(realpath -m "$repo/../grub-theme-udt")"
+[ -x "$grub_repo/build" ] || exit 0
+
+# install.sh runs udt-accent, which starts this detached, and then runs this
+# itself. build swaps dist/udt/ in with a rename, so two at once can collide.
+mkdir -p "$grub_repo/dist"
+exec 9>"$grub_repo/dist/.lock"
+flock 9
+
+"$grub_repo/build" >/dev/null || { echo "grub: its build failed, theme not refreshed" >&2; exit 1; }
+
+theme=/boot/grub/themes/udt
+copy="rm -rf $theme && cp -r \"$grub_repo/dist/udt\" /boot/grub/themes/"
+
+if diff -rq "$grub_repo/dist/udt" "$theme" >/dev/null 2>&1; then
+ echo "grub theme: up to date"
+elif [ -d "$theme" ]; then
+ echo "grub theme: refresh as root"
+ echo " $copy"
+ command -v notify-send >/dev/null 2>&1 &&
+ notify-send -a udt "grub: theme rebuilt" "As root:
+$copy"
+else
+ echo "grub theme: run these as root to install it"
+ echo " $copy"
+fi
+grep -q "^GRUB_THEME=\"$theme/theme.txt\"" /etc/default/grub 2>/dev/null || {
+ echo " # once: GRUB_THEME=\"$theme/theme.txt\" in /etc/default/grub, then"
+ echo " grub-mkconfig -o /boot/grub/grub.cfg"
+}
+# The panel title falls back to the OS name until one is set.
+[ -n "$("$repo/bin/udt-machine-name")" ] ||
+ echo " # optional, the panel title: sudo \"$repo/bin/udt-machine-name\" \"My Machine\""
+exit 0
diff --git a/install.sh b/install.sh
index b63f8c4..33e6f0c 100755
--- a/install.sh
+++ b/install.sh
@@ -104,7 +104,7 @@ ln -sfn "$HOME/.cache/udt/colors.json" "$HOME/.cache/wal/colors.json"
# The rofi menus are bound by name in hypr-theme's keybindings, so they keep
# the names they had when they lived loose in ~/bin.
-for f in udt-accent udt-palette udt-machine-name udt-powermenu.sh udt-launcher.sh qar-scrotmenu.sh; do
+for f in udt-accent udt-grub udt-palette udt-machine-name udt-powermenu.sh udt-launcher.sh qar-scrotmenu.sh; do
ln -sfn "$repo/bin/$f" "$HOME/bin/$f"
done
@@ -339,34 +339,11 @@ if [ -d "$sddm_repo/theme" ]; then
echo " printf '[Theme]\\nCurrent=udt\\n' > /etc/sddm.conf.d/udt.conf"
fi
-# GRUB. Same shape as SDDM: udt-palette has rendered theme.txt, the sibling
-# repo's build assembles the theme as the user, and the root half is printed.
-# A copy, not a link: GRUB reads /boot before /home is mounted. The background
-# is a snapshot of the wallpaper, so the copy needs rerunning after a scheme
-# or wallpaper change; grub-mkconfig only when GRUB_THEME changes.
-grub_repo="$(realpath -m "$repo/../grub-theme-udt")"
-if [ -x "$grub_repo/build" ]; then
- if "$grub_repo/build"; then
- grub_theme='/boot/grub/themes/udt/theme.txt'
- echo
- if diff -rq "$grub_repo/dist/udt" /boot/grub/themes/udt >/dev/null 2>&1; then
- echo "grub theme: up to date"
- elif grep -q "^GRUB_THEME=\"$grub_theme\"" /etc/default/grub 2>/dev/null; then
- echo "grub theme: refresh as root"
- echo " rm -rf /boot/grub/themes/udt && cp -r \"$grub_repo/dist/udt\" /boot/grub/themes/"
- else
- echo "grub theme: run these as root to install or refresh it"
- echo " rm -rf /boot/grub/themes/udt && cp -r \"$grub_repo/dist/udt\" /boot/grub/themes/"
- echo " # once: GRUB_THEME=\"$grub_theme\" in /etc/default/grub, then"
- echo " grub-mkconfig -o /boot/grub/grub.cfg"
- fi
- # The panel title falls back to the OS name until one is set.
- [ -n "$("$repo/bin/udt-machine-name")" ] || \
- echo " # optional, the panel title: sudo \"$repo/bin/udt-machine-name\" \"My Machine\""
- else
- echo "grub: its build failed, theme not refreshed" >&2
- fi
-fi
+# GRUB. Same shape as SDDM: udt-palette has rendered theme.txt, and udt-grub
+# builds the theme as the user and prints the root half. udt-accent also runs
+# it, detached, so a wallpaper change alone rebuilds it too.
+echo
+"$repo/bin/udt-grub"
[ -n "$reloaded" ] && echo "reloaded:$reloaded"