diff -r 8f3cdd9362e7 include/SDL_events.h --- a/include/SDL_events.h Wed Nov 11 20:09:56 2020 -0800 +++ b/include/SDL_events.h Thu Nov 12 13:15:38 2020 +0100 @@ -117,6 +117,7 @@ SDL_JOYBUTTONUP, /**< Joystick button released */ SDL_JOYDEVICEADDED, /**< A new joystick has been inserted into the system */ SDL_JOYDEVICEREMOVED, /**< An opened joystick has been removed */ + SDL_JOYSENSORUPDATE, /**< A sensor was updated */ /* Game controller events */ SDL_CONTROLLERAXISMOTION = 0x650, /**< Game controller axis motion */ @@ -500,10 +501,16 @@ */ typedef struct SDL_SensorEvent { - Uint32 type; /**< ::SDL_SENSORUPDATE */ + Uint32 type; /**< ::SDL_SENSORUPDATE or ::SDL_JOYSENSORUPDATE */ Uint32 timestamp; /**< In milliseconds, populated using SDL_GetTicks() */ - Sint32 which; /**< The instance ID of the sensor */ - float data[6]; /**< Up to 6 values from the sensor - additional values can be queried using SDL_SensorGetData() */ + Sint32 which; /**< The instance ID of the sensor if ::SDL_SENSORUPDATE + * or the instance ID of the joystick if ::SDL_JOYSENSORUPDATE + */ + float data[6]; /**< Up to 6 values from the sensor - additional values + * can be queried using SDL_SensorGetData(). + * For joystick data, the first 3 values are from gyro + * and the last 3 from accelerometer. + */ } SDL_SensorEvent; /** diff -r 8f3cdd9362e7 include/SDL_hints.h --- a/include/SDL_hints.h Wed Nov 11 20:09:56 2020 -0800 +++ b/include/SDL_hints.h Thu Nov 12 13:15:38 2020 +0100 @@ -571,6 +571,7 @@ /** * \brief A variable that lets you enable joystick (and gamecontroller) events even when your app is in the background. + * This does not disable joystick sensor event (see SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS). * * The variable can be set to the following values: * "0" - Disable joystick & gamecontroller input events when the @@ -583,6 +584,19 @@ #define SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS" /** + * \brief A variable that lets you enable joystick sensor (and gamecontroller) events even when your app is in the background. + * + * The variable can be set to the following values: + * "0" - Disable joystick & gamecontroller sensor events when the + * application is in the background. + * "1" - Enable joystick & gamecontroller sensor events when the + * application is in the background. + * + * The default value is "1". This hint may be set at any time. + */ +#define SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_SENSOR_EVENTS "SDL_JOYSTICK_ALLOW_BACKGROUND_SENSOR_EVENTS" + +/** * \brief A variable controlling whether the HIDAPI joystick drivers should be used. * * This variable can be set to the following values: @@ -1300,7 +1314,7 @@ * This variable can be set to the following values: * * "0" - Don't log any events (default) - * "1" - Log all events except mouse and finger motion, which are pretty spammy. + * "1" - Log all events except mouse/finger motion and sensor, which are pretty spammy. * "2" - Log all events. * * This is generally meant to be used to debug SDL itself, but can be useful diff -r 8f3cdd9362e7 include/SDL_joystick.h --- a/include/SDL_joystick.h Wed Nov 11 20:09:56 2020 -0800 +++ b/include/SDL_joystick.h Thu Nov 12 13:15:38 2020 +0100 @@ -325,6 +325,21 @@ extern DECLSPEC int SDLCALL SDL_JoystickNumButtons(SDL_Joystick * joystick); /** + * Return SDL_TRUE if joystick has a gyroscope or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_JoystickHasGyroscope(SDL_Joystick * joystick); + +/** + * Return SDL_TRUE if joystick has an accelerometer or -1 on error. + */ +extern DECLSPEC int SDLCALL SDL_JoystickHasAccelerometer(SDL_Joystick * joystick); + +/** + * Return the rate, in hertz, at which sensors will send SDL_JOYSENSORUPDATE event. + */ +extern DECLSPEC float SDLCALL SDL_JoystickGetSensorsRate(SDL_Joystick * joystick); + +/** * Update the current state of the open joysticks. * * This is called automatically by the event loop if any joystick @@ -420,6 +435,42 @@ int button); /** + * Get the current state of the joystick's gyroscope. + * _______ Y (out of screen) + * /+ ::\ O---> X + * / 0___0 \ | + * |_/ \_| v Z + * + * \param joystick The joystick from which to retrieve gyroscope data + * \param gyro_xyz Pointer to an array of 3 floats that is filled with the joystick's angular velocity in radians per second. + * accel_xyz[0]: Angular speed around the x axis + * accel_xyz[1]: Angular speed around the y axis + * accel_xyz[2]: Angular speed around the z axis + * + * \return 0, or -1 if this joystick does not provide an gyroscope + * + */ +extern DECLSPEC int SDLCALL SDL_JoystickGetGyroscope(SDL_Joystick * joystick, float *gyro_xyz); + +/** + * Get the current state of the joystick's accelerometer, including the force of gravity. + * _______ Y (out of screen) + * /+ ::\ O---> X + * / 0___0 \ | + * |_/ \_| v Z + * + * \param joystick The joystick from which to retrieve accelerometer data + * \param accel_xyz A pointer to an array of 3 floats that is filled with the joystick's acceleration, in m/s² + * accel_xyz[0]: Acceleration on the x axis + * accel_xyz[1]: Acceleration on the y axis + * accel_xyz[2]: Acceleration on the z axis + * + * \return 0, or -1 if this joystick does not provide an accelerometer + * + */ +extern DECLSPEC int SDLCALL SDL_JoystickGetAccelerometer(SDL_Joystick * joystick, float *accel_xyz); + +/** * Start a rumble effect * Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling. * diff -r 8f3cdd9362e7 include/SDL_touch.h --- a/include/SDL_touch.h Wed Nov 11 20:09:56 2020 -0800 +++ b/include/SDL_touch.h Thu Nov 12 13:15:38 2020 +0100 @@ -40,7 +40,10 @@ typedef Sint64 SDL_TouchID; typedef Sint64 SDL_FingerID; - +/** Phone screens are an example of a direct device type. Mac trackpads are the + * indirect-absolute touch device type. The Apple TV remote is an + * indirect-relative touch device type. + */ typedef enum { SDL_TOUCH_DEVICE_INVALID = -1, diff -r 8f3cdd9362e7 src/events/SDL_events.c --- a/src/events/SDL_events.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/events/SDL_events.c Thu Nov 12 13:15:38 2020 +0100 @@ -92,7 +92,7 @@ } SDL_EventQ = { NULL, { 1 }, { 0 }, 0, NULL, NULL, NULL, NULL, NULL }; -/* 0 (default) means no logging, 1 means logging, 2 means logging with mouse and finger motion */ +/* 0 (default) means no logging, 1 means logging, 2 means logging with sensor, mouse and finger motion */ static int SDL_DoEventLogging = 0; static void SDLCALL @@ -107,9 +107,11 @@ char name[32]; char details[128]; - /* mouse/finger motion are spammy, ignore these if they aren't demanded. */ + /* mouse/finger motion and sensor are spammy, ignore these if they aren't demanded. */ if ( (SDL_DoEventLogging < 2) && ( (event->type == SDL_MOUSEMOTION) || + (event->type == SDL_SENSORUPDATE) || + (event->type == SDL_JOYSENSORUPDATE) || (event->type == SDL_FINGERMOTION) ) ) { return; } @@ -251,6 +253,19 @@ (uint) event->jhat.hat, (uint) event->jhat.value); break; + SDL_EVENT_CASE(SDL_JOYSENSORUPDATE) + SDL_snprintf(details, sizeof (details), " (timestamp=%u which=%d data=%f %f %f %f %f %f)", + (uint) event->sensor.timestamp, (int) event->sensor.which, + event->sensor.data[0], event->sensor.data[1], event->sensor.data[2], + event->sensor.data[3], event->sensor.data[4], event->sensor.data[5]); + break; + + SDL_EVENT_CASE(SDL_SENSORUPDATE) + SDL_snprintf(details, sizeof (details), " (timestamp=%u which=%d data=%f %f %f)", + (uint) event->sensor.timestamp, (int) event->sensor.which, + event->sensor.data[0], event->sensor.data[1], event->sensor.data[2]); + break; + #define PRINT_JBUTTON_EVENT(event) \ SDL_snprintf(details, sizeof (details), " (timestamp=%u which=%d button=%u state=%s)", \ (uint) event->jbutton.timestamp, (int) event->jbutton.which, \ diff -r 8f3cdd9362e7 src/events/SDL_touch.c --- a/src/events/SDL_touch.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/events/SDL_touch.c Thu Nov 12 13:15:38 2020 +0100 @@ -27,7 +27,7 @@ #include "SDL_events_c.h" #include "../video/SDL_sysvideo.h" - +static SDL_atomic_t touch_init_ref_count; static int SDL_num_touch = 0; static SDL_Touch **SDL_touchDevices = NULL; @@ -45,6 +45,7 @@ int SDL_TouchInit(void) { + SDL_AtomicIncRef(&touch_init_ref_count); return (0); } @@ -464,16 +465,18 @@ void SDL_TouchQuit(void) { - int i; + if (SDL_AtomicDecRef(&touch_init_ref_count)) { + int i; + for (i = SDL_num_touch; i--; ) { + SDL_DelTouch(SDL_touchDevices[i]->id); + } + SDL_assert(SDL_num_touch == 0); - for (i = SDL_num_touch; i--; ) { - SDL_DelTouch(SDL_touchDevices[i]->id); + SDL_free(SDL_touchDevices); + SDL_touchDevices = NULL; + SDL_GestureQuit(); } - SDL_assert(SDL_num_touch == 0); - - SDL_free(SDL_touchDevices); - SDL_touchDevices = NULL; - SDL_GestureQuit(); + } /* vi: set ts=4 sw=4 expandtab: */ diff -r 8f3cdd9362e7 src/joystick/SDL_joystick.c --- a/src/joystick/SDL_joystick.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/SDL_joystick.c Thu Nov 12 13:15:38 2020 +0100 @@ -93,6 +93,7 @@ #endif }; static SDL_bool SDL_joystick_allows_background_events = SDL_FALSE; +static SDL_bool SDL_joystick_allows_background_sensor_events = SDL_TRUE; static SDL_Joystick *SDL_joysticks = NULL; static SDL_bool SDL_updating_joystick = SDL_FALSE; static SDL_mutex *SDL_joystick_lock = NULL; /* This needs to support recursive locks */ @@ -211,6 +212,16 @@ } } +static void SDLCALL +SDL_JoystickAllowBackgroundSensorEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) +{ + if (hint && *hint == '0') { /* default to true */ + SDL_joystick_allows_background_sensor_events = SDL_FALSE; + } else { + SDL_joystick_allows_background_sensor_events = SDL_TRUE; + } +} + int SDL_JoystickInit(void) { @@ -226,6 +237,8 @@ /* See if we should allow joystick events while in the background */ SDL_AddHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, SDL_JoystickAllowBackgroundEventsChanged, NULL); + SDL_AddHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_SENSOR_EVENTS, + SDL_JoystickAllowBackgroundSensorEventsChanged, NULL); #if !SDL_EVENTS_DISABLED if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0) { @@ -239,6 +252,7 @@ status = 0; } } + return status; } @@ -647,6 +661,45 @@ return joystick->nbuttons; } +int +SDL_JoystickHasAccelerometer(SDL_Joystick* joystick) +{ + if (!SDL_PrivateJoystickValid(joystick)) { + return -1; + } + return joystick->has_accelerometer; +} + +int +SDL_JoystickHasGyroscope(SDL_Joystick* joystick) +{ + if (!SDL_PrivateJoystickValid(joystick)) { + return -1; + } + return joystick->has_gyro; +} + +float +SDL_JoystickGetSensorsRate(SDL_Joystick * joystick) +{ + float result = 0.f; + + if (!SDL_PrivateJoystickValid(joystick)) { + return 0.f; + } + + SDL_LockJoysticks(); + + if (joystick->driver->GetSensorRate) { + result = joystick->driver->GetSensorRate(joystick); + } + + SDL_UnlockJoysticks(); + + return result; +} + + /* * Get the current state of an axis control on a joystick */ @@ -754,6 +807,36 @@ return state; } +int +SDL_JoystickGetAccelerometer(SDL_Joystick * joystick, float *xyz) +{ + if (!SDL_PrivateJoystickValid(joystick)) { + return -1; + } + + if (!joystick->has_accelerometer) { + return SDL_SetError("Joystick does not have an accelerometer"); + } + + if (xyz) {SDL_memcpy(xyz, joystick->accel, 3 * sizeof(float));} + return 0; +} + +int +SDL_JoystickGetGyroscope(SDL_Joystick * joystick, float *xyz) +{ + if (!SDL_PrivateJoystickValid(joystick)) { + return -1; + } + + if (!joystick->has_gyro) { + return SDL_SetError("Joystick does not have an accelerometer"); + } + + if (xyz) {SDL_memcpy(xyz, joystick->gyro, 3 * sizeof(float));} + return 0; +} + /* * Return if the joystick in question is currently attached to the system, * \return SDL_FALSE if not plugged in, SDL_TRUE if still present. @@ -1084,6 +1167,8 @@ SDL_DelHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, SDL_JoystickAllowBackgroundEventsChanged, NULL); + SDL_DelHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_SENSOR_EVENTS, + SDL_JoystickAllowBackgroundSensorEventsChanged, NULL); if (SDL_joystick_lock) { SDL_mutex *mutex = SDL_joystick_lock; @@ -1109,6 +1194,20 @@ return SDL_FALSE; } +static SDL_bool +SDL_PrivateJoystickShouldIgnoreSensorEvent() +{ + if (SDL_joystick_allows_background_sensor_events) { + return SDL_FALSE; + } + + if (SDL_HasWindows() && SDL_GetKeyboardFocus() == NULL) { + /* We have windows but we don't have focus, ignore the event. */ + return SDL_TRUE; + } + return SDL_FALSE; +} + /* These are global for SDL_sysjoystick.c and SDL_events.c */ void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance) @@ -1196,6 +1295,8 @@ for (i = 0; i < joystick->nhats; i++) { SDL_PrivateJoystickHat(joystick, i, SDL_HAT_CENTERED); } + + SDL_PrivateJoystickSensors(joystick, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f); } void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance) @@ -1425,6 +1526,55 @@ return posted; } +int +SDL_PrivateJoystickSensors(SDL_Joystick * joystick, + float gyro_x, float gyro_y, float gyro_z, + float accel_x, float accel_y, float accel_z) +{ + int posted; + + /* Make sure we're not getting garbage events */ + if (!(joystick->has_accelerometer || joystick->has_gyro)) { + return 0; + } + + /* Set acceleration and rotation to zero if we don't have keyboard focus. */ + if (SDL_PrivateJoystickShouldIgnoreSensorEvent()) { + joystick->accel[0] = 0.f; + joystick->accel[1] = 0.f; + joystick->accel[2] = 0.f; + joystick->gyro[0] = 0.f; + joystick->gyro[1] = 0.f; + joystick->gyro[2] = 0.f; + return 0; + } + + /* Send sensor values even if they are identical too previous ones. Device + * sends data at a fixed rate (as reported by SDL_GetJoystickSensorRate()) + * and user is expecting this fixed rate for integration. */ + + joystick->accel[0] = accel_x; + joystick->accel[1] = accel_y; + joystick->accel[2] = accel_z; + joystick->gyro[0] = gyro_x; + joystick->gyro[1] = gyro_y; + joystick->gyro[2] = gyro_z; + + /* Post the event, if desired */ + posted = 0; +#if !SDL_EVENTS_DISABLED + if (SDL_GetEventState(SDL_JOYSENSORUPDATE) == SDL_ENABLE) { + SDL_Event event; + event.sensor.type = SDL_JOYSENSORUPDATE; + event.sensor.which = joystick->instance_id; + SDL_memcpy(&event.sensor.data[0], joystick->gyro, 3 * sizeof(float)); + SDL_memcpy(&event.sensor.data[3], joystick->accel, 3 * sizeof(float)); + posted = SDL_PushEvent(&event) == 1; + } +#endif /* !SDL_EVENTS_DISABLED */ + return posted; +} + void SDL_JoystickUpdate(void) { @@ -1516,7 +1666,8 @@ #else const Uint32 event_list[] = { SDL_JOYAXISMOTION, SDL_JOYBALLMOTION, SDL_JOYHATMOTION, - SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED + SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED, + SDL_JOYSENSORUPDATE }; unsigned int i; diff -r 8f3cdd9362e7 src/joystick/SDL_joystick_c.h --- a/src/joystick/SDL_joystick_c.h Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/SDL_joystick_c.h Thu Nov 12 13:15:38 2020 +0100 @@ -112,6 +112,9 @@ extern void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance); extern int SDL_PrivateJoystickAxis(SDL_Joystick * joystick, Uint8 axis, Sint16 value); +extern int SDL_PrivateJoystickSensors(SDL_Joystick * joystick, + float gyro_x, float gyro_y, float gyro_z, + float accel_x, float accel_y, float accel_z); extern int SDL_PrivateJoystickBall(SDL_Joystick * joystick, Uint8 ball, Sint16 xrel, Sint16 yrel); extern int SDL_PrivateJoystickHat(SDL_Joystick * joystick, diff -r 8f3cdd9362e7 src/joystick/SDL_sysjoystick.h --- a/src/joystick/SDL_sysjoystick.h Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/SDL_sysjoystick.h Thu Nov 12 13:15:38 2020 +0100 @@ -60,6 +60,11 @@ int nbuttons; /* Number of buttons on the joystick */ Uint8 *buttons; /* Current button states */ + SDL_bool has_gyro; + SDL_bool has_accelerometer; + float gyro[3]; /* rad/s */ + float accel[3]; /* m/s² */ + Uint16 low_frequency_rumble; Uint16 high_frequency_rumble; Uint32 rumble_expiration; @@ -136,6 +141,9 @@ SDL_bool (*HasLED)(SDL_Joystick * joystick); int (*SetLED)(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue); + /* Implement SDL_JoystickGetSensorRate() for this driver */ + float (*GetSensorRate)(SDL_Joystick * joystick); + /* 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 -r 8f3cdd9362e7 src/joystick/android/SDL_sysjoystick.c --- a/src/joystick/android/SDL_sysjoystick.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/android/SDL_sysjoystick.c Thu Nov 12 13:15:38 2020 +0100 @@ -651,6 +651,12 @@ return SDL_Unsupported(); } +static float +ANDROID_JoystickGetSensorRate(SDL_Joystick * joystick) +{ + return 0.f; +} + static void ANDROID_JoystickUpdate(SDL_Joystick * joystick) { @@ -732,6 +738,7 @@ ANDROID_JoystickRumbleTriggers, ANDROID_JoystickHasLED, ANDROID_JoystickSetLED, + ANDROID_JoystickGetSensorRate, ANDROID_JoystickUpdate, ANDROID_JoystickClose, ANDROID_JoystickQuit, diff -r 8f3cdd9362e7 src/joystick/bsd/SDL_sysjoystick.c --- a/src/joystick/bsd/SDL_sysjoystick.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/bsd/SDL_sysjoystick.c Thu Nov 12 13:15:38 2020 +0100 @@ -786,6 +786,12 @@ return SDL_Unsupported(); } +static float +BSD_JoystickGetSensorRate(SDL_Joystick * joystick) +{ + return 0.f; +} + SDL_JoystickDriver SDL_BSD_JoystickDriver = { BSD_JoystickInit, @@ -801,6 +807,7 @@ BSD_JoystickRumbleTriggers, BSD_JoystickHasLED, BSD_JoystickSetLED, + BSD_JoystickGetSensorRate, BSD_JoystickUpdate, BSD_JoystickClose, BSD_JoystickQuit, diff -r 8f3cdd9362e7 src/joystick/darwin/SDL_sysjoystick.c --- a/src/joystick/darwin/SDL_sysjoystick.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/darwin/SDL_sysjoystick.c Thu Nov 12 13:15:38 2020 +0100 @@ -940,6 +940,12 @@ return SDL_Unsupported(); } +static float +DARWIN_JoystickGetSensorRate(SDL_Joystick * joystick) +{ + return 0.f; +} + static void DARWIN_JoystickUpdate(SDL_Joystick * joystick) { @@ -1090,6 +1096,7 @@ DARWIN_JoystickRumbleTriggers, DARWIN_JoystickHasLED, DARWIN_JoystickSetLED, + DARWIN_JoystickGetSensorRate, DARWIN_JoystickUpdate, DARWIN_JoystickClose, DARWIN_JoystickQuit, diff -r 8f3cdd9362e7 src/joystick/dummy/SDL_sysjoystick.c --- a/src/joystick/dummy/SDL_sysjoystick.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/dummy/SDL_sysjoystick.c Thu Nov 12 13:15:38 2020 +0100 @@ -107,6 +107,12 @@ return SDL_Unsupported(); } +static float +DUMMY_JoystickGetSensorRate(SDL_Joystick * joystick) +{ + return 0.f; +} + static void DUMMY_JoystickUpdate(SDL_Joystick * joystick) { @@ -143,6 +149,7 @@ DUMMY_JoystickRumbleTriggers, DUMMY_JoystickHasLED, DUMMY_JoystickSetLED, + DUMMY_JoystickGetSensorRate, DUMMY_JoystickUpdate, DUMMY_JoystickClose, DUMMY_JoystickQuit, diff -r 8f3cdd9362e7 src/joystick/emscripten/SDL_sysjoystick.c --- a/src/joystick/emscripten/SDL_sysjoystick.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/emscripten/SDL_sysjoystick.c Thu Nov 12 13:15:38 2020 +0100 @@ -427,6 +427,12 @@ return SDL_Unsupported(); } +static float +EMSCRIPTEM_JoystickGetSensorRate(SDL_Joystick * joystick) +{ + return 0.f; +} + SDL_JoystickDriver SDL_EMSCRIPTEN_JoystickDriver = { EMSCRIPTEN_JoystickInit, @@ -442,6 +448,7 @@ EMSCRIPTEN_JoystickRumbleTriggers, EMSCRIPTEN_JoystickHasLED, EMSCRIPTEN_JoystickSetLED, + EMSCRIPTEM_JoystickGetSensorRate, EMSCRIPTEN_JoystickUpdate, EMSCRIPTEN_JoystickClose, EMSCRIPTEN_JoystickQuit, diff -r 8f3cdd9362e7 src/joystick/haiku/SDL_haikujoystick.cc --- a/src/joystick/haiku/SDL_haikujoystick.cc Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/haiku/SDL_haikujoystick.cc Thu Nov 12 13:15:38 2020 +0100 @@ -281,6 +281,11 @@ return SDL_Unsupported(); } + static float HAIKU_JoystickGetSensorRate(SDL_Joystick * joystick) + { + return 0.f; + } + SDL_JoystickDriver SDL_HAIKU_JoystickDriver = { HAIKU_JoystickInit, @@ -296,6 +301,7 @@ HAIKU_JoystickRumbleTriggers, HAIKU_JoystickHasLED, HAIKU_JoystickSetLED, + HAIKU_JoystickGetSensorRate, HAIKU_JoystickUpdate, HAIKU_JoystickClose, HAIKU_JoystickQuit, diff -r 8f3cdd9362e7 src/joystick/hidapi/SDL_hidapi_gamecube.c --- a/src/joystick/hidapi/SDL_hidapi_gamecube.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/hidapi/SDL_hidapi_gamecube.c Thu Nov 12 13:15:38 2020 +0100 @@ -381,6 +381,12 @@ return SDL_Unsupported(); } +static float +HIDAPI_DriverGameCube_GetSensrorRate(SDL_HIDAPI_Device* dev) +{ + return 0.f; +} + static void HIDAPI_DriverGameCube_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) { @@ -423,6 +429,7 @@ HIDAPI_DriverGameCube_RumbleJoystickTriggers, HIDAPI_DriverGameCube_HasJoystickLED, HIDAPI_DriverGameCube_SetJoystickLED, + HIDAPI_DriverGameCube_GetSensrorRate, HIDAPI_DriverGameCube_CloseJoystick, HIDAPI_DriverGameCube_FreeDevice, NULL, diff -r 8f3cdd9362e7 src/joystick/hidapi/SDL_hidapi_ps4.c --- a/src/joystick/hidapi/SDL_hidapi_ps4.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/hidapi/SDL_hidapi_ps4.c Thu Nov 12 13:15:38 2020 +0100 @@ -26,11 +26,14 @@ #ifdef SDL_JOYSTICK_HIDAPI #include "SDL_hints.h" +#include "SDL_platform.h" #include "SDL_events.h" +#include "SDL_endian.h" #include "SDL_timer.h" #include "SDL_joystick.h" #include "SDL_gamecontroller.h" #include "../SDL_sysjoystick.h" +#include "../../events/SDL_touch_c.h" #include "SDL_hidapijoystick_c.h" #include "SDL_hidapi_rumble.h" @@ -61,6 +64,9 @@ k_ePS4FeatureReportIdSerialNumber = 0x12, } EPS4FeatureReportID; +/* We cannot use only joystick instance id as touch id because it may collide with other touch id */ +#define MAKE_TOUCH_ID(joy_instance_id) (((Sint64)USB_PRODUCT_SONY_DS4 << 32) + (Sint64)joy_instance_id) + typedef struct { Uint8 ucLeftJoystickX; @@ -79,11 +85,13 @@ Sint16 sAccelZ; Uint8 _rgucPad1[ 5 ]; Uint8 ucBatteryLevel; - Uint8 _rgucPad2[ 4 ]; - Uint8 ucTrackpadCounter1; - Uint8 rgucTrackpadData1[ 3 ]; - Uint8 ucTrackpadCounter2; - Uint8 rgucTrackpadData2[ 3 ]; + Uint8 _rgucPad2[ 2 ]; + Uint8 n_trackpad_packet; + Uint8 packet_counter_1; + struct { + Uint8 id; + Uint8 rgucTrackpadData[ 3 ]; + } finger[2]; } PS4StatePacket_t; typedef struct @@ -102,7 +110,46 @@ Uint8 ucVolumeSpeaker; } DS4EffectsState_t; +typedef struct +{ + Sint16 gyro_offset_x; + Sint16 gyro_offset_y; + Sint16 gyro_offset_z; + union { + struct { + Sint16 max_x; + Sint16 min_x; + Sint16 max_y; + Sint16 min_y; + Sint16 max_z; + Sint16 min_z; + } usb; + struct { + Sint16 max_x; + Sint16 max_y; + Sint16 max_z; + Sint16 min_x; + Sint16 min_y; + Sint16 min_z; + } bt; + } gyro; + Sint16 gyro_k1; + Sint16 gyro_k2; + Sint16 accel_max_x; + Sint16 accel_min_x; + Sint16 accel_max_y; + Sint16 accel_min_y; + Sint16 accel_max_z; + Sint16 accel_min_z; +} DS4CalibrationData_t; + typedef struct { + Sint16 gyro_offset[3]; + float gyro_k[3]; + + Sint16 accel_offset[3]; + float accel_k[3]; + SDL_bool is_dongle; SDL_bool is_bluetooth; SDL_bool audio_supported; @@ -196,6 +243,111 @@ return SDL_TRUE; } +static SDL_bool CalibrateSensors(SDL_HIDAPI_Device *device) +{ + int retry = 0; + float res; + Sint32 range; + int n; + + SDL_DriverPS4_Context* ctx = device->context; + + DS4CalibrationData_t c; + int data_offset = ctx->is_bluetooth ? 2 : 1; + Uint8 data[USB_PACKET_LENGTH + 1]; + + SDL_memset(data, 0, sizeof(data)); + if (ctx->is_bluetooth) { + data[0] = k_ePS4FeatureReportIdGyroCalibration_BT; + } else { + data[0] = k_ePS4FeatureReportIdGyroCalibration_USB; + } + + while (1) { + n = hid_get_feature_report(device->dev, data, sizeof(data)); + if (n < sizeof(c) + 1) { + SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "HIDAPI PS4: couldn't get calibration report."); + return SDL_FALSE; + } + + if (ctx->is_bluetooth) { + Uint32 ubHdr = 0xA3; + Uint32 crc_received, crc_calculated; + SDL_memcpy(&crc_received, &data[n - sizeof(crc_received)], sizeof(crc_received)); + + crc_calculated = crc32(0, &ubHdr, 1); + crc_calculated = crc32(crc_calculated, &data[1], n - 1 - sizeof(crc_received)); + + if (crc_calculated != crc_received) { + if (retry < 5) { + ++retry; + continue; + } + SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "HIDAPI PS4: calibration crc mismatch."); + return SDL_FALSE; + } + } + break; + } +#ifdef DEBUG_PS4 + { + SDL_Log("---- calibration data %d", n); + for (int i = 0; i < n; ++i) { + if (i < data_offset || i >= data_offset + sizeof(c)) { + SDL_Log("0x%02x", data[i]); + } else { + SDL_Log("%5d", (Sint16)(data[i + 1] << 8 | data[i])); + ++i; + } + } + SDL_Log("----"); + } +#endif + + SDL_memcpy(&c, &data[data_offset], sizeof(c)); + + ctx->gyro_offset[0] = SDL_SwapLE16(c.gyro_offset_x); + ctx->gyro_offset[1] = SDL_SwapLE16(c.gyro_offset_y); + ctx->gyro_offset[2] = SDL_SwapLE16(c.gyro_offset_z); + res = (float)((Sint32)SDL_SwapLE16(c.gyro_k1) + (Sint32)SDL_SwapLE16(c.gyro_k2)) * M_PI / 180.f; + if (ctx->is_bluetooth) { + ctx->gyro_k[0] = res / (float)((Sint32)SDL_SwapLE16(c.gyro.bt.max_x) - (Sint32)SDL_SwapLE16(c.gyro.bt.min_x)); + ctx->gyro_k[1] = res / (float)((Sint32)SDL_SwapLE16(c.gyro.bt.max_y) - (Sint32)SDL_SwapLE16(c.gyro.bt.min_y)); + ctx->gyro_k[2] = res / (float)((Sint32)SDL_SwapLE16(c.gyro.bt.max_z) - (Sint32)SDL_SwapLE16(c.gyro.bt.min_z)); + } else { + ctx->gyro_k[0] = res / (float)((Sint32)SDL_SwapLE16(c.gyro.usb.max_x) - (Sint32)SDL_SwapLE16(c.gyro.usb.min_x)); + ctx->gyro_k[1] = res / (float)((Sint32)SDL_SwapLE16(c.gyro.usb.max_y) - (Sint32)SDL_SwapLE16(c.gyro.usb.min_y)); + ctx->gyro_k[2] = res / (float)((Sint32)SDL_SwapLE16(c.gyro.usb.max_z) - (Sint32)SDL_SwapLE16(c.gyro.usb.min_z)); + } + + range = (Sint32)SDL_SwapLE16(c.accel_max_x) - (Sint32)SDL_SwapLE16(c.accel_min_x); + ctx->accel_offset[0] = (Sint32)SDL_SwapLE16(c.accel_max_x) - range / 2; + ctx->accel_k[0] = 9.80665f * 2.f / (float)range; + + range = (Sint32)SDL_SwapLE16(c.accel_max_y) - (Sint32)SDL_SwapLE16(c.accel_min_y); + ctx->accel_offset[1] = (Sint32)SDL_SwapLE16(c.accel_max_y) - range / 2; + ctx->accel_k[1] = 9.80665f * 2.f / (float)range; + + range = (Sint32)SDL_SwapLE16(c.accel_max_z) - (Sint32)SDL_SwapLE16(c.accel_min_z); + ctx->accel_offset[2] = (Sint32)SDL_SwapLE16(c.accel_max_z) - range / 2; + ctx->accel_k[2] = 9.80665f * 2.f / (float)range; + +#ifdef DEBUG_PS4 + SDL_Log("gyro offset: %d %d %d k: %f %f %f (%f)", + ctx->gyro_offset[0], ctx->gyro_offset[1], ctx->gyro_offset[2], + ctx->gyro_k[0], ctx->gyro_k[1], ctx->gyro_k[2], 34.906585f / 32767.f); + SDL_Log("accel offset: %d %d %d k: %f %f %f (%f)", + ctx->accel_offset[0], ctx->accel_offset[1], ctx->accel_offset[2], + ctx->accel_k[0], ctx->accel_k[1], ctx->accel_k[2], 9.80665f / 8192.f); +#endif + return SDL_TRUE; +} + +static float HIDAPI_DriverPS4_GetSensorRate(SDL_HIDAPI_Device* dev) +{ + return 250.f; +} + static void SetLedsForPlayerIndex(DS4EffectsState_t *effects, int player_index) { @@ -292,13 +444,43 @@ } if (HIDAPI_DriverPS4_CanRumble(device->vendor_id, device->product_id)) { - if (ctx->is_bluetooth) { +#ifdef __WINDOWS__ + if (ctx->is_bluetooth) { ctx->rumble_supported = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, SDL_FALSE); } else { ctx->rumble_supported = SDL_TRUE; } +#else + ctx->rumble_supported = SDL_TRUE; +#endif } + if (!CalibrateSensors(device)) { + ctx->gyro_offset[0] = 0; + ctx->gyro_offset[1] = 0; + ctx->gyro_offset[2] = 0; + ctx->accel_offset[0] = 0; + ctx->accel_offset[1] = 0; + ctx->accel_offset[2] = 0; + /* 32767 <=> 34.906585rad/s (2000°/s) */ + ctx->gyro_k[0] = 34.906585f / 32767.f; + ctx->gyro_k[1] = 34.906585f / 32767.f; + ctx->gyro_k[2] = 34.906585f / 32767.f; + /* 32767 <=> 4g */ + ctx->accel_k[0] = 4.f * 9.80665f / 32767.f; + ctx->accel_k[1] = 4.f * 9.80665f / 32767.f; + ctx->accel_k[2] = 4.f * 9.80665f / 32767.f; + } + + if (-1 == SDL_AddTouch(MAKE_TOUCH_ID(joystick->instance_id), + SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE, + "Dualshock 4 trackpad")) { + return SDL_FALSE; + } + /* Initialize finger state to up */ + ctx->last_state.finger[0].id = 0x80; /* finger pressed bit is active low */ + ctx->last_state.finger[1].id = 0x80; + /* Initialize player index (needed for setting LEDs) */ ctx->player_index = SDL_JoystickGetPlayerIndex(joystick); @@ -308,6 +490,9 @@ /* Initialize the joystick capabilities */ joystick->nbuttons = 16; joystick->naxes = SDL_CONTROLLER_AXIS_MAX; + /* TODO: Some third-party controllers don't have a gyro, find a way to check for that. */ + joystick->has_accelerometer = SDL_TRUE; + joystick->has_gyro = SDL_TRUE; joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; return SDL_TRUE; @@ -403,6 +588,7 @@ HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet) { Sint16 axis; + int i; if (ctx->last_state.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) { { @@ -470,14 +656,14 @@ SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data & 0x80) ? SDL_PRESSED : SDL_RELEASED); } - /* Some fightsticks, ex: Victrix FS Pro will only this these digital trigger bits and not the analog values so this needs to run whenever the - trigger is evaluated - */ - if ((packet->rgucButtonsHatAndCounter[1] & 0x0C) != 0) { - Uint8 data = packet->rgucButtonsHatAndCounter[1]; - packet->ucTriggerLeft = (data & 0x04) && packet->ucTriggerLeft == 0 ? 255 : packet->ucTriggerLeft; - packet->ucTriggerRight = (data & 0x08) && packet->ucTriggerRight == 0 ? 255 : packet->ucTriggerRight; - } + /* Some fightsticks, ex: Victrix FS Pro will only this these digital trigger bits and not the analog values so this needs to run whenever the + trigger is evaluated + */ + if ((packet->rgucButtonsHatAndCounter[1] & 0x0C) != 0) { + Uint8 data = packet->rgucButtonsHatAndCounter[1]; + packet->ucTriggerLeft = (data & 0x04) && packet->ucTriggerLeft == 0 ? 255 : packet->ucTriggerLeft; + packet->ucTriggerRight = (data & 0x08) && packet->ucTriggerRight == 0 ? 255 : packet->ucTriggerRight; + } if (ctx->last_state.rgucButtonsHatAndCounter[2] != packet->rgucButtonsHatAndCounter[2]) { Uint8 data = (packet->rgucButtonsHatAndCounter[2] & 0x03); @@ -499,6 +685,34 @@ axis = ((int)packet->ucRightJoystickY * 257) - 32768; SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis); + { + float gyro_x = (float)((Sint32)SDL_SwapLE16(packet->sGyroX) - (Sint32)ctx->gyro_offset[0]) * ctx->gyro_k[0]; + float gyro_y = (float)((Sint32)SDL_SwapLE16(packet->sGyroY) - (Sint32)ctx->gyro_offset[1]) * ctx->gyro_k[1]; + float gyro_z = (float)((Sint32)SDL_SwapLE16(packet->sGyroZ) - (Sint32)ctx->gyro_offset[2]) * ctx->gyro_k[2]; + + float accel_x = (float)((Sint32)SDL_SwapLE16(packet->sAccelX) - (Sint32)ctx->accel_offset[0]) * ctx->accel_k[0]; + float accel_y = (float)((Sint32)SDL_SwapLE16(packet->sAccelY) - (Sint32)ctx->accel_offset[1]) * ctx->accel_k[1]; + float accel_z = (float)((Sint32)SDL_SwapLE16(packet->sAccelZ) - (Sint32)ctx->accel_offset[2]) * ctx->accel_k[2]; + + SDL_PrivateJoystickSensors(joystick, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); + } + + for (i = 0; i < 2; ++i) { + SDL_bool finger_is_down = !(packet->finger[i].id & 0x80); + SDL_bool finger_was_down = !(ctx->last_state.finger[i].id & 0x80); + if (finger_is_down || finger_was_down) { + float x = (float)SDL_SwapLE32( packet->finger[i].rgucTrackpadData[0] + + (((Uint32)packet->finger[i].rgucTrackpadData[1] & 0x0F) << 8)) / 1920.f; + float y = (float)SDL_SwapLE32((packet->finger[i].rgucTrackpadData[1] >> 4) + + ( (Uint32)packet->finger[i].rgucTrackpadData[2] << 4)) / 943.f; + if (finger_was_down && finger_is_down) { + SDL_SendTouchMotion(MAKE_TOUCH_ID(joystick->instance_id), i, NULL, x, y, 1.f); + } else { + SDL_SendTouch(MAKE_TOUCH_ID(joystick->instance_id), i, NULL, finger_is_down, x, y, 1.f); + } + } + } + if (packet->ucBatteryLevel & 0x10) { joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; } else { @@ -523,6 +737,7 @@ { SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; SDL_Joystick *joystick = NULL; + PS4StatePacket_t state; Uint8 data[USB_PACKET_LENGTH]; int size; @@ -536,7 +751,8 @@ while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { switch (data[0]) { case k_EPS4ReportIdUsbState: - HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[1]); + SDL_memcpy(&state, &data[1], sizeof(state)); + HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, &state); break; case k_EPS4ReportIdBluetoothState1: case k_EPS4ReportIdBluetoothState2: @@ -549,7 +765,8 @@ case k_EPS4ReportIdBluetoothState9: /* Bluetooth state packets have two additional bytes at the beginning, the first notes if HID is present */ if (data[1] & 0x80) { - HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t*)&data[3]); + SDL_memcpy(&state, &data[3], sizeof(state)); + HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, &state); } break; default: @@ -570,6 +787,8 @@ static void HIDAPI_DriverPS4_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) { + SDL_DelTouch(MAKE_TOUCH_ID(joystick->instance_id)); + hid_close(device->dev); device->dev = NULL; @@ -597,6 +816,7 @@ HIDAPI_DriverPS4_RumbleJoystickTriggers, HIDAPI_DriverPS4_HasJoystickLED, HIDAPI_DriverPS4_SetJoystickLED, + HIDAPI_DriverPS4_GetSensorRate, HIDAPI_DriverPS4_CloseJoystick, HIDAPI_DriverPS4_FreeDevice, NULL diff -r 8f3cdd9362e7 src/joystick/hidapi/SDL_hidapi_ps5.c --- a/src/joystick/hidapi/SDL_hidapi_ps5.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/hidapi/SDL_hidapi_ps5.c Thu Nov 12 13:15:38 2020 +0100 @@ -150,6 +150,12 @@ return SDL_Unsupported(); } +static float +HIDAPI_DriverPS5_GetSensrorRate(SDL_HIDAPI_Device* dev) +{ + return 0.f; +} + static void HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS5_Context *ctx, PS5SimpleStatePacket_t *packet) { @@ -410,6 +416,7 @@ HIDAPI_DriverPS5_RumbleJoystickTriggers, HIDAPI_DriverPS5_HasJoystickLED, HIDAPI_DriverPS5_SetJoystickLED, + HIDAPI_DriverPS5_GetSensrorRate, HIDAPI_DriverPS5_CloseJoystick, HIDAPI_DriverPS5_FreeDevice, NULL diff -r 8f3cdd9362e7 src/joystick/hidapi/SDL_hidapi_steam.c --- a/src/joystick/hidapi/SDL_hidapi_steam.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/hidapi/SDL_hidapi_steam.c Thu Nov 12 13:15:38 2020 +0100 @@ -239,6 +239,9 @@ { if ( pAssembler->bIsBle ) { + uint8_t uSegmentHeader; + int nSegmentNumber; + HEXDUMP( pSegment, nSegmentLength ); if ( pSegment[ 0 ] != BLE_REPORT_NUMBER ) @@ -255,7 +258,7 @@ return -1; } - uint8_t uSegmentHeader = pSegment[ 1 ]; + uSegmentHeader = pSegment[ 1 ]; DPRINTF("GOT PACKET HEADER = 0x%x\n", uSegmentHeader); if ( ( uSegmentHeader & REPORT_SEGMENT_DATA_FLAG ) == 0 ) @@ -264,7 +267,7 @@ return 0; } - int nSegmentNumber = uSegmentHeader & 0x07; + nSegmentNumber = uSegmentHeader & 0x07; if ( nSegmentNumber != pAssembler->nExpectedSegmentNumber ) { ResetSteamControllerPacketAssembler( pAssembler ); @@ -306,20 +309,21 @@ static int SetFeatureReport( hid_device *dev, unsigned char uBuffer[65], int nActualDataLen ) { - DPRINTF("SetFeatureReport %p %p %d\n", dev, uBuffer, nActualDataLen); int nRet = -1; bool bBle = true; // only wireless/BLE for now, though macOS could do wired in the future + DPRINTF("SetFeatureReport %p %p %d\n", dev, uBuffer, nActualDataLen); if ( bBle ) { - if ( nActualDataLen < 1 ) - return -1; - int nSegmentNumber = 0; uint8_t uPacketBuffer[ MAX_REPORT_SEGMENT_SIZE ]; // Skip report number in data unsigned char *pBufferPtr = uBuffer + 1; + + if ( nActualDataLen < 1 ) + return -1; + nActualDataLen--; while ( nActualDataLen > 0 ) @@ -347,17 +351,18 @@ static int GetFeatureReport( hid_device *dev, unsigned char uBuffer[65] ) { - DPRINTF("GetFeatureReport( %p %p )\n", dev, uBuffer ); int nRet = -1; bool bBle = true; + DPRINTF("GetFeatureReport( %p %p )\n", dev, uBuffer ); if ( bBle ) { + int nRetries = 0; + uint8_t uSegmentBuffer[ MAX_REPORT_SEGMENT_SIZE ]; + SteamControllerPacketAssembler assembler; InitializeSteamControllerPacketAssembler( &assembler ); - int nRetries = 0; - uint8_t uSegmentBuffer[ MAX_REPORT_SEGMENT_SIZE ]; while( nRetries < BLE_MAX_READ_RETRIES ) { memset( uSegmentBuffer, 0, sizeof( uSegmentBuffer ) ); @@ -398,8 +403,8 @@ static int ReadResponse( hid_device *dev, uint8_t uBuffer[65], int nExpectedResponse ) { + int nRet = GetFeatureReport( dev, uBuffer ); DPRINTF("ReadResponse( %p %p %d )\n", dev, uBuffer, nExpectedResponse ); - int nRet = GetFeatureReport( dev, uBuffer ); if ( nRet < 0 ) return nRet; @@ -418,10 +423,12 @@ //--------------------------------------------------------------------------- static bool ResetSteamController( hid_device *dev, bool bSuppressErrorSpew ) { - DPRINTF( "ResetSteamController hid=%p\n", dev ); // Firmware quirk: Set Feature and Get Feature requests always require a 65-byte buffer. unsigned char buf[65]; int res = -1; + int nAttributesLength; + int nSettings = 0; + DPRINTF( "ResetSteamController hid=%p\n", dev ); buf[0] = 0; buf[1] = ID_GET_ATTRIBUTES_VALUES; @@ -444,7 +451,7 @@ return false; } - int nAttributesLength = buf[ 2 ]; + nAttributesLength = buf[ 2 ]; if ( nAttributesLength > res ) { if ( !bSuppressErrorSpew ) @@ -476,7 +483,6 @@ } // Apply custom settings - clear trackpad modes (cancel mouse emulation), etc - int nSettings = 0; #define ADD_SETTING(SETTING, VALUE) \ buf[3+nSettings*3] = SETTING; \ buf[3+nSettings*3+1] = ((uint16_t)VALUE)&0xFF; \ @@ -735,6 +741,7 @@ pState->sRightPadX = pStatePacket->sRightPadX; pState->sRightPadY = pStatePacket->sRightPadY; + { int nLeftPadX = pState->sLeftPadX; int nLeftPadY = pState->sLeftPadY; int nRightPadX = pState->sRightPadX; @@ -742,11 +749,11 @@ // 15 degrees in rad const float flRotationAngle = 0.261799f; + int nPadOffset; RotatePad(&nLeftPadX, &nLeftPadY, -flRotationAngle); RotatePad(&nRightPadX, &nRightPadY, flRotationAngle); - int nPadOffset; if (pState->ulButtons & STEAM_LEFTPAD_FINGERDOWN_MASK) nPadOffset = 1000; else @@ -766,6 +773,7 @@ pState->sTriggerL = (unsigned short)RemapValClamped( (pStatePacket->ButtonTriggerData.Triggers.nLeft << 7) | pStatePacket->ButtonTriggerData.Triggers.nLeft, 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16 ); pState->sTriggerR = (unsigned short)RemapValClamped( (pStatePacket->ButtonTriggerData.Triggers.nRight << 7) | pStatePacket->ButtonTriggerData.Triggers.nRight, 0, STEAMCONTROLLER_TRIGGER_MAX_ANALOG, 0, SDL_MAX_SINT16 ); + } } @@ -953,9 +961,26 @@ SteamControllerPacketAssembler m_assembler; SteamControllerStateInternal_t m_state; SteamControllerStateInternal_t m_last_state; + + Sint16 gyro_offset[3]; + float gyro_k[3]; + + Sint16 accel_offset[3]; + float accel_k[3]; } SDL_DriverSteam_Context; +static SDL_bool CalibrateSensors(SDL_HIDAPI_Device *dev) +{ + // TODO: read calibration data + return SDL_FALSE; +} + +static float HIDAPI_DriverSteam_GetSensorRate(SDL_HIDAPI_Device* dev) +{ + return 0.f; // TODO find correct value +} + static SDL_bool HIDAPI_DriverSteam_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol) { @@ -1009,10 +1034,29 @@ InitializeSteamControllerPacketAssembler(&ctx->m_assembler); + if (!CalibrateSensors(device)) { + ctx->gyro_offset[0] = 0; + ctx->gyro_offset[1] = 0; + ctx->gyro_offset[2] = 0; + ctx->accel_offset[0] = 0; + ctx->accel_offset[1] = 0; + ctx->accel_offset[2] = 0; + /* 32767 <=> 34.906585rad/s (2000°/s) */ // FIXME: find correct value + ctx->gyro_k[0] = 34.906585f / 32767.f; + ctx->gyro_k[1] = 34.906585f / 32767.f; + ctx->gyro_k[2] = 34.906585f / 32767.f; + /* 32767 <=> 8g */ // FIXME: find correct value + ctx->accel_k[0] = 8.f * 9.80665f / 32767.f; + ctx->accel_k[1] = 8.f * 9.80665f / 32767.f; + ctx->accel_k[2] = 8.f * 9.80665f / 32767.f; + } + /* Initialize the joystick capabilities */ joystick->nbuttons = 17; joystick->naxes = SDL_CONTROLLER_AXIS_MAX; - + /* joystick->has_accelerometer = SDL_TRUE; */ /* TODO: enable this once CalibrateSensors and GetSensorRate are implemented */ + /* joystick->has_gyro = SDL_TRUE; */ + return SDL_TRUE; error: @@ -1148,6 +1192,19 @@ SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, ctx->m_state.sRightPadX); SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, ~ctx->m_state.sRightPadY); + { + float gyro_x = (float)((Sint32)SDL_SwapLE16(ctx->m_state.sGyroX) - (Sint32)ctx->gyro_offset[0]) * ctx->gyro_k[0]; + float gyro_y = (float)((Sint32)SDL_SwapLE16(ctx->m_state.sGyroY) - (Sint32)ctx->gyro_offset[1]) * ctx->gyro_k[1]; + float gyro_z = (float)((Sint32)SDL_SwapLE16(ctx->m_state.sGyroZ) - (Sint32)ctx->gyro_offset[2]) * ctx->gyro_k[2]; + + float accel_x = (float)((Sint32)SDL_SwapLE16(ctx->m_state.sAccelX) - (Sint32)ctx->accel_offset[0]) * ctx->accel_k[0]; + float accel_y = (float)((Sint32)SDL_SwapLE16(ctx->m_state.sAccelY) - (Sint32)ctx->accel_offset[1]) * ctx->accel_k[1]; + float accel_z = (float)((Sint32)SDL_SwapLE16(ctx->m_state.sAccelZ) - (Sint32)ctx->accel_offset[2]) * ctx->accel_k[2]; + + // TODO: check orientaion of steam controller actually matches ds4. + SDL_PrivateJoystickSensors(joystick, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z); + } + ctx->m_last_state = ctx->m_state; } @@ -1191,6 +1248,7 @@ HIDAPI_DriverSteam_RumbleJoystickTriggers, HIDAPI_DriverSteam_HasJoystickLED, HIDAPI_DriverSteam_SetJoystickLED, + HIDAPI_DriverSteam_GetSensorRate, HIDAPI_DriverSteam_CloseJoystick, HIDAPI_DriverSteam_FreeDevice, NULL diff -r 8f3cdd9362e7 src/joystick/hidapi/SDL_hidapi_switch.c --- a/src/joystick/hidapi/SDL_hidapi_switch.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/hidapi/SDL_hidapi_switch.c Thu Nov 12 13:15:38 2020 +0100 @@ -27,6 +27,7 @@ #include "SDL_hints.h" #include "SDL_events.h" +#include "SDL_endian.h" #include "SDL_timer.h" #include "SDL_joystick.h" #include "SDL_gamecontroller.h" @@ -207,10 +208,12 @@ typedef struct { SDL_HIDAPI_Device *device; + SDL_bool m_bInputOnly; SDL_bool m_bHasHomeLED; SDL_bool m_bUsingBluetooth; SDL_bool m_bIsGameCube; + SDL_bool is_right_joycon; SDL_bool m_bUseButtonLabels; Uint8 m_nCommandNumber; SwitchCommonOutputPacket_t m_RumblePacket; @@ -239,6 +242,13 @@ Sint16 sMax; } axis[2]; } m_StickExtents[2]; + + Sint16 gyro_offset[3]; + float gyro_k[3]; + + Sint16 accel_offset[3]; + float accel_k[3]; + } SDL_DriverSwitch_Context; @@ -621,6 +631,17 @@ return SDL_TRUE; } +static SDL_bool CalibrateSensors(SDL_HIDAPI_Device *dev) +{ + // TODO: read calibration data + return SDL_FALSE; +} + +static float HIDAPI_DriverSwitch_GetSensorRate(SDL_HIDAPI_Device *dev) +{ + return 200.f; +} + static float fsel(float fComparand, float fValGE, float fLT) { return fComparand >= 0 ? fValGE : fLT; @@ -761,12 +782,37 @@ device->product_id == USB_PRODUCT_NINTENDO_SWITCH_PRO) { input_mode = k_eSwitchInputReportIDs_FullControllerState; } - + + if (device->vendor_id == USB_VENDOR_NINTENDO && + device->product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) { + ctx->is_right_joycon = SDL_TRUE; + } else { + ctx->is_right_joycon = SDL_FALSE; + } + if (!LoadStickCalibration(ctx, input_mode)) { SDL_SetError("Couldn't load stick calibration"); goto error; } - + + if (!CalibrateSensors(device)) { + // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#accelerometer-and-gyroscope + ctx->gyro_offset[0] = 0; + ctx->gyro_offset[1] = 0; + ctx->gyro_offset[2] = 0; + ctx->accel_offset[0] = 0; + ctx->accel_offset[1] = 0; + ctx->accel_offset[2] = 0; + /* 32767 <=> 34.906585rad/s (2000°/s) */ + ctx->gyro_k[0] = 34.906585f / 32767.f; + ctx->gyro_k[1] = 34.906585f / 32767.f; + ctx->gyro_k[2] = 34.906585f / 32767.f; + /* 32767 <=> 8g */ + ctx->accel_k[0] = 8.f * 9.80665f / 32767.f; + ctx->accel_k[1] = 8.f * 9.80665f / 32767.f; + ctx->accel_k[2] = 8.f * 9.80665f / 32767.f; + } + if (!SetVibrationEnabled(ctx, 1)) { SDL_SetError("Couldn't enable vibration"); goto error; @@ -806,6 +852,8 @@ joystick->nbuttons = 16; joystick->naxes = SDL_CONTROLLER_AXIS_MAX; joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; + joystick->has_accelerometer = SDL_TRUE; + joystick->has_gyro = SDL_TRUE; return SDL_TRUE; @@ -1133,6 +1181,7 @@ static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_Context *ctx, SwitchStatePacket_t *packet) { Sint16 axis; + int i; if (packet->controllerState.rgucButtons[0] != ctx->m_lastFullState.controllerState.rgucButtons[0]) { Uint8 data = packet->controllerState.rgucButtons[0]; @@ -1204,6 +1253,24 @@ } } + for (i = 0; i < 3; ++i) { + float gyro_x = (float)((Sint32)SDL_SwapLE16(packet->imuState[i].sGyroX) - (Sint32)ctx->gyro_offset[0]) * ctx->gyro_k[0]; + float gyro_y = (float)((Sint32)SDL_SwapLE16(packet->imuState[i].sGyroY) - (Sint32)ctx->gyro_offset[1]) * ctx->gyro_k[1]; + float gyro_z = (float)((Sint32)SDL_SwapLE16(packet->imuState[i].sGyroZ) - (Sint32)ctx->gyro_offset[2]) * ctx->gyro_k[2]; + + float accel_x = (float)((Sint32)SDL_SwapLE16(packet->imuState[i].sAccelX) - (Sint32)ctx->accel_offset[0]) * ctx->accel_k[0]; + float accel_y = (float)((Sint32)SDL_SwapLE16(packet->imuState[i].sAccelY) - (Sint32)ctx->accel_offset[1]) * ctx->accel_k[1]; + float accel_z = (float)((Sint32)SDL_SwapLE16(packet->imuState[i].sAccelZ) - (Sint32)ctx->accel_offset[2]) * ctx->accel_k[2]; + + // Sensor on the right joycon is not mounted the same way. + // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/imu_sensor_notes.md#axes-definition + if (ctx->is_right_joycon) { + SDL_PrivateJoystickSensors(joystick, gyro_y, -gyro_z, -gyro_x, accel_y, -accel_z, -accel_x); + } else { + SDL_PrivateJoystickSensors(joystick, -gyro_y, gyro_z, -gyro_x, -accel_y, accel_z, -accel_x); + } + } + ctx->m_lastFullState = *packet; } @@ -1213,6 +1280,7 @@ SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context; SDL_Joystick *joystick = NULL; int size; + SwitchStatePacket_t packet; if (device->num_joysticks > 0) { joystick = SDL_JoystickFromInstanceID(device->joysticks[0]); @@ -1233,7 +1301,8 @@ HandleSimpleControllerState(joystick, ctx, (SwitchSimpleStatePacket_t *)&ctx->m_rgucReadBuffer[1]); break; case k_eSwitchInputReportIDs_FullControllerState: - HandleFullControllerState(joystick, ctx, (SwitchStatePacket_t *)&ctx->m_rgucReadBuffer[1]); + SDL_memcpy(&packet, &ctx->m_rgucReadBuffer[1], sizeof(packet)); + HandleFullControllerState(joystick, ctx, &packet); break; default: break; @@ -1298,6 +1367,7 @@ HIDAPI_DriverSwitch_RumbleJoystickTriggers, HIDAPI_DriverSwitch_HasJoystickLED, HIDAPI_DriverSwitch_SetJoystickLED, + HIDAPI_DriverSwitch_GetSensorRate, HIDAPI_DriverSwitch_CloseJoystick, HIDAPI_DriverSwitch_FreeDevice, NULL diff -r 8f3cdd9362e7 src/joystick/hidapi/SDL_hidapi_xbox360.c --- a/src/joystick/hidapi/SDL_hidapi_xbox360.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c Thu Nov 12 13:15:38 2020 +0100 @@ -815,6 +815,12 @@ return SDL_Unsupported(); } +static float +HIDAPI_DriverXbox360_GetSensrorRate(SDL_HIDAPI_Device* dev) +{ + return 0.f; +} + #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 @@ HIDAPI_DriverXbox360_RumbleJoystickTriggers, HIDAPI_DriverXbox360_HasJoystickLED, HIDAPI_DriverXbox360_SetJoystickLED, + HIDAPI_DriverXbox360_GetSensrorRate, HIDAPI_DriverXbox360_CloseJoystick, HIDAPI_DriverXbox360_FreeDevice, HIDAPI_DriverXbox360_PostUpdate, diff -r 8f3cdd9362e7 src/joystick/hidapi/SDL_hidapi_xbox360w.c --- a/src/joystick/hidapi/SDL_hidapi_xbox360w.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/hidapi/SDL_hidapi_xbox360w.c Thu Nov 12 13:15:38 2020 +0100 @@ -176,6 +176,12 @@ return SDL_Unsupported(); } +static float +HIDAPI_DriverXbox360W_GetSensrorRate(SDL_HIDAPI_Device* dev) +{ + return 0.f; +} + static void HIDAPI_DriverXbox360W_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360W_Context *ctx, Uint8 *data, int size) { @@ -316,6 +322,7 @@ HIDAPI_DriverXbox360W_RumbleJoystickTriggers, HIDAPI_DriverXbox360W_HasJoystickLED, HIDAPI_DriverXbox360W_SetJoystickLED, + HIDAPI_DriverXbox360W_GetSensrorRate, HIDAPI_DriverXbox360W_CloseJoystick, HIDAPI_DriverXbox360W_FreeDevice, NULL diff -r 8f3cdd9362e7 src/joystick/hidapi/SDL_hidapi_xboxone.c --- a/src/joystick/hidapi/SDL_hidapi_xboxone.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c Thu Nov 12 13:15:38 2020 +0100 @@ -369,6 +369,12 @@ return SDL_Unsupported(); } +static float +HIDAPI_DriverXboxOne_GetSensrorRate(SDL_HIDAPI_Device* dev) +{ + return 0.f; +} + static void HIDAPI_DriverXboxOne_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXboxOne_Context *ctx, Uint8 *data, int size) { @@ -896,6 +902,7 @@ HIDAPI_DriverXboxOne_RumbleJoystickTriggers, HIDAPI_DriverXboxOne_HasJoystickLED, HIDAPI_DriverXboxOne_SetJoystickLED, + HIDAPI_DriverXboxOne_GetSensrorRate, HIDAPI_DriverXboxOne_CloseJoystick, HIDAPI_DriverXboxOne_FreeDevice, NULL diff -r 8f3cdd9362e7 src/joystick/hidapi/SDL_hidapijoystick.c --- a/src/joystick/hidapi/SDL_hidapijoystick.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/hidapi/SDL_hidapijoystick.c Thu Nov 12 13:15:38 2020 +0100 @@ -30,6 +30,7 @@ #include "SDL_timer.h" #include "SDL_joystick.h" #include "../SDL_sysjoystick.h" +#include "../../events/SDL_touch_c.h" #include "SDL_hidapijoystick_c.h" #include "SDL_hidapi_rumble.h" #include "../../SDL_hints_c.h" @@ -618,6 +619,11 @@ } SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPIDriverHintChanged, NULL); + + if (SDL_TouchInit() < 0) { // needed for dualshock trackpad + return -1; + } + HIDAPI_InitializeDiscovery(); HIDAPI_JoystickDetect(); HIDAPI_UpdateDevices(); @@ -1119,6 +1125,22 @@ return result; } +static float +HIDAPI_JoystickGetSensorRate(SDL_Joystick * joystick) +{ + float result; + + if (joystick->hwdata) { + SDL_HIDAPI_Device *device = joystick->hwdata->device; + result = device->driver->GetSensorRate(device); + } else { + SDL_SetError("GetSensorRate failed, device disconnected"); + result = 0.f; + } + + return result; +} + static void HIDAPI_JoystickUpdate(SDL_Joystick * joystick) { @@ -1167,6 +1189,8 @@ hid_exit(); + SDL_TouchQuit(); + /* Make sure the drivers cleaned up properly */ SDL_assert(SDL_HIDAPI_numjoysticks == 0); @@ -1195,6 +1219,7 @@ HIDAPI_JoystickRumbleTriggers, HIDAPI_JoystickHasLED, HIDAPI_JoystickSetLED, + HIDAPI_JoystickGetSensorRate, HIDAPI_JoystickUpdate, HIDAPI_JoystickClose, HIDAPI_JoystickQuit, diff -r 8f3cdd9362e7 src/joystick/hidapi/SDL_hidapijoystick_c.h --- a/src/joystick/hidapi/SDL_hidapijoystick_c.h Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h Thu Nov 12 13:15:38 2020 +0100 @@ -37,6 +37,7 @@ #define SDL_JOYSTICK_HIDAPI_XBOX360 #define SDL_JOYSTICK_HIDAPI_XBOXONE #define SDL_JOYSTICK_HIDAPI_GAMECUBE +/*#define SDL_JOYSTICK_HIDAPI_STEAM*/ #ifdef __WINDOWS__ /* On Windows, Xbox One controllers are handled by the Xbox 360 driver */ @@ -98,6 +99,7 @@ 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); + float (*GetSensorRate)(SDL_HIDAPI_Device *device); void (*CloseJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick); void (*FreeDevice)(SDL_HIDAPI_Device *device); void (*PostUpdate)(void); diff -r 8f3cdd9362e7 src/joystick/iphoneos/SDL_sysjoystick.m --- a/src/joystick/iphoneos/SDL_sysjoystick.m Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/iphoneos/SDL_sysjoystick.m Thu Nov 12 13:15:38 2020 +0100 @@ -1173,6 +1173,12 @@ return SDL_Unsupported(); } +static float +IOS_JoystickGetSensorRate(SDL_Joystick * joystick) +{ + return 0.f; +} + static void IOS_JoystickUpdate(SDL_Joystick * joystick) { @@ -1284,6 +1290,7 @@ IOS_JoystickRumbleTriggers, IOS_JoystickHasLED, IOS_JoystickSetLED, + IOS_JoystickGetSensorRate, IOS_JoystickUpdate, IOS_JoystickClose, IOS_JoystickQuit, diff -r 8f3cdd9362e7 src/joystick/linux/SDL_sysjoystick.c --- a/src/joystick/linux/SDL_sysjoystick.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/linux/SDL_sysjoystick.c Thu Nov 12 13:15:38 2020 +0100 @@ -1046,6 +1046,12 @@ return SDL_Unsupported(); } +static float +LINUX_JoystickGetSensorRate(SDL_Joystick * joystick) +{ + return 0.f; +} + static SDL_INLINE void HandleHat(SDL_Joystick * stick, Uint8 hat, int axis, int value) { @@ -1529,6 +1535,7 @@ LINUX_JoystickRumbleTriggers, LINUX_JoystickHasLED, LINUX_JoystickSetLED, + LINUX_JoystickGetSensorRate, LINUX_JoystickUpdate, LINUX_JoystickClose, LINUX_JoystickQuit, diff -r 8f3cdd9362e7 src/joystick/usb_ids.h --- a/src/joystick/usb_ids.h Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/usb_ids.h Thu Nov 12 13:15:38 2020 +0100 @@ -36,6 +36,8 @@ #define USB_VENDOR_VALVE 0x28de #define USB_PRODUCT_NINTENDO_GAMECUBE_ADAPTER 0x0337 +#define USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT 0x2006 +#define USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT 0x2007 #define USB_PRODUCT_NINTENDO_SWITCH_PRO 0x2009 #define USB_PRODUCT_RAZER_PANTHERA 0x0401 #define USB_PRODUCT_RAZER_PANTHERA_EVO 0x1008 diff -r 8f3cdd9362e7 src/joystick/virtual/SDL_virtualjoystick.c --- a/src/joystick/virtual/SDL_virtualjoystick.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/virtual/SDL_virtualjoystick.c Thu Nov 12 13:15:38 2020 +0100 @@ -358,6 +358,11 @@ return SDL_Unsupported(); } +static float +VIRTUAL_JoystickGetSensorRate(SDL_Joystick * joystick) +{ + return 0.f; +} static void VIRTUAL_JoystickUpdate(SDL_Joystick * joystick) @@ -432,6 +437,7 @@ VIRTUAL_JoystickRumbleTriggers, VIRTUAL_JoystickHasLED, VIRTUAL_JoystickSetLED, + VIRTUAL_JoystickGetSensorRate, VIRTUAL_JoystickUpdate, VIRTUAL_JoystickClose, VIRTUAL_JoystickQuit, diff -r 8f3cdd9362e7 src/joystick/windows/SDL_rawinputjoystick.c --- a/src/joystick/windows/SDL_rawinputjoystick.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/windows/SDL_rawinputjoystick.c Thu Nov 12 13:15:38 2020 +0100 @@ -635,6 +635,11 @@ return SDL_Unsupported(); } +static float +RAWINPUT_JoystickGetSensorRate(SDL_Joystick * joystick) +{ + return 0.f; +} static void RAWINPUT_JoystickUpdate(SDL_Joystick * joystick) @@ -768,6 +773,7 @@ RAWINPUT_JoystickRumbleTriggers, RAWINPUT_JoystickHasLED, RAWINPUT_JoystickSetLED, + RAWINPUT_JoystickGetSensorRate, RAWINPUT_JoystickUpdate, RAWINPUT_JoystickClose, RAWINPUT_JoystickQuit, diff -r 8f3cdd9362e7 src/joystick/windows/SDL_windows_gaming_input.c --- a/src/joystick/windows/SDL_windows_gaming_input.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/windows/SDL_windows_gaming_input.c Thu Nov 12 13:15:38 2020 +0100 @@ -607,6 +607,12 @@ return SDL_Unsupported(); } +static float +WGI_JoystickGetSensorRate(SDL_Joystick * joystick) +{ + return 0.f; +} + static Uint8 ConvertHatValue(__x_ABI_CWindows_CGaming_CInput_CGameControllerSwitchPosition value) { @@ -752,6 +758,7 @@ WGI_JoystickRumbleTriggers, WGI_JoystickHasLED, WGI_JoystickSetLED, + WGI_JoystickGetSensorRate, WGI_JoystickUpdate, WGI_JoystickClose, WGI_JoystickQuit, diff -r 8f3cdd9362e7 src/joystick/windows/SDL_windowsjoystick.c --- a/src/joystick/windows/SDL_windowsjoystick.c Wed Nov 11 20:09:56 2020 -0800 +++ b/src/joystick/windows/SDL_windowsjoystick.c Thu Nov 12 13:15:38 2020 +0100 @@ -518,6 +518,12 @@ return SDL_Unsupported(); } +static float +WINDOWS_JoystickGetSensorRate(SDL_Joystick * joystick) +{ + return 0.f; +} + static void WINDOWS_JoystickUpdate(SDL_Joystick * joystick) { @@ -604,6 +610,7 @@ WINDOWS_JoystickRumbleTriggers, WINDOWS_JoystickHasLED, WINDOWS_JoystickSetLED, + WINDOWS_JoystickGetSensorRate, WINDOWS_JoystickUpdate, WINDOWS_JoystickClose, WINDOWS_JoystickQuit, diff -r 8f3cdd9362e7 test/controllermap.c --- a/test/controllermap.c Wed Nov 11 20:09:56 2020 -0800 +++ b/test/controllermap.c Thu Nov 12 13:15:38 2020 +0100 @@ -526,6 +526,8 @@ break; case SDL_JOYBALLMOTION: break; + case SDL_JOYSENSORUPDATE: + break; case SDL_JOYBUTTONDOWN: if (event.jbutton.which == nJoystickID) { SDL_GameControllerExtendedBind binding; diff -r 8f3cdd9362e7 test/testjoystick.c --- a/test/testjoystick.c Wed Nov 11 20:09:56 2020 -0800 +++ b/test/testjoystick.c Thu Nov 12 13:15:38 2020 +0100 @@ -78,15 +78,17 @@ break; } SDL_Log("Joystick\n"); - SDL_Log(" name: %s\n", SDL_JoystickName(joystick)); - SDL_Log(" type: %s\n", type); - SDL_Log(" axes: %d\n", SDL_JoystickNumAxes(joystick)); - SDL_Log(" balls: %d\n", SDL_JoystickNumBalls(joystick)); - SDL_Log(" hats: %d\n", SDL_JoystickNumHats(joystick)); - SDL_Log(" buttons: %d\n", SDL_JoystickNumButtons(joystick)); - SDL_Log("instance id: %d\n", SDL_JoystickInstanceID(joystick)); - SDL_Log(" guid: %s\n", guid); - SDL_Log(" VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick)); + SDL_Log(" name: %s\n", SDL_JoystickName(joystick)); + SDL_Log(" type: %s\n", type); + SDL_Log(" axes: %d\n", SDL_JoystickNumAxes(joystick)); + SDL_Log(" balls: %d\n", SDL_JoystickNumBalls(joystick)); + SDL_Log(" hats: %d\n", SDL_JoystickNumHats(joystick)); + SDL_Log(" buttons: %d\n", SDL_JoystickNumButtons(joystick)); + SDL_Log(" gyroscope: %d\n", SDL_JoystickHasGyroscope(joystick)); + SDL_Log("accelerometer: %d\n", SDL_JoystickHasAccelerometer(joystick)); + SDL_Log(" instance id: %d\n", SDL_JoystickInstanceID(joystick)); + SDL_Log(" guid: %s\n", guid); + SDL_Log(" VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick)); } static void @@ -96,6 +98,12 @@ SDL_RenderFillRect(r, &area); } +/* for gyroscope */ +static float dt = 0.f; /* joystick sensor rate in second */ +static double heading = 0.f; /* ° */ +static double max_a = 0.0, max_w = 0.0; +static SDL_bool log_sensor = SDL_FALSE; + void loop(void *arg) { @@ -113,6 +121,7 @@ SDL_Log("Joystick device %d added.\n", (int) event.jdevice.which); if (!joystick) { joystick = SDL_JoystickOpen(event.jdevice.which); + dt = 1.f / SDL_JoystickGetSensorsRate(joystick); if (joystick) { PrintJoystick(joystick); } else { @@ -129,6 +138,35 @@ } break; + case SDL_JOYSENSORUPDATE: + { + float ax = event.sensor.data[3], ay = event.sensor.data[4], az = event.sensor.data[5]; + float gx = event.sensor.data[0], gy = event.sensor.data[1], gz = event.sensor.data[2]; + + if (SDL_fabs(ax) > max_a) {max_a = SDL_fabs(ax);} + if (SDL_fabs(ay) > max_a) {max_a = SDL_fabs(ay);} + if (SDL_fabs(az) > max_a) {max_a = SDL_fabs(az);} + if (SDL_fabs(gx) > max_w) {max_w = SDL_fabs(gx);} + if (SDL_fabs(gy) > max_w) {max_w = SDL_fabs(gy);} + if (SDL_fabs(gz) > max_w) {max_w = SDL_fabs(gz);} + + heading += gy * dt * (180.0 / 3.1415); + + /* To test sensors implementation: + * Holding your gamepad: + * - with the button facing up: accel = 0, 9.81, 0 + * - with trigger facing up: accel = 0, 0, -9.81 + * - on its side, button facing left: accel = 9.81, 0, 0 + * + * With button facing up, rotating gamepad to the left around + * vertical axis should increase heading. + */ + if (log_sensor) { + SDL_Log("Joystick %d heading: %6.1f accel: %6.2f, %6.2f, %6.2f gyro: %6.2f, %6.2f, %6.2f", + event.sensor.which, heading, ax, ay, az, gx, gy, gz); + } + break; + } case SDL_JOYAXISMOTION: SDL_Log("Joystick %d axis %d value: %d\n", event.jaxis.which, @@ -160,6 +198,8 @@ /* First button triggers a 0.5 second full strength rumble */ if (event.jbutton.button == 0) { SDL_JoystickRumble(joystick, 0xFFFF, 0xFFFF, 500); + } else if (event.jbutton.button == 1) { + log_sensor = !log_sensor; } break; case SDL_JOYBUTTONUP: @@ -304,6 +344,10 @@ } #endif + if (max_a != 0) { + SDL_Log("max acceleration = %fm/s², max rotation = %f°/s\n", max_a, max_w * (180 / 3.1415)); + } + SDL_DestroyRenderer(screen); SDL_DestroyWindow(window);