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/xf86dga.h>
58 #include <X11/extensions/xf86vmode.h>
59 #ifdef Joystick
60 # if defined (__linux__)
61 #include <linux/joystick.h>
62 # elif defined (__FreeBSD__)
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 mx = my = 0;
432 switch(event.type) {
433 case KeyPress:
434 myxtime = event.xkey.time;
435 if (in_state && in_state->Key_Event_fp)
436 in_state->Key_Event_fp (XLateKey(&event.xkey), true);
437 break;
438 case KeyRelease:
439 if (! X11_KeyRepeat(dpy, &event)) {
440 if (in_state && in_state->Key_Event_fp)
441 in_state->Key_Event_fp (XLateKey(&event.xkey), false);
442 }
443 break;
444 case MotionNotify:
445 if (mouse_active) {
446 if (dgamouse) {
447 mx += (event.xmotion.x + win_x) * 2;
448 my += (event.xmotion.y + win_y) * 2;
449 }
450 else
451 {
452 mx -= ((int)event.xmotion.x - mwx) * 2;
453 my -= ((int)event.xmotion.y - mwy) * 2;
454 mwx = event.xmotion.x;
455 mwy = event.xmotion.y;
456
457 if (mx || my)
458 dowarp = true;
459 }
460 }
461 break;
462
463
464 case ButtonPress:
465 myxtime = event.xbutton.time;
466
467 b=-1;
468 if (event.xbutton.button == 1)
469 b = 0;
470 else if (event.xbutton.button == 2)
471 b = 2;
472 else if (event.xbutton.button == 3)
473 b = 1;
474 else if (event.xbutton.button == 4)
475 in_state->Key_Event_fp (K_MWHEELUP, 1);
476 else if (event.xbutton.button == 5)
477 in_state->Key_Event_fp (K_MWHEELDOWN, 1);
478 if (b>=0 && in_state && in_state->Key_Event_fp)
479 in_state->Key_Event_fp (K_MOUSE1 + b, true);
480 if (b>=0)
481 mouse_buttonstate |= 1<<b;
482 break;
483
484 case ButtonRelease:
485 b=-1;
486 if (event.xbutton.button == 1)
487 b = 0;
488 else if (event.xbutton.button == 2)
489 b = 2;
490 else if (event.xbutton.button == 3)
491 b = 1;
492 else if (event.xbutton.button == 4)
493 in_state->Key_Event_fp (K_MWHEELUP, 0);
494 else if (event.xbutton.button == 5)
495 in_state->Key_Event_fp (K_MWHEELDOWN, 0);
496 if (b>=0 && in_state && in_state->Key_Event_fp)
497 in_state->Key_Event_fp (K_MOUSE1 + b, false);
498 if (b>=0)
499 mouse_buttonstate &= ~(1<<b);
500 break;
501
502 case CreateNotify :
503 win_x = event.xcreatewindow.x;
504 win_y = event.xcreatewindow.y;
505 break;
506
507 case ConfigureNotify :
508 win_x = event.xconfigure.x;
509 win_y = event.xconfigure.y;
510 break;
511
512 case ClientMessage:
513 if (event.xclient.data.l[0] == wmDeleteWindow)
514 ri.Cmd_ExecuteText(EXEC_NOW, "quit");
515 break;
516 }
517 }
518
519 if (dowarp) {
520 /* move the mouse to the window center again */
521 XWarpPointer(dpy,None,win,0,0,0,0, vid.width/2,vid.height/2);
522 }
523 }
524
525 Key_Event_fp_t Key_Event_fp;
526
KBD_Init(Key_Event_fp_t fp)527 void KBD_Init(Key_Event_fp_t fp)
528 {
529 Key_Event_fp = fp;
530 }
531
KBD_Update(void)532 void KBD_Update(void)
533 {
534 // get events from x server
535 HandleEvents();
536 }
537
KBD_Close(void)538 void KBD_Close(void)
539 {
540 }
541
542 /*****************************************************************************/
543
RW_Sys_GetClipboardData()544 char *RW_Sys_GetClipboardData()
545 {
546 Window sowner;
547 Atom type, property;
548 unsigned long len, bytes_left, tmp;
549 unsigned char *data;
550 int format, result;
551 char *ret = NULL;
552
553 sowner = XGetSelectionOwner(dpy, XA_PRIMARY);
554
555 if (sowner != None) {
556 property = XInternAtom(dpy,
557 "GETCLIPBOARDDATA_PROP",
558 False);
559
560 XConvertSelection(dpy,
561 XA_PRIMARY, XA_STRING,
562 property, win, myxtime); /* myxtime == time of last X event */
563 XFlush(dpy);
564
565 XGetWindowProperty(dpy,
566 win, property,
567 0, 0, False, AnyPropertyType,
568 &type, &format, &len,
569 &bytes_left, &data);
570 if (bytes_left > 0) {
571 result =
572 XGetWindowProperty(dpy,
573 win, property,
574 0, bytes_left, True, AnyPropertyType,
575 &type, &format, &len,
576 &tmp, &data);
577 if (result == Success) {
578 ret = strdup(data);
579 }
580 XFree(data);
581 }
582 }
583 return ret;
584 }
585
586 /*****************************************************************************/
587
588 qboolean GLimp_InitGL (void);
589
signal_handler(int sig)590 static void signal_handler(int sig)
591 {
592 printf("Received signal %d, exiting...\n", sig);
593 GLimp_Shutdown();
594 _exit(0);
595 }
596
InitSig(void)597 static void InitSig(void)
598 {
599 signal(SIGHUP, signal_handler);
600 signal(SIGQUIT, signal_handler);
601 signal(SIGILL, signal_handler);
602 signal(SIGTRAP, signal_handler);
603 signal(SIGIOT, signal_handler);
604 signal(SIGBUS, signal_handler);
605 signal(SIGFPE, signal_handler);
606 signal(SIGSEGV, signal_handler);
607 signal(SIGTERM, signal_handler);
608 }
609
610 /*
611 ** GLimp_SetMode
612 */
GLimp_SetMode(int * pwidth,int * pheight,int mode,qboolean fullscreen)613 int GLimp_SetMode( int *pwidth, int *pheight, int mode, qboolean fullscreen )
614 {
615 int width, height;
616 int attrib[] = {
617 GLX_RGBA,
618 GLX_DOUBLEBUFFER,
619 GLX_RED_SIZE, 1,
620 GLX_GREEN_SIZE, 1,
621 GLX_BLUE_SIZE, 1,
622 GLX_DEPTH_SIZE, 1,
623 GLX_STENCIL_SIZE, 1,
624 None
625 };
626 int attrib_nostencil[] = {
627 GLX_RGBA,
628 GLX_DOUBLEBUFFER,
629 GLX_RED_SIZE, 1,
630 GLX_GREEN_SIZE, 1,
631 GLX_BLUE_SIZE, 1,
632 GLX_DEPTH_SIZE, 1,
633 None
634 };
635
636 Window root;
637 XVisualInfo *visinfo;
638 XSetWindowAttributes attr;
639 XSizeHints *sizehints;
640 XWMHints *wmhints;
641 unsigned long mask;
642 int MajorVersion, MinorVersion;
643 int actualWidth, actualHeight;
644 int i;
645
646 r_fakeFullscreen = ri.Cvar_Get( "r_fakeFullscreen", "0", CVAR_ARCHIVE);
647
648 ri.Con_Printf( PRINT_ALL, "Initializing OpenGL display\n");
649
650 if (fullscreen)
651 ri.Con_Printf (PRINT_ALL, "...setting fullscreen mode %d:", mode );
652 else
653 ri.Con_Printf (PRINT_ALL, "...setting mode %d:", mode );
654
655 if ( !ri.Vid_GetModeInfo( &width, &height, mode ) )
656 {
657 ri.Con_Printf( PRINT_ALL, " invalid mode\n" );
658 return rserr_invalid_mode;
659 }
660
661 ri.Con_Printf( PRINT_ALL, " %d %d\n", width, height );
662
663 // destroy the existing window
664 GLimp_Shutdown ();
665
666 #if 0 // this breaks getenv()? - sbf
667 // Mesa VooDoo hacks
668 if (fullscreen)
669 putenv("MESA_GLX_FX=fullscreen");
670 else
671 putenv("MESA_GLX_FX=window");
672 #endif
673
674 if (!(dpy = XOpenDisplay(NULL))) {
675 fprintf(stderr, "Error couldn't open the X display\n");
676 return rserr_invalid_mode;
677 }
678
679 scrnum = DefaultScreen(dpy);
680 root = RootWindow(dpy, scrnum);
681
682 // Get video mode list
683 MajorVersion = MinorVersion = 0;
684 if (!XF86VidModeQueryVersion(dpy, &MajorVersion, &MinorVersion)) {
685 vidmode_ext = false;
686 } else {
687 ri.Con_Printf(PRINT_ALL, "Using XFree86-VidModeExtension Version %d.%d\n",
688 MajorVersion, MinorVersion);
689 vidmode_ext = true;
690 }
691
692 visinfo = qglXChooseVisual(dpy, scrnum, attrib);
693 if (!visinfo) {
694 fprintf(stderr, "W: couldn't get an RGBA, DOUBLEBUFFER, DEPTH, STENCIL visual\n");
695 visinfo = qglXChooseVisual(dpy, scrnum, attrib_nostencil);
696 if (!visinfo) {
697 fprintf(stderr, "E: couldn't get an RGBA, DOUBLEBUFFER, DEPTH visual\n");
698 return rserr_invalid_mode;
699 }
700 }
701
702 gl_state.hwgamma = false;
703
704 /* do some pantsness */
705 if ( qglXGetConfig )
706 {
707 int red_bits, blue_bits, green_bits, depth_bits, alpha_bits;
708
709 qglXGetConfig(dpy, visinfo, GLX_RED_SIZE, &red_bits);
710 qglXGetConfig(dpy, visinfo, GLX_BLUE_SIZE, &blue_bits);
711 qglXGetConfig(dpy, visinfo, GLX_GREEN_SIZE, &green_bits);
712 qglXGetConfig(dpy, visinfo, GLX_DEPTH_SIZE, &depth_bits);
713 qglXGetConfig(dpy, visinfo, GLX_ALPHA_SIZE, &alpha_bits);
714
715 ri.Con_Printf(PRINT_ALL, "I: got %d bits of red\n", red_bits);
716 ri.Con_Printf(PRINT_ALL, "I: got %d bits of blue\n", blue_bits);
717 ri.Con_Printf(PRINT_ALL, "I: got %d bits of green\n", green_bits);
718 ri.Con_Printf(PRINT_ALL, "I: got %d bits of depth\n", depth_bits);
719 ri.Con_Printf(PRINT_ALL, "I: got %d bits of alpha\n", alpha_bits);
720 }
721
722 /* stencilbuffer shadows */
723 if ( qglXGetConfig )
724 {
725 int stencil_bits;
726
727 if (!qglXGetConfig(dpy, visinfo, GLX_STENCIL_SIZE, &stencil_bits)) {
728 ri.Con_Printf(PRINT_ALL, "I: got %d bits of stencil\n", stencil_bits);
729 if (stencil_bits >= 1) {
730 have_stencil = true;
731 }
732 }
733 } else {
734 have_stencil = true;
735 }
736
737 if (vidmode_ext) {
738 int best_fit, best_dist, dist, x, y;
739
740 XF86VidModeGetAllModeLines(dpy, scrnum, &num_vidmodes, &vidmodes);
741
742 // Are we going fullscreen? If so, let's change video mode
743 if (fullscreen && !r_fakeFullscreen->value) {
744 best_dist = 9999999;
745 best_fit = -1;
746
747 for (i = 0; i < num_vidmodes; i++) {
748 if (width > vidmodes[i]->hdisplay ||
749 height > vidmodes[i]->vdisplay)
750 continue;
751
752 x = width - vidmodes[i]->hdisplay;
753 y = height - vidmodes[i]->vdisplay;
754 dist = (x * x) + (y * y);
755 if (dist < best_dist) {
756 best_dist = dist;
757 best_fit = i;
758 }
759 }
760
761 if (best_fit != -1) {
762 actualWidth = vidmodes[best_fit]->hdisplay;
763 actualHeight = vidmodes[best_fit]->vdisplay;
764
765 // change to the mode
766 XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]);
767 vidmode_active = true;
768
769 if (XF86VidModeGetGamma(dpy, scrnum, &oldgamma)) {
770 gl_state.hwgamma = true;
771 /* We can not reliably detect hardware gamma
772 changes across software gamma calls, which
773 can reset the flag, so change it anyway */
774 vid_gamma->modified = true;
775 ri.Con_Printf( PRINT_ALL, "Using hardware gamma\n");
776 }
777
778 // Move the viewport to top left
779 XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
780 } else
781 fullscreen = 0;
782 }
783 }
784
785 /* window attributes */
786 attr.background_pixel = 0;
787 attr.border_pixel = 0;
788 attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
789 attr.event_mask = X_MASK;
790 if (vidmode_active) {
791 mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore |
792 CWEventMask | CWOverrideRedirect;
793 attr.override_redirect = True;
794 attr.backing_store = NotUseful;
795 attr.save_under = False;
796 } else
797 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
798
799 win = XCreateWindow(dpy, root, 0, 0, width, height,
800 0, visinfo->depth, InputOutput,
801 visinfo->visual, mask, &attr);
802
803 sizehints = XAllocSizeHints();
804 if (sizehints) {
805 sizehints->min_width = width;
806 sizehints->min_height = height;
807 sizehints->max_width = width;
808 sizehints->max_height = height;
809 sizehints->base_width = width;
810 sizehints->base_height = vid.height;
811
812 sizehints->flags = PMinSize | PMaxSize | PBaseSize;
813 }
814
815 wmhints = XAllocWMHints();
816 if (wmhints) {
817 #include "q2icon.xbm"
818
819 Pixmap icon_pixmap, icon_mask;
820 unsigned long fg, bg;
821 int i;
822
823 fg = BlackPixel(dpy, visinfo->screen);
824 bg = WhitePixel(dpy, visinfo->screen);
825 icon_pixmap = XCreatePixmapFromBitmapData(dpy, win, (char *)q2icon_bits, q2icon_width, q2icon_height, fg, bg, visinfo->depth);
826 for (i = 0; i < sizeof(q2icon_bits); i++)
827 q2icon_bits[i] = ~q2icon_bits[i];
828 icon_mask = XCreatePixmapFromBitmapData(dpy, win, (char *)q2icon_bits, q2icon_width, q2icon_height, bg, fg, visinfo->depth);
829
830 wmhints->flags = IconPixmapHint|IconMaskHint;
831 wmhints->icon_pixmap = icon_pixmap;
832 wmhints->icon_mask = icon_mask;
833 }
834
835 XSetWMProperties(dpy, win, NULL, NULL, NULL, 0,
836 sizehints, wmhints, None);
837 if (sizehints)
838 XFree(sizehints);
839 if (wmhints)
840 XFree(wmhints);
841
842 XStoreName(dpy, win, "Quake II");
843
844 wmDeleteWindow = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
845 XSetWMProtocols(dpy, win, &wmDeleteWindow, 1);
846
847 XMapWindow(dpy, win);
848
849 if (vidmode_active) {
850 XMoveWindow(dpy, win, 0, 0);
851 XRaiseWindow(dpy, win);
852 XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0);
853 XFlush(dpy);
854 // Move the viewport to top left
855 XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
856 }
857
858 XFlush(dpy);
859
860 ctx = qglXCreateContext(dpy, visinfo, NULL, True);
861
862 qglXMakeCurrent(dpy, win, ctx);
863
864 *pwidth = width;
865 *pheight = height;
866
867 // let the sound and input subsystems know about the new window
868 ri.Vid_NewWindow (width, height);
869
870 qglXMakeCurrent(dpy, win, ctx);
871
872 return rserr_ok;
873 }
874
875 /*
876 ** GLimp_Shutdown
877 **
878 ** This routine does all OS specific shutdown procedures for the OpenGL
879 ** subsystem. Under OpenGL this means NULLing out the current DC and
880 ** HGLRC, deleting the rendering context, and releasing the DC acquired
881 ** for the window. The state structure is also nulled out.
882 **
883 */
GLimp_Shutdown(void)884 void GLimp_Shutdown( void )
885 {
886 uninstall_grabs();
887 mouse_active = false;
888 dgamouse = false;
889
890 if (dpy) {
891 if (ctx)
892 qglXDestroyContext(dpy, ctx);
893 if (win)
894 XDestroyWindow(dpy, win);
895 if (gl_state.hwgamma) {
896 XF86VidModeSetGamma(dpy, scrnum, &oldgamma);
897 /* The gamma has changed, but SetMode will change it
898 anyway, so why bother?
899 vid_gamma->modified = true; */
900 }
901 if (vidmode_active)
902 XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[0]);
903 XUngrabKeyboard(dpy, CurrentTime);
904 XCloseDisplay(dpy);
905 }
906 ctx = NULL;
907 dpy = NULL;
908 win = 0;
909 ctx = NULL;
910 /*
911 qglXChooseVisual = NULL;
912 qglXCreateContext = NULL;
913 qglXDestroyContext = NULL;
914 qglXMakeCurrent = NULL;
915 qglXCopyContext = NULL;
916 qglXSwapBuffers = NULL;
917 */
918 }
919
920 /*
921 ** GLimp_Init
922 **
923 ** This routine is responsible for initializing the OS specific portions
924 ** of OpenGL.
925 */
GLimp_Init(void * hinstance,void * wndproc)926 int GLimp_Init( void *hinstance, void *wndproc )
927 {
928 InitSig();
929
930 if ( glw_state.OpenGLLib) {
931 #define GPA( a ) dlsym( glw_state.OpenGLLib, a )
932
933 qglXChooseVisual = GPA("glXChooseVisual");
934 qglXCreateContext = GPA("glXCreateContext");
935 qglXDestroyContext = GPA("glXDestroyContext");
936 qglXMakeCurrent = GPA("glXMakeCurrent");
937 qglXCopyContext = GPA("glXCopyContext");
938 qglXSwapBuffers = GPA("glXSwapBuffers");
939 qglXGetConfig = GPA("glXGetConfig");
940
941 return true;
942 }
943
944 return false;
945 }
946
947 /*
948 ** GLimp_BeginFrame
949 */
GLimp_BeginFrame(float camera_seperation)950 void GLimp_BeginFrame( float camera_seperation )
951 {
952 }
953
954 /*
955 ** GLimp_EndFrame
956 **
957 ** Responsible for doing a swapbuffers and possibly for other stuff
958 ** as yet to be determined. Probably better not to make this a GLimp
959 ** function and instead do a call to GLimp_SwapBuffers.
960 */
GLimp_EndFrame(void)961 void GLimp_EndFrame (void)
962 {
963 qglFlush();
964 qglXSwapBuffers(dpy, win);
965 }
966
967 /*
968 ** UpdateHardwareGamma
969 **
970 ** We are using gamma relative to the desktop, so that we can share it
971 ** with software renderer and don't require to change desktop gamma
972 ** to match hardware gamma image brightness. It seems that Quake 3 is
973 ** using the opposite approach, but it has no software renderer after
974 ** all.
975 */
UpdateHardwareGamma()976 void UpdateHardwareGamma()
977 {
978 XF86VidModeGamma gamma;
979 float g;
980
981 g = (1.3 - vid_gamma->value + 1);
982 g = (g>1 ? g : 1);
983 gamma.red = oldgamma.red * g;
984 gamma.green = oldgamma.green * g;
985 gamma.blue = oldgamma.blue * g;
986 XF86VidModeSetGamma(dpy, scrnum, &gamma);
987 }
988
989 /*
990 ** GLimp_AppActivate
991 */
GLimp_AppActivate(qboolean active)992 void GLimp_AppActivate( qboolean active )
993 {
994 }
995
Fake_glColorTableEXT(GLenum target,GLenum internalformat,GLsizei width,GLenum format,GLenum type,const GLvoid * table)996 void Fake_glColorTableEXT( GLenum target, GLenum internalformat,
997 GLsizei width, GLenum format, GLenum type,
998 const GLvoid *table )
999 {
1000 byte temptable[256][4];
1001 byte *intbl;
1002 int i;
1003
1004 for (intbl = (byte *)table, i = 0; i < 256; i++) {
1005 temptable[i][2] = *intbl++;
1006 temptable[i][1] = *intbl++;
1007 temptable[i][0] = *intbl++;
1008 temptable[i][3] = 255;
1009 }
1010 qgl3DfxSetPaletteEXT((GLuint *)temptable);
1011 }
1012
1013
1014 #ifdef Joystick
OpenJoystick(cvar_t * joy_dev)1015 qboolean OpenJoystick(cvar_t *joy_dev) {
1016 int i, err;
1017 glob_t pglob;
1018 struct js_event e;
1019
1020 err = glob(joy_dev->string, 0, NULL, &pglob);
1021
1022 if (err) {
1023 switch (err) {
1024 case GLOB_NOSPACE:
1025 ri.Con_Printf(PRINT_ALL, "Error, out of memory while looking for joysticks\n");
1026 break;
1027 case GLOB_NOMATCH:
1028 ri.Con_Printf(PRINT_ALL, "No joysticks found\n");
1029 break;
1030 default:
1031 ri.Con_Printf(PRINT_ALL, "Error #%d while looking for joysticks\n",err);
1032 }
1033 return false;
1034 }
1035
1036 for (i=0;i<pglob.gl_pathc;i++) {
1037 ri.Con_Printf(PRINT_ALL, "Trying joystick dev %s\n", pglob.gl_pathv[i]);
1038 joy_fd = open (pglob.gl_pathv[i], O_RDONLY | O_NONBLOCK);
1039 if (joy_fd == -1) {
1040 ri.Con_Printf(PRINT_ALL, "Error opening joystick dev %s\n",
1041 pglob.gl_pathv[i]);
1042 return false;
1043 }
1044 else {
1045 while (read(joy_fd, &e, sizeof(struct js_event))!=-1 &&
1046 (e.type & JS_EVENT_INIT))
1047 ri.Con_Printf(PRINT_ALL, "Read init event\n");
1048 ri.Con_Printf(PRINT_ALL, "Using joystick dev %s\n", pglob.gl_pathv[i]);
1049 return true;
1050 }
1051 }
1052 globfree(&pglob);
1053 return false;
1054 }
1055
PlatformJoyCommands(int * axis_vals,int * axis_map)1056 void PlatformJoyCommands(int *axis_vals, int *axis_map) {
1057 struct js_event e;
1058 int key_index;
1059 in_state_t *in_state = getState();
1060
1061 while (read(joy_fd, &e, sizeof(struct js_event))!=-1) {
1062 if (JS_EVENT_BUTTON & e.type) {
1063 key_index = (e.number < 4) ? K_JOY1 : K_AUX1;
1064 if (e.value) {
1065 in_state->Key_Event_fp (key_index + e.number, true);
1066 }
1067 else {
1068 in_state->Key_Event_fp (key_index + e.number, false);
1069 }
1070 }
1071 else if (JS_EVENT_AXIS & e.type) {
1072 axis_vals[axis_map[e.number]] = e.value;
1073 }
1074 }
1075 }
1076
CloseJoystick(void)1077 qboolean CloseJoystick(void) {
1078 if (close(joy_fd))
1079 ri.Con_Printf(PRINT_ALL, "Error, Problem closing joystick.");
1080 return true;
1081 }
1082 #endif
1083