doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
input.cpp
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21 
22 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.
23 
24 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.
25 
26 ===========================================================================
27 */
28 #include "../../idlib/precompiled.h"
29 #include "../posix/posix_public.h"
30 #include "local.h"
31 
32 #include <pthread.h>
33 
34 idCVar in_mouse( "in_mouse", "1", CVAR_SYSTEM | CVAR_ARCHIVE, "" );
35 idCVar in_dgamouse( "in_dgamouse", "1", CVAR_SYSTEM | CVAR_ARCHIVE, "" );
36 idCVar in_nograb( "in_nograb", "0", CVAR_SYSTEM | CVAR_NOCHEAT, "" );
37 
38 // have a working xkb extension
39 static bool have_xkb = false;
40 
41 // toggled by grab calls - decides if we ignore MotionNotify events
42 static bool mouse_active = false;
43 
44 // non-DGA pointer-warping mouse input
45 static int mwx, mwy;
46 static int mx = 0, my = 0;
47 
48 // time mouse was last reset, we ignore the first 50ms of the mouse to allow settling of events
49 static int mouse_reset_time = 0;
50 #define MOUSE_RESET_DELAY 50
51 
52 // backup original values for pointer grab/ungrab
53 static int mouse_accel_numerator;
54 static int mouse_accel_denominator;
55 static int mouse_threshold;
56 
57 static byte s_scantokey[128] = {
58 /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0,
59 /* 8 */ 0, 27, '1', '2', '3', '4', '5', '6', // 27 - ESC
60 /* 10 */ '7', '8', '9', '0', '-', '=', K_BACKSPACE, 9, // 9 - TAB
61 /* 18 */ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
62 /* 20 */ 'o', 'p', '[', ']', K_ENTER, K_CTRL, 'a', 's',
63 /* 28 */ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
64 /* 30 */ '\'', '`', K_SHIFT, '\\', 'z', 'x', 'c', 'v',
65 /* 38 */ 'b', 'n', 'm', ',', '.', '/', K_SHIFT, K_KP_STAR,
66 /* 40 */ K_ALT, ' ', K_CAPSLOCK, K_F1, K_F2, K_F3, K_F4, K_F5,
67 /* 48 */ K_F6, K_F7, K_F8, K_F9, K_F10, K_PAUSE, 0, K_HOME,
69 /* 58 */ K_DOWNARROW, K_PGDN, K_INS, K_DEL, 0, 0, '\\', K_F11,
72 /* 70 */ '/', K_ALT, 0, 0, 0, 0, 0, 0,
73 /* 78 */ 0, 0, 0, 0, 0, 0, 0, 0
74 };
75 
76 /*
77 =================
78 IN_Clear_f
79 =================
80 */
81 void IN_Clear_f( const idCmdArgs &args ) {
83 }
84 
85 /*
86 =================
87 Sys_InitInput
88 =================
89 */
90 void Sys_InitInput(void) {
91  int major_in_out, minor_in_out, opcode_rtrn, event_rtrn, error_rtrn;
92  bool ret;
93 
94  common->Printf( "\n------- Input Initialization -------\n" );
95  assert( dpy );
96  cmdSystem->AddCommand( "in_clear", IN_Clear_f, CMD_FL_SYSTEM, "reset the input keys" );
97  major_in_out = XkbMajorVersion;
98  minor_in_out = XkbMinorVersion;
99  ret = XkbLibraryVersion( &major_in_out, &minor_in_out );
100  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" );
101  if ( ret ) {
102  ret = XkbQueryExtension( dpy, &opcode_rtrn, &event_rtrn, &error_rtrn, &major_in_out, &minor_in_out );
103  if ( ret ) {
104  common->Printf( "XKB extension present on server ( 0x%x:0x%x )\n", major_in_out, minor_in_out );
105  have_xkb = true;
106  } else {
107  common->Printf( "XKB extension not present on server\n" );
108  have_xkb = false;
109  }
110  } else {
111  have_xkb = false;
112  }
113  common->Printf( "------------------------------------\n" );
114 }
115 
116 //#define XEVT_DBG
117 //#define XEVT_DBG2
118 
119 static Cursor Sys_XCreateNullCursor( Display *display, Window root ) {
120  Pixmap cursormask;
121  XGCValues xgc;
122  GC gc;
123  XColor dummycolour;
124  Cursor cursor;
125 
126  cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/);
127  xgc.function = GXclear;
128  gc = XCreateGC(display, cursormask, GCFunction, &xgc);
129  XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
130  dummycolour.pixel = 0;
131  dummycolour.red = 0;
132  dummycolour.flags = 04;
133  cursor = XCreatePixmapCursor(display, cursormask, cursormask,
134  &dummycolour,&dummycolour, 0,0);
135  XFreePixmap(display,cursormask);
136  XFreeGC(display,gc);
137  return cursor;
138 }
139 
140 static void Sys_XInstallGrabs( void ) {
141  assert( dpy );
142 
143  XWarpPointer( dpy, None, win,
144  0, 0, 0, 0,
145  glConfig.vidWidth / 2, glConfig.vidHeight / 2 );
146 
147  XSync( dpy, False );
148 
149  XDefineCursor( dpy, win, Sys_XCreateNullCursor( dpy, win ) );
150 
151  XGrabPointer( dpy, win,
152  False,
153  MOUSE_MASK,
154  GrabModeAsync, GrabModeAsync,
155  win,
156  None,
157  CurrentTime );
158 
159  XGetPointerControl( dpy, &mouse_accel_numerator, &mouse_accel_denominator,
160  &mouse_threshold );
161 
162  XChangePointerControl( dpy, True, True, 1, 1, 0 );
163 
164  XSync( dpy, False );
165 
166  mouse_reset_time = Sys_Milliseconds ();
167 
168  if ( in_dgamouse.GetBool() && !dga_found ) {
169  common->Printf("XF86DGA not available, forcing DGA mouse off\n");
170  in_dgamouse.SetBool( false );
171  }
172 
173  if ( in_dgamouse.GetBool() ) {
174 #if defined( ID_ENABLE_DGA )
175  XF86DGADirectVideo( dpy, DefaultScreen( dpy ), XF86DGADirectMouse );
176  XWarpPointer( dpy, None, win, 0, 0, 0, 0, 0, 0 );
177 #endif
178  } else {
179  mwx = glConfig.vidWidth / 2;
180  mwy = glConfig.vidHeight / 2;
181  mx = my = 0;
182  }
183 
184  XGrabKeyboard( dpy, win,
185  False,
186  GrabModeAsync, GrabModeAsync,
187  CurrentTime );
188 
189  XSync( dpy, False );
190 
191  mouse_active = true;
192 }
193 
195  assert( dpy );
196 
197 #if defined( ID_ENABLE_DGA )
198  if ( in_dgamouse.GetBool() ) {
199  common->DPrintf( "DGA Mouse - Disabling DGA DirectVideo\n" );
200  XF86DGADirectVideo( dpy, DefaultScreen( dpy ), 0 );
201  }
202 #endif
203 
204  XChangePointerControl( dpy, true, true, mouse_accel_numerator,
205  mouse_accel_denominator, mouse_threshold );
206 
207  XUngrabPointer( dpy, CurrentTime );
208  XUngrabKeyboard( dpy, CurrentTime );
209 
210  XWarpPointer( dpy, None, win,
211  0, 0, 0, 0,
213 
214  XUndefineCursor( dpy, win );
215 
216  mouse_active = false;
217 }
218 
219 void Sys_GrabMouseCursor( bool grabIt ) {
220 
221 #if defined( ID_DEDICATED )
222  return;
223 #endif
224 
225  if ( !dpy ) {
226  #ifdef XEVT_DBG
227  common->DPrintf("Sys_GrabMouseCursor: !dpy\n");
228  #endif
229  return;
230  }
231 
232  if ( glConfig.isFullscreen ) {
233  if ( !grabIt ) {
234  return; // never ungrab while fullscreen
235  }
236  if ( in_nograb.GetBool() ) {
237  common->DPrintf("forcing in_nograb 0 while running fullscreen\n");
238  in_nograb.SetBool( false );
239  }
240  }
241 
242  if ( in_nograb.GetBool() ) {
243  if ( in_dgamouse.GetBool() ) {
244  common->DPrintf("in_nograb 1, forcing forcing DGA mouse off\n");
245  in_dgamouse.SetBool( false );
246  }
247  if (grabIt) {
248  mouse_active = true;
249  } else {
250  mouse_active = false;
251  }
252  return;
253  }
254 
255  if ( grabIt && !mouse_active ) {
256  Sys_XInstallGrabs();
257  } else if ( !grabIt && mouse_active ) {
259  }
260 }
261 
274 static bool Sys_XPendingInput( void ) {
275  // Flush the display connection
276  // and look to see if events are queued
277  XFlush( dpy );
278  if ( XEventsQueued( dpy, QueuedAlready) ) {
279  return true;
280  }
281 
282  // More drastic measures are required -- see if X is ready to talk
283  static struct timeval zero_time;
284  int x11_fd;
285  fd_set fdset;
286 
287  x11_fd = ConnectionNumber( dpy );
288  FD_ZERO( &fdset );
289  FD_SET( x11_fd, &fdset );
290  if ( select( x11_fd+1, &fdset, NULL, NULL, &zero_time ) == 1 ) {
291  return XPending( dpy );
292  }
293 
294  // Oh well, nothing is ready ..
295  return false;
296 }
297 
301 static bool Sys_XRepeatPress( XEvent *event ) {
302  XEvent peekevent;
303  bool repeated = false;
304  int lookupRet;
305  char buf[5];
306  KeySym keysym;
307 
308  if ( Sys_XPendingInput() ) {
309  XPeekEvent( dpy, &peekevent );
310 
311  if ((peekevent.type == KeyPress) &&
312  (peekevent.xkey.keycode == event->xkey.keycode) &&
313  (peekevent.xkey.time == event->xkey.time)) {
314  repeated = true;
315  XNextEvent( dpy, &peekevent );
316  // emit an SE_CHAR for the repeat
317  lookupRet = XLookupString( (XKeyEvent*)&peekevent, buf, sizeof(buf), &keysym, NULL );
318  if (lookupRet > 0) {
319  Posix_QueEvent( SE_CHAR, buf[ 0 ], 0, 0, NULL);
320  } else {
321  // shouldn't we be doing a release/press in this order rather?
322  // ( doesn't work .. but that's what I would have expected to do though )
323  Posix_QueEvent( SE_KEY, s_scantokey[peekevent.xkey.keycode], true, 0, NULL);
324  Posix_QueEvent( SE_KEY, s_scantokey[peekevent.xkey.keycode], false, 0, NULL);
325  }
326  }
327  }
328 
329  return repeated;
330 }
331 
332 /*
333 ==========================
334 Posix_PollInput
335 ==========================
336 */
338  static char buf[16];
339  static XEvent event;
340  static XKeyEvent *key_event = (XKeyEvent*)&event;
341  int lookupRet;
342  int b, dx, dy;
343  KeySym keysym;
344 
345  if ( !dpy ) {
346  return;
347  }
348 
349  // NOTE: Sys_GetEvent only calls when there are no events left
350  // but here we pump all X events that have accumulated
351  // pump one by one? or use threaded input?
352  while ( XPending( dpy ) ) {
353  XNextEvent( dpy, &event );
354  switch (event.type) {
355  case KeyPress:
356  #ifdef XEVT_DBG
357  if (key_event->keycode > 0x7F)
358  common->DPrintf("WARNING: KeyPress keycode > 0x7F");
359  #endif
360  key_event->keycode &= 0x7F;
361  #ifdef XEVT_DBG2
362  printf("SE_KEY press %d\n", key_event->keycode);
363  #endif
364  Posix_QueEvent( SE_KEY, s_scantokey[key_event->keycode], true, 0, NULL);
365  lookupRet = XLookupString(key_event, buf, sizeof(buf), &keysym, NULL);
366  if (lookupRet > 0) {
367  char s = buf[0];
368  #ifdef XEVT_DBG
369  if (buf[1]!=0)
370  common->DPrintf("WARNING: got XLookupString buffer '%s' (%d)\n", buf, strlen(buf));
371  #endif
372  #ifdef XEVT_DBG2
373  printf("SE_CHAR %s\n", buf);
374  #endif
375  Posix_QueEvent( SE_CHAR, s, 0, 0, NULL);
376  }
377  if (!Posix_AddKeyboardPollEvent( s_scantokey[key_event->keycode], true ))
378  return;
379  break;
380 
381  case KeyRelease:
382  if (Sys_XRepeatPress(&event)) {
383  #ifdef XEVT_DBG2
384  printf("RepeatPress\n");
385  #endif
386  continue;
387  }
388  #ifdef XEVT_DBG
389  if (key_event->keycode > 0x7F)
390  common->DPrintf("WARNING: KeyRelease keycode > 0x7F");
391  #endif
392  key_event->keycode &= 0x7F;
393  #ifdef XEVT_DBG2
394  printf("SE_KEY release %d\n", key_event->keycode);
395  #endif
396  Posix_QueEvent( SE_KEY, s_scantokey[key_event->keycode], false, 0, NULL);
397  if (!Posix_AddKeyboardPollEvent( s_scantokey[key_event->keycode], false ))
398  return;
399  break;
400 
401  case ButtonPress:
402  if (event.xbutton.button == 4) {
403  Posix_QueEvent( SE_KEY, K_MWHEELUP, true, 0, NULL);
405  return;
406  } else if (event.xbutton.button == 5) {
407  Posix_QueEvent( SE_KEY, K_MWHEELDOWN, true, 0, NULL);
408  if (!Posix_AddMousePollEvent( M_DELTAZ, -1 ))
409  return;
410  } else {
411  b = -1;
412  if (event.xbutton.button == 1) {
413  b = 0; // K_MOUSE1
414  } else if (event.xbutton.button == 2) {
415  b = 2; // K_MOUSE3
416  } else if (event.xbutton.button == 3) {
417  b = 1; // K_MOUSE2
418  } else if (event.xbutton.button == 6) {
419  b = 3; // K_MOUSE4
420  } else if (event.xbutton.button == 7) {
421  b = 4; // K_MOUSE5
422  }
423  if (b == -1 || b > 4) {
424  common->DPrintf("X ButtonPress %d not supported\n", event.xbutton.button);
425  } else {
426  Posix_QueEvent( SE_KEY, K_MOUSE1 + b, true, 0, NULL);
427  if (!Posix_AddMousePollEvent( M_ACTION1 + b, true ))
428  return;
429  }
430  }
431  break;
432 
433  case ButtonRelease:
434  if (event.xbutton.button == 4) {
435  Posix_QueEvent( SE_KEY, K_MWHEELUP, false, 0, NULL);
436  } else if (event.xbutton.button == 5) {
437  Posix_QueEvent( SE_KEY, K_MWHEELDOWN, false, 0, NULL);
438  } else {
439  b = -1;
440  if (event.xbutton.button == 1) {
441  b = 0;
442  } else if (event.xbutton.button == 2) {
443  b = 2;
444  } else if (event.xbutton.button == 3) {
445  b = 1;
446  } else if (event.xbutton.button == 6) {
447  b = 3; // K_MOUSE4
448  } else if (event.xbutton.button == 7) {
449  b = 4; // K_MOUSE5
450  }
451  if (b == -1 || b > 4) {
452  common->DPrintf("X ButtonRelease %d not supported\n", event.xbutton.button);
453  } else {
454  Posix_QueEvent( SE_KEY, K_MOUSE1 + b, false, 0, NULL);
455  if (!Posix_AddMousePollEvent( M_ACTION1 + b, false ))
456  return;
457  }
458  }
459  break;
460 
461  case MotionNotify:
462  if (!mouse_active)
463  break;
464  if (in_dgamouse.GetBool()) {
465  dx = event.xmotion.x_root;
466  dy = event.xmotion.y_root;
467 
468  Posix_QueEvent( SE_MOUSE, dx, dy, 0, NULL);
469 
470  // if we overflow here, we'll get a warning, but the delta will be completely processed anyway
472  if (!Posix_AddMousePollEvent( M_DELTAY, dy ))
473  return;
474  } else {
475  // if it's a center motion, we've just returned from our warp
476  // FIXME: we generate mouse delta on wrap return, but that lags us quite a bit from the initial event..
477  if (event.xmotion.x == glConfig.vidWidth / 2 &&
478  event.xmotion.y == glConfig.vidHeight / 2) {
479  mwx = glConfig.vidWidth / 2;
480  mwy = glConfig.vidHeight / 2;
481 
482  Posix_QueEvent( SE_MOUSE, mx, my, 0, NULL);
483 
485  if (!Posix_AddMousePollEvent( M_DELTAY, my ))
486  return;
487  mx = my = 0;
488  break;
489  }
490 
491  dx = ((int) event.xmotion.x - mwx);
492  dy = ((int) event.xmotion.y - mwy);
493  mx += dx;
494  my += dy;
495 
496  mwx = event.xmotion.x;
497  mwy = event.xmotion.y;
498  XWarpPointer(dpy,None,win,0,0,0,0, (glConfig.vidWidth/2),(glConfig.vidHeight/2));
499  }
500  break;
501  }
502  }
503 }
504 
505 /*
506 =================
507 Sys_ShutdownInput
508 =================
509 */
510 void Sys_ShutdownInput( void ) { }
511 
512 /*
513 ===============
514 Sys_MapCharForKey
515 ===============
516 */
517 unsigned char Sys_MapCharForKey( int _key ) {
518  int key; // scan key ( != doom key )
519  XkbStateRec kbd_state;
520  XEvent event;
521  KeySym keysym;
522  int lookupRet;
523  char buf[5];
524 
525  if ( !have_xkb || !dpy ) {
526  return (unsigned char)_key;
527  }
528 
529  // query the current keyboard group, must be passed as bit 13-14 in the constructed XEvent
530  // see X Keyboard Extension library specifications
531  XkbGetState( dpy, XkbUseCoreKbd, &kbd_state );
532 
533  // lookup scancode from doom key code. unique hits
534  for ( key = 0; key < 128; key++ ) {
535  if ( _key == s_scantokey[ key ] ) {
536  break;
537  }
538  }
539  if ( key == 128 ) {
540  // it happens. these, we can't convert
541  common->DPrintf( "Sys_MapCharForKey: doom key %d -> keycode failed\n", _key );
542  return (unsigned char)_key;
543  }
544 
545  memset( &event, 0, sizeof( XEvent ) );
546  event.xkey.type = KeyPress;
547  event.xkey.display = dpy;
548  event.xkey.time = CurrentTime;
549  event.xkey.keycode = key;
550  event.xkey.state = kbd_state.group << 13;
551 
552  lookupRet = XLookupString( (XKeyEvent *)&event, buf, sizeof( buf ), &keysym, NULL );
553  if ( lookupRet <= 0 ) {
554  Sys_Printf( "Sys_MapCharForKey: XLookupString key 0x%x failed\n", key );
555  return (unsigned char)_key;
556  }
557  if ( lookupRet > 1 ) {
558  // only ever expecting 1 char..
559  Sys_Printf( "Sys_MapCharForKey: XLookupString returned '%s'\n", buf );
560  }
561  return buf[ 0 ];
562 }
Definition: KeyInput.h:78
Definition: KeyInput.h:83
Definition: KeyInput.h:82
assert(prefInfo.fullscreenBtn)
bool Posix_AddKeyboardPollEvent(int key, bool state)
Definition: posix_input.cpp:55
Definition: KeyInput.h:88
void IN_Clear_f(const idCmdArgs &args)
Definition: input.cpp:81
Definition: KeyInput.h:86
idCVar in_mouse("in_mouse","1", CVAR_SYSTEM|CVAR_ARCHIVE,"")
Definition: KeyInput.h:74
void Sys_Printf(const char *msg,...)
static void ClearStates(void)
Definition: KeyInput.cpp:746
Definition: KeyInput.h:85
int Sys_Milliseconds(void)
case const int
Definition: Callbacks.cpp:52
bool Posix_AddMousePollEvent(int action, int value)
Definition: posix_input.cpp:72
Definition: KeyInput.h:81
void Posix_PollInput()
Definition: input.cpp:337
idCmdSystem * cmdSystem
Definition: CmdSystem.cpp:116
bool isFullscreen
Definition: RenderSystem.h:90
GLdouble s
Definition: glext.h:2935
void Sys_InitInput(void)
Definition: input.cpp:90
void Sys_GrabMouseCursor(bool grabIt)
Definition: input.cpp:219
Definition: KeyInput.h:91
#define MOUSE_MASK
Definition: local.h:52
Definition: KeyInput.h:90
Definition: KeyInput.h:80
Definition: KeyInput.h:73
void Sys_XUninstallGrabs(void)
Definition: input.cpp:194
idCommon * common
Definition: Common.cpp:206
#define NULL
Definition: Lib.h:88
#define select(args...)
Definition: amigaos.h:39
idCVar in_nograb("in_nograb","0", CVAR_SYSTEM|CVAR_NOCHEAT,"")
Definition: KeyInput.h:84
bool dga_found
Definition: glimp.cpp:48
virtual void Printf(const char *fmt,...) id_attribute((format(printf
Definition: KeyInput.h:87
GLubyte GLubyte b
Definition: glext.h:4662
unsigned char Sys_MapCharForKey(int _key)
Definition: input.cpp:517
bool GetBool(void) const
Definition: CVarSystem.h:142
glconfig_t glConfig
void Posix_QueEvent(sysEventType_t type, int value, int value2, int ptrLength, void *ptr)
Definition: posix_main.cpp:275
Definition: KeyInput.h:70
unsigned char byte
Definition: Lib.h:75
idCVar in_dgamouse("in_dgamouse","1", CVAR_SYSTEM|CVAR_ARCHIVE,"")
Window win
Definition: glimp.cpp:46
void Sys_ShutdownInput(void)
Definition: input.cpp:510
Display * dpy
Definition: glimp.cpp:43
void SetBool(const bool value)
Definition: CVarSystem.h:147
virtual void DPrintf(const char *fmt,...) id_attribute((format(printf
Definition: KeyInput.h:89
virtual void AddCommand(const char *cmdName, cmdFunction_t function, int flags, const char *description, argCompletion_t argCompletion=NULL)=0