aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/udt-accent56
1 files changed, 55 insertions, 1 deletions
diff --git a/bin/udt-accent b/bin/udt-accent
index a47460e..fbd6a65 100755
--- a/bin/udt-accent
+++ b/bin/udt-accent
@@ -14,6 +14,7 @@ including the terminal's ANSI colors. See the design spec for why that matters.
import math
import os
+import subprocess
import sys
import tempfile
from pathlib import Path
@@ -37,6 +38,7 @@ FALLBACK = "mauve"
MIN_CHROMA = 10.0
OUTPUT = Path.home() / ".cache" / "wal" / "udt-accent.rasi"
+BORDER_OUTPUT = Path.home() / ".cache" / "wal" / "udt-border.lua"
def _to_lab(hexval):
@@ -121,6 +123,45 @@ def write_accent(name):
raise
+def hue_neighbours(name):
+ """The accent either side of `name` on the perceptual hue wheel.
+
+ The animated border rotates a gradient, so it needs more than one colour to
+ show motion. Using the accent's own neighbours keeps the movement visible
+ while staying inside one region of the palette.
+ """
+ order = sorted(ACCENTS, key=lambda n: _hue(ACCENTS[n]))
+ i = order.index(name)
+ return order[(i - 1) % len(order)], order[(i + 1) % len(order)]
+
+
+def write_border(name):
+ """Write the Hyprland border gradient, atomically.
+
+ Emitted as Lua rather than a .conf snippet: Hyprland's Lua parser refuses
+ `hyprctl keyword` ("keyword can't work with non-legacy parsers") and has no
+ source directive, so the config reads this file with dofile() instead.
+ """
+ before, after = hue_neighbours(name)
+ stops = ", ".join(f'"rgb({ACCENTS[n].lstrip("#")})"' for n in (before, name, after))
+
+ content = (
+ "-- Generated by udt-accent. Do not edit.\n"
+ f"return {{ {stops} }}\n"
+ )
+
+ BORDER_OUTPUT.parent.mkdir(parents=True, exist_ok=True)
+ fd, tmp = tempfile.mkstemp(dir=str(BORDER_OUTPUT.parent), suffix=".tmp")
+ try:
+ with os.fdopen(fd, "w") as handle:
+ handle.write(content)
+ os.replace(tmp, BORDER_OUTPUT)
+ except BaseException:
+ if os.path.exists(tmp):
+ os.unlink(tmp)
+ raise
+
+
def main(image):
try:
name = snap(signature_color(image))
@@ -130,7 +171,13 @@ def main(image):
name = FALLBACK
write_accent(name)
- print(f"{name} {ACCENTS[name]}")
+ write_border(name)
+
+ # Hyprland only rereads its config on request, and may not be running.
+ subprocess.run(["hyprctl", "reload"], capture_output=True, check=False)
+
+ before, after = hue_neighbours(name)
+ print(f"{name} {ACCENTS[name]} (border: {before} .. {name} .. {after})")
def selftest():
@@ -146,6 +193,13 @@ def selftest():
# A near-grey has an unstable hue angle and must take the fallback.
assert snap("#888888") == FALLBACK, snap("#888888")
+ # Border neighbours must be distinct from the accent and from each other,
+ # or the gradient has nothing to animate between.
+ for name in ACCENTS:
+ before, after = hue_neighbours(name)
+ assert len({before, name, after}) == 3, (name, before, after)
+ assert hue_neighbours("peach") == ("red", "yellow"), hue_neighbours("peach")
+
print("selftest OK")