Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

in_win.c (19786B)


      1 /*
      2 Copyright (C) 1997-2001 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 // in_win.c -- windows 95 mouse and joystick code
     21 // 02/21/97 JCB Added extended DirectInput code to support external controllers.
     22 
     23 #include "../client/client.h"
     24 #include "winquake.h"
     25 
     26 extern	unsigned	sys_msg_time;
     27 
     28 // joystick defines and variables
     29 // where should defines be moved?
     30 #define JOY_ABSOLUTE_AXIS	0x00000000		// control like a joystick
     31 #define JOY_RELATIVE_AXIS	0x00000010		// control like a mouse, spinner, trackball
     32 #define	JOY_MAX_AXES		6				// X, Y, Z, R, U, V
     33 #define JOY_AXIS_X			0
     34 #define JOY_AXIS_Y			1
     35 #define JOY_AXIS_Z			2
     36 #define JOY_AXIS_R			3
     37 #define JOY_AXIS_U			4
     38 #define JOY_AXIS_V			5
     39 
     40 enum _ControlList
     41 {
     42 	AxisNada = 0, AxisForward, AxisLook, AxisSide, AxisTurn, AxisUp
     43 };
     44 
     45 DWORD	dwAxisFlags[JOY_MAX_AXES] =
     46 {
     47 	JOY_RETURNX, JOY_RETURNY, JOY_RETURNZ, JOY_RETURNR, JOY_RETURNU, JOY_RETURNV
     48 };
     49 
     50 DWORD	dwAxisMap[JOY_MAX_AXES];
     51 DWORD	dwControlMap[JOY_MAX_AXES];
     52 PDWORD	pdwRawValue[JOY_MAX_AXES];
     53 
     54 cvar_t	*in_mouse;
     55 cvar_t	*in_joystick;
     56 
     57 
     58 // none of these cvars are saved over a session
     59 // this means that advanced controller configuration needs to be executed
     60 // each time.  this avoids any problems with getting back to a default usage
     61 // or when changing from one controller to another.  this way at least something
     62 // works.
     63 cvar_t	*joy_name;
     64 cvar_t	*joy_advanced;
     65 cvar_t	*joy_advaxisx;
     66 cvar_t	*joy_advaxisy;
     67 cvar_t	*joy_advaxisz;
     68 cvar_t	*joy_advaxisr;
     69 cvar_t	*joy_advaxisu;
     70 cvar_t	*joy_advaxisv;
     71 cvar_t	*joy_forwardthreshold;
     72 cvar_t	*joy_sidethreshold;
     73 cvar_t	*joy_pitchthreshold;
     74 cvar_t	*joy_yawthreshold;
     75 cvar_t	*joy_forwardsensitivity;
     76 cvar_t	*joy_sidesensitivity;
     77 cvar_t	*joy_pitchsensitivity;
     78 cvar_t	*joy_yawsensitivity;
     79 cvar_t	*joy_upthreshold;
     80 cvar_t	*joy_upsensitivity;
     81 
     82 qboolean	joy_avail, joy_advancedinit, joy_haspov;
     83 DWORD		joy_oldbuttonstate, joy_oldpovstate;
     84 
     85 int			joy_id;
     86 DWORD		joy_flags;
     87 DWORD		joy_numbuttons;
     88 
     89 static JOYINFOEX	ji;
     90 
     91 qboolean	in_appactive;
     92 
     93 // forward-referenced functions
     94 void IN_StartupJoystick (void);
     95 void Joy_AdvancedUpdate_f (void);
     96 void IN_JoyMove (usercmd_t *cmd);
     97 
     98 /*
     99 ============================================================
    100 
    101   MOUSE CONTROL
    102 
    103 ============================================================
    104 */
    105 
    106 // mouse variables
    107 cvar_t	*m_filter;
    108 
    109 qboolean	mlooking;
    110 
    111 void IN_MLookDown (void) { mlooking = true; }
    112 void IN_MLookUp (void) {
    113 mlooking = false;
    114 if (!freelook->value && lookspring->value)
    115 		IN_CenterView ();
    116 }
    117 
    118 int			mouse_buttons;
    119 int			mouse_oldbuttonstate;
    120 POINT		current_pos;
    121 int			mouse_x, mouse_y, old_mouse_x, old_mouse_y, mx_accum, my_accum;
    122 
    123 int			old_x, old_y;
    124 
    125 qboolean	mouseactive;	// false when not focus app
    126 
    127 qboolean	restore_spi;
    128 qboolean	mouseinitialized;
    129 int		originalmouseparms[3], newmouseparms[3] = {0, 0, 1};
    130 qboolean	mouseparmsvalid;
    131 
    132 int			window_center_x, window_center_y;
    133 RECT		window_rect;
    134 
    135 
    136 /*
    137 ===========
    138 IN_ActivateMouse
    139 
    140 Called when the window gains focus or changes in some way
    141 ===========
    142 */
    143 void IN_ActivateMouse (void)
    144 {
    145 	int		width, height;
    146 
    147 	if (!mouseinitialized)
    148 		return;
    149 	if (!in_mouse->value)
    150 	{
    151 		mouseactive = false;
    152 		return;
    153 	}
    154 	if (mouseactive)
    155 		return;
    156 
    157 	mouseactive = true;
    158 
    159 	if (mouseparmsvalid)
    160 		restore_spi = SystemParametersInfo (SPI_SETMOUSE, 0, newmouseparms, 0);
    161 
    162 	width = GetSystemMetrics (SM_CXSCREEN);
    163 	height = GetSystemMetrics (SM_CYSCREEN);
    164 
    165 	GetWindowRect ( cl_hwnd, &window_rect);
    166 	if (window_rect.left < 0)
    167 		window_rect.left = 0;
    168 	if (window_rect.top < 0)
    169 		window_rect.top = 0;
    170 	if (window_rect.right >= width)
    171 		window_rect.right = width-1;
    172 	if (window_rect.bottom >= height-1)
    173 		window_rect.bottom = height-1;
    174 
    175 	window_center_x = (window_rect.right + window_rect.left)/2;
    176 	window_center_y = (window_rect.top + window_rect.bottom)/2;
    177 
    178 	SetCursorPos (window_center_x, window_center_y);
    179 
    180 	old_x = window_center_x;
    181 	old_y = window_center_y;
    182 
    183 	SetCapture ( cl_hwnd );
    184 	ClipCursor (&window_rect);
    185 	while (ShowCursor (FALSE) >= 0)
    186 		;
    187 }
    188 
    189 
    190 /*
    191 ===========
    192 IN_DeactivateMouse
    193 
    194 Called when the window loses focus
    195 ===========
    196 */
    197 void IN_DeactivateMouse (void)
    198 {
    199 	if (!mouseinitialized)
    200 		return;
    201 	if (!mouseactive)
    202 		return;
    203 
    204 	if (restore_spi)
    205 		SystemParametersInfo (SPI_SETMOUSE, 0, originalmouseparms, 0);
    206 
    207 	mouseactive = false;
    208 
    209 	ClipCursor (NULL);
    210 	ReleaseCapture ();
    211 	while (ShowCursor (TRUE) < 0)
    212 		;
    213 }
    214 
    215 
    216 
    217 /*
    218 ===========
    219 IN_StartupMouse
    220 ===========
    221 */
    222 void IN_StartupMouse (void)
    223 {
    224 	cvar_t		*cv;
    225 
    226 	cv = Cvar_Get ("in_initmouse", "1", CVAR_NOSET);
    227 	if ( !cv->value ) 
    228 		return; 
    229 
    230 	mouseinitialized = true;
    231 	mouseparmsvalid = SystemParametersInfo (SPI_GETMOUSE, 0, originalmouseparms, 0);
    232 	mouse_buttons = 3;
    233 }
    234 
    235 /*
    236 ===========
    237 IN_MouseEvent
    238 ===========
    239 */
    240 void IN_MouseEvent (int mstate)
    241 {
    242 	int		i;
    243 
    244 	if (!mouseinitialized)
    245 		return;
    246 
    247 // perform button actions
    248 	for (i=0 ; i<mouse_buttons ; i++)
    249 	{
    250 		if ( (mstate & (1<<i)) &&
    251 			!(mouse_oldbuttonstate & (1<<i)) )
    252 		{
    253 			Key_Event (K_MOUSE1 + i, true, sys_msg_time);
    254 		}
    255 
    256 		if ( !(mstate & (1<<i)) &&
    257 			(mouse_oldbuttonstate & (1<<i)) )
    258 		{
    259 				Key_Event (K_MOUSE1 + i, false, sys_msg_time);
    260 		}
    261 	}	
    262 		
    263 	mouse_oldbuttonstate = mstate;
    264 }
    265 
    266 
    267 /*
    268 ===========
    269 IN_MouseMove
    270 ===========
    271 */
    272 void IN_MouseMove (usercmd_t *cmd)
    273 {
    274 	int		mx, my;
    275 
    276 	if (!mouseactive)
    277 		return;
    278 
    279 	// find mouse movement
    280 	if (!GetCursorPos (&current_pos))
    281 		return;
    282 
    283 	mx = current_pos.x - window_center_x;
    284 	my = current_pos.y - window_center_y;
    285 
    286 #if 0
    287 	if (!mx && !my)
    288 		return;
    289 #endif
    290 
    291 	if (m_filter->value)
    292 	{
    293 		mouse_x = (mx + old_mouse_x) * 0.5;
    294 		mouse_y = (my + old_mouse_y) * 0.5;
    295 	}
    296 	else
    297 	{
    298 		mouse_x = mx;
    299 		mouse_y = my;
    300 	}
    301 
    302 	old_mouse_x = mx;
    303 	old_mouse_y = my;
    304 
    305 	mouse_x *= sensitivity->value;
    306 	mouse_y *= sensitivity->value;
    307 
    308 // add mouse X/Y movement to cmd
    309 	if ( (in_strafe.state & 1) || (lookstrafe->value && mlooking ))
    310 		cmd->sidemove += m_side->value * mouse_x;
    311 	else
    312 		cl.viewangles[YAW] -= m_yaw->value * mouse_x;
    313 
    314 	if ( (mlooking || freelook->value) && !(in_strafe.state & 1))
    315 	{
    316 		cl.viewangles[PITCH] += m_pitch->value * mouse_y;
    317 	}
    318 	else
    319 	{
    320 		cmd->forwardmove -= m_forward->value * mouse_y;
    321 	}
    322 
    323 	// force the mouse to the center, so there's room to move
    324 	if (mx || my)
    325 		SetCursorPos (window_center_x, window_center_y);
    326 }
    327 
    328 
    329 /*
    330 =========================================================================
    331 
    332 VIEW CENTERING
    333 
    334 =========================================================================
    335 */
    336 
    337 cvar_t	*v_centermove;
    338 cvar_t	*v_centerspeed;
    339 
    340 
    341 /*
    342 ===========
    343 IN_Init
    344 ===========
    345 */
    346 void IN_Init (void)
    347 {
    348 	// mouse variables
    349 	m_filter				= Cvar_Get ("m_filter",					"0",		0);
    350     in_mouse				= Cvar_Get ("in_mouse",					"1",		CVAR_ARCHIVE);
    351 
    352 	// joystick variables
    353 	in_joystick				= Cvar_Get ("in_joystick",				"0",		CVAR_ARCHIVE);
    354 	joy_name				= Cvar_Get ("joy_name",					"joystick",	0);
    355 	joy_advanced			= Cvar_Get ("joy_advanced",				"0",		0);
    356 	joy_advaxisx			= Cvar_Get ("joy_advaxisx",				"0",		0);
    357 	joy_advaxisy			= Cvar_Get ("joy_advaxisy",				"0",		0);
    358 	joy_advaxisz			= Cvar_Get ("joy_advaxisz",				"0",		0);
    359 	joy_advaxisr			= Cvar_Get ("joy_advaxisr",				"0",		0);
    360 	joy_advaxisu			= Cvar_Get ("joy_advaxisu",				"0",		0);
    361 	joy_advaxisv			= Cvar_Get ("joy_advaxisv",				"0",		0);
    362 	joy_forwardthreshold	= Cvar_Get ("joy_forwardthreshold",		"0.15",		0);
    363 	joy_sidethreshold		= Cvar_Get ("joy_sidethreshold",		"0.15",		0);
    364 	joy_upthreshold  		= Cvar_Get ("joy_upthreshold",			"0.15",		0);
    365 	joy_pitchthreshold		= Cvar_Get ("joy_pitchthreshold",		"0.15",		0);
    366 	joy_yawthreshold		= Cvar_Get ("joy_yawthreshold",			"0.15",		0);
    367 	joy_forwardsensitivity	= Cvar_Get ("joy_forwardsensitivity",	"-1",		0);
    368 	joy_sidesensitivity		= Cvar_Get ("joy_sidesensitivity",		"-1",		0);
    369 	joy_upsensitivity		= Cvar_Get ("joy_upsensitivity",		"-1",		0);
    370 	joy_pitchsensitivity	= Cvar_Get ("joy_pitchsensitivity",		"1",		0);
    371 	joy_yawsensitivity		= Cvar_Get ("joy_yawsensitivity",		"-1",		0);
    372 
    373 	// centering
    374 	v_centermove			= Cvar_Get ("v_centermove",				"0.15",		0);
    375 	v_centerspeed			= Cvar_Get ("v_centerspeed",			"500",		0);
    376 
    377 	Cmd_AddCommand ("+mlook", IN_MLookDown);
    378 	Cmd_AddCommand ("-mlook", IN_MLookUp);
    379 
    380 	Cmd_AddCommand ("joy_advancedupdate", Joy_AdvancedUpdate_f);
    381 
    382 	IN_StartupMouse ();
    383 	IN_StartupJoystick ();
    384 }
    385 
    386 /*
    387 ===========
    388 IN_Shutdown
    389 ===========
    390 */
    391 void IN_Shutdown (void)
    392 {
    393 	IN_DeactivateMouse ();
    394 }
    395 
    396 
    397 /*
    398 ===========
    399 IN_Activate
    400 
    401 Called when the main window gains or loses focus.
    402 The window may have been destroyed and recreated
    403 between a deactivate and an activate.
    404 ===========
    405 */
    406 void IN_Activate (qboolean active)
    407 {
    408 	in_appactive = active;
    409 	mouseactive = !active;		// force a new window check or turn off
    410 }
    411 
    412 
    413 /*
    414 ==================
    415 IN_Frame
    416 
    417 Called every frame, even if not generating commands
    418 ==================
    419 */
    420 void IN_Frame (void)
    421 {
    422 	if (!mouseinitialized)
    423 		return;
    424 
    425 	if (!in_mouse || !in_appactive)
    426 	{
    427 		IN_DeactivateMouse ();
    428 		return;
    429 	}
    430 
    431 	if ( !cl.refresh_prepped
    432 		|| cls.key_dest == key_console
    433 		|| cls.key_dest == key_menu)
    434 	{
    435 		// temporarily deactivate if in fullscreen
    436 		if (Cvar_VariableValue ("vid_fullscreen") == 0)
    437 		{
    438 			IN_DeactivateMouse ();
    439 			return;
    440 		}
    441 	}
    442 
    443 	IN_ActivateMouse ();
    444 }
    445 
    446 /*
    447 ===========
    448 IN_Move
    449 ===========
    450 */
    451 void IN_Move (usercmd_t *cmd)
    452 {
    453 	IN_MouseMove (cmd);
    454 
    455 	if (ActiveApp)
    456 		IN_JoyMove (cmd);
    457 }
    458 
    459 
    460 /*
    461 ===================
    462 IN_ClearStates
    463 ===================
    464 */
    465 void IN_ClearStates (void)
    466 {
    467 	mx_accum = 0;
    468 	my_accum = 0;
    469 	mouse_oldbuttonstate = 0;
    470 }
    471 
    472 
    473 /*
    474 =========================================================================
    475 
    476 JOYSTICK
    477 
    478 =========================================================================
    479 */
    480 
    481 /* 
    482 =============== 
    483 IN_StartupJoystick 
    484 =============== 
    485 */  
    486 void IN_StartupJoystick (void) 
    487 { 
    488 	int			numdevs;
    489 	JOYCAPS		jc;
    490 	MMRESULT	mmr;
    491 	cvar_t		*cv;
    492 
    493  	// assume no joystick
    494 	joy_avail = false; 
    495 
    496 	// abort startup if user requests no joystick
    497 	cv = Cvar_Get ("in_initjoy", "1", CVAR_NOSET);
    498 	if ( !cv->value ) 
    499 		return; 
    500  
    501 	// verify joystick driver is present
    502 	if ((numdevs = joyGetNumDevs ()) == 0)
    503 	{
    504 //		Com_Printf ("\njoystick not found -- driver not present\n\n");
    505 		return;
    506 	}
    507 
    508 	// cycle through the joystick ids for the first valid one
    509 	for (joy_id=0 ; joy_id<numdevs ; joy_id++)
    510 	{
    511 		memset (&ji, 0, sizeof(ji));
    512 		ji.dwSize = sizeof(ji);
    513 		ji.dwFlags = JOY_RETURNCENTERED;
    514 
    515 		if ((mmr = joyGetPosEx (joy_id, &ji)) == JOYERR_NOERROR)
    516 			break;
    517 	} 
    518 
    519 	// abort startup if we didn't find a valid joystick
    520 	if (mmr != JOYERR_NOERROR)
    521 	{
    522 		Com_Printf ("\njoystick not found -- no valid joysticks (%x)\n\n", mmr);
    523 		return;
    524 	}
    525 
    526 	// get the capabilities of the selected joystick
    527 	// abort startup if command fails
    528 	memset (&jc, 0, sizeof(jc));
    529 	if ((mmr = joyGetDevCaps (joy_id, &jc, sizeof(jc))) != JOYERR_NOERROR)
    530 	{
    531 		Com_Printf ("\njoystick not found -- invalid joystick capabilities (%x)\n\n", mmr); 
    532 		return;
    533 	}
    534 
    535 	// save the joystick's number of buttons and POV status
    536 	joy_numbuttons = jc.wNumButtons;
    537 	joy_haspov = jc.wCaps & JOYCAPS_HASPOV;
    538 
    539 	// old button and POV states default to no buttons pressed
    540 	joy_oldbuttonstate = joy_oldpovstate = 0;
    541 
    542 	// mark the joystick as available and advanced initialization not completed
    543 	// this is needed as cvars are not available during initialization
    544 
    545 	joy_avail = true; 
    546 	joy_advancedinit = false;
    547 
    548 	Com_Printf ("\njoystick detected\n\n"); 
    549 }
    550 
    551 
    552 /*
    553 ===========
    554 RawValuePointer
    555 ===========
    556 */
    557 PDWORD RawValuePointer (int axis)
    558 {
    559 	switch (axis)
    560 	{
    561 	case JOY_AXIS_X:
    562 		return &ji.dwXpos;
    563 	case JOY_AXIS_Y:
    564 		return &ji.dwYpos;
    565 	case JOY_AXIS_Z:
    566 		return &ji.dwZpos;
    567 	case JOY_AXIS_R:
    568 		return &ji.dwRpos;
    569 	case JOY_AXIS_U:
    570 		return &ji.dwUpos;
    571 	case JOY_AXIS_V:
    572 		return &ji.dwVpos;
    573 	}
    574 }
    575 
    576 
    577 /*
    578 ===========
    579 Joy_AdvancedUpdate_f
    580 ===========
    581 */
    582 void Joy_AdvancedUpdate_f (void)
    583 {
    584 
    585 	// called once by IN_ReadJoystick and by user whenever an update is needed
    586 	// cvars are now available
    587 	int	i;
    588 	DWORD dwTemp;
    589 
    590 	// initialize all the maps
    591 	for (i = 0; i < JOY_MAX_AXES; i++)
    592 	{
    593 		dwAxisMap[i] = AxisNada;
    594 		dwControlMap[i] = JOY_ABSOLUTE_AXIS;
    595 		pdwRawValue[i] = RawValuePointer(i);
    596 	}
    597 
    598 	if( joy_advanced->value == 0.0)
    599 	{
    600 		// default joystick initialization
    601 		// 2 axes only with joystick control
    602 		dwAxisMap[JOY_AXIS_X] = AxisTurn;
    603 		// dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS;
    604 		dwAxisMap[JOY_AXIS_Y] = AxisForward;
    605 		// dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS;
    606 	}
    607 	else
    608 	{
    609 		if (strcmp (joy_name->string, "joystick") != 0)
    610 		{
    611 			// notify user of advanced controller
    612 			Com_Printf ("\n%s configured\n\n", joy_name->string);
    613 		}
    614 
    615 		// advanced initialization here
    616 		// data supplied by user via joy_axisn cvars
    617 		dwTemp = (DWORD) joy_advaxisx->value;
    618 		dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f;
    619 		dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS;
    620 		dwTemp = (DWORD) joy_advaxisy->value;
    621 		dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f;
    622 		dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS;
    623 		dwTemp = (DWORD) joy_advaxisz->value;
    624 		dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f;
    625 		dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS;
    626 		dwTemp = (DWORD) joy_advaxisr->value;
    627 		dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f;
    628 		dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS;
    629 		dwTemp = (DWORD) joy_advaxisu->value;
    630 		dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f;
    631 		dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS;
    632 		dwTemp = (DWORD) joy_advaxisv->value;
    633 		dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f;
    634 		dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS;
    635 	}
    636 
    637 	// compute the axes to collect from DirectInput
    638 	joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV;
    639 	for (i = 0; i < JOY_MAX_AXES; i++)
    640 	{
    641 		if (dwAxisMap[i] != AxisNada)
    642 		{
    643 			joy_flags |= dwAxisFlags[i];
    644 		}
    645 	}
    646 }
    647 
    648 
    649 /*
    650 ===========
    651 IN_Commands
    652 ===========
    653 */
    654 void IN_Commands (void)
    655 {
    656 	int		i, key_index;
    657 	DWORD	buttonstate, povstate;
    658 
    659 	if (!joy_avail)
    660 	{
    661 		return;
    662 	}
    663 
    664 	
    665 	// loop through the joystick buttons
    666 	// key a joystick event or auxillary event for higher number buttons for each state change
    667 	buttonstate = ji.dwButtons;
    668 	for (i=0 ; i < joy_numbuttons ; i++)
    669 	{
    670 		if ( (buttonstate & (1<<i)) && !(joy_oldbuttonstate & (1<<i)) )
    671 		{
    672 			key_index = (i < 4) ? K_JOY1 : K_AUX1;
    673 			Key_Event (key_index + i, true, 0);
    674 		}
    675 
    676 		if ( !(buttonstate & (1<<i)) && (joy_oldbuttonstate & (1<<i)) )
    677 		{
    678 			key_index = (i < 4) ? K_JOY1 : K_AUX1;
    679 			Key_Event (key_index + i, false, 0);
    680 		}
    681 	}
    682 	joy_oldbuttonstate = buttonstate;
    683 
    684 	if (joy_haspov)
    685 	{
    686 		// convert POV information into 4 bits of state information
    687 		// this avoids any potential problems related to moving from one
    688 		// direction to another without going through the center position
    689 		povstate = 0;
    690 		if(ji.dwPOV != JOY_POVCENTERED)
    691 		{
    692 			if (ji.dwPOV == JOY_POVFORWARD)
    693 				povstate |= 0x01;
    694 			if (ji.dwPOV == JOY_POVRIGHT)
    695 				povstate |= 0x02;
    696 			if (ji.dwPOV == JOY_POVBACKWARD)
    697 				povstate |= 0x04;
    698 			if (ji.dwPOV == JOY_POVLEFT)
    699 				povstate |= 0x08;
    700 		}
    701 		// determine which bits have changed and key an auxillary event for each change
    702 		for (i=0 ; i < 4 ; i++)
    703 		{
    704 			if ( (povstate & (1<<i)) && !(joy_oldpovstate & (1<<i)) )
    705 			{
    706 				Key_Event (K_AUX29 + i, true, 0);
    707 			}
    708 
    709 			if ( !(povstate & (1<<i)) && (joy_oldpovstate & (1<<i)) )
    710 			{
    711 				Key_Event (K_AUX29 + i, false, 0);
    712 			}
    713 		}
    714 		joy_oldpovstate = povstate;
    715 	}
    716 }
    717 
    718 
    719 /* 
    720 =============== 
    721 IN_ReadJoystick
    722 =============== 
    723 */  
    724 qboolean IN_ReadJoystick (void)
    725 {
    726 
    727 	memset (&ji, 0, sizeof(ji));
    728 	ji.dwSize = sizeof(ji);
    729 	ji.dwFlags = joy_flags;
    730 
    731 	if (joyGetPosEx (joy_id, &ji) == JOYERR_NOERROR)
    732 	{
    733 		return true;
    734 	}
    735 	else
    736 	{
    737 		// read error occurred
    738 		// turning off the joystick seems too harsh for 1 read error,\
    739 		// but what should be done?
    740 		// Com_Printf ("IN_ReadJoystick: no response\n");
    741 		// joy_avail = false;
    742 		return false;
    743 	}
    744 }
    745 
    746 
    747 /*
    748 ===========
    749 IN_JoyMove
    750 ===========
    751 */
    752 void IN_JoyMove (usercmd_t *cmd)
    753 {
    754 	float	speed, aspeed;
    755 	float	fAxisValue;
    756 	int		i;
    757 
    758 	// complete initialization if first time in
    759 	// this is needed as cvars are not available at initialization time
    760 	if( joy_advancedinit != true )
    761 	{
    762 		Joy_AdvancedUpdate_f();
    763 		joy_advancedinit = true;
    764 	}
    765 
    766 	// verify joystick is available and that the user wants to use it
    767 	if (!joy_avail || !in_joystick->value)
    768 	{
    769 		return; 
    770 	}
    771  
    772 	// collect the joystick data, if possible
    773 	if (IN_ReadJoystick () != true)
    774 	{
    775 		return;
    776 	}
    777 
    778 	if ( (in_speed.state & 1) ^ (int)cl_run->value)
    779 		speed = 2;
    780 	else
    781 		speed = 1;
    782 	aspeed = speed * cls.frametime;
    783 
    784 	// loop through the axes
    785 	for (i = 0; i < JOY_MAX_AXES; i++)
    786 	{
    787 		// get the floating point zero-centered, potentially-inverted data for the current axis
    788 		fAxisValue = (float) *pdwRawValue[i];
    789 		// move centerpoint to zero
    790 		fAxisValue -= 32768.0;
    791 
    792 		// convert range from -32768..32767 to -1..1 
    793 		fAxisValue /= 32768.0;
    794 
    795 		switch (dwAxisMap[i])
    796 		{
    797 		case AxisForward:
    798 			if ((joy_advanced->value == 0.0) && mlooking)
    799 			{
    800 				// user wants forward control to become look control
    801 				if (fabs(fAxisValue) > joy_pitchthreshold->value)
    802 				{		
    803 					// if mouse invert is on, invert the joystick pitch value
    804 					// only absolute control support here (joy_advanced is false)
    805 					if (m_pitch->value < 0.0)
    806 					{
    807 						cl.viewangles[PITCH] -= (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value;
    808 					}
    809 					else
    810 					{
    811 						cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value;
    812 					}
    813 				}
    814 			}
    815 			else
    816 			{
    817 				// user wants forward control to be forward control
    818 				if (fabs(fAxisValue) > joy_forwardthreshold->value)
    819 				{
    820 					cmd->forwardmove += (fAxisValue * joy_forwardsensitivity->value) * speed * cl_forwardspeed->value;
    821 				}
    822 			}
    823 			break;
    824 
    825 		case AxisSide:
    826 			if (fabs(fAxisValue) > joy_sidethreshold->value)
    827 			{
    828 				cmd->sidemove += (fAxisValue * joy_sidesensitivity->value) * speed * cl_sidespeed->value;
    829 			}
    830 			break;
    831 
    832 		case AxisUp:
    833 			if (fabs(fAxisValue) > joy_upthreshold->value)
    834 			{
    835 				cmd->upmove += (fAxisValue * joy_upsensitivity->value) * speed * cl_upspeed->value;
    836 			}
    837 			break;
    838 
    839 		case AxisTurn:
    840 			if ((in_strafe.state & 1) || (lookstrafe->value && mlooking))
    841 			{
    842 				// user wants turn control to become side control
    843 				if (fabs(fAxisValue) > joy_sidethreshold->value)
    844 				{
    845 					cmd->sidemove -= (fAxisValue * joy_sidesensitivity->value) * speed * cl_sidespeed->value;
    846 				}
    847 			}
    848 			else
    849 			{
    850 				// user wants turn control to be turn control
    851 				if (fabs(fAxisValue) > joy_yawthreshold->value)
    852 				{
    853 					if(dwControlMap[i] == JOY_ABSOLUTE_AXIS)
    854 					{
    855 						cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity->value) * aspeed * cl_yawspeed->value;
    856 					}
    857 					else
    858 					{
    859 						cl.viewangles[YAW] += (fAxisValue * joy_yawsensitivity->value) * speed * 180.0;
    860 					}
    861 
    862 				}
    863 			}
    864 			break;
    865 
    866 		case AxisLook:
    867 			if (mlooking)
    868 			{
    869 				if (fabs(fAxisValue) > joy_pitchthreshold->value)
    870 				{
    871 					// pitch movement detected and pitch movement desired by user
    872 					if(dwControlMap[i] == JOY_ABSOLUTE_AXIS)
    873 					{
    874 						cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * aspeed * cl_pitchspeed->value;
    875 					}
    876 					else
    877 					{
    878 						cl.viewangles[PITCH] += (fAxisValue * joy_pitchsensitivity->value) * speed * 180.0;
    879 					}
    880 				}
    881 			}
    882 			break;
    883 
    884 		default:
    885 			break;
    886 		}
    887 	}
    888 }
    889