From 5b3616c325c4461fe7e038706502ff6c300f539d Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 23 Nov 2020 18:24:05 -0800 Subject: [PATCH] Generalized the raw input controller driver and moved XInput/WGI detection into it for XInput devices This fixes bad report parsing for various newer Xbox controllers, and this driver is now preferred over XInput, since it handles more than 4 controllers. --- VisualC/SDL/SDL.vcxproj | 4 +- VisualC/SDL/SDL.vcxproj.filters | 27 +- src/core/windows/SDL_hid.c | 95 ++ src/core/windows/SDL_hid.h | 208 +++ src/hidapi/SDL_hidapi.c | 133 +- src/hidapi/windows/hid.c | 5 + src/joystick/SDL_gamecontroller.c | 2 +- src/joystick/SDL_joystick.c | 7 +- src/joystick/hidapi/SDL_hidapi_gamecube.c | 9 +- src/joystick/hidapi/SDL_hidapi_ps4.c | 7 +- src/joystick/hidapi/SDL_hidapi_ps5.c | 7 +- src/joystick/hidapi/SDL_hidapi_steam.c | 5 +- src/joystick/hidapi/SDL_hidapi_switch.c | 7 +- src/joystick/hidapi/SDL_hidapi_xbox360.c | 1026 +---------- src/joystick/hidapi/SDL_hidapi_xbox360w.c | 7 +- src/joystick/hidapi/SDL_hidapi_xboxone.c | 11 +- src/joystick/hidapi/SDL_hidapijoystick.c | 35 +- src/joystick/hidapi/SDL_hidapijoystick_c.h | 13 +- src/joystick/usb_ids.h | 23 + src/joystick/windows/SDL_rawinputjoystick.c | 1519 ++++++++++++++--- src/joystick/windows/SDL_rawinputjoystick_c.h | 3 + .../windows/SDL_windows_gaming_input.c | 7 + src/joystick/windows/SDL_xinputjoystick.c | 7 +- 23 files changed, 1804 insertions(+), 1363 deletions(-) create mode 100644 src/core/windows/SDL_hid.c create mode 100644 src/core/windows/SDL_hid.h diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index a061864e1..e592e94e3 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -299,6 +299,7 @@ + @@ -405,6 +406,7 @@ + @@ -426,7 +428,7 @@ - + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index af4c6b28a..f14a8b556 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -231,6 +231,7 @@ API Headers + @@ -262,12 +263,15 @@ + + + @@ -294,6 +298,7 @@ + @@ -327,10 +332,7 @@ - - - - + @@ -348,6 +350,7 @@ + @@ -364,13 +367,16 @@ + - + + + @@ -382,6 +388,8 @@ + + @@ -407,6 +415,8 @@ + + @@ -435,6 +445,8 @@ + + @@ -488,11 +500,6 @@ - - - - - diff --git a/src/core/windows/SDL_hid.c b/src/core/windows/SDL_hid.c new file mode 100644 index 000000000..155d87a21 --- /dev/null +++ b/src/core/windows/SDL_hid.c @@ -0,0 +1,95 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2020 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef __WINRT__ + +#include "SDL_assert.h" +#include "SDL_hid.h" + + +HidD_GetString_t SDL_HidD_GetManufacturerString; +HidD_GetString_t SDL_HidD_GetProductString; +HidD_GetPreparsedData_t SDL_HidD_GetPreparsedData; +HidD_FreePreparsedData_t SDL_HidD_FreePreparsedData; +HidP_GetCaps_t SDL_HidP_GetCaps; +HidP_GetButtonCaps_t SDL_HidP_GetButtonCaps; +HidP_GetValueCaps_t SDL_HidP_GetValueCaps; +HidP_MaxDataListLength_t SDL_HidP_MaxDataListLength; +HidP_GetData_t SDL_HidP_GetData; + +static HMODULE s_pHIDDLL = 0; +static int s_HIDDLLRefCount = 0; + + +int +WIN_LoadHIDDLL(void) +{ + if (s_pHIDDLL) { + SDL_assert(s_HIDDLLRefCount > 0); + s_HIDDLLRefCount++; + return 0; /* already loaded */ + } + + s_pHIDDLL = LoadLibrary(L"hid.dll"); + if (!s_pHIDDLL) { + return -1; + } + + SDL_assert(s_HIDDLLRefCount == 0); + s_HIDDLLRefCount = 1; + + SDL_HidD_GetManufacturerString = (HidD_GetString_t)GetProcAddress(s_pHIDDLL, "HidD_GetManufacturerString"); + SDL_HidD_GetProductString = (HidD_GetString_t)GetProcAddress(s_pHIDDLL, "HidD_GetProductString"); + SDL_HidD_GetPreparsedData = (HidD_GetPreparsedData_t)GetProcAddress(s_pHIDDLL, "HidD_GetPreparsedData"); + SDL_HidD_FreePreparsedData = (HidD_FreePreparsedData_t)GetProcAddress(s_pHIDDLL, "HidD_FreePreparsedData"); + SDL_HidP_GetCaps = (HidP_GetCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetCaps"); + SDL_HidP_GetButtonCaps = (HidP_GetButtonCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetButtonCaps"); + SDL_HidP_GetValueCaps = (HidP_GetValueCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetValueCaps"); + SDL_HidP_MaxDataListLength = (HidP_MaxDataListLength_t)GetProcAddress(s_pHIDDLL, "HidP_MaxDataListLength"); + SDL_HidP_GetData = (HidP_GetData_t)GetProcAddress(s_pHIDDLL, "HidP_GetData"); + if (!SDL_HidD_GetManufacturerString || !SDL_HidD_GetProductString || !SDL_HidD_GetPreparsedData || + !SDL_HidD_FreePreparsedData || !SDL_HidP_GetCaps || !SDL_HidP_GetButtonCaps || + !SDL_HidP_GetValueCaps || !SDL_HidP_MaxDataListLength || !SDL_HidP_GetData) { + WIN_UnloadHIDDLL(); + return -1; + } + + return 0; +} + +void +WIN_UnloadHIDDLL(void) +{ + if (s_pHIDDLL) { + SDL_assert(s_HIDDLLRefCount > 0); + if (--s_HIDDLLRefCount == 0) { + FreeLibrary(s_pHIDDLL); + s_pHIDDLL = NULL; + } + } else { + SDL_assert(s_HIDDLLRefCount == 0); + } +} + +#endif /* !__WINRT__ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/core/windows/SDL_hid.h b/src/core/windows/SDL_hid.h new file mode 100644 index 000000000..fb4c80ea4 --- /dev/null +++ b/src/core/windows/SDL_hid.h @@ -0,0 +1,208 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2020 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#ifndef SDL_hid_h_ +#define SDL_hid_h_ + +#include "SDL_windows.h" + +#ifndef __WINRT__ + +typedef LONG NTSTATUS; +typedef USHORT USAGE; +typedef struct _HIDP_PREPARSED_DATA *PHIDP_PREPARSED_DATA; + +typedef struct _HIDD_ATTRIBUTES +{ + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; +} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + +typedef enum { + HidP_Input = 0, + HidP_Output = 1, + HidP_Feature = 2 +} HIDP_REPORT_TYPE; + +typedef struct { + USAGE UsagePage; + UCHAR ReportID; + BOOLEAN IsAlias; + USHORT BitField; + USHORT LinkCollection; + USAGE LinkUsage; + USAGE LinkUsagePage; + BOOLEAN IsRange; + BOOLEAN IsStringRange; + BOOLEAN IsDesignatorRange; + BOOLEAN IsAbsolute; + ULONG Reserved[ 10 ]; + union { + struct { + USAGE UsageMin; + USAGE UsageMax; + USHORT StringMin; + USHORT StringMax; + USHORT DesignatorMin; + USHORT DesignatorMax; + USHORT DataIndexMin; + USHORT DataIndexMax; + } Range; + struct { + USAGE Usage; + USAGE Reserved1; + USHORT StringIndex; + USHORT Reserved2; + USHORT DesignatorIndex; + USHORT Reserved3; + USHORT DataIndex; + USHORT Reserved4; + } NotRange; + }; +} HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS; + +typedef struct { + USAGE UsagePage; + UCHAR ReportID; + BOOLEAN IsAlias; + USHORT BitField; + USHORT LinkCollection; + USAGE LinkUsage; + USAGE LinkUsagePage; + BOOLEAN IsRange; + BOOLEAN IsStringRange; + BOOLEAN IsDesignatorRange; + BOOLEAN IsAbsolute; + BOOLEAN HasNull; + UCHAR Reserved; + USHORT BitSize; + USHORT ReportCount; + USHORT Reserved2[ 5 ]; + ULONG UnitsExp; + ULONG Units; + LONG LogicalMin; + LONG LogicalMax; + LONG PhysicalMin; + LONG PhysicalMax; + union { + struct { + USAGE UsageMin; + USAGE UsageMax; + USHORT StringMin; + USHORT StringMax; + USHORT DesignatorMin; + USHORT DesignatorMax; + USHORT DataIndexMin; + USHORT DataIndexMax; + } Range; + struct { + USAGE Usage; + USAGE Reserved1; + USHORT StringIndex; + USHORT Reserved2; + USHORT DesignatorIndex; + USHORT Reserved3; + USHORT DataIndex; + USHORT Reserved4; + } NotRange; + }; +} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS; + +typedef struct { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[ 17 ]; + USHORT NumberLinkCollectionNodes; + USHORT NumberInputButtonCaps; + USHORT NumberInputValueCaps; + USHORT NumberInputDataIndices; + USHORT NumberOutputButtonCaps; + USHORT NumberOutputValueCaps; + USHORT NumberOutputDataIndices; + USHORT NumberFeatureButtonCaps; + USHORT NumberFeatureValueCaps; + USHORT NumberFeatureDataIndices; +} HIDP_CAPS, *PHIDP_CAPS; + +typedef struct { + USHORT DataIndex; + USHORT Reserved; + union { + ULONG RawValue; + BOOLEAN On; + }; +} HIDP_DATA, *PHIDP_DATA; + +#define HIDP_ERROR_CODES( p1, p2 ) ((NTSTATUS)(((p1) << 28) | (0x11 << 16) | (p2))) +#define HIDP_STATUS_SUCCESS HIDP_ERROR_CODES( 0x0, 0x0000 ) +#define HIDP_STATUS_NULL HIDP_ERROR_CODES( 0x8, 0x0001 ) +#define HIDP_STATUS_INVALID_PREPARSED_DATA HIDP_ERROR_CODES( 0xC, 0x0001 ) +#define HIDP_STATUS_INVALID_REPORT_TYPE HIDP_ERROR_CODES( 0xC, 0x0002 ) +#define HIDP_STATUS_INVALID_REPORT_LENGTH HIDP_ERROR_CODES( 0xC, 0x0003 ) +#define HIDP_STATUS_USAGE_NOT_FOUND HIDP_ERROR_CODES( 0xC, 0x0004 ) +#define HIDP_STATUS_VALUE_OUT_OF_RANGE HIDP_ERROR_CODES( 0xC, 0x0005 ) +#define HIDP_STATUS_BAD_LOG_PHY_VALUES HIDP_ERROR_CODES( 0xC, 0x0006 ) +#define HIDP_STATUS_BUFFER_TOO_SMALL HIDP_ERROR_CODES( 0xC, 0x0007 ) +#define HIDP_STATUS_INTERNAL_ERROR HIDP_ERROR_CODES( 0xC, 0x0008 ) +#define HIDP_STATUS_I8042_TRANS_UNKNOWN HIDP_ERROR_CODES( 0xC, 0x0009 ) +#define HIDP_STATUS_INCOMPATIBLE_REPORT_ID HIDP_ERROR_CODES( 0xC, 0x000A ) +#define HIDP_STATUS_NOT_VALUE_ARRAY HIDP_ERROR_CODES( 0xC, 0x000B ) +#define HIDP_STATUS_IS_VALUE_ARRAY HIDP_ERROR_CODES( 0xC, 0x000C ) +#define HIDP_STATUS_DATA_INDEX_NOT_FOUND HIDP_ERROR_CODES( 0xC, 0x000D ) +#define HIDP_STATUS_DATA_INDEX_OUT_OF_RANGE HIDP_ERROR_CODES( 0xC, 0x000E ) +#define HIDP_STATUS_BUTTON_NOT_PRESSED HIDP_ERROR_CODES( 0xC, 0x000F ) +#define HIDP_STATUS_REPORT_DOES_NOT_EXIST HIDP_ERROR_CODES( 0xC, 0x0010 ) +#define HIDP_STATUS_NOT_IMPLEMENTED HIDP_ERROR_CODES( 0xC, 0x0020 ) + + +extern int WIN_LoadHIDDLL(void); +extern void WIN_UnloadHIDDLL(void); + +typedef BOOLEAN (WINAPI *HidD_GetString_t)(HANDLE HidDeviceObject, PVOID Buffer, ULONG BufferLength); +typedef BOOLEAN (WINAPI *HidD_GetPreparsedData_t)(HANDLE HidDeviceObject, PHIDP_PREPARSED_DATA *PreparsedData); +typedef BOOLEAN (WINAPI *HidD_FreePreparsedData_t)(PHIDP_PREPARSED_DATA PreparsedData); +typedef NTSTATUS (WINAPI *HidP_GetCaps_t)(PHIDP_PREPARSED_DATA PreparsedData, PHIDP_CAPS Capabilities); +typedef NTSTATUS (WINAPI *HidP_GetButtonCaps_t)(HIDP_REPORT_TYPE ReportType, PHIDP_BUTTON_CAPS ButtonCaps, PUSHORT ButtonCapsLength, PHIDP_PREPARSED_DATA PreparsedData); +typedef NTSTATUS (WINAPI *HidP_GetValueCaps_t)(HIDP_REPORT_TYPE ReportType, PHIDP_VALUE_CAPS ValueCaps, PUSHORT ValueCapsLength, PHIDP_PREPARSED_DATA PreparsedData); +typedef ULONG (WINAPI *HidP_MaxDataListLength_t)(HIDP_REPORT_TYPE ReportType, PHIDP_PREPARSED_DATA PreparsedData); +typedef NTSTATUS (WINAPI *HidP_GetData_t)(HIDP_REPORT_TYPE ReportType, PHIDP_DATA DataList, PULONG DataLength, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength); + +extern HidD_GetString_t SDL_HidD_GetManufacturerString; +extern HidD_GetString_t SDL_HidD_GetProductString; +extern HidD_GetPreparsedData_t SDL_HidD_GetPreparsedData; +extern HidD_FreePreparsedData_t SDL_HidD_FreePreparsedData; +extern HidP_GetCaps_t SDL_HidP_GetCaps; +extern HidP_GetButtonCaps_t SDL_HidP_GetButtonCaps; +extern HidP_GetValueCaps_t SDL_HidP_GetValueCaps; +extern HidP_MaxDataListLength_t SDL_HidP_MaxDataListLength; +extern HidP_GetData_t SDL_HidP_GetData; + +#endif /* !__WINRT__ */ + +#endif /* SDL_hid_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/hidapi/SDL_hidapi.c b/src/hidapi/SDL_hidapi.c index 6372ffa4d..aa44e6930 100644 --- a/src/hidapi/SDL_hidapi.c +++ b/src/hidapi/SDL_hidapi.c @@ -129,6 +129,67 @@ static const SDL_UDEV_Symbols *udev_ctx = NULL; #undef make_path #undef read_thread +#ifdef HAVE_HIDAPI_NVAGIPMAN +#define HAVE_DRIVER_BACKEND +#endif + +#ifdef HAVE_DRIVER_BACKEND + +/* DRIVER HIDAPI Implementation */ + +#define hid_device_ DRIVER_hid_device_ +#define hid_device DRIVER_hid_device +#define hid_device_info DRIVER_hid_device_info +#define hid_init DRIVER_hid_init +#define hid_exit DRIVER_hid_exit +#define hid_enumerate DRIVER_hid_enumerate +#define hid_free_enumeration DRIVER_hid_free_enumeration +#define hid_open DRIVER_hid_open +#define hid_open_path DRIVER_hid_open_path +#define hid_write DRIVER_hid_write +#define hid_read_timeout DRIVER_hid_read_timeout +#define hid_read DRIVER_hid_read +#define hid_set_nonblocking DRIVER_hid_set_nonblocking +#define hid_send_feature_report DRIVER_hid_send_feature_report +#define hid_get_feature_report DRIVER_hid_get_feature_report +#define hid_close DRIVER_hid_close +#define hid_get_manufacturer_string DRIVER_hid_get_manufacturer_string +#define hid_get_product_string DRIVER_hid_get_product_string +#define hid_get_serial_number_string DRIVER_hid_get_serial_number_string +#define hid_get_indexed_string DRIVER_hid_get_indexed_string +#define hid_error DRIVER_hid_error + +#ifdef HAVE_HIDAPI_NVAGIPMAN +#include "nvagipman/hid.c" +#else +#error Need a driver hid.c for this platform! +#endif + +#undef hid_device_ +#undef hid_device +#undef hid_device_info +#undef hid_init +#undef hid_exit +#undef hid_enumerate +#undef hid_free_enumeration +#undef hid_open +#undef hid_open_path +#undef hid_write +#undef hid_read_timeout +#undef hid_read +#undef hid_set_nonblocking +#undef hid_send_feature_report +#undef hid_get_feature_report +#undef hid_close +#undef hid_get_manufacturer_string +#undef hid_get_product_string +#undef hid_get_serial_number_string +#undef hid_get_indexed_string +#undef hid_error + +#endif /* HAVE_DRIVER_BACKEND */ + + #ifdef SDL_LIBUSB_DYNAMIC /* libusb HIDAPI Implementation */ @@ -298,23 +359,21 @@ SDL_libusb_get_string_descriptor(libusb_device_handle *dev, /* Shared HIDAPI Implementation */ #undef HIDAPI_H__ -#include "hidapi.h" +#include "hidapi/hidapi.h" struct hidapi_backend { -#define F(x) typeof(x) *x - F(hid_write); - F(hid_read_timeout); - F(hid_read); - F(hid_set_nonblocking); - F(hid_send_feature_report); - F(hid_get_feature_report); - F(hid_close); - F(hid_get_manufacturer_string); - F(hid_get_product_string); - F(hid_get_serial_number_string); - F(hid_get_indexed_string); - F(hid_error); -#undef F + int (*hid_write)(hid_device* device, const unsigned char* data, size_t length); + int (*hid_read_timeout)(hid_device* device, unsigned char* data, size_t length, int milliseconds); + int (*hid_read)(hid_device* device, unsigned char* data, size_t length); + int (*hid_set_nonblocking)(hid_device* device, int nonblock); + int (*hid_send_feature_report)(hid_device* device, const unsigned char* data, size_t length); + int (*hid_get_feature_report)(hid_device* device, unsigned char* data, size_t length); + void (*hid_close)(hid_device* device); + int (*hid_get_manufacturer_string)(hid_device* device, wchar_t* string, size_t maxlen); + int (*hid_get_product_string)(hid_device* device, wchar_t* string, size_t maxlen); + int (*hid_get_serial_number_string)(hid_device* device, wchar_t* string, size_t maxlen); + int (*hid_get_indexed_string)(hid_device* device, int string_index, wchar_t* string, size_t maxlen); + const wchar_t* (*hid_error)(hid_device* device); }; #if HAVE_PLATFORM_BACKEND @@ -334,6 +393,23 @@ static const struct hidapi_backend PLATFORM_Backend = { }; #endif /* HAVE_PLATFORM_BACKEND */ +#if HAVE_DRIVER_BACKEND +static const struct hidapi_backend DRIVER_Backend = { + (void*)DRIVER_hid_write, + (void*)DRIVER_hid_read_timeout, + (void*)DRIVER_hid_read, + (void*)DRIVER_hid_set_nonblocking, + (void*)DRIVER_hid_send_feature_report, + (void*)DRIVER_hid_get_feature_report, + (void*)DRIVER_hid_close, + (void*)DRIVER_hid_get_manufacturer_string, + (void*)DRIVER_hid_get_product_string, + (void*)DRIVER_hid_get_serial_number_string, + (void*)DRIVER_hid_get_indexed_string, + (void*)DRIVER_hid_error +}; +#endif /* HAVE_DRIVER_BACKEND */ + #ifdef SDL_LIBUSB_DYNAMIC static const struct hidapi_backend LIBUSB_Backend = { (void*)LIBUSB_hid_write, @@ -361,7 +437,7 @@ struct _HIDDeviceWrapper static HIDDeviceWrapper * CreateHIDDeviceWrapper(hid_device *device, const struct hidapi_backend *backend) { - HIDDeviceWrapper *ret = SDL_malloc(sizeof(*ret)); + HIDDeviceWrapper *ret = (HIDDeviceWrapper *)SDL_malloc(sizeof(*ret)); ret->device = device; ret->backend = backend; return ret; @@ -540,6 +616,10 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor struct LIBUSB_hid_device_info *usb_devs = NULL; struct LIBUSB_hid_device_info *usb_dev; #endif +#if HAVE_DRIVER_BACKEND + struct DRIVER_hid_device_info* driver_devs = NULL; + struct DRIVER_hid_device_info* driver_dev; +#endif #if HAVE_PLATFORM_BACKEND struct PLATFORM_hid_device_info *raw_devs = NULL; struct PLATFORM_hid_device_info *raw_dev; @@ -636,6 +716,15 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsi return WrapHIDDevice(wrapper); } #endif /* HAVE_PLATFORM_BACKEND */ + +#if HAVE_DRIVER_BACKEND + if ((pDevice = (hid_device*) DRIVER_hid_open(vendor_id, product_id, serial_number)) != NULL) { + + HIDDeviceWrapper *wrapper = CreateHIDDeviceWrapper(pDevice, &DRIVER_Backend); + return WrapHIDDevice(wrapper); + } +#endif /* HAVE_DRIVER_BACKEND */ + #ifdef SDL_LIBUSB_DYNAMIC if (libusb_ctx.libhandle && (pDevice = (hid_device*) LIBUSB_hid_open(vendor_id, product_id, serial_number)) != NULL) { @@ -644,6 +733,7 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsi return WrapHIDDevice(wrapper); } #endif /* SDL_LIBUSB_DYNAMIC */ + return NULL; } @@ -663,6 +753,16 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bEx return WrapHIDDevice(wrapper); } #endif /* HAVE_PLATFORM_BACKEND */ + +#if HAVE_DRIVER_BACKEND + if (udev_ctx && + (pDevice = (hid_device*) DRIVER_hid_open_path(path, bExclusive)) != NULL) { + + HIDDeviceWrapper *wrapper = CreateHIDDeviceWrapper(pDevice, &DRIVER_Backend); + return WrapHIDDevice(wrapper); + } +#endif /* HAVE_DRIVER_BACKEND */ + #ifdef SDL_LIBUSB_DYNAMIC if (libusb_ctx.libhandle && (pDevice = (hid_device*) LIBUSB_hid_open_path(path, bExclusive)) != NULL) { @@ -671,6 +771,7 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path, int bEx return WrapHIDDevice(wrapper); } #endif /* SDL_LIBUSB_DYNAMIC */ + return NULL; } diff --git a/src/hidapi/windows/hid.c b/src/hidapi/windows/hid.c index 358cc1000..7802924d6 100644 --- a/src/hidapi/windows/hid.c +++ b/src/hidapi/windows/hid.c @@ -409,6 +409,11 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor goto cont; } + /* XInput devices don't get real HID reports and are better handled by the raw input driver */ + if (strstr(device_interface_detail_data->DevicePath, "&ig_") != NULL) { + goto cont; + } + /* Make sure this device is of Setup Class "HIDClass" and has a driver bound to it. */ /* In the main HIDAPI tree this is a loop which will erroneously open diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index fa9c7c62d..1162a371a 100644 --- a/src/joystick/SDL_gamecontroller.c +++ b/src/joystick/SDL_gamecontroller.c @@ -616,7 +616,7 @@ static ControllerMapping_t *SDL_CreateMappingForRAWINPUTController(SDL_JoystickG char mapping_string[1024]; SDL_strlcpy(mapping_string, "none,*,", sizeof(mapping_string)); - SDL_strlcat(mapping_string, "a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,", sizeof(mapping_string)); + SDL_strlcat(mapping_string, "a:b0,b:b1,x:b2,y:b3,back:b6,guide:b10,start:b7,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a1,lefty:a0,rightx:a3,righty:a2,lefttrigger:a4,righttrigger:a5,", sizeof(mapping_string)); return SDL_PrivateAddMappingForGUID(guid, mapping_string, &existing, SDL_CONTROLLER_MAPPING_PRIORITY_DEFAULT); diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 8516dd903..c9560f5a4 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -51,13 +51,12 @@ #endif static SDL_JoystickDriver *SDL_joystick_drivers[] = { -#ifdef SDL_JOYSTICK_RAWINPUT /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */ - /* Also before HIDAPI, as HIDAPI wants to check if this driver is handling things */ - &SDL_RAWINPUT_JoystickDriver, -#endif #ifdef SDL_JOYSTICK_HIDAPI /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */ &SDL_HIDAPI_JoystickDriver, #endif +#ifdef SDL_JOYSTICK_RAWINPUT /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */ + &SDL_RAWINPUT_JoystickDriver, +#endif #if defined(SDL_JOYSTICK_WGI) &SDL_WGI_JoystickDriver, #endif diff --git a/src/joystick/hidapi/SDL_hidapi_gamecube.c b/src/joystick/hidapi/SDL_hidapi_gamecube.c index d85516ba3..ab36742bb 100644 --- a/src/joystick/hidapi/SDL_hidapi_gamecube.c +++ b/src/joystick/hidapi/SDL_hidapi_gamecube.c @@ -175,11 +175,11 @@ HIDAPI_DriverGameCube_InitDevice(SDL_HIDAPI_Device *device) if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */ if (ctx->joysticks[i] == -1) { ResetAxisRange(ctx, i); - HIDAPI_JoystickConnected(device, &ctx->joysticks[i], SDL_FALSE); + HIDAPI_JoystickConnected(device, &ctx->joysticks[i]); } } else { if (ctx->joysticks[i] != -1) { - HIDAPI_JoystickDisconnected(device, ctx->joysticks[i], SDL_FALSE); + HIDAPI_JoystickDisconnected(device, ctx->joysticks[i]); ctx->joysticks[i] = -1; } continue; @@ -251,7 +251,7 @@ HIDAPI_DriverGameCube_UpdateDevice(SDL_HIDAPI_Device *device) if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */ if (ctx->joysticks[i] == -1) { ResetAxisRange(ctx, i); - HIDAPI_JoystickConnected(device, &ctx->joysticks[i], SDL_FALSE); + HIDAPI_JoystickConnected(device, &ctx->joysticks[i]); } joystick = SDL_JoystickFromInstanceID(ctx->joysticks[i]); @@ -261,7 +261,7 @@ HIDAPI_DriverGameCube_UpdateDevice(SDL_HIDAPI_Device *device) } } else { if (ctx->joysticks[i] != -1) { - HIDAPI_JoystickDisconnected(device, ctx->joysticks[i], SDL_FALSE); + HIDAPI_JoystickDisconnected(device, ctx->joysticks[i]); ctx->joysticks[i] = -1; } continue; @@ -432,7 +432,6 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube = HIDAPI_DriverGameCube_SetJoystickSensorsEnabled, HIDAPI_DriverGameCube_CloseJoystick, HIDAPI_DriverGameCube_FreeDevice, - NULL, }; #endif /* SDL_JOYSTICK_HIDAPI_GAMECUBE */ diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c index 1bb753d92..f3d351f97 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps4.c +++ b/src/joystick/hidapi/SDL_hidapi_ps4.c @@ -143,7 +143,7 @@ typedef struct { static SDL_bool HIDAPI_DriverPS4_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol) { - return (type == SDL_CONTROLLER_TYPE_PS4); + return (type == SDL_CONTROLLER_TYPE_PS4) ? SDL_TRUE : SDL_FALSE; } static const char * @@ -202,7 +202,7 @@ SetLedsForPlayerIndex(DS4EffectsState_t *effects, int player_index) static SDL_bool HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device) { - return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE); + return HIDAPI_JoystickConnected(device, NULL); } static int @@ -769,7 +769,7 @@ HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device) if (size < 0) { /* Read error, device is disconnected */ - HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE); + HIDAPI_JoystickDisconnected(device, joystick->instance_id); } return (size >= 0); } @@ -807,7 +807,6 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 = HIDAPI_DriverPS4_SetJoystickSensorsEnabled, HIDAPI_DriverPS4_CloseJoystick, HIDAPI_DriverPS4_FreeDevice, - NULL }; #endif /* SDL_JOYSTICK_HIDAPI_PS4 */ diff --git a/src/joystick/hidapi/SDL_hidapi_ps5.c b/src/joystick/hidapi/SDL_hidapi_ps5.c index bbf6d9342..f312c4edf 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps5.c +++ b/src/joystick/hidapi/SDL_hidapi_ps5.c @@ -171,7 +171,7 @@ typedef struct { static SDL_bool HIDAPI_DriverPS5_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol) { - return (type == SDL_CONTROLLER_TYPE_PS5); + return (type == SDL_CONTROLLER_TYPE_PS5) ? SDL_TRUE : SDL_FALSE; } static const char * @@ -220,7 +220,7 @@ SetLedsForPlayerIndex(DS5EffectsState_t *effects, int player_index) static SDL_bool HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device) { - return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE); + return HIDAPI_JoystickConnected(device, NULL); } static int @@ -885,7 +885,7 @@ HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device) if (size < 0) { /* Read error, device is disconnected */ - HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE); + HIDAPI_JoystickDisconnected(device, joystick->instance_id); } return (size >= 0); } @@ -923,7 +923,6 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS5 = HIDAPI_DriverPS5_SetJoystickSensorsEnabled, HIDAPI_DriverPS5_CloseJoystick, HIDAPI_DriverPS5_FreeDevice, - NULL }; #endif /* SDL_JOYSTICK_HIDAPI_PS5 */ diff --git a/src/joystick/hidapi/SDL_hidapi_steam.c b/src/joystick/hidapi/SDL_hidapi_steam.c index 0ae4d2243..87827f476 100644 --- a/src/joystick/hidapi/SDL_hidapi_steam.c +++ b/src/joystick/hidapi/SDL_hidapi_steam.c @@ -971,7 +971,7 @@ HIDAPI_DriverSteam_GetDeviceName(Uint16 vendor_id, Uint16 product_id) static SDL_bool HIDAPI_DriverSteam_InitDevice(SDL_HIDAPI_Device *device) { - return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE); + return HIDAPI_JoystickConnected(device, NULL); } static int @@ -1160,7 +1160,7 @@ HIDAPI_DriverSteam_UpdateDevice(SDL_HIDAPI_Device *device) if (r <= 0) { /* Failed to read from controller */ - HIDAPI_JoystickDisconnected(device, device->joysticks[0], SDL_FALSE); + HIDAPI_JoystickDisconnected(device, device->joysticks[0]); return SDL_FALSE; } } @@ -1201,7 +1201,6 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam = HIDAPI_DriverSteam_SetSensorsEnabled, HIDAPI_DriverSteam_CloseJoystick, HIDAPI_DriverSteam_FreeDevice, - NULL }; #endif /* SDL_JOYSTICK_HIDAPI_STEAM */ diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c index 16e1e242f..aad03de0c 100644 --- a/src/joystick/hidapi/SDL_hidapi_switch.c +++ b/src/joystick/hidapi/SDL_hidapi_switch.c @@ -288,7 +288,7 @@ HIDAPI_DriverSwitch_IsSupportedDevice(const char *name, SDL_GameControllerType t if (SDL_strcmp( name, "HORI Wireless Switch Pad" ) == 0) { return SDL_FALSE; } - return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO); + return (type == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO) ? SDL_TRUE : SDL_FALSE; } static const char * @@ -697,7 +697,7 @@ static Uint8 RemapButton(SDL_DriverSwitch_Context *ctx, Uint8 button) static SDL_bool HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device) { - return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE); + return HIDAPI_JoystickConnected(device, NULL); } static int @@ -1259,7 +1259,7 @@ HIDAPI_DriverSwitch_UpdateDevice(SDL_HIDAPI_Device *device) if (size < 0) { /* Read error, device is disconnected */ - HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE); + HIDAPI_JoystickDisconnected(device, joystick->instance_id); } return (size >= 0); } @@ -1307,7 +1307,6 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch = HIDAPI_DriverSwitch_SetJoystickSensorsEnabled, HIDAPI_DriverSwitch_CloseJoystick, HIDAPI_DriverSwitch_FreeDevice, - NULL }; #endif /* SDL_JOYSTICK_HIDAPI_SWITCH */ diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360.c b/src/joystick/hidapi/SDL_hidapi_xbox360.c index dfc7c31a4..5da6cd498 100644 --- a/src/joystick/hidapi/SDL_hidapi_xbox360.c +++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c @@ -34,546 +34,10 @@ #ifdef SDL_JOYSTICK_HIDAPI_XBOX360 -#ifdef __WIN32__ -#define SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT -/* This requires the Windows 10 SDK to build */ -/*#define SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT*/ -#endif - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT -#include "../../core/windows/SDL_xinput.h" -#endif - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT -#include "../../core/windows/SDL_windows.h" -typedef struct WindowsGamingInputGamepadState WindowsGamingInputGamepadState; -#define GamepadButtons_GUIDE 0x40000000 -#define COBJMACROS -#include "windows.gaming.input.h" -#endif - -#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT) -#define SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING -#endif - typedef struct { Uint8 last_state[USB_PACKET_LENGTH]; -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING - Uint32 match_state; /* Low 16 bits for button states, high 16 for 4 4bit axes */ - Uint32 last_state_packet; -#endif -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT - SDL_bool xinput_enabled; - SDL_bool xinput_correlated; - Uint8 xinput_correlation_id; - Uint8 xinput_correlation_count; - Uint8 xinput_uncorrelate_count; - Uint8 xinput_slot; -#endif -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT - SDL_bool wgi_correlated; - Uint8 wgi_correlation_id; - Uint8 wgi_correlation_count; - Uint8 wgi_uncorrelate_count; - WindowsGamingInputGamepadState *wgi_slot; -#endif } SDL_DriverXbox360_Context; -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING -static struct { - Uint32 last_state_packet; - SDL_Joystick *joystick; - SDL_Joystick *last_joystick; -} guide_button_candidate; - -typedef struct WindowsMatchState { - SHORT match_axes[4]; -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT - WORD xinput_buttons; -#endif -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT - Uint32 wgi_buttons; -#endif - SDL_bool any_data; -} WindowsMatchState; - -static void HIDAPI_DriverXbox360_FillMatchState(WindowsMatchState *state, Uint32 match_state) -{ - int ii; - state->any_data = SDL_FALSE; - /* SHORT state->match_axes[4] = { - (match_state & 0x000F0000) >> 4, - (match_state & 0x00F00000) >> 8, - (match_state & 0x0F000000) >> 12, - (match_state & 0xF0000000) >> 16, - }; */ - for (ii = 0; ii < 4; ii++) { - state->match_axes[ii] = (match_state & (0x000F0000 << (ii * 4))) >> (4 + ii * 4); - if ((Uint32)(state->match_axes[ii] + 0x1000) > 0x2000) { /* match_state bit is not 0xF, 0x1, or 0x2 */ - state->any_data = SDL_TRUE; - } - } - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT - /* Match axes by checking if the distance between the high 4 bits of axis and the 4 bits from match_state is 1 or less */ -#define XInputAxesMatch(gamepad) (\ - (Uint32)(gamepad.sThumbLX - state->match_axes[0] + 0x1000) <= 0x2fff && \ - (Uint32)(~gamepad.sThumbLY - state->match_axes[1] + 0x1000) <= 0x2fff && \ - (Uint32)(gamepad.sThumbRX - state->match_axes[2] + 0x1000) <= 0x2fff && \ - (Uint32)(~gamepad.sThumbRY - state->match_axes[3] + 0x1000) <= 0x2fff) - /* Explicit -#define XInputAxesMatch(gamepad) (\ - SDL_abs((Sint8)((gamepad.sThumbLX & 0xF000) >> 8) - ((match_state & 0x000F0000) >> 12)) <= 0x10 && \ - SDL_abs((Sint8)((~gamepad.sThumbLY & 0xF000) >> 8) - ((match_state & 0x00F00000) >> 16)) <= 0x10 && \ - SDL_abs((Sint8)((gamepad.sThumbRX & 0xF000) >> 8) - ((match_state & 0x0F000000) >> 20)) <= 0x10 && \ - SDL_abs((Sint8)((~gamepad.sThumbRY & 0xF000) >> 8) - ((match_state & 0xF0000000) >> 24)) <= 0x10) */ - - - state->xinput_buttons = - /* Bitwise map .RLDUWVQTS.KYXBA -> YXBA..WVQTKSRLDU */ - match_state << 12 | (match_state & 0x0780) >> 1 | (match_state & 0x0010) << 1 | (match_state & 0x0040) >> 2 | (match_state & 0x7800) >> 11; - /* Explicit - ((match_state & (1<xinput_buttons) - state->any_data = SDL_TRUE; -#endif - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT - /* Match axes by checking if the distance between the high 4 bits of axis and the 4 bits from match_state is 1 or less */ -#define WindowsGamingInputAxesMatch(gamepad) (\ - (Uint16)(((Sint16)(gamepad.LeftThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[0] + 0x1000) <= 0x2fff && \ - (Uint16)((~(Sint16)(gamepad.LeftThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[1] + 0x1000) <= 0x2fff && \ - (Uint16)(((Sint16)(gamepad.RightThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[2] + 0x1000) <= 0x2fff && \ - (Uint16)((~(Sint16)(gamepad.RightThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[3] + 0x1000) <= 0x2fff) - - - state->wgi_buttons = - /* Bitwise map .RLD UWVQ TS.K YXBA -> ..QT WVRL DUYX BAKS */ - /* RStick/LStick (QT) RShould/LShould (WV) DPad R/L/D/U YXBA bac(K) (S)tart */ - (match_state & 0x0180) << 5 | (match_state & 0x0600) << 1 | (match_state & 0x7800) >> 5 | (match_state & 0x000F) << 2 | (match_state & 0x0010) >> 3 | (match_state & 0x0040) >> 6; - /* Explicit - ((match_state & (1<wgi_buttons) - state->any_data = SDL_TRUE; -#endif - -} - - -#endif - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT -static struct { - XINPUT_STATE_EX state; - SDL_bool connected; /* Currently has an active XInput device */ - SDL_bool used; /* Is currently mapped to an SDL device */ - Uint8 correlation_id; -} xinput_state[XUSER_MAX_COUNT]; -static SDL_bool xinput_device_change = SDL_TRUE; -static SDL_bool xinput_state_dirty = SDL_TRUE; - -static void -HIDAPI_DriverXbox360_UpdateXInput() -{ - DWORD user_index; - if (xinput_device_change) { - for (user_index = 0; user_index < XUSER_MAX_COUNT; user_index++) { - XINPUT_CAPABILITIES capabilities; - xinput_state[user_index].connected = XINPUTGETCAPABILITIES(user_index, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS; - } - xinput_device_change = SDL_FALSE; - xinput_state_dirty = SDL_TRUE; - } - if (xinput_state_dirty) { - xinput_state_dirty = SDL_FALSE; - for (user_index = 0; user_index < SDL_arraysize(xinput_state); ++user_index) { - if (xinput_state[user_index].connected) { - if (XINPUTGETSTATE(user_index, &xinput_state[user_index].state) != ERROR_SUCCESS) { - xinput_state[user_index].connected = SDL_FALSE; - } - } - } - } -} - -static void -HIDAPI_DriverXbox360_MarkXInputSlotUsed(Uint8 xinput_slot) -{ - if (xinput_slot != XUSER_INDEX_ANY) { - xinput_state[xinput_slot].used = SDL_TRUE; - } -} - -static void -HIDAPI_DriverXbox360_MarkXInputSlotFree(Uint8 xinput_slot) -{ - if (xinput_slot != XUSER_INDEX_ANY) { - xinput_state[xinput_slot].used = SDL_FALSE; - } -} -static SDL_bool -HIDAPI_DriverXbox360_MissingXInputSlot() -{ - int ii; - for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) { - if (xinput_state[ii].connected && !xinput_state[ii].used) { - return SDL_TRUE; - } - } - return SDL_FALSE; -} - -static SDL_bool -HIDAPI_DriverXbox360_XInputSlotMatches(const WindowsMatchState *state, Uint8 slot_idx) -{ - if (xinput_state[slot_idx].connected) { - WORD xinput_buttons = xinput_state[slot_idx].state.Gamepad.wButtons; - if ((xinput_buttons & ~XINPUT_GAMEPAD_GUIDE) == state->xinput_buttons && XInputAxesMatch(xinput_state[slot_idx].state.Gamepad)) { - return SDL_TRUE; - } - } - return SDL_FALSE; -} - - -static SDL_bool -HIDAPI_DriverXbox360_GuessXInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, Uint8 *slot_idx) -{ - int user_index; - int match_count; - - *slot_idx = 0; - - match_count = 0; - for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) { - if (!xinput_state[user_index].used && HIDAPI_DriverXbox360_XInputSlotMatches(state, user_index)) { - ++match_count; - *slot_idx = (Uint8)user_index; - /* Incrementing correlation_id for any match, as negative evidence for others being correlated */ - *correlation_id = ++xinput_state[user_index].correlation_id; - } - } - /* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched. - Note that we're still invalidating *other* potential correlations if we have more than one match or we have no - data. */ - if (match_count == 1 && state->any_data) { - return SDL_TRUE; - } - return SDL_FALSE; -} - -#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */ - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT - -typedef struct WindowsGamingInputGamepadState { - __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad; - struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state; - SDL_DriverXbox360_Context *correlated_context; - SDL_bool used; /* Is currently mapped to an SDL device */ - SDL_bool connected; /* Just used during update to track disconnected */ - Uint8 correlation_id; - struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration; -} WindowsGamingInputGamepadState; - -static struct { - WindowsGamingInputGamepadState **per_gamepad; - int per_gamepad_count; - SDL_bool initialized; - SDL_bool dirty; - SDL_bool need_device_list_update; - int ref_count; - __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics; -} wgi_state; - -static void -HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotUsed(WindowsGamingInputGamepadState *wgi_slot, SDL_DriverXbox360_Context *ctx) -{ - wgi_slot->used = SDL_TRUE; - wgi_slot->correlated_context = ctx; -} - -static void -HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotFree(WindowsGamingInputGamepadState *wgi_slot) -{ - wgi_slot->used = SDL_FALSE; - wgi_slot->correlated_context = NULL; -} - -static SDL_bool -HIDAPI_DriverXbox360_MissingWindowsGamingInputSlot() -{ - int ii; - for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) { - if (!wgi_state.per_gamepad[ii]->used) { - return SDL_TRUE; - } - } - return SDL_FALSE; -} - -static void -HIDAPI_DriverXbox360_UpdateWindowsGamingInput() -{ - int ii; - if (!wgi_state.gamepad_statics) - return; - - if (!wgi_state.dirty) - return; - wgi_state.dirty = SDL_FALSE; - - if (wgi_state.need_device_list_update) { - wgi_state.need_device_list_update = SDL_FALSE; - for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) { - wgi_state.per_gamepad[ii]->connected = SDL_FALSE; - } - HRESULT hr; - __FIVectorView_1_Windows__CGaming__CInput__CGamepad *gamepads; - - hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_get_Gamepads(wgi_state.gamepad_statics, &gamepads); - if (SUCCEEDED(hr)) { - unsigned int num_gamepads; - - hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_get_Size(gamepads, &num_gamepads); - if (SUCCEEDED(hr)) { - unsigned int i; - for (i = 0; i < num_gamepads; ++i) { - __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad; - - hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, i, &gamepad); - if (SUCCEEDED(hr)) { - SDL_bool found = SDL_FALSE; - int jj; - for (jj = 0; jj < wgi_state.per_gamepad_count ; jj++) { - if (wgi_state.per_gamepad[jj]->gamepad == gamepad) { - found = SDL_TRUE; - wgi_state.per_gamepad[jj]->connected = SDL_TRUE; - break; - } - } - if (!found) { - /* New device, add it */ - wgi_state.per_gamepad_count++; - wgi_state.per_gamepad = SDL_realloc(wgi_state.per_gamepad, sizeof(wgi_state.per_gamepad[0]) * wgi_state.per_gamepad_count); - if (!wgi_state.per_gamepad) { - SDL_OutOfMemory(); - return; - } - WindowsGamingInputGamepadState *gamepad_state = SDL_calloc(1, sizeof(*gamepad_state)); - if (!gamepad_state) { - SDL_OutOfMemory(); - return; - } - wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1] = gamepad_state; - gamepad_state->gamepad = gamepad; - gamepad_state->connected = SDL_TRUE; - } else { - /* Already tracked */ - __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad); - } - } - } - for (ii = wgi_state.per_gamepad_count - 1; ii >= 0; ii--) { - WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii]; - if (!gamepad_state->connected) { - /* Device missing, must be disconnected */ - if (gamepad_state->correlated_context) { - gamepad_state->correlated_context->wgi_correlated = SDL_FALSE; - gamepad_state->correlated_context->wgi_slot = NULL; - } - __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad_state->gamepad); - SDL_free(gamepad_state); - wgi_state.per_gamepad[ii] = wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1]; - --wgi_state.per_gamepad_count; - } - } - } - __FIVectorView_1_Windows__CGaming__CInput__CGamepad_Release(gamepads); - } - } /* need_device_list_update */ - - for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) { - HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(wgi_state.per_gamepad[ii]->gamepad, &wgi_state.per_gamepad[ii]->state); - if (!SUCCEEDED(hr)) { - wgi_state.per_gamepad[ii]->connected = SDL_FALSE; /* Not used by anything, currently */ - } - } -} -static void -HIDAPI_DriverXbox360_InitWindowsGamingInput(SDL_DriverXbox360_Context *ctx) -{ - wgi_state.need_device_list_update = SDL_TRUE; - wgi_state.ref_count++; - if (!wgi_state.initialized) { - /* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */ - if (FAILED(WIN_CoInitialize())) { - return; - } - wgi_state.initialized = SDL_TRUE; - wgi_state.dirty = SDL_TRUE; - - static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } }; - HRESULT hr; - HMODULE hModule = LoadLibraryA("combase.dll"); - if (hModule != NULL) { - typedef HRESULT (WINAPI *WindowsCreateString_t)(PCNZWCH sourceString, UINT32 length, HSTRING* string); - typedef HRESULT (WINAPI *WindowsDeleteString_t)(HSTRING string); - typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory); - - WindowsCreateString_t WindowsCreateStringFunc = (WindowsCreateString_t)GetProcAddress(hModule, "WindowsCreateString"); - WindowsDeleteString_t WindowsDeleteStringFunc = (WindowsDeleteString_t)GetProcAddress(hModule, "WindowsDeleteString"); - RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory"); - if (WindowsCreateStringFunc && WindowsDeleteStringFunc && RoGetActivationFactoryFunc) { - LPTSTR pNamespace = L"Windows.Gaming.Input.Gamepad"; - HSTRING hNamespaceString; - - hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString); - if (SUCCEEDED(hr)) { - RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, &wgi_state.gamepad_statics); - WindowsDeleteStringFunc(hNamespaceString); - } - } - FreeLibrary(hModule); - } - } -} - -static SDL_bool -HIDAPI_DriverXbox360_WindowsGamingInputSlotMatches(const WindowsMatchState *state, WindowsGamingInputGamepadState *slot) -{ - Uint32 wgi_buttons = slot->state.Buttons; - if ((wgi_buttons & 0x3FFF) == state->wgi_buttons && WindowsGamingInputAxesMatch(slot->state)) { - return SDL_TRUE; - } - return SDL_FALSE; -} - -static SDL_bool -HIDAPI_DriverXbox360_GuessWindowsGamingInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, WindowsGamingInputGamepadState **slot) -{ - int match_count; - - match_count = 0; - for (int user_index = 0; user_index < wgi_state.per_gamepad_count; ++user_index) { - WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[user_index]; - if (HIDAPI_DriverXbox360_WindowsGamingInputSlotMatches(state, gamepad_state)) { - ++match_count; - *slot = gamepad_state; - /* Incrementing correlation_id for any match, as negative evidence for others being correlated */ - *correlation_id = ++gamepad_state->correlation_id; - } - } - /* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched. - Note that we're still invalidating *other* potential correlations if we have more than one match or we have no - data. */ - if (match_count == 1 && state->any_data) { - return SDL_TRUE; - } - return SDL_FALSE; -} - -static void -HIDAPI_DriverXbox360_QuitWindowsGamingInput(SDL_DriverXbox360_Context *ctx) -{ - wgi_state.need_device_list_update = SDL_TRUE; - --wgi_state.ref_count; - if (!wgi_state.ref_count && wgi_state.initialized) { - for (int ii = 0; ii < wgi_state.per_gamepad_count; ii++) { - __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(wgi_state.per_gamepad[ii]->gamepad); - } - if (wgi_state.per_gamepad) { - SDL_free(wgi_state.per_gamepad); - wgi_state.per_gamepad = NULL; - } - wgi_state.per_gamepad_count = 0; - if (wgi_state.gamepad_statics) { - __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi_state.gamepad_statics); - wgi_state.gamepad_statics = NULL; - } - WIN_CoUninitialize(); - wgi_state.initialized = SDL_FALSE; - } -} - -#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */ - -static void -HIDAPI_DriverXbox360_PostUpdate(void) -{ -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING - SDL_bool unmapped_guide_pressed = SDL_FALSE; - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT - if (!wgi_state.dirty) { - int ii; - for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) { - WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii]; - if (!gamepad_state->used && (gamepad_state->state.Buttons & GamepadButtons_GUIDE)) { - unmapped_guide_pressed = SDL_TRUE; - break; - } - } - } - wgi_state.dirty = SDL_TRUE; -#endif - - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT - if (!xinput_state_dirty) { - int ii; - for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) { - if (xinput_state[ii].connected && !xinput_state[ii].used && (xinput_state[ii].state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE)) { - unmapped_guide_pressed = SDL_TRUE; - break; - } - } - } - xinput_state_dirty = SDL_TRUE; -#endif - - if (unmapped_guide_pressed) { - if (guide_button_candidate.joystick && !guide_button_candidate.last_joystick) { - SDL_PrivateJoystickButton(guide_button_candidate.joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_PRESSED); - guide_button_candidate.last_joystick = guide_button_candidate.joystick; - } - } else if (guide_button_candidate.last_joystick) { - SDL_PrivateJoystickButton(guide_button_candidate.last_joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_RELEASED); - guide_button_candidate.last_joystick = NULL; - } - guide_button_candidate.joystick = NULL; -#endif -} - #if defined(__MACOSX__) static SDL_bool IsBluetoothXboxOneController(Uint16 vendor_id, Uint16 product_id) @@ -624,9 +88,9 @@ HIDAPI_DriverXbox360_IsSupportedDevice(const char *name, SDL_GameControllerType return SDL_FALSE; } #endif - return (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE); + return (type == SDL_CONTROLLER_TYPE_XBOX360 || type == SDL_CONTROLLER_TYPE_XBOXONE) ? SDL_TRUE : SDL_FALSE; #else - return (type == SDL_CONTROLLER_TYPE_XBOX360); + return (type == SDL_CONTROLLER_TYPE_XBOX360) ? SDL_TRUE : SDL_FALSE; #endif } @@ -650,7 +114,7 @@ static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot) static SDL_bool HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device) { - return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE); + return HIDAPI_JoystickConnected(device, NULL); } static int @@ -662,9 +126,7 @@ HIDAPI_DriverXbox360_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_Joystic static void HIDAPI_DriverXbox360_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) { - if (device->dev) { - SetSlotLED(device->dev, (player_index % 4)); - } + SetSlotLED(device->dev, (player_index % 4)); } static SDL_bool @@ -679,31 +141,17 @@ HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst return SDL_FALSE; } - if (device->path) { /* else opened for RAWINPUT driver */ - device->dev = hid_open_path(device->path, 0); - if (!device->dev) { - SDL_SetError("Couldn't open %s", device->path); - SDL_free(ctx); - return SDL_FALSE; - } + device->dev = hid_open_path(device->path, 0); + if (!device->dev) { + SDL_SetError("Couldn't open %s", device->path); + SDL_free(ctx); + return SDL_FALSE; } device->context = ctx; -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT - xinput_device_change = SDL_TRUE; - ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_CORRELATE_XINPUT, SDL_TRUE); - if (ctx->xinput_enabled && (WIN_LoadXInputDLL() < 0 || !XINPUTGETSTATE)) { - ctx->xinput_enabled = SDL_FALSE; - } - ctx->xinput_slot = XUSER_INDEX_ANY; -#endif -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT - HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx); -#endif - /* Set the controller LED */ player_index = SDL_JoystickGetPlayerIndex(joystick); - if (player_index >= 0 && device->dev) { + if (player_index >= 0) { SetSlotLED(device->dev, (player_index % 4)); } @@ -718,46 +166,6 @@ HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joyst static int HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) { -#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) - SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context; -#endif - -#ifdef __WIN32__ - SDL_bool rumbled = SDL_FALSE; - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT - if (!rumbled && ctx->wgi_correlated) { - WindowsGamingInputGamepadState *gamepad_state = ctx->wgi_slot; - HRESULT hr; - gamepad_state->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16; - gamepad_state->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16; - hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(gamepad_state->gamepad, gamepad_state->vibration); - if (SUCCEEDED(hr)) { - rumbled = SDL_TRUE; - } - } -#endif - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT - if (!rumbled && ctx->xinput_correlated) { - XINPUT_VIBRATION XVibration; - - if (!XINPUTSETSTATE) { - return SDL_Unsupported(); - } - - XVibration.wLeftMotorSpeed = low_frequency_rumble; - XVibration.wRightMotorSpeed = high_frequency_rumble; - if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) == ERROR_SUCCESS) { - rumbled = SDL_TRUE; - } else { - return SDL_SetError("XInputSetState() failed"); - } - } -#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */ - -#else /* !__WIN32__ */ - #ifdef __MACOSX__ if (IsBluetoothXboxOneController(device->vendor_id, device->product_id)) { Uint8 rumble_packet[] = { 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00 }; @@ -791,8 +199,6 @@ HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joy return SDL_SetError("Couldn't send rumble packet"); } #endif -#endif /* __WIN32__ */ - return 0; } @@ -821,149 +227,8 @@ HIDAPI_DriverXbox360_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Jo return SDL_Unsupported(); } -#ifdef __WIN32__ - /* This is the packet format for Xbox 360 and Xbox One controllers on Windows, - however with this interface there is no rumble support, no guide button, - and the left and right triggers are tied together as a single axis. - - We use XInput and Windows.Gaming.Input to make up for these shortcomings. - */ static void -HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size) -{ -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING - Uint32 match_state = ctx->match_state; - /* Update match_state with button bit, then fall through */ -# define SDL_PrivateJoystickButton(joystick, button, state) if (state) match_state |= 1 << (button); else match_state &=~(1<<(button)); SDL_PrivateJoystickButton(joystick, button, state) - /* Grab high 4 bits of value, then fall through */ -# define SDL_PrivateJoystickAxis(joystick, axis, value) if (axis < 4) match_state = (match_state & ~(0xF << (4 * axis + 16))) | ((value) & 0xF000) << (4 * axis + 4); SDL_PrivateJoystickAxis(joystick, axis, value) -#endif - Sint16 axis; - SDL_bool has_trigger_data = SDL_FALSE; - - if (ctx->last_state[10] != data[10]) { - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[10] & 0x01) ? SDL_PRESSED : SDL_RELEASED); - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[10] & 0x02) ? SDL_PRESSED : SDL_RELEASED); - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[10] & 0x04) ? SDL_PRESSED : SDL_RELEASED); - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[10] & 0x08) ? SDL_PRESSED : SDL_RELEASED); - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[10] & 0x10) ? SDL_PRESSED : SDL_RELEASED); - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[10] & 0x20) ? SDL_PRESSED : SDL_RELEASED); - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[10] & 0x40) ? SDL_PRESSED : SDL_RELEASED); - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[10] & 0x80) ? SDL_PRESSED : SDL_RELEASED); - } - - if (ctx->last_state[11] != data[11] || ctx->last_state[12] != data[12]) { - SDL_bool dpad_up = SDL_FALSE; - SDL_bool dpad_down = SDL_FALSE; - SDL_bool dpad_left = SDL_FALSE; - SDL_bool dpad_right = SDL_FALSE; - Uint8 dpad_state = (((data[11] >> 2) & 0x0F) | (data[12] & 0x0F)); - - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[11] & 0x01) ? SDL_PRESSED : SDL_RELEASED); - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[11] & 0x02) ? SDL_PRESSED : SDL_RELEASED); - - switch (dpad_state) { - case 1: - dpad_up = SDL_TRUE; - break; - case 2: - dpad_up = SDL_TRUE; - dpad_right = SDL_TRUE; - break; - case 3: - dpad_right = SDL_TRUE; - break; - case 4: - dpad_right = SDL_TRUE; - dpad_down = SDL_TRUE; - break; - case 5: - dpad_down = SDL_TRUE; - break; - case 6: - dpad_left = SDL_TRUE; - dpad_down = SDL_TRUE; - break; - case 7: - dpad_left = SDL_TRUE; - break; - case 8: - dpad_up = SDL_TRUE; - dpad_left = SDL_TRUE; - break; - default: - break; - } - - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down); - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up); - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right); - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left); - } - - axis = (int)*(Uint16*)(&data[0]) - 0x8000; - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, axis); - axis = (int)*(Uint16*)(&data[2]) - 0x8000; - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, axis); - axis = (int)*(Uint16*)(&data[4]) - 0x8000; - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, axis); - axis = (int)*(Uint16*)(&data[6]) - 0x8000; - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, axis); - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING -#undef SDL_PrivateJoystickAxis -#endif - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT - /* Prefer XInput over WindowsGamingInput, it continues to provide data in the background */ - if (!has_trigger_data && ctx->xinput_enabled && ctx->xinput_correlated) { - has_trigger_data = SDL_TRUE; - } -#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */ - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT - if (!has_trigger_data && ctx->wgi_correlated) { - has_trigger_data = SDL_TRUE; - } -#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */ - - if (!has_trigger_data) { - axis = (data[9] * 257) - 32768; - if (data[9] < 0x80) { - axis = -axis * 2 - 32769; - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16); - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, axis); - } else if (data[9] > 0x80) { - axis = axis * 2 - 32767; - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, axis); - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16); - } else { - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_MIN_SINT16); - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_MIN_SINT16); - } - } - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING - ctx->match_state = match_state; - ctx->last_state_packet = SDL_GetTicks(); -#undef SDL_PrivateJoystickButton -#endif - SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); -} - -#ifdef SDL_JOYSTICK_RAWINPUT -static void -HIDAPI_DriverXbox360_HandleStatePacketFromRAWINPUT(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 *data, int size) -{ - SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context; - HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, data, size); -} -#endif - -#else - -static void -HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size) +HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverXbox360_Context *ctx, Uint8 *data, int size) { Sint16 axis; #ifdef __MACOSX__ @@ -1014,238 +279,6 @@ HIDAPI_DriverXbox360_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); } -#endif /* __WIN32__ */ - -static void -HIDAPI_DriverXbox360_UpdateOtherAPIs(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) -{ -#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT) - SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context; - SDL_bool has_trigger_data = SDL_FALSE; - SDL_bool correlated = SDL_FALSE; -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING - WindowsMatchState match_state_xinput; -#endif - - /* Poll for trigger data once (not per-state-packet) */ -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT - /* Prefer XInput over WindowsGamingInput, it continues to provide data in the background */ - if (!has_trigger_data && ctx->xinput_enabled && ctx->xinput_correlated) { - HIDAPI_DriverXbox360_UpdateXInput(); - if (xinput_state[ctx->xinput_slot].connected) { - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (xinput_state[ctx->xinput_slot].state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED); - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)xinput_state[ctx->xinput_slot].state.Gamepad.bLeftTrigger * 257) - 32768); - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)xinput_state[ctx->xinput_slot].state.Gamepad.bRightTrigger * 257) - 32768); - has_trigger_data = SDL_TRUE; - } - } -#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT */ - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT - if (!has_trigger_data && ctx->wgi_correlated) { - HIDAPI_DriverXbox360_UpdateWindowsGamingInput(); /* May detect disconnect / cause uncorrelation */ - if (ctx->wgi_correlated) { /* Still connected */ - struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading *state = &ctx->wgi_slot->state; - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, (state->Buttons & GamepadButtons_GUIDE) ? SDL_PRESSED : SDL_RELEASED); - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, ((int)(state->LeftTrigger * SDL_MAX_UINT16)) - 32768); - SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, ((int)(state->RightTrigger * SDL_MAX_UINT16)) - 32768); - has_trigger_data = SDL_TRUE; - } - } -#endif /* SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT */ - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING - HIDAPI_DriverXbox360_FillMatchState(&match_state_xinput, ctx->match_state); - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT - /* Parallel logic to WINDOWS_XINPUT below */ - HIDAPI_DriverXbox360_UpdateWindowsGamingInput(); - if (ctx->wgi_correlated) { - /* We have been previously correlated, ensure we are still matching, see comments in XINPUT section */ - if (HIDAPI_DriverXbox360_WindowsGamingInputSlotMatches(&match_state_xinput, ctx->wgi_slot)) { - ctx->wgi_uncorrelate_count = 0; - } else { - ++ctx->wgi_uncorrelate_count; - /* Only un-correlate if this is consistent over multiple Update() calls - the timing of polling/event - pumping can easily cause this to uncorrelate for a frame. 2 seemed reliable in my testing, but - let's set it to 3 to be safe. An incorrect un-correlation will simply result in lower precision - triggers for a frame. */ - if (ctx->wgi_uncorrelate_count >= 3) { -#ifdef DEBUG_JOYSTICK - SDL_Log("UN-Correlated joystick %d to WindowsGamingInput device #%d\n", joystick->instance_id, ctx->wgi_slot); -#endif - HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotFree(ctx->wgi_slot); - ctx->wgi_correlated = SDL_FALSE; - ctx->wgi_correlation_count = 0; - /* Force immediate update of triggers */ - HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, ctx->last_state, sizeof(ctx->last_state)); - /* Force release of Guide button, it can't possibly be down on this device now. */ - /* It gets left down if we were actually correlated incorrectly and it was released on the WindowsGamingInput - device but we didn't get a state packet. */ - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_RELEASED); - } - } - } - if (!ctx->wgi_correlated) { - SDL_bool new_correlation_count = 0; - if (HIDAPI_DriverXbox360_MissingWindowsGamingInputSlot()) { - Uint8 correlation_id; - WindowsGamingInputGamepadState *slot_idx; - if (HIDAPI_DriverXbox360_GuessWindowsGamingInputSlot(&match_state_xinput, &correlation_id, &slot_idx)) { - /* we match exactly one WindowsGamingInput device */ - /* Probably can do without wgi_correlation_count, just check and clear wgi_slot to NULL, unless we need - even more frames to be sure. */ - if (ctx->wgi_correlation_count && ctx->wgi_slot == slot_idx) { - /* was correlated previously, and still the same device */ - if (ctx->wgi_correlation_id + 1 == correlation_id) { - /* no one else was correlated in the meantime */ - new_correlation_count = ctx->wgi_correlation_count + 1; - if (new_correlation_count == 2) { - /* correlation stayed steady and uncontested across multiple frames, guaranteed match */ - ctx->wgi_correlated = SDL_TRUE; -#ifdef DEBUG_JOYSTICK - SDL_Log("Correlated joystick %d to WindowsGamingInput device #%d\n", joystick->instance_id, slot_idx); -#endif - correlated = SDL_TRUE; - HIDAPI_DriverXbox360_MarkWindowsGamingInputSlotUsed(ctx->wgi_slot, ctx); - /* If the generalized Guide button was using us, it doesn't need to anymore */ - if (guide_button_candidate.joystick == joystick) - guide_button_candidate.joystick = NULL; - if (guide_button_candidate.last_joystick == joystick) - guide_button_candidate.last_joystick = NULL; - /* Force immediate update of guide button / triggers */ - HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, ctx->last_state, sizeof(ctx->last_state)); - } - } else { - /* someone else also possibly correlated to this device, start over */ - new_correlation_count = 1; - } - } else { - /* new possible correlation */ - new_correlation_count = 1; - ctx->wgi_slot = slot_idx; - } - ctx->wgi_correlation_id = correlation_id; - } else { - /* Match multiple WindowsGamingInput devices, or none (possibly due to no buttons pressed) */ - } - } - ctx->wgi_correlation_count = new_correlation_count; - } else { - correlated = SDL_TRUE; - } -#endif - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT - /* Parallel logic to WINDOWS_GAMING_INPUT above */ - if (ctx->xinput_enabled) { - HIDAPI_DriverXbox360_UpdateXInput(); - if (ctx->xinput_correlated) { - /* We have been previously correlated, ensure we are still matching */ - /* This is required to deal with two (mostly) un-preventable mis-correlation situations: - A) Since the HID data stream does not provide an initial state (but polling XInput does), if we open - 5 controllers (#1-4 XInput mapped, #5 is not), and controller 1 had the A button down (and we don't - know), and the user presses A on controller #5, we'll see exactly 1 controller with A down (#5) and - exactly 1 XInput device with A down (#1), and incorrectly correlate. This code will then un-correlate - when A is released from either controller #1 or #5. - B) Since the app may not open all controllers, we could have a similar situation where only controller #5 - is opened, and the user holds A on controllers #1 and #5 simultaneously - again we see only 1 controller - with A down and 1 XInput device with A down, and incorrectly correlate. This should be very unusual - (only when apps do not open all controllers, yet are listening to Guide button presses, yet - for some reason want to ignore guide button presses on the un-opened controllers, yet users are - pressing buttons on the unopened controllers), and will resolve itself when either button is released - and we un-correlate. We could prevent this by processing the state packets for *all* controllers, - even un-opened ones, as that would allow more precise correlation. - */ - if (HIDAPI_DriverXbox360_XInputSlotMatches(&match_state_xinput, ctx->xinput_slot)) { - ctx->xinput_uncorrelate_count = 0; - } else { - ++ctx->xinput_uncorrelate_count; - /* Only un-correlate if this is consistent over multiple Update() calls - the timing of polling/event - pumping can easily cause this to uncorrelate for a frame. 2 seemed reliable in my testing, but - let's set it to 3 to be safe. An incorrect un-correlation will simply result in lower precision - triggers for a frame. */ - if (ctx->xinput_uncorrelate_count >= 3) { -#ifdef DEBUG_JOYSTICK - SDL_Log("UN-Correlated joystick %d to XInput device #%d\n", joystick->instance_id, ctx->xinput_slot); -#endif - HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot); - ctx->xinput_correlated = SDL_FALSE; - ctx->xinput_correlation_count = 0; - /* Force immediate update of triggers */ - HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, ctx->last_state, sizeof(ctx->last_state)); - /* Force release of Guide button, it can't possibly be down on this device now. */ - /* It gets left down if we were actually correlated incorrectly and it was released on the XInput - device but we didn't get a state packet. */ - SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_GUIDE, SDL_RELEASED); - } - } - } - if (!ctx->xinput_correlated) { - SDL_bool new_correlation_count = 0; - if (HIDAPI_DriverXbox360_MissingXInputSlot()) { - Uint8 correlation_id = 0; - Uint8 slot_idx = 0; - if (HIDAPI_DriverXbox360_GuessXInputSlot(&match_state_xinput, &correlation_id, &slot_idx)) { - /* we match exactly one XInput device */ - /* Probably can do without xinput_correlation_count, just check and clear xinput_slot to ANY, unless - we need even more frames to be sure */ - if (ctx->xinput_correlation_count && ctx->xinput_slot == slot_idx) { - /* was correlated previously, and still the same device */ - if (ctx->xinput_correlation_id + 1 == correlation_id) { - /* no one else was correlated in the meantime */ - new_correlation_count = ctx->xinput_correlation_count + 1; - if (new_correlation_count == 2) { - /* correlation stayed steady and uncontested across multiple frames, guaranteed match */ - ctx->xinput_correlated = SDL_TRUE; -#ifdef DEBUG_JOYSTICK - SDL_Log("Correlated joystick %d to XInput device #%d\n", joystick->instance_id, slot_idx); -#endif - correlated = SDL_TRUE; - HIDAPI_DriverXbox360_MarkXInputSlotUsed(ctx->xinput_slot); - /* If the generalized Guide button was using us, it doesn't need to anymore */ - if (guide_button_candidate.joystick == joystick) - guide_button_candidate.joystick = NULL; - if (guide_button_candidate.last_joystick == joystick) - guide_button_candidate.last_joystick = NULL; - /* Force immediate update of guide button / triggers */ - HIDAPI_DriverXbox360_HandleStatePacket(joystick, NULL, ctx, ctx->last_state, sizeof(ctx->last_state)); - } - } else { - /* someone else also possibly correlated to this device, start over */ - new_correlation_count = 1; - } - } else { - /* new possible correlation */ - new_correlation_count = 1; - ctx->xinput_slot = slot_idx; - } - ctx->xinput_correlation_id = correlation_id; - } else { - /* Match multiple XInput devices, or none (possibly due to no buttons pressed) */ - } - } - ctx->xinput_correlation_count = new_correlation_count; - } else { - correlated = SDL_TRUE; - } - } -#endif - - if (!correlated) { - if (!guide_button_candidate.joystick || - (ctx->last_state_packet && ( - !guide_button_candidate.last_state_packet || - SDL_TICKS_PASSED(ctx->last_state_packet, guide_button_candidate.last_state_packet) - )) - ) { - guide_button_candidate.joystick = joystick; - guide_button_candidate.last_state_packet = ctx->last_state_packet; - } - } -#endif -#endif -} static SDL_bool HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device) @@ -1262,47 +295,20 @@ HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device) return SDL_FALSE; } - while (device->dev && (size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { - HIDAPI_DriverXbox360_HandleStatePacket(joystick, device->dev, ctx, data, size); + while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { + HIDAPI_DriverXbox360_HandleStatePacket(joystick, ctx, data, size); } if (size < 0) { /* Read error, device is disconnected */ - HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE); - } else { - HIDAPI_DriverXbox360_UpdateOtherAPIs(device, joystick); + HIDAPI_JoystickDisconnected(device, joystick->instance_id); } - return (size >= 0); } static void HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) { -#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT) - SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context; -#endif - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_MATCHING - if (guide_button_candidate.joystick == joystick) - guide_button_candidate.joystick = NULL; - if (guide_button_candidate.last_joystick == joystick) - guide_button_candidate.last_joystick = NULL; -#endif - -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT - xinput_device_change = SDL_TRUE; - if (ctx->xinput_enabled) { - if (ctx->xinput_correlated) { - HIDAPI_DriverXbox360_MarkXInputSlotFree(ctx->xinput_slot); - } - WIN_UnloadXInputDLL(); - } -#endif -#ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT - HIDAPI_DriverXbox360_QuitWindowsGamingInput(ctx); -#endif - if (device->dev) { hid_close(device->dev); device->dev = NULL; @@ -1335,10 +341,6 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 = HIDAPI_DriverXbox360_SetJoystickSensorsEnabled, HIDAPI_DriverXbox360_CloseJoystick, HIDAPI_DriverXbox360_FreeDevice, - HIDAPI_DriverXbox360_PostUpdate, -#ifdef SDL_JOYSTICK_RAWINPUT - HIDAPI_DriverXbox360_HandleStatePacketFromRAWINPUT, -#endif }; #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */ diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360w.c b/src/joystick/hidapi/SDL_hidapi_xbox360w.c index 294139cf2..2a2513422 100644 --- a/src/joystick/hidapi/SDL_hidapi_xbox360w.c +++ b/src/joystick/hidapi/SDL_hidapi_xbox360w.c @@ -255,10 +255,10 @@ HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device *device) if (connected) { SDL_JoystickID joystickID; - HIDAPI_JoystickConnected(device, &joystickID, SDL_FALSE); + HIDAPI_JoystickConnected(device, &joystickID); } else if (device->num_joysticks > 0) { - HIDAPI_JoystickDisconnected(device, device->joysticks[0], SDL_FALSE); + HIDAPI_JoystickDisconnected(device, device->joysticks[0]); } } } else if (size == 29 && data[0] == 0x00 && data[1] == 0x0f && data[2] == 0x00 && data[3] == 0xf0) { @@ -286,7 +286,7 @@ HIDAPI_DriverXbox360W_UpdateDevice(SDL_HIDAPI_Device *device) if (joystick) { if (size < 0) { /* Read error, device is disconnected */ - HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE); + HIDAPI_JoystickDisconnected(device, joystick->instance_id); } } return (size >= 0); @@ -325,7 +325,6 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360W = HIDAPI_DriverXbox360W_SetJoystickSensorsEnabled, HIDAPI_DriverXbox360W_CloseJoystick, HIDAPI_DriverXbox360W_FreeDevice, - NULL }; #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */ diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c index 5218cb6ab..4fd366562 100644 --- a/src/joystick/hidapi/SDL_hidapi_xboxone.c +++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c @@ -291,7 +291,7 @@ HIDAPI_DriverXboxOne_IsSupportedDevice(const char *name, SDL_GameControllerType return SDL_FALSE; } #endif - return (type == SDL_CONTROLLER_TYPE_XBOXONE); + return (type == SDL_CONTROLLER_TYPE_XBOXONE) ? SDL_TRUE : SDL_FALSE; } static const char * @@ -303,7 +303,7 @@ HIDAPI_DriverXboxOne_GetDeviceName(Uint16 vendor_id, Uint16 product_id) static SDL_bool HIDAPI_DriverXboxOne_InitDevice(SDL_HIDAPI_Device *device) { - return HIDAPI_JoystickConnected(device, NULL, SDL_FALSE); + return HIDAPI_JoystickConnected(device, NULL); } static int @@ -852,7 +852,7 @@ HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device) !ControllerSendsWaitingForInit(device->vendor_id, device->product_id)) { if (SDL_TICKS_PASSED(SDL_GetTicks(), ctx->start_time + CONTROLLER_INIT_DELAY_MS)) { if (!SendControllerInit(device, ctx)) { - HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE); + HIDAPI_JoystickDisconnected(device, joystick->instance_id); return SDL_FALSE; } ctx->initialized = SDL_TRUE; @@ -918,7 +918,7 @@ HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device) SDL_Log("Delay after init: %ums\n", SDL_GetTicks() - ctx->start_time); #endif if (!SendControllerInit(device, ctx)) { - HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE); + HIDAPI_JoystickDisconnected(device, joystick->instance_id); return SDL_FALSE; } ctx->initialized = SDL_TRUE; @@ -973,7 +973,7 @@ HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device) if (size < 0) { /* Read error, device is disconnected */ - HIDAPI_JoystickDisconnected(device, joystick->instance_id, SDL_FALSE); + HIDAPI_JoystickDisconnected(device, joystick->instance_id); } return (size >= 0); } @@ -1011,7 +1011,6 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne = HIDAPI_DriverXboxOne_SetJoystickSensorsEnabled, HIDAPI_DriverXboxOne_CloseJoystick, HIDAPI_DriverXboxOne_FreeDevice, - NULL }; #endif /* SDL_JOYSTICK_HIDAPI_XBOXONE */ diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index f303440ee..a93bd18f4 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -443,13 +443,6 @@ HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device) return NULL; } -#ifdef SDL_JOYSTICK_RAWINPUT - if (RAWINPUT_IsDevicePresent(device->vendor_id, device->product_id, device->version)) { - /* The RAWINPUT driver is taking care of this device */ - return NULL; - } -#endif - if (device->vendor_id != USB_VENDOR_VALVE) { if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) { return NULL; @@ -535,7 +528,7 @@ HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device) /* Disconnect any joysticks */ while (device->num_joysticks) { - HIDAPI_JoystickDisconnected(device, device->joysticks[0], SDL_FALSE); + HIDAPI_JoystickDisconnected(device, device->joysticks[0]); } device->driver->FreeDevice(device); @@ -608,11 +601,6 @@ HIDAPI_JoystickInit(void) return -1; } -#ifdef __WINDOWS__ - /* On Windows, turns out HIDAPI for Xbox controllers doesn't allow background input, so off by default */ - SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_HIDAPI_XBOX, "0", SDL_HINT_DEFAULT); -#endif - for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL); @@ -629,7 +617,7 @@ HIDAPI_JoystickInit(void) } SDL_bool -HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID, SDL_bool is_external) +HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID) { SDL_JoystickID joystickID; SDL_JoystickID *joysticks = (SDL_JoystickID *)SDL_realloc(device->joysticks, (device->num_joysticks + 1)*sizeof(*device->joysticks)); @@ -640,9 +628,7 @@ HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID, joystickID = SDL_GetNextJoystickInstanceID(); device->joysticks = joysticks; device->joysticks[device->num_joysticks++] = joystickID; - if (!is_external) { - ++SDL_HIDAPI_numjoysticks; - } + ++SDL_HIDAPI_numjoysticks; SDL_PrivateJoystickAdded(joystickID); @@ -653,14 +639,14 @@ HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID, } void -HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID, SDL_bool is_external) +HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID) { int i, size; for (i = 0; i < device->num_joysticks; ++i) { if (device->joysticks[i] == joystickID) { SDL_Joystick *joystick = SDL_JoystickFromInstanceID(joystickID); - if (joystick && !is_external) { + if (joystick) { HIDAPI_JoystickClose(joystick); } @@ -668,9 +654,7 @@ HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID SDL_memmove(&device->joysticks[i], &device->joysticks[i+1], size); --device->num_joysticks; - if (!is_external) { - --SDL_HIDAPI_numjoysticks; - } + --SDL_HIDAPI_numjoysticks; if (device->num_joysticks == 0) { SDL_free(device->joysticks); device->joysticks = NULL; @@ -932,7 +916,6 @@ HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, cons static void HIDAPI_JoystickDetect(void) { - int i; if (SDL_AtomicTryLock(&SDL_HIDAPI_spinlock)) { HIDAPI_UpdateDiscovery(); if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) { @@ -942,12 +925,6 @@ HIDAPI_JoystickDetect(void) } SDL_AtomicUnlock(&SDL_HIDAPI_spinlock); } - for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { - SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; - if (driver->enabled && driver->PostUpdate) { - driver->PostUpdate(); - } - } } void diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h index f5c995176..d16a67360 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick_c.h +++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h @@ -38,11 +38,6 @@ #define SDL_JOYSTICK_HIDAPI_XBOXONE #define SDL_JOYSTICK_HIDAPI_GAMECUBE -#ifdef __WINDOWS__ -/* On Windows, Xbox One controllers are handled by the Xbox 360 driver */ -#undef SDL_JOYSTICK_HIDAPI_XBOXONE -#endif - #if defined(__IPHONEOS__) || defined(__TVOS__) || defined(__ANDROID__) /* Very basic Steam Controller support on mobile devices */ #define SDL_JOYSTICK_HIDAPI_STEAM @@ -102,10 +97,6 @@ typedef struct _SDL_HIDAPI_DeviceDriver int (*SetJoystickSensorsEnabled)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled); void (*CloseJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick); void (*FreeDevice)(SDL_HIDAPI_Device *device); - void (*PostUpdate)(void); -#ifdef SDL_JOYSTICK_RAWINPUT - void (*HandleStatePacketFromRAWINPUT)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 *data, int size); -#endif } SDL_HIDAPI_DeviceDriver; @@ -124,8 +115,8 @@ extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube; extern SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name); extern void HIDAPI_UpdateDevices(void); -extern SDL_bool HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID, SDL_bool is_external); -extern void HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID, SDL_bool is_external); +extern SDL_bool HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID); +extern void HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID); extern void HIDAPI_DumpPacket(const char *prefix, Uint8 *data, int size); diff --git a/src/joystick/usb_ids.h b/src/joystick/usb_ids.h index 00de4bd68..3b37a77af 100644 --- a/src/joystick/usb_ids.h +++ b/src/joystick/usb_ids.h @@ -53,6 +53,29 @@ #define USB_PRODUCT_XBOX_ONE_SERIES_X 0x0b12 #define USB_PRODUCT_XBOX_ONE_SERIES_X_BLUETOOTH 0x0b13 +/* USB usage pages */ +#define USB_USAGEPAGE_GENERIC_DESKTOP 0x0001 +#define USB_USAGEPAGE_BUTTON 0x0009 + +/* USB usages for USAGE_PAGE_GENERIC_DESKTOP */ +#define USB_USAGE_GENERIC_POINTER 0x0001 +#define USB_USAGE_GENERIC_MOUSE 0x0002 +#define USB_USAGE_GENERIC_JOYSTICK 0x0004 +#define USB_USAGE_GENERIC_GAMEPAD 0x0005 +#define USB_USAGE_GENERIC_KEYBOARD 0x0006 +#define USB_USAGE_GENERIC_KEYPAD 0x0007 +#define USB_USAGE_GENERIC_MULTIAXISCONTROLLER 0x0008 +#define USB_USAGE_GENERIC_X 0x0030 +#define USB_USAGE_GENERIC_Y 0x0031 +#define USB_USAGE_GENERIC_Z 0x0032 +#define USB_USAGE_GENERIC_RX 0x0033 +#define USB_USAGE_GENERIC_RY 0x0034 +#define USB_USAGE_GENERIC_RZ 0x0035 +#define USB_USAGE_GENERIC_SLIDER 0x0036 +#define USB_USAGE_GENERIC_DIAL 0x0037 +#define USB_USAGE_GENERIC_WHEEL 0x0038 +#define USB_USAGE_GENERIC_HAT 0x0039 + #endif /* usb_ids_h_ */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/windows/SDL_rawinputjoystick.c b/src/joystick/windows/SDL_rawinputjoystick.c index 4bd0f5d08..6ec92a3fc 100644 --- a/src/joystick/windows/SDL_rawinputjoystick.c +++ b/src/joystick/windows/SDL_rawinputjoystick.c @@ -35,15 +35,39 @@ #include "SDL_assert.h" #include "SDL_endian.h" +#include "SDL_events.h" #include "SDL_hints.h" +#include "SDL_timer.h" +#include "../usb_ids.h" #include "../SDL_sysjoystick.h" #include "../../core/windows/SDL_windows.h" +#include "../../core/windows/SDL_hid.h" #include "../hidapi/SDL_hidapijoystick_c.h" -#if !defined(SDL_JOYSTICK_HIDAPI) || !defined(SDL_JOYSTICK_HIDAPI_XBOX360) -#error RAWINPUT requires the XBOX360 HIDAPI driver +#ifdef __WIN32__ +#define SDL_JOYSTICK_RAWINPUT_XINPUT +/* This requires the Windows 10 SDK to build */ +/*#define SDL_JOYSTICK_RAWINPUT_GAMING_INPUT*/ #endif +#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT +#include "../../core/windows/SDL_xinput.h" +#endif + +#ifdef SDL_JOYSTICK_RAWINPUT_GAMING_INPUT +#include "../../core/windows/SDL_windows.h" +typedef struct WindowsGamingInputGamepadState WindowsGamingInputGamepadState; +#define GamepadButtons_GUIDE 0x40000000 +#define COBJMACROS +#include "windows.gaming.input.h" +#endif + +#if defined(SDL_JOYSTICK_RAWINPUT_XINPUT) || defined(SDL_JOYSTICK_RAWINPUT_GAMING_INPUT) +#define SDL_JOYSTICK_RAWINPUT_MATCHING +#endif + +/* #define DEBUG_RAWINPUT */ + #ifndef RIDEV_EXINPUTSINK #define RIDEV_EXINPUTSINK 0x00001000 #define RIDEV_DEVNOTIFY 0x00002000 @@ -60,38 +84,17 @@ #define GIDC_REMOVAL 2 #endif - -/* #define DEBUG_RAWINPUT */ - -#define USB_PACKET_LENGTH 64 - -#define SDL_callocStruct(type) (type *)SDL_calloc(1, sizeof(type)) -#define SDL_callocStructs(type, count) (type *)SDL_calloc((count), sizeof(type)) - -#define USAGE_PAGE_GENERIC_DESKTOP 0x0001 -#define USAGE_JOYSTICK 0x0004 -#define USAGE_GAMEPAD 0x0005 -#define USAGE_MULTIAXISCONTROLLER 0x0008 - - /* external variables referenced. */ extern HWND SDL_HelperWindow; -static SDL_HIDAPI_DeviceDriver *SDL_RAWINPUT_drivers[] = { -#ifdef SDL_JOYSTICK_HIDAPI_XBOX360 - &SDL_HIDAPI_DriverXbox360, -#endif -}; - static SDL_bool SDL_RAWINPUT_inited = SDL_FALSE; static int SDL_RAWINPUT_numjoysticks = 0; static SDL_bool SDL_RAWINPUT_need_pump = SDL_TRUE; static void RAWINPUT_JoystickDetect(void); static void RAWINPUT_PumpMessages(void); -static SDL_bool RAWINPUT_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version); -static void RAWINPUT_JoystickClose(SDL_Joystick * joystick); +static void RAWINPUT_JoystickClose(SDL_Joystick *joystick); typedef struct _SDL_RAWINPUT_Device { @@ -101,10 +104,8 @@ typedef struct _SDL_RAWINPUT_Device Uint16 product_id; Uint16 version; SDL_JoystickGUID guid; - Uint16 usage_page; - Uint16 usage; - SDL_HIDAPI_Device hiddevice; - SDL_HIDAPI_DeviceDriver *driver; + SDL_bool is_xinput; + PHIDP_PREPARSED_DATA preparsed_data; HANDLE hDevice; SDL_Joystick *joystick; @@ -115,74 +116,502 @@ typedef struct _SDL_RAWINPUT_Device struct joystick_hwdata { - void *reserved; /* reserving a value here to ensure the new SDL_hidapijoystick.c code never dereferences this */ + SDL_bool is_xinput; + PHIDP_PREPARSED_DATA preparsed_data; + ULONG max_data_length; + HIDP_DATA *data; + USHORT *button_indices; + USHORT *axis_indices; + USHORT *hat_indices; + SDL_bool guide_hack; + SDL_bool trigger_hack; + USHORT trigger_hack_index; + +#ifdef SDL_JOYSTICK_RAWINPUT_MATCHING + Uint32 match_state; /* Low 16 bits for button states, high 16 for 4 4bit axes */ + Uint32 last_state_packet; +#endif + +#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT + SDL_bool xinput_enabled; + SDL_bool xinput_correlated; + Uint8 xinput_correlation_id; + Uint8 xinput_correlation_count; + Uint8 xinput_uncorrelate_count; + Uint8 xinput_slot; +#endif + +#ifdef SDL_JOYSTICK_RAWINPUT_GAMING_INPUT + SDL_bool wgi_correlated; + Uint8 wgi_correlation_id; + Uint8 wgi_correlation_count; + Uint8 wgi_uncorrelate_count; + WindowsGamingInputGamepadState *wgi_slot; +#endif + SDL_RAWINPUT_Device *device; }; +typedef struct joystick_hwdata RAWINPUT_DeviceContext; SDL_RAWINPUT_Device *SDL_RAWINPUT_devices; static const Uint16 subscribed_devices[] = { - USAGE_GAMEPAD, + USB_USAGE_GENERIC_GAMEPAD, /* Don't need Joystick for any devices we're handling here (XInput-capable) - USAGE_JOYSTICK, - USAGE_MULTIAXISCONTROLLER, + USB_USAGE_GENERIC_JOYSTICK, + USB_USAGE_GENERIC_MULTIAXISCONTROLLER, */ }; -SDL_bool RAWINPUT_AllXInputDevicesSupported() { - UINT i, device_count = 0; - PRAWINPUTDEVICELIST devices; - SDL_bool any_unsupported = SDL_FALSE; +#ifdef SDL_JOYSTICK_RAWINPUT_MATCHING - if ((GetRawInputDeviceList(NULL, &device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!device_count)) { - return SDL_FALSE; +static struct { + Uint32 last_state_packet; + SDL_Joystick *joystick; + SDL_Joystick *last_joystick; +} guide_button_candidate; + +typedef struct WindowsMatchState { + SHORT match_axes[4]; +#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT + WORD xinput_buttons; +#endif +#ifdef SDL_JOYSTICK_RAWINPUT_GAMING_INPUT + Uint32 wgi_buttons; +#endif + SDL_bool any_data; +} WindowsMatchState; + +static void RAWINPUT_FillMatchState(WindowsMatchState *state, Uint32 match_state) +{ + int ii; + state->any_data = SDL_FALSE; + /* SHORT state->match_axes[4] = { + (match_state & 0x000F0000) >> 4, + (match_state & 0x00F00000) >> 8, + (match_state & 0x0F000000) >> 12, + (match_state & 0xF0000000) >> 16, + }; */ + for (ii = 0; ii < 4; ii++) { + state->match_axes[ii] = (match_state & (0x000F0000 << (ii * 4))) >> (4 + ii * 4); + if ((Uint32)(state->match_axes[ii] + 0x1000) > 0x2000) { /* match_state bit is not 0xF, 0x1, or 0x2 */ + state->any_data = SDL_TRUE; + } } - devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * device_count); - if (devices == NULL) { - return SDL_FALSE; +#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT + /* Match axes by checking if the distance between the high 4 bits of axis and the 4 bits from match_state is 1 or less */ +#define XInputAxesMatch(gamepad) (\ + (Uint32)(gamepad.sThumbLX - state->match_axes[0] + 0x1000) <= 0x2fff && \ + (Uint32)(~gamepad.sThumbLY - state->match_axes[1] + 0x1000) <= 0x2fff && \ + (Uint32)(gamepad.sThumbRX - state->match_axes[2] + 0x1000) <= 0x2fff && \ + (Uint32)(~gamepad.sThumbRY - state->match_axes[3] + 0x1000) <= 0x2fff) + /* Explicit +#define XInputAxesMatch(gamepad) (\ + SDL_abs((Sint8)((gamepad.sThumbLX & 0xF000) >> 8) - ((match_state & 0x000F0000) >> 12)) <= 0x10 && \ + SDL_abs((Sint8)((~gamepad.sThumbLY & 0xF000) >> 8) - ((match_state & 0x00F00000) >> 16)) <= 0x10 && \ + SDL_abs((Sint8)((gamepad.sThumbRX & 0xF000) >> 8) - ((match_state & 0x0F000000) >> 20)) <= 0x10 && \ + SDL_abs((Sint8)((~gamepad.sThumbRY & 0xF000) >> 8) - ((match_state & 0xF0000000) >> 24)) <= 0x10) */ + + state->xinput_buttons = + /* Bitwise map .RLDUWVQTS.KYXBA -> YXBA..WVQTKSRLDU */ + match_state << 12 | (match_state & 0x0780) >> 1 | (match_state & 0x0010) << 1 | (match_state & 0x0040) >> 2 | (match_state & 0x7800) >> 11; + /* Explicit + ((match_state & (1<xinput_buttons) + state->any_data = SDL_TRUE; +#endif + +#ifdef SDL_JOYSTICK_RAWINPUT_GAMING_INPUT + /* Match axes by checking if the distance between the high 4 bits of axis and the 4 bits from match_state is 1 or less */ +#define WindowsGamingInputAxesMatch(gamepad) (\ + (Uint16)(((Sint16)(gamepad.LeftThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[0] + 0x1000) <= 0x2fff && \ + (Uint16)((~(Sint16)(gamepad.LeftThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[1] + 0x1000) <= 0x2fff && \ + (Uint16)(((Sint16)(gamepad.RightThumbstickX * SDL_MAX_SINT16) & 0xF000) - state->match_axes[2] + 0x1000) <= 0x2fff && \ + (Uint16)((~(Sint16)(gamepad.RightThumbstickY * SDL_MAX_SINT16) & 0xF000) - state->match_axes[3] + 0x1000) <= 0x2fff) + + + state->wgi_buttons = + /* Bitwise map .RLD UWVQ TS.K YXBA -> ..QT WVRL DUYX BAKS */ + /* RStick/LStick (QT) RShould/LShould (WV) DPad R/L/D/U YXBA bac(K) (S)tart */ + (match_state & 0x0180) << 5 | (match_state & 0x0600) << 1 | (match_state & 0x7800) >> 5 | (match_state & 0x000F) << 2 | (match_state & 0x0010) >> 3 | (match_state & 0x0040) >> 6; + /* Explicit + ((match_state & (1<wgi_buttons) + state->any_data = SDL_TRUE; +#endif + +} + +#endif /* SDL_JOYSTICK_RAWINPUT_MATCHING */ + +#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT + +static struct { + XINPUT_STATE_EX state; + SDL_bool connected; /* Currently has an active XInput device */ + SDL_bool used; /* Is currently mapped to an SDL device */ + Uint8 correlation_id; +} xinput_state[XUSER_MAX_COUNT]; +static SDL_bool xinput_device_change = SDL_TRUE; +static SDL_bool xinput_state_dirty = SDL_TRUE; + +static void +RAWINPUT_UpdateXInput() +{ + DWORD user_index; + if (xinput_device_change) { + for (user_index = 0; user_index < XUSER_MAX_COUNT; user_index++) { + XINPUT_CAPABILITIES capabilities; + xinput_state[user_index].connected = (XINPUTGETCAPABILITIES(user_index, XINPUT_FLAG_GAMEPAD, &capabilities) == ERROR_SUCCESS) ? SDL_TRUE : SDL_FALSE; + } + xinput_device_change = SDL_FALSE; + xinput_state_dirty = SDL_TRUE; } - - if (GetRawInputDeviceList(devices, &device_count, sizeof(RAWINPUTDEVICELIST)) == -1) { - SDL_free(devices); - return SDL_FALSE; - } - - for (i = 0; i < device_count; i++) { - RID_DEVICE_INFO rdi; - char devName[128]; - UINT rdiSize = sizeof(rdi); - UINT nameSize = SDL_arraysize(devName); - - rdi.cbSize = sizeof(rdi); - if ((devices[i].dwType == RIM_TYPEHID) && - (GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != ((UINT)-1)) && - (GetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != ((UINT)-1)) && - (SDL_strstr(devName, "IG_") != NULL) - ) { - /* XInput-capable */ - if (!RAWINPUT_IsDeviceSupported((Uint16)rdi.hid.dwVendorId, (Uint16)rdi.hid.dwProductId, (Uint16)rdi.hid.dwVersionNumber)) { - /* But not supported, probably Valve virtual controller */ - any_unsupported = SDL_TRUE; + if (xinput_state_dirty) { + xinput_state_dirty = SDL_FALSE; + for (user_index = 0; user_index < SDL_arraysize(xinput_state); ++user_index) { + if (xinput_state[user_index].connected) { + if (XINPUTGETSTATE(user_index, &xinput_state[user_index].state) != ERROR_SUCCESS) { + xinput_state[user_index].connected = SDL_FALSE; + } } } } - SDL_free(devices); - if (any_unsupported) { - /* This happens with Valve virtual controllers that shows up in the RawInputDeviceList, but do not - generate WM_INPUT events, so we must use XInput or DInput to read from it, and with XInput if we - have some supported and some not, we can't easily tell which device is actually showing up in - RawInput, so we must just disable RawInput for now. Additionally, if these unsupported devices - are locally connected, they still show up in RawInput under a *different* HID path, with - different vendor/product IDs, so there's no way to reconcile. */ -#ifdef DEBUG_RAWINPUT - SDL_Log("Found some supported and some unsupported XInput devices, disabling RawInput\n"); -#endif - return SDL_FALSE; - } - return SDL_TRUE; } +static void +RAWINPUT_MarkXInputSlotUsed(Uint8 xinput_slot) +{ + if (xinput_slot != XUSER_INDEX_ANY) { + xinput_state[xinput_slot].used = SDL_TRUE; + } +} + +static void +RAWINPUT_MarkXInputSlotFree(Uint8 xinput_slot) +{ + if (xinput_slot != XUSER_INDEX_ANY) { + xinput_state[xinput_slot].used = SDL_FALSE; + } +} +static SDL_bool +RAWINPUT_MissingXInputSlot() +{ + int ii; + for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) { + if (xinput_state[ii].connected && !xinput_state[ii].used) { + return SDL_TRUE; + } + } + return SDL_FALSE; +} + +static SDL_bool +RAWINPUT_XInputSlotMatches(const WindowsMatchState *state, Uint8 slot_idx) +{ + if (xinput_state[slot_idx].connected) { + WORD xinput_buttons = xinput_state[slot_idx].state.Gamepad.wButtons; + if ((xinput_buttons & ~XINPUT_GAMEPAD_GUIDE) == state->xinput_buttons && XInputAxesMatch(xinput_state[slot_idx].state.Gamepad)) { + return SDL_TRUE; + } + } + return SDL_FALSE; +} + + +static SDL_bool +RAWINPUT_GuessXInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, Uint8 *slot_idx) +{ + int user_index; + int match_count; + + *slot_idx = 0; + + match_count = 0; + for (user_index = 0; user_index < XUSER_MAX_COUNT; ++user_index) { + if (!xinput_state[user_index].used && RAWINPUT_XInputSlotMatches(state, user_index)) { + ++match_count; + *slot_idx = (Uint8)user_index; + /* Incrementing correlation_id for any match, as negative evidence for others being correlated */ + *correlation_id = ++xinput_state[user_index].correlation_id; + } + } + /* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched. + Note that we're still invalidating *other* potential correlations if we have more than one match or we have no + data. */ + if (match_count == 1 && state->any_data) { + return SDL_TRUE; + } + return SDL_FALSE; +} + +#endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */ + +#ifdef SDL_JOYSTICK_RAWINPUT_GAMING_INPUT + +typedef struct WindowsGamingInputGamepadState { + __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad; + struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading state; + RAWINPUT_DeviceContext *correlated_context; + SDL_bool used; /* Is currently mapped to an SDL device */ + SDL_bool connected; /* Just used during update to track disconnected */ + Uint8 correlation_id; + struct __x_ABI_CWindows_CGaming_CInput_CGamepadVibration vibration; +} WindowsGamingInputGamepadState; + +static struct { + WindowsGamingInputGamepadState **per_gamepad; + int per_gamepad_count; + SDL_bool initialized; + SDL_bool dirty; + SDL_bool need_device_list_update; + int ref_count; + __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics *gamepad_statics; +} wgi_state; + +static void +RAWINPUT_MarkWindowsGamingInputSlotUsed(WindowsGamingInputGamepadState *wgi_slot, RAWINPUT_DeviceContext *ctx) +{ + wgi_slot->used = SDL_TRUE; + wgi_slot->correlated_context = ctx; +} + +static void +RAWINPUT_MarkWindowsGamingInputSlotFree(WindowsGamingInputGamepadState *wgi_slot) +{ + wgi_slot->used = SDL_FALSE; + wgi_slot->correlated_context = NULL; +} + +static SDL_bool +RAWINPUT_MissingWindowsGamingInputSlot() +{ + int ii; + for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) { + if (!wgi_state.per_gamepad[ii]->used) { + return SDL_TRUE; + } + } + return SDL_FALSE; +} + +static void +RAWINPUT_UpdateWindowsGamingInput() +{ + int ii; + if (!wgi_state.gamepad_statics) + return; + + if (!wgi_state.dirty) + return; + + wgi_state.dirty = SDL_FALSE; + + if (wgi_state.need_device_list_update) { + wgi_state.need_device_list_update = SDL_FALSE; + for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) { + wgi_state.per_gamepad[ii]->connected = SDL_FALSE; + } + HRESULT hr; + __FIVectorView_1_Windows__CGaming__CInput__CGamepad *gamepads; + + hr = __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_get_Gamepads(wgi_state.gamepad_statics, &gamepads); + if (SUCCEEDED(hr)) { + unsigned int num_gamepads; + + hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_get_Size(gamepads, &num_gamepads); + if (SUCCEEDED(hr)) { + unsigned int i; + for (i = 0; i < num_gamepads; ++i) { + __x_ABI_CWindows_CGaming_CInput_CIGamepad *gamepad; + + hr = __FIVectorView_1_Windows__CGaming__CInput__CGamepad_GetAt(gamepads, i, &gamepad); + if (SUCCEEDED(hr)) { + SDL_bool found = SDL_FALSE; + int jj; + for (jj = 0; jj < wgi_state.per_gamepad_count ; jj++) { + if (wgi_state.per_gamepad[jj]->gamepad == gamepad) { + found = SDL_TRUE; + wgi_state.per_gamepad[jj]->connected = SDL_TRUE; + break; + } + } + if (!found) { + /* New device, add it */ + wgi_state.per_gamepad_count++; + wgi_state.per_gamepad = SDL_realloc(wgi_state.per_gamepad, sizeof(wgi_state.per_gamepad[0]) * wgi_state.per_gamepad_count); + if (!wgi_state.per_gamepad) { + SDL_OutOfMemory(); + return; + } + WindowsGamingInputGamepadState *gamepad_state = SDL_calloc(1, sizeof(*gamepad_state)); + if (!gamepad_state) { + SDL_OutOfMemory(); + return; + } + wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1] = gamepad_state; + gamepad_state->gamepad = gamepad; + gamepad_state->connected = SDL_TRUE; + } else { + /* Already tracked */ + __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad); + } + } + } + for (ii = wgi_state.per_gamepad_count - 1; ii >= 0; ii--) { + WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii]; + if (!gamepad_state->connected) { + /* Device missing, must be disconnected */ + if (gamepad_state->correlated_context) { + gamepad_state->correlated_context->wgi_correlated = SDL_FALSE; + gamepad_state->correlated_context->wgi_slot = NULL; + } + __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(gamepad_state->gamepad); + SDL_free(gamepad_state); + wgi_state.per_gamepad[ii] = wgi_state.per_gamepad[wgi_state.per_gamepad_count - 1]; + --wgi_state.per_gamepad_count; + } + } + } + __FIVectorView_1_Windows__CGaming__CInput__CGamepad_Release(gamepads); + } + } /* need_device_list_update */ + + for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) { + HRESULT hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_GetCurrentReading(wgi_state.per_gamepad[ii]->gamepad, &wgi_state.per_gamepad[ii]->state); + if (!SUCCEEDED(hr)) { + wgi_state.per_gamepad[ii]->connected = SDL_FALSE; /* Not used by anything, currently */ + } + } +} +static void +RAWINPUT_InitWindowsGamingInput(RAWINPUT_DeviceContext *ctx) +{ + wgi_state.need_device_list_update = SDL_TRUE; + wgi_state.ref_count++; + if (!wgi_state.initialized) { + /* I think this takes care of RoInitialize() in a way that is compatible with the rest of SDL */ + if (FAILED(WIN_CoInitialize())) { + return; + } + wgi_state.initialized = SDL_TRUE; + wgi_state.dirty = SDL_TRUE; + + static const IID SDL_IID_IGamepadStatics = { 0x8BBCE529, 0xD49C, 0x39E9, { 0x95, 0x60, 0xE4, 0x7D, 0xDE, 0x96, 0xB7, 0xC8 } }; + HRESULT hr; + HMODULE hModule = LoadLibraryA("combase.dll"); + if (hModule != NULL) { + typedef HRESULT (WINAPI *WindowsCreateString_t)(PCNZWCH sourceString, UINT32 length, HSTRING* string); + typedef HRESULT (WINAPI *WindowsDeleteString_t)(HSTRING string); + typedef HRESULT (WINAPI *RoGetActivationFactory_t)(HSTRING activatableClassId, REFIID iid, void** factory); + + WindowsCreateString_t WindowsCreateStringFunc = (WindowsCreateString_t)GetProcAddress(hModule, "WindowsCreateString"); + WindowsDeleteString_t WindowsDeleteStringFunc = (WindowsDeleteString_t)GetProcAddress(hModule, "WindowsDeleteString"); + RoGetActivationFactory_t RoGetActivationFactoryFunc = (RoGetActivationFactory_t)GetProcAddress(hModule, "RoGetActivationFactory"); + if (WindowsCreateStringFunc && WindowsDeleteStringFunc && RoGetActivationFactoryFunc) { + LPTSTR pNamespace = L"Windows.Gaming.Input.Gamepad"; + HSTRING hNamespaceString; + + hr = WindowsCreateStringFunc(pNamespace, SDL_wcslen(pNamespace), &hNamespaceString); + if (SUCCEEDED(hr)) { + RoGetActivationFactoryFunc(hNamespaceString, &SDL_IID_IGamepadStatics, &wgi_state.gamepad_statics); + WindowsDeleteStringFunc(hNamespaceString); + } + } + FreeLibrary(hModule); + } + } +} + +static SDL_bool +RAWINPUT_WindowsGamingInputSlotMatches(const WindowsMatchState *state, WindowsGamingInputGamepadState *slot) +{ + Uint32 wgi_buttons = slot->state.Buttons; + if ((wgi_buttons & 0x3FFF) == state->wgi_buttons && WindowsGamingInputAxesMatch(slot->state)) { + return SDL_TRUE; + } + return SDL_FALSE; +} + +static SDL_bool +RAWINPUT_GuessWindowsGamingInputSlot(const WindowsMatchState *state, Uint8 *correlation_id, WindowsGamingInputGamepadState **slot) +{ + int match_count; + + match_count = 0; + for (int user_index = 0; user_index < wgi_state.per_gamepad_count; ++user_index) { + WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[user_index]; + if (RAWINPUT_WindowsGamingInputSlotMatches(state, gamepad_state)) { + ++match_count; + *slot = gamepad_state; + /* Incrementing correlation_id for any match, as negative evidence for others being correlated */ + *correlation_id = ++gamepad_state->correlation_id; + } + } + /* Only return a match if we match exactly one, and we have some non-zero data (buttons or axes) that matched. + Note that we're still invalidating *other* potential correlations if we have more than one match or we have no + data. */ + if (match_count == 1 && state->any_data) { + return SDL_TRUE; + } + return SDL_FALSE; +} + +static void +RAWINPUT_QuitWindowsGamingInput(RAWINPUT_DeviceContext *ctx) +{ + wgi_state.need_device_list_update = SDL_TRUE; + --wgi_state.ref_count; + if (!wgi_state.ref_count && wgi_state.initialized) { + for (int ii = 0; ii < wgi_state.per_gamepad_count; ii++) { + __x_ABI_CWindows_CGaming_CInput_CIGamepad_Release(wgi_state.per_gamepad[ii]->gamepad); + } + if (wgi_state.per_gamepad) { + SDL_free(wgi_state.per_gamepad); + wgi_state.per_gamepad = NULL; + } + wgi_state.per_gamepad_count = 0; + if (wgi_state.gamepad_statics) { + __x_ABI_CWindows_CGaming_CInput_CIGamepadStatics_Release(wgi_state.gamepad_statics); + wgi_state.gamepad_statics = NULL; + } + WIN_CoUninitialize(); + wgi_state.initialized = SDL_FALSE; + } +} + +#endif /* SDL_JOYSTICK_RAWINPUT_GAMING_INPUT */ + static int RAWINPUT_JoystickInit(void) { @@ -191,15 +620,16 @@ RAWINPUT_JoystickInit(void) SDL_assert(!SDL_RAWINPUT_inited); SDL_assert(SDL_HelperWindow); - if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_RAWINPUT, SDL_TRUE)) + if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_RAWINPUT, SDL_TRUE)) { return -1; + } - if (!RAWINPUT_AllXInputDevicesSupported()) { + if (WIN_LoadHIDDLL() < 0) { return -1; } for (ii = 0; ii < SDL_arraysize(subscribed_devices); ii++) { - rid[ii].usUsagePage = USAGE_PAGE_GENERIC_DESKTOP; + rid[ii].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP; rid[ii].usUsage = subscribed_devices[ii]; rid[ii].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; /* Receive messages when in background, including device add/remove */ rid[ii].hwndTarget = SDL_HelperWindow; @@ -207,6 +637,7 @@ RAWINPUT_JoystickInit(void) if (!RegisterRawInputDevices(rid, SDL_arraysize(rid), sizeof(RAWINPUTDEVICE))) { SDL_SetError("Couldn't initialize RAWINPUT"); + WIN_UnloadHIDDLL(); return -1; } @@ -234,6 +665,9 @@ static void RAWINPUT_ReleaseDevice(SDL_RAWINPUT_Device *device) { if (SDL_AtomicDecRef(&device->refcount)) { + if (device->preparsed_data) { + SDL_HidD_FreePreparsedData(device->preparsed_data); + } SDL_free(device->name); SDL_free(device); } @@ -251,47 +685,22 @@ RAWINPUT_DeviceFromHandle(HANDLE hDevice) return NULL; } -static SDL_HIDAPI_DeviceDriver * -RAWINPUT_GetDeviceDriver(SDL_RAWINPUT_Device *device) -{ - int i; - SDL_GameControllerType type; - - if (SDL_ShouldIgnoreJoystick(device->name, device->guid)) { - return NULL; - } - - if (device->usage_page && device->usage_page != USAGE_PAGE_GENERIC_DESKTOP) { - return NULL; - } - if (device->usage && device->usage != USAGE_JOYSTICK && device->usage != USAGE_GAMEPAD && device->usage != USAGE_MULTIAXISCONTROLLER) { - return NULL; - } - - type = SDL_GetJoystickGameControllerType("", device->vendor_id, device->product_id, -1, 0, 0, 0); - - for (i = 0; i < SDL_arraysize(SDL_RAWINPUT_drivers); ++i) { - SDL_HIDAPI_DeviceDriver *driver = SDL_RAWINPUT_drivers[i]; - if (/*driver->enabled && */driver->IsSupportedDevice(NULL, type, device->vendor_id, device->product_id, device->version, -1, 0, 0, 0)) { - return driver; - } - } - return NULL; -} - static void RAWINPUT_AddDevice(HANDLE hDevice) { #define CHECK(exp) { if(!(exp)) goto err; } SDL_RAWINPUT_Device *device = NULL; + SDL_RAWINPUT_Device *curr, *last; RID_DEVICE_INFO rdi; UINT rdi_size = sizeof(rdi); - char dev_name[128]; + char dev_name[MAX_PATH]; UINT name_size = SDL_arraysize(dev_name); - const char *name; - SDL_RAWINPUT_Device *curr, *last; + HANDLE hFile = INVALID_HANDLE_VALUE; - SDL_assert(!RAWINPUT_DeviceFromHandle(hDevice)); + /* Make sure we're not trying to add the same device twice */ + if (RAWINPUT_DeviceFromHandle(hDevice)) { + return; + } /* Figure out what kind of device it is */ CHECK(GetRawInputDeviceInfoA(hDevice, RIDI_DEVICEINFO, &rdi, &rdi_size) != (UINT)-1); @@ -301,14 +710,17 @@ RAWINPUT_AddDevice(HANDLE hDevice) CHECK(GetRawInputDeviceInfoA(hDevice, RIDI_DEVICENAME, dev_name, &name_size) != (UINT)-1); /* Only take XInput-capable devices */ CHECK(SDL_strstr(dev_name, "IG_") != NULL); +#ifdef SDL_JOYSTICK_HIDAPI + /* Don't take devices handled by HIDAPI */ + CHECK(!HIDAPI_IsDevicePresent((Uint16)rdi.hid.dwVendorId, (Uint16)rdi.hid.dwProductId, (Uint16)rdi.hid.dwVersionNumber, "")); +#endif - CHECK(device = SDL_callocStruct(SDL_RAWINPUT_Device)); + CHECK(device = (SDL_RAWINPUT_Device *)SDL_calloc(1, sizeof(SDL_RAWINPUT_Device))); device->hDevice = hDevice; device->vendor_id = (Uint16)rdi.hid.dwVendorId; device->product_id = (Uint16)rdi.hid.dwProductId; device->version = (Uint16)rdi.hid.dwVersionNumber; - device->usage = rdi.hid.usUsage; - device->usage_page = rdi.hid.usUsagePage; + device->is_xinput = SDL_TRUE; { const Uint16 vendor = device->vendor_id; @@ -330,36 +742,19 @@ RAWINPUT_AddDevice(HANDLE hDevice) device->guid.data[15] = 0; } - CHECK(device->driver = RAWINPUT_GetDeviceDriver(device)); + hFile = CreateFileA(dev_name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + CHECK(hFile != INVALID_HANDLE_VALUE); - name = device->driver->GetDeviceName(device->vendor_id, device->product_id); - if (name) { - device->name = SDL_strdup(name); - } else { + { char *manufacturer_string = NULL; char *product_string = NULL; - HMODULE hHID; + WCHAR string[128]; - hHID = LoadLibrary( TEXT( "hid.dll" ) ); - if (hHID) { - typedef BOOLEAN (WINAPI * HidD_GetStringFunc)(HANDLE HidDeviceObject, PVOID Buffer, ULONG BufferLength); - HidD_GetStringFunc GetManufacturerString = (HidD_GetStringFunc)GetProcAddress(hHID, "HidD_GetManufacturerString"); - HidD_GetStringFunc GetProductString = (HidD_GetStringFunc)GetProcAddress(hHID, "HidD_GetProductString"); - if (GetManufacturerString && GetProductString) { - HANDLE hFile = CreateFileA(dev_name, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - if (hFile != INVALID_HANDLE_VALUE) { - WCHAR string[128]; - - if (GetManufacturerString(hFile, string, sizeof(string))) { - manufacturer_string = WIN_StringToUTF8(string); - } - if (GetProductString(hFile, string, sizeof(string))) { - product_string = WIN_StringToUTF8(string); - } - CloseHandle(hFile); - } - } - FreeLibrary(hHID); + if (SDL_HidD_GetManufacturerString(hFile, string, sizeof(string))) { + manufacturer_string = WIN_StringToUTF8(string); + } + if (SDL_HidD_GetProductString(hFile, string, sizeof(string))) { + product_string = WIN_StringToUTF8(string); } device->name = SDL_CreateJoystickName(device->vendor_id, device->product_id, manufacturer_string, product_string); @@ -372,6 +767,14 @@ RAWINPUT_AddDevice(HANDLE hDevice) } } + CHECK(SDL_HidD_GetPreparsedData(hFile, &device->preparsed_data)); + + CloseHandle(hFile); + hFile = INVALID_HANDLE_VALUE; + + device->joystick_id = SDL_GetNextJoystickInstanceID(); + +#define DEBUG_RAWINPUT #ifdef DEBUG_RAWINPUT SDL_Log("Adding RAWINPUT device '%s' VID 0x%.4x, PID 0x%.4x, version %d, handle 0x%.8x\n", device->name, device->vendor_id, device->product_id, device->version, device->hDevice); #endif @@ -388,19 +791,21 @@ RAWINPUT_AddDevice(HANDLE hDevice) } ++SDL_RAWINPUT_numjoysticks; - /* HIDAPI_JoystickConnected calls SDL_GetNextJoystickInstanceID() and SDL_PrivateJoystickAdded(), and calls back in to us, so - the device list must be updated before calling this. */ - CHECK(HIDAPI_JoystickConnected(&device->hiddevice, &device->joystick_id, SDL_TRUE)); - /* Old: CHECK(device->driver->InitDevice(&device->hiddevice)); But, we need the joystick_id */ + + SDL_PrivateJoystickAdded(device->joystick_id); return; err: + if (hFile != INVALID_HANDLE_VALUE) { + CloseHandle(hFile); + } if (device) { if (device->name) SDL_free(device->name); SDL_free(device); } +#undef CHECK } static void @@ -416,8 +821,7 @@ RAWINPUT_DelDevice(SDL_RAWINPUT_Device *device, SDL_bool send_event) } --SDL_RAWINPUT_numjoysticks; - /* Calls SDL_PrivateJoystickRemoved() */ - HIDAPI_JoystickDisconnected(&device->hiddevice, device->joystick_id, SDL_TRUE); + SDL_PrivateJoystickRemoved(device->joystick_id); #ifdef DEBUG_RAWINPUT SDL_Log("Removing RAWINPUT device '%s' VID 0x%.4x, PID 0x%.4x, version %d, handle 0x%.8x\n", device->name, device->vendor_id, device->product_id, device->version, device->hDevice); @@ -453,24 +857,70 @@ RAWINPUT_UpdateDeviceList(void) } } -static SDL_bool -RAWINPUT_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version) +static void +RAWINPUT_PostUpdate(void) { - int i; +#ifdef SDL_JOYSTICK_RAWINPUT_MATCHING + SDL_bool unmapped_guide_pressed = SDL_FALSE; - SDL_GameControllerType type = SDL_GetJoystickGameControllerType("", vendor_id, product_id, -1, 0, 0, 0); - - for (i = 0; i < SDL_arraysize(SDL_RAWINPUT_drivers); ++i) { - SDL_HIDAPI_DeviceDriver *driver = SDL_RAWINPUT_drivers[i]; - /* Ignoring driver->enabled here, and elsewhere in this file, as the if the driver is enabled by disabling HID, - we still want RawInput to use it. If we end up with more than one RawInput driver, we may need to rework - how the hints interact (separate enabled state, perhaps). - */ - if (/*driver->enabled && */driver->IsSupportedDevice(NULL, type, vendor_id, product_id, version, -1, 0, 0, 0)) { - return SDL_TRUE; +#ifdef SDL_JOYSTICK_RAWINPUT_GAMING_INPUT + if (!wgi_state.dirty) { + int ii; + for (ii = 0; ii < wgi_state.per_gamepad_count; ii++) { + WindowsGamingInputGamepadState *gamepad_state = wgi_state.per_gamepad[ii]; + if (!gamepad_state->used && (gamepad_state->state.Buttons & GamepadButtons_GUIDE)) { + unmapped_guide_pressed = SDL_TRUE; + break; + } } } - return SDL_FALSE; + wgi_state.dirty = SDL_TRUE; +#endif + + +#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT + if (!xinput_state_dirty) { + int ii; + for (ii = 0; ii < SDL_arraysize(xinput_state); ii++) { + if (xinput_state[ii].connected && !xinput_state[ii].used && (xinput_state[ii].state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE)) { + unmapped_guide_pressed = SDL_TRUE; + break; + } + } + } + xinput_state_dirty = SDL_TRUE; +#endif + + if (unmapped_guide_pressed) { + if (guide_button_candidate.joystick && !guide_button_candidate.last_joystick) { + SDL_Joystick *joystick = guide_button_candidate.joystick; + RAWINPUT_DeviceContext *ctx = joystick->hwdata; + if (ctx->guide_hack) { + int guide_button = joystick->nbuttons - 1; + + SDL_PrivateJoystickButton(guide_button_candidate.joystick, guide_button, SDL_PRESSED); + } + guide_button_candidate.last_joystick = guide_button_candidate.joystick; + } + } else if (guide_button_candidate.last_joystick) { + SDL_Joystick *joystick = guide_button_candidate.last_joystick; + RAWINPUT_DeviceContext *ctx = joystick->hwdata; + if (ctx->guide_hack) { + int guide_button = joystick->nbuttons - 1; + + SDL_PrivateJoystickButton(joystick, guide_button, SDL_RELEASED); + } + guide_button_candidate.last_joystick = NULL; + } + guide_button_candidate.joystick = NULL; + +#endif /* SDL_JOYSTICK_RAWINPUT_MATCHING */ +} + +SDL_bool +RAWINPUT_IsEnabled() +{ + return SDL_RAWINPUT_inited; } SDL_bool @@ -478,11 +928,6 @@ RAWINPUT_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version) { SDL_RAWINPUT_Device *device; - /* Don't update the device list for devices we know aren't supported */ - if (!RAWINPUT_IsDeviceSupported(vendor_id, product_id, version)) { - return SDL_FALSE; - } - /* Make sure the device list is completely up to date when we check for device presence */ RAWINPUT_UpdateDeviceList(); @@ -499,138 +944,306 @@ RAWINPUT_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version) static void RAWINPUT_JoystickDetect(void) { - int i; /* Just ensure the window's add/remove messages have been pumped */ RAWINPUT_UpdateDeviceList(); - for (i = 0; i < SDL_arraysize(SDL_RAWINPUT_drivers); ++i) { - SDL_HIDAPI_DeviceDriver *driver = SDL_RAWINPUT_drivers[i]; - /* Running PostUpdate here only if it's *not* enabled (and ran elsewhere) */ - if (!driver->enabled && driver->PostUpdate) { - driver->PostUpdate(); - } - } + RAWINPUT_PostUpdate(); + SDL_RAWINPUT_need_pump = SDL_TRUE; } static SDL_RAWINPUT_Device * -RAWINPUT_GetJoystickByIndex(int device_index, SDL_JoystickID *pJoystickID) +RAWINPUT_GetDeviceByIndex(int device_index) { SDL_RAWINPUT_Device *device = SDL_RAWINPUT_devices; while (device) { - if (device->driver) { - SDL_assert(device->hiddevice.num_joysticks == 1); - if (device_index < device->hiddevice.num_joysticks) { - if (pJoystickID) { - *pJoystickID = device->hiddevice.joysticks[device_index]; - } - return device; - } - device_index -= device->hiddevice.num_joysticks; + if (device_index == 0) { + break; } + --device_index; device = device->next; } - return NULL; + return device; } static const char * RAWINPUT_JoystickGetDeviceName(int device_index) { - return RAWINPUT_GetJoystickByIndex(device_index, NULL)->name; + return RAWINPUT_GetDeviceByIndex(device_index)->name; } static int RAWINPUT_JoystickGetDevicePlayerIndex(int device_index) { - SDL_RAWINPUT_Device *device; - SDL_JoystickID instance_id; - int player_index = -1; - - device = RAWINPUT_GetJoystickByIndex(device_index, &instance_id); - if (device && device->driver) { - player_index = device->driver->GetDevicePlayerIndex(&device->hiddevice, instance_id); - } - - return player_index; + return -1; } static void RAWINPUT_JoystickSetDevicePlayerIndex(int device_index, int player_index) { - SDL_RAWINPUT_Device *device; - SDL_JoystickID instance_id; - - device = RAWINPUT_GetJoystickByIndex(device_index, &instance_id); - if (device) { - device->driver->SetDevicePlayerIndex(&device->hiddevice, instance_id, player_index); - } } static SDL_JoystickGUID RAWINPUT_JoystickGetDeviceGUID(int device_index) { - return RAWINPUT_GetJoystickByIndex(device_index, NULL)->guid; + return RAWINPUT_GetDeviceByIndex(device_index)->guid; } static SDL_JoystickID RAWINPUT_JoystickGetDeviceInstanceID(int device_index) { - SDL_JoystickID instance_id = -1; - RAWINPUT_GetJoystickByIndex(device_index, &instance_id); - return instance_id; + return RAWINPUT_GetDeviceByIndex(device_index)->joystick_id; } static int -RAWINPUT_JoystickOpen(SDL_Joystick * joystick, int device_index) +RAWINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) { - SDL_RAWINPUT_Device *device = RAWINPUT_GetJoystickByIndex(device_index, NULL); - struct joystick_hwdata *hwdata = SDL_callocStruct(struct joystick_hwdata); + SDL_RAWINPUT_Device *device = RAWINPUT_GetDeviceByIndex(device_index); + RAWINPUT_DeviceContext *ctx; + HIDP_CAPS caps; + HIDP_BUTTON_CAPS *button_caps; + HIDP_VALUE_CAPS *value_caps; + ULONG i; - if (!hwdata) { + ctx = (RAWINPUT_DeviceContext *)SDL_calloc(1, sizeof(RAWINPUT_DeviceContext)); + if (!ctx) { + return SDL_OutOfMemory(); + } + joystick->hwdata = ctx; + + ctx->device = RAWINPUT_AcquireDevice(device); + device->joystick = joystick; + + if (device->is_xinput) { + /* We'll try to get guide button and trigger axes from XInput */ +#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT + xinput_device_change = SDL_TRUE; + ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_CORRELATE_XINPUT, SDL_TRUE); + if (ctx->xinput_enabled && (WIN_LoadXInputDLL() < 0 || !XINPUTGETSTATE)) { + ctx->xinput_enabled = SDL_FALSE; + } + ctx->xinput_slot = XUSER_INDEX_ANY; +#endif +#ifdef SDL_JOYSTICK_RAWINPUT_GAMING_INPUT + RAWINPUT_InitWindowsGamingInput(ctx); +#endif + } + + ctx->is_xinput = device->is_xinput; + ctx->preparsed_data = device->preparsed_data; + ctx->max_data_length = SDL_HidP_MaxDataListLength(HidP_Input, ctx->preparsed_data); + ctx->data = (HIDP_DATA *)SDL_malloc(ctx->max_data_length * sizeof(*ctx->data)); + if (!ctx->data) { + RAWINPUT_JoystickClose(joystick); return SDL_OutOfMemory(); } - if (!device->driver->OpenJoystick(&device->hiddevice, joystick)) { - SDL_free(hwdata); - return -1; + if (SDL_HidP_GetCaps(ctx->preparsed_data, &caps) != HIDP_STATUS_SUCCESS) { + RAWINPUT_JoystickClose(joystick); + return SDL_SetError("Couldn't get device capabilities"); } - hwdata->reserved = (void*)-1; /* crash if some code slips by that tries to use this */ - hwdata->device = RAWINPUT_AcquireDevice(device); - device->joystick = joystick; + button_caps = SDL_stack_alloc(HIDP_BUTTON_CAPS, caps.NumberInputButtonCaps); + if (SDL_HidP_GetButtonCaps(HidP_Input, button_caps, &caps.NumberInputButtonCaps, ctx->preparsed_data) != HIDP_STATUS_SUCCESS) { + RAWINPUT_JoystickClose(joystick); + return SDL_SetError("Couldn't get device button capabilities"); + } - joystick->hwdata = hwdata; + value_caps = SDL_stack_alloc(HIDP_VALUE_CAPS, caps.NumberInputValueCaps); + if (SDL_HidP_GetValueCaps(HidP_Input, value_caps, &caps.NumberInputValueCaps, ctx->preparsed_data) != HIDP_STATUS_SUCCESS) { + RAWINPUT_JoystickClose(joystick); + return SDL_SetError("Couldn't get device value capabilities"); + } + + for (i = 0; i < caps.NumberInputButtonCaps; ++i) { + HIDP_BUTTON_CAPS *cap = &button_caps[i]; + + if (cap->UsagePage == USB_USAGEPAGE_BUTTON) { + int count; + + if (cap->IsRange) { + count = 1 + (cap->Range.DataIndexMax - cap->Range.DataIndexMin); + } else { + count = 1; + } + + joystick->nbuttons += count; + } + } + + if (joystick->nbuttons > 0) { + int button_index = 0; + + ctx->button_indices = (USHORT *)SDL_malloc(joystick->nbuttons * sizeof(*ctx->button_indices)); + if (!ctx->button_indices) { + RAWINPUT_JoystickClose(joystick); + return SDL_OutOfMemory(); + } + + for (i = 0; i < caps.NumberInputButtonCaps; ++i) { + HIDP_BUTTON_CAPS *cap = &button_caps[i]; + + if (cap->UsagePage == USB_USAGEPAGE_BUTTON) { + if (cap->IsRange) { + int j, count = 1 + (cap->Range.DataIndexMax - cap->Range.DataIndexMin); + + for (j = 0; j < count; ++j) { + ctx->button_indices[button_index++] = cap->Range.DataIndexMin + j; + } + } else { + ctx->button_indices[button_index++] = cap->NotRange.DataIndex; + } + } + } + } + if (ctx->is_xinput && joystick->nbuttons == 10) { + ctx->guide_hack = SDL_TRUE; + joystick->nbuttons += 1; + } + + for (i = 0; i < caps.NumberInputValueCaps; ++i) { + HIDP_VALUE_CAPS *cap = &value_caps[i]; + + if (cap->IsRange) { + continue; + } + + if (ctx->trigger_hack && cap->NotRange.Usage == USB_USAGE_GENERIC_Z) { + continue; + } + + if (cap->NotRange.Usage == USB_USAGE_GENERIC_HAT) { + joystick->nhats += 1; + continue; + } + + if (ctx->is_xinput && cap->NotRange.Usage == USB_USAGE_GENERIC_Z) { + continue; + } + + joystick->naxes += 1; + } + + if (joystick->naxes > 0) { + int axis_index = 0; + + ctx->axis_indices = (USHORT *)SDL_malloc(joystick->naxes * sizeof(*ctx->axis_indices)); + if (!ctx->axis_indices) { + RAWINPUT_JoystickClose(joystick); + return SDL_OutOfMemory(); + } + + for (i = 0; i < caps.NumberInputValueCaps; ++i) { + HIDP_VALUE_CAPS *cap = &value_caps[i]; + + if (cap->IsRange) { + continue; + } + + if (cap->NotRange.Usage == USB_USAGE_GENERIC_HAT) { + continue; + } + + if (ctx->is_xinput && cap->NotRange.Usage == USB_USAGE_GENERIC_Z) { + ctx->trigger_hack = SDL_TRUE; + ctx->trigger_hack_index = cap->NotRange.DataIndex; + continue; + } + + ctx->axis_indices[axis_index++] = cap->NotRange.DataIndex; + } + } + if (ctx->trigger_hack) { + joystick->naxes += 2; + } + + if (joystick->nhats > 0) { + int hat_index = 0; + + ctx->hat_indices = (USHORT *)SDL_malloc(joystick->nhats * sizeof(*ctx->hat_indices)); + if (!ctx->hat_indices) { + RAWINPUT_JoystickClose(joystick); + return SDL_OutOfMemory(); + } + + for (i = 0; i < caps.NumberInputValueCaps; ++i) { + HIDP_VALUE_CAPS *cap = &value_caps[i]; + + if (cap->IsRange) { + continue; + } + + if (cap->NotRange.Usage != USB_USAGE_GENERIC_HAT) { + continue; + } + + ctx->hat_indices[hat_index++] = cap->NotRange.DataIndex; + } + } + + joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; return 0; } static int -RAWINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +RAWINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) { - struct joystick_hwdata *hwdata = joystick->hwdata; - SDL_RAWINPUT_Device *device = hwdata->device; +#if defined(SDL_JOYSTICK_RAWINPUT_GAMING_INPUT) || defined(SDL_JOYSTICK_RAWINPUT_XINPUT) + RAWINPUT_DeviceContext *ctx = joystick->hwdata; +#endif - return device->driver->RumbleJoystick(&device->hiddevice, joystick, low_frequency_rumble, high_frequency_rumble); + SDL_bool rumbled = SDL_FALSE; + +#ifdef SDL_JOYSTICK_RAWINPUT_GAMING_INPUT + if (!rumbled && ctx->wgi_correlated) { + WindowsGamingInputGamepadState *gamepad_state = ctx->wgi_slot; + HRESULT hr; + gamepad_state->vibration.LeftMotor = (DOUBLE)low_frequency_rumble / SDL_MAX_UINT16; + gamepad_state->vibration.RightMotor = (DOUBLE)high_frequency_rumble / SDL_MAX_UINT16; + hr = __x_ABI_CWindows_CGaming_CInput_CIGamepad_put_Vibration(gamepad_state->gamepad, gamepad_state->vibration); + if (SUCCEEDED(hr)) { + rumbled = SDL_TRUE; + } + } +#endif + +#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT + if (!rumbled && ctx->xinput_correlated) { + XINPUT_VIBRATION XVibration; + + if (!XINPUTSETSTATE) { + return SDL_Unsupported(); + } + + XVibration.wLeftMotorSpeed = low_frequency_rumble; + XVibration.wRightMotorSpeed = high_frequency_rumble; + if (XINPUTSETSTATE(ctx->xinput_slot, &XVibration) == ERROR_SUCCESS) { + rumbled = SDL_TRUE; + } else { + return SDL_SetError("XInputSetState() failed"); + } + } +#endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */ + + return 0; } static int -RAWINPUT_JoystickRumbleTriggers(SDL_Joystick * joystick, Uint16 left_rumble, Uint16 right_rumble) +RAWINPUT_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) { - struct joystick_hwdata *hwdata = joystick->hwdata; - SDL_RAWINPUT_Device *device = hwdata->device; - - return device->driver->RumbleJoystickTriggers(&device->hiddevice, joystick, left_rumble, right_rumble); + return SDL_Unsupported(); } static SDL_bool -RAWINPUT_JoystickHasLED(SDL_Joystick * joystick) +RAWINPUT_JoystickHasLED(SDL_Joystick *joystick) { return SDL_FALSE; } static int -RAWINPUT_JoystickSetLED(SDL_Joystick * joystick, Uint8 red, Uint8 green, Uint8 blue) +RAWINPUT_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) { return SDL_Unsupported(); } @@ -641,37 +1254,445 @@ RAWINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) return SDL_Unsupported(); } - -static void -RAWINPUT_JoystickUpdate(SDL_Joystick * joystick) +static HIDP_DATA *GetData(USHORT index, HIDP_DATA *data, ULONG length) { - struct joystick_hwdata *hwdata; - SDL_RAWINPUT_Device *device; - /* Ensure data messages have been pumped */ - RAWINPUT_PumpMessages(); - hwdata = joystick->hwdata; - device = hwdata->device; + ULONG i; - device->driver->UpdateDevice(&device->hiddevice); + /* Check to see if the data is at the expected offset */ + if (index < length && data[index].DataIndex == index) { + return &data[index]; + } + + /* Loop through the data to find it */ + for (i = 0; i < length; ++i) { + if (data[i].DataIndex == index) { + return &data[i]; + } + } + return NULL; +} + +/* This is the packet format for Xbox 360 and Xbox One controllers on Windows, + however with this interface there is no rumble support, no guide button, + and the left and right triggers are tied together as a single axis. + + We use XInput and Windows.Gaming.Input to make up for these shortcomings. + */ +static void +RAWINPUT_HandleStatePacket(SDL_Joystick *joystick, Uint8 *data, int size) +{ + RAWINPUT_DeviceContext *ctx = joystick->hwdata; +#ifdef SDL_JOYSTICK_RAWINPUT_MATCHING + /* Map new buttons and axes into game controller controls */ + static int axis_map[] = { + SDL_CONTROLLER_AXIS_LEFTY, + SDL_CONTROLLER_AXIS_LEFTX, + SDL_CONTROLLER_AXIS_RIGHTY, + SDL_CONTROLLER_AXIS_RIGHTX + }; + static int button_map[] = { + SDL_CONTROLLER_BUTTON_A, + SDL_CONTROLLER_BUTTON_B, + SDL_CONTROLLER_BUTTON_X, + SDL_CONTROLLER_BUTTON_Y, + SDL_CONTROLLER_BUTTON_LEFTSHOULDER, + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, + SDL_CONTROLLER_BUTTON_BACK, + SDL_CONTROLLER_BUTTON_START, + SDL_CONTROLLER_BUTTON_LEFTSTICK, + SDL_CONTROLLER_BUTTON_RIGHTSTICK + }; + Uint32 match_state = ctx->match_state; + /* Update match_state with button bit, then fall through */ +#define SDL_PrivateJoystickButton(joystick, button, state) if (button < SDL_arraysize(button_map)) if (state) match_state |= 1 << button_map[button]; else match_state &= ~(1 << button_map[button]); SDL_PrivateJoystickButton(joystick, button, state) + /* Grab high 4 bits of value, then fall through */ +#define SDL_PrivateJoystickAxis(joystick, axis, value) if (axis < SDL_arraysize(axis_map)) match_state = (match_state & ~(0xF << (4 * axis_map[axis] + 16))) | ((value) & 0xF000) << (4 * axis_map[axis] + 4); SDL_PrivateJoystickAxis(joystick, axis, value) +#endif + ULONG data_length = ctx->max_data_length; + int i; + int nbuttons = joystick->nbuttons - (ctx->guide_hack * 1); + int naxes = joystick->naxes - (ctx->trigger_hack * 2); + int nhats = joystick->nhats; + Uint32 button_mask = 0; + + if (SDL_HidP_GetData(HidP_Input, ctx->data, &data_length, ctx->preparsed_data, (PCHAR)data, size) != HIDP_STATUS_SUCCESS) { + return; + } + + for (i = 0; i < nbuttons; ++i) { + HIDP_DATA *item = GetData(ctx->button_indices[i], ctx->data, data_length); + if (item && item->On) { + button_mask |= (1 << i); + } + } + for (i = 0; i < nbuttons; ++i) { + SDL_PrivateJoystickButton(joystick, i, (button_mask & (1 << i)) ? SDL_PRESSED : SDL_RELEASED); + } + + for (i = 0; i < naxes; ++i) { + HIDP_DATA *item = GetData(ctx->axis_indices[i], ctx->data, data_length); + if (item) { + Sint16 axis = (int)(Uint16)item->RawValue - 0x8000; + SDL_PrivateJoystickAxis(joystick, i, axis); + } + } + + for (i = 0; i < nhats; ++i) { + HIDP_DATA *item = GetData(ctx->hat_indices[i], ctx->data, data_length); + if (item) { + const Uint8 hat_states[] = { + SDL_HAT_CENTERED, + SDL_HAT_UP, + SDL_HAT_UP | SDL_HAT_RIGHT, + SDL_HAT_RIGHT, + SDL_HAT_DOWN | SDL_HAT_RIGHT, + SDL_HAT_DOWN, + SDL_HAT_DOWN | SDL_HAT_LEFT, + SDL_HAT_LEFT, + SDL_HAT_UP | SDL_HAT_LEFT, + }; + ULONG state = item->RawValue; + + if (state < SDL_arraysize(hat_states)) { + SDL_PrivateJoystickHat(joystick, i, hat_states[state]); + } + } + } + +#ifdef SDL_JOYSTICK_RAWINPUT_MATCHING +#undef SDL_PrivateJoystickButton +#undef SDL_PrivateJoystickAxis +#endif + + if (ctx->trigger_hack) { + SDL_bool has_trigger_data = SDL_FALSE; + +#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT + /* Prefer XInput over WindowsGamingInput, it continues to provide data in the background */ + if (!has_trigger_data && ctx->xinput_enabled && ctx->xinput_correlated) { + has_trigger_data = SDL_TRUE; + } +#endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */ + +#ifdef SDL_JOYSTICK_RAWINPUT_GAMING_INPUT + if (!has_trigger_data && ctx->wgi_correlated) { + has_trigger_data = SDL_TRUE; + } +#endif /* SDL_JOYSTICK_RAWINPUT_GAMING_INPUT */ + + if (!has_trigger_data) { + HIDP_DATA *item = GetData(ctx->trigger_hack_index, ctx->data, data_length); + if (item) { + int left_trigger = joystick->naxes - 2; + int right_trigger = joystick->naxes - 1; + Sint16 value = (int)(Uint16)item->RawValue - 0x8000; + if (value < 0) { + value = -value * 2 - 32769; + SDL_PrivateJoystickAxis(joystick, left_trigger, SDL_MIN_SINT16); + SDL_PrivateJoystickAxis(joystick, right_trigger, value); + } else if (value > 0) { + value = value * 2 - 32767; + SDL_PrivateJoystickAxis(joystick, left_trigger, value); + SDL_PrivateJoystickAxis(joystick, right_trigger, SDL_MIN_SINT16); + } else { + SDL_PrivateJoystickAxis(joystick, left_trigger, SDL_MIN_SINT16); + SDL_PrivateJoystickAxis(joystick, right_trigger, SDL_MIN_SINT16); + } + } + } + } + +#ifdef SDL_JOYSTICK_RAWINPUT_MATCHING + if (ctx->is_xinput) { + ctx->match_state = match_state; + ctx->last_state_packet = SDL_GetTicks(); + } +#endif } static void -RAWINPUT_JoystickClose(SDL_Joystick * joystick) +RAWINPUT_UpdateOtherAPIs(SDL_Joystick *joystick) { - struct joystick_hwdata *hwdata = joystick->hwdata; +#ifdef SDL_JOYSTICK_RAWINPUT_MATCHING + RAWINPUT_DeviceContext *ctx = joystick->hwdata; + SDL_bool has_trigger_data = SDL_FALSE; + SDL_bool correlated = SDL_FALSE; + WindowsMatchState match_state_xinput; + int guide_button = joystick->nbuttons - 1; + int left_trigger = joystick->naxes - 2; + int right_trigger = joystick->naxes - 1; - if (hwdata) { + RAWINPUT_FillMatchState(&match_state_xinput, ctx->match_state); + +#ifdef SDL_JOYSTICK_RAWINPUT_GAMING_INPUT + /* Parallel logic to WINDOWS_XINPUT below */ + RAWINPUT_UpdateWindowsGamingInput(); + if (ctx->wgi_correlated) { + /* We have been previously correlated, ensure we are still matching, see comments in XINPUT section */ + if (RAWINPUT_WindowsGamingInputSlotMatches(&match_state_xinput, ctx->wgi_slot)) { + ctx->wgi_uncorrelate_count = 0; + } else { + ++ctx->wgi_uncorrelate_count; + /* Only un-correlate if this is consistent over multiple Update() calls - the timing of polling/event + pumping can easily cause this to uncorrelate for a frame. 2 seemed reliable in my testing, but + let's set it to 3 to be safe. An incorrect un-correlation will simply result in lower precision + triggers for a frame. */ + if (ctx->wgi_uncorrelate_count >= 3) { +#ifdef DEBUG_JOYSTICK + SDL_Log("UN-Correlated joystick %d to WindowsGamingInput device #%d\n", joystick->instance_id, ctx->wgi_slot); +#endif + RAWINPUT_MarkWindowsGamingInputSlotFree(ctx->wgi_slot); + ctx->wgi_correlated = SDL_FALSE; + ctx->wgi_correlation_count = 0; + /* Force release of Guide button, it can't possibly be down on this device now. */ + /* It gets left down if we were actually correlated incorrectly and it was released on the WindowsGamingInput + device but we didn't get a state packet. */ + if (ctx->guide_hack) { + SDL_PrivateJoystickButton(joystick, guide_button, SDL_RELEASED); + } + } + } + } + if (!ctx->wgi_correlated) { + SDL_bool new_correlation_count = 0; + if (RAWINPUT_MissingWindowsGamingInputSlot()) { + Uint8 correlation_id; + WindowsGamingInputGamepadState *slot_idx; + if (RAWINPUT_GuessWindowsGamingInputSlot(&match_state_xinput, &correlation_id, &slot_idx)) { + /* we match exactly one WindowsGamingInput device */ + /* Probably can do without wgi_correlation_count, just check and clear wgi_slot to NULL, unless we need + even more frames to be sure. */ + if (ctx->wgi_correlation_count && ctx->wgi_slot == slot_idx) { + /* was correlated previously, and still the same device */ + if (ctx->wgi_correlation_id + 1 == correlation_id) { + /* no one else was correlated in the meantime */ + new_correlation_count = ctx->wgi_correlation_count + 1; + if (new_correlation_count == 2) { + /* correlation stayed steady and uncontested across multiple frames, guaranteed match */ + ctx->wgi_correlated = SDL_TRUE; +#ifdef DEBUG_JOYSTICK + SDL_Log("Correlated joystick %d to WindowsGamingInput device #%d\n", joystick->instance_id, slot_idx); +#endif + correlated = SDL_TRUE; + RAWINPUT_MarkWindowsGamingInputSlotUsed(ctx->wgi_slot, ctx); + /* If the generalized Guide button was using us, it doesn't need to anymore */ + if (guide_button_candidate.joystick == joystick) + guide_button_candidate.joystick = NULL; + if (guide_button_candidate.last_joystick == joystick) + guide_button_candidate.last_joystick = NULL; + } + } else { + /* someone else also possibly correlated to this device, start over */ + new_correlation_count = 1; + } + } else { + /* new possible correlation */ + new_correlation_count = 1; + ctx->wgi_slot = slot_idx; + } + ctx->wgi_correlation_id = correlation_id; + } else { + /* Match multiple WindowsGamingInput devices, or none (possibly due to no buttons pressed) */ + } + } + ctx->wgi_correlation_count = new_correlation_count; + } else { + correlated = SDL_TRUE; + } +#endif /* SDL_JOYSTICK_RAWINPUT_GAMING_INPUT */ + +#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT + /* Parallel logic to WINDOWS_GAMING_INPUT above */ + if (ctx->xinput_enabled) { + RAWINPUT_UpdateXInput(); + if (ctx->xinput_correlated) { + /* We have been previously correlated, ensure we are still matching */ + /* This is required to deal with two (mostly) un-preventable mis-correlation situations: + A) Since the HID data stream does not provide an initial state (but polling XInput does), if we open + 5 controllers (#1-4 XInput mapped, #5 is not), and controller 1 had the A button down (and we don't + know), and the user presses A on controller #5, we'll see exactly 1 controller with A down (#5) and + exactly 1 XInput device with A down (#1), and incorrectly correlate. This code will then un-correlate + when A is released from either controller #1 or #5. + B) Since the app may not open all controllers, we could have a similar situation where only controller #5 + is opened, and the user holds A on controllers #1 and #5 simultaneously - again we see only 1 controller + with A down and 1 XInput device with A down, and incorrectly correlate. This should be very unusual + (only when apps do not open all controllers, yet are listening to Guide button presses, yet + for some reason want to ignore guide button presses on the un-opened controllers, yet users are + pressing buttons on the unopened controllers), and will resolve itself when either button is released + and we un-correlate. We could prevent this by processing the state packets for *all* controllers, + even un-opened ones, as that would allow more precise correlation. + */ + if (RAWINPUT_XInputSlotMatches(&match_state_xinput, ctx->xinput_slot)) { + ctx->xinput_uncorrelate_count = 0; + } else { + ++ctx->xinput_uncorrelate_count; + /* Only un-correlate if this is consistent over multiple Update() calls - the timing of polling/event + pumping can easily cause this to uncorrelate for a frame. 2 seemed reliable in my testing, but + let's set it to 3 to be safe. An incorrect un-correlation will simply result in lower precision + triggers for a frame. */ + if (ctx->xinput_uncorrelate_count >= 3) { +#ifdef DEBUG_JOYSTICK + SDL_Log("UN-Correlated joystick %d to XInput device #%d\n", joystick->instance_id, ctx->xinput_slot); +#endif + RAWINPUT_MarkXInputSlotFree(ctx->xinput_slot); + ctx->xinput_correlated = SDL_FALSE; + ctx->xinput_correlation_count = 0; + /* Force release of Guide button, it can't possibly be down on this device now. */ + /* It gets left down if we were actually correlated incorrectly and it was released on the XInput + device but we didn't get a state packet. */ + if (ctx->guide_hack) { + SDL_PrivateJoystickButton(joystick, guide_button, SDL_RELEASED); + } + } + } + } + if (!ctx->xinput_correlated) { + Uint8 new_correlation_count = 0; + if (RAWINPUT_MissingXInputSlot()) { + Uint8 correlation_id = 0; + Uint8 slot_idx = 0; + if (RAWINPUT_GuessXInputSlot(&match_state_xinput, &correlation_id, &slot_idx)) { + /* we match exactly one XInput device */ + /* Probably can do without xinput_correlation_count, just check and clear xinput_slot to ANY, unless + we need even more frames to be sure */ + if (ctx->xinput_correlation_count && ctx->xinput_slot == slot_idx) { + /* was correlated previously, and still the same device */ + if (ctx->xinput_correlation_id + 1 == correlation_id) { + /* no one else was correlated in the meantime */ + new_correlation_count = ctx->xinput_correlation_count + 1; + if (new_correlation_count == 2) { + /* correlation stayed steady and uncontested across multiple frames, guaranteed match */ + ctx->xinput_correlated = SDL_TRUE; +#ifdef DEBUG_JOYSTICK + SDL_Log("Correlated joystick %d to XInput device #%d\n", joystick->instance_id, slot_idx); +#endif + correlated = SDL_TRUE; + RAWINPUT_MarkXInputSlotUsed(ctx->xinput_slot); + /* If the generalized Guide button was using us, it doesn't need to anymore */ + if (guide_button_candidate.joystick == joystick) + guide_button_candidate.joystick = NULL; + if (guide_button_candidate.last_joystick == joystick) + guide_button_candidate.last_joystick = NULL; + } + } else { + /* someone else also possibly correlated to this device, start over */ + new_correlation_count = 1; + } + } else { + /* new possible correlation */ + new_correlation_count = 1; + ctx->xinput_slot = slot_idx; + } + ctx->xinput_correlation_id = correlation_id; + } else { + /* Match multiple XInput devices, or none (possibly due to no buttons pressed) */ + } + } + ctx->xinput_correlation_count = new_correlation_count; + } else { + correlated = SDL_TRUE; + } + } +#endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */ + + /* Poll for trigger data once (not per-state-packet) */ +#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT + /* Prefer XInput over WindowsGamingInput, it continues to provide data in the background */ + if (!has_trigger_data && ctx->xinput_enabled && ctx->xinput_correlated) { + RAWINPUT_UpdateXInput(); + if (xinput_state[ctx->xinput_slot].connected) { + if (ctx->guide_hack) { + SDL_PrivateJoystickButton(joystick, guide_button, (xinput_state[ctx->xinput_slot].state.Gamepad.wButtons & XINPUT_GAMEPAD_GUIDE) ? SDL_PRESSED : SDL_RELEASED); + } + if (ctx->trigger_hack) { + SDL_PrivateJoystickAxis(joystick, left_trigger, ((int)xinput_state[ctx->xinput_slot].state.Gamepad.bLeftTrigger * 257) - 32768); + SDL_PrivateJoystickAxis(joystick, right_trigger, ((int)xinput_state[ctx->xinput_slot].state.Gamepad.bRightTrigger * 257) - 32768); + } + has_trigger_data = SDL_TRUE; + } + } +#endif /* SDL_JOYSTICK_RAWINPUT_XINPUT */ + +#ifdef SDL_JOYSTICK_RAWINPUT_GAMING_INPUT + if (!has_trigger_data && ctx->wgi_correlated) { + RAWINPUT_UpdateWindowsGamingInput(); /* May detect disconnect / cause uncorrelation */ + if (ctx->wgi_correlated) { /* Still connected */ + struct __x_ABI_CWindows_CGaming_CInput_CGamepadReading *state = &ctx->wgi_slot->state; + + if (ctx->guide_hack) { + SDL_PrivateJoystickButton(joystick, guide_button, (state->Buttons & GamepadButtons_GUIDE) ? SDL_PRESSED : SDL_RELEASED); + } + if (ctx->trigger_hack) { + SDL_PrivateJoystickAxis(joystick, left_trigger, ((int)(state->LeftTrigger * SDL_MAX_UINT16)) - 32768); + SDL_PrivateJoystickAxis(joystick, right_trigger, ((int)(state->RightTrigger * SDL_MAX_UINT16)) - 32768); + } + has_trigger_data = SDL_TRUE; + } + } +#endif /* SDL_JOYSTICK_RAWINPUT_GAMING_INPUT */ + + if (!correlated) { + if (!guide_button_candidate.joystick || + (ctx->last_state_packet && ( + !guide_button_candidate.last_state_packet || + SDL_TICKS_PASSED(ctx->last_state_packet, guide_button_candidate.last_state_packet) + )) + ) { + guide_button_candidate.joystick = joystick; + guide_button_candidate.last_state_packet = ctx->last_state_packet; + } + } +#endif /* SDL_JOYSTICK_RAWINPUT_MATCHING */ +} + +static void +RAWINPUT_JoystickUpdate(SDL_Joystick *joystick) +{ + /* Ensure data messages have been pumped */ + RAWINPUT_PumpMessages(); + RAWINPUT_UpdateOtherAPIs(joystick); +} + +static void +RAWINPUT_JoystickClose(SDL_Joystick *joystick) +{ + RAWINPUT_DeviceContext *ctx = joystick->hwdata; + +#ifdef SDL_JOYSTICK_RAWINPUT_MATCHING + if (guide_button_candidate.joystick == joystick) + guide_button_candidate.joystick = NULL; + if (guide_button_candidate.last_joystick == joystick) + guide_button_candidate.last_joystick = NULL; +#endif + + if (ctx) { SDL_RAWINPUT_Device *device; - device = hwdata->device; +#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT + xinput_device_change = SDL_TRUE; + if (ctx->xinput_enabled) { + if (ctx->xinput_correlated) { + RAWINPUT_MarkXInputSlotFree(ctx->xinput_slot); + } + WIN_UnloadXInputDLL(); + } +#endif +#ifdef SDL_JOYSTICK_RAWINPUT_GAMING_INPUT + RAWINPUT_QuitWindowsGamingInput(ctx); +#endif + + device = ctx->device; if (device) { SDL_assert(device->joystick == joystick); - device->driver->CloseJoystick(&device->hiddevice, joystick); device->joystick = NULL; RAWINPUT_ReleaseDevice(device); } - SDL_free(hwdata); + SDL_free(ctx->data); + SDL_free(ctx->button_indices); + SDL_free(ctx->axis_indices); + SDL_free(ctx->hat_indices); + SDL_free(ctx); joystick->hwdata = NULL; } } @@ -702,6 +1723,7 @@ LRESULT RAWINPUT_WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) } } return 0; + case WM_INPUT: { Uint8 data[sizeof(RAWINPUTHEADER) + sizeof(RAWHID) + USB_PACKET_LENGTH]; @@ -711,10 +1733,9 @@ LRESULT RAWINPUT_WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) PRAWINPUT raw_input = (PRAWINPUT)data; SDL_RAWINPUT_Device *device = RAWINPUT_DeviceFromHandle(raw_input->header.hDevice); if (device) { - SDL_HIDAPI_DeviceDriver *driver = device->driver; SDL_Joystick *joystick = device->joystick; if (joystick) { - driver->HandleStatePacketFromRAWINPUT(&device->hiddevice, joystick, &raw_input->data.hid.bRawData[1], raw_input->data.hid.dwSizeHid - 1); + RAWINPUT_HandleStatePacket(joystick, raw_input->data.hid.bRawData, raw_input->data.hid.dwSizeHid); } } } @@ -734,7 +1755,7 @@ RAWINPUT_JoystickQuit(void) return; for (ii = 0; ii < SDL_arraysize(subscribed_devices); ii++) { - rid[ii].usUsagePage = USAGE_PAGE_GENERIC_DESKTOP; + rid[ii].usUsagePage = USB_USAGEPAGE_GENERIC_DESKTOP; rid[ii].usUsage = subscribed_devices[ii]; rid[ii].dwFlags = RIDEV_REMOVE; rid[ii].hwndTarget = NULL; @@ -748,6 +1769,8 @@ RAWINPUT_JoystickQuit(void) RAWINPUT_DelDevice(SDL_RAWINPUT_devices, SDL_FALSE); } + WIN_UnloadHIDDLL(); + SDL_RAWINPUT_numjoysticks = 0; SDL_RAWINPUT_inited = SDL_FALSE; diff --git a/src/joystick/windows/SDL_rawinputjoystick_c.h b/src/joystick/windows/SDL_rawinputjoystick_c.h index f95e0debe..a683364f9 100644 --- a/src/joystick/windows/SDL_rawinputjoystick_c.h +++ b/src/joystick/windows/SDL_rawinputjoystick_c.h @@ -20,6 +20,9 @@ */ #include "../../SDL_internal.h" +/* Return true if the RawInput driver is enabled */ +extern SDL_bool RAWINPUT_IsEnabled(); + /* Return true if a RawInput device is present and supported as a joystick */ extern SDL_bool RAWINPUT_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version); diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c index 1af18af72..90f018b84 100644 --- a/src/joystick/windows/SDL_windows_gaming_input.c +++ b/src/joystick/windows/SDL_windows_gaming_input.c @@ -26,6 +26,7 @@ #include "SDL_events.h" #include "../SDL_sysjoystick.h" #include "../hidapi/SDL_hidapijoystick_c.h" +#include "SDL_rawinputjoystick_c.h" #include "../../core/windows/SDL_windows.h" #define COBJMACROS @@ -247,6 +248,12 @@ static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdde } #endif +#ifdef SDL_JOYSTICK_RAWINPUT + if (!ignore_joystick && RAWINPUT_IsDevicePresent(vendor, product, version)) { + ignore_joystick = SDL_TRUE; + } +#endif + if (!ignore_joystick && SDL_DINPUT_JoystickPresent(vendor, product, version)) { ignore_joystick = SDL_TRUE; } diff --git a/src/joystick/windows/SDL_xinputjoystick.c b/src/joystick/windows/SDL_xinputjoystick.c index 72fa4391a..40ba799fa 100644 --- a/src/joystick/windows/SDL_xinputjoystick.c +++ b/src/joystick/windows/SDL_xinputjoystick.c @@ -65,6 +65,11 @@ SDL_XINPUT_JoystickInit(void) { s_bXInputEnabled = SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE); + if (RAWINPUT_IsEnabled()) { + /* The raw input driver handles more than 4 controllers, so prefer that when available */ + s_bXInputEnabled = SDL_FALSE; + } + if (s_bXInputEnabled && WIN_LoadXInputDLL() < 0) { s_bXInputEnabled = SDL_FALSE; /* oh well. */ } @@ -208,7 +213,7 @@ GuessXInputDevice(Uint8 userid, Uint16 *pVID, Uint16 *pPID, Uint16 *pVersion) } } SDL_free(devices); -#endif /* ifndef __WINRT__ */ +#endif /* !__WINRT__ */ /* The device wasn't in the raw HID device list, it's probably Bluetooth */ *pVID = 0x045e; /* Microsoft */