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/Xxf86dga.h>
58 #include <X11/extensions/xf86vmode.h>
59 #ifdef Joystick
60 # if defined (__linux__)
61 #include <linux/joystick.h>
62 # elif defined (__DragonFly__)
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     switch(event.type) {
432     case KeyPress:
433       myxtime = event.xkey.time;
434       if (in_state && in_state->Key_Event_fp)
435 	in_state->Key_Event_fp (XLateKey(&event.xkey), true);
436       break;
437     case KeyRelease:
438       if (! X11_KeyRepeat(dpy, &event)) {
439 	if (in_state && in_state->Key_Event_fp)
440 	  in_state->Key_Event_fp (XLateKey(&event.xkey), false);
441       }
442       break;
443     case MotionNotify:
444       if (mouse_active) {
445 	if (dgamouse) {
446 	  mx += (event.xmotion.x + win_x) * 2;
447 	  my += (event.xmotion.y + win_y) * 2;
448 	}
449 	else
450 	  {
451 	    mx += ((int)event.xmotion.x - mwx) * 2;
452 	    my += ((int)event.xmotion.y - mwy) * 2;
453 
454 	    if (mx || my)
455 	      dowarp = true;
456 	  }
457       }
458       break;
459 
460 
461     case ButtonPress:
462       myxtime = event.xbutton.time;
463 
464       b=-1;
465       if (event.xbutton.button == 1)
466 	b = 0;
467       else if (event.xbutton.button == 2)
468 	b = 2;
469       else if (event.xbutton.button == 3)
470 	b = 1;
471       else if (event.xbutton.button == 4)
472 	in_state->Key_Event_fp (K_MWHEELUP, 1);
473       else if (event.xbutton.button == 5)
474 	in_state->Key_Event_fp (K_MWHEELDOWN, 1);
475       else if (event.xbutton.button == 6)
476 	in_state->Key_Event_fp (K_MOUSE4, 1);
477       else if (event.xbutton.button == 7)
478 	in_state->Key_Event_fp (K_MOUSE5, 1);
479       if (b>=0 && in_state && in_state->Key_Event_fp)
480 	in_state->Key_Event_fp (K_MOUSE1 + b, true);
481       if (b>=0)
482 	mouse_buttonstate |= 1<<b;
483       break;
484 
485     case ButtonRelease:
486       b=-1;
487       if (event.xbutton.button == 1)
488 	b = 0;
489       else if (event.xbutton.button == 2)
490 	b = 2;
491       else if (event.xbutton.button == 3)
492 	b = 1;
493       else if (event.xbutton.button == 4)
494 	in_state->Key_Event_fp (K_MWHEELUP, 0);
495       else if (event.xbutton.button == 5)
496 	in_state->Key_Event_fp (K_MWHEELDOWN, 0);
497       else if (event.xbutton.button == 6)
498 	in_state->Key_Event_fp (K_MOUSE4, 0);
499       else if (event.xbutton.button == 7)
500 	in_state->Key_Event_fp (K_MOUSE5, 0);
501       if (b>=0 && in_state && in_state->Key_Event_fp)
502 	in_state->Key_Event_fp (K_MOUSE1 + b, false);
503       if (b>=0)
504 	mouse_buttonstate &= ~(1<<b);
505       break;
506 
507     case CreateNotify :
508       win_x = event.xcreatewindow.x;
509       win_y = event.xcreatewindow.y;
510       break;
511 
512     case ConfigureNotify :
513       win_x = event.xconfigure.x;
514       win_y = event.xconfigure.y;
515       break;
516 
517     case ClientMessage:
518       if (event.xclient.data.l[0] == wmDeleteWindow)
519 	ri.Cmd_ExecuteText(EXEC_NOW, "quit");
520       break;
521     }
522   }
523 
524   if (dowarp) {
525     /* move the mouse to the window center again */
526     XWarpPointer(dpy,None,win,0,0,0,0, vid.width/2,vid.height/2);
527   }
528 }
529 
530 Key_Event_fp_t Key_Event_fp;
531 
KBD_Init(Key_Event_fp_t fp)532 void KBD_Init(Key_Event_fp_t fp)
533 {
534   Key_Event_fp = fp;
535 }
536 
KBD_Update(void)537 void KBD_Update(void)
538 {
539   // get events from x server
540   HandleEvents();
541 }
542 
KBD_Close(void)543 void KBD_Close(void)
544 {
545 }
546 
547 /*****************************************************************************/
548 
RW_Sys_GetClipboardData()549 char *RW_Sys_GetClipboardData()
550 {
551 	Window sowner;
552 	Atom type, property;
553 	unsigned long len, bytes_left, tmp;
554 	unsigned char *data;
555 	int format, result;
556 	char *ret = NULL;
557 
558 	sowner = XGetSelectionOwner(dpy, XA_PRIMARY);
559 
560 	if (sowner != None) {
561 		property = XInternAtom(dpy,
562 				       "GETCLIPBOARDDATA_PROP",
563 				       False);
564 
565 		XConvertSelection(dpy,
566 				  XA_PRIMARY, XA_STRING,
567 				  property, win, myxtime); /* myxtime == time of last X event */
568 		XFlush(dpy);
569 
570 		XGetWindowProperty(dpy,
571 				   win, property,
572 				   0, 0, False, AnyPropertyType,
573 				   &type, &format, &len,
574 				   &bytes_left, &data);
575 		if (bytes_left > 0) {
576 			result =
577 			XGetWindowProperty(dpy,
578 					   win, property,
579 					   0, bytes_left, True, AnyPropertyType,
580 					   &type, &format, &len,
581 					   &tmp, &data);
582 			if (result == Success) {
583 				ret = strdup(data);
584 			}
585 			XFree(data);
586 		}
587 	}
588 	return ret;
589 }
590 
591 /*****************************************************************************/
592 
593 qboolean GLimp_InitGL (void);
594 
signal_handler(int sig)595 static void signal_handler(int sig)
596 {
597 	printf("Received signal %d, exiting...\n", sig);
598 	GLimp_Shutdown();
599 	_exit(0);
600 }
601 
InitSig(void)602 static void InitSig(void)
603 {
604 	signal(SIGHUP, signal_handler);
605 	signal(SIGQUIT, signal_handler);
606 	signal(SIGILL, signal_handler);
607 	signal(SIGTRAP, signal_handler);
608 	signal(SIGIOT, signal_handler);
609 	signal(SIGBUS, signal_handler);
610 	signal(SIGFPE, signal_handler);
611 	signal(SIGSEGV, signal_handler);
612 	signal(SIGTERM, signal_handler);
613 }
614 
615 /*
616 ** GLimp_SetMode
617 */
GLimp_SetMode(int * pwidth,int * pheight,int mode,qboolean fullscreen)618 int GLimp_SetMode( int *pwidth, int *pheight, int mode, qboolean fullscreen )
619 {
620 	int width, height;
621 	int attrib[] = {
622 		GLX_RGBA,
623 		GLX_DOUBLEBUFFER,
624 		GLX_RED_SIZE, 1,
625 		GLX_GREEN_SIZE, 1,
626 		GLX_BLUE_SIZE, 1,
627 		GLX_DEPTH_SIZE, 1,
628 		GLX_STENCIL_SIZE, 1,
629 		None
630 	};
631 	int attrib_nostencil[] = {
632 		GLX_RGBA,
633 		GLX_DOUBLEBUFFER,
634 		GLX_RED_SIZE, 1,
635 		GLX_GREEN_SIZE, 1,
636 		GLX_BLUE_SIZE, 1,
637 		GLX_DEPTH_SIZE, 1,
638 		None
639 	};
640 
641 	Window root;
642 	XVisualInfo *visinfo;
643 	XSetWindowAttributes attr;
644 	XSizeHints *sizehints;
645 	XWMHints *wmhints;
646 	unsigned long mask;
647 	int MajorVersion, MinorVersion;
648 	int actualWidth, actualHeight;
649 	int i;
650 
651 	r_fakeFullscreen = ri.Cvar_Get( "r_fakeFullscreen", "0", CVAR_ARCHIVE);
652 
653 	ri.Con_Printf( PRINT_ALL, "Initializing OpenGL display\n");
654 
655 	if (fullscreen)
656 		ri.Con_Printf (PRINT_ALL, "...setting fullscreen mode %d:", mode );
657 	else
658 		ri.Con_Printf (PRINT_ALL, "...setting mode %d:", mode );
659 
660 	if ( !ri.Vid_GetModeInfo( &width, &height, mode ) )
661 	{
662 		ri.Con_Printf( PRINT_ALL, " invalid mode\n" );
663 		return rserr_invalid_mode;
664 	}
665 
666 	ri.Con_Printf( PRINT_ALL, " %d %d\n", width, height );
667 
668 	// destroy the existing window
669 	GLimp_Shutdown ();
670 
671 #if 0 // this breaks getenv()? - sbf
672 	// Mesa VooDoo hacks
673 	if (fullscreen)
674 		putenv("MESA_GLX_FX=fullscreen");
675 	else
676 		putenv("MESA_GLX_FX=window");
677 #endif
678 
679 	if (!(dpy = XOpenDisplay(NULL))) {
680 		fprintf(stderr, "Error couldn't open the X display\n");
681 		return rserr_invalid_mode;
682 	}
683 
684 	scrnum = DefaultScreen(dpy);
685 	root = RootWindow(dpy, scrnum);
686 
687 	// Get video mode list
688 	MajorVersion = MinorVersion = 0;
689 	if (!XF86VidModeQueryVersion(dpy, &MajorVersion, &MinorVersion)) {
690 		vidmode_ext = false;
691 	} else {
692 		ri.Con_Printf(PRINT_ALL, "Using XFree86-VidModeExtension Version %d.%d\n",
693 			MajorVersion, MinorVersion);
694 		vidmode_ext = true;
695 	}
696 
697 	visinfo = qglXChooseVisual(dpy, scrnum, attrib);
698 	if (!visinfo) {
699 		fprintf(stderr, "W: couldn't get an RGBA, DOUBLEBUFFER, DEPTH, STENCIL visual\n");
700 		visinfo = qglXChooseVisual(dpy, scrnum, attrib_nostencil);
701 		if (!visinfo) {
702 			fprintf(stderr, "E: couldn't get an RGBA, DOUBLEBUFFER, DEPTH visual\n");
703 			return rserr_invalid_mode;
704 		}
705 	}
706 
707 	gl_state.hwgamma = false;
708 
709 	/* do some pantsness */
710 	if ( qglXGetConfig )
711 	{
712 		int red_bits, blue_bits, green_bits, depth_bits, alpha_bits;
713 
714 		qglXGetConfig(dpy, visinfo, GLX_RED_SIZE, &red_bits);
715 		qglXGetConfig(dpy, visinfo, GLX_BLUE_SIZE, &blue_bits);
716 		qglXGetConfig(dpy, visinfo, GLX_GREEN_SIZE, &green_bits);
717 		qglXGetConfig(dpy, visinfo, GLX_DEPTH_SIZE, &depth_bits);
718 		qglXGetConfig(dpy, visinfo, GLX_ALPHA_SIZE, &alpha_bits);
719 
720 		ri.Con_Printf(PRINT_ALL, "I: got %d bits of red\n", red_bits);
721 		ri.Con_Printf(PRINT_ALL, "I: got %d bits of blue\n", blue_bits);
722 		ri.Con_Printf(PRINT_ALL, "I: got %d bits of green\n", green_bits);
723 		ri.Con_Printf(PRINT_ALL, "I: got %d bits of depth\n", depth_bits);
724 		ri.Con_Printf(PRINT_ALL, "I: got %d bits of alpha\n", alpha_bits);
725 	}
726 
727 	/* stencilbuffer shadows */
728 	if ( qglXGetConfig )
729 	{
730 		int stencil_bits;
731 
732 		if (!qglXGetConfig(dpy, visinfo, GLX_STENCIL_SIZE, &stencil_bits)) {
733 			ri.Con_Printf(PRINT_ALL, "I: got %d bits of stencil\n", stencil_bits);
734 			if (stencil_bits >= 1) {
735 				have_stencil = true;
736 			}
737 		}
738 	} else {
739 		have_stencil = true;
740 	}
741 
742 	if (vidmode_ext) {
743 		int best_fit, best_dist, dist, x, y;
744 
745 		XF86VidModeGetAllModeLines(dpy, scrnum, &num_vidmodes, &vidmodes);
746 
747 		// Are we going fullscreen?  If so, let's change video mode
748 		if (fullscreen && !r_fakeFullscreen->value) {
749 			best_dist = 9999999;
750 			best_fit = -1;
751 
752 			for (i = 0; i < num_vidmodes; i++) {
753 				if (width > vidmodes[i]->hdisplay ||
754 					height > vidmodes[i]->vdisplay)
755 					continue;
756 
757 				x = width - vidmodes[i]->hdisplay;
758 				y = height - vidmodes[i]->vdisplay;
759 				dist = (x * x) + (y * y);
760 				if (dist < best_dist) {
761 					best_dist = dist;
762 					best_fit = i;
763 				}
764 			}
765 
766 			if (best_fit != -1) {
767 				actualWidth = vidmodes[best_fit]->hdisplay;
768 				actualHeight = vidmodes[best_fit]->vdisplay;
769 
770 				// change to the mode
771 				XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]);
772 				vidmode_active = true;
773 
774 				if (XF86VidModeGetGamma(dpy, scrnum, &oldgamma)) {
775 					gl_state.hwgamma = true;
776 					/* We can not reliably detect hardware gamma
777 					   changes across software gamma calls, which
778 					   can reset the flag, so change it anyway */
779 					vid_gamma->modified = true;
780 					ri.Con_Printf( PRINT_ALL, "Using hardware gamma\n");
781 				}
782 
783 				// Move the viewport to top left
784 				XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
785 			} else
786 				fullscreen = 0;
787 		}
788 	}
789 
790 	/* window attributes */
791 	attr.background_pixel = 0;
792 	attr.border_pixel = 0;
793 	attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
794 	attr.event_mask = X_MASK;
795 	if (vidmode_active) {
796 		mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore |
797 			CWEventMask | CWOverrideRedirect;
798 		attr.override_redirect = True;
799 		attr.backing_store = NotUseful;
800 		attr.save_under = False;
801 	} else
802 		mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
803 
804 	win = XCreateWindow(dpy, root, 0, 0, width, height,
805 						0, visinfo->depth, InputOutput,
806 						visinfo->visual, mask, &attr);
807 
808 	sizehints = XAllocSizeHints();
809 	if (sizehints) {
810 		sizehints->min_width = width;
811 		sizehints->min_height = height;
812 		sizehints->max_width = width;
813 		sizehints->max_height = height;
814 		sizehints->base_width = width;
815 		sizehints->base_height = vid.height;
816 
817 		sizehints->flags = PMinSize | PMaxSize | PBaseSize;
818 	}
819 
820 	wmhints = XAllocWMHints();
821 	if (wmhints) {
822 		#include "q2icon.xbm"
823 
824 		Pixmap icon_pixmap, icon_mask;
825 		unsigned long fg, bg;
826 		int i;
827 
828 		fg = BlackPixel(dpy, visinfo->screen);
829 		bg = WhitePixel(dpy, visinfo->screen);
830 		icon_pixmap = XCreatePixmapFromBitmapData(dpy, win, (char *)q2icon_bits, q2icon_width, q2icon_height, fg, bg, visinfo->depth);
831 		for (i = 0; i < sizeof(q2icon_bits); i++)
832 			q2icon_bits[i] = ~q2icon_bits[i];
833 		icon_mask = XCreatePixmapFromBitmapData(dpy, win, (char *)q2icon_bits, q2icon_width, q2icon_height, bg, fg, visinfo->depth);
834 
835 		wmhints->flags = IconPixmapHint|IconMaskHint;
836 		wmhints->icon_pixmap = icon_pixmap;
837 		wmhints->icon_mask = icon_mask;
838 	}
839 
840 	XSetWMProperties(dpy, win, NULL, NULL, NULL, 0,
841 			sizehints, wmhints, None);
842 	if (sizehints)
843 		XFree(sizehints);
844 	if (wmhints)
845 		XFree(wmhints);
846 
847 	XStoreName(dpy, win, "Quake II");
848 
849 	wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
850 	XSetWMProtocols(dpy, win, &wmDeleteWindow, 1);
851 
852 	XMapWindow(dpy, win);
853 
854 	if (vidmode_active) {
855 		XMoveWindow(dpy, win, 0, 0);
856 		XRaiseWindow(dpy, win);
857 		XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0);
858 		XFlush(dpy);
859 		// Move the viewport to top left
860 		XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
861 	}
862 
863 	XFlush(dpy);
864 
865 	ctx = qglXCreateContext(dpy, visinfo, NULL, True);
866 
867 	qglXMakeCurrent(dpy, win, ctx);
868 
869 	*pwidth = width;
870 	*pheight = height;
871 
872 	// let the sound and input subsystems know about the new window
873 	ri.Vid_NewWindow (width, height);
874 
875 	qglXMakeCurrent(dpy, win, ctx);
876 
877 	return rserr_ok;
878 }
879 
880 /*
881 ** GLimp_Shutdown
882 **
883 ** This routine does all OS specific shutdown procedures for the OpenGL
884 ** subsystem.  Under OpenGL this means NULLing out the current DC and
885 ** HGLRC, deleting the rendering context, and releasing the DC acquired
886 ** for the window.  The state structure is also nulled out.
887 **
888 */
GLimp_Shutdown(void)889 void GLimp_Shutdown( void )
890 {
891 	uninstall_grabs();
892 	mouse_active = false;
893 	dgamouse = false;
894 
895 	if (dpy) {
896 		if (ctx)
897 			qglXDestroyContext(dpy, ctx);
898 		if (win)
899 			XDestroyWindow(dpy, win);
900 		if (gl_state.hwgamma) {
901 			XF86VidModeSetGamma(dpy, scrnum, &oldgamma);
902 			/* The gamma has changed, but SetMode will change it
903 			   anyway, so why bother?
904 			vid_gamma->modified = true; */
905 		}
906 		if (vidmode_active)
907 			XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[0]);
908 		XUngrabKeyboard(dpy, CurrentTime);
909 		XCloseDisplay(dpy);
910 	}
911 	ctx = NULL;
912 	dpy = NULL;
913 	win = 0;
914 	ctx = NULL;
915 /*
916 	qglXChooseVisual             = NULL;
917 	qglXCreateContext            = NULL;
918 	qglXDestroyContext           = NULL;
919 	qglXMakeCurrent              = NULL;
920 	qglXCopyContext              = NULL;
921 	qglXSwapBuffers              = NULL;
922 */
923 }
924 
925 /*
926 ** GLimp_Init
927 **
928 ** This routine is responsible for initializing the OS specific portions
929 ** of OpenGL.
930 */
GLimp_Init(void * hinstance,void * wndproc)931 int GLimp_Init( void *hinstance, void *wndproc )
932 {
933 	InitSig();
934 
935 	if ( glw_state.OpenGLLib) {
936 		#define GPA( a ) dlsym( glw_state.OpenGLLib, a )
937 
938 		qglXChooseVisual             =  GPA("glXChooseVisual");
939 		qglXCreateContext            =  GPA("glXCreateContext");
940 		qglXDestroyContext           =  GPA("glXDestroyContext");
941 		qglXMakeCurrent              =  GPA("glXMakeCurrent");
942 		qglXCopyContext              =  GPA("glXCopyContext");
943 		qglXSwapBuffers              =  GPA("glXSwapBuffers");
944 		qglXGetConfig                =  GPA("glXGetConfig");
945 
946 		return true;
947 	}
948 
949 	return false;
950 }
951 
952 /*
953 ** GLimp_BeginFrame
954 */
GLimp_BeginFrame(float camera_seperation)955 void GLimp_BeginFrame( float camera_seperation )
956 {
957 }
958 
959 /*
960 ** GLimp_EndFrame
961 **
962 ** Responsible for doing a swapbuffers and possibly for other stuff
963 ** as yet to be determined.  Probably better not to make this a GLimp
964 ** function and instead do a call to GLimp_SwapBuffers.
965 */
GLimp_EndFrame(void)966 void GLimp_EndFrame (void)
967 {
968 	qglFlush();
969 	qglXSwapBuffers(dpy, win);
970 }
971 
972 /*
973 ** UpdateHardwareGamma
974 **
975 ** We are using gamma relative to the desktop, so that we can share it
976 ** with software renderer and don't require to change desktop gamma
977 ** to match hardware gamma image brightness. It seems that Quake 3 is
978 ** using the opposite approach, but it has no software renderer after
979 ** all.
980 */
UpdateHardwareGamma()981 void UpdateHardwareGamma()
982 {
983 	XF86VidModeGamma gamma;
984 	float g;
985 
986 	g = (1.3 - vid_gamma->value + 1);
987 	g = (g>1 ? g : 1);
988 	gamma.red = oldgamma.red * g;
989 	gamma.green = oldgamma.green * g;
990 	gamma.blue = oldgamma.blue * g;
991 	XF86VidModeSetGamma(dpy, scrnum, &gamma);
992 }
993 
994 /*
995 ** GLimp_AppActivate
996 */
GLimp_AppActivate(qboolean active)997 void GLimp_AppActivate( qboolean active )
998 {
999 }
1000 
Fake_glColorTableEXT(GLenum target,GLenum internalformat,GLsizei width,GLenum format,GLenum type,const GLvoid * table)1001 void Fake_glColorTableEXT( GLenum target, GLenum internalformat,
1002                              GLsizei width, GLenum format, GLenum type,
1003                              const GLvoid *table )
1004 {
1005 	byte temptable[256][4];
1006 	byte *intbl;
1007 	int i;
1008 
1009 	for (intbl = (byte *)table, i = 0; i < 256; i++) {
1010 		temptable[i][2] = *intbl++;
1011 		temptable[i][1] = *intbl++;
1012 		temptable[i][0] = *intbl++;
1013 		temptable[i][3] = 255;
1014 	}
1015 	qgl3DfxSetPaletteEXT((GLuint *)temptable);
1016 }
1017 
1018 
1019 #ifdef Joystick
OpenJoystick(cvar_t * joy_dev)1020 qboolean OpenJoystick(cvar_t *joy_dev) {
1021   int i, err;
1022   glob_t pglob;
1023   struct joystick j;
1024 
1025   err = glob(joy_dev->string, 0, NULL, &pglob);
1026 
1027   if (err) {
1028     switch (err) {
1029     case GLOB_NOSPACE:
1030       ri.Con_Printf(PRINT_ALL, "Error, out of memory while looking for joysticks\n");
1031       break;
1032     case GLOB_NOMATCH:
1033       ri.Con_Printf(PRINT_ALL, "No joysticks found\n");
1034       break;
1035     default:
1036       ri.Con_Printf(PRINT_ALL, "Error #%d while looking for joysticks\n",err);
1037     }
1038     goto out;
1039   }
1040 
1041   for (i=0;i<pglob.gl_pathc;i++) {
1042     ri.Con_Printf(PRINT_ALL, "Trying joystick dev %s\n", pglob.gl_pathv[i]);
1043     joy_fd = open (pglob.gl_pathv[i], O_RDONLY | O_NONBLOCK);
1044     if (joy_fd == -1) {
1045       ri.Con_Printf(PRINT_ALL, "Error opening joystick dev %s\n",
1046 		    pglob.gl_pathv[i]);
1047       goto out;
1048     }
1049     else if (read(joy_fd, &j, sizeof(struct joystick)) != -1) {
1050       ri.Con_Printf(PRINT_ALL, "Using joystick dev %s\n", pglob.gl_pathv[i]);
1051       return true;
1052     }
1053   }
1054 out:
1055   globfree(&pglob);
1056   return false;
1057 }
1058 
PlatformJoyCommands(int * axis_vals,int * axis_map)1059 void PlatformJoyCommands(int *axis_vals, int *axis_map) {
1060   struct joystick j;
1061   in_state_t *in_state = getState();
1062 
1063   /*
1064    * FreeBSD does not fully support multi-buttoned joysticks.
1065    */
1066   if (read(joy_fd, &j, sizeof(struct joystick)) != -1) {
1067     if (j.b1)
1068       in_state->Key_Event_fp (K_JOY1, true);
1069     else
1070       in_state->Key_Event_fp (K_JOY1, false);
1071     if (j.b2)
1072       in_state->Key_Event_fp (K_JOY2, true);
1073     else
1074       in_state->Key_Event_fp (K_JOY2, false);
1075     axis_vals[axis_map[0]] = j.x;
1076     axis_vals[axis_map[1]] = j.y;
1077   }
1078 }
1079 
CloseJoystick(void)1080 qboolean CloseJoystick(void) {
1081   if (close(joy_fd))
1082     ri.Con_Printf(PRINT_ALL, "Error, Problem closing joystick.");
1083   return true;
1084 }
1085 #endif
1086