SDL: Added infrastructure for reporting GameInput touchpads

From ae8a9107813d4a8bbb6c82c5633c1f64b8a0bb3c Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 17 Feb 2024 21:21:54 -0800
Subject: [PATCH] Added infrastructure for reporting GameInput touchpads

PlayStation controllers don't seem to report touch info, so we'll need to figure out how to interpret the touch data once it's available.
---
 src/joystick/gdk/SDL_gameinputjoystick.c | 59 ++++++++++++++----------
 1 file changed, 35 insertions(+), 24 deletions(-)

diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c
index ee302305b5ea..f402b231e95e 100644
--- a/src/joystick/gdk/SDL_gameinputjoystick.c
+++ b/src/joystick/gdk/SDL_gameinputjoystick.c
@@ -35,13 +35,9 @@ typedef struct GAMEINPUT_InternalDevice
     IGameInputDevice *device;
     char path[(APP_LOCAL_DEVICE_ID_SIZE * 2) + 1];
     char *name;
-    Uint16 vendor;
-    Uint16 product;
     SDL_JoystickGUID guid;          /* generated by SDL */
     SDL_JoystickID device_instance; /* generated by SDL */
-    SDL_bool wireless;
-    SDL_bool sensors_supported;
-    GameInputRumbleMotors supportedRumbleMotors;
+    const GameInputDeviceInfo *info;
     SDL_bool isAdded;
     SDL_bool isDeleteRequested;
 } GAMEINPUT_InternalDevice;
@@ -70,7 +66,7 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
 {
     GAMEINPUT_InternalDevice **devicelist = NULL;
     GAMEINPUT_InternalDevice *elem = NULL;
-    const GameInputDeviceInfo *devinfo = NULL;
+    const GameInputDeviceInfo *info = NULL;
     Uint16 bus = SDL_HARDWARE_BUS_USB;
     Uint16 vendor = 0;
     Uint16 product = 0;
@@ -80,15 +76,15 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
     char tmp[4];
     int idx = 0;
 
-    devinfo = IGameInputDevice_GetDeviceInfo(pDevice);
-    if (devinfo->capabilities & GameInputDeviceCapabilityWireless) {
+    info = IGameInputDevice_GetDeviceInfo(pDevice);
+    if (info->capabilities & GameInputDeviceCapabilityWireless) {
         bus = SDL_HARDWARE_BUS_BLUETOOTH;
     } else {
         bus = SDL_HARDWARE_BUS_USB;
     }
-    vendor = devinfo->vendorId;
-    product = devinfo->productId;
-    version = (devinfo->firmwareVersion.major << 8) | devinfo->firmwareVersion.minor;
+    vendor = info->vendorId;
+    product = info->productId;
+    version = (info->firmwareVersion.major << 8) | info->firmwareVersion.minor;
 
     if (SDL_JoystickHandledByAnotherDriver(&SDL_GAMEINPUT_JoystickDriver, vendor, product, version, "")) {
         return 0;
@@ -115,28 +111,24 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice)
 
     /* Generate a device path */
     for (idx = 0; idx < APP_LOCAL_DEVICE_ID_SIZE; ++idx) {
-        SDL_snprintf(tmp, SDL_arraysize(tmp), "%02hhX", devinfo->deviceId.value[idx]);
+        SDL_snprintf(tmp, SDL_arraysize(tmp), "%02hhX", info->deviceId.value[idx]);
         SDL_strlcat(elem->path, tmp, SDL_arraysize(tmp));
     }
 
-    if (devinfo->deviceStrings) {
+    if (info->deviceStrings) {
         /* In theory we could get the manufacturer and product strings here, but they're NULL for all the controllers I've tested */
     }
 
-    if (devinfo->displayName) {
+    if (info->displayName) {
         /* This could give us a product string, but it's NULL for all the controllers I've tested */
     }
 
     IGameInputDevice_AddRef(pDevice);
     elem->device = pDevice;
     elem->name = SDL_CreateJoystickName(vendor, product, manufacturer_string, product_string);
-    elem->vendor = vendor;
-    elem->product = product;
     elem->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, manufacturer_string, product_string, 'g', 0);
     elem->device_instance = SDL_GetNextObjectID();
-    elem->wireless = (devinfo->capabilities & GameInputDeviceCapabilityWireless);
-    elem->sensors_supported = (devinfo->supportedInput & GameInputKindMotion);
-    elem->supportedRumbleMotors = devinfo->supportedRumbleMotors;
+    elem->info = info;
 
     g_GameInputList.devices = devicelist;
     g_GameInputList.devices[g_GameInputList.count++] = elem;
@@ -297,7 +289,7 @@ static SDL_bool GAMEINPUT_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 produ
 
     for (idx = 0; idx < g_GameInputList.count; ++idx) {
         elem = g_GameInputList.devices[idx];
-        if (elem && vendor_id == elem->vendor && product_id == elem->product) {
+        if (elem && vendor_id == elem->info->vendorId && product_id == elem->info->productId) {
             return SDL_TRUE;
         }
     }
@@ -374,20 +366,24 @@ static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index)
     joystick->nbuttons = 11;
     joystick->nhats = 1;
 
-    if (elem->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) {
+    if (elem->info->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) {
         SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, SDL_TRUE);
     }
-    if (elem->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) {
+    if (elem->info->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) {
         SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, SDL_TRUE);
     }
 
-    if (elem->sensors_supported) {
+    if (elem->info->supportedInput & GameInputKindTouch) {
+        SDL_PrivateJoystickAddTouchpad(joystick, elem->info->touchPointCount);
+    }
+
+    if (elem->info->supportedInput & GameInputKindMotion) {
         /* FIXME: What's the sensor update rate? */
         SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f);
         SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f);
     }
 
-    if (elem->wireless) {
+    if (elem->info->capabilities & GameInputDeviceCapabilityWireless) {
         joystick->epowerlevel = GAMEINPUT_InternalGetPowerLevel(elem->device);
     } else {
         joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
@@ -451,6 +447,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
     Uint8 btnidx = 0, btnstate = 0, hat = 0;
     GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata;
     IGameInputDevice *device = hwdata->devref->device;
+    const GameInputDeviceInfo *info = hwdata->devref->info;
     IGameInputReading *reading = NULL;
     Uint64 timestamp = SDL_GetTicksNS();
     GameInputGamepadState state;
@@ -497,6 +494,20 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick)
 #undef CONVERT_TRIGGER
     }
 
+    if (info->supportedInput & GameInputKindTouch) {
+        GameInputTouchState *touch_state = SDL_stack_alloc(GameInputTouchState, info->touchPointCount);
+        if (touch_state) {
+            uint32_t i;
+            uint32_t touch_count = IGameInputReading_GetTouchState(reading, info->touchPointCount, touch_state);
+            for (i = 0; i < touch_count; ++i) {
+                GameInputTouchState *touch = &touch_state[i];
+                /* FIXME: We should use touch->touchId to track fingers instead of using i below */
+                SDL_SendJoystickTouchpad(timestamp, joystick, 0, i, SDL_PRESSED, touch->positionX * info->touchSensorInfo[i].resolutionX, touch->positionY * info->touchSensorInfo[0].resolutionY, touch->pressure);
+            }
+            SDL_stack_free(touch_state);
+        }
+    }
+
     if (hwdata->report_sensors) {
         GameInputMotionState motion_state;