diff -r 4d04f91e1776 include/SDL_events.h --- a/include/SDL_events.h Sun May 03 22:13:48 2020 -0400 +++ b/include/SDL_events.h Mon May 04 19:01:43 2020 +0200 @@ -115,6 +115,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 */ @@ -498,10 +499,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 4d04f91e1776 include/SDL_joystick.h --- a/include/SDL_joystick.h Sun May 03 22:13:48 2020 -0400 +++ b/include/SDL_joystick.h Mon May 04 19:01:43 2020 +0200 @@ -325,6 +325,16 @@ 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); + +/** * Update the current state of the open joysticks. * * This is called automatically by the event loop if any joystick @@ -420,6 +430,43 @@ 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); + +/** * Trigger a rumble effect * Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling. * diff -r 4d04f91e1776 src/events/SDL_events.c --- a/src/events/SDL_events.c Sun May 03 22:13:48 2020 -0400 +++ b/src/events/SDL_events.c Mon May 04 19:01:43 2020 +0200 @@ -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 4d04f91e1776 src/joystick/SDL_joystick.c --- a/src/joystick/SDL_joystick.c Sun May 03 22:13:48 2020 -0400 +++ b/src/joystick/SDL_joystick.c Mon May 04 19:01:43 2020 +0200 @@ -632,6 +632,23 @@ 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; +} /* * Get the current state of an axis control on a joystick */ @@ -739,6 +756,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. @@ -1096,6 +1143,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) @@ -1325,6 +1374,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_PrivateJoystickShouldIgnoreEvent()) { + gyro_x = 0.f; + gyro_y = 0.f; + gyro_z = 0.f; + accel_x = 0.f; + accel_y = 0.f; + accel_z = 0.f; + } + + if (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) { + return 0; + } + + 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) { @@ -1406,7 +1504,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 4d04f91e1776 src/joystick/SDL_joystick_c.h --- a/src/joystick/SDL_joystick_c.h Sun May 03 22:13:48 2020 -0400 +++ b/src/joystick/SDL_joystick_c.h Mon May 04 19:01:43 2020 +0200 @@ -99,6 +99,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 4d04f91e1776 src/joystick/SDL_sysjoystick.h --- a/src/joystick/SDL_sysjoystick.h Sun May 03 22:13:48 2020 -0400 +++ b/src/joystick/SDL_sysjoystick.h Mon May 04 19:01:43 2020 +0200 @@ -64,6 +64,11 @@ Uint16 high_frequency_rumble; Uint32 rumble_expiration; + SDL_bool has_gyro; + SDL_bool has_accelerometer; + float gyro[3]; /* rad/s */ + float accel[3]; /* m/s² */ + SDL_bool attached; SDL_bool is_game_controller; SDL_bool delayed_guide_button; /* SDL_TRUE if this device has the guide button event delayed */ diff -r 4d04f91e1776 src/joystick/hidapi/SDL_hidapi_ps4.c --- a/src/joystick/hidapi/SDL_hidapi_ps4.c Sun May 03 22:13:48 2020 -0400 +++ b/src/joystick/hidapi/SDL_hidapi_ps4.c Mon May 04 19:01:43 2020 +0200 @@ -26,8 +26,10 @@ #ifdef SDL_JOYSTICK_HIDAPI #include "SDL_hints.h" +#include "SDL_platform.h" #include "SDL_events.h" #include "SDL_timer.h" +#include "SDL_endian.h" #include "SDL_joystick.h" #include "SDL_gamecontroller.h" #include "../SDL_sysjoystick.h" @@ -94,7 +96,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; @@ -182,6 +223,106 @@ 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 void SetLedsForPlayerIndex(DS4EffectsState_t *effects, int player_index) { @@ -278,11 +419,32 @@ } 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; + /* 8192 <=> 9.80665 (1g) */ + ctx->accel_k[0] = 9.80665f / 8192.f; + ctx->accel_k[1] = 9.80665f / 8192.f; + ctx->accel_k[2] = 9.80665f / 8192.f; } /* Initialize player index (needed for setting LEDs) */ @@ -294,6 +456,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; @@ -451,6 +616,18 @@ 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); + } + if (packet->ucBatteryLevel & 0x10) { joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; } else { @@ -475,6 +652,7 @@ { SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; SDL_Joystick *joystick = NULL; + PS4StatePacket_t state; Uint8 data[USB_PACKET_LENGTH]; int size; @@ -488,11 +666,13 @@ 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_EPS4ReportIdBluetoothState: /* Bluetooth state packets have two additional bytes at the beginning */ - 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: #ifdef DEBUG_JOYSTICK diff -r 4d04f91e1776 test/controllermap.c --- a/test/controllermap.c Sun May 03 22:13:48 2020 -0400 +++ b/test/controllermap.c Mon May 04 19:01:43 2020 +0200 @@ -515,6 +515,8 @@ break; case SDL_JOYBALLMOTION: break; + case SDL_JOYSENSORUPDATE: + break; case SDL_JOYBUTTONDOWN: if (event.jbutton.which == nJoystickID) { SDL_GameControllerExtendedBind binding; diff -r 4d04f91e1776 test/testjoystick.c --- a/test/testjoystick.c Sun May 03 22:13:48 2020 -0400 +++ b/test/testjoystick.c Mon May 04 19:01:43 2020 +0200 @@ -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 Uint32 t = (Uint32)-1; /* millisecond */ +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) { @@ -129,6 +137,44 @@ } break; + case SDL_JOYSENSORUPDATE: + { + float dt = (float)(event.sensor.timestamp - t) * 0.001f; + 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);} + + if (t == (Uint32)-1) { + dt = 1.f; + gx = 0.0; + gy = 0.0; + gz = 0.0; + } + heading += gy * dt * (180.0 / 3.1415); + t = event.sensor.timestamp; + + /* 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) { + float acc[3], gyro[3]; + SDL_Log("Joystick %d t: %3d heading: %6.1f accel: %6.2f, %6.2f, %6.2f gyro: %6.2f, %6.2f, %6.2f", + event.sensor.which, t/1000, 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 +206,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: @@ -296,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);