From 03f8528315fa46c95991a34f3325d7b33ae5538c Mon Sep 17 00:00:00 2001
From: Edward Rudd <urkle@outoforder.cc>
Date: Sat, 02 May 2020 21:48:36 +0000
Subject: [PATCH] Update source to SDL2 2.0.12

---
 source/src/joystick/iphoneos/SDL_sysjoystick.m |  287 +++++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 208 insertions(+), 79 deletions(-)

diff --git a/source/src/joystick/iphoneos/SDL_sysjoystick.m b/source/src/joystick/iphoneos/SDL_sysjoystick.m
index d85efad..d14e047 100644
--- a/source/src/joystick/iphoneos/SDL_sysjoystick.m
+++ b/source/src/joystick/iphoneos/SDL_sysjoystick.m
@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
@@ -24,7 +24,7 @@
 #include "SDL_sysjoystick_c.h"
 
 /* needed for SDL_IPHONE_MAX_GFORCE macro */
-#include "SDL_config_iphoneos.h"
+#include "../../../include/SDL_config_iphoneos.h"
 
 #include "SDL_assert.h"
 #include "SDL_events.h"
@@ -48,6 +48,29 @@
 
 static id connectObserver = nil;
 static id disconnectObserver = nil;
+
+#include <Availability.h>
+#include <objc/message.h>
+
+/* remove compilation warnings for strict builds by defining these selectors, even though
+ * they are only ever used indirectly through objc_msgSend
+ */
+@interface GCExtendedGamepad (SDL)
+#if (__IPHONE_OS_VERSION_MAX_ALLOWED < 121000) || (__APPLETV_OS_VERSION_MAX_ALLOWED < 121000) || (__MAC_OS_VERSION_MAX_ALLOWED < 1401000)
+@property (nonatomic, readonly, nullable) GCControllerButtonInput *leftThumbstickButton;
+@property (nonatomic, readonly, nullable) GCControllerButtonInput *rightThumbstickButton;
+#endif
+#if (__IPHONE_OS_VERSION_MAX_ALLOWED < 130000) || (__APPLETV_OS_VERSION_MAX_ALLOWED < 130000) || (__MAC_OS_VERSION_MAX_ALLOWED < 1500000)
+@property (nonatomic, readonly) GCControllerButtonInput *buttonMenu;
+@property (nonatomic, readonly, nullable) GCControllerButtonInput *buttonOptions;
+#endif
+@end
+@interface GCMicroGamepad (SDL)
+#if (__IPHONE_OS_VERSION_MAX_ALLOWED < 130000) || (__APPLETV_OS_VERSION_MAX_ALLOWED < 130000) || (__MAC_OS_VERSION_MAX_ALLOWED < 1500000)
+@property (nonatomic, readonly) GCControllerButtonInput *buttonMenu;
+#endif
+@end
+
 #endif /* SDL_JOYSTICK_MFI */
 
 #if !TARGET_OS_TV
@@ -82,10 +105,11 @@
 {
 #ifdef SDL_JOYSTICK_MFI
     const Uint16 VENDOR_APPLE = 0x05AC;
+    const Uint16 VENDOR_MICROSOFT = 0x045e;
+    const Uint16 VENDOR_SONY = 0x054C;
     Uint16 *guid16 = (Uint16 *)device->guid.data;
     Uint16 vendor = 0;
     Uint16 product = 0;
-    Uint16 version = 0;
     Uint8 subtype = 0;
 
     const char *name = NULL;
@@ -104,28 +128,107 @@
     device->name = SDL_strdup(name);
 
     if (controller.extendedGamepad) {
-        vendor = VENDOR_APPLE;
-        product = 1;
-        subtype = 1;
+        GCExtendedGamepad *gamepad = controller.extendedGamepad;
+        BOOL is_xbox = [controller.vendorName containsString: @"Xbox"];
+        BOOL is_ps4 = [controller.vendorName containsString: @"DUALSHOCK"];
+#if TARGET_OS_TV
+        BOOL is_MFi = (!is_xbox && !is_ps4);
+#endif
+        int nbuttons = 0;
+
+        /* These buttons are part of the original MFi spec */
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_A);
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_B);
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_X);
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_Y);
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
+        nbuttons += 6;
+
+        /* These buttons are available on some newer controllers */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+        if ([gamepad respondsToSelector:@selector(leftThumbstickButton)] && gamepad.leftThumbstickButton) {
+            device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_LEFTSTICK);
+            ++nbuttons;
+        }
+        if ([gamepad respondsToSelector:@selector(rightThumbstickButton)] && gamepad.rightThumbstickButton) {
+            device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_RIGHTSTICK);
+            ++nbuttons;
+        }
+        if ([gamepad respondsToSelector:@selector(buttonOptions)] && gamepad.buttonOptions) {
+            device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_BACK);
+            ++nbuttons;
+        }
+        BOOL has_direct_menu = [gamepad respondsToSelector:@selector(buttonMenu)] && gamepad.buttonMenu;
+#if TARGET_OS_TV
+        /* On tvOS MFi controller menu button brings you to the home screen */
+        if (is_MFi) {
+            has_direct_menu = FALSE;
+        }
+#endif
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_START);
+        ++nbuttons;
+        if (!has_direct_menu) {
+            device->uses_pause_handler = SDL_TRUE;
+        }
+#pragma clang diagnostic pop
+
+        if (is_xbox) {
+            vendor = VENDOR_MICROSOFT;
+            product = 0x02E0; /* Assume Xbox One S BLE Controller unless/until GCController flows VID/PID */
+        } else if (is_ps4) {
+            vendor = VENDOR_SONY;
+            product = 0x09CC; /* Assume DS4 Slim unless/until GCController flows VID/PID */
+        } else {
+            vendor = VENDOR_APPLE;
+            product = 1;
+            subtype = 1;
+        }
+
         device->naxes = 6; /* 2 thumbsticks and 2 triggers */
         device->nhats = 1; /* d-pad */
