• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 The zlib/libpng License
3 
4 Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
5 
6 This software is provided 'as-is', without any express or implied warranty. In no event will
7 the authors be held liable for any damages arising from the use of this software.
8 
9 Permission is granted to anyone to use this software for any purpose, including commercial
10 applications, and to alter it and redistribute it freely, subject to the following
11 restrictions:
12 
13     1. The origin of this software must not be misrepresented; you must not claim that
14 		you wrote the original software. If you use this software in a product,
15 		an acknowledgment in the product documentation would be appreciated but is
16 		not required.
17 
18     2. Altered source versions must be plainly marked as such, and must not be
19 		misrepresented as being the original software.
20 
21     3. This notice may not be removed or altered from any source distribution.
22 */
23 #include "win32/Win32JoyStick.h"
24 #include "win32/Win32InputManager.h"
25 #include "win32/Win32ForceFeedback.h"
26 #include "OISEvents.h"
27 #include "OISException.h"
28 
29 #include <cassert>
30 
31 // Only if xinput support is enabled
32 #ifdef OIS_WIN32_XINPUT_SUPPORT
33 #include <wbemidl.h>
34 #include <oleauto.h>
35 //#include <wmsstd.h>
36 #ifndef SAFE_RELEASE
37 #define SAFE_RELEASE(x) \
38    if(x != NULL)        \
39    {                    \
40       x->Release();     \
41       x = NULL;         \
42    }
43 #endif
44 
45 #pragma comment(lib, "xinput.lib")
46 #endif
47 
48 //DX Only defines macros for the JOYSTICK not JOYSTICK2, so fix it
49 #undef DIJOFS_BUTTON
50 #undef DIJOFS_POV
51 
52 #define DIJOFS_BUTTON(n)  (FIELD_OFFSET(DIJOYSTATE2, rgbButtons) + (n))
53 #define DIJOFS_POV(n)     (FIELD_OFFSET(DIJOYSTATE2, rgdwPOV)+(n)*sizeof(DWORD))
54 #define DIJOFS_SLIDER0(n) (FIELD_OFFSET(DIJOYSTATE2, rglSlider)+(n) * sizeof(LONG))
55 #define DIJOFS_SLIDER1(n) (FIELD_OFFSET(DIJOYSTATE2, rglVSlider)+(n) * sizeof(LONG))
56 #define DIJOFS_SLIDER2(n) (FIELD_OFFSET(DIJOYSTATE2, rglASlider)+(n) * sizeof(LONG))
57 #define DIJOFS_SLIDER3(n) (FIELD_OFFSET(DIJOYSTATE2, rglFSlider)+(n) * sizeof(LONG))
58 
59 #define XINPUT_TRANSLATED_BUTTON_COUNT 12
60 #define XINPUT_TRANSLATED_AXIS_COUNT 6
61 
62 using namespace OIS;
63 
64 //--------------------------------------------------------------------------------------------------//
Win32JoyStick(InputManager * creator,IDirectInput8 * pDI,bool buffered,DWORD coopSettings,const JoyStickInfo & info)65 Win32JoyStick::Win32JoyStick( InputManager* creator, IDirectInput8* pDI, bool buffered, DWORD coopSettings, const JoyStickInfo &info ) :
66 	JoyStick(info.vendor, buffered, info.devId, creator),
67 	mDirectInput(pDI),
68 	coopSetting(coopSettings),
69 	mJoyStick(0),
70 	mJoyInfo(info),
71 	mFfDevice(0)
72 {
73 }
74 
75 //--------------------------------------------------------------------------------------------------//
~Win32JoyStick()76 Win32JoyStick::~Win32JoyStick()
77 {
78 	delete mFfDevice;
79 
80 	if(mJoyStick)
81 	{
82 		mJoyStick->Unacquire();
83 		mJoyStick->Release();
84 		mJoyStick = 0;
85 	}
86 
87 	//Return joystick to pool
88 	static_cast<Win32InputManager*>(mCreator)->_returnJoyStick(mJoyInfo);
89 }
90 
91 //--------------------------------------------------------------------------------------------------//
_initialize()92 void Win32JoyStick::_initialize()
93 {
94     if (mJoyInfo.isXInput)
95     {
96         _enumerate();
97     }
98     else
99     {
100 	    //Clear old state
101 	    mState.mAxes.clear();
102 
103 	    delete mFfDevice;
104 	    mFfDevice = 0;
105 
106 	    DIPROPDWORD dipdw;
107 
108 	    dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
109 	    dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
110 	    dipdw.diph.dwObj        = 0;
111 	    dipdw.diph.dwHow        = DIPH_DEVICE;
112 	    dipdw.dwData            = JOYSTICK_DX_BUFFERSIZE;
113 
114 	    if(FAILED(mDirectInput->CreateDevice(mJoyInfo.deviceID, &mJoyStick, NULL)))
115 		    OIS_EXCEPT( E_General, "Win32JoyStick::_initialize() >> Could not initialize joy device!");
116 
117 	    if(FAILED(mJoyStick->SetDataFormat(&c_dfDIJoystick2)))
118 		    OIS_EXCEPT( E_General, "Win32JoyStick::_initialize() >> data format error!");
119 
120 	    HWND hwin = ((Win32InputManager*)mCreator)->getWindowHandle();
121 
122 	    if(FAILED(mJoyStick->SetCooperativeLevel( hwin, coopSetting)))
123 		    OIS_EXCEPT( E_General, "Win32JoyStick::_initialize() >> failed to set cooperation level!");
124 
125 	    if( FAILED(mJoyStick->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)) )
126 		    OIS_EXCEPT( E_General, "Win32Mouse::Win32Mouse >> Failed to set buffer size property" );
127 
128 	    //Enumerate all axes/buttons/sliders/etc before aquiring
129 	    _enumerate();
130 
131 	    mState.clear();
132 
133 	    capture();
134     }
135 }
136 
137 //--------------------------------------------------------------------------------------------------//
_enumerate()138 void Win32JoyStick::_enumerate()
139 {
140     if (mJoyInfo.isXInput)
141     {
142         mPOVs = 1;
143 
144         mState.mButtons.resize(XINPUT_TRANSLATED_BUTTON_COUNT);
145 	    mState.mAxes.resize(XINPUT_TRANSLATED_AXIS_COUNT);
146     }
147     else
148     {
149 		// Get joystick capabilities.
150 		mDIJoyCaps.dwSize = sizeof(DIDEVCAPS);
151 		if( FAILED(mJoyStick->GetCapabilities(&mDIJoyCaps)) )
152 			OIS_EXCEPT( E_General, "Win32JoyStick::_enumerate >> Failed to get capabilities" );
153 
154 	    mPOVs = (short)mDIJoyCaps.dwPOVs;
155 
156 	    mState.mButtons.resize(mDIJoyCaps.dwButtons);
157 	    mState.mAxes.resize(mDIJoyCaps.dwAxes);
158 
159 	    //Reset the axis mapping enumeration value
160 	    _AxisNumber = 0;
161 
162 	    //Enumerate Force Feedback (if any)
163 	    mJoyStick->EnumEffects(DIEnumEffectsCallback, this, DIEFT_ALL);
164 
165 	    //Enumerate and set axis constraints (and check FF Axes)
166 	    mJoyStick->EnumObjects(DIEnumDeviceObjectsCallback, this, DIDFT_AXIS);
167     }
168 }
169 
170 //--------------------------------------------------------------------------------------------------//
DIEnumDeviceObjectsCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi,LPVOID pvRef)171 BOOL CALLBACK Win32JoyStick::DIEnumDeviceObjectsCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef)
172 {
173 	Win32JoyStick* _this = (Win32JoyStick*)pvRef;
174 
175 	//Setup mappings
176 	DIPROPPOINTER diptr;
177 	diptr.diph.dwSize       = sizeof(DIPROPPOINTER);
178 	diptr.diph.dwHeaderSize = sizeof(DIPROPHEADER);
179 	diptr.diph.dwHow        = DIPH_BYID;
180 	diptr.diph.dwObj        = lpddoi->dwType;
181 	//Add a magic number to recognise we set seomthing
182 	diptr.uData             = 0x13130000 | _this->_AxisNumber;
183 
184 	//Check if axis is slider, if so, do not treat as regular axis
185 	if(GUID_Slider == lpddoi->guidType)
186 	{
187 		++_this->mSliders;
188 
189 		//Decrease Axes, since this slider shows up in a different place
190 		_this->mState.mAxes.pop_back();
191 	}
192 	else if (FAILED(_this->mJoyStick->SetProperty(DIPROP_APPDATA, &diptr.diph)))
193 	{	//If for some reason we could not set needed user data, just ignore this axis
194 		return DIENUM_CONTINUE;
195 	}
196 
197 	//Increase for next time through
198 	if(GUID_Slider != lpddoi->guidType)
199 		_this->_AxisNumber += 1;
200 
201 	//Set range
202 	DIPROPRANGE diprg;
203 	diprg.diph.dwSize       = sizeof(DIPROPRANGE);
204 	diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
205 	diprg.diph.dwHow        = DIPH_BYID;
206 	diprg.diph.dwObj        = lpddoi->dwType;
207 	diprg.lMin              = MIN_AXIS;
208 	diprg.lMax              = MAX_AXIS;
209 
210 	if (FAILED(_this->mJoyStick->SetProperty(DIPROP_RANGE, &diprg.diph)))
211 		OIS_EXCEPT( E_General, "Win32JoyStick::_DIEnumDeviceObjectsCallback >> Failed to set min/max range property" );
212 
213 	//Check if FF Axes, and if so, increment counter
214 	if((lpddoi->dwFlags & DIDOI_FFACTUATOR) != 0 )
215 	{
216 		if( _this->mFfDevice )
217 		{
218 			_this->mFfDevice->_addFFAxis();
219 		}
220 	}
221 
222 	//Force the flags for gain and auto-center support to true,
223 	//as DInput has no API to query the device for these capabilities
224 	//(the only way to know is to try them ...)
225 	if( _this->mFfDevice )
226 	{
227 	    _this->mFfDevice->_setGainSupport(true);
228 	    _this->mFfDevice->_setAutoCenterSupport(true);
229 	}
230 
231 	return DIENUM_CONTINUE;
232 }
233 
234 //--------------------------------------------------------------------------------------------------//
DIEnumEffectsCallback(LPCDIEFFECTINFO pdei,LPVOID pvRef)235 BOOL CALLBACK Win32JoyStick::DIEnumEffectsCallback(LPCDIEFFECTINFO pdei, LPVOID pvRef)
236 {
237 	Win32JoyStick* _this = (Win32JoyStick*)pvRef;
238 
239 	//Create the FF instance only after we know there is at least one effect type
240 	if( _this->mFfDevice == 0 )
241 		_this->mFfDevice = new Win32ForceFeedback(_this->mJoyStick, &_this->mDIJoyCaps);
242 
243 	_this->mFfDevice->_addEffectSupport(pdei);
244 
245 	return DIENUM_CONTINUE;
246 }
247 
248 //--------------------------------------------------------------------------------------------------//
capture()249 void Win32JoyStick::capture()
250 {
251 #ifdef OIS_WIN32_XINPUT_SUPPORT
252 	//handle xbox controller differently
253     if (mJoyInfo.isXInput)
254 	{
255 		captureXInput();
256 		return;
257 	}
258 #endif
259 
260 	//handle directinput based devices
261 	DIDEVICEOBJECTDATA diBuff[JOYSTICK_DX_BUFFERSIZE];
262 	DWORD entries = JOYSTICK_DX_BUFFERSIZE;
263 
264 	// Poll the device to read the current state
265 	HRESULT hr = mJoyStick->Poll();
266 	if( hr == DI_OK )
267 		hr = mJoyStick->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), diBuff, &entries, 0 );
268 
269 	if( hr != DI_OK )
270 	{
271 		hr = mJoyStick->Acquire();
272 		while( hr == DIERR_INPUTLOST )
273 			hr = mJoyStick->Acquire();
274 
275 		// Poll the device to read the current state
276 		mJoyStick->Poll();
277 		hr = mJoyStick->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), diBuff, &entries, 0 );
278 		//Perhaps the user just tabbed away
279 		if( FAILED(hr) )
280 			return;
281 	}
282 
283 	bool axisMoved[24] = {false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,
284 						  false,false,false,false,false,false,false,false};
285 	bool sliderMoved[4] = {false,false,false,false};
286 
287 	//Loop through all the events
288 	for(unsigned int i = 0; i < entries; ++i)
289 	{
290 		//This may seem outof order, but is in order of the way these variables
291 		//are declared in the JoyStick State 2 structure.
292 		switch(diBuff[i].dwOfs)
293 		{
294 		//------ slider -//
295 		case DIJOFS_SLIDER0(0):
296 			sliderMoved[0] = true;
297 			mState.mSliders[0].abX = diBuff[i].dwData;
298 			break;
299 		case DIJOFS_SLIDER0(1):
300 			sliderMoved[0] = true;
301 			mState.mSliders[0].abY = diBuff[i].dwData;
302 			break;
303 		//----- Max 4 POVs Next ---------------//
304 		case DIJOFS_POV(0):
305 			if(!_changePOV(0,diBuff[i]))
306 				return;
307 			break;
308 		case DIJOFS_POV(1):
309 			if(!_changePOV(1,diBuff[i]))
310 				return;
311 			break;
312 		case DIJOFS_POV(2):
313 			if(!_changePOV(2,diBuff[i]))
314 				return;
315 			break;
316 		case DIJOFS_POV(3):
317 			if(!_changePOV(3,diBuff[i]))
318 				return;
319 			break;
320 		case DIJOFS_SLIDER1(0):
321 			sliderMoved[1] = true;
322 			mState.mSliders[1].abX = diBuff[i].dwData;
323 			break;
324 		case DIJOFS_SLIDER1(1):
325 			sliderMoved[1] = true;
326 			mState.mSliders[1].abY = diBuff[i].dwData;
327 			break;
328 		case DIJOFS_SLIDER2(0):
329 			sliderMoved[2] = true;
330 			mState.mSliders[2].abX = diBuff[i].dwData;
331 			break;
332 		case DIJOFS_SLIDER2(1):
333 			sliderMoved[2] = true;
334 			mState.mSliders[2].abY = diBuff[i].dwData;
335 			break;
336 		case DIJOFS_SLIDER3(0):
337 			sliderMoved[3] = true;
338 			mState.mSliders[3].abX = diBuff[i].dwData;
339 			break;
340 		case DIJOFS_SLIDER3(1):
341 			sliderMoved[3] = true;
342 			mState.mSliders[3].abY = diBuff[i].dwData;
343 			break;
344 		//-----------------------------------------//
345 		default:
346 			//Handle Button Events Easily using the DX Offset Macros
347 			if( diBuff[i].dwOfs >= DIJOFS_BUTTON(0) && diBuff[i].dwOfs < DIJOFS_BUTTON(128) )
348 			{
349 				if(!_doButtonClick((diBuff[i].dwOfs - DIJOFS_BUTTON(0)), diBuff[i]))
350 					return;
351 			}
352 			else if((short)(diBuff[i].uAppData >> 16) == 0x1313)
353 			{	//If it was nothing else, might be axis enumerated earlier (determined by magic number)
354 				int axis = (int)(0x0000FFFF & diBuff[i].uAppData); //Mask out the high bit
355 				assert( axis >= 0 && axis < (int)mState.mAxes.size() && "Axis out of range!");
356 
357 				if(axis >= 0 && axis < (int)mState.mAxes.size())
358 				{
359 					mState.mAxes[axis].abs = diBuff[i].dwData;
360 					axisMoved[axis] = true;
361 				}
362 			}
363 
364 			break;
365 		} //end case
366 	} //end for
367 
368 	//Check to see if any of the axes values have changed.. if so send events
369 	if( mBuffered && mListener && entries > 0 )
370 	{
371 		JoyStickEvent temp(this, mState);
372 
373 		//Update axes
374 		for( int i = 0; i < 24; ++i )
375 			if( axisMoved[i] )
376 				if( mListener->axisMoved( temp, i ) == false )
377 					return;
378 
379 		//Now update sliders
380 		for( int i = 0; i < 4; ++i )
381 			if( sliderMoved[i] )
382 				if( mListener->sliderMoved( temp, i ) == false )
383 					return;
384 	}
385 }
386 
387 //--------------------------------------------------------------------------------------------------//
captureXInput()388 void Win32JoyStick::captureXInput()
389 {
390 #ifdef OIS_WIN32_XINPUT_SUPPORT
391     XINPUT_STATE inputState;
392 	if (XInputGetState((DWORD)mJoyInfo.xInputDev, &inputState) != ERROR_SUCCESS)
393         memset(&inputState, 0, sizeof(inputState));
394 
395     //Sticks and triggers
396 	int value;
397     bool axisMoved[XINPUT_TRANSLATED_AXIS_COUNT] = {false,false,false,false,false,false};
398 
399 	//LeftY
400 	value = -(int)inputState.Gamepad.sThumbLY;
401 	mState.mAxes[0].rel = value - mState.mAxes[0].abs;
402 	mState.mAxes[0].abs = value;
403 	if(mState.mAxes[0].rel != 0)
404         axisMoved[0] = true;
405 
406 	//LeftX
407     mState.mAxes[1].rel = inputState.Gamepad.sThumbLX - mState.mAxes[1].abs;
408     mState.mAxes[1].abs = inputState.Gamepad.sThumbLX;
409 
410 	if(mState.mAxes[1].rel != 0)
411         axisMoved[1] = true;
412 
413 	//RightY
414 	value = -(int)inputState.Gamepad.sThumbRY;
415     mState.mAxes[2].rel = value - mState.mAxes[2].abs;
416     mState.mAxes[2].abs = value;
417 	if(mState.mAxes[2].rel != 0)
418         axisMoved[2] = true;
419 
420 	//RightX
421     mState.mAxes[3].rel = inputState.Gamepad.sThumbRX - mState.mAxes[3].abs;
422     mState.mAxes[3].abs = inputState.Gamepad.sThumbRX;
423 	if(mState.mAxes[3].rel != 0)
424 		axisMoved[3] = true;
425 
426 	//Left trigger
427     value = inputState.Gamepad.bLeftTrigger * 129;
428 	if(value > JoyStick::MAX_AXIS)
429 		value = JoyStick::MAX_AXIS;
430 
431     mState.mAxes[4].rel = value - mState.mAxes[4].abs;
432     mState.mAxes[4].abs = value;
433 	if(mState.mAxes[4].rel != 0)
434 		axisMoved[4] = true;
435 
436 	//Right trigger
437     value = (int)inputState.Gamepad.bRightTrigger * 129;
438 	if(value > JoyStick::MAX_AXIS)
439 		value = JoyStick::MAX_AXIS;
440 
441 	mState.mAxes[5].rel = value - mState.mAxes[5].abs;
442     mState.mAxes[5].abs = value;
443 	if(mState.mAxes[5].rel != 0)
444 		axisMoved[5] = true;
445 
446     //POV
447     int previousPov = mState.mPOV[0].direction;
448     int& pov = mState.mPOV[0].direction;
449     pov = Pov::Centered;
450     if (inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP)
451         pov |= Pov::North;
452     else if (inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN)
453         pov |= Pov::South;
454     if (inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT)
455         pov |= Pov::West;
456     else if (inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT)
457         pov |= Pov::East;
458 
459     //Buttons - The first 4 buttons don't need to be checked since they represent the dpad
460     bool previousButtons[XINPUT_TRANSLATED_BUTTON_COUNT];
461     std::copy(mState.mButtons.begin(), mState.mButtons.end(), previousButtons);
462     for (size_t i = 0; i < XINPUT_TRANSLATED_BUTTON_COUNT; i++)
463         mState.mButtons[i] = (inputState.Gamepad.wButtons & (1 << (i + 4))) != 0;
464 
465     //Send events
466     if (mBuffered && mListener)
467     {
468 	    JoyStickEvent joystickEvent(this, mState);
469 
470 	    //Axes
471 	    for (int i = 0; i < XINPUT_TRANSLATED_AXIS_COUNT; i++)
472         {
473 		    if (axisMoved[i] && !mListener->axisMoved(joystickEvent, i))
474 			    return;
475         }
476 
477         //POV
478         if (previousPov != pov && !mListener->povMoved(joystickEvent, 0))
479             return;
480 
481         //Buttons
482         for (int i = 0; i < XINPUT_TRANSLATED_BUTTON_COUNT; i++)
483         {
484             if (!previousButtons[i] && mState.mButtons[i])
485             {
486                 if (!mListener->buttonPressed(joystickEvent, i))
487                     return;
488             }
489             else if (previousButtons[i] && !mState.mButtons[i])
490             {
491                 if (!mListener->buttonReleased(joystickEvent, i))
492                     return;
493             }
494         }
495     }
496 #endif
497 }
498 
499 //--------------------------------------------------------------------------------------------------//
_doButtonClick(int button,DIDEVICEOBJECTDATA & di)500 bool Win32JoyStick::_doButtonClick( int button, DIDEVICEOBJECTDATA& di )
501 {
502 	if( di.dwData & 0x80 )
503 	{
504 		mState.mButtons[button] = true;
505 		if( mBuffered && mListener )
506 			return mListener->buttonPressed( JoyStickEvent( this, mState ), button );
507 	}
508 	else
509 	{
510 		mState.mButtons[button] = false;
511 		if( mBuffered && mListener )
512 			return mListener->buttonReleased( JoyStickEvent( this, mState ), button );
513 	}
514 
515 	return true;
516 }
517 
518 //--------------------------------------------------------------------------------------------------//
_changePOV(int pov,DIDEVICEOBJECTDATA & di)519 bool Win32JoyStick::_changePOV( int pov, DIDEVICEOBJECTDATA& di )
520 {
521 	//Some drivers report a value of 65,535, instead of �1,
522 	//for the center position
523 	if(LOWORD(di.dwData) == 0xFFFF)
524 	{
525 		mState.mPOV[pov].direction = Pov::Centered;
526 	}
527 	else
528 	{
529 		switch(di.dwData)
530 		{
531 			case 0: mState.mPOV[pov].direction = Pov::North; break;
532 			case 4500: mState.mPOV[pov].direction = Pov::NorthEast; break;
533 			case 9000: mState.mPOV[pov].direction = Pov::East; break;
534 			case 13500: mState.mPOV[pov].direction = Pov::SouthEast; break;
535 			case 18000: mState.mPOV[pov].direction = Pov::South; break;
536 			case 22500: mState.mPOV[pov].direction = Pov::SouthWest; break;
537 			case 27000: mState.mPOV[pov].direction = Pov::West; break;
538 			case 31500: mState.mPOV[pov].direction = Pov::NorthWest; break;
539 		}
540 	}
541 
542 	if( mBuffered && mListener )
543 		return mListener->povMoved( JoyStickEvent( this, mState ), pov );
544 
545 	return true;
546 }
547 
548 //--------------------------------------------------------------------------------------------------//
setBuffered(bool buffered)549 void Win32JoyStick::setBuffered(bool buffered)
550 {
551 	mBuffered = buffered;
552 }
553 
554 //--------------------------------------------------------------------------------------------------//
queryInterface(Interface::IType type)555 Interface* Win32JoyStick::queryInterface(Interface::IType type)
556 {
557 	if( mFfDevice && type == Interface::ForceFeedback )
558 		return mFfDevice;
559 	else
560 		return 0;
561 }
562 
563 //--------------------------------------------------------------------------------------------------//
564 #ifdef OIS_WIN32_XINPUT_SUPPORT
CheckXInputDevices(JoyStickInfoList & joys)565 void Win32JoyStick::CheckXInputDevices(JoyStickInfoList &joys)
566 {
567     IWbemLocator*           pIWbemLocator  = NULL;
568     IEnumWbemClassObject*   pEnumDevices   = NULL;
569     IWbemClassObject*       pDevices[20]   = {0};
570     IWbemServices*          pIWbemServices = NULL;
571     BSTR                    bstrNamespace  = NULL;
572     BSTR                    bstrDeviceID   = NULL;
573     BSTR                    bstrClassName  = NULL;
574     DWORD                   uReturned      = 0;
575     bool                    bIsXinputDevice= false;
576 	DWORD                   iDevice        = 0;
577 	int                     xDevice        = 0;
578     VARIANT                 var;
579     HRESULT                 hr;
580 
581 	if(joys.size() == 0)
582 		return;
583 
584     // CoInit if needed
585     hr = CoInitialize(NULL);
586     bool bCleanupCOM = SUCCEEDED(hr);
587 
588     // Create WMI
589     hr = CoCreateInstance(__uuidof(WbemLocator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*)&pIWbemLocator);
590     if( FAILED(hr) || pIWbemLocator == NULL )
591         goto LCleanup;
592 
593     bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );
594 	if( bstrNamespace == NULL )
595 		goto LCleanup;
596 
597     bstrClassName = SysAllocString( L"Win32_PNPEntity" );
598 	if( bstrClassName == NULL )
599 		goto LCleanup;
600 
601     bstrDeviceID  = SysAllocString( L"DeviceID" );
602 	if( bstrDeviceID == NULL )
603 		goto LCleanup;
604 
605     // Connect to WMI
606     hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices );
607     if( FAILED(hr) || pIWbemServices == NULL )
608         goto LCleanup;
609 
610     // Switch security level to IMPERSONATE.
611     CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE );
612 
613     hr = pIWbemServices->CreateInstanceEnum( bstrClassName, 0, NULL, &pEnumDevices );
614     if( FAILED(hr) || pEnumDevices == NULL )
615         goto LCleanup;
616 
617     // Loop over all devices
618     for( ;; )
619     {
620         // Get 20 at a time
621         hr = pEnumDevices->Next(5000, 20, pDevices, &uReturned);
622         if( FAILED(hr) )
623             goto LCleanup;
624 
625         if( uReturned == 0 )
626             break;
627 
628         for(iDevice = 0; iDevice < uReturned; iDevice++)
629         {
630             // For each device, get its device ID
631             hr = pDevices[iDevice]->Get(bstrDeviceID, 0L, &var, NULL, NULL);
632             if(SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL)
633             {
634                 // Check if the device ID contains "IG_".  If it does, then it's an XInput device - This information can not be found from DirectInput
635                 if(wcsstr(var.bstrVal, L"IG_"))
636                 {
637                     // If it does, then get the VID/PID from var.bstrVal
638                     DWORD dwPid = 0, dwVid = 0;
639                     WCHAR* strVid = wcsstr( var.bstrVal, L"VID_" );
640                     if(strVid && swscanf_s( strVid, L"VID_%4X", &dwVid ) != 1)
641 						dwVid = 0;
642 
643                     WCHAR* strPid = wcsstr( var.bstrVal, L"PID_" );
644                     if(strPid && swscanf_s( strPid, L"PID_%4X", &dwPid ) != 1)
645                         dwPid = 0;
646 
647                     // Compare the VID/PID to the DInput device
648                     DWORD dwVidPid = MAKELONG(dwVid, dwPid);
649 					for(JoyStickInfoList::iterator i = joys.begin(); i != joys.end(); ++i)
650 					{
651 						if(!i->isXInput && dwVidPid == i->productGuid.Data1)
652 						{
653 							i->isXInput = true;
654 							i->xInputDev = xDevice;
655 							++xDevice;
656 						}
657 					}
658 
659 					if(joys.size() == 0)
660 						goto LCleanup;
661                 }
662             }
663 
664             SAFE_RELEASE(pDevices[iDevice]);
665         }
666     }
667 
668 LCleanup:
669     if(bstrNamespace)
670         SysFreeString(bstrNamespace);
671 
672     if(bstrDeviceID)
673         SysFreeString(bstrDeviceID);
674 
675     if(bstrClassName)
676         SysFreeString(bstrClassName);
677 
678     for(iDevice=0; iDevice < 20; iDevice++)
679         SAFE_RELEASE(pDevices[iDevice]);
680 
681     SAFE_RELEASE(pEnumDevices);
682     SAFE_RELEASE(pIWbemLocator);
683     SAFE_RELEASE(pIWbemServices);
684 
685     if(bCleanupCOM)
686         CoUninitialize();
687 }
688 #endif
689