فهرست منبع

Fall back to using the physical profile for Apple controllers if they don't match a standard profile

We may want to flip this to be the default, but it needs more testing.
Sam Lantinga 1 سال پیش
والد
کامیت
f5600fd9f4
2فایلهای تغییر یافته به همراه183 افزوده شده و 123 حذف شده
  1. 176 123
      src/joystick/apple/SDL_mfijoystick.m
  2. 7 0
      src/joystick/apple/SDL_mfijoystick_c.h

+ 176 - 123
src/joystick/apple/SDL_mfijoystick.m

@@ -271,32 +271,116 @@ static BOOL IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCControlle
     }
 #endif
 
+    BOOL is_xbox = IsControllerXbox(controller);
+    BOOL is_ps4 = IsControllerPS4(controller);
+    BOOL is_ps5 = IsControllerPS5(controller);
+    BOOL is_switch_pro = IsControllerSwitchPro(controller);
+    BOOL is_switch_joycon_pair = IsControllerSwitchJoyConPair(controller);
+    BOOL is_stadia = IsControllerStadia(controller);
+    BOOL is_backbone_one = IsControllerBackboneOne(controller);
+    BOOL is_switch_joyconL = IsControllerSwitchJoyConL(controller);
+    BOOL is_switch_joyconR = IsControllerSwitchJoyConR(controller);
+#ifdef SDL_JOYSTICK_HIDAPI
+    if ((is_xbox && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_XBOXONE)) ||
+        (is_ps4 && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_PS4)) ||
+        (is_ps5 && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_PS5)) ||
+        (is_switch_pro && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO)) ||
+        (is_switch_joycon_pair && HIDAPI_IsDevicePresent(USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR, 0, "")) ||
+        (is_stadia && HIDAPI_IsDevicePresent(USB_VENDOR_GOOGLE, USB_PRODUCT_GOOGLE_STADIA_CONTROLLER, 0, "")) ||
+        (is_switch_joyconL && HIDAPI_IsDevicePresent(USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT, 0, "")) ||
+        (is_switch_joyconR && HIDAPI_IsDevicePresent(USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT, 0, ""))) {
+        /* The HIDAPI driver is taking care of this device */
+        return FALSE;
+    }
+#else
+    (void)is_stadia;
+    (void)is_switch_joyconL;
+    (void)is_switch_joyconR;
+#endif
+
+    if (is_backbone_one) {
+        vendor = USB_VENDOR_BACKBONE;
+        if (is_ps5) {
+            product = USB_PRODUCT_BACKBONE_ONE_IOS_PS5;
+        } else {
+            product = USB_PRODUCT_BACKBONE_ONE_IOS;
+        }
+        subtype = 0;
+    } else if (is_xbox) {
+        vendor = USB_VENDOR_MICROSOFT;
+        if (device->has_xbox_paddles) {
+            /* Assume Xbox One Elite Series 2 Controller unless/until GCController flows VID/PID */
+            product = USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH;
+            subtype = 1;
+        } else if (device->has_xbox_share_button) {
+            /* Assume Xbox Series X Controller unless/until GCController flows VID/PID */
+            product = USB_PRODUCT_XBOX_SERIES_X_BLE;
+            subtype = 1;
+        } else {
+            /* Assume Xbox One S Bluetooth Controller unless/until GCController flows VID/PID */
+            product = USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH;
+            subtype = 0;
+        }
+    } else if (is_ps4) {
+        /* Assume DS4 Slim unless/until GCController flows VID/PID */
+        vendor = USB_VENDOR_SONY;
+        product = USB_PRODUCT_SONY_DS4_SLIM;
+        if (device->has_dualshock_touchpad) {
+            subtype = 1;
+        } else {
+            subtype = 0;
+        }
+    } else if (is_ps5) {
+        vendor = USB_VENDOR_SONY;
+        product = USB_PRODUCT_SONY_DS5;
+        subtype = 0;
+    } else if (is_switch_pro) {
+        vendor = USB_VENDOR_NINTENDO;
+        product = USB_PRODUCT_NINTENDO_SWITCH_PRO;
+        subtype = 0;
+    } else if (is_switch_joycon_pair) {
+        vendor = USB_VENDOR_NINTENDO;
+        product = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR;
+        subtype = 0;
+    } else if (is_switch_joyconL) {
+        vendor = USB_VENDOR_NINTENDO;
+        product = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT;
+        subtype = 0;
+    } else if (is_switch_joyconR) {
+        vendor = USB_VENDOR_NINTENDO;
+        product = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT;
+        subtype = 0;
+    } else if (controller.extendedGamepad) {
+        vendor = USB_VENDOR_APPLE;
+        product = 1;
+        subtype = 1;
+    } else if (controller.gamepad) {
+        vendor = USB_VENDOR_APPLE;
+        product = 2;
+        subtype = 2;
+#if TARGET_OS_TV
+    } else if (controller.microGamepad) {
+        vendor = USB_VENDOR_APPLE;
+        product = 3;
+        subtype = 3;
+#endif
+#ifdef ENABLE_PHYSICAL_INPUT_PROFILE
+    } else if ([controller respondsToSelector:@selector(physicalInputProfile)]) {
+        vendor = USB_VENDOR_APPLE;
+        product = 4;
+        subtype = 4;
+#endif
+    } else {
+        vendor = USB_VENDOR_APPLE;
+        product = 5;
+        subtype = 5;
+    }
+
     if (controller.extendedGamepad) {
         GCExtendedGamepad *gamepad = controller.extendedGamepad;
-        BOOL is_xbox = IsControllerXbox(controller);
-        BOOL is_ps4 = IsControllerPS4(controller);
-        BOOL is_ps5 = IsControllerPS5(controller);
-        BOOL is_switch_pro = IsControllerSwitchPro(controller);
-        BOOL is_switch_joycon_pair = IsControllerSwitchJoyConPair(controller);
-        BOOL is_stadia = IsControllerStadia(controller);
-        BOOL is_backbone_one = IsControllerBackboneOne(controller);
         int nbuttons = 0;
         BOOL has_direct_menu;
 
-#ifdef SDL_JOYSTICK_HIDAPI
-        if ((is_xbox && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_XBOXONE)) ||
-            (is_ps4 && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_PS4)) ||
-            (is_ps5 && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_PS5)) ||
-            (is_switch_pro && HIDAPI_IsDeviceTypePresent(SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO)) ||
-            (is_switch_joycon_pair && HIDAPI_IsDevicePresent(USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR, 0, "")) ||
-            (is_stadia && HIDAPI_IsDevicePresent(USB_VENDOR_GOOGLE, USB_PRODUCT_GOOGLE_STADIA_CONTROLLER, 0, ""))) {
-            /* The HIDAPI driver is taking care of this device */
-            return FALSE;
-        }
-#else
-        (void)is_stadia;
-#endif
-
         /* These buttons are part of the original MFi spec */
         device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_SOUTH);
         device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_EAST);
