SDL: Replaced SDL_GetNumTouchFingers() and SDL_GetTouchFinger() with SDL_GetTouchFingers()

From 1862a62b5d22c90323ddb257d75974348c3c807e Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 15 Apr 2024 12:04:26 -0700
Subject: [PATCH] Replaced SDL_GetNumTouchFingers() and SDL_GetTouchFinger()
 with SDL_GetTouchFingers()

Fixes https://github.com/libsdl-org/SDL/issues/9484
---
 docs/README-migration.md          |  8 ++++--
 include/SDL3/SDL_touch.h          | 27 +++----------------
 src/dynapi/SDL_dynapi.sym         |  3 +--
 src/dynapi/SDL_dynapi_overrides.h |  3 +--
 src/dynapi/SDL_dynapi_procs.h     |  3 +--
 src/events/SDL_touch.c            | 43 +++++++++++++++++++------------
 src/video/cocoa/SDL_cocoawindow.m | 26 +++++++++++--------
 7 files changed, 54 insertions(+), 59 deletions(-)

diff --git a/docs/README-migration.md b/docs/README-migration.md
index 4d5590e5084c6..38a7399882cc1 100644
--- a/docs/README-migration.md
+++ b/docs/README-migration.md
@@ -1631,15 +1631,19 @@ If you were using this macro for other things besides SDL ticks values, you can
 
 ## SDL_touch.h
 
-SDL_GetNumTouchFingers() returns a negative error code if there was an error.
-
 SDL_GetTouchName is replaced with SDL_GetTouchDeviceName(), which takes an SDL_TouchID instead of an index.
 
 SDL_TouchID and SDL_FingerID are now Uint64 with 0 being an invalid value.
 
+Rather than iterating over touch devices using an index, there is a new function SDL_GetTouchDevices() to get the available devices.
+
+Rather than iterating over touch fingers using an index, there is a new function SDL_GetTouchFingers() to get the current set of active fingers.
+
 The following functions have been removed:
 * SDL_GetNumTouchDevices() - replaced with SDL_GetTouchDevices()
+* SDL_GetNumTouchFingers() - replaced with SDL_GetTouchFingers()
 * SDL_GetTouchDevice() - replaced with SDL_GetTouchDevices()
+* SDL_GetTouchFinger() - replaced with SDL_GetTouchFingers()
 
 
 ## SDL_version.h
diff --git a/include/SDL3/SDL_touch.h b/include/SDL3/SDL_touch.h
index d7a6da4d0994b..12ac290a6fd8f 100644
--- a/include/SDL3/SDL_touch.h
+++ b/include/SDL3/SDL_touch.h
@@ -117,34 +117,15 @@ extern DECLSPEC const char* SDLCALL SDL_GetTouchDeviceName(SDL_TouchID touchID);
 extern DECLSPEC SDL_TouchDeviceType SDLCALL SDL_GetTouchDeviceType(SDL_TouchID touchID);
 
 /**
- * Get the number of active fingers for a given touch device.
+ * Get a list of active fingers for a given touch device.
  *
  * \param touchID the ID of a touch device
- * \returns the number of active fingers for a given touch device on success
- *          or a negative error code on failure; call SDL_GetError() for more
- *          information.
+ * \param count a pointer filled in with the number of fingers returned, can be NULL.
+ * \returns a NULL terminated array of SDL_Finger pointers which should be freed with SDL_free(), or NULL on error; call SDL_GetError() for more details.
  *
  * \since This function is available since SDL 3.0.0.
- *
- * \sa SDL_GetTouchFinger
- */
-extern DECLSPEC int SDLCALL SDL_GetNumTouchFingers(SDL_TouchID touchID);
-
-/**
- * Get the finger object for specified touch device ID and finger index.
- *
- * The returned resource is owned by SDL and should not be deallocated.
- *
- * \param touchID the ID of the requested touch device
- * \param index the index of the requested finger
- * \returns a pointer to the SDL_Finger object or NULL if no object at the
- *          given ID and index could be found.
- *
- * \since This function is available since SDL 3.0.0.
- *
- * \sa SDL_GetNumTouchFingers
  */
