mirror of
https://github.com/ioquake/jedi-academy.git
synced 2024-11-29 23:41:52 +00:00
1544 lines
39 KiB
C
1544 lines
39 KiB
C
|
/*
|
||
|
** 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
|
||
|
**
|
||
|
*/
|
||
|
|
||
|
#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 3: 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;
|
||
|
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;
|
||
|
|
||
|
// 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 dgamouse = qfalse;
|
||
|
qboolean vidmode_ext = qfalse;
|
||
|
|
||
|
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 */
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
// bk001204 - unused
|
||
|
// static unsigned int keyshift[256]; // key to map to if shift held down in console
|
||
|
// static qboolean shift_down=qfalse;
|
||
|
|
||
|
static char *XLateKey(XKeyEvent *ev, int *key)
|
||
|
{
|
||
|
static char buf[64];
|
||
|
KeySym keysym;
|
||
|
// static qboolean setup = qfalse; // bk001204 - unused
|
||
|
// int i; // bk001204 - unused
|
||
|
|
||
|
*key = 0;
|
||
|
|
||
|
XLookupString(ev, buf, sizeof buf, &keysym, 0);
|
||
|
|
||
|
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;
|
||
|
|
||
|
default:
|
||
|
*key = *(unsigned char *)buf;
|
||
|
if (*key >= 'A' && *key <= 'Z')
|
||
|
*key = *key - 'A' + 'a';
|
||
|
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
|
||
|
ri.Printf( PRINT_ALL, "Failed to detect XF86DGA Mouse\n" );
|
||
|
ri.Cvar_Set( "in_dgamouse", "0" );
|
||
|
} else {
|
||
|
dgamouse = qtrue;
|
||
|
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 (dgamouse) {
|
||
|
dgamouse = qfalse;
|
||
|
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
|
||
|
|
||
|
|
||
|
|
||
|
static void HandleEvents(void)
|
||
|
{
|
||
|
int b;
|
||
|
int key;
|
||
|
XEvent event;
|
||
|
qboolean dowarp = qfalse;
|
||
|
char *p;
|
||
|
int dx, dy;
|
||
|
int t;
|
||
|
|
||
|
if (!dpy)
|
||
|
return;
|
||
|
|
||
|
while (XPending(dpy)) {
|
||
|
XNextEvent(dpy, &event);
|
||
|
switch(event.type) {
|
||
|
case KeyPress:
|
||
|
p = XLateKey(&event.xkey, &key);
|
||
|
if (key)
|
||
|
Sys_QueEvent( 0, SE_KEY, key, qtrue, 0, NULL );
|
||
|
while (*p)
|
||
|
Sys_QueEvent( 0, SE_CHAR, *p++, 0, 0, NULL );
|
||
|
break;
|
||
|
|
||
|
case KeyRelease:
|
||
|
|
||
|
// 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( 0, SE_KEY, key, qfalse, 0, NULL );
|
||
|
break;
|
||
|
|
||
|
case MotionNotify:
|
||
|
if (mouse_active) {
|
||
|
if (dgamouse) {
|
||
|
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;
|
||
|
t = Sys_Milliseconds();
|
||
|
if (t - mouseResetTime > MOUSE_RESET_DELAY ) {
|
||
|
Sys_QueEvent( t, SE_MOUSE, mx, my, 0, NULL );
|
||
|
}
|
||
|
mx = my = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// ri.Printf( PRINT_ALL, "MotionNotify: %d,%d: ", event.xmotion.x, event.xmotion.y );
|
||
|
// 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;
|
||
|
// ri.Printf( PRINT_ALL, "SE_MOUSE (%d,%d)\n", mx, my );
|
||
|
t = Sys_Milliseconds();
|
||
|
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;
|
||
|
|
||
|
// ri.Printf( PRINT_ALL, "mx=%d,my=%d [%d - %d,%d - %d]\n", mx, my, event.xmotion.x, mwx, event.xmotion.y, mwy );
|
||
|
mwx = event.xmotion.x;
|
||
|
mwy = event.xmotion.y;
|
||
|
dowarp = qtrue;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ButtonPress:
|
||
|
if (event.xbutton.button == 4) {
|
||
|
Sys_QueEvent( 0, SE_KEY, K_MWHEELUP, qtrue, 0, NULL );
|
||
|
} else if (event.xbutton.button == 5) {
|
||
|
Sys_QueEvent( 0, SE_KEY, K_MWHEELDOWN, qtrue, 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;
|
||
|
}
|
||
|
|
||
|
Sys_QueEvent( 0, SE_KEY, K_MOUSE1 + b, qtrue, 0, NULL );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case ButtonRelease:
|
||
|
if (event.xbutton.button == 4) {
|
||
|
Sys_QueEvent( 0, SE_KEY, K_MWHEELUP, qfalse, 0, NULL );
|
||
|
} else if (event.xbutton.button == 5) {
|
||
|
Sys_QueEvent( 0, 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;
|
||
|
}
|
||
|
Sys_QueEvent( 0, 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));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void KBD_Init(void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void KBD_Close(void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void IN_ActivateMouse( void )
|
||
|
{
|
||
|
if (!mouse_avail || !dpy || !win)
|
||
|
return;
|
||
|
|
||
|
if (!mouse_active) {
|
||
|
install_grabs();
|
||
|
mouse_active = qtrue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void IN_DeactivateMouse( void )
|
||
|
{
|
||
|
if (!mouse_avail || !dpy || !win)
|
||
|
return;
|
||
|
|
||
|
if (mouse_active) {
|
||
|
uninstall_grabs();
|
||
|
mouse_active = qfalse;
|
||
|
}
|
||
|
}
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
static qboolean signalcaught = qfalse;;
|
||
|
|
||
|
void Sys_Exit(int); // bk010104 - abstraction
|
||
|
|
||
|
static void signal_handler(int sig) // bk010104 - replace this...
|
||
|
{
|
||
|
if (signalcaught) {
|
||
|
printf("DOUBLE SIGNAL FAULT: Received signal %d, exiting...\n", sig);
|
||
|
Sys_Exit(1); // bk010104 - abstraction
|
||
|
}
|
||
|
|
||
|
signalcaught = qtrue;
|
||
|
printf("Received signal %d, exiting...\n", sig);
|
||
|
GLimp_Shutdown(); // bk010104 - shouldn't this be CL_Shutdown
|
||
|
Sys_Exit(1); // bk010104 - abstraction
|
||
|
}
|
||
|
|
||
|
static void InitSig(void)
|
||
|
{
|
||
|
signal(SIGHUP, signal_handler);
|
||
|
signal(SIGQUIT, signal_handler);
|
||
|
signal(SIGILL, signal_handler);
|
||
|
signal(SIGTRAP, signal_handler);
|
||
|
signal(SIGIOT, signal_handler);
|
||
|
signal(SIGBUS, signal_handler);
|
||
|
signal(SIGFPE, signal_handler);
|
||
|
signal(SIGSEGV, signal_handler);
|
||
|
signal(SIGTERM, signal_handler);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** 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] )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** 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]);
|
||
|
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
|
||
|
|
||
|
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;
|
||
|
unsigned long mask;
|
||
|
int colorbits, depthbits, stencilbits;
|
||
|
int tcolorbits, tdepthbits, tstencilbits;
|
||
|
int MajorVersion, 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
|
||
|
MajorVersion = MinorVersion = 0;
|
||
|
if (!XF86VidModeQueryVersion(dpy, &MajorVersion, &MinorVersion)) {
|
||
|
vidmode_ext = qfalse;
|
||
|
} else {
|
||
|
ri.Printf(PRINT_ALL, "Using XFree86-VidModeExtension Version %d.%d\n",
|
||
|
MajorVersion, MinorVersion);
|
||
|
vidmode_ext = qtrue;
|
||
|
}
|
||
|
|
||
|
// Check for DGA
|
||
|
if (in_dgamouse->value) {
|
||
|
if (!XF86DGAQueryVersion(dpy, &MajorVersion, &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",
|
||
|
MajorVersion, 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 );
|
||
|
|
||
|
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)
|
||
|
|
||
|
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" );
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** 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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** 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 );
|
||
|
// cvar_t *cv; // bk001204 - unused
|
||
|
|
||
|
r_allowSoftwareGL = ri.Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH );
|
||
|
|
||
|
r_previousglDriver = ri.Cvar_Get( "r_previousglDriver", "", CVAR_ROM );
|
||
|
|
||
|
glConfig.deviceSupportsGamma = qfalse;
|
||
|
|
||
|
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 );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 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 ( !attempted3Dfx && !success )
|
||
|
{
|
||
|
attempted3Dfx = qtrue;
|
||
|
if ( GLW_LoadOpenGL( _3DFX_DRIVER_NAME ) )
|
||
|
{
|
||
|
ri.Cvar_Set( "r_glDriver", _3DFX_DRIVER_NAME );
|
||
|
r_glDriver->modified = qfalse;
|
||
|
success = qtrue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 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();
|
||
|
|
||
|
InitSig();
|
||
|
|
||
|
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
|
||
|
|
||
|
===========================================================
|
||
|
*/
|
||
|
|
||
|
sem_t renderCommandsEvent;
|
||
|
sem_t renderCompletedEvent;
|
||
|
sem_t renderActiveEvent;
|
||
|
|
||
|
void (*glimpRenderThread)( void );
|
||
|
|
||
|
void *GLimp_RenderThreadWrapper( void *stub ) {
|
||
|
glimpRenderThread();
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
=======================
|
||
|
GLimp_SpawnRenderThread
|
||
|
=======================
|
||
|
*/
|
||
|
pthread_t renderThreadHandle;
|
||
|
qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) {
|
||
|
|
||
|
sem_init( &renderCommandsEvent, 0, 0 );
|
||
|
sem_init( &renderCompletedEvent, 0, 0 );
|
||
|
sem_init( &renderActiveEvent, 0, 0 );
|
||
|
|
||
|
glimpRenderThread = function;
|
||
|
|
||
|
if (pthread_create( &renderThreadHandle, NULL,
|
||
|
GLimp_RenderThreadWrapper, NULL)) {
|
||
|
return qfalse;
|
||
|
}
|
||
|
|
||
|
return qtrue;
|
||
|
}
|
||
|
|
||
|
static void *smpData;
|
||
|
//static int glXErrors; // bk001204 - unused
|
||
|
|
||
|
void *GLimp_RendererSleep( void ) {
|
||
|
void *data;
|
||
|
|
||
|
// after this, the front end can exit GLimp_FrontEndSleep
|
||
|
sem_post ( &renderCompletedEvent );
|
||
|
|
||
|
sem_wait ( &renderCommandsEvent );
|
||
|
|
||
|
data = smpData;
|
||
|
|
||
|
// after this, the main thread can exit GLimp_WakeRenderer
|
||
|
sem_post ( &renderActiveEvent );
|
||
|
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
|
||
|
void GLimp_FrontEndSleep( void ) {
|
||
|
sem_wait ( &renderCompletedEvent );
|
||
|
}
|
||
|
|
||
|
|
||
|
void GLimp_WakeRenderer( void *data ) {
|
||
|
smpData = data;
|
||
|
|
||
|
// after this, the renderer can continue through GLimp_RendererSleep
|
||
|
sem_post( &renderCommandsEvent );
|
||
|
|
||
|
sem_wait( &renderActiveEvent );
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
void GLimp_RenderThreadWrapper( void *stub ) { }
|
||
|
qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) {
|
||
|
return qfalse;
|
||
|
}
|
||
|
void *GLimp_RendererSleep( void ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
void GLimp_FrontEndSleep( void ) { }
|
||
|
void GLimp_WakeRenderer( void *data ) { }
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
/* MOUSE */
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
void IN_Init(void) {
|
||
|
// mouse variables
|
||
|
in_mouse = Cvar_Get ("in_mouse", "1", CVAR_ARCHIVE);
|
||
|
in_dgamouse = Cvar_Get ("in_dgamouse", "1", CVAR_ARCHIVE);
|
||
|
|
||
|
// 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)
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
// bk001206 - not used, now done the H2/Fakk2 way
|
||
|
//if (dpy && !autorepeaton) {
|
||
|
// XAutoRepeatOn(dpy);
|
||
|
// autorepeaton = qtrue;
|
||
|
//}
|
||
|
}
|
||
|
//else if (dpy && autorepeaton) {
|
||
|
//XAutoRepeatOff(dpy);
|
||
|
//autorepeaton = qfalse;
|
||
|
//}
|
||
|
|
||
|
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
|