feat(protocol,clients): codec preference negotiation + Linux client decodes per Welcome (Phase 2a)
Adds a client-selectable **preferred codec** and wires the core + ABI + probe + Linux client to
negotiate and decode it. (Windows/Apple/Android follow in 2b.)
**Core:**
- `Hello.preferred_codec` (a single CODEC_* bit, 0 = auto) — a soft hint appended after
`video_codecs`. `resolve_codec(client, host, preferred)` now honors the preference when the host
can also emit it, else falls back to precedence (HEVC > AV1 > H.264). Roundtrip + preference tests.
- `NativeClient::connect` takes `video_codecs` + `preferred_codec`; `NativeClient.codec` exposes the
resolved `Welcome.codec`.
- ABI: `punktfunk_connect_ex7` (adds the two codec params; `ex6` delegates to it advertising
HEVC-only) + `punktfunk_connection_codec` getter + `PUNKTFUNK_CODEC_{H264,HEVC,AV1}` constants
(drift-guarded against the wire values). Header regenerated.
**Host:** passes `hello.preferred_codec` into `resolve_codec`.
**probe:** `--codec h264|hevc|av1|auto` sets the preference (still advertises it can decode all
three); the dump extension already follows the resolved codec.
**Linux client:** advertises the codecs FFmpeg can actually decode (`decodable_codecs()`), threads
the user's `codec` setting as the preference, and builds the decoder — both the software and VAAPI
paths, plus the mid-session VAAPI→software demotion — from the negotiated `Welcome.codec` instead of
hardcoding HEVC. New "Video codec" dropdown in Preferences (Automatic/HEVC/H.264/AV1).
Live-validated on the dev box: probe `--codec hevc` against a software (H.264-only) host resolves to
H.264 (graceful soft-preference fallback), no failure. clippy + core (57) + host (133) tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ DualSense a near-native feel with **no external gamepad dependencies** (no ViGEm
|
||||
Shipping: the driver is one member of the in-tree driver workspace
|
||||
([`packaging/windows/drivers/`](../../README.md)), built from source in CI, and bundled +
|
||||
`pnputil`-installed by the Windows host [installer](../../README.md). The host feeds it over a shared
|
||||
memory channel from `crates/punktfunk-host/src/inject/dualsense_windows.rs`. The same UMDF driver also
|
||||
memory channel from `crates/punktfunk-host/src/inject/windows/dualsense_windows.rs`. The same UMDF driver also
|
||||
serves the **DualShock 4** identity per a `device_type` byte the host stamps.
|
||||
|
||||
This README captures the driver-authoring lore — the bugs and the signing recipe that make a
|
||||
@@ -37,7 +37,9 @@ $env:LIBCLANG_PATH = 'C:\Program Files\LLVM\bin'
|
||||
$env:Version_Number = '10.0.26100.0' # else wdk-build picks 10.0.28000.0 (no km/crt) and bindgen fails
|
||||
```
|
||||
|
||||
Then, in the example dir:
|
||||
The shipping flow is `build-gamepad-drivers.ps1` (one level up): workspace `cargo build --release`
|
||||
plus the sign steps below, staged for the installer. The original manual dev-box recipe, kept as
|
||||
lore (paths reflect that era's cargo-make layout):
|
||||
|
||||
```powershell
|
||||
cargo make # -> target\debug\pf_dualsense_package\ (.inf/.cat/.dll)
|
||||
@@ -45,7 +47,8 @@ cargo make # -> target\debug\pf_dualsense_package\
|
||||
# *** CRITICAL: clear the PE FORCE_INTEGRITY bit ***
|
||||
# windows-drivers-rs links the DLL with /INTEGRITYCHECK, which forces a CI-trusted page-hash
|
||||
# signature a self-signed cert cannot satisfy (CodeIntegrity 3004 "hash not found" /
|
||||
# 3089 VerificationError 7). SudoVDA.dll has this bit OFF. Clear bit 0x80 at PE-header offset +0x5e:
|
||||
# 3089 VerificationError 7). SudoVDA.dll (third-party VDD prior art, not used by punktfunk) has
|
||||
# this bit OFF. Clear bit 0x80 at PE-header offset +0x5e:
|
||||
$f = 'target\debug\pf_dualsense_package\pf_dualsense.dll'
|
||||
$b = [IO.File]::ReadAllBytes($f); $pe = [BitConverter]::ToInt32($b,0x3c); $off = $pe + 0x5e
|
||||
$dc = [BitConverter]::ToUInt16($b,$off); $bb = [BitConverter]::GetBytes([uint16]($dc -band 0xFF7F))
|
||||
@@ -60,9 +63,10 @@ pnputil /add-driver target\debug\pf_dualsense_package\pf_dualsense.inf /install
|
||||
devgen /add /hardwareid "root\pf_dualsense" # creates the (transient, SWD) device node
|
||||
```
|
||||
|
||||
`devgen` is at `...\Windows Kits\10\Tools\10.0.26100.0\x64\devgen.exe`. SWD devgen devices clear on
|
||||
reboot (recreate after each boot). TODO: drop the post-build PE patch by stopping wdk-build emitting
|
||||
`/INTEGRITYCHECK`.
|
||||
`devgen` (under `Windows Kits\10\Tools\<ver>\x64\`) is only for manual testing — the shipping
|
||||
install is `punktfunk-host.exe driver install --gamepad`, and the host SwDeviceCreate's the device
|
||||
per session (no persistent devnode). SWD devgen devices clear on reboot. TODO: drop the post-build
|
||||
PE patch by stopping wdk-build emitting `/INTEGRITYCHECK`.
|
||||
|
||||
## The three bugs that made it work (porting a WDK C sample to Rust)
|
||||
|
||||
|
||||
@@ -43,19 +43,21 @@ Y `0x8000`). `dwPacketNumber` (GET_STATE `[5]`) must increment whenever the payl
|
||||
@8` · `LT @10` · `RT @11` · `LX/LY/RX/RY i16 @12/@14/@16/@18` · `rumble_seq u32 @24` (driver bumps) ·
|
||||
`large @28` · `small @29`.
|
||||
|
||||
## Validated live on `.173` (2026-06-22)
|
||||
## Validated live (2026-06-22, maintainer's RTX test box)
|
||||
|
||||
`XInputGetState(0)` returns **CONNECTED** with the pushed buttons/sticks and an incrementing
|
||||
`dwPacketNumber`; `XInputSetState(0xC000, 0x4000)` reaches the driver as `00 00 c0 40 02` → host sees
|
||||
`large=192 small=64`. Test tools: `C:\Users\Public\giprobe\xusbtest.exe` (creates the `pf_xusb`
|
||||
`large=192 small=64`. Test tools (on that box): `xusbtest.exe` (creates the `pf_xusb`
|
||||
devnode + cycling state via shm) and `xinputtest.exe` (`XInputGetState`/`SetState` harness).
|
||||
|
||||
## Build / sign / install (same recipe as the DualSense driver)
|
||||
|
||||
Built from `C:\Users\Public\m0\windows-drivers-rs\examples\pf-xusb` (the `../../crates` paths resolve
|
||||
there); these repo files are the canonical copies — keep them in sync.
|
||||
Built as a member of the in-tree [`packaging/windows/drivers/`](../) workspace — one
|
||||
`cargo build --release` builds all three drivers; `build-gamepad-drivers.ps1` (one level up) wraps
|
||||
the whole build/sign/stage flow in CI. The manual steps:
|
||||
|
||||
1. `cargo make` (env `LIBCLANG_PATH`, `Version_Number=10.0.26100.0`) → `target\debug\pf_xusb_package\`.
|
||||
1. `cargo build --release` in the workspace (env `LIBCLANG_PATH`, `Version_Number=10.0.26100.0`) →
|
||||
`target\x86_64-pc-windows-msvc\release\pf_xusb.dll`.
|
||||
2. Clear the FORCE_INTEGRITY PE bit (bit `0x80` at `e_lfanew+0x5e` of `pf_xusb.dll`).
|
||||
3. `signtool sign /fd SHA256 /sha1 6A52984E54376C45A1C236B1A2C8A746C5AB6131 pf_xusb.dll`.
|
||||
4. `Inf2Cat /driver:<pkg> /os:10_X64` → re-sign `pf_xusb.cat` with the same thumbprint.
|
||||
@@ -63,11 +65,12 @@ there); these repo files are the canonical copies — keep them in sync.
|
||||
|
||||
## Host integration (done)
|
||||
|
||||
`crates/punktfunk-host/src/inject/gamepad_windows.rs` is the Windows `GamepadManager` (used by
|
||||
`crates/punktfunk-host/src/inject/windows/gamepad_windows.rs` is the Windows `GamepadManager` (used by
|
||||
`PadBackend::Xbox360`): it SwDeviceCreate's the `pf_xusb` companion, maps `pfxusb-shm-<index>`, writes
|
||||
the XInput state from the client's gamepad frame (already XInput-convention) and forwards rumble. There
|
||||
is **no ViGEmBus dependency** anymore. The driver is built from source (`packaging/windows/drivers/pf-xusb`),
|
||||
signed, and pnputil-installed by the Inno Setup installer (via `install-gamepad-drivers.ps1`).
|
||||
is **no ViGEmBus dependency** anymore. The driver is built + signed from source in CI
|
||||
(`build-gamepad-drivers.ps1`) and installed by the Inno Setup installer via
|
||||
`punktfunk-host.exe driver install --gamepad`.
|
||||
|
||||
## Multi-pad
|
||||
|
||||
|
||||
Reference in New Issue
Block a user