Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ behind backend implementations.
## 🎮 Capabilities

- Gamepad profiles for generic HID, Xbox 360, Xbox One, Xbox Series,
DualShock 4, DualSense, and Nintendo Switch Pro-style controllers.
- Descriptor-driven PlayStation gamepads through Linux `uhid`; Generic, Xbox,
DualShock 4, DualSense, Nintendo Switch Pro-style, and Steam Deck controllers.
- Descriptor-driven PlayStation and Steam Deck gamepads through Linux `uhid`; Generic, Xbox,
and Switch Pro gamepads plus keyboard, mouse, touchscreen, trackpad, and pen
tablet devices through `uinput`.
- Windows gamepads through a user-mode UMDF2 control driver backed by Virtual
Expand Down Expand Up @@ -149,6 +149,7 @@ Alternatives exist if `libvirtualhid` does not meet your needs.
| DualShock 4 gamepad | ✅ | ✅ | ✅ | ❌ | ✅<sup><a href="#alternatives-note-4">4</a></sup> |
| DualSense gamepad | ✅ | ❌ | ✅ | ✅ | ✅<sup><a href="#alternatives-note-4">4</a></sup> |
| Nintendo Switch Pro-style gamepad | ✅ | ❌ | ✅ | ✅ | ✅<sup><a href="#alternatives-note-4">4</a></sup> |
| Steam Deck gamepad | ✅ | ❌ | ❌ | ❌ | ❌ |
| Rumble or output callbacks | ✅ | ❌ | ✅ | ✅ | ✅<sup><a href="#alternatives-note-4">4</a></sup> |
| Data-driven profiles | ❌ | ❌ | ✅ | ❌ | ❌ |
| Actively developed | ✅ | ❌ | ✅ | ✅ | ✅ |
Expand Down
35 changes: 28 additions & 7 deletions docs/platform-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,17 @@ The VHF driver answers the calibration, pairing, and firmware feature reports
used to initialize DualShock 4 and DualSense HIDAPI output. It also answers the
Switch Pro USB and subcommand initialization sequence and accepts the native
`0x30` input layout, so descriptor-aware consumers can initialize those
controllers before sending their native output reports.
controllers before sending their native output reports. The Steam Deck profile
uses Valve's `0x28DE:0x1205` USB identity and native 64-byte state and feature
reports. Its single top-level collection is identified as a Generic Desktop
Game Pad while retaining the Valve-native report bytes, so Windows Game
Controllers and generic HID clients can enumerate it even if the Valve-specific
path is unavailable. SDL/HIDAPI can recognize it as a Steam Deck, disable its
desktop mappings, and send native rumble without exposing Valve protocol details
through the public C++ API. The driver seeds a neutral report before exposing the
device, the backend submits another synchronously during creation, and periodic
updates continue afterward. Each backend packet carries an advancing native
sequence number.

See [Windows driver package](windows-driver.md) for build, install, validation,
and signing details.
Expand All @@ -84,7 +94,7 @@ and signing details.

The Linux backend uses standard user-space kernel interfaces:

- `uhid` for descriptor-driven HID gamepads.
- `uhid` for descriptor-driven HID gamepads, including Steam Deck.
- `uinput` for Generic, Xbox 360, Xbox One, Xbox Series, and Switch Pro
gamepads, plus keyboard, mouse, touchscreen, trackpad, and pen tablet
devices.
Expand Down Expand Up @@ -131,13 +141,24 @@ buttons, Guide, L3, and R3 at their expected indices. D-pad directions are
reported through the hat axes and exposed as logical buttons by standard
gamepad consumers.

DualShock 4 and DualSense remain on `uhid` so their descriptors, motion,
DualShock 4, DualSense, and Steam Deck remain on `uhid` so their descriptors, motion,
touchpad, battery, feature reports, and profile-specific output reports stay
available. The backend accepts PlayStation output through both UHID interrupt
and control channels. Numbered control-channel output is normalized before
parsing, whether the kernel includes the report number in the payload or
provides it separately on the UHID event.

Steam Deck retains Valve's native identity and emits the 64-byte Deck state
packet periodically so SDL's direct HIDAPI path can initialize before the first
client input arrives. Linux exposes that endpoint as Bluetooth HID so SDL can
own the native hidraw stream directly; advertising it as USB would make the
kernel's `hid-steam` driver claim the virtual endpoint and substitute an evdev
controller. The Generic Desktop/Game Pad descriptor remains available as an
evdev fallback. The backend answers the unit-serial feature query, accepts the
desktop-mapping/settings commands used by SDL, and forwards native `0xEB`
rumble requests through the portable output callback. Each submitted native
packet carries an advancing sequence number.

The backend opens `/dev/uhid` in nonblocking mode, matching the original
asynchronous gamepad registration path. Its event reader is active before
device registration begins, and creation does not report success until the
Expand Down Expand Up @@ -246,8 +267,8 @@ The FreeBSD backend uses the native evdev compatibility stack through
FreeBSD path, and `/dev/uinput` for environments that provide the Linux-style
alias. It supports the same uinput device categories as the Linux backend:

- Generic, Xbox 360, Xbox One, Xbox Series, DualShock 4, DualSense, and Switch
Pro gamepads.
- Generic, Xbox 360, Xbox One, Xbox Series, DualShock 4, DualSense, Switch Pro,
and Steam Deck gamepads.
- Keyboard and mouse devices, with X11/XTest available as a fallback.
- Touchscreen, trackpad, and pen tablet devices.

