1 /*
2 Copyright (C) 1997-2001 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 /*
21 ** GLW_IMP.C
22 **
23 ** This file contains ALL Linux specific stuff having to do with the
24 ** OpenGL refresh.  When a port is being made the following functions
25 ** must be implemented by the port:
26 **
27 ** GLimp_EndFrame
28 ** GLimp_Init
29 ** GLimp_Shutdown
30 ** GLimp_SwitchFullscreen
31 **
32 */
33 
34 #include <termios.h>
35 #include <sys/ioctl.h>
36 #include <sys/stat.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <unistd.h>
40 #include <signal.h>
41 #include <dlfcn.h>
42 #ifdef Joystick
43 #include <fcntl.h>
44 #endif
45 #include "../ref_gl/gl_local.h"
46 
47 #include "../client/keys.h"
48 
49 #include "../linux/rw_linux.h"
50 #include "../linux/glw_linux.h"
51 
52 #include <X11/Xlib.h>
53 #include <X11/Xatom.h>
54 #include <X11/keysym.h>
55 #include <X11/cursorfont.h>
56 
57 #include <X11/extensions/xf86dga.h>
58 #include <X11/extensions/xf86vmode.h>
59 #ifdef Joystick
60 # if defined (__linux__)
61 #include <linux/joystick.h>
62 # elif defined (__FreeBSD__)
63 #include <sys/joystick.h>
64 # endif
65 #include <glob.h>
66 #endif
67 #include <GL/glx.h>
68 
69 glwstate_t glw_state;
70 
71 static Display *dpy = NULL;
72 static int scrnum;
73 static Window win;
74 static GLXContext ctx = NULL;
75 static Atom wmDeleteWindow;
76 
77 #ifdef Joystick
78 static int joy_fd;
79 #endif
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 | StructureNotifyMask )
85 
86 //GLX Functions
87 static XVisualInfo * (*qglXChooseVisual)( Display *dpy, int screen, int *attribList );
88 static GLXContext (*qglXCreateContext)( Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct );
89 static void (*qglXDestroyContext)( Display *dpy, GLXContext ctx );
90 static Bool (*qglXMakeCurrent)( Display *dpy, GLXDrawable drawable, GLXContext ctx);
91 static void (*qglXCopyContext)( Display *dpy, GLXContext src, GLXContext dst, GLuint mask );
92 static void (*qglXSwapBuffers)( Display *dpy, GLXDrawable drawable );
93 static int (*qglXGetConfig) (Display *dpy, XVisualInfo *vis, int attrib, int *value);
94 
95 
96 
97 
98 /*****************************************************************************/
99 /* MOUSE                                                                     */
100 /*****************************************************************************/
101 
102 // this is inside the renderer shared lib, so these are called from vid_so
103 
104 int mx, my, mouse_buttonstate;
105 int win_x, win_y;
106 
107 static cvar_t	*in_dgamouse;
108 
109 static cvar_t	*r_fakeFullscreen;
110 
111 static XF86VidModeModeInfo **vidmodes;
112 static int num_vidmodes;
113 static qboolean vidmode_active = false;
114 static XF86VidModeGamma oldgamma;
115 
116 static qboolean mouse_active = false;
117 static qboolean dgamouse = false;
118 static qboolean vidmode_ext = false;
119 
120 /* stencilbuffer shadows */
121 qboolean have_stencil = false;
122 
123 static Time myxtime;
124 
CreateNullCursor(Display * display,Window root)125 static Cursor CreateNullCursor(Display *display, Window root)
126 {
127     Pixmap cursormask;
128     XGCValues xgc;
129     GC gc;
130     XColor dummycolour;
131     Cursor cursor;
132 
133     cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/);
134     xgc.function = GXclear;
135     gc =  XCreateGC(display, cursormask, GCFunction, &xgc);
136     XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
137     dummycolour.pixel = 0;
138     dummycolour.red = 0;
139     dummycolour.flags = 04;
140     cursor = XCreatePixmapCursor(display, cursormask, cursormask,
141           &dummycolour,&dummycolour, 0,0);
142     XFreePixmap(display,cursormask);
143     XFreeGC(display,gc);
144     return cursor;
145 }
146 
install_grabs(void)147 static void install_grabs(void)
148 {
149 
150 // inviso cursor
151   XDefineCursor(dpy, win, CreateNullCursor(dpy, win));
152 
153   XGrabPointer(dpy, win,
154 	       True,
155 	       0,
156 	       GrabModeAsync, GrabModeAsync,
157 	       win,
158 	       None,
159 	       CurrentTime);
160 
161   if (in_dgamouse->value) {
162     int MajorVersion, MinorVersion;
163 
164     if (!XF86DGAQueryVersion(dpy, &MajorVersion, &MinorVersion)) {
165       // unable to query, probalby not supported
166       ri.Con_Printf( PRINT_ALL, "Failed to detect XF86DGA Mouse\n" );
167       ri.Cvar_Set( "in_dgamouse", "0" );
168     } else {
169       dgamouse = true;
170       XF86DGADirectVideo(dpy, DefaultScreen(dpy), XF86DGADirectMouse);
171       XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0);
172     }
173   } else {
174     XWarpPointer(dpy, None, win,
175 		 0, 0, 0, 0,
176 		 vid.width / 2, vid.height / 2);
177   }
178 
179   XGrabKeyboard(dpy, win,
180 		False,
181 		GrabModeAsync, GrabModeAsync,
182 		CurrentTime);
183 
184   mouse_active = true;
185 
186   //	XSync(dpy, True);
187 }
188 
uninstall_grabs(void)189 static void uninstall_grabs(void)
190 {
191   if (!dpy || !win)
192     return;
193 
194   if (dgamouse) {
195     dgamouse = false;
196     XF86DGADirectVideo(dpy, DefaultScreen(dpy), 0);
197   }
198 
199   XUngrabPointer(dpy, CurrentTime);
200   XUngrabKeyboard(dpy, CurrentTime);
201 
202   // inviso cursor
203   XUndefineCursor(dpy, win);
204 
205   mouse_active = false;
206 }
207 
IN_DeactivateMouse(void)208 static void IN_DeactivateMouse( void )
209 {
210   //if (!mouse_avail || !dpy || !win)
211   //return;
212 
213   if (mouse_active) {
214     uninstall_grabs();
215     mouse_active = false;
216   }
217 }
218 
IN_ActivateMouse(void)219 static void IN_ActivateMouse( void )
220 {
221   //if (!mouse_avail || !dpy || !win)
222   //return;
223 
224   if (!mouse_active) {
225     mx = my = 0; // don't spazz
226     install_grabs();
227     mouse_active = true;
228   }
229 }
230 
getMouse(int * x,int * y,int * state)231 void getMouse(int *x, int *y, int *state) {
232   *x = mx;
233   *y = my;
234   *state = mouse_buttonstate;
235 }
236 
doneMouse()237 void doneMouse() {
238   mx = my = 0;
239 }
240 
RW_IN_PlatformInit()241 void RW_IN_PlatformInit()
242 {
243 
244   in_dgamouse = ri.Cvar_Get ("in_dgamouse", "0", CVAR_ARCHIVE);
245 }
246 
RW_IN_Activate(qboolean active)247 void RW_IN_Activate(qboolean active)
248 {
249   if (active || vidmode_active)
250     IN_ActivateMouse();
251   else
252     IN_DeactivateMouse ();
253 }
254 
255 /*****************************************************************************/
256 /* KEYBOARD                                                                  */
257 /*****************************************************************************/
258 
XLateKey(XKeyEvent * ev)259 static int XLateKey(XKeyEvent *ev)
260 {
261 
262   int key;
263   char buf[64];
264   KeySym keysym;
265 
266   key = 0;
267 
268   XLookupString(ev, buf, sizeof buf, &keysym, 0);
269 
270   switch(keysym)
271     {
272     case XK_KP_Page_Up:	 key = K_KP_PGUP; break;
273     case XK_Page_Up:	 key = K_PGUP; break;
274 
275     case XK_KP_Page_Down: key = K_KP_PGDN; break;
276     case XK_Page_Down:	 key = K_PGDN; break;
277 
278     case XK_KP_Home: key = K_KP_HOME; break;
279     case XK_Home:	 key = K_HOME; break;
280 
281     case XK_KP_End:  key = K_KP_END; break;
282     case XK_End:	 key = K_END; break;
283 
284     case XK_KP_Left: key = K_KP_LEFTARROW; break;
285     case XK_Left:	 key = K_LEFTARROW; break;
286 
287     case XK_KP_Right: key = K_KP_RIGHTARROW; break;
288     case XK_Right:	key = K_RIGHTARROW;		break;
289 
290     case XK_KP_Down: key = K_KP_DOWNARROW; break;
291     case XK_Down:	 key = K_DOWNARROW; break;
292 
293     case XK_KP_Up:   key = K_KP_UPARROW; break;
294     case XK_Up:		 key = K_UPARROW;	 break;
295 
296     case XK_Escape: key = K_ESCAPE;		break;
297 
298     case XK_KP_Enter: key = K_KP_ENTER;	break;
299     case XK_Return: key = K_ENTER;		 break;
300 
301     case XK_Tab:		key = K_TAB;			 break;
302 
303     case XK_F1:		 key = K_F1;				break;
304 
305     case XK_F2:		 key = K_F2;				break;
306 
307     case XK_F3:		 key = K_F3;				break;
308 
309     case XK_F4:		 key = K_F4;				break;
310 
311     case XK_F5:		 key = K_F5;				break;
312 
313     case XK_F6:		 key = K_F6;				break;
314 
315     case XK_F7:		 key = K_F7;				break;
316 
317     case XK_F8:		 key = K_F8;				break;
318 
319     case XK_F9:		 key = K_F9;				break;
320 
321     case XK_F10:		key = K_F10;			 break;
322 
323     case XK_F11:		key = K_F11;			 break;
324 
325     case XK_F12:		key = K_F12;			 break;
326 
327     case XK_BackSpace: key = K_BACKSPACE; break;
328 
329     case XK_KP_Delete: key = K_KP_DEL; break;
330     case XK_Delete: key = K_DEL; break;
331 
332     case XK_Pause:	key = K_PAUSE;		 break;
333 
334     case XK_Shift_L:
335     case XK_Shift_R:	key = K_SHIFT;		break;
336 
337 		case XK_Execute:
338 		case XK_Control_L:
339 		case XK_Control_R:	key = K_CTRL;		 break;
340 
341 		case XK_Alt_L:
342 		case XK_Meta_L:
343 		case XK_Alt_R:
344 		case XK_Meta_R: key = K_ALT;			break;
345 
346 		case XK_KP_Begin: key = K_KP_5;	break;
347 
348 		case XK_Insert:key = K_INS; break;
349 		case XK_KP_Insert: key = K_KP_INS; break;
350 
351 		case XK_KP_Multiply: key = '*'; break;
352 		case XK_KP_Add:  key = K_KP_PLUS; break;
353 		case XK_KP_Subtract: key = K_KP_MINUS; break;
354 		case XK_KP_Divide: key = K_KP_SLASH; break;
355 
356 #if 0
357 		case 0x021: key = '1';break;/* [!] */
358 		case 0x040: key = '2';break;/* [@] */
359 		case 0x023: key = '3';break;/* [#] */
360 		case 0x024: key = '4';break;/* [$] */
361 		case 0x025: key = '5';break;/* [%] */
362 		case 0x05e: key = '6';break;/* [^] */
363 		case 0x026: key = '7';break;/* [&] */
364 		case 0x02a: key = '8';break;/* [*] */
365 		case 0x028: key = '9';;break;/* [(] */
366 		case 0x029: key = '0';break;/* [)] */
367 		case 0x05f: key = '-';break;/* [_] */
368 		case 0x02b: key = '=';break;/* [+] */
369 		case 0x07c: key = '\'';break;/* [|] */
370 		case 0x07d: key = '[';break;/* [}] */
371 		case 0x07b: key = ']';break;/* [{] */
372 		case 0x022: key = '\'';break;/* ["] */
373 		case 0x03a: key = ';';break;/* [:] */
374 		case 0x03f: key = '/';break;/* [?] */
375 		case 0x03e: key = '.';break;/* [>] */
376 		case 0x03c: key = ',';break;/* [<] */
377 #endif
378 
379 		default:
380 			key = *(unsigned char*)buf;
381 			if (key >= 'A' && key <= 'Z')
382 				key = key - 'A' + 'a';
383 			if (key >= 1 && key <= 26) /* ctrl+alpha */
384 				key = key + 'a' - 1;
385 			break;
386 	}
387 
388 	return key;
389 }
390 
391 
392 /* Check to see if this is a repeated key.
393    (idea shamelessly lifted from SDL who...)
394    (idea shamelessly lifted from GII -- thanks guys! :)
395    This has bugs if two keys are being pressed simultaneously and the
396    events start getting interleaved.
397 */
X11_KeyRepeat(Display * display,XEvent * event)398 int X11_KeyRepeat(Display *display, XEvent *event)
399 {
400 	XEvent peekevent;
401 	int repeated;
402 
403 	repeated = 0;
404 	if ( XPending(display) ) {
405 		XPeekEvent(display, &peekevent);
406 		if ( (peekevent.type == KeyPress) &&
407 		     (peekevent.xkey.keycode == event->xkey.keycode) &&
408 		     ((peekevent.xkey.time-event->xkey.time) < 2) ) {
409 		  repeated = 1;
410 		  XNextEvent(display, &peekevent);
411 		}
412 	}
413 	return(repeated);
414 }
415 
416 
HandleEvents(void)417 static void HandleEvents(void)
418 {
419   XEvent event;
420   int b;
421   qboolean dowarp = false;
422   int mwx = vid.width/2;
423   int mwy = vid.height/2;
424   in_state_t *in_state = getState();
425   if (!dpy)
426     return;
427 
428   while (XPending(dpy)) {
429     //ri.Con_Printf(PRINT_ALL,"Bar");
430     XNextEvent(dpy, &event);
431     mx = my = 0;
432     switch(event.type) {
433     case KeyPress:
434       myxtime = event.xkey.time;
435       if (in_state && in_state->Key_Event_fp)
436 	in_state->Key_Event_fp (XLateKey(&event.xkey), true);
437       break;
438     case KeyRelease:
439       if (! X11_KeyRepeat(dpy, &event)) {
440 	if (in_state && in_state->Key_Event_fp)
441 	  in_state->Key_Event_fp (XLateKey(&event.xkey), false);
442       }
443       break;
444     case MotionNotify:
445       if (mouse_active) {
446 	if (dgamouse) {
447 	  mx += (event.xmotion.x + win_x) * 2;
448 	  my += (event.xmotion.y + win_y) * 2;
449 	}
450 	else
451 	  {
452 	    mx -= ((int)event.xmotion.x - mwx) * 2;
453 	    my -= ((int)event.xmotion.y - mwy) * 2;
454 	    mwx = event.xmotion.x;
455 	    mwy = event.xmotion.y;
456 
457 	    if (mx || my)
458 	      dowarp = true;
459 	  }
460       }
461       break;
462 
463 
464     case ButtonPress:
465       myxtime = event.xbutton.time;
466 
467       b=-1;
468       if (event.xbutton.button == 1)
469 	b = 0;
470       else if (event.xbutton.button == 2)
471 	b = 2;
472       else if (event.xbutton.button == 3)
473 	b = 1;
474       else if (event.xbutton.button == 4)
475 	in_state->Key_Event_fp (K_MWHEELUP, 1);
476       else if (event.xbutton.button == 5)
477 	in_state->Key_Event_fp (K_MWHEELDOWN, 1);
478       if (b>=0 && in_state && in_state->Key_Event_fp)
479 	in_state->Key_Event_fp (K_MOUSE1 + b, true);
480       if (b>=0)
481 	mouse_buttonstate |= 1<<b;
482       break;
483 
484     case ButtonRelease:
485       b=-1;
486       if (event.xbutton.button == 1)
487 	b = 0;
488       else if (event.xbutton.button == 2)
489 	b = 2;
490       else if (event.xbutton.button == 3)
491 	b = 1;
492       else if (event.xbutton.button == 4)
493 	in_state->Key_Event_fp (K_MWHEELUP, 0);
494       else if (event.xbutton.button == 5)
495 	in_state->Key_Event_fp (K_MWHEELDOWN, 0);
496       if (b>=0 && in_state && in_state->Key_Event_fp)
497 	in_state->Key_Event_fp (K_MOUSE1 + b, false);
498       if (b>=0)
499 	mouse_buttonstate &= ~(1<<b);
500       break;
501 
502     case CreateNotify :
503       win_x = event.xcreatewindow.x;
504       win_y = event.xcreatewindow.y;
505       break;
506 
507     case ConfigureNotify :
508       win_x = event.xconfigure.x;
509       win_y = event.xconfigure.y;
510       break;
511 
512     case ClientMessage:
513       if (event.xclient.data.l[0] == wmDeleteWindow)
514 	ri.Cmd_ExecuteText(EXEC_NOW, "quit");
515       break;
516     }
517   }
518 
519   if (dowarp) {
520     /* move the mouse to the window center again */
521     XWarpPointer(dpy,None,win,0,0,0,0, vid.width/2,vid.height/2);
522   }
523 }
524 
525 Key_Event_fp_t Key_Event_fp;
526 
KBD_Init(Key_Event_fp_t fp)527 void KBD_Init(Key_Event_fp_t fp)
528 {
529   Key_Event_fp = fp;
530 }
531 
KBD_Update(void)532 void KBD_Update(void)
533 {
534   // get events from x server
535   HandleEvents();
536 }
537 
KBD_Close(void)538 void KBD_Close(void)
539 {
540 }
541 
542 /*****************************************************************************/
543 
RW_Sys_GetClipboardData()544 char *RW_Sys_GetClipboardData()
545 {
546 	Window sowner;
547 	Atom type, property;
548 	unsigned long len, bytes_left, tmp;
549 	unsigned char *data;
550 	int format, result;
551 	char *ret = NULL;
552 
553 	sowner = XGetSelectionOwner(dpy, XA_PRIMARY);
554 
555 	if (sowner != None) {
556 		property = XInternAtom(dpy,
557 				       "GETCLIPBOARDDATA_PROP",
558 				       False);
559 
560 		XConvertSelection(dpy,
561 				  XA_PRIMARY, XA_STRING,
562 				  property, win, myxtime); /* myxtime == time of last X event */
563 		XFlush(dpy);
564 
565 		XGetWindowProperty(dpy,
566 				   win, property,
567 				   0, 0, False, AnyPropertyType,
568 				   &type, &format, &len,
569 				   &bytes_left, &data);
570 		if (bytes_left > 0) {
571 			result =
572 			XGetWindowProperty(dpy,
573 					   win, property,
574 					   0, bytes_left, True, AnyPropertyType,
575 					   &type, &format, &len,
576 					   &tmp, &data);
577 			if (result == Success) {
578 				ret = strdup(data);
579 			}
580 			XFree(data);
581 		}
582 	}
583 	return ret;
584 }
585 
586 /*****************************************************************************/
587 
588 qboolean GLimp_InitGL (void);
589 
signal_handler(int sig)590 static void signal_handler(int sig)
591 {
592 	printf("Received signal %d, exiting...\n", sig);
593 	GLimp_Shutdown();
594 	_exit(0);
595 }
596 
InitSig(void)597 static void InitSig(void)
598 {
599 	signal(SIGHUP, signal_handler);
600 	signal(SIGQUIT, signal_handler);
601 	signal(SIGILL, signal_handler);
602 	signal(SIGTRAP, signal_handler);
603 	signal(SIGIOT, signal_handler);
604 	signal(SIGBUS, signal_handler);
605 	signal(SIGFPE, signal_handler);
606 	signal(SIGSEGV, signal_handler);
607 	signal(SIGTERM, signal_handler);
608 }
609 
610 /*
611 ** GLimp_SetMode
612 */
GLimp_SetMode(int * pwidth,int * pheight,int mode,qboolean fullscreen)613 int GLimp_SetMode( int *pwidth, int *pheight, int mode, qboolean fullscreen )
614 {
615 	int width, height;
616 	int attrib[] = {
617 		GLX_RGBA,
618 		GLX_DOUBLEBUFFER,
619 		GLX_RED_SIZE, 1,
620 		GLX_GREEN_SIZE, 1,
621 		GLX_BLUE_SIZE, 1,
622 		GLX_DEPTH_SIZE, 1,
623 		GLX_STENCIL_SIZE, 1,
624 		None
625 	};
626 	int attrib_nostencil[] = {
627 		GLX_RGBA,
628 		GLX_DOUBLEBUFFER,
629 		GLX_RED_SIZE, 1,
630 		GLX_GREEN_SIZE, 1,
631 		GLX_BLUE_SIZE, 1,
632 		GLX_DEPTH_SIZE, 1,
633 		None
634 	};
635 
636 	Window root;
637 	XVisualInfo *visinfo;
638 	XSetWindowAttributes attr;
639 	XSizeHints *sizehints;
640 	XWMHints *wmhints;
641 	unsigned long mask;
642 	int MajorVersion, MinorVersion;
643 	int actualWidth, actualHeight;
644 	int i;
645 
646 	r_fakeFullscreen = ri.Cvar_Get( "r_fakeFullscreen", "0", CVAR_ARCHIVE);
647 
648 	ri.Con_Printf( PRINT_ALL, "Initializing OpenGL display\n");
649 
650 	if (fullscreen)
651 		ri.Con_Printf (PRINT_ALL, "...setting fullscreen mode %d:", mode );
652 	else
653 		ri.Con_Printf (PRINT_ALL, "...setting mode %d:", mode );
654 
655 	if ( !ri.Vid_GetModeInfo( &width, &height, mode ) )
656 	{
657 		ri.Con_Printf( PRINT_ALL, " invalid mode\n" );
658 		return rserr_invalid_mode;
659 	}
660 
661 	ri.Con_Printf( PRINT_ALL, " %d %d\n", width, height );
662 
663 	// destroy the existing window
664 	GLimp_Shutdown ();
665 
666 #if 0 // this breaks getenv()? - sbf
667 	// Mesa VooDoo hacks
668 	if (fullscreen)
669 		putenv("MESA_GLX_FX=fullscreen");
670 	else
671 		putenv("MESA_GLX_FX=window");
672 #endif
673 
674 	if (!(dpy = XOpenDisplay(NULL))) {
675 		fprintf(stderr, "Error couldn't open the X display\n");
676 		return rserr_invalid_mode;
677 	}
678 
679 	scrnum = DefaultScreen(dpy);
680 	root = RootWindow(dpy, scrnum);
681 
682 	// Get video mode list
683 	MajorVersion = MinorVersion = 0;
684 	if (!XF86VidModeQueryVersion(dpy, &MajorVersion, &MinorVersion)) {
685 		vidmode_ext = false;
686 	} else {
687 		ri.Con_Printf(PRINT_ALL, "Using XFree86-VidModeExtension Version %d.%d\n",
688 			MajorVersion, MinorVersion);
689 		vidmode_ext = true;
690 	}
691 
692 	visinfo = qglXChooseVisual(dpy, scrnum, attrib);
693 	if (!visinfo) {
694 		fprintf(stderr, "W: couldn't get an RGBA, DOUBLEBUFFER, DEPTH, STENCIL visual\n");
695 		visinfo = qglXChooseVisual(dpy, scrnum, attrib_nostencil);
696 		if (!visinfo) {
697 			fprintf(stderr, "E: couldn't get an RGBA, DOUBLEBUFFER, DEPTH visual\n");
698 			return rserr_invalid_mode;
699 		}
700 	}
701 
702 	gl_state.hwgamma = false;
703 
704 	/* do some pantsness */
705 	if ( qglXGetConfig )
706 	{
707 		int red_bits, blue_bits, green_bits, depth_bits, alpha_bits;
708 
709 		qglXGetConfig(dpy, visinfo, GLX_RED_SIZE, &red_bits);
710 		qglXGetConfig(dpy, visinfo, GLX_BLUE_SIZE, &blue_bits);
711 		qglXGetConfig(dpy, visinfo, GLX_GREEN_SIZE, &green_bits);
712 		qglXGetConfig(dpy, visinfo, GLX_DEPTH_SIZE, &depth_bits);
713 		qglXGetConfig(dpy, visinfo, GLX_ALPHA_SIZE, &alpha_bits);
714 
715 		ri.Con_Printf(PRINT_ALL, "I: got %d bits of red\n", red_bits);
716 		ri.Con_Printf(PRINT_ALL, "I: got %d bits of blue\n", blue_bits);
717 		ri.Con_Printf(PRINT_ALL, "I: got %d bits of green\n", green_bits);
718 		ri.Con_Printf(PRINT_ALL, "I: got %d bits of depth\n", depth_bits);
719 		ri.Con_Printf(PRINT_ALL, "I: got %d bits of alpha\n", alpha_bits);
720 	}
721 
722 	/* stencilbuffer shadows */
723 	if ( qglXGetConfig )
724 	{
725 		int stencil_bits;
726 
727 		if (!qglXGetConfig(dpy, visinfo, GLX_STENCIL_SIZE, &stencil_bits)) {
728 			ri.Con_Printf(PRINT_ALL, "I: got %d bits of stencil\n", stencil_bits);
729 			if (stencil_bits >= 1) {
730 				have_stencil = true;
731 			}
732 		}
733 	} else {
734 		have_stencil = true;
735 	}
736 
737 	if (vidmode_ext) {
738 		int best_fit, best_dist, dist, x, y;
739 
740 		XF86VidModeGetAllModeLines(dpy, scrnum, &num_vidmodes, &vidmodes);
741 
742 		// Are we going fullscreen?  If so, let's change video mode
743 		if (fullscreen && !r_fakeFullscreen->value) {
744 			best_dist = 9999999;
745 			best_fit = -1;
746 
747 			for (i = 0; i < num_vidmodes; i++) {
748 				if (width > vidmodes[i]->hdisplay ||
749 					height > vidmodes[i]->vdisplay)
750 					continue;
751 
752 				x = width - vidmodes[i]->hdisplay;
753 				y = height - vidmodes[i]->vdisplay;
754 				dist = (x * x) + (y * y);
755 				if (dist < best_dist) {
756 					best_dist = dist;
757 					best_fit = i;
758 				}
759 			}
760 
761 			if (best_fit != -1) {
762 				actualWidth = vidmodes[best_fit]->hdisplay;
763 				actualHeight = vidmodes[best_fit]->vdisplay;
764 
765 				// change to the mode
766 				XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]);
767 				vidmode_active = true;
768 
769 				if (XF86VidModeGetGamma(dpy, scrnum, &oldgamma)) {
770 					gl_state.hwgamma = true;
771 					/* We can not reliably detect hardware gamma
772 					   changes across software gamma calls, which
773 					   can reset the flag, so change it anyway */
774 					vid_gamma->modified = true;
775 					ri.Con_Printf( PRINT_ALL, "Using hardware gamma\n");
776 				}
777 
778 				// Move the viewport to top left
779 				XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
780 			} else
781 				fullscreen = 0;
782 		}
783 	}
784 
785 	/* window attributes */
786 	attr.background_pixel = 0;
787 	attr.border_pixel = 0;
788 	attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
789 	attr.event_mask = X_MASK;
790 	if (vidmode_active) {
791 		mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore |
792 			CWEventMask | CWOverrideRedirect;
793 		attr.override_redirect = True;
794 		attr.backing_store = NotUseful;
795 		attr.save_under = False;
796 	} else
797 		mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
798 
799 	win = XCreateWindow(dpy, root, 0, 0, width, height,
800 						0, visinfo->depth, InputOutput,
801 						visinfo->visual, mask, &attr);
802 
803 	sizehints = XAllocSizeHints();
804 	if (sizehints) {
805 		sizehints->min_width = width;
806 		sizehints->min_height = height;
807 		sizehints->max_width = width;
808 		sizehints->max_height = height;
809 		sizehints->base_width = width;
810 		sizehints->base_height = vid.height;
811 
812 		sizehints->flags = PMinSize | PMaxSize | PBaseSize;
813 	}
814 
815 	wmhints = XAllocWMHints();
816 	if (wmhints) {
817 		#include "q2icon.xbm"
818 
819 		Pixmap icon_pixmap, icon_mask;
820 		unsigned long fg, bg;
821 		int i;
822 
823 		fg = BlackPixel(dpy, visinfo->screen);
824 		bg = WhitePixel(dpy, visinfo->screen);
825 		icon_pixmap = XCreatePixmapFromBitmapData(dpy, win, (char *)q2icon_bits, q2icon_width, q2icon_height, fg, bg, visinfo->depth);
826 		for (i = 0; i < sizeof(q2icon_bits); i++)
827 			q2icon_bits[i] = ~q2icon_bits[i];
828 		icon_mask = XCreatePixmapFromBitmapData(dpy, win, (char *)q2icon_bits, q2icon_width, q2icon_height, bg, fg, visinfo->depth);
829 
830 		wmhints->flags = IconPixmapHint|IconMaskHint;
831 		wmhints->icon_pixmap = icon_pixmap;
832 		wmhints->icon_mask = icon_mask;
833 	}
834 
835 	XSetWMProperties(dpy, win, NULL, NULL, NULL, 0,
836 			sizehints, wmhints, None);
837 	if (sizehints)
838 		XFree(sizehints);
839 	if (wmhints)
840 		XFree(wmhints);
841 
842 	XStoreName(dpy, win, "Quake II");
843 
844 	wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
845 	XSetWMProtocols(dpy, win, &wmDeleteWindow, 1);
846 
847 	XMapWindow(dpy, win);
848 
849 	if (vidmode_active) {
850 		XMoveWindow(dpy, win, 0, 0);
851 		XRaiseWindow(dpy, win);
852 		XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0);
853 		XFlush(dpy);
854 		// Move the viewport to top left
855 		XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
856 	}
857 
858 	XFlush(dpy);
859 
860 	ctx = qglXCreateContext(dpy, visinfo, NULL, True);
861 
862 	qglXMakeCurrent(dpy, win, ctx);
863 
864 	*pwidth = width;
865 	*pheight = height;
866 
867 	// let the sound and input subsystems know about the new window
868 	ri.Vid_NewWindow (width, height);
869 
870 	qglXMakeCurrent(dpy, win, ctx);
871 
872 	return rserr_ok;
873 }
874 
875 /*
876 ** GLimp_Shutdown
877 **
878 ** This routine does all OS specific shutdown procedures for the OpenGL
879 ** subsystem.  Under OpenGL this means NULLing out the current DC and
880 ** HGLRC, deleting the rendering context, and releasing the DC acquired
881 ** for the window.  The state structure is also nulled out.
882 **
883 */
GLimp_Shutdown(void)884 void GLimp_Shutdown( void )
885 {
886 	uninstall_grabs();
887 	mouse_active = false;
888 	dgamouse = false;
889 
890 	if (dpy) {
891 		if (ctx)
892 			qglXDestroyContext(dpy, ctx);
893 		if (win)
894 			XDestroyWindow(dpy, win);
895 		if (gl_state.hwgamma) {
896 			XF86VidModeSetGamma(dpy, scrnum, &oldgamma);
897 			/* The gamma has changed, but SetMode will change it
898 			   anyway, so why bother?
899 			vid_gamma->modified = true; */
900 		}
901 		if (vidmode_active)
902 			XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[0]);
903 		XUngrabKeyboard(dpy, CurrentTime);
904 		XCloseDisplay(dpy);
905 	}
906 	ctx = NULL;
907 	dpy = NULL;
908 	win = 0;
909 	ctx = NULL;
910 /*
911 	qglXChooseVisual             = NULL;
912 	qglXCreateContext            = NULL;
913 	qglXDestroyContext           = NULL;
914 	qglXMakeCurrent              = NULL;
915 	qglXCopyContext              = NULL;
916 	qglXSwapBuffers              = NULL;
917 */
918 }
919 
920 /*
921 ** GLimp_Init
922 **
923 ** This routine is responsible for initializing the OS specific portions
924 ** of OpenGL.
925 */
GLimp_Init(void * hinstance,void * wndproc)926 int GLimp_Init( void *hinstance, void *wndproc )
927 {
928 	InitSig();
929 
930 	if ( glw_state.OpenGLLib) {
931 		#define GPA( a ) dlsym( glw_state.OpenGLLib, a )
932 
933 		qglXChooseVisual             =  GPA("glXChooseVisual");
934 		qglXCreateContext            =  GPA("glXCreateContext");
935 		qglXDestroyContext           =  GPA("glXDestroyContext");
936 		qglXMakeCurrent              =  GPA("glXMakeCurrent");
937 		qglXCopyContext              =  GPA("glXCopyContext");
938 		qglXSwapBuffers              =  GPA("glXSwapBuffers");
939 		qglXGetConfig                =  GPA("glXGetConfig");
940 
941 		return true;
942 	}
943 
944 	return false;
945 }
946 
947 /*
948 ** GLimp_BeginFrame
949 */
GLimp_BeginFrame(float camera_seperation)950 void GLimp_BeginFrame( float camera_seperation )
951 {
952 }
953 
954 /*
955 ** GLimp_EndFrame
956 **
957 ** Responsible for doing a swapbuffers and possibly for other stuff
958 ** as yet to be determined.  Probably better not to make this a GLimp
959 ** function and instead do a call to GLimp_SwapBuffers.
960 */
GLimp_EndFrame(void)961 void GLimp_EndFrame (void)
962 {
963 	qglFlush();
964 	qglXSwapBuffers(dpy, win);
965 }
966 
967 /*
968 ** UpdateHardwareGamma
969 **
970 ** We are using gamma relative to the desktop, so that we can share it
971 ** with software renderer and don't require to change desktop gamma
972 ** to match hardware gamma image brightness. It seems that Quake 3 is
973 ** using the opposite approach, but it has no software renderer after
974 ** all.
975 */
UpdateHardwareGamma()976 void UpdateHardwareGamma()
977 {
978 	XF86VidModeGamma gamma;
979 	float g;
980 
981 	g = (1.3 - vid_gamma->value + 1);
982 	g = (g>1 ? g : 1);
983 	gamma.red = oldgamma.red * g;
984 	gamma.green = oldgamma.green * g;
985 	gamma.blue = oldgamma.blue * g;
986 	XF86VidModeSetGamma(dpy, scrnum, &gamma);
987 }
988 
989 /*
990 ** GLimp_AppActivate
991 */
GLimp_AppActivate(qboolean active)992 void GLimp_AppActivate( qboolean active )
993 {
994 }
995 
Fake_glColorTableEXT(GLenum target,GLenum internalformat,GLsizei width,GLenum format,GLenum type,const GLvoid * table)996 void Fake_glColorTableEXT( GLenum target, GLenum internalformat,
997                              GLsizei width, GLenum format, GLenum type,
998                              const GLvoid *table )
999 {
1000 	byte temptable[256][4];
1001 	byte *intbl;
1002 	int i;
1003 
1004 	for (intbl = (byte *)table, i = 0; i < 256; i++) {
1005 		temptable[i][2] = *intbl++;
1006 		temptable[i][1] = *intbl++;
1007 		temptable[i][0] = *intbl++;
1008 		temptable[i][3] = 255;
1009 	}
1010 	qgl3DfxSetPaletteEXT((GLuint *)temptable);
1011 }
1012 
1013 
1014 #ifdef Joystick
OpenJoystick(cvar_t * joy_dev)1015 qboolean OpenJoystick(cvar_t *joy_dev) {
1016   int i, err;
1017   glob_t pglob;
1018   struct js_event e;
1019 
1020   err = glob(joy_dev->string, 0, NULL, &pglob);
1021 
1022   if (err) {
1023     switch (err) {
1024     case GLOB_NOSPACE:
1025       ri.Con_Printf(PRINT_ALL, "Error, out of memory while looking for joysticks\n");
1026       break;
1027     case GLOB_NOMATCH:
1028       ri.Con_Printf(PRINT_ALL, "No joysticks found\n");
1029       break;
1030     default:
1031       ri.Con_Printf(PRINT_ALL, "Error #%d while looking for joysticks\n",err);
1032     }
1033     return false;
1034   }
1035 
1036   for (i=0;i<pglob.gl_pathc;i++) {
1037     ri.Con_Printf(PRINT_ALL, "Trying joystick dev %s\n", pglob.gl_pathv[i]);
1038     joy_fd = open (pglob.gl_pathv[i], O_RDONLY | O_NONBLOCK);
1039     if (joy_fd == -1) {
1040       ri.Con_Printf(PRINT_ALL, "Error opening joystick dev %s\n",
1041 		    pglob.gl_pathv[i]);
1042       return false;
1043     }
1044     else {
1045       while (read(joy_fd, &e, sizeof(struct js_event))!=-1 &&
1046 	     (e.type & JS_EVENT_INIT))
1047 	ri.Con_Printf(PRINT_ALL, "Read init event\n");
1048       ri.Con_Printf(PRINT_ALL, "Using joystick dev %s\n", pglob.gl_pathv[i]);
1049       return true;
1050     }
1051   }
1052   globfree(&pglob);
1053   return false;
1054 }
1055 
PlatformJoyCommands(int * axis_vals,int * axis_map)1056 void PlatformJoyCommands(int *axis_vals, int *axis_map) {
1057   struct js_event e;
1058   int key_index;
1059   in_state_t *in_state = getState();
1060 
1061   while (read(joy_fd, &e, sizeof(struct js_event))!=-1) {
1062     if (JS_EVENT_BUTTON & e.type) {
1063       key_index = (e.number < 4) ? K_JOY1 : K_AUX1;
1064       if (e.value) {
1065 	in_state->Key_Event_fp (key_index + e.number, true);
1066       }
1067       else {
1068 	in_state->Key_Event_fp (key_index + e.number, false);
1069       }
1070     }
1071     else if (JS_EVENT_AXIS & e.type) {
1072       axis_vals[axis_map[e.number]] = e.value;
1073     }
1074   }
1075 }
1076 
CloseJoystick(void)1077 qboolean CloseJoystick(void) {
1078   if (close(joy_fd))
1079     ri.Con_Printf(PRINT_ALL, "Error, Problem closing joystick.");
1080   return true;
1081 }
1082 #endif
1083