📄 linux_glimp.c
字号:
}
// 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -