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