105 lines
3.8 KiB
Bash
105 lines
3.8 KiB
Bash
#!/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
|