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