Add Debian/Ubuntu support via a thin distro dispatch layer
Adds a parallel install path for Debian/Ubuntu hosts alongside the existing
Arch/Omarchy/Hyprland one. The Arch path is untouched at runtime; everything
new is gated on $DISTRO and (for headless) $COMPOSITOR.
Highlights:
- lib/distro.sh: detect_distro + pkg_install/pkg_remove/ca_anchor_path/
ca_update_trust dispatch helpers
- lib/packages.sh: Ubuntu sunshine install pulls LizardByte's official .deb
from GitHub releases (override via SUNSHINE_DEB_URL/SUNSHINE_DEB_VERSION);
GPU encoder packages branch per $DISTRO:$GPU_VENDOR
- bin/sunshine-stream-{do,undo,prestart}-sway.sh + files/sway-headless.*:
swaymsg-based headless capture path for hosts without Hyprland. sway runs
under a systemd-user unit that sunshine.service depends on via drop-in.
- lib/preflight.sh: clearer NVIDIA driver guidance on Ubuntu (we don't install
the driver - too many branch/kernel/Secure-Boot variants); sway-aware
headless preflight
- lib/certs.sh + lib/verify.sh + uninstall.sh: distro-aware CA trust anchor
(Arch: /etc/ca-certificates/trust-source/anchors + update-ca-trust;
Debian: /usr/local/share/ca-certificates + update-ca-certificates)
Verified on Ubuntu 24.04: ./install.sh --doctor --headless loads cleanly,
distro/GPU/compositor detection report the right values, all pre-install
failures correspond to the actual missing pieces.
This commit is contained in:
112
bin/sunshine-stream-do-sway.sh
Executable file
112
bin/sunshine-stream-do-sway.sh
Executable file
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env bash
|
||||
# Sunshine global_prep_cmd `do` hook for the Sway-based headless capture path
|
||||
# (Debian/Ubuntu installs where Hyprland isn't available).
|
||||
#
|
||||
# On client connect:
|
||||
# - Ensures a HEADLESS-1 output exists on the running sway session
|
||||
# - Resizes it to the client's negotiated mode (WxH@FPS)
|
||||
# - Snapshots state for the undo hook
|
||||
#
|
||||
# Sunshine env vars set on connect:
|
||||
# SUNSHINE_CLIENT_WIDTH, SUNSHINE_CLIENT_HEIGHT, SUNSHINE_CLIENT_FPS
|
||||
#
|
||||
# This script intentionally mirrors the Hyprland do-hook's shape so debugging
|
||||
# transfers across the two paths.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
WIDTH="${SUNSHINE_CLIENT_WIDTH:-1920}"
|
||||
HEIGHT="${SUNSHINE_CLIENT_HEIGHT:-1080}"
|
||||
FPS="${SUNSHINE_CLIENT_FPS:-60}"
|
||||
HEADLESS_NAME="${OMARCHY_VIRTUAL_OUTPUT:-HEADLESS-1}"
|
||||
STATE_DIR="${XDG_RUNTIME_DIR:-/tmp}/sunshine-headless"
|
||||
mkdir -p "$STATE_DIR"
|
||||
|
||||
HOOK_LOG="$STATE_DIR/hook.log"
|
||||
: > "$HOOK_LOG"
|
||||
log() {
|
||||
local msg
|
||||
msg="$(date +%H:%M:%S.%3N) [sunshine-do-sway] $*"
|
||||
printf '%s\n' "$msg" >&2
|
||||
printf '%s\n' "$msg" >> "$HOOK_LOG"
|
||||
}
|
||||
log "do-hook start: client=${WIDTH}x${HEIGHT}@${FPS} target=${HEADLESS_NAME}"
|
||||
|
||||
if ! command -v swaymsg >/dev/null 2>&1; then
|
||||
log "swaymsg not found; nothing to configure."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Recover SWAYSOCK if the unit env didn't propagate it. sway writes the socket
|
||||
# path into a predictable /run/user/$UID location, but the env var is the
|
||||
# clean handle.
|
||||
if [[ -z "${SWAYSOCK:-}" ]]; then
|
||||
for sock in "${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"/sway-ipc.*.sock; do
|
||||
[[ -S "$sock" ]] || continue
|
||||
export SWAYSOCK="$sock"
|
||||
log "Discovered SWAYSOCK=$SWAYSOCK"
|
||||
break
|
||||
done
|
||||
if [[ -z "${SWAYSOCK:-}" ]]; then
|
||||
log "sway not running (no IPC socket found); nothing to configure."
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# True if an output named $HEADLESS_NAME currently exists on the session.
|
||||
_headless_present() {
|
||||
swaymsg -t get_outputs -r 2>/dev/null \
|
||||
| jq -e --arg n "$HEADLESS_NAME" '.[] | select(.name == $n)' >/dev/null
|
||||
}
|
||||
|
||||
# Create the headless output if missing. Sway's `create_output` accepts an
|
||||
# optional name; without one it auto-assigns HEADLESS-N like Hyprland does.
|
||||
if ! _headless_present; then
|
||||
log "Creating headless output $HEADLESS_NAME"
|
||||
if ! swaymsg create_output "$HEADLESS_NAME" >/dev/null 2>&1; then
|
||||
# Older sway versions ignore the name argument; fall back and rename via
|
||||
# output detection after the fact.
|
||||
swaymsg create_output >/dev/null 2>&1 || true
|
||||
fi
|
||||
# Poll briefly for the output to appear.
|
||||
for _ in 1 2 3 4 5; do
|
||||
_headless_present && break
|
||||
sleep 0.1
|
||||
done
|
||||
if ! _headless_present; then
|
||||
# Last-ditch: take the highest-numbered HEADLESS-* that exists and treat
|
||||
# it as ours. Update HEADLESS_NAME in-memory so the resize below targets it.
|
||||
found="$(swaymsg -t get_outputs -r 2>/dev/null \
|
||||
| jq -r '[.[].name | select(startswith("HEADLESS-"))] | sort_by(.) | last // empty')"
|
||||
if [[ -n "$found" ]]; then
|
||||
HEADLESS_NAME="$found"
|
||||
log "Adopted existing headless output: $HEADLESS_NAME"
|
||||
else
|
||||
log "Failed to create a headless output; stream will rely on whatever Sunshine selects."
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Snapshot state so undo can put things back. We don't move workspaces around
|
||||
# on a headless-only box (there is no other monitor), but we still record what
|
||||
# was active in case the user runs sway with a real display attached.
|
||||
swaymsg -t get_outputs -r > "$STATE_DIR/prev-outputs.json" 2>/dev/null || true
|
||||
echo "$HEADLESS_NAME" > "$STATE_DIR/headless-name"
|
||||
|
||||
# Resize the headless output. Sway accepts mode strings as "WIDTHxHEIGHT@FPSHz".
|
||||
log "Sizing $HEADLESS_NAME → ${WIDTH}x${HEIGHT}@${FPS}Hz"
|
||||
if ! swaymsg output "$HEADLESS_NAME" mode "${WIDTH}x${HEIGHT}@${FPS}Hz" >/dev/null 2>&1; then
|
||||
log "Mode set with refresh rate failed; retrying without refresh"
|
||||
swaymsg output "$HEADLESS_NAME" mode "${WIDTH}x${HEIGHT}" >/dev/null 2>&1 || \
|
||||
log "Mode set failed; sway will keep the previous mode."
|
||||
fi
|
||||
|
||||
# Focus the headless output so window placement lands there.
|
||||
swaymsg focus output "$HEADLESS_NAME" >/dev/null 2>&1 || true
|
||||
|
||||
post="$(swaymsg -t get_outputs -r 2>/dev/null \
|
||||
| jq -r '.[] | "\(.name) \(.current_mode.width)x\(.current_mode.height)@\(.current_mode.refresh) focused=\(.focused)"' \
|
||||
| tr '\n' ';' || true)"
|
||||
log "post-state outputs: $post"
|
||||
log "Stream ready: ${WIDTH}x${HEIGHT}@${FPS} on $HEADLESS_NAME"
|
||||
Reference in New Issue
Block a user