# wallp — Unified Wallpaper Manager — Design Date: 2026-06-11 ## Overview Single bash script `wallp` replacing two scripts: - `multiwal.sh` — interactive wallpaper setter (qarma picker, two screens). - `qar-lastwall.sh` — restores last-session wallpapers at session start. Command-line flags pick the action. qarma is used **only** for GUI (file picker, selection menu, dialogs, help). `swaybg` actually sets wallpapers and needs no qarma. Per-output PID tracking allows updating a single screen without blanking the other (the old `pkill swaybg` killed both). A config file holds defaults; flags override where applicable. Two-screen setup: - Horizontal screen — logical `H`, physical output from conf `OUTPUT_H` (e.g. `DP-1`). - Vertical screen — logical `V`, physical output from conf `OUTPUT_V` (e.g. `DP-3`). ## CLI ``` wallp # help screen wallp --help / -h # help screen wallp --set # qarma picker: menu H/V/Both -> file picker(s) wallp --set H= # set horizontal only, no GUI wallp --set V= # set vertical only, no GUI wallp --set H= V= # set both, no GUI wallp --restore # restore last session from persistent state wallp --theme # override conf theme (combine with --set/--restore) ``` Rules: - Bare invocation or `--help`/`-h` → help screen. Shown in a qarma window when `$WAYLAND_DISPLAY` is set, otherwise printed to stdout. - `--set` with no `H=`/`V=` args: - `$WAYLAND_DISPLAY` set → qarma selection flow. - no display → error: "no display: pass H= and/or V=". Exit non-zero. - `--set` with `H=`/`V=` args → set those outputs directly, no GUI (works headless; swaybg does not need qarma). - `--restore` → no display required (intended for session-start autostart). - `--theme ` → overrides conf `THEME` for this invocation. - Unknown flag → error message + help, exit non-zero. Logical `H=`/`V=` keys map to physical `OUTPUT_H`/`OUTPUT_V` from conf, so output renames only touch the conf, not command lines or muscle memory. ## Config — `~/.config/wallp/wallp.conf` Format: simple `key=value`, one per line. Parsed manually (no shell sourcing — avoids code execution from the conf). Blank lines and lines starting with `#` ignored. Leading `~` in path values expanded to `$HOME`. Keys: ``` THEME=sexy-splurge OUTPUT_H=DP-1 OUTPUT_V=DP-3 DEFAULT_H=/home/danix/Pictures/wallpapers/SFW/wallhaven-4yd27g_2560x1080.png DEFAULT_V=/home/danix/Pictures/wallpapers/SFW/wallhaven-gwzqjl_2560x1080.png ``` Behavior: - **Conf file missing** → write a commented template to the conf path, tell the user (stdout + notify-send if available) to fill it in and re-run, then **exit 0 without touching any wallpaper**. Applies to every action. - **Conf present, required path/output key missing or empty** → hard error naming the missing key, exit non-zero, no wallpaper change. Required keys: `OUTPUT_H`, `OUTPUT_V`, `DEFAULT_H`, `DEFAULT_V`. - **THEME** is the one exception: if absent, built-in fallback `sexy-splurge` is used. `--theme` flag also overrides it. An example conf is shipped in the repo (`wallp.conf.example`). ## State Files Persistent state (wallp-owned, `~/.config/wallp/`, survives reboot): - `~/.config/wallp/wall_h` — H wallpaper path. - `~/.config/wallp/wall_v` — V wallpaper path. - `~/.config/wallp/theme` — last-used theme name. - `~/.config/wallp/wallp.conf` — config (see Config section). Runtime state (volatile, reset on reboot): - `~/.cache/wal/wpaper` — symlink to current H image. **Kept** because it has external consumers (rofi themes under `~/.config/rofi/darknix/*.rasi`). wallp maintains it. - `~/.cache/wallp/H.pid` — swaybg PID for horizontal output. - `~/.cache/wallp/V.pid` — swaybg PID for vertical output. Dropped (no external consumer; confirmed via grep over `~/bin` and `~/.config`): - `~/.cache/wal/wal`, `~/.cache/wal/wal2` — only the replaced scripts wrote these; nothing else reads them. - `~/.config/wal/wal`, `~/.config/wal/wal2` — superseded by `~/.config/wallp/`. wallp owns all its persistence directly in `~/.config/wallp/`. This moves persistence out of `wal.sh` and into wallp. `wal.sh` is reduced to its theme-app glue only (dunst symlink+restart, kitty symlink); it loses the wal/wal2/wpaper copy logic. `wal.sh` remains the wal `-o` hook for future use. Note: `~/.cache/wal/colors.json` is pywal-native (written by `wal` itself, read by `pywal_sublime.py`); not touched by wallp. ## Theme Persistence The last-used theme is persisted to `~/.config/wallp/theme` and survives reboot. Conf `THEME` is only the first-run default. Precedence on every action: 1. `--theme ` flag → use it. 2. else persisted `~/.config/wallp/theme` (if present). 3. else conf `THEME`. 4. else built-in `sexy-splurge`. Whatever theme is resolved gets persisted to `~/.config/wallp/theme`. This means once a theme is set it sticks (for both `--set` and `--restore`) until changed via `--theme`. `--restore` thus brings back the last-used theme, not the conf default. ## Set Flow For each chosen output (H and/or V): 1. Resolve the file path (from qarma picker or `H=`/`V=` arg). Validate it exists; if not, error and skip that output (do not blank it). 2. Selective kill: read `.pid`; if the PID is alive, `kill` it. The other output's swaybg is left untouched. 3. `swaybg -o -i -m fill &`; save the new PID to `.pid`. 4. Persist the path to `~/.config/wallp/wall_h` (H) or `wall_v` (V). After all chosen outputs are done: 5. Update `~/.cache/wal/wpaper` symlink → current H image (for rofi). 6. Resolve theme (see Theme Persistence), persist to `~/.config/wallp/theme`. 7. Run `wal --backend colorz -nq --theme -o ~/bin/wal.sh`. 8. `notify-send` color-theme update. qarma selection flow (`--set`, display present, no args): - qarma list/menu: choose `H` / `V` / `Both` (no initial yes/no confirmation). - File picker per chosen output: `qarma --file-selection --preview-images 500 --width 1300 --height 600` with a per-output title. - Picker cancelled → skip that output (no error). ## Restore Flow (`--restore`) For each output H/V: 1. If the persistent pointer (`~/.config/wallp/wall_h` for H, `wall_v` for V) exists → read the path, selective-kill that output's old PID, `swaybg`, save PID. 2. If no persistent pointer for that output → use `DEFAULT_H`/`DEFAULT_V` from conf, persist it (`wall_h`/`wall_v`), swaybg, save PID. After both outputs: 3. Update `wpaper` symlink → H image. 4. Resolve theme (persisted theme wins, see Theme Persistence), persist it, run wal theme + notify (same as set steps 7-8). No display required. Replaces `qar-lastwall.sh`. Intended for session-start autostart. ## Error Handling - Missing/invalid file for an output → error, skip that output, do not blank it. - qarma cancel → skip output, no error. - Dead or missing PID file → start fresh swaybg, no kill attempt. - GUI needed but no display → clear error, exit non-zero. - Required binary missing (`swaybg`, `wal`, `qarma` when GUI needed, `notify-send` optional) → checked at start; error and exit if a required one is absent. `notify-send` treated as optional (skip notification if absent). - Conf missing → generate template, instruct user, exit 0, no change. - Required conf key missing → hard error, exit non-zero, no change. ## Testing The script is side-effecting (swaybg, wal, filesystem). Strategy: - Factor pure logic into functions: conf parsing, arg parsing, `~` expansion, logical→physical output mapping, file-existence validation. - Test with bats (or a stub harness): place fake `swaybg`, `wal`, `qarma`, `notify-send` as executable stubs on `PATH` in a temp dir; point `HOME` at a temp dir. - Assertions: - Conf-missing path generates the template and changes no wallpaper. - Missing required key hard-errors. - `--set V=` writes `~/.config/wallp/wall_v` and leaves `H.pid` untouched (partial update does not blank H). - Theme persistence: `--theme X` writes `~/.config/wallp/theme`; a later no-flag run resolves X (persisted wins over conf THEME). - Selective kill targets only the changed output's PID. - Restore falls back to `DEFAULT_*` when no persistent pointer exists. - `~` in conf paths expands to `$HOME`. - Manual smoke test on the real two-screen setup. ## Migration Notes - `wal.sh` lines 16-27 (wal/wal2/wpaper copy + symlink) — remove the wal/wal2 copy lines (16,19,22-23) entirely; the `wpaper` symlink moves into wallp. Keep dunst + kitty glue (lines 7,13). `wal.sh` stays the wal `-o` hook. - Old `qar-lastwall.sh` read `~/.config/wal/*`; old `multiwal.sh` wrote `~/.cache/wal/*` — mismatch bug resolved: wallp uses a single owned location `~/.config/wallp/`. - Old default-wallpaper paths had a literal quoted `~` (swaybg would not expand) — fixed via `~`→`$HOME` expansion on conf load. - `~/.cache/wal/wpaper` retained — consumed by `~/.config/rofi/darknix/*.rasi`. - `~/.cache/wal/wal` + `wal2` dropped — no external consumer (grep-verified).