@@ -378,56 +462,6 @@ static BOOL IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCControlle
 #endif
 #pragma clang diagnostic pop
 
-        if (is_backbone_one) {
-            vendor = USB_VENDOR_BACKBONE;
-            if (is_ps5) {
-                product = USB_PRODUCT_BACKBONE_ONE_IOS_PS5;
-            } else {
-                product = USB_PRODUCT_BACKBONE_ONE_IOS;
-            }
-            subtype = 0;
-        } else if (is_xbox) {
-            vendor = USB_VENDOR_MICROSOFT;
-            if (device->has_xbox_paddles) {
-                /* Assume Xbox One Elite Series 2 Controller unless/until GCController flows VID/PID */
-                product = USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH;
-                subtype = 1;
-            } else if (device->has_xbox_share_button) {
-                /* Assume Xbox Series X Controller unless/until GCController flows VID/PID */
-                product = USB_PRODUCT_XBOX_SERIES_X_BLE;
-                subtype = 1;
-            } else {
-                /* Assume Xbox One S Bluetooth Controller unless/until GCController flows VID/PID */
-                product = USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH;
-                subtype = 0;
-            }
-        } else if (is_ps4) {
-            /* Assume DS4 Slim unless/until GCController flows VID/PID */
-            vendor = USB_VENDOR_SONY;
-            product = USB_PRODUCT_SONY_DS4_SLIM;
-            if (device->has_dualshock_touchpad) {
-                subtype = 1;
-            } else {
-                subtype = 0;
-            }
-        } else if (is_ps5) {
-            vendor = USB_VENDOR_SONY;
-            product = USB_PRODUCT_SONY_DS5;
-            subtype = 0;
-        } else if (is_switch_pro) {
-            vendor = USB_VENDOR_NINTENDO;
-            product = USB_PRODUCT_NINTENDO_SWITCH_PRO;
-            subtype = 0;
-        } else if (is_switch_joycon_pair) {
-            vendor = USB_VENDOR_NINTENDO;
-            product = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR;
-            subtype = 0;
-        } else {
-            vendor = USB_VENDOR_APPLE;
-            product = 1;
-            subtype = 1;
-        }
-
         if (is_backbone_one) {
             /* The Backbone app uses share button */
             if ((device->button_mask & (1 << SDL_GAMEPAD_BUTTON_MISC1)) != 0) {
@@ -442,35 +476,8 @@ static BOOL IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCControlle
         device->nbuttons = nbuttons;
 
     } else if (controller.gamepad) {
-        BOOL is_switch_joyconL = IsControllerSwitchJoyConL(controller);
-        BOOL is_switch_joyconR = IsControllerSwitchJoyConR(controller);
         int nbuttons = 0;
 
-#ifdef SDL_JOYSTICK_HIDAPI
-        if ((is_switch_joyconL && HIDAPI_IsDevicePresent(USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT, 0, "")) ||
-            (is_switch_joyconR && HIDAPI_IsDevicePresent(USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT, 0, ""))) {
-            /* The HIDAPI driver is taking care of this device */
-            return FALSE;
-        }
-#else
-        (void)is_switch_joyconL;
-        (void)is_switch_joyconR;
-#endif
-
-        if (is_switch_joyconL) {
-            vendor = USB_VENDOR_NINTENDO;
-            product = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT;
-            subtype = 0;
-        } else if (is_switch_joyconR) {
-            vendor = USB_VENDOR_NINTENDO;
-            product = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT;
-            subtype = 0;
-        } else {
-            vendor = USB_VENDOR_APPLE;
-            product = 2;
-            subtype = 2;
-        }
-
         /* These buttons are part of the original MFi spec */
         device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_SOUTH);
         device->button_mask |= (1 << SDL_GAMEPAD_BUTTON_EAST);
@@ -503,16 +510,29 @@ static BOOL IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCControlle
         ++nbuttons;
         device->uses_pause_handler = SDL_TRUE;
 
-        vendor = USB_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 = nbuttons;
 
         controller.microGamepad.allowsRotation = SDL_GetHintBoolean(SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION, SDL_FALSE);
     }
