#!/usr/bin/env bash # Preflight: catch problems before we install anything. # Fail fast and loud, with remediation hints. preflight_all() { preflight_session preflight_remote_session preflight_gpu preflight_kms_modeset preflight_audio preflight_headless } preflight_session() { if [[ "${SESSION_TYPE:-}" != "wayland" ]]; then warn "Session type '$SESSION_TYPE' — KMS capture works at the DRM level so it can still succeed," warn "but this installer's defaults assume Hyprland on Wayland." else ok "Wayland session detected" fi } preflight_remote_session() { # Sunshine's user service needs the local Wayland session to be reachable. # If we're over pure SSH (no forwarded DBUS / WAYLAND_DISPLAY), the service start # at the end of the install will fail. Warn now so the user knows. if [[ -n "${SSH_CONNECTION:-}" && -z "${WAYLAND_DISPLAY:-}" && -z "${DISPLAY:-}" ]]; then warn "Running over SSH without a graphical session. Packages will install," warn "but the systemd --user service will only come up cleanly once you log in" warn "to Hyprland on the console. That's fine — just log in afterwards." fi } preflight_gpu() { case "$GPU_VENDOR" in nvidia) if ! command -v nvidia-smi >/dev/null 2>&1; then case "$DISTRO" in arch) warn "nvidia-smi not found yet — nvidia-utils will be installed shortly." ;; debian) # On Ubuntu the NVIDIA driver install isn't our job; we don't pull # in nvidia-driver-* because the right version depends on the # kernel / Secure Boot / cloud-vendor combo. Tell the user. err "nvidia-smi not found and no NVIDIA kernel module loaded." err "Install the driver before re-running this installer. Common paths on Ubuntu:" err " sudo ubuntu-drivers install # picks the recommended branch" err " sudo apt install nvidia-driver-550-server # explicit pin" err "Then reboot (or modprobe nvidia) so 'nvidia-smi -L' returns the GPU." exit 1 ;; esac return 0 fi if ! nvidia-smi -L >/dev/null 2>&1; then err "nvidia-smi exists but can't talk to the driver. Driver not loaded?" err "Check: 'lsmod | grep nvidia' and 'dmesg | grep -i nvidia'" exit 1 fi ok "NVIDIA driver responsive ($(nvidia-smi -L | head -n1))" ;; amd) if ! lsmod | grep -q '^amdgpu'; then warn "amdgpu kernel module not loaded. Hardware encode (VAAPI) will likely fail until it is." else ok "amdgpu kernel module loaded" fi ;; intel) ok "Intel GPU — encoder packages will be installed in the packages step." ;; esac } preflight_kms_modeset() { # KMS capture needs nvidia-drm.modeset=1 on NVIDIA. Driver 560+ defaults to 1, # but if the user is on something older or has explicitly set 0, capture will fail. [[ "$GPU_VENDOR" == "nvidia" ]] || return 0 # Cmdline check is read-only and reliable when explicitly set. if grep -q 'nvidia-drm.modeset=1' /proc/cmdline; then ok "nvidia-drm.modeset=1 set on kernel cmdline" return 0 fi if grep -q 'nvidia-drm.modeset=0' /proc/cmdline; then err "nvidia-drm.modeset=0 is set on the kernel cmdline. KMS capture WILL fail." err "Remove it (e.g., from /boot/loader/entries/*.conf or /etc/kernel/cmdline + reinstall-kernels)" err "and reboot before running this installer." exit 1 fi # Not on cmdline — check the runtime parameter. On 560+ this defaults to Y. # /sys file is root-readable; modinfo gives the default without sudo. local default="" if command -v modinfo >/dev/null 2>&1; then default="$(modinfo -F parm nvidia_drm 2>/dev/null | awk -F: '/^modeset:/ {print $2}' | head -n1 || true)" fi if [[ -n "$default" ]]; then info "nvidia_drm modeset default per modinfo: ${default## }" fi warn "nvidia-drm.modeset=1 not explicitly set on cmdline." warn "Modern drivers (≥560) default to enabled, so this is usually fine — but if KMS" warn "capture fails after install, add 'nvidia-drm.modeset=1' to your kernel cmdline." } preflight_audio() { # Sunshine captures audio via the PulseAudio API; on Omarchy that's pipewire-pulse. if pkg_installed pipewire-pulse; then ok "pipewire-pulse present (audio capture path OK)" else warn "pipewire-pulse is not installed. Audio capture may not work until it is." fi } preflight_headless() { # Only relevant in headless mode. Checks are non-fatal: install can proceed # even if the compositor isn't reachable right now (hooks just won't # function until it is). [[ "${STREAM_MODE:-}" == "headless" ]] || return 0 case "${COMPOSITOR:-none}" in hyprland) ok "hyprctl on PATH" if [[ -n "${HYPRLAND_INSTANCE_SIGNATURE:-}" ]]; then ok "Hyprland instance signature present in environment" else local rt="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" if compgen -G "$rt/hypr/*/" >/dev/null 2>&1; then ok "Hyprland runtime directory found under $rt/hypr/" else warn "Hyprland not currently running. Install will proceed; hooks engage on next Hyprland login." fi fi ;; sway) ok "swaymsg/sway on PATH" local rt="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" if compgen -G "$rt/sway-ipc.*.sock" >/dev/null 2>&1; then ok "Sway IPC socket present under $rt" else info "Sway not running yet — install will start sway-headless.service before sunshine." fi ;; none|*) warn "No wlroots compositor detected. Install will attempt to install one (Sway on Debian/Ubuntu)." ;; esac if pkg_installed jq; then ok "jq installed (prep-cmd hooks have their parser)" else info "jq not installed yet — will be installed in the packages step." fi }