aurorae3b4e57
aurora: own the wallpaper, setwall becomes an IPC wrapper
+62 −77
6 files changed
- M .config/hypr/hyprland.lua +1 −1
- M .config/quickshell/aurora/Settings.qml +1 −0
- M .config/quickshell/aurora/Wall.qml +28 −10
- M .config/quickshell/aurora/shell.qml +1 −0
- M .local/bin/setwall +30 −65
- M README.md +1 −1
| @@ -26,7 +26,7 @@ hl.on("hyprland.start", function() | |||
| 26 | 26 | hl.exec_cmd("gammastep") | |
| 27 | 27 | hl.exec_cmd("hypridle") | |
| 28 | 28 | -- hl.exec_cmd("blueman-applet") | |
| 29 | − | hl.exec_cmd(os.getenv("HOME") .. "/.local/bin/setwall -n") | |
| 29 | + | hl.exec_cmd("hyprpaper") -- wallpaper is set by aurora (Wall.qml), it reads hyprpaper.conf | |
| 30 | 30 | hl.exec_cmd("systemctl --user start hyprpolkitagent") | |
| 31 | 31 | end) | |
| 32 | 32 | ||
| @@ -42,6 +42,7 @@ Singleton { | |||
| 42 | 42 | FileView { | |
| 43 | 43 | id: fv | |
| 44 | 44 | path: root.file | |
| 45 | + | blockLoading: true // loaded before first use (e.g. Wall.random() right after startup) | |
| 45 | 46 | watchChanges: true | |
| 46 | 47 | onFileChanged: reload() | |
| 47 | 48 | onAdapterUpdated: writeAdapter() | |
| @@ -3,15 +3,16 @@ import QtQuick | |||
| 3 | 3 | import Quickshell | |
| 4 | 4 | import Quickshell.Io | |
| 5 | 5 | ||
| 6 | − | // Wallpapers: folder listing, cached thumbnails, apply through ~/.local/bin/setwall | |
| 7 | − | // (setwall writes hyprpaper.conf, switches hyprpaper live and runs `wal -i`; Theme then recolours itself). | |
| 6 | + | // Wallpapers: folder listing, cached thumbnails, and applying them. aurora owns the wallpaper: | |
| 7 | + | // apply() writes hyprpaper.conf, switches hyprpaper live and runs `wal -i` (Theme then recolours itself). | |
| 8 | + | // ~/.local/bin/setwall is only a CLI front-end for the IPC calls (wallrandom / wall / wallset). | |
| 8 | 9 | Singleton { | |
| 9 | 10 | id: root | |
| 10 | 11 | ||
| 11 | 12 | readonly property string home: Quickshell.env("HOME") | |
| 12 | 13 | property string rootDir: home + "/pix/wallpapers" | |
| 13 | − | property string folder: Settings.wallFolder // default dusky-dark, same as setwall's WALL_DIR | |
| 14 | − | readonly property string setwall: home + "/.local/bin/setwall" | |
| 14 | + | property string folder: Settings.wallFolder // default dusky-dark | |
| 15 | + | readonly property string conf: home + "/.config/hypr/hyprpaper.conf" | |
| 15 | 16 | readonly property string thumbDir: home + "/.cache/aurora/thumbs" | |
| 16 | 17 | ||
| 17 | 18 | property var folders: [] // subfolder names; "" = everything under rootDir | |
| @@ -19,6 +20,7 @@ Singleton { | |||
| 19 | 20 | property var ready: ({}) // md5(path) -> true once its thumbnail exists | |
| 20 | 21 | property string current: "" // wallpaper hyprpaper is showing | |
| 21 | 22 | property bool loading: false | |
| 23 | + | property bool pendingRandom: false // random() asked before the folder was listed | |
| 22 | 24 | ||
| 23 | 25 | function dirOf(name) { return name === "" ? rootDir : rootDir + "/" + name } | |
| 24 | 26 | function thumbOf(path) { return thumbDir + "/" + Qt.md5(path) + ".jpg" } | |
| @@ -37,15 +39,29 @@ Singleton { | |||
| 37 | 39 | "-regextype", "posix-extended", "-iregex", ".*\\.(png|jpe?g|webp)"] | |
| 38 | 40 | listProc.running = true | |
| 39 | 41 | } | |
| 42 | + | // hyprpaper.conf is the source of truth for the current wallpaper (hyprpaper reads it on start). | |
| 43 | + | // If hyprpaper isn't running, let Hyprland start it so it doesn't live in aurora's service cgroup. | |
| 44 | + | readonly property string applyJob: | |
| 45 | + | 'p="$1"; c="$2"; ' + | |
| 46 | + | 'printf "splash = false\\nwallpaper {\\n monitor =\\n path = %s\\n}\\n" "$p" > "$c"; ' + | |
| 47 | + | 'if pgrep -x hyprpaper >/dev/null; then hyprctl hyprpaper wallpaper ",$p"; ' + | |
| 48 | + | 'else hyprctl dispatch \'hl.dsp.exec_cmd("hyprpaper")\'; fi; ' + | |
| 49 | + | 'wal -i "$p" -n' | |
| 40 | 50 | function apply(path) { | |
| 41 | 51 | if (!path) return | |
| 42 | 52 | current = path | |
| 43 | − | Quickshell.execDetached([setwall, "-p", path]) | |
| 53 | + | Quickshell.execDetached(["sh", "-c", applyJob, "sh", path, conf]) | |
| 44 | 54 | } | |
| 45 | − | // random from the folder shown last; before the first listing let setwall pick (its own WALL_DIR) | |
| 55 | + | // random from the picker's folder (Settings.wallFolder), never the one already shown | |
| 46 | 56 | function random() { | |
| 47 | − | if (files.length) apply(files[Math.floor(Math.random() * files.length)]) | |
| 48 | − | else Quickshell.execDetached([setwall]) | |
| 57 | + | if (!files.length) { | |
| 58 | + | pendingRandom = true | |
| 59 | + | if (!loading) listFolder(folder) | |
| 60 | + | return | |
| 61 | + | } | |
| 62 | + | var pick = files.filter(f => f !== current) | |
| 63 | + | if (!pick.length) pick = files | |
| 64 | + | apply(pick[Math.floor(Math.random() * pick.length)]) | |
| 49 | 65 | } | |
| 50 | 66 | ||
| 51 | 67 | Process { | |
| @@ -73,6 +89,7 @@ Singleton { | |||
| 73 | 89 | out.sort() | |
| 74 | 90 | root.files = out | |
| 75 | 91 | root.loading = false | |
| 92 | + | if (root.pendingRandom) { root.pendingRandom = false; if (out.length) root.random() } | |
| 76 | 93 | root.makeThumbs() | |
| 77 | 94 | } | |
| 78 | 95 | } | |
| @@ -115,11 +132,12 @@ Singleton { | |||
| 115 | 132 | } | |
| 116 | 133 | } | |
| 117 | 134 | ||
| 118 | − | // current wallpaper, straight from the config setwall writes | |
| 135 | + | // current wallpaper, straight from hyprpaper.conf. None yet (fresh install): pick a random one. | |
| 119 | 136 | FileView { | |
| 120 | − | path: root.home + "/.config/hypr/hyprpaper.conf" | |
| 137 | + | path: root.conf | |
| 121 | 138 | watchChanges: true | |
| 122 | 139 | onFileChanged: reload() | |
| 140 | + | onLoadFailed: root.random() | |
| 123 | 141 | onLoaded: { | |
| 124 | 142 | var m = text().match(/^\s*path\s*=\s*(.+)$/m) | |
| 125 | 143 | if (m) root.current = m[1].trim() | |
| @@ -152,6 +152,7 @@ ShellRoot { | |||
| 152 | 152 | function clip(): void { root.toggleOverlay("clip") } | |
| 153 | 153 | function wall(): void { root.toggleOverlay("wall") } | |
| 154 | 154 | function wallrandom(): void { Wall.random() } | |
| 155 | + | function wallset(path: string): void { Wall.apply(path) } // absolute path (setwall resolves it) | |
| 155 | 156 | function rec(mode: string): void { root.close(); Rec.toggle(mode === "region" ? "region" : "screen") } // screen (default) | region | |
| 156 | 157 | function caffeine(): void { Toggles.caffeine = !Toggles.caffeine } | |
| 157 | 158 | function bright(dir: string): void { Bright.step(dir === "down" ? -1 : 1) } // up | down | |
| @@ -1,71 +1,36 @@ | |||
| 1 | − | #!/bin/bash | |
| 2 | − | WALL_DIR="$HOME/pix/wallpapers/dusky-dark/" | |
| 3 | − | MODE="random" | |
| 4 | − | CUSTOM_PATH="" | |
| 1 | + | #!/bin/sh | |
| 2 | + | # Set the wallpaper through aurora. aurora owns the wallpaper: it writes hyprpaper.conf, | |
| 3 | + | # switches hyprpaper live and runs pywal. This is just the CLI front-end for its IPC. | |
| 4 | + | # | |
| 5 | + | # setwall random wallpaper from the folder selected in aurora's picker | |
| 6 | + | # setwall -m open (toggle) aurora's wallpaper picker | |
| 7 | + | # setwall [-p] FILE set FILE as the wallpaper | |
| 5 | 8 | ||
| 6 | − | apply_wallpaper() { | |
| 7 | − | # Apply wallpaper | |
| 8 | − | if ! pgrep -x hyprpaper >/dev/null; then | |
| 9 | − | # Hyprpaper not running → start with chosen wallpaper | |
| 10 | − | cat > ~/.config/hypr/hyprpaper.conf <<EOF | |
| 11 | − | splash = false | |
| 12 | − | wallpaper { | |
| 13 | − | monitor = | |
| 14 | − | path = $NEW_WALL | |
| 9 | + | usage() { | |
| 10 | + | echo "Usage: setwall [-m] [[-p] /path/to/image]" >&2 | |
| 11 | + | exit 1 | |
| 15 | 12 | } | |
| 16 | − | EOF | |
| 17 | − | hyprpaper & | |
| 18 | − | wal -i "$NEW_WALL" -n | |
| 19 | − | else | |
| 20 | − | # Hyprpaper is running → change it live | |
| 21 | − | cat > ~/.config/hypr/hyprpaper.conf <<EOF | |
| 22 | − | splash = false | |
| 23 | − | wallpaper { | |
| 24 | − | monitor = | |
| 25 | − | path = $NEW_WALL | |
| 26 | − | } | |
| 27 | − | EOF | |
| 28 | − | hyprctl hyprpaper wallpaper ",$NEW_WALL" | |
| 29 | − | wal -i "$NEW_WALL" -n | |
| 30 | − | fi | |
| 31 | 13 | ||
| 14 | + | # `qs ipc call` exits 0 even when it fails, but void calls print nothing on success | |
| 15 | + | ipc() { | |
| 16 | + | out=$(qs -c aurora ipc call shell "$@" 2>&1) && [ -z "$out" ] && return | |
| 17 | + | echo "setwall: aurora: ${out:-not running}" >&2 | |
| 18 | + | echo " (is it running? systemctl --user start aurora)" >&2 | |
| 19 | + | exit 1 | |
| 32 | 20 | } | |
| 33 | 21 | ||
| 34 | − | # Parse options | |
| 35 | − | while getopts ":mnp:" opt; do | |
| 36 | − | case $opt in | |
| 37 | − | m) MODE="menu" ;; | |
| 38 | − | p) MODE="path"; CUSTOM_PATH="$(realpath "$OPTARG")" ;; | |
| 39 | − | n) MODE="nc" ;; | |
| 40 | − | \?) echo "Usage: $0 [-m] [-p /path/to/image] [-n]" >&2; exit 1 ;; | |
| 41 | − | :) echo "Option -$OPTARG requires an argument." >&2; exit 1 ;; | |
| 42 | − | esac | |
| 43 | − | done | |
| 44 | − | ||
| 45 | − | # Choose wallpaper | |
| 46 | − | case $MODE in | |
| 47 | − | random) | |
| 48 | − | NEW_WALL=$(find "$WALL_DIR" -type f | shuf -n 1) | |
| 49 | − | if [[ -z "$NEW_WALL" ]]; then | |
| 50 | − | NEW_WALL="$HOME/.config/hypr/default-wallpaper.png" | |
| 51 | − | fi | |
| 52 | − | apply_wallpaper | |
| 53 | − | ;; | |
| 54 | − | path) | |
| 55 | − | if [[ -f "$CUSTOM_PATH" ]]; then | |
| 56 | − | NEW_WALL="$CUSTOM_PATH" | |
| 57 | − | else | |
| 58 | − | echo "Error: File not found -> $CUSTOM_PATH" >&2 | |
| 59 | − | exit 1 | |
| 60 | − | fi | |
| 61 | − | apply_wallpaper | |
| 62 | − | ;; | |
| 63 | − | menu) | |
| 64 | − | NEW_WALL=$(nsxiv -tfpo $WALL_DIR) | |
| 65 | − | apply_wallpaper | |
| 66 | − | ;; | |
| 67 | − | nc) | |
| 68 | − | echo "Not changing anything" | |
| 69 | − | pgrep 'hyprpaper' || hyprpaper & | |
| 70 | − | ;; | |
| 22 | + | case "$1" in | |
| 23 | + | "") ipc wallrandom ;; | |
| 24 | + | -m) ipc wall ;; | |
| 25 | + | -p) [ -n "$2" ] || usage; file="$2" ;; | |
| 26 | + | -h|-*) usage ;; | |
| 27 | + | *) file="$1" ;; | |
| 71 | 28 | esac | |
| 29 | + | ||
| 30 | + | if [ -n "$file" ]; then | |
| 31 | + | if [ ! -f "$file" ]; then | |
| 32 | + | echo "setwall: file not found -> $file" >&2 | |
| 33 | + | exit 1 | |
| 34 | + | fi | |
| 35 | + | ipc wallset "$(realpath "$file")" | |
| 36 | + | fi | |
| @@ -64,7 +64,7 @@ The lists live in `packages/*.txt`: one package per line, `#` for comments, and | |||
| 64 | 64 | |---|---| | |
| 65 | 65 | | WM | Hyprland (Lua config), hypridle, hyprlock, hyprpaper | | |
| 66 | 66 | | Shell / bar | aurora (Quickshell) | | |
| 67 | − | | Theming | pywal16 (`setwall` picks a wallpaper and re-themes everything) | | |
| 67 | + | | Theming | pywal16: picking a wallpaper in aurora (or `setwall`) re-themes everything | | |
| 68 | 68 | | Terminal / shell | foot, zsh | | |
| 69 | 69 | | Editor | Neovim (LazyVim) | | |
| 70 | 70 | | Launcher / menus | fuzzel | | |