-        device->nbuttons = 7; /* ABXY, shoulder buttons, pause button */
+        device->nbuttons = nbuttons;
+
     } else if (controller.gamepad) {
+        int nbuttons = 0;
+
+        /* These buttons are part of the original MFi spec */
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_A);
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_B);
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_X);
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_Y);
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_START);
+        nbuttons += 7;
+        device->uses_pause_handler = SDL_TRUE;
+
         vendor = VENDOR_APPLE;
         product = 2;
         subtype = 2;
         device->naxes = 0; /* no traditional analog inputs */
         device->nhats = 1; /* d-pad */
-        device->nbuttons = 7; /* ABXY, shoulder buttons, pause button */
+        device->nbuttons = nbuttons;
     }
 #if TARGET_OS_TV
     else if (controller.microGamepad) {
+        int nbuttons = 0;
+
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_A);
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_B); /* Button X on microGamepad */
+        nbuttons += 2;
+
+        device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_START);
+        ++nbuttons;
+        device->uses_pause_handler = SDL_TRUE;
+
         vendor = VENDOR_APPLE;
         product = 3;
         subtype = 3;
         device->naxes = 2; /* treat the touch surface as two axes */
         device->nhats = 0; /* apparently the touch surface-as-dpad is buggy */
-        device->nbuttons = 3; /* AX, pause button */
+        device->nbuttons = nbuttons;
 
         controller.microGamepad.allowsRotation = SDL_GetHintBoolean(SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION, SDL_FALSE);
     }
@@ -139,12 +242,14 @@
     *guid16++ = 0;
     *guid16++ = SDL_SwapLE16(product);
     *guid16++ = 0;
-    *guid16++ = SDL_SwapLE16(version);
-    *guid16++ = 0;
 
-    /* Note that this is an MFI controller and what subtype it is */
-    device->guid.data[14] = 'm';
-    device->guid.data[15] = subtype;
+    *guid16++ = SDL_SwapLE16(device->button_mask);
+
+    if (subtype != 0) {
+        /* Note that this is an MFI controller and what subtype it is */
+        device->guid.data[14] = 'm';
+        device->guid.data[15] = subtype;
+    }
 
     /* This will be set when the first button press of the controller is
      * detected. */
@@ -361,7 +466,17 @@
 static int
 IOS_JoystickGetDevicePlayerIndex(int device_index)
 {
-    return -1;
+    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
+    return device ? (int)device->controller.playerIndex : -1;
+}
+
+static void
+IOS_JoystickSetDevicePlayerIndex(int device_index, int player_index)
+{
+    SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index);
+    if (device) {
+        device->controller.playerIndex = player_index;
+    }
 }
 
 static SDL_JoystickGUID
