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