Much of design/ described work that has since shipped. Trim each doc to
its durable rationale + still-open items (the code is the source of truth
for shipped detail; git history holds the full originals).
- Shipped plans -> status stubs: stats-capture, gamestream-host-plan,
apple-stage2-presenter, windows-service.
- Trimmed completed-out / open-kept: implementation-plan, hdr-pipeline,
host-latency, gpu-contention (fixed stale status table), game-library,
linux-setup (fixed m0->spike + stale zero-copy claim),
session-aware-host-followups, windows-client-bootstrap,
windows-dualsense-{scoping,game-detection}, windows-virtual-display,
security-review (per-finding status table; #12 still open),
apollo-comparison (shipped backlog collapsed to one-liners).
- Windows-host cluster consolidated: windows-host.md -> redirect into
windows-host-rewrite.md (whose stale scorecard is corrected -- goal1 is
merged, M4 done); windows-secure-desktop.md archived (now a fallback
behind IDD-push primary).
- Kept evergreen: ci.md, gamescope-multiuser.md, windows-build-and-packaging.md.
- New design/README.md: per-doc status table + consolidated open-items
roll-up so nothing is tracked in only one buried doc.
- Repoint 5 code comments to the archived secure-desktop doc path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
11 KiB
Windows host — virtual DualSense scoping
Status: SHIPPED — M0 feasibility gate PASSED (2026-06-21), M1–M4 landed. Driver:
packaging/windows/drivers/pf-dualsense/(README there); host backendcrates/punktfunk-host/src/inject/dualsense_windows.rs+ shared contractinject/dualsense_proto.rs. Commitsaa159df(Rust UMDF driver + shm channel),4a73102(host backend),fde438a/6db3525(SwDeviceCreate per-session devnode),b0c8233(pure-user-mode DS4/Xbox 360, ViGEm dropped). This doc is trimmed to design rationale + open items; implementation detail lives in the code and the driver README.
Why UMDF2, and why a real virtual DualSense (the WHY)
Apollo's backlog "DS4 ViGEm target on Windows" is the wrong target for actual DualSense.
ViGEmBus emulates only Xbox 360 (XUSB) and DualShock 4 — never a DualSense. Because this
is a host-side virtual pad, the DualSense-defining features (adaptive triggers, the fine haptic
actuators, DS5 identity) only work end-to-end if the game sees a real DualSense and therefore
drives them. A DS4 virtual pad makes the game take its DS4 code path and never emit those commands,
so the client's adaptive-trigger rendering is never exercised. ViGEm DS4 structurally cannot
deliver adaptive triggers — that ceiling is the whole reason not to copy Apollo here (and Apollo
itself does DualSense only on Linux via inputtino; its Windows path is ViGEm XUSB/DS4_REPORT_EX
only — zero virtual-HID/DualSense code to vendor).
The right path is the Windows analog of the Linux host's /dev/uhid device: present a real virtual
DualSense HID device (Sony VID 054C / PID 0CE6, the inputtino PS5 report descriptor we already
ship) so the game/Steam/GameInput bind it as genuine.
Mechanism = a UMDF2 (user-mode) HID minidriver, created/torn-down per session via
SwDeviceCreate, as a lower filter under the OS pass-through driver mshidumdf.sys. This is the
same driver tier as SudoVDA (UMDF, not kernel), so the existing vendor → sign → Inno-installer
machinery applies almost unchanged. Two corrections drove this conclusion over the 2026-06-20 draft:
- VHF (Virtual HID Framework) supports a HID source driver only in kernel mode — it is not
the mechanism for a user-mode virtual pad. The user-mode mechanism is a UMDF2 HID minidriver built
from the
vhidmini2sample. So the earlier "KMDF, a higher bar than SudoVDA" framing was wrong: it is the same UMDF tier. - UMDF 2.0 is NOT COM-based (COM/
IDriverEntry/IWDFDriverare legacy UMDF 1.x). UMDF 2.0 uses the same C-style WDF object model as KMDF — aDriverEntrysymbol + C function pointers, no vtable. This is precisely why a Rust FFI implementation is even conceivable.
Everything except the host backend was already platform-agnostic and DualSense-complete (protocol
planes 0xCC/0xCA/0xCD, the HidOutput feedback abstraction, pad-type negotiation, clients, the
C-ABI). The DualSense HID contract (the 232-byte DUALSENSE_RDESC, serialize_state for input report
0x01, parse_ds_output for output report 0x02, the 0x05/0x09/0x20 feature blobs, USB framing
no-CRC) was already pure transport-independent Rust — so the report bytes are identical to Linux and
only the device-framing layer is new.
Why Rust ("Option R") despite zero precedent
The user's strong preference was a self-authored Rust driver, accepted as pioneering risk.
microsoft/windows-drivers-rs officially targets UMDF and ships a real (but bare-stub) UMDF sample;
because UMDF 2.0 is the C function-pointer model, the FFI maps cleanly. The honest gap going in: the
whole HID-minidriver layer (WdfFdoInitSetFilter, the manual inverted-call queue, IOCTL_UMDF_HID_*
dispatch, HID_XFER_PACKET) was hand-written unsafe FFI with no safe wrappers, and every other
shipping virtual-HID controller driver (vhidmini2, HIDMaestro, DsHidMini) is C — so symbol coverage
for the UMDF target was unproven. The de-risk plan was a C vhidmini2 shim fallback (keeping all
DualSense logic in the Rust host either way), with forking HIDMaestro as the last resort (rejected for
real use because HIDMaestro omits adaptive triggers — it cannot prove the one thing that makes a
virtual DualSense worth building).
Outcome: Option R confirmed. The M0 spike answered both the build-symbol question and the on-glass gate with a Rust driver — no C shim needed. The DualSense logic stays in Rust where it already lived.
M0 feasibility gate — PASSED (2026-06-21), and the three bugs
The blocking gate (RTX box 192.168.1.173; the dev VM is headless/WARP and cannot validate
game-facing HID recognition) asked two questions no prior art settled:
- Recognition — is a virtual
054C:0CE6UMDF2 device accepted as a genuine DualSense byWindows.Gaming.Input/ GameInput / Steam? YES — Steam recognized it and drove its DualSense-specific LEDs. - Adaptive-trigger fidelity — does the game's output report
0x02(the adaptive-trigger block) actually reach the driver'sWriteReport/SetOutputReportcallback? YES — captured two Steam-Input output reports (validFlag1=0x14= LIGHTBAR|PLAYER_INDICATOR). Adaptive-trigger bytes ride the same0x02path.
Three M0 bugs — reference for any future UMDF-in-Rust work:
- PE FORCE_INTEGRITY blocks self-signed load.
wdk-build's/INTEGRITYCHECKsets the PE FORCE_INTEGRITY bit, which demands a Microsoft-trusted signature to load. Fix: clear bit0x80at offset PE+0x5epost-build and re-sign. This was the load wall (earlier "Secure Boot blocks self-signed UMDF" conclusions were wrong).- Timer
ExecutionLevelmust beInheritFromParent, not zeroed. Amem::zeroedWDF_TIMER_CONFIGgives ExecutionLevel 0, which the framework rejects.- Queue
NumberOfPresentedRequestsmust beu32::MAX, not 0. A zeroed parallel-queue config caps in-flight requests at 0 →EvtIoDeviceControlnever fires.
Milestones
| # | Milestone | State | Commit(s) |
|---|---|---|---|
| M0 | Feasibility spike (Rust UMDF build + on-glass recognition + 0x02 callback) |
✅ SHIPPED | driver aa159df |
| M1 | Extract transport-independent contract into inject/dualsense_proto.rs (DUALSENSE_RDESC, serialize_state, parse_ds_output, feature blobs; calibration trimmed 42→41) |
✅ SHIPPED | 4a73102 |
| M2 | UMDF2 HID minidriver + INF + signed .cat (authored in Rust) |
✅ SHIPPED | aa159df |
| M3 | Rust host bridge inject/dualsense_windows.rs (DualSenseWindowsManager over Global\pfds-shm-<idx>; SwDeviceCreate per-session devnode) |
✅ SHIPPED | 4a73102, fde438a, 6db3525 |
| M4 | Un-gate the PadBackend::DualSense seam + GamepadPref::DualSense resolution on Windows; ViGEm dropped (pure user-mode DS4/Xbox 360 too) |
✅ SHIPPED | b0c8233 |
A SwDeviceCreate gotcha surfaced during M3 and is worth keeping: two E_INVALIDARG causes were found
— (1) an underscore in the enumerator name (pf_dualsense → must be punktfunk), and (2) passing
the completion callback was rejected; the INF lists both root\pf_dualsense (devgen) and pf_dualsense
(SwDevice) and the host falls back to an out-of-band devnode when per-session create fails.
Decision matrix (condensed)
| Option | Adaptive triggers / DS5 identity | Effort | When it's right |
|---|---|---|---|
| A. UMDF2 virtual DualSense (shipped) | ✅ full | medium — UMDF, same tier as SudoVDA | the goal — matches the Linux host |
| B. ViGEm DS4 | ❌ never (DS4 ceiling) | small | quick PS-pad, no adaptive triggers — rejected, ViGEm removed |
| C. Hybrid | A for DS5, Xbox 360 fallback | A + small | belt-and-suspenders (Xbox 360/XInput still covers most games) |
| D. Defer | — | — | would have applied only if the M0 0x02 gate had failed |
Xbox 360 (XInput) covers most Windows games regardless; Xbox One/Series fold into it on Windows.
Risk register (condensed)
| Risk | Status |
|---|---|
Output 0x02 never reaches the driver write callback (fatal to value prop) |
resolved — M0 measured it directly, YES |
054C:0CE6 not accepted as a real DualSense |
resolved — Steam recognizes it |
| Rust UMDF pioneering risk (no safe WDF/HID wrappers; symbol coverage) | resolved — Rust driver shipped, no C shim |
SwDeviceCreate device lifetime tied to host process handle |
accepted — hold HSWDEVICE for the session (matches Linux UHID fd semantics) |
windows-drivers-rs transient toolchain breaks (LLVM-22 bindgen, Disc. #591) |
low — pin LLVM 21.1.2 |
| EV cert + Partner Center attestation lead time / friction | open (see below) |
Open items
-
Public-distribution signing — EV cert + Microsoft attestation. The fleet/self-signed recipe (bundled
.cer→ machine Root + TrustedPublisher viacertutil -addstore -f, thenpnputil /add-driver /install, cloned frominstall-sudovda.ps1) works for dev/internal boxes only — Microsoft is explicit it "should never be followed for any driver package distributed outside your organization." For arms-length public users the minimal correct path is Microsoft attestation signing via Partner Center (it re-signs the.catwith a Microsoft cert → silent PnP install, no publisher prompt, no Root-store import). A bare Authenticode/OV/EV signature is not sufficient: it installs but with the blocking "would you like to install this device software?" prompt (setupapi0x800b0109/0xe0000242). Attestation needs a registered Windows Hardware Developer Program (Partner Center) account and an EV code-signing cert (FIPS hardware token, ~USD 250–560/yr, 1–7 day vetting) to register and to sign the submission CAB. UMDF is exempt from kernel-mode load enforcement so the.dllloads unsigned, but installation still needs a trusted catalog. The EV key is non-exportable → CAB signing + submission is a manual offline step, not a CI secret; vendor the Microsoft-resigned.catlike SudoVDA's. (Azure Trusted Signing cannot substitute — it signs only user-mode PE//INTEGRITYCHECK/SmartScreen, not the driver.cat.) Blocks public release; dev/fleet self-signed works today. -
GameInput API detection reads VID/PID as
0x0000. The GameInput path does not pick up the054C:0CE6identity (reads0x0000); may require the KMDF USB-emulating bus driver rather than the root-enumerated UMDF HID device. Tracked indesign/windows-dualsense-game-detection.md. -
HidHide integration — unclear value on a usually-headless host; only relevant when a physical pad is also attached. Decide whether to bundle/integrate at all.
-
Minimum-OS /
UMDFVERSIONtargeting decision — whichUmdfLibraryVersion/ WDK to target for the widest Win10/11 install base, consistent with punktfunk's existing host support matrix. -
Single multi-driver CAB — can one Partner Center submission carry both SudoVDA and the DualSense driver? Multi-driver CABs are supported in general; unverified for this account.
-
DsHidMini end-user signing tier — self-signed vs attestation in its WixSharp MSI, useful as a second public-distribution data point.