SDL: Support returning sfSymbolsName for gamepad controls on macOS / iOS / tvOS

From 149e5c656a063f64fa3e257d0bca5e8fcaada603 Mon Sep 17 00:00:00 2001
From: Patrick Hogan <[EMAIL REDACTED]>
Date: Sun, 3 Oct 2021 12:49:36 -0500
Subject: [PATCH] Support returning sfSymbolsName for gamepad controls on macOS
 / iOS / tvOS

---
 include/SDL_gamecontroller.h            |  20 ++
 src/joystick/iphoneos/SDL_mfijoystick.m | 257 ++++++++++++++++++++++++
 2 files changed, 277 insertions(+)

diff --git a/include/SDL_gamecontroller.h b/include/SDL_gamecontroller.h
index 695201e56b..9ca41bd1e8 100644
--- a/include/SDL_gamecontroller.h
+++ b/include/SDL_gamecontroller.h
@@ -931,6 +931,26 @@ extern DECLSPEC int SDLCALL SDL_GameControllerSendEffect(SDL_GameController *gam
  */
 extern DECLSPEC void SDLCALL SDL_GameControllerClose(SDL_GameController *gamecontroller);
 
+/**
+ * Return the sfSymbolsName for a given button on a game controller on Apple platforms.
+ *
+ * \param gamecontroller the controller to query
+ * \param button a button on the game controller
+ *
+ */
+extern DECLSPEC const char* SDLCALL SDL_GameControllerGetAppleSFSymbolsNameForButton(SDL_GameController *gamecontroller, SDL_GameControllerButton button);
+
+/**
+ * Return the sfSymbolsName for a given axis on a game controller on Apple platforms.
+ *
+ * \param gamecontroller the controller to query
+ * \param button an axis on the game controller
+ *
+ */
+extern DECLSPEC const char* SDLCALL SDL_GameControllerGetAppleSFSymbolsNameForAxis(SDL_GameController *gamecontroller, SDL_GameControllerAxis axis);
+
+
+
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
 }
diff --git a/src/joystick/iphoneos/SDL_mfijoystick.m b/src/joystick/iphoneos/SDL_mfijoystick.m
index d0d1bf58dc..0bfa692093 100644
--- a/src/joystick/iphoneos/SDL_mfijoystick.m
+++ b/src/joystick/iphoneos/SDL_mfijoystick.m
@@ -1548,6 +1548,263 @@ SDL_bool IOS_SupportedHIDDevice(IOHIDDeviceRef device)
 }
 #endif
 
