#!/bin/sh # Watches sway for the user leaving Big Picture. Installed to /usr/local/bin/session-watcher. # # THIS IS THE "MEDIA APPS ONLY START WHEN YOU LEAVE BIG PICTURE" MECHANISM, HALF TWO. # Read steam-session's header first — it explains why there are two halves. # # The case this one covers: Steam is *still running*, but the user is no longer looking # at it. "Exit Big Picture mode" (which drops to Steam's desktop client), the remote's # channel keys, $mod+3, or Home Assistant's Screen select — all of those change the # focused sway workspace and none of them make the Steam process exit, so steam-session # never resumes and something else has to notice. # # WHY WORKSPACE EVENTS AND NOT WINDOW TITLES # ------------------------------------------ # The obvious implementation is to poll for a window titled "Steam Big Picture Mode" # and react when it goes away. That was rejected: the title, the window class and the # very existence of a separate Big Picture window have all changed across Steam client # rewrites, and a media session that silently stops appearing after a Steam update is a # bad failure — it looks like the image is broken, not like a string moved. A sway # workspace name is a contract this repo owns and can only change by editing the sway # config and steamtv_agent/sway_control.py together. # # Cost: one blocked read on a socket, and a jq per workspace switch. It publishes # nothing and listens on nothing — the session-mode sensor in Home Assistant is # steamtv-agent asking sway, not this script pushing anywhere. (Adding an inbound # listener here would breach the MQTT-is-the-only-control-surface rule; see # steamtv_agent/mqtt_discovery.py.) set -eu WS_STEAM="1:steam" if ! command -v jq >/dev/null 2>&1; then echo "session-watcher: jq is missing; the media session will only start when Steam" >&2 echo " itself exits (steam-session's path). Install jq and reload sway." >&2 exit 1 fi # `swaymsg -m` streams one JSON object per event line and blocks forever. If sway goes # away the pipe closes and this exits — which is correct, since sway restarting means # `exec_always` starts a fresh watcher. swaymsg -t subscribe -m '["workspace"]' 2>/dev/null | while read -r event; do # "focus" is the only change that means the user went somewhere; "init", "empty" and # "rename" fire during ordinary window churn and must not trigger a launch. change=$(printf '%s' "$event" | jq -r '.change // empty' 2>/dev/null) || continue [ "$change" = "focus" ] || continue current=$(printf '%s' "$event" | jq -r '.current.name // empty' 2>/dev/null) || continue [ -n "$current" ] || continue # An `if` rather than `[ … ] && continue`: the && form returns non-zero when the test # is false, which under `set -e` would kill the watcher the first time somebody moved # to a non-Steam workspace — i.e. exactly once, silently, on the first use. if [ "$current" = "$WS_STEAM" ]; then continue fi # No --focus: the user has already chosen where they want to be, and yanking them to # the browser workspace because they pressed the channel key would be worse than # doing nothing. This only makes sure the apps exist. /usr/local/bin/media-session start >/dev/null 2>&1 || true done