Преглед на файлове

For gamepads that don't have their own sensors, try to use the system sensors.

This allows using the gyro and accelerometer in handheld devices in conjunction with built-in or wraparound controllers.
Sam Lantinga преди 2 години
родител
ревизия
42e4639a5e
променени са 8 файла, в които са добавени 271 реда и са изтрити 10 реда
  1. 12 0
      include/SDL3/SDL_hints.h
  2. 17 0
      src/SDL_hints.c
  3. 1 0
      src/SDL_hints_c.h
  4. 49 8
      src/joystick/SDL_gamepad.c
  5. 179 2
      src/joystick/SDL_joystick.c
  6. 5 0
      src/joystick/SDL_sysjoystick.h
  7. 5 0
      src/sensor/SDL_sensor.c
  8. 3 0
      src/sensor/SDL_sensor_c.h

+ 12 - 0
include/SDL3/SDL_hints.h

@@ -616,6 +616,18 @@ extern "C" {
  */
 #define SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS "SDL_GAMECONTROLLER_USE_BUTTON_LABELS"
 
+/**
+ *  \brief  Controls whether the device's built-in accelerometer and gyro should be used as sensors for gamepads.
+ *
+ *  The variable can be set to the following values:
+ *    "auto"    - Sensor fusion is enabled for known wraparound controllers like the Razer Kishi and Backbone One
+ *    "0"       - Sensor fusion is disabled
+ *    "1"       - Sensor fusion is enabled for all controllers that lack sensors
+ *
+ *  The default value is "auto". This hint is checked when a gamepad is opened.
+ */
+#define SDL_HINT_GAMECONTROLLER_SENSOR_FUSION "SDL_GAMECONTROLLER_SENSOR_FUSION"
+
 /**
  *  \brief  A variable controlling whether grabbing input grabs the keyboard
  *

+ 17 - 0
src/SDL_hints.c

@@ -171,6 +171,23 @@ const char *SDL_GetHint(const char *name)
     return env;
 }
 
+int SDL_GetStringInteger(const char *value, int default_value)
+{
+    if (value == NULL || !*value) {
+        return default_value;
+    }
+    if (*value == '0' || SDL_strcasecmp(value, "false") == 0) {
+        return 0;
+    }
+    if (*value == '1' || SDL_strcasecmp(value, "true") == 0) {
+        return 1;
+    }
+    if (*value == '-' || SDL_isdigit(*value)) {
+        return SDL_atoi(value);
+    }
+    return default_value;
+}
+
 SDL_bool SDL_GetStringBoolean(const char *value, SDL_bool default_value)
 {
     if (value == NULL || !*value) {

+ 1 - 0
src/SDL_hints_c.h

@@ -26,5 +26,6 @@
 #define SDL_hints_c_h_
 
 extern SDL_bool SDL_GetStringBoolean(const char *value, SDL_bool default_value);
+extern int SDL_GetStringInteger(const char *value, int default_value);
 
 #endif /* SDL_hints_c_h_ */

+ 49 - 8
src/joystick/SDL_gamepad.c

@@ -444,6 +444,19 @@ 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) {
+                SDL_SendJoystickSensor(event->common.timestamp, gamepad->joystick, SDL_SENSOR_ACCEL, event->sensor.sensor_timestamp, event->sensor.data, SDL_arraysize(event->sensor.data));
+            }
+            if (gamepad->joystick->gyro && gamepad->joystick->gyro_sensor == event->sensor.which) {
+                SDL_SendJoystickSensor(event->common.timestamp, gamepad->joystick, SDL_SENSOR_GYRO, event->sensor.sensor_timestamp, event->sensor.data, SDL_arraysize(event->sensor.data));
+            }
+        }
+        SDL_UnlockJoysticks();
+    } break;
     default:
         break;
     }
@@ -2523,22 +2536,50 @@ int SDL_SetGamepadSensorEnabled(SDL_Gamepad *gamepad, SDL_SensorType type, SDL_b
                         return 0;
                     }
 
-                    if (enabled) {
-                        if (joystick->nsensors_enabled == 0) {
-                            if (joystick->driver->SetSensorsEnabled(joystick, SDL_TRUE) < 0) {
+                    if (type == SDL_SENSOR_ACCEL && joystick->accel_sensor) {
+                        if (enabled) {
+                            joystick->accel = SDL_OpenSensor(joystick->accel_sensor);
+                            if (!joystick->accel) {
                                 SDL_UnlockJoysticks();
                                 return -1;
                             }
+                        } else {
+                            if (joystick->accel) {
+                                SDL_CloseSensor(joystick->accel);
+                                joystick->accel = NULL;
+                            }
                         }
-                        ++joystick->nsensors_enabled;
-                    } else {
-                        if (joystick->nsensors_enabled == 1) {
-                            if (joystick->driver->SetSensorsEnabled(joystick, SDL_FALSE) < 0) {
+                    } else if (type == SDL_SENSOR_GYRO && joystick->gyro_sensor) {
+                        if (enabled) {
+                            joystick->gyro = SDL_OpenSensor(joystick->gyro_sensor);
+                            if (!joystick->gyro) {
                                 SDL_UnlockJoysticks();
                                 return -1;
                             }
+                        } else {
+                            if (joystick->gyro) {
+                                SDL_CloseSensor(joystick->gyro);
+                                joystick->gyro = NULL;
+                            }
+                        }
+                    } else {
+                        if (enabled) {
+                            if (joystick->nsensors_enabled == 0) {
+                                if (joystick->driver->SetSensorsEnabled(joystick, SDL_TRUE) < 0) {
+                                    SDL_UnlockJoysticks();
+                                    return -1;
+                                }
+                            }
+                            ++joystick->nsensors_enabled;
+                        } else {
+                            if (joystick->nsensors_enabled == 1) {
+                                if (joystick->driver->SetSensorsEnabled(joystick, SDL_FALSE) < 0) {
+                                    SDL_UnlockJoysticks();
+                                    return -1;
+                                }
+                            }
+                            --joystick->nsensors_enabled;
                         }
-                        --joystick->nsensors_enabled;
                     }
 
                     sensor->enabled = enabled;

+ 179 - 2
src/joystick/SDL_joystick.c

@@ -31,6 +31,7 @@
 #include "../events/SDL_events_c.h"
 #endif
 #include "../video/SDL_sysvideo.h"
+#include "../sensor/SDL_sensor_c.h"
 #include "hidapi/SDL_hidapijoystick_c.h"
 
 /* This is included in only one place because it has a large static list of controllers */
@@ -510,6 +511,166 @@ static SDL_bool SDL_JoystickAxesCenteredAtZero(SDL_Joystick *joystick)
 #endif /* __WINRT__ */
 }
 
+static SDL_bool IsBackboneOne(SDL_Joystick *joystick)
+{
+    if (joystick->name && SDL_strstr(joystick->name, "Backbone One")) {
+        return SDL_TRUE;
+    }
+    return SDL_FALSE;
+}
+
+static SDL_bool IsROGAlly(SDL_Joystick *joystick)
+{
+    Uint16 vendor, product;
+    SDL_JoystickGUID guid = SDL_GetJoystickGUID(joystick);
+
+    /* The ROG Ally controller spoofs an Xbox 360 controller */
+    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
+    if (vendor == USB_VENDOR_MICROSOFT && product == USB_PRODUCT_XBOX360_WIRED_CONTROLLER) {
+        /* Check to see if this system has the expected sensors */
+        SDL_bool has_ally_accel = SDL_FALSE;
+        SDL_bool has_ally_gyro = SDL_FALSE;
+
+        if (SDL_InitSubSystem(SDL_INIT_SENSOR) == 0) {
+            SDL_SensorID *sensors = SDL_GetSensors(NULL);
+            if (sensors) {
+                int i;
+                for (i = 0; sensors[i]; ++i) {
+                    SDL_SensorID sensor = sensors[i];
+
+                    if (!has_ally_accel && SDL_GetSensorInstanceType(sensor) == SDL_SENSOR_ACCEL) {
+                        const char *sensor_name = SDL_GetSensorInstanceName(sensor);
+                        if (sensor_name && SDL_strcmp(sensor_name, "Sensor BMI320 Acc") == 0) {
+                            has_ally_accel = SDL_TRUE;
+                        }
+                    }
+                    if (!has_ally_gyro && SDL_GetSensorInstanceType(sensor) == SDL_SENSOR_GYRO) {
+                        const char *sensor_name = SDL_GetSensorInstanceName(sensor);
+                        if (sensor_name && SDL_strcmp(sensor_name, "Sensor BMI320 Gyr") == 0) {
+                            has_ally_gyro = SDL_TRUE;
+                        }
+                    }
+                }
+                SDL_free(sensors);
+            }
+            SDL_QuitSubSystem(SDL_INIT_SENSOR);
+        }
+        if (has_ally_accel && has_ally_gyro) {
+            return SDL_TRUE;
+        }
+    }
+    return SDL_FALSE;
+}
+
+static SDL_bool ShouldAttemptSensorFusion(SDL_Joystick *joystick)
+{
+    static Uint32 wraparound_gamepads[] = {
+        MAKE_VIDPID(0x1949, 0x0402),    /* Ipega PG-9083S */
+        MAKE_VIDPID(0x27f8, 0x0bbc),    /* Gamevice */
+        MAKE_VIDPID(0x27f8, 0x0bbf),    /* Razer Kishi */
+    };
+    SDL_JoystickGUID guid;
+    Uint16 vendor, product;
+    Uint32 vidpid;
+    int i;
+    int hint;
+
+    /* The SDL controller sensor API is only available for gamepads (at the moment) */
+    if (!joystick->is_gamepad) {
+        return SDL_FALSE;
+    }
+
+    /* If the controller already has sensors, use those */
+    if (joystick->nsensors > 0) {
+        return SDL_FALSE;
+    }
+
+    hint = SDL_GetStringInteger(SDL_GetHint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION), -1);
+    if (hint > 0) {
+        return SDL_TRUE;
+    }
+    if (hint == 0) {
+        return SDL_FALSE;
+    }
+
+    /* See if the controller is in our list of wraparound gamepads */
+    guid = SDL_GetJoystickGUID(joystick);
+    SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
+    vidpid = MAKE_VIDPID(vendor, product);
+    for (i = 0; i < SDL_arraysize(wraparound_gamepads); ++i) {
+        if (vidpid == wraparound_gamepads[i]) {
+            return SDL_TRUE;
+        }
+    }
+
+    /* See if this is another known wraparound gamepad */
+    if (IsBackboneOne(joystick) || IsROGAlly(joystick)) {
+        return SDL_TRUE;
+    }
+
+    return SDL_FALSE;
+}
+
+static void AttemptSensorFusion(SDL_Joystick *joystick)
+{
+    SDL_SensorID *sensors;
+    int i;
+
+    if (SDL_InitSubSystem(SDL_INIT_SENSOR) < 0) {
+        return;
+    }
+
+    sensors = SDL_GetSensors(NULL);
+    if (sensors) {
+        for (i = 0; sensors[i]; ++i) {
+            SDL_SensorID sensor = sensors[i];
+
+            if (!joystick->accel_sensor && SDL_GetSensorInstanceType(sensor) == SDL_SENSOR_ACCEL) {
+                /* Increment the sensor subsystem reference count */
+                SDL_InitSubSystem(SDL_INIT_SENSOR);
+
+                joystick->accel_sensor = sensor;
+                SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 0.0f);
+            }
+            if (!joystick->gyro_sensor && SDL_GetSensorInstanceType(sensor) == SDL_SENSOR_GYRO) {
+                /* Increment the sensor subsystem reference count */
+                SDL_InitSubSystem(SDL_INIT_SENSOR);
+
+                joystick->gyro_sensor = sensor;
+                SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 0.0f);
+            }
+        }
+        SDL_free(sensors);
+    }
+    SDL_QuitSubSystem(SDL_INIT_SENSOR);
+}
+
+static void CleanupSensorFusion(SDL_Joystick *joystick)
+{
+    if (joystick->accel_sensor || joystick->gyro_sensor) {
+        if (joystick->accel_sensor) {
+            if (joystick->accel) {
+                SDL_CloseSensor(joystick->accel);
+                joystick->accel = NULL;
+            }
+            joystick->accel_sensor = 0;
+
+            /* Decrement the sensor subsystem reference count */
+            SDL_QuitSubSystem(SDL_INIT_SENSOR);
+        }
+        if (joystick->gyro_sensor) {
+            if (joystick->gyro) {
+                SDL_CloseSensor(joystick->gyro);
+                joystick->gyro = NULL;
+            }
+            joystick->gyro_sensor = 0;
+
+            /* Decrement the sensor subsystem reference count */
+            SDL_QuitSubSystem(SDL_INIT_SENSOR);
+        }
+    }
+}
+
 /*
  * Open a joystick for use - the index passed as an argument refers to
  * the N'th joystick on the system.  This index is the value which will
@@ -611,6 +772,11 @@ 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);
+    }
+
     /* Add joystick to list */
     ++joystick->ref_count;
     /* Link the joystick in the list */
@@ -1253,6 +1419,8 @@ void SDL_CloseJoystick(SDL_Joystick *joystick)
             SDL_RumbleJoystickTriggers(joystick, 0, 0, 0);
         }
 
+        CleanupSensorFusion(joystick);
+
         joystick->driver->Close(joystick);
         joystick->hwdata = NULL;
         joystick->magic = NULL;
@@ -1273,11 +1441,10 @@ void SDL_CloseJoystick(SDL_Joystick *joystick)
             joysticklist = joysticklist->next;
         }
 
+        /* Free the data associated with this joystick */
         SDL_free(joystick->name);
         SDL_free(joystick->path);
         SDL_free(joystick->serial);
-
-        /* Free the data associated with this joystick */
         SDL_free(joystick->axes);
         SDL_free(joystick->hats);
         SDL_free(joystick->buttons);
@@ -1682,6 +1849,16 @@ void SDL_UpdateJoysticks(void)
 
     for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
         if (joystick->attached) {
+            if (joystick->accel || joystick->gyro) {
+                SDL_LockSensors();
+                if (joystick->gyro) {
+                    SDL_UpdateSensor(joystick->gyro);
+                }
+                if (joystick->accel) {
+                    SDL_UpdateSensor(joystick->accel);
+                }
+                SDL_UnlockSensors();
+            }
             joystick->driver->Update(joystick);
 
             if (joystick->delayed_guide_button) {

+ 5 - 0
src/joystick/SDL_sysjoystick.h

@@ -113,6 +113,11 @@ struct SDL_Joystick
     SDL_bool delayed_guide_button _guarded;      /* SDL_TRUE if this device has the guide button event delayed */
     SDL_JoystickPowerLevel epowerlevel _guarded; /* power level of this joystick, SDL_JOYSTICK_POWER_UNKNOWN if not supported */
 
+    SDL_SensorID accel_sensor _guarded;
+    SDL_Sensor *accel _guarded;
+    SDL_SensorID gyro_sensor _guarded;
+    SDL_Sensor *gyro _guarded;
+
     struct SDL_JoystickDriver *driver _guarded;
 
     struct joystick_hwdata *hwdata _guarded; /* Driver dependent information */

+ 5 - 0
src/sensor/SDL_sensor.c

@@ -504,6 +504,11 @@ int SDL_SendSensorUpdate(Uint64 timestamp, SDL_Sensor *sensor, Uint64 sensor_tim
     return posted;
 }
 
+void SDL_UpdateSensor(SDL_Sensor *sensor)
+{
+    sensor->driver->Update(sensor);
+}
+
 void SDL_UpdateSensors(void)
 {
     int i;

+ 3 - 0
src/sensor/SDL_sensor_c.h

@@ -40,6 +40,9 @@ extern void SDL_UnlockSensors(void);
 /* Function to return whether there are any sensors opened by the application */
 extern SDL_bool SDL_SensorsOpened(void);
 
+/* Update an individual sensor, used by gamepad sensor fusion */
+extern void SDL_UpdateSensor(SDL_Sensor *sensor);
+
 /* Internal event queueing functions */
 extern int SDL_SendSensorUpdate(Uint64 timestamp, SDL_Sensor *sensor, Uint64 sensor_timestamp, float *data, int num_values);