Просмотр исходного кода

Added support for Xbox controllers via libusb on macOS

A number of third party Xbox controllers are not supported by macOS, but work with libusb and the SDL HIDAPI driver.
Sam Lantinga 2 дней назад
Родитель
Сommit
18fc4d931a

+ 17 - 6
src/hidapi/SDL_hidapi.c

@@ -543,8 +543,8 @@ static void HIDAPI_ShutdownDiscovery(void)
 // Platform HIDAPI Implementation
 
 #define HIDAPI_USING_SDL_RUNTIME
-#define HIDAPI_IGNORE_DEVICE(BUS, VID, PID, USAGE_PAGE, USAGE, LIBUSB) \
-        SDL_HIDAPI_ShouldIgnoreDevice(BUS, VID, PID, USAGE_PAGE, USAGE, LIBUSB)
+#define HIDAPI_IGNORE_DEVICE(BUS, VID, PID, USAGE_PAGE, USAGE, LIBUSB, LIBUSB_XBOX) \
+        SDL_HIDAPI_ShouldIgnoreDevice(BUS, VID, PID, USAGE_PAGE, USAGE, LIBUSB, LIBUSB_XBOX)
 
 struct PLATFORM_hid_device_;
 typedef struct PLATFORM_hid_device_ PLATFORM_hid_device;
@@ -859,7 +859,7 @@ static const struct {
     { USB_VENDOR_NINTENDO, USB_PRODUCT_NINTENDO_SWITCH2_PRO },
 };
 
-static bool RequiresLibUSB(Uint16 vendor, Uint16 product)
+static bool RequiresLibUSB(Uint16 vendor, Uint16 product, bool libusb_xbox)
 {
     for (int i = 0; i < SDL_arraysize(SDL_libusb_required); ++i) {
         if (vendor == SDL_libusb_required[i].vendor &&
@@ -867,6 +867,17 @@ static bool RequiresLibUSB(Uint16 vendor, Uint16 product)
             return true;
         }
     }
+
+#ifdef SDL_PLATFORM_MACOS
+    // On macOS we want to use libusb if possible for Xbox controllers
+    // that are not supported by the OS. Opening the device via libusb
+    // will fail if the device is supported (and opened) by the OS, so
+    // any devices we return here and can open are fair game.
+    if (libusb_xbox) {
+        return true;
+    }
+#endif // SDL_PLATFORM_MACOS
+
     return false;
 }
 
@@ -1054,10 +1065,10 @@ static void SDLCALL IgnoredDevicesChanged(void *userdata, const char *name, cons
     }
 }
 