@@ -415,12 +530,14 @@
 #endif /* !TARGET_OS_TV */
         } else {
 #ifdef SDL_JOYSTICK_MFI
-            GCController *controller = device->controller;
-            controller.controllerPausedHandler = ^(GCController *c) {
-                if (joystick->hwdata) {
-                    ++joystick->hwdata->num_pause_presses;
-                }
-            };
+            if (device->uses_pause_handler) {
+                GCController *controller = device->controller;
+                controller.controllerPausedHandler = ^(GCController *c) {
+                    if (joystick->hwdata) {
+                        ++joystick->hwdata->num_pause_presses;
+                    }
+                };
+            }
 #endif /* SDL_JOYSTICK_MFI */
         }
     }
@@ -509,7 +626,7 @@
         GCController *controller = joystick->hwdata->controller;
         Uint8 hatstate = SDL_HAT_CENTERED;
         int i;
-        int updateplayerindex = 0;
+        int pause_button_index = 0;
 
         if (controller.extendedGamepad) {
             GCExtendedGamepad *gamepad = controller.extendedGamepad;
@@ -525,44 +642,67 @@
             };
 
             /* Button order matches the XInput Windows mappings. */
-            Uint8 buttons[] = {
-                gamepad.buttonA.isPressed, gamepad.buttonB.isPressed,
-                gamepad.buttonX.isPressed, gamepad.buttonY.isPressed,
-                gamepad.leftShoulder.isPressed,
-                gamepad.rightShoulder.isPressed,
-            };
+            Uint8 buttons[joystick->nbuttons];
+            int button_count = 0;
+
+            /* These buttons are part of the original MFi spec */
+            buttons[button_count++] = gamepad.buttonA.isPressed;
+            buttons[button_count++] = gamepad.buttonB.isPressed;
+            buttons[button_count++] = gamepad.buttonX.isPressed;
+            buttons[button_count++] = gamepad.buttonY.isPressed;
+            buttons[button_count++] = gamepad.leftShoulder.isPressed;
+            buttons[button_count++] = gamepad.rightShoulder.isPressed;
+
+            /* These buttons are available on some newer controllers */
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+            if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_LEFTSTICK)) {
+                buttons[button_count++] = gamepad.leftThumbstickButton.isPressed;
+            }
+            if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_RIGHTSTICK)) {
+                buttons[button_count++] = gamepad.rightThumbstickButton.isPressed;
+            }
+            if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_BACK)) {
+                buttons[button_count++] = gamepad.buttonOptions.isPressed;
+            }
+            /* This must be the last button, so we can optionally handle it with pause_button_index below */
+            if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
+                if (joystick->hwdata->uses_pause_handler) {
+                    pause_button_index = button_count;
+                    buttons[button_count++] = joystick->delayed_guide_button;
+                } else {
+                    buttons[button_count++] = gamepad.buttonMenu.isPressed;
+                }
+            }
+#pragma clang diagnostic pop
 
             hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
 
             for (i = 0; i < SDL_arraysize(axes); i++) {
-                /* The triggers (axes 2 and 5) are resting at -32768 but SDL
-                 * initializes its values to 0. We only want to make sure the
-                 * player index is up to date if the user actually moves an axis. */
-                if ((i != 2 && i != 5) || axes[i] != -32768) {
-                    updateplayerindex |= (joystick->axes[i].value != axes[i]);
-                }
                 SDL_PrivateJoystickAxis(joystick, i, axes[i]);
             }
 
-            for (i = 0; i < SDL_arraysize(buttons); i++) {
-                updateplayerindex |= (joystick->buttons[i] != buttons[i]);
+            for (i = 0; i < button_count; i++) {
                 SDL_PrivateJoystickButton(joystick, i, buttons[i]);
             }
         } else if (controller.gamepad) {
             GCGamepad *gamepad = controller.gamepad;
 
             /* Button order matches the XInput Windows mappings. */
-            Uint8 buttons[] = {
-                gamepad.buttonA.isPressed, gamepad.buttonB.isPressed,
-                gamepad.buttonX.isPressed, gamepad.buttonY.isPressed,
-                gamepad.leftShoulder.isPressed,
-                gamepad.rightShoulder.isPressed,
-            };
+            Uint8 buttons[joystick->nbuttons];
+            int button_count = 0;
+            buttons[button_count++] = gamepad.buttonA.isPressed;
+            buttons[button_count++] = gamepad.buttonB.isPressed;
+            buttons[button_count++] = gamepad.buttonX.isPressed;
+            buttons[button_count++] = gamepad.buttonY.isPressed;
+            buttons[button_count++] = gamepad.leftShoulder.isPressed;
+            buttons[button_count++] = gamepad.rightShoulder.isPressed;
+            pause_button_index = button_count;
+            buttons[button_count++] = joystick->delayed_guide_button;
 
             hatstate = IOS_MFIJoystickHatStateForDPad(gamepad.dpad);
 
-            for (i = 0; i < SDL_arraysize(buttons); i++) {
-                updateplayerindex |= (joystick->buttons[i] != buttons[i]);
+            for (i = 0; i < button_count; i++) {
                 SDL_PrivateJoystickButton(joystick, i, buttons[i]);
             }
         }
@@ -576,61 +716,49 @@
             };
 
             for (i = 0; i < SDL_arraysize(axes); i++) {
-                updateplayerindex |= (joystick->axes[i].value != axes[i]);
                 SDL_PrivateJoystickAxis(joystick, i, axes[i]);
             }
 
