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 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <termios.h>
26 #include <sys/ioctl.h>
27 #include <sys/stat.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #if defined HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33 #include <signal.h>
34 #if defined HAVE_DLFCN_H
35 #include <dlfcn.h>
36 #endif
37 
38 #include "qcommon/qcommon.h"
39 #include "ref_gl/r_local.h"
40 #include "client/keys.h"
41 #include "unix/glw_unix.h"
42 
43 #include <X11/Xlib.h>
44 #include <X11/Xatom.h>
45 #include <X11/keysym.h>
46 #include <X11/cursorfont.h>
47 #if defined HAVE_XXF86VM
48 #include <X11/extensions/xf86vmode.h>
49 #endif // defined HAVE_XXF86VM
50 #if defined HAVE_XXF86DGA
51 # if defined HAVE_X11_EXTENSIONS_XXF86DGA_H
52 #include <X11/extensions/Xxf86dga.h>
53 # else // defined HAVE_X11_EXTENSIONS_XXF86DGA_H
54 #include <X11/extensions/xf86dga.h>
55 # endif // defined HAVE_X11_EXTENSIONS_XXF86DGA_H
56 #endif
57 
58 #include <GL/glx.h>
59 
60 /* extern globals */
61 // using include files for these has some problems
62 extern cursor_t cursor;
63 extern qboolean mouse_available;
64 extern qboolean mouse_is_position;
65 extern int mouse_diff_x;
66 extern int mouse_diff_y;
67 extern float rs_realtime;
68 extern viddef_t vid;
69 
70 void		GLimp_BeginFrame( float camera_separation );
71 void		GLimp_EndFrame( void );
72 qboolean	GLimp_Init( void *hinstance, void *hWnd );
73 void		GLimp_Shutdown( void );
74 rserr_t    	GLimp_SetMode( unsigned *pwidth, unsigned *pheight, int mode, qboolean fullscreen );
75 void		GLimp_AppActivate( qboolean active );
76 void		GLimp_EnableLogging( qboolean enable );
77 void		GLimp_LogNewFrame( void );
78 
79 
80 //---------------------------------------------------------------------------
81 
82 
83 glwstate_t glw_state;
84 
85 static Display *dpy = NULL;
86 static int scrnum;
87 static Window win;
88 static GLXContext ctx = NULL;
89 static Atom wmDeleteWindow;
90 static Atom cor_clipboard;
91 
92 qboolean have_stencil = false; // Stencil shadows - MrG
93 
94 #define KEY_MASK (KeyPressMask | KeyReleaseMask)
95 #define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \
96 		    PointerMotionMask | ButtonMotionMask )
97 #define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask | \
98 	       	StructureNotifyMask | PropertyChangeMask )
99 
100 /*****************************************************************************/
101 /* MOUSE                                                                     */
102 /*****************************************************************************/
103 
104 static cvar_t	*r_fakeFullscreen;
105 extern cvar_t	*in_dgamouse;
106 static int win_x, win_y;
107 
108 #if defined HAVE_XXF86VM
109 static XF86VidModeModeInfo **vidmodes;
110 #endif // defined HAVE_XXF86VM
111 static qboolean vidmode_active = false;
112 
113 qboolean mouse_active = false;
114 qboolean dgamouse = false;
115 qboolean vidmode_ext = false;
116 
CreateNullCursor(Display * display,Window root)117 static Cursor CreateNullCursor(Display *display, Window root)
118 {
119     Pixmap cursormask;
120     XGCValues xgc;
121     GC gc;
122     XColor dummycolour;
123     Cursor cursor;
124 
125     cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/);
126     xgc.function = GXclear;
127     gc =  XCreateGC(display, cursormask, GCFunction, &xgc);
128     XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
129     dummycolour.pixel = 0;
130     dummycolour.red = 0;
131     dummycolour.flags = 04;
132     cursor = XCreatePixmapCursor(display, cursormask, cursormask,
133           &dummycolour,&dummycolour, 0,0);
134     XFreePixmap(display,cursormask);
135     XFreeGC(display,gc);
136     return cursor;
137 }
138 
install_grabs(void)139 void install_grabs(void)
140 {
141 
142 #if defined DEBUG_GDB_NOGRAB
143 // for running on gdb debug. prevents "lockup".
144 	Cvar_Set( "in_dgamouse", "0" );
145 	dgamouse = false;
146 	mouse_is_position = false;
147 #else // defined DEBUG_GDB_NOGRAB
148 # if !defined HAVE_XXF86DGA
149 	XGrabPointer(dpy, win, True, 0, GrabModeAsync, GrabModeAsync, win, None, CurrentTime);
150 	XWarpPointer(dpy, None, win, 0, 0, 0, 0, vid.width / 2, vid.height / 2);
151 	Cvar_Set( "in_dgamouse", "0" );
152 	dgamouse = false;
153 # else // !defined HAVE_XXF86DGA
154 	XGrabPointer(dpy, win, True, 0, GrabModeAsync, GrabModeAsync, win, None, CurrentTime);
155 
156 	if (in_dgamouse->integer)
157 	{
158 		int MajorVersion, MinorVersion;
159 
160 		if (XF86DGAQueryVersion(dpy, &MajorVersion, &MinorVersion)) {
161 			XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0);
162 			XF86DGADirectVideo(dpy, DefaultScreen(dpy), XF86DGADirectMouse);
163 			dgamouse = true;
164 		} else {
165 			// unable to query, probably not supported
166 			Com_Printf ( "Failed to detect XF86DGA Mouse\n" );
167 			Cvar_Set( "in_dgamouse", "0" );
168 			dgamouse = false;
169 			XWarpPointer(dpy, None, win, 0, 0, 0, 0, vid.width / 2, vid.height / 2);
170 		}
171 	} else {
172 		XWarpPointer(dpy, None, win, 0, 0, 0, 0, vid.width / 2, vid.height / 2);
173 	}
174 # endif // !defined HAVE_XXF86DGA
175 
176 	XGrabKeyboard(dpy, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
177 
178 	mouse_is_position = false;
179 	mouse_diff_x = mouse_diff_y = 0;
180 #endif // defined DEBUG_GDB_NOGRAB
181 }
182 
uninstall_grabs(void)183 void uninstall_grabs(void)
184 {
185 	if (!dpy || !win)
186 		return;
187 
188 #if defined HAVE_XXF86DGA
189 	if (dgamouse) {
190 		dgamouse = false;
191 		XF86DGADirectVideo(dpy, DefaultScreen(dpy), 0);
192 	}
193 #endif
194 
195 	XUngrabPointer(dpy, CurrentTime);
196 	XUngrabKeyboard(dpy, CurrentTime);
197 
198 	mouse_is_position = true;
199 }
200 
IN_DeactivateMouse(void)201 static void IN_DeactivateMouse( void )
202 {
203 	if (!dpy || !win)
204 		return;
205 
206 	if (!mouse_is_position) {
207 		uninstall_grabs();
208 	}
209 }
210 
IN_ActivateMouse(void)211 static void IN_ActivateMouse( void )
212 {
213 	if (!dpy || !win)
214 		return;
215 
216 	if (mouse_is_position) {
217 		install_grabs();
218 	}
219 }
220 
IN_Activate(qboolean active)221 void IN_Activate(qboolean active)
222 {
223 	if (active || vidmode_active)
224 		IN_ActivateMouse();
225 	else
226 		IN_DeactivateMouse ();
227 }
228 
229 /*****************************************************************************/
230 /* KEYBOARD                                                                  */
231 /*****************************************************************************/
232 
XLateKey(XKeyEvent * ev)233 static int XLateKey( XKeyEvent *ev )
234 {
235 
236 	int key;
237 	char buf[64];
238 	int buflen;
239 	KeySym keysym;
240 
241 	key = 0;
242 	buflen = XLookupString( ev, buf, sizeof buf, &keysym, 0 );
243 
244 	switch ( keysym )
245 	{
246 
247 	case XK_KP_Page_Up:
248 		key = K_KP_PGUP;
249 		break;
250 
251 	case XK_Page_Up:
252 		key = K_PGUP;
253 		break;
254 
255 	case XK_KP_Page_Down:
256 		key = K_KP_PGDN;
257 		break;
258 
259 	case XK_Page_Down:
260 		key = K_PGDN;
261 		break;
262 
263 	case XK_KP_Home:
264 		key = K_KP_HOME;
265 		break;
266 
267 	case XK_Home:
268 		key = K_HOME;
269 		break;
270 
271 	case XK_KP_End:
272 		key = K_KP_END;
273 		break;
274 
275 	case XK_End:
276 		key = K_END;
277 		break;
278 
279 	case XK_KP_Left:
280 		key = K_KP_LEFTARROW;
281 		break;
282 
283 	case XK_Left:
284 		key = K_LEFTARROW;
285 		break;
286 
287 	case XK_KP_Right:
288 		key = K_KP_RIGHTARROW;
289 		break;
290 
291 	case XK_Right:
292 		key = K_RIGHTARROW;
293 		break;
294 
295 	case XK_KP_Down:
296 		key = K_KP_DOWNARROW;
297 		break;
298 
299 	case XK_Down:
300 		key = K_DOWNARROW;
301 		break;
302 
303 	case XK_KP_Up:
304 		key = K_KP_UPARROW;
305 		break;
306 
307 	case XK_Up:
308 		key = K_UPARROW;
309 		break;
310 
311 	case XK_Escape:
312 		key = K_ESCAPE;
313 		break;
314 
315 	case XK_KP_Enter:
316 		key = K_KP_ENTER;
317 		break;
318 
319 	case XK_Return:
320 		key = K_ENTER;
321 		break;
322 
323 	case XK_Tab:
324 		key = K_TAB;
325 		break;
326 
327 	case XK_F1:
328 		key = K_F1;
329 		break;
330 
331 	case XK_F2:
332 		key = K_F2;
333 		break;
334 
335 	case XK_F3:
336 		key = K_F3;
337 		break;
338 
339 	case XK_F4:
340 		key = K_F4;
341 		break;
342 
343 	case XK_F5:
344 		key = K_F5;
345 		break;
346 
347 	case XK_F6:
348 		key = K_F6;
349 		break;
350 
351 	case XK_F7:
352 		key = K_F7;
353 		break;
354 
355 	case XK_F8:
356 		key = K_F8;
357 		break;
358 
359 	case XK_F9:
360 		key = K_F9;
361 		break;
362 
363 	case XK_F10:
364 		key = K_F10;
365 		break;
366 
367 	case XK_F11:
368 		key = K_F11;
369 		break;
370 
371 	case XK_F12:
372 		key = K_F12;
373 		break;
374 
375 	case XK_BackSpace:
376 		key = K_BACKSPACE;
377 		break;
378 
379 	case XK_KP_Delete:
380 		key = K_KP_DEL;
381 		break;
382 
383 	case XK_Delete:
384 		key = K_DEL;
385 		break;
386 
387 	case XK_Pause:
388 		key = K_PAUSE;
389 		break;
390 
391 	case XK_Shift_L:
392 	case XK_Shift_R:
393 		key = K_SHIFT;
394 		break;
395 
396 	case XK_Execute:
397 	case XK_Control_L:
398 	case XK_Control_R:
399 		key = K_CTRL;
400 		break;
401 
402 	case XK_Alt_L:
403 	case XK_Meta_L:
404 	case XK_Alt_R:
405 	case XK_Meta_R:
406 		key = K_ALT;
407 		break;
408 
409 	case XK_KP_Begin:
410 		key = K_KP_5;
411 		break;
412 
413 	case XK_Insert:
414 		key = K_INS;
415 		break;
416 
417 	case XK_KP_Insert:
418 		key = K_KP_INS;
419 		break;
420 
421 	case XK_KP_Multiply:
422 		key = '*';
423 		break;
424 
425 	case XK_KP_Add:
426 		key = K_KP_PLUS;
427 		break;
428 
429 	case XK_KP_Subtract:
430 		key = K_KP_MINUS;
431 		break;
432 
433 	case XK_KP_Divide:
434 		key = K_KP_SLASH;
435 		break;
436 
437 	default:
438 		if ( buflen == 1 )
439 		{
440 			key = *(unsigned char*)buf;
441 
442 			if ( key >= 'A' && key <= 'Z' )
443 				key = key - 'A' + 'a';
444 
445 			if ( key >= 1 && key <= 26 ) /* ctrl+alpha */
446 				key = key + 'a' - 1;
447 		}
448 		break;
449 	}
450 
451 	return key;
452 }
453 
HandleEvents(void)454 void HandleEvents( void )
455 {
456 	XEvent event;
457 	qboolean dowarp = false;
458 	int mwx = vid.width / 2;
459 	int mwy = vid.height / 2;
460 	int multiclicktime = 750;
461 	int mouse_button;
462 	static int last_mouse_x = 0, last_mouse_y = 0;
463 
464 	float f_sys_msecs;
465 	unsigned u_sys_msecs;
466 
467 	if ( !dpy )
468 		return;
469 
470 	// do one read of time for consistency
471 	// theory is that all pending events occurring before this point in time
472 	// should be considered occurring at this single point in time.
473 	u_sys_msecs = (unsigned)Sys_Milliseconds();
474 	f_sys_msecs = (float)u_sys_msecs;
475 
476 	while ( XPending( dpy ) )
477 	{
478 		XNextEvent( dpy, &event );
479 
480 		// TODO: check for errors here, maybe
481 
482 		switch ( event.type )
483 		{
484 
485 		case KeyPress:
486 			Key_Event( XLateKey( &event.xkey ), true, u_sys_msecs );
487 			break;
488 
489 		case KeyRelease:
490 			Key_Event( XLateKey( &event.xkey ), false, u_sys_msecs );
491 			break;
492 
493 		case MotionNotify:
494 			last_mouse_x = event.xmotion.x;
495 			last_mouse_y = event.xmotion.y;
496 			break;
497 
498 		case ButtonPress:
499 			if ( event.xbutton.button )
500 			{
501 				mouse_button = event.xbutton.button - 1;
502 				switch ( mouse_button )
503 				{ // index swap, buttons 2 & 3
504 				case 1:
505 					mouse_button = 2;
506 					break;
507 				case 2:
508 					mouse_button = 1;
509 					break;
510 				}
511 
512 				if (mouse_button < MENU_CURSOR_BUTTON_MAX)
513 				{
514 					if ( (f_sys_msecs - cursor.buttontime[mouse_button])
515 							< multiclicktime )
516 						cursor.buttonclicks[mouse_button] += 1;
517 					else
518 						cursor.buttonclicks[mouse_button] = 1;
519 
520 					if ( cursor.buttonclicks[mouse_button] > 3 )
521 						cursor.buttonclicks[mouse_button] = 3;
522 
523 					cursor.buttontime[mouse_button] = f_sys_msecs;
524 
525 					cursor.buttondown[mouse_button] = true;
526 					cursor.buttonused[mouse_button] = false;
527 					cursor.mouseaction = true;
528 				}
529 
530 				switch ( event.xbutton.button )
531 				{
532 				case 1:
533 					Key_Event( K_MOUSE1, true, u_sys_msecs );
534 					break;
535 				case 2:
536 					Key_Event( K_MOUSE3, true, u_sys_msecs );
537 					break;
538 				case 3:
539 					Key_Event( K_MOUSE2, true, u_sys_msecs );
540 					break;
541 				case 4:
542 					Key_Event( K_MWHEELUP, true, u_sys_msecs );
543 					break;
544 				case 5:
545 					Key_Event( K_MWHEELDOWN, true, u_sys_msecs );
546 					break;
547 				case 6:
548 					Key_Event( K_MOUSE4, true, u_sys_msecs );
549 					break;
550 				case 7:
551 					Key_Event( K_MOUSE5, true, u_sys_msecs );
552 					break;
553 				case 8:
554 					Key_Event( K_MOUSE6, true, u_sys_msecs );
555 					break;
556 				case 9:
557 					Key_Event( K_MOUSE7, true, u_sys_msecs );
558 					break;
559 				case 10:
560 					Key_Event( K_MOUSE8, true, u_sys_msecs );
561 					break;
562 				case 11:
563 					Key_Event( K_MOUSE9, true, u_sys_msecs );
564 					break;
565 				}
566 			}
567 			break;
568 
569 		case ButtonRelease:
570 			if ( event.xbutton.button )
571 			{
572 				mouse_button = event.xbutton.button - 1;
573 				switch ( mouse_button )
574 				{ // index swap, buttons 2 & 3
575 				case 1:
576 					mouse_button = 2;
577 					break;
578 				case 2:
579 					mouse_button = 1;
580 					break;
581 				}
582 
583 				if (mouse_button < MENU_CURSOR_BUTTON_MAX)
584 				{
585 					cursor.buttondown[mouse_button] = false;
586 					cursor.buttonused[mouse_button] = false;
587 				}
588 
589 				switch ( event.xbutton.button )
590 				{
591 				case 1:
592 					Key_Event( K_MOUSE1, false, u_sys_msecs );
593 					break;
594 				case 2:
595 					Key_Event( K_MOUSE3, false, u_sys_msecs );
596 					break;
597 				case 3:
598 					Key_Event( K_MOUSE2, false, u_sys_msecs );
599 					break;
600 				case 4:
601 					Key_Event( K_MWHEELUP, false, u_sys_msecs );
602 					break;
603 				case 5:
604 					Key_Event( K_MWHEELDOWN, false, u_sys_msecs );
605 					break;
606 				case 6:
607 					Key_Event( K_MOUSE4, false, u_sys_msecs );
608 					break;
609 				case 7:
610 					Key_Event( K_MOUSE5, false, u_sys_msecs );
611 					break;
612 				case 8:
613 					Key_Event( K_MOUSE6, false, u_sys_msecs );
614 					break;
615 				case 9:
616 					Key_Event( K_MOUSE7, false, u_sys_msecs );
617 					break;
618 				case 10:
619 					Key_Event( K_MOUSE8, false, u_sys_msecs );
620 					break;
621 				case 11:
622 					Key_Event( K_MOUSE9, false, u_sys_msecs );
623 					break;
624 				}
625 			}
626 			break;
627 
628 		case CreateNotify:
629 			win_x = event.xcreatewindow.x;
630 			win_y = event.xcreatewindow.y;
631 			break;
632 
633 		case ConfigureNotify:
634 			win_x = event.xconfigure.x;
635 			win_y = event.xconfigure.y;
636 			break;
637 
638 		case ClientMessage:
639 			if ( event.xclient.data.l[0] == wmDeleteWindow )
640 				Cbuf_ExecuteText( EXEC_NOW, "quit" );
641 			break;
642 		}
643 	}
644 
645 	if ( mouse_is_position )
646 	{ // allow mouse movement on menus in windowed mode
647 		mouse_diff_x = last_mouse_x;
648 		mouse_diff_y = last_mouse_y;
649 	}
650 	else
651 	{
652 		if ( dgamouse )
653 		{ // TODO: find documentation for DGA mouse, explain this
654 			mouse_diff_x += (last_mouse_x + (vidmode_active ? 0 : win_x)) * 2;
655 			mouse_diff_y += (last_mouse_y + (vidmode_active ? 0 : win_y)) * 2;
656 		}
657 		else
658 		{ // add the delta from the current position to the center
659 			//  to the pointer motion accumulator
660 			mouse_diff_x += ((int)last_mouse_x - mwx);
661 			mouse_diff_y += ((int)last_mouse_y - mwy);
662 
663 			// flag to recenter pointer
664 			dowarp = (mouse_diff_x != 0 || mouse_diff_y != 0 );
665 		}
666 	}
667 
668 	if ( dowarp )
669 	{ /* move the pointer back to the window center */
670 		XWarpPointer( dpy, None, win, 0, 0, 0, 0, mwx, mwy );
671 	}
672 
673 }
674 
675 /*****************************************************************************/
676 
677 qboolean GLimp_InitGL (void);
678 
679 // TODO: modernize signal handler
680 
signal_handler(int sig)681 static void signal_handler(int sig)
682 {
683 	printf("Received signal %d, exiting...\n", sig);
684 	GLimp_Shutdown();
685 	exit(0);
686 }
687 
InitSig(void)688 static void InitSig(void)
689 {
690 	signal(SIGHUP, signal_handler);
691 	signal(SIGQUIT, signal_handler);
692 	signal(SIGILL, signal_handler);
693 	signal(SIGTRAP, signal_handler);
694 	signal(SIGIOT, signal_handler);
695 	signal(SIGBUS, signal_handler);
696 	signal(SIGFPE, signal_handler);
697 	signal(SIGSEGV, signal_handler);
698 	signal(SIGTERM, signal_handler);
699 }
700 
701 /*
702 ** GLimp_SetMode
703 */
GLimp_SetMode(unsigned * pwidth,unsigned * pheight,int mode,qboolean fullscreen)704 rserr_t GLimp_SetMode( unsigned *pwidth, unsigned *pheight, int mode, qboolean fullscreen )
705 {
706 	extern cvar_t	*vid_xpos;	// X coordinate of window position
707 	extern cvar_t	*vid_ypos;	// Y coordinate of window position
708 	int xpos, ypos;
709 
710 	int width, height;
711 	int attrib[] = {
712 		GLX_RGBA,
713 		GLX_DOUBLEBUFFER,
714 		GLX_RED_SIZE, 4,
715 		GLX_GREEN_SIZE, 4,
716 		GLX_BLUE_SIZE, 4,
717 		GLX_DEPTH_SIZE, 24,
718 		GLX_STENCIL_SIZE, 8,
719 		None
720 	};
721 	int attrib2[] = { //no stencil buffer, original settings
722 		GLX_RGBA,
723 		GLX_RED_SIZE, 1,
724 		GLX_GREEN_SIZE, 1,
725 		GLX_BLUE_SIZE, 1,
726 		GLX_DOUBLEBUFFER,
727 		GLX_DEPTH_SIZE, 1,
728 		None
729 	};
730 
731 	Window root;
732 	XVisualInfo *visinfo;
733 	XSetWindowAttributes attr;
734 	XSizeHints *sizehints;
735 	unsigned long mask;
736 #if defined HAVE_XXF86VM
737 	int MajorVersion, MinorVersion;
738 	int actualWidth, actualHeight;
739 	int i;
740 #endif
741 
742 	r_fakeFullscreen = Cvar_Get( "r_fakeFullscreen", "0", CVAR_ARCHIVE);
743 
744 	Com_Printf ( "Initializing OpenGL display\n");
745 
746 	if (fullscreen)
747 		Com_Printf ("...setting fullscreen mode %d:", mode );
748 	else
749 		Com_Printf ("...setting mode %d:", mode );
750 
751 	if ( !VID_GetModeInfo( &width, &height, mode ) )
752 	{
753 		Com_Printf ( " invalid mode\n" );
754 		return rserr_invalid_mode;
755 	}
756 
757 	Com_Printf ( " %d %d\n", width, height );
758 
759 	// destroy the existing window
760 	GLimp_Shutdown ();
761 
762 	if (!(dpy = XOpenDisplay(NULL))) {
763 		fprintf(stderr, "Error couldn't open the X display\n");
764 		return rserr_invalid_mode;
765 	}
766 
767 	scrnum = DefaultScreen(dpy);
768 	root = RootWindow(dpy, scrnum);
769 
770 	// Get video mode list
771 #if defined HAVE_XXF86VM
772 	MajorVersion = MinorVersion = 0;
773 	if (!XF86VidModeQueryVersion(dpy, &MajorVersion, &MinorVersion)) {
774 		vidmode_ext = false;
775 	} else {
776 		Com_Printf ( "Using XFree86-VidModeExtension Version %d.%d\n",
777 			MajorVersion, MinorVersion);
778 		vidmode_ext = true;
779 	}
780 #else // defined HAVE_XXF86VM
781 	vidmode_ext = false;
782 #endif // defined HAVE_XXF86VM
783 
784 	visinfo = qglXChooseVisual(dpy, scrnum, attrib);
785 	if (!visinfo) {
786 		fprintf(stderr, "Error couldn't get an RGB, Double-buffered, Depth visual, Stencil Buffered\n");
787 		visinfo = qglXChooseVisual(dpy, scrnum, attrib2);
788 		if(!visinfo){
789 			fprintf(stderr, "Error couldn't get an RGB, Double-buffered, Depth visual\n");
790 			return rserr_invalid_mode;
791 		}
792 	}
793 	else
794 		have_stencil = true;
795 
796 	vidmode_active = false;
797 
798 	xpos = ypos = 0;
799 
800 #if defined HAVE_XXF86VM
801 	if (vidmode_ext) {
802 		int best_fit, best_dist, dist, x, y, num_vidmodes;
803 
804 		XF86VidModeGetAllModeLines(dpy, scrnum, &num_vidmodes, &vidmodes);
805 
806 		// Are we going fullscreen?  If so, let's change video mode
807 		if (fullscreen && !r_fakeFullscreen->integer) {
808 			best_dist = 9999999;
809 			best_fit = -1;
810 
811 			for (i = 0; i < num_vidmodes; i++) {
812 				if (width > vidmodes[i]->hdisplay ||
813 					height > vidmodes[i]->vdisplay)
814 					continue;
815 
816 				x = width - vidmodes[i]->hdisplay;
817 				y = height - vidmodes[i]->vdisplay;
818 				dist = (x * x) + (y * y);
819 				if (dist < best_dist) {
820 					best_dist = dist;
821 					best_fit = i;
822 				}
823 			}
824 
825 			if (best_fit != -1) {
826 				actualWidth = vidmodes[best_fit]->hdisplay;
827 				actualHeight = vidmodes[best_fit]->vdisplay;
828 
829 				// change to the mode
830 				XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]);
831 				vidmode_active = true;
832 
833 				// Move the viewport to top left
834 				XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
835 
836 				if (width != actualWidth || height != actualHeight)
837 				{
838 					xpos = vid_xpos->integer;
839 					ypos = vid_ypos->integer;
840 					Com_Printf ("Resolution %dx%d is not supported natively by the display!\n", width, height);
841 					Com_Printf ("Closest screen resolution is %dx%d. ", actualWidth, actualHeight);
842 					Com_Printf ("Use vid_xpos and vid_ypos to adjust the position of the game window (current offset is %d, %d)\n", xpos, ypos);
843 				}
844 			} else
845 				fullscreen = 0;
846 		}
847 	}
848 #endif // defined HAVE_XXF86VM
849 
850 	/* window attributes */
851 	attr.background_pixel = 0;
852 	attr.border_pixel = 0;
853 	attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
854 	attr.event_mask = X_MASK;
855 	if (vidmode_active) {
856 		mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore |
857 			CWEventMask | CWOverrideRedirect;
858 		attr.override_redirect = True;
859 		attr.backing_store = NotUseful;
860 		attr.save_under = False;
861 	} else
862 		mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
863 
864 	win = XCreateWindow(dpy, root, 0, 0, width, height,
865 						0, visinfo->depth, InputOutput,
866 						visinfo->visual, mask, &attr);
867 
868 	sizehints = XAllocSizeHints();
869 	if (sizehints) {
870 		sizehints->min_width = width;
871 		sizehints->min_height = height;
872 		sizehints->max_width = width;
873 		sizehints->max_height = height;
874 		sizehints->base_width = width;
875 		sizehints->base_height = vid.height;
876 
877 		sizehints->flags = PMinSize | PMaxSize | PBaseSize;
878 	}
879 
880 	XSetWMProperties(dpy, win, NULL, NULL, NULL, 0,
881 			sizehints, None, None);
882 
883 	if (sizehints)
884 		XFree(sizehints);
885 
886 	XStoreName(dpy, win, "Alien Arena");
887 
888 	wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
889 	XSetWMProtocols(dpy, win, &wmDeleteWindow, 1);
890 
891 	cor_clipboard = XInternAtom(dpy, "COR_CLIPBOARD", False);
892 
893 	XMapWindow(dpy, win);
894 
895 #if defined HAVE_XXF86VM
896 	if (vidmode_active) {
897 		XMoveWindow(dpy, win, xpos, ypos);
898 		XRaiseWindow(dpy, win);
899 		XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0);
900 		XFlush(dpy);
901 		// Move the viewport to top left
902 		XF86VidModeSetViewPort(dpy, scrnum, xpos, ypos);
903 	}
904 #endif // defined HAVE_XXF86VM
905 
906 	XFlush(dpy);
907 
908 	ctx = qglXCreateContext(dpy, visinfo, NULL, True);
909 
910 	qglXMakeCurrent(dpy, win, ctx);
911 
912 	*pwidth = width;
913 	*pheight = height;
914 
915 	// let the sound and input subsystems know about the new window
916 	VID_NewWindow (width, height);
917 
918 	XDefineCursor(dpy, win, CreateNullCursor(dpy, win));
919 
920 	qglXMakeCurrent(dpy, win, ctx);
921 
922 	RS_ScanPathForScripts();		// load all found scripts
923 
924 	return rserr_ok;
925 }
926 
927 /*
928 ** GLimp_Shutdown
929 **
930 ** This routine does all OS specific shutdown procedures for the OpenGL
931 ** subsystem.  Under OpenGL this means NULLing out the current DC and
932 ** HGLRC, deleting the rendering context, and releasing the DC acquired
933 ** for the window.  The state structure is also nulled out.
934 **
935 */
GLimp_Shutdown(void)936 void GLimp_Shutdown( void )
937 {
938 	uninstall_grabs();
939 	mouse_is_position = true;
940 	dgamouse = false;
941 
942 	if (dpy) {
943 		if (ctx)
944 			qglXDestroyContext(dpy, ctx);
945 		if (win)
946 			XDestroyWindow(dpy, win);
947 #if defined HAVE_XXF86VM
948 		if (vidmode_active)
949 			XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[0]);
950 #endif // defined HAVE_XXF86VM
951 		XUngrabKeyboard(dpy, CurrentTime);
952 		XCloseDisplay(dpy);
953 	}
954 	ctx = NULL;
955 	dpy = NULL;
956 	win = 0;
957 }
958 
959 /*
960 ** GLimp_Init
961 **
962 ** This routine is responsible for initializing the OS specific portions
963 ** of OpenGL.
964 */
GLimp_Init(void * hinstance,void * wndproc)965 qboolean GLimp_Init( void *hinstance, void *wndproc )
966 {
967 	InitSig();
968 
969 	return true;
970 }
971 
972 /*
973 ** GLimp_BeginFrame
974 */
GLimp_BeginFrame(float camera_seperation)975 void GLimp_BeginFrame( float camera_seperation )
976 {
977 }
978 
979 /*
980 ** GLimp_EndFrame
981 **
982 ** Responsible for doing a swapbuffers and possibly for other stuff
983 ** as yet to be determined.  Probably better not to make this a GLimp
984 ** function and instead do a call to GLimp_SwapBuffers.
985 */
GLimp_EndFrame(void)986 void GLimp_EndFrame (void)
987 {
988 	qglXSwapBuffers(dpy, win); // does glFlush()
989 
990 	// rscript - MrG
991 	rs_realtime = (float)Sys_Milliseconds() * 0.0005f;
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 /* X11 Input Stuff				  */
1020 /*------------------------------------------------*/
1021 
1022 /*
1023 ** Sys_GetClipboardData()
1024 **
1025 ** Returns the contents of the X clipboard; needs to
1026 ** be here instead of inside sys_unix.c because it
1027 ** needs to access the X display.
1028 */
Sys_GetClipboardData(void)1029 char *Sys_GetClipboardData(void)
1030 {
1031 	XEvent evt;
1032 	int sent_at;
1033 	Bool received;
1034 	Atom prop_type;
1035 	int prop_fmt;
1036 	unsigned long prop_items, prop_size;
1037 	unsigned char *buffer, *output = NULL;
1038 
1039 	// request the contents of the clipboard
1040 	XConvertSelection( dpy, XA_PRIMARY, XA_STRING,
1041 		cor_clipboard, win, CurrentTime );
1042 	sent_at = Sys_Milliseconds();
1043 
1044 	// now we need to wait until either:
1045 	//  1) we get a response
1046 	//  2) 250ms have gone by and we got nothing
1047 	do
1048        	{
1049 		received = XCheckTypedEvent( dpy, SelectionNotify, &evt );
1050 	} while ( !( received || Sys_Milliseconds() - sent_at > 250 ) );
1051 
1052 	// no reply received, return NULL
1053 	if ( !received )
1054 		return NULL;
1055 
1056 	// get information about property
1057 	XGetWindowProperty( dpy , win, cor_clipboard, 0, 0, False,
1058 			AnyPropertyType, &prop_type, &prop_fmt,
1059 			&prop_items, &prop_size, &buffer );
1060 	XFree(buffer);
1061 
1062 	// only get the text if it's actually ASCII
1063 	if ( prop_fmt == 8 )
1064 	{
1065 		XGetWindowProperty( dpy , win, cor_clipboard, 0,
1066 				prop_size, False, AnyPropertyType,
1067 				&prop_type, &prop_fmt, &prop_items,
1068 				&prop_size, &buffer );
1069 
1070 		output = malloc( prop_items + 1 );
1071 		memcpy( output, buffer, prop_items );
1072 		output[prop_items] = 0;
1073 	}
1074 
1075 	XDeleteProperty(dpy, win, cor_clipboard);
1076 
1077 	return (char*)output;
1078 }
1079 
1080