diff --git a/dlls/winebus.sys/bus.h b/dlls/winebus.sys/bus.h index 34783f2d597..141e6d619bd 100644 --- a/dlls/winebus.sys/bus.h +++ b/dlls/winebus.sys/bus.h @@ -49,3 +49,4 @@ DEVICE_OBJECT* bus_enumerate_hid_devices(const platform_vtbl *vtbl, enum_func fu /* General Bus Functions */ DWORD check_bus_option(UNICODE_STRING *registry_path, const UNICODE_STRING *option, DWORD default_value) DECLSPEC_HIDDEN; +BOOL is_xbox_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; diff --git a/dlls/winebus.sys/bus_iohid.c b/dlls/winebus.sys/bus_iohid.c index 10635f8b0f5..501a40d26ed 100644 --- a/dlls/winebus.sys/bus_iohid.c +++ b/dlls/winebus.sys/bus_iohid.c @@ -287,7 +287,7 @@ static void handle_DeviceMatchingCallback(void *context, IOReturn result, void * DWORD vid, pid, version; CFStringRef str = NULL; WCHAR serial_string[256]; - BOOL is_gamepad; + BOOL is_gamepad = FALSE; TRACE("OS/X IOHID Device Added %p\n", IOHIDDevice); @@ -297,8 +297,50 @@ static void handle_DeviceMatchingCallback(void *context, IOReturn result, void * str = IOHIDDeviceGetProperty(IOHIDDevice, CFSTR(kIOHIDSerialNumberKey)); if (str) CFStringToWSTR(str, serial_string, sizeof(serial_string) / sizeof(WCHAR)); - is_gamepad = (IOHIDDeviceConformsTo(IOHIDDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad) || - IOHIDDeviceConformsTo(IOHIDDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick)); + if (IOHIDDeviceConformsTo(IOHIDDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad) || + IOHIDDeviceConformsTo(IOHIDDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick)) + { + if (is_xbox_gamepad(vid, pid)) + is_gamepad = TRUE; + else + { + int axes=0, buttons=0; + CFArrayRef element_array = IOHIDDeviceCopyMatchingElements( + IOHIDDevice, NULL, kIOHIDOptionsTypeNone); + + if (element_array) { + CFIndex index; + CFIndex count = CFArrayGetCount(element_array); + for (index = 0; index < count; index++) + { + IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(element_array, index); + if (element) + { + int type = IOHIDElementGetType(element); + if (type == kIOHIDElementTypeInput_Button) buttons++; + if (type == kIOHIDElementTypeInput_Axis) axes++; + if (type == kIOHIDElementTypeInput_Misc) + { + uint32_t usage = IOHIDElementGetUsage(element); + switch (usage) + { + case kHIDUsage_GD_X: + case kHIDUsage_GD_Y: + case kHIDUsage_GD_Z: + case kHIDUsage_GD_Rx: + case kHIDUsage_GD_Ry: + case kHIDUsage_GD_Rz: + case kHIDUsage_GD_Slider: + axes ++; + } + } + } + } + CFRelease(element_array); + } + is_gamepad = (axes == 6 && buttons >= 14); + } + } device = bus_create_hid_device(iohid_driver_obj, busidW, vid, pid, version, 0, str?serial_string:NULL, is_gamepad, &GUID_DEVCLASS_IOHID, &iohid_vtbl, sizeof(struct platform_private)); if (!device) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index f80c5d715f0..18e1854f62d 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -439,13 +439,55 @@ static void set_rel_axis_value(struct wine_input_private *ext, int code, int val } } +static INT count_buttons(int device_fd, BYTE *map) +{ + int i; + int button_count = 0; + BYTE keybits[(KEY_MAX+7)/8]; + + if (ioctl(device_fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits) == -1) + { + WARN("ioctl(EVIOCGBIT, EV_KEY) failed: %d %s\n", errno, strerror(errno)); + return FALSE; + } + + for (i = BTN_MISC; i < KEY_MAX; i++) + { + if (test_bit(keybits, i)) + { + if (map) map[i] = button_count; + button_count++; + } + } + return button_count; +} + +static INT count_abs_axis(int device_fd) +{ + BYTE absbits[(ABS_MAX+7)/8]; + int abs_count = 0; + int i; + + if (ioctl(device_fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits) == -1) + { + WARN("ioctl(EVIOCGBIT, EV_ABS) failed: %d %s\n", errno, strerror(errno)); + return 0; + } + + for (i = 0; i < HID_ABS_MAX; i++) + if (test_bit(absbits, i) && + (ABS_TO_HID_MAP[i][1] >= HID_USAGE_GENERIC_X && + ABS_TO_HID_MAP[i][1] <= HID_USAGE_GENERIC_WHEEL)) + abs_count++; + return abs_count; +} + static BOOL build_report_descriptor(struct wine_input_private *ext, struct udev_device *dev) { int abs_pages[TOP_ABS_PAGE][HID_ABS_MAX+1]; int rel_pages[TOP_REL_PAGE][HID_REL_MAX+1]; BYTE absbits[(ABS_MAX+7)/8]; BYTE relbits[(REL_MAX+7)/8]; - BYTE keybits[(KEY_MAX+7)/8]; BYTE *report_ptr; INT i, descript_size; INT report_size; @@ -462,25 +504,12 @@ static BOOL build_report_descriptor(struct wine_input_private *ext, struct udev_ WARN("ioctl(EVIOCGBIT, EV_ABS) failed: %d %s\n", errno, strerror(errno)); return FALSE; } - if (ioctl(ext->base.device_fd, EVIOCGBIT(EV_KEY, sizeof(keybits)), keybits) == -1) - { - WARN("ioctl(EVIOCGBIT, EV_KEY) failed: %d %s\n", errno, strerror(errno)); - return FALSE; - } descript_size = sizeof(REPORT_HEADER) + sizeof(REPORT_TAIL); report_size = 0; /* For now lump all buttons just into incremental usages, Ignore Keys */ - button_count = 0; - for (i = BTN_MISC; i < KEY_MAX; i++) - { - if (test_bit(keybits, i)) - { - ext->button_map[i] = button_count; - button_count++; - } - } + button_count = count_buttons(ext->base.device_fd, ext->button_map); if (button_count) { descript_size += sizeof(REPORT_BUTTONS); @@ -1138,7 +1167,7 @@ static void try_add_device(struct udev_device *dev) const char *subsystem; const char *devnode; WCHAR *serial = NULL; - const char* gamepad = NULL; + BOOL is_gamepad = FALSE; int fd; if (!(devnode = udev_device_get_devnode(dev))) @@ -1188,7 +1217,6 @@ static void try_add_device(struct udev_device *dev) if (ioctl(fd, EVIOCGUNIQ(254), device_uid) >= 0 && device_uid[0]) serial = strdupAtoW(device_uid); - gamepad = udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK"); vid = device_id.vendor; pid = device_id.product; version = device_id.version; @@ -1198,18 +1226,31 @@ static void try_add_device(struct udev_device *dev) WARN("Could not get device to query VID, PID, Version and Serial\n"); #endif + if (is_xbox_gamepad(vid, pid)) + is_gamepad = TRUE; +#ifdef HAS_PROPER_INPUT_HEADER + else + { + int axes=0, buttons=0; + axes = count_abs_axis(fd); + buttons = count_buttons(fd, NULL); + is_gamepad = (axes == 6 && buttons >= 14); + } +#endif + + TRACE("Found udev device %s (vid %04x, pid %04x, version %u, serial %s)\n", debugstr_a(devnode), vid, pid, version, debugstr_w(serial)); if (strcmp(subsystem, "hidraw") == 0) { - device = bus_create_hid_device(udev_driver_obj, hidraw_busidW, vid, pid, version, 0, serial, FALSE, + device = bus_create_hid_device(udev_driver_obj, hidraw_busidW, vid, pid, version, 0, serial, is_gamepad, &GUID_DEVCLASS_HIDRAW, &hidraw_vtbl, sizeof(struct platform_private)); } #ifdef HAS_PROPER_INPUT_HEADER else if (strcmp(subsystem, "input") == 0) { - device = bus_create_hid_device(udev_driver_obj, lnxev_busidW, vid, pid, version, 0, serial, (gamepad != NULL), + device = bus_create_hid_device(udev_driver_obj, lnxev_busidW, vid, pid, version, 0, serial, is_gamepad, &GUID_DEVCLASS_LINUXEVENT, &lnxev_vtbl, sizeof(struct wine_input_private)); } #endif diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index 87d05f056d6..633447746de 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -43,6 +43,22 @@ WINE_DEFAULT_DEBUG_CHANNEL(plugplay); WINE_DECLARE_DEBUG_CHANNEL(hid_report); +#define VID_MICROSOFT 0x045e + +static const WORD PID_XBOX_CONTROLLERS[] = { + 0x0202, /* Xbox Controller */ + 0x0285, /* Xbox Controller S */ + 0x0289, /* Xbox Controller S */ + 0x028e, /* Xbox360 Controller */ + 0x028f, /* Xbox360 Wireless Controller */ + 0x02d1, /* Xbox One Controller */ + 0x02dd, /* Xbox One Controller (Covert Forces/Firmware 2015) */ + 0x02e3, /* Xbox One Elite Controller */ + 0x02e6, /* Wireless XBox Controller Dongle */ + 0x02ea, /* Xbox One S Controller */ + 0x0719, /* Xbox 360 Wireless Adapter */ +}; + struct pnp_device { struct list entry; @@ -673,6 +689,19 @@ DWORD check_bus_option(UNICODE_STRING *registry_path, const UNICODE_STRING *opti return output; } +BOOL is_xbox_gamepad(WORD vid, WORD pid) +{ + int i; + + if (vid != VID_MICROSOFT) + return FALSE; + + for (i = 0; i < sizeof(PID_XBOX_CONTROLLERS)/sizeof(*PID_XBOX_CONTROLLERS); i++) + if (pid == PID_XBOX_CONTROLLERS[i]) return TRUE; + + return FALSE; +} + NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) { static const WCHAR udevW[] = {'\\','D','r','i','v','e','r','\\','U','D','E','V',0};