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#include <termios.h>
21#include <sys/ioctl.h>
22#include <sys/stat.h>
23#include <sys/vt.h>
24#include <stdarg.h>
25#include <stdio.h>
26#include <signal.h>
27
28#include "quakedef.h"
29
30#include <GL/glx.h>
31
32#include <X11/keysym.h>
33#include <X11/cursorfont.h>
34
35#ifdef USE_DGA
36#include <X11/extensions/xf86dga.h>
37#endif
38
39
40#define WARP_WIDTH              320
41#define WARP_HEIGHT             200
42
43static Display *dpy = NULL;
44static Window win;
45static GLXContext ctx = NULL;
46
47static float old_windowed_mouse = 0;
48
49#define KEY_MASK (KeyPressMask | KeyReleaseMask)
50#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \
51		    PointerMotionMask)
52
53#define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask)
54
55unsigned short	d_8to16table[256];
56unsigned		d_8to24table[256];
57unsigned char	d_15to8table[65536];
58
59cvar_t	_windowed_mouse = {"_windowed_mouse","0", true};
60cvar_t	vid_mode = {"vid_mode","0",false};
61
62static float   mouse_x, mouse_y;
63static float	old_mouse_x, old_mouse_y;
64
65cvar_t	m_filter = {"m_filter", "0"};
66
67static int scr_width, scr_height;
68
69/*-----------------------------------------------------------------------*/
70
71//int		texture_mode = GL_NEAREST;
72//int		texture_mode = GL_NEAREST_MIPMAP_NEAREST;
73//int		texture_mode = GL_NEAREST_MIPMAP_LINEAR;
74int		texture_mode = GL_LINEAR;
75//int		texture_mode = GL_LINEAR_MIPMAP_NEAREST;
76//int		texture_mode = GL_LINEAR_MIPMAP_LINEAR;
77
78int		texture_extension_number = 1;
79
80float		gldepthmin, gldepthmax;
81
82cvar_t	gl_ztrick = {"gl_ztrick","1"};
83
84const char *gl_vendor;
85const char *gl_renderer;
86const char *gl_version;
87const char *gl_extensions;
88
89qboolean is8bit = false;
90qboolean isPermedia = false;
91qboolean gl_mtexable = false;
92
93/*-----------------------------------------------------------------------*/
94void D_BeginDirectRect (int x, int y, byte *pbitmap, int width, int height)
95{
96}
97
98void D_EndDirectRect (int x, int y, int width, int height)
99{
100}
101
102static int XLateKey(XKeyEvent *ev)
103{
104
105	int key;
106	char buf[64];
107	KeySym keysym;
108
109	key = 0;
110
111	XLookupString(ev, buf, sizeof buf, &keysym, 0);
112
113	switch(keysym)
114	{
115		case XK_KP_Page_Up:
116		case XK_Page_Up:	 key = K_PGUP; break;
117
118		case XK_KP_Page_Down:
119		case XK_Page_Down:	 key = K_PGDN; break;
120
121		case XK_KP_Home:
122		case XK_Home:	 key = K_HOME; break;
123
124		case XK_KP_End:
125		case XK_End:	 key = K_END; break;
126
127		case XK_KP_Left:
128		case XK_Left:	 key = K_LEFTARROW; break;
129
130		case XK_KP_Right:
131		case XK_Right:	key = K_RIGHTARROW;		break;
132
133		case XK_KP_Down:
134		case XK_Down:	 key = K_DOWNARROW; break;
135
136		case XK_KP_Up:
137		case XK_Up:		 key = K_UPARROW;	 break;
138
139		case XK_Escape: key = K_ESCAPE;		break;
140
141		case XK_KP_Enter:
142		case XK_Return: key = K_ENTER;		 break;
143
144		case XK_Tab:		key = K_TAB;			 break;
145
146		case XK_F1:		 key = K_F1;				break;
147
148		case XK_F2:		 key = K_F2;				break;
149
150		case XK_F3:		 key = K_F3;				break;
151
152		case XK_F4:		 key = K_F4;				break;
153
154		case XK_F5:		 key = K_F5;				break;
155
156		case XK_F6:		 key = K_F6;				break;
157
158		case XK_F7:		 key = K_F7;				break;
159
160		case XK_F8:		 key = K_F8;				break;
161
162		case XK_F9:		 key = K_F9;				break;
163
164		case XK_F10:		key = K_F10;			 break;
165
166		case XK_F11:		key = K_F11;			 break;
167
168		case XK_F12:		key = K_F12;			 break;
169
170		case XK_BackSpace: key = K_BACKSPACE; break;
171
172		case XK_KP_Delete:
173		case XK_Delete: key = K_DEL; break;
174
175		case XK_Pause:	key = K_PAUSE;		 break;
176
177		case XK_Shift_L:
178		case XK_Shift_R:	key = K_SHIFT;		break;
179
180		case XK_Execute:
181		case XK_Control_L:
182		case XK_Control_R:	key = K_CTRL;		 break;
183
184		case XK_Alt_L:
185		case XK_Meta_L:
186		case XK_Alt_R:
187		case XK_Meta_R: key = K_ALT;			break;
188
189		case XK_KP_Begin: key = '5';	break;
190
191		case XK_KP_Insert:
192		case XK_Insert:key = K_INS; break;
193
194		case XK_KP_Multiply: key = '*'; break;
195		case XK_KP_Add:  key = '+'; break;
196		case XK_KP_Subtract: key = '-'; break;
197		case XK_KP_Divide: key = '/'; break;
198
199#if 0
200		case 0x021: key = '1';break;/* [!] */
201		case 0x040: key = '2';break;/* [@] */
202		case 0x023: key = '3';break;/* [#] */
203		case 0x024: key = '4';break;/* [$] */
204		case 0x025: key = '5';break;/* [%] */
205		case 0x05e: key = '6';break;/* [^] */
206		case 0x026: key = '7';break;/* [&] */
207		case 0x02a: key = '8';break;/* [*] */
208		case 0x028: key = '9';;break;/* [(] */
209		case 0x029: key = '0';break;/* [)] */
210		case 0x05f: key = '-';break;/* [_] */
211		case 0x02b: key = '=';break;/* [+] */
212		case 0x07c: key = '\'';break;/* [|] */
213		case 0x07d: key = '[';break;/* [}] */
214		case 0x07b: key = ']';break;/* [{] */
215		case 0x022: key = '\'';break;/* ["] */
216		case 0x03a: key = ';';break;/* [:] */
217		case 0x03f: key = '/';break;/* [?] */
218		case 0x03e: key = '.';break;/* [>] */
219		case 0x03c: key = ',';break;/* [<] */
220#endif
221
222		default:
223			key = *(unsigned char*)buf;
224			if (key >= 'A' && key <= 'Z')
225				key = key - 'A' + 'a';
226			break;
227	}
228
229	return key;
230}
231
232static void install_grabs(void)
233{
234	XGrabPointer(dpy, win,
235				 True,
236				 0,
237				 GrabModeAsync, GrabModeAsync,
238				 win,
239				 None,
240				 CurrentTime);
241
242#ifdef USE_DGA
243	XF86DGADirectVideo(dpy, DefaultScreen(dpy), XF86DGADirectMouse);
244	dgamouse = 1;
245#else
246	XWarpPointer(dpy, None, win,
247				 0, 0, 0, 0,
248				 vid.width / 2, vid.height / 2);
249#endif
250
251	XGrabKeyboard(dpy, win,
252				  False,
253				  GrabModeAsync, GrabModeAsync,
254				  CurrentTime);
255
256//	XSync(dpy, True);
257}
258
259static void uninstall_grabs(void)
260{
261#ifdef USE_DGA
262	XF86DGADirectVideo(dpy, DefaultScreen(dpy), 0);
263	dgamouse = 0;
264#endif
265
266	XUngrabPointer(dpy, CurrentTime);
267	XUngrabKeyboard(dpy, CurrentTime);
268
269//	XSync(dpy, True);
270}
271
272static void GetEvent(void)
273{
274	XEvent event;
275	int b;
276
277	if (!dpy)
278		return;
279
280	XNextEvent(dpy, &event);
281
282	switch (event.type) {
283	case KeyPress:
284	case KeyRelease:
285		Key_Event(XLateKey(&event.xkey), event.type == KeyPress);
286		break;
287
288	case MotionNotify:
289#ifdef USE_DGA
290		if (dgamouse && _windowed_mouse.value) {
291			mouse_x = event.xmotion.x_root;
292			mouse_y = event.xmotion.y_root;
293		} else
294#endif
295		{
296			if (_windowed_mouse.value) {
297				mouse_x = (float) ((int)event.xmotion.x - (int)(vid.width/2));
298				mouse_y = (float) ((int)event.xmotion.y - (int)(vid.height/2));
299
300				/* move the mouse to the window center again */
301				XSelectInput(dpy, win, X_MASK & ~PointerMotionMask);
302				XWarpPointer(dpy, None, win, 0, 0, 0, 0,
303					(vid.width/2), (vid.height/2));
304				XSelectInput(dpy, win, X_MASK);
305			}
306		}
307		break;
308
309	case ButtonPress:
310		b=-1;
311		if (event.xbutton.button == 1)
312			b = 0;
313		else if (event.xbutton.button == 2)
314			b = 2;
315		else if (event.xbutton.button == 3)
316			b = 1;
317		if (b>=0)
318			Key_Event(K_MOUSE1 + b, true);
319		break;
320
321	case ButtonRelease:
322		b=-1;
323		if (event.xbutton.button == 1)
324			b = 0;
325		else if (event.xbutton.button == 2)
326			b = 2;
327		else if (event.xbutton.button == 3)
328			b = 1;
329		if (b>=0)
330			Key_Event(K_MOUSE1 + b, false);
331		break;
332	}
333
334	if (old_windowed_mouse != _windowed_mouse.value) {
335		old_windowed_mouse = _windowed_mouse.value;
336
337		if (!_windowed_mouse.value) {
338			/* ungrab the pointer */
339			uninstall_grabs();
340		} else {
341			/* grab the pointer */
342			install_grabs();
343		}
344	}
345}
346
347
348void VID_Shutdown(void)
349{
350	if (!ctx)
351		return;
352
353	glXDestroyContext(dpy, ctx);
354}
355
356void signal_handler(int sig)
357{
358	printf("Received signal %d, exiting...\n", sig);
359	Sys_Quit();
360	exit(0);
361}
362
363void InitSig(void)
364{
365	signal(SIGHUP, signal_handler);
366	signal(SIGINT, signal_handler);
367	signal(SIGQUIT, signal_handler);
368	signal(SIGILL, signal_handler);
369	signal(SIGTRAP, signal_handler);
370	signal(SIGIOT, signal_handler);
371	signal(SIGBUS, signal_handler);
372	signal(SIGFPE, signal_handler);
373	signal(SIGSEGV, signal_handler);
374	signal(SIGTERM, signal_handler);
375}
376
377void VID_ShiftPalette(unsigned char *p)
378{
379//	VID_SetPalette(p);
380}
381
382void	VID_SetPalette (unsigned char *palette)
383{
384	byte	*pal;
385	unsigned r,g,b;
386	unsigned v;
387	int     r1,g1,b1;
388	int		k;
389	unsigned short i;
390	unsigned	*table;
391	FILE *f;
392	char s[255];
393	float dist, bestdist;
394	static qboolean palflag = false;
395
396//
397// 8 8 8 encoding
398//
399	Con_Printf("Converting 8to24\n");
400
401	pal = palette;
402	table = d_8to24table;
403	for (i=0 ; i<256 ; i++)
404	{
405		r = pal[0];
406		g = pal[1];
407		b = pal[2];
408		pal += 3;
409
410//		v = (255<<24) + (r<<16) + (g<<8) + (b<<0);
411//		v = (255<<0) + (r<<8) + (g<<16) + (b<<24);
412		v = (255<<24) + (r<<0) + (g<<8) + (b<<16);
413		*table++ = v;
414	}
415	d_8to24table[255] &= 0xffffff;	// 255 is transparent
416
417	// JACK: 3D distance calcs - k is last closest, l is the distance.
418	// FIXME: Precalculate this and cache to disk.
419	if (palflag)
420		return;
421	palflag = true;
422
423	COM_FOpenFile("glquake/15to8.pal", &f);
424	if (f) {
425		fread(d_15to8table, 1<<15, 1, f);
426		fclose(f);
427	} else {
428		for (i=0; i < (1<<15); i++) {
429			/* Maps
430 			000000000000000
431 			000000000011111 = Red  = 0x1F
432 			000001111100000 = Blue = 0x03E0
433 			111110000000000 = Grn  = 0x7C00
434 			*/
435 			r = ((i & 0x1F) << 3)+4;
436 			g = ((i & 0x03E0) >> 2)+4;
437 			b = ((i & 0x7C00) >> 7)+4;
438			pal = (unsigned char *)d_8to24table;
439			for (v=0,k=0,bestdist=10000.0; v<256; v++,pal+=4) {
440 				r1 = (int)r - (int)pal[0];
441 				g1 = (int)g - (int)pal[1];
442 				b1 = (int)b - (int)pal[2];
443				dist = sqrt(((r1*r1)+(g1*g1)+(b1*b1)));
444				if (dist < bestdist) {
445					k=v;
446					bestdist = dist;
447				}
448			}
449			d_15to8table[i]=k;
450		}
451		sprintf(s, "%s/glquake", com_gamedir);
452 		Sys_mkdir (s);
453		sprintf(s, "%s/glquake/15to8.pal", com_gamedir);
454		if ((f = fopen(s, "wb")) != NULL) {
455			fwrite(d_15to8table, 1<<15, 1, f);
456			fclose(f);
457		}
458	}
459}
460
461/*
462===============
463GL_Init
464===============
465*/
466void GL_Init (void)
467{
468	gl_vendor = glGetString (GL_VENDOR);
469	Con_Printf ("GL_VENDOR: %s\n", gl_vendor);
470	gl_renderer = glGetString (GL_RENDERER);
471	Con_Printf ("GL_RENDERER: %s\n", gl_renderer);
472
473	gl_version = glGetString (GL_VERSION);
474	Con_Printf ("GL_VERSION: %s\n", gl_version);
475	gl_extensions = glGetString (GL_EXTENSIONS);
476	Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions);
477
478//	Con_Printf ("%s %s\n", gl_renderer, gl_version);
479
480	glClearColor (1,0,0,0);
481	glCullFace(GL_FRONT);
482	glEnable(GL_TEXTURE_2D);
483
484	glEnable(GL_ALPHA_TEST);
485	glAlphaFunc(GL_GREATER, 0.666);
486
487	glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
488	glShadeModel (GL_FLAT);
489
490	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
491	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
492	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
493	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
494
495	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
496
497//	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
498	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
499}
500
501/*
502=================
503GL_BeginRendering
504
505=================
506*/
507void GL_BeginRendering (int *x, int *y, int *width, int *height)
508{
509	extern cvar_t gl_clear;
510
511	*x = *y = 0;
512	*width = scr_width;
513	*height = scr_height;
514
515//    if (!wglMakeCurrent( maindc, baseRC ))
516//		Sys_Error ("wglMakeCurrent failed");
517
518//	glViewport (*x, *y, *width, *height);
519}
520
521
522void GL_EndRendering (void)
523{
524	glFlush();
525	glXSwapBuffers(dpy, win);
526}
527
528qboolean VID_Is8bit(void)
529{
530	return is8bit;
531}
532
533#ifdef GL_EXT_SHARED
534void VID_Init8bitPalette()
535{
536	// Check for 8bit Extensions and initialize them.
537	int i;
538	char thePalette[256*3];
539	char *oldPalette, *newPalette;
540
541	if (strstr(gl_extensions, "GL_EXT_shared_texture_palette") == NULL)
542		return;
543
544	Con_SafePrintf("8-bit GL extensions enabled.\n");
545	glEnable( GL_SHARED_TEXTURE_PALETTE_EXT );
546	oldPalette = (char *) d_8to24table; //d_8to24table3dfx;
547	newPalette = thePalette;
548	for (i=0;i<256;i++) {
549		*newPalette++ = *oldPalette++;
550		*newPalette++ = *oldPalette++;
551		*newPalette++ = *oldPalette++;
552		oldPalette++;
553	}
554	glColorTableEXT(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, (void *) thePalette);
555	is8bit = true;
556}
557
558#else
559extern void gl3DfxSetPaletteEXT(GLuint *pal);
560
561void VID_Init8bitPalette(void)
562{
563	// Check for 8bit Extensions and initialize them.
564	int i;
565	GLubyte table[256][4];
566	char *oldpal;
567
568	if (strstr(gl_extensions, "3DFX_set_global_palette") == NULL)
569		return;
570
571	Con_SafePrintf("8-bit GL extensions enabled.\n");
572	glEnable( GL_SHARED_TEXTURE_PALETTE_EXT );
573	oldpal = (char *) d_8to24table; //d_8to24table3dfx;
574	for (i=0;i<256;i++) {
575		table[i][2] = *oldpal++;
576		table[i][1] = *oldpal++;
577		table[i][0] = *oldpal++;
578		table[i][3] = 255;
579		oldpal++;
580	}
581	gl3DfxSetPaletteEXT((GLuint *)table);
582	is8bit = true;
583}
584#endif
585
586void VID_Init(unsigned char *palette)
587{
588	int i;
589	int attrib[] = {
590		GLX_RGBA,
591		GLX_RED_SIZE, 1,
592		GLX_GREEN_SIZE, 1,
593		GLX_BLUE_SIZE, 1,
594		GLX_DOUBLEBUFFER,
595		GLX_DEPTH_SIZE, 1,
596		None
597	};
598	char	gldir[MAX_OSPATH];
599	int width = 640, height = 480;
600	int scrnum;
601	XSetWindowAttributes attr;
602	unsigned long mask;
603	Window root;
604	XVisualInfo *visinfo;
605
606	S_Init();
607
608	Cvar_RegisterVariable (&vid_mode);
609	Cvar_RegisterVariable (&gl_ztrick);
610	Cvar_RegisterVariable (&_windowed_mouse);
611
612	vid.maxwarpwidth = WARP_WIDTH;
613	vid.maxwarpheight = WARP_HEIGHT;
614	vid.colormap = host_colormap;
615	vid.fullbright = 256 - LittleLong (*((int *)vid.colormap + 2048));
616
617// interpret command-line params
618
619// set vid parameters
620	if ((i = COM_CheckParm("-width")) != 0)
621		width = atoi(com_argv[i+1]);
622	if ((i = COM_CheckParm("-height")) != 0)
623		height = atoi(com_argv[i+1]);
624
625	if ((i = COM_CheckParm("-conwidth")) != 0)
626		vid.conwidth = Q_atoi(com_argv[i+1]);
627	else
628		vid.conwidth = 640;
629
630	vid.conwidth &= 0xfff8; // make it a multiple of eight
631
632	if (vid.conwidth < 320)
633		vid.conwidth = 320;
634
635	// pick a conheight that matches with correct aspect
636	vid.conheight = vid.conwidth*3 / 4;
637
638	if ((i = COM_CheckParm("-conheight")) != 0)
639		vid.conheight = Q_atoi(com_argv[i+1]);
640	if (vid.conheight < 200)
641		vid.conheight = 200;
642
643	if (!(dpy = XOpenDisplay(NULL))) {
644		fprintf(stderr, "Error couldn't open the X display\n");
645		exit(1);
646	}
647
648	scrnum = DefaultScreen(dpy);
649	root = RootWindow(dpy, scrnum);
650
651	visinfo = glXChooseVisual(dpy, scrnum, attrib);
652	if (!visinfo) {
653		fprintf(stderr, "qkHack: Error couldn't get an RGB, Double-buffered, Depth visual\n");
654		exit(1);
655	}
656	/* window attributes */
657	attr.background_pixel = 0;
658	attr.border_pixel = 0;
659	attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
660	attr.event_mask = X_MASK;
661	mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
662
663	win = XCreateWindow(dpy, root, 0, 0, width, height,
664						0, visinfo->depth, InputOutput,
665						visinfo->visual, mask, &attr);
666	XMapWindow(dpy, win);
667
668	XMoveWindow(dpy, win, 0, 0);
669
670	XFlush(dpy);
671
672	ctx = glXCreateContext(dpy, visinfo, NULL, True);
673
674	glXMakeCurrent(dpy, win, ctx);
675
676	scr_width = width;
677	scr_height = height;
678
679	if (vid.conheight > height)
680		vid.conheight = height;
681	if (vid.conwidth > width)
682		vid.conwidth = width;
683	vid.width = vid.conwidth;
684	vid.height = vid.conheight;
685
686	vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0);
687	vid.numpages = 2;
688
689	InitSig(); // trap evil signals
690
691	GL_Init();
692
693	sprintf (gldir, "%s/glquake", com_gamedir);
694	Sys_mkdir (gldir);
695
696	VID_SetPalette(palette);
697
698	// Check for 3DFX Extensions and initialize them.
699	VID_Init8bitPalette();
700
701	Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height);
702
703	vid.recalc_refdef = 1;				// force a surface cache flush
704}
705
706void Sys_SendKeyEvents(void)
707{
708	if (dpy) {
709		while (XPending(dpy))
710			GetEvent();
711	}
712}
713
714void Force_CenterView_f (void)
715{
716	cl.viewangles[PITCH] = 0;
717}
718
719void IN_Init(void)
720{
721}
722
723void IN_Shutdown(void)
724{
725}
726
727/*
728===========
729IN_Commands
730===========
731*/
732void IN_Commands (void)
733{
734}
735
736/*
737===========
738IN_Move
739===========
740*/
741void IN_MouseMove (usercmd_t *cmd)
742{
743	if (m_filter.value)
744	{
745		mouse_x = (mouse_x + old_mouse_x) * 0.5;
746		mouse_y = (mouse_y + old_mouse_y) * 0.5;
747	}
748	old_mouse_x = mouse_x;
749	old_mouse_y = mouse_y;
750
751	mouse_x *= sensitivity.value;
752	mouse_y *= sensitivity.value;
753
754// add mouse X/Y movement to cmd
755	if ( (in_strafe.state & 1) || (lookstrafe.value && (in_mlook.state & 1) ))
756		cmd->sidemove += m_side.value * mouse_x;
757	else
758		cl.viewangles[YAW] -= m_yaw.value * mouse_x;
759
760	if (in_mlook.state & 1)
761		V_StopPitchDrift ();
762
763	if ( (in_mlook.state & 1) && !(in_strafe.state & 1))
764	{
765		cl.viewangles[PITCH] += m_pitch.value * mouse_y;
766		if (cl.viewangles[PITCH] > 80)
767			cl.viewangles[PITCH] = 80;
768		if (cl.viewangles[PITCH] < -70)
769			cl.viewangles[PITCH] = -70;
770	}
771	else
772	{
773		if ((in_strafe.state & 1) && noclip_anglehack)
774			cmd->upmove -= m_forward.value * mouse_y;
775		else
776			cmd->forwardmove -= m_forward.value * mouse_y;
777	}
778	mouse_x = mouse_y = 0.0;
779}
780
781void IN_Move (usercmd_t *cmd)
782{
783	IN_MouseMove(cmd);
784}
785
786
787void VID_UnlockBuffer() {}
788void VID_LockBuffer() {}
789
790