From 20ea35138f2f11dc8a0f35e949e372ae8b916534 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 19 Jun 2023 16:17:34 -0700
Subject: [PATCH] Use a separate sensor watching function for gamepad events to
avoid reliance on system sensor events and prevent a potential deadlock
---
src/joystick/SDL_gamepad.c | 79 +++++++++++++++++++++---------------
src/joystick/SDL_gamepad_c.h | 3 ++
src/sensor/SDL_sensor.c | 4 ++
3 files changed, 53 insertions(+), 33 deletions(-)
diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c
index 8443ba03abcf..2df24a6336af 100644
--- a/src/joystick/SDL_gamepad.c
+++ b/src/joystick/SDL_gamepad.c
@@ -318,22 +318,6 @@ static void RecenterGamepad(SDL_Gamepad *gamepad)
}
}
-/* 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(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];
- }
- }
-}
-
/*
* Event filter to fire gamepad events from joystick ones
*/
@@ -408,23 +392,6 @@ static int SDLCALL SDL_GamepadEventWatcher(void *userdata, SDL_Event *event)
SDL_PushEvent(&deviceevent);
}
} break;
- case SDL_EVENT_SENSOR_UPDATE:
- {
- SDL_LockJoysticks();
- for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
- if (gamepad->joystick->accel && gamepad->joystick->accel_sensor == event->sensor.which) {
- float data[3];
- 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(gamepad->joystick, event->sensor.data, data);
- SDL_SendJoystickSensor(event->common.timestamp, gamepad->joystick, SDL_SENSOR_GYRO, event->sensor.sensor_timestamp, data, SDL_arraysize(data));
- }
- }
- SDL_UnlockJoysticks();
- } break;
default:
break;
}
@@ -432,6 +399,52 @@ static int SDLCALL SDL_GamepadEventWatcher(void *userdata, SDL_Event *event)
return 1;
}
+/* 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(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];
+ }
+ }
+}
+
+/*
+ * Event filter to fire gamepad sensor events from system sensor events
+ *
+ * We don't use SDL_GamepadEventWatcher() for this because we want to
+ * deliver gamepad sensor events when system sensor events are disabled,
+ * and we also need to avoid a potential deadlock where joystick event
+ * delivery locks the joysticks and then the event queue, but sensor
+ * event delivery would lock the event queue and then from within the
+ * event watcher function lock the joysticks.
+ */
+void SDL_GamepadSensorWatcher(Uint64 timestamp, SDL_SensorID sensor, Uint64 sensor_timestamp, float *data, int num_values)
+{
+ SDL_Gamepad *gamepad;
+
+ SDL_LockJoysticks();
+ for (gamepad = SDL_gamepads; gamepad; gamepad = gamepad->next) {
+ if (gamepad->joystick->accel && gamepad->joystick->accel_sensor == sensor) {
+ float gamepad_data[3];
+ AdjustSensorOrientation(gamepad->joystick, data, gamepad_data);
+ SDL_SendJoystickSensor(timestamp, gamepad->joystick, SDL_SENSOR_ACCEL, sensor_timestamp, gamepad_data, SDL_arraysize(gamepad_data));
+ }
+ if (gamepad->joystick->gyro && gamepad->joystick->gyro_sensor == sensor) {
+ float gamepad_data[3];
+ AdjustSensorOrientation(gamepad->joystick, data, gamepad_data);
+ SDL_SendJoystickSensor(timestamp, gamepad->joystick, SDL_SENSOR_GYRO, sensor_timestamp, gamepad_data, SDL_arraysize(gamepad_data));
+ }
+ }
+ SDL_UnlockJoysticks();
+}
+
#ifdef __ANDROID__
/*
* Helper function to guess at a mapping based on the elements reported for this gamepad
diff --git a/src/joystick/SDL_gamepad_c.h b/src/joystick/SDL_gamepad_c.h
index 16db7f69ee8d..d7472db5088d 100644
--- a/src/joystick/SDL_gamepad_c.h
+++ b/src/joystick/SDL_gamepad_c.h
@@ -42,4 +42,7 @@ extern SDL_bool SDL_ShouldIgnoreGamepad(const char *name, SDL_JoystickGUID guid)
/* Handle delayed guide button on a gamepad */
extern void SDL_GamepadHandleDelayedGuideButton(SDL_Joystick *joystick);
+/* Handle system sensor data */
+extern void SDL_GamepadSensorWatcher(Uint64 timestamp, SDL_SensorID sensor, Uint64 sensor_timestamp, float *data, int num_values);
+
#endif /* SDL_gamepad_c_h_ */
diff --git a/src/sensor/SDL_sensor.c b/src/sensor/SDL_sensor.c
index d3eb6beee9f7..c9a5ab74bfe7 100644
--- a/src/sensor/SDL_sensor.c
+++ b/src/sensor/SDL_sensor.c
@@ -27,6 +27,7 @@
#ifndef SDL_EVENTS_DISABLED
#include "../events/SDL_events_c.h"
#endif
+#include "../joystick/SDL_gamepad_c.h"
static SDL_SensorDriver *SDL_sensor_drivers[] = {
#ifdef SDL_SENSOR_ANDROID
@@ -501,6 +502,9 @@ int SDL_SendSensorUpdate(Uint64 timestamp, SDL_Sensor *sensor, Uint64 sensor_tim
posted = SDL_PushEvent(&event) == 1;
}
#endif /* !SDL_EVENTS_DISABLED */
+
+ SDL_GamepadSensorWatcher(timestamp, sensor->instance_id, sensor_timestamp, data, num_values);
+
return posted;
}