kcms/gamecontroller: Replace SDL Game Controller API with Joystick API
This MR replaces usage of higher-level "Game Controller" API with lower-level "Joystick" API in Game Controller KCM. This fixes the following issues:
This should also fix the following bugs, although I don't have a steering wheel or handbrake to actually test the changes:
Current issues
SDL2 provides two distinct APIs:
- Joystick API. It basically reports raw values from buttons, axes, POV hats and trackballs. It tells nothing about actual meaning of these values, say that axis 0 is a left analog stick. This kind of API is most suitable for the current implementation of KCM.
- Game Controller API. This is a higher-level API that automatically recognizes popular gamepads and remaps controls to actual analog sticks, triggers, buttons etc.
For now the KCM uses a mix of Game Controller API and Joystick API. Unfortunately, this results in several bugs:
- Non-gamepad devices, such as classic joysticks, steering wheels or handbrakes, are not detected at all. The Game Controller API seems to be limited to standard gamepads only.
- The state of some buttons/axes, such as D-pad or right trigger, is not displayed correctly. This happens due to the mismatch between APIs. We grab indices of buttons/axes from Game Controller API but then pass them to Joystick API. This inevitably leads to bugs since different APIs use different numbering of buttons/axes.
- D-pad presses are not displayed correctly. Similar to the issue above. Joystick API often exposes D-pad as a joystick POV hat whereas Game Controller API typically remaps D-pad to four regular buttons. Again, this results in bugs due to different number of buttons reported by different APIs.
What does this MR do
The MR tries to sort out all issues by replacing most of usages of Game Controller API with Joystick API. It basically introduces new class Device
that encapsulates SDL_Joystick
object. Both ButtonModel
and AxesModel
now use Device
object to report raw values from buttons and axes. DeviceModel
now manages Device
objects and shows them in the combo box. This ensures SDL Joystick API is used consistently across all code and avoids API mismatch issues. This should also properly detect classic joysticks, wheels and handbrakes. Unfortunately I don't have any of these devices and can't really test them. Technically the KCM should properly handle them similar to sdl-jstest app.
The MR also handles the case when D-pad is exposed as a joystick POV hat instead of four regular buttons. It adds a new class HatModel
that provides raw position data from POV hats. Each POV hat is mapped to a pair of X/Y model rows. Both axes values and hat positions are now displayed under Axes table using new class AxesProxyModel
. Let me know if you want to display POV hats under Buttons table instead.
The purpose of Gamepad
class has been changed a bit. Instead of being the central device class, now it only encapsulates SDL_GameController
object and provides higher-level access to gamepads. For now it is only used to display the position of left stick in PositionWidget
. This is something that cannot be done with plain Joystick API.
Future work
It was proposed to implement two different user interfaces:
- A "simple" mode which is the default, which shows a symbolic generic game controller (360-style) with buttons that change on controller state changes.
- An "advanced" mode similar to the current interface (but less reliant on tables), which gives more of a proper info dump including all buttons for users testing more obscure controllers.
This MR is implemented with this proposal in mind. It provides two different classes Gamepad
and Device
for two different modes. The "Simple" mode should always use Gamepad
class (i.e. Game Controller API) while "Advanced" mode should always use Device
class (i.e. Joystick API). This way, both modes will operate fully independently and won't conflict with each other.