-extern DECLSPEC SDL_Finger * SDLCALL SDL_GetTouchFinger(SDL_TouchID touchID, int index);
+extern DECLSPEC SDL_Finger **SDLCALL SDL_GetTouchFingers(SDL_TouchID touchID, int *count);
 
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index 802f6954d4304..a018f095473fd 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -346,7 +346,6 @@ SDL3_0.0.0 {
     SDL_GetNumJoystickButtons;
     SDL_GetNumJoystickHats;
     SDL_GetNumRenderDrivers;
-    SDL_GetNumTouchFingers;
     SDL_GetNumVideoDrivers;
     SDL_GetNumberProperty;
     SDL_GetOriginalMemoryFunctions;
@@ -455,7 +454,7 @@ SDL3_0.0.0 {
     SDL_GetTouchDeviceName;
     SDL_GetTouchDeviceType;
     SDL_GetTouchDevices;
-    SDL_GetTouchFinger;
+    SDL_GetTouchFingers;
     SDL_GetUserFolder;
     SDL_GetVersion;
     SDL_GetVideoDriver;
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index c45e30b7effa6..2c3200bf9f6d1 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -371,7 +371,6 @@
 #define SDL_GetNumJoystickButtons SDL_GetNumJoystickButtons_REAL
 #define SDL_GetNumJoystickHats SDL_GetNumJoystickHats_REAL
 #define SDL_GetNumRenderDrivers SDL_GetNumRenderDrivers_REAL
-#define SDL_GetNumTouchFingers SDL_GetNumTouchFingers_REAL
 #define SDL_GetNumVideoDrivers SDL_GetNumVideoDrivers_REAL
 #define SDL_GetNumberProperty SDL_GetNumberProperty_REAL
 #define SDL_GetOriginalMemoryFunctions SDL_GetOriginalMemoryFunctions_REAL
@@ -480,7 +479,7 @@
 #define SDL_GetTouchDeviceName SDL_GetTouchDeviceName_REAL
 #define SDL_GetTouchDeviceType SDL_GetTouchDeviceType_REAL
 #define SDL_GetTouchDevices SDL_GetTouchDevices_REAL
-#define SDL_GetTouchFinger SDL_GetTouchFinger_REAL
+#define SDL_GetTouchFingers SDL_GetTouchFingers_REAL
 #define SDL_GetUserFolder SDL_GetUserFolder_REAL
 #define SDL_GetVersion SDL_GetVersion_REAL
 #define SDL_GetVideoDriver SDL_GetVideoDriver_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 64e7d72a01afa..0104900d978b3 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -409,7 +409,6 @@ SDL_DYNAPI_PROC(int,SDL_GetNumJoystickBalls,(SDL_Joystick *a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_GetNumJoystickButtons,(SDL_Joystick *a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_GetNumJoystickHats,(SDL_Joystick *a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_GetNumRenderDrivers,(void),(),return)
-SDL_DYNAPI_PROC(int,SDL_GetNumTouchFingers,(SDL_TouchID a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_GetNumVideoDrivers,(void),(),return)
 SDL_DYNAPI_PROC(Sint64,SDL_GetNumberProperty,(SDL_PropertiesID a, const char *b, Sint64 c),(a,b,c),return)
 SDL_DYNAPI_PROC(void,SDL_GetOriginalMemoryFunctions,(SDL_malloc_func *a, SDL_calloc_func *b, SDL_realloc_func *c, SDL_free_func *d),(a,b,c,d),)
@@ -511,7 +510,7 @@ SDL_DYNAPI_PROC(Uint64,SDL_GetTicksNS,(void),(),return)
 SDL_DYNAPI_PROC(const char*,SDL_GetTouchDeviceName,(SDL_TouchID a),(a),return)
 SDL_DYNAPI_PROC(SDL_TouchDeviceType,SDL_GetTouchDeviceType,(SDL_TouchID a),(a),return)
 SDL_DYNAPI_PROC(SDL_TouchID*,SDL_GetTouchDevices,(int *a),(a),return)
-SDL_DYNAPI_PROC(SDL_Finger*,SDL_GetTouchFinger,(SDL_TouchID a, int b),(a,b),return)
+SDL_DYNAPI_PROC(SDL_Finger**,SDL_GetTouchFingers,(SDL_TouchID a, int *b),(a,b),return)
 SDL_DYNAPI_PROC(char*,SDL_GetUserFolder,(SDL_Folder a),(a),return)
 SDL_DYNAPI_PROC(int,SDL_GetVersion,(SDL_Version *a),(a),return)
 SDL_DYNAPI_PROC(const char*,SDL_GetVideoDriver,(int a),(a),return)
diff --git a/src/events/SDL_touch.c b/src/events/SDL_touch.c
index 98975e9ca028f..04651f314a313 100644
--- a/src/events/SDL_touch.c
+++ b/src/events/SDL_touch.c
@@ -131,26 +131,37 @@ static SDL_Finger *SDL_GetFinger(const SDL_Touch *touch, SDL_FingerID id)
     return touch->fingers[index];
 }
 
-int SDL_GetNumTouchFingers(SDL_TouchID touchID)
+SDL_Finger **SDL_GetTouchFingers(SDL_TouchID touchID, int *count)
 {
-    SDL_Touch *touch = SDL_GetTouch(touchID);
-    if (touch) {
-        return touch->num_fingers;
+    SDL_Finger **fingers;
+    SDL_Finger *finger_data;
+
+    if (count) {
+        *count = 0;
     }
-    return 0;
-}
 
-SDL_Finger *SDL_GetTouchFinger(SDL_TouchID touchID, int index)
-{
     SDL_Touch *touch = SDL_GetTouch(touchID);
     if (!touch) {
         return NULL;
     }
-    if (index < 0 || index >= touch->num_fingers) {
-        SDL_SetError("Unknown touch finger");
+
+    /* Create a snapshot of the current finger state */
+    fingers = (SDL_Finger **)SDL_malloc((touch->num_fingers + 1) * sizeof(*fingers) + touch->num_fingers * sizeof(**fingers));
+    if (!fingers) {
         return NULL;
     }
-    return touch->fingers[index];
+    finger_data = (SDL_Finger *)((Uint8 *)fingers + (touch->num_fingers + 1) * sizeof(*fingers));
+
+    for (int i = 0; i < touch->num_fingers; ++i) {
+        fingers[i] = &finger_data[i];
+        SDL_copyp(fingers[i], touch->fingers[i]);
+    }
+    fingers[touch->num_fingers] = NULL;
+
+    if (count) {
+        *count = touch->num_fingers;
+    }
+    return fingers;
 }
 
 int SDL_AddTouch(SDL_TouchID touchID, SDL_TouchDeviceType type, const char *name)
@@ -224,17 +235,15 @@ static int SDL_AddFinger(SDL_Touch *touch, SDL_FingerID fingerid, float x, float
 
 static int SDL_DelFinger(SDL_Touch *touch, SDL_FingerID fingerid)
 {
-    SDL_Finger *temp;
-
     int index = SDL_GetFingerIndex(touch, fingerid);
     if (index < 0) {
         return -1;
     }
 
-    touch->num_fingers--;
-    temp = touch->fingers[index];
-    touch->fingers[index] = touch->fingers[touch->num_fingers];
-    touch->fingers[touch->num_fingers] = temp;
+    if (index < (touch->num_fingers - 1)) {
+        SDL_memmove(&touch->fingers[index], &touch->fingers[index + 1], (touch->num_fingers - index - 1) * sizeof(touch->fingers[index]));
+    }
+    --touch->num_fingers;
     return 0;
 }
 
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index e18e4a3f0fdb6..01085419810be 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -1716,17 +1716,21 @@ - (void)touchesBeganWithEvent:(NSEvent *)theEvent
         }
     }
     if (existingTouchCount == 0) {
-        int numFingers = SDL_GetNumTouchFingers(touchID);
-        DLog("Reset Lost Fingers: %d", numFingers);
-        for (--numFingers; numFingers >= 0; --numFingers) {
-            SDL_Finger *finger = SDL_GetTouchFinger(touchID, numFingers);
-            /* trackpad touches have no window. If we really wanted one we could
-             * use the window that has mouse or keyboard focus.
-             * Sending a null window currently also prevents synthetic mouse
-             * events from being generated from touch events.
-             */
-            SDL_Window *window = NULL;
-            SDL_SendTouch(Cocoa_GetEventTimestamp([theEvent timestamp]), touchID, finger->id, window, SDL_FALSE, 0, 0, 0);
+        int numFingers;
+        SDL_Finger **fingers = SDL_GetTouchFingers(touchID, &numFingers);
+        if (fingers) {
+            DLog("Reset Lost Fingers: %d", numFingers);
+            for (--numFingers; numFingers >= 0; --numFingers) {
+                SDL_Finger *finger = fingers[numFingers];
+                /* trackpad touches have no window. If we really wanted one we could
+                 * use the window that has mouse or keyboard focus.
+                 * Sending a null window currently also prevents synthetic mouse
+                 * events from being generated from touch events.
+                 */
+                SDL_Window *window = NULL;
+                SDL_SendTouch(Cocoa_GetEventTimestamp([theEvent timestamp]), touchID, finger->id, window, SDL_FALSE, 0, 0, 0);
+            }
+            SDL_free(fingers);
         }
     }