#!/usr/bin/env bash # Invoked by Sunshine as a stream-stop hook (global_prep_cmd `undo`). # Returns the promoted workspace to the original real monitor and restores # focus to whichever workspace the user had active at connect time. # # With the vkms-based design, no monitors get disabled, so undo is just a # workspace-and-focus restore. Virtual-1 stays alive; the next stream resizes # it as needed. set -euo pipefail VIRT_MON="${OMARCHY_VIRTUAL_OUTPUT:-Virtual-1}" STATE_DIR="${XDG_RUNTIME_DIR:-/tmp}/sunshine-headless" HOOK_LOG="$STATE_DIR/hook.log" log() { local msg msg="$(date +%H:%M:%S.%3N) [sunshine-undo] $*" printf '%s\n' "$msg" >&2 # Append (not truncate) so the undo log lands alongside the do log. printf '%s\n' "$msg" >> "$HOOK_LOG" 2>/dev/null || true } log "undo-hook start" if ! command -v hyprctl >/dev/null 2>&1; then log "hyprctl not found; nothing to undo." exit 0 fi if [[ -z "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then for sig_dir in "${XDG_RUNTIME_DIR:-/tmp}"/hypr/*/; do [[ -d "$sig_dir" ]] || continue export HYPRLAND_INSTANCE_SIGNATURE="$(basename "$sig_dir")" break done if [[ -z "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then log "Hyprland not running; nothing to undo." exit 0 fi fi PREV_WS="$(cat "$STATE_DIR/prev-workspace-id" 2>/dev/null || echo 1)" ORIG_ACTIVE_WS="$(cat "$STATE_DIR/orig-active-workspace-id" 2>/dev/null || echo "$PREV_WS")" # Find a non-virtual monitor to move the promoted workspace back to. REAL_MON="$(hyprctl monitors -j 2>/dev/null \ | jq -r --arg m "$VIRT_MON" '.[] | select(.name != $m) | .name' \ | head -n1)" if [[ -n "$REAL_MON" ]]; then log "Returning workspace $PREV_WS → $REAL_MON" hyprctl dispatch moveworkspacetomonitor "$PREV_WS $REAL_MON" >/dev/null || true # If the do-hook promoted a non-active workspace because the active one was # empty, ORIG_ACTIVE_WS differs from PREV_WS — restore focus to where the # user actually was at connect time. if [[ "$ORIG_ACTIVE_WS" != "$PREV_WS" ]]; then log "Restoring focus to original active workspace $ORIG_ACTIVE_WS" hyprctl dispatch workspace "$ORIG_ACTIVE_WS" >/dev/null || true fi hyprctl dispatch focusmonitor "$REAL_MON" >/dev/null || true else log "No real monitor connected; leaving workspace assignment to Hyprland defaults." fi # Clean state files but keep the directory + hook.log for the next run. rm -f "$STATE_DIR/prev-monitors.json" "$STATE_DIR/prev-workspace-id" "$STATE_DIR/orig-active-workspace-id" log "Stream teardown complete ($VIRT_MON kept alive for next connect)"