fix(steamdeck): install the plugin runner + harden Deck pad typing + repair update.sh
- scripts/steamdeck/install.sh (§2b) + update.sh: build and install the scripting runner user-scoped (~/.local wrapper + pinned bun + bundle) — SteamOS's read-only /usr can't take the .deb layout, so the console's plugin store reported "the plugin runner isn't installed" on every SteamOS host. update.sh retrofits it onto existing installs. - plugins.rs runner discovery: check the ~/.local layout after the /usr ones; mention the SteamOS path in the not-installed error. - update.sh: define warn() — it was used but never defined, so under `set -e` the first warn aborted the update before services restarted. - pf-client-core gamepad: a Steam Input virtual pad forwarded on a real Deck (the only-pad case) now declares the DECK kind in its per-pad arrival instead of the wrapper's Xbox 360 identity — the session-level auto_pref already resolved SteamDeck, but the arrival overrides it on current hosts. - reject tests: cover SetupFailed; move the foreign-code probe off 0x68. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -876,10 +876,16 @@ impl Worker {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let pref = self
|
let pref = match self.pad_info(id) {
|
||||||
.pad_info(id)
|
// Steam Input's virtual pad standing in front of the Deck's built-in controls (the
|
||||||
.map(|p| p.pref)
|
// only-pad-forwarded case, [`Self::forwarded_ids`]): declare the DECK kind, not the
|
||||||
.unwrap_or(GamepadPref::Xbox360);
|
// wrapper's Xbox 360 identity. [`Self::auto_pref`] already resolves the SESSION
|
||||||
|
// default this way, but a current host honors the per-pad arrival over the session
|
||||||
|
// default — so without this the host builds an X-Box 360 pad on a real Deck.
|
||||||
|
Some(p) if p.steam_virtual && is_steam_deck() => GamepadPref::SteamDeck,
|
||||||
|
Some(p) => p.pref,
|
||||||
|
None => GamepadPref::Xbox360,
|
||||||
|
};
|
||||||
match self.subsystem.open(sdl3::sys::joystick::SDL_JoystickID(id)) {
|
match self.subsystem.open(sdl3::sys::joystick::SDL_JoystickID(id)) {
|
||||||
Ok(pad) => {
|
Ok(pad) => {
|
||||||
let mut slot = Slot::new(id, index, pref, pad);
|
let mut slot = Slot::new(id, index, pref, pad);
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ impl std::fmt::Display for RejectReason {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const ALL: [RejectReason; 9] = [
|
const ALL: [RejectReason; 10] = [
|
||||||
RejectReason::PairingNotArmed,
|
RejectReason::PairingNotArmed,
|
||||||
RejectReason::PairingBoundToOtherDevice,
|
RejectReason::PairingBoundToOtherDevice,
|
||||||
RejectReason::PairingRateLimited,
|
RejectReason::PairingRateLimited,
|
||||||
@@ -156,6 +156,7 @@ mod tests {
|
|||||||
RejectReason::Superseded,
|
RejectReason::Superseded,
|
||||||
RejectReason::WireVersionMismatch,
|
RejectReason::WireVersionMismatch,
|
||||||
RejectReason::Busy,
|
RejectReason::Busy,
|
||||||
|
RejectReason::SetupFailed,
|
||||||
];
|
];
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -177,7 +178,7 @@ mod tests {
|
|||||||
fn foreign_codes_stay_untyped() {
|
fn foreign_codes_stay_untyped() {
|
||||||
// Bare closes, the client's own pair-done codes, and the deliberate-end codes must
|
// Bare closes, the client's own pair-done codes, and the deliberate-end codes must
|
||||||
// never read as a host rejection.
|
// never read as a host rejection.
|
||||||
for code in [0u32, 1, 0x41, 0x51, 0x52, 0x5f, 0x68, u32::MAX] {
|
for code in [0u32, 1, 0x41, 0x51, 0x52, 0x5f, 0x69, u32::MAX] {
|
||||||
assert_eq!(RejectReason::from_close_code(code), None);
|
assert_eq!(RejectReason::from_close_code(code), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -463,7 +463,7 @@ pub(crate) async fn serve(
|
|||||||
}
|
}
|
||||||
conn_err.close(
|
conn_err.close(
|
||||||
punktfunk_core::reject::SETUP_FAILED_CLOSE_CODE.into(),
|
punktfunk_core::reject::SETUP_FAILED_CLOSE_CODE.into(),
|
||||||
detail[..cut].as_bytes(),
|
&detail.as_bytes()[..cut],
|
||||||
);
|
);
|
||||||
tracing::warn!(%peer, error = %detail, "session ended with error")
|
tracing::warn!(%peer, error = %detail, "session ended with error")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,9 +158,25 @@ pub(crate) fn runner_command() -> Result<(std::path::PathBuf, Vec<String>)> {
|
|||||||
if bun.exists() && runner.exists() {
|
if bun.exists() && runner.exists() {
|
||||||
return Ok((bun, vec![runner.to_string_lossy().into_owned()]));
|
return Ok((bun, vec![runner.to_string_lossy().into_owned()]));
|
||||||
}
|
}
|
||||||
|
// Immutable-/usr distros (SteamOS): scripts/steamdeck/install.sh lays the SAME payload
|
||||||
|
// out user-scoped under ~/.local — wrapper, private bun, and bundle mirroring the deb's
|
||||||
|
// /usr layout — because a system package can't exist there.
|
||||||
|
if let Ok(home) = std::env::var("HOME") {
|
||||||
|
let home = std::path::Path::new(&home);
|
||||||
|
let wrapper = home.join(".local/bin/punktfunk-scripting");
|
||||||
|
if wrapper.exists() {
|
||||||
|
return Ok((wrapper, Vec::new()));
|
||||||
|
}
|
||||||
|
let bun = home.join(".local/lib/punktfunk-scripting/bun");
|
||||||
|
let runner = home.join(".local/share/punktfunk-scripting/runner-cli.js");
|
||||||
|
if bun.exists() && runner.exists() {
|
||||||
|
return Ok((bun, vec![runner.to_string_lossy().into_owned()]));
|
||||||
|
}
|
||||||
|
}
|
||||||
bail!(
|
bail!(
|
||||||
"the plugin runner isn't installed — install it first (Debian/Ubuntu: \
|
"the plugin runner isn't installed — install it first (Debian/Ubuntu: \
|
||||||
`sudo apt install punktfunk-scripting`)"
|
`sudo apt install punktfunk-scripting`; SteamOS: re-run \
|
||||||
|
scripts/steamdeck/install.sh)"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,6 +153,35 @@ cd '$SRC/web' && bun install --frozen-lockfile && bun run build
|
|||||||
ok "web console built"
|
ok "web console built"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# --- 2b. plugin runner (scripting) -----------------------------------------
|
||||||
|
# The console's plugin store and `punktfunk-host plugins …` shell out to the scripting runner —
|
||||||
|
# the SDK's runner CLI bundled to one self-contained JS, run on a pinned bun (it import()s the
|
||||||
|
# operator's .ts plugins; see packaging/debian/build-scripting-deb.sh). The .deb lays it out under
|
||||||
|
# /usr, which is read-only here, so ship the SAME payload user-scoped — wrapper + private bun +
|
||||||
|
# bundle under ~/.local, where the host's runner discovery looks after the /usr layouts.
|
||||||
|
log "Building the plugin runner (scripting)"
|
||||||
|
mkdir -p "$HOME/.local/bin" "$HOME/.local/lib/punktfunk-scripting" "$HOME/.local/share/punktfunk-scripting"
|
||||||
|
distrobox enter "$BOX" -- bash -lc "
|
||||||
|
set -e
|
||||||
|
export PATH=\$HOME/.bun/bin:\$PATH
|
||||||
|
cd '$SRC/sdk'
|
||||||
|
bun install --frozen-lockfile --ignore-scripts
|
||||||
|
bun build src/runner-cli.ts --target=bun --outfile \"\$HOME/.local/share/punktfunk-scripting/runner-cli.js\"
|
||||||
|
# Pin the runtime: copy the box's bun next to the bundle so a bun self-update (or a box rebuild)
|
||||||
|
# never changes what the runner executes under.
|
||||||
|
install -m0755 \"\$(command -v bun)\" \"\$HOME/.local/lib/punktfunk-scripting/bun\"
|
||||||
|
"
|
||||||
|
grep -q 'attempt=' "$HOME/.local/share/punktfunk-scripting/runner-cli.js" \
|
||||||
|
|| die "runner bundle missing the dynamic plugin import — wrong build"
|
||||||
|
cat > "$HOME/.local/bin/punktfunk-scripting" <<'WRAP'
|
||||||
|
#!/bin/sh
|
||||||
|
# Generated by scripts/steamdeck/install.sh — user-scoped punktfunk-scripting (the .deb's /usr/bin
|
||||||
|
# wrapper, relocated): the runner bundle on its private pinned bun.
|
||||||
|
exec "$HOME/.local/lib/punktfunk-scripting/bun" "$HOME/.local/share/punktfunk-scripting/runner-cli.js" "$@"
|
||||||
|
WRAP
|
||||||
|
chmod 0755 "$HOME/.local/bin/punktfunk-scripting"
|
||||||
|
ok "plugin runner: ~/.local/bin/punktfunk-scripting"
|
||||||
|
|
||||||
# --- 3. config -------------------------------------------------------------
|
# --- 3. config -------------------------------------------------------------
|
||||||
log "Configuration ($CONFIG)"
|
log "Configuration ($CONFIG)"
|
||||||
mkdir -p "$CONFIG"
|
mkdir -p "$CONFIG"
|
||||||
@@ -306,6 +335,13 @@ EOF
|
|||||||
ok "punktfunk-web.service (port $WEB_PORT)"
|
ok "punktfunk-web.service (port $WEB_PORT)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# The runner's user unit (OPT-IN, matching the .deb: installed but NOT enabled — the runner is
|
||||||
|
# inert until you add scripts/plugins). ExecStart is rewritten from the packaged /usr wrapper to
|
||||||
|
# the user-scoped one section 2b installed.
|
||||||
|
sed 's|^ExecStart=.*|ExecStart=%h/.local/bin/punktfunk-scripting|' \
|
||||||
|
"$SRC/scripts/punktfunk-scripting.service" > "$UNITS/punktfunk-scripting.service"
|
||||||
|
ok "punktfunk-scripting.service (opt-in: systemctl --user enable --now punktfunk-scripting)"
|
||||||
|
|
||||||
systemctl --user daemon-reload
|
systemctl --user daemon-reload
|
||||||
loginctl show-user "$USER" 2>/dev/null | grep -q 'Linger=yes' || { sudo loginctl enable-linger "$USER" 2>/dev/null && ok "enabled linger (services run without login)" || warn "could not enable linger — services stop when you log out (sudo loginctl enable-linger $USER)"; }
|
loginctl show-user "$USER" 2>/dev/null | grep -q 'Linger=yes' || { sudo loginctl enable-linger "$USER" 2>/dev/null && ok "enabled linger (services run without login)" || warn "could not enable linger — services stop when you log out (sudo loginctl enable-linger $USER)"; }
|
||||||
# enable + restart (not `enable --now`): restart picks up unit-file changes on a re-run, where
|
# enable + restart (not `enable --now`): restart picks up unit-file changes on a re-run, where
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
log() { printf '\033[1;36m==>\033[0m %s\n' "$*"; }
|
log() { printf '\033[1;36m==>\033[0m %s\n' "$*"; }
|
||||||
ok() { printf '\033[1;32m ok\033[0m %s\n' "$*"; }
|
ok() { printf '\033[1;32m ok\033[0m %s\n' "$*"; }
|
||||||
|
# warn was USED below but never defined — under `set -e` the first warn call ("command not
|
||||||
|
# found") aborted the whole update before the service restarts.
|
||||||
|
warn() { printf '\033[1;33m !!\033[0m %s\n' "$*" >&2; }
|
||||||
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
SRC="${PUNKTFUNK_SRC:-$HOME/punktfunk}"
|
SRC="${PUNKTFUNK_SRC:-$HOME/punktfunk}"
|
||||||
@@ -30,6 +33,25 @@ if [ "$WEB" = 1 ]; then
|
|||||||
ok "web rebuilt"
|
ok "web rebuilt"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Plugin runner (scripting): rebuild the user-scoped runner payload (install.sh §2b) — also
|
||||||
|
# RETROFITS it onto older installs that predate it (the "plugin runner isn't installed" console
|
||||||
|
# state on SteamOS).
|
||||||
|
log "Rebuilding plugin runner (scripting)"
|
||||||
|
mkdir -p "$HOME/.local/bin" "$HOME/.local/lib/punktfunk-scripting" "$HOME/.local/share/punktfunk-scripting"
|
||||||
|
distrobox enter "$BOX" -- bash -lc "set -e; export PATH=\$HOME/.bun/bin:\$PATH; cd '$SRC/sdk' && bun install --frozen-lockfile --ignore-scripts && bun build src/runner-cli.ts --target=bun --outfile \"\$HOME/.local/share/punktfunk-scripting/runner-cli.js\" && install -m0755 \"\$(command -v bun)\" \"\$HOME/.local/lib/punktfunk-scripting/bun\""
|
||||||
|
grep -q 'attempt=' "$HOME/.local/share/punktfunk-scripting/runner-cli.js" \
|
||||||
|
|| die "runner bundle missing the dynamic plugin import — wrong build"
|
||||||
|
cat > "$HOME/.local/bin/punktfunk-scripting" <<'WRAP'
|
||||||
|
#!/bin/sh
|
||||||
|
# Generated by scripts/steamdeck/update.sh — user-scoped punktfunk-scripting (see install.sh §2b).
|
||||||
|
exec "$HOME/.local/lib/punktfunk-scripting/bun" "$HOME/.local/share/punktfunk-scripting/runner-cli.js" "$@"
|
||||||
|
WRAP
|
||||||
|
chmod 0755 "$HOME/.local/bin/punktfunk-scripting"
|
||||||
|
sed 's|^ExecStart=.*|ExecStart=%h/.local/bin/punktfunk-scripting|' \
|
||||||
|
"$SRC/scripts/punktfunk-scripting.service" > "$HOME/.config/systemd/user/punktfunk-scripting.service"
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
ok "plugin runner rebuilt (opt-in service: systemctl --user enable --now punktfunk-scripting)"
|
||||||
|
|
||||||
# Retrofit config that install.sh now writes but older installs predate (both idempotent):
|
# Retrofit config that install.sh now writes but older installs predate (both idempotent):
|
||||||
# RADV_PERFTEST — Van Gogh RADV still gates VK_KHR_video_encode_* behind it; without it the
|
# RADV_PERFTEST — Van Gogh RADV still gates VK_KHR_video_encode_* behind it; without it the
|
||||||
# Vulkan backend can't open and sessions silently fall back to libav VAAPI. The KWin .desktop —
|
# Vulkan backend can't open and sessions silently fall back to libav VAAPI. The KWin .desktop —
|
||||||
|
|||||||
Reference in New Issue
Block a user