From fcb21aa8834f5f60bf24944101246c5b4324d35b Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 17 Nov 2020 10:30:20 -0800 Subject: [PATCH] Added API for sensors on game controllers Added support for the PS4 controller gyro and accelerometer on iOS and HIDAPI drivers Also fixed an issue with the accelerometer on iOS having inverted axes --- include/SDL_events.h | 17 +- include/SDL_gamecontroller.h | 47 ++++ include/SDL_sensor.h | 19 +- src/dynapi/SDL_dynapi_overrides.h | 4 + src/dynapi/SDL_dynapi_procs.h | 4 + src/joystick/SDL_gamecontroller.c | 105 +++++++ src/joystick/SDL_joystick.c | 56 +++- src/joystick/SDL_joystick_c.h | 3 + src/joystick/SDL_sysjoystick.h | 15 + src/joystick/android/SDL_sysjoystick.c | 7 + src/joystick/bsd/SDL_sysjoystick.c | 25 +- src/joystick/darwin/SDL_sysjoystick.c | 7 + src/joystick/dummy/SDL_sysjoystick.c | 21 +- src/joystick/emscripten/SDL_sysjoystick.c | 21 +- src/joystick/haiku/SDL_haikujoystick.cc | 22 +- src/joystick/hidapi/SDL_hidapi_gamecube.c | 7 + src/joystick/hidapi/SDL_hidapi_ps4.c | 258 ++++++++++++++++-- src/joystick/hidapi/SDL_hidapi_ps5.c | 15 +- src/joystick/hidapi/SDL_hidapi_steam.c | 8 + src/joystick/hidapi/SDL_hidapi_switch.c | 7 + src/joystick/hidapi/SDL_hidapi_xbox360.c | 7 + src/joystick/hidapi/SDL_hidapi_xbox360w.c | 7 + src/joystick/hidapi/SDL_hidapi_xboxone.c | 7 + src/joystick/hidapi/SDL_hidapijoystick.c | 18 ++ src/joystick/hidapi/SDL_hidapijoystick_c.h | 1 + src/joystick/iphoneos/SDL_sysjoystick.m | 78 +++++- src/joystick/linux/SDL_sysjoystick.c | 39 +-- src/joystick/virtual/SDL_virtualjoystick.c | 7 + src/joystick/windows/SDL_rawinputjoystick.c | 7 + .../windows/SDL_windows_gaming_input.c | 7 + src/joystick/windows/SDL_windowsjoystick.c | 7 + src/sensor/coremotion/SDL_coremotionsensor.m | 6 +- test/testgamecontroller.c | 69 +++-- 33 files changed, 810 insertions(+), 118 deletions(-) diff --git a/include/SDL_events.h b/include/SDL_events.h index a9da3fa2e..ae560c085 100644 --- a/include/SDL_events.h +++ b/include/SDL_events.h @@ -128,6 +128,7 @@ typedef enum SDL_CONTROLLERTOUCHPADDOWN, /**< Game controller touchpad was touched */ SDL_CONTROLLERTOUCHPADMOTION, /**< Game controller touchpad finger was moved */ SDL_CONTROLLERTOUCHPADUP, /**< Game controller touchpad finger was lifted */ + SDL_CONTROLLERSENSORUPDATE, /**< Game controller sensor was updated */ /* Touch events */ SDL_FINGERDOWN = 0x700, @@ -426,13 +427,24 @@ typedef struct SDL_ControllerTouchpadEvent Uint32 type; /**< ::SDL_CONTROLLERTOUCHPADDOWN or ::SDL_CONTROLLERTOUCHPADMOTION or ::SDL_CONTROLLERTOUCHPADUP */ Uint32 timestamp; /**< In milliseconds, populated using SDL_GetTicks() */ SDL_JoystickID which; /**< The joystick instance id */ - int touchpad; /**< The index of the touchpad */ - int finger; /**< The index of the finger on the touchpad */ + Sint32 touchpad; /**< The index of the touchpad */ + Sint32 finger; /**< The index of the finger on the touchpad */ float x; /**< Normalized in the range 0...1 with 0 being on the left */ float y; /**< Normalized in the range 0...1 with 0 being at the top */ float pressure; /**< Normalized in the range 0...1 */ } SDL_ControllerTouchpadEvent; +/** + * \brief Game controller sensor event structure (event.csensor.*) + */ +typedef struct SDL_ControllerSensorEvent +{ + Uint32 type; /**< ::SDL_CONTROLLERSENSORUPDATE */ + Uint32 timestamp; /**< In milliseconds, populated using SDL_GetTicks() */ + SDL_JoystickID which; /**< The joystick instance id */ + Sint32 sensor; /**< The type of the sensor, one of the values of ::SDL_SensorType */ + float data[3]; /**< Up to 3 values from the sensor, as defined in SDL_sensor.h */ +} SDL_ControllerSensorEvent; /** * \brief Audio device event structure (event.adevice.*) @@ -597,6 +609,7 @@ typedef union SDL_Event SDL_ControllerButtonEvent cbutton; /**< Game Controller button event data */ SDL_ControllerDeviceEvent cdevice; /**< Game Controller device event data */ SDL_ControllerTouchpadEvent ctouchpad; /**< Game Controller touchpad event data */ + SDL_ControllerSensorEvent csensor; /**< Game Controller sensor event data */ SDL_AudioDeviceEvent adevice; /**< Audio device event data */ SDL_SensorEvent sensor; /**< Sensor event data */ SDL_QuitEvent quit; /**< Quit request event data */ diff --git a/include/SDL_gamecontroller.h b/include/SDL_gamecontroller.h index ee7ef6475..e42433c95 100644 --- a/include/SDL_gamecontroller.h +++ b/include/SDL_gamecontroller.h @@ -31,6 +31,7 @@ #include "SDL_stdinc.h" #include "SDL_error.h" #include "SDL_rwops.h" +#include "SDL_sensor.h" #include "SDL_joystick.h" #include "begin_code.h" @@ -430,6 +431,52 @@ extern DECLSPEC int SDLCALL SDL_GameControllerGetNumTouchpadFingers(SDL_GameCont */ extern DECLSPEC int SDLCALL SDL_GameControllerGetTouchpadFinger(SDL_GameController *gamecontroller, int touchpad, int finger, Uint8 *state, float *x, float *y, float *pressure); +/** + * Return whether a game controller has a particular sensor. + * + * \param gamecontroller The controller to query + * \param type The type of sensor to query + * + * \return SDL_TRUE if the sensor exists, SDL_FALSE otherwise. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_GameControllerHasSensor(SDL_GameController *gamecontroller, SDL_SensorType type); + +/** + * Set whether data reporting for a game controller sensor is enabled + * + * \param gamecontroller The controller to update + * \param type The type of sensor to enable/disable + * \param enabled Whether data reporting should be enabled + * + * \return 0 or -1 if an error occurred. + */ +extern DECLSPEC int SDLCALL SDL_GameControllerSetSensorEnabled(SDL_GameController *gamecontroller, SDL_SensorType type, SDL_bool enabled); + +/** + * Query whether sensor data reporting is enabled for a game controller + * + * \param gamecontroller The controller to query + * \param type The type of sensor to query + * + * \return SDL_TRUE if the sensor is enabled, SDL_FALSE otherwise. + */ +extern DECLSPEC SDL_bool SDLCALL SDL_GameControllerIsSensorEnabled(SDL_GameController *gamecontroller, SDL_SensorType type); + +/** + * Get the current state of a game controller sensor. + * + * The number of values and interpretation of the data is sensor dependent. + * See SDL_sensor.h for the details for each type of sensor. + * + * \param gamecontroller The controller to query + * \param type The type of sensor to query + * \param data A pointer filled with the current sensor state + * \param num_values The number of values to write to data + * + * \return 0 or -1 if an error occurred. + */ +extern DECLSPEC int SDLCALL SDL_GameControllerGetSensorData(SDL_GameController *gamecontroller, SDL_SensorType type, float *data, int num_values); + /** * Start a rumble effect * Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling. diff --git a/include/SDL_sensor.h b/include/SDL_sensor.h index 83abf1ee4..e6236341e 100644 --- a/include/SDL_sensor.h +++ b/include/SDL_sensor.h @@ -78,14 +78,16 @@ typedef enum * Accelerometer sensor * * The accelerometer returns the current acceleration in SI meters per - * second squared. This includes gravity, so a device at rest will have - * an acceleration of SDL_STANDARD_GRAVITY straight down. + * second squared. This measurement includes the force of gravity, so + * a device at rest will have an value of SDL_STANDARD_GRAVITY away + * from the center of the earth. * * values[0]: Acceleration on the x axis * values[1]: Acceleration on the y axis * values[2]: Acceleration on the z axis * - * For phones held in portrait mode, the axes are defined as follows: + * For phones held in portrait mode and game controllers held in front of you, + * the axes are defined as follows: * -X ... +X : left ... right * -Y ... +Y : bottom ... top * -Z ... +Z : farther ... closer @@ -105,16 +107,17 @@ typedef enum * see positive rotation on that axis when it appeared to be rotating * counter-clockwise. * - * values[0]: Angular speed around the x axis - * values[1]: Angular speed around the y axis - * values[2]: Angular speed around the z axis + * values[0]: Angular speed around the x axis (pitch) + * values[1]: Angular speed around the y axis (yaw) + * values[2]: Angular speed around the z axis (roll) * - * For phones held in portrait mode, the axes are defined as follows: + * For phones held in portrait mode and game controllers held in front of you, + * the axes are defined as follows: * -X ... +X : left ... right * -Y ... +Y : bottom ... top * -Z ... +Z : farther ... closer * - * The axis data is not changed when the phone is rotated. + * The axis data is not changed when the phone or controller is rotated. * * \sa SDL_GetDisplayOrientation() */ diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 278bbdff9..1a583cf53 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -782,3 +782,7 @@ #define SDL_crc32 SDL_crc32_REAL #define SDL_GameControllerGetSerial SDL_GameControllerGetSerial_REAL #define SDL_JoystickGetSerial SDL_JoystickGetSerial_REAL +#define SDL_GameControllerHasSensor SDL_GameControllerHasSensor_REAL +#define SDL_GameControllerSetSensorEnabled SDL_GameControllerSetSensorEnabled_REAL +#define SDL_GameControllerIsSensorEnabled SDL_GameControllerIsSensorEnabled_REAL +#define SDL_GameControllerGetSensorData SDL_GameControllerGetSensorData_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 8847a9ff4..0fd1939ad 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -843,3 +843,7 @@ SDL_DYNAPI_PROC(int,SDL_GameControllerGetTouchpadFinger,(SDL_GameController *a, SDL_DYNAPI_PROC(Uint32,SDL_crc32,(Uint32 a, const void *b, size_t c),(a,b,c),return) SDL_DYNAPI_PROC(const char*,SDL_GameControllerGetSerial,(SDL_GameController *a),(a),return) SDL_DYNAPI_PROC(const char*,SDL_JoystickGetSerial,(SDL_Joystick *a),(a),return) +SDL_DYNAPI_PROC(SDL_bool,SDL_GameControllerHasSensor,(SDL_GameController *a, SDL_SensorType b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_GameControllerSetSensorEnabled,(SDL_GameController *a, SDL_SensorType b, SDL_bool c),(a,b,c),return) +SDL_DYNAPI_PROC(SDL_bool,SDL_GameControllerIsSensorEnabled,(SDL_GameController *a, SDL_SensorType b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_GameControllerGetSensorData,(SDL_GameController *a, SDL_SensorType b, float *c, int d),(a,b,c,d),return) diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index 89a216467..a90d2b295 100644 --- a/src/joystick/SDL_gamecontroller.c +++ b/src/joystick/SDL_gamecontroller.c @@ -2059,6 +2059,111 @@ SDL_GameControllerGetTouchpadFinger(SDL_GameController *gamecontroller, int touc } } +/** + * Return whether a game controller has a particular sensor. + */ +SDL_bool +SDL_GameControllerHasSensor(SDL_GameController *gamecontroller, SDL_SensorType type) +{ + SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller); + int i; + + if (joystick) { + for (i = 0; i < joystick->nsensors; ++i) { + if (joystick->sensors[i].type == type) { + return SDL_TRUE; + } + } + } + return SDL_FALSE; +} + +/* + * Set whether data reporting for a game controller sensor is enabled + */ +int SDL_GameControllerSetSensorEnabled(SDL_GameController *gamecontroller, SDL_SensorType type, SDL_bool enabled) +{ + SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller); + int i; + + if (!joystick) { + return SDL_InvalidParamError("gamecontroller"); + } + + for (i = 0; i < joystick->nsensors; ++i) { + SDL_JoystickSensorInfo *sensor = &joystick->sensors[i]; + + if (sensor->type == type) { + if (sensor->enabled == enabled) { + return 0; + } + + if (enabled) { + if (joystick->nsensors_enabled == 0) { + if (joystick->driver->SetSensorsEnabled(joystick, SDL_TRUE) < 0) { + return -1; + } + } + ++joystick->nsensors_enabled; + } else { + if (joystick->nsensors_enabled == 1) { + if (joystick->driver->SetSensorsEnabled(joystick, SDL_FALSE) < 0) { + return -1; + } + } + --joystick->nsensors_enabled; + } + + sensor->enabled = enabled; + return 0; + } + } + return SDL_Unsupported(); +} + +/* + * Query whether sensor data reporting is enabled for a game controller + */ +SDL_bool SDL_GameControllerIsSensorEnabled(SDL_GameController *gamecontroller, SDL_SensorType type) +{ + SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller); + int i; + + if (joystick) { + for (i = 0; i < joystick->nsensors; ++i) { + if (joystick->sensors[i].type == type) { + return joystick->sensors[i].enabled; + } + } + } + return SDL_FALSE; +} + +/* + * Get the current state of a game controller sensor. + */ +int +SDL_GameControllerGetSensorData(SDL_GameController *gamecontroller, SDL_SensorType type, float *data, int num_values) +{ + SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller); + int i; + + if (!joystick) { + return SDL_InvalidParamError("gamecontroller"); + } + + for (i = 0; i < joystick->nsensors; ++i) { + SDL_JoystickSensorInfo *sensor = &joystick->sensors[i]; + + if (sensor->type == type) { + num_values = SDL_min(num_values, SDL_arraysize(sensor->data)); + SDL_memcpy(data, sensor->data, num_values*sizeof(*data)); + return 0; + } + } + return SDL_Unsupported(); +} + const char * SDL_GameControllerName(SDL_GameController *gamecontroller) { diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 8ee009aac..4c5623e19 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -1109,7 +1109,7 @@ SDL_PrivateJoystickShouldIgnoreEvent() void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers) { int ntouchpads = joystick->ntouchpads + 1; - SDL_JoystickTouchpadInfo *touchpads = (SDL_JoystickTouchpadInfo *)SDL_realloc(joystick->touchpads, sizeof(SDL_JoystickTouchpadInfo)); + SDL_JoystickTouchpadInfo *touchpads = (SDL_JoystickTouchpadInfo *)SDL_realloc(joystick->touchpads, (ntouchpads * sizeof(SDL_JoystickTouchpadInfo))); if (touchpads) { SDL_JoystickTouchpadInfo *touchpad = &touchpads[ntouchpads - 1]; SDL_JoystickTouchpadFingerInfo *fingers = (SDL_JoystickTouchpadFingerInfo *)SDL_calloc(nfingers, sizeof(SDL_JoystickTouchpadFingerInfo)); @@ -1128,6 +1128,21 @@ void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers) } } +void SDL_PrivateJoystickAddSensor(SDL_Joystick *joystick, SDL_SensorType type) +{ + int nsensors = joystick->nsensors + 1; + SDL_JoystickSensorInfo *sensors = (SDL_JoystickSensorInfo *)SDL_realloc(joystick->sensors, (nsensors * sizeof(SDL_JoystickSensorInfo))); + if (sensors) { + SDL_JoystickSensorInfo *sensor = &sensors[nsensors - 1]; + + SDL_zerop(sensor); + sensor->type = type; + + joystick->nsensors = nsensors; + joystick->sensors = sensors; + } +} + void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance) { SDL_JoystickDriver *driver; @@ -2463,8 +2478,8 @@ int SDL_PrivateJoystickTouchpad(SDL_Joystick *joystick, int touchpad, int finger { SDL_JoystickTouchpadInfo *touchpad_info; SDL_JoystickTouchpadFingerInfo *finger_info; -#if !SDL_EVENTS_DISABLED int posted; +#if !SDL_EVENTS_DISABLED Uint32 event_type; #endif @@ -2544,4 +2559,41 @@ int SDL_PrivateJoystickTouchpad(SDL_Joystick *joystick, int touchpad, int finger return posted; } +int SDL_PrivateJoystickSensor(SDL_Joystick *joystick, SDL_SensorType type, const float *data, int num_values) +{ + int i; + int posted = 0; + + for (i = 0; i < joystick->nsensors; ++i) { + SDL_JoystickSensorInfo *sensor = &joystick->sensors[i]; + + if (sensor->type == type) { + if (sensor->enabled) { + /* Allow duplicate events, for things like gyro updates */ + + /* Update internal sensor state */ + num_values = SDL_min(num_values, SDL_arraysize(sensor->data)); + SDL_memcpy(sensor->data, data, num_values*sizeof(*data)); + + /* Post the event, if desired */ + posted = 0; +#if !SDL_EVENTS_DISABLED + if (SDL_GetEventState(SDL_CONTROLLERSENSORUPDATE) == SDL_ENABLE) { + SDL_Event event; + event.type = SDL_CONTROLLERSENSORUPDATE; + event.csensor.which = joystick->instance_id; + event.csensor.sensor = type; + num_values = SDL_min(num_values, SDL_arraysize(event.csensor.data)); + SDL_memset(event.csensor.data, 0, sizeof(event.csensor.data)); + SDL_memcpy(event.csensor.data, data, num_values*sizeof(*data)); + posted = SDL_PushEvent(&event) == 1; + } +#endif /* !SDL_EVENTS_DISABLED */ + } + break; + } + } + return posted; +} + /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h index 2839930d2..2cceb3410 100644 --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -109,6 +109,7 @@ extern void SDL_GameControllerHandleDelayedGuideButton(SDL_Joystick *joystick); /* Internal event queueing functions */ extern void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers); +extern void SDL_PrivateJoystickAddSensor(SDL_Joystick *joystick, SDL_SensorType type); extern void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance); extern void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance); extern int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, @@ -121,6 +122,8 @@ extern int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state); extern int SDL_PrivateJoystickTouchpad(SDL_Joystick *joystick, int touchpad, int finger, Uint8 state, float x, float y, float pressure); +extern int SDL_PrivateJoystickSensor(SDL_Joystick *joystick, + SDL_SensorType type, const float *data, int num_values); extern void SDL_PrivateJoystickBatteryLevel(SDL_Joystick *joystick, SDL_JoystickPowerLevel ePowerLevel); diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h index 158bb86cd..3ea7d39c1 100644 --- a/src/joystick/SDL_sysjoystick.h +++ b/src/joystick/SDL_sysjoystick.h @@ -53,6 +53,13 @@ typedef struct _SDL_JoystickTouchpadInfo SDL_JoystickTouchpadFingerInfo *fingers; } SDL_JoystickTouchpadInfo; +typedef struct _SDL_JoystickSensorInfo +{ + SDL_SensorType type; + SDL_bool enabled; + float data[3]; /* If this needs to expand, update SDL_ControllerSensorEvent */ +} SDL_JoystickSensorInfo; + struct _SDL_Joystick { SDL_JoystickID instance_id; /* Device instance, monotonically increasing from 0 */ @@ -78,6 +85,10 @@ struct _SDL_Joystick int ntouchpads; /* Number of touchpads on the joystick */ SDL_JoystickTouchpadInfo *touchpads; /* Current touchpad states */ + int nsensors; /* Number of sensors on the joystick */ + int nsensors_enabled; + SDL_JoystickSensorInfo *sensors; + Uint16 low_frequency_rumble; Uint16 high_frequency_rumble; Uint32 rumble_expiration; @@ -94,6 +105,7 @@ struct _SDL_Joystick SDL_bool is_game_controller; SDL_bool delayed_guide_button; /* SDL_TRUE if this device has the guide button event delayed */ SDL_JoystickPowerLevel epowerlevel; /* power level of this joystick, SDL_JOYSTICK_POWER_UNKNOWN if not supported */ + struct _SDL_JoystickDriver *driver; struct joystick_hwdata *hwdata; /* Driver dependent information */ @@ -154,6 +166,9 @@ typedef struct _SDL_JoystickDriver SDL_bool (*HasLED)(SDL_Joystick *joystick); int (*SetLED)(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue); + /* Sensor functionality */ + int (*SetSensorsEnabled)(SDL_Joystick *joystick, SDL_bool enabled); + /* Function to update the state of a joystick - called as a device poll. * This function shouldn't update the joystick structure directly, * but instead should call SDL_PrivateJoystick*() to deliver events diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c index a00343e7a..a226d3909 100644 --- a/src/joystick/android/SDL_sysjoystick.c +++ b/src/joystick/android/SDL_sysjoystick.c @@ -630,6 +630,12 @@ ANDROID_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 bl return SDL_Unsupported(); } +static int +ANDROID_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) +{ + return SDL_Unsupported(); +} + static void ANDROID_JoystickUpdate(SDL_Joystick * joystick) { @@ -711,6 +717,7 @@ SDL_JoystickDriver SDL_ANDROID_JoystickDriver = ANDROID_JoystickRumbleTriggers, ANDROID_JoystickHasLED, ANDROID_JoystickSetLED, + ANDROID_JoystickSetSensorsEnabled, ANDROID_JoystickUpdate, ANDROID_JoystickClose, ANDROID_JoystickQuit, diff --git a/src/joystick/bsd/SDL_sysjoystick.c b/src/joystick/bsd/SDL_sysjoystick.c index c68629ae7..1595acf3c 100644 --- a/src/joystick/bsd/SDL_sysjoystick.c +++ b/src/joystick/bsd/SDL_sysjoystick.c @@ -206,8 +206,8 @@ static void report_free(struct report *); static int numjoysticks = 0; -static int BSD_JoystickOpen(SDL_Joystick * joy, int device_index); -static void BSD_JoystickClose(SDL_Joystick * joy); +static int BSD_JoystickOpen(SDL_Joystick *joy, int device_index); +static void BSD_JoystickClose(SDL_Joystick *joy); static int BSD_JoystickInit(void) @@ -340,7 +340,7 @@ hatval_to_sdl(Sint32 hatval) static int -BSD_JoystickOpen(SDL_Joystick * joy, int device_index) +BSD_JoystickOpen(SDL_Joystick *joy, int device_index) { char *path = joynames[device_index]; struct joystick_hwdata *hw; @@ -532,7 +532,7 @@ desc_failed: } static void -BSD_JoystickUpdate(SDL_Joystick * joy) +BSD_JoystickUpdate(SDL_Joystick *joy) { struct hid_item hitem; struct hid_data *hdata; @@ -666,7 +666,7 @@ BSD_JoystickUpdate(SDL_Joystick * joy) /* Function to close a joystick after use */ static void -BSD_JoystickClose(SDL_Joystick * joy) +BSD_JoystickClose(SDL_Joystick *joy) { if (SDL_strncmp(joy->hwdata->path, "/dev/joy", 8)) { report_free(&joy->hwdata->inreport); @@ -757,13 +757,13 @@ report_free(struct report *r) } static int -BSD_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +BSD_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) { return SDL_Unsupported(); } static int -BSD_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble) +BSD_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) { return SDL_Unsupported(); } @@ -775,13 +775,19 @@ BSD_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) } static SDL_bool -BSD_JoystickHasLED(SDL_Joystick * joystick) +BSD_JoystickHasLED(SDL_Joystick *joystick) { return SDL_FALSE; } static int -BSD_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue) +BSD_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) +{ + return SDL_Unsupported(); +} + +static int +BSD_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) { return SDL_Unsupported(); } @@ -801,6 +807,7 @@ SDL_JoystickDriver SDL_BSD_JoystickDriver = BSD_JoystickRumbleTriggers, BSD_JoystickHasLED, BSD_JoystickSetLED, + BSD_JoystickSetSensorsEnabled, BSD_JoystickUpdate, BSD_JoystickClose, BSD_JoystickQuit, diff --git a/src/joystick/darwin/SDL_sysjoystick.c b/src/joystick/darwin/SDL_sysjoystick.c index 408bdab67..8bea24410 100644 --- a/src/joystick/darwin/SDL_sysjoystick.c +++ b/src/joystick/darwin/SDL_sysjoystick.c @@ -940,6 +940,12 @@ DARWIN_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blu return SDL_Unsupported(); } +static int +DARWIN_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) +{ + return SDL_Unsupported(); +} + static void DARWIN_JoystickUpdate(SDL_Joystick * joystick) { @@ -1090,6 +1096,7 @@ SDL_JoystickDriver SDL_DARWIN_JoystickDriver = DARWIN_JoystickRumbleTriggers, DARWIN_JoystickHasLED, DARWIN_JoystickSetLED, + DARWIN_JoystickSetSensorsEnabled, DARWIN_JoystickUpdate, DARWIN_JoystickClose, DARWIN_JoystickQuit, diff --git a/src/joystick/dummy/SDL_sysjoystick.c b/src/joystick/dummy/SDL_sysjoystick.c index 04d744d2d..a51fb206a 100644 --- a/src/joystick/dummy/SDL_sysjoystick.c +++ b/src/joystick/dummy/SDL_sysjoystick.c @@ -78,42 +78,48 @@ DUMMY_JoystickGetDeviceInstanceID(int device_index) } static int -DUMMY_JoystickOpen(SDL_Joystick * joystick, int device_index) +DUMMY_JoystickOpen(SDL_Joystick *joystick, int device_index) { return SDL_SetError("Logic error: No joysticks available"); } static int -DUMMY_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +DUMMY_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) { return SDL_Unsupported(); } static int -DUMMY_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble) +DUMMY_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) { return SDL_Unsupported(); } static SDL_bool -DUMMY_JoystickHasLED(SDL_Joystick * joystick) +DUMMY_JoystickHasLED(SDL_Joystick *joystick) { return SDL_FALSE; } static int -DUMMY_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue) +DUMMY_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) +{ + return SDL_Unsupported(); +} + +static int +DUMMY_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) { return SDL_Unsupported(); } static void -DUMMY_JoystickUpdate(SDL_Joystick * joystick) +DUMMY_JoystickUpdate(SDL_Joystick *joystick) { } static void -DUMMY_JoystickClose(SDL_Joystick * joystick) +DUMMY_JoystickClose(SDL_Joystick *joystick) { } @@ -143,6 +149,7 @@ SDL_JoystickDriver SDL_DUMMY_JoystickDriver = DUMMY_JoystickRumbleTriggers, DUMMY_JoystickHasLED, DUMMY_JoystickSetLED, + DUMMY_JoystickSetSensorsEnabled, DUMMY_JoystickUpdate, DUMMY_JoystickClose, DUMMY_JoystickQuit, diff --git a/src/joystick/emscripten/SDL_sysjoystick.c b/src/joystick/emscripten/SDL_sysjoystick.c index 74327ab3e..cafea91f7 100644 --- a/src/joystick/emscripten/SDL_sysjoystick.c +++ b/src/joystick/emscripten/SDL_sysjoystick.c @@ -304,7 +304,7 @@ EMSCRIPTEN_JoystickGetDeviceInstanceID(int device_index) It returns 0, or -1 if there is an error. */ static int -EMSCRIPTEN_JoystickOpen(SDL_Joystick * joystick, int device_index) +EMSCRIPTEN_JoystickOpen(SDL_Joystick *joystick, int device_index) { SDL_joylist_item *item = JoystickByDeviceIndex(device_index); @@ -336,7 +336,7 @@ EMSCRIPTEN_JoystickOpen(SDL_Joystick * joystick, int device_index) * and update joystick device state. */ static void -EMSCRIPTEN_JoystickUpdate(SDL_Joystick * joystick) +EMSCRIPTEN_JoystickUpdate(SDL_Joystick *joystick) { EmscriptenGamepadEvent gamepadState; SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata; @@ -378,7 +378,7 @@ EMSCRIPTEN_JoystickUpdate(SDL_Joystick * joystick) /* Function to close a joystick after use */ static void -EMSCRIPTEN_JoystickClose(SDL_Joystick * joystick) +EMSCRIPTEN_JoystickClose(SDL_Joystick *joystick) { SDL_joylist_item *item = (SDL_joylist_item *) joystick->hwdata; if (item) { @@ -398,13 +398,13 @@ EMSCRIPTEN_JoystickGetDeviceGUID(int device_index) } static int -EMSCRIPTEN_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +EMSCRIPTEN_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) { return SDL_Unsupported(); } static int -EMSCRIPTEN_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble) +EMSCRIPTEN_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) { return SDL_Unsupported(); } @@ -416,13 +416,19 @@ EMSCRIPTEN_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) } static SDL_bool -EMSCRIPTEN_JoystickHasLED(SDL_Joystick * joystick) +EMSCRIPTEN_JoystickHasLED(SDL_Joystick *joystick) { return SDL_FALSE; } static int -EMSCRIPTEN_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue) +EMSCRIPTEN_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) +{ + return SDL_Unsupported(); +} + +static int +EMSCRIPTEN_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) { return SDL_Unsupported(); } @@ -442,6 +448,7 @@ SDL_JoystickDriver SDL_EMSCRIPTEN_JoystickDriver = EMSCRIPTEN_JoystickRumbleTriggers, EMSCRIPTEN_JoystickHasLED, EMSCRIPTEN_JoystickSetLED, + EMSCRIPTEN_JoystickSetSensorsEnabled, EMSCRIPTEN_JoystickUpdate, EMSCRIPTEN_JoystickClose, EMSCRIPTEN_JoystickQuit, diff --git a/src/joystick/haiku/SDL_haikujoystick.cc b/src/joystick/haiku/SDL_haikujoystick.cc index 6adb4da3e..31f52ae0e 100644 --- a/src/joystick/haiku/SDL_haikujoystick.cc +++ b/src/joystick/haiku/SDL_haikujoystick.cc @@ -114,14 +114,14 @@ extern "C" return device_index; } - static void HAIKU_JoystickClose(SDL_Joystick * joystick); + static void HAIKU_JoystickClose(SDL_Joystick *joystick); /* Function to open a joystick for use. The joystick to open is specified by the device index. This should fill the nbuttons and naxes fields of the joystick structure. It returns 0, or -1 if there is an error. */ - static int HAIKU_JoystickOpen(SDL_Joystick * joystick, int device_index) + static int HAIKU_JoystickOpen(SDL_Joystick *joystick, int device_index) { BJoystick *stick; @@ -168,7 +168,7 @@ extern "C" * but instead should call SDL_PrivateJoystick*() to deliver events * and update joystick device state. */ - static void HAIKU_JoystickUpdate(SDL_Joystick * joystick) + static void HAIKU_JoystickUpdate(SDL_Joystick *joystick) { static const Uint8 hat_map[9] = { SDL_HAT_CENTERED, @@ -217,7 +217,7 @@ extern "C" } /* Function to close a joystick after use */ - static void HAIKU_JoystickClose(SDL_Joystick * joystick) + static void HAIKU_JoystickClose(SDL_Joystick *joystick) { if (joystick->hwdata) { joystick->hwdata->stick->Close(); @@ -254,13 +254,13 @@ extern "C" return guid; } - static int HAIKU_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) + static int HAIKU_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) { return SDL_Unsupported(); } - static int HAIKU_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble) + static int HAIKU_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) { return SDL_Unsupported(); } @@ -271,12 +271,17 @@ extern "C" return SDL_FALSE; } - static SDL_bool HAIKU_JoystickHasLED(SDL_Joystick * joystick) + static SDL_bool HAIKU_JoystickHasLED(SDL_Joystick *joystick) { return SDL_FALSE; } - static int HAIKU_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue) + static int HAIKU_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) + { + return SDL_Unsupported(); + } + + static int HAIKU_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) { return SDL_Unsupported(); } @@ -296,6 +301,7 @@ extern "C" HAIKU_JoystickRumbleTriggers, HAIKU_JoystickHasLED, HAIKU_JoystickSetLED, + HAIKU_JoystickSetSensorsEnabled, HAIKU_JoystickUpdate, HAIKU_JoystickClose, HAIKU_JoystickQuit, diff --git a/src/joystick/hidapi/SDL_hidapi_gamecube.c b/src/joystick/hidapi/SDL_hidapi_gamecube.c index 61688c8b5..d85516ba3 100644 --- a/src/joystick/hidapi/SDL_hidapi_gamecube.c +++ b/src/joystick/hidapi/SDL_hidapi_gamecube.c @@ -381,6 +381,12 @@ HIDAPI_DriverGameCube_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *jo return SDL_Unsupported(); } +static int +HIDAPI_DriverGameCube_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled) +{ + return SDL_Unsupported(); +} + static void HIDAPI_DriverGameCube_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) { @@ -423,6 +429,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube = HIDAPI_DriverGameCube_RumbleJoystickTriggers, HIDAPI_DriverGameCube_HasJoystickLED, HIDAPI_DriverGameCube_SetJoystickLED, + HIDAPI_DriverGameCube_SetJoystickSensorsEnabled, HIDAPI_DriverGameCube_CloseJoystick, HIDAPI_DriverGameCube_FreeDevice, NULL, diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c index c7d6e25b0..e4056fd28 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps4.c +++ b/src/joystick/hidapi/SDL_hidapi_ps4.c @@ -37,6 +37,17 @@ #ifdef SDL_JOYSTICK_HIDAPI_PS4 +/* Define this if you want to log all packets from the controller */ +/*#define DEBUG_PS4_PROTOCOL*/ + +/* Define this if you want to log calibration data */ +/*#define DEBUG_PS4_CALIBRATION*/ + +#define GYRO_RES_PER_DEGREE 1024.0f +#define ACCEL_RES_PER_G 8192.0f + +#define LOAD16(A, B) (Sint16)((Uint16)(A) | (((Uint16)(B)) << 8)) + typedef enum { k_EPS4ReportIdUsbState = 1, @@ -71,12 +82,12 @@ typedef struct Uint8 ucTriggerLeft; Uint8 ucTriggerRight; Uint8 _rgucPad0[ 3 ]; - Sint16 sGyroX; - Sint16 sGyroY; - Sint16 sGyroZ; - Sint16 sAccelX; - Sint16 sAccelY; - Sint16 sAccelZ; + Uint8 rgucGyroX[2]; + Uint8 rgucGyroY[2]; + Uint8 rgucGyroZ[2]; + Uint8 rgucAccelX[2]; + Uint8 rgucAccelY[2]; + Uint8 rgucAccelZ[2]; Uint8 _rgucPad1[ 5 ]; Uint8 ucBatteryLevel; Uint8 _rgucPad2[ 4 ]; @@ -102,11 +113,20 @@ typedef struct Uint8 ucVolumeSpeaker; } DS4EffectsState_t; +typedef struct { + Sint16 bias; + float sensitivity; +} IMUCalibrationData; + typedef struct { SDL_bool is_dongle; SDL_bool is_bluetooth; + SDL_bool official_controller; SDL_bool audio_supported; SDL_bool effects_supported; + SDL_bool report_sensors; + SDL_bool hardware_calibration; + IMUCalibrationData calibration[6]; int player_index; Uint8 rumble_left; Uint8 rumble_right; @@ -191,6 +211,168 @@ HIDAPI_DriverPS4_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID return -1; } +static void +HIDAPI_DriverPS4_LoadCalibrationData(SDL_HIDAPI_Device *device) +{ + SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; + int i, tries, size; + SDL_bool have_data = SDL_FALSE; + Uint8 data[USB_PACKET_LENGTH]; + + if (!ctx->official_controller) { +#ifdef DEBUG_PS4_CALIBRATION + SDL_Log("Not an official controller, ignoring calibration\n"); +#endif + return; + } + + for( tries = 0; tries < 5; ++tries ) { + /* For Bluetooth controllers, this report switches them into advanced report mode */ + size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdGyroCalibration_USB, data, sizeof(data)); + if (size < 35) { +#ifdef DEBUG_PS4_CALIBRATION + SDL_Log("Short read of calibration data: %d, ignoring calibration\n", size); +#endif + return; + } + + if (ctx->is_bluetooth) { + size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdGyroCalibration_BT, data, sizeof(data)); + if (size < 35) { +#ifdef DEBUG_PS4_CALIBRATION + SDL_Log("Short read of calibration data: %d, ignoring calibration\n", size); +#endif + return; + } + } + + /* In some cases this report returns all zeros. Usually immediately after connection with the PS4 Dongle */ + for (i = 0; i < size; ++i) { + if (data[i]) { + have_data = SDL_TRUE; + break; + } + } + if (have_data) { + break; + } + + SDL_Delay(2); + } + + if (have_data) { + Sint16 sGyroPitchBias, sGyroYawBias, sGyroRollBias; + Sint16 sGyroPitchPlus, sGyroPitchMinus; + Sint16 sGyroYawPlus, sGyroYawMinus; + Sint16 sGyroRollPlus, sGyroRollMinus; + Sint16 sGyroSpeedPlus, sGyroSpeedMinus; + + Sint16 sAccXPlus, sAccXMinus; + Sint16 sAccYPlus, sAccYMinus; + Sint16 sAccZPlus, sAccZMinus; + + float flNumerator; + Sint16 sRange2g; + +#ifdef DEBUG_PS4_CALIBRATION + HIDAPI_DumpPacket("PS4 calibration packet: size = %d", data, size); +#endif + + sGyroPitchBias = LOAD16(data[1], data[2]); + sGyroYawBias = LOAD16(data[3], data[4]); + sGyroRollBias = LOAD16(data[5], data[6]); + + if (ctx->is_bluetooth || ctx->is_dongle) { + sGyroPitchPlus = LOAD16(data[7], data[8]); + sGyroYawPlus = LOAD16(data[9], data[10]); + sGyroRollPlus = LOAD16(data[11], data[12]); + sGyroPitchMinus = LOAD16(data[13], data[14]); + sGyroYawMinus = LOAD16(data[15], data[16]); + sGyroRollMinus = LOAD16(data[17], data[18]); + } else { + sGyroPitchPlus = LOAD16(data[7], data[8]); + sGyroPitchMinus = LOAD16(data[9], data[10]); + sGyroYawPlus = LOAD16(data[11], data[12]); + sGyroYawMinus = LOAD16(data[13], data[14]); + sGyroRollPlus = LOAD16(data[15], data[16]); + sGyroRollMinus = LOAD16(data[17], data[18]); + } + + sGyroSpeedPlus = LOAD16(data[19], data[20]); + sGyroSpeedMinus = LOAD16(data[21], data[22]); + + sAccXPlus = LOAD16(data[23], data[24]); + sAccXMinus = LOAD16(data[25], data[26]); + sAccYPlus = LOAD16(data[27], data[28]); + sAccYMinus = LOAD16(data[29], data[30]); + sAccZPlus = LOAD16(data[31], data[32]); + sAccZMinus = LOAD16(data[33], data[34]); + + flNumerator = (sGyroSpeedPlus + sGyroSpeedMinus) * GYRO_RES_PER_DEGREE; + ctx->calibration[0].bias = sGyroPitchBias; + ctx->calibration[0].sensitivity = flNumerator / (sGyroPitchPlus - sGyroPitchMinus); + + ctx->calibration[1].bias = sGyroYawBias; + ctx->calibration[1].sensitivity = flNumerator / (sGyroYawPlus - sGyroYawMinus); + + ctx->calibration[2].bias = sGyroRollBias; + ctx->calibration[2].sensitivity = flNumerator / (sGyroRollPlus - sGyroRollMinus); + + sRange2g = sAccXPlus - sAccXMinus; + ctx->calibration[3].bias = sAccXPlus - sRange2g / 2; + ctx->calibration[3].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g; + + sRange2g = sAccYPlus - sAccYMinus; + ctx->calibration[4].bias = sAccYPlus - sRange2g / 2; + ctx->calibration[4].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g; + + sRange2g = sAccZPlus - sAccZMinus; + ctx->calibration[5].bias = sAccZPlus - sRange2g / 2; + ctx->calibration[5].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g; + + ctx->hardware_calibration = SDL_TRUE; + for (i = 0; i < 6; ++i) { + float divisor = (i < 3 ? 64.0f : 1.0f); +#ifdef DEBUG_PS4_CALIBRATION + SDL_Log("calibration[%d] bias = %d, sensitivity = %f\n", i, ctx->calibration[i].bias, ctx->calibration[i].sensitivity); +#endif + /* Some controllers have a bad calibration */ + if ((SDL_abs(ctx->calibration[i].bias) > 1024) || (SDL_fabs(1.0f - ctx->calibration[i].sensitivity / divisor) > 0.5f)) { +#ifdef DEBUG_PS4_CALIBRATION + SDL_Log("invalid calibration, ignoring\n"); +#endif + ctx->hardware_calibration = SDL_FALSE; + } + } + } else { +#ifdef DEBUG_PS4_CALIBRATION + SDL_Log("Calibration data not available\n"); +#endif + } +} + +static float +HIDAPI_DriverPS4_ApplyCalibrationData(SDL_DriverPS4_Context *ctx, int index, Sint16 value) +{ + float result; + + if (ctx->hardware_calibration) { + IMUCalibrationData *calibration = &ctx->calibration[index]; + + result = (value - calibration->bias) * calibration->sensitivity; + } else { + result = value; + } + + /* Convert the raw data to the units expected by SDL */ + if (index < 3) { + result = (result / GYRO_RES_PER_DEGREE) * M_PI / 180.0f; + } else { + result = (result / ACCEL_RES_PER_G) * SDL_STANDARD_GRAVITY; + } + return result; +} + static int HIDAPI_DriverPS4_UpdateEffects(SDL_HIDAPI_Device *device) { @@ -286,37 +468,24 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) ctx->is_dongle = (device->vendor_id == USB_VENDOR_SONY && device->product_id == USB_PRODUCT_SONY_DS4_DONGLE); if (ctx->is_dongle) { ctx->is_bluetooth = SDL_FALSE; + ctx->official_controller = SDL_TRUE; } else if (device->vendor_id == USB_VENDOR_SONY) { - int i; Uint8 data[USB_PACKET_LENGTH]; - int length; + int size; /* This will fail if we're on Bluetooth */ - length = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data)); - if (length >= 7) { - SDL_bool had_data = SDL_FALSE; - - for (i = 0; i < length; ++i) { - if (data[i] != 0x00) { - had_data = SDL_TRUE; - break; - } - } - - if (had_data) { - char serial[18]; - - SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", - data[6], data[5], data[4], data[3], data[2], data[1]); - joystick->serial = SDL_strdup(serial); - } else { - /* Maybe the dongle without a connected controller? */ - } + size = ReadFeatureReport(device->dev, k_ePS4FeatureReportIdSerialNumber, data, sizeof(data)); + if (size >= 7) { + char serial[18]; + SDL_snprintf(serial, sizeof(serial), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x", + data[6], data[5], data[4], data[3], data[2], data[1]); + joystick->serial = SDL_strdup(serial); ctx->is_bluetooth = SDL_FALSE; } else { ctx->is_bluetooth = SDL_TRUE; } + ctx->official_controller = SDL_TRUE; } else { /* Third party controllers appear to all be wired */ ctx->is_bluetooth = SDL_FALSE; @@ -351,6 +520,8 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; SDL_PrivateJoystickAddTouchpad(joystick, 2); + SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO); + SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL); return SDL_TRUE; } @@ -391,6 +562,19 @@ HIDAPI_DriverPS4_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystic return HIDAPI_DriverPS4_UpdateEffects(device); } +static int +HIDAPI_DriverPS4_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled) +{ + SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; + + if (enabled) { + HIDAPI_DriverPS4_LoadCalibrationData(device); + } + ctx->report_sensors = enabled; + + return 0; +} + static void HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet) { @@ -521,6 +705,20 @@ HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_ touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4); SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f); + if (ctx->report_sensors) { + float data[3]; + + data[0] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 0, LOAD16(packet->rgucGyroX[0], packet->rgucGyroX[1])); + data[1] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 1, LOAD16(packet->rgucGyroY[0], packet->rgucGyroY[1])); + data[2] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 2, LOAD16(packet->rgucGyroZ[0], packet->rgucGyroZ[1])); + SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_GYRO, data, 3); + + data[0] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 3, LOAD16(packet->rgucAccelX[0], packet->rgucAccelX[1])); + data[1] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 4, LOAD16(packet->rgucAccelY[0], packet->rgucAccelY[1])); + data[2] = HIDAPI_DriverPS4_ApplyCalibrationData(ctx, 5, LOAD16(packet->rgucAccelZ[0], packet->rgucAccelZ[1])); + SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_ACCEL, data, 3); + } + SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state)); } @@ -540,6 +738,9 @@ HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device) } while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { +#ifdef DEBUG_PS4_PROTOCOL + HIDAPI_DumpPacket("PS4 packet: size = %d", data, size); +#endif switch (data[0]) { case k_EPS4ReportIdUsbState: HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[1]); @@ -603,6 +804,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 = HIDAPI_DriverPS4_RumbleJoystickTriggers, HIDAPI_DriverPS4_HasJoystickLED, HIDAPI_DriverPS4_SetJoystickLED, + HIDAPI_DriverPS4_SetJoystickSensorsEnabled, HIDAPI_DriverPS4_CloseJoystick, HIDAPI_DriverPS4_FreeDevice, NULL diff --git a/src/joystick/hidapi/SDL_hidapi_ps5.c b/src/joystick/hidapi/SDL_hidapi_ps5.c index 6580a29b3..833a29998 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps5.c +++ b/src/joystick/hidapi/SDL_hidapi_ps5.c @@ -72,8 +72,12 @@ typedef struct Uint8 rgucButtonsAndHat[3]; /* 7 */ Uint8 ucZero; /* 10 */ Uint8 rgucPacketSequence[4]; /* 11 - 32 bit little endian */ - Uint8 rgucAccel[6]; /* 15 */ - Uint8 rgucGyro[6]; /* 21 */ + Uint8 rgucGyroX[2]; /* 15 */ + Uint8 rgucGyroY[2]; /* 17 */ + Uint8 rgucGyroZ[2]; /* 19 */ + Uint8 rgucAccelX[2]; /* 21 */ + Uint8 rgucAccelY[2]; /* 23 */ + Uint8 rgucAccelZ[2]; /* 25 */ Uint8 rgucTimer1[4]; /* 27 - 32 bit little endian */ Uint8 ucBatteryTemp; /* 31 */ Uint8 ucTouchpadCounter1; /* 32 - high bit clear + counter */ @@ -351,6 +355,12 @@ HIDAPI_DriverPS5_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystic return HIDAPI_DriverPS5_UpdateEffects(device); } +static int +HIDAPI_DriverPS5_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled) +{ + return 0; +} + static void HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS5_Context *ctx, PS5SimpleStatePacket_t *packet) { @@ -649,6 +659,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS5 = HIDAPI_DriverPS5_RumbleJoystickTriggers, HIDAPI_DriverPS5_HasJoystickLED, HIDAPI_DriverPS5_SetJoystickLED, + HIDAPI_DriverPS5_SetJoystickSensorsEnabled, HIDAPI_DriverPS5_CloseJoystick, HIDAPI_DriverPS5_FreeDevice, NULL diff --git a/src/joystick/hidapi/SDL_hidapi_steam.c b/src/joystick/hidapi/SDL_hidapi_steam.c index be3aeb681..0ae4d2243 100644 --- a/src/joystick/hidapi/SDL_hidapi_steam.c +++ b/src/joystick/hidapi/SDL_hidapi_steam.c @@ -1054,6 +1054,13 @@ HIDAPI_DriverSteam_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joyst return SDL_Unsupported(); } +static int +HIDAPI_DriverSteam_SetSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled) +{ + /* You should use the full Steam Input API for sensor support */ + return SDL_Unsupported(); +} + static SDL_bool HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device) { @@ -1191,6 +1198,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam = HIDAPI_DriverSteam_RumbleJoystickTriggers, HIDAPI_DriverSteam_HasJoystickLED, HIDAPI_DriverSteam_SetJoystickLED, + HIDAPI_DriverSteam_SetSensorsEnabled, HIDAPI_DriverSteam_CloseJoystick, HIDAPI_DriverSteam_FreeDevice, NULL diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c index 0d9c1f208..16e1e242f 100644 --- a/src/joystick/hidapi/SDL_hidapi_switch.c +++ b/src/joystick/hidapi/SDL_hidapi_switch.c @@ -942,6 +942,12 @@ HIDAPI_DriverSwitch_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joys return SDL_Unsupported(); } +static int +HIDAPI_DriverSwitch_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled) +{ + return SDL_Unsupported(); +} + static void HandleInputOnlyControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchInputOnlyControllerStatePacket_t *packet) { Sint16 axis; @@ -1298,6 +1304,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch = HIDAPI_DriverSwitch_RumbleJoystickTriggers, HIDAPI_DriverSwitch_HasJoystickLED, HIDAPI_DriverSwitch_SetJoystickLED, + HIDAPI_DriverSwitch_SetJoystickSensorsEnabled, HIDAPI_DriverSwitch_CloseJoystick, HIDAPI_DriverSwitch_FreeDevice, NULL diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360.c b/src/joystick/hidapi/SDL_hidapi_xbox360.c index 77efb4c1a..dfc7c31a4 100644 --- a/src/joystick/hidapi/SDL_hidapi_xbox360.c +++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c @@ -815,6 +815,12 @@ HIDAPI_DriverXbox360_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joy return SDL_Unsupported(); } +static int +HIDAPI_DriverXbox360_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled) +{ + return SDL_Unsupported(); +} + #ifdef __WIN32__ /* This is the packet format for Xbox 360 and Xbox One controllers on Windows, however with this interface there is no rumble support, no guide button, @@ -1326,6 +1332,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 = HIDAPI_DriverXbox360_RumbleJoystickTriggers, HIDAPI_DriverXbox360_HasJoystickLED, HIDAPI_DriverXbox360_SetJoystickLED, + HIDAPI_DriverXbox360_SetJoystickSensorsEnabled, HIDAPI_DriverXbox360_CloseJoystick, HIDAPI_DriverXbox360_FreeDevice, HIDAPI_DriverXbox360_PostUpdate, diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360w.c b/src/joystick/hidapi/SDL_hidapi_xbox360w.c index 3fe06eb2e..294139cf2 100644 --- a/src/joystick/hidapi/SDL_hidapi_xbox360w.c +++ b/src/joystick/hidapi/SDL_hidapi_xbox360w.c @@ -176,6 +176,12 @@ HIDAPI_DriverXbox360W_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *jo return SDL_Unsupported(); } +static int +HIDAPI_DriverXbox360W_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled) +{ + return SDL_Unsupported(); +} + static void HIDAPI_DriverXbox360W_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360W_Context *ctx, Uint8 *data, int size) { @@ -316,6 +322,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360W = HIDAPI_DriverXbox360W_RumbleJoystickTriggers, HIDAPI_DriverXbox360W_HasJoystickLED, HIDAPI_DriverXbox360W_SetJoystickLED, + HIDAPI_DriverXbox360W_SetJoystickSensorsEnabled, HIDAPI_DriverXbox360W_CloseJoystick, HIDAPI_DriverXbox360W_FreeDevice, NULL diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c index 9e622dd0a..3f89ac653 100644 --- a/src/joystick/hidapi/SDL_hidapi_xboxone.c +++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c @@ -406,6 +406,12 @@ HIDAPI_DriverXboxOne_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joy return SDL_Unsupported(); } +static int +HIDAPI_DriverXboxOne_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled) +{ + return SDL_Unsupported(); +} + static void HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size) { @@ -934,6 +940,7 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne = HIDAPI_DriverXboxOne_RumbleJoystickTriggers, HIDAPI_DriverXboxOne_HasJoystickLED, HIDAPI_DriverXboxOne_SetJoystickLED, + HIDAPI_DriverXboxOne_SetJoystickSensorsEnabled, HIDAPI_DriverXboxOne_CloseJoystick, HIDAPI_DriverXboxOne_FreeDevice, NULL diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index 9d7f65b94..f303440ee 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -1130,6 +1130,23 @@ HIDAPI_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blu return result; } +static int +HIDAPI_JoystickSetSensorsEnabled(SDL_Joystick * joystick, SDL_bool enabled) +{ + int result; + + if (joystick->hwdata) { + SDL_HIDAPI_Device *device = joystick->hwdata->device; + + result = device->driver->SetJoystickSensorsEnabled(device, joystick, enabled); + } else { + SDL_SetError("SetSensorsEnabled failed, device disconnected"); + result = -1; + } + + return result; +} + static void HIDAPI_JoystickUpdate(SDL_Joystick * joystick) { @@ -1206,6 +1223,7 @@ SDL_JoystickDriver SDL_HIDAPI_JoystickDriver = HIDAPI_JoystickRumbleTriggers, HIDAPI_JoystickHasLED, HIDAPI_JoystickSetLED, + HIDAPI_JoystickSetSensorsEnabled, HIDAPI_JoystickUpdate, HIDAPI_JoystickClose, HIDAPI_JoystickQuit, diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h index f84c1ec3f..f5c995176 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick_c.h +++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h @@ -99,6 +99,7 @@ typedef struct _SDL_HIDAPI_DeviceDriver int (*RumbleJoystickTriggers)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble); SDL_bool (*HasJoystickLED)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick); int (*SetJoystickLED)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue); + int (*SetJoystickSensorsEnabled)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled); void (*CloseJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick); void (*FreeDevice)(SDL_HIDAPI_Device *device); void (*PostUpdate)(void); diff --git a/src/joystick/iphoneos/SDL_sysjoystick.m b/src/joystick/iphoneos/SDL_sysjoystick.m index 8946762d8..3a4c73f39 100644 --- a/src/joystick/iphoneos/SDL_sysjoystick.m +++ b/src/joystick/iphoneos/SDL_sysjoystick.m @@ -79,6 +79,7 @@ static id disconnectObserver = nil; #define ENABLE_MFI_BATTERY #define ENABLE_MFI_RUMBLE #define ENABLE_MFI_LIGHT +#define ENABLE_MFI_SENSORS #define ENABLE_PHYSICAL_INPUT_PROFILE #endif @@ -569,7 +570,7 @@ IOS_JoystickGetDeviceInstanceID(int device_index) } static int -IOS_JoystickOpen(SDL_Joystick * joystick, int device_index) +IOS_JoystickOpen(SDL_Joystick *joystick, int device_index) { SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index); if (device == NULL) { @@ -611,6 +612,20 @@ IOS_JoystickOpen(SDL_Joystick * joystick, int device_index) } }; } + +#ifdef ENABLE_MFI_SENSORS + if (@available(iOS 14.0, tvOS 14.0, *)) { + GCController *controller = joystick->hwdata->controller; + GCMotion *motion = controller.motion; + if (motion && motion.hasRotationRate) { + SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO); + } + if (motion && motion.hasGravityAndUserAcceleration) { + SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL); + } + } +#endif /* ENABLE_MFI_SENSORS */ + #endif /* SDL_JOYSTICK_MFI */ } } @@ -622,7 +637,7 @@ IOS_JoystickOpen(SDL_Joystick * joystick, int device_index) } static void -IOS_AccelerometerUpdate(SDL_Joystick * joystick) +IOS_AccelerometerUpdate(SDL_Joystick *joystick) { #if !TARGET_OS_TV const float maxgforce = SDL_IPHONE_MAX_GFORCE; @@ -692,7 +707,7 @@ IOS_MFIJoystickHatStateForDPad(GCControllerDirectionPad *dpad) #endif static void -IOS_MFIJoystickUpdate(SDL_Joystick * joystick) +IOS_MFIJoystickUpdate(SDL_Joystick *joystick) { #if SDL_JOYSTICK_MFI @autoreleasepool { @@ -806,6 +821,31 @@ IOS_MFIJoystickUpdate(SDL_Joystick * joystick) for (i = 0; i < button_count; i++) { SDL_PrivateJoystickButton(joystick, i, buttons[i]); } + +#ifdef ENABLE_MFI_SENSORS + if (@available(iOS 14.0, tvOS 14.0, *)) { + GCMotion *motion = controller.motion; + if (motion && motion.sensorsActive) { + float data[3]; + + if (motion.hasRotationRate) { + GCRotationRate rate = motion.rotationRate; + data[0] = rate.x; + data[1] = rate.z; + data[2] = -rate.y; + SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_GYRO, data, 3); + } + if (motion.hasGravityAndUserAcceleration) { + GCAcceleration accel = motion.acceleration; + data[0] = -accel.x * SDL_STANDARD_GRAVITY; + data[1] = -accel.y * SDL_STANDARD_GRAVITY; + data[2] = -accel.z * SDL_STANDARD_GRAVITY; + SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_ACCEL, data, 3); + } + } + } +#endif /* ENABLE_MFI_SENSORS */ + } else if (controller.gamepad) { GCGamepad *gamepad = controller.gamepad; @@ -1103,7 +1143,7 @@ static SDL_RumbleContext *IOS_JoystickInitRumble(GCController *controller) #endif /* ENABLE_MFI_RUMBLE */ static int -IOS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +IOS_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) { #ifdef ENABLE_MFI_RUMBLE SDL_JoystickDeviceItem *device = joystick->hwdata; @@ -1129,7 +1169,7 @@ IOS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 } static int -IOS_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble) +IOS_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) { #ifdef ENABLE_MFI_RUMBLE SDL_JoystickDeviceItem *device = joystick->hwdata; @@ -1155,7 +1195,7 @@ IOS_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 r } static SDL_bool -IOS_JoystickHasLED(SDL_Joystick * joystick) +IOS_JoystickHasLED(SDL_Joystick *joystick) { #ifdef ENABLE_MFI_LIGHT @autoreleasepool { @@ -1173,7 +1213,7 @@ IOS_JoystickHasLED(SDL_Joystick * joystick) } static int -IOS_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue) +IOS_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) { #ifdef ENABLE_MFI_LIGHT @autoreleasepool { @@ -1193,8 +1233,27 @@ IOS_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue) return SDL_Unsupported(); } +static int +IOS_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) +{ +#ifdef ENABLE_MFI_SENSORS + @autoreleasepool { + if (@available(iOS 14.0, tvOS 14.0, *)) { + GCController *controller = joystick->hwdata->controller; + GCMotion *motion = controller.motion; + if (motion) { + motion.sensorsActive = enabled ? YES : NO; + return 0; + } + } + } +#endif /* ENABLE_MFI_SENSORS */ + + return SDL_Unsupported(); +} + static void -IOS_JoystickUpdate(SDL_Joystick * joystick) +IOS_JoystickUpdate(SDL_Joystick *joystick) { SDL_JoystickDeviceItem *device = joystick->hwdata; @@ -1210,7 +1269,7 @@ IOS_JoystickUpdate(SDL_Joystick * joystick) } static void -IOS_JoystickClose(SDL_Joystick * joystick) +IOS_JoystickClose(SDL_Joystick *joystick) { SDL_JoystickDeviceItem *device = joystick->hwdata; @@ -1304,6 +1363,7 @@ SDL_JoystickDriver SDL_IOS_JoystickDriver = IOS_JoystickRumbleTriggers, IOS_JoystickHasLED, IOS_JoystickSetLED, + IOS_JoystickSetSensorsEnabled, IOS_JoystickUpdate, IOS_JoystickClose, IOS_JoystickQuit, diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index 782f696b1..76b83c6e5 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -764,7 +764,7 @@ LINUX_JoystickGetDeviceInstanceID(int device_index) } static int -allocate_hatdata(SDL_Joystick * joystick) +allocate_hatdata(SDL_Joystick *joystick) { int i; @@ -782,7 +782,7 @@ allocate_hatdata(SDL_Joystick * joystick) } static int -allocate_balldata(SDL_Joystick * joystick) +allocate_balldata(SDL_Joystick *joystick) { int i; @@ -800,7 +800,7 @@ allocate_balldata(SDL_Joystick * joystick) } static void -ConfigJoystick(SDL_Joystick * joystick, int fd) +ConfigJoystick(SDL_Joystick *joystick, int fd) { int i, t; unsigned long keybit[NBITS(KEY_MAX)] = { 0 }; @@ -926,7 +926,7 @@ ConfigJoystick(SDL_Joystick * joystick, int fd) It returns 0, or -1 if there is an error. */ static int -LINUX_JoystickOpen(SDL_Joystick * joystick, int device_index) +LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index) { SDL_joylist_item *item = JoystickByDevIndex(device_index); @@ -985,7 +985,7 @@ LINUX_JoystickOpen(SDL_Joystick * joystick, int device_index) } static int -LINUX_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +LINUX_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) { struct input_event event; @@ -1027,25 +1027,31 @@ LINUX_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint1 } static int -LINUX_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble) +LINUX_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) { return SDL_Unsupported(); } static SDL_bool -LINUX_JoystickHasLED(SDL_Joystick * joystick) +LINUX_JoystickHasLED(SDL_Joystick *joystick) { return SDL_FALSE; } static int -LINUX_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue) +LINUX_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) +{ + return SDL_Unsupported(); +} + +static int +LINUX_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) { return SDL_Unsupported(); } static SDL_INLINE void -HandleHat(SDL_Joystick * stick, Uint8 hat, int axis, int value) +HandleHat(SDL_Joystick *stick, Uint8 hat, int axis, int value) { struct hwdata_hat *the_hat; const Uint8 position_map[3][3] = { @@ -1070,14 +1076,14 @@ HandleHat(SDL_Joystick * stick, Uint8 hat, int axis, int value) } static SDL_INLINE void -HandleBall(SDL_Joystick * stick, Uint8 ball, int axis, int value) +HandleBall(SDL_Joystick *stick, Uint8 ball, int axis, int value) { stick->hwdata->balls[ball].axis[axis] += value; } static SDL_INLINE int -AxisCorrect(SDL_Joystick * joystick, int which, int value) +AxisCorrect(SDL_Joystick *joystick, int which, int value) { struct axis_correct *correct; @@ -1106,7 +1112,7 @@ AxisCorrect(SDL_Joystick * joystick, int which, int value) } static SDL_INLINE void -PollAllValues(SDL_Joystick * joystick) +PollAllValues(SDL_Joystick *joystick) { struct input_absinfo absinfo; unsigned long keyinfo[NBITS(KEY_MAX)]; @@ -1166,7 +1172,7 @@ PollAllValues(SDL_Joystick * joystick) } static SDL_INLINE void -HandleInputEvents(SDL_Joystick * joystick) +HandleInputEvents(SDL_Joystick *joystick) { struct input_event events[32]; int i, len; @@ -1260,7 +1266,7 @@ HandleInputEvents(SDL_Joystick * joystick) } static void -LINUX_JoystickUpdate(SDL_Joystick * joystick) +LINUX_JoystickUpdate(SDL_Joystick *joystick) { int i; @@ -1287,7 +1293,7 @@ LINUX_JoystickUpdate(SDL_Joystick * joystick) /* Function to close a joystick after use */ static void -LINUX_JoystickClose(SDL_Joystick * joystick) +LINUX_JoystickClose(SDL_Joystick *joystick) { if (joystick->hwdata) { if (joystick->hwdata->effect.id >= 0) { @@ -1345,7 +1351,7 @@ LINUX_JoystickQuit(void) static SDL_bool LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) { - SDL_Joystick * joystick; + SDL_Joystick *joystick; joystick = (SDL_Joystick *) SDL_calloc(sizeof(*joystick), 1); if (joystick == NULL) { @@ -1527,6 +1533,7 @@ SDL_JoystickDriver SDL_LINUX_JoystickDriver = LINUX_JoystickRumbleTriggers, LINUX_JoystickHasLED, LINUX_JoystickSetLED, + LINUX_JoystickSetSensorsEnabled, LINUX_JoystickUpdate, LINUX_JoystickClose, LINUX_JoystickQuit, diff --git a/src/joystick/virtual/SDL_virtualjoystick.c b/src/joystick/virtual/SDL_virtualjoystick.c index 9d441d7e8..e140b4fa0 100644 --- a/src/joystick/virtual/SDL_virtualjoystick.c +++ b/src/joystick/virtual/SDL_virtualjoystick.c @@ -358,6 +358,12 @@ VIRTUAL_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 bl return SDL_Unsupported(); } +static int +VIRTUAL_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) +{ + return SDL_Unsupported(); +} + static void VIRTUAL_JoystickUpdate(SDL_Joystick * joystick) @@ -432,6 +438,7 @@ SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver = VIRTUAL_JoystickRumbleTriggers, VIRTUAL_JoystickHasLED, VIRTUAL_JoystickSetLED, + VIRTUAL_JoystickSetSensorsEnabled, VIRTUAL_JoystickUpdate, VIRTUAL_JoystickClose, VIRTUAL_JoystickQuit, diff --git a/src/joystick/windows/SDL_rawinputjoystick.c b/src/joystick/windows/SDL_rawinputjoystick.c index b8f2d5f5c..4bd0f5d08 100644 --- a/src/joystick/windows/SDL_rawinputjoystick.c +++ b/src/joystick/windows/SDL_rawinputjoystick.c @@ -635,6 +635,12 @@ RAWINPUT_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 b return SDL_Unsupported(); } +static int +RAWINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) +{ + return SDL_Unsupported(); +} + static void RAWINPUT_JoystickUpdate(SDL_Joystick * joystick) @@ -768,6 +774,7 @@ SDL_JoystickDriver SDL_RAWINPUT_JoystickDriver = RAWINPUT_JoystickRumbleTriggers, RAWINPUT_JoystickHasLED, RAWINPUT_JoystickSetLED, + RAWINPUT_JoystickSetSensorsEnabled, RAWINPUT_JoystickUpdate, RAWINPUT_JoystickClose, RAWINPUT_JoystickQuit, diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c index 4aedf0d7a..1af18af72 100644 --- a/src/joystick/windows/SDL_windows_gaming_input.c +++ b/src/joystick/windows/SDL_windows_gaming_input.c @@ -607,6 +607,12 @@ WGI_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue) return SDL_Unsupported(); } +static int +WGI_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) +{ + return SDL_Unsupported(); +} + static Uint8 ConvertHatValue(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition value) { @@ -752,6 +758,7 @@ SDL_JoystickDriver SDL_WGI_JoystickDriver = WGI_JoystickRumbleTriggers, WGI_JoystickHasLED, WGI_JoystickSetLED, + WGI_JoystickSetSensorsEnabled, WGI_JoystickUpdate, WGI_JoystickClose, WGI_JoystickQuit, diff --git a/src/joystick/windows/SDL_windowsjoystick.c b/src/joystick/windows/SDL_windowsjoystick.c index b66e6c7b3..fb18faa98 100644 --- a/src/joystick/windows/SDL_windowsjoystick.c +++ b/src/joystick/windows/SDL_windowsjoystick.c @@ -518,6 +518,12 @@ WINDOWS_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 bl return SDL_Unsupported(); } +static int +WINDOWS_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) +{ + return SDL_Unsupported(); +} + static void WINDOWS_JoystickUpdate(SDL_Joystick * joystick) { @@ -604,6 +610,7 @@ SDL_JoystickDriver SDL_WINDOWS_JoystickDriver = WINDOWS_JoystickRumbleTriggers, WINDOWS_JoystickHasLED, WINDOWS_JoystickSetLED, + WINDOWS_JoystickSetSensorsEnabled, WINDOWS_JoystickUpdate, WINDOWS_JoystickClose, WINDOWS_JoystickQuit, diff --git a/src/sensor/coremotion/SDL_coremotionsensor.m b/src/sensor/coremotion/SDL_coremotionsensor.m index 276109f26..0c61df188 100644 --- a/src/sensor/coremotion/SDL_coremotionsensor.m +++ b/src/sensor/coremotion/SDL_coremotionsensor.m @@ -158,9 +158,9 @@ SDL_COREMOTION_SensorUpdate(SDL_Sensor *sensor) if (accelerometerData) { CMAcceleration acceleration = accelerometerData.acceleration; float data[3]; - data[0] = acceleration.x * SDL_STANDARD_GRAVITY; - data[1] = acceleration.y * SDL_STANDARD_GRAVITY; - data[2] = acceleration.z * SDL_STANDARD_GRAVITY; + data[0] = -acceleration.x * SDL_STANDARD_GRAVITY; + data[1] = -acceleration.y * SDL_STANDARD_GRAVITY; + data[2] = -acceleration.z * SDL_STANDARD_GRAVITY; if (SDL_memcmp(data, sensor->hwdata->data, sizeof(data)) != 0) { SDL_PrivateSensorUpdate(sensor, data, SDL_arraysize(data)); SDL_memcpy(sensor->hwdata->data, data, sizeof(data)); diff --git a/test/testgamecontroller.c b/test/testgamecontroller.c index bc1f66dd5..f484e6c4f 100644 --- a/test/testgamecontroller.c +++ b/test/testgamecontroller.c @@ -98,26 +98,48 @@ LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent) } static void -UpdateWindowTitle() +InitGameController() { const char *name = SDL_GameControllerName(gamecontroller); - const char *serial = SDL_GameControllerGetSerial(gamecontroller); - const char *basetitle = "Game Controller Test: "; - const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + (serial ? 3 + SDL_strlen(serial) : 0) + 1; - char *title = (char *)SDL_malloc(titlelen); - retval = SDL_FALSE; - done = SDL_FALSE; + SDL_Log("Opened game controller %s\n", name); - if (title) { - SDL_snprintf(title, titlelen, "%s%s", basetitle, name); - if (serial) { - SDL_strlcat(title, " (", titlelen); - SDL_strlcat(title, serial, titlelen); - SDL_strlcat(title, ")", titlelen); + if (SDL_GameControllerHasSensor(gamecontroller, SDL_SENSOR_ACCEL)) { + SDL_Log("Enabling accelerometer\n"); + SDL_GameControllerSetSensorEnabled(gamecontroller, SDL_SENSOR_ACCEL, SDL_TRUE); + } + + if (SDL_GameControllerHasSensor(gamecontroller, SDL_SENSOR_GYRO)) { + SDL_Log("Enabling gyro\n"); + SDL_GameControllerSetSensorEnabled(gamecontroller, SDL_SENSOR_GYRO, SDL_TRUE); + } +} + +static void +UpdateWindowTitle() +{ + if (gamecontroller) { + const char *name = SDL_GameControllerName(gamecontroller); + const char *serial = SDL_GameControllerGetSerial(gamecontroller); + const char *basetitle = "Game Controller Test: "; + const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + (serial ? 3 + SDL_strlen(serial) : 0) + 1; + char *title = (char *)SDL_malloc(titlelen); + + retval = SDL_FALSE; + done = SDL_FALSE; + + if (title) { + SDL_snprintf(title, titlelen, "%s%s", basetitle, name); + if (serial) { + SDL_strlcat(title, " (", titlelen); + SDL_strlcat(title, serial, titlelen); + SDL_strlcat(title, ")", titlelen); + } + SDL_SetWindowTitle(window, title); + SDL_free(title); } - SDL_SetWindowTitle(window, title); - SDL_free(title); + } else { + SDL_SetWindowTitle(window, "Waiting for controller..."); } } @@ -146,10 +168,11 @@ loop(void *arg) if (!gamecontroller) { gamecontroller = SDL_GameControllerOpen(event.cdevice.which); if (gamecontroller) { - UpdateWindowTitle(); + InitGameController(); } else { SDL_Log("Couldn't open controller: %s\n", SDL_GetError()); } + UpdateWindowTitle(); } break; @@ -159,8 +182,9 @@ loop(void *arg) SDL_GameControllerClose(gamecontroller); gamecontroller = SDL_GameControllerOpen(0); if (gamecontroller) { - UpdateWindowTitle(); + InitGameController(); } + UpdateWindowTitle(); } break; @@ -178,6 +202,15 @@ loop(void *arg) event.ctouchpad.pressure); break; + case SDL_CONTROLLERSENSORUPDATE: + SDL_Log("Controller sensor %s: %.2f, %.2f, %.2f\n", + event.csensor.sensor == SDL_SENSOR_ACCEL ? "accelerometer" : + event.csensor.sensor == SDL_SENSOR_GYRO ? "gyro" : "unknown", + event.csensor.data[0], + event.csensor.data[1], + event.csensor.data[2]); + break; + case SDL_CONTROLLERAXISMOTION: SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)event.caxis.axis), event.caxis.value); break; @@ -414,6 +447,8 @@ main(int argc, char *argv[]) /* !!! FIXME: */ /*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/ + UpdateWindowTitle(); + /* Loop, getting controller events! */ #ifdef __EMSCRIPTEN__ emscripten_set_main_loop_arg(loop, NULL, 0, 1);