2acfc4c6eb6c0b92fbc9eee22d8411ce2334cddc
[xonotic/darkplaces.git] / vid_glx.c
1 /*
2 Copyright (C) 1996-1997 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 #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 <dlfcn.h>
29
30 #include "quakedef.h"
31
32 #include <GL/glx.h>
33
34 #include <X11/keysym.h>
35 #include <X11/cursorfont.h>
36
37 #include <X11/extensions/XShm.h>
38 #include <X11/extensions/xf86dga.h>
39 #include <X11/extensions/xf86vmode.h>
40
41 static Display *dpy = NULL;
42 static int scrnum;
43 static Window win;
44 static GLXContext ctx = NULL;
45
46 #define KEY_MASK (KeyPressMask | KeyReleaseMask)
47 #define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \
48                     PointerMotionMask | ButtonMotionMask )
49 #define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask | StructureNotifyMask )
50
51
52 cvar_t vid_mode = {"vid_mode", "0", false};
53 cvar_t vid_fullscreen = {"vid_fullscreen", "1"};
54
55 viddef_t        vid;                            // global video state
56
57 static qboolean         mouse_avail = true;
58 static qboolean         mouse_active = false;
59 // static qboolean              dga_active;
60 static float    mouse_x, mouse_y;
61 static float    old_mouse_x, old_mouse_y;
62 static int p_mouse_x, p_mouse_y;
63
64 static cvar_t in_mouse = {"in_mouse", "1", false};
65 static cvar_t in_dga = {"in_dga", "1", false};
66 static cvar_t in_dga_mouseaccel = {"in_dga_mouseaccel", "1", false};
67 static cvar_t m_filter = {"m_filter", "0"};
68 static cvar_t _windowed_mouse = {"_windowed_mouse", "1"};
69
70 qboolean vidmode_ext = false;
71
72 static int win_x, win_y;
73
74 static int scr_width, scr_height;
75
76 static XF86VidModeModeInfo **vidmodes;
77 //static int default_dotclock_vidmode;
78 static int num_vidmodes;
79 static qboolean vidmode_active = false;
80
81 /*-----------------------------------------------------------------------*/
82
83 float           gldepthmin, gldepthmax;
84
85 const char *gl_vendor;
86 const char *gl_renderer;
87 const char *gl_version;
88 const char *gl_extensions;
89
90 //static float vid_gamma = 1.0;
91
92 /*-----------------------------------------------------------------------*/
93 static int
94 XLateKey(XKeyEvent *ev/*, qboolean modified*/)
95 {
96 //      char tmp[2];
97         int key = 0;
98         KeySym keysym;
99
100         keysym = XLookupKeysym(ev, 0);
101
102         switch(keysym) {
103                 case XK_KP_Page_Up:     key = KP_PGUP; break;
104                 case XK_Page_Up:        key = K_PGUP; break;
105
106                 case XK_KP_Page_Down:   key = KP_PGDN; break;
107                 case XK_Page_Down:      key = K_PGDN; break;
108
109                 case XK_KP_Home:        key = KP_HOME; break;
110                 case XK_Home:           key = K_HOME; break;
111
112                 case XK_KP_End:         key = KP_END; break;
113                 case XK_End:            key = K_END; break;
114
115                 case XK_KP_Left:        key = KP_LEFTARROW; break;
116                 case XK_Left:           key = K_LEFTARROW; break;
117
118                 case XK_KP_Right:       key = KP_RIGHTARROW; break;
119                 case XK_Right:          key = K_RIGHTARROW; break;
120
121                 case XK_KP_Down:        key = KP_DOWNARROW; break;
122                 case XK_Down:           key = K_DOWNARROW; break;
123
124                 case XK_KP_Up:          key = KP_UPARROW; break;
125                 case XK_Up:                     key = K_UPARROW; break;
126
127                 case XK_Escape:         key = K_ESCAPE; break;
128
129                 case XK_KP_Enter:       key = KP_ENTER; break;
130                 case XK_Return:         key = K_ENTER; break;
131
132                 case XK_Tab:            key = K_TAB; break;
133
134                 case XK_F1:                     key = K_F1; break;
135                 case XK_F2:                     key = K_F2; break;
136                 case XK_F3:                     key = K_F3; break;
137                 case XK_F4:                     key = K_F4; break;
138                 case XK_F5:                     key = K_F5; break;
139                 case XK_F6:                     key = K_F6; break;
140                 case XK_F7:                     key = K_F7; break;
141                 case XK_F8:                     key = K_F8; break;
142                 case XK_F9:                     key = K_F9; break;
143                 case XK_F10:            key = K_F10; break;
144                 case XK_F11:            key = K_F11; break;
145                 case XK_F12:            key = K_F12; break;
146
147                 case XK_BackSpace:      key = K_BACKSPACE; break;
148
149                 case XK_KP_Delete:      key = KP_DEL; break;
150                 case XK_Delete:         key = K_DEL; break;
151
152                 case XK_Pause:          key = K_PAUSE; break;
153
154                 case XK_Shift_L:
155                 case XK_Shift_R:        key = K_SHIFT; break;
156
157                 case XK_Execute:
158                 case XK_Control_L:
159                 case XK_Control_R:      key = K_CTRL; break;
160
161                 case XK_Mode_switch:
162                 case XK_Alt_L:
163                 case XK_Meta_L:
164                 case XK_Alt_R:
165                 case XK_Meta_R:         key = K_ALT; break;
166
167                 case XK_Caps_Lock:      key = K_CAPSLOCK; break;
168                 case XK_KP_Begin:       key = KP_5; break;
169
170                 case XK_Insert:         key = K_INS; break;
171                 case XK_KP_Insert:      key = KP_INS; break;
172
173                 case XK_KP_Multiply:    key = KP_MULTIPLY; break;
174                 case XK_KP_Add:         key = KP_PLUS; break;
175                 case XK_KP_Subtract:    key = KP_MINUS; break;
176                 case XK_KP_Divide:      key = KP_DIVIDE; break;
177
178                 /* For Sun keyboards */
179                 case XK_F27:            key = K_HOME; break;
180                 case XK_F29:            key = K_PGUP; break;
181                 case XK_F33:            key = K_END; break;
182                 case XK_F35:            key = K_PGDN; break;
183
184                 default:
185                         if (keysym < 128) {
186                                 /* ASCII keys */
187                                 key = keysym;
188                                 if (/*!modified && */((key >= 'A') && (key <= 'Z'))) {
189                                         key = key + ('a' - 'A');
190                                 }
191                         }
192                         break;
193         }
194
195         return key;
196 }
197
198 static Cursor CreateNullCursor(Display *display, Window root)
199 {
200     Pixmap cursormask; 
201     XGCValues xgc;
202     GC gc;
203     XColor dummycolour;
204     Cursor cursor;
205
206     cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/);
207     xgc.function = GXclear;
208     gc =  XCreateGC(display, cursormask, GCFunction, &xgc);
209     XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
210     dummycolour.pixel = 0;
211     dummycolour.red = 0;
212     dummycolour.flags = 04;
213     cursor = XCreatePixmapCursor(display, cursormask, cursormask,
214           &dummycolour,&dummycolour, 0,0);
215     XFreePixmap(display,cursormask);
216     XFreeGC(display,gc);
217     return cursor;
218 }
219
220 static void install_grabs(void)
221 {
222         XWindowAttributes attribs_1;
223         XSetWindowAttributes attribs_2;
224
225         XGetWindowAttributes(dpy, win, &attribs_1);
226         attribs_2.event_mask = attribs_1.your_event_mask | KEY_MASK | MOUSE_MASK;
227         XChangeWindowAttributes(dpy, win, CWEventMask, &attribs_2);
228
229 // inviso cursor
230         XDefineCursor(dpy, win, CreateNullCursor(dpy, win));
231
232         XGrabPointer(dpy, win,  True, 0, GrabModeAsync, GrabModeAsync,
233                      win, None, CurrentTime);
234
235         if (in_dga.value) {
236                 int MajorVersion, MinorVersion;
237
238                 if (!XF86DGAQueryVersion(dpy, &MajorVersion, &MinorVersion)) { 
239                         // unable to query, probalby not supported
240                         Con_Printf( "Failed to detect XF86DGA Mouse\n" );
241                         in_dga.value = 0;
242                 } else {
243                         in_dga.value = 1;
244                         XF86DGADirectVideo(dpy, DefaultScreen(dpy), XF86DGADirectMouse);
245                         XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0);
246                 }
247         } else {
248                 XWarpPointer(dpy, None, win,
249                                          0, 0, 0, 0,
250                                          vid.width / 2, vid.height / 2);
251         }
252
253         XGrabKeyboard(dpy, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
254
255         mouse_active = true;
256         mouse_x = mouse_y = 0;
257
258 //      XSync(dpy, True);
259 }
260
261 static void uninstall_grabs(void)
262 {
263         if (!dpy || !win)
264                 return;
265
266         if (in_dga.value == 1) {
267                 XF86DGADirectVideo(dpy, DefaultScreen(dpy), 0);
268         }
269
270         XUngrabPointer(dpy, CurrentTime);
271         XUngrabKeyboard(dpy, CurrentTime);
272
273 // inviso cursor
274         XUndefineCursor(dpy, win);
275
276         mouse_active = false;
277 }
278
279 static void HandleEvents(void)
280 {
281         XEvent event;
282 //      KeySym ks;
283         int b;
284         qboolean dowarp = false;
285
286         if (!dpy)
287                 return;
288
289         while (XPending(dpy)) {
290                 XNextEvent(dpy, &event);
291
292                 switch (event.type) {
293                 case KeyPress:
294                 case KeyRelease:
295                         Key_Event(XLateKey(&event.xkey), event.type == KeyPress);
296                         break;
297
298                 case MotionNotify:
299                         if (in_dga.value == 1) {
300                                 mouse_x += event.xmotion.x_root * in_dga_mouseaccel.value;
301                                 mouse_y += event.xmotion.y_root * in_dga_mouseaccel.value;
302                         } else {
303                                 if (!p_mouse_x && !p_mouse_y) {
304                                         Con_Printf("event->xmotion.x: %d\n", event.xmotion.x);
305                                         Con_Printf("event->xmotion.y: %d\n", event.xmotion.y);
306                                 }
307                                 if (vid_fullscreen.value || _windowed_mouse.value) {
308                                         if (!event.xmotion.send_event) {
309                                                 mouse_x += (event.xmotion.x - p_mouse_x);
310                                                 mouse_y += (event.xmotion.y - p_mouse_y);
311                                                 if (abs(vid.width/2 - event.xmotion.x) > vid.width / 4
312                                                 || abs(vid.height/2 - event.xmotion.y) > vid.height / 4) {
313                                                         dowarp = true;
314                                                 }
315                                         }
316                                 } else {
317                                         mouse_x += (event.xmotion.x - p_mouse_x);
318                                         mouse_y += (event.xmotion.y - p_mouse_y);
319                                 }
320                                 p_mouse_x = event.xmotion.x;
321                                 p_mouse_y = event.xmotion.y;
322                         }
323                         break;
324
325                 case ButtonPress:
326                         b=-1;
327                         if (event.xbutton.button == 1)
328                                 b = 0;
329                         else if (event.xbutton.button == 2)
330                                 b = 2;
331                         else if (event.xbutton.button == 3)
332                                 b = 1;
333                         if (b>=0)
334                                 Key_Event(K_MOUSE1 + b, true);
335                         break;
336
337                 case ButtonRelease:
338                         b=-1;
339                         if (event.xbutton.button == 1)
340                                 b = 0;
341                         else if (event.xbutton.button == 2)
342                                 b = 2;
343                         else if (event.xbutton.button == 3)
344                                 b = 1;
345                         if (b>=0)
346                                 Key_Event(K_MOUSE1 + b, false);
347                         break;
348
349                 case CreateNotify :
350                         win_x = event.xcreatewindow.x;
351                         win_y = event.xcreatewindow.y;
352                         break;
353
354                 case ConfigureNotify :
355                         win_x = event.xconfigure.x;
356                         win_y = event.xconfigure.y;
357                         break;
358                 }
359         }
360
361         if (dowarp) {
362                 /* move the mouse to the window center again */
363                 p_mouse_x = vid.width / 2;
364                 p_mouse_y = vid.height / 2;
365                 XWarpPointer(dpy, None, win, 0, 0, 0, 0, p_mouse_x, p_mouse_y);
366         }
367
368 }
369
370 static void IN_DeactivateMouse( void ) 
371 {
372         if (!mouse_avail || !dpy || !win)
373                 return;
374
375         if (mouse_active) {
376                 uninstall_grabs();
377                 mouse_active = false;
378         }
379 }
380
381 static void IN_ActivateMouse( void ) 
382 {
383         if (!mouse_avail || !dpy || !win)
384                 return;
385
386         if (!mouse_active) {
387                 mouse_x = mouse_y = 0; // don't spazz
388                 install_grabs();
389                 mouse_active = true;
390         }
391 }
392
393
394 void VID_Shutdown(void)
395 {
396         if (!ctx || !dpy)
397                 return;
398
399         if (dpy) {
400                 uninstall_grabs();
401
402                 if (vidmode_active)
403                         XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[0]);
404 /* Disabled, causes a segfault during shutdown.
405                 if (ctx)
406                         glXDestroyContext(dpy, ctx);
407 */
408                 if (win)
409                         XDestroyWindow(dpy, win);
410                 XCloseDisplay(dpy);
411         }
412         vidmode_active = false;
413         dpy = NULL;
414         win = 0;
415         ctx = NULL;
416 }
417
418 void signal_handler(int sig)
419 {
420         printf("Received signal %d, exiting...\n", sig);
421         Sys_Quit();
422         exit(0);
423 }
424
425 void InitSig(void)
426 {
427         signal(SIGHUP, signal_handler);
428         signal(SIGINT, signal_handler);
429         signal(SIGQUIT, signal_handler);
430         signal(SIGILL, signal_handler);
431         signal(SIGTRAP, signal_handler);
432         signal(SIGIOT, signal_handler);
433         signal(SIGBUS, signal_handler);
434         signal(SIGFPE, signal_handler);
435         signal(SIGSEGV, signal_handler);
436         signal(SIGTERM, signal_handler);
437 }
438
439 void VID_CheckMultitexture(void) 
440 {
441         void *prjobj;
442         qglMTexCoord2f = NULL;
443         qglSelectTexture = NULL;
444         // Check to see if multitexture is disabled
445         if (COM_CheckParm("-nomtex"))
446         {
447                 Con_Printf("...multitexture disabled\n");
448                 return;
449         }
450         if ((prjobj = dlopen(NULL, RTLD_LAZY)) == NULL)
451         {
452                 Con_Printf("Unable to open symbol list for main program.\n");
453                 return;
454         }
455         // Test for ARB_multitexture
456         if (!COM_CheckParm("-SGISmtex") && strstr(gl_extensions, "GL_ARB_multitexture "))
457         {
458                 Con_Printf("...using GL_ARB_multitexture\n");
459                 qglMTexCoord2f = (void *) dlsym(prjobj, "glMultiTexCoord2fARB");
460                 qglSelectTexture = (void *) dlsym(prjobj, "glActiveTextureARB");
461                 gl_mtexable = true;
462                 gl_mtex_enum = GL_TEXTURE0_ARB;
463         }
464         else if (strstr(gl_extensions, "GL_SGIS_multitexture ")) // Test for SGIS_multitexture (if ARB_multitexture not found)
465         {
466                 Con_Printf("...using GL_SGIS_multitexture\n");
467                 qglMTexCoord2f = (void *) dlsym(prjobj, "glMTexCoord2fSGIS");
468                 qglSelectTexture = (void *) dlsym(prjobj, "glSelectTextureSGIS");
469                 gl_mtexable = true;
470                 gl_mtex_enum = TEXTURE0_SGIS;
471         }
472         else
473                 Con_Printf("...multitexture disabled (not detected)\n");
474         dlclose(prjobj);
475 }
476
477 void VID_CheckCVA(void)
478 {
479         void *prjobj;
480         qglLockArraysEXT = NULL;
481         qglUnlockArraysEXT = NULL;
482         gl_supportslockarrays = false;
483         if (COM_CheckParm("-nocva"))
484         {
485                 Con_Printf("...compiled vertex arrays disabled\n");
486                 return;
487         }
488         if ((prjobj = dlopen(NULL, RTLD_LAZY)) == NULL)
489         {
490                 Con_Printf("Unable to open symbol list for main program.\n");
491                 return;
492         }
493         if (strstr(gl_extensions, "GL_EXT_compiled_vertex_array"))
494         {
495                 Con_Printf("...using compiled vertex arrays\n");
496                 qglLockArraysEXT = (void *) dlsym(prjobj, "glLockArraysEXT");
497                 qglUnlockArraysEXT = (void *) dlsym(prjobj, "glUnlockArraysEXT");
498                 gl_supportslockarrays = true;
499         }
500         dlclose(prjobj);
501 }
502
503 /*
504 =================
505 GL_BeginRendering
506
507 =================
508 */
509 void GL_BeginRendering (int *x, int *y, int *width, int *height)
510 {
511         *x = *y = 0;
512         *width = scr_width;
513         *height = scr_height;
514
515 //      glViewport (*x, *y, *width, *height);
516 }
517
518
519 void GL_EndRendering (void)
520 {
521         if (!r_render.value)
522                 return;
523         glFlush();
524         glXSwapBuffers(dpy, win);
525 }
526
527 void VID_Init()
528 {
529         int i;
530         int attrib[] = {
531                 GLX_RGBA,
532                 GLX_RED_SIZE, 1,
533                 GLX_GREEN_SIZE, 1,
534                 GLX_BLUE_SIZE, 1,
535                 GLX_DOUBLEBUFFER,
536                 GLX_DEPTH_SIZE, 1,
537                 None
538         };
539 //      char    gldir[MAX_OSPATH];
540         int width = 640, height = 480;
541         XSetWindowAttributes attr;
542         unsigned long mask;
543         Window root;
544         XVisualInfo *visinfo;
545         qboolean fullscreen = true;
546         int MajorVersion, MinorVersion;
547         int actualWidth, actualHeight;
548
549         Cvar_RegisterVariable (&vid_mode);
550         Cvar_RegisterVariable (&vid_fullscreen);
551         Cvar_RegisterVariable (&in_mouse);
552         Cvar_RegisterVariable (&in_dga);
553         Cvar_RegisterVariable (&in_dga_mouseaccel);
554         Cvar_RegisterVariable (&m_filter);
555         
556 // interpret command-line params
557
558 // set vid parameters
559         if ((i = COM_CheckParm("-window")) != 0)
560                 fullscreen = false;
561
562         if ((i = COM_CheckParm("-width")) != 0)
563                 width = atoi(com_argv[i+1]);
564
565         if ((i = COM_CheckParm("-height")) != 0)
566                 height = atoi(com_argv[i+1]);
567
568         if ((i = COM_CheckParm("-conwidth")) != 0)
569                 vid.conwidth = atoi(com_argv[i+1]);
570         else
571                 vid.conwidth = 640;
572
573         vid.conwidth &= 0xfff8; // make it a multiple of eight
574
575         if (vid.conwidth < 320)
576                 vid.conwidth = 320;
577
578         // pick a conheight that matches with correct aspect
579         vid.conheight = vid.conwidth*3 / 4;
580
581         if ((i = COM_CheckParm("-conheight")) != 0)
582                 vid.conheight = atoi(com_argv[i+1]);
583         if (vid.conheight < 200)
584                 vid.conheight = 200;
585
586         if (!(dpy = XOpenDisplay(NULL))) {
587                 fprintf(stderr, "Error couldn't open the X display\n");
588                 exit(1);
589         }
590
591         scrnum = DefaultScreen(dpy);
592         root = RootWindow(dpy, scrnum);
593
594         // Get video mode list
595         MajorVersion = MinorVersion = 0;
596         if (!XF86VidModeQueryVersion(dpy, &MajorVersion, &MinorVersion)) { 
597                 vidmode_ext = false;
598         } else {
599                 Con_Printf("Using XFree86-VidModeExtension Version %d.%d\n", MajorVersion, MinorVersion);
600                 vidmode_ext = true;
601         }
602
603         visinfo = glXChooseVisual(dpy, scrnum, attrib);
604         if (!visinfo) {
605                 fprintf(stderr, "qkHack: Error couldn't get an RGB, Double-buffered, Depth visual\n");
606                 exit(1);
607         }
608
609         if (vidmode_ext) {
610                 int best_fit, best_dist, dist, x, y;
611                 
612                 XF86VidModeGetAllModeLines(dpy, scrnum, &num_vidmodes, &vidmodes);
613
614                 // Are we going fullscreen?  If so, let's change video mode
615                 if (fullscreen) {
616                         best_dist = 9999999;
617                         best_fit = -1;
618
619                         for (i = 0; i < num_vidmodes; i++) {
620                                 if (width > vidmodes[i]->hdisplay ||
621                                         height > vidmodes[i]->vdisplay)
622                                         continue;
623
624                                 x = width - vidmodes[i]->hdisplay;
625                                 y = height - vidmodes[i]->vdisplay;
626                                 dist = (x * x) + (y * y);
627                                 if (dist < best_dist) {
628                                         best_dist = dist;
629                                         best_fit = i;
630                                 }
631                         }
632
633                         if (best_fit != -1) {
634                                 actualWidth = vidmodes[best_fit]->hdisplay;
635                                 actualHeight = vidmodes[best_fit]->vdisplay;
636
637                                 // change to the mode
638                                 XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]);
639                                 vidmode_active = true;
640
641                                 // Move the viewport to top left
642                                 XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
643                         } else
644                                 fullscreen = 0;
645                 }
646         }
647
648         /* window attributes */
649         attr.background_pixel = 0;
650         attr.border_pixel = 0;
651         attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
652         attr.event_mask = X_MASK;
653         if (vidmode_active) {
654                 mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | 
655                         CWEventMask | CWOverrideRedirect;
656                 attr.override_redirect = True;
657                 attr.backing_store = NotUseful;
658                 attr.save_under = False;
659         } else
660                 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
661
662         win = XCreateWindow(dpy, root, 0, 0, width, height,
663                                                 0, visinfo->depth, InputOutput,
664                                                 visinfo->visual, mask, &attr);
665         XMapWindow(dpy, win);
666
667         if (vidmode_active) {
668                 XMoveWindow(dpy, win, 0, 0);
669                 XRaiseWindow(dpy, win);
670                 XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0);
671                 XFlush(dpy);
672                 // Move the viewport to top left
673                 XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
674         }
675
676         XFlush(dpy);
677
678         ctx = glXCreateContext(dpy, visinfo, NULL, True);
679
680         glXMakeCurrent(dpy, win, ctx);
681
682         scr_width = width;
683         scr_height = height;
684
685         if (vid.conheight > height)
686                 vid.conheight = height;
687         if (vid.conwidth > width)
688                 vid.conwidth = width;
689         vid.width = vid.conwidth;
690         vid.height = vid.conheight;
691
692         vid.aspect = ((float)vid.height / (float)vid.width) * (320.0 / 240.0);
693
694         InitSig(); // trap evil signals
695
696         GL_Init();
697
698         Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height);
699
700         vid.recalc_refdef = 1;                          // force a surface cache flush
701
702         install_grabs();
703 }
704
705 void Sys_SendKeyEvents(void)
706 {
707         HandleEvents();
708 }
709
710 void Force_CenterView_f (void)
711 {
712         cl.viewangles[PITCH] = 0;
713 }
714
715 void IN_Init(void)
716 {
717 }
718
719 void IN_Shutdown(void)
720 {
721 }
722
723 /*
724 ===========
725 IN_Commands
726 ===========
727 */
728 void IN_Commands (void)
729 {
730         if (!dpy || !win)
731                 return;
732
733         if (vidmode_active || key_dest == key_game)
734                 IN_ActivateMouse();
735         else
736                 IN_DeactivateMouse ();
737 }
738
739 /*
740 ===========
741 IN_Move
742 ===========
743 */
744 void IN_MouseMove (usercmd_t *cmd)
745 {
746         if (!mouse_avail)
747                 return;
748
749         if (!mouse_avail)
750                 return;
751
752         if (m_filter.value) {
753                 mouse_x = (mouse_x + old_mouse_x) * 0.5;
754                 mouse_y = (mouse_y + old_mouse_y) * 0.5;
755
756                 old_mouse_x = mouse_x;
757                 old_mouse_y = mouse_y;
758         }
759
760         mouse_x *= sensitivity.value;
761         mouse_y *= sensitivity.value;
762         
763         if (in_strafe.state & 1)
764                 cmd->sidemove += m_side.value * mouse_x;
765         else
766                 cl.viewangles[YAW] -= m_yaw.value * mouse_x;
767                 
768 /*        if (freelook)*/
769                 V_StopPitchDrift ();
770
771         if (/*freelook && */!(in_strafe.state & 1)) {
772                 cl.viewangles[PITCH] += m_pitch.value * mouse_y;
773                 cl.viewangles[PITCH] = bound (-70, cl.viewangles[PITCH], 80);
774         } else {
775                 if ((in_strafe.state & 1) && noclip_anglehack)
776                         cmd->upmove -= m_forward.value * mouse_y;
777                 else
778                         cmd->forwardmove -= m_forward.value * mouse_y;
779         }
780         mouse_x = mouse_y = 0.0;
781 }
782
783 void IN_Move (usercmd_t *cmd)
784 {
785         IN_MouseMove(cmd);
786 }
787
788