#!/usr/bin/env bash # Runs as a systemd ExecStartPre for the Sunshine service. Two jobs: # 1. Make sure exactly one Hyprland headless output exists. # 2. Sync sunshine.conf's `output_name` to whatever the headless output is # currently named — Hyprland's HEADLESS-N counter doesn't reset across # session restarts, so pinning to HEADLESS-1 drifts after the first # remove/create cycle. # # Non-fatal at every step: a stale state can't worsen things by aborting here. set -uo pipefail log() { printf '[sunshine-prestart] %s\n' "$*" >&2; } CONF="$HOME/.config/sunshine/sunshine.conf" # Recover Hyprland's instance signature when the unit's env didn't propagate it. if [[ -z "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then for sig in "${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"/hypr/*/; do [[ -d "$sig" ]] || continue export HYPRLAND_INSTANCE_SIGNATURE="$(basename "$sig")" break done fi if [[ -z "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then log "Hyprland not running; nothing to prepare." exit 0 fi if ! command -v hyprctl >/dev/null || ! command -v jq >/dev/null; then log "hyprctl/jq missing; skipping prestart." exit 0 fi # Reduce to exactly one headless output. Hyprland's HEADLESS-N counter # increments on every create and never decrements, so previous failed runs # leave extras laying around. Remove all but the lowest-numbered one (most # likely to be the one with workspaces bound to it). mapfile -t headless_outputs < <(hyprctl monitors -j 2>/dev/null \ | jq -r '.[] | select(.name | startswith("HEADLESS")) | .name' \ | sort -V) existing="${headless_outputs[0]:-}" if [[ -z "$existing" ]]; then log "No headless output present; creating one" hyprctl output create headless >/dev/null for _ in 1 2 3 4 5; do existing="$(hyprctl monitors -j 2>/dev/null \ | jq -r '.[] | select(.name | startswith("HEADLESS")) | .name' \ | sort -V | head -1)" [[ -n "$existing" ]] && break sleep 0.1 done elif [[ ${#headless_outputs[@]} -gt 1 ]]; then log "Found ${#headless_outputs[@]} headless outputs; keeping $existing, removing the rest" for extra in "${headless_outputs[@]:1}"; do hyprctl output remove "$extra" >/dev/null 2>&1 || true done fi if [[ -z "$existing" ]]; then log "Failed to obtain a headless output; Sunshine will start without one." exit 0 fi log "Headless output present: $existing" # Sync sunshine.conf's output_name. Only touch the file if it's our managed # variant (has the management marker) AND the line has actually drifted. if [[ -f "$CONF" ]] && grep -qF '# managed-by: omarchy-moonlight' "$CONF"; then current="$(awk '/^output_name = / {print $3; exit}' "$CONF" 2>/dev/null || true)" if [[ "$current" != "$existing" ]]; then log "Updating sunshine.conf output_name: ${current:-(unset)} -> $existing" if grep -q '^output_name = ' "$CONF"; then sed -i "s|^output_name = .*|output_name = $existing|" "$CONF" else printf '\noutput_name = %s\n' "$existing" >> "$CONF" fi fi fi exit 0