Files
punktfunk/tools/win-input-matrix/Cargo.toml
T
enricobuehler 94c2f62490 test(tools): a GameInput probe — and it cannot see our promoted Xbox pad
`win-input-matrix` covered four of the five rows and said so; GameInput was the gap, because it has
no binding in the `windows` crate and needs hand-written COM. This adds it: `--gameinput` reports
whether GameInput has a reading, and `--gi-rumble l,h,lt,rt [--gi-pid PID]` drives
`SetRumbleState`. Every vtable slot is taken from the SDK header, not guessed — a COM vtable is
positional, so a wrong slot calls a different method with the wrong signature.

WHY RUMBLE AND NOT JUST ENUMERATION. `XINPUT_VIBRATION` has two members, so classic XInput can never
exercise an Xbox pad's two IMPULSE-TRIGGER motors. `GameInputRumbleParams` has four
(`lowFrequency`, `highFrequency`, `leftTrigger`, `rightTrigger`), which makes GameInput the only API
that can settle `design/trigger-rumble-plane.md` §2.1's open question — the `enable`-mask bit
assignment for the two trigger actuators, where bits 2/3 (the handles) are measured and bits 0/1
(the triggers) are inferred from field order and nothing else.

TWO THINGS MEASURED ON .173, 2026-08-09:

1.  GameInput's device enumeration is ASYNCHRONOUS, and the first `GetCurrentReading` reliably
   returns nothing even with pads actively reporting. This is the GameInput analogue of `wake_wgi`:
   the API looks like a query and is really a cache someone else fills. A bounded poll fixes it.
   ⚠️ Focus is NOT the cause, and the header rules it out rather than my guessing:
   `GameInputDefaultFocusPolicy` is 0 and every `GameInputFocusPolicy` flag is a RESTRICTION, so the
   default already admits background input. Do not "fix" this with `SetFocusPolicy`.

2. 🛑 **GameInput never sees our pad.** Hunting by product id for six seconds with the pad live and
   sweeping, it enumerated `054C:0CE6` (DualSense) and `3434:D031` (8BitDo) — both plain HID pads —
   and never `045E:02FD`, ours, while classic XInput was reading ours live in the same moment.

⇒ THE TRIGGER ENABLE BITS REMAIN CONJECTURE, but for a better reason than before: it is not that
nobody has tried, it is that on this box NOTHING CAN DELIVER a four-motor rumble to our pad. XInput
structurally cannot; GameInput can but does not see it.

⚠️ The obvious suspicion is that `xinputhid` claiming the HID collection exclusively is what hides
the pad from GameInput — which would mean promotion costs us the API most Game-Pass-era titles use,
a trade we have shipped by default. **That is NOT established here.** The decisive control is cheap
and has not been run: power on the REAL Xbox Elite, which Microsoft's own driver promotes the same
way, and see whether GameInput enumerates it. If a real promoted Xbox pad is also absent, this is a
property of GameInput in a non-interactive session and not our defect — the same shape as the WGI
`ts=0` row, which a real Elite reproduced.

VERIFIED
  * `cargo fmt --check` clean; `cargo clippy --target x86_64-pc-windows-msvc --all-targets
    -- -D warnings` clean (cross-checked from macOS).
  * Builds and runs on .173; `GameInputCreate` succeeds, readings arrive after the poll, and
    `SetRumbleState` is accepted.
  * The runtime is loaded by name, so a box without GameInput reports "unavailable" rather than
    failing to link or crashing.

NOT VERIFIED
  * That `SetRumbleState` reaches ANY pad's motors — it was accepted for the DualSense but nothing
    observable was checked on that device, and it never reached ours.
  * `GameInputDeviceInfo` is read only for `vendorId`/`productId` (offsets 4 and 6). The rest of the
    struct has variable-size members whose layout would have to be mirrored exactly; nothing here
    needs them. `supportedRumbleMotors` is in there and would answer "does GameInput think this pad
    has trigger motors" — worth adding if this line of enquiry continues.
2026-08-09 22:55:17 +02:00

36 lines
1.3 KiB
TOML

# Ask every Windows input API, in one shot, whether it can see a given gamepad.
#
# The whole Xbox-pad-on-Windows programme is a matrix of five rows — classic XInput, WGI `Gamepad`,
# WGI `RawGameController`, GameInput, and the HID/DirectInput/Steam family — and until this crate
# existed NOTHING in the tree measured any of it. Every reading in
# `design/xbox-pad-windows-handoff.md` came from ad-hoc off-tree tools, which is why several of them
# could not be reproduced or A/B'd later. This makes the matrix a command.
#
# Deliberately NOT a workspace member (see the root `Cargo.toml` `exclude` list): it is a
# bring-your-own-hardware measurement tool, Windows-only, and has no business on a CI leg.
#
# cargo run --release -- --watch 20
#
[workspace]
[package]
name = "win-input-matrix"
description = "Which Windows input APIs can see this gamepad? XInput / WGI Gamepad / WGI RawGameController / XUSB"
version = "0.26.0"
edition = "2024"
rust-version = "1.96.0"
license = "MIT OR Apache-2.0"
publish = false
[target.'cfg(windows)'.dependencies]
windows = { version = "0.62", features = [
"Win32_Foundation",
"Win32_UI_Input_XboxController",
"Win32_Devices_DeviceAndDriverInstallation",
"Win32_System_Com",
"Gaming_Input",
"Foundation",
"Win32_System_LibraryLoader",
"Foundation_Collections",
] }