-bool SDL_HIDAPI_ShouldIgnoreDevice(int bus, Uint16 vendor_id, Uint16 product_id, Uint16 usage_page, Uint16 usage, bool libusb)
+bool SDL_HIDAPI_ShouldIgnoreDevice(int bus, Uint16 vendor_id, Uint16 product_id, Uint16 usage_page, Uint16 usage, bool libusb, bool libusb_xbox)
 {
     if (libusb) {
-        if (use_libusb_whitelist && !RequiresLibUSB(vendor_id, product_id)) {
+        if (use_libusb_whitelist && !RequiresLibUSB(vendor_id, product_id, libusb_xbox)) {
             return true;
         }
         if (!use_libusb_gamecube &&
@@ -1065,7 +1076,7 @@ bool SDL_HIDAPI_ShouldIgnoreDevice(int bus, Uint16 vendor_id, Uint16 product_id,
             return true;
         }
     } else {
-        if (RequiresLibUSB(vendor_id, product_id)) {
+        if (RequiresLibUSB(vendor_id, product_id, libusb_xbox)) {
             return true;
         }
     }

+ 1 - 1
src/hidapi/SDL_hidapi_c.h

@@ -22,5 +22,5 @@
 
 
 /* Return true if the HIDAPI should ignore a device during enumeration */
-extern bool SDL_HIDAPI_ShouldIgnoreDevice(int bus_type, Uint16 vendor_id, Uint16 product_id, Uint16 usage_page, Uint16 usage, bool libusb);
+extern bool SDL_HIDAPI_ShouldIgnoreDevice(int bus_type, Uint16 vendor_id, Uint16 product_id, Uint16 usage_page, Uint16 usage, bool libusb, bool is_xbox);
 

+ 1 - 1
src/hidapi/android/hid.cpp

@@ -1114,7 +1114,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor
 		const hid_device_info *info = pDevice->GetDeviceInfo();
 
 		/* See if there are any devices we should skip in enumeration */
-		if (SDL_HIDAPI_ShouldIgnoreDevice(HID_API_BUS_UNKNOWN, info->vendor_id, info->product_id, 0, 0, false)) {
+		if (SDL_HIDAPI_ShouldIgnoreDevice(HID_API_BUS_UNKNOWN, info->vendor_id, info->product_id, 0, 0, false, false)) {
 			continue;
 		}
 

+ 1 - 1
src/hidapi/ios/hid.m

@@ -995,7 +995,7 @@ struct hid_device_info  HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
         }
 
         /* See if there are any devices we should skip in enumeration */
-        if (SDL_HIDAPI_ShouldIgnoreDevice(HID_API_BUS_BLUETOOTH, VALVE_USB_VID, device.pid, 0, 0, false)) {
+        if (SDL_HIDAPI_ShouldIgnoreDevice(HID_API_BUS_BLUETOOTH, VALVE_USB_VID, device.pid, 0, 0, false, false)) {
             continue;
         }
 

+ 15 - 16
src/hidapi/libusb/hid.c

@@ -911,21 +911,27 @@ static int is_xboxone(unsigned short vendor_id, const struct libusb_interface_de
 	return 0;
 }
 
-static int should_enumerate_interface(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc)
+static int should_enumerate_interface(unsigned short vendor_id, unsigned short product_id, const struct libusb_interface_descriptor *intf_desc)
 {
+	int is_xbox = (is_xbox360(vendor_id, intf_desc) ||
+	               is_xboxone(vendor_id, intf_desc));
+
 #if 0
 	printf("Checking interface 0x%x %d/%d/%d/%d\n", vendor_id, intf_desc->bInterfaceNumber, intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol);
 #endif
 
-	if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID)
-		return 1;
+#ifdef HIDAPI_IGNORE_DEVICE
+	/* See if there are any devices we should skip in enumeration */
+	if (HIDAPI_IGNORE_DEVICE(HID_API_BUS_USB, vendor_id, product_id, 0, 0, true, is_xbox)) {
+		return 0;
+	}
+#endif
 
-	/* Also enumerate Xbox 360 controllers */
-	if (is_xbox360(vendor_id, intf_desc))
+	/* Enumerate Xbox 360 and Xbox One controllers */
+	if (is_xbox)
 		return 1;
 
-	/* Also enumerate Xbox One controllers */
-	if (is_xboxone(vendor_id, intf_desc))
+	if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID)
 		return 1;
 
 	return 0;
@@ -982,13 +988,6 @@ struct hid_device_info  HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
 			continue;
 		}
 
-#ifdef HIDAPI_IGNORE_DEVICE
-		/* See if there are any devices we should skip in enumeration */
-		if (HIDAPI_IGNORE_DEVICE(HID_API_BUS_USB, dev_vid, dev_pid, 0, 0, true)) {
-			continue;
-		}
-#endif
-
 		res = libusb_get_active_config_descriptor(dev, &conf_desc);
 		if (res < 0)
 			libusb_get_config_descriptor(dev, 0, &conf_desc);
@@ -998,7 +997,7 @@ struct hid_device_info  HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
 				for (k = 0; k < intf->num_altsetting; k++) {
 					const struct libusb_interface_descriptor *intf_desc;
 					intf_desc = &intf->altsetting[k];
-					if (should_enumerate_interface(dev_vid, intf_desc)) {
+					if (should_enumerate_interface(dev_vid, dev_pid, intf_desc)) {
 						struct hid_device_info *tmp;
 
 						res = libusb_open(dev, &handle);
@@ -1485,7 +1484,7 @@ HID_API_EXPORT hid_device *hid_open_path(const char *path)
 			const struct libusb_interface *intf = &conf_desc->interface[j];
 			for (k = 0; k < intf->num_altsetting && !good_open; k++) {
 				const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k];
-				if (should_enumerate_interface(desc.idVendor, intf_desc)) {
+				if (should_enumerate_interface(desc.idVendor, desc.idProduct, intf_desc)) {
 					char dev_path[64];
 					get_path(&dev_path, usb_dev, conf_desc->bConfigurationValue, intf_desc->bInterfaceNumber);
 					if (!strcmp(dev_path, path)) {

+ 1 - 1
src/hidapi/linux/hid.c

@@ -906,7 +906,7 @@ static struct hid_device_info * create_device_info_for_device(struct udev_device
 
 		cur_dev = root;
 		while (cur_dev) {
-			if (HIDAPI_IGNORE_DEVICE(cur_dev->bus_type, cur_dev->vendor_id, cur_dev->product_id, cur_dev->usage_page, cur_dev->usage, false)) {
+			if (HIDAPI_IGNORE_DEVICE(cur_dev->bus_type, cur_dev->vendor_id, cur_dev->product_id, cur_dev->usage_page, cur_dev->usage, false, false)) {
 				struct hid_device_info *tmp = cur_dev;
 
 				cur_dev = tmp->next;

+ 1 - 1
src/hidapi/mac/hid.c

@@ -598,7 +598,7 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
 
 #ifdef HIDAPI_IGNORE_DEVICE
 	/* See if there are any devices we should skip in enumeration */
-	if (HIDAPI_IGNORE_DEVICE(get_bus_type(dev), dev_vid, dev_pid, usage_page, usage, false)) {
+	if (HIDAPI_IGNORE_DEVICE(get_bus_type(dev), dev_vid, dev_pid, usage_page, usage, false, false)) {
 		free(cur_dev);
 		return NULL;
 	}

+ 1 - 1
src/hidapi/windows/hid.c

@@ -1046,7 +1046,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor
 			HidP_GetCaps(pp_data, &caps);
 			HidD_FreePreparsedData(pp_data);
 		}
-		if (HIDAPI_IGNORE_DEVICE(bus_type, attrib.VendorID, attrib.ProductID, caps.UsagePage, caps.Usage, false)) {
+		if (HIDAPI_IGNORE_DEVICE(bus_type, attrib.VendorID, attrib.ProductID, caps.UsagePage, caps.Usage, false, false)) {
 			goto cont_close;
 		}
 #endif

+ 7 - 6
src/joystick/hidapi/SDL_hidapi_xbox360.c

@@ -112,8 +112,8 @@ static void FetchXInputCapabilities(SDL_HIDAPI_Device *device)
         const struct libusb_interface *intf = &conf_desc->interface[device->interface_number];
         intf_desc = &intf->altsetting[0];
         if (intf_desc->extra_length == 17 && intf_desc->extra[1] == 0x21) {
-			ctx->capabilities.type = intf_desc->extra[3];
-			ctx->capabilities.subType = intf_desc->extra[4];
+            ctx->capabilities.type = intf_desc->extra[3];
+            ctx->capabilities.subType = intf_desc->extra[4];
             switch (ctx->capabilities.subType) {
                 case 0x01: // XINPUT_DEVSUBTYPE_GAMEPAD
                     device->joystick_type = SDL_JOYSTICK_TYPE_GAMEPAD;
@@ -177,7 +177,7 @@ static void FetchXInputCapabilities(SDL_HIDAPI_Device *device)
             SDL_Log("   wLeftMotorSpeed: %02x", ctx->capabilities.vibration.wLeftMotorSpeed);
             SDL_Log("   wRightMotorSpeed: %02x", ctx->capabilities.vibration.wRightMotorSpeed);
 #endif
-		}
+        }
         SDL_QuitLibUSB();
     }
 }
@@ -217,10 +217,11 @@ static bool HIDAPI_DriverXbox360_IsSupportedDevice(SDL_HIDAPI_Device *device, co
         if (SDL_IsJoystickSteamVirtualGamepad(vendor_id, product_id, version)) {
             // GCController support doesn't work with the Steam Virtual Gamepad
             return true;
-        } else {
+        }
+        if (device && SDL_strncmp(device->path, "DevSrvsID", 9) == 0) {
             // On macOS when it isn't controlled by the 360Controller driver and
-            // it doesn't look like a Steam virtual gamepad we should rely on
-            // GCController support instead.
+            // it doesn't look like a Steam virtual gamepad and it's not
+            // available via libusb we should rely on GCController support.
             return false;
         }
     }

+ 2 - 1
src/joystick/hidapi/SDL_hidapi_xboxone.c

@@ -369,7 +369,8 @@ static bool HIDAPI_DriverXboxOne_IsSupportedDevice(SDL_HIDAPI_Device *device, co
 
 #if defined(SDL_PLATFORM_MACOS) && defined(SDL_JOYSTICK_MFI)
     if (SDL_GetHintBoolean(SDL_HINT_JOYSTICK_MFI, true) &&
-        !SDL_IsJoystickBluetoothXboxOne(vendor_id, product_id)) {
+        !SDL_IsJoystickBluetoothXboxOne(vendor_id, product_id) &&
+        (device && SDL_strncmp(device->path, "DevSrvsID", 9) == 0)) {
         // On macOS we get a shortened version of the real report and
         // you can't write output reports for wired controllers, so
         // we'll just use the GCController support instead, if available.