1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 
23 #ifdef USE_LOCAL_HEADERS
24 #	include "SDL.h"
25 #else
26 #	include <SDL.h>
27 #endif
28 
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 
33 #include "../renderer/tr_local.h"
34 #include "../client/client.h"
35 #include "../sys/sys_local.h"
36 
37 #define ARRAYLEN(x) (sizeof(x)/sizeof(x[0]))
38 
39 #ifdef MACOS_X
40 // Mouse acceleration needs to be disabled
41 #define MACOS_X_ACCELERATION_HACK
42 // Cursor needs hack to hide
43 #define MACOS_X_CURSOR_HACK
44 #endif
45 
46 #ifdef MACOS_X_ACCELERATION_HACK
47 #include <IOKit/IOTypes.h>
48 #include <IOKit/hidsystem/IOHIDLib.h>
49 #include <IOKit/hidsystem/IOHIDParameter.h>
50 #include <IOKit/hidsystem/event_status_driver.h>
51 #endif
52 
53 static cvar_t *in_keyboardDebug     = NULL;
54 
55 static SDL_Joystick *stick = NULL;
56 
57 static qboolean mouseAvailable = qfalse;
58 static qboolean mouseActive = qfalse;
59 static qboolean keyRepeatEnabled = qfalse;
60 
61 static cvar_t *in_mouse             = NULL;
62 #ifdef MACOS_X_ACCELERATION_HACK
63 static cvar_t *in_disablemacosxmouseaccel = NULL;
64 static double originalMouseSpeed = -1.0;
65 #endif
66 static cvar_t *in_nograb;
67 
68 static cvar_t *in_joystick          = NULL;
69 static cvar_t *in_joystickDebug     = NULL;
70 static cvar_t *in_joystickThreshold = NULL;
71 static cvar_t *in_joystickNo        = NULL;
72 
73 #define CTRL(a) ((a)-'a'+1)
74 
75 /*
76 ===============
77 IN_PrintKey
78 ===============
79 */
IN_PrintKey(const SDL_keysym * keysym,keyNum_t key,qboolean down)80 static void IN_PrintKey( const SDL_keysym *keysym, keyNum_t key, qboolean down )
81 {
82 	if( down )
83 		Com_Printf( "+ " );
84 	else
85 		Com_Printf( "  " );
86 
87 	Com_Printf( "0x%02x \"%s\"", keysym->scancode,
88 			SDL_GetKeyName( keysym->sym ) );
89 
90 	if( keysym->mod & KMOD_LSHIFT )   Com_Printf( " KMOD_LSHIFT" );
91 	if( keysym->mod & KMOD_RSHIFT )   Com_Printf( " KMOD_RSHIFT" );
92 	if( keysym->mod & KMOD_LCTRL )    Com_Printf( " KMOD_LCTRL" );
93 	if( keysym->mod & KMOD_RCTRL )    Com_Printf( " KMOD_RCTRL" );
94 	if( keysym->mod & KMOD_LALT )     Com_Printf( " KMOD_LALT" );
95 	if( keysym->mod & KMOD_RALT )     Com_Printf( " KMOD_RALT" );
96 	if( keysym->mod & KMOD_LMETA )    Com_Printf( " KMOD_LMETA" );
97 	if( keysym->mod & KMOD_RMETA )    Com_Printf( " KMOD_RMETA" );
98 	if( keysym->mod & KMOD_NUM )      Com_Printf( " KMOD_NUM" );
99 	if( keysym->mod & KMOD_CAPS )     Com_Printf( " KMOD_CAPS" );
100 	if( keysym->mod & KMOD_MODE )     Com_Printf( " KMOD_MODE" );
101 	if( keysym->mod & KMOD_RESERVED ) Com_Printf( " KMOD_RESERVED" );
102 
103 	Com_Printf( " Q:0x%02x(%s)", key, Key_KeynumToString( key ) );
104 
105 	if( keysym->unicode )
106 	{
107 		Com_Printf( " U:0x%02x", keysym->unicode );
108 
109 		if( keysym->unicode > ' ' && keysym->unicode < '~' )
110 			Com_Printf( "(%c)", (char)keysym->unicode );
111 	}
112 
113 	Com_Printf( "\n" );
114 }
115 
116 #define MAX_CONSOLE_KEYS 16
117 
118 /*
119 ===============
120 IN_IsConsoleKey
121 ===============
122 */
IN_IsConsoleKey(keyNum_t key,const unsigned char character)123 static qboolean IN_IsConsoleKey( keyNum_t key, const unsigned char character )
124 {
125 	typedef struct consoleKey_s
126 	{
127 		enum
128 		{
129 			KEY,
130 			CHARACTER
131 		} type;
132 
133 		union
134 		{
135 			keyNum_t key;
136 			unsigned char character;
137 		} u;
138 	} consoleKey_t;
139 
140 	static consoleKey_t consoleKeys[ MAX_CONSOLE_KEYS ];
141 	static int numConsoleKeys = 0;
142 	int i;
143 
144 	// Only parse the variable when it changes
145 	if( cl_consoleKeys->modified )
146 	{
147 		char *text_p, *token;
148 
149 		cl_consoleKeys->modified = qfalse;
150 		text_p = cl_consoleKeys->string;
151 		numConsoleKeys = 0;
152 
153 		while( numConsoleKeys < MAX_CONSOLE_KEYS )
154 		{
155 			consoleKey_t *c = &consoleKeys[ numConsoleKeys ];
156 			int charCode = 0;
157 
158 			token = COM_Parse( &text_p );
159 			if( !token[ 0 ] )
160 				break;
161 
162 			if( strlen( token ) == 4 )
163 				charCode = Com_HexStrToInt( token );
164 
165 			if( charCode > 0 )
166 			{
167 				c->type = CHARACTER;
168 				c->u.character = (unsigned char)charCode;
169 			}
170 			else
171 			{
172 				c->type = KEY;
173 				c->u.key = Key_StringToKeynum( token );
174 
175 				// 0 isn't a key
176 				if( c->u.key <= 0 )
177 					continue;
178 			}
179 
180 			numConsoleKeys++;
181 		}
182 	}
183 
184 	// If the character is the same as the key, prefer the character
185 	if( key == character )
186 		key = 0;
187 
188 	for( i = 0; i < numConsoleKeys; i++ )
189 	{
190 		consoleKey_t *c = &consoleKeys[ i ];
191 
192 		switch( c->type )
193 		{
194 			case KEY:
195 				if( key && c->u.key == key )
196 					return qtrue;
197 				break;
198 
199 			case CHARACTER:
200 				if( c->u.character == character )
201 					return qtrue;
202 				break;
203 		}
204 	}
205 
206 	return qfalse;
207 }
208 
209 /*
210 ===============
211 IN_TranslateSDLToQ3Key
212 ===============
213 */
IN_TranslateSDLToQ3Key(SDL_keysym * keysym,keyNum_t * key,qboolean down)214 static const char *IN_TranslateSDLToQ3Key( SDL_keysym *keysym,
215 	keyNum_t *key, qboolean down )
216 {
217 	static unsigned char buf[ 2 ] = { '\0', '\0' };
218 
219 	*buf = '\0';
220 	*key = 0;
221 
222 	if( keysym->sym >= SDLK_SPACE && keysym->sym < SDLK_DELETE )
223 	{
224 		// These happen to match the ASCII chars
225 		*key = (int)keysym->sym;
226 	}
227 	else
228 	{
229 		switch( keysym->sym )
230 		{
231 			case SDLK_PAGEUP:       *key = K_PGUP;          break;
232 			case SDLK_KP9:          *key = K_KP_PGUP;       break;
233 			case SDLK_PAGEDOWN:     *key = K_PGDN;          break;
234 			case SDLK_KP3:          *key = K_KP_PGDN;       break;
235 			case SDLK_KP7:          *key = K_KP_HOME;       break;
236 			case SDLK_HOME:         *key = K_HOME;          break;
237 			case SDLK_KP1:          *key = K_KP_END;        break;
238 			case SDLK_END:          *key = K_END;           break;
239 			case SDLK_KP4:          *key = K_KP_LEFTARROW;  break;
240 			case SDLK_LEFT:         *key = K_LEFTARROW;     break;
241 			case SDLK_KP6:          *key = K_KP_RIGHTARROW; break;
242 			case SDLK_RIGHT:        *key = K_RIGHTARROW;    break;
243 			case SDLK_KP2:          *key = K_KP_DOWNARROW;  break;
244 			case SDLK_DOWN:         *key = K_DOWNARROW;     break;
245 			case SDLK_KP8:          *key = K_KP_UPARROW;    break;
246 			case SDLK_UP:           *key = K_UPARROW;       break;
247 			case SDLK_ESCAPE:       *key = K_ESCAPE;        break;
248 			case SDLK_KP_ENTER:     *key = K_KP_ENTER;      break;
249 			case SDLK_RETURN:       *key = K_ENTER;         break;
250 			case SDLK_TAB:          *key = K_TAB;           break;
251 			case SDLK_F1:           *key = K_F1;            break;
252 			case SDLK_F2:           *key = K_F2;            break;
253 			case SDLK_F3:           *key = K_F3;            break;
254 			case SDLK_F4:           *key = K_F4;            break;
255 			case SDLK_F5:           *key = K_F5;            break;
256 			case SDLK_F6:           *key = K_F6;            break;
257 			case SDLK_F7:           *key = K_F7;            break;
258 			case SDLK_F8:           *key = K_F8;            break;
259 			case SDLK_F9:           *key = K_F9;            break;
260 			case SDLK_F10:          *key = K_F10;           break;
261 			case SDLK_F11:          *key = K_F11;           break;
262 			case SDLK_F12:          *key = K_F12;           break;
263 			case SDLK_F13:          *key = K_F13;           break;
264 			case SDLK_F14:          *key = K_F14;           break;
265 			case SDLK_F15:          *key = K_F15;           break;
266 
267 			case SDLK_BACKSPACE:    *key = K_BACKSPACE;     break;
268 			case SDLK_KP_PERIOD:    *key = K_KP_DEL;        break;
269 			case SDLK_DELETE:       *key = K_DEL;           break;
270 			case SDLK_PAUSE:        *key = K_PAUSE;         break;
271 
272 			case SDLK_LSHIFT:
273 			case SDLK_RSHIFT:       *key = K_SHIFT;         break;
274 
275 			case SDLK_LCTRL:
276 			case SDLK_RCTRL:        *key = K_CTRL;          break;
277 
278 			case SDLK_RMETA:
279 			case SDLK_LMETA:        *key = K_COMMAND;       break;
280 
281 			case SDLK_RALT:
282 			case SDLK_LALT:         *key = K_ALT;           break;
283 
284 			case SDLK_LSUPER:
285 			case SDLK_RSUPER:       *key = K_SUPER;         break;
286 
287 			case SDLK_KP5:          *key = K_KP_5;          break;
288 			case SDLK_INSERT:       *key = K_INS;           break;
289 			case SDLK_KP0:          *key = K_KP_INS;        break;
290 			case SDLK_KP_MULTIPLY:  *key = K_KP_STAR;       break;
291 			case SDLK_KP_PLUS:      *key = K_KP_PLUS;       break;
292 			case SDLK_KP_MINUS:     *key = K_KP_MINUS;      break;
293 			case SDLK_KP_DIVIDE:    *key = K_KP_SLASH;      break;
294 
295 			case SDLK_MODE:         *key = K_MODE;          break;
296 			case SDLK_COMPOSE:      *key = K_COMPOSE;       break;
297 			case SDLK_HELP:         *key = K_HELP;          break;
298 			case SDLK_PRINT:        *key = K_PRINT;         break;
299 			case SDLK_SYSREQ:       *key = K_SYSREQ;        break;
300 			case SDLK_BREAK:        *key = K_BREAK;         break;
301 			case SDLK_MENU:         *key = K_MENU;          break;
302 			case SDLK_POWER:        *key = K_POWER;         break;
303 			case SDLK_EURO:         *key = K_EURO;          break;
304 			case SDLK_UNDO:         *key = K_UNDO;          break;
305 			case SDLK_SCROLLOCK:    *key = K_SCROLLOCK;     break;
306 			case SDLK_NUMLOCK:      *key = K_KP_NUMLOCK;    break;
307 			case SDLK_CAPSLOCK:     *key = K_CAPSLOCK;      break;
308 
309 			default:
310 				if( keysym->sym >= SDLK_WORLD_0 && keysym->sym <= SDLK_WORLD_95 )
311 					*key = ( keysym->sym - SDLK_WORLD_0 ) + K_WORLD_0;
312 				break;
313 		}
314 	}
315 
316 	if( down && keysym->unicode && !( keysym->unicode & 0xFF00 ) )
317 	{
318 		unsigned char ch = (unsigned char)keysym->unicode & 0xFF;
319 
320 		switch( ch )
321 		{
322 			case 127: // ASCII delete
323 				if( *key != K_DEL )
324 				{
325 					// ctrl-h
326 					*buf = CTRL('h');
327 					break;
328 				}
329 				// fallthrough
330 
331 			default: *buf = ch; break;
332 		}
333 	}
334 
335 	if( in_keyboardDebug->integer )
336 		IN_PrintKey( keysym, *key, down );
337 
338 	// Keys that have ASCII names but produce no character are probably
339 	// dead keys -- ignore them
340 	if( down && strlen( Key_KeynumToString( *key ) ) == 1 &&
341 		keysym->unicode == 0 )
342 	{
343 		if( in_keyboardDebug->integer )
344 			Com_Printf( "  Ignored dead key '%c'\n", *key );
345 
346 		*key = 0;
347 	}
348 
349 	if( IN_IsConsoleKey( *key, *buf ) )
350 	{
351 		// Console keys can't be bound or generate characters
352 		*key = K_CONSOLE;
353 		*buf = '\0';
354 	}
355 
356 	// Don't allow extended ASCII to generate characters
357 	if( *buf & 0x80 )
358 		*buf = '\0';
359 
360 	return (char *)buf;
361 }
362 
363 #ifdef MACOS_X_ACCELERATION_HACK
364 /*
365 ===============
366 IN_GetIOHandle
367 ===============
368 */
IN_GetIOHandle(void)369 static io_connect_t IN_GetIOHandle(void) // mac os x mouse accel hack
370 {
371 	io_connect_t iohandle = MACH_PORT_NULL;
372 	kern_return_t status;
373 	io_service_t iohidsystem = MACH_PORT_NULL;
374 	mach_port_t masterport;
375 
376 	status = IOMasterPort(MACH_PORT_NULL, &masterport);
377 	if(status != KERN_SUCCESS)
378 		return 0;
379 
380 	iohidsystem = IORegistryEntryFromPath(masterport, kIOServicePlane ":/IOResources/IOHIDSystem");
381 	if(!iohidsystem)
382 		return 0;
383 
384 	status = IOServiceOpen(iohidsystem, mach_task_self(), kIOHIDParamConnectType, &iohandle);
385 	IOObjectRelease(iohidsystem);
386 
387 	return iohandle;
388 }
389 #endif
390 
391 /*
392 ===============
393 IN_GobbleMotionEvents
394 ===============
395 */
IN_GobbleMotionEvents(void)396 static void IN_GobbleMotionEvents( void )
397 {
398 	SDL_Event dummy[ 1 ];
399 
400 	// Gobble any mouse motion events
401 	SDL_PumpEvents( );
402 	while( SDL_PeepEvents( dummy, 1, SDL_GETEVENT,
403 		SDL_EVENTMASK( SDL_MOUSEMOTION ) ) ) { }
404 }
405 
406 /*
407 ===============
408 IN_ActivateMouse
409 ===============
410 */
IN_ActivateMouse(void)411 static void IN_ActivateMouse( void )
412 {
413 	if (!mouseAvailable || !SDL_WasInit( SDL_INIT_VIDEO ) )
414 		return;
415 
416 #ifdef MACOS_X_ACCELERATION_HACK
417 	if (!mouseActive) // mac os x mouse accel hack
418 	{
419 		// Save the status of mouse acceleration
420 		originalMouseSpeed = -1.0; // in case of error
421 		if(in_disablemacosxmouseaccel->integer)
422 		{
423 			io_connect_t mouseDev = IN_GetIOHandle();
424 			if(mouseDev != 0)
425 			{
426 				if(IOHIDGetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), &originalMouseSpeed) == kIOReturnSuccess)
427 				{
428 					Com_Printf("previous mouse acceleration: %f\n", originalMouseSpeed);
429 					if(IOHIDSetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), -1.0) != kIOReturnSuccess)
430 					{
431 						Com_Printf("Could not disable mouse acceleration (failed at IOHIDSetAccelerationWithKey).\n");
432 						Cvar_Set ("in_disablemacosxmouseaccel", 0);
433 					}
434 				}
435 				else
436 				{
437 					Com_Printf("Could not disable mouse acceleration (failed at IOHIDGetAccelerationWithKey).\n");
438 					Cvar_Set ("in_disablemacosxmouseaccel", 0);
439 				}
440 				IOServiceClose(mouseDev);
441 			}
442 			else
443 			{
444 				Com_Printf("Could not disable mouse acceleration (failed at IO_GetIOHandle).\n");
445 				Cvar_Set ("in_disablemacosxmouseaccel", 0);
446 			}
447 		}
448 	}
449 #endif
450 
451 	if( !mouseActive )
452 	{
453 		SDL_ShowCursor( 0 );
454 #ifdef MACOS_X_CURSOR_HACK
455 		// This is a bug in the current SDL/macosx...have to toggle it a few
456 		//  times to get the cursor to hide.
457 		SDL_ShowCursor( 1 );
458 		SDL_ShowCursor( 0 );
459 #endif
460 		SDL_WM_GrabInput( SDL_GRAB_ON );
461 
462 		IN_GobbleMotionEvents( );
463 	}
464 
465 	// in_nograb makes no sense in fullscreen mode
466 	if( !r_fullscreen->integer )
467 	{
468 		if( in_nograb->modified || !mouseActive )
469 		{
470 			if( in_nograb->integer )
471 				SDL_WM_GrabInput( SDL_GRAB_OFF );
472 			else
473 				SDL_WM_GrabInput( SDL_GRAB_ON );
474 
475 			in_nograb->modified = qfalse;
476 		}
477 	}
478 
479 	mouseActive = qtrue;
480 }
481 
482 /*
483 ===============
484 IN_DeactivateMouse
485 ===============
486 */
IN_DeactivateMouse(void)487 static void IN_DeactivateMouse( void )
488 {
489 	if( !SDL_WasInit( SDL_INIT_VIDEO ) )
490 		return;
491 
492 	// Always show the cursor when the mouse is disabled,
493 	// but not when fullscreen
494 	if( !r_fullscreen->integer )
495 		SDL_ShowCursor( 1 );
496 
497 	if( !mouseAvailable )
498 		return;
499 
500 #ifdef MACOS_X_ACCELERATION_HACK
501 	if (mouseActive) // mac os x mouse accel hack
502 	{
503 		if(originalMouseSpeed != -1.0)
504 		{
505 			io_connect_t mouseDev = IN_GetIOHandle();
506 			if(mouseDev != 0)
507 			{
508 				Com_Printf("restoring mouse acceleration to: %f\n", originalMouseSpeed);
509 				if(IOHIDSetAccelerationWithKey(mouseDev, CFSTR(kIOHIDMouseAccelerationType), originalMouseSpeed) != kIOReturnSuccess)
510 					Com_Printf("Could not re-enable mouse acceleration (failed at IOHIDSetAccelerationWithKey).\n");
511 				IOServiceClose(mouseDev);
512 			}
513 			else
514 				Com_Printf("Could not re-enable mouse acceleration (failed at IO_GetIOHandle).\n");
515 		}
516 	}
517 #endif
518 
519 	if( mouseActive )
520 	{
521 		IN_GobbleMotionEvents( );
522 
523 		SDL_WM_GrabInput( SDL_GRAB_OFF );
524 
525 		// Don't warp the mouse unless the cursor is within the window
526 		if( SDL_GetAppState( ) & SDL_APPMOUSEFOCUS )
527 			SDL_WarpMouse( glConfig.vidWidth / 2, glConfig.vidHeight / 2 );
528 
529 		mouseActive = qfalse;
530 	}
531 }
532 
533 // We translate axes movement into keypresses
534 static int joy_keys[16] = {
535 	K_LEFTARROW, K_RIGHTARROW,
536 	K_UPARROW, K_DOWNARROW,
537 	K_JOY16, K_JOY17,
538 	K_JOY18, K_JOY19,
539 	K_JOY20, K_JOY21,
540 	K_JOY22, K_JOY23,
541 
542 	K_JOY24, K_JOY25,
543 	K_JOY26, K_JOY27
544 };
545 
546 // translate hat events into keypresses
547 // the 4 highest buttons are used for the first hat ...
548 static int hat_keys[16] = {
549 	K_JOY29, K_JOY30,
550 	K_JOY31, K_JOY32,
551 	K_JOY25, K_JOY26,
552 	K_JOY27, K_JOY28,
553 	K_JOY21, K_JOY22,
554 	K_JOY23, K_JOY24,
555 	K_JOY17, K_JOY18,
556 	K_JOY19, K_JOY20
557 };
558 
559 
560 struct
561 {
562 	qboolean buttons[16];  // !!! FIXME: these might be too many.
563 	unsigned int oldaxes;
564 	unsigned int oldhats;
565 } stick_state;
566 
567 
568 /*
569 ===============
570 IN_InitJoystick
571 ===============
572 */
IN_InitJoystick(void)573 static void IN_InitJoystick( void )
574 {
575 	int i = 0;
576 	int total = 0;
577 
578 	if (stick != NULL)
579 		SDL_JoystickClose(stick);
580 
581 	stick = NULL;
582 	memset(&stick_state, '\0', sizeof (stick_state));
583 
584 	if( !in_joystick->integer ) {
585 		Com_DPrintf( "Joystick is not active.\n" );
586 		return;
587 	}
588 
589 	if (!SDL_WasInit(SDL_INIT_JOYSTICK))
590 	{
591 		Com_DPrintf("Calling SDL_Init(SDL_INIT_JOYSTICK)...\n");
592 		if (SDL_Init(SDL_INIT_JOYSTICK) == -1)
593 		{
594 			Com_DPrintf("SDL_Init(SDL_INIT_JOYSTICK) failed: %s\n", SDL_GetError());
595 			return;
596 		}
597 		Com_DPrintf("SDL_Init(SDL_INIT_JOYSTICK) passed.\n");
598 	}
599 
600 	total = SDL_NumJoysticks();
601 	Com_DPrintf("%d possible joysticks\n", total);
602 	for (i = 0; i < total; i++)
603 		Com_DPrintf("[%d] %s\n", i, SDL_JoystickName(i));
604 
605 	in_joystickNo = Cvar_Get( "in_joystickNo", "0", CVAR_ARCHIVE );
606 	if( in_joystickNo->integer < 0 || in_joystickNo->integer >= total )
607 		Cvar_Set( "in_joystickNo", "0" );
608 
609 	stick = SDL_JoystickOpen( in_joystickNo->integer );
610 
611 	if (stick == NULL) {
612 		Com_DPrintf( "No joystick opened.\n" );
613 		return;
614 	}
615 
616 	Com_DPrintf( "Joystick %d opened\n", in_joystickNo->integer );
617 	Com_DPrintf( "Name:    %s\n", SDL_JoystickName(in_joystickNo->integer) );
618 	Com_DPrintf( "Axes:    %d\n", SDL_JoystickNumAxes(stick) );
619 	Com_DPrintf( "Hats:    %d\n", SDL_JoystickNumHats(stick) );
620 	Com_DPrintf( "Buttons: %d\n", SDL_JoystickNumButtons(stick) );
621 	Com_DPrintf( "Balls: %d\n", SDL_JoystickNumBalls(stick) );
622 
623 	SDL_JoystickEventState(SDL_QUERY);
624 }
625 
626 /*
627 ===============
628 IN_ShutdownJoystick
629 ===============
630 */
IN_ShutdownJoystick(void)631 static void IN_ShutdownJoystick( void )
632 {
633 	if (stick)
634 	{
635 		SDL_JoystickClose(stick);
636 		stick = NULL;
637 	}
638 
639 	SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
640 }
641 
642 /*
643 ===============
644 IN_JoyMove
645 ===============
646 */
IN_JoyMove(void)647 static void IN_JoyMove( void )
648 {
649 	qboolean joy_pressed[ARRAYLEN(joy_keys)];
650 	unsigned int axes = 0;
651 	unsigned int hats = 0;
652 	int total = 0;
653 	int i = 0;
654 
655 	if (!stick)
656 		return;
657 
658 	SDL_JoystickUpdate();
659 
660 	memset(joy_pressed, '\0', sizeof (joy_pressed));
661 
662 	// update the ball state.
663 	total = SDL_JoystickNumBalls(stick);
664 	if (total > 0)
665 	{
666 		int balldx = 0;
667 		int balldy = 0;
668 		for (i = 0; i < total; i++)
669 		{
670 			int dx = 0;
671 			int dy = 0;
672 			SDL_JoystickGetBall(stick, i, &dx, &dy);
673 			balldx += dx;
674 			balldy += dy;
675 		}
676 		if (balldx || balldy)
677 		{
678 			// !!! FIXME: is this good for stick balls, or just mice?
679 			// Scale like the mouse input...
680 			if (abs(balldx) > 1)
681 				balldx *= 2;
682 			if (abs(balldy) > 1)
683 				balldy *= 2;
684 			Com_QueueEvent( 0, SE_MOUSE, balldx, balldy, 0, NULL );
685 		}
686 	}
687 
688 	// now query the stick buttons...
689 	total = SDL_JoystickNumButtons(stick);
690 	if (total > 0)
691 	{
692 		if (total > ARRAYLEN(stick_state.buttons))
693 			total = ARRAYLEN(stick_state.buttons);
694 		for (i = 0; i < total; i++)
695 		{
696 			qboolean pressed = (SDL_JoystickGetButton(stick, i) != 0);
697 			if (pressed != stick_state.buttons[i])
698 			{
699 				Com_QueueEvent( 0, SE_KEY, K_JOY1 + i, pressed, 0, NULL );
700 				stick_state.buttons[i] = pressed;
701 			}
702 		}
703 	}
704 
705 	// look at the hats...
706 	total = SDL_JoystickNumHats(stick);
707 	if (total > 0)
708 	{
709 		if (total > 4) total = 4;
710 		for (i = 0; i < total; i++)
711 		{
712 			((Uint8 *)&hats)[i] = SDL_JoystickGetHat(stick, i);
713 		}
714 	}
715 
716 	// update hat state
717 	if (hats != stick_state.oldhats)
718 	{
719 		for( i = 0; i < 4; i++ ) {
720 			if( ((Uint8 *)&hats)[i] != ((Uint8 *)&stick_state.oldhats)[i] ) {
721 				// release event
722 				switch( ((Uint8 *)&stick_state.oldhats)[i] ) {
723 					case SDL_HAT_UP:
724 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 0], qfalse, 0, NULL );
725 						break;
726 					case SDL_HAT_RIGHT:
727 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 1], qfalse, 0, NULL );
728 						break;
729 					case SDL_HAT_DOWN:
730 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 2], qfalse, 0, NULL );
731 						break;
732 					case SDL_HAT_LEFT:
733 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 3], qfalse, 0, NULL );
734 						break;
735 					case SDL_HAT_RIGHTUP:
736 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 0], qfalse, 0, NULL );
737 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 1], qfalse, 0, NULL );
738 						break;
739 					case SDL_HAT_RIGHTDOWN:
740 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 2], qfalse, 0, NULL );
741 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 1], qfalse, 0, NULL );
742 						break;
743 					case SDL_HAT_LEFTUP:
744 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 0], qfalse, 0, NULL );
745 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 3], qfalse, 0, NULL );
746 						break;
747 					case SDL_HAT_LEFTDOWN:
748 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 2], qfalse, 0, NULL );
749 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 3], qfalse, 0, NULL );
750 						break;
751 					default:
752 						break;
753 				}
754 				// press event
755 				switch( ((Uint8 *)&hats)[i] ) {
756 					case SDL_HAT_UP:
757 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 0], qtrue, 0, NULL );
758 						break;
759 					case SDL_HAT_RIGHT:
760 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 1], qtrue, 0, NULL );
761 						break;
762 					case SDL_HAT_DOWN:
763 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 2], qtrue, 0, NULL );
764 						break;
765 					case SDL_HAT_LEFT:
766 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 3], qtrue, 0, NULL );
767 						break;
768 					case SDL_HAT_RIGHTUP:
769 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 0], qtrue, 0, NULL );
770 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 1], qtrue, 0, NULL );
771 						break;
772 					case SDL_HAT_RIGHTDOWN:
773 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 2], qtrue, 0, NULL );
774 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 1], qtrue, 0, NULL );
775 						break;
776 					case SDL_HAT_LEFTUP:
777 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 0], qtrue, 0, NULL );
778 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 3], qtrue, 0, NULL );
779 						break;
780 					case SDL_HAT_LEFTDOWN:
781 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 2], qtrue, 0, NULL );
782 						Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 3], qtrue, 0, NULL );
783 						break;
784 					default:
785 						break;
786 				}
787 			}
788 		}
789 	}
790 
791 	// save hat state
792 	stick_state.oldhats = hats;
793 
794 	// finally, look at the axes...
795 	total = SDL_JoystickNumAxes(stick);
796 	if (total > 0)
797 	{
798 		if (total > 16) total = 16;
799 		for (i = 0; i < total; i++)
800 		{
801 			Sint16 axis = SDL_JoystickGetAxis(stick, i);
802 			float f = ( (float) axis ) / 32767.0f;
803 			if( f < -in_joystickThreshold->value ) {
804 				axes |= ( 1 << ( i * 2 ) );
805 			} else if( f > in_joystickThreshold->value ) {
806 				axes |= ( 1 << ( ( i * 2 ) + 1 ) );
807 			}
808 		}
809 	}
810 
811 	/* Time to update axes state based on old vs. new. */
812 	if (axes != stick_state.oldaxes)
813 	{
814 		for( i = 0; i < 16; i++ ) {
815 			if( ( axes & ( 1 << i ) ) && !( stick_state.oldaxes & ( 1 << i ) ) ) {
816 				Com_QueueEvent( 0, SE_KEY, joy_keys[i], qtrue, 0, NULL );
817 			}
818 
819 			if( !( axes & ( 1 << i ) ) && ( stick_state.oldaxes & ( 1 << i ) ) ) {
820 				Com_QueueEvent( 0, SE_KEY, joy_keys[i], qfalse, 0, NULL );
821 			}
822 		}
823 	}
824 
825 	/* Save for future generations. */
826 	stick_state.oldaxes = axes;
827 }
828 
829 /*
830 ===============
831 IN_ProcessEvents
832 ===============
833 */
IN_ProcessEvents(void)834 static void IN_ProcessEvents( void )
835 {
836 	SDL_Event e;
837 	const char *character = NULL;
838 	keyNum_t key = 0;
839 
840 	if( !SDL_WasInit( SDL_INIT_VIDEO ) )
841 			return;
842 
843 	if( Key_GetCatcher( ) == 0 && keyRepeatEnabled )
844 	{
845 		SDL_EnableKeyRepeat( 0, 0 );
846 		keyRepeatEnabled = qfalse;
847 	}
848 	else if( !keyRepeatEnabled )
849 	{
850 		SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY,
851 			SDL_DEFAULT_REPEAT_INTERVAL );
852 		keyRepeatEnabled = qtrue;
853 	}
854 
855 	while( SDL_PollEvent( &e ) )
856 	{
857 		switch( e.type )
858 		{
859 			case SDL_KEYDOWN:
860 				character = IN_TranslateSDLToQ3Key( &e.key.keysym, &key, qtrue );
861 				if( key )
862 					Com_QueueEvent( 0, SE_KEY, key, qtrue, 0, NULL );
863 
864 				if( character )
865 					Com_QueueEvent( 0, SE_CHAR, *character, 0, 0, NULL );
866 				break;
867 
868 			case SDL_KEYUP:
869 				IN_TranslateSDLToQ3Key( &e.key.keysym, &key, qfalse );
870 
871 				if( key )
872 					Com_QueueEvent( 0, SE_KEY, key, qfalse, 0, NULL );
873 				break;
874 
875 			case SDL_MOUSEMOTION:
876 				if( mouseActive )
877 					Com_QueueEvent( 0, SE_MOUSE, e.motion.xrel, e.motion.yrel, 0, NULL );
878 				break;
879 
880 			case SDL_MOUSEBUTTONDOWN:
881 			case SDL_MOUSEBUTTONUP:
882 				{
883 					unsigned char b;
884 					switch( e.button.button )
885 					{
886 						case 1:   b = K_MOUSE1;     break;
887 						case 2:   b = K_MOUSE3;     break;
888 						case 3:   b = K_MOUSE2;     break;
889 						case 4:   b = K_MWHEELUP;   break;
890 						case 5:   b = K_MWHEELDOWN; break;
891 						case 6:   b = K_MOUSE4;     break;
892 						case 7:   b = K_MOUSE5;     break;
893 						default:  b = K_AUX1 + ( e.button.button - 8 ) % 16; break;
894 					}
895 					Com_QueueEvent( 0, SE_KEY, b,
896 						( e.type == SDL_MOUSEBUTTONDOWN ? qtrue : qfalse ), 0, NULL );
897 				}
898 				break;
899 
900 			case SDL_QUIT:
901 				Sys_Quit( );
902 				break;
903 
904 			default:
905 				break;
906 		}
907 	}
908 }
909 
910 /*
911 ===============
912 IN_Frame
913 ===============
914 */
IN_Frame(void)915 void IN_Frame( void )
916 {
917 	qboolean loading;
918 
919 	IN_JoyMove( );
920 	IN_ProcessEvents( );
921 
922 	// If not DISCONNECTED (main menu) or ACTIVE (in game), we're loading
923 	loading = !!( cls.state != CA_DISCONNECTED && cls.state != CA_ACTIVE );
924 
925 	if( !r_fullscreen->integer && ( Key_GetCatcher( ) & KEYCATCH_CONSOLE ) )
926 	{
927 		// Console is down in windowed mode
928 		IN_DeactivateMouse( );
929 	}
930 	else if( !r_fullscreen->integer && loading )
931 	{
932 		// Loading in windowed mode
933 		IN_DeactivateMouse( );
934 	}
935 	else if( !( SDL_GetAppState() & SDL_APPINPUTFOCUS ) )
936 	{
937 		// Window not got focus
938 		IN_DeactivateMouse( );
939 	}
940 	else
941 		IN_ActivateMouse( );
942 }
943 
944 /*
945 ===============
946 IN_Init
947 ===============
948 */
IN_Init(void)949 void IN_Init( void )
950 {
951 	if( !SDL_WasInit( SDL_INIT_VIDEO ) )
952 	{
953 		Com_Error( ERR_FATAL, "IN_Init called before SDL_Init( SDL_INIT_VIDEO )\n" );
954 		return;
955 	}
956 
957 	Com_DPrintf( "\n------- Input Initialization -------\n" );
958 
959 	in_keyboardDebug = Cvar_Get( "in_keyboardDebug", "0", CVAR_ARCHIVE );
960 
961 	// mouse variables
962 	in_mouse = Cvar_Get( "in_mouse", "1", CVAR_ARCHIVE );
963 	in_nograb = Cvar_Get( "in_nograb", "0", CVAR_ARCHIVE );
964 
965 	in_joystick = Cvar_Get( "in_joystick", "0", CVAR_ARCHIVE|CVAR_LATCH );
966 	in_joystickDebug = Cvar_Get( "in_joystickDebug", "0", CVAR_TEMP );
967 	in_joystickThreshold = Cvar_Get( "in_joystickThreshold", "0.15", CVAR_ARCHIVE );
968 
969 #ifdef MACOS_X_ACCELERATION_HACK
970 	in_disablemacosxmouseaccel = Cvar_Get( "in_disablemacosxmouseaccel", "1", CVAR_ARCHIVE );
971 #endif
972 
973 	SDL_EnableUNICODE( 1 );
974 	SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
975 	keyRepeatEnabled = qtrue;
976 
977 	if( in_mouse->value )
978 	{
979 		mouseAvailable = qtrue;
980 		IN_ActivateMouse( );
981 	}
982 	else
983 	{
984 		IN_DeactivateMouse( );
985 		mouseAvailable = qfalse;
986 	}
987 
988 	IN_InitJoystick( );
989 	Com_DPrintf( "------------------------------------\n" );
990 }
991 
992 /*
993 ===============
994 IN_Shutdown
995 ===============
996 */
IN_Shutdown(void)997 void IN_Shutdown( void )
998 {
999 	IN_DeactivateMouse( );
1000 	mouseAvailable = qfalse;
1001 
1002 	IN_ShutdownJoystick( );
1003 }
1004 
1005 /*
1006 ===============
1007 IN_Restart
1008 ===============
1009 */
IN_Restart(void)1010 void IN_Restart( void )
1011 {
1012 	IN_ShutdownJoystick( );
1013 	IN_Init( );
1014 }
1015