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