From f9beef760a778cc74d48d17971b6ce0451979c2e Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 22 Jul 2022 18:58:29 -0700
Subject: [PATCH] Added support for the Nintendo Switch Joy-Con Controllers on
iOS and tvOS 16
---
src/joystick/SDL_gamecontrollerdb.h | 5 ++
src/joystick/iphoneos/SDL_mfijoystick.m | 76 +++++++++++++++++++++++--
2 files changed, 76 insertions(+), 5 deletions(-)
diff --git a/src/joystick/SDL_gamecontrollerdb.h b/src/joystick/SDL_gamecontrollerdb.h
index 1fa03474784..1e7f1f43f38 100644
--- a/src/joystick/SDL_gamecontrollerdb.h
+++ b/src/joystick/SDL_gamecontrollerdb.h
@@ -913,6 +913,11 @@ static const char *s_ControllerMappings [] =
"05000000ac05000001000000df076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,",
"05000000ac05000001000000ff076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,",
"05000000ac050000020000004f066d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,",
+ "050000007e050000062000004f060000,Nintendo Switch Joy-Con (L),+leftx:h0.1,+lefty:h0.2,-leftx:h0.4,-lefty:h0.8,dpdown:b2,dpleft:b0,dpright:b3,dpup:b1,leftshoulder:b4,misc1:b6,rightshoulder:b5,",
+ "050000007e0500000e200000df070000,Nintendo Switch Joy-Con (L/R),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
+ "050000007e0500000e200000df070000,Nintendo Switch Joy-Con (L/R),a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
+ "050000007e050000072000004f060000,Nintendo Switch Joy-Con (R),+rightx:h0.4,+righty:h0.8,-rightx:h0.1,-righty:h0.2,a:b0,b:b1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
+ "050000007e050000072000004f060000,Nintendo Switch Joy-Con (R),+rightx:h0.4,+righty:h0.8,-rightx:h0.1,-righty:h0.2,a:b1,b:b0,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b3,y:b2,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e05000009200000ff870000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b3,y:b2,hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000007e05000009200000ff870000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,hint:!SDL_GAMECONTROLLER_USE_BUTTON_LABELS:=1,",
"050000004c050000cc090000df070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,",
diff --git a/src/joystick/iphoneos/SDL_mfijoystick.m b/src/joystick/iphoneos/SDL_mfijoystick.m
index 639ee27975e..ad542848cf5 100644
--- a/src/joystick/iphoneos/SDL_mfijoystick.m
+++ b/src/joystick/iphoneos/SDL_mfijoystick.m
@@ -188,6 +188,36 @@ @interface GCMicroGamepad (SDL)
return FALSE;
}
static BOOL
+IsControllerSwitchJoyConL(GCController *controller)
+{
+ if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
+ if ([controller.productCategory isEqualToString:@"Nintendo Switch Joy-Con (L)"]) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+static BOOL
+IsControllerSwitchJoyConR(GCController *controller)
+{
+ if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
+ if ([controller.productCategory isEqualToString:@"Nintendo Switch Joy-Con (R)"]) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+static BOOL
+IsControllerSwitchJoyConPair(GCController *controller)
+{
+ if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
+ if ([controller.productCategory isEqualToString:@"Nintendo Switch Joy-Con (L/R)"]) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+static BOOL
IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
{
Uint16 *guid16 = (Uint16 *)device->guid.data;
@@ -222,8 +252,9 @@ @interface GCMicroGamepad (SDL)
BOOL is_ps4 = IsControllerPS4(controller);
BOOL is_ps5 = IsControllerPS5(controller);
BOOL is_switch_pro = IsControllerSwitchPro(controller);
+ BOOL is_switch_joycon_pair = IsControllerSwitchJoyConPair(controller);
#if TARGET_OS_TV
- BOOL is_MFi = (!is_xbox && !is_ps4 && !is_ps5 && !is_switch_pro);
+ BOOL is_MFi = (!is_xbox && !is_ps4 && !is_ps5 && !is_switch_pro && !is_switch_joycon_pair);
#endif
int nbuttons = 0;
BOOL has_direct_menu;
@@ -262,7 +293,8 @@ @interface GCMicroGamepad (SDL)
device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_BACK);
++nbuttons;
}
- if ([gamepad respondsToSelector:@selector(buttonHome)] && gamepad.buttonHome) {
+ /* The Nintendo Switch JoyCon home button doesn't ever show as being held down */
+ if ([gamepad respondsToSelector:@selector(buttonHome)] && gamepad.buttonHome && !is_switch_joycon_pair) {
device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_GUIDE);
++nbuttons;
}
@@ -347,6 +379,10 @@ @interface GCMicroGamepad (SDL)
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_JOY_CON_GRIP;
+ subtype = 0;
} else {
vendor = USB_VENDOR_APPLE;
product = 1;
@@ -367,8 +403,24 @@ @interface GCMicroGamepad (SDL)
device->nbuttons = nbuttons;
} else if (controller.gamepad) {
+ BOOL is_switch_joyconL = IsControllerSwitchJoyConL(controller);
+ BOOL is_switch_joyconR = IsControllerSwitchJoyConR(controller);
int nbuttons = 0;
+ if (is_switch_joyconL) {
+ vendor = USB_VENDOR_NINTENDO;
+ product = USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_LEFT;
+ subtype = 0;
+ } else if (is_switch_joyconR) {
+ vendor = USB_VENDOR_NINTENDO;
+ product = USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_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_CONTROLLER_BUTTON_A);
device->button_mask |= (1 << SDL_CONTROLLER_BUTTON_B);
@@ -380,9 +432,6 @@ @interface GCMicroGamepad (SDL)
nbuttons += 7;
device->uses_pause_handler = SDL_TRUE;
- vendor = USB_VENDOR_APPLE;
- product = 2;
- subtype = 2;
device->naxes = 0; /* no traditional analog inputs */
device->nhats = 1; /* d-pad */
device->nbuttons = nbuttons;
@@ -877,6 +926,23 @@ static int is_macos11(void)
int i;
int pause_button_index = 0;
+#ifdef DEBUG_CONTROLLER_STATE
+ if (@available(macOS 10.16, iOS 14.0, tvOS 14.0, *)) {
+ if (controller.physicalInputProfile) {
+ for (id key in controller.physicalInputProfile.buttons) {
+ GCControllerButtonInput *button = controller.physicalInputProfile.buttons[key];
+ if (button.isPressed)
+ NSLog(@"Button %@ = %s\n", key, button.isPressed ? "pressed" : "released");
+ }
+ for (id key in controller.physicalInputProfile.axes) {
+ GCControllerAxisInput *axis = controller.physicalInputProfile.axes[key];
+ if (axis.value != 0.0f)
+ NSLog(@"Axis %@ = %.2f\n", key, axis.value);
+ }
+ }
+ }
+#endif
+
if (controller.extendedGamepad) {
GCExtendedGamepad *gamepad = controller.extendedGamepad;