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