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
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,16 @@ behind backend implementations.

- Gamepad profiles for generic HID, Xbox 360, Xbox One, Xbox Series,
DualShock 4, DualSense, and Nintendo Switch Pro-style controllers.
- Descriptor-driven PlayStation and Switch Pro gamepads through Linux `uhid`;
Generic and Xbox gamepads plus keyboard, mouse, touchscreen, trackpad, and
pen tablet devices through `uinput`.
- Descriptor-driven PlayStation, Switch Pro, Xbox One, and Xbox Series gamepads
through Linux `uhid`; Generic and Xbox 360 gamepads plus keyboard, mouse,
touchscreen, trackpad, and pen tablet devices through `uinput`. Xbox One and
Xbox Series fall back to `uinput` when `uhid` is unavailable.
- Windows gamepads, keyboards, and Raw Input-visible mice through a user-mode
UMDF2 control driver backed by Virtual HID Framework, with Win32 keyboard and
mouse fallbacks when the licensed driver path is unavailable.
- Output callbacks for profile-specific feedback such as rumble, RGB and player
LEDs, adaptive triggers, and raw HID output reports when available.
- Output callbacks for profile-specific feedback such as ordinary and trigger
rumble, RGB and player LEDs, adaptive triggers, and raw HID output reports
when available.
- An optional `virtualhid_control` native UI tool for creating, removing,
controlling, and inspecting test gamepads and mice through the public C++ API.
- CMake consumption through installed packages, vendored source,
Expand Down
34 changes: 17 additions & 17 deletions docs/end-user-gamepad-guide.md

Large diffs are not rendered by default.

84 changes: 55 additions & 29 deletions docs/platform-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,30 +109,49 @@ and signing details.

The Linux backend uses standard user-space kernel interfaces:

- `uhid` for descriptor-driven PlayStation and Switch Pro gamepads.
- `uinput` for Generic, Xbox 360, Xbox One, and Xbox Series gamepads, plus
keyboard, mouse, touchscreen, trackpad, and pen tablet devices.
- `uhid` for descriptor-driven PlayStation, Switch Pro, Xbox One, and Xbox
Series gamepads.
- `uinput` for Generic and Xbox 360 gamepads, for Xbox One and Xbox Series when
`uhid` is unavailable, and for keyboard, mouse, touchscreen, trackpad, and pen
tablet devices.
- `libevdev` internally for uinput device construction.
- X11/XTest only as a keyboard and mouse fallback when `uinput` cannot be used
and an X11 session is available.

Gamepad support normally prefers `uhid` because descriptors, raw HID identity,
feature reports, and output reports matter for controller compatibility.
Generic and Xbox-family profiles instead use `uinput` so SDL,
Steam, browser Gamepad API implementations, and other evdev consumers receive
canonical Linux gamepad events. Face buttons, shoulders, menu buttons, stick
clicks, and Guide use their native evdev codes; sticks use absolute axes. Every
uinput gamepad exposes its directional pad through `ABS_HAT0X` and `ABS_HAT0Y`.
Generic and Xbox triggers remain independent analog `ABS_Z` and `ABS_RZ` axes.
Profiles with rumble support normalize rumble, constant, periodic, and ramp
uinput force-feedback effects back into the public callback. Each requested playback repetition
restarts the effect's ramp and envelope timing. A zero-length effect remains
active until its explicit stop event, matching the infinite-effect contract used
by SDL and Steam. The Linux backend lets a new uinput device settle before
reading those effects, so an early poll error cannot disable feedback for the
device lifetime. Generated UHID nodes are correlated by stable physical and
unique identifiers when available, with device-name matching used only as a
fallback. PlayStation rumble is read from native UHID interrupt-channel output
feature reports, and output reports matter for controller compatibility. Xbox
One and Xbox Series use backend-only Bluetooth identities with a 283-byte BLE
descriptor, sparse input bitmap, and four-motor output framing. The report keeps
the native byte layout used by HIDAPI while advertising `Rx`/`Ry` for the right
stick and `Z`/`Rz` for the triggers, so Linux evdev exposes the canonical
`ABS_RX`/`ABS_RY` and `ABS_Z`/`ABS_RZ` axes expected by Steam. This keeps the bus,
vendor/product identity, descriptor, and reports consistent so Linux input,
Steam, SDL2, and SDL3 select the canonical Xbox mapping and can expose ordinary
and independent trigger rumble. A Bluetooth transport is necessary because
Linux HIDAPI implementations require a physical USB parent for `BUS_USB`
hidraw devices, which a user-space UHID device cannot provide. This transport
override does not change the public Xbox profiles or the platform-neutral API.

Generic and Xbox 360 profiles use `uinput` so SDL, Steam, browser Gamepad API
implementations, and other evdev consumers receive canonical Linux gamepad
events. Xbox One and Xbox Series use the same path only as a fallback when
`/dev/uhid` cannot be opened or initialized. Face buttons, shoulders, menu
buttons, stick clicks, and Guide use their native evdev codes; sticks use
absolute axes. Every uinput gamepad exposes its directional pad through
`ABS_HAT0X` and `ABS_HAT0Y`. Generic and Xbox triggers remain independent analog
`ABS_Z` and `ABS_RZ` axes. Profiles with rumble support normalize rumble,
constant, periodic, and ramp uinput force-feedback effects back into the public
callback. Each requested playback repetition restarts the effect's ramp and
envelope timing. A zero-length effect remains active until its explicit stop
event, matching the infinite-effect contract used by SDL and Steam. The Linux
backend lets a new uinput device settle before reading those effects, so an
early poll error cannot disable feedback for the device lifetime.

