diff --git a/src/client/input/sdl.c b/src/client/input/sdl.c index 0ff9fa8a..d3effd64 100644 --- a/src/client/input/sdl.c +++ b/src/client/input/sdl.c @@ -167,12 +167,25 @@ static qboolean gyro_active = false; // Gyro calibration static float gyro_accum[3]; -static unsigned int num_samples; - 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) + +static unsigned int num_samples; +#define NATIVE_SDL_GYRO 1 // 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 + +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. static qboolean first_init = true; @@ -766,7 +779,7 @@ IN_Update(void) break; } -#if SDL_VERSION_ATLEAST(2, 0, 16) // support for controller sensors (gyro, accelerometer) +#ifdef NATIVE_SDL_GYRO // controller sensors' reading supported (gyro, accelerometer) case SDL_CONTROLLERSENSORUPDATE: if (event.csensor.sensor != SDL_SENSOR_GYRO) { @@ -781,9 +794,39 @@ IN_Update(void) break; } +#else // gyro read as "secondary joystick" + case SDL_JOYAXISMOTION: + if ( !imu_joystick || event.cdevice.which != SDL_JoystickInstanceID(imu_joystick) ) + { + break; // controller axes handled by SDL_CONTROLLERAXISMOTION + } + + int axis_value = event.caxis.value; + if (countdown_reason == REASON_GYROCALIBRATION && updates_countdown) + { + switch (event.caxis.axis) + { + case IMU_JOY_AXIS_GYRO_PITCH: + gyro_accum[0] += axis_value; + num_samples[0]++; + break; + case IMU_JOY_AXIS_GYRO_YAW: + gyro_accum[1] += axis_value; + num_samples[1]++; + break; + case IMU_JOY_AXIS_GYRO_ROLL: + gyro_accum[2] += axis_value; + num_samples[2]++; + } + break; + } + +#endif // NATIVE_SDL_GYRO + if (gyro_active && gyro_mode->value && !cl_paused->value && cls.key_dest == key_game) { +#ifdef NATIVE_SDL_GYRO if (!gyro_turning_axis->value) { gyro_yaw = event.csensor.data[1] - gyro_calibration_y->value; // yaw @@ -793,13 +836,31 @@ IN_Update(void) gyro_yaw = -(event.csensor.data[2] - gyro_calibration_z->value); // roll } gyro_pitch = event.csensor.data[0] - gyro_calibration_x->value; +#else // old "joystick" gyro + switch (event.caxis.axis) // inside "case SDL_JOYAXISMOTION" here + { + case IMU_JOY_AXIS_GYRO_PITCH: + gyro_pitch = -(axis_value - gyro_calibration_x->value); + break; + case IMU_JOY_AXIS_GYRO_YAW: + if (!gyro_turning_axis->value) + { + gyro_yaw = axis_value - gyro_calibration_y->value; + } + break; + case IMU_JOY_AXIS_GYRO_ROLL: + if (gyro_turning_axis->value) + { + gyro_yaw = axis_value - gyro_calibration_z->value; + } + } +#endif // NATIVE_SDL_GYRO } else { gyro_yaw = gyro_pitch = 0; } break; -#endif // SDL_VERSION_ATLEAST(2, 0, 16) case SDL_CONTROLLERDEVICEREMOVED: if (!controller) @@ -889,10 +950,23 @@ IN_Update(void) case REASON_GYROCALIBRATION: // finish and save calibration { +#ifdef NATIVE_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); Cvar_SetValue("gyro_calibration_z", gyro_accum[2] * inverseSamples); +#else + if (!num_samples[0] || !num_samples[1] || !num_samples[2]) + { + Com_Printf("Calibration failed, please retry inside a level after having moved your controller a little.\n"); + } + else + { + Cvar_SetValue("gyro_calibration_x", gyro_accum[0] / num_samples[0]); + Cvar_SetValue("gyro_calibration_y", gyro_accum[1] / num_samples[1]); + Cvar_SetValue("gyro_calibration_z", gyro_accum[2] / num_samples[2]); + } +#endif Com_Printf("Calibration results:\n X=%f Y=%f Z=%f\n", gyro_calibration_x->value, gyro_calibration_y->value, gyro_calibration_z->value); CalibrationFinishedCallback(); @@ -1269,7 +1343,11 @@ 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 float gyroViewFactor = (1.0f / M_PI) * joyViewFactor; +#else + float gyroViewFactor = (1.0f / 2560.0f) * joyViewFactor; // normalized for Switch gyro +#endif if (joystick_yaw) { @@ -1915,10 +1993,14 @@ Controller_Rumble(const char *name, vec3_t source, qboolean from_player, void StartCalibration(void) { +#ifdef NATIVE_SDL_GYRO + num_samples = 0; +#else + num_samples[0] = num_samples[1] = num_samples[2] = 0; +#endif gyro_accum[0] = 0.0; gyro_accum[1] = 0.0; gyro_accum[2] = 0.0; - num_samples = 0; updates_countdown = 300; countdown_reason = REASON_GYROCALIBRATION; } @@ -2033,9 +2115,26 @@ IN_Controller_Init(qboolean notify_user) // 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)) { - Com_Printf ("Skipping IMU device.\n"); 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 ) + { + imu_joystick = SDL_JoystickOpen(i); + if (imu_joystick) + { + show_gyro = true; + Com_Printf ("Using this device as Gyro sensor.\n"); + } + else + { + Com_Printf ("Couldn't open IMU: %s.\n", SDL_GetError()); + } + } +#endif continue; } @@ -2059,7 +2158,7 @@ IN_Controller_Init(qboolean notify_user) SDL_JoystickClose(joystick); joystick = NULL; - if (is_controller) + if (is_controller && !controller) { controller = SDL_GameControllerOpen(i); if (!controller) @@ -2071,7 +2170,7 @@ IN_Controller_Init(qboolean notify_user) show_gamepad = true; Com_Printf("Enabled as Game Controller, settings:\n%s\n", SDL_GameControllerMapping(controller)); -#if SDL_VERSION_ATLEAST(2, 0, 16) // support for controller sensors +#ifdef NATIVE_SDL_GYRO if ( SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) && !SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE) ) @@ -2090,7 +2189,7 @@ IN_Controller_Init(qboolean notify_user) SDL_GameControllerSetLED(controller, 0, 80, 0); // green light } -#endif // SDL_VERSION_ATLEAST(2, 0, 16) +#endif // NATIVE_SDL_GYRO joystick_haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(controller)); @@ -2124,7 +2223,9 @@ 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 break; +#endif } } } @@ -2236,6 +2337,14 @@ IN_Controller_Shutdown(qboolean notify_user) show_gamepad = show_gyro = show_haptic = false; joystick_left_x = joystick_left_y = joystick_right_x = joystick_right_y = 0; gyro_yaw = gyro_pitch = 0; + +#ifndef NATIVE_SDL_GYRO + if (imu_joystick) + { + SDL_JoystickClose(imu_joystick); + imu_joystick = NULL; + } +#endif } /*