Files
amane-config/tools/amane-remote.sh
T
server2 e847cbde73 tools: pick the live hyprland instance
$XDG_RUNTIME_DIR/hypr keeps folders of dead instances, and the newest one
isn't always alive; take the newest with a .socket.sock
2026-10-08 01:25:33 +02:00

145 lines
6.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Visual test loop for an amane shell on a remote Wayland desktop, driven over ssh.
#
# usage: amane-remote.sh HOST COMMAND [ARGS...]
# deploy SRC_DIR back up the remote ~/.config/amane/src once, then replace it with SRC_DIR
# build `amane compile` on the remote (backs up a saved amane-shell once)
# start | stop run / stop the compiled shell, with AMANE_FRAMES=1 logging
# log [N] last N lines of the shell's log (default 30)
# fps [SECONDS] frames drawn over SECONDS (default 3), with average gap and gpu wait
# anim HANDLER 3x `amane ipc call HANDLER open|close`: frames, gaps over 20 ms, every gap
# ipc ARGS... amane ipc call ARGS...
# shot NAMESPACE FILE [PAD] screenshot the layer window with that namespace (Hyprland) into local FILE
# players start N | stop run N fake MPRIS players (fake_mpris.py, next to this script)
# sinks start | stop two inaudible null sinks (island_test_a/_b); fake players play into _a
# sh 'COMMAND' any command inside the remote session environment
set -euo pipefail
host=${1:?host}
command=${2:?command}
shift 2
here=$(dirname "$(readlink -f "$0")")
work='$HOME/.cache/amane-remote-test'
# what a terminal inside the graphical session would have
session='uid=$(id -u)
export XDG_RUNTIME_DIR=/run/user/$uid
export DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus
export WAYLAND_DISPLAY=$(ls "$XDG_RUNTIME_DIR" | grep -m1 -x "wayland-[0-9]*")
export HYPRLAND_INSTANCE_SIGNATURE=$(cd "$XDG_RUNTIME_DIR/hypr" 2>/dev/null && for d in $(ls -t); do [ -S "$d/.socket.sock" ] && echo "$d" && break; done)
export PATH=$HOME/.cargo/bin:$PATH
mkdir -p '"$work"' && cd '"$work"'
'
remote() {
ssh -o BatchMode=yes "$host" "$session$1"
}
case $command in
deploy)
source_dir=${1:?source folder}
remote 'test -d ~/.config/amane/src.before-test || cp -a ~/.config/amane/src ~/.config/amane/src.before-test 2>/dev/null || true
mkdir -p ~/.config/amane/src && find ~/.config/amane/src -maxdepth 1 -name "*.rs" -delete'
# -m: the two clocks may disagree, and future timestamps only cause warnings
tar -C "$source_dir" -cf - . | ssh -o BatchMode=yes "$host" 'tar -C ~/.config/amane/src -xmf -'
scp -q "$here/fake_mpris.py" "$host:.cache/amane-remote-test/"
remote 'ls ~/.config/amane/src'
;;
build)
remote 'test -f ~/.cache/amane/amane-shell.before-test || cp -a ~/.cache/amane/amane-shell ~/.cache/amane/amane-shell.before-test 2>/dev/null || true
amane compile 2>&1 | tail -40'
;;
start)
# newer CLIs save ~/.cache/amane/amane-shell; older ones only leave cargo's output
# the old shell can take a few seconds to let go of amane's socket; wait for it, or the new one panics
remote 'test -f shell.pid && kill $(cat shell.pid) 2>/dev/null
for i in $(seq 50); do pgrep -F shell.pid > /dev/null 2>&1 || break; sleep 0.1; done
binary=~/.cache/amane/amane-shell
test -x "$binary" || binary=~/.cache/amane/project/target/debug/amane-shell
AMANE_FRAMES=1 setsid -f sh -c "echo \$\$ > shell.pid; exec $binary" > shell.log 2>&1
sleep 2; pgrep -F shell.pid > /dev/null && echo "running, pid $(cat shell.pid)" || { echo "exited:"; tail -20 shell.log; }'
;;
stop)
remote 'test -f shell.pid && kill $(cat shell.pid) 2>/dev/null; rm -f shell.pid; echo stopped'
;;
log)
remote "tail -n ${1:-30} shell.log"
;;
fps)
# AMANE_FRAMES lines: frame <window> <w>x<h> gap <ms> view <ms> draw <ms> gpu <ms>
remote "a=\$(wc -l < shell.log); sleep ${1:-3}; tail -n +\$((a + 1)) shell.log | grep '^frame' |
awk '{gap+=\$5; view+=\$7; gpu+=\$11; n++} END {if (n) printf \"%d frames in ${1:-3} s, gap %.1f ms, view %.2f ms, gpu wait %.2f ms\n\", n, gap/n, view/n, gpu/n; else print \"no frames\"}'"
;;
anim)
# a smooth animation shows only ~17 ms gaps; 33 ms runs mean it fell to 30 fps
handler=${1:?ipc handler with open and close}
remote "for round in 1 2 3; do for action in open close; do
a=\$(wc -l < shell.log); amane ipc call $handler \$action > /dev/null; sleep 1
tail -n +\$((a + 1)) shell.log | grep '^frame' | awk -v act=\$action '
{gap[n++]=\$5; gpu+=\$11}
END {slow=0; for (i=1;i<n;i++) if (gap[i]>20) slow++;
printf \"%-5s frames %2d over 20 ms %2d gpu wait %5.2f ms gaps:\", act, n, slow, gpu/n;
for (i=1;i<n && i<26;i++) printf \" %.0f\", gap[i]; print \"\"}'
done; done"
;;
ipc)
remote "amane ipc call $*"
;;
shot)
namespace=${1:?namespace}
file=${2:?local file}
pad=${3:-0}
# hyprctl layers -j: {monitor: {levels: {n: [{namespace, x, y, w, h}]}}}, x and y in global layout pixels
remote "geometry=\$(hyprctl layers -j | python3 -c '
import json, sys
for monitor in json.load(sys.stdin).values():
for level in monitor[\"levels\"].values():
for layer in level:
if layer[\"namespace\"] == \"$namespace\":
p = $pad
print(f\"{layer[\"x\"] - p},{max(0, layer[\"y\"] - p)} {layer[\"w\"] + 2 * p}x{layer[\"h\"] + 2 * p}\")
sys.exit()
sys.exit(\"no layer named $namespace\")
') && grim -g \"\$geometry\" shot.png && echo \"\$geometry\" >&2" && ssh -o BatchMode=yes "$host" 'cat ~/.cache/amane-remote-test/shot.png' > "$file"
echo "saved $file"
;;
players)
case ${1:?start N or stop} in
start)
count=${2:-3}
remote "sink=\$(pactl list sinks short | grep -q island_test_a && echo island_test_a)
for index in \$(seq 0 $((count - 1))); do
FAKE_MPRIS_SINK=\$sink setsid -f python3 fake_mpris.py \$index >> players.log 2>&1
done
sleep 1.5; playerctl -l"
;;
stop)
# [p]: the remote shell's own command line holds this text, and must not match it
remote 'pkill -TERM -f "[p]ython3 fake_mpris.py" || true; sleep 0.5; playerctl -l 2>&1'
;;
esac
;;
sinks)
case ${1:?start or stop} in
start)
remote 'for name in island_test_a island_test_b; do
pactl list sinks short | grep -q "$name" ||
pactl load-module module-null-sink sink_name=$name sink_properties=device.description=$name > /dev/null
done
pactl list sinks short | grep island_test'
;;
stop)
remote 'pactl list modules short | awk "/module-null-sink/ && /island_test/ {print \$1}" | xargs -r -n1 pactl unload-module; pactl list sinks short | grep -c island_test'
;;
esac
;;
sh)
remote "${1:?command}"
;;
*)
sed -n '2,15p' "$0"
exit 1
;;
esac