Generated UHID nodes are correlated by stable physical and unique identifiers
when available, with device-name matching used only as a fallback. UHID
identities include the virtual profile's vendor and product IDs so applications
do not reuse metadata from another profile after the same virtual slot changes
profiles. PlayStation rumble is read from native UHID interrupt-channel output
reports.

The Generic profile keeps its public `0x1209:0x0001` identity, USB bus, and
Expand All @@ -142,16 +161,23 @@ which avoids changing the raw button capability surface. It uses a compact
Generic button layout rather than the sparse Xbox button slots.

Xbox 360 retains its `0x045E:0x028E` identity, while its Linux uinput device uses
the Bluetooth bus, so consumers select the sparse button mapping.
Xbox One and Xbox Series retain their public USB identities, but their Linux
uinput devices use the corresponding Bluetooth product identities (`0x0B20`
and `0x0B13`, respectively), whose standard consumer mappings match the events
that uinput exposes. Those three Xbox profiles preserve the 15-slot
Linux gamepad button sequence: unused `BTN_C`, `BTN_Z`, `BTN_TL2`, and `BTN_TR2`
slots are advertised but never pressed, keeping face buttons, shoulders, menu
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.
the Bluetooth bus, so consumers select the sparse button mapping. The Xbox One
and Xbox Series UHID transports use the native Bluetooth product identities
`0x045E:0x0B20` and `0x045E:0x0B13`, respectively. Their Bluetooth HID reports
carry canonical gamepad input and four-motor output, which the backend decodes
into ordinary and independent trigger-rumble callbacks.

If UHID is unavailable, the Xbox One and Xbox Series uinput fallbacks use the
corresponding Bluetooth product identities (`0x0B20` and `0x0B13`, respectively),
whose standard consumer mappings match the events that uinput exposes. The Xbox
uinput profiles preserve the 15-slot Linux gamepad button sequence: unused
`BTN_C`, `BTN_Z`, `BTN_TL2`, and `BTN_TR2` slots are advertised but never
pressed, keeping face buttons, shoulders, menu 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. The fallback retains
all of those controls, analog trigger input, and ordinary force feedback, but
Linux uinput cannot expose independent trigger motors, so its effective profile
clears trigger-rumble support.

DualShock 4 and DualSense remain on `uhid` so their descriptors, motion,
touchpad, battery, feature reports, and profile-specific output reports stay
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 @@ -52,8 +52,10 @@ 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 and Switch Pro gamepads through `uhid`, Generic/Xbox
gamepads through `uinput`, and `uinput` keyboard/pointer devices.
- Linux PlayStation, Switch Pro, Xbox One, and Xbox Series gamepads through
descriptor-driven `uhid`, Generic and Xbox 360 gamepads through `uinput`,
Xbox One and Xbox Series uinput fallbacks, and `uinput` keyboard/pointer
devices.
- Native Switch Pro motion, initialization replies, rumble, HOME-light, and
player-light output handling on Linux and Windows descriptor-driven backends.
- Linux DualSense and DualShock 4 USB/Bluetooth report handling.
Expand Down
4 changes: 3 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,9 @@ Profiles advertise support for features such as rumble, trigger rumble, RGB and
player 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.
features. Xbox One and Xbox Series advertise `supports_trigger_rumble`; the
Linux UHID Bluetooth transport preserves that capability, while the uinput
fallback clears it and retains ordinary rumble.
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.
1 change: 1 addition & 0 deletions src/core/gamepad_adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ namespace lvh {
}

support.supports_rumble = profile.capabilities.supports_rumble;
support.supports_trigger_rumble = profile.capabilities.supports_trigger_rumble;
support.supports_rgb_led = profile.capabilities.supports_rgb_led;
support.supports_adaptive_triggers = profile.capabilities.supports_adaptive_triggers;
support.supports_player_leds = profile.capabilities.supports_player_leds;
Expand Down
6 changes: 5 additions & 1 deletion src/core/profiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1958,7 +1958,11 @@ namespace lvh::profiles {
profile.output_report_size = pid_rumble_payload_size;
profile.name = std::move(name);
profile.manufacturer = "Microsoft";
profile.capabilities = {.supports_rumble = true, .supports_battery = include_share_button};
profile.capabilities = {
.supports_rumble = true,
.supports_trigger_rumble = true,
.supports_battery = include_share_button,
};
profile.report_descriptor = make_xbox_gip_report_descriptor(include_share_button);
return profile;
}
Expand Down
6 changes: 3 additions & 3 deletions src/core/report.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,9 +367,9 @@ namespace lvh::reports {
return std::nullopt;
}

// Some native Xbox Series transports use report ID 3 followed by the
// same eight-byte four-motor rumble payload.
if (profile.gamepad_kind == xbox_series && report.size() >= pid_rumble_report_size && report[0] == 0x03U) {
// Native Xbox Bluetooth transports use report ID 3 followed by the same
// eight-byte four-motor rumble payload for both Xbox One and Series.
if (report.size() >= pid_rumble_report_size && report[0] == 0x03U) {
return 1U;
}

Expand Down
5 changes: 5 additions & 0 deletions src/include/libvirtualhid/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@ namespace lvh {
*/
bool supports_rumble = false;

/**
* @brief Whether the profile supports independent trigger rumble output.
*/
bool supports_trigger_rumble = false;

/**
* @brief Whether the profile exposes motion sensors.
*/
Expand Down
Loading
Loading