#!/usr/bin/env bash # The lock screen follows the colour scheme. # # It did not. hyprlock.conf shipped with Tokyo Night Moon hardcoded in six # places, so choosing light mode left the one screen a user sees most often # stubbornly dark. Every other surface -- kitty, GTK, the launcher, btop, tmux, # neovim -- had been taught to follow the scheme; this was the last one. # # It is also the worst place to discover a theming bug, because you find out # while locked out of the machine and cannot fix it from there. Hence a test. # # Generated into a fixture, never the live config: this contract must not # retheme the lock screen of the desktop it is running on. set -uo pipefail repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" template="$repo_dir/config/dot/hypr/hyprlock.conf.template" theme_apps="$repo_dir/config/dot/quickshell/scripts/panama-theme-apps" fail() { printf 'lock screen theme contract: %s\n' "$1" >&2 exit 1 } [[ -r "$template" ]] || fail 'hyprlock.conf.template is missing, so nothing generates the lock screen' # The committed template must carry no literal colours. One left behind is a # colour that silently stays dark in light mode -- exactly the original bug. literal="$(grep -vE '^\s*#' "$template" | grep -oE 'rgba\([0-9]+, *[0-9]+, *[0-9]+' || true)" [[ -z "$literal" ]] \ || fail "the template still contains hardcoded colours, which will not follow the scheme: $literal" fixture="$(mktemp -d /tmp/panama-lockscreen.XXXXXX)" trap 'rm -rf "$fixture"' EXIT mkdir -p "$fixture/hypr" cp "$template" "$fixture/hypr/" for scheme in dark light; do XDG_CONFIG_HOME="$fixture" "$theme_apps" "$scheme" >/dev/null 2>&1 generated="$fixture/hypr/hyprlock.conf" [[ -r "$generated" ]] || fail "no hyprlock.conf was generated for $scheme" # An unsubstituted placeholder is not a parse error to hyprlock; it is an # invalid colour it quietly ignores, falling back to its own default. leftover="$(grep -oE '@[A-Z_]+@' "$generated" || true)" [[ -z "$leftover" ]] \ || fail "$scheme left placeholders unsubstituted: $leftover" # Every colour hyprlock is given must be a complete decimal triple. It # takes rgba(r, g, b, a), NOT hex, and a hex value here is silently ignored. while read -r colour; do [[ -n "$colour" ]] || continue grep -qE '^rgba\([0-9]{1,3}, [0-9]{1,3}, [0-9]{1,3}$' <<<"$colour" \ || fail "$scheme produced a malformed colour: $colour" done < <(grep -vE '^\s*#' "$generated" | grep -oE 'rgba\([^)]*' | sed 's/,[^,]*$//') # The Pango markup values are hex, and hyprlock wants them doubled-hashed. while read -r pango; do [[ -n "$pango" ]] || continue grep -qE '^##[0-9a-f]{6}$' <<<"$pango" \ || fail "$scheme produced malformed Pango markup colour: $pango" done < <(grep -vE '^\s*#' "$generated" | grep -oE '##[0-9a-fA-F]{6}') # The whole background block, not a fixed number of lines after it: the # colour sits near the end, after the blur and noise settings. background="$(awk '/^background \{/,/^\}/' "$generated" \ | grep -vE '^\s*#' | grep -oE 'color = rgba\([0-9]+' | grep -oE '[0-9]+$' | head -1)" [[ -n "$background" ]] || fail "$scheme produced no background colour" # The point of the whole exercise: a light lock screen must actually be # light. 128 splits the two cleanly for these palettes. if [[ "$scheme" == "light" ]]; then (( background > 128 )) \ || fail "light mode produced a DARK lock screen background (red channel $background) -- the original bug" else (( background < 128 )) \ || fail "dark mode produced a LIGHT lock screen background (red channel $background)" fi done # The two schemes must actually differ, or the substitution is a no-op that # passes every check above. XDG_CONFIG_HOME="$fixture" "$theme_apps" dark >/dev/null 2>&1 dark_hash="$(sha256sum "$fixture/hypr/hyprlock.conf" | cut -d' ' -f1)" XDG_CONFIG_HOME="$fixture" "$theme_apps" light >/dev/null 2>&1 light_hash="$(sha256sum "$fixture/hypr/hyprlock.conf" | cut -d' ' -f1)" [[ "$dark_hash" != "$light_hash" ]] \ || fail 'the light and dark lock screens are byte-identical, so the scheme is not being applied' printf 'lock screen theme contract: PASS\n'