winDirectInput.cpp 34 KB

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