-#endif /* TARGET_OS_TV */
+#endif
+#ifdef ENABLE_PHYSICAL_INPUT_PROFILE
+    else if ([controller respondsToSelector:@selector(physicalInputProfile)]) {
+        device->use_physical_profile = SDL_TRUE;
+        device->axes = [controller.physicalInputProfile.axes allKeys];
+        device->naxes = controller.physicalInputProfile.axes.count;
+        device->dpads = [controller.physicalInputProfile.dpads allKeys];
+        device->nhats = controller.physicalInputProfile.dpads.count;
+        device->buttons = [controller.physicalInputProfile.buttons allKeys];
+        device->nbuttons = controller.physicalInputProfile.buttons.count;
+        subtype = 4;
+    }
+#endif
+    else {
+        /* We can't detect any inputs on this */
+        return SDL_FALSE;
+    }
 
     if (vendor == USB_VENDOR_APPLE) {
         /* Note that this is an MFI controller and what subtype it is */
@@ -957,7 +977,8 @@ static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
 {
 #ifdef SDL_JOYSTICK_MFI
     @autoreleasepool {
-        GCController *controller = joystick->hwdata->controller;
+        SDL_JoystickDeviceItem *device = joystick->hwdata;
+        GCController *controller = device->controller;
         Uint8 hatstate = SDL_HAT_CENTERED;
         int i;
         int pause_button_index = 0;
@@ -976,11 +997,39 @@ static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
                     if (axis.value != 0.0f)
                         NSLog(@"Axis %@ = %g\n", key, axis.value);
                 }
+                for (id key in controller.physicalInputProfile.dpads) {
+                    GCControllerDirectionPad *dpad = controller.physicalInputProfile.dpads[key];
+                    if (dpad.up.isPressed || dpad.down.isPressed || dpad.left.isPressed || dpad.right.isPressed) {
+                        NSLog(@"Hat %@ =%s%s%s%s\n", key,
+                            dpad.up.isPressed ? " UP" : "",
+                            dpad.down.isPressed ? " DOWN" : "",
+                            dpad.left.isPressed ? " LEFT" : "",
+                            dpad.right.isPressed ? " RIGHT" : "");
+                    }
+                }
             }
         }
 #endif
 
