4.7 KiB
How controllers work in Proton
There are five methods that Windows games can use to access controllers: dinput, xinput, winmm, hid, and rawinput. Games can use any combination of all of these APIs.
rawinput allows direct access to the gamepad hardware. The application must know the HID protocol, and/or know the device-specific protocol for non-standard devices like Xbox controllers.
hid is a layer above rawinput, where Windows will talk HID to the controller on the game's behalf. This turns the raw HID protocol data into usable things like buttons and joysticks.
dinput is a "legacy" API that allows applictions to talk to any type of joystick. On Windows, it is implemented on top of HID. Notably, dinput allows easy access to controllers that no other API does, so it is still used by modern games despite being "legacy."
xinput is the new API that supports only Xbox controllers. On Windows, it is likely implemented on top of rawinput, as Xbox controllers do not behave like standard HID devices.
winmm is the very legacy API, for when joysticks were hooked up through the soundcard. On modern Windows, it is implemented on top of dinput.
Here is a diagram for how these APIs are mapped down to the system by Proton:
----------
| game.exe |
----------
/ | | | \
/ | | | \ application
*********/****|*|**|****\******************
| | | \ \ wine
| | | | \
------ | | ----- \
|xinput| | | |winmm| |
------ | | ----- |
| | \ | |
| | | | |
\ | ------ |
\ | |dinput| |
\ | ------ /
| | / /
| | | /
--- /
|hid| /
--- /
| /
| |
--------
|rawinput|
--------
|
-----------
|winebus.sys|
-----------
| | wine
************|******|***********************
| | linux
| ----
| |SDL2|
| ----
| | \
| | \
| | |
------ -----------
|hidraw| |input event|
------ -----------
| |
\ /
========
|hardware|
========
Some things to note:
SDL2 provides the controller mapping feature of the Steam client. If you don't go through SDL2, then you don't get that mapping feature. Also notice that winebus.sys must turn SDL2 events into usable winebus data (HID protocol). We also allow direct access to hidraw devices so games which can speak HID (or other) protocol can talk directly to those devices.
Xbox controllers do not speak real HID. Instead Windows provides a HID compatibility layer so dinput, which is implemented on top of HID, will present the Xbox controller to legacy games. Of course some games (Unity) have noticed that, and talk directly to this internal HID interface, so we need to duplicate it bit-for-bit in winebus.sys.
Some games support talking directly to certain controller types. For example, many modern games support PlayStation 4 controllers directly and will provide layouts and button images in-game specific to DualShock 4 controllers. For this reason, we don't want to present every controller through xinput, which should only present Xbox controllers.
However, we also want users to be able to use any controller, even if the game only supports xinput. Steam provides a controller mapping feature, which is presented as a virtual Steam Controller. We turn this virtual Steam Controller into an xinput device. This means any controller which is mapped will appear to the game as an xinput device, in addition to the other APIs. Controllers which are not mapped will appear as the real controller, which the game may or may not support.
One final snag is that many distros do not allow user access to hidraw devices. Steam ships some udev rules to allow this for certain common controller types, but not most. In other words, your user may not have access to the hidraw device for your controller, especially if it is a less well-known controller. In those cases, we access it through SDL2 via its linux js backend and try to treat it as an Xbox controller, even if it is not mapped with the Steam client mapping feature.