From c0c5758bfe4c8c4ede25a79df79d8bef4a50c819 Mon Sep 17 00:00:00 2001 From: 28allday Date: Thu, 23 Apr 2026 22:47:02 +0100 Subject: [PATCH] Recover HYPRLAND_INSTANCE_SIGNATURE when launcher env is stripped 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. --- motion-wallpaper-toggle | 49 ++++++++++++++++++++++++++++++++++++---- motion-wallpaper-watcher | 12 ++++++++-- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/motion-wallpaper-toggle b/motion-wallpaper-toggle index a0ef25d..6f23a9d 100644 --- a/motion-wallpaper-toggle +++ b/motion-wallpaper-toggle @@ -181,11 +181,44 @@ show_header() { # ===== selection ============================================================== +ensure_hyprland_env() { + # The TUI can be launched from contexts whose env doesn't carry + # HYPRLAND_INSTANCE_SIGNATURE (XDG launchers, cron, ssh). `hyprctl instances` + # still works in that case and prints the running signatures, so recover by + # picking the first one. If we can't, leave HIS unset and let the caller + # surface a friendly error. + if [ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]; then + return 0 + fi + local sig + sig="$(hyprctl instances 2>/dev/null | awk '/^instance /{sub(/:$/,"",$2); print $2; exit}')" + if [ -n "$sig" ]; then + export HYPRLAND_INSTANCE_SIGNATURE="$sig" + log "recovered HYPRLAND_INSTANCE_SIGNATURE=$sig from hyprctl instances" + fi +} + get_monitors() { - local mon_json + # hyprctl's failure modes are noisy: when HYPRLAND_INSTANCE_SIGNATURE isn't + # set (can happen when the TUI is spawned from a launcher whose env doesn't + # inherit the Hyprland session), hyprctl prints a plain-text error to stdout + # and exits 0 — which then makes jq complain loudly. Validate the JSON and + # fall back to parsing the human-readable form. + ensure_hyprland_env + local mon_json mon_text mon_json="$(hyprctl monitors -j 2>/dev/null || true)" - [ -z "$mon_json" ] && return 1 - printf '%s' "$mon_json" | jq -r '.[].name' + if [ -n "$mon_json" ] && printf '%s' "$mon_json" | jq -e . >/dev/null 2>&1; then + printf '%s' "$mon_json" | jq -r '.[].name' + return 0 + fi + mon_text="$(hyprctl monitors 2>/dev/null || true)" + if [ -n "$mon_text" ]; then + printf '%s\n' "$mon_text" \ + | awk '/^Monitor /{print $2; ok=1} END{exit !ok}' \ + && return 0 + fi + log "get_monitors: hyprctl produced no usable output (HIS=${HYPRLAND_INSTANCE_SIGNATURE:-unset})" + return 1 } pick_target() { @@ -193,8 +226,14 @@ pick_target() { command -v jq >/dev/null || { tui_err "jq is not installed."; return 1; } local monitors - monitors="$(get_monitors)" || { tui_err "Could not read monitors from hyprctl."; return 1; } - [ -z "$monitors" ] && { tui_err "No monitors detected."; return 1; } + if ! monitors="$(get_monitors)" || [ -z "$monitors" ]; then + if [ -z "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]; then + tui_err "HYPRLAND_INSTANCE_SIGNATURE is not set — the launcher didn't inherit the Hyprland session. Try running motion-wallpaper-toggle from a regular terminal." + else + tui_err "Could not read monitors from hyprctl. See $LOG_FILE." + fi + return 1 + fi local count count="$(printf '%s\n' "$monitors" | wc -l)" diff --git a/motion-wallpaper-watcher b/motion-wallpaper-watcher index 83fb736..67c7ed7 100644 --- a/motion-wallpaper-watcher +++ b/motion-wallpaper-watcher @@ -21,8 +21,16 @@ log() { } if [ -z "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]; then - log "HYPRLAND_INSTANCE_SIGNATURE not set — exiting" - exit 0 + # Recover the signature from `hyprctl instances` — same trick the toggle + # script uses when launched from an env that didn't inherit HIS. + sig="$(hyprctl instances 2>/dev/null | awk '/^instance /{sub(/:$/,"",$2); print $2; exit}')" + if [ -n "$sig" ]; then + export HYPRLAND_INSTANCE_SIGNATURE="$sig" + log "recovered HYPRLAND_INSTANCE_SIGNATURE=$sig from hyprctl instances" + else + log "HYPRLAND_INSTANCE_SIGNATURE not set and no instance found — exiting" + exit 0 + fi fi HYPR_SOCK="$RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock"