From df496776b04b91d59ee3850f67575f30620f5e84 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Sat, 4 Jul 2026 10:06:48 +0000 Subject: [PATCH] =?UTF-8?q?fix(client-linux):=20Deck=20raw-pad=20capture?= =?UTF-8?q?=20=E2=80=94=20clear=20Steam's=20SDL=20device=20filter,=20hones?= =?UTF-8?q?t=20degradation=20warning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Deck's built-in controller can never leave Steam Input ("Steam Controller" is always-required in the shortcut's matrix; Disable Steam Input only affects other controller brands), so the raw 28DE:1205 device is the only path to the trackpads/paddles/gyro. Steam hides it from SDL by launching shortcuts with SDL_GAMECONTROLLER_IGNORE_DEVICES naming every physical pad it virtualized — clear it (and _EXCEPT) at startup while single-threaded, logging what Steam set as field evidence. The post-attach warning now states the real condition (raw pad never enumerated; sticks + buttons still work) instead of advising a Steam Input toggle that doesn't exist for the built-in controller. Co-Authored-By: Claude Fable 5 --- clients/linux/src/app.rs | 17 +++++++++++++++++ clients/linux/src/launch.rs | 28 ++++++++++++++++------------ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/clients/linux/src/app.rs b/clients/linux/src/app.rs index 13fceb9..cbde161 100644 --- a/clients/linux/src/app.rs +++ b/clients/linux/src/app.rs @@ -116,6 +116,23 @@ pub fn run() -> glib::ExitCode { tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()), ) .init(); + // Steam launches its shortcuts with SDL_GAMECONTROLLER_IGNORE_DEVICES naming every + // physical pad Steam Input has virtualized — SDL then hides the real device so games + // only see the virtual X360 pad. Right for games, wrong for us: capturing the Deck's + // built-in controller (trackpads/paddles/gyro, 28DE:1205) needs SDL's HIDAPI driver + // to enumerate the REAL device, and the built-in pad can never leave Steam Input + // ("Steam Controller" is always-required), so this filter is the only off switch we + // get. Clear it while still single-threaded (the gamepad worker starts with the UI); + // we dedupe the virtual pad ourselves (`gamepad.rs` `active_id` skips steam_virtual). + for var in [ + "SDL_GAMECONTROLLER_IGNORE_DEVICES", + "SDL_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT", + ] { + if let Ok(v) = std::env::var(var) { + tracing::info!(var, value = %v, "clearing Steam's SDL device filter"); + std::env::remove_var(var); + } + } // Headless pairing path (no GTK window): `--pair --connect host[:port] [--name N]`. // Used by the Decky plugin (a GTK dialog can't pop under gamescope) and for scripting. if let Some(pin) = crate::cli::arg_value("--pair") { diff --git a/clients/linux/src/launch.rs b/clients/linux/src/launch.rs index d9019ea..ecf036f 100644 --- a/clients/linux/src/launch.rs +++ b/clients/linux/src/launch.rs @@ -280,12 +280,15 @@ impl SessionUi { if self.app.fullscreen || self.app.settings.borrow().fullscreen_on_stream { self.app.window.fullscreen(); } - // Steam Input owning the Deck's built-in controller is invisible degradation: SDL - // then sees only Steam's virtual X360 pad, so the right trackpad arrives at the - // host as a plain right stick (Steam's default template) and the left trackpad, - // paddles and gyro not at all. The real 28DE:1205 identity enumerates shortly - // after attach (the Valve HIDAPI drivers are session-scoped) — check once that - // settles and say so, instead of streaming silently degraded. + // A Deck streaming without its raw built-in controller is invisible degradation: + // SDL sees only Steam's virtual X360 pad, so the right trackpad arrives at the + // host as whatever Steam's template synthesizes (a right stick by default) and + // the left trackpad, paddles and gyro not at all. The built-in pad can never + // leave Steam Input ("Steam Controller" is always-required in the shortcut's + // matrix — Disable Steam Input only affects other brands), so raw capture rides + // the session-scoped Valve HIDAPI drivers + the cleared SDL device filter (see + // `app::run`). The real 28DE:1205 identity enumerates shortly after attach — + // check once that settles and say so, instead of streaming silently degraded. if crate::gamepad::is_steam_deck() { let app = self.app.clone(); let stop = self.stop.clone(); @@ -295,14 +298,15 @@ impl SessionUi { } if app.gamepad.active().is_none_or(|pad| pad.steam_virtual) { tracing::warn!( - "Steam Input is holding the built-in controller — trackpads, \ - paddles and gyro won't reach the host (right pad degrades to a \ - right stick). Disable Steam Input for Punktfunk to fix this." + "the Deck's raw built-in controller (28DE:1205) never enumerated \ + — only Steam's virtual pad is visible, so trackpads, paddles and \ + gyro can't be captured (sticks + buttons still work). Check the \ + startup log for SDL_GAMECONTROLLER_IGNORE_DEVICES and the \ + Settings controller list." ); let toast = adw::Toast::new( - "Steam Input is holding the Deck's controls — trackpads, paddles \ - and gyro won't reach the game. Fix: Punktfunk → controller \ - settings → Disable Steam Input.", + "Steam is only exposing its virtual gamepad — trackpads, paddles \ + and gyro won't reach the game (sticks and buttons still work).", ); toast.set_timeout(12); app.toasts.add_toast(toast);