1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20// in_mouse.c -- dos mouse code
21
22#include "quakedef.h"
23#include "dosisms.h"
24
25#define AUX_FLAG_FREELOOK	0x00000001
26
27typedef struct
28{
29	long    interruptVector;
30	char    deviceName[16];
31	long    numAxes;
32	long	numButtons;
33	long	flags;
34
35	vec3_t  viewangles;
36
37// intended velocities
38	float   forwardmove;
39	float   sidemove;
40	float   upmove;
41
42	long	buttons;
43} externControl_t;
44
45/*
46#define AUX_FLAG_FORCEFREELOOK	0x00000001	// r/o
47#define AUX_FLAG_EXTENDED		0x00000002	// r/o
48#define AUX_FLAG_RUN			0x00000004	// w/o
49#define AUX_FLAG_STRAFE			0x00000008	// w/o
50#define AUX_FLAG_FREELOOK		0x00000010	// w/o
51
52#define AUX_MAP_UNDEFINED	0
53#define AUX_MAP_PITCH		1
54#define AUX_MAP_YAW			2
55#define AUX_MAP_ROLL		3
56#define AUX_MAP_FORWARD		4
57#define AUX_MAP_SIDE		5
58#define AUX_MAP_UP			6
59
60typedef struct
61{
62	long    interruptVector;
63	// r/o
64	char    deviceName[16];
65	// r/o
66	long    numAxes;
67			// r/o	1-6
68	long	numButtons;			// r/o	0-32
69	long	flags;				// see above
70	byte	axisMapping[6];		// w/o	default = p,y,r,f,s,u
71	float	axisValue[6];		// r/w
72	float	sensitivity[6];		// w/o	default = 1.0
73	long	buttons;			// r/o
74	float	last_frame_time;	// w/o
75} externControl_t;
76*/
77
78cvar_t	m_filter = {"m_filter","1"};
79
80qboolean	mouse_avail;
81int		mouse_buttons;
82int		mouse_oldbuttonstate;
83int		mouse_buttonstate;
84float	mouse_x, mouse_y;
85float	old_mouse_x, old_mouse_y;
86
87
88cvar_t	in_joystick = {"joystick","1"};
89cvar_t	joy_numbuttons = {"joybuttons","4", true};
90
91qboolean	joy_avail;
92int		joy_oldbuttonstate;
93int		joy_buttonstate;
94
95int     joyxl, joyxh, joyyl, joyyh;
96int		joystickx, joysticky;
97
98qboolean		need_center;
99
100qboolean		extern_avail;
101int				extern_buttons;
102int				extern_oldbuttonstate;
103int				extern_buttonstate;
104cvar_t	aux_look = {"auxlook","1", true};
105externControl_t	*extern_control;
106void IN_StartupExternal (void);
107void IN_ExternalMove (usercmd_t *cmd);
108
109void IN_StartupJoystick (void);
110qboolean IN_ReadJoystick (void);
111
112
113void Toggle_AuxLook_f (void)
114{
115	if (aux_look.value)
116		Cvar_Set ("auxlook","0");
117	else
118		Cvar_Set ("auxlook","1");
119}
120
121
122void Force_CenterView_f (void)
123{
124	cl.viewangles[PITCH] = 0;
125}
126
127
128/*
129===========
130IN_StartupMouse
131===========
132*/
133void IN_StartupMouse (void)
134{
135	if ( COM_CheckParm ("-nomouse") )
136		return;
137
138// check for mouse
139	regs.x.ax = 0;
140	dos_int86(0x33);
141	mouse_avail = regs.x.ax;
142	if (!mouse_avail)
143	{
144		Con_Printf ("No mouse found\n");
145		return;
146	}
147
148	mouse_buttons = regs.x.bx;
149	if (mouse_buttons > 3)
150		mouse_buttons = 3;
151	Con_Printf("%d-button mouse available\n", mouse_buttons);
152}
153
154/*
155===========
156IN_Init
157===========
158*/
159void IN_Init (void)
160{
161	int i;
162
163	Cvar_RegisterVariable (&m_filter);
164	Cvar_RegisterVariable (&in_joystick);
165	Cvar_RegisterVariable (&joy_numbuttons);
166	Cvar_RegisterVariable (&aux_look);
167	Cmd_AddCommand ("toggle_auxlook", Toggle_AuxLook_f);
168	Cmd_AddCommand ("force_centerview", Force_CenterView_f);
169
170	IN_StartupMouse ();
171	IN_StartupJoystick ();
172
173	i = COM_CheckParm ("-control");
174	if (i)
175	{
176		extern_control = real2ptr(Q_atoi (com_argv[i+1]));
177		IN_StartupExternal ();
178	}
179}
180
181/*
182===========
183IN_Shutdown
184===========
185*/
186void IN_Shutdown (void)
187{
188
189}
190
191
192/*
193===========
194IN_Commands
195===========
196*/
197void IN_Commands (void)
198{
199	int		i;
200
201	if (mouse_avail)
202	{
203		regs.x.ax = 3;		// read buttons
204		dos_int86(0x33);
205		mouse_buttonstate = regs.x.bx;
206
207	// perform button actions
208		for (i=0 ; i<mouse_buttons ; i++)
209		{
210			if ( (mouse_buttonstate & (1<<i)) &&
211			!(mouse_oldbuttonstate & (1<<i)) )
212			{
213				Key_Event (K_MOUSE1 + i, true);
214			}
215			if ( !(mouse_buttonstate & (1<<i)) &&
216			(mouse_oldbuttonstate & (1<<i)) )
217			{
218				Key_Event (K_MOUSE1 + i, false);
219			}
220		}
221
222		mouse_oldbuttonstate = mouse_buttonstate;
223	}
224
225	if (joy_avail)
226	{
227		joy_buttonstate = ((dos_inportb(0x201) >> 4)&15)^15;
228	// perform button actions
229		for (i=0 ; i<joy_numbuttons.value ; i++)
230		{
231			if ( (joy_buttonstate & (1<<i)) &&
232			!(joy_oldbuttonstate & (1<<i)) )
233			{
234				Key_Event (K_JOY1 + i, true);
235			}
236			if ( !(joy_buttonstate & (1<<i)) &&
237			(joy_oldbuttonstate & (1<<i)) )
238			{
239				Key_Event (K_JOY1 + i, false);
240			}
241		}
242
243		joy_oldbuttonstate = joy_buttonstate;
244	}
245
246	if (extern_avail)
247	{
248		extern_buttonstate = extern_control->buttons;
249
250	// perform button actions
251		for (i=0 ; i<extern_buttons ; i++)
252		{
253			if ( (extern_buttonstate & (1<<i)) &&
254			!(extern_oldbuttonstate & (1<<i)) )
255			{
256				Key_Event (K_AUX1 + i, true);
257			}
258			if ( !(extern_buttonstate & (1<<i)) &&
259			(extern_oldbuttonstate & (1<<i)) )
260			{
261				Key_Event (K_AUX1 + i, false);
262			}
263		}
264
265		extern_oldbuttonstate = extern_buttonstate;
266	}
267
268}
269
270
271/*
272===========
273IN_Move
274===========
275*/
276void IN_MouseMove (usercmd_t *cmd)
277{
278	int		mx, my;
279
280	if (!mouse_avail)
281		return;
282
283	regs.x.ax = 11;		// read move
284	dos_int86(0x33);
285	mx = (short)regs.x.cx;
286	my = (short)regs.x.dx;
287
288	if (m_filter.value)
289	{
290		mouse_x = (mx + old_mouse_x) * 0.5;
291		mouse_y = (my + old_mouse_y) * 0.5;
292	}
293	else
294	{
295		mouse_x = mx;
296		mouse_y = my;
297	}
298	old_mouse_x = mx;
299	old_mouse_y = my;
300
301	mouse_x *= sensitivity.value;
302	mouse_y *= sensitivity.value;
303
304// add mouse X/Y movement to cmd
305	if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
306		cmd->sidemove += m_side.value * mouse_x;
307	else
308		cl.viewangles[YAW] -= m_yaw.value * mouse_x;
309
310	if (in_mlook.state & 1)
311		V_StopPitchDrift ();
312
313	if ( (in_mlook.state & 1) && !(in_strafe.state & 1))
314	{
315		cl.viewangles[PITCH] += m_pitch.value * mouse_y;
316		if (cl.viewangles[PITCH] > 80)
317			cl.viewangles[PITCH] = 80;
318		if (cl.viewangles[PITCH] < -70)
319			cl.viewangles[PITCH] = -70;
320	}
321	else
322	{
323		if ((in_strafe.state & 1) && noclip_anglehack)
324			cmd->upmove -= m_forward.value * mouse_y;
325		else
326			cmd->forwardmove -= m_forward.value * mouse_y;
327	}
328}
329
330/*
331===========
332IN_JoyMove
333===========
334*/
335void IN_JoyMove (usercmd_t *cmd)
336{
337	float	speed, aspeed;
338
339	if (!joy_avail || !in_joystick.value)
340		return;
341
342	IN_ReadJoystick ();
343	if (joysticky > joyyh*2 || joystickx > joyxh*2)
344		return;		// assume something jumped in and messed up the joystick
345					// reading time (win 95)
346
347	if (in_speed.state & 1)
348		speed = cl_movespeedkey.value;
349	else
350		speed = 1;
351	aspeed = speed*host_frametime;
352
353	if (in_strafe.state & 1)
354	{
355		if (joystickx < joyxl)
356			cmd->sidemove -= speed*cl_sidespeed.value;
357		else if (joystickx > joyxh)
358			cmd->sidemove += speed*cl_sidespeed.value;
359	}
360	else
361	{
362		if (joystickx < joyxl)
363			cl.viewangles[YAW] += aspeed*cl_yawspeed.value;
364		else if (joystickx > joyxh)
365			cl.viewangles[YAW] -= aspeed*cl_yawspeed.value;
366		cl.viewangles[YAW] = anglemod(cl.viewangles[YAW]);
367	}
368
369	if (in_mlook.state & 1)
370	{
371		if (m_pitch.value < 0)
372			speed *= -1;
373
374		if (joysticky < joyyl)
375			cl.viewangles[PITCH] += aspeed*cl_pitchspeed.value;
376		else if (joysticky > joyyh)
377			cl.viewangles[PITCH] -= aspeed*cl_pitchspeed.value;
378	}
379	else
380	{
381		if (joysticky < joyyl)
382			cmd->forwardmove += speed*cl_forwardspeed.value;
383		else if (joysticky > joyyh)
384			cmd->forwardmove -= speed*cl_backspeed.value;
385	}
386}
387
388/*
389===========
390IN_Move
391===========
392*/
393void IN_Move (usercmd_t *cmd)
394{
395	IN_MouseMove (cmd);
396	IN_JoyMove (cmd);
397	IN_ExternalMove (cmd);
398}
399
400/*
401============================================================================
402
403					JOYSTICK
404
405============================================================================
406*/
407
408
409
410qboolean IN_ReadJoystick (void)
411{
412	int		b;
413	int		count;
414
415	joystickx = 0;
416	joysticky = 0;
417
418	count = 0;
419
420	b = dos_inportb(0x201);
421	dos_outportb(0x201, b);
422
423// clear counters
424	while (++count < 10000)
425	{
426		b = dos_inportb(0x201);
427
428		joystickx += b&1;
429		joysticky += (b&2)>>1;
430		if ( !(b&3) )
431			return true;
432	}
433
434	Con_Printf ("IN_ReadJoystick: no response\n");
435	joy_avail = false;
436	return false;
437}
438
439/*
440=============
441WaitJoyButton
442=============
443*/
444qboolean WaitJoyButton (void)
445{
446	int             oldbuttons, buttons;
447
448	oldbuttons = 0;
449	do
450	{
451		key_count = -1;
452		Sys_SendKeyEvents ();
453		key_count = 0;
454		if (key_lastpress == K_ESCAPE)
455		{
456			Con_Printf ("aborted.\n");
457			return false;
458		}
459		key_lastpress = 0;
460		SCR_UpdateScreen ();
461		buttons =  ((dos_inportb(0x201) >> 4)&1)^1;
462		if (buttons != oldbuttons)
463		{
464			oldbuttons = buttons;
465			continue;
466		}
467	} while ( !buttons);
468
469	do
470	{
471		key_count = -1;
472		Sys_SendKeyEvents ();
473		key_count = 0;
474		if (key_lastpress == K_ESCAPE)
475		{
476			Con_Printf ("aborted.\n");
477			return false;
478		}
479		key_lastpress = 0;
480		SCR_UpdateScreen ();
481		buttons =  ((dos_inportb(0x201) >> 4)&1)^1;
482		if (buttons != oldbuttons)
483		{
484			oldbuttons = buttons;
485			continue;
486		}
487	} while ( buttons);
488
489	return true;
490}
491
492
493
494/*
495===============
496IN_StartupJoystick
497===============
498*/
499void IN_StartupJoystick (void)
500{
501	int     centerx, centery;
502
503 	Con_Printf ("\n");
504
505	joy_avail = false;
506	if ( COM_CheckParm ("-nojoy") )
507		return;
508
509	if (!IN_ReadJoystick ())
510	{
511		joy_avail = false;
512		Con_Printf ("joystick not found\n");
513		return;
514	}
515
516	Con_Printf ("joystick found\n");
517
518	Con_Printf ("CENTER the joystick\nand press button 1 (ESC to skip):\n");
519	if (!WaitJoyButton ())
520		return;
521	IN_ReadJoystick ();
522	centerx = joystickx;
523	centery = joysticky;
524
525	Con_Printf ("Push the joystick to the UPPER LEFT\nand press button 1 (ESC to skip):\n");
526	if (!WaitJoyButton ())
527		return;
528	IN_ReadJoystick ();
529	joyxl = (centerx + joystickx)/2;
530	joyyl = (centerx + joysticky)/2;
531
532	Con_Printf ("Push the joystick to the LOWER RIGHT\nand press button 1 (ESC to skip):\n");
533	if (!WaitJoyButton ())
534		return;
535	IN_ReadJoystick ();
536	joyxh = (centerx + joystickx)/2;
537	joyyh = (centery + joysticky)/2;
538
539	joy_avail = true;
540	Con_Printf ("joystick configured.\n");
541
542 	Con_Printf ("\n");
543}
544
545
546/*
547============================================================================
548
549					EXTERNAL
550
551============================================================================
552*/
553
554
555/*
556===============
557IN_StartupExternal
558===============
559*/
560void IN_StartupExternal (void)
561{
562	if (extern_control->numButtons > 32)
563		extern_control->numButtons = 32;
564
565	Con_Printf("%s Initialized\n", extern_control->deviceName);
566	Con_Printf("  %u axes  %u buttons\n", extern_control->numAxes, extern_control->numButtons);
567
568	extern_avail = true;
569	extern_buttons = extern_control->numButtons;
570}
571
572
573/*
574===========
575IN_ExternalMove
576===========
577*/
578void IN_ExternalMove (usercmd_t *cmd)
579{
580	qboolean freelook;
581
582	if (! extern_avail)
583		return;
584
585	extern_control->viewangles[YAW] = cl.viewangles[YAW];
586	extern_control->viewangles[PITCH] = cl.viewangles[PITCH];
587	extern_control->viewangles[ROLL] = cl.viewangles[ROLL];
588	extern_control->forwardmove = cmd->forwardmove;
589	extern_control->sidemove = cmd->sidemove;
590	extern_control->upmove = cmd->upmove;
591
592Con_DPrintf("IN:  y:%f p:%f r:%f f:%f s:%f u:%f\n", extern_control->viewangles[YAW], extern_control->viewangles[PITCH], extern_control->viewangles[ROLL], extern_control->forwardmove, extern_control->sidemove, extern_control->upmove);
593
594	dos_int86(extern_control->interruptVector);
595
596Con_DPrintf("OUT: y:%f p:%f r:%f f:%f s:%f u:%f\n", extern_control->viewangles[YAW], extern_control->viewangles[PITCH], extern_control->viewangles[ROLL], extern_control->forwardmove, extern_control->sidemove, extern_control->upmove);
597
598	cl.viewangles[YAW] = extern_control->viewangles[YAW];
599	cl.viewangles[PITCH] = extern_control->viewangles[PITCH];
600	cl.viewangles[ROLL] = extern_control->viewangles[ROLL];
601	cmd->forwardmove = extern_control->forwardmove;
602	cmd->sidemove = extern_control->sidemove;
603	cmd->upmove = extern_control->upmove;
604
605	if (cl.viewangles[PITCH] > 80)
606		cl.viewangles[PITCH] = 80;
607	if (cl.viewangles[PITCH] < -70)
608		cl.viewangles[PITCH] = -70;
609
610	freelook = (extern_control->flags & AUX_FLAG_FREELOOK || aux_look.value || in_mlook.state & 1);
611
612	if (freelook)
613		V_StopPitchDrift ();
614}
615
616