-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpywal-theme-setup.sh
More file actions
executable file
·539 lines (479 loc) · 19.9 KB
/
Copy pathpywal-theme-setup.sh
File metadata and controls
executable file
·539 lines (479 loc) · 19.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
#!/usr/bin/env bash
# =============================================================================
# pywal-theme-setup.sh
#
# One-shot installer that replicates Enjo's pywal rice on ANY DE/WM:
# • Checks if pywal is installed → installs it if missing
# (Arch: yay/paru from AUR · everything else: pipx or pip --user)
# • Sets up user-level pywal access (~/.config/wal/templates, ~/.cache/wal)
# • Installs the `done` hook so wal auto-reloads bars/notifications
# • Optionally installs awww-daemon (AUR) on Wayland for nicer wallpapers
# • Installs the `wall` command (~/.local/bin/set-theme) that:
# wall /path/to/wallpaper.jpg
# sets the wallpaper, runs pywal, and re-themes Waybar, Rofi, Kitty,
# the compositor, the panel and every open terminal.
# It always checks for awww-daemon first (auto-starts it if needed) and
# otherwise picks the right setter for your DE/WM (GNOME, KDE, XFCE,
# MATE, Cinnamon, Hyprland, Sway, River, Wayfire, i3, bspwm, awesome…).
# • Wires the shell (zsh/bash) with the `wall` alias + pywal sequences
# • Patches Waybar / Rofi / Kitty configs to import the generated colors
#
# Usage:
# ./pywal-theme-setup.sh # full setup
# ./pywal-theme-setup.sh --no-install # setup only, never install anything
#
# After it finishes, open a NEW terminal and run:
# wall ~/Pictures/your-wallpaper.jpg
# =============================================================================
set -euo pipefail
# pip/pipx put binaries here — export early so we can detect them after install
export PATH="$HOME/.local/bin:$PATH"
NO_INSTALL="${1:-}"
CACHE_DIR="$HOME/.cache/wal"
CONFIG_WAL="$HOME/.config/wal"
TEMPLATE_DIR="$HOME/.config/wal/templates"
BIN_DIR="$HOME/.local/bin"
SET_THEME="$BIN_DIR/set-theme"
green() { printf '\033[1;32m%s\033[0m\n' "$*"; }
yellow() { printf '\033[1;33m%s\033[0m\n' "$*"; }
red() { printf '\033[1;31m%s\033[0m\n' "$*"; }
info() { printf '\033[1;34m%s\033[0m\n' "$*"; }
# ----------------------------------------------------------------------------
# 1. Make sure pywal exists
# ----------------------------------------------------------------------------
install_pywal_pip() {
if command -v pipx >/dev/null 2>&1; then
info "Installing pywal with pipx..."
pipx install pywal
elif command -v pip3 >/dev/null 2>&1; then
info "Installing pywal with pip3 --user..."
pip3 install --user pywal
elif command -v python3 >/dev/null 2>&1; then
info "Installing pywal with python3 -m pip --user..."
python3 -m pip install --user pywal
else
red "No pip/pipx/python3 found. Install python-pip first, then re-run."
exit 1
fi
}
install_pywal() {
if command -v wal >/dev/null 2>&1; then
info "pywal already installed → $(command -v wal)"
return 0
fi
if [ "$NO_INSTALL" = "--no-install" ]; then
red "pywal is not installed and --no-install was given. Aborting."
exit 1
fi
yellow "pywal not found — installing it..."
if command -v pacman >/dev/null 2>&1; then
if command -v yay >/dev/null 2>&1; then
info "Arch + yay → installing python-pywal from AUR"
yay -S --needed --noconfirm python-pywal
elif command -v paru >/dev/null 2>&1; then
info "Arch + paru → installing python-pywal from AUR"
paru -S --needed --noconfirm python-pywal
else
yellow "No AUR helper (yay/paru) found — falling back to pip."
install_pywal_pip
fi
else
install_pywal_pip
fi
if ! command -v wal >/dev/null 2>&1; then
red "pywal still not found on PATH. If installed via pip, re-run after:"
red " source ~/.zshrc (or ~/.bashrc)"
exit 1
fi
green "pywal installed → $(command -v wal)"
}
# ----------------------------------------------------------------------------
# 1b. Optional awww-daemon (Wayland layer-shell wallpaper daemon)
# ----------------------------------------------------------------------------
setup_awww() {
if command -v awww-daemon >/dev/null 2>&1; then
info "awww-daemon available → wall will prefer it on Wayland."
return 0
fi
[ "$NO_INSTALL" = "--no-install" ] && return 0
# Only relevant on a Wayland session
case "${XDG_SESSION_TYPE:-}" in
wayland) ;;
*) return 0 ;;
esac
local helper=""
command -v yay >/dev/null 2>&1 && helper=yay
command -v paru >/dev/null 2>&1 && helper=paru
[ -z "$helper" ] && { yellow "skip: awww-daemon is AUR-only and no yay/paru found."; return 0; }
read -r -p "Wayland detected. Install awww-daemon (AUR) for nicer wallpapers? [y/N] " ans
case "$ans" in
y|Y|yes|YES)
info "Installing awww from AUR..."
"$helper" -S --needed --noconfirm awww
green "awww-daemon installed."
;;
*) yellow "skipped awww-daemon." ;;
esac
}
# ----------------------------------------------------------------------------
# 2. User-level pywal access
# ----------------------------------------------------------------------------
setup_dirs() {
mkdir -p "$CONFIG_WAL" "$TEMPLATE_DIR" "$CACHE_DIR" "$BIN_DIR"
info "Directories ready: $CONFIG_WAL, $TEMPLATE_DIR, $CACHE_DIR"
# Custom templates go here; pywal 3.3 picks them up automatically.
}
# ----------------------------------------------------------------------------
# 3. wal done-hook (runs automatically after every `wal` call)
# DE/WM-agnostic: only touches what is actually running
# ----------------------------------------------------------------------------
write_done_hook() {
cat > "$CONFIG_WAL/done" <<'DONE_EOF'
#!/bin/bash
# Pywal done hook — auto-reloads your bar/notifications/compositor after wal runs.
# Triggered automatically by: wal -i <image> -q
CACHE="$HOME/.cache/wal"
HYPRLAND_CONF="$HOME/.config/hypr/hyprland.conf"
have() { command -v "$1" >/dev/null 2>&1; }
is_running() { pgrep -x "$1" >/dev/null 2>&1; }
# Generate Hyprland variables + patch borders (only if hyprland config exists)
if [ -f "$CACHE/colors-waybar.css" ]; then
ACCENT=$(awk '/@define-color color4/{gsub(/;/,""); print $3}' "$CACHE/colors-waybar.css")
INACTIVE=$(awk '/@define-color color0/{gsub(/;/,""); print $3}' "$CACHE/colors-waybar.css")
FG=$(awk '/@define-color foreground/{gsub(/;/,""); print $3}' "$CACHE/colors-waybar.css")
cat > "$CACHE/colors-hyprland.conf" <<EOF
\$accent_color = rgb(${ACCENT#\#})
\$inactive_color = rgb(${INACTIVE#\#})
\$foreground = rgb(${FG#\#})
EOF
if [ -f "$HYPRLAND_CONF" ]; then
sed -i "s|^ col.active_border = .*| col.active_border = rgb(${ACCENT#\#})|" "$HYPRLAND_CONF"
sed -i "s|^ col.inactive_border = .*| col.inactive_border = rgb(${INACTIVE#\#})|" "$HYPRLAND_CONF"
fi
fi
# Broadcast colors to every open terminal
for tty in /dev/pts/*; do
if [ -w "$tty" ]; then
cat "$CACHE/sequences" > "$tty" 2>/dev/null || true
fi
done
# Reload Waybar
if is_running waybar; then
killall waybar 2>/dev/null || true
sleep 0.2
waybar & disown
fi
# Reload Polybar (X11)
if is_running polybar; then
pkill -x polybar 2>/dev/null || true
sleep 0.2
if [ -f "$HOME/.config/polybar/launch.sh" ]; then
"$HOME/.config/polybar/launch.sh" & disown
elif [ -f "$HOME/.config/polybar/config.ini" ]; then
polybar main & disown
fi
fi
# Reload SwayNC (timeout: swaync-client blocks forever without a session bus)
if is_running swaync; then
timeout 5 swaync-client -rs 2>/dev/null || true
fi
# Reload XFCE panel
if is_running xfce4-panel; then
xfce4-panel -r 2>/dev/null || true
fi
# Reload KDE Plasma shell (re-reads colors)
if is_running plasmashell; then
killall plasmashell 2>/dev/null || true
plasmashell & disown
fi
# Reload the compositor (Hyprland / Sway)
have hyprctl && hyprctl reload 2>/dev/null || true
have swaymsg && swaymsg reload 2>/dev/null || true
DONE_EOF
chmod +x "$CONFIG_WAL/done"
green "done-hook installed → $CONFIG_WAL/done"
}
# ----------------------------------------------------------------------------
# 4. The `wall` command itself (DE/WM-agnostic)
# ----------------------------------------------------------------------------
write_set_theme() {
cat > "$SET_THEME" <<'SETTHEME_EOF'
#!/bin/bash
# set-theme — apply a wallpaper and sync pywal colors to any DE/WM.
#
# Works on: Hyprland, Sway, River, Wayfire, GNOME, KDE Plasma, XFCE,
# MATE, Cinnamon, i3, bspwm, awesome, and any Wayland/X11 setup.
#
# Wallpaper logic: always checks for awww-daemon first (and auto-starts it),
# then falls back to the setter for your desktop / compositor / toolkit.
#
# Usage: set-theme /path/to/wallpaper.png (aliased as `wall`)
set -euo pipefail
WALLPAPER="${1:-}"
CACHE="$HOME/.cache/wal"
HYPRLAND_CONF="$HOME/.config/hypr/hyprland.conf"
HYPRPAPER_CONF="$HOME/.config/hypr/hyprpaper.conf"
HYPRLOCK_CONF="$HOME/.config/hypr/hyprlock.conf"
if [ -z "$WALLPAPER" ] || [ ! -f "$WALLPAPER" ]; then
echo "Usage: set-theme <wallpaper-path>"
exit 1
fi
have() { command -v "$1" >/dev/null 2>&1; }
is_running() { pgrep -x "$1" >/dev/null 2>&1; }
# ── Detect session type (wayland / x11) ─────────────────────────────────────
if [ -n "${XDG_SESSION_TYPE:-}" ]; then
SESSION_TYPE="$(echo "$XDG_SESSION_TYPE" | tr '[:upper:]' '[:lower:]')"
elif [ -n "${WAYLAND_DISPLAY:-}" ]; then
SESSION_TYPE=wayland
elif [ -n "${DISPLAY:-}" ]; then
SESSION_TYPE=x11
else
SESSION_TYPE=""
fi
# ── Detect desktop environment ──────────────────────────────────────────────
DE=""
if [ -n "${XDG_CURRENT_DESKTOP:-}" ]; then
DE="$(echo "$XDG_CURRENT_DESKTOP" | tr '[:upper:]' '[:lower:]')"
fi
case "$DE" in
*gnome*) DE=gnome ;;
*kde*|*plasma*) DE=kde ;;
*xfce*) DE=xfce ;;
*mate*) DE=mate ;;
*cinnamon*) DE=cinnamon ;;
*hyprland*) DE=hyprland ;;
*sway*) DE=sway ;;
*river*) DE=river ;;
*wayfire*) DE=wayfire ;;
*)
# Fall back to what is actually running
if is_running Hyprland; then DE=hyprland
elif is_running sway; then DE=sway
elif is_running river; then DE=river
elif is_running wayfire; then DE=wayfire
elif is_running gnome-shell; then DE=gnome
elif is_running plasmashell; then DE=kde
elif is_running xfce4-session; then DE=xfce
elif is_running mate-session; then DE=mate
elif is_running cinnamon; then DE=cinnamon
elif is_running i3; then DE=i3
elif is_running bspwm; then DE=bspwm
elif is_running awesome; then DE=awesome
fi
;;
esac
# ── 1. Set the wallpaper ─────────────────────────────────────────────────────
set_wallpaper() {
local img="$1"
# awww-daemon — always preferred on Wayland when installed.
# Auto-start the daemon if it is not already running.
if [ "$SESSION_TYPE" = wayland ] && have awww-daemon; then
if ! is_running awww-daemon; then
awww-daemon & disown
sleep 1
fi
awww img "$img" --transition-type simple 2>/dev/null && return 0
fi
case "$DE" in
hyprland)
have hyprctl && { hyprctl hyprpaper wallpaper ",$img" 2>/dev/null && return 0; }
;;
gnome)
have gsettings && {
gsettings set org.gnome.desktop.background picture-uri "file://$img" 2>/dev/null
gsettings set org.gnome.desktop.background picture-uri-dark "file://$img" 2>/dev/null
gsettings set org.gnome.desktop.background picture-options "zoom" 2>/dev/null
return 0
}
;;
kde)
have plasma-apply-wallpaperimage && {
plasma-apply-wallpaperimage "$img" 2>/dev/null && return 0
}
;;
xfce)
if have xfconf-query; then
for prop in $(xfconf-query -c xfce4-desktop -l 2>/dev/null | grep -E 'last-image|image-path'); do
case "$prop" in
*last-image) xfconf-query -c xfce4-desktop -p "$prop" -s "$img" 2>/dev/null || true ;;
*image-path) xfconf-query -c xfce4-desktop -p "$prop" -s "$(dirname "$img")" 2>/dev/null || true ;;
esac
done
return 0
fi
;;
mate)
have gsettings && {
gsettings set org.mate.background picture-filename "$img" 2>/dev/null
return 0
}
;;
cinnamon)
have gsettings && {
gsettings set org.cinnamon.desktop.background picture-uri "file://$img" 2>/dev/null
gsettings set org.cinnamon.desktop.background picture-options "zoom" 2>/dev/null
return 0
}
;;
esac
# Wayland compositors without awww
if [ "$SESSION_TYPE" = wayland ]; then
if have swww; then
swww img "$img" --transition-type simple 2>/dev/null && return 0
fi
if have swaybg; then
killall swaybg 2>/dev/null || true
swaybg -i "$img" -m fill & disown
return 0
fi
fi
# X11 tooling (i3, bspwm, awesome, openbox, …)
have feh && { feh --bg-fill "$img" 2>/dev/null && return 0; }
have nitrogen && { nitrogen --set-zoom-fill "$img" 2>/dev/null && return 0; }
have hsetroot && { hsetroot -fill "$img" 2>/dev/null && return 0; }
have xwallpaper && { xwallpaper --zoom "$img" 2>/dev/null && return 0; }
# Last resort: let pywal set it itself
wal -i "$img" -q 2>/dev/null || true
return 0
}
set_wallpaper "$WALLPAPER"
# ── 2. Keep hyprpaper + hyprlock configs in sync (only if they exist) ───────
[ -f "$HYPRPAPER_CONF" ] && sed -i "s|^preload = .*|preload = $WALLPAPER|" "$HYPRPAPER_CONF"
[ -f "$HYPRPAPER_CONF" ] && sed -i "s|^wallpaper = .*|wallpaper = ,$WALLPAPER|" "$HYPRPAPER_CONF"
[ -f "$HYPRLOCK_CONF" ] && sed -i "s|^ path = .*| path = $WALLPAPER|" "$HYPRLOCK_CONF"
# ── 3. Run pywal (skip its own wallpaper setter with -n; done-hook fires) ───
wal -i "$WALLPAPER" -n -q || true
# ── 4. Broadcast color sequences to open terminals ──────────────────────────
for tty in /dev/pts/*; do
if [ -w "$tty" ]; then
cat "$CACHE/sequences" > "$tty" 2>/dev/null || true
fi
done
# ── 5. Extract accent colors from the wal cache ─────────────────────────────
ACCENT=$(awk '/@define-color color4/{gsub(/;/,""); print $3}' "$CACHE/colors-waybar.css" 2>/dev/null || true)
INACTIVE=$(awk '/@define-color color0/{gsub(/;/,""); print $3}' "$CACHE/colors-waybar.css" 2>/dev/null || true)
FG=$(awk '/@define-color foreground/{gsub(/;/,""); print $3}' "$CACHE/colors-waybar.css" 2>/dev/null || true)
# ── 6. Generate Hyprland variables (used if you source this file) ───────────
cat > "$CACHE/colors-hyprland.conf" <<EOF
\$accent_color = rgb(${ACCENT#\#})
\$inactive_color = rgb(${INACTIVE#\#})
\$foreground = rgb(${FG#\#})
EOF
# ── 7. Patch Hyprland borders (only if the config exists) ───────────────────
if [ -f "$HYPRLAND_CONF" ]; then
sed -i "s|^ col.active_border = .*| col.active_border = rgb(${ACCENT#\#})|" "$HYPRLAND_CONF"
sed -i "s|^ col.inactive_border = .*| col.inactive_border = rgb(${INACTIVE#\#})|" "$HYPRLAND_CONF"
fi
# ── 8. Reload the bar / notifications / panel / compositor ──────────────────
if is_running waybar; then
killall waybar 2>/dev/null || true
sleep 0.2
waybar & disown
fi
if is_running polybar; then
pkill -x polybar 2>/dev/null || true
sleep 0.2
if [ -f "$HOME/.config/polybar/launch.sh" ]; then
"$HOME/.config/polybar/launch.sh" & disown
elif [ -f "$HOME/.config/polybar/config.ini" ]; then
polybar main & disown
fi
fi
if is_running swaync; then
timeout 5 swaync-client -rs 2>/dev/null || true
fi
if is_running xfce4-panel; then
xfce4-panel -r 2>/dev/null || true
fi
if [ "$DE" = kde ] && is_running plasmashell; then
killall plasmashell 2>/dev/null || true
plasmashell & disown
fi
have hyprctl && hyprctl reload 2>/dev/null || true
have swaymsg && swaymsg reload 2>/dev/null || true
echo "Theme applied: $WALLPAPER"
echo " desktop = ${DE:-unknown} (${SESSION_TYPE:-unknown})"
echo " active_border = rgb(${ACCENT#\#})"
echo " inactive_border= rgb(${INACTIVE#\#})"
echo " foreground = rgb(${FG#\#})"
SETTHEME_EOF
chmod +x "$SET_THEME"
green "wall command installed → $SET_THEME"
}
# ----------------------------------------------------------------------------
# 5. Point Waybar / Rofi / Kitty at the generated pywal colors
# ----------------------------------------------------------------------------
append_if_missing() { # file line
local file="$1" line="$2"
grep -qF "$line" "$file" || printf '%s\n' "$line" >> "$file"
}
patch_waybar() {
local ws="$HOME/.config/waybar/style.css"
[ -f "$ws" ] || { yellow "skip: no $ws"; return 0; }
grep -qF "colors-waybar.css" "$ws" \
|| sed -i "1i @import \"$HOME/.cache/wal/colors-waybar.css\";" "$ws"
green "waybar → imports pywal colors in $ws"
}
patch_rofi() {
local rc="$HOME/.config/rofi/config.rasi"
[ -f "$rc" ] || { yellow "skip: no $rc"; return 0; }
append_if_missing "$rc" "@import \"$HOME/.cache/wal/colors-rofi-dark.rasi\""
green "rofi → imports pywal colors in $rc"
}
patch_kitty() {
local kc="$HOME/.config/kitty/kitty.conf"
[ -f "$kc" ] || { yellow "skip: no $kc"; return 0; }
append_if_missing "$kc" "include ~/.cache/wal/colors-kitty.conf"
green "kitty → includes pywal colors in $kc"
}
# ----------------------------------------------------------------------------
# 6. Wire up the shell (zsh + bash)
# ----------------------------------------------------------------------------
patch_rc() {
local rc="$1"
[ -f "$rc" ] || touch "$rc"
append_if_missing "$rc" 'export PATH="$HOME/.local/bin:$PATH"'
append_if_missing "$rc" 'cat ~/.cache/wal/sequences 2>/dev/null'
append_if_missing "$rc" 'alias wall="set-theme"'
green "shell → $(basename "$rc") wired (wall alias + live pywal colors)"
}
patch_shell() {
[ -f "$HOME/.zshrc" ] && patch_rc "$HOME/.zshrc"
[ -f "$HOME/.bashrc" ] && patch_rc "$HOME/.bashrc"
if [ ! -f "$HOME/.zshrc" ] && [ ! -f "$HOME/.bashrc" ]; then
case "${SHELL##*/}" in
zsh) patch_rc "$HOME/.zshrc" ;;
bash) patch_rc "$HOME/.bashrc" ;;
*) patch_rc "$HOME/.bashrc" ;;
esac
fi
}
# ----------------------------------------------------------------------------
# main
# ----------------------------------------------------------------------------
main() {
echo
info "┌──────────────────────────────────────────────┐"
info "│ pywal setup — any DE / any WM │"
info "└──────────────────────────────────────────────┘"
echo
install_pywal
setup_awww
setup_dirs
write_done_hook
write_set_theme
patch_waybar
patch_rofi
patch_kitty
patch_shell
echo
green "✓ Setup complete!"
info " Open a NEW terminal (or run: source ~/.zshrc) and use:"
green " wall /path/to/your/wallpaper.jpg"
echo
info " Wallpaper: awww-daemon (Wayland) → your DE/WM setter → pywal"
info " Themed: Waybar/Polybar · Rofi · Kitty · panel · terminals ·"
info " compositor borders (Hyprland/Sway)"
echo
}
main