From aacd610b68891ce2482d1ecac8b820b85e4a4c62 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Tue, 21 Jul 2026 19:21:38 +0200 Subject: [PATCH] fix(steamdeck/install): set up controller passthrough + surface the required reboot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A fresh SteamOS host install failed on-glass two ways: KWin denied Desktop-mode capture (zkde_screencast_unstable_v1) so every session died in the pipeline build, and the native Steam Deck pad degraded to an Xbox 360 controller. Both stem from setup that only a fresh login applies — the host user-service keeps the pre-install group set and KWin reads its .desktop grant at session start — plus a missing vhci-hcd autoload the pad's USB/IP transport needs (the deb/rpm/arch packages ship it; the Deck installer did not). - install.sh: install punktfunk-modules.conf (+ modprobe vhci-hcd now) for the native Deck controller transport, seed the KDE RemoteDesktop grant (kde-authorized) for Desktop-mode input, and print a loud "reboot before streaming" notice when a relogin is actually required (input group added or the .desktop grant first installed). - update.sh: retrofit the udev rule, vhci-hcd autoload, input group, and grant so existing installs pick them up on the next update without a reinstall. - steamos-host.md: document the first-install reboot and the native controller passthrough requirements (input group + vhci-hcd + client-side Steam Input Off). Co-Authored-By: Claude Opus 4.8 (1M context) --- docs-site/content/docs/steamos-host.md | 20 +++++++++- scripts/steamdeck/install.sh | 52 +++++++++++++++++++++++--- scripts/steamdeck/update.sh | 26 +++++++++++++ 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/docs-site/content/docs/steamos-host.md b/docs-site/content/docs/steamos-host.md index 21dcae33..d0a3da46 100644 --- a/docs-site/content/docs/steamos-host.md +++ b/docs-site/content/docs/steamos-host.md @@ -58,8 +58,10 @@ It is idempotent — safe to re-run. In one pass it: 1. creates the `pf2` Debian-trixie distrobox and installs the build toolchain, 2. builds `punktfunk-host` (and the web console), 3. writes config to `~/.config/punktfunk/` (a generated web-console login password), -4. raises the UDP socket buffers to 32 MB and adds you to the `input` group (needs `sudo`; skipped - with a warning if unavailable), +4. raises the UDP socket buffers to 32 MB, installs the gamepad udev rule + the `vhci-hcd` autoload + and adds you to the `input` group (virtual gamepads / **native Steam Deck controller passthrough**), + and seeds the KDE RemoteDesktop grant for Desktop-mode input (needs `sudo`; skipped with a warning + if unavailable), 5. installs + starts the `punktfunk-host` and `punktfunk-web` **systemd user services** (with linger, so they run without a login session). @@ -82,6 +84,14 @@ When it finishes it prints the web-console URL and how to pair. > If you only ever use native clients, install with `--no-gamestream` for a host with no GameStream > surface at all. +> **First install — reboot once before streaming.** KWin only authorizes Desktop-mode screen capture +> on a fresh session, and the new `input` group (native Steam Deck controller passthrough) only takes +> effect on a new login — so after the **first** install, **reboot the Deck** (a re-run that changes +> nothing doesn't need it). Streaming **Game Mode** with a generic Xbox pad works right away; **Desktop +> capture and the native Steam Deck controller need the reboot.** If a client connects and every +> session ends with `KWin does not expose zkde_screencast_unstable_v1` or the pad shows up as an Xbox +> 360 controller, you haven't rebooted yet. + ## 3. Pair a device By default the host **requires PIN pairing** (secure). Two ways to pair: @@ -128,6 +138,12 @@ bash ~/punktfunk/scripts/steamdeck/update.sh thrash the managed session. Pick one mode per session. - **Keep the device awake.** On handhelds, Game Mode auto-suspends on idle, which drops the host off the network mid stream — disable auto-suspend (Settings → Power) for a headless host. +- **Native Steam Deck controller passthrough** presents the client's pad as a real Steam Deck + controller (paddles, trackpads, gyro) via a virtual USB device — that needs the `input` group and the + `vhci-hcd` module live, so it only works **after the first-install reboot** above; until then the pad + degrades to a generic Xbox 360 controller (still fully playable). If you're streaming *to* another + Steam Deck, also set Steam Input to **Off** for Punktfunk on that Deck — see + [Stream to a Steam Deck](/docs/steam-deck). - **It survives OS updates**, but a major SteamOS bump can move library versions; if the host fails to start after an update, just re-run `update.sh` to rebuild against the new base. - Deeper reference (services, container, manual steps): [`scripts/steamdeck/README.md`](https://git.unom.io/unom/punktfunk/src/branch/main/scripts/steamdeck/README.md). diff --git a/scripts/steamdeck/install.sh b/scripts/steamdeck/install.sh index fe630a31..148bc545 100755 --- a/scripts/steamdeck/install.sh +++ b/scripts/steamdeck/install.sh @@ -48,6 +48,9 @@ BIN="$TARGET_DIR/release/punktfunk-host" CONFIG="$HOME/.config/punktfunk" UNITS="$HOME/.config/systemd/user" XRD="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" +# Set when this run does something that only a fresh login picks up (input-group add, first-time +# KWin .desktop grant). Drives the loud "reboot before streaming" note in the summary. +NEED_RELOGIN=0 # --- 0. preflight ---------------------------------------------------------- log "Preflight" @@ -148,11 +151,29 @@ fi # the restricted Wayland globals it lists (see packaging/linux/io.unom.Punktfunk.Host.desktop). # Exec must therefore be THIS install's binary path, not the packaged /usr/bin one. KWin reads # grants at session start — after first install, restart the Desktop session (Game Mode and back). +DESKTOP_DST="$HOME/.local/share/applications/io.unom.Punktfunk.Host.desktop" +# First-time install of the grant: KWin only reads it at session start, so a fresh login is required +# before Desktop-mode capture works. A re-run that just rewrites it needs no relogin. +[ -f "$DESKTOP_DST" ] || NEED_RELOGIN=1 mkdir -p "$HOME/.local/share/applications" -sed "s|^Exec=.*|Exec=$BIN|" "$SRC/packaging/linux/io.unom.Punktfunk.Host.desktop" \ - > "$HOME/.local/share/applications/io.unom.Punktfunk.Host.desktop" +sed "s|^Exec=.*|Exec=$BIN|" "$SRC/packaging/linux/io.unom.Punktfunk.Host.desktop" > "$DESKTOP_DST" ok "KWin desktop-capture authorization (io.unom.Punktfunk.Host.desktop → $BIN)" +# KDE Desktop-mode INPUT: a normal Plasma login lacks the RemoteDesktop portal grant the host's libei +# input path needs, so it would pop an "Allow remote control?" dialog a headless host can't answer. +# Seed it once (per-user, no root) — mirrors packaging/bazzite/kde-desktop-setup.sh. Game Mode +# (gamescope) needs none of this; the .desktop above already grants org_kde_kwin_fake_input. +GRANT_SRC="$SRC/scripts/headless/kde-authorized" +GRANT_DST="$HOME/.local/share/flatpak/db/kde-authorized" +if [ -s "$GRANT_DST" ]; then + ok "KDE RemoteDesktop grant already present" +elif [ -s "$GRANT_SRC" ]; then + mkdir -p "$(dirname "$GRANT_DST")" + install -m644 "$GRANT_SRC" "$GRANT_DST" + systemctl --user restart xdg-permission-store 2>/dev/null || true + ok "seeded KDE RemoteDesktop grant (Desktop-mode input)" +fi + if [ "$WITH_WEB" = 1 ] && [ ! -f "$CONFIG/web.env" ]; then # Random login password + session secret for the web console, generated once. # `|| true` swallows the SIGPIPE `tr` takes when `head` closes the pipe (pipefail would abort). @@ -169,7 +190,7 @@ else fi # --- 4. system tuning (needs sudo; skipped gracefully if unavailable) ------ -log "System tuning (UDP buffers + input group) — needs sudo" +log "System tuning (UDP buffers + gamepad rules + vhci-hcd + input group) — needs sudo" if sudo -n true 2>/dev/null; then printf 'net.core.wmem_max=33554432\nnet.core.rmem_max=33554432\n' \ | sudo tee /etc/sysctl.d/99-punktfunk-net.conf >/dev/null @@ -178,9 +199,23 @@ if sudo -n true 2>/dev/null; then if [ -f "$SRC/scripts/60-punktfunk.rules" ]; then sudo install -m644 "$SRC/scripts/60-punktfunk.rules" /etc/udev/rules.d/60-punktfunk.rules sudo udevadm control --reload-rules && sudo udevadm trigger || true - ok "installed udev rule (virtual gamepads)" + ok "installed udev rule (virtual gamepads + native Steam Deck controller)" + fi + # vhci-hcd: the usbip transport that makes the virtual Steam Deck pad a *real* USB device so Steam + # Input adopts it (else it degrades to plain UHID, which Steam ignores — "no controller appears"). + # Persist the autoload AND load it now so passthrough works without waiting for a reboot. + if [ -f "$SRC/scripts/punktfunk-modules.conf" ]; then + sudo install -m644 "$SRC/scripts/punktfunk-modules.conf" /etc/modules-load.d/punktfunk.conf + sudo modprobe vhci-hcd 2>/dev/null || warn "could not load vhci-hcd now (loads on next boot) — needed for the native Steam Deck pad" + ok "vhci-hcd autoload installed (native Steam Deck controller transport)" + fi + if id -nG "$USER" | grep -qw input; then + ok "already in the 'input' group" + else + sudo usermod -aG input "$USER" + NEED_RELOGIN=1 + warn "added $USER to the 'input' group (applies on next login)" fi - id -nG "$USER" | grep -qw input || { sudo usermod -aG input "$USER"; warn "added $USER to 'input' group — log out/in (or reboot) for gamepad support"; } else warn "passwordless sudo unavailable — skipping UDP-buffer + udev tuning." warn "Without it, high-bitrate streaming drops packets. Apply manually later:" @@ -265,3 +300,10 @@ else echo " • Pairing required (secure default). From a client, pick this host and enter the PIN the host shows." fi echo " • Update later: bash $SRC/scripts/steamdeck/update.sh" +if [ "$NEED_RELOGIN" = 1 ]; then + echo + warn "ONE MORE STEP before streaming — reboot the Deck (or fully log out and back in)." + echo " KWin only authorizes Desktop-mode screen capture on a fresh session, and the new 'input'" + echo " group (native Steam Deck controller passthrough) only applies to a new login. Streaming" + echo " Game Mode with a generic Xbox pad works now; Desktop capture + the native Deck pad need the reboot." +fi diff --git a/scripts/steamdeck/update.sh b/scripts/steamdeck/update.sh index 80eed916..4b45c545 100755 --- a/scripts/steamdeck/update.sh +++ b/scripts/steamdeck/update.sh @@ -44,6 +44,32 @@ sed "s|^Exec=.*|Exec=$TARGET_DIR/release/punktfunk-host|" "$SRC/packaging/linux/ > "$HOME/.local/share/applications/io.unom.Punktfunk.Host.desktop" ok "KWin desktop-capture authorization refreshed" +# Retrofit the system bits install.sh now sets up but older installs predate (idempotent; the sudo +# parts are skipped if passwordless sudo isn't available). vhci-hcd = usbip transport for the native +# Steam Deck pad; 60-punktfunk.rules = /dev/uhid + vhci access; input group = uhid write; the +# kde-authorized grant (per-user, no root) = Desktop-mode input. A newly-added input group still needs +# a re-login to apply. +if sudo -n true 2>/dev/null; then + if [ -f "$SRC/scripts/60-punktfunk.rules" ]; then + sudo install -m644 "$SRC/scripts/60-punktfunk.rules" /etc/udev/rules.d/60-punktfunk.rules + sudo udevadm control --reload-rules >/dev/null 2>&1 || true + sudo udevadm trigger >/dev/null 2>&1 || true + fi + if [ -f "$SRC/scripts/punktfunk-modules.conf" ]; then + sudo install -m644 "$SRC/scripts/punktfunk-modules.conf" /etc/modules-load.d/punktfunk.conf + sudo modprobe vhci-hcd 2>/dev/null || true + ok "vhci-hcd autoload ensured (native Steam Deck controller)" + fi + id -nG "$USER" | grep -qw input || { sudo usermod -aG input "$USER"; ok "added $USER to 'input' group — log out/in for it to apply"; } +fi +GRANT_SRC="$SRC/scripts/headless/kde-authorized" +GRANT_DST="$HOME/.local/share/flatpak/db/kde-authorized" +if [ ! -s "$GRANT_DST" ] && [ -s "$GRANT_SRC" ]; then + mkdir -p "$(dirname "$GRANT_DST")" + install -m644 "$GRANT_SRC" "$GRANT_DST" + ok "seeded KDE RemoteDesktop grant (Desktop-mode input)" +fi + log "Restarting services" systemctl --user restart punktfunk-host.service ok "punktfunk-host restarted"