GCPadNew: Added option to disable input when Dolphin window isn't active. Some other...
[dolphin.git] / Source / Plugins / Plugin_GCPadNew / Src / ControllerInterface / DirectInput / DirectInputJoystick.cpp
blob5fd209b7c289453778634f16360f27416fb9f0e8
1 #include "../ControllerInterface.h"
3 #ifdef CIFACE_USE_DIRECTINPUT_JOYSTICK
5 #include "DirectInputJoystick.h"
7 namespace ciface
9 namespace DirectInput
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;
29 DWORD uReturned = 0;
30 VARIANT var;
31 HRESULT hr;
33 // CoInit if needed
34 hr = CoInitialize(NULL);
35 bool bCleanupCOM = SUCCEEDED(hr);
37 // Create WMI
38 hr = CoCreateInstance( __uuidof(WbemLocator),
39 NULL,
40 CLSCTX_INPROC_SERVER,
41 __uuidof(IWbemLocator),
42 (LPVOID*) &pIWbemLocator);
43 if( FAILED(hr) || pIWbemLocator == NULL )
44 goto LCleanup;
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;
50 // Connect to WMI
51 hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices );
52 if( FAILED(hr) || pIWbemServices == NULL )
53 goto LCleanup;
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 )
61 goto LCleanup;
63 // Loop over all devices
64 while( true )
66 // Get 20 at a time
67 hr = pEnumDevices->Next( 10000, 20, pDevices, &uReturned );
68 if( FAILED(hr) || uReturned == 0 )
69 break;
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 )
85 dwVid = 0;
86 WCHAR* strPid = wcsstr( var.bstrVal, L"PID_" );
87 if( strPid && swscanf( strPid, L"PID_%4X", &dwPid ) != 1 )
88 dwPid = 0;
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] );
100 LCleanup:
101 if(bstrNamespace)
102 SysFreeString(bstrNamespace);
103 if(bstrDeviceID)
104 SysFreeString(bstrDeviceID);
105 if(bstrClassName)
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 );
113 if( bCleanupCOM )
114 CoUninitialize();
116 #endif
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 );
122 if ( 0 == size )
123 return "";
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 );
128 delete[] data;
129 return out;
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 );
164 #endif
166 std::vector<DIDEVICEINSTANCE>::iterator i = joysticks.begin(),
167 e = joysticks.end();
168 for ( ; i!=e; ++i )
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() )
173 continue;
174 #endif
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();
186 continue;
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 );
196 else
197 delete js;
199 else
200 js_device->Release();
205 Joystick::Joystick( /*const LPCDIDEVICEINSTANCE lpddi, */const LPDIRECTINPUTDEVICE8 device, const unsigned int index )
206 : m_device(device)
207 , m_index(index)
208 //, m_name(TStringToString(lpddi->tszInstanceName))
210 // get joystick caps
211 DIDEVCAPS js_caps;
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 );
222 // buttons
223 for ( unsigned int i = 0; i < js_caps.dwButtons; ++i )
224 inputs.push_back( new Button( i ) );
225 // hats
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(),
239 e = axes.rend();
240 for( ; i!=e; ++i )
242 DIPROPRANGE range;
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 ) )
256 int offset = -1;
257 const GUID type = i->guidType;
259 // figure out which axis this is
260 if ( type == GUID_XAxis )
261 offset = 0;
262 else if ( type == GUID_YAxis )
263 offset = 1;
264 else if ( type == GUID_ZAxis )
265 offset = 2;
266 else if ( type == GUID_RxAxis )
267 offset = 3;
268 else if ( type == GUID_RyAxis )
269 offset = 4;
270 else if ( type == GUID_RzAxis )
271 offset = 5;
272 else if ( type == GUID_Slider )
273 if ( cur_slider < 2 )
274 offset = 6 + cur_slider++;
276 if ( offset >= 0 )
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() )
292 // temporary
293 DWORD rgdwAxes[] = { DIJOFS_X, DIJOFS_Y };
294 LONG rglDirection[] = { 0, 0 };
295 DICONSTANTFORCE cf = { 0 };
296 DIEFFECT eff;
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 ) )
312 // temp
313 outputs.push_back( new Force( 0 ) );
314 m_state_out.push_back( EffectState( pEffect ) );
318 // disable autocentering
319 if ( outputs.size() )
321 DIPROPDWORD dipdw;
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 );
330 ClearInputState();
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();
338 for ( ; i!=e; ++i )
340 i->iface->Stop();
341 i->iface->Unload();
342 i->iface->Release();
345 m_device->Unacquire();
346 m_device->Release();
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
358 DIPROPSTRING str;
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 );
365 //return m_name;
368 int Joystick::GetId() const
370 return m_index;
373 std::string Joystick::GetSource() const
375 return "DirectInput";
378 // update IO
381 bool Joystick::UpdateInput()
383 if ( m_must_poll )
384 m_device->Poll();
385 //return false;
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()
398 // temporary
399 size_t ok_count = 0;
400 std::vector<EffectState>::iterator i = m_state_out.begin(),
401 e = m_state_out.end();
402 for ( ; i!=e; ++i )
404 if ( i->changed )
406 i->changed = false;
407 DICONSTANTFORCE cf;
408 cf.lMagnitude = LONG(10000 * i->magnitude);
410 if ( cf.lMagnitude )
412 DIEFFECT eff;
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 ) );
421 else
422 ok_count += ( DI_OK == i->iface->Stop() );
424 else
425 ++ok_count;
428 return ( m_state_out.size() == ok_count );
431 // get name
433 std::string Joystick::Button::GetName() const
435 std::ostringstream ss;
436 ss << "Button " << m_index;
437 return ss.str();
440 std::string Joystick::Axis::GetName() const
442 std::ostringstream ss;
443 // axis
444 if ( m_index < 6 )
446 ss << "Axis " << "XYZ"[m_index%3];
447 if ( m_index > 2 )
448 ss << 'r';
450 // slider
451 else
452 ss << "Slider " << m_index-6;
454 ss << ( m_range>0 ? '+' : '-' );
455 return ss.str();
458 std::string Joystick::Hat::GetName() const
460 std::ostringstream ss;
461 ss << "Hat " << m_index << ' ' << "NESW"[m_direction];
462 return ss.str();
465 std::string Joystick::Force::GetName() const
467 // temporary
468 return "Constant";
471 // get / set state
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] );
483 // get / set state
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) )
501 return 0;
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;
514 #endif