Add wallpaper switch
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
# shellcheck shell=bash
|
||||
# wallpaper-pick — print a random wallpaper from $wallpapersDir whose palette
|
||||
# best matches the given theme variant. Per-image palettes are extracted once
|
||||
# (ImageMagick) and cached, keyed by the (immutable) source path so a rebuild
|
||||
# that changes the wallpaper set reindexes automatically.
|
||||
#
|
||||
# Matching uses several key colors on both sides: the variant's base16
|
||||
# base00–base05 (the background→foreground neutral range) are each matched to
|
||||
# their nearest wallpaper dominant color; the sum of those nearest distances
|
||||
# is the score. A random image is picked from the lowest-score tier.
|
||||
#
|
||||
# Env (injected by the Nix wrapper):
|
||||
# wallpapersDir directory of wallpaper images (~/Wallpapers, a git clone)
|
||||
# wallpaperRepo git URL cloned into wallpapersDir on first use
|
||||
# variantSchemes space-separated `variant:schemeFile` pairs (base16 YAML)
|
||||
# MAGICK path to the ImageMagick `magick` binary
|
||||
# GIT path to the `git` binary
|
||||
# Arg: variant name (e.g. gruvbox-dark)
|
||||
|
||||
: "${wallpapersDir:?wallpaper-pick: wallpapersDir not set}"
|
||||
: "${wallpaperRepo:?wallpaper-pick: wallpaperRepo not set}"
|
||||
: "${variantSchemes:?wallpaper-pick: variantSchemes not set}"
|
||||
: "${MAGICK:?wallpaper-pick: MAGICK not set}"
|
||||
: "${GIT:?wallpaper-pick: GIT not set}"
|
||||
set -euo pipefail
|
||||
|
||||
variant="${1:?usage: wallpaper-pick <variant>}"
|
||||
|
||||
# Ensure the wallpaper repo is cloned locally. The clone is a one-time cost
|
||||
# paid on first use (not at build time), keeping `nix build` fast.
|
||||
if [ ! -d "$wallpapersDir/.git" ]; then
|
||||
log_info "cloning wallpaper repo (one-time, may take a few minutes)..."
|
||||
mkdir -p "$(dirname "$wallpapersDir")"
|
||||
"$GIT" clone --depth=1 --single-branch "$wallpaperRepo" "$wallpapersDir" >&2
|
||||
log_info "clone ready"
|
||||
fi
|
||||
|
||||
cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/wallpaper-pick"
|
||||
palettes="$cache_dir/palettes.tsv"
|
||||
stamp_file="$cache_dir/source"
|
||||
|
||||
# Cache key: the clone's current commit, so a `git pull` reindexes. Falls back
|
||||
# to the resolved path if git fails for some reason.
|
||||
src_stamp="$("$GIT" -C "$wallpapersDir" rev-parse HEAD 2>/dev/null || readlink -f "$wallpapersDir")"
|
||||
|
||||
rebuild_cache() {
|
||||
img_count="$(find "$wallpapersDir" -type f \( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.webp' \) -printf . 2>/dev/null | wc -c)"
|
||||
log_info "indexing ${img_count} wallpapers (one-time, building palette cache)..."
|
||||
mkdir -p "$cache_dir"
|
||||
tmp="$palettes.tmp"
|
||||
: > "$tmp"
|
||||
jobs="$(nproc 2>/dev/null || echo 4)"
|
||||
|
||||
# For each image: top dominant colors (by pixel count) as space-separated #hex.
|
||||
find "$wallpapersDir" -type f \( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.webp' \) -print0 \
|
||||
| xargs -0 -P "$jobs" -n1 sh -c '
|
||||
MAGICK="$0" img="$1"
|
||||
"$MAGICK" "$img" -resize 128x128^ -gravity center -extent 128x128 +dither -colors 8 -format "%c" histogram:info: 2>/dev/null \
|
||||
| grep -E "^[[:space:]]*[0-9]+:" | sort -rn | grep -oE "#[0-9A-Fa-f]{6}" | head -6 | paste -sd" " - \
|
||||
| { read -r hex; [ -n "$hex" ] && printf "%s\t%s\n" "$img" "$hex"; }
|
||||
' "$MAGICK" >> "$tmp" || true
|
||||
|
||||
mv "$tmp" "$palettes"
|
||||
printf '%s\n' "$src_stamp" > "$stamp_file"
|
||||
}
|
||||
|
||||
if [ ! -f "$palettes" ] || [ ! -f "$stamp_file" ] || [ "$(cat "$stamp_file")" != "$src_stamp" ]; then
|
||||
rebuild_cache
|
||||
fi
|
||||
|
||||
# Resolve the base16 scheme file for the requested variant.
|
||||
scheme=""
|
||||
for pair in $variantSchemes; do
|
||||
case "$pair" in
|
||||
"${variant}":*) scheme="${pair#*:}"; break ;;
|
||||
esac
|
||||
done
|
||||
[ -n "$scheme" ] || { log_error "no scheme mapped for variant '$variant'"; exit 2; }
|
||||
|
||||
# Theme key colors: base00–base05 hex (one per line, with leading #).
|
||||
theme_colors="$(grep -E '^[[:space:]]*base0[0-5]:' "$scheme" | grep -oE '#[0-9A-Fa-f]{6}')"
|
||||
[ -n "$theme_colors" ] || { log_error "could not parse base00–05 from $scheme"; exit 2; }
|
||||
|
||||
# hex (no #) -> decimal rgb on stdout
|
||||
hex2dec() { printf '%d %d %d\n' "$((16#${1:0:2}))" "$((16#${1:2:2}))" "$((16#${1:4:2}))"; }
|
||||
|
||||
# Theme colors as parallel arrays.
|
||||
t_r=(); t_g=(); t_b=()
|
||||
while read -r h; do
|
||||
read -r r g b < <(hex2dec "${h#\#}")
|
||||
t_r+=("$r"); t_g+=("$g"); t_b+=("$b")
|
||||
done <<< "$theme_colors"
|
||||
|
||||
# Score every indexed wallpaper: sum over theme colors of nearest wallpaper color.
|
||||
scores="$cache_dir/scores.tmp"
|
||||
: > "$scores"
|
||||
while IFS=$'\t' read -r path whexes; do
|
||||
w_r=(); w_g=(); w_b=()
|
||||
for wh in $whexes; do
|
||||
read -r wr wg wb < <(hex2dec "${wh#\#}")
|
||||
w_r+=("$wr"); w_g+=("$wg"); w_b+=("$wb")
|
||||
done
|
||||
if [ "${#w_r[@]}" -eq 0 ]; then continue; fi
|
||||
total=0
|
||||
for i in "${!t_r[@]}"; do
|
||||
mind=1000000000
|
||||
for j in "${!w_r[@]}"; do
|
||||
dr=$(( t_r[i] - w_r[j] )); dg=$(( t_g[i] - w_g[j] )); db=$(( t_b[i] - w_b[j] ))
|
||||
cd=$(( dr*dr + dg*dg + db*db ))
|
||||
if [ "$cd" -lt "$mind" ]; then mind=$cd; fi
|
||||
done
|
||||
total=$(( total + mind ))
|
||||
done
|
||||
printf '%s\t%s\n' "$total" "$path"
|
||||
done < "$palettes" | sort -n -k1 > "$scores"
|
||||
|
||||
[ -s "$scores" ] || { log_error "no wallpapers indexed"; exit 3; }
|
||||
|
||||
# Matched tier: within 1.4x of the best score (fall back to closest 20).
|
||||
best="$(head -n1 "$scores" | cut -f1)"
|
||||
threshold=$(( best * 14 / 10 ))
|
||||
matched="$(awk -F'\t' -v t="$threshold" '$1 <= t {print $2}' "$scores")"
|
||||
if [ "$(printf '%s\n' "$matched" | grep -c .)" -lt 5 ]; then
|
||||
matched="$(head -n20 "$scores" | cut -f2)"
|
||||
fi
|
||||
|
||||
# Print the chosen PATH on stdout (data channel for callers) and log the
|
||||
# human-readable choice on stderr.
|
||||
chosen="$(printf '%s\n' "$matched" | shuf -n1)"
|
||||
log_info "picked $(basename "$chosen") for ${variant}"
|
||||
printf '%s\n' "$chosen"
|
||||
Reference in New Issue
Block a user