Analog Trigger Buttons in Retropie
-
I am currently trying to get analog trigger buttons to work correctly in Retropie. Usually they are just mapped to digital buttons. I personally use an Xbox360 Wireless pad on Lubuntu x64 16.04.
At first I thought it was a problem with the xpad driver and I thought it would be a good idea to manually re-map the ranges from +/- 32k to 0-32k but now I think the driver is fine and patching the driver is the wrong way.
I did quite some reading in lots of code (xpad, linux, sdl, retroarch...) and there are some things I have learned (or I think I have learned) about joysticks in Linux though. Maybe nothing new for you guys but I will just list it up to make sure we are on the same page:
- /dev/input/js* and /dev/input/event* are two different APIs. The first is considered the "legacy joystick API". The latter is the current evdev.
- the usual joystick calibration tools (jscal etc.) are meant for the legacy joystick API and don't have an effect for evdev
- evdev actually can deliver infos about the value ranges of analog axis (http://lxr.free-electrons.com/source/include/uapi/linux/input.h#L68)
- "Support for the older joystick API (/dev/input/js*) for Linux has been dropped from SDL2."
- SDLs abstract joystick API intentionally re-ranges all axis to +/- 32k
- SDL2 introduced a new API called 'gamecontroller' (https://wiki.libsdl.org/CategoryGameController) on top of the joystick API. The old joystick API only can identify axis and buttons by indices (0, 1, etc) without being able to interpret them. Mapping those buttons/axis-indices to actual buttons (START, A, B, LEFT-STICK, RIGHT-TRIGGER) was up the application (ES does that).
The new gamecontroller API is backed up by a database of known gamepad models. Only supported gamepads can be used with that API (->
SDL_IsGameController
).
Now I come to the point: When using that API (with a supported gamepad) then SDL2 actually will deliver correctly "ranged" axis value for analog triggers. You get then values from 0-32k. They can do it because they know, for example, that axis #5 is "right-trigger". And they just map all trigger axis to positive values.As a proof-of-concept I implemented this:
https://github.com/verybadsoldier/EmulationStation/commit/9b6710b0e839293c51b8329b2d88e91017886343
It replaces the SDL joystick API in ES completely by the gamecontroller API. Analog triggers work then and can be normally configured (you need to disable xpad's trigger_to_button ofc).So, I would like to use that gamecontroller API in ES. But the main benefit of the gamecontroller API is that SDL takes care of the button mapping. As this only works for the supported gamepads it is of limited use for ES I guess. To be able to use also unknown gamepads ES would still have to manage the button mapping for those gamepads itself.
This is the list of currently supported controllers by SDL2:
https://github.com/spurious/SDL-mirror/blob/master/src/joystick/SDL_gamecontrollerdb.h#L67
The configuration for a gamepad consists solely of a single string which describes all buttons and axis:
050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
You can also add custom gamepad strings at runtime using SDL functions to add gamepads not supported out of the box.In theory, ES could use that feature to avoid the need to configure gamepads at all. If the user connects a supported gamepad then ES could just pass the mapping provided by SDL's DB to the emulators. Mapping unknown controllers manually would still be mandatory of course. But unsupported gamepads would have to be added manually to the gamepad database or ES would have to fall back to the SDL1-joystick API.
Well, I don't know if I could explain it in an understandable way. I think SDL2's gamecontroller API offers some nice features. Maybe you know about it already anway. So what do you think about it? I already coded a bit of a mixed-joystick-API in ES so that supported gamepads use the gamecontroller API while other pads use the joystick API. But I would like to hear your opinion before putting more work into it.
-
very interesting - thanks.
I was aware of some of your observations, and knew about the SDL2 new api, but hadn't tried it.
Good that it resolves the issue and fixes the ranges. I had already tried hacking the xpad driver BTW as you had - and came out with similar results.
Shame the new API doesn't have some default fallback, unless I have misunderstood if an unknown joystick is added, we would have to implement both apis ?
-
@BuZz
Yeah kinda, currently I see three solutions:- Implement both APIs (as you said).
- Use solely the game controller API and inject a dummy configuration on-the-fly for gamepads that are not supported out-of-the-box. This might work as long as ES does not really make use of the button mapping that is offered by the API.
- Keep everything as it is but use the game controller API once when a controller is connected to query information which axis IDs are trigger actually trigger axis (of course only possible for known gamepads). Then use that information to do the half-range mapping manually for trigger axis event.
I am in favor of #3 at the moment. I think using the game controller API is only justified when actually making use of the built-in button mapping feature that it brings. Otherwise just the trigger functionality does not outweight the added complexity it would bring when using both APIs in parallel.
The downside of #3 is that the manually done half-range mapping is actually a bit of a hack cause it replicates the logic which is already implemented in SDL. But in my opinion this is still the best we can get as it is only one calculationjaxis.value / 2 + 16384;
. Of course I am open for your ideas.
So with that solution half-range triggers do work with controllers found in the controller DB. Behavior of other controllers is untouched.I actually implemented it already (WIP) and I think I would dare a PR soon :) Also I added functionality that ES loads a custom game device DB from
~/.emulationstation/es_gamecontroller_db.cfg
in addition to the SDL built-in DB. So the user or Retropie could extend the DB at any time.I really tried to make the code as unintrusive as possible and keep changes to existing code to a minimum. Here is my try:
https://github.com/verybadsoldier/EmulationStation/commits/sdl_trigger_rangeLooks like this then:
lvl2: No additional game controller mapping file found at: '/home/vbs/.emulationstation/es_gamecontroller_db.cfg' lvl2: Controller 'X360 Wireless Controller' supports SDL2 game controller API. Number of trigger axis: 2 lvl2: Added known joystick Xbox 360 Wireless Receiver (instance ID: 0, device index: 0)
Of course I am totally open to suggestions and ideas. :)
EDIT:
Just saw that Retroarch is doing the hybrid-approach for their SDL input:
https://github.com/libretro/RetroArch/blob/master/input/drivers_joypad/sdl_joypad.c#L104 -
Happy to accept your changes as a PR.
-
@BuZz
Hm, there popped this problem up:
The SDL game controller API does not play nicely with xpad'strigger_to_button
option because it changes the number of axis of the gamepad and then the SDL DB does not match anymore. It is basically a different gamepad then. This is a general problem between xpad and SDL game controller API in my opinion (unrelated to ES).The gory details:
According to the game controller API the axis IDs #2 and #5 of the X360W pad are trigger axis. But this is only true iftrigger_to_button
is not set. If that option is set then the right stick axis are #2 and #3 (conflict with DB for ID #2), otherwise they are #3 and #4. The xpad driver does register two axis less to the kernel iftrigger_to_button
is set. This does not affect evdev itself (right stick axis always have the IDs #3 and #4 regardless oftrigger_to_button
). But it turned out that SDL reassigns the axis IDs internally by just counting them. So if xpad changes the number of axis then SDL's internal axis IDs become different too.So, is the
trigger_to_button
option still needed if triggers would work anyway? As far as I know all XBOX pads are supported by SDL's DB so they should work.Another idea would be to do some sanity check in ES before using game controller API by comparing the number of axis of the connected pad actually has to the number of axis the pad should have according to the DB. But this is quite dirty I think.
-
@vbs I noticed this as well when I was trying to remap my xpad driver for X360W in KODI which by default was setup for the Xboxdrv version.
-
So, I am still on my journey to get analog triggers to work for Dreamcast at least (lr-reicast). After disabling triggers_to_button in xpad.confg I get analog values from the triggers by evdev API nicely. But still they don't work correctly in (for example) Crazy Taxi: Pressing full trigger right only makes a little acceleration and you can't get good speed.
But it took me again a couple of hours to read through retroarch, lr-reicast and lr-pcsx and I came to a quite strange (but simple) conclusion (which is hopefully wrong):
Retroarch just does not support any analog inputs despite of one left and one right analog stick. No analog shoulder triggers and no analog (pressure sensitive) buttons. :(This is the part of the header of libretro which defines all analog inputs of the libretro gamepad:
https://github.com/libretro/RetroArch/blob/master/libretro-common/include/libretro.h#L196
Just two analog inputs (left and right) with X and Y axis.Can this be true? Before starting that debugging, I didn't even imagine that this could be the case which is probably the reason why I searched that long :) If anyone confirms it then I can at least stop searching. Having no analog triggers wouldn't be the end of the world anyway.
lr-reicast uses 8 bit analog triggers internally and has a workaround by mapping two different keys to half-pressed and full-pressed analog triggers. So pressing L1 means half-pressed (127) and L2 means full pressed (255).
-
Well, the guys over at libretro confirmed that currently analog triggers are not supported. For some reason I took analog trigger support for granted. Was a long road to get there :)
Anyway, does enabling analog triggers for Retropie make any sense then if they cannot be used in Retroarch anyway? I could either still do a PR or just let this branch go stale and maybe it can be of use later. I personally think it does not make sense.
Contributions to the project are always appreciated, so if you would like to support us with a donation you can do so here.
Hosting provided by Mythic-Beasts. See the Hosting Information page for more information.