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