forked from unom/punktfunk
On a Deck in Gaming Mode the Steam menu and the QAM are driven by the SAME physical controller the client forwards, so opening either one moved the game on the host as well as Steam's UI — a second, invisible player. Steam Input masks a normal game here; it cannot mask us, because masking happens on Steam Input's virtual pad and we deliberately forward the REAL one (28DE:1205 — the virtual pad has no gyro, trackpads or paddles). SDL ships the exact behaviour we want and it is on by default: presses are dropped while the process has windows but no keyboard focus, releases still get through. It CANNOT fire on a Deck. gamescope resolves focus per Xwayland ctx and the client sits alone in its own, so the Steam overlay — which lives in the root ctx — never takes our X focus away and no FocusOut is ever generated. Measured on glass: with the QAM open, X input focus inside the client's ctx stayed on its window for the whole 4 s, while GAMESCOPE_FOCUSED_APP flipped to 769 (Steam) and GAMESCOPE_FOCUSED_APP_GFX stayed on the app. So the signal is explicit. `overlay_focus` watches those two atoms on the gamescope root ctx — which is NOT our own $DISPLAY under `--xwayland-count 2`, hence the socket-directory walk and the flatpak filesystem line — and the presenter ORs it with window focus into one `set_masked`. Masking is deliberately not `set_forwarding`: that closes the slot and sends GamepadRemove, so the game would see a controller UNPLUG every time somebody opened the QAM. This keeps every slot open and only stops the transitions, after flushing what the host believes is held so a stick deflected at overlay-open stops steering instead of freezing at its last value. On the way back, held buttons are adopted rather than replayed — the A that picked a QAM row must not fire in the game as it closes — while axes are re-sent, since a stick has no press to ghost and SDL only speaks on change. Fails open throughout: no gamescope, no X, or an unreadable signal all leave forwarding exactly as it was. `PUNKTFUNK_OVERLAY_MASK=0` opts out.
121 lines
6.3 KiB
Rust
121 lines
6.3 KiB
Rust
//! Shared, UI-agnostic client plumbing, extracted verbatim from the GTK client
|
|
//! (design: punktfunk-planning `linux-client-rearchitecture.md`, Phase 0) so the desktop
|
|
//! shells and the Vulkan session binary build on one implementation — on Linux AND
|
|
//! Windows (the session binary runs on both; macOS stays `wol`-only, clients/apple is
|
|
//! the client there).
|
|
//!
|
|
//! Nothing here may depend on a UI toolkit: the presenter contract is `session`'s
|
|
//! channels (`SessionHandle`) and `video`'s `DecodedImage` (RGBA bytes, dmabuf fds +
|
|
//! plane layout, or a decoded VkImage) — how frames reach the screen is the consumer's
|
|
//! business.
|
|
//!
|
|
//! Audio is the one per-OS module swap: `audio.rs` (PipeWire) on Linux,
|
|
//! `audio_wasapi.rs` (WASAPI) on Windows — same public surface, picked here by `#[path]`
|
|
//! so `crate::audio` is the only name the session pump ever sees. `keymap` (evdev-keyed)
|
|
//! stays Linux: the session path uses pf-presenter's SDL-scancode table instead.
|
|
|
|
// Unsafe-proof program: every `unsafe {}` / `unsafe impl` in this crate carries a `// SAFETY:`
|
|
// proof of why it is sound. This crate held ~91 unsafe items with NO enforcement while every
|
|
// other subsystem crate denied it — the decoders' `unsafe impl Send`s had a one-line aside
|
|
// instead of an argument precisely because nothing required one.
|
|
#![deny(clippy::undocumented_unsafe_blocks)]
|
|
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
mod au_dump;
|
|
#[cfg(target_os = "linux")]
|
|
pub mod audio;
|
|
#[cfg(windows)]
|
|
#[path = "audio_wasapi.rs"]
|
|
pub mod audio;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod discovery;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod gamepad;
|
|
#[cfg(target_os = "linux")]
|
|
pub mod keymap;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod library;
|
|
// The `punktfunk://` grammar (design/client-deep-links.md §2): one parser/emitter for the
|
|
// shells, the session and the CLI, held to the Swift/Kotlin ports by a shared vector file.
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod deeplink;
|
|
// The brain layer (design/client-architecture-split.md §3): what a connect is, the wake
|
|
// state machine every front-end drives, and the session spawn + stdout contract.
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod orchestrate;
|
|
// The host's OS-identity chain (mDNS `os=` TXT): sanitize + icon-walk order. Pure string
|
|
// logic, built everywhere (the Apple/Android ports mirror it rather than link it).
|
|
pub mod os;
|
|
// "A system overlay owns the controller" for gamescope Gaming Mode — the signal behind the
|
|
// gamepad input mask, which SDL's own focus gate structurally cannot provide there.
|
|
#[cfg(target_os = "linux")]
|
|
pub mod overlay_focus;
|
|
// Client settings profiles: the override catalog + the one connect-time resolver
|
|
// (design/client-settings-profiles.md §4). Sits beside `trust`, which owns the host records
|
|
// the bindings live on.
|
|
// Pad audio (the 0xD1 plane): DualSense voice-coil haptics + speaker rendered on the wired
|
|
// physical pad's own 4-ch audio device — correlation, the per-session renderer worker, and
|
|
// the tier-A pad registry the gamepad worker feeds it through.
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod pad_audio;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod profiles;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod session;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod trust;
|
|
// "Is a newer client available, and can this box install it?" — the client half of the
|
|
// signed-manifest update check the host already runs (design: host-update-from-web-console.md).
|
|
// Linux only: the Windows client ships inside the host installer and the Mac one through
|
|
// clients/apple, so neither has a package to reason about here.
|
|
#[cfg(target_os = "linux")]
|
|
pub mod update;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod video;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
mod video_color;
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
mod video_software;
|
|
// Native VAAPI decode (M6 of the native-decode program): pf-vaadec's plans driven
|
|
// straight into libva, dlopen'd at runtime, exporting DRM-PRIME dmabufs the presenter
|
|
// imports. Since M10 it is the ONLY VAAPI rung there is — the libavcodec one it
|
|
// replaced is deleted — so `auto` reaches it wherever the vendor order puts VAAPI
|
|
// first; `PUNKTFUNK_DECODER=native-vaapi` reaches it by pin regardless. See `video`'s
|
|
// evidence table for what hardware has actually run it.
|
|
#[cfg(target_os = "linux")]
|
|
pub mod video_vaapi_native;
|
|
// Native Vulkan Video decode (WP-C of the native-decode program, HEVC added by M3
|
|
// WP-2, AV1 by M7): pf-vkdecode's H.264/H.265/AV1 decoders on the presenter's shared
|
|
// device — auto's TOP rung on both desktop OSes since M9, for all three codecs (each
|
|
// leg has hardware parity against libavcodec; see `video`'s evidence table), also
|
|
// pinnable via `PUNKTFUNK_DECODER=native-vulkan`.
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
mod video_vk_native;
|
|
// The OS-clipboard bridge for the shared clipboard (design/clipboard-and-file-transfer.md §5).
|
|
// Built everywhere the session client is; the platform seam inside is Windows-real,
|
|
// stub elsewhere.
|
|
#[cfg(any(target_os = "linux", windows))]
|
|
pub mod clipboard;
|
|
// PyroWave decode — Linux + Windows (plan §4.5; the Apple Metal port is its own phase).
|
|
// Windows joined once its client moved to the SAME spawned Vulkan session presenter as
|
|
// Linux's: the decoder is plain Vulkan compute on the presenter's device (no fds, no
|
|
// dmabuf, no D3D11 interop), so the old "Windows present-path decision" that gated it
|
|
// resolved itself — the present path is now literally the same code.
|
|
// D3D11 decode-device plumbing: the shareable-texture hand-off ring, the decode-device
|
|
// creation and `display_hdr_volume`. Field-proven, FFmpeg-free code that
|
|
// `video_d3d11_native` (and `clients/session`) build on; the libavcodec DECODER that used
|
|
// to live alongside it went with M10's excision.
|
|
#[cfg(windows)]
|
|
pub mod video_d3d11;
|
|
// Native D3D11VA (M5): `ID3D11VideoDecoder` driven from pf-bitstream plans, filling the
|
|
// hand-off ring `video_d3d11` owns. Since M10 it is the only DXVA rung there is. In `auto`
|
|
// for all three codecs, each of which now has hardware evidence — H.264/H.265 since M5, AV1
|
|
// since 2026-08-07 — see `video`'s evidence table; `PUNKTFUNK_DECODER=native-d3d11va`
|
|
// reaches every leg by pin.
|
|
#[cfg(windows)]
|
|
pub mod video_d3d11_native;
|
|
#[cfg(all(any(target_os = "linux", windows), feature = "pyrowave"))]
|
|
pub mod video_pyrowave;
|
|
|
|
pub mod wol;
|