cleaned up vid_glx.c somewhat, it can now quit properly when the close button is...
[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 *vidx11_display = NULL;
42 static int scrnum;
43 static Window win;
44 static GLXContext ctx = NULL;
45
46 Atom wm_delete_window_atom;
47
48 #define KEY_MASK (KeyPressMask | KeyReleaseMask)
49 #define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \
50                     PointerMotionMask | ButtonMotionMask )
51 #define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask | StructureNotifyMask )
52
53
54 viddef_t        vid;                            // global video state
55
56 static qboolean         mouse_avail = true;
57 static qboolean         mouse_active = false, usingmouse = false;
58 static float    mouse_x, mouse_y;
59 static int p_mouse_x, p_mouse_y;
60
61 cvar_t vid_dga = {CVAR_SAVE, "vid_dga", "1"};
62 cvar_t vid_dga_mouseaccel = {0, "vid_dga_mouseaccel", "1"};
63
64 qboolean vidmode_ext = false;
65
66 static int win_x, win_y;
67
68 static int scr_width, scr_height;
69
70 static XF86VidModeModeInfo **vidmodes;
71 static int num_vidmodes;
72 static qboolean vidmode_active = false;
73
74 static Visual *vidx11_visual;
75 static Colormap vidx11_colormap;
76
77 /*-----------------------------------------------------------------------*/
78
79 const char *gl_vendor;
80 const char *gl_renderer;
81 const char *gl_version;
82 const char *gl_extensions;
83
84 /*-----------------------------------------------------------------------*/
85 static int
86 XLateKey(XKeyEvent *ev)
87 {
88         int key = 0;
89         KeySym keysym;
90
91         keysym = XLookupKeysym(ev, 0);
92
93         switch(keysym)
94         {
95                 case XK_KP_Page_Up:     key = KP_PGUP; break;
96                 case XK_Page_Up:        key = K_PGUP; break;
97
98                 case XK_KP_Page_Down:   key = KP_PGDN; break;
99                 case XK_Page_Down:      key = K_PGDN; break;
100
101                 case XK_KP_Home:        key = KP_HOME; break;
102                 case XK_Home:           key = K_HOME; break;
103
104                 case XK_KP_End:         key = KP_END; break;
105                 case XK_End:            key = K_END; break;
106
107                 case XK_KP_Left:        key = KP_LEFTARROW; break;
108                 case XK_Left:           key = K_LEFTARROW; break;
109
110                 case XK_KP_Right:       key = KP_RIGHTARROW; break;
111                 case XK_Right:          key = K_RIGHTARROW; break;
112
113                 case XK_KP_Down:        key = KP_DOWNARROW; break;
114                 case XK_Down:           key = K_DOWNARROW; break;
115
116                 case XK_KP_Up:          key = KP_UPARROW; break;
117                 case XK_Up:                     key = K_UPARROW; break;
118
119                 case XK_Escape:         key = K_ESCAPE; break;
120
121                 case XK_KP_Enter:       key = KP_ENTER; break;
122                 case XK_Return:         key = K_ENTER; break;
123
124                 case XK_Tab:            key = K_TAB; break;
125
126                 case XK_F1:                     key = K_F1; break;
127                 case XK_F2:                     key = K_F2; break;
128                 case XK_F3:                     key = K_F3; break;
129                 case XK_F4:                     key = K_F4; break;
130                 case XK_F5:                     key = K_F5; break;
131                 case XK_F6:                     key = K_F6; break;
132                 case XK_F7:                     key = K_F7; break;
133                 case XK_F8:                     key = K_F8; break;
134                 case XK_F9:                     key = K_F9; break;
135                 case XK_F10:            key = K_F10; break;
136                 case XK_F11:            key = K_F11; break;
137                 case XK_F12:            key = K_F12; break;
138
139                 case XK_BackSpace:      key = K_BACKSPACE; break;
140
141                 case XK_KP_Delete:      key = KP_DEL; break;
142                 case XK_Delete:         key = K_DEL; break;
143
144                 case XK_Pause:          key = K_PAUSE; break;
145
146                 case XK_Shift_L:
147                 case XK_Shift_R:        key = K_SHIFT; break;
148
149                 case XK_Execute:
150                 case XK_Control_L:
151                 case XK_Control_R:      key = K_CTRL; break;
152
153                 case XK_Mode_switch:
154                 case XK_Alt_L:
155                 case XK_Meta_L:
156                 case XK_Alt_R:
157                 case XK_Meta_R:         key = K_ALT; break;
158
159                 case XK_Caps_Lock:      key = K_CAPSLOCK; break;
160                 case XK_KP_Begin:       key = KP_5; break;
161
162                 case XK_Insert:         key = K_INS; break;
163                 case XK_KP_Insert:      key = KP_INS; break;
164
165                 case XK_KP_Multiply:    key = KP_MULTIPLY; break;
166                 case XK_KP_Add:         key = KP_PLUS; break;
167                 case XK_KP_Subtract:    key = KP_MINUS; break;
168                 case XK_KP_Divide:      key = KP_DIVIDE; break;
169
170                 /* For Sun keyboards */
171                 case XK_F27:            key = K_HOME; break;
172                 case XK_F29:            key = K_PGUP; break;
173                 case XK_F33:            key = K_END; break;
174                 case XK_F35:            key = K_PGDN; break;
175
176                 default:
177                         if (keysym < 128)
178                         {
179                                 /* ASCII keys */
180                                 key = keysym;
181                                 if ((key >= 'A') && (key <= 'Z'))
182                                         key = key + ('a' - 'A');
183                         }
184                         break;
185         }
186
187         return key;
188 }
189
190 static Cursor CreateNullCursor(Display *display, Window root)
191 {
192         Pixmap cursormask;
193         XGCValues xgc;
194         GC gc;
195         XColor dummycolour;
196         Cursor cursor;
197
198         cursormask = XCreatePixmap(display, root, 1, 1, 1);
199         xgc.function = GXclear;
200         gc =  XCreateGC(display, cursormask, GCFunction, &xgc);
201         XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
202         dummycolour.pixel = 0;
203         dummycolour.red = 0;
204         dummycolour.flags = 04;
205         cursor = XCreatePixmapCursor(display, cursormask, cursormask, &dummycolour,&dummycolour, 0,0);
206         XFreePixmap(display,cursormask);
207         XFreeGC(display,gc);
208         return cursor;
209 }
210
211 static void install_grabs(void)
212 {
213         XWindowAttributes attribs_1;
214         XSetWindowAttributes attribs_2;
215
216         XGetWindowAttributes(vidx11_display, win, &attribs_1);
217         attribs_2.event_mask = attribs_1.your_event_mask | KEY_MASK | MOUSE_MASK;
218         XChangeWindowAttributes(vidx11_display, win, CWEventMask, &attribs_2);
219
220 // inviso cursor
221         XDefineCursor(vidx11_display, win, CreateNullCursor(vidx11_display, win));
222
223         XGrabPointer(vidx11_display, win,  True, 0, GrabModeAsync, GrabModeAsync, win, None, CurrentTime);
224
225         if (vid_dga.integer)
226         {
227                 int MajorVersion, MinorVersion;
228
229                 if (!XF86DGAQueryVersion(vidx11_display, &MajorVersion, &MinorVersion))
230                 {
231                         // unable to query, probalby not supported
232                         Con_Printf( "Failed to detect XF86DGA Mouse\n" );
233                         vid_dga.integer = 0;
234                 }
235                 else
236                 {
237                         vid_dga.integer = 1;
238                         XF86DGADirectVideo(vidx11_display, DefaultScreen(vidx11_display), XF86DGADirectMouse);
239                         XWarpPointer(vidx11_display, None, win, 0, 0, 0, 0, 0, 0);
240                 }
241         }
242         else
243                 XWarpPointer(vidx11_display, None, win, 0, 0, 0, 0, scr_width / 2, scr_height / 2);
244
245         XGrabKeyboard(vidx11_display, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
246
247         mouse_active = true;
248         mouse_x = mouse_y = 0;
249 }
250
251 static void uninstall_grabs(void)
252 {
253         if (!vidx11_display || !win)
254                 return;
255
256         if (vid_dga.integer == 1)
257                 XF86DGADirectVideo(vidx11_display, DefaultScreen(vidx11_display), 0);
258
259         XUngrabPointer(vidx11_display, CurrentTime);
260         XUngrabKeyboard(vidx11_display, CurrentTime);
261
262 // inviso cursor
263         XUndefineCursor(vidx11_display, win);
264
265         mouse_active = false;
266 }
267
268 static void HandleEvents(void)
269 {
270         XEvent event;
271         qboolean dowarp = false;
272
273         if (!vidx11_display)
274                 return;
275
276         while (XPending(vidx11_display))
277         {
278                 XNextEvent(vidx11_display, &event);
279
280                 switch (event.type)
281                 {
282                 case KeyPress:
283                         // key pressed
284                         Key_Event(XLateKey(&event.xkey), true);
285                         break;
286
287                 case KeyRelease:
288                         // key released
289                         Key_Event(XLateKey(&event.xkey), false);
290                         break;
291
292                 case MotionNotify:
293                         // mouse moved
294                         if (usingmouse)
295                         {
296                                 if (vid_dga.integer == 1)
297                                 {
298                                         mouse_x += event.xmotion.x_root * vid_dga_mouseaccel.value;
299                                         mouse_y += event.xmotion.y_root * vid_dga_mouseaccel.value;
300                                 }
301                                 else
302                                 {
303
304                                         if (!event.xmotion.send_event)
305                                         {
306                                                 mouse_x += event.xmotion.x - p_mouse_x;
307                                                 mouse_y += event.xmotion.y - p_mouse_y;
308                                                 if (abs(scr_width/2 - event.xmotion.x) > scr_width / 4 || abs(scr_height/2 - event.xmotion.y) > scr_height / 4)
309                                                         dowarp = true;
310                                         }
311                                         p_mouse_x = event.xmotion.x;
312                                         p_mouse_y = event.xmotion.y;
313                                 }
314                         }
315                         else
316                                 ui_mouseupdate(event.xmotion.x, event.xmotion.y);
317                         break;
318
319                 case ButtonPress:
320                         // mouse button pressed
321                         switch(event.xbutton.button)
322                         {
323                         case 1:
324                                 Key_Event(K_MOUSE1, true);
325                                 break;
326                         case 2:
327                                 Key_Event(K_MOUSE3, true);
328                                 break;
329                         case 3:
330                                 Key_Event(K_MOUSE2, true);
331                                 break;
332                         case 4:
333                                 Key_Event(K_MWHEELUP, true);
334                                 break;
335                         case 5:
336                                 Key_Event(K_MWHEELDOWN, true);
337                                 break;
338                 default:
339                                 Con_Printf("HandleEvents: ButtonPress gave value %d, 1-5 expected\n", event.xbutton.button);
340                                 break;
341                         }
342                         break;
343
344                 case ButtonRelease:
345                         // mouse button released
346                         switch(event.xbutton.button)
347                         {
348                         case 1:
349                                 Key_Event(K_MOUSE1, false);
350                                 break;
351                         case 2:
352                                 Key_Event(K_MOUSE3, false);
353                                 break;
354                         case 3:
355                                 Key_Event(K_MOUSE2, false);
356                                 break;
357                         case 4:
358                                 Key_Event(K_MWHEELUP, false);
359                                 break;
360                         case 5:
361                                 Key_Event(K_MWHEELDOWN, false);
362                                 break;
363                 default:
364                                 Con_Printf("HandleEvents: ButtonRelease gave value %d, 1-5 expected\n", event.xbutton.button);
365                                 break;
366                         }
367                         break;
368
369                 case CreateNotify:
370                         // window created
371                         win_x = event.xcreatewindow.x;
372                         win_y = event.xcreatewindow.y;
373                         break;
374
375                 case ConfigureNotify:
376                         // window changed size/location
377                         win_x = event.xconfigure.x;
378                         win_y = event.xconfigure.y;
379                         break;
380                 case DestroyNotify:
381                         // window has been destroyed
382                         Sys_Quit();
383                         break;
384                 case ClientMessage:
385                         // window manager messages
386                         if ((event.xclient.format == 32) && (event.xclient.data.l[0] == wm_delete_window_atom))
387                                 Sys_Quit();
388                         break;
389                 case MapNotify:
390                         // window restored
391                         vid_hidden = false;
392                         break;
393                 case UnmapNotify:
394                         // window iconified/rolledup/whatever
395                         vid_hidden = true;
396                         break;
397                 case FocusIn:
398                         // window is now the input focus
399                         break;
400                 case FocusOut:
401                         // window is no longer the input focus
402                         break;
403                 case EnterNotify:
404                         // mouse entered window
405                         break;
406                 case LeaveNotify:
407                         // mouse left window
408                         break;
409                 }
410         }
411
412         if (dowarp)
413         {
414                 /* move the mouse to the window center again */
415                 p_mouse_x = scr_width / 2;
416                 p_mouse_y = scr_height / 2;
417                 XWarpPointer(vidx11_display, None, win, 0, 0, 0, 0, p_mouse_x, p_mouse_y);
418         }
419
420 }
421
422 static void IN_DeactivateMouse( void )
423 {
424         if (!mouse_avail || !vidx11_display || !win)
425                 return;
426
427         if (mouse_active)
428         {
429                 uninstall_grabs();
430                 mouse_active = false;
431         }
432 }
433
434 static void IN_ActivateMouse( void )
435 {
436         if (!mouse_avail || !vidx11_display || !win)
437                 return;
438
439         if (!mouse_active)
440         {
441                 mouse_x = mouse_y = 0; // don't spazz
442                 install_grabs();
443                 mouse_active = true;
444         }
445 }
446
447
448 void VID_Shutdown(void)
449 {
450         if (!ctx || !vidx11_display)
451                 return;
452
453         if (vidx11_display)
454         {
455                 uninstall_grabs();
456
457                 if (vidmode_active)
458                         XF86VidModeSwitchToMode(vidx11_display, scrnum, vidmodes[0]);
459                 if (win)
460                         XDestroyWindow(vidx11_display, win);
461                 XCloseDisplay(vidx11_display);
462         }
463         vidmode_active = false;
464         vidx11_display = NULL;
465         win = 0;
466         ctx = NULL;
467 }
468
469 void signal_handler(int sig)
470 {
471         printf("Received signal %d, exiting...\n", sig);
472         Sys_Quit();
473         exit(0);
474 }
475
476 void InitSig(void)
477 {
478         signal(SIGHUP, signal_handler);
479         signal(SIGINT, signal_handler);
480         signal(SIGQUIT, signal_handler);
481         signal(SIGILL, signal_handler);
482         signal(SIGTRAP, signal_handler);
483         signal(SIGIOT, signal_handler);
484         signal(SIGBUS, signal_handler);
485         signal(SIGFPE, signal_handler);
486         signal(SIGSEGV, signal_handler);
487         signal(SIGTERM, signal_handler);
488 }
489
490 /*
491 =================
492 VID_GetWindowSize
493 =================
494 */
495 void VID_GetWindowSize (int *x, int *y, int *width, int *height)
496 {
497         *x = *y = 0;
498         *width = scr_width;
499         *height = scr_height;
500 }
501
502 void VID_Finish (void)
503 {
504         int usemouse;
505         if (r_render.integer)
506         {
507                 qglFinish();
508                 glXSwapBuffers(vidx11_display, win);
509         }
510
511 // handle the mouse state when windowed if that's changed
512         usemouse = false;
513         if (vid_mouse.integer && key_dest == key_game)
514                 usemouse = true;
515         if (vidmode_active)
516                 usemouse = true;
517         if (usemouse)
518         {
519                 if (!usingmouse)
520                 {
521                         usingmouse = true;
522                         IN_ActivateMouse ();
523                 }
524         }
525         else
526         {
527                 if (usingmouse)
528                 {
529                         usingmouse = false;
530                         IN_DeactivateMouse ();
531                 }
532         }
533 }
534
535 // LordHavoc: ported from SDL 1.2.2, this was far more difficult to port from
536 // SDL than to simply use the XFree gamma ramp extension, but that affects the
537 // whole screen even when the game window is inactive, this only affects the
538 // screen while the window is active, very desirable behavior :)
539 int VID_SetGamma(float prescale, float gamma, float scale, float base)
540 {
541 // LordHavoc: FIXME: finish this code, we need to allocate colors before we can store them
542 #if 1
543         return FALSE;
544 #else
545         int i, ncolors, c;
546         unsigned int Rmask, Gmask, Bmask, Rloss, Gloss, Bloss, Rshift, Gshift, Bshift, mask;
547         XColor xcmap[256];
548         unsigned short ramp[256];
549
550         if (COM_CheckParm("-nogamma"))
551                 return FALSE;
552
553         if (vidx11_visual->class != DirectColor)
554         {
555                 Con_Printf("X11 Visual class is %d, can only do gamma on %d\n", vidx11_visual->class, DirectColor);
556                 return FALSE;
557         }
558
559         Rmask = vidx11_visual->red_mask;
560         Gmask = vidx11_visual->green_mask;
561         Bmask = vidx11_visual->blue_mask;
562
563         Rshift = 0;
564         Rloss = 8;
565         if ((mask = Rmask))
566         {
567                 for (;!(mask & 1);mask >>= 1)
568                         ++Rshift;
569                 for (;(mask & 1);mask >>= 1)
570                         --Rloss;
571         }
572         Gshift = 0;
573         Gloss = 8;
574         if ((mask = Gmask))
575         {
576                 for (;!(mask & 1);mask >>= 1)
577                         ++Gshift;
578                 for (;(mask & 1);mask >>= 1)
579                         --Gloss;
580         }
581         Bshift = 0;
582         Bloss = 8;
583         if ((mask = Bmask))
584         {
585                 for (;!(mask & 1);mask >>= 1)
586                         ++Bshift;
587                 for (;(mask & 1);mask >>= 1)
588                         --Bloss;
589         }
590
591         BuildGammaTable16(prescale, gamma, scale, base, ramp);
592
593         // convert gamma ramp to palette (yes this seems odd)
594         ncolors = vidx11_visual->map_entries;
595         for (i = 0;i < ncolors;i++)
596         {
597                 c = (256 * i / ncolors);
598                 xcmap[i].pixel = ((c >> Rloss) << Rshift) | ((c >> Gloss) << Gshift) | ((c >> Bloss) << Bshift);
599                 xcmap[i].red   = ramp[c];
600                 xcmap[i].green = ramp[c];
601                 xcmap[i].blue  = ramp[c];
602                 xcmap[i].flags = (DoRed|DoGreen|DoBlue);
603         }
604         XStoreColors(vidx11_display, vidx11_colormap, xcmap, ncolors);
605         XSync(vidx11_display, false);
606         // FIXME: should this check for BadAccess/BadColor/BadValue errors produced by XStoreColors before setting this true?
607         return TRUE;
608 #endif
609 }
610
611 void VID_Init(void)
612 {
613         int i;
614 // LordHavoc: FIXME: finish this code, we need to allocate colors before we can store them
615 #if 0
616         int gammaattrib[] =
617         {
618                 GLX_RGBA,
619                 GLX_RED_SIZE, 1,
620                 GLX_GREEN_SIZE, 1,
621                 GLX_BLUE_SIZE, 1,
622                 GLX_DOUBLEBUFFER,
623                 GLX_DEPTH_SIZE, 1,
624                 GLX_X_VISUAL_TYPE, GLX_DIRECT_COLOR,
625                 None
626         };
627 #endif
628         int nogammaattrib[] =
629         {
630                 GLX_RGBA,
631                 GLX_RED_SIZE, 1,
632                 GLX_GREEN_SIZE, 1,
633                 GLX_BLUE_SIZE, 1,
634                 GLX_DOUBLEBUFFER,
635                 GLX_DEPTH_SIZE, 1,
636                 None
637         };
638         int width = 640, height = 480;
639         XSetWindowAttributes attr;
640         unsigned long mask;
641         Window root;
642         XVisualInfo *visinfo;
643         qboolean fullscreen = true;
644         int MajorVersion, MinorVersion;
645
646         Cvar_RegisterVariable (&vid_dga);
647         Cvar_RegisterVariable (&vid_dga_mouseaccel);
648
649 // interpret command-line params
650
651 // set vid parameters
652         if ((i = COM_CheckParm("-window")) != 0)
653                 fullscreen = false;
654
655         if ((i = COM_CheckParm("-width")) != 0)
656                 width = atoi(com_argv[i+1]);
657
658         if ((i = COM_CheckParm("-height")) != 0)
659                 height = atoi(com_argv[i+1]);
660
661         if ((i = COM_CheckParm("-conwidth")) != 0)
662                 vid.conwidth = atoi(com_argv[i+1]);
663         else
664                 vid.conwidth = 640;
665
666         vid.conwidth &= 0xfff8; // make it a multiple of eight
667
668         if (vid.conwidth < 320)
669                 vid.conwidth = 320;
670
671         // pick a conheight that matches with correct aspect
672         vid.conheight = vid.conwidth*3 / 4;
673
674         if ((i = COM_CheckParm("-conheight")) != 0)
675                 vid.conheight = atoi(com_argv[i+1]);
676         if (vid.conheight < 200)
677                 vid.conheight = 200;
678
679         if (!(vidx11_display = XOpenDisplay(NULL)))
680         {
681                 fprintf(stderr, "Error couldn't open the X display\n");
682                 exit(1);
683         }
684
685         scrnum = DefaultScreen(vidx11_display);
686         root = RootWindow(vidx11_display, scrnum);
687
688         // Get video mode list
689         MajorVersion = MinorVersion = 0;
690         if (!XF86VidModeQueryVersion(vidx11_display, &MajorVersion, &MinorVersion))
691                 vidmode_ext = false;
692         else
693         {
694                 Con_Printf("Using XFree86-VidModeExtension Version %d.%d\n", MajorVersion, MinorVersion);
695                 vidmode_ext = true;
696         }
697
698         visinfo = NULL;
699 // LordHavoc: FIXME: finish this code, we need to allocate colors before we can store them
700 #if 0
701         if (!COM_CheckParm("-nogamma"))
702                 visinfo = glXChooseVisual(vidx11_display, scrnum, gammaattrib);
703 #endif
704         if (!visinfo)
705         {
706                 visinfo = glXChooseVisual(vidx11_display, scrnum, nogammaattrib);
707                 if (!visinfo)
708                 {
709                         fprintf(stderr, "qkHack: Error couldn't get an RGB, Double-buffered, Depth visual\n");
710                         exit(1);
711                 }
712         }
713
714         if (vidmode_ext)
715         {
716                 int best_fit, best_dist, dist, x, y;
717
718                 XF86VidModeGetAllModeLines(vidx11_display, scrnum, &num_vidmodes, &vidmodes);
719
720                 // Are we going fullscreen?  If so, let's change video mode
721                 if (fullscreen)
722                 {
723                         best_dist = 9999999;
724                         best_fit = -1;
725
726                         for (i = 0; i < num_vidmodes; i++)
727                         {
728                                 if (width > vidmodes[i]->hdisplay || height > vidmodes[i]->vdisplay)
729                                         continue;
730
731                                 x = width - vidmodes[i]->hdisplay;
732                                 y = height - vidmodes[i]->vdisplay;
733                                 dist = (x * x) + (y * y);
734                                 if (dist < best_dist)
735                                 {
736                                         best_dist = dist;
737                                         best_fit = i;
738                                 }
739                         }
740
741                         if (best_fit != -1)
742                         {
743                                 // LordHavoc: changed from ActualWidth/ActualHeight =,
744                                 // to width/height =, so the window will take the full area of
745                                 // the mode chosen
746                                 width = vidmodes[best_fit]->hdisplay;
747                                 height = vidmodes[best_fit]->vdisplay;
748
749                                 // change to the mode
750                                 XF86VidModeSwitchToMode(vidx11_display, scrnum, vidmodes[best_fit]);
751                                 vidmode_active = true;
752
753                                 // Move the viewport to top left
754                                 XF86VidModeSetViewPort(vidx11_display, scrnum, 0, 0);
755                         }
756                         else
757                                 fullscreen = 0;
758                 }
759         }
760
761         // LordHavoc: save the visual for use in gamma ramp settings later
762         vidx11_visual = visinfo->visual;
763
764         /* window attributes */
765         attr.background_pixel = 0;
766         attr.border_pixel = 0;
767         // LordHavoc: save the colormap for later, too
768         vidx11_colormap = attr.colormap = XCreateColormap(vidx11_display, root, visinfo->visual, AllocNone);
769         attr.event_mask = X_MASK;
770         if (vidmode_active)
771         {
772                 mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | CWEventMask | CWOverrideRedirect;
773                 attr.override_redirect = True;
774                 attr.backing_store = NotUseful;
775                 attr.save_under = False;
776         }
777         else
778                 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
779
780         win = XCreateWindow(vidx11_display, root, 0, 0, width, height, 0, visinfo->depth, InputOutput, visinfo->visual, mask, &attr);
781         XStoreName(vidx11_display, win, gamename);
782         XMapWindow(vidx11_display, win);
783
784         // LordHavoc: making the close button on a window do the right thing
785         // seems to involve this mess, sigh...
786         wm_delete_window_atom = XInternAtom(vidx11_display, "WM_DELETE_WINDOW", false);
787         XSetWMProtocols(vidx11_display, win, &wm_delete_window_atom, 1);
788
789         if (vidmode_active)
790         {
791                 XMoveWindow(vidx11_display, win, 0, 0);
792                 XRaiseWindow(vidx11_display, win);
793                 XWarpPointer(vidx11_display, None, win, 0, 0, 0, 0, 0, 0);
794                 XFlush(vidx11_display);
795                 // Move the viewport to top left
796                 XF86VidModeSetViewPort(vidx11_display, scrnum, 0, 0);
797         }
798
799         XFlush(vidx11_display);
800
801         ctx = glXCreateContext(vidx11_display, visinfo, NULL, True);
802
803         glXMakeCurrent(vidx11_display, win, ctx);
804
805         scr_width = width;
806         scr_height = height;
807
808         if (vid.conheight > height)
809                 vid.conheight = height;
810         if (vid.conwidth > width)
811                 vid.conwidth = width;
812
813         InitSig(); // trap evil signals
814
815         vid_hidden = false;
816
817         GL_Init();
818
819         Con_SafePrintf ("Video mode %dx%d initialized.\n", width, height);
820 }
821
822 void Sys_SendKeyEvents(void)
823 {
824         HandleEvents();
825 }
826
827 void IN_Init(void)
828 {
829         if (COM_CheckParm ("-nomouse"))
830                 mouse_avail = false;
831 }
832
833 void IN_Shutdown(void)
834 {
835 }
836
837 /*
838 ===========
839 IN_Commands
840 ===========
841 */
842 void IN_Commands (void)
843 {
844 }
845
846 void IN_Move (usercmd_t *cmd)
847 {
848         if (mouse_avail)
849                 IN_Mouse(cmd, mouse_x, mouse_y);
850         mouse_x = 0;
851         mouse_y = 0;
852 }
853