winDirectInput.cpp 34 KB

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