From 9f51fad36188de6f539bb9cd3bbea458798252df Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 13 Nov 2020 18:01:29 -0800 Subject: [PATCH] Added support for the touchpad on PS4 and PS5 controllers --- include/SDL_events.h | 74 +++++++++----- include/SDL_gamecontroller.h | 40 ++++++-- src/dynapi/SDL_dynapi_overrides.h | 5 + src/dynapi/SDL_dynapi_procs.h | 5 + src/joystick/SDL_gamecontroller.c | 93 ++++++++++++++++- src/joystick/SDL_gamecontrollerdb.h | 2 +- src/joystick/SDL_joystick.c | 126 +++++++++++++++++++++++- src/joystick/SDL_joystick_c.h | 15 +-- src/joystick/SDL_sysjoystick.h | 17 ++++ src/joystick/hidapi/SDL_hidapi_ps4.c | 24 ++++- src/joystick/hidapi/SDL_hidapi_ps5.c | 56 ++++++++--- src/joystick/iphoneos/SDL_sysjoystick.m | 20 ++++ test/testgamecontroller.c | 18 +++- 13 files changed, 434 insertions(+), 61 deletions(-) diff --git a/include/SDL_events.h b/include/SDL_events.h index 74cbb4b59..a9da3fa2e 100644 --- a/include/SDL_events.h +++ b/include/SDL_events.h @@ -125,6 +125,9 @@ typedef enum SDL_CONTROLLERDEVICEADDED, /**< A new Game controller has been inserted into the system */ SDL_CONTROLLERDEVICEREMOVED, /**< An opened Game controller has been removed */ 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 */ 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 */ } 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.*) */ @@ -559,33 +578,34 @@ typedef struct SDL_SysWMEvent */ typedef union SDL_Event { - Uint32 type; /**< Event type, shared with all events */ - SDL_CommonEvent common; /**< Common event data */ - SDL_DisplayEvent display; /**< Display event data */ - SDL_WindowEvent window; /**< Window event data */ - SDL_KeyboardEvent key; /**< Keyboard event data */ - SDL_TextEditingEvent edit; /**< Text editing event data */ - SDL_TextInputEvent text; /**< Text input event data */ - SDL_MouseMotionEvent motion; /**< Mouse motion event data */ - SDL_MouseButtonEvent button; /**< Mouse button event data */ - SDL_MouseWheelEvent wheel; /**< Mouse wheel event data */ - SDL_JoyAxisEvent jaxis; /**< Joystick axis event data */ - SDL_JoyBallEvent jball; /**< Joystick ball event data */ - SDL_JoyHatEvent jhat; /**< Joystick hat event data */ - SDL_JoyButtonEvent jbutton; /**< Joystick button event data */ - SDL_JoyDeviceEvent jdevice; /**< Joystick device change event data */ - SDL_ControllerAxisEvent caxis; /**< Game Controller axis event data */ - SDL_ControllerButtonEvent cbutton; /**< Game Controller button event data */ - SDL_ControllerDeviceEvent cdevice; /**< Game Controller device event data */ - SDL_AudioDeviceEvent adevice; /**< Audio device event data */ - SDL_SensorEvent sensor; /**< Sensor event data */ - SDL_QuitEvent quit; /**< Quit request event data */ - SDL_UserEvent user; /**< Custom event data */ - SDL_SysWMEvent syswm; /**< System dependent window event data */ - SDL_TouchFingerEvent tfinger; /**< Touch finger event data */ - SDL_MultiGestureEvent mgesture; /**< Gesture event data */ - SDL_DollarGestureEvent dgesture; /**< Gesture event data */ - SDL_DropEvent drop; /**< Drag and drop event data */ + Uint32 type; /**< Event type, shared with all events */ + SDL_CommonEvent common; /**< Common event data */ + SDL_DisplayEvent display; /**< Display event data */ + SDL_WindowEvent window; /**< Window event data */ + SDL_KeyboardEvent key; /**< Keyboard event data */ + SDL_TextEditingEvent edit; /**< Text editing event data */ + SDL_TextInputEvent text; /**< Text input event data */ + SDL_MouseMotionEvent motion; /**< Mouse motion event data */ + SDL_MouseButtonEvent button; /**< Mouse button event data */ + SDL_MouseWheelEvent wheel; /**< Mouse wheel event data */ + SDL_JoyAxisEvent jaxis; /**< Joystick axis event data */ + SDL_JoyBallEvent jball; /**< Joystick ball event data */ + SDL_JoyHatEvent jhat; /**< Joystick hat event data */ + SDL_JoyButtonEvent jbutton; /**< Joystick button event data */ + SDL_JoyDeviceEvent jdevice; /**< Joystick device change event data */ + SDL_ControllerAxisEvent caxis; /**< Game Controller axis event data */ + 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_AudioDeviceEvent adevice; /**< Audio device event data */ + SDL_SensorEvent sensor; /**< Sensor event data */ + SDL_QuitEvent quit; /**< Quit request event data */ + SDL_UserEvent user; /**< Custom event data */ + SDL_SysWMEvent syswm; /**< System dependent window event data */ + SDL_TouchFingerEvent tfinger; /**< Touch finger event data */ + SDL_MultiGestureEvent mgesture; /**< Gesture event data */ + SDL_DollarGestureEvent dgesture; /**< Gesture event data */ + SDL_DropEvent drop; /**< Drag and drop event data */ /* This is necessary for ABI compatibility between Visual C++ and GCC Visual C++ will respect the push pack pragma and use 52 bytes for diff --git a/include/SDL_gamecontroller.h b/include/SDL_gamecontroller.h index 7ef500bb4..8498e89ee 100644 --- a/include/SDL_gamecontroller.h +++ b/include/SDL_gamecontroller.h @@ -330,6 +330,12 @@ extern DECLSPEC SDL_GameControllerButtonBind SDLCALL SDL_GameControllerGetBindForAxis(SDL_GameController *gamecontroller, 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. * @@ -339,8 +345,7 @@ SDL_GameControllerGetBindForAxis(SDL_GameController *gamecontroller, * The axis indices start at index 0. */ extern DECLSPEC Sint16 SDLCALL -SDL_GameControllerGetAxis(SDL_GameController *gamecontroller, - SDL_GameControllerAxis axis); +SDL_GameControllerGetAxis(SDL_GameController *gamecontroller, SDL_GameControllerAxis axis); /** * The list of buttons available from a controller @@ -363,11 +368,12 @@ typedef enum SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_LEFT, 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_PADDLE1, // Xbox Elite paddle P1 - SDL_CONTROLLER_BUTTON_PADDLE2, // Xbox Elite paddle P3 - SDL_CONTROLLER_BUTTON_PADDLE3, // Xbox Elite paddle P2 - SDL_CONTROLLER_BUTTON_PADDLE4, // Xbox Elite paddle P4 + 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_PADDLE2, /* Xbox Elite paddle P3 */ + SDL_CONTROLLER_BUTTON_PADDLE3, /* Xbox Elite paddle P2 */ + SDL_CONTROLLER_BUTTON_PADDLE4, /* Xbox Elite paddle P4 */ + SDL_CONTROLLER_BUTTON_TOUCHPAD, /* PS4/PS5 touchpad button */ SDL_CONTROLLER_BUTTON_MAX } SDL_GameControllerButton; @@ -388,6 +394,11 @@ extern DECLSPEC SDL_GameControllerButtonBind SDLCALL SDL_GameControllerGetBindForButton(SDL_GameController *gamecontroller, 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. @@ -397,6 +408,21 @@ SDL_GameControllerGetBindForButton(SDL_GameController *gamecontroller, extern DECLSPEC Uint8 SDLCALL SDL_GameControllerGetButton(SDL_GameController *gamecontroller, 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 * Each call to this function cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling. diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 4a596a948..9f2ec4423 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -774,3 +774,8 @@ #define SDL_JoystickSetLED SDL_JoystickSetLED_REAL #define SDL_GameControllerRumbleTriggers SDL_GameControllerRumbleTriggers_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 diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index d65153119..a7584c041 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -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_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(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) diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index 9cb9d4fd3..f5d18eaec 100644 --- a/src/joystick/SDL_gamecontroller.c +++ b/src/joystick/SDL_gamecontroller.c @@ -580,9 +580,12 @@ static ControllerMapping_t *SDL_CreateMappingForHIDAPIController(SDL_JoystickGUI } else { switch (SDL_GetJoystickGameControllerTypeFromGUID(guid, NULL)) { 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: - /* PS4/PS5 controllers have an additional touchpad button */ - SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string)); + /* PS5 controllers have a microphone button and an additional touchpad button */ + SDL_strlcat(mapping_string, "misc1:b15,touchpad:b16", sizeof(mapping_string)); break; case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO: /* Nintendo Switch Pro controllers have a screenshot button */ @@ -713,6 +716,7 @@ static const char* map_StringForControllerButton[] = { "paddle2", "paddle3", "paddle4", + "touchpad", NULL }; @@ -1870,6 +1874,16 @@ SDL_GameControllerUpdate(void) 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 */ @@ -1929,6 +1943,16 @@ SDL_GameControllerGetAxis(SDL_GameController * gamecontroller, SDL_GameControlle 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 */ @@ -1970,6 +1994,71 @@ SDL_GameControllerGetButton(SDL_GameController * gamecontroller, SDL_GameControl 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 * SDL_GameControllerName(SDL_GameController * gamecontroller) { diff --git a/src/joystick/SDL_gamecontrollerdb.h b/src/joystick/SDL_gamecontrollerdb.h index f559acf3f..6fd9a7f25 100644 --- a/src/joystick/SDL_gamecontrollerdb.h +++ b/src/joystick/SDL_gamecontrollerdb.h @@ -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,", "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,", - "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,", "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,", diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 94838f6d5..ee5493052 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -991,6 +991,7 @@ SDL_JoystickClose(SDL_Joystick * joystick) { SDL_Joystick *joysticklist; SDL_Joystick *joysticklistprev; + int i; if (!SDL_PrivateJoystickValid(joystick)) { return; @@ -1042,6 +1043,11 @@ SDL_JoystickClose(SDL_Joystick * joystick) SDL_free(joystick->hats); SDL_free(joystick->balls); 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_UnlockJoysticks(); @@ -1111,6 +1117,28 @@ SDL_PrivateJoystickShouldIgnoreEvent() /* 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) { SDL_JoystickDriver *driver; @@ -1180,7 +1208,7 @@ static void UpdateEventsForDeviceRemoval() static void SDL_PrivateJoystickForceRecentering(SDL_Joystick *joystick) { - int i; + int i, j; /* Tell the app that everything is centered/unpressed... */ for (i = 0; i < joystick->naxes; i++) { @@ -1190,12 +1218,21 @@ SDL_PrivateJoystickForceRecentering(SDL_Joystick *joystick) } 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++) { 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) @@ -2425,4 +2462,89 @@ SDL_JoystickPowerLevel SDL_JoystickCurrentPowerLevel(SDL_Joystick * joystick) 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: */ diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h index ce96d5040..2839930d2 100644 --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -108,21 +108,24 @@ extern SDL_bool SDL_ShouldIgnoreGameController(const char *name, SDL_JoystickGUI extern void SDL_GameControllerHandleDelayedGuideButton(SDL_Joystick *joystick); /* Internal event queueing functions */ +extern void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers); extern void SDL_PrivateJoystickAdded(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, Uint8 axis, Sint16 value); -extern int SDL_PrivateJoystickBall(SDL_Joystick * joystick, +extern int SDL_PrivateJoystickBall(SDL_Joystick *joystick, Uint8 ball, Sint16 xrel, Sint16 yrel); -extern int SDL_PrivateJoystickHat(SDL_Joystick * joystick, +extern int SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value); -extern int SDL_PrivateJoystickButton(SDL_Joystick * joystick, +extern int SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state); -extern void SDL_PrivateJoystickBatteryLevel(SDL_Joystick * joystick, +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, SDL_JoystickPowerLevel ePowerLevel); /* Internal sanity checking functions */ -extern SDL_bool SDL_PrivateJoystickValid(SDL_Joystick * joystick); +extern SDL_bool SDL_PrivateJoystickValid(SDL_Joystick *joystick); typedef enum { diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h index 5cf10a719..02dc71ea2 100644 --- a/src/joystick/SDL_sysjoystick.h +++ b/src/joystick/SDL_sysjoystick.h @@ -39,6 +39,20 @@ typedef struct _SDL_JoystickAxisInfo SDL_bool sent_initial_value; /* Whether we've sent the initial axis value */ } 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 { 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 */ Uint8 *buttons; /* Current button states */ + int ntouchpads; /* Number of touchpads on the joystick */ + SDL_JoystickTouchpadInfo *touchpads; /* Current touchpad states */ + Uint16 low_frequency_rumble; Uint16 high_frequency_rumble; Uint32 rumble_expiration; diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c index 1c12aa2a9..f2de0efd7 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps4.c +++ b/src/joystick/hidapi/SDL_hidapi_ps4.c @@ -80,10 +80,10 @@ typedef struct Uint8 _rgucPad1[ 5 ]; Uint8 ucBatteryLevel; Uint8 _rgucPad2[ 4 ]; - Uint8 ucTrackpadCounter1; - Uint8 rgucTrackpadData1[ 3 ]; - Uint8 ucTrackpadCounter2; - Uint8 rgucTrackpadData2[ 3 ]; + Uint8 ucTouchpadCounter1; + Uint8 rgucTouchpadData1[ 3 ]; + Uint8 ucTouchpadCounter2; + Uint8 rgucTouchpadData2[ 3 ]; } PS4StatePacket_t; typedef struct @@ -310,6 +310,8 @@ HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) joystick->naxes = SDL_CONTROLLER_AXIS_MAX; joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; + SDL_PrivateJoystickAddTouchpad(joystick, 2); + return SDL_TRUE; } @@ -402,7 +404,11 @@ HIDAPI_DriverPS4_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystic static void 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; + Uint8 touchpad_state; + int touchpad_x, touchpad_y; 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)); } diff --git a/src/joystick/hidapi/SDL_hidapi_ps5.c b/src/joystick/hidapi/SDL_hidapi_ps5.c index f692c5003..6dc485e41 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps5.c +++ b/src/joystick/hidapi/SDL_hidapi_ps5.c @@ -52,7 +52,7 @@ typedef struct Uint8 ucLeftJoystickY; Uint8 ucRightJoystickX; Uint8 ucRightJoystickY; - Uint8 rgucButtonsHatAndCounter[ 3 ]; + Uint8 rgucButtonsHatAndCounter[3]; Uint8 ucTriggerLeft; Uint8 ucTriggerRight; } PS5SimpleStatePacket_t; @@ -66,20 +66,20 @@ typedef struct Uint8 ucTriggerLeft; /* 4 */ Uint8 ucTriggerRight; /* 5 */ Uint8 ucCounter; /* 6 */ - Uint8 rgucButtonsAndHat[ 3 ]; /* 7 */ + Uint8 rgucButtonsAndHat[3]; /* 7 */ Uint8 ucZero; /* 10 */ Uint8 rgucPacketSequence[4]; /* 11 - 32 bit little endian */ - Uint8 rgucAccel[ 6 ]; /* 15 */ - Uint8 rgucGyro[ 6 ]; /* 21 */ + Uint8 rgucAccel[6]; /* 15 */ + Uint8 rgucGyro[6]; /* 21 */ Uint8 rgucTimer1[4]; /* 27 - 32 bit little endian */ 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 ucTrackpadCounter2; /* 36 - high bit clear + counter */ + Uint8 ucTouchpadCounter2; /* 36 - high bit clear + counter */ 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 ucBatteryState; /* 52 - 0x13 on USB, 0x05 - 0x06 on Bluetooth ? */ + Uint8 ucBatteryLevel; /* 52 */ Uint8 ucConnectState; /* 53 - 0x08 = USB, 0x03 = headphone */ /* 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); /* Initialize the joystick capabilities */ - joystick->nbuttons = 16; + joystick->nbuttons = 17; joystick->naxes = SDL_CONTROLLER_AXIS_MAX; + joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; + + SDL_PrivateJoystickAddTouchpad(joystick, 2); return SDL_TRUE; } @@ -295,7 +298,11 @@ HIDAPI_DriverPS5_HandleSimpleStatePacket(SDL_Joystick *joystick, hid_device *dev static void 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; + Uint8 touchpad_state; + int touchpad_x, touchpad_y; 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]) { - 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, 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; @@ -383,6 +391,32 @@ HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_ axis = ((int)packet->ucRightJoystickY * 257) - 32768; 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)); } diff --git a/src/joystick/iphoneos/SDL_sysjoystick.m b/src/joystick/iphoneos/SDL_sysjoystick.m index 57e9e9d1f..8946762d8 100644 --- a/src/joystick/iphoneos/SDL_sysjoystick.m +++ b/src/joystick/iphoneos/SDL_sysjoystick.m @@ -584,6 +584,10 @@ IOS_JoystickOpen(SDL_Joystick * joystick, int device_index) joystick->nbuttons = device->nbuttons; joystick->nballs = 0; + if (device->has_dualshock_touchpad) { + SDL_PrivateJoystickAddTouchpad(joystick, 2); + } + device->joystick = joystick; @autoreleasepool { @@ -750,6 +754,22 @@ IOS_MFIJoystickUpdate(SDL_Joystick * joystick) #ifdef ENABLE_PHYSICAL_INPUT_PROFILE if (joystick->hwdata->has_dualshock_touchpad) { 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) { diff --git a/test/testgamecontroller.c b/test/testgamecontroller.c index 676af8b57..910231460 100644 --- a/test/testgamecontroller.c +++ b/test/testgamecontroller.c @@ -157,13 +157,29 @@ loop(void *arg) } 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: SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)event.caxis.axis), event.caxis.value); break; + case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONUP: SDL_Log("Controller button %s %s\n", SDL_GameControllerGetStringForButton((SDL_GameControllerButton)event.cbutton.button), event.cbutton.state ? "pressed" : "released"); break; + case SDL_KEYDOWN: if (event.key.keysym.sym != SDLK_ESCAPE) { break; @@ -194,7 +210,7 @@ loop(void *arg) if (gamecontroller) { /* 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) { SDL_bool on_front = (i < SDL_CONTROLLER_BUTTON_PADDLE1 || i > SDL_CONTROLLER_BUTTON_PADDLE4); if (on_front == showing_front) {