Added support for the touchpad on PS4 and PS5 controllers

This commit is contained in:
Sam Lantinga 2020-11-13 18:01:29 -08:00
parent 78422fa3c8
commit 9f51fad361
13 changed files with 434 additions and 61 deletions

View File

@ -125,6 +125,9 @@ typedef enum
SDL_CONTROLLERDEVICEADDED, /**< A new Game controller has been inserted into the system */ SDL_CONTROLLERDEVICEADDED, /**< A new Game controller has been inserted into the system */
SDL_CONTROLLERDEVICEREMOVED, /**< An opened Game controller has been removed */ SDL_CONTROLLERDEVICEREMOVED, /**< An opened Game controller has been removed */
SDL_CONTROLLERDEVICEREMAPPED, /**< The controller mapping was updated */ SDL_CONTROLLERDEVICEREMAPPED, /**< The controller mapping was updated */
SDL_CONTROLLERTOUCHPADDOWN, /**< Game controller touchpad was touched */
SDL_CONTROLLERTOUCHPADMOTION, /**< Game controller touchpad finger was moved */
SDL_CONTROLLERTOUCHPADUP, /**< Game controller touchpad finger was lifted */
/* Touch events */ /* Touch events */
SDL_FINGERDOWN = 0x700, SDL_FINGERDOWN = 0x700,
@ -415,6 +418,22 @@ typedef struct SDL_ControllerDeviceEvent
Sint32 which; /**< The joystick device index for the ADDED event, instance id for the REMOVED or REMAPPED event */ Sint32 which; /**< The joystick device index for the ADDED event, instance id for the REMOVED or REMAPPED event */
} SDL_ControllerDeviceEvent; } SDL_ControllerDeviceEvent;
/**
* \brief Game controller touchpad event structure (event.ctouchpad.*)
*/
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 */
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 Audio device event structure (event.adevice.*) * \brief Audio device event structure (event.adevice.*)
*/ */
@ -577,6 +596,7 @@ typedef union SDL_Event
SDL_ControllerAxisEvent caxis; /**< Game Controller axis event data */ SDL_ControllerAxisEvent caxis; /**< Game Controller axis event data */
SDL_ControllerButtonEvent cbutton; /**< Game Controller button event data */ SDL_ControllerButtonEvent cbutton; /**< Game Controller button event data */
SDL_ControllerDeviceEvent cdevice; /**< Game Controller device event data */ SDL_ControllerDeviceEvent cdevice; /**< Game Controller device event data */
SDL_ControllerTouchpadEvent ctouchpad; /**< Game Controller touchpad event data */
SDL_AudioDeviceEvent adevice; /**< Audio device event data */ SDL_AudioDeviceEvent adevice; /**< Audio device event data */
SDL_SensorEvent sensor; /**< Sensor event data */ SDL_SensorEvent sensor; /**< Sensor event data */
SDL_QuitEvent quit; /**< Quit request event data */ SDL_QuitEvent quit; /**< Quit request event data */

View File

