#!/bin/sh
# Owns the visibility of the eww now-playing widget. Installed to
# /usr/local/bin/fullscreen-watcher, started from configs/sway/config.
#
# The rule this enforces: the widget may only appear for *audio-only* playback with
# nothing visual on screen. A status overlay on top of a film or a Steam Link stream is
# worse than no status overlay at all, so every ambiguous case resolves to "hidden".
#
# Two independent signals, because neither alone is sufficient:
#
#   1. The sway tree. Catches fullscreen anything (mpv, Steam Link, a fullscreen
#      Firefox video) via fullscreen_mode, and catches a *windowed* visual app via its
#      app_id/class. mpv is the interesting case here: playing an audio-only file it
#      opens no window at all, so "an mpv window exists in the tree" is already a
#      reliable proxy for "mpv has a video stream".
#
#   2. mpv's IPC socket, queried for the `video-format` property. This is the direct
#      answer to "does the current mpv track have video", and covers the case where
#      mpv was started with --force-window and so has a window even for audio. It is
#      best-effort: if the socket is absent (mpv not running, or configs/mpv/mpv.conf
#      not installed) signal 1 still stands on its own.
set -eu

WINDOW=now-playing
LOCK="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/fullscreen-watcher.pid"

# exec_always in the sway config re-runs this on every `swaymsg reload`; without this
# guard each reload would leave another subscription and another swaymsg behind.
if [ -f "$LOCK" ] && kill -0 "$(cat "$LOCK" 2>/dev/null)" 2>/dev/null; then
  exit 0
fi
echo $$ > "$LOCK"

if ! command -v eww >/dev/null 2>&1; then
  echo "fullscreen-watcher: eww is not installed, no now-playing widget on this image." >&2
  echo "  See live-build/config/hooks/normal/0800-eww-widget.hook.chroot." >&2
  exit 0
fi

eww daemon >/dev/null 2>&1 || true

# Anything whose presence on screen means "do not draw over this". Steam Link runs
# under Xwayland so it appears as an X11 class, not a Wayland app_id — both are
# checked. This is a fixed list in this file; nothing external feeds it.
VISUAL_MATCH='^(mpv|steam|steamlink|steam_app_.*|org\.videolan\.VLC)$'

visual_on_screen() {
  swaymsg -t get_tree 2>/dev/null | jq -e --arg m "$VISUAL_MATCH" '
    [ recurse(.nodes[]?, .floating_nodes[]?)
      | select(.type == "con" or .type == "floating_con")
      | select(
          ((.fullscreen_mode // 0) != 0)
          or ((.visible // false)
              and (((.app_id // "") | ascii_downcase | test($m))
                   or ((.window_properties.class // "") | ascii_downcase | test($m))))
        )
    ] | length > 0
  ' >/dev/null 2>&1
}

mpv_has_video() {
  for sock in "${HOME:-/root}/.mpv-socket" "${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/mpv.sock"; do
    [ -S "$sock" ] || continue
    python3 - "$sock" <<'PY' && return 0
import json, socket, sys

try:
    conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    conn.settimeout(1.0)
    conn.connect(sys.argv[1])
    conn.sendall(b'{"command":["get_property","video-format"]}\n')
    reply = json.loads(conn.recv(4096).splitlines()[0])
except Exception:
    sys.exit(1)

sys.exit(0 if reply.get("data") else 1)
PY
  done
  return 1
}

apply() {
  if visual_on_screen || mpv_has_video; then
    eww close "$WINDOW" >/dev/null 2>&1 || true
  else
    eww open "$WINDOW" >/dev/null 2>&1 || true
  fi
}

cleanup() {
  rm -f "$LOCK"
  eww close "$WINDOW" >/dev/null 2>&1 || true
}
trap cleanup EXIT INT TERM

apply

# The event payloads are not parsed: any window or workspace change simply triggers a
# fresh look at the whole tree. That is one extra `swaymsg -t get_tree` per event and
# it removes a whole class of bugs around which fields a given sway version puts in a
# "fullscreen_mode" event.
swaymsg -t subscribe -m '["window","workspace"]' 2>/dev/null | while read -r _event; do
  apply
done
