mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-06-13 07:03:54 +00:00
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.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -911,23 +911,29 @@ 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
|
||||
|
||||
#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
|
||||
|
||||
/* Enumerate Xbox 360 and Xbox One controllers */
|
||||
if (is_xbox)
|
||||
return 1;
|
||||
|
||||
if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID)
|
||||
return 1;
|
||||
|
||||
/* Also enumerate Xbox 360 controllers */
|
||||
if (is_xbox360(vendor_id, intf_desc))
|
||||
return 1;
|
||||
|
||||
/* Also enumerate Xbox One controllers */
|
||||
if (is_xboxone(vendor_id, intf_desc))
|
||||
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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user