@ -330,6 +330,12 @@ extern DECLSPEC SDL_GameControllerButtonBind SDLCALL
SDL_GameControllerGetBindForAxis(SDL_GameController *gamecontroller, SDL_GameControllerGetBindForAxis(SDL_GameController *gamecontroller,
SDL_GameControllerAxis axis); SDL_GameControllerAxis axis);
/**
* Return whether a game controller has a given axis
*/
extern DECLSPEC SDL_bool SDLCALL
SDL_GameControllerHasAxis(SDL_GameController *gamecontroller, SDL_GameControllerAxis axis);
/** /**
* Get the current state of an axis control on a game controller. * Get the current state of an axis control on a game controller.
* *
@ -339,8 +345,7 @@ SDL_GameControllerGetBindForAxis(SDL_GameController *gamecontroller,
* The axis indices start at index 0. * The axis indices start at index 0.
*/ */
extern DECLSPEC Sint16 SDLCALL extern DECLSPEC Sint16 SDLCALL
SDL_GameControllerGetAxis(SDL_GameController *gamecontroller, SDL_GameControllerGetAxis(SDL_GameController *gamecontroller, SDL_GameControllerAxis axis);
SDL_GameControllerAxis axis);
/** /**
* The list of buttons available from a controller * The list of buttons available from a controller
@ -363,11 +368,12 @@ typedef enum
SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_DOWN,
SDL_CONTROLLER_BUTTON_DPAD_LEFT, SDL_CONTROLLER_BUTTON_DPAD_LEFT,
SDL_CONTROLLER_BUTTON_DPAD_RIGHT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
SDL_CONTROLLER_BUTTON_MISC1, // Xbox Series X share button, PS4/PS5 touchpad button, Nintendo Switch Pro capture button SDL_CONTROLLER_BUTTON_MISC1, /* Xbox Series X share button, PS5 microphone button, Nintendo Switch Pro capture button */
SDL_CONTROLLER_BUTTON_PADDLE1, // Xbox Elite paddle P1 SDL_CONTROLLER_BUTTON_PADDLE1, /* Xbox Elite paddle P1 */
SDL_CONTROLLER_BUTTON_PADDLE2, // Xbox Elite paddle P3 SDL_CONTROLLER_BUTTON_PADDLE2, /* Xbox Elite paddle P3 */
SDL_CONTROLLER_BUTTON_PADDLE3, // Xbox Elite paddle P2 SDL_CONTROLLER_BUTTON_PADDLE3, /* Xbox Elite paddle P2 */
SDL_CONTROLLER_BUTTON_PADDLE4, // Xbox Elite paddle P4 SDL_CONTROLLER_BUTTON_PADDLE4, /* Xbox Elite paddle P4 */
SDL_CONTROLLER_BUTTON_TOUCHPAD, /* PS4/PS5 touchpad button */
SDL_CONTROLLER_BUTTON_MAX SDL_CONTROLLER_BUTTON_MAX
} SDL_GameControllerButton; } SDL_GameControllerButton;
@ -388,6 +394,11 @@ extern DECLSPEC SDL_GameControllerButtonBind SDLCALL
SDL_GameControllerGetBindForButton(SDL_GameController *gamecontroller, SDL_GameControllerGetBindForButton(SDL_GameController *gamecontroller,
SDL_GameControllerButton button); SDL_GameControllerButton button);
/**
* Return whether a game controller has a given button
*/
extern DECLSPEC SDL_bool SDLCALL SDL_GameControllerHasButton(SDL_GameController *gamecontroller,
SDL_GameControllerButton button);
/** /**
* Get the current state of a button on a game controller. * Get the current state of a button on a game controller.
@ -397,6 +408,21 @@ SDL_GameControllerGetBindForButton(SDL_GameController *gamecontroller,
extern DECLSPEC Uint8 SDLCALL SDL_GameControllerGetButton(SDL_GameController *gamecontroller, extern DECLSPEC Uint8 SDLCALL SDL_GameControllerGetButton(SDL_GameController *gamecontroller,
SDL_GameControllerButton button); SDL_GameControllerButton button);
/**
* Get the number of touchpads on a game controller.
*/
extern DECLSPEC int SDLCALL SDL_GameControllerGetNumTouchpads(SDL_GameController *gamecontroller);
/**
* Get the number of supported simultaneous fingers on a touchpad on a game controller.
*/
extern DECLSPEC int SDLCALL SDL_GameControllerGetNumTouchpadFingers(SDL_GameController *gamecontroller, int touchpad);
/**
* Get the current state of a finger on a touchpad on a game controller.
*/
extern DECLSPEC int SDLCALL SDL_GameControllerGetTouchpadFinger(SDL_GameController *gamecontroller, int touchpad, int finger, Uint8 *state, float *x, float *y, float *pressure);
/** /**
* Start a rumble effect * Start a rumble effect
* Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling. * Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling.

View File

@ -774,3 +774,8 @@
#define SDL_JoystickSetLED SDL_JoystickSetLED_REAL #define SDL_JoystickSetLED SDL_JoystickSetLED_REAL
#define SDL_GameControllerRumbleTriggers SDL_GameControllerRumbleTriggers_REAL #define SDL_GameControllerRumbleTriggers SDL_GameControllerRumbleTriggers_REAL
#define SDL_JoystickRumbleTriggers SDL_JoystickRumbleTriggers_REAL #define SDL_JoystickRumbleTriggers SDL_JoystickRumbleTriggers_REAL
#define SDL_GameControllerHasAxis SDL_GameControllerHasAxis_REAL
#define SDL_GameControllerHasButton SDL_GameControllerHasButton_REAL
#define SDL_GameControllerGetNumTouchpads SDL_GameControllerGetNumTouchpads_REAL
#define SDL_GameControllerGetNumTouchpadFingers SDL_GameControllerGetNumTouchpadFingers_REAL
#define SDL_GameControllerGetTouchpadFinger SDL_GameControllerGetTouchpadFinger_REAL

View File

@ -835,3 +835,8 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_JoystickHasLED,(SDL_Joystick *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_JoystickSetLED,(SDL_Joystick *a, Uint8 b, Uint8 c, Uint8 d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_JoystickSetLED,(SDL_Joystick *a, Uint8 b, Uint8 c, Uint8 d),(a,b,c,d),return)
SDL_DYNAPI_PROC(int,SDL_GameControllerRumbleTriggers,(SDL_GameController *a, Uint16 b, Uint16 c, Uint32 d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_GameControllerRumbleTriggers,(SDL_GameController *a, Uint16 b, Uint16 c, Uint32 d),(a,b,c,d),return)
SDL_DYNAPI_PROC(int,SDL_JoystickRumbleTriggers,(SDL_Joystick *a, Uint16 b, Uint16 c, Uint32 d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_JoystickRumbleTriggers,(SDL_Joystick *a, Uint16 b, Uint16 c, Uint32 d),(a,b,c,d),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_GameControllerHasAxis,(SDL_GameController *a, SDL_GameControllerAxis b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_GameControllerHasButton,(SDL_GameController *a, SDL_GameControllerButton b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GameControllerGetNumTouchpads,(SDL_GameController *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GameControllerGetNumTouchpadFingers,(SDL_GameController *a, int b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GameControllerGetTouchpadFinger,(SDL_GameController *a, int b, int c, Uint8 *d, float *e, float *f, float *g),(a,b,c,d,e,f,g),return)

View File

@ -580,9 +580,12 @@ static ControllerMapping_t *SDL_CreateMappingForHIDAPIController(SDL_JoystickGUI
} else { } else {
switch (SDL_GetJoystickGameControllerTypeFromGUID(guid, NULL)) { switch (SDL_GetJoystickGameControllerTypeFromGUID(guid, NULL)) {
case SDL_CONTROLLER_TYPE_PS4: case SDL_CONTROLLER_TYPE_PS4:
/* PS4 controllers have an additional touchpad button */
SDL_strlcat(mapping_string, "touchpad:b15,", sizeof(mapping_string));
break;
case SDL_CONTROLLER_TYPE_PS5: case SDL_CONTROLLER_TYPE_PS5:
/* PS4/PS5 controllers have an additional touchpad button */ /* PS5 controllers have a microphone button and an additional touchpad button */
SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string)); SDL_strlcat(mapping_string, "misc1:b15,touchpad:b16", sizeof(mapping_string));
break; break;
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO: case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
/* Nintendo Switch Pro controllers have a screenshot button */ /* Nintendo Switch Pro controllers have a screenshot button */
@ -713,6 +716,7 @@ static const char* map_StringForControllerButton[] = {
"paddle2", "paddle2",
"paddle3", "paddle3",
"paddle4", "paddle4",
"touchpad",
NULL NULL
}; };
@ -1870,6 +1874,16 @@ SDL_GameControllerUpdate(void)
SDL_JoystickUpdate(); SDL_JoystickUpdate();
} }
/**
* Return whether a game controller has a given axis
*/
SDL_bool
SDL_GameControllerHasAxis(SDL_GameController *gamecontroller, SDL_GameControllerAxis axis)
{
SDL_GameControllerButtonBind bind = SDL_GameControllerGetBindForAxis(gamecontroller, axis);
return (bind.bindType != SDL_CONTROLLER_BINDTYPE_NONE) ? SDL_TRUE : SDL_FALSE;
}
/* /*
* Get the current state of an axis control on a controller * Get the current state of an axis control on a controller
*/ */
@ -1929,6 +1943,16 @@ SDL_GameControllerGetAxis(SDL_GameController * gamecontroller, SDL_GameControlle
return 0; return 0;
} }
/**
* Return whether a game controller has a given button
*/
SDL_bool
SDL_GameControllerHasButton(SDL_GameController *gamecontroller, SDL_GameControllerButton button)
{
SDL_GameControllerButtonBind bind = SDL_GameControllerGetBindForButton(gamecontroller, button);
return (bind.bindType != SDL_CONTROLLER_BINDTYPE_NONE) ? SDL_TRUE : SDL_FALSE;
}
/* /*
* Get the current state of a button on a controller * Get the current state of a button on a controller
*/ */
@ -1970,6 +1994,71 @@ SDL_GameControllerGetButton(SDL_GameController * gamecontroller, SDL_GameControl
return SDL_RELEASED; return SDL_RELEASED;
} }
/**
* Get the number of touchpads on a game controller.
*/
int
SDL_GameControllerGetNumTouchpads(SDL_GameController *gamecontroller)
{
SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller);
if (joystick) {
return joystick->ntouchpads;
}
return 0;
}
/**
* Get the number of supported simultaneous fingers on a touchpad on a game controller.
*/
int SDL_GameControllerGetNumTouchpadFingers(SDL_GameController *gamecontroller, int touchpad)
{
SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller);
if (joystick && touchpad >= 0 && touchpad < joystick->ntouchpads) {
return joystick->touchpads[touchpad].nfingers;
}
return 0;
}
/**
* Get the current state of a finger on a touchpad on a game controller.
*/
int
SDL_GameControllerGetTouchpadFinger(SDL_GameController *gamecontroller, int touchpad, int finger, Uint8 *state, float *x, float *y, float *pressure)
{
SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gamecontroller);
if (joystick ) {
if (touchpad >= 0 && touchpad < joystick->ntouchpads) {
SDL_JoystickTouchpadInfo *touchpad_info = &joystick->touchpads[touchpad];
if (finger >= 0 && finger < touchpad_info->nfingers) {
SDL_JoystickTouchpadFingerInfo *info = &touchpad_info->fingers[finger];
if (state) {
*state = info->state;
}
if (x) {
*x = info->x;
}
if (y) {
*y = info->y;
}
if (pressure) {
*pressure = info->pressure;
}
return 0;
} else {
return SDL_InvalidParamError("finger");
}
} else {
return SDL_InvalidParamError("touchpad");
}
} else {
return SDL_InvalidParamError("gamecontroller");
}
}
const char * const char *
SDL_GameControllerName(SDL_GameController * gamecontroller) SDL_GameControllerName(SDL_GameController * gamecontroller)
{ {

View File

@ -826,7 +826,7 @@ static const char *s_ControllerMappings [] =
"05000000ac050000020000004f066d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,", "05000000ac050000020000004f066d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,",
"050000004c050000cc090000df070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,", "050000004c050000cc090000df070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,",
"050000004c050000cc090000ff070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,", "050000004c050000cc090000ff070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,",
"050000004c050000cc090000ff870001,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,", "050000004c050000cc090000ff870001,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,touchpad:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,",
"05000000ac0500000300000043006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,", "05000000ac0500000300000043006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,",
"050000005e040000050b0000ff070001,Xbox Elite Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,", "050000005e040000050b0000ff070001,Xbox Elite Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,",
"050000005e040000e0020000df070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,", "050000005e040000e0020000df070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,",

View File

@ -991,6 +991,7 @@ SDL_JoystickClose(SDL_Joystick * joystick)
{ {
SDL_Joystick *joysticklist; SDL_Joystick *joysticklist;
SDL_Joystick *joysticklistprev; SDL_Joystick *joysticklistprev;
int i;
if (!SDL_PrivateJoystickValid(joystick)) { if (!SDL_PrivateJoystickValid(joystick)) {
return; return;
@ -1042,6 +1043,11 @@ SDL_JoystickClose(SDL_Joystick * joystick)
SDL_free(joystick->hats); SDL_free(joystick->hats);
SDL_free(joystick->balls); SDL_free(joystick->balls);
SDL_free(joystick->buttons); SDL_free(joystick->buttons);
for (i = 0; i < joystick->ntouchpads; i++) {
SDL_JoystickTouchpadInfo *touchpad = &joystick->touchpads[i];
SDL_free(touchpad->fingers);
}
SDL_free(joystick->touchpads);
SDL_free(joystick); SDL_free(joystick);
SDL_UnlockJoysticks(); SDL_UnlockJoysticks();
@ -1111,6 +1117,28 @@ SDL_PrivateJoystickShouldIgnoreEvent()
/* These are global for SDL_sysjoystick.c and SDL_events.c */ /* These are global for SDL_sysjoystick.c and SDL_events.c */
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));
if (touchpads) {
SDL_JoystickTouchpadInfo *touchpad = &touchpads[ntouchpads - 1];
SDL_JoystickTouchpadFingerInfo *fingers = (SDL_JoystickTouchpadFingerInfo *)SDL_calloc(nfingers, sizeof(SDL_JoystickTouchpadFingerInfo));
if (fingers) {
touchpad->nfingers = nfingers;
touchpad->fingers = fingers;
} else {
/* Out of memory, this touchpad won't be active */
touchpad->nfingers = 0;
touchpad->fingers = NULL;
}
joystick->ntouchpads = ntouchpads;
joystick->touchpads = touchpads;
}
}
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance) void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
{ {
SDL_JoystickDriver *driver; SDL_JoystickDriver *driver;
@ -1180,7 +1208,7 @@ static void UpdateEventsForDeviceRemoval()
static void static void
SDL_PrivateJoystickForceRecentering(SDL_Joystick *joystick) SDL_PrivateJoystickForceRecentering(SDL_Joystick *joystick)
{ {
int i; int i, j;
/* Tell the app that everything is centered/unpressed... */ /* Tell the app that everything is centered/unpressed... */
for (i = 0; i < joystick->naxes; i++) { for (i = 0; i < joystick->naxes; i++) {
@ -1190,12 +1218,21 @@ SDL_PrivateJoystickForceRecentering(SDL_Joystick *joystick)
} }
for (i = 0; i < joystick->nbuttons; i++) { for (i = 0; i < joystick->nbuttons; i++) {
SDL_PrivateJoystickButton(joystick, i, 0); SDL_PrivateJoystickButton(joystick, i, SDL_RELEASED);
} }
for (i = 0; i < joystick->nhats; i++) { for (i = 0; i < joystick->nhats; i++) {
SDL_PrivateJoystickHat(joystick, i, SDL_HAT_CENTERED); SDL_PrivateJoystickHat(joystick, i, SDL_HAT_CENTERED);
} }
for (i = 0; i < joystick->ntouchpads; i++) {
SDL_JoystickTouchpadInfo *touchpad = &joystick->touchpads[j];
for (j = 0; j < touchpad->nfingers; ++j) {
SDL_PrivateJoystickTouchpad(joystick, i, j, SDL_RELEASED, 0.0f, 0.0f, 0.0f);
}
}
} }
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance) void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
@ -2425,4 +2462,89 @@ SDL_JoystickPowerLevel SDL_JoystickCurrentPowerLevel(SDL_Joystick * joystick)
return joystick->epowerlevel; return joystick->epowerlevel;
} }
int SDL_PrivateJoystickTouchpad(SDL_Joystick *joystick, int touchpad, int finger, Uint8 state, float x, float y, float pressure)
{
SDL_JoystickTouchpadInfo *touchpad_info;
SDL_JoystickTouchpadFingerInfo *finger_info;
#if !SDL_EVENTS_DISABLED
int posted;
Uint32 event_type;
#endif
if (touchpad < 0 || touchpad >= joystick->ntouchpads) {
return 0;
}
touchpad_info = &joystick->touchpads[touchpad];
if (finger < 0 || finger >= touchpad_info->nfingers) {
return 0;
}
finger_info = &touchpad_info->fingers[finger];
if (!state) {
if (!x && !y) {
x = finger_info->x;
y = finger_info->y;
}
pressure = 0.0f;
}
if (x < 0.0f) {
x = 0.0f;
} else if (x > 1.0f) {
x = 1.0f;
}
if (y < 0.0f) {
y = 0.0f;
} else if (y > 1.0f) {
y = 1.0f;
}
if (pressure < 0.0f) {
pressure = 0.0f;
} else if (pressure > 1.0f) {
pressure = 1.0f;
}
if (state == finger_info->state) {
if (!state ||
(x == finger_info->x && y == finger_info->y && pressure == finger_info->pressure)) {
return 0;
}
}
#if !SDL_EVENTS_DISABLED
if (state == finger_info->state) {
event_type = SDL_CONTROLLERTOUCHPADMOTION;
} else if (state) {
event_type = SDL_CONTROLLERTOUCHPADDOWN;
} else {
event_type = SDL_CONTROLLERTOUCHPADUP;
}
#endif
/* Update internal joystick state */
finger_info->state = state;
finger_info->x = x;
finger_info->y = y;
finger_info->pressure = pressure;
/* Post the event, if desired */
posted = 0;
#if !SDL_EVENTS_DISABLED
if (SDL_GetEventState(event_type) == SDL_ENABLE) {
SDL_Event event;
event.type = event_type;
event.ctouchpad.which = joystick->instance_id;
event.ctouchpad.touchpad = touchpad;
event.ctouchpad.finger = finger;
event.ctouchpad.x = x;
event.ctouchpad.y = y;
event.ctouchpad.pressure = pressure;
posted = SDL_PushEvent(&event) == 1;
}
#endif /* !SDL_EVENTS_DISABLED */
return posted;
}
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */

View File

@ -108,6 +108,7 @@ extern SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUI
extern void SDL_GameControllerHandleDelayedGuideButton(SDL_Joystick *joystick); extern void SDL_GameControllerHandleDelayedGuideButton(SDL_Joystick *joystick);
/* Internal event queueing functions */ /* Internal event queueing functions */
extern void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers);
extern void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance); extern void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance);
extern void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance); extern void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance);
extern int SDL_PrivateJoystickAxis(SDL_Joystick *joystick, extern int SDL_PrivateJoystickAxis(SDL_Joystick *joystick,
@ -118,6 +119,8 @@ extern int SDL_PrivateJoystickHat(SDL_Joystick * joystick,
Uint8 hat, Uint8 value); Uint8 hat, Uint8 value);
extern int SDL_PrivateJoystickButton(SDL_Joystick *joystick, extern int SDL_PrivateJoystickButton(SDL_Joystick *joystick,
Uint8 button, Uint8 state); Uint8 button, Uint8 state);
extern int SDL_PrivateJoystickTouchpad(SDL_Joystick *joystick,
int touchpad, int finger, Uint8 state, float x, float y, float pressure);
extern void SDL_PrivateJoystickBatteryLevel(SDL_Joystick *joystick, extern void SDL_PrivateJoystickBatteryLevel(SDL_Joystick *joystick,
SDL_JoystickPowerLevel ePowerLevel); SDL_JoystickPowerLevel ePowerLevel);

View File

@ -39,6 +39,20 @@ typedef struct _SDL_JoystickAxisInfo
SDL_bool sent_initial_value; /* Whether we've sent the initial axis value */ SDL_bool sent_initial_value; /* Whether we've sent the initial axis value */
} SDL_JoystickAxisInfo; } SDL_JoystickAxisInfo;
typedef struct _SDL_JoystickTouchpadFingerInfo
{
Uint8 state;
float x;
float y;
float pressure;
} SDL_JoystickTouchpadFingerInfo;
typedef struct _SDL_JoystickTouchpadInfo
{
int nfingers;
SDL_JoystickTouchpadFingerInfo *fingers;
} SDL_JoystickTouchpadInfo;
struct _SDL_Joystick struct _SDL_Joystick
{ {
SDL_JoystickID instance_id; /* Device instance, monotonically increasing from 0 */ SDL_JoystickID instance_id; /* Device instance, monotonically increasing from 0 */
@ -60,6 +74,9 @@ struct _SDL_Joystick
int nbuttons; /* Number of buttons on the joystick */ int nbuttons; /* Number of buttons on the joystick */
Uint8 *buttons; /* Current button states */ Uint8 *buttons; /* Current button states */
int ntouchpads; /* Number of touchpads on the joystick */
SDL_JoystickTouchpadInfo *touchpads; /* Current touchpad states */
Uint16 low_frequency_rumble; Uint16 low_frequency_rumble;
Uint16 high_frequency_rumble; Uint16 high_frequency_rumble;
Uint32 rumble_expiration; Uint32 rumble_expiration;

View File

@ -80,10 +80,10 @@ typedef struct
Uint8 _rgucPad1[ 5 ]; Uint8 _rgucPad1[ 5 ];
Uint8 ucBatteryLevel; Uint8 ucBatteryLevel;
Uint8 _rgucPad2[ 4 ]; Uint8 _rgucPad2[ 4 ];
Uint8 ucTrackpadCounter1; Uint8 ucTouchpadCounter1;
Uint8 rgucTrackpadData1[ 3 ]; Uint8 rgucTouchpadData1[ 3 ];
Uint8 ucTrackpadCounter2; Uint8 ucTouchpadCounter2;
Uint8 rgucTrackpadData2[ 3 ]; Uint8 rgucTouchpadData2[ 3 ];
} PS4StatePacket_t; } PS4StatePacket_t;
typedef struct typedef struct
@ -310,6 +310,8 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
joystick->naxes = SDL_CONTROLLER_AXIS_MAX; joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
SDL_PrivateJoystickAddTouchpad(joystick, 2);
return SDL_TRUE; return SDL_TRUE;
} }
@ -402,7 +404,11 @@ HIDAPI_DriverPS4_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystic
static void static void
HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet) HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS4_Context *ctx, PS4StatePacket_t *packet)
{ {
static const float TOUCHPAD_SCALEX = 1.0f / 1920;
static const float TOUCHPAD_SCALEY = 1.0f / 920; /* This is noted as being 944 resolution, but 920 feels better */
Sint16 axis; Sint16 axis;
Uint8 touchpad_state;
int touchpad_x, touchpad_y;
if (ctx->last_state.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) { if (ctx->last_state.rgucButtonsHatAndCounter[0] != packet->rgucButtonsHatAndCounter[0]) {
{ {
@ -515,6 +521,16 @@ HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_
} }
} }
touchpad_state = ((packet->ucTouchpadCounter1 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8);
touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4);
SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
touchpad_state = ((packet->ucTouchpadCounter2 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8);
touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4);
SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state)); SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));
} }

