diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index c2ddaaf9b..eefe38144 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -828,6 +828,7 @@ ConfigJoystick(SDL_Joystick *joystick, int fd) unsigned long absbit[NBITS(ABS_MAX)] = { 0 }; unsigned long relbit[NBITS(REL_MAX)] = { 0 }; unsigned long ffbit[NBITS(FF_MAX)] = { 0 }; + SDL_bool use_deadzones = SDL_GetHintBoolean(SDL_HINT_LINUX_JOYSTICK_DEADZONES, SDL_FALSE); /* See if this device uses the new unified event API */ if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) && @@ -863,7 +864,7 @@ ConfigJoystick(SDL_Joystick *joystick, int fd) } if (test_bit(i, absbit)) { struct input_absinfo absinfo; - SDL_bool hint_used = SDL_GetHintBoolean(SDL_HINT_LINUX_JOYSTICK_DEADZONES, SDL_TRUE); + struct axis_correct *correct = &joystick->hwdata->abs_correct[i]; if (ioctl(fd, EVIOCGABS(i), &absinfo) < 0) { continue; @@ -876,20 +877,25 @@ ConfigJoystick(SDL_Joystick *joystick, int fd) #endif /* DEBUG_INPUT_EVENTS */ joystick->hwdata->abs_map[i] = joystick->naxes; joystick->hwdata->has_abs[i] = SDL_TRUE; - if (absinfo.minimum == absinfo.maximum) { - joystick->hwdata->abs_correct[i].used = 0; - } else { - joystick->hwdata->abs_correct[i].used = hint_used; - joystick->hwdata->abs_correct[i].coef[0] = - (absinfo.maximum + absinfo.minimum) - 2 * absinfo.flat; - joystick->hwdata->abs_correct[i].coef[1] = - (absinfo.maximum + absinfo.minimum) + 2 * absinfo.flat; - t = ((absinfo.maximum - absinfo.minimum) - 4 * absinfo.flat); - if (t != 0) { - joystick->hwdata->abs_correct[i].coef[2] = - (1 << 28) / t; + + correct->minimum = absinfo.minimum; + correct->maximum = absinfo.maximum; + if (correct->minimum != correct->maximum) { + if (use_deadzones) { + correct->use_deadzones = SDL_TRUE; + correct->coef[0] = (absinfo.maximum + absinfo.minimum) - 2 * absinfo.flat; + correct->coef[1] = (absinfo.maximum + absinfo.minimum) + 2 * absinfo.flat; + t = ((absinfo.maximum - absinfo.minimum) - 4 * absinfo.flat); + if (t != 0) { + correct->coef[2] = (1 << 28) / t; + } else { + correct->coef[2] = 0; + } } else { - joystick->hwdata->abs_correct[i].coef[2] = 0; + float value_range = (correct->maximum - correct->minimum); + float output_range = (SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN); + + correct->scale = (output_range / value_range); } } ++joystick->naxes; @@ -1109,26 +1115,53 @@ AxisCorrect(SDL_Joystick *joystick, int which, int value) struct axis_correct *correct; correct = &joystick->hwdata->abs_correct[which]; - if (correct->used) { - value *= 2; - if (value > correct->coef[0]) { - if (value < correct->coef[1]) { - return 0; + if (correct->minimum != correct->maximum) { + if (correct->use_deadzones) { + value *= 2; + if (value > correct->coef[0]) { + if (value < correct->coef[1]) { + return 0; + } + value -= correct->coef[1]; + } else { + value -= correct->coef[0]; } - value -= correct->coef[1]; + value *= correct->coef[2]; + value >>= 13; } else { - value -= correct->coef[0]; + 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 *= correct->coef[2]; - value >>= 13; } /* Clamp and return */ - if (value < -32768) - return -32768; - if (value > 32767) - return 32767; - + if (value < SDL_JOYSTICK_AXIS_MIN) { + return SDL_JOYSTICK_AXIS_MIN; + } + if (value > SDL_JOYSTICK_AXIS_MAX) { + return SDL_JOYSTICK_AXIS_MAX; + } return value; } diff --git a/src/joystick/linux/SDL_sysjoystick_c.h b/src/joystick/linux/SDL_sysjoystick_c.h index bcbc2550d..dea9405b7 100644 --- a/src/joystick/linux/SDL_sysjoystick_c.h +++ b/src/joystick/linux/SDL_sysjoystick_c.h @@ -58,8 +58,15 @@ struct joystick_hwdata struct axis_correct { - int used; + SDL_bool use_deadzones; + + /* Deadzone coefficients */ int coef[3]; + + /* Raw coordinate scale */ + int minimum; + int maximum; + float scale; } abs_correct[ABS_MAX]; SDL_bool fresh;