Better XInput detection code for DirectInput device enumeration.
This code is way faster than the Wbem code, and less ugly.
1.1 --- a/src/joystick/windows/SDL_dxjoystick.c Wed Aug 28 00:07:02 2013 -0400
1.2 +++ b/src/joystick/windows/SDL_dxjoystick.c Wed Aug 28 16:35:32 2013 -0400
1.3 @@ -46,33 +46,9 @@
1.4 #include "../../events/SDL_events_c.h"
1.5 #endif
1.6
1.7 -/* The latest version of mingw-w64 defines IID_IWbemLocator in wbemcli.h
1.8 - instead of declaring it like Visual Studio and other mingw32 compilers.
1.9 - So, we need to take care of this here before we define INITGUID.
1.10 -*/
1.11 -#ifdef __MINGW32__
1.12 -#define __IWbemLocator_INTERFACE_DEFINED__
1.13 -#endif /* __MINGW32__ */
1.14 -
1.15 #define INITGUID /* Only set here, if set twice will cause mingw32 to break. */
1.16 #include "SDL_dxjoystick_c.h"
1.17
1.18 -#ifdef __MINGW32__
1.19 -/* And now that we've included wbemcli.h we need to declare these interfaces */
1.20 -typedef struct IWbemLocatorVtbl {
1.21 - BEGIN_INTERFACE
1.22 - HRESULT (WINAPI *QueryInterface)(IWbemLocator *This,REFIID riid,void **ppvObject);
1.23 - ULONG (WINAPI *AddRef)(IWbemLocator *This);
1.24 - ULONG (WINAPI *Release)(IWbemLocator *This);
1.25 - HRESULT (WINAPI *ConnectServer)(IWbemLocator *This,const BSTR strNetworkResource,const BSTR strUser,const BSTR strPassword,const BSTR strLocale,LONG lSecurityFlags,const BSTR strAuthority,IWbemContext *pCtx,IWbemServices **ppNamespace);
1.26 - END_INTERFACE
1.27 -} IWbemLocatorVtbl;
1.28 -struct IWbemLocator {
1.29 - CONST_VTBL struct IWbemLocatorVtbl *lpVtbl;
1.30 -};
1.31 -#define IWbemLocator_ConnectServer(This,strNetworkResource,strUser,strPassword,strLocale,lSecurityFlags,strAuthority,pCtx,ppNamespace) (This)->lpVtbl->ConnectServer(This,strNetworkResource,strUser,strPassword,strLocale,lSecurityFlags,strAuthority,pCtx,ppNamespace)
1.32 -#endif /* __MINGW32__ */
1.33 -
1.34 #ifndef DIDFT_OPTIONAL
1.35 #define DIDFT_OPTIONAL 0x80000000
1.36 #endif
1.37 @@ -396,156 +372,75 @@
1.38 } \
1.39 }
1.40
1.41 -DEFINE_GUID(CLSID_WbemLocator, 0x4590f811,0x1d3a,0x11d0,0x89,0x1F,0x00,0xaa,0x00,0x4b,0x2e,0x24);
1.42 -DEFINE_GUID(IID_IWbemLocator, 0xdc12a687,0x737f,0x11cf,0x88,0x4d,0x00,0xaa,0x00,0x4b,0x2e,0x24);
1.43 +DEFINE_GUID(IID_ValveStreamingGamepad, MAKELONG( 0x28DE, 0x11FF ),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44);
1.44 +DEFINE_GUID(IID_X360WiredGamepad, MAKELONG( 0x045E, 0x02A1 ),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44);
1.45 +DEFINE_GUID(IID_X360WirelessGamepad, MAKELONG( 0x045E, 0x028E ),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44);
1.46
1.47 -DEFINE_GUID(IID_ValveStreamingGamepad, MAKELONG( 0x28DE, 0x11FF ),0x0000,0x0000,0x00,0x00,0x50,0x49,0x44,0x56,0x49,0x44);
1.48 +static PRAWINPUTDEVICELIST SDL_RawDevList = NULL;
1.49 +static UINT SDL_RawDevListCount = 0;
1.50
1.51 -/*-----------------------------------------------------------------------------
1.52 - *
1.53 - * code from MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx
1.54 - *
1.55 - * Enum each PNP device using WMI and check each device ID to see if it contains
1.56 - * "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it's an XInput device
1.57 - * Unfortunately this information can not be found by just using DirectInput
1.58 - *-----------------------------------------------------------------------------*/
1.59 -BOOL IsXInputDevice( const GUID* pGuidProductFromDirectInput )
1.60 +static SDL_bool
1.61 +SDL_IsXInputDevice( const GUID* pGuidProductFromDirectInput )
1.62 {
1.63 static const GUID *s_XInputProductGUID[] = {
1.64 - &IID_ValveStreamingGamepad
1.65 + &IID_ValveStreamingGamepad,
1.66 + &IID_X360WiredGamepad, /* Microsoft's wired X360 controller for Windows. */
1.67 + &IID_X360WirelessGamepad /* Microsoft's wireless X360 controller for Windows. */
1.68 };
1.69 - IWbemLocator* pIWbemLocator = NULL;
1.70 - IEnumWbemClassObject* pEnumDevices = NULL;
1.71 - IWbemClassObject* pDevices[20];
1.72 - IWbemServices* pIWbemServices = NULL;
1.73 - DWORD uReturned = 0;
1.74 - BSTR bstrNamespace = NULL;
1.75 - BSTR bstrDeviceID = NULL;
1.76 - BSTR bstrClassName = NULL;
1.77 - SDL_bool bIsXinputDevice= SDL_FALSE;
1.78 - UINT iDevice = 0;
1.79 - VARIANT var;
1.80 - HRESULT hr;
1.81 - DWORD bCleanupCOM;
1.82
1.83 - if (!s_bXInputEnabled)
1.84 - {
1.85 + size_t iDevice;
1.86 + SDL_bool retval = SDL_FALSE;
1.87 + UINT i;
1.88 +
1.89 + if (!s_bXInputEnabled) {
1.90 return SDL_FALSE;
1.91 }
1.92
1.93 /* Check for well known XInput device GUIDs */
1.94 - /* We need to do this for the Valve Streaming Gamepad because it's virtualized and doesn't show up in the device list. */
1.95 + /* This lets us skip RAWINPUT for popular devices. Also, we need to do this for the Valve Streaming Gamepad because it's virtualized and doesn't show up in the device list. */
1.96 for ( iDevice = 0; iDevice < SDL_arraysize(s_XInputProductGUID); ++iDevice ) {
1.97 if (SDL_memcmp(pGuidProductFromDirectInput, s_XInputProductGUID[iDevice], sizeof(GUID)) == 0) {
1.98 return SDL_TRUE;
1.99 }
1.100 }
1.101
1.102 - SDL_memset( pDevices, 0x0, sizeof(pDevices) );
1.103 + /* Go through RAWINPUT (WinXP and later) to find HID devices. */
1.104 + /* Cache this if we end up using it. */
1.105 + if (SDL_RawDevList == NULL) {
1.106 + if ((GetRawInputDeviceList(NULL, &SDL_RawDevListCount, sizeof (RAWINPUTDEVICELIST)) == -1) || (!SDL_RawDevListCount)) {
1.107 + return SDL_FALSE; /* oh well. */
1.108 + }
1.109
1.110 - /* CoInit if needed */
1.111 - hr = CoInitialize(NULL);
1.112 - bCleanupCOM = SUCCEEDED(hr);
1.113 + SDL_RawDevList = (PRAWINPUTDEVICELIST) SDL_malloc(sizeof (RAWINPUTDEVICELIST) * SDL_RawDevListCount);
1.114 + if (SDL_RawDevList == NULL) {
1.115 + SDL_OutOfMemory();
1.116 + return SDL_FALSE;
1.117 + }
1.118
1.119 - /* Create WMI */
1.120 - hr = CoCreateInstance( &CLSID_WbemLocator,
1.121 - NULL,
1.122 - CLSCTX_INPROC_SERVER,
1.123 - &IID_IWbemLocator,
1.124 - (LPVOID*) &pIWbemLocator);
1.125 - if( FAILED(hr) || pIWbemLocator == NULL )
1.126 - goto LCleanup;
1.127 -
1.128 - bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup;
1.129 - bstrClassName = SysAllocString( L"Win32_PNPEntity" ); if( bstrClassName == NULL ) goto LCleanup;
1.130 - bstrDeviceID = SysAllocString( L"DeviceID" ); if( bstrDeviceID == NULL ) goto LCleanup;
1.131 -
1.132 - /* Connect to WMI */
1.133 - hr = IWbemLocator_ConnectServer( pIWbemLocator, bstrNamespace, NULL, NULL, 0L,
1.134 - 0L, NULL, NULL, &pIWbemServices );
1.135 - if( FAILED(hr) || pIWbemServices == NULL )
1.136 - goto LCleanup;
1.137 -
1.138 - /* Switch security level to IMPERSONATE. */
1.139 - CoSetProxyBlanket( (IUnknown *)pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
1.140 - RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE );
1.141 -
1.142 - hr = IWbemServices_CreateInstanceEnum( pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices );
1.143 - if( FAILED(hr) || pEnumDevices == NULL )
1.144 - goto LCleanup;
1.145 -
1.146 - /* Loop over all devices */
1.147 - for( ;; )
1.148 - {
1.149 - /* Get 20 at a time */
1.150 - hr = IEnumWbemClassObject_Next( pEnumDevices, 10000, 20, pDevices, &uReturned );
1.151 - if( FAILED(hr) )
1.152 - goto LCleanup;
1.153 - if( uReturned == 0 )
1.154 - break;
1.155 -
1.156 - for( iDevice=0; iDevice<uReturned; iDevice++ )
1.157 - {
1.158 - /* For each device, get its device ID */
1.159 - hr = IWbemClassObject_Get( pDevices[iDevice], bstrDeviceID, 0L, &var, NULL, NULL );
1.160 - if( SUCCEEDED( hr ) && var.vt == VT_BSTR && var.bstrVal != NULL )
1.161 - {
1.162 - /* Check if the device ID contains "IG_". If it does, then it's an XInput device */
1.163 - /* This information can not be found from DirectInput */
1.164 - char *pDeviceString = WIN_StringToUTF8( var.bstrVal );
1.165 - if( SDL_strstr( pDeviceString, "IG_" ) )
1.166 - {
1.167 - /* If it does, then get the VID/PID from var.bstrVal */
1.168 - long dwPid = 0, dwVid = 0;
1.169 - char * strPid = NULL;
1.170 - DWORD dwVidPid = 0;
1.171 - char * strVid = SDL_strstr( pDeviceString, "VID_" );
1.172 - if( strVid )
1.173 - {
1.174 - dwVid = SDL_strtol( strVid + 4, NULL, 16 );
1.175 - }
1.176 - strPid = SDL_strstr( pDeviceString, "PID_" );
1.177 - if( strPid )
1.178 - {
1.179 - dwPid = SDL_strtol( strPid + 4, NULL, 16 );
1.180 - }
1.181 -
1.182 - /* Compare the VID/PID to the DInput device */
1.183 - dwVidPid = MAKELONG( dwVid, dwPid );
1.184 - if( dwVidPid == pGuidProductFromDirectInput->Data1 )
1.185 - {
1.186 - bIsXinputDevice = SDL_TRUE;
1.187 - }
1.188 - }
1.189 - if ( pDeviceString )
1.190 - SDL_free( pDeviceString );
1.191 -
1.192 - if ( bIsXinputDevice )
1.193 - break;
1.194 - }
1.195 - SAFE_RELEASE( pDevices[iDevice] );
1.196 + if (GetRawInputDeviceList(SDL_RawDevList, &SDL_RawDevListCount, sizeof (RAWINPUTDEVICELIST)) == -1) {
1.197 + SDL_free(SDL_RawDevList);
1.198 + SDL_RawDevList = NULL;
1.199 + return SDL_FALSE; /* oh well. */
1.200 }
1.201 }
1.202
1.203 -LCleanup:
1.204 + for (i = 0; i < SDL_RawDevListCount; i++) {
1.205 + RID_DEVICE_INFO rdi;
1.206 + char devName[128];
1.207 + UINT rdiSize = sizeof (rdi);
1.208 + UINT nameSize = SDL_arraysize(devName);
1.209
1.210 - for( iDevice=0; iDevice<20; iDevice++ )
1.211 - SAFE_RELEASE( pDevices[iDevice] );
1.212 - SAFE_RELEASE( pEnumDevices );
1.213 - SAFE_RELEASE( pIWbemLocator );
1.214 - SAFE_RELEASE( pIWbemServices );
1.215 + rdi.cbSize = sizeof (rdi);
1.216 + if ( (SDL_RawDevList[i].dwType == RIM_TYPEHID) &&
1.217 + (GetRawInputDeviceInfoA(SDL_RawDevList[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != ((UINT)-1)) &&
1.218 + (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == ((LONG)pGuidProductFromDirectInput->Data1)) &&
1.219 + (GetRawInputDeviceInfoA(SDL_RawDevList[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) != ((UINT)-1)) &&
1.220 + (SDL_strstr(devName, "IG_") != NULL) ) {
1.221 + return SDL_TRUE;
1.222 + }
1.223 + }
1.224
1.225 - if ( bstrNamespace )
1.226 - SysFreeString( bstrNamespace );
1.227 - if ( bstrClassName )
1.228 - SysFreeString( bstrClassName );
1.229 - if ( bstrDeviceID )
1.230 - SysFreeString( bstrDeviceID );
1.231 -
1.232 - if( bCleanupCOM )
1.233 - CoUninitialize();
1.234 -
1.235 - return bIsXinputDevice;
1.236 + return SDL_FALSE;
1.237 }
1.238
1.239
1.240 @@ -808,7 +703,7 @@
1.241
1.242 s_bDeviceAdded = SDL_TRUE;
1.243
1.244 - bXInputDevice = IsXInputDevice( &pdidInstance->guidProduct );
1.245 + bXInputDevice = SDL_IsXInputDevice( &pdidInstance->guidProduct );
1.246
1.247 pNewJoystick = (JoyStick_DeviceData *)SDL_malloc( sizeof(JoyStick_DeviceData) );
1.248
1.249 @@ -872,6 +767,9 @@
1.250 EnumJoysticksCallback,
1.251 &pCurList, DIEDFL_ATTACHEDONLY);
1.252
1.253 + SDL_free(SDL_RawDevList); /* in case we used this. */
1.254 + SDL_RawDevList = NULL;
1.255 +
1.256 SDL_UnlockMutex( s_mutexJoyStickEnum );
1.257 }
1.258