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 // cl.input.c  -- builds an intended movement command to send to the server
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "client.h"
27 
28 cvar_t	*cl_nodelta;
29 
30 extern	unsigned	sys_frame_time;
31 unsigned	frame_msec;
32 unsigned	old_sys_frame_time;
33 
34 /*
35 ===============================================================================
36 
37 KEY BUTTONS
38 
39 Continuous button event tracking is complicated by the fact that two different
40 input sources (say, mouse button 1 and the control key) can both press the
41 same button, but the button should only be released when both of the
42 pressing key have been released.
43 
44 When a key event issues a button command (+forward, +attack, etc), it appends
45 its key number as a parameter to the command so it can be matched up with
46 the release.
47 
48 state bit 0 is the current state of the key
49 state bit 1 is edge triggered on the up to down transition
50 state bit 2 is edge triggered on the down to up transition
51 
52 
53 Key_Event (int key, qboolean down, unsigned time);
54 
55   +mlook src time
56 
57 ===============================================================================
58 */
59 
60 
61 kbutton_t	in_klook;
62 kbutton_t	in_left, in_right, in_forward, in_back;
63 kbutton_t	in_lookup, in_lookdown, in_moveleft, in_moveright;
64 kbutton_t	in_strafe, in_speed, in_use, in_attack, in_attack2;
65 kbutton_t	in_up, in_down;
66 
67 int			in_impulse;
68 
69 
KeyDown(kbutton_t * b)70 void KeyDown (kbutton_t *b)
71 {
72 	int		k;
73 	char	*c;
74 
75 	c = Cmd_Argv(1);
76 	if (c[0])
77 		k = atoi(c);
78 	else
79 		k = -1;		// typed manually at the console for continuous down
80 
81 	if (k == b->down[0] || k == b->down[1])
82 		return;		// repeating key
83 
84 	if (!b->down[0])
85 		b->down[0] = k;
86 	else if (!b->down[1])
87 		b->down[1] = k;
88 	else
89 	{
90 		Com_Printf ("Three keys down for a button!\n");
91 		return;
92 	}
93 
94 	if (b->state & 1)
95 		return;		// still down
96 
97 	// save timestamp
98 	c = Cmd_Argv(2);
99 	b->downtime = atoi(c);
100 	if (!b->downtime)
101 		b->downtime = sys_frame_time - 100;
102 
103 	b->state |= 1 + 2;	// down + impulse down
104 }
105 
KeyUp(kbutton_t * b)106 void KeyUp (kbutton_t *b)
107 {
108 	int		k;
109 	char	*c;
110 	unsigned	uptime;
111 
112 	c = Cmd_Argv(1);
113 	if (c[0])
114 		k = atoi(c);
115 	else
116 	{ // typed manually at the console, assume for unsticking, so clear all
117 		b->down[0] = b->down[1] = 0;
118 		b->state = 4;	// impulse up
119 		return;
120 	}
121 
122 	if (b->down[0] == k)
123 		b->down[0] = 0;
124 	else if (b->down[1] == k)
125 		b->down[1] = 0;
126 	else
127 		return;		// key up without coresponding down (menu pass through)
128 	if (b->down[0] || b->down[1])
129 		return;		// some other key is still holding it down
130 
131 	if (!(b->state & 1))
132 		return;		// still up (this should not happen)
133 
134 	// save timestamp
135 	c = Cmd_Argv(2);
136 	uptime = atoi(c);
137 	if (uptime)
138 		b->msec += uptime - b->downtime;
139 	else
140 		b->msec += 10;
141 
142 	b->state &= ~1;		// now up
143 	b->state |= 4; 		// impulse up
144 }
145 
IN_KLookDown(void)146 void IN_KLookDown (void) {KeyDown(&in_klook);}
IN_KLookUp(void)147 void IN_KLookUp (void) {KeyUp(&in_klook);}
IN_UpDown(void)148 void IN_UpDown(void) {KeyDown(&in_up);}
IN_UpUp(void)149 void IN_UpUp(void) {KeyUp(&in_up);}
IN_DownDown(void)150 void IN_DownDown(void) {KeyDown(&in_down);}
IN_DownUp(void)151 void IN_DownUp(void) {KeyUp(&in_down);}
IN_LeftDown(void)152 void IN_LeftDown(void) {KeyDown(&in_left);}
IN_LeftUp(void)153 void IN_LeftUp(void) {KeyUp(&in_left);}
IN_RightDown(void)154 void IN_RightDown(void) {KeyDown(&in_right);}
IN_RightUp(void)155 void IN_RightUp(void) {KeyUp(&in_right);}
IN_ForwardDown(void)156 void IN_ForwardDown(void) {KeyDown(&in_forward);}
IN_ForwardUp(void)157 void IN_ForwardUp(void) {KeyUp(&in_forward);}
IN_BackDown(void)158 void IN_BackDown(void) {KeyDown(&in_back);}
IN_BackUp(void)159 void IN_BackUp(void) {KeyUp(&in_back);}
IN_LookupDown(void)160 void IN_LookupDown(void) {KeyDown(&in_lookup);}
IN_LookupUp(void)161 void IN_LookupUp(void) {KeyUp(&in_lookup);}
IN_LookdownDown(void)162 void IN_LookdownDown(void) {KeyDown(&in_lookdown);}
IN_LookdownUp(void)163 void IN_LookdownUp(void) {KeyUp(&in_lookdown);}
IN_MoveleftDown(void)164 void IN_MoveleftDown(void) {KeyDown(&in_moveleft);}
IN_MoveleftUp(void)165 void IN_MoveleftUp(void) {KeyUp(&in_moveleft);}
IN_MoverightDown(void)166 void IN_MoverightDown(void) {KeyDown(&in_moveright);}
IN_MoverightUp(void)167 void IN_MoverightUp(void) {KeyUp(&in_moveright);}
168 
IN_SpeedDown(void)169 void IN_SpeedDown(void) {KeyDown(&in_speed);}
IN_SpeedUp(void)170 void IN_SpeedUp(void) {KeyUp(&in_speed);}
IN_StrafeDown(void)171 void IN_StrafeDown(void) {KeyDown(&in_strafe);}
IN_StrafeUp(void)172 void IN_StrafeUp(void) {KeyUp(&in_strafe);}
173 
IN_AttackDown(void)174 void IN_AttackDown(void) {KeyDown(&in_attack);}
IN_AttackUp(void)175 void IN_AttackUp(void) {KeyUp(&in_attack);}
176 
177 //alt fire
IN_Attack2Down(void)178 void IN_Attack2Down(void) {KeyDown(&in_attack2);}
IN_Attack2Up(void)179 void IN_Attack2Up(void) {KeyUp(&in_attack2);}
180 
IN_UseDown(void)181 void IN_UseDown (void) {KeyDown(&in_use);}
IN_UseUp(void)182 void IN_UseUp (void) {KeyUp(&in_use);}
183 
IN_Impulse(void)184 void IN_Impulse (void) {in_impulse=atoi(Cmd_Argv(1));}
185 
186 /*
187 ===============
188 CL_KeyState
189 
190 Returns the fraction of the frame that the key was down
191 ===============
192 */
CL_KeyState(kbutton_t * key)193 float CL_KeyState (kbutton_t *key)
194 {
195 	float		val;
196 	int			msec;
197 
198 	key->state &= 1;		// clear impulses
199 
200 	msec = key->msec;
201 	key->msec = 0;
202 
203 	if (key->state)
204 	{	// still down
205 		msec += sys_frame_time - key->downtime;
206 		key->downtime = sys_frame_time;
207 	}
208 
209 #if 0
210 	if (msec)
211 	{
212 		Com_Printf ("%i ", msec);
213 	}
214 #endif
215 
216 	val = (float)msec / frame_msec;
217 	if (val < 0)
218 		val = 0;
219 	if (val > 1)
220 		val = 1;
221 
222 	return val;
223 }
224 
225 
226 
227 
228 //==========================================================================
229 
230 cvar_t	*cl_upspeed;
231 cvar_t	*cl_forwardspeed;
232 cvar_t	*cl_sidespeed;
233 
234 cvar_t	*cl_yawspeed;
235 cvar_t	*cl_pitchspeed;
236 
237 cvar_t	*cl_run;
238 
239 cvar_t	*cl_anglespeedkey;
240 
241 
242 /*
243 ================
244 CL_AdjustAngles
245 
246 Moves the local angle positions
247 ================
248 */
CL_AdjustAngles(void)249 void CL_AdjustAngles (void)
250 {
251 	float	speed;
252 	float	up, down;
253 
254 	if (in_speed.state & 1)
255 		speed = cls.frametime * cl_anglespeedkey->value;
256 	else
257 		speed = cls.frametime;
258 
259 	if (!(in_strafe.state & 1))
260 	{
261 		cl.viewangles[YAW] -= speed*cl_yawspeed->value*CL_KeyState (&in_right);
262 		cl.viewangles[YAW] += speed*cl_yawspeed->value*CL_KeyState (&in_left);
263 	}
264 	if (in_klook.state & 1)
265 	{
266 		cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * CL_KeyState (&in_forward);
267 		cl.viewangles[PITCH] += speed*cl_pitchspeed->value * CL_KeyState (&in_back);
268 	}
269 
270 	up = CL_KeyState (&in_lookup);
271 	down = CL_KeyState(&in_lookdown);
272 
273 	cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * up;
274 	cl.viewangles[PITCH] += speed*cl_pitchspeed->value * down;
275 }
276 
277 /*
278 ================
279 CL_BaseMove
280 
281 Send the intended movement message to the server
282 ================
283 */
CL_BaseMove(usercmd_t * cmd)284 void CL_BaseMove (usercmd_t *cmd)
285 {
286 	CL_AdjustAngles ();
287 
288 	memset (cmd, 0, sizeof(*cmd));
289 
290 	VectorCopy (cl.viewangles, cmd->angles);
291 	if (in_strafe.state & 1)
292 	{
293 		cmd->sidemove += cl_sidespeed->value * CL_KeyState (&in_right);
294 		cmd->sidemove -= cl_sidespeed->value * CL_KeyState (&in_left);
295 	}
296 
297 	cmd->sidemove += cl_sidespeed->value * CL_KeyState (&in_moveright);
298 	cmd->sidemove -= cl_sidespeed->value * CL_KeyState (&in_moveleft);
299 
300 	cmd->upmove += cl_upspeed->value * CL_KeyState (&in_up);
301 	cmd->upmove -= cl_upspeed->value * CL_KeyState (&in_down);
302 
303 	if (! (in_klook.state & 1) )
304 	{
305 		cmd->forwardmove += cl_forwardspeed->value * CL_KeyState (&in_forward);
306 		cmd->forwardmove -= cl_forwardspeed->value * CL_KeyState (&in_back);
307 	}
308 
309 //
310 // adjust for speed key / running
311 //
312 	if (!cl.tactical && ( (in_speed.state & 1) ^ cl_run->integer ))
313 	{
314 		cmd->forwardmove *= 2;
315 		cmd->sidemove *= 2;
316 		cmd->upmove *= 2;
317 	}
318 }
319 
CL_ClampPitch(void)320 void CL_ClampPitch (void)
321 {
322 	float	pitch;
323 
324 	pitch = SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
325 	if (pitch > 180)
326 		pitch -= 360;
327 
328 	if (cl.viewangles[PITCH] + pitch < -360)
329 		cl.viewangles[PITCH] += 360; // wrapped
330 	if (cl.viewangles[PITCH] + pitch > 360)
331 		cl.viewangles[PITCH] -= 360; // wrapped
332 
333 	if (cl.viewangles[PITCH] + pitch > 89)
334 		cl.viewangles[PITCH] = 89 - pitch;
335 	if (cl.viewangles[PITCH] + pitch < -89)
336 		cl.viewangles[PITCH] = -89 - pitch;
337 }
338 
339 /*
340 ==============
341 CL_FinishMove
342 ==============
343 */
CL_FinishMove(usercmd_t * cmd)344 void CL_FinishMove (usercmd_t *cmd)
345 {
346 	int		ms;
347 	int		i;
348 
349 //
350 // figure button bits
351 //
352 	if ( in_attack.state & 3 )
353 		cmd->buttons |= BUTTON_ATTACK;
354 	in_attack.state &= ~2;
355 
356 	//alt fire
357 	if ( in_attack2.state & 3 )
358 		cmd->buttons |= BUTTON_ATTACK2;
359 	in_attack2.state &= ~2;
360 
361 	if (in_use.state & 3)
362 		cmd->buttons |= BUTTON_USE;
363 	in_use.state &= ~2;
364 
365 	if (anykeydown && cls.key_dest == key_game)
366 		cmd->buttons |= BUTTON_ANY;
367 
368 	// send milliseconds of time to apply the move
369 	ms = cls.frametime * 1000;
370 	if (ms > 250)
371 		ms = 100;		// time was unreasonable
372 	cmd->msec = ms;
373 
374 	CL_ClampPitch ();
375 	for (i=0 ; i<3 ; i++)
376 		cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]);
377 
378 	cmd->impulse = in_impulse;
379 	in_impulse = 0;
380 }
381 
382 /*
383 =================
384 CL_CreateCmd
385 =================
386 */
CL_CreateCmd(void)387 usercmd_t CL_CreateCmd (void)
388 {
389 	usercmd_t	cmd;
390 
391 	frame_msec = sys_frame_time - old_sys_frame_time;
392 	if (frame_msec < 1)
393 		frame_msec = 1;
394 	if (frame_msec > 200)
395 		frame_msec = 200;
396 
397 	// get basic movement from keyboard
398 	CL_BaseMove (&cmd);
399 
400 	// allow mice or other external controllers to add to the move
401 	IN_Move (&cmd);
402 	IN_JoyMove (&cmd);
403 
404 	CL_FinishMove (&cmd);
405 
406 	old_sys_frame_time = sys_frame_time;
407 
408 //cmd.impulse = cls.framecount;
409 
410 	return cmd;
411 }
412 
413 
IN_CenterView(void)414 void IN_CenterView (void)
415 {
416 	cl.viewangles[PITCH] = -SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
417 }
418 
419 /*
420 ============
421 CL_InitInput
422 ============
423 */
CL_InitInput(void)424 void CL_InitInput (void)
425 {
426 	Cmd_AddCommand ("centerview",IN_CenterView);
427 
428 	Cmd_AddCommand ("+moveup",IN_UpDown);
429 	Cmd_AddCommand ("-moveup",IN_UpUp);
430 	Cmd_AddCommand ("+movedown",IN_DownDown);
431 	Cmd_AddCommand ("-movedown",IN_DownUp);
432 	Cmd_AddCommand ("+left",IN_LeftDown);
433 	Cmd_AddCommand ("-left",IN_LeftUp);
434 	Cmd_AddCommand ("+right",IN_RightDown);
435 	Cmd_AddCommand ("-right",IN_RightUp);
436 	Cmd_AddCommand ("+forward",IN_ForwardDown);
437 	Cmd_AddCommand ("-forward",IN_ForwardUp);
438 	Cmd_AddCommand ("+back",IN_BackDown);
439 	Cmd_AddCommand ("-back",IN_BackUp);
440 	Cmd_AddCommand ("+lookup", IN_LookupDown);
441 	Cmd_AddCommand ("-lookup", IN_LookupUp);
442 	Cmd_AddCommand ("+lookdown", IN_LookdownDown);
443 	Cmd_AddCommand ("-lookdown", IN_LookdownUp);
444 	Cmd_AddCommand ("+strafe", IN_StrafeDown);
445 	Cmd_AddCommand ("-strafe", IN_StrafeUp);
446 	Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
447 	Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
448 	Cmd_AddCommand ("+moveright", IN_MoverightDown);
449 	Cmd_AddCommand ("-moveright", IN_MoverightUp);
450 	Cmd_AddCommand ("+speed", IN_SpeedDown);
451 	Cmd_AddCommand ("-speed", IN_SpeedUp);
452 	Cmd_AddCommand ("+attack", IN_AttackDown);
453 	Cmd_AddCommand ("-attack", IN_AttackUp);
454 	Cmd_AddCommand ("+use", IN_UseDown);
455 	Cmd_AddCommand ("-use", IN_UseUp);
456 	Cmd_AddCommand ("impulse", IN_Impulse);
457 	Cmd_AddCommand ("+klook", IN_KLookDown);
458 	Cmd_AddCommand ("-klook", IN_KLookUp);
459 	//alt fire
460 	Cmd_AddCommand ("+attack2", IN_Attack2Down);
461 	Cmd_AddCommand ("-attack2", IN_Attack2Up);
462 
463 	cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0);
464 }
465 
466 
467 
468 /*
469 =================
470 CL_SendCmd
471 =================
472 */
CL_SendCmd(void)473 void CL_SendCmd (void)
474 {
475 	sizebuf_t	buf;
476 	byte		data[128];
477 	int			i;
478 	usercmd_t	*cmd, *oldcmd;
479 	usercmd_t	nullcmd;
480 	int			checksumIndex;
481 
482 	// build a command even if not connected
483 
484 	// save this command off for prediction
485 	i = cls.netchan.outgoing_sequence & (CMD_BACKUP-1);
486 	cmd = &cl.cmds[i];
487 	cl.cmd_time[i] = cls.realtime;	// for netgraph ping calculation
488 
489 	*cmd = CL_CreateCmd ();
490 
491 	cl.cmd = *cmd;
492 
493 	if (cls.state == ca_disconnected || cls.state == ca_connecting)
494 		return;
495 
496 	if ( cls.state == ca_connected)
497 	{
498 		if (cls.netchan.message.cursize	|| curtime - cls.netchan.last_sent > 1000 )
499 			Netchan_Transmit (&cls.netchan, 0, data);
500 		return;
501 	}
502 
503 	// send a userinfo update if needed
504 	if (userinfo_modified)
505 	{
506 		CL_FixUpGender();
507 		userinfo_modified = false;
508 		MSG_WriteByte (&cls.netchan.message, clc_userinfo);
509 		MSG_WriteString (&cls.netchan.message, Cvar_Userinfo() );
510 	}
511 
512 	SZ_Init (&buf, data, sizeof(data));
513 	SZ_SetName ( &buf, "CL_SendCmd", false );
514 
515 	// begin a client move command
516 	MSG_WriteByte (&buf, clc_move);
517 
518 	// save the position for a checksum byte
519 	checksumIndex = buf.cursize;
520 	MSG_WriteByte (&buf, 0);
521 
522 	// let the server know what the last frame we
523 	// got was, so the next message can be delta compressed
524 	if (cl_nodelta->value || !cl.frame.valid || cls.demowaiting)
525 		MSG_WriteLong (&buf, -1);	// no compression
526 	else
527 		MSG_WriteLong (&buf, cl.frame.serverframe);
528 
529 	// send this and the previous cmds in the message, so
530 	// if the last packet was dropped, it can be recovered
531 	i = (cls.netchan.outgoing_sequence-2) & (CMD_BACKUP-1);
532 	cmd = &cl.cmds[i];
533 	memset (&nullcmd, 0, sizeof(nullcmd));
534 	MSG_WriteDeltaUsercmd (&buf, &nullcmd, cmd);
535 	oldcmd = cmd;
536 
537 	i = (cls.netchan.outgoing_sequence-1) & (CMD_BACKUP-1);
538 	cmd = &cl.cmds[i];
539 	MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);
540 	oldcmd = cmd;
541 
542 	i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP-1);
543 	cmd = &cl.cmds[i];
544 	MSG_WriteDeltaUsercmd (&buf, oldcmd, cmd);
545 
546 	// calculate a checksum over the move commands
547 	buf.data[checksumIndex] = COM_BlockSequenceCRCByte(
548 		buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1,
549 		cls.netchan.outgoing_sequence);
550 
551 	//
552 	// deliver the message
553 	//
554 	Netchan_Transmit (&cls.netchan, buf.cursize, buf.data);
555 }
556 
557 
558 #if defined WIN32_VARIANT
559 # define OS_MENU_MOUSESCALE 0.35
560 #else
561 # if defined UNIX_VARIANT
562 #  define OS_MENU_MOUSESCALE 0.1
563 # else
564 #  define OS_MENU_MOUSESCALE 1
565 # endif
566 #endif
567 
568 
569 qboolean mouse_available = false;
570 qboolean mouse_is_position = false;
571 qboolean mlooking = false;
572 int mouse_diff_x = 0;
573 int mouse_diff_y = 0;
574 int mouse_odiff_x = 0;
575 int mouse_odiff_y = 0;
576 cursor_t cursor;
577 
578 
IN_MLookDown(void)579 void IN_MLookDown (void)
580 {
581 	mlooking = true;
582 }
583 
IN_MLookUp(void)584 void IN_MLookUp (void)
585 {
586 	mlooking = false;
587 	if (!freelook->integer && lookspring->integer) {
588 		IN_CenterView ();
589 	}
590 }
591 
IN_MoveMenuMouse(int x,int y)592 static void IN_MoveMenuMouse( int x , int y )
593 {
594 	cursor.oldx = cursor.x;
595 	cursor.oldy = cursor.y;
596 
597 	// Clip cursor location to window
598 	if ( x < 0 ) {
599 		x = 0;
600 	} else if ( x > viddef.width ) {
601 		x = viddef.width;
602 	}
603 	if ( y < 0 ) {
604 		y = 0;
605 	} else if ( y > viddef.height ) {
606 		y = viddef.height;
607 	}
608 
609 	cursor.x = x;
610 	cursor.y = y;
611 	cursor.mouseaction = cursor.mouseaction || cursor.x != cursor.oldx
612 		|| cursor.y != cursor.oldy;
613 
614 	M_Think_MouseCursor();
615 }
616 
617 extern cvar_t *fov;
IN_Move(usercmd_t * cmd)618 void IN_Move (usercmd_t *cmd)
619 {
620 	float fmx;
621 	float fmy;
622 	float adjust;
623 
624 	if ( ! mouse_available ) {
625 		return;
626 	}
627 
628 	// If we have a position instead of a diff, don't go through
629 	// the normal process. Instead, update the menu's cursor
630 	// as necessary, and bail out.
631 	if ( mouse_is_position ) {
632 		if ( cls.key_dest == key_menu ) {
633 			IN_MoveMenuMouse( mouse_diff_x , mouse_diff_y );
634 		}
635 		return;
636 	}
637 
638 	// Apply interpolation with previous value if necessary
639 	fmx = (float) mouse_diff_x;
640 	fmy = (float) mouse_diff_y;
641 	if ( m_filter->integer ) {
642 		fmx = ( fmx + mouse_odiff_x ) * 0.5f;
643 		fmy = ( fmy + mouse_odiff_y ) * 0.5f;
644 	}
645 	if (cmd)
646 	{
647 		mouse_odiff_x = mouse_diff_x;
648 		mouse_odiff_y = mouse_diff_y;
649 		mouse_diff_x = mouse_diff_y = 0;
650 	}
651 
652 	// No mouse in console
653 	if ( cls.key_dest == key_console ) {
654 		return;
655 	}
656 
657 	// Compute adjustments
658 	adjust = 1.0;
659 	if ( m_smoothing->value ) {
660 		// reduce sensitivity when frames per sec is below maximum
661 		// setting by multiplying by:
662 		//	current measured fps / cvar set maximum fps
663 		adjust /= cls.frametime * cl_maxfps->value;
664 	}
665 
666 	// Update menu cursor location
667 	if ( cls.key_dest == key_menu ) {
668 		adjust *= menu_sensitivity->value * OS_MENU_MOUSESCALE;
669 		IN_MoveMenuMouse( cursor.x + fmx * adjust ,
670 				cursor.y + fmy * adjust );
671 		return;
672 	}
673 
674 	// Game mouse
675 	adjust *= sensitivity->value * cl.refdef.fov_x/fov->value;
676 	fmx *= adjust;
677 	fmy *= adjust;
678 
679 	// Add mouse X/Y movement to cmd
680 	if ( ( lookstrafe->integer && mlooking ) || ( in_strafe.state & 1 ) ) {
681 		if (!cmd)
682 			return;
683 		cmd->sidemove += (short)( ( m_side->value * fmx ) + 0.5f );
684 	} else {
685 		if (cmd)
686 			cl.viewangles[ YAW ] -= m_yaw->value * fmx;
687 		else
688 			cl.predicted_angles[ YAW ] = cl.last_predicted_angles[ YAW ] - m_yaw->value * fmx;
689 	}
690 
691 	if ( ( mlooking || freelook->integer ) && !( in_strafe.state & 1 ) ) {
692 		if (cmd)
693 			cl.viewangles[ PITCH ] += m_pitch->value * fmy;
694 		else
695 			cl.predicted_angles[ PITCH ] = cl.last_predicted_angles[ PITCH ] + m_pitch->value * fmy;
696 	} else {
697 		if (!cmd)
698 			return;
699 		cmd->forwardmove -= (short)( ( m_forward->value * fmy )
700 				+ 0.5f );
701 	}
702 }
703