1 #include "../ControllerInterface.h"
3 #ifdef CIFACE_USE_DINPUT_JOYSTICK
5 #include "DInputJoystick.h"
13 // template instantiation
14 template class Joystick::Force
<DICONSTANTFORCE
>;
15 template class Joystick::Force
<DIRAMPFORCE
>;
16 template class Joystick::Force
<DIPERIODIC
>;
22 } force_type_names
[] =
24 {GUID_ConstantForce
, "Constant"}, // DICONSTANTFORCE
25 {GUID_RampForce
, "Ramp"}, // DIRAMPFORCE
26 {GUID_Square
, "Square"}, // DIPERIODIC ...
28 {GUID_Triangle
, "Triangle"},
29 {GUID_SawtoothUp
, "SawtoothUp"},
30 {GUID_SawtoothDown
, "SawtoothDown"},
31 //{GUID_Spring, "Spring"}, // DIEFT_CUSTOMFORCE ... < i think
32 //{GUID_Damper, "Damper"},
33 //{GUID_Inertia, "Inertia"},
34 //{GUID_Friction, "Friction"},
37 #define DATA_BUFFER_SIZE 32
39 #ifdef NO_DUPLICATE_DINPUT_XINPUT
40 //-----------------------------------------------------------------------------
41 // Modified some MSDN code to get all the XInput device GUID.Data1 values in a vector,
42 // faster than checking all the devices for each DirectInput device, like MSDN says to do
43 //-----------------------------------------------------------------------------
44 void GetXInputGUIDS( std::vector
<DWORD
>& guids
)
47 #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
49 IWbemLocator
* pIWbemLocator
= NULL
;
50 IEnumWbemClassObject
* pEnumDevices
= NULL
;
51 IWbemClassObject
* pDevices
[20] = {0};
52 IWbemServices
* pIWbemServices
= NULL
;
53 BSTR bstrNamespace
= NULL
;
54 BSTR bstrDeviceID
= NULL
;
55 BSTR bstrClassName
= NULL
;
61 hr
= CoInitialize(NULL
);
62 bool bCleanupCOM
= SUCCEEDED(hr
);
65 hr
= CoCreateInstance( __uuidof(WbemLocator
),
68 __uuidof(IWbemLocator
),
69 (LPVOID
*) &pIWbemLocator
);
70 if( FAILED(hr
) || pIWbemLocator
== NULL
)
73 bstrNamespace
= SysAllocString( L
"\\\\.\\root\\cimv2" );if( bstrNamespace
== NULL
) goto LCleanup
;
74 bstrClassName
= SysAllocString( L
"Win32_PNPEntity" ); if( bstrClassName
== NULL
) goto LCleanup
;
75 bstrDeviceID
= SysAllocString( L
"DeviceID" ); if( bstrDeviceID
== NULL
) goto LCleanup
;
78 hr
= pIWbemLocator
->ConnectServer( bstrNamespace
, NULL
, NULL
, 0L, 0L, NULL
, NULL
, &pIWbemServices
);
79 if( FAILED(hr
) || pIWbemServices
== NULL
)
82 // Switch security level to IMPERSONATE.
83 CoSetProxyBlanket( pIWbemServices
, RPC_C_AUTHN_WINNT
, RPC_C_AUTHZ_NONE
, NULL
,
84 RPC_C_AUTHN_LEVEL_CALL
, RPC_C_IMP_LEVEL_IMPERSONATE
, NULL
, EOAC_NONE
);
86 hr
= pIWbemServices
->CreateInstanceEnum( bstrClassName
, 0, NULL
, &pEnumDevices
);
87 if( FAILED(hr
) || pEnumDevices
== NULL
)
90 // Loop over all devices
94 hr
= pEnumDevices
->Next( 10000, 20, pDevices
, &uReturned
);
95 if( FAILED(hr
) || uReturned
== 0 )
98 for( UINT iDevice
=0; iDevice
<uReturned
; ++iDevice
)
100 // For each device, get its device ID
101 hr
= pDevices
[iDevice
]->Get( bstrDeviceID
, 0L, &var
, NULL
, NULL
);
102 if( SUCCEEDED( hr
) && var
.vt
== VT_BSTR
&& var
.bstrVal
!= NULL
)
104 // Check if the device ID contains "IG_". If it does, then it's an XInput device
105 // This information can not be found from DirectInput
106 if( wcsstr( var
.bstrVal
, L
"IG_" ) )
108 // If it does, then get the VID/PID from var.bstrVal
109 DWORD dwPid
= 0, dwVid
= 0;
110 WCHAR
* strVid
= wcsstr( var
.bstrVal
, L
"VID_" );
111 if( strVid
&& swscanf( strVid
, L
"VID_%4X", &dwVid
) != 1 )
113 WCHAR
* strPid
= wcsstr( var
.bstrVal
, L
"PID_" );
114 if( strPid
&& swscanf( strPid
, L
"PID_%4X", &dwPid
) != 1 )
117 // Compare the VID/PID to the DInput device
118 DWORD dwVidPid
= MAKELONG( dwVid
, dwPid
);
119 guids
.push_back( dwVidPid
);
120 //bIsXinputDevice = true;
123 SAFE_RELEASE( pDevices
[iDevice
] );
129 SysFreeString(bstrNamespace
);
131 SysFreeString(bstrDeviceID
);
133 SysFreeString(bstrClassName
);
134 for( UINT iDevice
=0; iDevice
<20; iDevice
++ )
135 SAFE_RELEASE( pDevices
[iDevice
] );
136 SAFE_RELEASE( pEnumDevices
);
137 SAFE_RELEASE( pIWbemLocator
);
138 SAFE_RELEASE( pIWbemServices
);
145 void InitJoystick( IDirectInput8
* const idi8
, std::vector
<ControllerInterface::Device
*>& devices
/*, HWND hwnd*/ )
147 std::list
<DIDEVICEINSTANCE
> joysticks
;
148 idi8
->EnumDevices( DI8DEVCLASS_GAMECTRL
, DIEnumDevicesCallback
, (LPVOID
)&joysticks
, DIEDFL_ATTACHEDONLY
);
150 // this is used to number the joysticks
151 // multiple joysticks with the same name shall get unique ids starting at 0
152 std::map
< std::basic_string
<TCHAR
>, int> name_counts
;
154 #ifdef NO_DUPLICATE_DINPUT_XINPUT
155 std::vector
<DWORD
> xinput_guids
;
156 GetXInputGUIDS( xinput_guids
);
159 std::list
<DIDEVICEINSTANCE
>::iterator
160 i
= joysticks
.begin(),
164 #ifdef NO_DUPLICATE_DINPUT_XINPUT
165 // skip XInput Devices
166 if ( std::find( xinput_guids
.begin(), xinput_guids
.end(), i
->guidProduct
.Data1
) != xinput_guids
.end() )
169 LPDIRECTINPUTDEVICE8 js_device
;
170 if (SUCCEEDED(idi8
->CreateDevice(i
->guidInstance
, &js_device
, NULL
)))
172 if (SUCCEEDED(js_device
->SetDataFormat(&c_dfDIJoystick
)))
174 // using foregroundwindow seems like a hack
175 if (FAILED(js_device
->SetCooperativeLevel(GetForegroundWindow(), DISCL_BACKGROUND
| DISCL_EXCLUSIVE
)))
177 //PanicAlert("SetCooperativeLevel(DISCL_EXCLUSIVE) failed!");
178 // fall back to non-exclusive mode, with no rumble
179 if (FAILED(js_device
->SetCooperativeLevel(NULL
, DISCL_BACKGROUND
| DISCL_NONEXCLUSIVE
)))
181 //PanicAlert("SetCooperativeLevel failed!");
182 js_device
->Release();
187 Joystick
* js
= new Joystick(/*&*i, */js_device
, name_counts
[i
->tszInstanceName
]++);
188 // only add if it has some inputs/outpus
189 if (js
->Inputs().size() || js
->Outputs().size())
190 devices
.push_back(js
);
196 //PanicAlert("SetDataFormat failed!");
197 js_device
->Release();
204 Joystick::Joystick( /*const LPCDIDEVICEINSTANCE lpddi, */const LPDIRECTINPUTDEVICE8 device
, const unsigned int index
)
207 //, m_name(TStringToString(lpddi->tszInstanceName))
209 // this needs to be done before GetCapabilities() maybe?
214 js_caps
.dwSize
= sizeof(js_caps
);
215 if (FAILED(m_device
->GetCapabilities(&js_caps
)))
218 // max of 32 buttons and 4 hats / the limit of the data format i am using
219 js_caps
.dwButtons
= std::min((DWORD
)32, js_caps
.dwButtons
);
220 js_caps
.dwPOVs
= std::min((DWORD
)4, js_caps
.dwPOVs
);
222 //m_must_poll = (js_caps.dwFlags & DIDC_POLLEDDATAFORMAT) != 0;
224 // polled or buffered data
226 dipdw
.diph
.dwSize
= sizeof(DIPROPDWORD
);
227 dipdw
.diph
.dwHeaderSize
= sizeof(DIPROPHEADER
);
228 dipdw
.diph
.dwObj
= 0;
229 dipdw
.diph
.dwHow
= DIPH_DEVICE
;
230 dipdw
.dwData
= DATA_BUFFER_SIZE
;
231 // set the buffer size,
232 // if we can't set the property, we can't use buffered data
233 m_buffered
= SUCCEEDED(m_device
->SetProperty(DIPROP_BUFFERSIZE
, &dipdw
.diph
));
236 for ( unsigned int i
= 0; i
< js_caps
.dwButtons
; ++i
)
237 AddInput( new Button( i
) );
240 for ( unsigned int i
= 0; i
< js_caps
.dwPOVs
; ++i
)
242 // each hat gets 4 input instances associated with it, (up down left right)
243 for ( unsigned int d
= 0; d
<4; ++d
)
244 AddInput( new Hat( i
, d
) );
247 // get up to 6 axes and 2 sliders
249 range
.diph
.dwSize
= sizeof(range
);
250 range
.diph
.dwHeaderSize
= sizeof(range
.diph
);
251 range
.diph
.dwHow
= DIPH_BYOFFSET
;
252 // screw EnumObjects, just go through all the axis offsets and try to GetProperty
253 // this should be more foolproof, less code, and probably faster
254 for (unsigned int offset
= 0; offset
< DIJOFS_BUTTON(0) / sizeof(LONG
); ++offset
)
256 range
.diph
.dwObj
= offset
* sizeof(LONG
);
257 // try to set some nice power of 2 values (8192)
258 range
.lMin
= -(1 << 13);
259 range
.lMax
= (1 << 13);
260 m_device
->SetProperty(DIPROP_RANGE
, &range
.diph
);
261 // but i guess not all devices support setting range
262 // so i getproperty right afterward incase it didn't set :P
263 // this also checks that the axis is present
264 if (SUCCEEDED(m_device
->GetProperty(DIPROP_RANGE
, &range
.diph
)))
266 const LONG base
= (range
.lMin
+ range
.lMax
) / 2;
267 // each axis gets a negative and a positive input instance associated with it
268 AddInput(new Axis(offset
, base
, range
.lMin
-base
));
269 AddInput(new Axis(offset
, base
, range
.lMax
-base
));
273 // TODO: check for DIDC_FORCEFEEDBACK in devcaps?
275 // get supported ff effects
276 std::list
<DIDEVICEOBJECTINSTANCE
> objects
;
277 m_device
->EnumObjects(DIEnumDeviceObjectsCallback
, (LPVOID
)&objects
, DIDFT_AXIS
);
278 // got some ff axes or something
279 if ( objects
.size() )
282 DWORD rgdwAxes
[] = {DIJOFS_X
, DIJOFS_Y
};
283 LONG rglDirection
[] = {0, 0};
286 ZeroMemory(&eff
, sizeof(eff
));
287 eff
.dwSize
= sizeof(DIEFFECT
);
288 eff
.dwFlags
= DIEFF_CARTESIAN
| DIEFF_OBJECTOFFSETS
;
289 eff
.dwDuration
= INFINITE
; // (4 * DI_SECONDS)
290 eff
.dwGain
= DI_FFNOMINALMAX
;
291 eff
.dwTriggerButton
= DIEB_NOTRIGGER
;
292 eff
.cAxes
= std::min((DWORD
)2, (DWORD
)objects
.size());
293 eff
.rgdwAxes
= rgdwAxes
;
294 eff
.rglDirection
= rglDirection
;
296 // DIPERIODIC is the largest, so we'll use that
298 eff
.lpvTypeSpecificParams
= &f
;
299 ZeroMemory(&f
, sizeof(f
));
301 // doesn't seem needed
303 //eff.lpEnvelope = &env;
304 //ZeroMemory(&env, sizeof(env));
305 //env.dwSize = sizeof(env);
307 for (unsigned int f
= 0, i
= 0; f
< sizeof(force_type_names
)/sizeof(*force_type_names
); ++f
)
311 eff
.cbTypeSpecificParams
= sizeof(DICONSTANTFORCE
);
313 eff
.cbTypeSpecificParams
= sizeof(DIRAMPFORCE
);
315 eff
.cbTypeSpecificParams
= sizeof(DIPERIODIC
);
317 LPDIRECTINPUTEFFECT pEffect
;
318 if (SUCCEEDED(m_device
->CreateEffect(force_type_names
[f
].guid
, &eff
, &pEffect
, NULL
)))
320 // ugly if ladder again :/
322 AddOutput(new ForceConstant(i
, f
));
324 AddOutput(new ForceRamp(i
, f
));
326 AddOutput(new ForcePeriodic(i
, f
));
329 m_state_out
.push_back(EffectState(pEffect
));
334 // disable autocentering
335 if (Outputs().size())
338 dipdw
.diph
.dwSize
= sizeof( DIPROPDWORD
);
339 dipdw
.diph
.dwHeaderSize
= sizeof( DIPROPHEADER
);
340 dipdw
.diph
.dwObj
= 0;
341 dipdw
.diph
.dwHow
= DIPH_DEVICE
;
342 dipdw
.dwData
= DIPROPAUTOCENTER_OFF
;
343 m_device
->SetProperty( DIPROP_AUTOCENTER
, &dipdw
.diph
);
349 Joystick::~Joystick()
351 // release the ff effect iface's
352 std::vector
<EffectState
>::iterator
353 i
= m_state_out
.begin(),
354 e
= m_state_out
.end();
362 m_device
->Unacquire();
366 void Joystick::ClearInputState()
368 ZeroMemory(&m_state_in
, sizeof(m_state_in
));
369 // set hats to center
370 memset( m_state_in
.rgdwPOV
, 0xFF, sizeof(m_state_in
.rgdwPOV
) );
373 std::string
Joystick::GetName() const
375 return GetDeviceName(m_device
);
378 int Joystick::GetId() const
383 std::string
Joystick::GetSource() const
385 return DINPUT_SOURCE_NAME
;
390 bool Joystick::UpdateInput()
395 // msdn says if this isn't needed it doesnt do anything
402 DIDEVICEOBJECTDATA evtbuf
[DATA_BUFFER_SIZE
];
403 DWORD numevents
= DATA_BUFFER_SIZE
;
404 hr
= m_device
->GetDeviceData(sizeof(*evtbuf
), evtbuf
, &numevents
, 0);
408 for (LPDIDEVICEOBJECTDATA evt
= evtbuf
; evt
!= (evtbuf
+ numevents
); ++evt
)
410 // all the buttons are at the end of the data format
411 // they are bytes rather than longs
412 if (evt
->dwOfs
< DIJOFS_BUTTON(0))
413 *(DWORD
*)(((BYTE
*)&m_state_in
) + evt
->dwOfs
) = evt
->dwData
;
415 ((BYTE
*)&m_state_in
)[evt
->dwOfs
] = (BYTE
)evt
->dwData
;
418 // we lost some data, attempt to use GetDeviceState
419 // maybe this isn't the best thing to do
420 // maybe I should clear the input state?
421 if (DI_BUFFEROVERFLOW
== hr
)
422 hr
= m_device
->GetDeviceState(sizeof(m_state_in
), &m_state_in
);
426 hr
= m_device
->GetDeviceState(sizeof(m_state_in
), &m_state_in
);
428 // try reacquire if input lost
429 if (DIERR_INPUTLOST
== hr
|| DIERR_NOTACQUIRED
== hr
)
430 hr
= m_device
->Acquire();
432 return SUCCEEDED(hr
);
435 bool Joystick::UpdateOutput()
438 std::vector
<EffectState
>::iterator
439 i
= m_state_out
.begin(),
440 e
= m_state_out
.end();
448 ZeroMemory(&eff
, sizeof(eff
));
449 eff
.dwSize
= sizeof(DIEFFECT
);
450 eff
.dwFlags
= DIEFF_CARTESIAN
| DIEFF_OBJECTOFFSETS
;
451 eff
.cbTypeSpecificParams
= i
->size
;
452 eff
.lpvTypeSpecificParams
= i
->params
;
453 // set params and start effect
454 ok_count
+= SUCCEEDED(i
->iface
->SetParameters(&eff
, DIEP_TYPESPECIFICPARAMS
| DIEP_START
));
457 ok_count
+= SUCCEEDED(i
->iface
->Stop());
465 return (m_state_out
.size() == ok_count
);
470 std::string
Joystick::Button::GetName() const
472 std::ostringstream ss
;
473 ss
<< "Button " << m_index
;
477 std::string
Joystick::Axis::GetName() const
479 std::ostringstream ss
;
483 ss
<< "Axis " << (char)('X' + (m_index
% 3));
489 ss
<< "Slider " << m_index
-6;
491 ss
<< (m_range
<0 ? '-' : '+');
495 std::string
Joystick::Hat::GetName() const
497 static char tmpstr
[] = "Hat . .";
498 tmpstr
[4] = (char)('0' + m_index
);
499 tmpstr
[6] = "NESW"[m_direction
];
503 template <typename P
>
504 std::string
Joystick::Force
<P
>::GetName() const
506 return force_type_names
[m_type
].name
;
511 ControlState
Joystick::GetInputState( const ControllerInterface::Device::Input
* const input
) const
513 return ((Input
*)input
)->GetState( &m_state_in
);
516 void Joystick::SetOutputState( const ControllerInterface::Device::Output
* const output
, const ControlState state
)
518 ((Output
*)output
)->SetState( state
, &m_state_out
[0] );
523 ControlState
Joystick::Axis::GetState( const DIJOYSTATE
* const joystate
) const
525 return std::max( 0.0f
, ControlState((&joystate
->lX
)[m_index
]-m_base
) / m_range
);
528 ControlState
Joystick::Button::GetState( const DIJOYSTATE
* const joystate
) const
530 return ControlState( joystate
->rgbButtons
[m_index
] > 0 );
533 ControlState
Joystick::Hat::GetState( const DIJOYSTATE
* const joystate
) const
535 // can this func be simplified ?
536 const DWORD val
= joystate
->rgdwPOV
[m_index
];
537 // hat centered code from msdn
538 if ( 0xFFFF == LOWORD(val
) )
540 return ( abs( (int)(val
/4500-m_direction
*2+8)%8 - 4) > 2 );
543 void Joystick::ForceConstant::SetState(const ControlState state
, Joystick::EffectState
* const joystate
)
545 const LONG new_val
= LONG(10000 * state
);
547 LONG
&val
= params
.lMagnitude
;
551 joystate
[m_index
].params
= ¶ms
; // tells UpdateOutput the state has changed
553 // tells UpdateOutput to either start or stop the force
554 joystate
[m_index
].size
= new_val
? sizeof(params
) : 0;
558 void Joystick::ForceRamp::SetState(const ControlState state
, Joystick::EffectState
* const joystate
)
560 const LONG new_val
= LONG(10000 * state
);
562 if (params
.lStart
!= new_val
)
564 params
.lStart
= params
.lEnd
= new_val
;
565 joystate
[m_index
].params
= ¶ms
; // tells UpdateOutput the state has changed
567 // tells UpdateOutput to either start or stop the force
568 joystate
[m_index
].size
= new_val
? sizeof(params
) : 0;
572 void Joystick::ForcePeriodic::SetState(const ControlState state
, Joystick::EffectState
* const joystate
)
574 const LONG new_val
= LONG(10000 * state
);
576 DWORD
&val
= params
.dwMagnitude
;
580 //params.dwPeriod = 0;//DWORD(0.05 * DI_SECONDS); // zero is working fine for me
582 joystate
[m_index
].params
= ¶ms
; // tells UpdateOutput the state has changed
584 // tells UpdateOutput to either start or stop the force
585 joystate
[m_index
].size
= new_val
? sizeof(params
) : 0;
589 template <typename P
>
590 Joystick::Force
<P
>::Force(const unsigned int index
, const unsigned int type
)
591 : m_index(index
), m_type(type
)
593 ZeroMemory(¶ms
, sizeof(params
));