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