Fixed controllers showing up under both MFI and HIDAPI drivers The Game Controller Kit doesn't show the controllers at startup, so the HIDAPI driver sees them first and therefore gets preference when a controller is supported by both drivers. This fixes bug https://github.com/libsdl-org/SDL/issues/4209
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
index c8f34c2..3c7a993 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -1111,6 +1111,40 @@ HIDAPI_IsEquivalentToDevice(Uint16 vendor_id, Uint16 product_id, SDL_HIDAPI_Devi
}
SDL_bool
+HIDAPI_IsDeviceTypePresent(SDL_GameControllerType type)
+{
+ SDL_HIDAPI_Device *device;
+ SDL_bool result = SDL_FALSE;
+
+ /* Make sure we're initialized, as this could be called from other drivers during startup */
+ if (HIDAPI_JoystickInit() < 0) {
+ return SDL_FALSE;
+ }
+
+ if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) {
+ HIDAPI_UpdateDeviceList();
+ SDL_AtomicUnlock(&SDL_HIDAPI_spinlock);
+ }
+
+ SDL_LockJoysticks();
+ device = SDL_HIDAPI_devices;
+ while (device) {
+ if (device->driver &&
+ SDL_GetJoystickGameControllerType(device->name, device->vendor_id, device->product_id, device->interface_number, device->interface_class, device->interface_subclass, device->interface_protocol) == type) {
+ result = SDL_TRUE;
+ break;
+ }
+ device = device->next;
+ }
+ SDL_UnlockJoysticks();
+
+#ifdef DEBUG_HIDAPI
+ SDL_Log("HIDAPI_IsDeviceTypePresent() returning %s for %d\n", result ? "true" : "false", type);
+#endif
+ return result;
+}
+
+SDL_bool
HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
{
SDL_HIDAPI_Device *device;
@@ -1152,6 +1186,7 @@ HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, cons
if (device->driver &&
HIDAPI_IsEquivalentToDevice(vendor_id, product_id, device)) {
result = SDL_TRUE;
+ break;
}
device = device->next;
}
diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h
index b80c6be..9722775 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick_c.h
+++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h
@@ -119,6 +119,9 @@ extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360;
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360W;
extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne;
+/* Return true if a HID device is present and supported as a joystick of the given type */
+extern SDL_bool HIDAPI_IsDeviceTypePresent(SDL_GameControllerType type);
+
/* Return true if a HID device is present and supported as a joystick */
extern SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name);
diff --git a/src/joystick/iphoneos/SDL_mfijoystick.m b/src/joystick/iphoneos/SDL_mfijoystick.m
index 2e8c137..7aee4f5 100644
--- a/src/joystick/iphoneos/SDL_mfijoystick.m
+++ b/src/joystick/iphoneos/SDL_mfijoystick.m
@@ -27,6 +27,7 @@
#include "SDL_stdinc.h"
#include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h"
+#include "../hidapi/SDL_hidapijoystick_c.h"
#include "../usb_ids.h"
#include "SDL_mfijoystick_c.h"
@@ -128,7 +129,49 @@ GetDeviceForIndex(int device_index)
}
#ifdef SDL_JOYSTICK_MFI
-static void
+static BOOL
+IsControllerPS4(GCController *controller)
+{
+ if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
+ if ([controller.productCategory isEqualToString:@"DualShock 4"]) {
+ return TRUE;
+ }
+ } else {
+ if ([controller.vendorName containsString: @"DUALSHOCK"]) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+static BOOL
+IsControllerPS5(GCController *controller)
+{
+ if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
+ if ([controller.productCategory isEqualToString:@"DualSense"]) {
+ return TRUE;
+ }
+ } else {
+ if ([controller.vendorName containsString: @"DualSense"]) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+static BOOL
+IsControllerXbox(GCController *controller)
+{
+ if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
+ if ([controller.productCategory isEqualToString:@"Xbox One"]) {
+ return TRUE;
+ }
+ } else {
+ if ([controller.vendorName containsString: @"Xbox"]) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+static BOOL
IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controller)
{
Uint16 *guid16 = (Uint16 *)device->guid.data;
@@ -153,14 +196,23 @@ IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controlle
if (controller.extendedGamepad) {
GCExtendedGamepad *gamepad = controller.extendedGamepad;
- BOOL is_xbox = [controller.vendorName containsString: @"Xbox"];
- BOOL is_ps4 = [controller.vendorName containsString: @"DUALSHOCK"];
- BOOL is_ps5 = [controller.vendorName containsString: @"DualSense"];
+ BOOL is_xbox = IsControllerXbox(controller);
+ BOOL is_ps4 = IsControllerPS4(controller);
+ BOOL is_ps5 = IsControllerPS5(controller);
#if TARGET_OS_TV
BOOL is_MFi = (!is_xbox && !is_ps4 && !is_ps5);
#endif
int nbuttons = 0;
+#ifdef SDL_JOYSTICK_HIDAPI
+ if ((is_xbox && HIDAPI_IsDeviceTypePresent(SDL_CONTROLLER_TYPE_XBOXONE)) ||
+ (is_ps4 && HIDAPI_IsDeviceTypePresent(SDL_CONTROLLER_TYPE_PS4)) ||
+ (is_ps5 && HIDAPI_IsDeviceTypePresent(SDL_CONTROLLER_TYPE_PS5))) {
+ /* The HIDAPI driver is taking care of this device */
+ return FALSE;
+ }
+#endif
+
/* 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);
@@ -342,6 +394,7 @@ IOS_AddMFIJoystickDevice(SDL_JoystickDeviceItem *device, GCController *controlle
/* This will be set when the first button press of the controller is
* detected. */
controller.playerIndex = -1;
+ return TRUE;
}
#endif /* SDL_JOYSTICK_MFI */
@@ -390,7 +443,10 @@ IOS_AddJoystickDevice(GCController *controller, SDL_bool accelerometer)
#endif /* SDL_JOYSTICK_iOS_ACCELEROMETER */
} else if (controller) {
#ifdef SDL_JOYSTICK_MFI
- IOS_AddMFIJoystickDevice(device, controller);
+ if (!IOS_AddMFIJoystickDevice(device, controller)) {
+ SDL_free(device);
+ return;
+ }
#else
SDL_free(device);
return;