Running the TUI from XDG launchers (and some cron/ssh contexts) can leave
HYPRLAND_INSTANCE_SIGNATURE unset. hyprctl then prints a plain-text error
to stdout and exits 0, which made jq emit "Invalid numeric literal" to the
terminal before the tui_err message.
- ensure_hyprland_env: detect the running instance via `hyprctl instances`
and export HIS before the first hyprctl call.
- get_monitors: validate JSON with `jq -e` before parsing, fall back to
text-mode `hyprctl monitors` if the JSON is broken.
- pick_target: surface a clear error when HIS can't be recovered.
- watcher: same recovery path so auto-pause still works when launched
without HIS in env.
Doc drift from the last two commits:
* Intro mentioned "--auto-pause" — replaced with the external watcher,
so rewrite to describe that instead.
* Added socat to the deps table and the new motion-wallpaper-watcher
binary + SVG icon to the Files Created table.
* State file description updated to include LAST_DIR; mentions the
atomic write + safe KEY=VALUE parser.
* Autostart section replaced with the TUI-first flow (first-run
prompt, menu toggle, stop-offers-disable, disable-offers-stop),
with systemctl as an alternative.
* How It Works expanded with the setsid + uwsm-app spawn, the flock
on the stop path, and a new "Auto-pause (the watcher)" subsection
describing the Hyprland socket2 + mpv IPC bridge.
* Added a troubleshooting entry for auto-pause not firing
(fullscreen vs full-width, watcher log check).
* Uninstall removes the new watcher binary; package remove line
updated to the real deps (was still listing zenity).
Bug-check pass on top of the v2 rewrite. Five real issues fixed:
* autostart_enable used to return the exit code of the trailing `log`
call, so a failing `systemctl --user enable` was silently reported
as success. Now returns 1 with a tui_err on real failure.
* save_state wrote directly to STATE_FILE; a crash mid-write would
leave a truncated file that load_state would partially parse.
Switched to an atomic tmp + mv -f pattern.
* load_state used `source "$STATE_FILE"` which is arbitrary code
execution if a video path ever contained shell metacharacters.
Replaced with a read-based KEY=VALUE parser that only honours
LAST_VIDEO / LAST_TARGET / LAST_DIR.
* stop_mpvpaper can be called twice in quick succession (TUI stop
immediately followed by systemd's ExecStop). Wrapped the whole
body in a `flock -n` on $STATE_DIR/.stop.lock so the second caller
no-ops instead of racing against the first.
* Watcher `cleanup` trap used `[ -n VAR ] && kill`, which
short-circuits to non-zero when VAR is unset and aborts the trap
before `exit 0` under set -e. Restructured to a proper if/|| true.
Major rewrite of the runtime so the entry point is a proper gum TUI
instead of zenity dialogs, plus a handful of correctness fixes that
make it work on real Omarchy setups.
Runtime (motion-wallpaper-toggle, extracted from the installer heredoc):
* Full gum TUI: status header, monitor picker (with All monitors),
library / filesystem pickers, change-video, autostart toggle.
* State file at ~/.config/motion-wallpaper/state remembers last
video, target monitor, and last-used directory so Browse reopens
where the user was.
* Actions: toggle | start | stop | change | status.
Autostart:
* Ships a systemd user unit (motion-wallpaper.service).
* First fresh start prompts the user via gum confirm to enable it.
* Running-state menu offers a Turn autostart ON/OFF entry.
* Header shows the current autostart state.
Auto-pause:
* mpvpaper's -p is unreliable on Hyprland 0.54.x, so a small
motion-wallpaper-watcher subscribes to Hyprland's socket2 and
toggles mpv pause/resume via --input-ipc-server on fullscreen
enter/exit. Started/stopped alongside mpvpaper.
Omarchy compatibility:
* Stop path now respawns swaybg pointed at
~/.config/omarchy/current/background via setsid uwsm-app (the way
Omarchy autostarts it), instead of execing hyprpaper which isn't
present. Falls back to hyprpaper on non-Omarchy Hyprland setups.
* mpvpaper is launched under setsid uwsm-app so it survives the
Walker-spawned terminal closing.
Install / UX:
* Installer only invokes sudo/yay when packages are actually
missing, so reinstall is quiet.
* Dropped zenity; added gum + socat + libnotify.
* Custom SVG icon in the hicolor theme so Walker shows a proper
tile. Installer restarts elephant.service so the new entry/icon
appear without logout.
* .desktop flipped to Terminal=true so launchers spawn a terminal
for the TUI.
* Watcher lookup falls back to the script's own dir when PATH is
minimal (launcher-spawned terminals).
Bug fixes:
* Monitor picker was sending row-major data to zenity as one cell;
fixed (kept the correct form for the new gum picker).
* load_state / action_status no longer leak a non-zero exit code
from trailing test expressions.
* Stop path cleans up stray hyprpaper that would otherwise win the
background layer.
- Changed suggested keybind from SUPER+W (conflicts with Close window
in Omarchy) to SUPER ALT+W
- Stop hyprpaper/swaybg before starting mpvpaper so the video wallpaper
is actually visible
- Restart hyprpaper when toggling motion wallpaper off so the normal
wallpaper is restored
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>