From 610c31c7b7957e6f670cc94698f04861ff839d3f Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 17 Jun 2023 08:28:37 -0700
Subject: [PATCH] Generalized the sensor coordinate transform for wraparound
gamepads
---
src/joystick/SDL_gamepad.c | 42 ++++++++----------------
src/joystick/SDL_joystick.c | 58 ++++++++++++++++++++++++++++++----
src/joystick/SDL_sysjoystick.h | 1 +
3 files changed, 66 insertions(+), 35 deletions(-)
diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c
index 7091c11a2d79..3282c7284efb 100644
--- a/src/joystick/SDL_gamepad.c
+++ b/src/joystick/SDL_gamepad.c
@@ -370,33 +370,19 @@ static void RecenterGamepad(SDL_Gamepad *gamepad)
}
}
-/* SDL defines sensor orientation for phones relative to the natural
- orientation, and for gamepads relative to being held in front of you.
- When a phone is being used as a gamepad, its orientation changes,
- so adjust sensor axes to match.
+/* SDL defines sensor orientation relative to the device natural
+ orientation, so when it's changed orientation to be used as a
+ gamepad, change the sensor orientation to match.
*/
-static void AdjustSensorOrientation(float *src, float *dst)
-{
- if (SDL_GetDisplayNaturalOrientation(SDL_GetPrimaryDisplay()) == SDL_ORIENTATION_LANDSCAPE) {
- /* When a device in landscape orientation is laid flat, the axes change
- orientation as follows:
- -X to +X becomes -X to +X
- -Y to +Y becomes +Z to -Z
- -Z to +Z becomes -Y to +Y
- */
- dst[0] = src[0];
- dst[1] = src[2];
- dst[2] = -src[1];
- } else {
- /* When a device in portrait orientation is rotated left and laid flat,
- the axes change orientation as follows:
- -X to +X becomes +Z to -Z
- -Y to +Y becomes +X to -X
- -Z to +Z becomes -Y to +Y
- */
- dst[0] = -src[1];
- dst[1] = src[2];
- dst[2] = -src[0];
+static void AdjustSensorOrientation(SDL_Joystick *joystick, float *src, float *dst)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < 3; ++i) {
+ dst[i] = 0.0f;
+ for (j = 0; j < 3; ++j) {
+ dst[i] += joystick->sensor_transform[i][j] * src[j];
+ }
}
}
@@ -480,12 +466,12 @@ static int SDLCALL SDL_GamepadEventWatcher(void *userdata, SDL_Event *event)
for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
if (gamepad->joystick->accel && gamepad->joystick->accel_sensor == event->sensor.which) {
float data[3];
- AdjustSensorOrientation(event->sensor.data, data);
+ AdjustSensorOrientation(gamepad->joystick, event->sensor.data, data);
SDL_SendJoystickSensor(event->common.timestamp, gamepad->joystick, SDL_SENSOR_ACCEL, event->sensor.sensor_timestamp, data, SDL_arraysize(data));
}
if (gamepad->joystick->gyro && gamepad->joystick->gyro_sensor == event->sensor.which) {
float data[3];
- AdjustSensorOrientation(event->sensor.data, data);
+ AdjustSensorOrientation(gamepad->joystick, event->sensor.data, data);
SDL_SendJoystickSensor(event->common.timestamp, gamepad->joystick, SDL_SENSOR_GYRO, event->sensor.sensor_timestamp, data, SDL_arraysize(data));
}
}
diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c
index 498d31c0395f..993871b11a4c 100644
--- a/src/joystick/SDL_joystick.c
+++ b/src/joystick/SDL_joystick.c
@@ -562,7 +562,7 @@ static SDL_bool IsROGAlly(SDL_Joystick *joystick)
return SDL_FALSE;
}
-static SDL_bool ShouldAttemptSensorFusion(SDL_Joystick *joystick)
+static SDL_bool ShouldAttemptSensorFusion(SDL_Joystick *joystick, SDL_bool *invert_sensors)
{
static Uint32 wraparound_gamepads[] = {
MAKE_VIDPID(0x1532, 0x0709), /* Razer Junglecat (L) */
@@ -578,6 +578,8 @@ static SDL_bool ShouldAttemptSensorFusion(SDL_Joystick *joystick)
int i;
int hint;
+ *invert_sensors = SDL_FALSE;
+
/* The SDL controller sensor API is only available for gamepads (at the moment) */
if (!joystick->is_gamepad) {
return SDL_FALSE;
@@ -607,17 +609,23 @@ static SDL_bool ShouldAttemptSensorFusion(SDL_Joystick *joystick)
}
/* See if this is another known wraparound gamepad */
- if (IsBackboneOne(joystick) || IsROGAlly(joystick)) {
+ if (IsBackboneOne(joystick)) {
+ return SDL_TRUE;
+ }
+ if (IsROGAlly(joystick)) {
+ /* I'm not sure if this is a Windows thing, or a quirk for ROG Ally,
+ * but we need to invert the sensor data on all axes.
+ */
+ *invert_sensors = SDL_TRUE;
return SDL_TRUE;
}
-
return SDL_FALSE;
}
-static void AttemptSensorFusion(SDL_Joystick *joystick)
+static void AttemptSensorFusion(SDL_Joystick *joystick, SDL_bool invert_sensors)
{
SDL_SensorID *sensors;
- int i;
+ unsigned int i, j;
if (SDL_InitSubSystem(SDL_INIT_SENSOR) < 0) {
return;
@@ -646,6 +654,41 @@ static void AttemptSensorFusion(SDL_Joystick *joystick)
SDL_free(sensors);
}
SDL_QuitSubSystem(SDL_INIT_SENSOR);
+
+ /* SDL defines sensor orientation for phones relative to the natural
+ orientation, and for gamepads relative to being held in front of you.
+ When a phone is being used as a gamepad, its orientation changes,
+ so adjust sensor axes to match.
+ */
+ if (SDL_GetDisplayNaturalOrientation(SDL_GetPrimaryDisplay()) == SDL_ORIENTATION_LANDSCAPE) {
+ /* When a device in landscape orientation is laid flat, the axes change
+ orientation as follows:
+ -X to +X becomes -X to +X
+ -Y to +Y becomes +Z to -Z
+ -Z to +Z becomes -Y to +Y
+ */
+ joystick->sensor_transform[0][0] = 1.0f;
+ joystick->sensor_transform[1][2] = 1.0f;
+ joystick->sensor_transform[2][1] = -1.0f;
+ } else {
+ /* When a device in portrait orientation is rotated left and laid flat,
+ the axes change orientation as follows:
+ -X to +X becomes +Z to -Z
+ -Y to +Y becomes +X to -X
+ -Z to +Z becomes -Y to +Y
+ */
+ joystick->sensor_transform[0][1] = -1.0f;
+ joystick->sensor_transform[1][2] = 1.0f;
+ joystick->sensor_transform[2][0] = -1.0f;
+ }
+
+ if (invert_sensors) {
+ for (i = 0; i < SDL_arraysize(joystick->sensor_transform); ++i) {
+ for (j = 0; j < SDL_arraysize(joystick->sensor_transform[i]); ++j) {
+ joystick->sensor_transform[i][j] *= -1.0f;
+ }
+ }
+ }
}
static void CleanupSensorFusion(SDL_Joystick *joystick)
@@ -690,6 +733,7 @@ SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id)
const char *joystickname = NULL;
const char *joystickpath = NULL;
SDL_JoystickPowerLevel initial_power_level;
+ SDL_bool invert_sensors = SDL_FALSE;
SDL_LockJoysticks();
@@ -776,8 +820,8 @@ SDL_Joystick *SDL_OpenJoystick(SDL_JoystickID instance_id)
joystick->is_gamepad = SDL_IsGamepad(instance_id);
/* Use system gyro and accelerometer if the gamepad doesn't have built-in sensors */
- if (ShouldAttemptSensorFusion(joystick)) {
- AttemptSensorFusion(joystick);
+ if (ShouldAttemptSensorFusion(joystick, &invert_sensors)) {
+ AttemptSensorFusion(joystick, invert_sensors);
}
/* Add joystick to list */
diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index 4b2225ccdddf..99784dcf218c 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -117,6 +117,7 @@ struct SDL_Joystick
SDL_Sensor *accel _guarded;
SDL_SensorID gyro_sensor _guarded;
SDL_Sensor *gyro _guarded;
+ float sensor_transform[3][3] _guarded;
struct SDL_JoystickDriver *driver _guarded;