1 #include "../ControllerInterface.h"
3 #ifdef CIFACE_USE_DIRECTINPUT_JOYSTICK
5 #include "DirectInputJoystick.h"
12 #ifdef NO_DUPLICATE_DINPUT_XINPUT
13 //-----------------------------------------------------------------------------
14 // Modified some MSDN code to get all the XInput device GUID.Data1 values in a vector,
15 // faster than checking all the devices for each DirectInput device, like MSDN says to do
16 //-----------------------------------------------------------------------------
17 void GetXInputGUIDS( std::vector
<DWORD
>& guids
)
20 #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
22 IWbemLocator
* pIWbemLocator
= NULL
;
23 IEnumWbemClassObject
* pEnumDevices
= NULL
;
24 IWbemClassObject
* pDevices
[20] = {0};
25 IWbemServices
* pIWbemServices
= NULL
;
26 BSTR bstrNamespace
= NULL
;
27 BSTR bstrDeviceID
= NULL
;
28 BSTR bstrClassName
= NULL
;
34 hr
= CoInitialize(NULL
);
35 bool bCleanupCOM
= SUCCEEDED(hr
);
38 hr
= CoCreateInstance( __uuidof(WbemLocator
),
41 __uuidof(IWbemLocator
),
42 (LPVOID
*) &pIWbemLocator
);
43 if( FAILED(hr
) || pIWbemLocator
== NULL
)
46 bstrNamespace
= SysAllocString( L
"\\\\.\\root\\cimv2" );if( bstrNamespace
== NULL
) goto LCleanup
;
47 bstrClassName
= SysAllocString( L
"Win32_PNPEntity" ); if( bstrClassName
== NULL
) goto LCleanup
;
48 bstrDeviceID
= SysAllocString( L
"DeviceID" ); if( bstrDeviceID
== NULL
) goto LCleanup
;
51 hr
= pIWbemLocator
->ConnectServer( bstrNamespace
, NULL
, NULL
, 0L, 0L, NULL
, NULL
, &pIWbemServices
);
52 if( FAILED(hr
) || pIWbemServices
== NULL
)
55 // Switch security level to IMPERSONATE.
56 CoSetProxyBlanket( pIWbemServices
, RPC_C_AUTHN_WINNT
, RPC_C_AUTHZ_NONE
, NULL
,
57 RPC_C_AUTHN_LEVEL_CALL
, RPC_C_IMP_LEVEL_IMPERSONATE
, NULL
, EOAC_NONE
);
59 hr
= pIWbemServices
->CreateInstanceEnum( bstrClassName
, 0, NULL
, &pEnumDevices
);
60 if( FAILED(hr
) || pEnumDevices
== NULL
)
63 // Loop over all devices
67 hr
= pEnumDevices
->Next( 10000, 20, pDevices
, &uReturned
);
68 if( FAILED(hr
) || uReturned
== 0 )
71 for( UINT iDevice
=0; iDevice
<uReturned
; ++iDevice
)
73 // For each device, get its device ID
74 hr
= pDevices
[iDevice
]->Get( bstrDeviceID
, 0L, &var
, NULL
, NULL
);
75 if( SUCCEEDED( hr
) && var
.vt
== VT_BSTR
&& var
.bstrVal
!= NULL
)
77 // Check if the device ID contains "IG_". If it does, then it's an XInput device
78 // This information can not be found from DirectInput
79 if( wcsstr( var
.bstrVal
, L
"IG_" ) )
81 // If it does, then get the VID/PID from var.bstrVal
82 DWORD dwPid
= 0, dwVid
= 0;
83 WCHAR
* strVid
= wcsstr( var
.bstrVal
, L
"VID_" );
84 if( strVid
&& swscanf( strVid
, L
"VID_%4X", &dwVid
) != 1 )
86 WCHAR
* strPid
= wcsstr( var
.bstrVal
, L
"PID_" );
87 if( strPid
&& swscanf( strPid
, L
"PID_%4X", &dwPid
) != 1 )
90 // Compare the VID/PID to the DInput device
91 DWORD dwVidPid
= MAKELONG( dwVid
, dwPid
);
92 guids
.push_back( dwVidPid
);
93 //bIsXinputDevice = true;
96 SAFE_RELEASE( pDevices
[iDevice
] );
102 SysFreeString(bstrNamespace
);
104 SysFreeString(bstrDeviceID
);
106 SysFreeString(bstrClassName
);
107 for( UINT iDevice
=0; iDevice
<20; iDevice
++ )
108 SAFE_RELEASE( pDevices
[iDevice
] );
109 SAFE_RELEASE( pEnumDevices
);
110 SAFE_RELEASE( pIWbemLocator
);
111 SAFE_RELEASE( pIWbemServices
);
118 std::string
TStringToString( const std::basic_string
<TCHAR
>& in
)
120 const int size
= WideCharToMultiByte( CP_UTF8
, 0, in
.data(), int(in
.length()), NULL
, 0, NULL
, NULL
);
125 char* const data
= new char[size
];
126 WideCharToMultiByte( CP_UTF8
, 0, in
.data(), int(in
.length()), data
, size
, NULL
, NULL
);
127 const std::string
out( data
, size
);
132 //BOOL CALLBACK DIEnumEffectsCallback( LPCDIEFFECTINFO pdei, LPVOID pvRef )
134 // ((std::vector<DIEFFECTINFO>*)pvRef)->push_back( *pdei );
135 // return DIENUM_CONTINUE;
138 BOOL CALLBACK
DIEnumDeviceObjectsCallback( LPCDIDEVICEOBJECTINSTANCE lpddoi
, LPVOID pvRef
)
140 ((std::vector
<DIDEVICEOBJECTINSTANCE
>*)pvRef
)->push_back( *lpddoi
);
141 return DIENUM_CONTINUE
;
144 BOOL CALLBACK
DIEnumDevicesCallback( LPCDIDEVICEINSTANCE lpddi
, LPVOID pvRef
)
146 ((std::vector
<DIDEVICEINSTANCE
>*)pvRef
)->push_back( *lpddi
);
147 return DIENUM_CONTINUE
;
150 void InitJoystick( IDirectInput8
* const idi8
, std::vector
<ControllerInterface::Device
*>& devices
/*, HWND hwnd*/ )
152 std::vector
<DIDEVICEINSTANCE
> joysticks
;
153 idi8
->EnumDevices( DI8DEVCLASS_GAMECTRL
, DIEnumDevicesCallback
, (LPVOID
)&joysticks
, DIEDFL_ATTACHEDONLY
);
155 // just a struct with an int that is set to ZERO by default
156 struct ZeroedInt
{ZeroedInt():value(0){}unsigned int value
;};
157 // this is used to number the joysticks
158 // multiple joysticks with the same name shall get unique ids starting at 0
159 std::map
< std::basic_string
<TCHAR
>, ZeroedInt
> name_counts
;
161 #ifdef NO_DUPLICATE_DINPUT_XINPUT
162 std::vector
<DWORD
> xinput_guids
;
163 GetXInputGUIDS( xinput_guids
);
166 std::vector
<DIDEVICEINSTANCE
>::iterator i
= joysticks
.begin(),
170 #ifdef NO_DUPLICATE_DINPUT_XINPUT
171 // skip XInput Devices
172 if ( std::find( xinput_guids
.begin(), xinput_guids
.end(), i
->guidProduct
.Data1
) != xinput_guids
.end() )
175 // TODO: this has potential to mess up on createdev or setdatafmt failure
176 LPDIRECTINPUTDEVICE8 js_device
;
177 if ( DI_OK
== idi8
->CreateDevice( i
->guidInstance
, &js_device
, NULL
) )
178 if ( DI_OK
== js_device
->SetDataFormat( &c_dfDIJoystick
) )
179 // using foregroundwindow seems like a hack
180 if ( DI_OK
!= js_device
->SetCooperativeLevel( GetForegroundWindow(), DISCL_BACKGROUND
| DISCL_EXCLUSIVE
) )
182 // fall back to non-exclusive mode, with no rumble
183 if ( DI_OK
!= js_device
->SetCooperativeLevel( NULL
, DISCL_BACKGROUND
| DISCL_NONEXCLUSIVE
) )
185 js_device
->Release();
190 if ( DI_OK
== js_device
->Acquire() )
192 Joystick
* js
= new Joystick( /*&*i, */js_device
, name_counts
[i
->tszInstanceName
].value
++ );
193 // only add if it has some inputs/outpus
194 if ( js
->Inputs().size() || js
->Outputs().size() )
195 devices
.push_back( js
);
200 js_device
->Release();
205 Joystick::Joystick( /*const LPCDIDEVICEINSTANCE lpddi, */const LPDIRECTINPUTDEVICE8 device
, const unsigned int index
)
208 //, m_name(TStringToString(lpddi->tszInstanceName))
212 ZeroMemory( &js_caps
, sizeof(js_caps
) );
213 js_caps
.dwSize
= sizeof(js_caps
);
214 m_device
->GetCapabilities(&js_caps
);
216 // max of 32 buttons and 4 hats / the limit of the data format i am using
217 js_caps
.dwButtons
= std::min((DWORD
)32, js_caps
.dwButtons
);
218 js_caps
.dwPOVs
= std::min((DWORD
)4, js_caps
.dwPOVs
);
220 m_must_poll
= ( ( js_caps
.dwFlags
& DIDC_POLLEDDATAFORMAT
) > 0 );
223 for ( unsigned int i
= 0; i
< js_caps
.dwButtons
; ++i
)
224 inputs
.push_back( new Button( i
) );
226 for ( unsigned int i
= 0; i
< js_caps
.dwPOVs
; ++i
)
228 // each hat gets 4 input instances associated with it, (up down left right)
229 for ( unsigned int d
= 0; d
<4; ++d
)
230 inputs
.push_back( new Hat( i
, d
) );
232 // get up to 6 axes and 2 sliders
233 std::vector
<DIDEVICEOBJECTINSTANCE
> axes
;
234 unsigned int cur_slider
= 0;
235 m_device
->EnumObjects( DIEnumDeviceObjectsCallback
, (LPVOID
)&axes
, DIDFT_AXIS
);
237 // going in reverse leaves the list more organized in the end for me :/
238 std::vector
<DIDEVICEOBJECTINSTANCE
>::const_reverse_iterator i
= axes
.rbegin(),
243 ZeroMemory( &range
, sizeof(range
) );
244 range
.diph
.dwSize
= sizeof(range
);
245 range
.diph
.dwHeaderSize
= sizeof(range
.diph
);
246 range
.diph
.dwHow
= DIPH_BYID
;
247 range
.diph
.dwObj
= i
->dwType
;
248 // try to set some nice power of 2 values (8192)
249 range
.lMin
= -(1<<13);
250 range
.lMax
= (1<<13);
251 // but i guess not all devices support setting range
252 m_device
->SetProperty( DIPROP_RANGE
, &range
.diph
);
253 // so i getproperty right afterward incase it didn't set :P
254 if ( DI_OK
== m_device
->GetProperty( DIPROP_RANGE
, &range
.diph
) )
257 const GUID type
= i
->guidType
;
259 // figure out which axis this is
260 if ( type
== GUID_XAxis
)
262 else if ( type
== GUID_YAxis
)
264 else if ( type
== GUID_ZAxis
)
266 else if ( type
== GUID_RxAxis
)
268 else if ( type
== GUID_RyAxis
)
270 else if ( type
== GUID_RzAxis
)
272 else if ( type
== GUID_Slider
)
273 if ( cur_slider
< 2 )
274 offset
= 6 + cur_slider
++;
278 const LONG base
= (range
.lMin
+ range
.lMax
) / 2;
279 // each axis gets a negative and a positive input instance associated with it
280 inputs
.push_back( new Axis( offset
, base
, range
.lMin
-base
) );
281 inputs
.push_back( new Axis( offset
, base
, range
.lMax
-base
) );
286 // get supported ff effects
287 std::vector
<DIDEVICEOBJECTINSTANCE
> objects
;
288 m_device
->EnumObjects( DIEnumDeviceObjectsCallback
, (LPVOID
)&objects
, DIDFT_AXIS
);
289 // got some ff axes or something
290 if ( objects
.size() )
293 DWORD rgdwAxes
[] = { DIJOFS_X
, DIJOFS_Y
};
294 LONG rglDirection
[] = { 0, 0 };
295 DICONSTANTFORCE cf
= { 0 };
297 ZeroMemory( &eff
, sizeof( DIEFFECT
) );
298 eff
.dwSize
= sizeof( DIEFFECT
);
299 eff
.dwFlags
= DIEFF_CARTESIAN
| DIEFF_OBJECTOFFSETS
;
300 eff
.dwDuration
= INFINITE
;
301 eff
.dwGain
= DI_FFNOMINALMAX
;
302 eff
.dwTriggerButton
= DIEB_NOTRIGGER
;
303 eff
.cAxes
= std::min( (DWORD
)2, (DWORD
)objects
.size() );
304 eff
.rgdwAxes
= rgdwAxes
;
305 eff
.rglDirection
= rglDirection
;
306 eff
.cbTypeSpecificParams
= sizeof( DICONSTANTFORCE
);
307 eff
.lpvTypeSpecificParams
= &cf
;
309 LPDIRECTINPUTEFFECT pEffect
;
310 if ( DI_OK
== m_device
->CreateEffect( GUID_ConstantForce
, &eff
, &pEffect
, NULL
) )
313 outputs
.push_back( new Force( 0 ) );
314 m_state_out
.push_back( EffectState( pEffect
) );
318 // disable autocentering
319 if ( outputs
.size() )
322 dipdw
.diph
.dwSize
= sizeof( DIPROPDWORD
);
323 dipdw
.diph
.dwHeaderSize
= sizeof( DIPROPHEADER
);
324 dipdw
.diph
.dwObj
= 0;
325 dipdw
.diph
.dwHow
= DIPH_DEVICE
;
326 dipdw
.dwData
= FALSE
;
327 m_device
->SetProperty( DIPROP_AUTOCENTER
, &dipdw
.diph
);
333 Joystick::~Joystick()
335 // release the ff effect iface's
336 std::vector
<EffectState
>::iterator i
= m_state_out
.begin(),
337 e
= m_state_out
.end();
345 m_device
->Unacquire();
349 void Joystick::ClearInputState()
351 ZeroMemory(&m_state_in
, sizeof(m_state_in
));
352 // set hats to center
353 memset( m_state_in
.rgdwPOV
, 0xFF, sizeof(m_state_in
.rgdwPOV
) );
356 std::string
Joystick::GetName() const
359 ZeroMemory( &str
, sizeof(str
) );
360 str
.diph
.dwSize
= sizeof(str
);
361 str
.diph
.dwHeaderSize
= sizeof(str
.diph
);
362 str
.diph
.dwHow
= DIPH_DEVICE
;
363 m_device
->GetProperty( DIPROP_PRODUCTNAME
, &str
.diph
);
364 return TStringToString( str
.wsz
);
368 int Joystick::GetId() const
373 std::string
Joystick::GetSource() const
375 return "DirectInput";
381 bool Joystick::UpdateInput()
387 HRESULT hr
= m_device
->GetDeviceState( sizeof(m_state_in
), &m_state_in
);
389 // try reacquire if input lost
390 if ( DIERR_INPUTLOST
== hr
)
391 hr
= m_device
->Acquire();
393 return ( DI_OK
== hr
);
396 bool Joystick::UpdateOutput()
400 std::vector
<EffectState
>::iterator i
= m_state_out
.begin(),
401 e
= m_state_out
.end();
408 cf
.lMagnitude
= LONG(10000 * i
->magnitude
);
413 ZeroMemory( &eff
, sizeof( eff
) );
414 eff
.dwSize
= sizeof( DIEFFECT
);
415 eff
.dwFlags
= DIEFF_CARTESIAN
| DIEFF_OBJECTOFFSETS
;
416 eff
.cbTypeSpecificParams
= sizeof( cf
);
417 eff
.lpvTypeSpecificParams
= &cf
;
418 // set params and start effect
419 ok_count
+= ( DI_OK
== i
->iface
->SetParameters( &eff
, DIEP_TYPESPECIFICPARAMS
| DIEP_START
) );
422 ok_count
+= ( DI_OK
== i
->iface
->Stop() );
428 return ( m_state_out
.size() == ok_count
);
433 std::string
Joystick::Button::GetName() const
435 std::ostringstream ss
;
436 ss
<< "Button " << m_index
;
440 std::string
Joystick::Axis::GetName() const
442 std::ostringstream ss
;
446 ss
<< "Axis " << "XYZ"[m_index
%3];
452 ss
<< "Slider " << m_index
-6;
454 ss
<< ( m_range
>0 ? '+' : '-' );
458 std::string
Joystick::Hat::GetName() const
460 std::ostringstream ss
;
461 ss
<< "Hat " << m_index
<< ' ' << "NESW"[m_direction
];
465 std::string
Joystick::Force::GetName() const
473 ControlState
Joystick::GetInputState( const ControllerInterface::Device::Input
* const input
)
475 return ((Input
*)input
)->GetState( &m_state_in
);
478 void Joystick::SetOutputState( const ControllerInterface::Device::Output
* const output
, const ControlState state
)
480 ((Output
*)output
)->SetState( state
, &m_state_out
[0] );
485 ControlState
Joystick::Axis::GetState( const DIJOYSTATE
* const joystate
)
487 return std::max( 0.0f
, ControlState((&joystate
->lX
)[m_index
]-m_base
) / m_range
);
490 ControlState
Joystick::Button::GetState( const DIJOYSTATE
* const joystate
)
492 return ControlState( joystate
->rgbButtons
[m_index
] > 0 );
495 ControlState
Joystick::Hat::GetState( const DIJOYSTATE
* const joystate
)
497 // can this func be simplified ?
498 const DWORD val
= joystate
->rgdwPOV
[m_index
];
499 // hat centered code from msdn
500 if ( 0xFFFF == LOWORD(val
) )
502 return ( abs( (int)(val
/4500-m_direction
*2+8)%8 - 4) > 2 );
505 void Joystick::Force::SetState( const ControlState state
, Joystick::EffectState
* const joystate
)
507 joystate
[m_index
].magnitude
= state
;
508 joystate
[m_index
].changed
= true;