SDL2/src/joystick/linux/SDL_sysjoystick_c.h

100 lines
2.7 KiB
C
Raw Normal View History

/*
Simple DirectMedia Layer
2022-01-03 11:40:00 -06:00
Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_sysjoystick_c_h_
#define SDL_sysjoystick_c_h_
#include <linux/input.h>
struct SDL_joylist_item;
/* The private structure used to keep track of a joystick */
struct joystick_hwdata
{
int fd;
struct SDL_joylist_item *item;
SDL_JoystickGUID guid;
char *fname; /* Used in haptic subsystem */
SDL_bool ff_rumble;
SDL_bool ff_sine;
struct ff_effect effect;
Uint32 effect_expiration;
/* The current Linux joystick driver maps hats to two axes */
struct hwdata_hat
{
int axis[2];
} *hats;
/* The current Linux joystick driver maps balls to two axes */
struct hwdata_ball
{
int axis[2];
} *balls;
/* Support for the Linux 2.4 unified input interface */
Uint8 key_map[KEY_MAX];
Uint8 abs_map[ABS_MAX];
SDL_bool has_key[KEY_MAX];
SDL_bool has_abs[ABS_MAX];
/* Support for the classic joystick interface */
SDL_bool classic;
Uint16 *key_pam;
Uint8 *abs_pam;
struct axis_correct
{
Fixed bug 5241 - SDL on Linux needs a way to turn deadzones off pj5085 It occurred to me that my simple patch that comments out a few lines of code does not correctly remove the dead zone since the calculation presumably assumes the dead zone has been cut out of the range. Then, while looking into how to make it output the correct range of values, I realized SDL wasn't returning the correct range of values to begin with. This line of code was already present: printf("Values = { %d, %d, %d, %d, %d }\n", absinfo.value, absinfo.minimum, absinfo.maximum, absinfo.fuzz, absinfo.flat); For my joystick this yeilds: Values = { 0, -127, 127, 0, 15 } Then this code calculates the coefficients: In SDL1: 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) / 2 - 2 * absinfo.flat); if ( t != 0 ) { joystick->hwdata->abs_correct[i].coef[2] = (1 << 29) / t; } else { joystick->hwdata->abs_correct[i].coef[2] = 0; } In SDL2: 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; } else { joystick->hwdata->abs_correct[i].coef[2] = 0; } Neither calculates the correct coefficients for the code in the AxisCorrect function. In SDL1: if ( value > correct->coef[0] ) { if ( value < correct->coef[1] ) { return 0; } value -= correct->coef[1]; } else { value -= correct->coef[0]; } value *= correct->coef[2]; value >>= 14; In SDL2: value *= 2; if (value > correct->coef[0]) { if (value < correct->coef[1]) { return 0; } value -= correct->coef[1]; } else { value -= correct->coef[0]; } In SDL1, the calculated coefficients are coef[0]=15, coef[1]=-15 and coef[2]=5534751. So with a full-scale input of 127, it calculates an output value of 37835, which is considerably out of range. In SDL2, the calculated coefficients are coef[0]=30, coef[1]=-30, and coef[2]=1383687. So with a full-scale input of 127, it calculates the same output value of 37835. I tested it with the 3 joysticks I have, and it produces out-of-range values for all of them. Anyway, since dead zones are garbage, I just deleted all of that junk and wrote some code that takes the absinfo.minimum and absinfo.maximum values and uses them to scale the axis range to -32767 through +32767. I also made it detect when a range doesn't have an integer center point, e.g. the center of -128 to + 127 is -0.5. In such cases, if either value to the side of the center is provided, it zeros it, but it otherwise doesn't implement any kind of dead zone. This seemed important with my gamepad which provides only the values of 0, 127, and 255, since without this hack it would never be centered. Also, the previous minimum output value was -32768, but as that creates an output range that has no true center, I changed the minimum value to -32767. I tested it with the 3 joystick devices I have and it seems to create correct values for all of them.
2020-12-13 01:48:02 -06:00
SDL_bool use_deadzones;
/* Deadzone coefficients */
int coef[3];
Fixed bug 5241 - SDL on Linux needs a way to turn deadzones off pj5085 It occurred to me that my simple patch that comments out a few lines of code does not correctly remove the dead zone since the calculation presumably assumes the dead zone has been cut out of the range. Then, while looking into how to make it output the correct range of values, I realized SDL wasn't returning the correct range of values to begin with. This line of code was already present: printf("Values = { %d, %d, %d, %d, %d }\n", absinfo.value, absinfo.minimum, absinfo.maximum, absinfo.fuzz, absinfo.flat); For my joystick this yeilds: Values = { 0, -127, 127, 0, 15 } Then this code calculates the coefficients: In SDL1: 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) / 2 - 2 * absinfo.flat); if ( t != 0 ) { joystick->hwdata->abs_correct[i].coef[2] = (1 << 29) / t; } else { joystick->hwdata->abs_correct[i].coef[2] = 0; } In SDL2: 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; } else { joystick->hwdata->abs_correct[i].coef[2] = 0; } Neither calculates the correct coefficients for the code in the AxisCorrect function. In SDL1: if ( value > correct->coef[0] ) { if ( value < correct->coef[1] ) { return 0; } value -= correct->coef[1]; } else { value -= correct->coef[0]; } value *= correct->coef[2]; value >>= 14; In SDL2: value *= 2; if (value > correct->coef[0]) { if (value < correct->coef[1]) { return 0; } value -= correct->coef[1]; } else { value -= correct->coef[0]; } In SDL1, the calculated coefficients are coef[0]=15, coef[1]=-15 and coef[2]=5534751. So with a full-scale input of 127, it calculates an output value of 37835, which is considerably out of range. In SDL2, the calculated coefficients are coef[0]=30, coef[1]=-30, and coef[2]=1383687. So with a full-scale input of 127, it calculates the same output value of 37835. I tested it with the 3 joysticks I have, and it produces out-of-range values for all of them. Anyway, since dead zones are garbage, I just deleted all of that junk and wrote some code that takes the absinfo.minimum and absinfo.maximum values and uses them to scale the axis range to -32767 through +32767. I also made it detect when a range doesn't have an integer center point, e.g. the center of -128 to + 127 is -0.5. In such cases, if either value to the side of the center is provided, it zeros it, but it otherwise doesn't implement any kind of dead zone. This seemed important with my gamepad which provides only the values of 0, 127, and 255, since without this hack it would never be centered. Also, the previous minimum output value was -32768, but as that creates an output range that has no true center, I changed the minimum value to -32767. I tested it with the 3 joystick devices I have and it seems to create correct values for all of them.
2020-12-13 01:48:02 -06:00
/* Raw coordinate scale */
int minimum;
int maximum;
float scale;
} abs_correct[ABS_MAX];
SDL_bool fresh;
SDL_bool recovering_from_dropped;
/* Steam Controller support */
SDL_bool m_bSteamController;
2021-04-13 19:00:24 -05:00
/* 4 = (ABS_HAT3X-ABS_HAT0X)/2 (see input-event-codes.h in kernel) */
int hats_indices[4];
SDL_bool has_hat[4];
struct hat_axis_correct
{
SDL_bool use_deadzones;
int minimum[2];
int maximum[2];
} hat_correct[4];
/* Set when gamepad is pending removal due to ENODEV read error */
SDL_bool gone;
};
#endif /* SDL_sysjoystick_c_h_ */
/* vi: set ts=4 sw=4 expandtab: */