View File

@ -73,13 +73,13 @@ typedef struct
Uint8 rgucGyro[6]; /* 21 */ Uint8 rgucGyro[6]; /* 21 */
Uint8 rgucTimer1[4]; /* 27 - 32 bit little endian */ Uint8 rgucTimer1[4]; /* 27 - 32 bit little endian */
Uint8 ucBatteryTemp; /* 31 */ Uint8 ucBatteryTemp; /* 31 */
Uint8 ucTrackpadCounter1; /* 32 - high bit clear + counter */ Uint8 ucTouchpadCounter1; /* 32 - high bit clear + counter */
Uint8 rgucTouchpadData1[3]; /* 33 - X/Y, 12 bits per axis */ Uint8 rgucTouchpadData1[3]; /* 33 - X/Y, 12 bits per axis */
Uint8 ucTrackpadCounter2; /* 36 - high bit clear + counter */ Uint8 ucTouchpadCounter2; /* 36 - high bit clear + counter */
Uint8 rgucTouchpadData2[3]; /* 37 - X/Y, 12 bits per axis */ Uint8 rgucTouchpadData2[3]; /* 37 - X/Y, 12 bits per axis */
Uint8 rgucUnknown1[8]; /* 40 */ Uint8 rgucUnknown1[8]; /* 40 */
Uint8 rgucTimer2[4]; /* 48 - 32 bit little endian */ Uint8 rgucTimer2[4]; /* 48 - 32 bit little endian */
Uint8 ucBatteryState; /* 52 - 0x13 on USB, 0x05 - 0x06 on Bluetooth ? */ Uint8 ucBatteryLevel; /* 52 */
Uint8 ucConnectState; /* 53 - 0x08 = USB, 0x03 = headphone */ Uint8 ucConnectState; /* 53 - 0x08 = USB, 0x03 = headphone */
/* There's more unknown data at the end, and a 32-bit CRC on Bluetooth */ /* There's more unknown data at the end, and a 32-bit CRC on Bluetooth */
@ -168,8 +168,11 @@ HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
ReadFeatureReport(device->dev, k_EPS5FeatureReportIdSerialNumber); ReadFeatureReport(device->dev, k_EPS5FeatureReportIdSerialNumber);
/* Initialize the joystick capabilities */ /* Initialize the joystick capabilities */
joystick->nbuttons = 16; joystick->nbuttons = 17;
joystick->naxes = SDL_CONTROLLER_AXIS_MAX; joystick->naxes = SDL_CONTROLLER_AXIS_MAX;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
SDL_PrivateJoystickAddTouchpad(joystick, 2);
return SDL_TRUE; return SDL_TRUE;
} }
@ -295,7 +298,11 @@ HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, hid_device *dev
static void static void
HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacket_t *packet) HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverPS5_Context *ctx, PS5StatePacket_t *packet)
{ {
static const float TOUCHPAD_SCALEX = 1.0f / 1920;
static const float TOUCHPAD_SCALEY = 1.0f / 1070;
Sint16 axis; Sint16 axis;
Uint8 touchpad_state;
int touchpad_x, touchpad_y;
if (ctx->last_state.state.rgucButtonsAndHat[0] != packet->rgucButtonsAndHat[0]) { if (ctx->last_state.state.rgucButtonsAndHat[0] != packet->rgucButtonsAndHat[0]) {
{ {
@ -364,10 +371,11 @@ HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_
} }
if (ctx->last_state.state.rgucButtonsAndHat[2] != packet->rgucButtonsAndHat[2]) { if (ctx->last_state.state.rgucButtonsAndHat[2] != packet->rgucButtonsAndHat[2]) {
Uint8 data = (packet->rgucButtonsAndHat[2] & 0x03); Uint8 data = packet->rgucButtonsAndHat[2];
SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED); SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (data & 0x01) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, 15, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED); SDL_PrivateJoystickButton(joystick, 15, (data & 0x04) ? SDL_PRESSED : SDL_RELEASED);
SDL_PrivateJoystickButton(joystick, 16, (data & 0x02) ? SDL_PRESSED : SDL_RELEASED);
} }
axis = ((int)packet->ucTriggerLeft * 257) - 32768; axis = ((int)packet->ucTriggerLeft * 257) - 32768;
@ -383,6 +391,32 @@ HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_
axis = ((int)packet->ucRightJoystickY * 257) - 32768; axis = ((int)packet->ucRightJoystickY * 257) - 32768;
SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis); SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis);
if (packet->ucBatteryLevel & 0x10) {
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
} else {
/* Battery level ranges from 0 to 10 */
int level = (packet->ucBatteryLevel & 0xF);
if (level == 0) {
joystick->epowerlevel = SDL_JOYSTICK_POWER_EMPTY;
} else if (level <= 2) {
joystick->epowerlevel = SDL_JOYSTICK_POWER_LOW;
} else if (level <= 7) {
joystick->epowerlevel = SDL_JOYSTICK_POWER_MEDIUM;
} else {
joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
}
}
touchpad_state = ((packet->ucTouchpadCounter1 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
touchpad_x = packet->rgucTouchpadData1[0] | (((int)packet->rgucTouchpadData1[1] & 0x0F) << 8);
touchpad_y = (packet->rgucTouchpadData1[1] >> 4) | ((int)packet->rgucTouchpadData1[2] << 4);
SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
touchpad_state = ((packet->ucTouchpadCounter2 & 0x80) == 0) ? SDL_PRESSED : SDL_RELEASED;
touchpad_x = packet->rgucTouchpadData2[0] | (((int)packet->rgucTouchpadData2[1] & 0x0F) << 8);
touchpad_y = (packet->rgucTouchpadData2[1] >> 4) | ((int)packet->rgucTouchpadData2[2] << 4);
SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f);
SDL_memcpy(&ctx->last_state.state, packet, sizeof(ctx->last_state.state)); SDL_memcpy(&ctx->last_state.state, packet, sizeof(ctx->last_state.state));
} }