-            Uint8 buttons[] = {
-                gamepad.buttonA.isPressed,
-                gamepad.buttonX.isPressed,
-            };
+            Uint8 buttons[joystick->nbuttons];
+            int button_count = 0;
+            buttons[button_count++] = gamepad.buttonA.isPressed;
+            buttons[button_count++] = gamepad.buttonX.isPressed;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunguarded-availability-new"
+            /* This must be the last button, so we can optionally handle it with pause_button_index below */
+            if (joystick->hwdata->button_mask & (1 << SDL_CONTROLLER_BUTTON_START)) {
+                if (joystick->hwdata->uses_pause_handler) {
+                    pause_button_index = button_count;
+                    buttons[button_count++] = joystick->delayed_guide_button;
+                } else {
+                    buttons[button_count++] = gamepad.buttonMenu.isPressed;
+                }
+            }
+#pragma clang diagnostic pop
 
-            for (i = 0; i < SDL_arraysize(buttons); i++) {
-                updateplayerindex |= (joystick->buttons[i] != buttons[i]);
+            for (i = 0; i < button_count; i++) {
                 SDL_PrivateJoystickButton(joystick, i, buttons[i]);
             }
         }
 #endif /* TARGET_OS_TV */
 
         if (joystick->nhats > 0) {
-            updateplayerindex |= (joystick->hats[0] != hatstate);
             SDL_PrivateJoystickHat(joystick, 0, hatstate);
         }
 
-        for (i = 0; i < joystick->hwdata->num_pause_presses; i++) {
-            const Uint8 pausebutton = joystick->nbuttons - 1; /* The pause button is always last. */
-            SDL_PrivateJoystickButton(joystick, pausebutton, SDL_PRESSED);
-            SDL_PrivateJoystickButton(joystick, pausebutton, SDL_RELEASED);
-            updateplayerindex = YES;
-        }
-        joystick->hwdata->num_pause_presses = 0;
-
-        if (updateplayerindex && controller.playerIndex == -1) {
-            BOOL usedPlayerIndexSlots[4] = {NO, NO, NO, NO};
-
-            /* Find the player index of all other connected controllers. */
-            for (GCController *c in [GCController controllers]) {
-                if (c != controller && c.playerIndex >= 0) {
-                    usedPlayerIndexSlots[c.playerIndex] = YES;
-                }
+        if (joystick->hwdata->uses_pause_handler) {
+            for (i = 0; i < joystick->hwdata->num_pause_presses; i++) {
+                SDL_PrivateJoystickButton(joystick, pause_button_index, SDL_PRESSED);
+                SDL_PrivateJoystickButton(joystick, pause_button_index, SDL_RELEASED);
             }
-
-            /* Set this controller's player index to the first unused index.
-             * FIXME: This logic isn't great... but SDL doesn't expose this
-             * concept in its external API, so we don't have much to go on. */
-            for (i = 0; i < SDL_arraysize(usedPlayerIndexSlots); i++) {
-                if (!usedPlayerIndexSlots[i]) {
-                    controller.playerIndex = i;
-                    break;
-                }
-            }
+            joystick->hwdata->num_pause_presses = 0;
         }
     }
 #endif /* SDL_JOYSTICK_MFI */
 }
 
 static int
-IOS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
+IOS_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
 {
     return SDL_Unsupported();
 }
@@ -722,6 +850,7 @@
     IOS_JoystickDetect,
     IOS_JoystickGetDeviceName,
     IOS_JoystickGetDevicePlayerIndex,
+    IOS_JoystickSetDevicePlayerIndex,
     IOS_JoystickGetDeviceGUID,
     IOS_JoystickGetDeviceInstanceID,
     IOS_JoystickOpen,

--
Gitblit v1.9.3