winDirectInput.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platformWin32/platformWin32.h"
  23. #include "platformWin32/winDirectInput.h"
  24. #include "platformWin32/winDInputDevice.h"
  25. #include "console/console.h"
  26. #include "console/consoleTypes.h"
  27. #include "sim/actionMap.h"
  28. #include <xinput.h>
  29. //------------------------------------------------------------------------------
  30. // Static class variables:
  31. bool DInputManager::smJoystickEnabled = true;
  32. bool DInputManager::smXInputEnabled = true;
  33. // Type definitions:
  34. typedef HRESULT (WINAPI* FN_DirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
  35. //------------------------------------------------------------------------------
  36. DInputManager::DInputManager()
  37. {
  38. mEnabled = false;
  39. mDInputLib = NULL;
  40. mDInputInterface = NULL;
  41. mJoystickActive = mXInputActive = true;
  42. mXInputLib = NULL;
  43. for(S32 i=0; i<4; i++)
  44. mLastDisconnectTime[i] = -1;
  45. }
  46. //------------------------------------------------------------------------------
  47. void DInputManager::init()
  48. {
  49. Con::addVariable( "pref::Input::JoystickEnabled", TypeBool, &smJoystickEnabled,
  50. "@brief If true, the joystick is currently enabled.\n\n"
  51. "@ingroup Game");
  52. }
  53. //------------------------------------------------------------------------------
  54. bool DInputManager::enable()
  55. {
  56. FN_DirectInputCreate fnDInputCreate;
  57. disable();
  58. // Dynamically load the XInput 9 DLL and cache function pointers to the
  59. // two APIs we use
  60. #ifdef LOG_INPUT
  61. Input::log( "Enabling XInput...\n" );
  62. #endif
  63. mXInputLib = LoadLibrary( dT("xinput9_1_0.dll") );
  64. if ( mXInputLib )
  65. {
  66. mfnXInputGetState = (FN_XInputGetState) GetProcAddress( mXInputLib, "XInputGetState" );
  67. mfnXInputSetState = (FN_XInputSetState) GetProcAddress( mXInputLib, "XInputSetState" );
  68. if ( mfnXInputGetState && mfnXInputSetState )
  69. {
  70. #ifdef LOG_INPUT
  71. Input::log( "XInput detected.\n" );
  72. #endif
  73. mXInputStateReset = true;
  74. mXInputDeadZoneOn = true;
  75. smXInputEnabled = true;
  76. }
  77. }
  78. else
  79. {
  80. #ifdef LOG_INPUT
  81. Input::log( "XInput was not found.\n" );
  82. mXInputStateReset = false;
  83. mXInputDeadZoneOn = false;
  84. #endif
  85. }
  86. #ifdef LOG_INPUT
  87. Input::log( "Enabling DirectInput...\n" );
  88. #endif
  89. mDInputLib = LoadLibrary( dT("DInput8.dll") );
  90. if ( mDInputLib )
  91. {
  92. fnDInputCreate = (FN_DirectInputCreate) GetProcAddress( mDInputLib, "DirectInput8Create" );
  93. if ( fnDInputCreate )
  94. {
  95. bool result = SUCCEEDED( fnDInputCreate( winState.appInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast<void**>(&mDInputInterface), NULL ));
  96. if ( result )
  97. {
  98. #ifdef LOG_INPUT
  99. Input::log( "DirectX 8 or greater detected.\n" );
  100. #endif
  101. }
  102. if ( result )
  103. {
  104. enumerateDevices();
  105. mEnabled = true;
  106. return true;
  107. }
  108. }
  109. }
  110. disable();
  111. #ifdef LOG_INPUT
  112. Input::log( "Failed to enable DirectInput.\n" );
  113. #endif
  114. return false;
  115. }
  116. //------------------------------------------------------------------------------
  117. void DInputManager::disable()
  118. {
  119. unacquire( SI_ANY, SI_ANY );
  120. DInputDevice* dptr;
  121. iterator ptr = begin();
  122. while ( ptr != end() )
  123. {
  124. dptr = dynamic_cast<DInputDevice*>( *ptr );
  125. if ( dptr )
  126. {
  127. removeObject( dptr );
  128. //if ( dptr->getManager() )
  129. //dptr->getManager()->unregisterObject( dptr );
  130. delete dptr;
  131. ptr = begin();
  132. }
  133. else
  134. ptr++;
  135. }
  136. if ( mDInputInterface )
  137. {
  138. mDInputInterface->Release();
  139. mDInputInterface = NULL;
  140. }
  141. if ( mDInputLib )
  142. {
  143. FreeLibrary( mDInputLib );
  144. mDInputLib = NULL;
  145. }
  146. if ( mfnXInputGetState )
  147. {
  148. mXInputStateReset = true;
  149. mfnXInputGetState = NULL;
  150. mfnXInputSetState = NULL;
  151. }
  152. if ( mXInputLib )
  153. {
  154. FreeLibrary( mXInputLib );
  155. mXInputLib = NULL;
  156. }
  157. mEnabled = false;
  158. }
  159. //------------------------------------------------------------------------------
  160. void DInputManager::onDeleteNotify( SimObject* object )
  161. {
  162. Parent::onDeleteNotify( object );
  163. }
  164. //------------------------------------------------------------------------------
  165. bool DInputManager::onAdd()
  166. {
  167. if ( !Parent::onAdd() )
  168. return false;
  169. acquire( SI_ANY, SI_ANY );
  170. return true;
  171. }
  172. //------------------------------------------------------------------------------
  173. void DInputManager::onRemove()
  174. {
  175. unacquire( SI_ANY, SI_ANY );
  176. Parent::onRemove();
  177. }
  178. //------------------------------------------------------------------------------
  179. bool DInputManager::acquire( U8 deviceType, U8 deviceID )
  180. {
  181. bool anyActive = false;
  182. DInputDevice* dptr;
  183. for ( iterator ptr = begin(); ptr != end(); ptr++ )
  184. {
  185. dptr = dynamic_cast<DInputDevice*>( *ptr );
  186. if ( dptr
  187. && ( ( deviceType == SI_ANY ) || ( dptr->getDeviceType() == deviceType ) )
  188. && ( ( deviceID == SI_ANY ) || ( dptr->getDeviceID() == deviceID ) ) )
  189. {
  190. if ( dptr->acquire() )
  191. anyActive = true;
  192. }
  193. }
  194. return anyActive;
  195. }
  196. //------------------------------------------------------------------------------
  197. void DInputManager::unacquire( U8 deviceType, U8 deviceID )
  198. {
  199. DInputDevice* dptr;
  200. for ( iterator ptr = begin(); ptr != end(); ptr++ )
  201. {
  202. dptr = dynamic_cast<DInputDevice*>( *ptr );
  203. if ( dptr
  204. && ( ( deviceType == SI_ANY ) || ( dptr->getDeviceType() == deviceType ) )
  205. && ( ( deviceID == SI_ANY ) || ( dptr->getDeviceID() == deviceID ) ) )
  206. dptr->unacquire();
  207. }
  208. }
  209. //------------------------------------------------------------------------------
  210. void DInputManager::process()
  211. {
  212. // Because the XInput APIs manage everything for all four controllers trivially,
  213. // we don't need the abstraction of a DInputDevice for an unknown number of devices
  214. // with varying buttons & whistles... nice and easy!
  215. if ( isXInputActive() )
  216. processXInput();
  217. DInputDevice* dptr;
  218. for ( iterator ptr = begin(); ptr != end(); ptr++ )
  219. {
  220. dptr = dynamic_cast<DInputDevice*>( *ptr );
  221. if ( dptr )
  222. dptr->process();
  223. }
  224. }
  225. //------------------------------------------------------------------------------
  226. void DInputManager::enumerateDevices()
  227. {
  228. if ( mDInputInterface )
  229. {
  230. #ifdef LOG_INPUT
  231. Input::log( "Enumerating input devices...\n" );
  232. #endif
  233. DInputDevice::init();
  234. DInputDevice::smDInputInterface = mDInputInterface;
  235. mDInputInterface->EnumDevices( DI8DEVTYPE_KEYBOARD, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
  236. mDInputInterface->EnumDevices( DI8DEVTYPE_MOUSE, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
  237. mDInputInterface->EnumDevices( DI8DEVCLASS_GAMECTRL, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
  238. }
  239. }
  240. //------------------------------------------------------------------------------
  241. BOOL CALLBACK DInputManager::EnumDevicesProc( const DIDEVICEINSTANCE* pddi, LPVOID pvRef )
  242. {
  243. DInputManager* manager = (DInputManager*) pvRef;
  244. DInputDevice* newDevice = new DInputDevice( pddi );
  245. manager->addObject( newDevice );
  246. if ( !newDevice->create() )
  247. {
  248. manager->removeObject( newDevice );
  249. delete newDevice;
  250. }
  251. return (DIENUM_CONTINUE);
  252. }
  253. //------------------------------------------------------------------------------
  254. bool DInputManager::enableJoystick()
  255. {
  256. DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
  257. if ( !mgr || !mgr->isEnabled() )
  258. return( false );
  259. if ( smJoystickEnabled && mgr->isJoystickActive() )
  260. return( true );
  261. smJoystickEnabled = true;
  262. if ( Input::isActive() )
  263. smJoystickEnabled = mgr->activateJoystick();
  264. if ( smJoystickEnabled )
  265. {
  266. Con::printf( "DirectInput joystick enabled." );
  267. #ifdef LOG_INPUT
  268. Input::log( "Joystick enabled.\n" );
  269. #endif
  270. }
  271. else
  272. {
  273. Con::warnf( "DirectInput joystick failed to enable!" );
  274. #ifdef LOG_INPUT
  275. Input::log( "Joystick failed to enable!\n" );
  276. #endif
  277. }
  278. return( smJoystickEnabled );
  279. }
  280. //------------------------------------------------------------------------------
  281. void DInputManager::disableJoystick()
  282. {
  283. DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
  284. if ( !mgr || !mgr->isEnabled() || !smJoystickEnabled )
  285. return;
  286. mgr->deactivateJoystick();
  287. smJoystickEnabled = false;
  288. Con::printf( "DirectInput joystick disabled." );
  289. #ifdef LOG_INPUT
  290. Input::log( "Joystick disabled.\n" );
  291. #endif
  292. }
  293. //------------------------------------------------------------------------------
  294. bool DInputManager::isJoystickEnabled()
  295. {
  296. return( smJoystickEnabled );
  297. }
  298. //------------------------------------------------------------------------------
  299. bool DInputManager::activateJoystick()
  300. {
  301. if ( !mEnabled || !Input::isActive() || !smJoystickEnabled )
  302. return( false );
  303. mJoystickActive = acquire( JoystickDeviceType, SI_ANY );
  304. #ifdef LOG_INPUT
  305. Input::log( mJoystickActive ? "Joystick activated.\n" : "Joystick failed to activate!\n" );
  306. #endif
  307. return( mJoystickActive );
  308. }
  309. //------------------------------------------------------------------------------
  310. void DInputManager::deactivateJoystick()
  311. {
  312. if ( mEnabled && mJoystickActive )
  313. {
  314. unacquire( JoystickDeviceType, SI_ANY );
  315. mJoystickActive = false;
  316. #ifdef LOG_INPUT
  317. Input::log( "Joystick deactivated.\n" );
  318. #endif
  319. }
  320. }
  321. //------------------------------------------------------------------------------
  322. const char* DInputManager::getJoystickAxesString( U32 deviceID )
  323. {
  324. DInputDevice* dptr;
  325. for ( iterator ptr = begin(); ptr != end(); ptr++ )
  326. {
  327. dptr = dynamic_cast<DInputDevice*>( *ptr );
  328. if ( dptr && ( dptr->getDeviceType() == JoystickDeviceType ) && ( dptr->getDeviceID() == deviceID ) )
  329. return( dptr->getJoystickAxesString() );
  330. }
  331. return( "" );
  332. }
  333. //------------------------------------------------------------------------------
  334. bool DInputManager::enableXInput()
  335. {
  336. // Largely, this series of functions is identical to the Joystick versions,
  337. // except that XInput cannot be "activated" or "deactivated". You either have
  338. // the DLL or you don't. Beyond that, you have up to four controllers
  339. // connected at any given time
  340. DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
  341. if ( !mgr || !mgr->isEnabled() )
  342. return( false );
  343. if ( mgr->isXInputActive() )
  344. return( true );
  345. if ( Input::isActive() )
  346. mgr->activateXInput();
  347. if ( smXInputEnabled )
  348. {
  349. Con::printf( "XInput enabled." );
  350. #ifdef LOG_INPUT
  351. Input::log( "XInput enabled.\n" );
  352. #endif
  353. }
  354. else
  355. {
  356. Con::warnf( "XInput failed to enable!" );
  357. #ifdef LOG_INPUT
  358. Input::log( "XInput failed to enable!\n" );
  359. #endif
  360. }
  361. return( smXInputEnabled );
  362. }
  363. //------------------------------------------------------------------------------
  364. void DInputManager::disableXInput()
  365. {
  366. DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
  367. if ( !mgr || !mgr->isEnabled())
  368. return;
  369. mgr->deactivateXInput();
  370. Con::printf( "XInput disabled." );
  371. #ifdef LOG_INPUT
  372. Input::log( "XInput disabled.\n" );
  373. #endif
  374. }
  375. //------------------------------------------------------------------------------
  376. bool DInputManager::isXInputEnabled()
  377. {
  378. return( smXInputEnabled );
  379. }
  380. //------------------------------------------------------------------------------
  381. bool DInputManager::isXInputConnected(S32 controllerID)
  382. {
  383. return( mXInputStateNew[controllerID].bConnected );
  384. }
  385. int DInputManager::getXInputState(S32 controllerID, S32 property, bool current)
  386. {
  387. S32 retVal;
  388. switch(property)
  389. {
  390. #define CHECK_PROP_ANALOG(prop, stateTest) \
  391. case prop: (current) ? retVal = mXInputStateNew[controllerID].state.Gamepad.##stateTest : retVal = mXInputStateOld[controllerID].state.Gamepad.##stateTest; return retVal;
  392. CHECK_PROP_ANALOG(XI_THUMBLX, sThumbLX)
  393. CHECK_PROP_ANALOG(XI_THUMBLY, sThumbLY)
  394. CHECK_PROP_ANALOG(XI_THUMBRX, sThumbRX)
  395. CHECK_PROP_ANALOG(XI_THUMBRY, sThumbRY)
  396. CHECK_PROP_ANALOG(XI_LEFT_TRIGGER, bLeftTrigger)
  397. CHECK_PROP_ANALOG(XI_RIGHT_TRIGGER, bRightTrigger)
  398. #undef CHECK_PROP_ANALOG
  399. #define CHECK_PROP_DIGITAL(prop, stateTest) \
  400. case prop: (current) ? retVal = (( mXInputStateNew[controllerID].state.Gamepad.wButtons & stateTest ) != 0 ) : retVal = (( mXInputStateOld[controllerID].state.Gamepad.wButtons & stateTest ) != 0 ); return retVal;
  401. CHECK_PROP_DIGITAL(SI_UPOV, XINPUT_GAMEPAD_DPAD_UP)
  402. CHECK_PROP_DIGITAL(SI_DPOV, XINPUT_GAMEPAD_DPAD_DOWN)
  403. CHECK_PROP_DIGITAL(SI_LPOV, XINPUT_GAMEPAD_DPAD_LEFT)
  404. CHECK_PROP_DIGITAL(SI_RPOV, XINPUT_GAMEPAD_DPAD_RIGHT)
  405. CHECK_PROP_DIGITAL(XI_START, XINPUT_GAMEPAD_START)
  406. CHECK_PROP_DIGITAL(XI_BACK, XINPUT_GAMEPAD_BACK)
  407. CHECK_PROP_DIGITAL(XI_LEFT_THUMB, XINPUT_GAMEPAD_LEFT_THUMB)
  408. CHECK_PROP_DIGITAL(XI_RIGHT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB)
  409. CHECK_PROP_DIGITAL(XI_LEFT_SHOULDER, XINPUT_GAMEPAD_LEFT_SHOULDER)
  410. CHECK_PROP_DIGITAL(XI_RIGHT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER)
  411. CHECK_PROP_DIGITAL(XI_A, XINPUT_GAMEPAD_A)
  412. CHECK_PROP_DIGITAL(XI_B, XINPUT_GAMEPAD_B)
  413. CHECK_PROP_DIGITAL(XI_X, XINPUT_GAMEPAD_X)
  414. CHECK_PROP_DIGITAL(XI_Y, XINPUT_GAMEPAD_Y)
  415. #undef CHECK_PROP_DIGITAL
  416. }
  417. return -1;
  418. }
  419. //------------------------------------------------------------------------------
  420. bool DInputManager::activateXInput()
  421. {
  422. if ( !mEnabled || !Input::isActive())
  423. return( false );
  424. mXInputActive = true; //acquire( GamepadDeviceType, SI_ANY );
  425. #ifdef LOG_INPUT
  426. Input::log( mXInputActive ? "XInput activated.\n" : "XInput failed to activate!\n" );
  427. #endif
  428. return( mXInputActive );
  429. }
  430. //------------------------------------------------------------------------------
  431. void DInputManager::deactivateXInput()
  432. {
  433. if ( mEnabled && mXInputActive )
  434. {
  435. unacquire( GamepadDeviceType, SI_ANY );
  436. mXInputActive = false;
  437. #ifdef LOG_INPUT
  438. Input::log( "XInput deactivated.\n" );
  439. #endif
  440. }
  441. }
  442. //------------------------------------------------------------------------------
  443. bool DInputManager::rumble( const char *pDeviceName, F32 x, F32 y )
  444. {
  445. // Determine the device
  446. U32 deviceType;
  447. U32 deviceInst;
  448. // Find the requested device
  449. if ( !ActionMap::getDeviceTypeAndInstance(pDeviceName, deviceType, deviceInst) )
  450. {
  451. Con::printf("DInputManager::rumble: unknown device: %s", pDeviceName);
  452. return false;
  453. }
  454. // clamp (x, y) to the range of [0 ... 1] each
  455. x = mClampF(x, 0.f, 1.f);
  456. y = mClampF(y, 0.f, 1.f);
  457. // Easy path for xinput devices.
  458. if(deviceType == GamepadDeviceType)
  459. {
  460. XINPUT_VIBRATION vibration;
  461. vibration.wLeftMotorSpeed = static_cast<int>(x * 65535);
  462. vibration.wRightMotorSpeed = static_cast<int>(y * 65535);
  463. return ( mfnXInputSetState(deviceInst, &vibration) == ERROR_SUCCESS );
  464. }
  465. switch ( deviceType )
  466. {
  467. case JoystickDeviceType:
  468. // Find the device and shake it!
  469. DInputDevice* dptr;
  470. for ( iterator ptr = begin(); ptr != end(); ptr++ )
  471. {
  472. dptr = dynamic_cast<DInputDevice*>( *ptr );
  473. if ( dptr )
  474. {
  475. if (deviceType == dptr->getDeviceType() && deviceInst == dptr->getDeviceID())
  476. {
  477. dptr->rumble(x, y);
  478. return true;
  479. }
  480. }
  481. }
  482. // We should never get here... something's really messed up
  483. Con::errorf( "DInputManager::rumbleJoystick - Couldn't find device to rumble! This should never happen!\n" );
  484. return false;
  485. default:
  486. Con::printf("DInputManager::rumble - only supports joystick and xinput/gamepad devices");
  487. return false;
  488. }
  489. }
  490. void DInputManager::buildXInputEvent( U32 deviceInst, InputEventType objType, InputObjectInstances objInst, InputActionType action, F32 fValue )
  491. {
  492. InputEventInfo newEvent;
  493. newEvent.deviceType = GamepadDeviceType;
  494. newEvent.deviceInst = deviceInst;
  495. newEvent.objType = objType;
  496. newEvent.objInst = objInst;
  497. newEvent.action = action;
  498. newEvent.fValue = fValue;
  499. newEvent.postToSignal(Input::smInputEvent);
  500. }
  501. // The next three functions: fireXInputConnectEvent, fireXInputMoveEvent, and fireXInputButtonEvent
  502. // determine whether a "delta" has occurred between the last captured controller state and the
  503. // currently captured controller state and only if so, do we fire an event. The shortcutter
  504. // "mXInputStateReset" is the exception and is true whenever DirectInput gets reset (because
  505. // the user ALT-TABBED away, for example). That means that after every context switch,
  506. // you will get a full set of updates on the "true" state of the controller.
  507. inline void DInputManager::fireXInputConnectEvent( S32 controllerID, bool condition, bool connected )
  508. {
  509. if ( mXInputStateReset || condition )
  510. {
  511. #ifdef LOG_INPUT
  512. Input::log( "EVENT (XInput): xinput%d CONNECT %s\n", controllerID, connected ? "make" : "break" );
  513. #endif
  514. buildXInputEvent( controllerID, SI_BUTTON, XI_CONNECT, connected ? SI_MAKE : SI_BREAK, 0);
  515. }
  516. }
  517. inline void DInputManager::fireXInputMoveEvent( S32 controllerID, bool condition, InputObjectInstances objInst, F32 fValue )
  518. {
  519. if ( mXInputStateReset || condition )
  520. {
  521. #ifdef LOG_INPUT
  522. char *objName;
  523. switch (objInst)
  524. {
  525. case XI_THUMBLX: objName = "THUMBLX"; break;
  526. case XI_THUMBLY: objName = "THUMBLY"; break;
  527. case XI_THUMBRX: objName = "THUMBRX"; break;
  528. case XI_THUMBRY: objName = "THUMBRY"; break;
  529. case XI_LEFT_TRIGGER: objName = "LEFT_TRIGGER"; break;
  530. case XI_RIGHT_TRIGGER: objName = "RIGHT_TRIGGER"; break;
  531. default: objName = "UNKNOWN"; break;
  532. }
  533. Input::log( "EVENT (XInput): xinput%d %s MOVE %.1f.\n", controllerID, objName, fValue );
  534. #endif
  535. buildXInputEvent( controllerID, SI_AXIS, objInst, SI_MOVE, fValue );
  536. }
  537. }
  538. inline void DInputManager::fireXInputButtonEvent( S32 controllerID, bool forceFire, S32 button, InputObjectInstances objInst )
  539. {
  540. if ( mXInputStateReset || forceFire || ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != (mXInputStateOld[controllerID].state.Gamepad.wButtons & button)) )
  541. {
  542. #ifdef LOG_INPUT
  543. char *objName;
  544. switch (objInst)
  545. {
  546. /*
  547. case XI_DPAD_UP: objName = "DPAD_UP"; break;
  548. case XI_DPAD_DOWN: objName = "DPAD_DOWN"; break;
  549. case XI_DPAD_LEFT: objName = "DPAD_LEFT"; break;
  550. case XI_DPAD_RIGHT: objName = "DPAD_RIGHT"; break;
  551. */
  552. case XI_START: objName = "START"; break;
  553. case XI_BACK: objName = "BACK"; break;
  554. case XI_LEFT_THUMB: objName = "LEFT_THUMB"; break;
  555. case XI_RIGHT_THUMB: objName = "RIGHT_THUMB"; break;
  556. case XI_LEFT_SHOULDER: objName = "LEFT_SHOULDER"; break;
  557. case XI_RIGHT_SHOULDER: objName = "RIGHT_SHOULDER"; break;
  558. case XI_A: objName = "A"; break;
  559. case XI_B: objName = "B"; break;
  560. case XI_X: objName = "X"; break;
  561. case XI_Y: objName = "Y"; break;
  562. default: objName = "UNKNOWN"; break;
  563. }
  564. Input::log( "EVENT (XInput): xinput%d %s %s\n", controllerID, objName, ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != 0) ? "make" : "break" );
  565. #endif
  566. InputActionType action = ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != 0) ? SI_MAKE : SI_BREAK;
  567. buildXInputEvent( controllerID, SI_BUTTON, objInst, action, ( action == SI_MAKE ? 1 : 0 ) );
  568. }
  569. }
  570. void DInputManager::processXInput( void )
  571. {
  572. const U32 curTime = Platform::getRealMilliseconds();
  573. // We only want to check one disconnected device per frame.
  574. bool foundDisconnected = false;
  575. if ( mfnXInputGetState )
  576. {
  577. for ( S32 i=0; i<4; i++ )
  578. {
  579. // Calling XInputGetState on a disconnected controller takes a fair
  580. // amount of time (probably because it tries to locate it), so we
  581. // add a delay - only check every 250ms or so.
  582. if(mLastDisconnectTime[i] != -1)
  583. {
  584. // If it's not -1, then it was disconnected list time we checked.
  585. // So skip until it's time.
  586. if((curTime-mLastDisconnectTime[i]) < csmDisconnectedSkipDelay)
  587. {
  588. continue;
  589. }
  590. // If we already checked a disconnected controller, don't try any
  591. // further potentially disconnected devices.
  592. if(foundDisconnected)
  593. {
  594. // If we're skipping this, defer it out by the skip delay
  595. // so we don't get clumped checks.
  596. mLastDisconnectTime[i] += csmDisconnectedSkipDelay;
  597. continue;
  598. }
  599. }
  600. mXInputStateOld[i] = mXInputStateNew[i];
  601. mXInputStateNew[i].bConnected = ( mfnXInputGetState( i, &mXInputStateNew[i].state ) == ERROR_SUCCESS );
  602. // Update the last connected time.
  603. if(mXInputStateNew[i].bConnected)
  604. mLastDisconnectTime[i] = -1;
  605. else
  606. {
  607. foundDisconnected = true;
  608. mLastDisconnectTime[i] = curTime;
  609. }
  610. // trim the controller's thumbsticks to zero if they are within the deadzone
  611. if( mXInputDeadZoneOn )
  612. {
  613. // Zero value if thumbsticks are within the dead zone
  614. if( (mXInputStateNew[i].state.Gamepad.sThumbLX < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbLX > -XINPUT_DEADZONE) &&
  615. (mXInputStateNew[i].state.Gamepad.sThumbLY < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbLY > -XINPUT_DEADZONE) )
  616. {
  617. mXInputStateNew[i].state.Gamepad.sThumbLX = 0;
  618. mXInputStateNew[i].state.Gamepad.sThumbLY = 0;
  619. }
  620. if( (mXInputStateNew[i].state.Gamepad.sThumbRX < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbRX > -XINPUT_DEADZONE) &&
  621. (mXInputStateNew[i].state.Gamepad.sThumbRY < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbRY > -XINPUT_DEADZONE) )
  622. {
  623. mXInputStateNew[i].state.Gamepad.sThumbRX = 0;
  624. mXInputStateNew[i].state.Gamepad.sThumbRY = 0;
  625. }
  626. }
  627. // this controller was connected or disconnected
  628. bool bJustConnected = ( ( mXInputStateOld[i].bConnected != mXInputStateNew[i].bConnected ) && ( mXInputStateNew[i].bConnected ) );
  629. fireXInputConnectEvent( i, (mXInputStateOld[i].bConnected != mXInputStateNew[i].bConnected), mXInputStateNew[i].bConnected );
  630. // If this controller is disconnected, stop reporting events for it
  631. if ( !mXInputStateNew[i].bConnected )
  632. continue;
  633. // == LEFT THUMBSTICK ==
  634. fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbLX != mXInputStateOld[i].state.Gamepad.sThumbLX), XI_THUMBLX, (mXInputStateNew[i].state.Gamepad.sThumbLX / 32768.0f) );
  635. fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbLY != mXInputStateOld[i].state.Gamepad.sThumbLY), XI_THUMBLY, (mXInputStateNew[i].state.Gamepad.sThumbLY / 32768.0f) );
  636. // == RIGHT THUMBSTICK ==
  637. fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbRX != mXInputStateOld[i].state.Gamepad.sThumbRX), XI_THUMBRX, (mXInputStateNew[i].state.Gamepad.sThumbRX / 32768.0f) );
  638. fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbRY != mXInputStateOld[i].state.Gamepad.sThumbRY), XI_THUMBRY, (mXInputStateNew[i].state.Gamepad.sThumbRY / 32768.0f) );
  639. // == LEFT & RIGHT REAR TRIGGERS ==
  640. fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.bLeftTrigger != mXInputStateOld[i].state.Gamepad.bLeftTrigger), XI_LEFT_TRIGGER, (mXInputStateNew[i].state.Gamepad.bLeftTrigger / 255.0f) );
  641. fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.bRightTrigger != mXInputStateOld[i].state.Gamepad.bRightTrigger), XI_RIGHT_TRIGGER, (mXInputStateNew[i].state.Gamepad.bRightTrigger / 255.0f) );
  642. // == BUTTONS: DPAD ==
  643. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_UP, SI_UPOV );
  644. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_DOWN, SI_DPOV );
  645. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_LEFT, SI_LPOV );
  646. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_RIGHT, SI_RPOV );
  647. // == BUTTONS: START & BACK ==
  648. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_START, XI_START );
  649. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_BACK, XI_BACK );
  650. // == BUTTONS: LEFT AND RIGHT THUMBSTICK ==
  651. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_LEFT_THUMB, XI_LEFT_THUMB );
  652. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_RIGHT_THUMB, XI_RIGHT_THUMB );
  653. // == BUTTONS: LEFT AND RIGHT SHOULDERS (formerly WHITE and BLACK on Xbox 1) ==
  654. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_LEFT_SHOULDER, XI_LEFT_SHOULDER );
  655. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_RIGHT_SHOULDER, XI_RIGHT_SHOULDER );
  656. // == BUTTONS: A, B, X, and Y ==
  657. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_A, XI_A );
  658. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_B, XI_B );
  659. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_X, XI_X );
  660. fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_Y, XI_Y );
  661. }
  662. if ( mXInputStateReset )
  663. mXInputStateReset = false;
  664. }
  665. }
  666. ConsoleFunction( enableJoystick, bool, 1, 1, "()"
  667. "@brief Enables use of the joystick.\n\n"
  668. "@note DirectInput must be enabled and active to use this function.\n\n"
  669. "@ingroup Input")
  670. {
  671. argc; argv;
  672. return( DInputManager::enableJoystick() );
  673. }
  674. //------------------------------------------------------------------------------
  675. ConsoleFunction( disableJoystick, void, 1, 1,"()"
  676. "@brief Disables use of the joystick.\n\n"
  677. "@note DirectInput must be enabled and active to use this function.\n\n"
  678. "@ingroup Input")
  679. {
  680. argc; argv;
  681. DInputManager::disableJoystick();
  682. }
  683. //------------------------------------------------------------------------------
  684. ConsoleFunction( isJoystickEnabled, bool, 1, 1, "()"
  685. "@brief Queries input manager to see if a joystick is enabled\n\n"
  686. "@return 1 if a joystick exists and is enabled, 0 if it's not.\n"
  687. "@ingroup Input")
  688. {
  689. argc; argv;
  690. return DInputManager::isJoystickEnabled();
  691. }
  692. //------------------------------------------------------------------------------
  693. ConsoleFunction( enableXInput, bool, 1, 1, "()"
  694. "@brief Enables XInput for Xbox 360 controllers.\n\n"
  695. "@note XInput is enabled by default. Disable to use an Xbox 360 "
  696. "Controller as a joystick device.\n\n"
  697. "@ingroup Input")
  698. {
  699. // Although I said above that you couldn't change the "activation" of XInput,
  700. // you can enable and disable it. It gets enabled by default if you have the
  701. // DLL. You would want to disable it if you have 360 controllers and want to
  702. // read them as joysticks... why you'd want to do that is beyond me
  703. argc; argv;
  704. return( DInputManager::enableXInput() );
  705. }
  706. //------------------------------------------------------------------------------
  707. ConsoleFunction( disableXInput, void, 1, 1, "()"
  708. "@brief Disables XInput for Xbox 360 controllers.\n\n"
  709. "@ingroup Input")
  710. {
  711. argc; argv;
  712. DInputManager::disableXInput();
  713. }
  714. //------------------------------------------------------------------------------
  715. ConsoleFunction( resetXInput, void, 1, 1, "()"
  716. "@brief Rebuilds the XInput section of the InputManager\n\n"
  717. "Requests a full refresh of events for all controllers. Useful when called at the beginning "
  718. "of game code after actionMaps are set up to hook up all appropriate events.\n\n"
  719. "@ingroup Input")
  720. {
  721. // This function requests a full "refresh" of events for all controllers the
  722. // next time we go through the input processing loop. This is useful to call
  723. // at the beginning of your game code after your actionMap is set up to hook
  724. // all of the appropriate events
  725. argc; argv;
  726. DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
  727. if ( mgr && mgr->isEnabled() )
  728. mgr->resetXInput();
  729. }
  730. //------------------------------------------------------------------------------
  731. ConsoleFunction( isXInputConnected, bool, 2, 2, "( int controllerID )"
  732. "@brief Checks to see if an Xbox 360 controller is connected\n\n"
  733. "@param controllerID Zero-based index of the controller to check.\n"
  734. "@return 1 if the controller is connected, 0 if it isn't, and 205 if XInput "
  735. "hasn't been initialized."
  736. "@ingroup Input")
  737. {
  738. argc; argv;
  739. DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
  740. if ( mgr && mgr->isEnabled() ) return mgr->isXInputConnected( atoi( argv[1] ) );
  741. return false;
  742. }
  743. //------------------------------------------------------------------------------
  744. ConsoleFunction( getXInputState, S32, 3, 4, "( int controllerID, string property, bool current )"
  745. "@brief Queries the current state of a connected Xbox 360 controller.\n\n"
  746. "XInput Properties:\n\n"
  747. " - XI_THUMBLX, XI_THUMBLY - X and Y axes of the left thumbstick. \n"
  748. " - XI_THUMBRX, XI_THUMBRY - X and Y axes of the right thumbstick. \n"
  749. " - XI_LEFT_TRIGGER, XI_RIGHT_TRIGGER - Left and Right triggers. \n"
  750. " - SI_UPOV, SI_DPOV, SI_LPOV, SI_RPOV - Up, Down, Left, and Right on the directional pad.\n"
  751. " - XI_START, XI_BACK - The Start and Back buttons.\n"
  752. " - XI_LEFT_THUMB, XI_RIGHT_THUMB - Clicking in the left and right thumbstick.\n"
  753. " - XI_LEFT_SHOULDER, XI_RIGHT_SHOULDER - Left and Right bumpers.\n"
  754. " - XI_A, XI_B , XI_X, XI_Y - The A, B, X, and Y buttons.\n\n"
  755. "@param controllerID Zero-based index of the controller to return information about.\n"
  756. "@param property Name of input action being queried, such as \"XI_THUMBLX\".\n"
  757. "@param current True checks current device in action.\n"
  758. "@return Button queried - 1 if the button is pressed, 0 if it's not.\n"
  759. "@return Thumbstick queried - Int representing displacement from rest position."
  760. "@return %Trigger queried - Int from 0 to 255 representing how far the trigger is displaced."
  761. "@ingroup Input")
  762. {
  763. argc; argv;
  764. DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
  765. if ( !mgr || !mgr->isEnabled() )
  766. return -1;
  767. // Use a little bit of macro magic to simplify this otherwise monolothic
  768. // block of code.
  769. #define GET_XI_STATE(constName) \
  770. if (!dStricmp(argv[2], #constName)) \
  771. return mgr->getXInputState( dAtoi( argv[1] ), constName, ( dAtoi ( argv[3] ) == 1) );
  772. GET_XI_STATE(XI_THUMBLX);
  773. GET_XI_STATE(XI_THUMBLY);
  774. GET_XI_STATE(XI_THUMBRX);
  775. GET_XI_STATE(XI_THUMBRY);
  776. GET_XI_STATE(XI_LEFT_TRIGGER);
  777. GET_XI_STATE(XI_RIGHT_TRIGGER);
  778. GET_XI_STATE(SI_UPOV);
  779. GET_XI_STATE(SI_DPOV);
  780. GET_XI_STATE(SI_LPOV);
  781. GET_XI_STATE(SI_RPOV);
  782. GET_XI_STATE(XI_START);
  783. GET_XI_STATE(XI_BACK);
  784. GET_XI_STATE(XI_LEFT_THUMB);
  785. GET_XI_STATE(XI_RIGHT_THUMB);
  786. GET_XI_STATE(XI_LEFT_SHOULDER);
  787. GET_XI_STATE(XI_RIGHT_SHOULDER);
  788. GET_XI_STATE(XI_A);
  789. GET_XI_STATE(XI_B);
  790. GET_XI_STATE(XI_X);
  791. GET_XI_STATE(XI_Y);
  792. #undef GET_XI_STATE
  793. return -1;
  794. }
  795. //------------------------------------------------------------------------------
  796. ConsoleFunction( echoInputState, void, 1, 1, "()"
  797. "@brief Prints information to the console stating if DirectInput and a Joystick are enabled and active.\n\n"
  798. "@ingroup Input")
  799. {
  800. argc; argv;
  801. DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
  802. if ( mgr && mgr->isEnabled() )
  803. {
  804. Con::printf( "DirectInput is enabled %s.", Input::isActive() ? "and active" : "but inactive" );
  805. Con::printf( "- Joystick is %sabled and %sactive.",
  806. mgr->isJoystickEnabled() ? "en" : "dis",
  807. mgr->isJoystickActive() ? "" : "in" );
  808. }
  809. else
  810. Con::printf( "DirectInput is not enabled." );
  811. }
  812. ConsoleFunction( rumble, void, 4, 4, "(string device, float xRumble, float yRumble)"
  813. "@brief Activates the vibration motors in the specified controller.\n\n"
  814. "The controller will constantly at it's xRumble and yRumble intensities until "
  815. "changed or told to stop."
  816. "Valid inputs for xRumble/yRumble are [0 - 1].\n"
  817. "@param device Name of the device to rumble.\n"
  818. "@param xRumble Intensity to apply to the left motor.\n"
  819. "@param yRumble Intensity to apply to the right motor.\n"
  820. "@note in an Xbox 360 controller, the left motor is low-frequency, "
  821. "while the right motor is high-frequency."
  822. "@ingroup Input")
  823. {
  824. DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
  825. if ( mgr && mgr->isEnabled() )
  826. {
  827. mgr->rumble(argv[1], dAtof(argv[2]), dAtof(argv[3]));
  828. }
  829. else
  830. {
  831. Con::printf( "DirectInput/XInput is not enabled." );
  832. }
  833. }