Fixed bug 5241 - SDL on Linux needs a way to turn deadzones off

pj5085

I added some printf to verify the math being done.  Of the three joysticks I have, it works correctly for at least two, and seems to work correctly for the third.  I say "seems to" because, for the third joystick, the values never go through the AxisCorrect function, and thus never hit my printf statements, even though they did in the version I wrote my patch against.  I'm not sure what's going on there, but it at least seems to be working correctly in as much as I can tell.

I note this result in particular, for an SNES Gamepad (min=0, max=255):

Joystick value 0 becomes -32768
Joystick value 127 becomes 0
Joystick value 255 becomes 32767

Without the code that forces a zero point, the 127 input value would become -129, so I think you see why I added that code to turn it into zero.  However, I think Kai Krakow has a point about how SDL shouldn't assume that there should be a center.

Obviously in the majority of cases there actually should be a center, and the code that turns that 127 into an actual 0 is creating only a 0.2% error over 0.4% of this joystick's range.  However, what if there is an axis that is some kind of special control, like a 4-position switch, and, for whatever reason, the joystick reports it as an axis with 4 possible values, 0 to 3?  In that case, mutilating the two center values to the same value is much more of an error and and turns that 4-position switch into a 3-position switch.  If any joystick does this with a 2-position switch, then this code would render that control entirely useless as it would report the same value with the switch in either position.  Obviously the code could require that there be at least N possible values, to guess whether something is a proper axis or just some kind of switch, but the choice of N would be arbitrary and that's ugly.

I guess the real problem here is that my gamepad is just kind of broken.  It should be reporting a range of -1 to +1 since that's what it actually does.  Also, as Kai Krakow points out, it's probably not SDL's place to fix broken hardware.  I'll add that, if SDL does fix broken hardware, it should probably actually know that it's broken rather than be merely guessing that it is.

So, to the extent that SDL is able to do stuff like this, perhaps it's something better left for the user to configure in some kind of config file.
This commit is contained in:
Sam Lantinga 2020-12-14 09:15:47 -08:00
parent 44d4a61d92
commit 8795ca7067

View File

@ -1143,29 +1143,7 @@ AxisCorrect(SDL_Joystick *joystick, int which, int value)
value *= correct->coef[2];
value >>= 13;
} else {
int new_value = (int)SDL_floorf((value - correct->minimum) * correct->scale + SDL_JOYSTICK_AXIS_MIN + 0.5f);
/* At least one gamepad has a minimum of 0 and a maximum is 255,
* which makes it's center point a non-integer, 127.5. Thus it
* must report either 127 or 128, neither of which is center.
* At least one analog controller with the same range returns 128
* easily when at center, but seems completely incapable of
* returning 127 no matter how carefully the stick is moved.
* So here we detect if the axis' range does not have a center
* point, and if so, and if the reported position is on either
* side of center, we report that it is actually at the center.
*/
if ((correct->maximum - correct->minimum) & 1) {
int lower_center = (correct->maximum - correct->minimum - 1) / 2;
int higher_center = (correct->maximum - correct->minimum + 1) / 2;
if (value == lower_center || value == higher_center) {
new_value = 0;
}
}
value = new_value;
value = (int)SDL_floorf((value - correct->minimum) * correct->scale + SDL_JOYSTICK_AXIS_MIN + 0.5f);
}
}