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