View File

@ -584,6 +584,10 @@ IOS_JoystickOpen(SDL_Joystick * joystick, int device_index)
joystick->nbuttons = device->nbuttons; joystick->nbuttons = device->nbuttons;
joystick->nballs = 0; joystick->nballs = 0;
if (device->has_dualshock_touchpad) {
SDL_PrivateJoystickAddTouchpad(joystick, 2);
}
device->joystick = joystick; device->joystick = joystick;
@autoreleasepool { @autoreleasepool {
@ -750,6 +754,22 @@ IOS_MFIJoystickUpdate(SDL_Joystick * joystick)
#ifdef ENABLE_PHYSICAL_INPUT_PROFILE #ifdef ENABLE_PHYSICAL_INPUT_PROFILE
if (joystick->hwdata->has_dualshock_touchpad) { if (joystick->hwdata->has_dualshock_touchpad) {
buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputDualShockTouchpadButton].isPressed; buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputDualShockTouchpadButton].isPressed;
GCControllerDirectionPad *dpad;
dpad = controller.physicalInputProfile.dpads[GCInputDualShockTouchpadOne];
if (dpad.xAxis.value || dpad.yAxis.value) {
SDL_PrivateJoystickTouchpad(joystick, 0, 0, SDL_PRESSED, (1.0f + dpad.xAxis.value) * 0.5f, 1.0f - (1.0f + dpad.yAxis.value) * 0.5f, 1.0f);
} else {
SDL_PrivateJoystickTouchpad(joystick, 0, 0, SDL_RELEASED, 0.0f, 0.0f, 1.0f);
}
dpad = controller.physicalInputProfile.dpads[GCInputDualShockTouchpadTwo];
if (dpad.xAxis.value || dpad.yAxis.value) {
SDL_PrivateJoystickTouchpad(joystick, 0, 1, SDL_PRESSED, (1.0f + dpad.xAxis.value) * 0.5f, 1.0f - (1.0f + dpad.yAxis.value) * 0.5f, 1.0f);
} else {
SDL_PrivateJoystickTouchpad(joystick, 0, 1, SDL_RELEASED, 0.0f, 0.0f, 1.0f);
}
} }
if (joystick->hwdata->has_xbox_paddles) { if (joystick->hwdata->has_xbox_paddles) {

View File

@ -157,13 +157,29 @@ loop(void *arg)
} }
break; break;
case SDL_CONTROLLERTOUCHPADDOWN:
case SDL_CONTROLLERTOUCHPADMOTION:
case SDL_CONTROLLERTOUCHPADUP:
SDL_Log("Controller touchpad %d finger %d %s %.2f, %.2f, %.2f\n",
event.ctouchpad.touchpad,
event.ctouchpad.finger,
(event.type == SDL_CONTROLLERTOUCHPADDOWN ? "pressed at" :
(event.type == SDL_CONTROLLERTOUCHPADUP ? "released at" :
"moved to")),
event.ctouchpad.x,
event.ctouchpad.y,
event.ctouchpad.pressure);
break;
case SDL_CONTROLLERAXISMOTION: case SDL_CONTROLLERAXISMOTION:
SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)event.caxis.axis), event.caxis.value); SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)event.caxis.axis), event.caxis.value);
break; break;
case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP: case SDL_CONTROLLERBUTTONUP:
SDL_Log("Controller button %s %s\n", SDL_GameControllerGetStringForButton((SDL_GameControllerButton)event.cbutton.button), event.cbutton.state ? "pressed" : "released"); SDL_Log("Controller button %s %s\n", SDL_GameControllerGetStringForButton((SDL_GameControllerButton)event.cbutton.button), event.cbutton.state ? "pressed" : "released");
break; break;
case SDL_KEYDOWN: case SDL_KEYDOWN:
if (event.key.keysym.sym != SDLK_ESCAPE) { if (event.key.keysym.sym != SDLK_ESCAPE) {
break; break;
@ -194,7 +210,7 @@ loop(void *arg)
if (gamecontroller) { if (gamecontroller) {
/* Update visual controller state */ /* Update visual controller state */
for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) { for (i = 0; i < SDL_CONTROLLER_BUTTON_TOUCHPAD; ++i) {
if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) { if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) {
SDL_bool on_front = (i < SDL_CONTROLLER_BUTTON_PADDLE1 || i > SDL_CONTROLLER_BUTTON_PADDLE4); SDL_bool on_front = (i < SDL_CONTROLLER_BUTTON_PADDLE1 || i > SDL_CONTROLLER_BUTTON_PADDLE4);
if (on_front == showing_front) { if (on_front == showing_front) {