Browse Source

Added support for the touchpad on PS4 and PS5 controllers

Sam Lantinga 4 years ago
parent
commit
9f51fad361

+ 47 - 27
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

+ 33 - 7
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.

+ 5 - 0
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

+ 5 - 0
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)

+ 91 - 2
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)
 {

+ 1 - 1
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,",

+ 124 - 2
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: */

+ 9 - 6
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
 {

+ 17 - 0
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;

+ 20 - 4
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));
 }
 

+ 45 - 11
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));
 }
 

+ 20 - 0
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) {

+ 17 - 1
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) {