Add runtime status checker + headless/X11 docs; distro-support refinements
- status.sh: runtime health check (service state, boot wiring, display backend auto-detect, encoder, ports, web UI, /dev/uinput, pairing) ending in a g2g verdict or concrete TODO list
- docs: TROUBLESHOOTING §12 (headless graphical-session.target boot trap) + §13 (X11/NVENC path & stale wlr drop-in env conflict); ARCHITECTURE capture-backend comparison; FOLLOWUPS P3 (installer X11/Ubuntu support); README diagnostics pointer
- lib/{config,packages,permissions,service}.sh, files/sway-headless.service: in-progress Debian/Ubuntu + headless support refinements
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,10 +70,10 @@ $encoder_block
|
||||
min_threads = 4
|
||||
|
||||
# Audio sink is intentionally left unset so Sunshine auto-detects the default
|
||||
# PulseAudio/PipeWire sink and creates its virtual `sink-sunshine-stereo`.
|
||||
# PulseAudio/PipeWire sink and creates its virtual 'sink-sunshine-stereo'.
|
||||
# Hard-coding audio_sink = pulse here breaks capture: Sunshine treats it as a
|
||||
# literal sink name, can't resolve its monitor source, and pa_simple_new()
|
||||
# fails with "Invalid argument" → no audio in the stream.
|
||||
# fails with "Invalid argument" -> no audio in the stream.
|
||||
|
||||
# Keyboard / mouse / gamepad pass-through via /dev/uinput.
|
||||
# (Requires user to be in the 'input' group; install.sh handles this.)
|
||||
|
||||
@@ -108,11 +108,11 @@ _ubuntu_sunshine_deb_url() {
|
||||
}
|
||||
|
||||
_install_sunshine_debian() {
|
||||
# Universal runtime deps. libva-utils gives `vainfo`; jq is used by hooks.
|
||||
# Universal runtime deps. vainfo is what Ubuntu calls libva-utils on Arch.
|
||||
# pipewire-pulse is the Ubuntu 24.04+ default audio path; on older releases
|
||||
# `pulseaudio-utils` works too — we don't force the codename split since
|
||||
# sunshine just needs *a* PulseAudio API endpoint.
|
||||
pkg_install jq vulkan-tools libva-utils curl ca-certificates
|
||||
pkg_install jq vulkan-tools vainfo curl ca-certificates
|
||||
|
||||
if pkg_installed sunshine; then
|
||||
ok "sunshine already installed (dpkg)"
|
||||
@@ -198,15 +198,36 @@ install_gpu_encoder_packages() {
|
||||
yay_install intel-media-driver vulkan-intel
|
||||
;;
|
||||
debian:nvidia)
|
||||
# On Ubuntu, the proprietary driver is usually already installed via
|
||||
# `ubuntu-drivers autoinstall` or the Server install path. Don't force a
|
||||
# specific nvidia-* version — they vary by release / driver branch.
|
||||
# Pull only the userspace VAAPI bridge if available; harmless if missing.
|
||||
pkg_install libnvidia-encode-no-dkms 2>/dev/null \
|
||||
|| pkg_install libnvidia-encode-575 2>/dev/null \
|
||||
|| pkg_install libnvidia-encode-565 2>/dev/null \
|
||||
|| pkg_install libnvidia-encode-560 2>/dev/null \
|
||||
|| info "NVENC userspace library not found via a known package name — relying on the existing driver install."
|
||||
# Ubuntu's nvidia-driver-NNN(-server) metapackage pulls in the matching
|
||||
# libnvidia-encode-NNN(-server) as a dependency, so NVENC is normally
|
||||
# already present. Only intervene if it's missing — and derive the right
|
||||
# package name from the loaded driver's major version instead of
|
||||
# guessing.
|
||||
if dpkg-query -W -f='${Status}\n' 'libnvidia-encode-*' 2>/dev/null \
|
||||
| grep -q '^install ok installed$'; then
|
||||
ok "NVENC runtime library already installed via the driver metapackage"
|
||||
else
|
||||
local drv_major drv_full
|
||||
drv_full="$(nvidia-smi --query-gpu=driver_version --format=csv,noheader,nounits 2>/dev/null | head -n1)"
|
||||
drv_major="${drv_full%%.*}"
|
||||
if [[ -z "$drv_major" ]]; then
|
||||
warn "Could not detect NVIDIA driver version; NVENC may be missing."
|
||||
else
|
||||
# Try -server first (cloud GPUs usually run server branches), then the
|
||||
# consumer branch. Stop at the first one apt knows about.
|
||||
local picked=""
|
||||
for cand in "libnvidia-encode-${drv_major}-server" "libnvidia-encode-${drv_major}"; do
|
||||
if apt-cache show "$cand" >/dev/null 2>&1; then
|
||||
picked="$cand"; break
|
||||
fi
|
||||
done
|
||||
if [[ -n "$picked" ]]; then
|
||||
pkg_install "$picked"
|
||||
else
|
||||
warn "No libnvidia-encode-${drv_major}* package in apt — install it manually if NVENC fails."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
debian:amd)
|
||||
pkg_install mesa-va-drivers mesa-vulkan-drivers vainfo
|
||||
|
||||
@@ -8,13 +8,33 @@ UINPUT_RULE_PATH="/etc/udev/rules.d/60-uinput.rules"
|
||||
UINPUT_RULE_CONTENT='KERNEL=="uinput", SUBSYSTEM=="misc", OPTIONS+="static_node=uinput", TAG+="uaccess", OWNER="root", GROUP="input", MODE="0660"'
|
||||
|
||||
ensure_input_group() {
|
||||
if id -nG "$USER" | tr ' ' '\n' | grep -qx input; then
|
||||
ok "User '$USER' already in 'input' group"
|
||||
_ensure_user_in_group input
|
||||
# On Debian/Ubuntu, /dev/dri/renderD* nodes are mode 0660 owned by
|
||||
# root:render, and /dev/dri/card* are root:video. Sway's wlroots renderer
|
||||
# needs the render node (Vulkan/GLES FD); KMS capture needs the card node.
|
||||
# Arch typically grants both via udev tag=uaccess for the logged-in seat,
|
||||
# so we only add explicit memberships on Debian.
|
||||
if [[ "${DISTRO:-}" == "debian" ]]; then
|
||||
_ensure_user_in_group render
|
||||
_ensure_user_in_group video
|
||||
fi
|
||||
}
|
||||
|
||||
# Internal: add $USER to a group if it exists and they're not already in it.
|
||||
_ensure_user_in_group() {
|
||||
local g="$1"
|
||||
if ! getent group "$g" >/dev/null 2>&1; then
|
||||
info "Group '$g' does not exist on this system — skipping."
|
||||
return 0
|
||||
fi
|
||||
info "Adding '$USER' to 'input' group"
|
||||
as_root usermod -aG input "$USER"
|
||||
warn "You must log out and back in (or run 'newgrp input') for this to take effect."
|
||||
if id -nG "$USER" | tr ' ' '\n' | grep -qx "$g"; then
|
||||
ok "User '$USER' already in '$g' group"
|
||||
return 0
|
||||
fi
|
||||
info "Adding '$USER' to '$g' group"
|
||||
as_root usermod -aG "$g" "$USER"
|
||||
warn "Group '$g' change takes effect on next login (or 'newgrp $g'). For systemd-user"
|
||||
warn "services, you must fully log out and back in so the user manager restarts."
|
||||
}
|
||||
|
||||
ensure_uinput_udev_rule() {
|
||||
|
||||
@@ -4,23 +4,46 @@
|
||||
# Sway-headless capture path, also installs + enables sway-headless.service
|
||||
# and wires sunshine.service to depend on it.
|
||||
|
||||
# Resolves the actual unit file to operate on. Exports:
|
||||
# SUNSHINE_ENABLE_NAME - the name to pass to `systemctl --user enable`. May
|
||||
# be sunshine.service or app-dev.lizardbyte....service
|
||||
# depending on how the package shipped the unit.
|
||||
# SUNSHINE_SERVICE - the short name we use everywhere else (start, restart,
|
||||
# status, drop-ins). Always sunshine.service — systemd
|
||||
# resolves it via Alias= or our installed unit.
|
||||
ensure_sunshine_unit_present() {
|
||||
# Case 1: a sunshine.service unit already exists in any path systemd-user
|
||||
# scans. The LizardByte .deb on Ubuntu drops it at /lib/systemd/user/.
|
||||
# sunshine-bin on Arch drops it at /usr/lib/systemd/user/.
|
||||
SUNSHINE_SERVICE="sunshine.service"
|
||||
|
||||
# Clean up a broken symlink from older runs that pointed sunshine.service
|
||||
# at the FQDN unit. The upstream .deb already declares Alias=sunshine.service
|
||||
# in [Install], so the symlink we used to create conflicts with systemd's
|
||||
# enable path ("Refusing to operate on alias name").
|
||||
local user_unit="$HOME/.config/systemd/user/sunshine.service"
|
||||
if [[ -L "$user_unit" ]] && readlink "$user_unit" 2>/dev/null \
|
||||
| grep -q 'app-dev.lizardbyte.app.Sunshine.service$'; then
|
||||
info "Removing stale alias symlink at $user_unit (upstream unit declares Alias=)"
|
||||
rm -f "$user_unit"
|
||||
fi
|
||||
|
||||
# Case 1: a real sunshine.service unit ships in a system path (sunshine-bin
|
||||
# on Arch drops it at /usr/lib/systemd/user/).
|
||||
for p in \
|
||||
/lib/systemd/user/sunshine.service \
|
||||
/usr/lib/systemd/user/sunshine.service \
|
||||
/etc/systemd/user/sunshine.service \
|
||||
"$HOME/.config/systemd/user/sunshine.service" \
|
||||
"$HOME/.local/share/systemd/user/sunshine.service"
|
||||
do
|
||||
[[ -e "$p" ]] && return 0
|
||||
if [[ -f "$p" && ! -L "$p" ]]; then
|
||||
SUNSHINE_ENABLE_NAME="sunshine.service"
|
||||
export SUNSHINE_ENABLE_NAME SUNSHINE_SERVICE
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Case 2: the AUR source 'sunshine' package ships the unit under a
|
||||
# Flatpak-style reverse-DNS name. Symlink it as sunshine.service so the rest
|
||||
# of our tooling can keep using the short name.
|
||||
# Case 2: the LizardByte .deb (Ubuntu) and the AUR source package ship the
|
||||
# unit under a reverse-DNS FQDN with Alias=sunshine.service in [Install].
|
||||
# Do NOT symlink — `systemctl --user enable` on the FQDN name creates the
|
||||
# alias symlink itself in ~/.config/systemd/user/.
|
||||
local fqdn_unit=""
|
||||
for p in \
|
||||
/usr/lib/systemd/user/app-dev.lizardbyte.app.Sunshine.service \
|
||||
@@ -30,10 +53,9 @@ ensure_sunshine_unit_present() {
|
||||
[[ -f "$p" ]] && { fqdn_unit="$p"; break; }
|
||||
done
|
||||
if [[ -n "$fqdn_unit" ]]; then
|
||||
info "Found packaged unit at $fqdn_unit"
|
||||
info "Aliasing it as sunshine.service in $HOME/.config/systemd/user/"
|
||||
mkdir -p "$HOME/.config/systemd/user"
|
||||
ln -sf "$fqdn_unit" "$HOME/.config/systemd/user/sunshine.service"
|
||||
info "Found packaged unit at $fqdn_unit (enables via alias)"
|
||||
SUNSHINE_ENABLE_NAME="app-dev.lizardbyte.app.Sunshine.service"
|
||||
export SUNSHINE_ENABLE_NAME SUNSHINE_SERVICE
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -47,6 +69,8 @@ ensure_sunshine_unit_present() {
|
||||
mkdir -p "$HOME/.config/systemd/user"
|
||||
install -m 0644 "$fallback" "$HOME/.config/systemd/user/sunshine.service"
|
||||
ok "Installed $HOME/.config/systemd/user/sunshine.service"
|
||||
SUNSHINE_ENABLE_NAME="sunshine.service"
|
||||
export SUNSHINE_ENABLE_NAME SUNSHINE_SERVICE
|
||||
}
|
||||
|
||||
# Install and enable the headless sway compositor unit + config (Debian/Ubuntu
|
||||
@@ -114,8 +138,8 @@ enable_sunshine_service() {
|
||||
install_headless_prestart_dropin
|
||||
fi
|
||||
|
||||
if ! systemctl --user list-unit-files sunshine.service >/dev/null 2>&1; then
|
||||
err "sunshine.service still not found after fallback. Inspect: find /usr /lib ~/.config -name sunshine.service"
|
||||
if ! systemctl --user list-unit-files "$SUNSHINE_ENABLE_NAME" >/dev/null 2>&1; then
|
||||
err "$SUNSHINE_ENABLE_NAME not found after fallback. Inspect: find /usr /lib ~/.config -name '*[Ss]unshine*'"
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -126,8 +150,8 @@ enable_sunshine_service() {
|
||||
ok "User lingering already enabled"
|
||||
fi
|
||||
|
||||
info "Enabling sunshine.service (user)"
|
||||
systemctl --user enable sunshine.service >/dev/null
|
||||
info "Enabling ${SUNSHINE_ENABLE_NAME} (user)"
|
||||
systemctl --user enable "$SUNSHINE_ENABLE_NAME" >/dev/null
|
||||
|
||||
# Clear any prior start-limit state from a failed run so this attempt isn't
|
||||
# immediately rejected with "Start request repeated too quickly."
|
||||
|
||||
Reference in New Issue
Block a user