Makefile parameter to disable SDL Gyro support

"make NO_SDL_GYRO=1" disables sensor reading support and forces
behavior of SDL < 2.0.14 under any version, even SDL 3.
Meant for Arch Linux ARM, which supports 2.30.10 at this time, but
doesn't support gyro readings (no HIDAPI support?), even if it shows
an additional "IMU joystick" when connecting Nintendo gamepads.
This commit allows to use the "IMU joystick" in game, extending #990.
Identification of joysticks as "IMU" or "Left Joy-Con" is more
generic now, covering more possible naming conventions.
This commit is contained in:
Jaime Moreira 2024-12-15 22:51:56 -03:00
parent 661761f047
commit 60f0fdd969
3 changed files with 80 additions and 44 deletions

View file

@ -286,6 +286,10 @@ else
SDLCFLAGS := $(shell sdl2-config --cflags)
endif
ifdef NO_SDL_GYRO
SDLCFLAGS += -DNO_SDL_GYRO
endif
# ----------
# Base include path.

View file

@ -173,19 +173,20 @@ static cvar_t *gyro_calibration_x;
static cvar_t *gyro_calibration_y;
static cvar_t *gyro_calibration_z;
#if SDL_VERSION_ATLEAST(2, 0, 14) // support for controller sensors (gyro, accelerometer)
// If the used SDL version doesn't support gamepad sensors...
#if !SDL_VERSION_ATLEAST(2, 0, 14)
// ...disable support for reading them.
#define NO_SDL_GYRO
#endif
#ifndef NO_SDL_GYRO // use SDL_CONTROLLERSENSORUPDATE to read gyro
static unsigned int num_samples;
#define NATIVE_SDL_GYRO // uses SDL_CONTROLLERSENSORUPDATE to read gyro
#else // for SDL < 2.0.14, gyro can be read as a "secondary joystick" exposed by dkms-hid-nintendo
#else // gyro can be read as a "secondary joystick" exposed by dkms-hid-nintendo
static unsigned int num_samples[3];
static SDL_Joystick *imu_joystick = NULL; // gyro "joystick"
#define IMU_JOY_AXIS_GYRO_ROLL 3
#define IMU_JOY_AXIS_GYRO_PITCH 4
#define IMU_JOY_AXIS_GYRO_YAW 5
#endif
// To ignore SDL_JOYDEVICEADDED at game init. Allows for hot plugging of game controller afterwards.
@ -789,7 +790,7 @@ IN_Update(void)
break;
}
#ifdef NATIVE_SDL_GYRO // controller sensors' reading supported (gyro, accelerometer)
#ifndef NO_SDL_GYRO // gamepad sensors' reading is supported (gyro, accelerometer)
case SDL_CONTROLLERSENSORUPDATE:
if (event.csensor.sensor != SDL_SENSOR_GYRO)
{
@ -804,7 +805,7 @@ IN_Update(void)
break;
}
#else // gyro read as "secondary joystick"
#else // gyro read from a "secondary joystick" (usually with name ending in "IMU")
case SDL_JOYAXISMOTION:
if ( !imu_joystick || event.cdevice.which != SDL_JoystickInstanceID(imu_joystick) )
{
@ -831,12 +832,12 @@ IN_Update(void)
break;
}
#endif // NATIVE_SDL_GYRO
#endif // !NO_SDL_GYRO
if (gyro_active && gyro_mode->value &&
!cl_paused->value && cls.key_dest == key_game)
{
#ifdef NATIVE_SDL_GYRO
#ifndef NO_SDL_GYRO
if (!gyro_turning_axis->value)
{
gyro_yaw = event.csensor.data[1] - gyro_calibration_y->value; // yaw
@ -864,7 +865,7 @@ IN_Update(void)
gyro_yaw = axis_value - gyro_calibration_z->value;
}
}
#endif // NATIVE_SDL_GYRO
#endif // !NO_SDL_GYRO
}
else
{
@ -956,7 +957,7 @@ IN_Update(void)
case REASON_GYROCALIBRATION: // finish and save calibration
{
#ifdef NATIVE_SDL_GYRO
#ifndef NO_SDL_GYRO
const float inverseSamples = 1.f / num_samples;
Cvar_SetValue("gyro_calibration_x", gyro_accum[0] * inverseSamples);
Cvar_SetValue("gyro_calibration_y", gyro_accum[1] * inverseSamples);
@ -1081,7 +1082,7 @@ IN_TightenInput(float yaw, float pitch)
{
thumbstick_t input = { yaw, pitch };
const float magnitude = IN_StickMagnitude(input);
#ifdef NATIVE_SDL_GYRO
#ifndef NO_SDL_GYRO
const float threshold = (M_PI / 180.0f) * gyro_tightening->value;
#else
const float threshold = (2560.0f / 180.0f) * gyro_tightening->value;
@ -1369,7 +1370,7 @@ IN_Move(usercmd_t *cmd)
//
// For movement this is not needed, as those are absolute values independent of framerate
float joyViewFactor = cls.rframetime/0.01666f;
#ifdef NATIVE_SDL_GYRO
#ifndef NO_SDL_GYRO
float gyroViewFactor = (1.0f / M_PI) * joyViewFactor;
#else
float gyroViewFactor = (1.0f / 2560.0f) * joyViewFactor; // normalized for Switch gyro
@ -2039,7 +2040,7 @@ Controller_Rumble(const char *name, vec3_t source, qboolean from_player,
void
StartCalibration(void)
{
#ifdef NATIVE_SDL_GYRO
#ifndef NO_SDL_GYRO
num_samples = 0;
#else
num_samples[0] = num_samples[1] = num_samples[2] = 0;
@ -2159,27 +2160,36 @@ IN_Controller_Init(qboolean notify_user)
Com_Printf ("Trying joystick %d, '%s'\n", i+1, joystick_name);
// Ugly hack to detect IMU-only devices - works for Switch controllers at least
if (name_len > 4 && !strncmp(joystick_name + name_len - 4, " IMU", 4))
if ( name_len > 6 && strstr(joystick_name + name_len - 6, "IMU") )
{
#ifndef NO_SDL_GYRO
SDL_JoystickClose(joystick);
joystick = NULL;
#ifdef NATIVE_SDL_GYRO
Com_Printf ("Skipping IMU device.\n");
#else // if it's not a Left JoyCon, use it as Gyro
Com_Printf ("IMU device found.\n");
if ( !imu_joystick && name_len > 16 && strncmp(joystick_name + name_len - 16, "Left Joy-Con IMU", 16) != 0 )
qboolean using_imu = !imu_joystick && !( strstr(joystick_name, "Joy-Con") && strstr(joystick_name, "L") );
Com_Printf ("IMU device found... ");
SDL_JoystickClose(joystick);
joystick = NULL;
if (using_imu)
{
imu_joystick = SDL_JoystickOpen(i);
if (imu_joystick)
{
show_gyro = true;
Com_Printf ("Using this device as Gyro sensor.\n");
Com_Printf ("using it as Gyro sensor.\n");
}
else
{
Com_Printf ("Couldn't open IMU: %s.\n", SDL_GetError());
Com_Printf ("\nCouldn't open IMU: %s.\n", SDL_GetError());
}
}
else
{
Com_Printf ("skipping.\n");
}
#endif
continue;
}
@ -2216,7 +2226,7 @@ IN_Controller_Init(qboolean notify_user)
show_gamepad = true;
Com_Printf("Enabled as Game Controller, settings:\n%s\n", SDL_GameControllerMapping(controller));
#ifdef NATIVE_SDL_GYRO
#ifndef NO_SDL_GYRO
if ( SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO)
&& !SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE) )
@ -2239,7 +2249,7 @@ IN_Controller_Init(qboolean notify_user)
SDL_GameControllerSetLED(controller, 0, 80, 0); // green light
}
#endif // NATIVE_SDL_GYRO
#endif // !NO_SDL_GYRO
joystick_haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(controller));
@ -2273,7 +2283,7 @@ IN_Controller_Init(qboolean notify_user)
Com_Printf("Controller doesn't support rumble.\n");
}
#ifdef NATIVE_SDL_GYRO // "native" exits when finding a single working controller
#ifndef NO_SDL_GYRO // "native SDL gyro" exits when finding a single working gamepad
break;
#endif
}
@ -2390,7 +2400,7 @@ IN_Controller_Shutdown(qboolean notify_user)
joystick_left_x = joystick_left_y = joystick_right_x = joystick_right_y = 0;
gyro_yaw = gyro_pitch = 0;
#ifndef NATIVE_SDL_GYRO
#ifdef NO_SDL_GYRO
if (imu_joystick)
{
SDL_JoystickClose(imu_joystick);

View file

@ -175,8 +175,17 @@ static float gyro_accum[3];
static cvar_t *gyro_calibration_x;
static cvar_t *gyro_calibration_y;
static cvar_t *gyro_calibration_z;
// If "SDL gyro" is not explicitly disabled, use SDL_EVENT_GAMEPAD_SENSOR_UPDATE to read gyro
#ifndef NO_SDL_GYRO
static unsigned int num_samples;
#define NATIVE_SDL_GYRO // uses SDL_EVENT_GAMEPAD_SENSOR_UPDATE to read gyro
#else // otherwise, gyro can be read as a "secondary joystick" exposed by dkms-hid-nintendo
static unsigned int num_samples[3];
static SDL_Joystick *imu_joystick = NULL; // gyro "joystick"
#define IMU_JOY_AXIS_GYRO_ROLL 3
#define IMU_JOY_AXIS_GYRO_PITCH 4
#define IMU_JOY_AXIS_GYRO_YAW 5
#endif
// To ignore SDL_EVENT_JOYSTICK_ADDED at game init. Allows for hot plugging of gamepad afterwards.
static qboolean first_init = true;
@ -780,7 +789,7 @@ IN_Update(void)
break;
}
#ifdef NATIVE_SDL_GYRO // controller sensors' reading supported (gyro, accelerometer)
#ifndef NO_SDL_GYRO // gamepad sensors' reading is supported (gyro, accelerometer)
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE :
if (event.gsensor.sensor != SDL_SENSOR_GYRO)
{
@ -795,7 +804,7 @@ IN_Update(void)
break;
}
#else // gyro read as "secondary joystick"
#else // gyro read from a "secondary joystick" (usually with name ending in "IMU")
case SDL_EVENT_JOYSTICK_AXIS_MOTION :
if ( !imu_joystick || event.gdevice.which != SDL_GetJoystickID(imu_joystick) )
{
@ -822,12 +831,12 @@ IN_Update(void)
break;
}
#endif // NATIVE_SDL_GYRO
#endif // !NO_SDL_GYRO
if (gyro_active && gyro_mode->value &&
!cl_paused->value && cls.key_dest == key_game)
{
#ifdef NATIVE_SDL_GYRO
#ifndef NO_SDL_GYRO
if (!gyro_turning_axis->value)
{
gyro_yaw = event.gsensor.data[1] - gyro_calibration_y->value; // yaw
@ -855,7 +864,7 @@ IN_Update(void)
gyro_yaw = axis_value - gyro_calibration_z->value;
}
}
#endif // NATIVE_SDL_GYRO
#endif // !NO_SDL_GYRO
}
else
{
@ -945,7 +954,7 @@ IN_Update(void)
case REASON_GYROCALIBRATION: // finish and save calibration
{
#ifdef NATIVE_SDL_GYRO
#ifndef NO_SDL_GYRO
const float inverseSamples = 1.f / num_samples;
Cvar_SetValue("gyro_calibration_x", gyro_accum[0] * inverseSamples);
Cvar_SetValue("gyro_calibration_y", gyro_accum[1] * inverseSamples);
@ -1070,7 +1079,11 @@ IN_TightenInput(float yaw, float pitch)
{
thumbstick_t input = { yaw, pitch };
const float magnitude = IN_StickMagnitude(input);
#ifndef NO_SDL_GYRO
const float threshold = (M_PI / 180.0f) * gyro_tightening->value;
#else
const float threshold = (2560.0f / 180.0f) * gyro_tightening->value;
#endif
if (magnitude < threshold)
{
@ -1354,7 +1367,7 @@ IN_Move(usercmd_t *cmd)
//
// For movement this is not needed, as those are absolute values independent of framerate
float joyViewFactor = cls.rframetime/0.01666f;
#ifdef NATIVE_SDL_GYRO
#ifndef NO_SDL_GYRO
float gyroViewFactor = (1.0f / M_PI) * joyViewFactor;
#else
float gyroViewFactor = (1.0f / 2560.0f) * joyViewFactor; // normalized for Switch gyro
@ -2025,7 +2038,7 @@ Controller_Rumble(const char *name, vec3_t source, qboolean from_player,
void
StartCalibration(void)
{
#ifdef NATIVE_SDL_GYRO
#ifndef NO_SDL_GYRO
num_samples = 0;
#else
num_samples[0] = num_samples[1] = num_samples[2] = 0;
@ -2152,27 +2165,36 @@ IN_Controller_Init(qboolean notify_user)
Com_Printf ("Trying joystick %d, '%s'\n", i+1, joystick_name);
// Ugly hack to detect IMU-only devices - works for Switch controllers at least
if (name_len > 4 && !strncmp(joystick_name + name_len - 4, " IMU", 4))
if ( name_len > 6 && strstr(joystick_name + name_len - 6, "IMU") )
{
#ifndef NO_SDL_GYRO
SDL_CloseJoystick(joystick);
joystick = NULL;
#ifdef NATIVE_SDL_GYRO
Com_Printf ("Skipping IMU device.\n");
#else // if it's not a Left JoyCon, use it as Gyro
Com_Printf ("IMU device found.\n");
if ( !imu_joystick && name_len > 16 && strncmp(joystick_name + name_len - 16, "Left Joy-Con IMU", 16) != 0 )
qboolean using_imu = !imu_joystick && !( strstr(joystick_name, "Joy-Con") && strstr(joystick_name, "L") );
Com_Printf ("IMU device found... ");
SDL_CloseJoystick(joystick);
joystick = NULL;
if (using_imu)
{
imu_joystick = SDL_OpenJoystick(joysticks[i]);
if (imu_joystick)
{
show_gyro = true;
Com_Printf ("Using this device as Gyro sensor.\n");
Com_Printf ("using it as Gyro sensor.\n");
}
else
{
Com_Printf ("Couldn't open IMU: %s.\n", SDL_GetError());
Com_Printf ("\nCouldn't open IMU: %s.\n", SDL_GetError());
}
}
else
{
Com_Printf ("skipping.\n");
}
#endif
continue;
}
@ -2210,7 +2232,7 @@ IN_Controller_Init(qboolean notify_user)
Com_Printf("Enabled as Gamepad, settings:\n%s\n",
SDL_GetGamepadMapping(controller));
#ifdef NATIVE_SDL_GYRO
#ifndef NO_SDL_GYRO
if (SDL_GamepadHasSensor(controller, SDL_SENSOR_GYRO)
&& SDL_SetGamepadSensorEnabled(controller, SDL_SENSOR_GYRO, true) )
@ -2230,7 +2252,7 @@ IN_Controller_Init(qboolean notify_user)
SDL_SetGamepadLED(controller, 0, 80, 0); // green light
}
#endif // NATIVE_SDL_GYRO
#endif // !NO_SDL_GYRO
joystick_haptic = SDL_OpenHapticFromJoystick(SDL_GetGamepadJoystick(controller));
@ -2259,7 +2281,7 @@ IN_Controller_Init(qboolean notify_user)
Com_Printf("Gamepad doesn't support rumble.\n");
}
#ifdef NATIVE_SDL_GYRO // "native" exits when finding a single working controller
#ifndef NO_SDL_GYRO // "native SDL gyro" exits when finding a single working gamepad
break;
#endif
}
@ -2384,7 +2406,7 @@ IN_Controller_Shutdown(qboolean notify_user)
joystick_left_x = joystick_left_y = joystick_right_x = joystick_right_y = 0;
gyro_yaw = gyro_pitch = 0;
#ifndef NATIVE_SDL_GYRO
#ifdef NO_SDL_GYRO
if (imu_joystick)
{
SDL_CloseJoystick(imu_joystick);