We are currently migrating Bugzilla to GitHub issues.
Any changes made to the bug tracker now will be lost, so please do not post new bugs or make changes to them.
When we're done, all bug URLs will redirect to their equivalent location on the new bug tracker.

Bug 2823

Summary: Release events for triggers receive wrong centered value
Product: SDL Reporter: Ryochan7 <nickles.travis>
Component: joystickAssignee: Sam Lantinga <slouken>
Status: RESOLVED FIXED QA Contact: Sam Lantinga <slouken>
Severity: normal    
Priority: P2 CC: Gert-Jan, rodrigo.alfenas, x414e54
Version: 2.0.3   
Hardware: x86_64   
OS: Windows (All)   
Attachments: Patch for xinput trigger bug.
Patch to change trigger value if recentering

Description Ryochan7 2014-12-16 16:05:02 UTC
I have been using SDL 2 for a little project that I have been developing for a while. My project is called antimicro and it takes gamepad input and then translates gamepad events into keyboard and mouse events. SDL is used to read the input from an XInput gamepad and it works great for the most part. However, there is one glaring problem that I have encountered.

When a device is unplugged and SDL sends the centered value release events for all axes, buttons, and hats, SDL does not use the proper centered value for the triggers. It pushes an SDL_JOYAXISMOTION event onto the queue with a value of 0 for all axes. That value is converted to around 16,000 for a Game Controller. That value is incorrect for triggers and, in my program, that causes any bindings that are assigned to the triggers to get activated. With most profiles, that will typically mean that a mouse right click and left click will be activated before the device is finally seen as removed and then those bindings are released by antimicro.

http://hg.libsdl.org/SDL/file/2b2cfda26085/src/joystick/SDL_joystick.c#l676
Comment 1 x414e54 2015-07-23 13:03:17 UTC
I can confirm this with the testgamecontroller test case in the SDL repo.

Testing with both an Xbox controller and PS4 controller the shoulder triggers are both active to start with until they receive the first motion event.
Comment 2 Gert-Jan Stolk 2016-06-05 11:14:36 UTC
I've been looking into this, but I'm not quite sure how to implement a solution. I have a hard time finding where the _SDL_Joystick struct is initialized.

Here is what's going on and what I think could be changed to solve this:

Sequence of events
- The controller is initialized with 0 as it's trigger axis value (all axis are set to 0, which is great except for triggers...).
+ [Here all axis mapped to  SDL_CONTROLLER_AXIS_TRIGGERLEFT or  SDL_CONTROLLER_AXIS_TRIGGERRIGHT should be set to -32768]
- SDL_gamecontroller.c:1010 blows up the world by adding 16384, normally used to map the trigger to 0 - 32767, but now pulling the trigger halfway to 16384.
- When any action is done on the controller SDL_xinputjoystick.c:351 -> SDL_xinputjoystick.c:297 maps the trigger to -32768 to 32767, all is good again, because SDL_gamecontroller.c:1010 will map that to 0 - 32767.
- When the controller is disconnected all axis are reset to 0, which again causes one frame of trigger input.
+ [Here all axis mapped to  SDL_CONTROLLER_AXIS_TRIGGERLEFT or  SDL_CONTROLLER_AXIS_TRIGGERRIGHT should be set to -32768]


We had a lot of problems with this for SpeedRunners so far, as the boost power is mapped to a trigger. Since I replaced plain xinput with SDL_gamecontroller the boost is now regularly stuck.
Comment 3 Gert-Jan Stolk 2016-06-05 21:20:10 UTC
Created attachment 2481 [details]
Patch for xinput trigger bug.

Partial fix for trigger being pressed by default on game controller devices
Comment 4 Gert-Jan Stolk 2016-06-05 21:33:52 UTC
Oh wow that comment got auto-posted with the attachement, anyway continued:

Partial fix for trigger being pressed by default on game controller devices.
Normally SDL_PrivateJoystickAxis is used to set these kinds of values, but that won't work here because there is keyboard focus check in there which is not relevant here.

It fixes the most common part of this bug, which is the triggers being pressed at startup.
It does not fix the bug where the triggers will be pressed for one frame before disconnecting the controller.

That issue is a bit more complicated to solve because that trigger press state is set from SDL_events.c:404 -> SDL_joystick.c:691 where there is no reference to a SDL_GameController instance. Therefore we have no mapping, and therefor no idea which axes are triggers.
Ideally either SDL_joystick could have a reference to SDL_GameController, or maybe the GameController subsystem should also be informed of a disconnect when it happens.
Comment 5 Ryochan7 2016-06-13 22:44:53 UTC
Created attachment 2496 [details]
Patch to change trigger value if recentering

I had worked around this issue in my own program by using two passes for reading the event queue. The first pass would grab all events and determine if events for a gamepad should be considered a pre-removal event. Those generated events would be ignored and the actual device removal event would be used to release all buttons and axes of a gamepad. Looking at the current source code, it seems like the best place to account for this scenario would be in the SDL_GameControllerEventWatcher function. If force_recentering is currently set for an SDL_Joystick instance and the axis is determined to be mapped to a game controller trigger, the final value used when calling SDL_PrivateGameControllerAxis will be 0 instead of 16384 like what happens now. I have done some minor testing by commenting out my workaround and using the SDL tweak instead but adding a small check seems to do the trick.
Comment 6 Sam Lantinga 2016-10-07 23:04:51 UTC
Fixed, thanks!
https://hg.libsdl.org/SDL/rev/612fbaee8e3c