diff options
Diffstat (limited to 'bin')
| -rwxr-xr-x | bin/udt-accent | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/bin/udt-accent b/bin/udt-accent index e459a35..a47460e 100755 --- a/bin/udt-accent +++ b/bin/udt-accent @@ -13,7 +13,10 @@ including the terminal's ANSI colors. See the design spec for why that matters. """ import math +import os import sys +import tempfile +from pathlib import Path # The nine candidate accents. rosewater and flamingo are excluded as # near-neutral tints that capture saturated inputs; maroon, sapphire and @@ -33,6 +36,8 @@ ACCENTS = { FALLBACK = "mauve" MIN_CHROMA = 10.0 +OUTPUT = Path.home() / ".cache" / "wal" / "udt-accent.rasi" + def _to_lab(hexval): """Convert #rrggbb to CIELAB. sRGB D65, the standard conversion.""" @@ -80,6 +85,54 @@ def snap(hexval): return min(ACCENTS, key=distance) +def signature_color(image): + """Extract the image's most chromatic mid-tone color. + + Calls the colorz backend directly. It returns a list and writes nothing, + which is what keeps the pywal cache (and so the terminal) untouched. + """ + from pywal.backends import colorz + + colors = colorz.get(str(image), 16) + # Slot 0 trends near-black and the upper slots near-white; the signature + # color of an image lives in the middle. + return max(colors[1:7], key=_chroma) + + +def write_accent(name): + """Write the accent rasi file atomically.""" + hexval = ACCENTS[name] + content = ( + "/* Generated by udt-accent. Do not edit. */\n" + f"* {{ accent: {hexval}ff; }}\n" + ) + + OUTPUT.parent.mkdir(parents=True, exist_ok=True) + # Write-then-rename: a rofi launch during a wallpaper change must never + # read a half-written file. + fd, tmp = tempfile.mkstemp(dir=str(OUTPUT.parent), suffix=".tmp") + try: + with os.fdopen(fd, "w") as handle: + handle.write(content) + os.replace(tmp, OUTPUT) + except BaseException: + if os.path.exists(tmp): + os.unlink(tmp) + raise + + +def main(image): + try: + name = snap(signature_color(image)) + except Exception as exc: + # A broken image must still leave a working theme. + print(f"udt-accent: {exc}, falling back to {FALLBACK}", file=sys.stderr) + name = FALLBACK + + write_accent(name) + print(f"{name} {ACCENTS[name]}") + + def selftest(): # Every accent must snap to itself, or the metric is not self-consistent. for name, hexval in ACCENTS.items(): @@ -99,3 +152,8 @@ def selftest(): if __name__ == "__main__": if len(sys.argv) == 2 and sys.argv[1] == "--selftest": selftest() + elif len(sys.argv) == 2: + main(sys.argv[1]) + else: + print(__doc__, file=sys.stderr) + sys.exit(2) |
