win_input.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../../idlib/precompiled.h"
  22. #include "../sys_session_local.h"
  23. #include "win_local.h"
  24. #define DINPUT_BUFFERSIZE 256
  25. /*
  26. ============================================================
  27. DIRECT INPUT KEYBOARD CONTROL
  28. ============================================================
  29. */
  30. bool IN_StartupKeyboard() {
  31. HRESULT hr;
  32. bool bExclusive;
  33. bool bForeground;
  34. bool bImmediate;
  35. bool bDisableWindowsKey;
  36. DWORD dwCoopFlags;
  37. if (!win32.g_pdi) {
  38. common->Printf("keyboard: DirectInput has not been started\n");
  39. return false;
  40. }
  41. if (win32.g_pKeyboard) {
  42. win32.g_pKeyboard->Release();
  43. win32.g_pKeyboard = NULL;
  44. }
  45. // Detrimine where the buffer would like to be allocated
  46. bExclusive = false;
  47. bForeground = true;
  48. bImmediate = false;
  49. bDisableWindowsKey = true;
  50. if( bExclusive )
  51. dwCoopFlags = DISCL_EXCLUSIVE;
  52. else
  53. dwCoopFlags = DISCL_NONEXCLUSIVE;
  54. if( bForeground )
  55. dwCoopFlags |= DISCL_FOREGROUND;
  56. else
  57. dwCoopFlags |= DISCL_BACKGROUND;
  58. // Disabling the windows key is only allowed only if we are in foreground nonexclusive
  59. if( bDisableWindowsKey && !bExclusive && bForeground )
  60. dwCoopFlags |= DISCL_NOWINKEY;
  61. // Obtain an interface to the system keyboard device.
  62. if( FAILED( hr = win32.g_pdi->CreateDevice( GUID_SysKeyboard, &win32.g_pKeyboard, NULL ) ) ) {
  63. common->Printf("keyboard: couldn't find a keyboard device\n");
  64. return false;
  65. }
  66. // Set the data format to "keyboard format" - a predefined data format
  67. //
  68. // A data format specifies which controls on a device we
  69. // are interested in, and how they should be reported.
  70. //
  71. // This tells DirectInput that we will be passing an array
  72. // of 256 bytes to IDirectInputDevice::GetDeviceState.
  73. if( FAILED( hr = win32.g_pKeyboard->SetDataFormat( &c_dfDIKeyboard ) ) )
  74. return false;
  75. // Set the cooperativity level to let DirectInput know how
  76. // this device should interact with the system and with other
  77. // DirectInput applications.
  78. hr = win32.g_pKeyboard->SetCooperativeLevel( win32.hWnd, dwCoopFlags );
  79. if( hr == DIERR_UNSUPPORTED && !bForeground && bExclusive ) {
  80. common->Printf("keyboard: SetCooperativeLevel() returned DIERR_UNSUPPORTED.\nFor security reasons, background exclusive keyboard access is not allowed.\n");
  81. return false;
  82. }
  83. if( FAILED(hr) ) {
  84. return false;
  85. }
  86. if( !bImmediate ) {
  87. // IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
  88. //
  89. // DirectInput uses unbuffered I/O (buffer size = 0) by default.
  90. // If you want to read buffered data, you need to set a nonzero
  91. // buffer size.
  92. //
  93. // Set the buffer size to DINPUT_BUFFERSIZE (defined above) elements.
  94. //
  95. // The buffer size is a DWORD property associated with the device.
  96. DIPROPDWORD dipdw;
  97. dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  98. dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  99. dipdw.diph.dwObj = 0;
  100. dipdw.diph.dwHow = DIPH_DEVICE;
  101. dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
  102. if( FAILED( hr = win32.g_pKeyboard->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) )
  103. return false;
  104. }
  105. // Acquire the newly created device
  106. win32.g_pKeyboard->Acquire();
  107. common->Printf( "keyboard: DirectInput initialized.\n");
  108. return true;
  109. }
  110. /*
  111. ==========================
  112. IN_DeactivateKeyboard
  113. ==========================
  114. */
  115. void IN_DeactivateKeyboard() {
  116. if (!win32.g_pKeyboard) {
  117. return;
  118. }
  119. win32.g_pKeyboard->Unacquire( );
  120. }
  121. /*
  122. ============================================================
  123. DIRECT INPUT MOUSE CONTROL
  124. ============================================================
  125. */
  126. /*
  127. ========================
  128. IN_InitDirectInput
  129. ========================
  130. */
  131. void IN_InitDirectInput() {
  132. HRESULT hr;
  133. common->Printf( "Initializing DirectInput...\n" );
  134. if ( win32.g_pdi != NULL ) {
  135. win32.g_pdi->Release(); // if the previous window was destroyed we need to do this
  136. win32.g_pdi = NULL;
  137. }
  138. // Register with the DirectInput subsystem and get a pointer
  139. // to a IDirectInput interface we can use.
  140. // Create the base DirectInput object
  141. if ( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&win32.g_pdi, NULL ) ) ) {
  142. common->Printf ("DirectInputCreate failed\n");
  143. }
  144. }
  145. /*
  146. ========================
  147. IN_InitDIMouse
  148. ========================
  149. */
  150. bool IN_InitDIMouse() {
  151. HRESULT hr;
  152. if ( win32.g_pdi == NULL) {
  153. return false;
  154. }
  155. // obtain an interface to the system mouse device.
  156. hr = win32.g_pdi->CreateDevice( GUID_SysMouse, &win32.g_pMouse, NULL);
  157. if (FAILED(hr)) {
  158. common->Printf ("mouse: Couldn't open DI mouse device\n");
  159. return false;
  160. }
  161. // Set the data format to "mouse format" - a predefined data format
  162. //
  163. // A data format specifies which controls on a device we
  164. // are interested in, and how they should be reported.
  165. //
  166. // This tells DirectInput that we will be passing a
  167. // DIMOUSESTATE2 structure to IDirectInputDevice::GetDeviceState.
  168. if( FAILED( hr = win32.g_pMouse->SetDataFormat( &c_dfDIMouse2 ) ) ) {
  169. common->Printf ("mouse: Couldn't set DI mouse format\n");
  170. return false;
  171. }
  172. // set the cooperativity level.
  173. hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
  174. if (FAILED(hr)) {
  175. common->Printf ("mouse: Couldn't set DI coop level\n");
  176. return false;
  177. }
  178. // IMPORTANT STEP TO USE BUFFERED DEVICE DATA!
  179. //
  180. // DirectInput uses unbuffered I/O (buffer size = 0) by default.
  181. // If you want to read buffered data, you need to set a nonzero
  182. // buffer size.
  183. //
  184. // Set the buffer size to SAMPLE_BUFFER_SIZE (defined above) elements.
  185. //
  186. // The buffer size is a DWORD property associated with the device.
  187. DIPROPDWORD dipdw;
  188. dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  189. dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  190. dipdw.diph.dwObj = 0;
  191. dipdw.diph.dwHow = DIPH_DEVICE;
  192. dipdw.dwData = DINPUT_BUFFERSIZE; // Arbitary buffer size
  193. if( FAILED( hr = win32.g_pMouse->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph ) ) ) {
  194. common->Printf ("mouse: Couldn't set DI buffersize\n");
  195. return false;
  196. }
  197. IN_ActivateMouse();
  198. // clear any pending samples
  199. int mouseEvents[MAX_MOUSE_EVENTS][2];
  200. Sys_PollMouseInputEvents( mouseEvents );
  201. common->Printf( "mouse: DirectInput initialized.\n");
  202. return true;
  203. }
  204. /*
  205. ==========================
  206. IN_ActivateMouse
  207. ==========================
  208. */
  209. void IN_ActivateMouse() {
  210. int i;
  211. HRESULT hr;
  212. if ( !win32.in_mouse.GetBool() || win32.mouseGrabbed || !win32.g_pMouse ) {
  213. return;
  214. }
  215. win32.mouseGrabbed = true;
  216. for ( i = 0; i < 10; i++ ) {
  217. if ( ::ShowCursor( false ) < 0 ) {
  218. break;
  219. }
  220. }
  221. // we may fail to reacquire if the window has been recreated
  222. hr = win32.g_pMouse->Acquire();
  223. if (FAILED(hr)) {
  224. return;
  225. }
  226. // set the cooperativity level.
  227. hr = win32.g_pMouse->SetCooperativeLevel( win32.hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
  228. }
  229. /*
  230. ==========================
  231. IN_DeactivateMouse
  232. ==========================
  233. */
  234. void IN_DeactivateMouse() {
  235. int i;
  236. if (!win32.g_pMouse || !win32.mouseGrabbed ) {
  237. return;
  238. }
  239. win32.g_pMouse->Unacquire();
  240. for ( i = 0; i < 10; i++ ) {
  241. if ( ::ShowCursor( true ) >= 0 ) {
  242. break;
  243. }
  244. }
  245. win32.mouseGrabbed = false;
  246. }
  247. /*
  248. ==========================
  249. IN_DeactivateMouseIfWindowed
  250. ==========================
  251. */
  252. void IN_DeactivateMouseIfWindowed() {
  253. if ( !win32.cdsFullscreen ) {
  254. IN_DeactivateMouse();
  255. }
  256. }
  257. /*
  258. ============================================================
  259. MOUSE CONTROL
  260. ============================================================
  261. */
  262. /*
  263. ===========
  264. Sys_ShutdownInput
  265. ===========
  266. */
  267. void Sys_ShutdownInput() {
  268. IN_DeactivateMouse();
  269. IN_DeactivateKeyboard();
  270. if ( win32.g_pKeyboard ) {
  271. win32.g_pKeyboard->Release();
  272. win32.g_pKeyboard = NULL;
  273. }
  274. if ( win32.g_pMouse ) {
  275. win32.g_pMouse->Release();
  276. win32.g_pMouse = NULL;
  277. }
  278. if ( win32.g_pdi ) {
  279. win32.g_pdi->Release();
  280. win32.g_pdi = NULL;
  281. }
  282. }
  283. /*
  284. ===========
  285. Sys_InitInput
  286. ===========
  287. */
  288. void Sys_InitInput() {
  289. common->Printf ("\n------- Input Initialization -------\n");
  290. IN_InitDirectInput();
  291. if ( win32.in_mouse.GetBool() ) {
  292. IN_InitDIMouse();
  293. // don't grab the mouse on initialization
  294. Sys_GrabMouseCursor( false );
  295. } else {
  296. common->Printf ("Mouse control not active.\n");
  297. }
  298. IN_StartupKeyboard();
  299. common->Printf ("------------------------------------\n");
  300. win32.in_mouse.ClearModified();
  301. }
  302. /*
  303. ==================
  304. IN_Frame
  305. Called every frame, even if not generating commands
  306. ==================
  307. */
  308. void IN_Frame() {
  309. bool shouldGrab = true;
  310. if ( !win32.in_mouse.GetBool() ) {
  311. shouldGrab = false;
  312. }
  313. // if fullscreen, we always want the mouse
  314. if ( !win32.cdsFullscreen ) {
  315. if ( win32.mouseReleased ) {
  316. shouldGrab = false;
  317. }
  318. if ( win32.movingWindow ) {
  319. shouldGrab = false;
  320. }
  321. if ( !win32.activeApp ) {
  322. shouldGrab = false;
  323. }
  324. }
  325. if ( shouldGrab != win32.mouseGrabbed ) {
  326. if ( usercmdGen != NULL ) {
  327. usercmdGen->Clear();
  328. }
  329. if ( win32.mouseGrabbed ) {
  330. IN_DeactivateMouse();
  331. } else {
  332. IN_ActivateMouse();
  333. #if 0 // if we can't reacquire, try reinitializing
  334. if ( !IN_InitDIMouse() ) {
  335. win32.in_mouse.SetBool( false );
  336. return;
  337. }
  338. #endif
  339. }
  340. }
  341. }
  342. void Sys_GrabMouseCursor( bool grabIt ) {
  343. win32.mouseReleased = !grabIt;
  344. if ( !grabIt ) {
  345. // release it right now
  346. IN_Frame();
  347. }
  348. }
  349. //=====================================================================================
  350. static DIDEVICEOBJECTDATA polled_didod[ DINPUT_BUFFERSIZE ]; // Receives buffered data
  351. static int diFetch;
  352. static byte toggleFetch[2][ 256 ];
  353. #if 1
  354. // I tried doing the full-state get to address a keyboard problem on one system,
  355. // but it didn't make any difference
  356. /*
  357. ====================
  358. Sys_PollKeyboardInputEvents
  359. ====================
  360. */
  361. int Sys_PollKeyboardInputEvents() {
  362. DWORD dwElements;
  363. HRESULT hr;
  364. if( win32.g_pKeyboard == NULL ) {
  365. return 0;
  366. }
  367. dwElements = DINPUT_BUFFERSIZE;
  368. hr = win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
  369. polled_didod, &dwElements, 0 );
  370. if( hr != DI_OK )
  371. {
  372. // We got an error or we got DI_BUFFEROVERFLOW.
  373. //
  374. // Either way, it means that continuous contact with the
  375. // device has been lost, either due to an external
  376. // interruption, or because the buffer overflowed
  377. // and some events were lost.
  378. hr = win32.g_pKeyboard->Acquire();
  379. // nuke the garbage
  380. if (!FAILED(hr)) {
  381. //Bug 951: The following command really clears the garbage input.
  382. //The original will still process keys in the buffer and was causing
  383. //some problems.
  384. win32.g_pKeyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), NULL, &dwElements, 0 );
  385. dwElements = 0;
  386. }
  387. // hr may be DIERR_OTHERAPPHASPRIO or other errors. This
  388. // may occur when the app is minimized or in the process of
  389. // switching, so just try again later
  390. }
  391. if( FAILED(hr) ) {
  392. return 0;
  393. }
  394. return dwElements;
  395. }
  396. #else
  397. /*
  398. ====================
  399. Sys_PollKeyboardInputEvents
  400. Fake events by getting the entire device state
  401. and checking transitions
  402. ====================
  403. */
  404. int Sys_PollKeyboardInputEvents() {
  405. HRESULT hr;
  406. if( win32.g_pKeyboard == NULL ) {
  407. return 0;
  408. }
  409. hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
  410. if( hr != DI_OK )
  411. {
  412. // We got an error or we got DI_BUFFEROVERFLOW.
  413. //
  414. // Either way, it means that continuous contact with the
  415. // device has been lost, either due to an external
  416. // interruption, or because the buffer overflowed
  417. // and some events were lost.
  418. hr = win32.g_pKeyboard->Acquire();
  419. // nuke the garbage
  420. if (!FAILED(hr)) {
  421. hr = win32.g_pKeyboard->GetDeviceState( sizeof( toggleFetch[ diFetch ] ), toggleFetch[ diFetch ] );
  422. }
  423. // hr may be DIERR_OTHERAPPHASPRIO or other errors. This
  424. // may occur when the app is minimized or in the process of
  425. // switching, so just try again later
  426. }
  427. if( FAILED(hr) ) {
  428. return 0;
  429. }
  430. // build faked events
  431. int numChanges = 0;
  432. for ( int i = 0 ; i < 256 ; i++ ) {
  433. if ( toggleFetch[0][i] != toggleFetch[1][i] ) {
  434. polled_didod[ numChanges ].dwOfs = i;
  435. polled_didod[ numChanges ].dwData = toggleFetch[ diFetch ][i] ? 0x80 : 0;
  436. numChanges++;
  437. }
  438. }
  439. diFetch ^= 1;
  440. return numChanges;
  441. }
  442. #endif
  443. /*
  444. ====================
  445. Sys_PollKeyboardInputEvents
  446. ====================
  447. */
  448. int Sys_ReturnKeyboardInputEvent( const int n, int &ch, bool &state ) {
  449. ch = polled_didod[ n ].dwOfs;
  450. state = ( polled_didod[ n ].dwData & 0x80 ) == 0x80;
  451. if ( ch == K_PRINTSCREEN || ch == K_LCTRL || ch == K_LALT || ch == K_RCTRL || ch == K_RALT ) {
  452. // for windows, add a keydown event for print screen here, since
  453. // windows doesn't send keydown events to the WndProc for this key.
  454. // ctrl and alt are handled here to get around windows sending ctrl and
  455. // alt messages when the right-alt is pressed on non-US 102 keyboards.
  456. Sys_QueEvent( SE_KEY, ch, state, 0, NULL, 0 );
  457. }
  458. return ch;
  459. }
  460. void Sys_EndKeyboardInputEvents() {
  461. }
  462. //=====================================================================================
  463. int Sys_PollMouseInputEvents( int mouseEvents[MAX_MOUSE_EVENTS][2] ) {
  464. DWORD dwElements;
  465. HRESULT hr;
  466. if ( !win32.g_pMouse || !win32.mouseGrabbed ) {
  467. return 0;
  468. }
  469. dwElements = DINPUT_BUFFERSIZE;
  470. hr = win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
  471. if( hr != DI_OK ) {
  472. hr = win32.g_pMouse->Acquire();
  473. // clear the garbage
  474. if (!FAILED(hr)) {
  475. win32.g_pMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), polled_didod, &dwElements, 0 );
  476. }
  477. }
  478. if( FAILED(hr) ) {
  479. return 0;
  480. }
  481. if ( dwElements > MAX_MOUSE_EVENTS ) {
  482. dwElements = MAX_MOUSE_EVENTS;
  483. }
  484. for( DWORD i = 0; i < dwElements; i++ ) {
  485. mouseEvents[i][0] = M_INVALID;
  486. mouseEvents[i][1] = 0;
  487. if ( polled_didod[i].dwOfs >= DIMOFS_BUTTON0 && polled_didod[i].dwOfs <= DIMOFS_BUTTON7 ) {
  488. const int mouseButton = ( polled_didod[i].dwOfs - DIMOFS_BUTTON0 );
  489. const bool mouseDown = (polled_didod[i].dwData & 0x80) == 0x80;
  490. mouseEvents[i][0] = M_ACTION1 + mouseButton;
  491. mouseEvents[i][1] = mouseDown;
  492. Sys_QueEvent( SE_KEY, K_MOUSE1 + mouseButton, mouseDown, 0, NULL, 0 );
  493. } else {
  494. switch (polled_didod[i].dwOfs) {
  495. case DIMOFS_X:
  496. mouseEvents[i][0] = M_DELTAX;
  497. mouseEvents[i][1] = polled_didod[i].dwData;
  498. Sys_QueEvent( SE_MOUSE, polled_didod[i].dwData, 0, 0, NULL, 0 );
  499. break;
  500. case DIMOFS_Y:
  501. mouseEvents[i][0] = M_DELTAY;
  502. mouseEvents[i][1] = polled_didod[i].dwData;
  503. Sys_QueEvent( SE_MOUSE, 0, polled_didod[i].dwData, 0, NULL, 0 );
  504. break;
  505. case DIMOFS_Z:
  506. mouseEvents[i][0] = M_DELTAZ;
  507. mouseEvents[i][1] = (int)polled_didod[i].dwData / WHEEL_DELTA;
  508. {
  509. const int value = (int)polled_didod[i].dwData / WHEEL_DELTA;
  510. const int key = value < 0 ? K_MWHEELDOWN : K_MWHEELUP;
  511. const int iterations = abs( value );
  512. for ( int i = 0; i < iterations; i++ ) {
  513. Sys_QueEvent( SE_KEY, key, true, 0, NULL, 0 );
  514. Sys_QueEvent( SE_KEY, key, false, 0, NULL, 0 );
  515. }
  516. }
  517. break;
  518. }
  519. }
  520. }
  521. return dwElements;
  522. }
  523. //=====================================================================================
  524. // Joystick Input Handling
  525. //=====================================================================================
  526. void Sys_SetRumble( int device, int low, int hi ) {
  527. return win32.g_Joystick.SetRumble( device, low, hi );
  528. }
  529. int Sys_PollJoystickInputEvents( int deviceNum ) {
  530. return win32.g_Joystick.PollInputEvents( deviceNum );
  531. }
  532. int Sys_ReturnJoystickInputEvent( const int n, int &action, int &value ) {
  533. return win32.g_Joystick.ReturnInputEvent( n, action, value );
  534. }
  535. void Sys_EndJoystickInputEvents() {
  536. }
  537. /*
  538. ========================
  539. JoystickSamplingThread
  540. ========================
  541. */
  542. static int threadTimeDeltas[256];
  543. static int threadPacket[256];
  544. static int threadCount;
  545. void JoystickSamplingThread( void *data ) {
  546. static int prevTime = 0;
  547. static uint64 nextCheck[MAX_JOYSTICKS] = { 0 };
  548. const uint64 waitTime = 5000000; // poll every 5 seconds to see if a controller was connected
  549. while( 1 ) {
  550. // hopefully we see close to 4000 usec each loop
  551. int now = Sys_Microseconds();
  552. int delta;
  553. if ( prevTime == 0 ) {
  554. delta = 4000;
  555. } else {
  556. delta = now - prevTime;
  557. }
  558. prevTime = now;
  559. threadTimeDeltas[threadCount&255] = delta;
  560. threadCount++;
  561. {
  562. XINPUT_STATE joyData[MAX_JOYSTICKS];
  563. bool validData[MAX_JOYSTICKS];
  564. for ( int i = 0 ; i < MAX_JOYSTICKS ; i++ ) {
  565. if ( now >= nextCheck[i] ) {
  566. // XInputGetState might block... for a _really_ long time..
  567. validData[i] = XInputGetState( i, &joyData[i] ) == ERROR_SUCCESS;
  568. // allow an immediate data poll if the input device is connected else
  569. // wait for some time to see if another device was reconnected.
  570. // Checking input state infrequently for newly connected devices prevents
  571. // severe slowdowns on PC, especially on WinXP64.
  572. if ( validData[i] ) {
  573. nextCheck[i] = 0;
  574. } else {
  575. nextCheck[i] = now + waitTime;
  576. }
  577. }
  578. }
  579. // do this short amount of processing inside a critical section
  580. idScopedCriticalSection cs( win32.g_Joystick.mutexXis );
  581. for ( int i = 0 ; i < MAX_JOYSTICKS ; i++ ) {
  582. controllerState_t * cs = &win32.g_Joystick.controllers[i];
  583. if ( !validData[i] ) {
  584. cs->valid = false;
  585. continue;
  586. }
  587. cs->valid = true;
  588. XINPUT_STATE& current = joyData[i];
  589. cs->current = current;
  590. // Switch from using cs->current to current to reduce chance of Load-Hit-Store on consoles
  591. threadPacket[threadCount&255] = current.dwPacketNumber;
  592. #if 0
  593. if ( xis.dwPacketNumber == oldXis[ inputDeviceNum ].dwPacketNumber ) {
  594. return numEvents;
  595. }
  596. #endif
  597. cs->buttonBits |= current.Gamepad.wButtons;
  598. }
  599. }
  600. // we want this to be processed at least 250 times a second
  601. WaitForSingleObject( win32.g_Joystick.timer, INFINITE );
  602. }
  603. }
  604. /*
  605. ========================
  606. idJoystickWin32::idJoystickWin32
  607. ========================
  608. */
  609. idJoystickWin32::idJoystickWin32() {
  610. numEvents = 0;
  611. memset( &events, 0, sizeof( events ) );
  612. memset( &controllers, 0, sizeof( controllers ) );
  613. memset( buttonStates, 0, sizeof( buttonStates ) );
  614. memset( joyAxis, 0, sizeof( joyAxis ) );
  615. }
  616. /*
  617. ========================
  618. idJoystickWin32::Init
  619. ========================
  620. */
  621. bool idJoystickWin32::Init() {
  622. idJoystick::Init();
  623. // setup the timer that the high frequency thread will wait on
  624. // to fire every 4 msec
  625. timer = CreateWaitableTimer( NULL, FALSE, "JoypadTimer" );
  626. LARGE_INTEGER dueTime;
  627. dueTime.QuadPart = -1;
  628. if ( !SetWaitableTimer( timer, &dueTime, 4, NULL, NULL, FALSE ) ) {
  629. idLib::FatalError( "SetWaitableTimer for joystick failed" );
  630. }
  631. // spawn the high frequency joystick reading thread
  632. Sys_CreateThread( (xthread_t)JoystickSamplingThread, NULL, THREAD_HIGHEST, "Joystick", CORE_1A );
  633. return false;
  634. }
  635. /*
  636. ========================
  637. idJoystickWin32::SetRumble
  638. ========================
  639. */
  640. void idJoystickWin32::SetRumble( int inputDeviceNum, int rumbleLow, int rumbleHigh ) {
  641. if ( inputDeviceNum < 0 || inputDeviceNum >= MAX_JOYSTICKS ) {
  642. return;
  643. }
  644. if ( !controllers[inputDeviceNum].valid ) {
  645. return;
  646. }
  647. XINPUT_VIBRATION vibration;
  648. vibration.wLeftMotorSpeed = idMath::ClampInt( 0, 65535, rumbleLow );
  649. vibration.wRightMotorSpeed = idMath::ClampInt( 0, 65535, rumbleHigh );
  650. DWORD err = XInputSetState( inputDeviceNum, &vibration );
  651. if ( err != ERROR_SUCCESS ) {
  652. idLib::Warning( "XInputSetState error: 0x%x", err );
  653. }
  654. }
  655. /*
  656. ========================
  657. idJoystickWin32::PostInputEvent
  658. ========================
  659. */
  660. void idJoystickWin32::PostInputEvent( int inputDeviceNum, int event, int value, int range ) {
  661. // These events are used for GUI button presses
  662. if ( ( event >= J_ACTION1 ) && ( event <= J_ACTION_MAX ) ) {
  663. PushButton( inputDeviceNum, K_JOY1 + ( event - J_ACTION1 ), value != 0 );
  664. } else if ( event == J_AXIS_LEFT_X ) {
  665. PushButton( inputDeviceNum, K_JOY_STICK1_LEFT, ( value < -range ) );
  666. PushButton( inputDeviceNum, K_JOY_STICK1_RIGHT, ( value > range ) );
  667. } else if ( event == J_AXIS_LEFT_Y ) {
  668. PushButton( inputDeviceNum, K_JOY_STICK1_UP, ( value < -range ) );
  669. PushButton( inputDeviceNum, K_JOY_STICK1_DOWN, ( value > range ) );
  670. } else if ( event == J_AXIS_RIGHT_X ) {
  671. PushButton( inputDeviceNum, K_JOY_STICK2_LEFT, ( value < -range ) );
  672. PushButton( inputDeviceNum, K_JOY_STICK2_RIGHT, ( value > range ) );
  673. } else if ( event == J_AXIS_RIGHT_Y ) {
  674. PushButton( inputDeviceNum, K_JOY_STICK2_UP, ( value < -range ) );
  675. PushButton( inputDeviceNum, K_JOY_STICK2_DOWN, ( value > range ) );
  676. } else if ( ( event >= J_DPAD_UP ) && ( event <= J_DPAD_RIGHT ) ) {
  677. PushButton( inputDeviceNum, K_JOY_DPAD_UP + ( event - J_DPAD_UP ), value != 0 );
  678. } else if ( event == J_AXIS_LEFT_TRIG ) {
  679. PushButton( inputDeviceNum, K_JOY_TRIGGER1, ( value > range ) );
  680. } else if ( event == J_AXIS_RIGHT_TRIG ) {
  681. PushButton( inputDeviceNum, K_JOY_TRIGGER2, ( value > range ) );
  682. }
  683. if ( event >= J_AXIS_MIN && event <= J_AXIS_MAX ) {
  684. int axis = event - J_AXIS_MIN;
  685. int percent = ( value * 16 ) / range;
  686. if ( joyAxis[inputDeviceNum][axis] != percent ) {
  687. joyAxis[inputDeviceNum][axis] = percent;
  688. Sys_QueEvent( SE_JOYSTICK, axis, percent, 0, NULL, inputDeviceNum );
  689. }
  690. }
  691. // These events are used for actual game input
  692. events[numEvents].event = event;
  693. events[numEvents].value = value;
  694. numEvents++;
  695. }
  696. /*
  697. ========================
  698. idJoystickWin32::PollInputEvents
  699. ========================
  700. */
  701. int idJoystickWin32::PollInputEvents( int inputDeviceNum ) {
  702. numEvents = 0;
  703. if ( !win32.activeApp ) {
  704. return numEvents;
  705. }
  706. assert( inputDeviceNum < 4 );
  707. // if ( inputDeviceNum > in_joystick.GetInteger() ) {
  708. // return numEvents;
  709. // }
  710. controllerState_t *cs = &controllers[ inputDeviceNum ];
  711. // grab the current packet under a critical section
  712. XINPUT_STATE xis;
  713. XINPUT_STATE old;
  714. int orBits;
  715. {
  716. idScopedCriticalSection crit( mutexXis );
  717. xis = cs->current;
  718. old = cs->previous;
  719. cs->previous = xis;
  720. // fetch or'd button bits
  721. orBits = cs->buttonBits;
  722. cs->buttonBits = 0;
  723. }
  724. #if 0
  725. if ( XInputGetState( inputDeviceNum, &xis ) != ERROR_SUCCESS ) {
  726. return numEvents;
  727. }
  728. #endif
  729. for ( int i = 0 ; i < 32 ; i++ ) {
  730. int bit = 1<<i;
  731. if ( ( ( xis.Gamepad.wButtons | old.Gamepad.wButtons ) & bit ) == 0
  732. && ( orBits & bit ) ) {
  733. idLib::Printf( "Dropped button press on bit %i\n", i );
  734. }
  735. }
  736. if ( session->IsSystemUIShowing() ) {
  737. // memset xis so the current input does not get latched if the UI is showing
  738. memset( &xis, 0, sizeof( XINPUT_STATE ) );
  739. }
  740. int joyRemap[16] = {
  741. J_DPAD_UP, J_DPAD_DOWN, // Up, Down
  742. J_DPAD_LEFT, J_DPAD_RIGHT, // Left, Right
  743. J_ACTION9, J_ACTION10, // Start, Back
  744. J_ACTION7, J_ACTION8, // Left Stick Down, Right Stick Down
  745. J_ACTION5, J_ACTION6, // Black, White (Left Shoulder, Right Shoulder)
  746. 0, 0, // Unused
  747. J_ACTION1, J_ACTION2, // A, B
  748. J_ACTION3, J_ACTION4, // X, Y
  749. };
  750. // Check the digital buttons
  751. for ( int i = 0; i < 16; i++ ) {
  752. int mask = ( 1 << i );
  753. if ( ( xis.Gamepad.wButtons & mask ) != ( old.Gamepad.wButtons & mask ) ) {
  754. PostInputEvent( inputDeviceNum, joyRemap[i], ( xis.Gamepad.wButtons & mask ) > 0 );
  755. }
  756. }
  757. // Check the triggers
  758. if ( xis.Gamepad.bLeftTrigger != old.Gamepad.bLeftTrigger ) {
  759. PostInputEvent( inputDeviceNum, J_AXIS_LEFT_TRIG, xis.Gamepad.bLeftTrigger * 128 );
  760. }
  761. if ( xis.Gamepad.bRightTrigger != old.Gamepad.bRightTrigger ) {
  762. PostInputEvent( inputDeviceNum, J_AXIS_RIGHT_TRIG, xis.Gamepad.bRightTrigger * 128 );
  763. }
  764. if ( xis.Gamepad.sThumbLX != old.Gamepad.sThumbLX ) {
  765. PostInputEvent( inputDeviceNum, J_AXIS_LEFT_X, xis.Gamepad.sThumbLX );
  766. }
  767. if ( xis.Gamepad.sThumbLY != old.Gamepad.sThumbLY ) {
  768. PostInputEvent( inputDeviceNum, J_AXIS_LEFT_Y, -xis.Gamepad.sThumbLY );
  769. }
  770. if ( xis.Gamepad.sThumbRX != old.Gamepad.sThumbRX ) {
  771. PostInputEvent( inputDeviceNum, J_AXIS_RIGHT_X, xis.Gamepad.sThumbRX );
  772. }
  773. if ( xis.Gamepad.sThumbRY != old.Gamepad.sThumbRY ) {
  774. PostInputEvent( inputDeviceNum, J_AXIS_RIGHT_Y, -xis.Gamepad.sThumbRY );
  775. }
  776. return numEvents;
  777. }
  778. /*
  779. ========================
  780. idJoystickWin32::ReturnInputEvent
  781. ========================
  782. */
  783. int idJoystickWin32::ReturnInputEvent( const int n, int & action, int &value ) {
  784. if ( ( n < 0 ) || ( n >= MAX_JOY_EVENT ) ) {
  785. return 0;
  786. }
  787. action = events[ n ].event;
  788. value = events[ n ].value;
  789. return 1;
  790. }
  791. /*
  792. ========================
  793. idJoystickWin32::PushButton
  794. ========================
  795. */
  796. void idJoystickWin32::PushButton( int inputDeviceNum, int key, bool value ) {
  797. // So we don't keep sending the same SE_KEY message over and over again
  798. if ( buttonStates[inputDeviceNum][key] != value ) {
  799. buttonStates[inputDeviceNum][key] = value;
  800. Sys_QueEvent( SE_KEY, key, value, 0, NULL, inputDeviceNum );
  801. }
  802. }