diff --git a/clients/linux/src/launch.rs b/clients/linux/src/launch.rs index cb4d8cf..d9019ea 100644 --- a/clients/linux/src/launch.rs +++ b/clients/linux/src/launch.rs @@ -280,6 +280,35 @@ 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. + if crate::gamepad::is_steam_deck() { + let app = self.app.clone(); + let stop = self.stop.clone(); + glib::timeout_add_seconds_local_once(4, move || { + if stop.load(std::sync::atomic::Ordering::Relaxed) { + return; // session already over + } + 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." + ); + 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.", + ); + toast.set_timeout(12); + app.toasts.add_toast(toast); + } + }); + } self.page = Some(p); } diff --git a/clients/linux/src/video.rs b/clients/linux/src/video.rs index fff9b69..0c8beb1 100644 --- a/clients/linux/src/video.rs +++ b/clients/linux/src/video.rs @@ -187,6 +187,25 @@ impl Decoder { .ok() .filter(|v| !v.is_empty()) .unwrap_or_else(|| pref.to_string()); + // The Steam Deck's VAAPI zero-copy path renders corrupt/gray/washed-out — validated live; + // software decode is clean, correct-colour, and the Deck's APU handles 1280×800 HEVC + // easily. Likely cause: since Mesa 25.1 radeonsi exports VCN decode surfaces TILED (with + // AMD modifiers) instead of linear, and inside the Flatpak both the VAAPI driver and GTK's + // GL come from the runtime's Mesa 26.x — GTK's tiled-NV12 dmabuf import mishandles the new + // layout (desktop AMD/Intel boxes validated Tier-1 ran distro Mesa with linear export). + // So `auto` resolves to software on a Deck; an explicit `vaapi` (Settings or + // PUNKTFUNK_DECODER=vaapi) still forces the hw path for testing — the first-frame + // descriptor dump logs the modifier (LINEAR = 0x0), and GSK_RENDERER=ngl|vulkan bisects + // the import side. + let choice = if (choice == "auto" || choice.is_empty()) && crate::gamepad::is_steam_deck() { + tracing::info!( + "Steam Deck — defaulting to software decode (AMD VAAPI dmabuf is broken on this \ + SteamOS+Mesa combo); set the decoder to `vaapi` to override" + ); + "software".to_string() + } else { + choice + }; if choice != "software" { match VaapiDecoder::new(codec_id) { Ok(v) => { @@ -456,6 +475,14 @@ impl VaapiDecoder { (*ctx).get_format = Some(pick_vaapi); (*ctx).flags |= ffi::AV_CODEC_FLAG_LOW_DELAY as i32; (*ctx).thread_count = 1; // hwaccel: threads only add latency + + // The presenter holds mapped surfaces PAST receive_frame (the paintable's + // current texture + the newest frame in flight each pin one until GDK's + // release func) — surfaces libavcodec doesn't know are missing from its + // fixed-size VAAPI pool. Without headroom the decoder can recycle a surface + // the renderer is still sampling (intermittent block corruption) or fail + // allocation under scheduling jitter. + (*ctx).extra_hw_frames = 4; let r = ffi::avcodec_open2(ctx, codec, ptr::null_mut()); if r < 0 { let mut ctx = ctx; diff --git a/crates/punktfunk-host/src/inject/linux/steam_controller.rs b/crates/punktfunk-host/src/inject/linux/steam_controller.rs index 04cefbc..79373da 100644 --- a/crates/punktfunk-host/src/inject/linux/steam_controller.rs +++ b/crates/punktfunk-host/src/inject/linux/steam_controller.rs @@ -276,12 +276,43 @@ impl DeckTransport { } } +/// One-shot diagnostic: InputPlumber (shipped and enabled by default on Bazzite) hidraw-grabs +/// controllers it decides to manage and re-emits them under a different identity — historically +/// the Deck config re-emitted an Xbox Elite pad with the trackpads routed to a mouse target. If +/// it grabs our virtual Deck, everything downstream of hid-steam looks wrong (trackpads surface +/// as a stick/mouse, gyro vanishes) while punktfunk's own logs stay clean — so name the suspect +/// up front. Best-effort process-name scan; no dependency on its D-Bus API. +fn warn_if_inputplumber() { + use std::sync::atomic::{AtomicBool, Ordering}; + static ONCE: AtomicBool = AtomicBool::new(true); + if !ONCE.swap(false, Ordering::Relaxed) { + return; + } + let running = std::fs::read_dir("/proc") + .ok() + .into_iter() + .flatten() + .flatten() + .any(|e| { + std::fs::read_to_string(e.path().join("comm")).is_ok_and(|c| c.trim() == "inputplumber") + }); + if running { + tracing::warn!( + "InputPlumber is running on this host — if it manages the virtual Steam Deck pad, \ + games see InputPlumber's re-emitted device instead (trackpads may arrive as a \ + stick/mouse, gyro may vanish). Check `inputplumber devices` and exclude the \ + virtual pad from management if inputs look remapped." + ); + } +} + /// Open the best Steam-Input-promotable Deck transport available, in preference order: /// **`raw_gadget` (SteamOS validated fast-path) → `usbip`/`vhci_hcd` (universal, Secure-Boot-clean) /// → UHID (universal, but `Interface: -1` so Steam Input won't promote it).** Each rung degrades to /// the next on failure, so a host lacking the gadget modules still gets a *promotable* Deck via /// usbip, and one lacking both still gets a working (if non-promoted) UHID pad. fn open_transport(idx: u8) -> Result { + warn_if_inputplumber(); use crate::inject::{steam_gadget, steam_usbip}; // 1. raw_gadget — the validated SteamOS fast-path (default on there). if steam_gadget::gadget_preferred() {