Add native Linux SNI and Windows notification-area trays, single-instance reopening, background startup and login entries. Preserve recording and discovery after close, with clean Quit and reachable no-tray fallbacks. Track capture-source audio delivery independently of silent output tracks and warn without stopping video. Give clip metadata its own row and shape labels at final bounds to avoid GPUI 0.2.2 truncation cache and text-run bugs. Validated with strict workspace checks, 57 tests, Windows core/OBS/probe cross-clippy, all ten capture scenarios, tray/session/clips/playback rigs and narrow/wide screenshots. Document runtime validation gaps.
137 lines
5.3 KiB
Bash
Executable File
137 lines
5.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Run a command inside a disposable headless desktop for capture tests.
|
|
#
|
|
# scripts/rig/headless.sh [--x11] <command> [args...]
|
|
#
|
|
# Starts a private D-Bus session with PipeWire, WirePlumber, a virtual audio
|
|
# sink, headless Sway on the GPU and xdg-desktop-portal-wlr (the portal
|
|
# auto-selects the only output). With --x11, the game and the command run as
|
|
# X11 clients of Sway's Xwayland instead, which has GPU rendering (DRI3) and an
|
|
# EWMH window manager like a real X11 desktop. A 60 FPS test pattern with a
|
|
# 440 Hz tone stands in for a game. Everything is torn down when the command
|
|
# exits.
|
|
#
|
|
# The command sees CLIPBIN_RIG_DIR (logs, plus game.pid and xdpw.pid for the
|
|
# test game and the portal backend) and, with --x11, CLIPBIN_RIG_GAME_WINDOW
|
|
# (the test window's X id). Set CLIPBIN_RIG_KEEP_LOGS=<dir> to keep the logs.
|
|
#
|
|
# This exercises the capture paths on a machine without a display. It does not
|
|
# replace validation on real hardware (Hyprland, an X11 desktop, Windows).
|
|
set -euo pipefail
|
|
|
|
mode=wayland
|
|
if [[ "${1:-}" == "--x11" ]]; then
|
|
mode=x11
|
|
shift
|
|
fi
|
|
if [[ $# -eq 0 ]]; then
|
|
echo "usage: $0 [--x11] <command> [args...]" >&2
|
|
exit 2
|
|
fi
|
|
|
|
if [[ -z "${CLIPBIN_RIG_INNER:-}" ]]; then
|
|
args=("$@")
|
|
[[ "${mode}" == x11 ]] && args=(--x11 "${args[@]}")
|
|
exec env CLIPBIN_RIG_INNER=1 dbus-run-session -- "$0" "${args[@]}"
|
|
fi
|
|
|
|
rig_dir="$(mktemp -d "${TMPDIR:-/tmp}/clipbin-rig.XXXXXX")"
|
|
pids=()
|
|
cleanup() {
|
|
for pid in "${pids[@]}"; do kill "${pid}" 2>/dev/null || true; done
|
|
wait 2>/dev/null || true
|
|
# The D-Bus-activated document portal mounts a FUSE filesystem here.
|
|
fusermount3 -u "${XDG_RUNTIME_DIR}/doc" 2>/dev/null || true
|
|
if [[ -n "${CLIPBIN_RIG_KEEP_LOGS:-}" ]]; then
|
|
mkdir -p "${CLIPBIN_RIG_KEEP_LOGS}"
|
|
cp "${rig_dir}"/*.log "${CLIPBIN_RIG_KEEP_LOGS}/" 2>/dev/null || true
|
|
fi
|
|
rm -rf "${rig_dir}"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
export XDG_RUNTIME_DIR="${rig_dir}/runtime"
|
|
export XDG_CONFIG_HOME="${rig_dir}/config"
|
|
export XDG_CURRENT_DESKTOP=sway XDG_SESSION_TYPE=wayland
|
|
mkdir -p "${XDG_RUNTIME_DIR}" "${XDG_CONFIG_HOME}/sway" "${XDG_CONFIG_HOME}/xdg-desktop-portal-wlr"
|
|
chmod 700 "${XDG_RUNTIME_DIR}"
|
|
unset WAYLAND_DISPLAY DISPLAY
|
|
|
|
width="${CLIPBIN_RIG_WIDTH:-1920}"
|
|
height="${CLIPBIN_RIG_HEIGHT:-1080}"
|
|
|
|
wait_for() {
|
|
local what="$1" check="$2"
|
|
for _ in $(seq 100); do
|
|
if eval "${check}"; then return 0; fi
|
|
sleep 0.1
|
|
done
|
|
echo "rig: ${what} did not start; logs in ${rig_dir}" >&2
|
|
exit 1
|
|
}
|
|
|
|
pipewire > "${rig_dir}/pipewire.log" 2>&1 & pids+=($!)
|
|
wait_for pipewire '[[ -S "${XDG_RUNTIME_DIR}/pipewire-0" ]]'
|
|
wireplumber > "${rig_dir}/wireplumber.log" 2>&1 & pids+=($!)
|
|
pipewire-pulse > "${rig_dir}/pipewire-pulse.log" 2>&1 & pids+=($!)
|
|
echo "${pids[-1]}" > "${rig_dir}/pulse.pid"
|
|
wait_for pipewire-pulse '[[ -S "${XDG_RUNTIME_DIR}/pulse/native" ]]'
|
|
# No sound card here: route playback to a virtual sink whose monitor OBS
|
|
# records as desktop audio.
|
|
pactl load-module module-null-sink sink_name=clipbin-rig-sink > /dev/null
|
|
pactl set-default-sink clipbin-rig-sink
|
|
|
|
printf '%s\n' \
|
|
"output HEADLESS-1 resolution ${width}x${height}@60Hz" \
|
|
"default_border none" \
|
|
"xwayland force" \
|
|
"exec echo \"\$DISPLAY\" > ${rig_dir}/xwayland-display" \
|
|
> "${XDG_CONFIG_HOME}/sway/config"
|
|
printf '%s\n' \
|
|
"[screencast]" \
|
|
"chooser_type=none" \
|
|
"output_name=${CLIPBIN_RIG_PORTAL_OUTPUT:-HEADLESS-1}" \
|
|
"max_fps=60" \
|
|
> "${XDG_CONFIG_HOME}/xdg-desktop-portal-wlr/config"
|
|
|
|
export SWAYSOCK="${XDG_RUNTIME_DIR}/clipbin-sway.sock"
|
|
WLR_BACKENDS=headless WLR_LIBINPUT_NO_DEVICES=1 WLR_HEADLESS_OUTPUTS=1 \
|
|
sway --config "${XDG_CONFIG_HOME}/sway/config" > "${rig_dir}/sway.log" 2>&1 & pids+=($!)
|
|
wait_for sway 'compgen -G "${XDG_RUNTIME_DIR}/wayland-*" > /dev/null'
|
|
WAYLAND_DISPLAY="$(basename "$(compgen -G "${XDG_RUNTIME_DIR}/wayland-*" | grep -v lock | head -1)")"
|
|
wait_for "sway IPC" '[[ -S "${SWAYSOCK}" ]]'
|
|
export WAYLAND_DISPLAY SWAYSOCK
|
|
dbus-update-activation-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XDG_RUNTIME_DIR XDG_CONFIG_HOME SWAYSOCK
|
|
/usr/lib/xdg-desktop-portal-wlr > "${rig_dir}/xdpw.log" 2>&1 & pids+=($!)
|
|
echo "${pids[-1]}" > "${rig_dir}/xdpw.pid"
|
|
/usr/lib/xdg-desktop-portal -r > "${rig_dir}/xdp.log" 2>&1 & pids+=($!)
|
|
video_driver=wayland
|
|
|
|
if [[ "${mode}" == x11 ]]; then
|
|
wait_for Xwayland '[[ -s "${rig_dir}/xwayland-display" ]]'
|
|
DISPLAY="$(cat "${rig_dir}/xwayland-display")"
|
|
export DISPLAY XDG_SESSION_TYPE=x11
|
|
wait_for Xwayland 'xwininfo -root > /dev/null 2>&1'
|
|
unset WAYLAND_DISPLAY
|
|
video_driver=x11
|
|
fi
|
|
|
|
# A moving 60 FPS picture with a 440 Hz tone, standing in for a game.
|
|
SDL_VIDEODRIVER="${video_driver}" ffplay -loglevel error -fs -window_title clipbin-test-game \
|
|
-f lavfi "testsrc2=size=${width}x${height}:rate=60,format=yuv420p[out0];sine=frequency=440:sample_rate=48000[out1]" \
|
|
> "${rig_dir}/game.log" 2>&1 & pids+=($!)
|
|
echo "${pids[-1]}" > "${rig_dir}/game.pid"
|
|
if [[ "${mode}" == x11 ]]; then
|
|
wait_for "the test window" 'xwininfo -name clipbin-test-game > /dev/null 2>&1'
|
|
CLIPBIN_RIG_GAME_WINDOW="$(xwininfo -name clipbin-test-game | awk '/Window id:/ { print strtonum($4) }')"
|
|
export CLIPBIN_RIG_GAME_WINDOW
|
|
fi
|
|
sleep 1
|
|
|
|
export CLIPBIN_RIG_DIR="${rig_dir}"
|
|
echo "rig: ready (${mode}, logs in ${rig_dir})" >&2
|
|
status=0
|
|
"$@" || status=$?
|
|
|
|
exit "${status}"
|