vid_usingvsync now set to false after a vid_restart (so that vsync will be set again...
[divverent/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 <X11/Xlib.h>
31 #include <X11/Xutil.h>
32 #include <GL/glx.h>
33
34 #include <X11/keysym.h>
35 #include <X11/cursorfont.h>
36
37 #include <X11/extensions/XShm.h>
38 #if !defined(__APPLE__) && !defined(__MACH__)
39 #include <X11/extensions/xf86dga.h>
40 #endif
41 #include <X11/extensions/xf86vmode.h>
42
43 #include "quakedef.h"
44
45 // Tell startup code that we have a client
46 int cl_available = true;
47
48 //GLX prototypes
49 XVisualInfo *(GLAPIENTRY *qglXChooseVisual)(Display *dpy, int screen, int *attribList);
50 GLXContext (GLAPIENTRY *qglXCreateContext)(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct);
51 void (GLAPIENTRY *qglXDestroyContext)(Display *dpy, GLXContext ctx);
52 Bool (GLAPIENTRY *qglXMakeCurrent)(Display *dpy, GLXDrawable drawable, GLXContext ctx);
53 void (GLAPIENTRY *qglXSwapBuffers)(Display *dpy, GLXDrawable drawable);
54 const char *(GLAPIENTRY *qglXQueryExtensionsString)(Display *dpy, int screen);
55
56 //GLX_ARB_get_proc_address
57 void *(GLAPIENTRY *qglXGetProcAddressARB)(const GLubyte *procName);
58
59 static dllfunction_t getprocaddressfuncs[] =
60 {
61         {"glXGetProcAddressARB", (void **) &qglXGetProcAddressARB},
62         {NULL, NULL}
63 };
64
65 //GLX_SGI_swap_control
66 GLint (GLAPIENTRY *qglXSwapIntervalSGI)(GLint interval);
67
68 static dllfunction_t swapcontrolfuncs[] =
69 {
70         {"glXSwapIntervalSGI", (void **) &qglXSwapIntervalSGI},
71         {NULL, NULL}
72 };
73
74 static Display *vidx11_display = NULL;
75 static int vidx11_screen;
76 static Window win;
77 static GLXContext ctx = NULL;
78
79 Atom wm_delete_window_atom;
80
81 #define KEY_MASK (KeyPressMask | KeyReleaseMask)
82 #define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \
83                     PointerMotionMask | ButtonMotionMask)
84 #define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask | \
85                 StructureNotifyMask | FocusChangeMask | EnterWindowMask | \
86                 LeaveWindowMask)
87
88
89 static qboolean mouse_avail = true;
90 static qboolean mouse_active = false;
91 static qboolean vid_usingmouse = false;
92 static qboolean vid_usemouse = false;
93 static qboolean vid_usingvsync = false;
94 static qboolean vid_usevsync = false;
95 static qboolean ignoremousemove = false;
96 static float    mouse_x, mouse_y;
97 static int p_mouse_x, p_mouse_y;
98
99 #ifndef __APPLE__
100 cvar_t vid_dga = {CVAR_SAVE, "vid_dga", "1"};
101 cvar_t vid_dga_mouseaccel = {0, "vid_dga_mouseaccel", "1"};
102 #endif
103
104 qboolean vidmode_ext = false;
105
106 static int win_x, win_y;
107
108 static int scr_width, scr_height;
109
110 static XF86VidModeModeInfo **vidmodes;
111 static int num_vidmodes;
112 static qboolean vidmode_active = false;
113
114 static Visual *vidx11_visual;
115 static Colormap vidx11_colormap;
116
117 /*-----------------------------------------------------------------------*/
118
119 static int XLateKey(XKeyEvent *ev, char *ascii)
120 {
121         int key = 0;
122         char buf[64];
123         KeySym keysym, shifted;
124
125         keysym = XLookupKeysym (ev, 0);
126         XLookupString(ev, buf, sizeof buf, &shifted, 0);
127         *ascii = buf[0];
128
129         switch(keysym)
130         {
131                 case XK_KP_Page_Up:      key = K_KP_PGUP; break;
132                 case XK_Page_Up:         key = K_PGUP; break;
133
134                 case XK_KP_Page_Down: key = K_KP_PGDN; break;
135                 case XK_Page_Down:       key = K_PGDN; break;
136
137                 case XK_KP_Home: key = K_KP_HOME; break;
138                 case XK_Home:    key = K_HOME; break;
139
140                 case XK_KP_End:  key = K_KP_END; break;
141                 case XK_End:     key = K_END; break;
142
143                 case XK_KP_Left: key = K_KP_LEFTARROW; break;
144                 case XK_Left:    key = K_LEFTARROW; break;
145
146                 case XK_KP_Right: key = K_KP_RIGHTARROW; break;
147                 case XK_Right:  key = K_RIGHTARROW;             break;
148
149                 case XK_KP_Down: key = K_KP_DOWNARROW; break;
150                 case XK_Down:    key = K_DOWNARROW; break;
151
152                 case XK_KP_Up:   key = K_KP_UPARROW; break;
153                 case XK_Up:              key = K_UPARROW;        break;
154
155                 case XK_Escape: key = K_ESCAPE;         break;
156
157                 case XK_KP_Enter: key = K_KP_ENTER;     break;
158                 case XK_Return: key = K_ENTER;           break;
159
160                 case XK_Tab:            key = K_TAB;                     break;
161
162                 case XK_F1:              key = K_F1;                            break;
163
164                 case XK_F2:              key = K_F2;                            break;
165
166                 case XK_F3:              key = K_F3;                            break;
167
168                 case XK_F4:              key = K_F4;                            break;
169
170                 case XK_F5:              key = K_F5;                            break;
171
172                 case XK_F6:              key = K_F6;                            break;
173
174                 case XK_F7:              key = K_F7;                            break;
175
176                 case XK_F8:              key = K_F8;                            break;
177
178                 case XK_F9:              key = K_F9;                            break;
179
180                 case XK_F10:            key = K_F10;                     break;
181
182                 case XK_F11:            key = K_F11;                     break;
183
184                 case XK_F12:            key = K_F12;                     break;
185
186                 case XK_BackSpace: key = K_BACKSPACE; break;
187
188                 case XK_KP_Delete: key = K_KP_DEL; break;
189                 case XK_Delete: key = K_DEL; break;
190
191                 case XK_Pause:  key = K_PAUSE;           break;
192
193                 case XK_Shift_L:
194                 case XK_Shift_R:        key = K_SHIFT;          break;
195
196                 case XK_Execute:
197                 case XK_Control_L:
198                 case XK_Control_R:      key = K_CTRL;            break;
199
200                 case XK_Alt_L:
201                 case XK_Meta_L:
202                 case XK_Alt_R:
203                 case XK_Meta_R: key = K_ALT;                    break;
204
205                 case XK_KP_Begin: key = K_KP_5; break;
206
207                 case XK_Insert:key = K_INS; break;
208                 case XK_KP_Insert: key = K_KP_INS; break;
209
210                 case XK_KP_Multiply: key = '*'; break;
211                 case XK_KP_Add:  key = K_KP_PLUS; break;
212                 case XK_KP_Subtract: key = K_KP_MINUS; break;
213                 case XK_KP_Divide: key = K_KP_SLASH; break;
214
215                 default:
216                         if (keysym < 32 && keysym > 126)
217                                 break;
218
219                         if (keysym >= 'A' && keysym <= 'Z')
220                                 key = keysym - 'A' + 'a';
221                         else
222                                 key = keysym;
223
224                         break;
225         }
226
227         return key;
228 }
229
230 static Cursor CreateNullCursor(Display *display, Window root)
231 {
232         Pixmap cursormask;
233         XGCValues xgc;
234         GC gc;
235         XColor dummycolour;
236         Cursor cursor;
237
238         cursormask = XCreatePixmap(display, root, 1, 1, 1);
239         xgc.function = GXclear;
240         gc =  XCreateGC(display, cursormask, GCFunction, &xgc);
241         XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
242         dummycolour.pixel = 0;
243         dummycolour.red = 0;
244         dummycolour.flags = 04;
245         cursor = XCreatePixmapCursor(display, cursormask, cursormask, &dummycolour,&dummycolour, 0,0);
246         XFreePixmap(display,cursormask);
247         XFreeGC(display,gc);
248         return cursor;
249 }
250
251 static void install_grabs(void)
252 {
253         XWindowAttributes attribs_1;
254         XSetWindowAttributes attribs_2;
255
256         XGetWindowAttributes(vidx11_display, win, &attribs_1);
257         attribs_2.event_mask = attribs_1.your_event_mask | KEY_MASK | MOUSE_MASK;
258         XChangeWindowAttributes(vidx11_display, win, CWEventMask, &attribs_2);
259
260 // inviso cursor
261         XDefineCursor(vidx11_display, win, CreateNullCursor(vidx11_display, win));
262
263         XGrabPointer(vidx11_display, win,  True, 0, GrabModeAsync, GrabModeAsync, win, None, CurrentTime);
264
265 #ifndef __APPLE__
266         if (vid_dga.integer)
267         {
268                 int MajorVersion, MinorVersion;
269
270                 if (!XF86DGAQueryVersion(vidx11_display, &MajorVersion, &MinorVersion))
271                 {
272                         // unable to query, probalby not supported
273                         Con_Print( "Failed to detect XF86DGA Mouse\n" );
274                         vid_dga.integer = 0;
275                 }
276                 else
277                 {
278                         vid_dga.integer = 1;
279                         XF86DGADirectVideo(vidx11_display, DefaultScreen(vidx11_display), XF86DGADirectMouse);
280                         XWarpPointer(vidx11_display, None, win, 0, 0, 0, 0, 0, 0);
281                 }
282         }
283         else
284 #endif
285                 XWarpPointer(vidx11_display, None, win, 0, 0, 0, 0, scr_width / 2, scr_height / 2);
286
287         XGrabKeyboard(vidx11_display, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
288
289         mouse_active = true;
290         mouse_x = mouse_y = 0;
291         ignoremousemove = true;
292 }
293
294 static void uninstall_grabs(void)
295 {
296         if (!vidx11_display || !win)
297                 return;
298
299 #ifndef __APPLE__
300         if (vid_dga.integer == 1)
301                 XF86DGADirectVideo(vidx11_display, DefaultScreen(vidx11_display), 0);
302 #endif
303
304         XUngrabPointer(vidx11_display, CurrentTime);
305         XUngrabKeyboard(vidx11_display, CurrentTime);
306
307 // inviso cursor
308         XUndefineCursor(vidx11_display, win);
309
310         mouse_active = false;
311         ignoremousemove = true;
312 }
313
314 static void HandleEvents(void)
315 {
316         XEvent event;
317         int key;
318         char ascii;
319         qboolean dowarp = false;
320
321         if (!vidx11_display)
322                 return;
323
324         while (XPending(vidx11_display))
325         {
326                 XNextEvent(vidx11_display, &event);
327
328                 switch (event.type)
329                 {
330                 case KeyPress:
331                         // key pressed
332                         key = XLateKey (&event.xkey, &ascii);
333                         Key_Event(key, ascii, true);
334                         break;
335
336                 case KeyRelease:
337                         // key released
338                         key = XLateKey (&event.xkey, &ascii);
339                         Key_Event(key, ascii, false);
340                         break;
341
342                 case MotionNotify:
343                         // mouse moved
344                         if (vid_usingmouse)
345                         {
346 #ifndef __APPLE__
347                                 if (vid_dga.integer == 1)
348                                 {
349                                         mouse_x += event.xmotion.x_root * vid_dga_mouseaccel.value;
350                                         mouse_y += event.xmotion.y_root * vid_dga_mouseaccel.value;
351                                 }
352                                 else
353 #endif
354                                 {
355
356                                         if (!event.xmotion.send_event)
357                                         {
358                                                 mouse_x += event.xmotion.x - p_mouse_x;
359                                                 mouse_y += event.xmotion.y - p_mouse_y;
360                                                 if (abs(scr_width/2 - event.xmotion.x) > scr_width / 4 || abs(scr_height/2 - event.xmotion.y) > scr_height / 4)
361                                                         dowarp = true;
362                                         }
363                                         p_mouse_x = event.xmotion.x;
364                                         p_mouse_y = event.xmotion.y;
365                                 }
366                         }
367                         //else
368                         //      ui_mouseupdate(event.xmotion.x, event.xmotion.y);
369                         break;
370
371                 case ButtonPress:
372                         // mouse button pressed
373                         switch(event.xbutton.button)
374                         {
375                         case 1:
376                                 Key_Event(K_MOUSE1, 0, true);
377                                 break;
378                         case 2:
379                                 Key_Event(K_MOUSE3, 0, true);
380                                 break;
381                         case 3:
382                                 Key_Event(K_MOUSE2, 0, true);
383                                 break;
384                         case 4:
385                                 Key_Event(K_MWHEELUP, 0, true);
386                                 break;
387                         case 5:
388                                 Key_Event(K_MWHEELDOWN, 0, true);
389                                 break;
390                         case 6:
391                                 Key_Event(K_MOUSE4, 0, true);
392                                 break;
393                         case 7:
394                                 Key_Event(K_MOUSE5, 0, true);
395                                 break;
396                         case 8:
397                                 Key_Event(K_MOUSE6, 0, true);
398                                 break;
399                         case 9:
400                                 Key_Event(K_MOUSE7, 0, true);
401                                 break;
402                         case 10:
403                                 Key_Event(K_MOUSE8, 0, true);
404                                 break;
405                         case 11:
406                                 Key_Event(K_MOUSE9, 0, true);
407                                 break;
408                         case 12:
409                                 Key_Event(K_MOUSE10, 0, true);
410                                 break;
411                         default:
412                                 Con_Printf("HandleEvents: ButtonPress gave value %d, 1-12 expected\n", event.xbutton.button);
413                                 break;
414                         }
415                         break;
416
417                 case ButtonRelease:
418                         // mouse button released
419                         switch(event.xbutton.button)
420                         {
421                         case 1:
422                                 Key_Event(K_MOUSE1, 0, false);
423                                 break;
424                         case 2:
425                                 Key_Event(K_MOUSE3, 0, false);
426                                 break;
427                         case 3:
428                                 Key_Event(K_MOUSE2, 0, false);
429                                 break;
430                         case 4:
431                                 Key_Event(K_MWHEELUP, 0, false);
432                                 break;
433                         case 5:
434                                 Key_Event(K_MWHEELDOWN, 0, false);
435                                 break;
436                         case 6:
437                                 Key_Event(K_MOUSE4, 0, false);
438                                 break;
439                         case 7:
440                                 Key_Event(K_MOUSE5, 0, false);
441                                 break;
442                         case 8:
443                                 Key_Event(K_MOUSE6, 0, false);
444                                 break;
445                         case 9:
446                                 Key_Event(K_MOUSE7, 0, false);
447                                 break;
448                         case 10:
449                                 Key_Event(K_MOUSE8, 0, false);
450                                 break;
451                         case 11:
452                                 Key_Event(K_MOUSE9, 0, false);
453                                 break;
454                         case 12:
455                                 Key_Event(K_MOUSE10, 0, false);
456                                 break;
457                         default:
458                                 Con_Printf("HandleEvents: ButtonRelease gave value %d, 1-12 expected\n", event.xbutton.button);
459                                 break;
460                         }
461                         break;
462
463                 case CreateNotify:
464                         // window created
465                         win_x = event.xcreatewindow.x;
466                         win_y = event.xcreatewindow.y;
467                         break;
468
469                 case ConfigureNotify:
470                         // window changed size/location
471                         win_x = event.xconfigure.x;
472                         win_y = event.xconfigure.y;
473                         break;
474                 case DestroyNotify:
475                         // window has been destroyed
476                         Sys_Quit();
477                         break;
478                 case ClientMessage:
479                         // window manager messages
480                         if ((event.xclient.format == 32) && ((unsigned int)event.xclient.data.l[0] == wm_delete_window_atom))
481                                 Sys_Quit();
482                         break;
483                 case MapNotify:
484                         // window restored
485                         vid_hidden = false;
486                         vid_activewindow = false;
487                         VID_RestoreSystemGamma();
488                         break;
489                 case UnmapNotify:
490                         // window iconified/rolledup/whatever
491                         vid_hidden = true;
492                         vid_activewindow = false;
493                         VID_RestoreSystemGamma();
494                         break;
495                 case FocusIn:
496                         // window is now the input focus
497                         vid_activewindow = true;
498                         break;
499                 case FocusOut:
500                         // window is no longer the input focus
501                         vid_activewindow = false;
502                         VID_RestoreSystemGamma();
503                         break;
504                 case EnterNotify:
505                         // mouse entered window
506                         break;
507                 case LeaveNotify:
508                         // mouse left window
509                         break;
510                 }
511         }
512
513         if (dowarp)
514         {
515                 /* move the mouse to the window center again */
516                 p_mouse_x = scr_width / 2;
517                 p_mouse_y = scr_height / 2;
518                 XWarpPointer(vidx11_display, None, win, 0, 0, 0, 0, p_mouse_x, p_mouse_y);
519         }
520
521         // if told to ignore one mouse move, do so
522         if (ignoremousemove)
523         {
524                 ignoremousemove = false;
525                 mouse_x = 0;
526                 mouse_y = 0;
527         }
528 }
529
530 static void IN_DeactivateMouse( void )
531 {
532         if (!mouse_avail || !vidx11_display || !win)
533                 return;
534
535         if (mouse_active)
536         {
537                 uninstall_grabs();
538                 mouse_active = false;
539         }
540 }
541
542 static void IN_ActivateMouse( void )
543 {
544         if (!mouse_avail || !vidx11_display || !win)
545                 return;
546
547         if (!mouse_active)
548         {
549                 install_grabs();
550                 mouse_active = true;
551         }
552 }
553
554 void VID_Shutdown(void)
555 {
556         if (!ctx || !vidx11_display)
557                 return;
558
559         vid_hidden = true;
560         vid_usingmouse = false;
561         if (vidx11_display)
562         {
563                 VID_RestoreSystemGamma();
564                 uninstall_grabs();
565
566                 // FIXME: glXDestroyContext here?
567                 if (vidmode_active)
568                         XF86VidModeSwitchToMode(vidx11_display, vidx11_screen, vidmodes[0]);
569                 if (win)
570                         XDestroyWindow(vidx11_display, win);
571                 XCloseDisplay(vidx11_display);
572         }
573         vidmode_active = false;
574         vidx11_display = NULL;
575         win = 0;
576         ctx = NULL;
577
578         GL_CloseLibrary();
579         Key_ClearStates ();
580 }
581
582 void signal_handler(int sig)
583 {
584         printf("Received signal %d, exiting...\n", sig);
585         VID_RestoreSystemGamma();
586         Sys_Quit();
587         exit(0);
588 }
589
590 void InitSig(void)
591 {
592         signal(SIGHUP, signal_handler);
593         signal(SIGINT, signal_handler);
594         signal(SIGQUIT, signal_handler);
595         signal(SIGILL, signal_handler);
596         signal(SIGTRAP, signal_handler);
597         signal(SIGIOT, signal_handler);
598         signal(SIGBUS, signal_handler);
599         signal(SIGFPE, signal_handler);
600         signal(SIGSEGV, signal_handler);
601         signal(SIGTERM, signal_handler);
602 }
603
604 /*
605 =================
606 VID_GetWindowSize
607 =================
608 */
609 void VID_GetWindowSize (int *x, int *y, int *width, int *height)
610 {
611         *x = *y = 0;
612         *width = scr_width;
613         *height = scr_height;
614 }
615
616 void VID_Finish (void)
617 {
618         vid_usevsync = vid_vsync.integer && !cls.timedemo && gl_videosyncavailable;
619         if (vid_usingvsync != vid_usevsync && gl_videosyncavailable)
620         {
621                 vid_usingvsync = vid_usevsync;
622                 qglXSwapIntervalSGI (vid_usevsync);
623         }
624
625 // handle the mouse state when windowed if that's changed
626         vid_usemouse = false;
627         if (vid_mouse.integer && !key_consoleactive)
628                 vid_usemouse = true;
629         if (vidmode_active)
630                 vid_usemouse = true;
631         if (vid_usemouse)
632         {
633                 if (!vid_usingmouse)
634                 {
635                         vid_usingmouse = true;
636                         IN_ActivateMouse ();
637                 }
638         }
639         else
640         {
641                 if (vid_usingmouse)
642                 {
643                         vid_usingmouse = false;
644                         IN_DeactivateMouse ();
645                 }
646         }
647
648         if (r_render.integer)
649         {
650                 if (r_speeds.integer || gl_finish.integer)
651                         qglFinish();
652                 qglXSwapBuffers(vidx11_display, win);
653         }
654 }
655
656 int VID_SetGamma(unsigned short *ramps)
657 {
658         return XF86VidModeSetGammaRamp(vidx11_display, vidx11_screen, 256, ramps, ramps + 256, ramps + 512);
659 }
660
661 int VID_GetGamma(unsigned short *ramps)
662 {
663         return XF86VidModeGetGammaRamp(vidx11_display, vidx11_screen, 256, ramps, ramps + 256, ramps + 512);
664 }
665
666 void VID_Init(void)
667 {
668 #ifndef __APPLE__
669         Cvar_RegisterVariable (&vid_dga);
670         Cvar_RegisterVariable (&vid_dga_mouseaccel);
671 #endif
672         InitSig(); // trap evil signals
673 // COMMANDLINEOPTION: Input: -nomouse disables mouse support (see also vid_mouse cvar)
674         if (COM_CheckParm ("-nomouse") || COM_CheckParm("-safe"))
675                 mouse_avail = false;
676 }
677
678 void VID_BuildGLXAttrib(int *attrib, int stencil)
679 {
680         *attrib++ = GLX_RGBA;
681         *attrib++ = GLX_RED_SIZE;*attrib++ = 1;
682         *attrib++ = GLX_GREEN_SIZE;*attrib++ = 1;
683         *attrib++ = GLX_BLUE_SIZE;*attrib++ = 1;
684         *attrib++ = GLX_DOUBLEBUFFER;
685         *attrib++ = GLX_DEPTH_SIZE;*attrib++ = 1;
686         // if stencil is enabled, ask for alpha too
687         if (stencil)
688         {
689                 *attrib++ = GLX_STENCIL_SIZE;*attrib++ = 8;
690                 *attrib++ = GLX_ALPHA_SIZE;*attrib++ = 1;
691         }
692         *attrib++ = None;
693 }
694
695 int VID_InitMode(int fullscreen, int width, int height, int bpp)
696 {
697         int i;
698         int attrib[32];
699         XSetWindowAttributes attr;
700         unsigned long mask;
701         Window root;
702         XVisualInfo *visinfo;
703         int MajorVersion, MinorVersion;
704         const char *drivername;
705
706 #if defined(__APPLE__) && defined(__MACH__)
707         drivername = "/usr/X11R6/lib/libGL.1.dylib";
708 #else
709         drivername = "libGL.so.1";
710 #endif
711 // COMMANDLINEOPTION: Linux GLX: -gl_driver <drivername> selects a GL driver library, default is libGL.so.1, useful only for using fxmesa or similar, if you don't know what this is for, you don't need it
712 // COMMANDLINEOPTION: BSD GLX: -gl_driver <drivername> selects a GL driver library, default is libGL.so.1, useful only for using fxmesa or similar, if you don't know what this is for, you don't need it
713 // LordHavoc: although this works on MacOSX, it's useless there (as there is only one system libGL)
714         i = COM_CheckParm("-gl_driver");
715         if (i && i < com_argc - 1)
716                 drivername = com_argv[i + 1];
717         if (!GL_OpenLibrary(drivername))
718         {
719                 Con_Printf("Unable to load GL driver \"%s\"\n", drivername);
720                 return false;
721         }
722
723         if (!(vidx11_display = XOpenDisplay(NULL)))
724         {
725                 Con_Print("Couldn't open the X display\n");
726                 return false;
727         }
728
729         vidx11_screen = DefaultScreen(vidx11_display);
730         root = RootWindow(vidx11_display, vidx11_screen);
731
732         // Get video mode list
733         MajorVersion = MinorVersion = 0;
734         if (!XF86VidModeQueryVersion(vidx11_display, &MajorVersion, &MinorVersion))
735                 vidmode_ext = false;
736         else
737         {
738                 Con_DPrintf("Using XFree86-VidModeExtension Version %d.%d\n", MajorVersion, MinorVersion);
739                 vidmode_ext = true;
740         }
741
742         if ((qglXChooseVisual = GL_GetProcAddress("glXChooseVisual")) == NULL
743          || (qglXCreateContext = GL_GetProcAddress("glXCreateContext")) == NULL
744          || (qglXMakeCurrent = GL_GetProcAddress("glXMakeCurrent")) == NULL
745          || (qglXSwapBuffers = GL_GetProcAddress("glXSwapBuffers")) == NULL
746          || (qglXQueryExtensionsString = GL_GetProcAddress("glXQueryExtensionsString")) == NULL)
747         {
748                 Con_Printf("glX functions not found in %s\n", gl_driver);
749                 return false;
750         }
751
752         VID_BuildGLXAttrib(attrib, bpp == 32);
753         visinfo = qglXChooseVisual(vidx11_display, vidx11_screen, attrib);
754         if (!visinfo)
755         {
756                 Con_Print("Couldn't get an RGB, Double-buffered, Depth visual\n");
757                 return false;
758         }
759
760         if (vidmode_ext)
761         {
762                 int best_fit, best_dist, dist, x, y;
763
764                 // Are we going fullscreen?  If so, let's change video mode
765                 if (fullscreen)
766                 {
767                         XF86VidModeGetAllModeLines(vidx11_display, vidx11_screen, &num_vidmodes, &vidmodes);
768                         best_dist = 9999999;
769                         best_fit = -1;
770
771                         for (i = 0; i < num_vidmodes; i++)
772                         {
773                                 if (width > vidmodes[i]->hdisplay || height > vidmodes[i]->vdisplay)
774                                         continue;
775
776                                 x = width - vidmodes[i]->hdisplay;
777                                 y = height - vidmodes[i]->vdisplay;
778                                 dist = (x * x) + (y * y);
779                                 if (dist < best_dist)
780                                 {
781                                         best_dist = dist;
782                                         best_fit = i;
783                                 }
784                         }
785
786                         if (best_fit != -1)
787                         {
788                                 // LordHavoc: changed from ActualWidth/ActualHeight =,
789                                 // to width/height =, so the window will take the full area of
790                                 // the mode chosen
791                                 width = vidmodes[best_fit]->hdisplay;
792                                 height = vidmodes[best_fit]->vdisplay;
793
794                                 // change to the mode
795                                 XF86VidModeSwitchToMode(vidx11_display, vidx11_screen, vidmodes[best_fit]);
796                                 vidmode_active = true;
797
798                                 // Move the viewport to top left
799                                 XF86VidModeSetViewPort(vidx11_display, vidx11_screen, 0, 0);
800                         }
801                         else
802                                 fullscreen = 0;
803                 }
804         }
805
806         // LordHavoc: save the visual for use in gamma ramp settings later
807         vidx11_visual = visinfo->visual;
808
809         /* window attributes */
810         attr.background_pixel = 0;
811         attr.border_pixel = 0;
812         // LordHavoc: save the colormap for later, too
813         vidx11_colormap = attr.colormap = XCreateColormap(vidx11_display, root, visinfo->visual, AllocNone);
814         attr.event_mask = X_MASK;
815         if (vidmode_active)
816         {
817                 mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | CWEventMask | CWOverrideRedirect;
818                 attr.override_redirect = True;
819                 attr.backing_store = NotUseful;
820                 attr.save_under = False;
821         }
822         else
823                 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
824
825         win = XCreateWindow(vidx11_display, root, 0, 0, width, height, 0, visinfo->depth, InputOutput, visinfo->visual, mask, &attr);
826         XStoreName(vidx11_display, win, gamename);
827         XMapWindow(vidx11_display, win);
828
829         // LordHavoc: making the close button on a window do the right thing
830         // seems to involve this mess, sigh...
831         wm_delete_window_atom = XInternAtom(vidx11_display, "WM_DELETE_WINDOW", false);
832         XSetWMProtocols(vidx11_display, win, &wm_delete_window_atom, 1);
833
834         if (vidmode_active)
835         {
836                 XMoveWindow(vidx11_display, win, 0, 0);
837                 XRaiseWindow(vidx11_display, win);
838                 XWarpPointer(vidx11_display, None, win, 0, 0, 0, 0, 0, 0);
839                 XFlush(vidx11_display);
840                 // Move the viewport to top left
841                 XF86VidModeSetViewPort(vidx11_display, vidx11_screen, 0, 0);
842         }
843
844         //XSync(vidx11_display, False);
845
846         ctx = qglXCreateContext(vidx11_display, visinfo, NULL, True);
847         if (!ctx)
848                 Sys_Error ("glXCreateContext failed\n");
849
850         if (!qglXMakeCurrent(vidx11_display, win, ctx))
851                 Sys_Error ("glXMakeCurrent failed\n");
852
853         XSync(vidx11_display, False);
854
855         scr_width = width;
856         scr_height = height;
857
858         if ((qglGetString = GL_GetProcAddress("glGetString")) == NULL)
859                 Sys_Error("glGetString not found in %s", gl_driver);
860
861         gl_renderer = qglGetString(GL_RENDERER);
862         gl_vendor = qglGetString(GL_VENDOR);
863         gl_version = qglGetString(GL_VERSION);
864         gl_extensions = qglGetString(GL_EXTENSIONS);
865         gl_platform = "GLX";
866         gl_platformextensions = qglXQueryExtensionsString(vidx11_display, vidx11_screen);
867
868         gl_videosyncavailable = false;
869
870 // COMMANDLINEOPTION: Linux GLX: -nogetprocaddress disables GLX_ARB_get_proc_address (not required, more formal method of getting extension functions)
871 // COMMANDLINEOPTION: BSD GLX: -nogetprocaddress disables GLX_ARB_get_proc_address (not required, more formal method of getting extension functions)
872 // COMMANDLINEOPTION: MacOSX GLX: -nogetprocaddress disables GLX_ARB_get_proc_address (not required, more formal method of getting extension functions)
873         GL_CheckExtension("GLX_ARB_get_proc_address", getprocaddressfuncs, "-nogetprocaddress", false);
874 // COMMANDLINEOPTION: Linux GLX: -novideosync disables GLX_SGI_swap_control
875 // COMMANDLINEOPTION: BSD GLX: -novideosync disables GLX_SGI_swap_control
876 // COMMANDLINEOPTION: MacOSX GLX: -novideosync disables GLX_SGI_swap_control
877         gl_videosyncavailable = GL_CheckExtension("GLX_SGI_swap_control", swapcontrolfuncs, "-novideosync", false);
878
879         vid_usingmouse = false;
880         vid_usingvsync = false;
881         ignoremousemove = true;
882         vid_hidden = false;
883         vid_activewindow = true;
884         GL_Init();
885         return true;
886 }
887
888 void Sys_SendKeyEvents(void)
889 {
890         HandleEvents();
891 }
892
893 /*
894 ===========
895 IN_Commands
896 ===========
897 */
898 void IN_Commands (void)
899 {
900 }
901
902 void IN_Move (void)
903 {
904         if (mouse_avail)
905                 IN_Mouse(mouse_x, mouse_y);
906         mouse_x = 0;
907         mouse_y = 0;
908 }
909
910 static void *prjobj = NULL;
911
912 int GL_OpenLibrary(const char *name)
913 {
914         Con_Printf("Loading OpenGL driver %s\n", name);
915         GL_CloseLibrary();
916         if (!(prjobj = dlopen(name, RTLD_LAZY)))
917         {
918                 Con_Printf("Unable to open symbol list for %s\n", name);
919                 return false;
920         }
921         strcpy(gl_driver, name);
922         return true;
923 }
924
925 void GL_CloseLibrary(void)
926 {
927         if (prjobj)
928                 dlclose(prjobj);
929         prjobj = NULL;
930         gl_driver[0] = 0;
931         qglXGetProcAddressARB = NULL;
932         gl_extensions = "";
933         gl_platform = "";
934         gl_platformextensions = "";
935 }
936
937 void *GL_GetProcAddress(const char *name)
938 {
939         void *p = NULL;
940         if (qglXGetProcAddressARB != NULL)
941                 p = (void *) qglXGetProcAddressARB(name);
942         if (p == NULL)
943                 p = (void *) dlsym(prjobj, name);
944         return p;
945 }
946