-        if (controller.extendedGamepad) {
+        if (device->use_physical_profile) {
+            int axis = 0;
+            for (id key in device->axes) {
+                Sint16 value = (Sint16)(controller.physicalInputProfile.axes[key].value * 32767);
+                SDL_SendJoystickAxis(timestamp, joystick, axis++, value);
+            }
+
+            int button = 0;
+            for (id key in device->buttons) {
+                Uint8 value = controller.physicalInputProfile.buttons[key].isPressed;
+                SDL_SendJoystickButton(timestamp, joystick, button++, value);
+            }
+
+            int hat = 0;
+            for (id key in device->dpads) {
+                hatstate = IOS_MFIJoystickHatStateForDPad(controller.physicalInputProfile.dpads[key]);
+                SDL_SendJoystickHat(timestamp, joystick, hat++, hatstate);
+            }
+        } else if (controller.extendedGamepad) {
             SDL_bool isstack;
             GCExtendedGamepad *gamepad = controller.extendedGamepad;
 
@@ -1014,21 +1063,21 @@ static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
             /* 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_GAMEPAD_BUTTON_LEFT_STICK)) {
+            if (device->button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_STICK)) {
                 buttons[button_count++] = gamepad.leftThumbstickButton.isPressed;
             }
-            if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_STICK)) {
+            if (device->button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_STICK)) {
                 buttons[button_count++] = gamepad.rightThumbstickButton.isPressed;
             }
-            if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) {
+            if (device->button_mask & (1 << SDL_GAMEPAD_BUTTON_BACK)) {
                 buttons[button_count++] = gamepad.buttonOptions.isPressed;
             }
-            if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_GUIDE)) {
+            if (device->button_mask & (1 << SDL_GAMEPAD_BUTTON_GUIDE)) {
                 buttons[button_count++] = gamepad.buttonHome.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_GAMEPAD_BUTTON_START)) {
-                if (joystick->hwdata->uses_pause_handler) {
+            if (device->button_mask & (1 << SDL_GAMEPAD_BUTTON_START)) {
+                if (device->uses_pause_handler) {
                     pause_button_index = button_count;
                     buttons[button_count++] = joystick->delayed_guide_button;
                 } else {
@@ -1037,7 +1086,7 @@ static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
             }
 
 #ifdef ENABLE_PHYSICAL_INPUT_PROFILE
-            if (joystick->hwdata->has_dualshock_touchpad) {
+            if (device->has_dualshock_touchpad) {
                 GCControllerDirectionPad *dpad;
                 buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputDualShockTouchpadButton].isPressed;
 
@@ -1056,17 +1105,17 @@ static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
                 }
             }
 
-            if (joystick->hwdata->has_xbox_paddles) {
-                if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1)) {
+            if (device->has_xbox_paddles) {
+                if (device->button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1)) {
                     buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleOne].isPressed;
                 }
-                if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE1)) {
+                if (device->button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE1)) {
                     buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleTwo].isPressed;
                 }
-                if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2)) {
+                if (device->button_mask & (1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2)) {
                     buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleThree].isPressed;
                 }
-                if (joystick->hwdata->button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE2)) {
+                if (device->button_mask & (1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE2)) {
                     buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxPaddleFour].isPressed;
                 }
 
@@ -1079,7 +1128,7 @@ static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
                 */
             }
 
-            if (joystick->hwdata->has_xbox_share_button) {
+            if (device->has_xbox_share_button) {
                 buttons[button_count++] = controller.physicalInputProfile.buttons[GCInputXboxShareButton].isPressed;
             }
 #endif
@@ -1170,8 +1219,8 @@ static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
 #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_GAMEPAD_BUTTON_START)) {
-                if (joystick->hwdata->uses_pause_handler) {
+            if (device->button_mask & (1 << SDL_GAMEPAD_BUTTON_START)) {
+                if (device->uses_pause_handler) {
                     pause_button_index = button_count;
                     buttons[button_count++] = joystick->delayed_guide_button;
                 } else {
@@ -1186,16 +1235,16 @@ static void IOS_MFIJoystickUpdate(SDL_Joystick *joystick)
         }
 #endif /* TARGET_OS_TV */
 
-        if (joystick->nhats > 0) {
+        if (joystick->nhats > 0 && !device->use_physical_profile) {
             SDL_SendJoystickHat(timestamp, joystick, 0, hatstate);
         }
 
-        if (joystick->hwdata->uses_pause_handler) {
-            for (i = 0; i < joystick->hwdata->num_pause_presses; i++) {
+        if (device->uses_pause_handler) {
+            for (i = 0; i < device->num_pause_presses; i++) {
                 SDL_SendJoystickButton(timestamp, joystick, pause_button_index, SDL_PRESSED);
                 SDL_SendJoystickButton(timestamp, joystick, pause_button_index, SDL_RELEASED);
             }
-            joystick->hwdata->num_pause_presses = 0;
+            device->num_pause_presses = 0;
         }
 
 #ifdef ENABLE_MFI_BATTERY
@@ -1732,6 +1781,10 @@ static GCControllerDirectionPad *GetDirectionalPadForController(GCController *co
         return controller.microGamepad.dpad;
     }
 
+    if ([controller respondsToSelector:@selector(physicalInputProfile)]) {
+        return controller.physicalInputProfile.dpads[GCInputDirectionPad];
+    }
+
     return nil;
 }
 #endif /* SDL_JOYSTICK_MFI && ENABLE_PHYSICAL_INPUT_PROFILE */

+ 7 - 0
src/joystick/apple/SDL_mfijoystick_c.h

@@ -25,6 +25,8 @@
 
 #include "../SDL_sysjoystick.h"
 
+#include <CoreFoundation/CoreFoundation.h>
+
 @class GCController;
 
 typedef struct joystick_hwdata
@@ -51,6 +53,11 @@ typedef struct joystick_hwdata
     SDL_bool has_xbox_paddles;
     SDL_bool has_xbox_share_button;
 
+    SDL_bool use_physical_profile;
+    NSArray *axes;
+    NSArray *dpads;
+    NSArray *buttons;
+
     struct joystick_hwdata *next;
 } joystick_hwdata;