input.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 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 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 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 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 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. #ifndef USE_SDL
  21. #include "../../idlib/precompiled.h"
  22. #include "../posix/posix_public.h"
  23. #include "local.h"
  24. #include <pthread.h>
  25. idCVar in_mouse( "in_mouse", "1", CVAR_SYSTEM | CVAR_ARCHIVE, "" );
  26. idCVar in_dgamouse( "in_dgamouse", "1", CVAR_SYSTEM | CVAR_ARCHIVE, "" );
  27. idCVar in_nograb( "in_nograb", "0", CVAR_SYSTEM | CVAR_NOCHEAT, "" );
  28. // have a working xkb extension
  29. static bool have_xkb = false;
  30. // toggled by grab calls - decides if we ignore MotionNotify events
  31. static bool mouse_active = false;
  32. // non-DGA pointer-warping mouse input
  33. static int mwx, mwy;
  34. static int mx = 0, my = 0;
  35. // time mouse was last reset, we ignore the first 50ms of the mouse to allow settling of events
  36. static int mouse_reset_time = 0;
  37. #define MOUSE_RESET_DELAY 50
  38. // backup original values for pointer grab/ungrab
  39. static int mouse_accel_numerator;
  40. static int mouse_accel_denominator;
  41. static int mouse_threshold;
  42. static byte s_scantokey[128] = {
  43. /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0,
  44. /* 8 */ 0, 27, '1', '2', '3', '4', '5', '6', // 27 - ESC
  45. /* 10 */ '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 9 - TAB
  46. /* 18 */ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
  47. /* 20 */ 'o', 'p', '[', ']', K_ENTER, K_CTRL, 'a', 's',
  48. /* 28 */ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
  49. /* 30 */ '\'', '`', K_SHIFT, '\\', 'z', 'x', 'c', 'v',
  50. /* 38 */ 'b', 'n', 'm', ',', '.', '/', K_SHIFT, K_KP_STAR,
  51. /* 40 */ K_ALT, ' ', K_CAPSLOCK, K_F1, K_F2, K_F3, K_F4, K_F5,
  52. /* 48 */ K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0, K_HOME,
  53. /* 50 */ K_UPARROW, K_PGUP, K_KP_MINUS, K_LEFTARROW, K_KP_5, K_RIGHTARROW, K_KP_PLUS, K_END,
  54. /* 58 */ K_DOWNARROW, K_PGDN, K_INS, K_DEL, 0, 0, '\\', K_F11,
  55. /* 60 */ K_F12, K_HOME, K_UPARROW, K_PGUP, K_LEFTARROW, 0, K_RIGHTARROW, K_END,
  56. /* 68 */ K_DOWNARROW, K_PGDN, K_INS, K_DEL, K_ENTER, K_CTRL, K_PAUSE, 0,
  57. /* 70 */ '/', K_ALT, 0, 0, 0, 0, 0, 0,
  58. /* 78 */ 0, 0, 0, 0, 0, 0, 0, 0
  59. };
  60. /*
  61. =================
  62. IN_Clear_f
  63. =================
  64. */
  65. void IN_Clear_f( const idCmdArgs &args ) {
  66. idKeyInput::ClearStates();
  67. }
  68. /*
  69. =================
  70. Sys_InitInput
  71. =================
  72. */
  73. void Sys_InitInput(void) {
  74. int major_in_out, minor_in_out, opcode_rtrn, event_rtrn, error_rtrn;
  75. bool ret;
  76. common->Printf( "\n------- Input Initialization -------\n" );
  77. assert( dpy );
  78. cmdSystem->AddCommand( "in_clear", IN_Clear_f, CMD_FL_SYSTEM, "reset the input keys" );
  79. major_in_out = XkbMajorVersion;
  80. minor_in_out = XkbMinorVersion;
  81. ret = XkbLibraryVersion( &major_in_out, &minor_in_out );
  82. common->Printf( "XKB extension: compile time 0x%x:0x%x, runtime 0x%x:0x%x: %s\n", XkbMajorVersion, XkbMinorVersion, major_in_out, minor_in_out, ret ? "OK" : "Not compatible" );
  83. if ( ret ) {
  84. ret = XkbQueryExtension( dpy, &opcode_rtrn, &event_rtrn, &error_rtrn, &major_in_out, &minor_in_out );
  85. if ( ret ) {
  86. common->Printf( "XKB extension present on server ( 0x%x:0x%x )\n", major_in_out, minor_in_out );
  87. have_xkb = true;
  88. } else {
  89. common->Printf( "XKB extension not present on server\n" );
  90. have_xkb = false;
  91. }
  92. } else {
  93. have_xkb = false;
  94. }
  95. common->Printf( "------------------------------------\n" );
  96. }
  97. //#define XEVT_DBG
  98. //#define XEVT_DBG2
  99. static Cursor Sys_XCreateNullCursor( Display *display, Window root ) {
  100. Pixmap cursormask;
  101. XGCValues xgc;
  102. GC gc;
  103. XColor dummycolour;
  104. Cursor cursor;
  105. cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/);
  106. xgc.function = GXclear;
  107. gc = XCreateGC(display, cursormask, GCFunction, &xgc);
  108. XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
  109. dummycolour.pixel = 0;
  110. dummycolour.red = 0;
  111. dummycolour.flags = 04;
  112. cursor = XCreatePixmapCursor(display, cursormask, cursormask,
  113. &dummycolour,&dummycolour, 0,0);
  114. XFreePixmap(display,cursormask);
  115. XFreeGC(display,gc);
  116. return cursor;
  117. }
  118. static void Sys_XInstallGrabs( void ) {
  119. assert( dpy );
  120. XWarpPointer( dpy, None, win,
  121. 0, 0, 0, 0,
  122. glConfig.vidWidth / 2, glConfig.vidHeight / 2 );
  123. XSync( dpy, False );
  124. XDefineCursor( dpy, win, Sys_XCreateNullCursor( dpy, win ) );
  125. XGrabPointer( dpy, win,
  126. False,
  127. MOUSE_MASK,
  128. GrabModeAsync, GrabModeAsync,
  129. win,
  130. None,
  131. CurrentTime );
  132. XGetPointerControl( dpy, &mouse_accel_numerator, &mouse_accel_denominator,
  133. &mouse_threshold );
  134. XChangePointerControl( dpy, True, True, 1, 1, 0 );
  135. XSync( dpy, False );
  136. mouse_reset_time = Sys_Milliseconds ();
  137. if ( in_dgamouse.GetBool() && !dga_found ) {
  138. common->Printf("XF86DGA not available, forcing DGA mouse off\n");
  139. in_dgamouse.SetBool( false );
  140. }
  141. if ( in_dgamouse.GetBool() ) {
  142. #if defined( ID_ENABLE_DGA )
  143. XF86DGADirectVideo( dpy, DefaultScreen( dpy ), XF86DGADirectMouse );
  144. XWarpPointer( dpy, None, win, 0, 0, 0, 0, 0, 0 );
  145. #endif
  146. } else {
  147. mwx = glConfig.vidWidth / 2;
  148. mwy = glConfig.vidHeight / 2;
  149. mx = my = 0;
  150. }
  151. XGrabKeyboard( dpy, win,
  152. False,
  153. GrabModeAsync, GrabModeAsync,
  154. CurrentTime );
  155. XSync( dpy, False );
  156. mouse_active = true;
  157. }
  158. void Sys_XUninstallGrabs(void) {
  159. assert( dpy );
  160. #if defined( ID_ENABLE_DGA )
  161. if ( in_dgamouse.GetBool() ) {
  162. common->DPrintf( "DGA Mouse - Disabling DGA DirectVideo\n" );
  163. XF86DGADirectVideo( dpy, DefaultScreen( dpy ), 0 );
  164. }
  165. #endif
  166. XChangePointerControl( dpy, true, true, mouse_accel_numerator,
  167. mouse_accel_denominator, mouse_threshold );
  168. XUngrabPointer( dpy, CurrentTime );
  169. XUngrabKeyboard( dpy, CurrentTime );
  170. XWarpPointer( dpy, None, win,
  171. 0, 0, 0, 0,
  172. glConfig.vidWidth / 2, glConfig.vidHeight / 2);
  173. XUndefineCursor( dpy, win );
  174. mouse_active = false;
  175. }
  176. void Sys_GrabMouseCursor( bool grabIt ) {
  177. #if defined( ID_DEDICATED )
  178. return;
  179. #endif
  180. if ( !dpy ) {
  181. #ifdef XEVT_DBG
  182. common->DPrintf("Sys_GrabMouseCursor: !dpy\n");
  183. #endif
  184. return;
  185. }
  186. if ( glConfig.isFullscreen ) {
  187. if ( !grabIt ) {
  188. return; // never ungrab while fullscreen
  189. }
  190. if ( in_nograb.GetBool() ) {
  191. common->DPrintf("forcing in_nograb 0 while running fullscreen\n");
  192. in_nograb.SetBool( false );
  193. }
  194. }
  195. if ( in_nograb.GetBool() ) {
  196. if ( in_dgamouse.GetBool() ) {
  197. common->DPrintf("in_nograb 1, forcing forcing DGA mouse off\n");
  198. in_dgamouse.SetBool( false );
  199. }
  200. if (grabIt) {
  201. mouse_active = true;
  202. } else {
  203. mouse_active = false;
  204. }
  205. return;
  206. }
  207. if ( grabIt && !mouse_active ) {
  208. Sys_XInstallGrabs();
  209. } else if ( !grabIt && mouse_active ) {
  210. Sys_XUninstallGrabs();
  211. }
  212. }
  213. /**
  214. * XPending() actually performs a blocking read
  215. * if no events available. From Fakk2, by way of
  216. * Heretic2, by way of SDL, original idea GGI project.
  217. * The benefit of this approach over the quite
  218. * badly behaved XAutoRepeatOn/Off is that you get
  219. * focus handling for free, which is a major win
  220. * with debug and windowed mode. It rests on the
  221. * assumption that the X server will use the
  222. * same timestamp on press/release event pairs
  223. * for key repeats.
  224. */
  225. static bool Sys_XPendingInput( void ) {
  226. // Flush the display connection
  227. // and look to see if events are queued
  228. XFlush( dpy );
  229. if ( XEventsQueued( dpy, QueuedAlready) ) {
  230. return true;
  231. }
  232. // More drastic measures are required -- see if X is ready to talk
  233. static struct timeval zero_time;
  234. int x11_fd;
  235. fd_set fdset;
  236. x11_fd = ConnectionNumber( dpy );
  237. FD_ZERO( &fdset );
  238. FD_SET( x11_fd, &fdset );
  239. if ( select( x11_fd+1, &fdset, NULL, NULL, &zero_time ) == 1 ) {
  240. return XPending( dpy );
  241. }
  242. // Oh well, nothing is ready ..
  243. return false;
  244. }
  245. /**
  246. * Intercept a KeyRelease-KeyPress sequence and ignore
  247. */
  248. static bool Sys_XRepeatPress( XEvent *event ) {
  249. XEvent peekevent;
  250. bool repeated = false;
  251. int lookupRet;
  252. char buf[5];
  253. KeySym keysym;
  254. if ( Sys_XPendingInput() ) {
  255. XPeekEvent( dpy, &peekevent );
  256. if ((peekevent.type == KeyPress) &&
  257. (peekevent.xkey.keycode == event->xkey.keycode) &&
  258. (peekevent.xkey.time == event->xkey.time)) {
  259. repeated = true;
  260. XNextEvent( dpy, &peekevent );
  261. // emit an SE_CHAR for the repeat
  262. lookupRet = XLookupString( (XKeyEvent*)&peekevent, buf, sizeof(buf), &keysym, NULL );
  263. if (lookupRet > 0) {
  264. Posix_QueEvent( SE_CHAR, buf[ 0 ], 0, 0, NULL);
  265. } else {
  266. // shouldn't we be doing a release/press in this order rather?
  267. // ( doesn't work .. but that's what I would have expected to do though )
  268. Posix_QueEvent( SE_KEY, s_scantokey[peekevent.xkey.keycode], true, 0, NULL);
  269. Posix_QueEvent( SE_KEY, s_scantokey[peekevent.xkey.keycode], false, 0, NULL);
  270. }
  271. }
  272. }
  273. return repeated;
  274. }
  275. /*
  276. ==========================
  277. Posix_PollInput
  278. ==========================
  279. */
  280. void Posix_PollInput() {
  281. static char buf[16];
  282. static XEvent event;
  283. static XKeyEvent *key_event = (XKeyEvent*)&event;
  284. int lookupRet;
  285. int b, dx, dy;
  286. KeySym keysym;
  287. if ( !dpy ) {
  288. return;
  289. }
  290. // NOTE: Sys_GetEvent only calls when there are no events left
  291. // but here we pump all X events that have accumulated
  292. // pump one by one? or use threaded input?
  293. while ( XPending( dpy ) ) {
  294. XNextEvent( dpy, &event );
  295. switch (event.type) {
  296. case KeyPress:
  297. #ifdef XEVT_DBG
  298. if (key_event->keycode > 0x7F)
  299. common->DPrintf("WARNING: KeyPress keycode > 0x7F");
  300. #endif
  301. key_event->keycode &= 0x7F;
  302. #ifdef XEVT_DBG2
  303. printf("SE_KEY press %d\n", key_event->keycode);
  304. #endif
  305. Posix_QueEvent( SE_KEY, s_scantokey[key_event->keycode], true, 0, NULL);
  306. lookupRet = XLookupString(key_event, buf, sizeof(buf), &keysym, NULL);
  307. if (lookupRet > 0) {
  308. char s = buf[0];
  309. #ifdef XEVT_DBG
  310. if (buf[1]!=0)
  311. common->DPrintf("WARNING: got XLookupString buffer '%s' (%d)\n", buf, strlen(buf));
  312. #endif
  313. #ifdef XEVT_DBG2
  314. printf("SE_CHAR %s\n", buf);
  315. #endif
  316. Posix_QueEvent( SE_CHAR, s, 0, 0, NULL);
  317. }
  318. if (!Posix_AddKeyboardPollEvent( s_scantokey[key_event->keycode], true ))
  319. return;
  320. break;
  321. case KeyRelease:
  322. if (Sys_XRepeatPress(&event)) {
  323. #ifdef XEVT_DBG2
  324. printf("RepeatPress\n");
  325. #endif
  326. continue;
  327. }
  328. #ifdef XEVT_DBG
  329. if (key_event->keycode > 0x7F)
  330. common->DPrintf("WARNING: KeyRelease keycode > 0x7F");
  331. #endif
  332. key_event->keycode &= 0x7F;
  333. #ifdef XEVT_DBG2
  334. printf("SE_KEY release %d\n", key_event->keycode);
  335. #endif
  336. Posix_QueEvent( SE_KEY, s_scantokey[key_event->keycode], false, 0, NULL);
  337. if (!Posix_AddKeyboardPollEvent( s_scantokey[key_event->keycode], false ))
  338. return;
  339. break;
  340. case ButtonPress:
  341. if (event.xbutton.button == 4) {
  342. Posix_QueEvent( SE_KEY, K_MWHEELUP, true, 0, NULL);
  343. if (!Posix_AddMousePollEvent( M_DELTAZ, 1 ))
  344. return;
  345. } else if (event.xbutton.button == 5) {
  346. Posix_QueEvent( SE_KEY, K_MWHEELDOWN, true, 0, NULL);
  347. if (!Posix_AddMousePollEvent( M_DELTAZ, -1 ))
  348. return;
  349. } else {
  350. b = -1;
  351. if (event.xbutton.button == 1) {
  352. b = 0; // K_MOUSE1
  353. } else if (event.xbutton.button == 2) {
  354. b = 2; // K_MOUSE3
  355. } else if (event.xbutton.button == 3) {
  356. b = 1; // K_MOUSE2
  357. } else if (event.xbutton.button == 6) {
  358. b = 3; // K_MOUSE4
  359. } else if (event.xbutton.button == 7) {
  360. b = 4; // K_MOUSE5
  361. }
  362. if (b == -1 || b > 4) {
  363. common->DPrintf("X ButtonPress %d not supported\n", event.xbutton.button);
  364. } else {
  365. Posix_QueEvent( SE_KEY, K_MOUSE1 + b, true, 0, NULL);
  366. if (!Posix_AddMousePollEvent( M_ACTION1 + b, true ))
  367. return;
  368. }
  369. }
  370. break;
  371. case ButtonRelease:
  372. if (event.xbutton.button == 4) {
  373. Posix_QueEvent( SE_KEY, K_MWHEELUP, false, 0, NULL);
  374. } else if (event.xbutton.button == 5) {
  375. Posix_QueEvent( SE_KEY, K_MWHEELDOWN, false, 0, NULL);
  376. } else {
  377. b = -1;
  378. if (event.xbutton.button == 1) {
  379. b = 0;
  380. } else if (event.xbutton.button == 2) {
  381. b = 2;
  382. } else if (event.xbutton.button == 3) {
  383. b = 1;
  384. } else if (event.xbutton.button == 6) {
  385. b = 3; // K_MOUSE4
  386. } else if (event.xbutton.button == 7) {
  387. b = 4; // K_MOUSE5
  388. }
  389. if (b == -1 || b > 4) {
  390. common->DPrintf("X ButtonRelease %d not supported\n", event.xbutton.button);
  391. } else {
  392. Posix_QueEvent( SE_KEY, K_MOUSE1 + b, false, 0, NULL);
  393. if (!Posix_AddMousePollEvent( M_ACTION1 + b, false ))
  394. return;
  395. }
  396. }
  397. break;
  398. case MotionNotify:
  399. if (!mouse_active)
  400. break;
  401. if (in_dgamouse.GetBool()) {
  402. dx = event.xmotion.x_root;
  403. dy = event.xmotion.y_root;
  404. Posix_QueEvent( SE_MOUSE, dx, dy, 0, NULL);
  405. // if we overflow here, we'll get a warning, but the delta will be completely processed anyway
  406. Posix_AddMousePollEvent( M_DELTAX, dx );
  407. if (!Posix_AddMousePollEvent( M_DELTAY, dy ))
  408. return;
  409. } else {
  410. // if it's a center motion, we've just returned from our warp
  411. // FIXME: we generate mouse delta on wrap return, but that lags us quite a bit from the initial event..
  412. if (event.xmotion.x == glConfig.vidWidth / 2 &&
  413. event.xmotion.y == glConfig.vidHeight / 2) {
  414. mwx = glConfig.vidWidth / 2;
  415. mwy = glConfig.vidHeight / 2;
  416. Posix_QueEvent( SE_MOUSE, mx, my, 0, NULL);
  417. Posix_AddMousePollEvent( M_DELTAX, mx );
  418. if (!Posix_AddMousePollEvent( M_DELTAY, my ))
  419. return;
  420. mx = my = 0;
  421. break;
  422. }
  423. dx = ((int) event.xmotion.x - mwx);
  424. dy = ((int) event.xmotion.y - mwy);
  425. mx += dx;
  426. my += dy;
  427. mwx = event.xmotion.x;
  428. mwy = event.xmotion.y;
  429. XWarpPointer(dpy,None,win,0,0,0,0, (glConfig.vidWidth/2),(glConfig.vidHeight/2));
  430. }
  431. break;
  432. }
  433. }
  434. }
  435. /*
  436. =================
  437. Sys_ShutdownInput
  438. =================
  439. */
  440. void Sys_ShutdownInput( void ) { }
  441. /*
  442. ===============
  443. Sys_MapCharForKey
  444. ===============
  445. */
  446. unsigned char Sys_MapCharForKey( int _key ) {
  447. int key; // scan key ( != doom key )
  448. XkbStateRec kbd_state;
  449. XEvent event;
  450. KeySym keysym;
  451. int lookupRet;
  452. char buf[5];
  453. if ( !have_xkb || !dpy ) {
  454. return (unsigned char)_key;
  455. }
  456. // query the current keyboard group, must be passed as bit 13-14 in the constructed XEvent
  457. // see X Keyboard Extension library specifications
  458. XkbGetState( dpy, XkbUseCoreKbd, &kbd_state );
  459. // lookup scancode from doom key code. unique hits
  460. for ( key = 0; key < 128; key++ ) {
  461. if ( _key == s_scantokey[ key ] ) {
  462. break;
  463. }
  464. }
  465. if ( key == 128 ) {
  466. // it happens. these, we can't convert
  467. common->DPrintf( "Sys_MapCharForKey: doom key %d -> keycode failed\n", _key );
  468. return (unsigned char)_key;
  469. }
  470. memset( &event, 0, sizeof( XEvent ) );
  471. event.xkey.type = KeyPress;
  472. event.xkey.display = dpy;
  473. event.xkey.time = CurrentTime;
  474. event.xkey.keycode = key;
  475. event.xkey.state = kbd_state.group << 13;
  476. lookupRet = XLookupString( (XKeyEvent *)&event, buf, sizeof( buf ), &keysym, NULL );
  477. if ( lookupRet <= 0 ) {
  478. Sys_Printf( "Sys_MapCharForKey: XLookupString key 0x%x failed\n", key );
  479. return (unsigned char)_key;
  480. }
  481. if ( lookupRet > 1 ) {
  482. // only ever expecting 1 char..
  483. Sys_Printf( "Sys_MapCharForKey: XLookupString returned '%s'\n", buf );
  484. }
  485. return buf[ 0 ];
  486. }
  487. #endif