diff options
author | zakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2005-08-26 17:39:27 +0000 |
---|---|---|
committer | zakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2005-08-26 17:39:27 +0000 |
commit | 6bf20c78f5b69d40bcc4931df93d29198435ab67 (patch) | |
tree | e3eda937a05d7db42de725b7013bd0344b987f34 /code/unix/linux_glimp.c | |
parent | 872d4d7f55af706737ffb361bb76ad13e7496770 (diff) | |
download | ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.tar.gz ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.zip |
newlines fixed
git-svn-id: svn://svn.icculus.org/quake3/trunk@6 edf5b092-35ff-0310-97b2-ce42778d08ea
Diffstat (limited to 'code/unix/linux_glimp.c')
-rwxr-xr-x | code/unix/linux_glimp.c | 3560 |
1 files changed, 1780 insertions, 1780 deletions
diff --git a/code/unix/linux_glimp.c b/code/unix/linux_glimp.c index fa92501..7ecfb3f 100755 --- a/code/unix/linux_glimp.c +++ b/code/unix/linux_glimp.c @@ -1,1780 +1,1780 @@ -/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-/*
-** GLW_IMP.C
-**
-** This file contains ALL Linux specific stuff having to do with the
-** OpenGL refresh. When a port is being made the following functions
-** must be implemented by the port:
-**
-** GLimp_EndFrame
-** GLimp_Init
-** GLimp_Shutdown
-** GLimp_SwitchFullscreen
-** GLimp_SetGamma
-**
-*/
-
-#include <termios.h>
-#include <sys/ioctl.h>
-#ifdef __linux__
- #include <sys/stat.h>
- #include <sys/vt.h>
-#endif
-#include <stdarg.h>
-#include <stdio.h>
-#include <signal.h>
-#include <pthread.h>
-#include <semaphore.h>
-
-// bk001204
-#include <dlfcn.h>
-
-// bk001206 - from my Heretic2 by way of Ryan's Fakk2
-// Needed for the new X11_PendingInput() function.
-#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "../renderer/tr_local.h"
-#include "../client/client.h"
-#include "linux_local.h" // bk001130
-
-#include "unix_glw.h"
-
-#include <GL/glx.h>
-
-#include <X11/keysym.h>
-#include <X11/cursorfont.h>
-
-#include <X11/extensions/xf86dga.h>
-#include <X11/extensions/xf86vmode.h>
-
-#define WINDOW_CLASS_NAME "Quake III: Arena"
-
-typedef enum
-{
- RSERR_OK,
-
- RSERR_INVALID_FULLSCREEN,
- RSERR_INVALID_MODE,
-
- RSERR_UNKNOWN
-} rserr_t;
-
-glwstate_t glw_state;
-
-static Display *dpy = NULL;
-static int scrnum;
-static Window win = 0;
-static GLXContext ctx = NULL;
-
-// bk001206 - not needed anymore
-// static qboolean autorepeaton = qtrue;
-
-#define KEY_MASK (KeyPressMask | KeyReleaseMask)
-#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \
- PointerMotionMask | ButtonMotionMask )
-#define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask | StructureNotifyMask )
-
-static qboolean mouse_avail;
-static qboolean mouse_active = qfalse;
-static int mwx, mwy;
-static int mx = 0, my = 0;
-
-// Time mouse was reset, we ignore the first 50ms of the mouse to allow settling of events
-static int mouseResetTime = 0;
-#define MOUSE_RESET_DELAY 50
-
-static cvar_t *in_mouse;
-static cvar_t *in_dgamouse; // user pref for dga mouse
-cvar_t *in_subframe;
-cvar_t *in_nograb; // this is strictly for developers
-
-// bk001130 - from cvs1.17 (mkv), but not static
-cvar_t *in_joystick = NULL;
-cvar_t *in_joystickDebug = NULL;
-cvar_t *joy_threshold = NULL;
-
-cvar_t *r_allowSoftwareGL; // don't abort out if the pixelformat claims software
-cvar_t *r_previousglDriver;
-
-qboolean vidmode_ext = qfalse;
-static int vidmode_MajorVersion = 0, vidmode_MinorVersion = 0; // major and minor of XF86VidExtensions
-
-// gamma value of the X display before we start playing with it
-static XF86VidModeGamma vidmode_InitialGamma;
-
-static int win_x, win_y;
-
-static XF86VidModeModeInfo **vidmodes;
-//static int default_dotclock_vidmode; // bk001204 - unused
-static int num_vidmodes;
-static qboolean vidmode_active = qfalse;
-
-static int mouse_accel_numerator;
-static int mouse_accel_denominator;
-static int mouse_threshold;
-
-/*
-* Find the first occurrence of find in s.
-*/
-// bk001130 - from cvs1.17 (mkv), const
-// bk001130 - made first argument const
-static const char *Q_stristr( const char *s, const char *find)
-{
- register char c, sc;
- register size_t len;
-
- if ((c = *find++) != 0)
- {
- if (c >= 'a' && c <= 'z')
- {
- c -= ('a' - 'A');
- }
- len = strlen(find);
- do
- {
- do
- {
- if ((sc = *s++) == 0)
- return NULL;
- if (sc >= 'a' && sc <= 'z')
- {
- sc -= ('a' - 'A');
- }
- } while (sc != c);
- } while (Q_stricmpn(s, find, len) != 0);
- s--;
- }
- return s;
-}
-
-/*****************************************************************************
-** KEYBOARD
-** NOTE TTimo the keyboard handling is done with KeySyms
-** that means relying on the keyboard mapping provided by X
-** in-game it would probably be better to use KeyCode (i.e. hardware key codes)
-** you would still need the KeySyms in some cases, such as for the console and all entry textboxes
-** (cause there's nothing worse than a qwerty mapping on a french keyboard)
-**
-** you can turn on some debugging and verbose of the keyboard code with #define KBD_DBG
-******************************************************************************/
-
-//#define KBD_DBG
-
-static char *XLateKey(XKeyEvent *ev, int *key)
-{
- static char buf[64];
- KeySym keysym;
- int XLookupRet;
-
- *key = 0;
-
- XLookupRet = XLookupString(ev, buf, sizeof buf, &keysym, 0);
-#ifdef KBD_DBG
- ri.Printf(PRINT_ALL, "XLookupString ret: %d buf: %s keysym: %x\n", XLookupRet, buf, keysym);
-#endif
-
- switch (keysym)
- {
- case XK_KP_Page_Up:
- case XK_KP_9: *key = K_KP_PGUP; break;
- case XK_Page_Up: *key = K_PGUP; break;
-
- case XK_KP_Page_Down:
- case XK_KP_3: *key = K_KP_PGDN; break;
- case XK_Page_Down: *key = K_PGDN; break;
-
- case XK_KP_Home: *key = K_KP_HOME; break;
- case XK_KP_7: *key = K_KP_HOME; break;
- case XK_Home: *key = K_HOME; break;
-
- case XK_KP_End:
- case XK_KP_1: *key = K_KP_END; break;
- case XK_End: *key = K_END; break;
-
- case XK_KP_Left: *key = K_KP_LEFTARROW; break;
- case XK_KP_4: *key = K_KP_LEFTARROW; break;
- case XK_Left: *key = K_LEFTARROW; break;
-
- case XK_KP_Right: *key = K_KP_RIGHTARROW; break;
- case XK_KP_6: *key = K_KP_RIGHTARROW; break;
- case XK_Right: *key = K_RIGHTARROW; break;
-
- case XK_KP_Down:
- case XK_KP_2: *key = K_KP_DOWNARROW; break;
- case XK_Down: *key = K_DOWNARROW; break;
-
- case XK_KP_Up:
- case XK_KP_8: *key = K_KP_UPARROW; break;
- case XK_Up: *key = K_UPARROW; break;
-
- case XK_Escape: *key = K_ESCAPE; break;
-
- case XK_KP_Enter: *key = K_KP_ENTER; break;
- case XK_Return: *key = K_ENTER; break;
-
- case XK_Tab: *key = K_TAB; break;
-
- case XK_F1: *key = K_F1; break;
-
- case XK_F2: *key = K_F2; break;
-
- case XK_F3: *key = K_F3; break;
-
- case XK_F4: *key = K_F4; break;
-
- case XK_F5: *key = K_F5; break;
-
- case XK_F6: *key = K_F6; break;
-
- case XK_F7: *key = K_F7; break;
-
- case XK_F8: *key = K_F8; break;
-
- case XK_F9: *key = K_F9; break;
-
- case XK_F10: *key = K_F10; break;
-
- case XK_F11: *key = K_F11; break;
-
- case XK_F12: *key = K_F12; break;
-
- // bk001206 - from Ryan's Fakk2
- //case XK_BackSpace: *key = 8; break; // ctrl-h
- case XK_BackSpace: *key = K_BACKSPACE; break; // ctrl-h
-
- case XK_KP_Delete:
- case XK_KP_Decimal: *key = K_KP_DEL; break;
- case XK_Delete: *key = K_DEL; break;
-
- case XK_Pause: *key = K_PAUSE; break;
-
- case XK_Shift_L:
- case XK_Shift_R: *key = K_SHIFT; break;
-
- case XK_Execute:
- case XK_Control_L:
- case XK_Control_R: *key = K_CTRL; break;
-
- case XK_Alt_L:
- case XK_Meta_L:
- case XK_Alt_R:
- case XK_Meta_R: *key = K_ALT; break;
-
- case XK_KP_Begin: *key = K_KP_5; break;
-
- case XK_Insert: *key = K_INS; break;
- case XK_KP_Insert:
- case XK_KP_0: *key = K_KP_INS; break;
-
- case XK_KP_Multiply: *key = '*'; break;
- case XK_KP_Add: *key = K_KP_PLUS; break;
- case XK_KP_Subtract: *key = K_KP_MINUS; break;
- case XK_KP_Divide: *key = K_KP_SLASH; break;
-
- // bk001130 - from cvs1.17 (mkv)
- case XK_exclam: *key = '1'; break;
- case XK_at: *key = '2'; break;
- case XK_numbersign: *key = '3'; break;
- case XK_dollar: *key = '4'; break;
- case XK_percent: *key = '5'; break;
- case XK_asciicircum: *key = '6'; break;
- case XK_ampersand: *key = '7'; break;
- case XK_asterisk: *key = '8'; break;
- case XK_parenleft: *key = '9'; break;
- case XK_parenright: *key = '0'; break;
-
- // weird french keyboards ..
- // NOTE: console toggle is hardcoded in cl_keys.c, can't be unbound
- // cleaner would be .. using hardware key codes instead of the key syms
- // could also add a new K_KP_CONSOLE
- case XK_twosuperior: *key = '~'; break;
-
- // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=472
- case XK_space:
- case XK_KP_Space: *key = K_SPACE; break;
-
- default:
- if (XLookupRet == 0)
- {
- if (com_developer->value)
- {
- ri.Printf(PRINT_ALL, "Warning: XLookupString failed on KeySym %d\n", keysym);
- }
- return NULL;
- }
- else
- {
- // XK_* tests failed, but XLookupString got a buffer, so let's try it
- *key = *(unsigned char *)buf;
- if (*key >= 'A' && *key <= 'Z')
- *key = *key - 'A' + 'a';
- // if ctrl is pressed, the keys are not between 'A' and 'Z', for instance ctrl-z == 26 ^Z ^C etc.
- // see https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=19
- else if (*key >= 1 && *key <= 26)
- *key = *key + 'a' - 1;
- }
- break;
- }
-
- return buf;
-}
-
-// ========================================================================
-// makes a null cursor
-// ========================================================================
-
-static Cursor CreateNullCursor(Display *display, Window root)
-{
- Pixmap cursormask;
- XGCValues xgc;
- GC gc;
- XColor dummycolour;
- Cursor cursor;
-
- cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/);
- xgc.function = GXclear;
- gc = XCreateGC(display, cursormask, GCFunction, &xgc);
- XFillRectangle(display, cursormask, gc, 0, 0, 1, 1);
- dummycolour.pixel = 0;
- dummycolour.red = 0;
- dummycolour.flags = 04;
- cursor = XCreatePixmapCursor(display, cursormask, cursormask,
- &dummycolour,&dummycolour, 0,0);
- XFreePixmap(display,cursormask);
- XFreeGC(display,gc);
- return cursor;
-}
-
-static void install_grabs(void)
-{
- // inviso cursor
- XWarpPointer(dpy, None, win,
- 0, 0, 0, 0,
- glConfig.vidWidth / 2, glConfig.vidHeight / 2);
- XSync(dpy, False);
-
- XDefineCursor(dpy, win, CreateNullCursor(dpy, win));
-
- XGrabPointer(dpy, win, // bk010108 - do this earlier?
- False,
- MOUSE_MASK,
- GrabModeAsync, GrabModeAsync,
- win,
- None,
- CurrentTime);
-
- XGetPointerControl(dpy, &mouse_accel_numerator, &mouse_accel_denominator,
- &mouse_threshold);
-
- XChangePointerControl(dpy, True, True, 1, 1, 0);
-
- XSync(dpy, False);
-
- mouseResetTime = Sys_Milliseconds ();
-
- if (in_dgamouse->value)
- {
- int MajorVersion, MinorVersion;
-
- if (!XF86DGAQueryVersion(dpy, &MajorVersion, &MinorVersion))
- {
- // unable to query, probalby not supported, force the setting to 0
- ri.Printf( PRINT_ALL, "Failed to detect XF86DGA Mouse\n" );
- ri.Cvar_Set( "in_dgamouse", "0" );
- } else
- {
- XF86DGADirectVideo(dpy, DefaultScreen(dpy), XF86DGADirectMouse);
- XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0);
- }
- } else
- {
- mwx = glConfig.vidWidth / 2;
- mwy = glConfig.vidHeight / 2;
- mx = my = 0;
- }
-
- XGrabKeyboard(dpy, win,
- False,
- GrabModeAsync, GrabModeAsync,
- CurrentTime);
-
- XSync(dpy, False);
-}
-
-static void uninstall_grabs(void)
-{
- if (in_dgamouse->value)
- {
- if (com_developer->value)
- ri.Printf( PRINT_ALL, "DGA Mouse - Disabling DGA DirectVideo\n" );
- XF86DGADirectVideo(dpy, DefaultScreen(dpy), 0);
- }
-
- XChangePointerControl(dpy, qtrue, qtrue, mouse_accel_numerator,
- mouse_accel_denominator, mouse_threshold);
-
- XUngrabPointer(dpy, CurrentTime);
- XUngrabKeyboard(dpy, CurrentTime);
-
- XWarpPointer(dpy, None, win,
- 0, 0, 0, 0,
- glConfig.vidWidth / 2, glConfig.vidHeight / 2);
-
- // inviso cursor
- XUndefineCursor(dpy, win);
-}
-
-// bk001206 - from Ryan's Fakk2
-/**
- * XPending() actually performs a blocking read
- * if no events available. From Fakk2, by way of
- * Heretic2, by way of SDL, original idea GGI project.
- * The benefit of this approach over the quite
- * badly behaved XAutoRepeatOn/Off is that you get
- * focus handling for free, which is a major win
- * with debug and windowed mode. It rests on the
- * assumption that the X server will use the
- * same timestamp on press/release event pairs
- * for key repeats.
- */
-static qboolean X11_PendingInput(void) {
-
- assert(dpy != NULL);
-
- // Flush the display connection
- // and look to see if events are queued
- XFlush( dpy );
- if ( XEventsQueued( dpy, QueuedAlready) )
- {
- return qtrue;
- }
-
- // More drastic measures are required -- see if X is ready to talk
- {
- static struct timeval zero_time;
- int x11_fd;
- fd_set fdset;
-
- x11_fd = ConnectionNumber( dpy );
- FD_ZERO(&fdset);
- FD_SET(x11_fd, &fdset);
- if ( select(x11_fd+1, &fdset, NULL, NULL, &zero_time) == 1 )
- {
- return(XPending(dpy));
- }
- }
-
- // Oh well, nothing is ready ..
- return qfalse;
-}
-
-// bk001206 - from Ryan's Fakk2. See above.
-static qboolean repeated_press(XEvent *event)
-{
- XEvent peekevent;
- qboolean repeated = qfalse;
-
- assert(dpy != NULL);
-
- if (X11_PendingInput())
- {
- XPeekEvent(dpy, &peekevent);
-
- if ((peekevent.type == KeyPress) &&
- (peekevent.xkey.keycode == event->xkey.keycode) &&
- (peekevent.xkey.time == event->xkey.time))
- {
- repeated = qtrue;
- XNextEvent(dpy, &peekevent); // skip event.
- } // if
- } // if
-
- return(repeated);
-} // repeated_press
-
-int Sys_XTimeToSysTime (Time xtime);
-static void HandleEvents(void)
-{
- int b;
- int key;
- XEvent event;
- qboolean dowarp = qfalse;
- char *p;
- int dx, dy;
- int t = 0; // default to 0 in case we don't set
-
- if (!dpy)
- return;
-
- while (XPending(dpy))
- {
- XNextEvent(dpy, &event);
- switch (event.type)
- {
- case KeyPress:
- t = Sys_XTimeToSysTime(event.xkey.time);
- p = XLateKey(&event.xkey, &key);
- if (key)
- {
- Sys_QueEvent( t, SE_KEY, key, qtrue, 0, NULL );
- }
- if (p)
- {
- while (*p)
- {
- Sys_QueEvent( t, SE_CHAR, *p++, 0, 0, NULL );
- }
- }
- break;
-
- case KeyRelease:
- t = Sys_XTimeToSysTime(event.xkey.time);
- // bk001206 - handle key repeat w/o XAutRepatOn/Off
- // also: not done if console/menu is active.
- // From Ryan's Fakk2.
- // see game/q_shared.h, KEYCATCH_* . 0 == in 3d game.
- if (cls.keyCatchers == 0)
- { // FIXME: KEYCATCH_NONE
- if (repeated_press(&event) == qtrue)
- continue;
- } // if
- XLateKey(&event.xkey, &key);
-
- Sys_QueEvent( t, SE_KEY, key, qfalse, 0, NULL );
- break;
-
- case MotionNotify:
- t = Sys_XTimeToSysTime(event.xkey.time);
- if (mouse_active)
- {
- if (in_dgamouse->value)
- {
- if (abs(event.xmotion.x_root) > 1)
- mx += event.xmotion.x_root * 2;
- else
- mx += event.xmotion.x_root;
- if (abs(event.xmotion.y_root) > 1)
- my += event.xmotion.y_root * 2;
- else
- my += event.xmotion.y_root;
- if (t - mouseResetTime > MOUSE_RESET_DELAY )
- {
- Sys_QueEvent( t, SE_MOUSE, mx, my, 0, NULL );
- }
- mx = my = 0;
- } else
- {
- // If it's a center motion, we've just returned from our warp
- if (event.xmotion.x == glConfig.vidWidth/2 &&
- event.xmotion.y == glConfig.vidHeight/2)
- {
- mwx = glConfig.vidWidth/2;
- mwy = glConfig.vidHeight/2;
- if (t - mouseResetTime > MOUSE_RESET_DELAY )
- {
- Sys_QueEvent( t, SE_MOUSE, mx, my, 0, NULL );
- }
- mx = my = 0;
- break;
- }
-
- dx = ((int)event.xmotion.x - mwx);
- dy = ((int)event.xmotion.y - mwy);
- if (abs(dx) > 1)
- mx += dx * 2;
- else
- mx += dx;
- if (abs(dy) > 1)
- my += dy * 2;
- else
- my += dy;
-
- mwx = event.xmotion.x;
- mwy = event.xmotion.y;
- dowarp = qtrue;
- }
- }
- break;
-
- case ButtonPress:
- t = Sys_XTimeToSysTime(event.xkey.time);
- if (event.xbutton.button == 4)
- {
- Sys_QueEvent( t, SE_KEY, K_MWHEELUP, qtrue, 0, NULL );
- } else if (event.xbutton.button == 5)
- {
- Sys_QueEvent( t, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL );
- } else
- {
- // NOTE TTimo there seems to be a weird mapping for K_MOUSE1 K_MOUSE2 K_MOUSE3 ..
- b=-1;
- if (event.xbutton.button == 1)
- {
- b = 0; // K_MOUSE1
- } else if (event.xbutton.button == 2)
- {
- b = 2; // K_MOUSE3
- } else if (event.xbutton.button == 3)
- {
- b = 1; // K_MOUSE2
- } else if (event.xbutton.button == 6)
- {
- b = 3; // K_MOUSE4
- } else if (event.xbutton.button == 7)
- {
- b = 4; // K_MOUSE5
- };
-
- Sys_QueEvent( t, SE_KEY, K_MOUSE1 + b, qtrue, 0, NULL );
- }
- break;
-
- case ButtonRelease:
- t = Sys_XTimeToSysTime(event.xkey.time);
- if (event.xbutton.button == 4)
- {
- Sys_QueEvent( t, SE_KEY, K_MWHEELUP, qfalse, 0, NULL );
- } else if (event.xbutton.button == 5)
- {
- Sys_QueEvent( t, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL );
- } else
- {
- b=-1;
- if (event.xbutton.button == 1)
- {
- b = 0;
- } else if (event.xbutton.button == 2)
- {
- b = 2;
- } else if (event.xbutton.button == 3)
- {
- b = 1;
- } else if (event.xbutton.button == 6)
- {
- b = 3; // K_MOUSE4
- } else if (event.xbutton.button == 7)
- {
- b = 4; // K_MOUSE5
- };
- Sys_QueEvent( t, SE_KEY, K_MOUSE1 + b, qfalse, 0, NULL );
- }
- break;
-
- case CreateNotify :
- win_x = event.xcreatewindow.x;
- win_y = event.xcreatewindow.y;
- break;
-
- case ConfigureNotify :
- win_x = event.xconfigure.x;
- win_y = event.xconfigure.y;
- break;
- }
- }
-
- if (dowarp)
- {
- XWarpPointer(dpy,None,win,0,0,0,0,
- (glConfig.vidWidth/2),(glConfig.vidHeight/2));
- }
-}
-
-// NOTE TTimo for the tty console input, we didn't rely on those ..
-// it's not very surprising actually cause they are not used otherwise
-void KBD_Init(void)
-{
-}
-
-void KBD_Close(void)
-{
-}
-
-void IN_ActivateMouse( void )
-{
- if (!mouse_avail || !dpy || !win)
- return;
-
- if (!mouse_active)
- {
- if (!in_nograb->value)
- install_grabs();
- else if (in_dgamouse->value) // force dga mouse to 0 if using nograb
- ri.Cvar_Set("in_dgamouse", "0");
- mouse_active = qtrue;
- }
-}
-
-void IN_DeactivateMouse( void )
-{
- if (!mouse_avail || !dpy || !win)
- return;
-
- if (mouse_active)
- {
- if (!in_nograb->value)
- uninstall_grabs();
- else if (in_dgamouse->value) // force dga mouse to 0 if using nograb
- ri.Cvar_Set("in_dgamouse", "0");
- mouse_active = qfalse;
- }
-}
-/*****************************************************************************/
-
-/*
-** GLimp_SetGamma
-**
-** This routine should only be called if glConfig.deviceSupportsGamma is TRUE
-*/
-void GLimp_SetGamma( unsigned char red[256], unsigned char green[256], unsigned char blue[256] )
-{
- // NOTE TTimo we get the gamma value from cvar, because we can't work with the s_gammatable
- // the API wasn't changed to avoid breaking other OSes
- float g = Cvar_Get("r_gamma", "1.0", 0)->value;
- XF86VidModeGamma gamma;
- assert(glConfig.deviceSupportsGamma);
- gamma.red = g;
- gamma.green = g;
- gamma.blue = g;
- XF86VidModeSetGamma(dpy, scrnum, &gamma);
-}
-
-/*
-** GLimp_Shutdown
-**
-** This routine does all OS specific shutdown procedures for the OpenGL
-** subsystem. Under OpenGL this means NULLing out the current DC and
-** HGLRC, deleting the rendering context, and releasing the DC acquired
-** for the window. The state structure is also nulled out.
-**
-*/
-void GLimp_Shutdown( void )
-{
- if (!ctx || !dpy)
- return;
- IN_DeactivateMouse();
- // bk001206 - replaced with H2/Fakk2 solution
- // XAutoRepeatOn(dpy);
- // autorepeaton = qfalse; // bk001130 - from cvs1.17 (mkv)
- if (dpy)
- {
- if (ctx)
- qglXDestroyContext(dpy, ctx);
- if (win)
- XDestroyWindow(dpy, win);
- if (vidmode_active)
- XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[0]);
- if (glConfig.deviceSupportsGamma)
- {
- XF86VidModeSetGamma(dpy, scrnum, &vidmode_InitialGamma);
- }
- // NOTE TTimo opening/closing the display should be necessary only once per run
- // but it seems QGL_Shutdown gets called in a lot of occasion
- // in some cases, this XCloseDisplay is known to raise some X errors
- // ( https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=33 )
- XCloseDisplay(dpy);
- }
- vidmode_active = qfalse;
- dpy = NULL;
- win = 0;
- ctx = NULL;
-
- memset( &glConfig, 0, sizeof( glConfig ) );
- memset( &glState, 0, sizeof( glState ) );
-
- QGL_Shutdown();
-}
-
-/*
-** GLimp_LogComment
-*/
-void GLimp_LogComment( char *comment )
-{
- if ( glw_state.log_fp )
- {
- fprintf( glw_state.log_fp, "%s", comment );
- }
-}
-
-/*
-** GLW_StartDriverAndSetMode
-*/
-// bk001204 - prototype needed
-int GLW_SetMode( const char *drivername, int mode, qboolean fullscreen );
-static qboolean GLW_StartDriverAndSetMode( const char *drivername,
- int mode,
- qboolean fullscreen )
-{
- rserr_t err;
-
- // don't ever bother going into fullscreen with a voodoo card
-#if 1 // JDC: I reenabled this
- if ( Q_stristr( drivername, "Voodoo" ) )
- {
- ri.Cvar_Set( "r_fullscreen", "0" );
- r_fullscreen->modified = qfalse;
- fullscreen = qfalse;
- }
-#endif
-
- if (fullscreen && in_nograb->value)
- {
- ri.Printf( PRINT_ALL, "Fullscreen not allowed with in_nograb 1\n");
- ri.Cvar_Set( "r_fullscreen", "0" );
- r_fullscreen->modified = qfalse;
- fullscreen = qfalse;
- }
-
- err = GLW_SetMode( drivername, mode, fullscreen );
-
- switch ( err )
- {
- case RSERR_INVALID_FULLSCREEN:
- ri.Printf( PRINT_ALL, "...WARNING: fullscreen unavailable in this mode\n" );
- return qfalse;
- case RSERR_INVALID_MODE:
- ri.Printf( PRINT_ALL, "...WARNING: could not set the given mode (%d)\n", mode );
- return qfalse;
- default:
- break;
- }
- return qtrue;
-}
-
-/*
-** GLW_SetMode
-*/
-int GLW_SetMode( const char *drivername, int mode, qboolean fullscreen )
-{
- int attrib[] = {
- GLX_RGBA, // 0
- GLX_RED_SIZE, 4, // 1, 2
- GLX_GREEN_SIZE, 4, // 3, 4
- GLX_BLUE_SIZE, 4, // 5, 6
- GLX_DOUBLEBUFFER, // 7
- GLX_DEPTH_SIZE, 1, // 8, 9
- GLX_STENCIL_SIZE, 1, // 10, 11
- None
- };
- // these match in the array
-#define ATTR_RED_IDX 2
-#define ATTR_GREEN_IDX 4
-#define ATTR_BLUE_IDX 6
-#define ATTR_DEPTH_IDX 9
-#define ATTR_STENCIL_IDX 11
- Window root;
- XVisualInfo *visinfo;
- XSetWindowAttributes attr;
- XSizeHints sizehints;
- unsigned long mask;
- int colorbits, depthbits, stencilbits;
- int tcolorbits, tdepthbits, tstencilbits;
- int dga_MajorVersion, dga_MinorVersion;
- int actualWidth, actualHeight;
- int i;
- const char* glstring; // bk001130 - from cvs1.17 (mkv)
-
- ri.Printf( PRINT_ALL, "Initializing OpenGL display\n");
-
- ri.Printf (PRINT_ALL, "...setting mode %d:", mode );
-
- if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, mode ) )
- {
- ri.Printf( PRINT_ALL, " invalid mode\n" );
- return RSERR_INVALID_MODE;
- }
- ri.Printf( PRINT_ALL, " %d %d\n", glConfig.vidWidth, glConfig.vidHeight);
-
- if (!(dpy = XOpenDisplay(NULL)))
- {
- fprintf(stderr, "Error couldn't open the X display\n");
- return RSERR_INVALID_MODE;
- }
-
- scrnum = DefaultScreen(dpy);
- root = RootWindow(dpy, scrnum);
-
- actualWidth = glConfig.vidWidth;
- actualHeight = glConfig.vidHeight;
-
- // Get video mode list
- if (!XF86VidModeQueryVersion(dpy, &vidmode_MajorVersion, &vidmode_MinorVersion))
- {
- vidmode_ext = qfalse;
- } else
- {
- ri.Printf(PRINT_ALL, "Using XFree86-VidModeExtension Version %d.%d\n",
- vidmode_MajorVersion, vidmode_MinorVersion);
- vidmode_ext = qtrue;
- }
-
- // Check for DGA
- dga_MajorVersion = 0, dga_MinorVersion = 0;
- if (in_dgamouse->value)
- {
- if (!XF86DGAQueryVersion(dpy, &dga_MajorVersion, &dga_MinorVersion))
- {
- // unable to query, probalby not supported
- ri.Printf( PRINT_ALL, "Failed to detect XF86DGA Mouse\n" );
- ri.Cvar_Set( "in_dgamouse", "0" );
- } else
- {
- ri.Printf( PRINT_ALL, "XF86DGA Mouse (Version %d.%d) initialized\n",
- dga_MajorVersion, dga_MinorVersion);
- }
- }
-
- if (vidmode_ext)
- {
- int best_fit, best_dist, dist, x, y;
-
- XF86VidModeGetAllModeLines(dpy, scrnum, &num_vidmodes, &vidmodes);
-
- // Are we going fullscreen? If so, let's change video mode
- if (fullscreen)
- {
- best_dist = 9999999;
- best_fit = -1;
-
- for (i = 0; i < num_vidmodes; i++)
- {
- if (glConfig.vidWidth > vidmodes[i]->hdisplay ||
- glConfig.vidHeight > vidmodes[i]->vdisplay)
- continue;
-
- x = glConfig.vidWidth - vidmodes[i]->hdisplay;
- y = glConfig.vidHeight - vidmodes[i]->vdisplay;
- dist = (x * x) + (y * y);
- if (dist < best_dist)
- {
- best_dist = dist;
- best_fit = i;
- }
- }
-
- if (best_fit != -1)
- {
- actualWidth = vidmodes[best_fit]->hdisplay;
- actualHeight = vidmodes[best_fit]->vdisplay;
-
- // change to the mode
- XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]);
- vidmode_active = qtrue;
-
- // Move the viewport to top left
- XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
-
- ri.Printf(PRINT_ALL, "XFree86-VidModeExtension Activated at %dx%d\n",
- actualWidth, actualHeight);
-
- } else
- {
- fullscreen = 0;
- ri.Printf(PRINT_ALL, "XFree86-VidModeExtension: No acceptable modes found\n");
- }
- } else
- {
- ri.Printf(PRINT_ALL, "XFree86-VidModeExtension: Ignored on non-fullscreen/Voodoo\n");
- }
- }
-
-
- if (!r_colorbits->value)
- colorbits = 24;
- else
- colorbits = r_colorbits->value;
-
- if ( !Q_stricmp( r_glDriver->string, _3DFX_DRIVER_NAME ) )
- colorbits = 16;
-
- if (!r_depthbits->value)
- depthbits = 24;
- else
- depthbits = r_depthbits->value;
- stencilbits = r_stencilbits->value;
-
- for (i = 0; i < 16; i++)
- {
- // 0 - default
- // 1 - minus colorbits
- // 2 - minus depthbits
- // 3 - minus stencil
- if ((i % 4) == 0 && i)
- {
- // one pass, reduce
- switch (i / 4)
- {
- case 2 :
- if (colorbits == 24)
- colorbits = 16;
- break;
- case 1 :
- if (depthbits == 24)
- depthbits = 16;
- else if (depthbits == 16)
- depthbits = 8;
- case 3 :
- if (stencilbits == 24)
- stencilbits = 16;
- else if (stencilbits == 16)
- stencilbits = 8;
- }
- }
-
- tcolorbits = colorbits;
- tdepthbits = depthbits;
- tstencilbits = stencilbits;
-
- if ((i % 4) == 3)
- { // reduce colorbits
- if (tcolorbits == 24)
- tcolorbits = 16;
- }
-
- if ((i % 4) == 2)
- { // reduce depthbits
- if (tdepthbits == 24)
- tdepthbits = 16;
- else if (tdepthbits == 16)
- tdepthbits = 8;
- }
-
- if ((i % 4) == 1)
- { // reduce stencilbits
- if (tstencilbits == 24)
- tstencilbits = 16;
- else if (tstencilbits == 16)
- tstencilbits = 8;
- else
- tstencilbits = 0;
- }
-
- if (tcolorbits == 24)
- {
- attrib[ATTR_RED_IDX] = 8;
- attrib[ATTR_GREEN_IDX] = 8;
- attrib[ATTR_BLUE_IDX] = 8;
- } else
- {
- // must be 16 bit
- attrib[ATTR_RED_IDX] = 4;
- attrib[ATTR_GREEN_IDX] = 4;
- attrib[ATTR_BLUE_IDX] = 4;
- }
-
- attrib[ATTR_DEPTH_IDX] = tdepthbits; // default to 24 depth
- attrib[ATTR_STENCIL_IDX] = tstencilbits;
-
- visinfo = qglXChooseVisual(dpy, scrnum, attrib);
- if (!visinfo)
- {
- continue;
- }
-
- ri.Printf( PRINT_ALL, "Using %d/%d/%d Color bits, %d depth, %d stencil display.\n",
- attrib[ATTR_RED_IDX], attrib[ATTR_GREEN_IDX], attrib[ATTR_BLUE_IDX],
- attrib[ATTR_DEPTH_IDX], attrib[ATTR_STENCIL_IDX]);
-
- glConfig.colorBits = tcolorbits;
- glConfig.depthBits = tdepthbits;
- glConfig.stencilBits = tstencilbits;
- break;
- }
-
- if (!visinfo)
- {
- ri.Printf( PRINT_ALL, "Couldn't get a visual\n" );
- return RSERR_INVALID_MODE;
- }
-
- /* window attributes */
- attr.background_pixel = BlackPixel(dpy, scrnum);
- attr.border_pixel = 0;
- attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
- attr.event_mask = X_MASK;
- if (vidmode_active)
- {
- mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore |
- CWEventMask | CWOverrideRedirect;
- attr.override_redirect = True;
- attr.backing_store = NotUseful;
- attr.save_under = False;
- } else
- mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
-
- win = XCreateWindow(dpy, root, 0, 0,
- actualWidth, actualHeight,
- 0, visinfo->depth, InputOutput,
- visinfo->visual, mask, &attr);
-
- XStoreName( dpy, win, WINDOW_CLASS_NAME );
-
- /* GH: Don't let the window be resized */
- sizehints.flags = PMinSize | PMaxSize;
- sizehints.min_width = sizehints.max_width = actualWidth;
- sizehints.min_height = sizehints.max_height = actualHeight;
-
- XSetWMNormalHints( dpy, win, &sizehints );
-
- XMapWindow( dpy, win );
-
- if (vidmode_active)
- XMoveWindow(dpy, win, 0, 0);
-
- XFlush(dpy);
- XSync(dpy,False); // bk001130 - from cvs1.17 (mkv)
- ctx = qglXCreateContext(dpy, visinfo, NULL, True);
- XSync(dpy,False); // bk001130 - from cvs1.17 (mkv)
-
- /* GH: Free the visinfo after we're done with it */
- XFree( visinfo );
-
- qglXMakeCurrent(dpy, win, ctx);
-
- // bk001130 - from cvs1.17 (mkv)
- glstring = qglGetString (GL_RENDERER);
- ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glstring );
-
- // bk010122 - new software token (Indirect)
- if ( !Q_stricmp( glstring, "Mesa X11")
- || !Q_stricmp( glstring, "Mesa GLX Indirect") )
- {
- if ( !r_allowSoftwareGL->integer )
- {
- ri.Printf( PRINT_ALL, "\n\n***********************************************************\n" );
- ri.Printf( PRINT_ALL, " You are using software Mesa (no hardware acceleration)! \n" );
- ri.Printf( PRINT_ALL, " Driver DLL used: %s\n", drivername );
- ri.Printf( PRINT_ALL, " If this is intentional, add\n" );
- ri.Printf( PRINT_ALL, " \"+set r_allowSoftwareGL 1\"\n" );
- ri.Printf( PRINT_ALL, " to the command line when starting the game.\n" );
- ri.Printf( PRINT_ALL, "***********************************************************\n");
- GLimp_Shutdown( );
- return RSERR_INVALID_MODE;
- } else
- {
- ri.Printf( PRINT_ALL, "...using software Mesa (r_allowSoftwareGL==1).\n" );
- }
- }
-
- return RSERR_OK;
-}
-
-/*
-** GLW_InitExtensions
-*/
-static void GLW_InitExtensions( void )
-{
- if ( !r_allowExtensions->integer )
- {
- ri.Printf( PRINT_ALL, "*** IGNORING OPENGL EXTENSIONS ***\n" );
- return;
- }
-
- ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" );
-
- // GL_S3_s3tc
- if ( Q_stristr( glConfig.extensions_string, "GL_S3_s3tc" ) )
- {
- if ( r_ext_compressed_textures->value )
- {
- glConfig.textureCompression = TC_S3TC;
- ri.Printf( PRINT_ALL, "...using GL_S3_s3tc\n" );
- } else
- {
- glConfig.textureCompression = TC_NONE;
- ri.Printf( PRINT_ALL, "...ignoring GL_S3_s3tc\n" );
- }
- } else
- {
- glConfig.textureCompression = TC_NONE;
- ri.Printf( PRINT_ALL, "...GL_S3_s3tc not found\n" );
- }
-
- // GL_EXT_texture_env_add
- glConfig.textureEnvAddAvailable = qfalse;
- if ( Q_stristr( glConfig.extensions_string, "EXT_texture_env_add" ) )
- {
- if ( r_ext_texture_env_add->integer )
- {
- glConfig.textureEnvAddAvailable = qtrue;
- ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" );
- } else
- {
- glConfig.textureEnvAddAvailable = qfalse;
- ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" );
- }
- } else
- {
- ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" );
- }
-
- // GL_ARB_multitexture
- qglMultiTexCoord2fARB = NULL;
- qglActiveTextureARB = NULL;
- qglClientActiveTextureARB = NULL;
- if ( Q_stristr( glConfig.extensions_string, "GL_ARB_multitexture" ) )
- {
- if ( r_ext_multitexture->value )
- {
- qglMultiTexCoord2fARB = ( PFNGLMULTITEXCOORD2FARBPROC ) dlsym( glw_state.OpenGLLib, "glMultiTexCoord2fARB" );
- qglActiveTextureARB = ( PFNGLACTIVETEXTUREARBPROC ) dlsym( glw_state.OpenGLLib, "glActiveTextureARB" );
- qglClientActiveTextureARB = ( PFNGLCLIENTACTIVETEXTUREARBPROC ) dlsym( glw_state.OpenGLLib, "glClientActiveTextureARB" );
-
- if ( qglActiveTextureARB )
- {
- qglGetIntegerv( GL_MAX_ACTIVE_TEXTURES_ARB, &glConfig.maxActiveTextures );
-
- if ( glConfig.maxActiveTextures > 1 )
- {
- ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" );
- } else
- {
- qglMultiTexCoord2fARB = NULL;
- qglActiveTextureARB = NULL;
- qglClientActiveTextureARB = NULL;
- ri.Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" );
- }
- }
- } else
- {
- ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" );
- }
- } else
- {
- ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" );
- }
-
- // GL_EXT_compiled_vertex_array
- if ( Q_stristr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) )
- {
- if ( r_ext_compiled_vertex_array->value )
- {
- ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" );
- qglLockArraysEXT = ( void ( APIENTRY * )( int, int ) ) dlsym( glw_state.OpenGLLib, "glLockArraysEXT" );
- qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) dlsym( glw_state.OpenGLLib, "glUnlockArraysEXT" );
- if (!qglLockArraysEXT || !qglUnlockArraysEXT)
- {
- ri.Error (ERR_FATAL, "bad getprocaddress");
- }
- } else
- {
- ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" );
- }
- } else
- {
- ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" );
- }
-
-}
-
-static void GLW_InitGamma()
-{
- /* Minimum extension version required */
- #define GAMMA_MINMAJOR 2
- #define GAMMA_MINMINOR 0
-
- glConfig.deviceSupportsGamma = qfalse;
-
- if (vidmode_ext)
- {
- if (vidmode_MajorVersion < GAMMA_MINMAJOR ||
- (vidmode_MajorVersion == GAMMA_MINMAJOR && vidmode_MinorVersion < GAMMA_MINMINOR)) {
- ri.Printf( PRINT_ALL, "XF86 Gamma extension not supported in this version\n");
- return;
- }
- XF86VidModeGetGamma(dpy, scrnum, &vidmode_InitialGamma);
- ri.Printf( PRINT_ALL, "XF86 Gamma extension initialized\n");
- glConfig.deviceSupportsGamma = qtrue;
- }
-}
-
-/*
-** GLW_LoadOpenGL
-**
-** GLimp_win.c internal function that that attempts to load and use
-** a specific OpenGL DLL.
-*/
-static qboolean GLW_LoadOpenGL( const char *name )
-{
- qboolean fullscreen;
-
- ri.Printf( PRINT_ALL, "...loading %s: ", name );
-
- // disable the 3Dfx splash screen and set gamma
- // we do this all the time, but it shouldn't hurt anything
- // on non-3Dfx stuff
- putenv("FX_GLIDE_NO_SPLASH=0");
-
- // Mesa VooDoo hacks
- putenv("MESA_GLX_FX=fullscreen\n");
-
- // load the QGL layer
- if ( QGL_Init( name ) )
- {
- fullscreen = r_fullscreen->integer;
-
- // create the window and set up the context
- if ( !GLW_StartDriverAndSetMode( name, r_mode->integer, fullscreen ) )
- {
- if (r_mode->integer != 3)
- {
- if ( !GLW_StartDriverAndSetMode( name, 3, fullscreen ) )
- {
- goto fail;
- }
- } else
- goto fail;
- }
-
- return qtrue;
- } else
- {
- ri.Printf( PRINT_ALL, "failed\n" );
- }
- fail:
-
- QGL_Shutdown();
-
- return qfalse;
-}
-
-/*
-** XErrorHandler
-** the default X error handler exits the application
-** I found out that on some hosts some operations would raise X errors (GLXUnsupportedPrivateRequest)
-** but those don't seem to be fatal .. so the default would be to just ignore them
-** our implementation mimics the default handler behaviour (not completely cause I'm lazy)
-*/
-int qXErrorHandler(Display *dpy, XErrorEvent *ev)
-{
- static char buf[1024];
- XGetErrorText(dpy, ev->error_code, buf, 1024);
- ri.Printf( PRINT_ALL, "X Error of failed request: %s\n", buf);
- ri.Printf( PRINT_ALL, " Major opcode of failed request: %d\n", ev->request_code, buf);
- ri.Printf( PRINT_ALL, " Minor opcode of failed request: %d\n", ev->minor_code);
- ri.Printf( PRINT_ALL, " Serial number of failed request: %d\n", ev->serial);
- return 0;
-}
-
-/*
-** GLimp_Init
-**
-** This routine is responsible for initializing the OS specific portions
-** of OpenGL.
-*/
-void GLimp_Init( void )
-{
- qboolean attemptedlibGL = qfalse;
- qboolean attempted3Dfx = qfalse;
- qboolean success = qfalse;
- char buf[1024];
- cvar_t *lastValidRenderer = ri.Cvar_Get( "r_lastValidRenderer", "(uninitialized)", CVAR_ARCHIVE );
-
- // guarded, as this is only relevant to SMP renderer thread
-#ifdef SMP
- if (!XInitThreads())
- {
- Com_Printf("GLimp_Init() - XInitThreads() failed, disabling r_smp\n");
- ri.Cvar_Set( "r_smp", "0" );
- }
-#endif
-
- r_allowSoftwareGL = ri.Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH );
-
- r_previousglDriver = ri.Cvar_Get( "r_previousglDriver", "", CVAR_ROM );
-
- InitSig();
-
- // Hack here so that if the UI
- if ( *r_previousglDriver->string )
- {
- // The UI changed it on us, hack it back
- // This means the renderer can't be changed on the fly
- ri.Cvar_Set( "r_glDriver", r_previousglDriver->string );
- }
-
- // set up our custom error handler for X failures
- XSetErrorHandler(&qXErrorHandler);
-
- //
- // load and initialize the specific OpenGL driver
- //
- if ( !GLW_LoadOpenGL( r_glDriver->string ) )
- {
- if ( !Q_stricmp( r_glDriver->string, OPENGL_DRIVER_NAME ) )
- {
- attemptedlibGL = qtrue;
- } else if ( !Q_stricmp( r_glDriver->string, _3DFX_DRIVER_NAME ) )
- {
- attempted3Dfx = qtrue;
- }
-
- #if 0
- // TTimo
- // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=455
- // old legacy load code, was confusing people who had a bad OpenGL setup
- if ( !attempted3Dfx && !success )
- {
- attempted3Dfx = qtrue;
- if ( GLW_LoadOpenGL( _3DFX_DRIVER_NAME ) )
- {
- ri.Cvar_Set( "r_glDriver", _3DFX_DRIVER_NAME );
- r_glDriver->modified = qfalse;
- success = qtrue;
- }
- }
- #endif
-
- // try ICD before trying 3Dfx standalone driver
- if ( !attemptedlibGL && !success )
- {
- attemptedlibGL = qtrue;
- if ( GLW_LoadOpenGL( OPENGL_DRIVER_NAME ) )
- {
- ri.Cvar_Set( "r_glDriver", OPENGL_DRIVER_NAME );
- r_glDriver->modified = qfalse;
- success = qtrue;
- }
- }
-
- if (!success)
- ri.Error( ERR_FATAL, "GLimp_Init() - could not load OpenGL subsystem\n" );
-
- }
-
- // Save it in case the UI stomps it
- ri.Cvar_Set( "r_previousglDriver", r_glDriver->string );
-
- // This values force the UI to disable driver selection
- glConfig.driverType = GLDRV_ICD;
- glConfig.hardwareType = GLHW_GENERIC;
-
- // get our config strings
- Q_strncpyz( glConfig.vendor_string, qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) );
- Q_strncpyz( glConfig.renderer_string, qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) );
- if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n')
- glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0;
- Q_strncpyz( glConfig.version_string, qglGetString (GL_VERSION), sizeof( glConfig.version_string ) );
- Q_strncpyz( glConfig.extensions_string, qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) );
-
- //
- // chipset specific configuration
- //
- strcpy( buf, glConfig.renderer_string );
- strlwr( buf );
-
- //
- // NOTE: if changing cvars, do it within this block. This allows them
- // to be overridden when testing driver fixes, etc. but only sets
- // them to their default state when the hardware is first installed/run.
- //
- if ( Q_stricmp( lastValidRenderer->string, glConfig.renderer_string ) )
- {
- glConfig.hardwareType = GLHW_GENERIC;
-
- ri.Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST" );
-
- // VOODOO GRAPHICS w/ 2MB
- if ( Q_stristr( buf, "voodoo graphics/1 tmu/2 mb" ) )
- {
- ri.Cvar_Set( "r_picmip", "2" );
- ri.Cvar_Get( "r_picmip", "1", CVAR_ARCHIVE | CVAR_LATCH );
- } else
- {
- ri.Cvar_Set( "r_picmip", "1" );
-
- if ( Q_stristr( buf, "rage 128" ) || Q_stristr( buf, "rage128" ) )
- {
- ri.Cvar_Set( "r_finish", "0" );
- }
- // Savage3D and Savage4 should always have trilinear enabled
- else if ( Q_stristr( buf, "savage3d" ) || Q_stristr( buf, "s3 savage4" ) )
- {
- ri.Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" );
- }
- }
- }
-
- //
- // this is where hardware specific workarounds that should be
- // detected/initialized every startup should go.
- //
- if ( Q_stristr( buf, "banshee" ) || Q_stristr( buf, "Voodoo_Graphics" ) )
- {
- glConfig.hardwareType = GLHW_3DFX_2D3D;
- } else if ( Q_stristr( buf, "rage pro" ) || Q_stristr( buf, "RagePro" ) )
- {
- glConfig.hardwareType = GLHW_RAGEPRO;
- } else if ( Q_stristr( buf, "permedia2" ) )
- {
- glConfig.hardwareType = GLHW_PERMEDIA2;
- } else if ( Q_stristr( buf, "riva 128" ) )
- {
- glConfig.hardwareType = GLHW_RIVA128;
- } else if ( Q_stristr( buf, "riva tnt " ) )
- {
- }
-
- ri.Cvar_Set( "r_lastValidRenderer", glConfig.renderer_string );
-
- // initialize extensions
- GLW_InitExtensions();
- GLW_InitGamma();
-
- InitSig(); // not clear why this is at begin & end of function
-
- return;
-}
-
-
-/*
-** GLimp_EndFrame
-**
-** Responsible for doing a swapbuffers and possibly for other stuff
-** as yet to be determined. Probably better not to make this a GLimp
-** function and instead do a call to GLimp_SwapBuffers.
-*/
-void GLimp_EndFrame (void)
-{
- // don't flip if drawing to front buffer
- if ( stricmp( r_drawBuffer->string, "GL_FRONT" ) != 0 )
- {
- qglXSwapBuffers(dpy, win);
- }
-
- // check logging
- QGL_EnableLogging( (qboolean)r_logFile->integer ); // bk001205 - was ->value
-}
-
-#ifdef SMP
-/*
-===========================================================
-
-SMP acceleration
-
-===========================================================
-*/
-
-static pthread_mutex_t smpMutex = PTHREAD_MUTEX_INITIALIZER;
-
-static pthread_cond_t renderCommandsEvent = PTHREAD_COND_INITIALIZER;
-static pthread_cond_t renderCompletedEvent = PTHREAD_COND_INITIALIZER;
-
-static void (*glimpRenderThread)( void );
-
-static void *GLimp_RenderThreadWrapper( void *arg )
-{
- Com_Printf( "Render thread starting\n" );
-
- glimpRenderThread();
-
- qglXMakeCurrent( dpy, None, NULL );
-
- Com_Printf( "Render thread terminating\n" );
-
- return arg;
-}
-
-qboolean GLimp_SpawnRenderThread( void (*function)( void ) )
-{
- pthread_t renderThread;
- int ret;
-
- pthread_mutex_init( &smpMutex, NULL );
-
- pthread_cond_init( &renderCommandsEvent, NULL );
- pthread_cond_init( &renderCompletedEvent, NULL );
-
- glimpRenderThread = function;
-
- ret = pthread_create( &renderThread,
- NULL, // attributes
- GLimp_RenderThreadWrapper,
- NULL ); // argument
- if ( ret ) {
- ri.Printf( PRINT_ALL, "pthread_create returned %d: %s", ret, strerror( ret ) );
- return qfalse;
- } else {
- ret = pthread_detach( renderThread );
- if ( ret ) {
- ri.Printf( PRINT_ALL, "pthread_detach returned %d: %s", ret, strerror( ret ) );
- }
- }
-
- return qtrue;
-}
-
-static volatile void *smpData = NULL;
-static volatile qboolean smpDataReady;
-
-void *GLimp_RendererSleep( void )
-{
- void *data;
-
- qglXMakeCurrent( dpy, None, NULL );
-
- pthread_mutex_lock( &smpMutex );
- {
- smpData = NULL;
- smpDataReady = qfalse;
-
- // after this, the front end can exit GLimp_FrontEndSleep
- pthread_cond_signal( &renderCompletedEvent );
-
- while ( !smpDataReady ) {
- pthread_cond_wait( &renderCommandsEvent, &smpMutex );
- }
-
- data = (void *)smpData;
- }
- pthread_mutex_unlock( &smpMutex );
-
- qglXMakeCurrent( dpy, win, ctx );
-
- return data;
-}
-
-void GLimp_FrontEndSleep( void )
-{
- pthread_mutex_lock( &smpMutex );
- {
- while ( smpData ) {
- pthread_cond_wait( &renderCompletedEvent, &smpMutex );
- }
- }
- pthread_mutex_unlock( &smpMutex );
-
- qglXMakeCurrent( dpy, win, ctx );
-}
-
-void GLimp_WakeRenderer( void *data )
-{
- qglXMakeCurrent( dpy, None, NULL );
-
- pthread_mutex_lock( &smpMutex );
- {
- assert( smpData == NULL );
- smpData = data;
- smpDataReady = qtrue;
-
- // after this, the renderer can continue through GLimp_RendererSleep
- pthread_cond_signal( &renderCommandsEvent );
- }
- pthread_mutex_unlock( &smpMutex );
-}
-
-#else
-
-void GLimp_RenderThreadWrapper( void *stub ) {}
-qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) {
- ri.Printf( PRINT_WARNING, "ERROR: SMP support was disabled at compile time\n");
- return qfalse;
-}
-void *GLimp_RendererSleep( void ) {
- return NULL;
-}
-void GLimp_FrontEndSleep( void ) {}
-void GLimp_WakeRenderer( void *data ) {}
-
-#endif
-
-/*****************************************************************************/
-/* MOUSE */
-/*****************************************************************************/
-
-void IN_Init(void) {
- Com_Printf ("\n------- Input Initialization -------\n");
- // mouse variables
- in_mouse = Cvar_Get ("in_mouse", "1", CVAR_ARCHIVE);
- in_dgamouse = Cvar_Get ("in_dgamouse", "1", CVAR_ARCHIVE);
-
- // turn on-off sub-frame timing of X events
- in_subframe = Cvar_Get ("in_subframe", "1", CVAR_ARCHIVE);
-
- // developer feature, allows to break without loosing mouse pointer
- in_nograb = Cvar_Get ("in_nograb", "0", 0);
-
- // bk001130 - from cvs.17 (mkv), joystick variables
- in_joystick = Cvar_Get ("in_joystick", "0", CVAR_ARCHIVE|CVAR_LATCH);
- // bk001130 - changed this to match win32
- in_joystickDebug = Cvar_Get ("in_debugjoystick", "0", CVAR_TEMP);
- joy_threshold = Cvar_Get ("joy_threshold", "0.15", CVAR_ARCHIVE); // FIXME: in_joythreshold
-
- if (in_mouse->value)
- mouse_avail = qtrue;
- else
- mouse_avail = qfalse;
-
- IN_StartupJoystick( ); // bk001130 - from cvs1.17 (mkv)
- Com_Printf ("------------------------------------\n");
-}
-
-void IN_Shutdown(void)
-{
- mouse_avail = qfalse;
-}
-
-void IN_Frame (void) {
-
- // bk001130 - from cvs 1.17 (mkv)
- IN_JoyMove(); // FIXME: disable if on desktop?
-
- if ( cls.keyCatchers & KEYCATCH_CONSOLE )
- {
- // temporarily deactivate if not in the game and
- // running on the desktop
- // voodoo always counts as full screen
- if (Cvar_VariableValue ("r_fullscreen") == 0
- && strcmp( Cvar_VariableString("r_glDriver"), _3DFX_DRIVER_NAME ) )
- {
- IN_DeactivateMouse ();
- return;
- }
- }
-
- IN_ActivateMouse();
-}
-
-void IN_Activate(void)
-{
-}
-
-// bk001130 - cvs1.17 joystick code (mkv) was here, no linux_joystick.c
-
-void Sys_SendKeyEvents (void) {
- // XEvent event; // bk001204 - unused
-
- if (!dpy)
- return;
- HandleEvents();
-}
-
-
-// bk010216 - added stubs for non-Linux UNIXes here
-// FIXME - use NO_JOYSTICK or something else generic
-
-#if defined( __FreeBSD__ ) // rb010123
-void IN_StartupJoystick( void ) {}
-void IN_JoyMove( void ) {}
-#endif
+/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +/* +** GLW_IMP.C +** +** This file contains ALL Linux specific stuff having to do with the +** OpenGL refresh. When a port is being made the following functions +** must be implemented by the port: +** +** GLimp_EndFrame +** GLimp_Init +** GLimp_Shutdown +** GLimp_SwitchFullscreen +** GLimp_SetGamma +** +*/ + +#include <termios.h> +#include <sys/ioctl.h> +#ifdef __linux__ + #include <sys/stat.h> + #include <sys/vt.h> +#endif +#include <stdarg.h> +#include <stdio.h> +#include <signal.h> +#include <pthread.h> +#include <semaphore.h> + +// bk001204 +#include <dlfcn.h> + +// bk001206 - from my Heretic2 by way of Ryan's Fakk2 +// Needed for the new X11_PendingInput() function. +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include "../renderer/tr_local.h" +#include "../client/client.h" +#include "linux_local.h" // bk001130 + +#include "unix_glw.h" + +#include <GL/glx.h> + +#include <X11/keysym.h> +#include <X11/cursorfont.h> + +#include <X11/extensions/xf86dga.h> +#include <X11/extensions/xf86vmode.h> + +#define WINDOW_CLASS_NAME "Quake III: Arena" + +typedef enum +{ + RSERR_OK, + + RSERR_INVALID_FULLSCREEN, + RSERR_INVALID_MODE, + + RSERR_UNKNOWN +} rserr_t; + +glwstate_t glw_state; + +static Display *dpy = NULL; +static int scrnum; +static Window win = 0; +static GLXContext ctx = NULL; + +// bk001206 - not needed anymore +// static qboolean autorepeaton = qtrue; + +#define KEY_MASK (KeyPressMask | KeyReleaseMask) +#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | \ + PointerMotionMask | ButtonMotionMask ) +#define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask | StructureNotifyMask ) + +static qboolean mouse_avail; +static qboolean mouse_active = qfalse; +static int mwx, mwy; +static int mx = 0, my = 0; + +// Time mouse was reset, we ignore the first 50ms of the mouse to allow settling of events +static int mouseResetTime = 0; +#define MOUSE_RESET_DELAY 50 + +static cvar_t *in_mouse; +static cvar_t *in_dgamouse; // user pref for dga mouse +cvar_t *in_subframe; +cvar_t *in_nograb; // this is strictly for developers + +// bk001130 - from cvs1.17 (mkv), but not static +cvar_t *in_joystick = NULL; +cvar_t *in_joystickDebug = NULL; +cvar_t *joy_threshold = NULL; + +cvar_t *r_allowSoftwareGL; // don't abort out if the pixelformat claims software +cvar_t *r_previousglDriver; + +qboolean vidmode_ext = qfalse; +static int vidmode_MajorVersion = 0, vidmode_MinorVersion = 0; // major and minor of XF86VidExtensions + +// gamma value of the X display before we start playing with it +static XF86VidModeGamma vidmode_InitialGamma; + +static int win_x, win_y; + +static XF86VidModeModeInfo **vidmodes; +//static int default_dotclock_vidmode; // bk001204 - unused +static int num_vidmodes; +static qboolean vidmode_active = qfalse; + +static int mouse_accel_numerator; +static int mouse_accel_denominator; +static int mouse_threshold; + +/* +* Find the first occurrence of find in s. +*/ +// bk001130 - from cvs1.17 (mkv), const +// bk001130 - made first argument const +static const char *Q_stristr( const char *s, const char *find) +{ + register char c, sc; + register size_t len; + + if ((c = *find++) != 0) + { + if (c >= 'a' && c <= 'z') + { + c -= ('a' - 'A'); + } + len = strlen(find); + do + { + do + { + if ((sc = *s++) == 0) + return NULL; + if (sc >= 'a' && sc <= 'z') + { + sc -= ('a' - 'A'); + } + } while (sc != c); + } while (Q_stricmpn(s, find, len) != 0); + s--; + } + return s; +} + +/***************************************************************************** +** KEYBOARD +** NOTE TTimo the keyboard handling is done with KeySyms +** that means relying on the keyboard mapping provided by X +** in-game it would probably be better to use KeyCode (i.e. hardware key codes) +** you would still need the KeySyms in some cases, such as for the console and all entry textboxes +** (cause there's nothing worse than a qwerty mapping on a french keyboard) +** +** you can turn on some debugging and verbose of the keyboard code with #define KBD_DBG +******************************************************************************/ + +//#define KBD_DBG + +static char *XLateKey(XKeyEvent *ev, int *key) +{ + static char buf[64]; + KeySym keysym; + int XLookupRet; + + *key = 0; + + XLookupRet = XLookupString(ev, buf, sizeof buf, &keysym, 0); +#ifdef KBD_DBG + ri.Printf(PRINT_ALL, "XLookupString ret: %d buf: %s keysym: %x\n", XLookupRet, buf, keysym); +#endif + + switch (keysym) + { + case XK_KP_Page_Up: + case XK_KP_9: *key = K_KP_PGUP; break; + case XK_Page_Up: *key = K_PGUP; break; + + case XK_KP_Page_Down: + case XK_KP_3: *key = K_KP_PGDN; break; + case XK_Page_Down: *key = K_PGDN; break; + + case XK_KP_Home: *key = K_KP_HOME; break; + case XK_KP_7: *key = K_KP_HOME; break; + case XK_Home: *key = K_HOME; break; + + case XK_KP_End: + case XK_KP_1: *key = K_KP_END; break; + case XK_End: *key = K_END; break; + + case XK_KP_Left: *key = K_KP_LEFTARROW; break; + case XK_KP_4: *key = K_KP_LEFTARROW; break; + case XK_Left: *key = K_LEFTARROW; break; + + case XK_KP_Right: *key = K_KP_RIGHTARROW; break; + case XK_KP_6: *key = K_KP_RIGHTARROW; break; + case XK_Right: *key = K_RIGHTARROW; break; + + case XK_KP_Down: + case XK_KP_2: *key = K_KP_DOWNARROW; break; + case XK_Down: *key = K_DOWNARROW; break; + + case XK_KP_Up: + case XK_KP_8: *key = K_KP_UPARROW; break; + case XK_Up: *key = K_UPARROW; break; + + case XK_Escape: *key = K_ESCAPE; break; + + case XK_KP_Enter: *key = K_KP_ENTER; break; + case XK_Return: *key = K_ENTER; break; + + case XK_Tab: *key = K_TAB; break; + + case XK_F1: *key = K_F1; break; + + case XK_F2: *key = K_F2; break; + + case XK_F3: *key = K_F3; break; + + case XK_F4: *key = K_F4; break; + + case XK_F5: *key = K_F5; break; + + case XK_F6: *key = K_F6; break; + + case XK_F7: *key = K_F7; break; + + case XK_F8: *key = K_F8; break; + + case XK_F9: *key = K_F9; break; + + case XK_F10: *key = K_F10; break; + + case XK_F11: *key = K_F11; break; + + case XK_F12: *key = K_F12; break; + + // bk001206 - from Ryan's Fakk2 + //case XK_BackSpace: *key = 8; break; // ctrl-h + case XK_BackSpace: *key = K_BACKSPACE; break; // ctrl-h + + case XK_KP_Delete: + case XK_KP_Decimal: *key = K_KP_DEL; break; + case XK_Delete: *key = K_DEL; break; + + case XK_Pause: *key = K_PAUSE; break; + + case XK_Shift_L: + case XK_Shift_R: *key = K_SHIFT; break; + + case XK_Execute: + case XK_Control_L: + case XK_Control_R: *key = K_CTRL; break; + + case XK_Alt_L: + case XK_Meta_L: + case XK_Alt_R: + case XK_Meta_R: *key = K_ALT; break; + + case XK_KP_Begin: *key = K_KP_5; break; + + case XK_Insert: *key = K_INS; break; + case XK_KP_Insert: + case XK_KP_0: *key = K_KP_INS; break; + + case XK_KP_Multiply: *key = '*'; break; + case XK_KP_Add: *key = K_KP_PLUS; break; + case XK_KP_Subtract: *key = K_KP_MINUS; break; + case XK_KP_Divide: *key = K_KP_SLASH; break; + + // bk001130 - from cvs1.17 (mkv) + case XK_exclam: *key = '1'; break; + case XK_at: *key = '2'; break; + case XK_numbersign: *key = '3'; break; + case XK_dollar: *key = '4'; break; + case XK_percent: *key = '5'; break; + case XK_asciicircum: *key = '6'; break; + case XK_ampersand: *key = '7'; break; + case XK_asterisk: *key = '8'; break; + case XK_parenleft: *key = '9'; break; + case XK_parenright: *key = '0'; break; + + // weird french keyboards .. + // NOTE: console toggle is hardcoded in cl_keys.c, can't be unbound + // cleaner would be .. using hardware key codes instead of the key syms + // could also add a new K_KP_CONSOLE + case XK_twosuperior: *key = '~'; break; + + // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=472 + case XK_space: + case XK_KP_Space: *key = K_SPACE; break; + + default: + if (XLookupRet == 0) + { + if (com_developer->value) + { + ri.Printf(PRINT_ALL, "Warning: XLookupString failed on KeySym %d\n", keysym); + } + return NULL; + } + else + { + // XK_* tests failed, but XLookupString got a buffer, so let's try it + *key = *(unsigned char *)buf; + if (*key >= 'A' && *key <= 'Z') + *key = *key - 'A' + 'a'; + // if ctrl is pressed, the keys are not between 'A' and 'Z', for instance ctrl-z == 26 ^Z ^C etc. + // see https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=19 + else if (*key >= 1 && *key <= 26) + *key = *key + 'a' - 1; + } + break; + } + + return buf; +} + +// ======================================================================== +// makes a null cursor +// ======================================================================== + +static Cursor CreateNullCursor(Display *display, Window root) +{ + Pixmap cursormask; + XGCValues xgc; + GC gc; + XColor dummycolour; + Cursor cursor; + + cursormask = XCreatePixmap(display, root, 1, 1, 1/*depth*/); + xgc.function = GXclear; + gc = XCreateGC(display, cursormask, GCFunction, &xgc); + XFillRectangle(display, cursormask, gc, 0, 0, 1, 1); + dummycolour.pixel = 0; + dummycolour.red = 0; + dummycolour.flags = 04; + cursor = XCreatePixmapCursor(display, cursormask, cursormask, + &dummycolour,&dummycolour, 0,0); + XFreePixmap(display,cursormask); + XFreeGC(display,gc); + return cursor; +} + +static void install_grabs(void) +{ + // inviso cursor + XWarpPointer(dpy, None, win, + 0, 0, 0, 0, + glConfig.vidWidth / 2, glConfig.vidHeight / 2); + XSync(dpy, False); + + XDefineCursor(dpy, win, CreateNullCursor(dpy, win)); + + XGrabPointer(dpy, win, // bk010108 - do this earlier? + False, + MOUSE_MASK, + GrabModeAsync, GrabModeAsync, + win, + None, + CurrentTime); + + XGetPointerControl(dpy, &mouse_accel_numerator, &mouse_accel_denominator, + &mouse_threshold); + + XChangePointerControl(dpy, True, True, 1, 1, 0); + + XSync(dpy, False); + + mouseResetTime = Sys_Milliseconds (); + + if (in_dgamouse->value) + { + int MajorVersion, MinorVersion; + + if (!XF86DGAQueryVersion(dpy, &MajorVersion, &MinorVersion)) + { + // unable to query, probalby not supported, force the setting to 0 + ri.Printf( PRINT_ALL, "Failed to detect XF86DGA Mouse\n" ); + ri.Cvar_Set( "in_dgamouse", "0" ); + } else + { + XF86DGADirectVideo(dpy, DefaultScreen(dpy), XF86DGADirectMouse); + XWarpPointer(dpy, None, win, 0, 0, 0, 0, 0, 0); + } + } else + { + mwx = glConfig.vidWidth / 2; + mwy = glConfig.vidHeight / 2; + mx = my = 0; + } + + XGrabKeyboard(dpy, win, + False, + GrabModeAsync, GrabModeAsync, + CurrentTime); + + XSync(dpy, False); +} + +static void uninstall_grabs(void) +{ + if (in_dgamouse->value) + { + if (com_developer->value) + ri.Printf( PRINT_ALL, "DGA Mouse - Disabling DGA DirectVideo\n" ); + XF86DGADirectVideo(dpy, DefaultScreen(dpy), 0); + } + + XChangePointerControl(dpy, qtrue, qtrue, mouse_accel_numerator, + mouse_accel_denominator, mouse_threshold); + + XUngrabPointer(dpy, CurrentTime); + XUngrabKeyboard(dpy, CurrentTime); + + XWarpPointer(dpy, None, win, + 0, 0, 0, 0, + glConfig.vidWidth / 2, glConfig.vidHeight / 2); + + // inviso cursor + XUndefineCursor(dpy, win); +} + +// bk001206 - from Ryan's Fakk2 +/** + * XPending() actually performs a blocking read + * if no events available. From Fakk2, by way of + * Heretic2, by way of SDL, original idea GGI project. + * The benefit of this approach over the quite + * badly behaved XAutoRepeatOn/Off is that you get + * focus handling for free, which is a major win + * with debug and windowed mode. It rests on the + * assumption that the X server will use the + * same timestamp on press/release event pairs + * for key repeats. + */ +static qboolean X11_PendingInput(void) { + + assert(dpy != NULL); + + // Flush the display connection + // and look to see if events are queued + XFlush( dpy ); + if ( XEventsQueued( dpy, QueuedAlready) ) + { + return qtrue; + } + + // More drastic measures are required -- see if X is ready to talk + { + static struct timeval zero_time; + int x11_fd; + fd_set fdset; + + x11_fd = ConnectionNumber( dpy ); + FD_ZERO(&fdset); + FD_SET(x11_fd, &fdset); + if ( select(x11_fd+1, &fdset, NULL, NULL, &zero_time) == 1 ) + { + return(XPending(dpy)); + } + } + + // Oh well, nothing is ready .. + return qfalse; +} + +// bk001206 - from Ryan's Fakk2. See above. +static qboolean repeated_press(XEvent *event) +{ + XEvent peekevent; + qboolean repeated = qfalse; + + assert(dpy != NULL); + + if (X11_PendingInput()) + { + XPeekEvent(dpy, &peekevent); + + if ((peekevent.type == KeyPress) && + (peekevent.xkey.keycode == event->xkey.keycode) && + (peekevent.xkey.time == event->xkey.time)) + { + repeated = qtrue; + XNextEvent(dpy, &peekevent); // skip event. + } // if + } // if + + return(repeated); +} // repeated_press + +int Sys_XTimeToSysTime (Time xtime); +static void HandleEvents(void) +{ + int b; + int key; + XEvent event; + qboolean dowarp = qfalse; + char *p; + int dx, dy; + int t = 0; // default to 0 in case we don't set + + if (!dpy) + return; + + while (XPending(dpy)) + { + XNextEvent(dpy, &event); + switch (event.type) + { + case KeyPress: + t = Sys_XTimeToSysTime(event.xkey.time); + p = XLateKey(&event.xkey, &key); + if (key) + { + Sys_QueEvent( t, SE_KEY, key, qtrue, 0, NULL ); + } + if (p) + { + while (*p) + { + Sys_QueEvent( t, SE_CHAR, *p++, 0, 0, NULL ); + } + } + break; + + case KeyRelease: + t = Sys_XTimeToSysTime(event.xkey.time); + // bk001206 - handle key repeat w/o XAutRepatOn/Off + // also: not done if console/menu is active. + // From Ryan's Fakk2. + // see game/q_shared.h, KEYCATCH_* . 0 == in 3d game. + if (cls.keyCatchers == 0) + { // FIXME: KEYCATCH_NONE + if (repeated_press(&event) == qtrue) + continue; + } // if + XLateKey(&event.xkey, &key); + + Sys_QueEvent( t, SE_KEY, key, qfalse, 0, NULL ); + break; + + case MotionNotify: + t = Sys_XTimeToSysTime(event.xkey.time); + if (mouse_active) + { + if (in_dgamouse->value) + { + if (abs(event.xmotion.x_root) > 1) + mx += event.xmotion.x_root * 2; + else + mx += event.xmotion.x_root; + if (abs(event.xmotion.y_root) > 1) + my += event.xmotion.y_root * 2; + else + my += event.xmotion.y_root; + if (t - mouseResetTime > MOUSE_RESET_DELAY ) + { + Sys_QueEvent( t, SE_MOUSE, mx, my, 0, NULL ); + } + mx = my = 0; + } else + { + // If it's a center motion, we've just returned from our warp + if (event.xmotion.x == glConfig.vidWidth/2 && + event.xmotion.y == glConfig.vidHeight/2) + { + mwx = glConfig.vidWidth/2; + mwy = glConfig.vidHeight/2; + if (t - mouseResetTime > MOUSE_RESET_DELAY ) + { + Sys_QueEvent( t, SE_MOUSE, mx, my, 0, NULL ); + } + mx = my = 0; + break; + } + + dx = ((int)event.xmotion.x - mwx); + dy = ((int)event.xmotion.y - mwy); + if (abs(dx) > 1) + mx += dx * 2; + else + mx += dx; + if (abs(dy) > 1) + my += dy * 2; + else + my += dy; + + mwx = event.xmotion.x; + mwy = event.xmotion.y; + dowarp = qtrue; + } + } + break; + + case ButtonPress: + t = Sys_XTimeToSysTime(event.xkey.time); + if (event.xbutton.button == 4) + { + Sys_QueEvent( t, SE_KEY, K_MWHEELUP, qtrue, 0, NULL ); + } else if (event.xbutton.button == 5) + { + Sys_QueEvent( t, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL ); + } else + { + // NOTE TTimo there seems to be a weird mapping for K_MOUSE1 K_MOUSE2 K_MOUSE3 .. + b=-1; + if (event.xbutton.button == 1) + { + b = 0; // K_MOUSE1 + } else if (event.xbutton.button == 2) + { + b = 2; // K_MOUSE3 + } else if (event.xbutton.button == 3) + { + b = 1; // K_MOUSE2 + } else if (event.xbutton.button == 6) + { + b = 3; // K_MOUSE4 + } else if (event.xbutton.button == 7) + { + b = 4; // K_MOUSE5 + }; + + Sys_QueEvent( t, SE_KEY, K_MOUSE1 + b, qtrue, 0, NULL ); + } + break; + + case ButtonRelease: + t = Sys_XTimeToSysTime(event.xkey.time); + if (event.xbutton.button == 4) + { + Sys_QueEvent( t, SE_KEY, K_MWHEELUP, qfalse, 0, NULL ); + } else if (event.xbutton.button == 5) + { + Sys_QueEvent( t, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL ); + } else + { + b=-1; + if (event.xbutton.button == 1) + { + b = 0; + } else if (event.xbutton.button == 2) + { + b = 2; + } else if (event.xbutton.button == 3) + { + b = 1; + } else if (event.xbutton.button == 6) + { + b = 3; // K_MOUSE4 + } else if (event.xbutton.button == 7) + { + b = 4; // K_MOUSE5 + }; + Sys_QueEvent( t, SE_KEY, K_MOUSE1 + b, qfalse, 0, NULL ); + } + break; + + case CreateNotify : + win_x = event.xcreatewindow.x; + win_y = event.xcreatewindow.y; + break; + + case ConfigureNotify : + win_x = event.xconfigure.x; + win_y = event.xconfigure.y; + break; + } + } + + if (dowarp) + { + XWarpPointer(dpy,None,win,0,0,0,0, + (glConfig.vidWidth/2),(glConfig.vidHeight/2)); + } +} + +// NOTE TTimo for the tty console input, we didn't rely on those .. +// it's not very surprising actually cause they are not used otherwise +void KBD_Init(void) +{ +} + +void KBD_Close(void) +{ +} + +void IN_ActivateMouse( void ) +{ + if (!mouse_avail || !dpy || !win) + return; + + if (!mouse_active) + { + if (!in_nograb->value) + install_grabs(); + else if (in_dgamouse->value) // force dga mouse to 0 if using nograb + ri.Cvar_Set("in_dgamouse", "0"); + mouse_active = qtrue; + } +} + +void IN_DeactivateMouse( void ) +{ + if (!mouse_avail || !dpy || !win) + return; + + if (mouse_active) + { + if (!in_nograb->value) + uninstall_grabs(); + else if (in_dgamouse->value) // force dga mouse to 0 if using nograb + ri.Cvar_Set("in_dgamouse", "0"); + mouse_active = qfalse; + } +} +/*****************************************************************************/ + +/* +** GLimp_SetGamma +** +** This routine should only be called if glConfig.deviceSupportsGamma is TRUE +*/ +void GLimp_SetGamma( unsigned char red[256], unsigned char green[256], unsigned char blue[256] ) +{ + // NOTE TTimo we get the gamma value from cvar, because we can't work with the s_gammatable + // the API wasn't changed to avoid breaking other OSes + float g = Cvar_Get("r_gamma", "1.0", 0)->value; + XF86VidModeGamma gamma; + assert(glConfig.deviceSupportsGamma); + gamma.red = g; + gamma.green = g; + gamma.blue = g; + XF86VidModeSetGamma(dpy, scrnum, &gamma); +} + +/* +** GLimp_Shutdown +** +** This routine does all OS specific shutdown procedures for the OpenGL +** subsystem. Under OpenGL this means NULLing out the current DC and +** HGLRC, deleting the rendering context, and releasing the DC acquired +** for the window. The state structure is also nulled out. +** +*/ +void GLimp_Shutdown( void ) +{ + if (!ctx || !dpy) + return; + IN_DeactivateMouse(); + // bk001206 - replaced with H2/Fakk2 solution + // XAutoRepeatOn(dpy); + // autorepeaton = qfalse; // bk001130 - from cvs1.17 (mkv) + if (dpy) + { + if (ctx) + qglXDestroyContext(dpy, ctx); + if (win) + XDestroyWindow(dpy, win); + if (vidmode_active) + XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[0]); + if (glConfig.deviceSupportsGamma) + { + XF86VidModeSetGamma(dpy, scrnum, &vidmode_InitialGamma); + } + // NOTE TTimo opening/closing the display should be necessary only once per run + // but it seems QGL_Shutdown gets called in a lot of occasion + // in some cases, this XCloseDisplay is known to raise some X errors + // ( https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=33 ) + XCloseDisplay(dpy); + } + vidmode_active = qfalse; + dpy = NULL; + win = 0; + ctx = NULL; + + memset( &glConfig, 0, sizeof( glConfig ) ); + memset( &glState, 0, sizeof( glState ) ); + + QGL_Shutdown(); +} + +/* +** GLimp_LogComment +*/ +void GLimp_LogComment( char *comment ) +{ + if ( glw_state.log_fp ) + { + fprintf( glw_state.log_fp, "%s", comment ); + } +} + +/* +** GLW_StartDriverAndSetMode +*/ +// bk001204 - prototype needed +int GLW_SetMode( const char *drivername, int mode, qboolean fullscreen ); +static qboolean GLW_StartDriverAndSetMode( const char *drivername, + int mode, + qboolean fullscreen ) +{ + rserr_t err; + + // don't ever bother going into fullscreen with a voodoo card +#if 1 // JDC: I reenabled this + if ( Q_stristr( drivername, "Voodoo" ) ) + { + ri.Cvar_Set( "r_fullscreen", "0" ); + r_fullscreen->modified = qfalse; + fullscreen = qfalse; + } +#endif + + if (fullscreen && in_nograb->value) + { + ri.Printf( PRINT_ALL, "Fullscreen not allowed with in_nograb 1\n"); + ri.Cvar_Set( "r_fullscreen", "0" ); + r_fullscreen->modified = qfalse; + fullscreen = qfalse; + } + + err = GLW_SetMode( drivername, mode, fullscreen ); + + switch ( err ) + { + case RSERR_INVALID_FULLSCREEN: + ri.Printf( PRINT_ALL, "...WARNING: fullscreen unavailable in this mode\n" ); + return qfalse; + case RSERR_INVALID_MODE: + ri.Printf( PRINT_ALL, "...WARNING: could not set the given mode (%d)\n", mode ); + return qfalse; + default: + break; + } + return qtrue; +} + +/* +** GLW_SetMode +*/ +int GLW_SetMode( const char *drivername, int mode, qboolean fullscreen ) +{ + int attrib[] = { + GLX_RGBA, // 0 + GLX_RED_SIZE, 4, // 1, 2 + GLX_GREEN_SIZE, 4, // 3, 4 + GLX_BLUE_SIZE, 4, // 5, 6 + GLX_DOUBLEBUFFER, // 7 + GLX_DEPTH_SIZE, 1, // 8, 9 + GLX_STENCIL_SIZE, 1, // 10, 11 + None + }; + // these match in the array +#define ATTR_RED_IDX 2 +#define ATTR_GREEN_IDX 4 +#define ATTR_BLUE_IDX 6 +#define ATTR_DEPTH_IDX 9 +#define ATTR_STENCIL_IDX 11 + Window root; + XVisualInfo *visinfo; + XSetWindowAttributes attr; + XSizeHints sizehints; + unsigned long mask; + int colorbits, depthbits, stencilbits; + int tcolorbits, tdepthbits, tstencilbits; + int dga_MajorVersion, dga_MinorVersion; + int actualWidth, actualHeight; + int i; + const char* glstring; // bk001130 - from cvs1.17 (mkv) + + ri.Printf( PRINT_ALL, "Initializing OpenGL display\n"); + + ri.Printf (PRINT_ALL, "...setting mode %d:", mode ); + + if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, mode ) ) + { + ri.Printf( PRINT_ALL, " invalid mode\n" ); + return RSERR_INVALID_MODE; + } + ri.Printf( PRINT_ALL, " %d %d\n", glConfig.vidWidth, glConfig.vidHeight); + + if (!(dpy = XOpenDisplay(NULL))) + { + fprintf(stderr, "Error couldn't open the X display\n"); + return RSERR_INVALID_MODE; + } + + scrnum = DefaultScreen(dpy); + root = RootWindow(dpy, scrnum); + + actualWidth = glConfig.vidWidth; + actualHeight = glConfig.vidHeight; + + // Get video mode list + if (!XF86VidModeQueryVersion(dpy, &vidmode_MajorVersion, &vidmode_MinorVersion)) + { + vidmode_ext = qfalse; + } else + { + ri.Printf(PRINT_ALL, "Using XFree86-VidModeExtension Version %d.%d\n", + vidmode_MajorVersion, vidmode_MinorVersion); + vidmode_ext = qtrue; + } + + // Check for DGA + dga_MajorVersion = 0, dga_MinorVersion = 0; + if (in_dgamouse->value) + { + if (!XF86DGAQueryVersion(dpy, &dga_MajorVersion, &dga_MinorVersion)) + { + // unable to query, probalby not supported + ri.Printf( PRINT_ALL, "Failed to detect XF86DGA Mouse\n" ); + ri.Cvar_Set( "in_dgamouse", "0" ); + } else + { + ri.Printf( PRINT_ALL, "XF86DGA Mouse (Version %d.%d) initialized\n", + dga_MajorVersion, dga_MinorVersion); + } + } + + if (vidmode_ext) + { + int best_fit, best_dist, dist, x, y; + + XF86VidModeGetAllModeLines(dpy, scrnum, &num_vidmodes, &vidmodes); + + // Are we going fullscreen? If so, let's change video mode + if (fullscreen) + { + best_dist = 9999999; + best_fit = -1; + + for (i = 0; i < num_vidmodes; i++) + { + if (glConfig.vidWidth > vidmodes[i]->hdisplay || + glConfig.vidHeight > vidmodes[i]->vdisplay) + continue; + + x = glConfig.vidWidth - vidmodes[i]->hdisplay; + y = glConfig.vidHeight - vidmodes[i]->vdisplay; + dist = (x * x) + (y * y); + if (dist < best_dist) + { + best_dist = dist; + best_fit = i; + } + } + + if (best_fit != -1) + { + actualWidth = vidmodes[best_fit]->hdisplay; + actualHeight = vidmodes[best_fit]->vdisplay; + + // change to the mode + XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]); + vidmode_active = qtrue; + + // Move the viewport to top left + XF86VidModeSetViewPort(dpy, scrnum, 0, 0); + + ri.Printf(PRINT_ALL, "XFree86-VidModeExtension Activated at %dx%d\n", + actualWidth, actualHeight); + + } else + { + fullscreen = 0; + ri.Printf(PRINT_ALL, "XFree86-VidModeExtension: No acceptable modes found\n"); + } + } else + { + ri.Printf(PRINT_ALL, "XFree86-VidModeExtension: Ignored on non-fullscreen/Voodoo\n"); + } + } + + + if (!r_colorbits->value) + colorbits = 24; + else + colorbits = r_colorbits->value; + + if ( !Q_stricmp( r_glDriver->string, _3DFX_DRIVER_NAME ) ) + colorbits = 16; + + if (!r_depthbits->value) + depthbits = 24; + else + depthbits = r_depthbits->value; + stencilbits = r_stencilbits->value; + + for (i = 0; i < 16; i++) + { + // 0 - default + // 1 - minus colorbits + // 2 - minus depthbits + // 3 - minus stencil + if ((i % 4) == 0 && i) + { + // one pass, reduce + switch (i / 4) + { + case 2 : + if (colorbits == 24) + colorbits = 16; + break; + case 1 : + if (depthbits == 24) + depthbits = 16; + else if (depthbits == 16) + depthbits = 8; + case 3 : + if (stencilbits == 24) + stencilbits = 16; + else if (stencilbits == 16) + stencilbits = 8; + } + } + + tcolorbits = colorbits; + tdepthbits = depthbits; + tstencilbits = stencilbits; + + if ((i % 4) == 3) + { // reduce colorbits + if (tcolorbits == 24) + tcolorbits = 16; + } + + if ((i % 4) == 2) + { // reduce depthbits + if (tdepthbits == 24) + tdepthbits = 16; + else if (tdepthbits == 16) + tdepthbits = 8; + } + + if ((i % 4) == 1) + { // reduce stencilbits + if (tstencilbits == 24) + tstencilbits = 16; + else if (tstencilbits == 16) + tstencilbits = 8; + else + tstencilbits = 0; + } + + if (tcolorbits == 24) + { + attrib[ATTR_RED_IDX] = 8; + attrib[ATTR_GREEN_IDX] = 8; + attrib[ATTR_BLUE_IDX] = 8; + } else + { + // must be 16 bit + attrib[ATTR_RED_IDX] = 4; + attrib[ATTR_GREEN_IDX] = 4; + attrib[ATTR_BLUE_IDX] = 4; + } + + attrib[ATTR_DEPTH_IDX] = tdepthbits; // default to 24 depth + attrib[ATTR_STENCIL_IDX] = tstencilbits; + + visinfo = qglXChooseVisual(dpy, scrnum, attrib); + if (!visinfo) + { + continue; + } + + ri.Printf( PRINT_ALL, "Using %d/%d/%d Color bits, %d depth, %d stencil display.\n", + attrib[ATTR_RED_IDX], attrib[ATTR_GREEN_IDX], attrib[ATTR_BLUE_IDX], + attrib[ATTR_DEPTH_IDX], attrib[ATTR_STENCIL_IDX]); + + glConfig.colorBits = tcolorbits; + glConfig.depthBits = tdepthbits; + glConfig.stencilBits = tstencilbits; + break; + } + + if (!visinfo) + { + ri.Printf( PRINT_ALL, "Couldn't get a visual\n" ); + return RSERR_INVALID_MODE; + } + + /* window attributes */ + attr.background_pixel = BlackPixel(dpy, scrnum); + attr.border_pixel = 0; + attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone); + attr.event_mask = X_MASK; + if (vidmode_active) + { + mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | + CWEventMask | CWOverrideRedirect; + attr.override_redirect = True; + attr.backing_store = NotUseful; + attr.save_under = False; + } else + mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + + win = XCreateWindow(dpy, root, 0, 0, + actualWidth, actualHeight, + 0, visinfo->depth, InputOutput, + visinfo->visual, mask, &attr); + + XStoreName( dpy, win, WINDOW_CLASS_NAME ); + + /* GH: Don't let the window be resized */ + sizehints.flags = PMinSize | PMaxSize; + sizehints.min_width = sizehints.max_width = actualWidth; + sizehints.min_height = sizehints.max_height = actualHeight; + + XSetWMNormalHints( dpy, win, &sizehints ); + + XMapWindow( dpy, win ); + + if (vidmode_active) + XMoveWindow(dpy, win, 0, 0); + + XFlush(dpy); + XSync(dpy,False); // bk001130 - from cvs1.17 (mkv) + ctx = qglXCreateContext(dpy, visinfo, NULL, True); + XSync(dpy,False); // bk001130 - from cvs1.17 (mkv) + + /* GH: Free the visinfo after we're done with it */ + XFree( visinfo ); + + qglXMakeCurrent(dpy, win, ctx); + + // bk001130 - from cvs1.17 (mkv) + glstring = qglGetString (GL_RENDERER); + ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glstring ); + + // bk010122 - new software token (Indirect) + if ( !Q_stricmp( glstring, "Mesa X11") + || !Q_stricmp( glstring, "Mesa GLX Indirect") ) + { + if ( !r_allowSoftwareGL->integer ) + { + ri.Printf( PRINT_ALL, "\n\n***********************************************************\n" ); + ri.Printf( PRINT_ALL, " You are using software Mesa (no hardware acceleration)! \n" ); + ri.Printf( PRINT_ALL, " Driver DLL used: %s\n", drivername ); + ri.Printf( PRINT_ALL, " If this is intentional, add\n" ); + ri.Printf( PRINT_ALL, " \"+set r_allowSoftwareGL 1\"\n" ); + ri.Printf( PRINT_ALL, " to the command line when starting the game.\n" ); + ri.Printf( PRINT_ALL, "***********************************************************\n"); + GLimp_Shutdown( ); + return RSERR_INVALID_MODE; + } else + { + ri.Printf( PRINT_ALL, "...using software Mesa (r_allowSoftwareGL==1).\n" ); + } + } + + return RSERR_OK; +} + +/* +** GLW_InitExtensions +*/ +static void GLW_InitExtensions( void ) +{ + if ( !r_allowExtensions->integer ) + { + ri.Printf( PRINT_ALL, "*** IGNORING OPENGL EXTENSIONS ***\n" ); + return; + } + + ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" ); + + // GL_S3_s3tc + if ( Q_stristr( glConfig.extensions_string, "GL_S3_s3tc" ) ) + { + if ( r_ext_compressed_textures->value ) + { + glConfig.textureCompression = TC_S3TC; + ri.Printf( PRINT_ALL, "...using GL_S3_s3tc\n" ); + } else + { + glConfig.textureCompression = TC_NONE; + ri.Printf( PRINT_ALL, "...ignoring GL_S3_s3tc\n" ); + } + } else + { + glConfig.textureCompression = TC_NONE; + ri.Printf( PRINT_ALL, "...GL_S3_s3tc not found\n" ); + } + + // GL_EXT_texture_env_add + glConfig.textureEnvAddAvailable = qfalse; + if ( Q_stristr( glConfig.extensions_string, "EXT_texture_env_add" ) ) + { + if ( r_ext_texture_env_add->integer ) + { + glConfig.textureEnvAddAvailable = qtrue; + ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" ); + } else + { + glConfig.textureEnvAddAvailable = qfalse; + ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" ); + } + } else + { + ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" ); + } + + // GL_ARB_multitexture + qglMultiTexCoord2fARB = NULL; + qglActiveTextureARB = NULL; + qglClientActiveTextureARB = NULL; + if ( Q_stristr( glConfig.extensions_string, "GL_ARB_multitexture" ) ) + { + if ( r_ext_multitexture->value ) + { + qglMultiTexCoord2fARB = ( PFNGLMULTITEXCOORD2FARBPROC ) dlsym( glw_state.OpenGLLib, "glMultiTexCoord2fARB" ); + qglActiveTextureARB = ( PFNGLACTIVETEXTUREARBPROC ) dlsym( glw_state.OpenGLLib, "glActiveTextureARB" ); + qglClientActiveTextureARB = ( PFNGLCLIENTACTIVETEXTUREARBPROC ) dlsym( glw_state.OpenGLLib, "glClientActiveTextureARB" ); + + if ( qglActiveTextureARB ) + { + qglGetIntegerv( GL_MAX_ACTIVE_TEXTURES_ARB, &glConfig.maxActiveTextures ); + + if ( glConfig.maxActiveTextures > 1 ) + { + ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" ); + } else + { + qglMultiTexCoord2fARB = NULL; + qglActiveTextureARB = NULL; + qglClientActiveTextureARB = NULL; + ri.Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" ); + } + } + } else + { + ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" ); + } + } else + { + ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" ); + } + + // GL_EXT_compiled_vertex_array + if ( Q_stristr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) ) + { + if ( r_ext_compiled_vertex_array->value ) + { + ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" ); + qglLockArraysEXT = ( void ( APIENTRY * )( int, int ) ) dlsym( glw_state.OpenGLLib, "glLockArraysEXT" ); + qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) dlsym( glw_state.OpenGLLib, "glUnlockArraysEXT" ); + if (!qglLockArraysEXT || !qglUnlockArraysEXT) + { + ri.Error (ERR_FATAL, "bad getprocaddress"); + } + } else + { + ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" ); + } + } else + { + ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" ); + } + +} + +static void GLW_InitGamma() +{ + /* Minimum extension version required */ + #define GAMMA_MINMAJOR 2 + #define GAMMA_MINMINOR 0 + + glConfig.deviceSupportsGamma = qfalse; + + if (vidmode_ext) + { + if (vidmode_MajorVersion < GAMMA_MINMAJOR || + (vidmode_MajorVersion == GAMMA_MINMAJOR && vidmode_MinorVersion < GAMMA_MINMINOR)) { + ri.Printf( PRINT_ALL, "XF86 Gamma extension not supported in this version\n"); + return; + } + XF86VidModeGetGamma(dpy, scrnum, &vidmode_InitialGamma); + ri.Printf( PRINT_ALL, "XF86 Gamma extension initialized\n"); + glConfig.deviceSupportsGamma = qtrue; + } +} + +/* +** GLW_LoadOpenGL +** +** GLimp_win.c internal function that that attempts to load and use +** a specific OpenGL DLL. +*/ +static qboolean GLW_LoadOpenGL( const char *name ) +{ + qboolean fullscreen; + + ri.Printf( PRINT_ALL, "...loading %s: ", name ); + + // disable the 3Dfx splash screen and set gamma + // we do this all the time, but it shouldn't hurt anything + // on non-3Dfx stuff + putenv("FX_GLIDE_NO_SPLASH=0"); + + // Mesa VooDoo hacks + putenv("MESA_GLX_FX=fullscreen\n"); + + // load the QGL layer + if ( QGL_Init( name ) ) + { + fullscreen = r_fullscreen->integer; + + // create the window and set up the context + if ( !GLW_StartDriverAndSetMode( name, r_mode->integer, fullscreen ) ) + { + if (r_mode->integer != 3) + { + if ( !GLW_StartDriverAndSetMode( name, 3, fullscreen ) ) + { + goto fail; + } + } else + goto fail; + } + + return qtrue; + } else + { + ri.Printf( PRINT_ALL, "failed\n" ); + } + fail: + + QGL_Shutdown(); + + return qfalse; +} + +/* +** XErrorHandler +** the default X error handler exits the application +** I found out that on some hosts some operations would raise X errors (GLXUnsupportedPrivateRequest) +** but those don't seem to be fatal .. so the default would be to just ignore them +** our implementation mimics the default handler behaviour (not completely cause I'm lazy) +*/ +int qXErrorHandler(Display *dpy, XErrorEvent *ev) +{ + static char buf[1024]; + XGetErrorText(dpy, ev->error_code, buf, 1024); + ri.Printf( PRINT_ALL, "X Error of failed request: %s\n", buf); + ri.Printf( PRINT_ALL, " Major opcode of failed request: %d\n", ev->request_code, buf); + ri.Printf( PRINT_ALL, " Minor opcode of failed request: %d\n", ev->minor_code); + ri.Printf( PRINT_ALL, " Serial number of failed request: %d\n", ev->serial); + return 0; +} + +/* +** GLimp_Init +** +** This routine is responsible for initializing the OS specific portions +** of OpenGL. +*/ +void GLimp_Init( void ) +{ + qboolean attemptedlibGL = qfalse; + qboolean attempted3Dfx = qfalse; + qboolean success = qfalse; + char buf[1024]; + cvar_t *lastValidRenderer = ri.Cvar_Get( "r_lastValidRenderer", "(uninitialized)", CVAR_ARCHIVE ); + + // guarded, as this is only relevant to SMP renderer thread +#ifdef SMP + if (!XInitThreads()) + { + Com_Printf("GLimp_Init() - XInitThreads() failed, disabling r_smp\n"); + ri.Cvar_Set( "r_smp", "0" ); + } +#endif + + r_allowSoftwareGL = ri.Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH ); + + r_previousglDriver = ri.Cvar_Get( "r_previousglDriver", "", CVAR_ROM ); + + InitSig(); + + // Hack here so that if the UI + if ( *r_previousglDriver->string ) + { + // The UI changed it on us, hack it back + // This means the renderer can't be changed on the fly + ri.Cvar_Set( "r_glDriver", r_previousglDriver->string ); + } + + // set up our custom error handler for X failures + XSetErrorHandler(&qXErrorHandler); + + // + // load and initialize the specific OpenGL driver + // + if ( !GLW_LoadOpenGL( r_glDriver->string ) ) + { + if ( !Q_stricmp( r_glDriver->string, OPENGL_DRIVER_NAME ) ) + { + attemptedlibGL = qtrue; + } else if ( !Q_stricmp( r_glDriver->string, _3DFX_DRIVER_NAME ) ) + { + attempted3Dfx = qtrue; + } + + #if 0 + // TTimo + // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=455 + // old legacy load code, was confusing people who had a bad OpenGL setup + if ( !attempted3Dfx && !success ) + { + attempted3Dfx = qtrue; + if ( GLW_LoadOpenGL( _3DFX_DRIVER_NAME ) ) + { + ri.Cvar_Set( "r_glDriver", _3DFX_DRIVER_NAME ); + r_glDriver->modified = qfalse; + success = qtrue; + } + } + #endif + + // try ICD before trying 3Dfx standalone driver + if ( !attemptedlibGL && !success ) + { + attemptedlibGL = qtrue; + if ( GLW_LoadOpenGL( OPENGL_DRIVER_NAME ) ) + { + ri.Cvar_Set( "r_glDriver", OPENGL_DRIVER_NAME ); + r_glDriver->modified = qfalse; + success = qtrue; + } + } + + if (!success) + ri.Error( ERR_FATAL, "GLimp_Init() - could not load OpenGL subsystem\n" ); + + } + + // Save it in case the UI stomps it + ri.Cvar_Set( "r_previousglDriver", r_glDriver->string ); + + // This values force the UI to disable driver selection + glConfig.driverType = GLDRV_ICD; + glConfig.hardwareType = GLHW_GENERIC; + + // get our config strings + Q_strncpyz( glConfig.vendor_string, qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) ); + Q_strncpyz( glConfig.renderer_string, qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) ); + if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n') + glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0; + Q_strncpyz( glConfig.version_string, qglGetString (GL_VERSION), sizeof( glConfig.version_string ) ); + Q_strncpyz( glConfig.extensions_string, qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) ); + + // + // chipset specific configuration + // + strcpy( buf, glConfig.renderer_string ); + strlwr( buf ); + + // + // NOTE: if changing cvars, do it within this block. This allows them + // to be overridden when testing driver fixes, etc. but only sets + // them to their default state when the hardware is first installed/run. + // + if ( Q_stricmp( lastValidRenderer->string, glConfig.renderer_string ) ) + { + glConfig.hardwareType = GLHW_GENERIC; + + ri.Cvar_Set( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST" ); + + // VOODOO GRAPHICS w/ 2MB + if ( Q_stristr( buf, "voodoo graphics/1 tmu/2 mb" ) ) + { + ri.Cvar_Set( "r_picmip", "2" ); + ri.Cvar_Get( "r_picmip", "1", CVAR_ARCHIVE | CVAR_LATCH ); + } else + { + ri.Cvar_Set( "r_picmip", "1" ); + + if ( Q_stristr( buf, "rage 128" ) || Q_stristr( buf, "rage128" ) ) + { + ri.Cvar_Set( "r_finish", "0" ); + } + // Savage3D and Savage4 should always have trilinear enabled + else if ( Q_stristr( buf, "savage3d" ) || Q_stristr( buf, "s3 savage4" ) ) + { + ri.Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" ); + } + } + } + + // + // this is where hardware specific workarounds that should be + // detected/initialized every startup should go. + // + if ( Q_stristr( buf, "banshee" ) || Q_stristr( buf, "Voodoo_Graphics" ) ) + { + glConfig.hardwareType = GLHW_3DFX_2D3D; + } else if ( Q_stristr( buf, "rage pro" ) || Q_stristr( buf, "RagePro" ) ) + { + glConfig.hardwareType = GLHW_RAGEPRO; + } else if ( Q_stristr( buf, "permedia2" ) ) + { + glConfig.hardwareType = GLHW_PERMEDIA2; + } else if ( Q_stristr( buf, "riva 128" ) ) + { + glConfig.hardwareType = GLHW_RIVA128; + } else if ( Q_stristr( buf, "riva tnt " ) ) + { + } + + ri.Cvar_Set( "r_lastValidRenderer", glConfig.renderer_string ); + + // initialize extensions + GLW_InitExtensions(); + GLW_InitGamma(); + + InitSig(); // not clear why this is at begin & end of function + + return; +} + + +/* +** GLimp_EndFrame +** +** Responsible for doing a swapbuffers and possibly for other stuff +** as yet to be determined. Probably better not to make this a GLimp +** function and instead do a call to GLimp_SwapBuffers. +*/ +void GLimp_EndFrame (void) +{ + // don't flip if drawing to front buffer + if ( stricmp( r_drawBuffer->string, "GL_FRONT" ) != 0 ) + { + qglXSwapBuffers(dpy, win); + } + + // check logging + QGL_EnableLogging( (qboolean)r_logFile->integer ); // bk001205 - was ->value +} + +#ifdef SMP +/* +=========================================================== + +SMP acceleration + +=========================================================== +*/ + +static pthread_mutex_t smpMutex = PTHREAD_MUTEX_INITIALIZER; + +static pthread_cond_t renderCommandsEvent = PTHREAD_COND_INITIALIZER; +static pthread_cond_t renderCompletedEvent = PTHREAD_COND_INITIALIZER; + +static void (*glimpRenderThread)( void ); + +static void *GLimp_RenderThreadWrapper( void *arg ) +{ + Com_Printf( "Render thread starting\n" ); + + glimpRenderThread(); + + qglXMakeCurrent( dpy, None, NULL ); + + Com_Printf( "Render thread terminating\n" ); + + return arg; +} + +qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) +{ + pthread_t renderThread; + int ret; + + pthread_mutex_init( &smpMutex, NULL ); + + pthread_cond_init( &renderCommandsEvent, NULL ); + pthread_cond_init( &renderCompletedEvent, NULL ); + + glimpRenderThread = function; + + ret = pthread_create( &renderThread, + NULL, // attributes + GLimp_RenderThreadWrapper, + NULL ); // argument + if ( ret ) { + ri.Printf( PRINT_ALL, "pthread_create returned %d: %s", ret, strerror( ret ) ); + return qfalse; + } else { + ret = pthread_detach( renderThread ); + if ( ret ) { + ri.Printf( PRINT_ALL, "pthread_detach returned %d: %s", ret, strerror( ret ) ); + } + } + + return qtrue; +} + +static volatile void *smpData = NULL; +static volatile qboolean smpDataReady; + +void *GLimp_RendererSleep( void ) +{ + void *data; + + qglXMakeCurrent( dpy, None, NULL ); + + pthread_mutex_lock( &smpMutex ); + { + smpData = NULL; + smpDataReady = qfalse; + + // after this, the front end can exit GLimp_FrontEndSleep + pthread_cond_signal( &renderCompletedEvent ); + + while ( !smpDataReady ) { + pthread_cond_wait( &renderCommandsEvent, &smpMutex ); + } + + data = (void *)smpData; + } + pthread_mutex_unlock( &smpMutex ); + + qglXMakeCurrent( dpy, win, ctx ); + + return data; +} + +void GLimp_FrontEndSleep( void ) +{ + pthread_mutex_lock( &smpMutex ); + { + while ( smpData ) { + pthread_cond_wait( &renderCompletedEvent, &smpMutex ); + } + } + pthread_mutex_unlock( &smpMutex ); + + qglXMakeCurrent( dpy, win, ctx ); +} + +void GLimp_WakeRenderer( void *data ) +{ + qglXMakeCurrent( dpy, None, NULL ); + + pthread_mutex_lock( &smpMutex ); + { + assert( smpData == NULL ); + smpData = data; + smpDataReady = qtrue; + + // after this, the renderer can continue through GLimp_RendererSleep + pthread_cond_signal( &renderCommandsEvent ); + } + pthread_mutex_unlock( &smpMutex ); +} + +#else + +void GLimp_RenderThreadWrapper( void *stub ) {} +qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) { + ri.Printf( PRINT_WARNING, "ERROR: SMP support was disabled at compile time\n"); + return qfalse; +} +void *GLimp_RendererSleep( void ) { + return NULL; +} +void GLimp_FrontEndSleep( void ) {} +void GLimp_WakeRenderer( void *data ) {} + +#endif + +/*****************************************************************************/ +/* MOUSE */ +/*****************************************************************************/ + +void IN_Init(void) { + Com_Printf ("\n------- Input Initialization -------\n"); + // mouse variables + in_mouse = Cvar_Get ("in_mouse", "1", CVAR_ARCHIVE); + in_dgamouse = Cvar_Get ("in_dgamouse", "1", CVAR_ARCHIVE); + + // turn on-off sub-frame timing of X events + in_subframe = Cvar_Get ("in_subframe", "1", CVAR_ARCHIVE); + + // developer feature, allows to break without loosing mouse pointer + in_nograb = Cvar_Get ("in_nograb", "0", 0); + + // bk001130 - from cvs.17 (mkv), joystick variables + in_joystick = Cvar_Get ("in_joystick", "0", CVAR_ARCHIVE|CVAR_LATCH); + // bk001130 - changed this to match win32 + in_joystickDebug = Cvar_Get ("in_debugjoystick", "0", CVAR_TEMP); + joy_threshold = Cvar_Get ("joy_threshold", "0.15", CVAR_ARCHIVE); // FIXME: in_joythreshold + + if (in_mouse->value) + mouse_avail = qtrue; + else + mouse_avail = qfalse; + + IN_StartupJoystick( ); // bk001130 - from cvs1.17 (mkv) + Com_Printf ("------------------------------------\n"); +} + +void IN_Shutdown(void) +{ + mouse_avail = qfalse; +} + +void IN_Frame (void) { + + // bk001130 - from cvs 1.17 (mkv) + IN_JoyMove(); // FIXME: disable if on desktop? + + if ( cls.keyCatchers & KEYCATCH_CONSOLE ) + { + // temporarily deactivate if not in the game and + // running on the desktop + // voodoo always counts as full screen + if (Cvar_VariableValue ("r_fullscreen") == 0 + && strcmp( Cvar_VariableString("r_glDriver"), _3DFX_DRIVER_NAME ) ) + { + IN_DeactivateMouse (); + return; + } + } + + IN_ActivateMouse(); +} + +void IN_Activate(void) +{ +} + +// bk001130 - cvs1.17 joystick code (mkv) was here, no linux_joystick.c + +void Sys_SendKeyEvents (void) { + // XEvent event; // bk001204 - unused + + if (!dpy) + return; + HandleEvents(); +} + + +// bk010216 - added stubs for non-Linux UNIXes here +// FIXME - use NO_JOYSTICK or something else generic + +#if defined( __FreeBSD__ ) // rb010123 +void IN_StartupJoystick( void ) {} +void IN_JoyMove( void ) {} +#endif |