Expand All @@ -259,8 +280,8 @@ with the kernel HID bus. FreeBSD CUSE applications such as
a `uhid(4)`-compatible character device for direct consumers, but that is a
different integration surface and is not used by the current backend.

Generic, Xbox-family, Switch Pro, DualShock 4, and DualSense behavior therefore
uses uinput. Ordinary buttons, sticks, analog triggers, and rumble are available,
Generic, Xbox-family, Switch Pro, DualShock 4, DualSense, and Steam Deck behavior
therefore uses uinput. Ordinary buttons, sticks, analog triggers, and rumble are available,
but raw HID reports and descriptor-driven features are not.

For each created gamepad, `Gamepad::profile()` reports the effective FreeBSD
Expand Down
6 changes: 4 additions & 2 deletions docs/streaming-host-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ The core API and adapter shape cover the major streaming-host requirements:
- Rich controller metadata.
- Gamepad output callbacks.
- Keyboard and mouse input paths.
- Linux PlayStation gamepads through `uhid`, Generic/Xbox/Switch Pro gamepads
through `uinput`, and `uinput` keyboard/pointer devices.
- Linux PlayStation and Steam Deck gamepads through `uhid`, Generic/Xbox/Switch
Pro gamepads through `uinput`, and `uinput` keyboard/pointer devices.
- Linux DualSense and DualShock 4 USB/Bluetooth report handling.
- Native Steam Deck identity, input, feature-report initialization, touch/motion,
rear-button, Quick Access, and rumble handling on Windows and Linux.
- Linux touchscreen, trackpad, and pen tablet device types.
- FreeBSD uinput gamepads and pointer devices, with basic PlayStation input and
rumble but without Linux UHID-only PlayStation features.
Expand Down
9 changes: 6 additions & 3 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ Built-in gamepad profiles and their platform-neutral default device names are:
| DualShock 4 USB and Bluetooth | `(libvirtualhid) PS4 Controller` |
| DualSense USB and Bluetooth | `(libvirtualhid) PS5 Controller` |
| Nintendo Switch Pro | `(libvirtualhid) Nintendo Pro Controller` |
| Steam Deck | `(libvirtualhid) Steam Deck Controller` |

Consumers may replace `DeviceProfile::name` before creating a gamepad, for
example, to prepend an application name while preserving the default controller
Expand All @@ -226,6 +227,8 @@ Profiles advertise support for features such as rumble, trigger rumble, RGB
LEDs, adaptive triggers, motion sensors, touchpads, battery state,
profile-specific buttons, and raw output reports. Consumers should query profile and
backend capabilities before warning users about unsupported client features.
The `misc1` button represents Share/Capture/Mic Mute-style controls and is
available on the generic, Xbox Series, DualSense, and Switch Pro profiles; Xbox
360 and Xbox One do not advertise that extra button.
The `misc1` button represents Share/Capture/Mic Mute/Quick Access-style controls
and is available on the generic, Xbox Series, DualSense, Switch Pro, and Steam
Deck profiles; Xbox 360 and Xbox One do not advertise that extra button. Steam
Deck also exposes its two trackpads through the two portable touch contacts and
maps its L4/R4/L5/R5 rear controls to the four paddle buttons.
11 changes: 11 additions & 0 deletions docs/windows-driver.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,17 @@ gamepad.
DualShock 4 and DualSense answer the calibration, pairing, and firmware feature
requests used by their Windows HIDAPI initialization paths. Switch Pro answers
the native USB and subcommand handshake and submits native `0x30` input reports.
Steam Deck uses Valve's `VID_28DE&PID_1205` identity, responds to the unnumbered
feature-report sequence used by SDL/HIDAPI, submits native 64-byte Deck state
reports, and normalizes native `0xEB` rumble requests into the public callback.
The same native report is described by a Generic Desktop/Game Pad top-level
collection, allowing Windows' Game Controllers control panel and generic HID
clients to classify the VHF child as a gamepad. The backend begins sending a
sequenced neutral Deck state immediately after creation and repeats the latest
state every four milliseconds, keeping a packet available throughout SDL's
short endpoint-probe window. The driver also queues a neutral state before
starting VHF and handles Deck feature requests as unnumbered reports regardless
of the initial contents of the transfer buffer.
The built-in Generic profile is presented to Windows as a DirectInput PID
Joystick with the complete output-report set required for DirectInput
enumeration. Constant Force and Sine output is normalized to the portable
Expand Down
4 changes: 4 additions & 0 deletions examples/gamepad_adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ namespace {
if (name == "switch") {
return lvh::profiles::switch_pro();
}
if (name == "steamdeck") {
return lvh::profiles::steam_deck();
}

return std::nullopt;
}
Expand All @@ -58,6 +61,7 @@ namespace {
case switch_pro:
return nintendo;
case generic:
case steam_deck:
return unknown;
}

Expand Down
4 changes: 4 additions & 0 deletions src/core/gamepad_adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ namespace lvh {
case xbox_series:
case dualsense:
case switch_pro:
case steam_deck:
return true;
case xbox_360:
case xbox_one:
Expand Down Expand Up @@ -88,6 +89,9 @@ namespace lvh {
support.supports_battery = profile.capabilities.supports_battery;
support.supports_misc1_button = supports_common_misc1_button(profile.gamepad_kind);
support.supports_touchpad_button = profile.capabilities.supports_touchpad;
if (profile.gamepad_kind == GamepadProfileKind::steam_deck) {
support.supported_rear_paddle_count = 4U;
}

return support;
}
Expand Down
Loading
Loading