+#if defined(SDL_JOYSTICK_MFI) && defined(ENABLE_PHYSICAL_INPUT_PROFILE)
+static void
+GetAppleSFSymbolsNameForElement(GCControllerElement *element, char *name)
+{
+    if (@available(macos 11.0, iOS 14.0, tvOS 14.0, *)) {
+        if (element) {
+            [element.sfSymbolsName getCString: name maxLength: 255 encoding: NSASCIIStringEncoding];
+        }
+    }
+}
+
+static GCControllerDirectionPad *
+GetDirectionalPadForController(GCController *controller)
+{
+    if (controller.extendedGamepad) {
+        return controller.extendedGamepad.dpad;
+    }
+
+    if (controller.gamepad) {
+        return controller.gamepad.dpad;
+    }
+
+    if (controller.microGamepad) {
+        return controller.microGamepad.dpad;
+    }
+
+    return nil;
+}
+#endif
+
+
+static char elementName[256];
+
+const char *
+SDL_GameControllerGetAppleSFSymbolsNameForButton(SDL_GameController *gamecontroller, SDL_GameControllerButton button)
+{
+    elementName[0] = '\0';
+    if (!gamecontroller) {
+        return elementName;
+    }
+    if (SDL_GameControllerGetJoystick(gamecontroller)->driver != &SDL_IOS_JoystickDriver) {
+        return elementName;
+    }
+#if defined(SDL_JOYSTICK_MFI) && defined(ENABLE_PHYSICAL_INPUT_PROFILE)
+    if (@available(iOS 14.0, tvOS 14.0, macOS 11.0, *)) {
+        GCController *controller = SDL_GameControllerGetJoystick(gamecontroller)->hwdata->controller;
+        if ([controller respondsToSelector:@selector(physicalInputProfile)]) {
+            NSDictionary<NSString *,GCControllerElement *> *elements = controller.physicalInputProfile.elements;
+            switch (button)
+            {
+                case SDL_CONTROLLER_BUTTON_INVALID: {
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_A: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputButtonA], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_B: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputButtonB], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_X: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputButtonX], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_Y: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputButtonY], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_BACK: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputButtonOptions], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_GUIDE: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputButtonHome], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_START: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputButtonMenu], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_LEFTSTICK: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputLeftThumbstickButton], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_RIGHTSTICK: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputRightThumbstickButton], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputLeftShoulder], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputRightShoulder], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_DPAD_UP: {
+                    GCControllerDirectionPad * dpad = GetDirectionalPadForController(controller);
+                    if (dpad) {
+                        GetAppleSFSymbolsNameForElement(dpad.up, elementName);
+                        if (SDL_strlen(elementName) == 0) {
+                            SDL_strlcpy( elementName, "dpad.up.fill", 255 );
+                        }
+                    }
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_DPAD_DOWN: {
+                    GCControllerDirectionPad * dpad = GetDirectionalPadForController(controller);
+                    if (dpad) {
+                        GetAppleSFSymbolsNameForElement(dpad.down, elementName);
+                        if (SDL_strlen(elementName) == 0) {
+                            SDL_strlcpy( elementName, "dpad.down.fill", 255 );
+                        }
+                    }
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_DPAD_LEFT: {
+                    GCControllerDirectionPad * dpad = GetDirectionalPadForController(controller);
+                    if (dpad) {
+                        GetAppleSFSymbolsNameForElement(dpad.left, elementName);
+                        if (SDL_strlen(elementName) == 0) {
+                            SDL_strlcpy( elementName, "dpad.left.fill", 255 );
+                        }
+                    }
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: {
+                    GCControllerDirectionPad * dpad = GetDirectionalPadForController(controller);
+                    if (dpad) {
+                        GetAppleSFSymbolsNameForElement(dpad.right, elementName);
+                        if (SDL_strlen(elementName) == 0) {
+                            SDL_strlcpy( elementName, "dpad.right.fill", 255 );
+                        }
+                    }
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_MISC1: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputDualShockTouchpadButton], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_PADDLE1: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputXboxPaddleOne], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_PADDLE2: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputXboxPaddleTwo], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_PADDLE3: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputXboxPaddleThree], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_PADDLE4: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputXboxPaddleFour], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_TOUCHPAD: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputDualShockTouchpadButton], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_BUTTON_MAX: {
+                    break;
+                }
+            }
+        }
+    }
+#endif
+    return elementName;
+}
+
+
+const char *
+SDL_GameControllerGetAppleSFSymbolsNameForAxis(SDL_GameController *gamecontroller, SDL_GameControllerAxis axis)
+{
+    elementName[0] = '\0';
+    if (!gamecontroller) {
+        return elementName;
+    }
+    if (SDL_GameControllerGetJoystick(gamecontroller)->driver != &SDL_IOS_JoystickDriver) {
+        return elementName;
+    }
+#if defined(SDL_JOYSTICK_MFI) && defined(ENABLE_PHYSICAL_INPUT_PROFILE)
+    if (@available(iOS 14.0, tvOS 14.0, macOS 11.0, *)) {
+        GCController *controller = SDL_GameControllerGetJoystick(gamecontroller)->hwdata->controller;
+        if ([controller respondsToSelector:@selector(physicalInputProfile)]) {
+            NSDictionary<NSString *,GCControllerElement *> *elements = controller.physicalInputProfile.elements;
+            switch (axis)
+            {
+
+                case SDL_CONTROLLER_AXIS_INVALID:
+                    break;
+
+                case SDL_CONTROLLER_AXIS_LEFTX:{
+                    GetAppleSFSymbolsNameForElement(elements[GCInputLeftThumbstick], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_AXIS_LEFTY: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputLeftThumbstick], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_AXIS_RIGHTX: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputRightThumbstick], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_AXIS_RIGHTY: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputRightThumbstick], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_AXIS_TRIGGERLEFT: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputLeftTrigger], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: {
+                    GetAppleSFSymbolsNameForElement(elements[GCInputRightTrigger], elementName);
+                    break;
+                }
+
+                case SDL_CONTROLLER_AXIS_MAX: {
+                    break;
+                }
+            }
+        }
+    }
+#endif
+    return elementName;
+}
+
+
+
 SDL_JoystickDriver SDL_IOS_JoystickDriver =
 {
     IOS_JoystickInit,