#!/usr/bin/env bash VERBOSE=0 DIR="" while [[ $# -gt 0 ]]; do case "$1" in -v|--verbose) VERBOSE=1 ;; *) DIR="$1" ;; esac shift done DIR="${DIR:-$HOME/Pictures}" LOG="" (( VERBOSE )) && LOG="/tmp/wallpaper-picker-$$.log" IMAGES=() while IFS= read -r -d '' f; do IMAGES+=("$f") done < <(find "$DIR" -maxdepth 1 -type f \ \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' \ -o -iname '*.webp' -o -iname '*.bmp' -o -iname '*.gif' \ -o -iname '*.avif' -o -iname '*.tiff' \) \ -print0 | sort -z) [[ ${#IMAGES[@]} -eq 0 ]] && { echo "No images found in $DIR"; exit 1; } INDEX=0 CURRENT_WALLPAPER="" vlog() { (( VERBOSE )) && printf '%s\n' "$*" >> "$LOG"; } icat() { kitty +kitten icat --stdin=no --silent --transfer-mode=memory "$@" 2>/dev/null; } clear_images() { icat --clear; } draw() { local cols rows cols=$(tput cols); rows=$(tput lines) local gap=2 local side_w=$(( cols * 18 / 100 )) local center_w=$(( cols - 2 * (side_w + gap) )) local center_x=$(( side_w + gap )) local next_x=$(( center_x + center_w + gap )) local img_h=$(( rows - 3 )) local n=${#IMAGES[@]} local prev=$(( (INDEX - 1 + n) % n )) local next=$(( (INDEX + 1) % n )) printf '\033[2J\033[H' clear_images icat --place "${side_w}x${img_h}@0x0" "${IMAGES[$prev]}" icat --place "${center_w}x${img_h}@${center_x}x0" "${IMAGES[$INDEX]}" icat --place "${side_w}x${img_h}@${next_x}x0" "${IMAGES[$next]}" tput cup $(( rows - 2 )) 0 printf ' [%d/%d] %s\n' $(( INDEX + 1 )) "$n" "$(basename "${IMAGES[$INDEX]}")" local hints=' h/←: prev l/→: next Enter: set wallpaper q: quit' (( VERBOSE )) && hints+=" [log: $LOG]" printf '%s' "$hints" } set_wallpaper() { local path="$1" prev="$CURRENT_WALLPAPER" local out rc vlog "=== set_wallpaper: $path ===" out=$(hyprctl hyprpaper preload "$path" 2>&1); rc=$? vlog "preload exit=$rc out=$out" # Don't bail on preload failure — image may already be preloaded local monitors_out ok=0 monitors_out=$(hyprctl monitors 2>&1) vlog "monitors: $monitors_out" while IFS= read -r mon; do out=$(hyprctl hyprpaper wallpaper "$mon,$path" 2>&1); rc=$? vlog "wallpaper $mon exit=$rc out=$out" (( rc == 0 )) && ok=1 done < <(printf '%s\n' "$monitors_out" | awk '/^Monitor /{print $2}') if (( ok == 0 )); then vlog "FAILED: no monitor set" return 1 fi CURRENT_WALLPAPER="$path" if [[ -n "$prev" && "$prev" != "$path" ]]; then out=$(hyprctl hyprpaper unload "$prev" 2>&1); rc=$? vlog "unload prev exit=$rc out=$out" fi return 0 } read_key() { local key IFS= read -rsn1 key if [[ $key == $'\x1b' ]]; then local rest IFS= read -rsn2 -t 0.1 rest key+="$rest" fi printf '%s' "$key" } old_stty=$(stty -g) trap 'stty "$old_stty"; clear_images; tput cnorm; printf "\033[2J\033[H"' EXIT stty -echo -icanon -icrnl min 1 time 0 tput civis draw while true; do key=$(read_key) case "$key" in h|$'\x1b[D') INDEX=$(( (INDEX - 1 + ${#IMAGES[@]}) % ${#IMAGES[@]} )) draw ;; l|$'\x1b[C') INDEX=$(( (INDEX + 1) % ${#IMAGES[@]} )) draw ;; $'\r'|$'\n') if set_wallpaper "${IMAGES[$INDEX]}"; then tput cup $(( $(tput lines) - 1 )) 0 printf ' Wallpaper set: %s' "$(basename "${IMAGES[$INDEX]}")" else tput cup $(( $(tput lines) - 1 )) 0 printf ' Failed — is hyprpaper running?' (( VERBOSE )) && printf ' [see %s]' "$LOG" fi ;; q|Q) break ;; esac done