Skip to content

Commit

Permalink
Added API for sensors on game controllers
Browse files Browse the repository at this point in the history
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
  • Loading branch information
slouken committed Nov 17, 2020
1 parent b79e1ba commit fcb21aa
Show file tree
Hide file tree
Showing 33 changed files with 812 additions and 120 deletions.
17 changes: 15 additions & 2 deletions include/SDL_events.h
Expand Up @@ -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,
Expand Down Expand Up @@ -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.*)
Expand Down Expand Up @@ -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 */
Expand Down
47 changes: 47 additions & 0 deletions include/SDL_gamecontroller.h
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Expand Down
19 changes: 11 additions & 8 deletions include/SDL_sensor.h
Expand Up @@ -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
Expand All @@ -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()
*/
Expand Down
4 changes: 4 additions & 0 deletions src/dynapi/SDL_dynapi_overrides.h
Expand Up @@ -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
4 changes: 4 additions & 0 deletions src/dynapi/SDL_dynapi_procs.h
Expand Up @@ -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)
105 changes: 105 additions & 0 deletions src/joystick/SDL_gamecontroller.c
Expand Up @@ -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)
{
Expand Down
56 changes: 54 additions & 2 deletions src/joystick/SDL_joystick.c
Expand Up @@ -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));
Expand All @@ -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;
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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: */
3 changes: 3 additions & 0 deletions src/joystick/SDL_joystick_c.h
Expand Up @@ -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,
Expand All @@ -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);

Expand Down

0 comments on commit fcb21aa

Please sign in to comment.