Browse Source

Use a separate sensor watching function for gamepad events to avoid reliance on system sensor events and prevent a potential deadlock

Sam Lantinga 1 year ago
parent
commit
20ea35138f
3 changed files with 53 additions and 33 deletions
  1. 46 33
      src/joystick/SDL_gamepad.c
  2. 3 0
      src/joystick/SDL_gamepad_c.h
  3. 4 0
      src/sensor/SDL_sensor.c

+ 46 - 33
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

+ 3 - 0
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_ */

+ 4 - 0
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;
 }