From 60f0fdd9691367962247a4aad289b8ccb5b29c32 Mon Sep 17 00:00:00 2001 From: Jaime Moreira Date: Sun, 15 Dec 2024 22:51:56 -0300 Subject: [PATCH] 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. --- Makefile | 4 +++ src/client/input/sdl2.c | 60 ++++++++++++++++++++++++----------------- src/client/input/sdl3.c | 60 ++++++++++++++++++++++++++++------------- 3 files changed, 80 insertions(+), 44 deletions(-) diff --git a/Makefile b/Makefile index 89926966..a74d244f 100644 --- a/Makefile +++ b/Makefile @@ -286,6 +286,10 @@ else SDLCFLAGS := $(shell sdl2-config --cflags) endif +ifdef NO_SDL_GYRO +SDLCFLAGS += -DNO_SDL_GYRO +endif + # ---------- # Base include path. diff --git a/src/client/input/sdl2.c b/src/client/input/sdl2.c index ed2a28f9..0faece38 100644 --- a/src/client/input/sdl2.c +++ b/src/client/input/sdl2.c @@ -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); diff --git a/src/client/input/sdl3.c b/src/client/input/sdl3.c index b4b61080..808ae93c 100644 --- a/src/client/input/sdl3.c +++ b/src/client/input/sdl3.c @@ -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);