- Add comprehensive screenshot gallery in README.md and docs/GALLERY.md showcasing all desktop features, menus, and control panels - Clean and sanitize screenshots (no private work projects or processes) - Add 4 curated themes with accurate color palettes and btop themes: * Pirna (Bernardo Bellotto classic art) * Nordic Frost (Arctic aurora and mountain cliff) * Monolith (Sci-fi alien coast and bioluminescent cyan) * Dragon Fire (Dark fantasy gothic siege and fiery coral embers) - Enhance theme selector in rice-theme and rice-menu with live grid thumbnails - Add super + shift + t shortcut for instant visual theme switching - Make wallpaper lookup robust across repo and user directories
177 lines
7.2 KiB
Bash
Executable file
177 lines
7.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# rice-theme - render and apply a colour theme to every surface of the rice.
|
|
#
|
|
# rice-theme set <name> render themes/<name> and reload everything
|
|
# rice-theme select open the visual theme selector
|
|
# rice-theme reload re-render the current theme (after editing a template)
|
|
# rice-theme list available themes
|
|
# rice-theme current name of the active theme
|
|
# rice-theme get <key> one value from the active colors.toml (for scripts)
|
|
#
|
|
# Templates live in $RICE_DIR/templates/*.tpl and ~/.config/rice/templates/*.tpl
|
|
# (yours win). Placeholders, for a key like accent = "#92b9ea":
|
|
# {{ accent }} #92b9ea
|
|
# {{ accent_strip }} 92b9ea
|
|
# {{ accent_rgb }} 146,185,234
|
|
# A theme can ship a finished file (e.g. themes/foo/btop.theme); it is used
|
|
# as-is instead of the template with the same output name.
|
|
set -euo pipefail
|
|
|
|
RICE_DIR="${RICE_DIR:-$(cd "$(dirname "$(readlink -f "$0")")/.." && pwd)}"
|
|
STATE="${XDG_STATE_HOME:-$HOME/.local/state}/rice"
|
|
CURRENT="$STATE/theme"
|
|
USER_TEMPLATES="${XDG_CONFIG_HOME:-$HOME/.config}/rice/templates"
|
|
HOOKS="${XDG_CONFIG_HOME:-$HOME/.config}/rice/hooks/theme-set.d"
|
|
OVERRIDE="${XDG_CONFIG_HOME:-$HOME/.config}/rice/theme.override.toml"
|
|
|
|
die() { printf 'rice-theme: %s\n' "$*" >&2; exit 1; }
|
|
|
|
theme_dir() {
|
|
local user="${XDG_CONFIG_HOME:-$HOME/.config}/rice/themes/$1"
|
|
if [[ -d $user ]]; then echo "$user"
|
|
elif [[ -d $RICE_DIR/themes/$1 ]]; then echo "$RICE_DIR/themes/$1"
|
|
else return 1; fi
|
|
}
|
|
|
|
# Emit "key<TAB>value" for every key = value line of a colors.toml.
|
|
# Quoted values are taken whole (a colour's # is not a comment); bare values
|
|
# stop at a trailing # comment.
|
|
parse_colors() {
|
|
sed -n -E \
|
|
-e 's/^[[:space:]]*([a-z_][a-z0-9_]*)[[:space:]]*=[[:space:]]*"([^"]*)".*$/\1\t\2/p' \
|
|
-e 't' \
|
|
-e 's/^[[:space:]]*([a-z_][a-z0-9_]*)[[:space:]]*=[[:space:]]*([^"#[:space:]]([^#]*[^#[:space:]])?)[[:space:]]*(#.*)?$/\1\t\2/p' \
|
|
"$1"
|
|
}
|
|
|
|
# Build a sed program that replaces every placeholder form for every key.
|
|
sed_program() {
|
|
local key value esc r g b
|
|
while IFS=$'\t' read -r key value; do
|
|
esc=$(printf '%s' "$value" | sed -e 's/[\\&|]/\\&/g')
|
|
printf 's|{{ *%s *}}|%s|g\n' "$key" "$esc"
|
|
if [[ $value =~ ^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$ ]]; then
|
|
r=$((16#${BASH_REMATCH[1]})); g=$((16#${BASH_REMATCH[2]})); b=$((16#${BASH_REMATCH[3]}))
|
|
printf 's|{{ *%s_strip *}}|%s|g\n' "$key" "${value#\#}"
|
|
printf 's|{{ *%s_rgb *}}|%s,%s,%s|g\n' "$key" "$r" "$g" "$b"
|
|
fi
|
|
done < <(parse_colors "$1" | tac | awk -F'\t' '!seen[$1]++') # a later line wins (overrides)
|
|
}
|
|
|
|
render() {
|
|
local name="$1" src out next prog tpl base
|
|
src=$(theme_dir "$name") || die "no theme called '$name' (try: rice-theme list)"
|
|
[[ -f $src/colors.toml ]] || die "$src has no colors.toml"
|
|
|
|
mkdir -p "$STATE"
|
|
next=$(mktemp -d "$STATE/.theme.XXXXXX")
|
|
cp -a "$src/." "$next/"
|
|
# Your overrides (the font from rice-font, or any colour) apply on top of every theme.
|
|
if [[ -f $OVERRIDE ]]; then
|
|
printf '\n# --- from %s ---\n' "$OVERRIDE" >> "$next/colors.toml"
|
|
cat "$OVERRIDE" >> "$next/colors.toml"
|
|
fi
|
|
prog="$next/.render.sed"
|
|
sed_program "$next/colors.toml" > "$prog"
|
|
|
|
# User templates first, so a same-named built-in is skipped.
|
|
for tpl in "$USER_TEMPLATES"/*.tpl "$RICE_DIR"/templates/*.tpl; do
|
|
[[ -f $tpl ]] || continue
|
|
base=$(basename "$tpl" .tpl)
|
|
out="$next/$base"
|
|
[[ -e $out ]] && continue
|
|
sed -f "$prog" "$tpl" > "$out"
|
|
if grep -q '{{' "$out"; then
|
|
printf 'rice-theme: %s has unknown placeholders: %s\n' "$base" \
|
|
"$(grep -o '{{[^}]*}}' "$out" | sort -u | tr '\n' ' ')" >&2
|
|
fi
|
|
done
|
|
rm -f "$prog"
|
|
|
|
rm -rf "$CURRENT.old"
|
|
[[ -d $CURRENT ]] && mv "$CURRENT" "$CURRENT.old"
|
|
mv "$next" "$CURRENT"
|
|
rm -rf "$CURRENT.old"
|
|
printf '%s\n' "$name" > "$STATE/theme.name"
|
|
}
|
|
|
|
# signal_if_caught <SIGNAL> <process-name>: send the signal only to processes
|
|
# whose SigCgt mask says they install a handler for it.
|
|
signal_if_caught() {
|
|
local sig="$1" name="$2" num pid mask
|
|
num=$(kill -l "$sig")
|
|
for pid in $(pgrep -u "$UID" -x "$name"); do
|
|
mask=$(awk '/^SigCgt:/ { print $2 }' "/proc/$pid/status" 2>/dev/null)
|
|
[[ -n $mask ]] && (( (16#$mask >> (num - 1)) & 1 )) && kill -"$sig" "$pid"
|
|
done
|
|
return 0
|
|
}
|
|
|
|
# Push the freshly rendered files into running programs. Each step is
|
|
# best-effort: a program that isn't running simply has nothing to reload.
|
|
apply() {
|
|
local t="$CURRENT"
|
|
[[ -f $t/bspwm-colors.sh ]] && pgrep -x bspwm >/dev/null && bash "$t/bspwm-colors.sh"
|
|
[[ -f $t/Xresources ]] && command -v xrdb >/dev/null && xrdb -merge "$t/Xresources"
|
|
# One cursor for X, GTK and Qt apps: the default cursor theme follows the rice's.
|
|
local cursor; cursor=$("$0" get cursor_theme 2>/dev/null)
|
|
if [[ -n $cursor ]]; then
|
|
mkdir -p "$HOME/.icons/default"
|
|
printf '[Icon Theme]\nInherits=%s\n' "$cursor" > "$HOME/.icons/default/index.theme"
|
|
fi
|
|
pgrep -x polybar >/dev/null && polybar-msg cmd restart >/dev/null 2>&1 || true
|
|
pgrep -x dunst >/dev/null && dunstctl reload >/dev/null 2>&1 || true
|
|
pgrep -x eww >/dev/null && eww reload >/dev/null 2>&1 || true
|
|
# USR1 makes kitty and the rice's st re-read their colours, but it kills a
|
|
# process that doesn't handle it (an older st build), so check first.
|
|
signal_if_caught USR1 kitty
|
|
signal_if_caught USR1 st
|
|
pgrep -x xsettingsd >/dev/null && pkill -HUP -x xsettingsd || true
|
|
if command -v flameshot >/dev/null; then
|
|
flameshot config --maincolor "$("$0" get accent)" --contrastcolor "$("$0" get bg)" \
|
|
--showhelp false --trayicon false >/dev/null 2>&1 || true
|
|
fi
|
|
if [[ -d $HOOKS ]]; then
|
|
for h in "$HOOKS"/*; do [[ -x $h ]] && "$h" "$(cat "$STATE/theme.name")" || true; done
|
|
fi
|
|
}
|
|
|
|
cmd="${1:-}"
|
|
case "$cmd" in
|
|
set)
|
|
[[ -n ${2:-} ]] || die "usage: rice-theme set <name>"
|
|
exec 9>"${XDG_RUNTIME_DIR:-/tmp}/rice-theme.lock"; flock 9
|
|
render "$2"; apply
|
|
# Only a theme change swaps the wallpaper; `reload` keeps the one you picked.
|
|
command -v rice-wallpaper >/dev/null && { rice-wallpaper theme-default >/dev/null 2>&1 || true; }
|
|
command -v notify-send >/dev/null && notify-send -a rice -u low "Theme" "$(grep -m1 '^name' "$CURRENT/colors.toml" | cut -d'"' -f2)" || true
|
|
;;
|
|
render)
|
|
# Render only, touch no running program (used by install.sh and for testing).
|
|
render "${2:-$(cat "$STATE/theme.name" 2>/dev/null || echo sakura-line)}"
|
|
;;
|
|
reload)
|
|
exec 9>"${XDG_RUNTIME_DIR:-/tmp}/rice-theme.lock"; flock 9
|
|
render "$(cat "$STATE/theme.name" 2>/dev/null || echo sakura-line)"; apply
|
|
;;
|
|
select|picker|menu)
|
|
command -v rice-menu >/dev/null && exec rice-menu theme || die "rice-menu not found"
|
|
;;
|
|
list)
|
|
{
|
|
[[ -d $RICE_DIR/themes ]] && ls -1 "$RICE_DIR/themes" || true
|
|
[[ -d ${XDG_CONFIG_HOME:-$HOME/.config}/rice/themes ]] && ls -1 "${XDG_CONFIG_HOME:-$HOME/.config}/rice/themes" || true
|
|
} 2>/dev/null | sort -u
|
|
;;
|
|
current)
|
|
cat "$STATE/theme.name" 2>/dev/null || echo sakura-line
|
|
;;
|
|
get)
|
|
[[ -n ${2:-} ]] || die "usage: rice-theme get <key>"
|
|
parse_colors "$CURRENT/colors.toml" | awk -F'\t' -v k="$2" '$1 == k { v = $2; found = 1 } END { if (found) print v; exit !found }'
|
|
;;
|
|
*)
|
|
sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'
|
|
[[ -z $cmd ]] || exit 2
|
|
;;
|
|
esac
|