From d8bf2665f55d2e8666ba7a3c25af4086cc43c454 Mon Sep 17 00:00:00 2001 From: arQon Date: Wed, 25 Jan 2017 07:14:23 -0800 Subject: [PATCH] rewrite most of the linux mouse code: support (and prefer) xinput2 and raw support, default to the master pointer and add m_device to choose one explicitly. --- build/gmake/cnq3.make | 4 +- build/premake5.lua | 2 +- code/renderer/qgl.h | 13 - code/unix/{linux_glimp.c => linux_glimp.cpp} | 1566 +++++++++--------- code/unix/linux_qgl.c | 35 - code/unix/unix_glw.h | 4 +- code/unix/unix_shared.cpp | 116 +- 7 files changed, 819 insertions(+), 921 deletions(-) rename code/unix/{linux_glimp.c => linux_glimp.cpp} (54%) diff --git a/build/gmake/cnq3.make b/build/gmake/cnq3.make index 58a7558..1c99250 100644 --- a/build/gmake/cnq3.make +++ b/build/gmake/cnq3.make @@ -395,9 +395,9 @@ $(OBJDIR)/sv_snapshot.o: ../../code/server/sv_snapshot.cpp $(OBJDIR)/sv_world.o: ../../code/server/sv_world.cpp @echo $(notdir $<) $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" -$(OBJDIR)/linux_glimp.o: ../../code/unix/linux_glimp.c +$(OBJDIR)/linux_glimp.o: ../../code/unix/linux_glimp.cpp @echo $(notdir $<) - $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" + $(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" $(OBJDIR)/linux_joystick.o: ../../code/unix/linux_joystick.c @echo $(notdir $<) $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<" diff --git a/build/premake5.lua b/build/premake5.lua index 157c84c..8b45f66 100644 --- a/build/premake5.lua +++ b/build/premake5.lua @@ -393,7 +393,7 @@ local function ApplyExeProjectSettings(exeName, server) "unix/linux_signals.cpp", "unix/linux_qgl.c", "unix/linux_snd.c", - "unix/linux_glimp.c" + "unix/linux_glimp.cpp" } AddHeaders("botlib") diff --git a/code/renderer/qgl.h b/code/renderer/qgl.h index 9666dc4..44d9b9a 100644 --- a/code/renderer/qgl.h +++ b/code/renderer/qgl.h @@ -59,9 +59,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include -#if defined(__FX__) -#include -#endif #elif defined( __sun ) #include @@ -480,16 +477,6 @@ extern BOOL ( WINAPI * qwglSwapIntervalEXT)( int interval ); #if ( (defined __linux__ ) || (defined __FreeBSD__ ) || (defined __sun) ) -//FX Mesa Functions -#if defined (__FX__) -extern fxMesaContext (*qfxMesaCreateContext)(GLuint win, GrScreenResolution_t, GrScreenRefresh_t, const GLint attribList[]); -extern fxMesaContext (*qfxMesaCreateBestContext)(GLuint win, GLint width, GLint height, const GLint attribList[]); -extern void (*qfxMesaDestroyContext)(fxMesaContext ctx); -extern void (*qfxMesaMakeCurrent)(fxMesaContext ctx); -extern fxMesaContext (*qfxMesaGetCurrentContext)(void); -extern void (*qfxMesaSwapBuffers)(void); -#endif - //GLX Functions extern XVisualInfo * (*qglXChooseVisual)( Display *dpy, int screen, int *attribList ); extern GLXContext (*qglXCreateContext)( Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct ); diff --git a/code/unix/linux_glimp.c b/code/unix/linux_glimp.cpp similarity index 54% rename from code/unix/linux_glimp.c rename to code/unix/linux_glimp.cpp index b9f1afe..666eacc 100644 --- a/code/unix/linux_glimp.c +++ b/code/unix/linux_glimp.cpp @@ -42,196 +42,454 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include #endif -#include -#include +#include #include #include #include -// bk001204 -#include - -// bk001206 - from my Heretic2 by way of Ryan's Fakk2 -// Needed for the new X11_PendingInput() function. -#include -#include -#include - -#include "../renderer/tr_local.h" -#include "../client/client.h" -#include "linux_local.h" // bk001130 - -#include "unix_glw.h" - #include #include -#include +#include +#include -#if !defined(__sun) -// @TODO: -//#include +#include "../renderer/tr_local.h" +#include "../client/client.h" +#include "linux_local.h" +#include "unix_glw.h" + +//#if !defined(__sun) +//#include //#include -#endif +//#endif -#if defined(__sun) -#include -#endif - -#ifdef _XF86DGA_H_ -#define HAVE_XF86DGA -#endif - -#define WINDOW_CLASS_NAME "CNQ3" - -// OpenGL driver -#define OPENGL_DRIVER_NAME "libGL.so.1" - -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; -static XVisualInfo *visinfo = NULL; // drakkar - create once and reuse -// bk001206 - not needed anymore -// static qboolean autorepeaton = qtrue; -#define KEY_MASK (KeyPressMask | KeyReleaseMask) -#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ButtonMotionMask ) -// drakkar -//- #define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask | StructureNotifyMask ) -#define WINDOW_MASK ( VisibilityChangeMask | StructureNotifyMask | FocusChangeMask ) -#define X_MASK ( KEY_MASK | MOUSE_MASK | WINDOW_MASK ) -// /drakkar +/////////////////////////////////////////////////////////////// -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 +struct Mouse { + virtual qbool Init() { return qtrue; } + virtual qbool Activate( qbool active ); + virtual void Shutdown() {} + virtual void Read( int* mx, int* my ) = 0; + virtual qbool ProcessEvent( XEvent& event ) { return qfalse; } // returns true if the event was handled -static cvar_t *in_mouse; -static cvar_t *in_dgamouse; // user pref for dga mouse -static cvar_t *in_shiftedKeys; // obey modifiers for certain keys in non-console (comma, numbers, etc) -cvar_t *in_subframe; -cvar_t *in_nograb; // this is strictly for developers + Mouse() : active(qfalse) {} -// bk001130 - from cvs1.17 (mkv), but not static -cvar_t *in_joystick = NULL; -cvar_t *in_joystickDebug = NULL; -cvar_t *joy_threshold = NULL; + static const int buttons[]; -cvar_t *r_allowSoftwareGL; // don't abort out if the pixelformat claims software -cvar_t *r_fullscreen; +private: + qbool active; +}; -qboolean vidmode_ext = qfalse; -#ifdef HAVE_XF86DGA -static int vidmode_MajorVersion = 0, vidmode_MinorVersion = 0; // major and minor of XF86VidExtensions +static Mouse* mouse; +const int Mouse::buttons[] = { 0, K_MOUSE1, K_MOUSE3, K_MOUSE2, K_MWHEELUP, K_MWHEELDOWN, 0, 0, K_MOUSE4, K_MOUSE5 }; -// gamma value of the X display before we start playing with it -static XF86VidModeGamma vidmode_InitialGamma = { -1,-1,-1 }; // drakkar - initialized to nonvalid values -#endif /* HAVE_XF86DGA */ -static int win_x = 50, win_y = 50; // drakkar - initialize window position -qboolean win_active = qtrue; -qboolean win_minimized = qfalse; -static int desktopVideoMode = -1; - -#ifdef HAVE_XF86DGA -static XF86VidModeModeInfo **vidmodes = NULL; // drakkar - initialized to NULL -#endif /* HAVE_XF86DGA */ -//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) +qbool Mouse::Activate( qbool _active ) { - register char c, sc; - register size_t len; + if (active == _active) + return qfalse; - 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; + active = _active; + + return qtrue; } -/***************************************************************************** -** KEYBOARD + +/////////////////////////////////////////////////////////////// + + +struct xmouse_t : public Mouse { + virtual qbool Activate( qbool active ); + virtual void Read( int* mx, int* my ); + virtual qbool ProcessEvent( XEvent& event ); + + int x, y, prev_x, prev_y; + int window_center_x, window_center_y; +}; + +static xmouse_t xmouse; + + +qbool xmouse_t::Activate( qbool active ) +{ + if (!active) + return qtrue; + + window_center_x = glConfig.vidWidth / 2; + window_center_y = glConfig.vidHeight / 2; + + XWarpPointer( dpy, None, win, 0,0,0,0, window_center_x, window_center_y ); + return qtrue; +} + + +void xmouse_t::Read( int* mx, int* my ) +{ + // we could just use XQueryPointer here + // but since we're processing events anyway we might as well use the data from those + *mx = x; + *my = y; + x = y = 0; + + if (*mx || *my) { + XWarpPointer( dpy, None, win, 0,0,0,0, window_center_x, window_center_y ); + } +} + + +qbool xmouse_t::ProcessEvent( XEvent& event ) +{ + switch (event.type) { + + case MotionNotify: { + // if this is exactly recentering the mouse, it's probably from our warp + // but there's no way to actually handle this problem well in X + if ((event.xmotion.x == window_center_x) && (event.xmotion.y == window_center_y)) { + //Com_Printf( "WARP: mx %d my %d \n", event.xmotion.x, event.xmotion.y ); + prev_x = event.xmotion.x; + prev_y = event.xmotion.y; + break; + } + + //Com_Printf( "mx %d my %d \n", event.xmotion.x, event.xmotion.y ); + // note that accumulating motion events like this is actually wrong + // because button events are processed immediately + // but the windows code has the same bug, and nobody seems upset by it there + x += (event.xmotion.x - prev_x); + y += (event.xmotion.y - prev_y); + prev_x = event.xmotion.x; + prev_y = event.xmotion.y; + return qtrue; + } + break; + + case ButtonPress: + case ButtonRelease: + //Com_Printf( "Button %u \n", event.xbutton.button ); + if ((event.xbutton.button > 0) && (event.xbutton.button < (sizeof(buttons) / sizeof(buttons[0])))) { + int button = buttons[event.xbutton.button]; + if (button) { + Sys_QueEvent( 0, SE_KEY, button, (event.type == ButtonPress), 0, NULL ); + } + } + break; + + } + + return qfalse; +} + + +/////////////////////////////////////////////////////////////// + + +// this is a total suckfest. debian didn't multiarch xinput for some reason, +// so using it means having to dlsym everything or you can't build both i386 and x64 + + +typedef XIDeviceInfo* (*xiQueryDevice)( Display *display, int deviceid, int *ndevices_return ); +typedef void (*xiFreeDeviceInfo)( XIDeviceInfo* ); +typedef Status (*xiQueryVersion)( Display *display, int *major_version_inout, int *minor_version_inout ); +typedef Status (*xiSelectEvents)( Display *display, Window win, XIEventMask *masks, int num_masks ); + +static void* libxi = 0; +static xiQueryDevice pxiQueryDevice; +static xiFreeDeviceInfo pxiFreeDeviceInfo; +static xiQueryVersion pxiQueryVersion; +static xiSelectEvents pxiSelectEvents; +static int xiOPCode; + + +static void* XI_GetProcAddress( const char* symbol ) +{ + if (!libxi) + return 0; + return dlsym( libxi, symbol ); +} +#define XI(F) { pxi##F = (xi##F)XI_GetProcAddress( "XI"#F ); } + + +struct rawmouse_t : public Mouse { + virtual qbool Init(); + //virtual qbool Activate( qbool active ); + virtual void Shutdown(); + virtual void Read( int* mx, int* my ); + virtual qbool ProcessEvent( XEvent& event ); + +private: + int x, y, prev_x, prev_y; + int mode; + + static int FindMouse(); + static qbool ValidateMouse( const XIDeviceInfo* info ); +}; + +static rawmouse_t rawmouse; + + +qbool rawmouse_t::ValidateMouse( const XIDeviceInfo* info ) +{ + if (!info->enabled) + return qfalse; + + const XIAnyClassInfo** classes = (const XIAnyClassInfo**)info->classes; + for (int i = 0; i < info->num_classes; ++i) { + if (classes[i]->type == XIValuatorClass) { + const XIValuatorClassInfo* v = (const XIValuatorClassInfo*)classes[i]; + if ((v->mode == XIModeRelative) && !v->resolution) + return qfalse; // invalid combination (eg XTEST mouse) + ri.Printf( PRINT_DEVELOPER, "ValidateMouse: accepted device %d\n", info->deviceid ); + rawmouse.mode = v->mode; + return qtrue; + } + } + + return qfalse; +} + + +int rawmouse_t::FindMouse() +{ + int n, device = 0; + XIDeviceInfo* info = pxiQueryDevice( dpy, XIAllDevices, &n ); + + cvar_t* m_device = Cvar_Get( "m_device", "0", 0 ); + if (m_device->integer) { + for (int i = 0; i < n; ++i) { + if ((info[i].deviceid == m_device->integer) && ValidateMouse(&info[i])) { + device = info[i].deviceid; + pxiFreeDeviceInfo( info ); + return device; + } + } + } + + for (int i = 0; i < n; ++i) { + if ((info[i].use == XIMasterPointer) && ValidateMouse(&info[i])) { + device = info[i].deviceid; + break; + } + } + + pxiFreeDeviceInfo( info ); + return device; +} + + +qbool rawmouse_t::Init() +{ + int event, error; + if (!XQueryExtension(dpy, "XInputExtension", &xiOPCode, &event, &error)) + return qfalse; + + libxi = dlopen( "libXi.so.6", RTLD_GLOBAL | RTLD_LAZY ); + XI(QueryDevice); + XI(FreeDeviceInfo); + XI(QueryVersion); + XI(SelectEvents); + + int major = 2, minor = 1; + if (pxiQueryVersion(dpy, &major, &minor) == BadRequest) { + return qfalse; + } + + XIEventMask eventmask; + unsigned char mask[4] = { 0 }; + eventmask.deviceid = FindMouse(); + if (!eventmask.deviceid) { + return qfalse; + } + + eventmask.mask_len = sizeof(mask); + eventmask.mask = mask; + XISetMask( mask, XI_RawMotion ); + XISetMask( mask, XI_RawButtonPress ); + XISetMask( mask, XI_RawButtonRelease ); + if (pxiSelectEvents( dpy, DefaultRootWindow(dpy), &eventmask, 1 ) == BadRequest) { + return qfalse; + } + + XSelectInput( dpy, win, KeyPressMask | KeyReleaseMask ); + + return qtrue; +} + + +void rawmouse_t::Shutdown() +{ + if (libxi) { + dlclose( libxi ); + libxi = 0; + } +} + + +void rawmouse_t::Read( int* mx, int* my ) +{ + *mx = x; + *my = y; + x = y = 0; +} + + +qbool rawmouse_t::ProcessEvent( XEvent& event ) +{ + if ((event.xcookie.type != GenericEvent) || (event.xcookie.extension != xiOPCode)) + return qfalse; + + if (!XGetEventData(dpy, &event.xcookie)) + return qfalse; + + const XIRawEvent* raw = (const XIRawEvent*)event.xcookie.data; + switch (event.xcookie.evtype) { + case XI_RawMotion: { + int mx = 0, my = 0; + const double* val = raw->raw_values; + for (int i = 0; i < raw->valuators.mask_len * 8; ++i) { + if (XIMaskIsSet(raw->valuators.mask, i)) { + //Com_Printf( "RawMotion on axis %d: %lf \n", i, *val ); + if (i == 0) + mx += *val; + else if (i == 1) + my += *val; + ++val; + } + } + if (mode == XIModeRelative) { + x += mx; + y += my; + } + // workaround for virtualbox bugs + if (mode == XIModeAbsolute) { + //Com_Printf( "mx %d my %d dx %d dy %d \n", mx, my, (mx - prev_x), (my - prev_y) ); + if (mx) { x += (mx - prev_x) * 1920 / 0x8000; prev_x = mx; } + if (my) { y += (my - prev_y) * 1200 / 0x8000; prev_y = my; } + } + } + break; + + case XI_RawButtonPress: + case XI_RawButtonRelease: + //Com_Printf( "XI_RawButton %u \n", raw->detail ); + if ((raw->detail > 0) && (raw->detail < (sizeof(buttons) / sizeof(buttons[0])))) { + int button = buttons[raw->detail]; + if (button) { + Sys_QueEvent( 0, SE_KEY, button, (event.xcookie.evtype == XI_RawButtonPress), 0, NULL ); + } + } + break; + } + + XFreeEventData(dpy, &event.xcookie); + return qtrue; +} + + +/////////////////////////////////////////////////////////////// + + +#if 0 + +static KeySym keys[255]; + + +static void IN_InitKeyboard() +{ + int min, max; + XDisplayKeycodes( dpy, &min, &max ); + +/* + for (int i = min; i < max; ++i) { + KeySym keysym = XkbKeycodeToKeysym( dpy, i, 0, 0 ); + if ((keysym > 32) && (keysym < 127)) { + Com_Printf( "key[%d] : sym %04X %c \n", i, keysym, keysym ); + } else { + Com_Printf( "key[%d] : sym %04X %s \n", i, keysym, XKeysymToString(keysym) ); + } + } +*/ + + for (int i = min; i < max; ++i) { + KeySym keysym = XkbKeycodeToKeysym( dpy, i, 0, 0 ); + keys[i] = keysym; + } +} + + +static int TranslateKey( XKeyEvent* ev ) +{ + static char buf[64]; + KeySym keysym; + int XLookupRet = XLookupString( ev, buf, sizeof buf, &keysym, 0 ); + + switch (ev->keycode) { + //case XK_grave: return '~'; + case XK_Tab: return K_TAB; + case XK_Return: return K_ENTER; + + case XK_F1: return K_F1; + case XK_F2: return K_F2; + case XK_F3: return K_F3; + case XK_F4: return K_F4; + case XK_F5: return K_F5; + case XK_F6: return K_F6; + case XK_F7: return K_F7; + case XK_F8: return K_F8; + case XK_F9: return K_F9; + case XK_F10: return K_F10; + case XK_F11: return K_F11; + case XK_F12: return K_F12; + } + + if (ev->keycode > 255) { + Com_Printf( "key %ul out of range \n", ev->keycode ); + return 0; // whatever it is, it's a Magic Key that's not in our list + } + + // the keyCODE is just the key number, so map to ascii first + int key = (int)keys[ev->keycode]; + + if ((key >= 'A') && (key <= 'Z')) + return (key - 'A' + 'a'); + + return key; +} + +#endif + + +/////////////////////////////////////////////////////////////// + + +/* ** 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 const char* TranslateKey( XKeyEvent* ev, int* key ) { - static char buf[64]; - static char bufnomod[2]; - KeySym keysym; - int XLookupRet; + static char raw[2], translated[2]; + KeySym keysym; + int len; - *key = 0; + *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 + // get the normal interpretation of the key without messing with shifts, for SE_CHAR + XLookupString( ev, translated, sizeof(translated), &keysym, 0 ); + + // then get the keysym that we actually want with no shifts at all, for SE_KEY + ev->state = 0; + len = XLookupString( ev, raw, sizeof(raw), &keysym, 0 ); - if (!in_shiftedKeys->integer) { - // also get a buffer without modifiers held - ev->state = 0; - XLookupRet = XLookupString(ev, bufnomod, sizeof bufnomod, &keysym, 0); -#ifdef KBD_DBG - ri.Printf(PRINT_ALL, "XLookupString (minus modifiers) ret: %d buf: %s keysym: %x\n", XLookupRet, buf, keysym); -#endif - } switch (keysym) { @@ -274,33 +532,20 @@ static char *XLateKey(XKeyEvent *ev, int *key) case XK_Tab: *key = K_TAB; break; - case XK_F1: *key = K_F1; 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; - 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_BackSpace: *key = K_BACKSPACE; break; case XK_KP_Delete: case XK_KP_Decimal: *key = K_KP_DEL; break; @@ -320,24 +565,17 @@ static char *XLateKey(XKeyEvent *ev, int *key) case XK_Alt_R: case XK_Meta_R: *key = K_ALT; break; - // drakkar - case XK_Super_L: - case XK_Super_R: *key = K_WIN; break; - - case XK_Menu: *key = K_MENU; break; - // !drakkar - 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; - + case XK_KP_Add: *key = K_KP_PLUS; break; + case XK_KP_Divide: *key = K_KP_SLASH; break; + case XK_KP_Multiply: *key = K_KP_STAR; break; + case XK_KP_Subtract: *key = K_KP_MINUS; break; +/* // bk001130 - from cvs1.17 (mkv) case XK_exclam: *key = '1'; break; case XK_at: *key = '2'; break; @@ -349,7 +587,7 @@ static char *XLateKey(XKeyEvent *ev, int *key) 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 @@ -360,158 +598,88 @@ static char *XLateKey(XKeyEvent *ev, int *key) 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 - if (in_shiftedKeys->integer) { - *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; - } else { - *key = bufnomod[0]; - } - } - break; - } + default: + if (len) { + *key = raw[0]; + } else { + ri.Printf( PRINT_DEVELOPER, "XLookupString failed on KeySym %d\n", keysym ); + return NULL; + } + break; + } - return buf; + return translated; } -// ======================================================================== -// makes a null cursor -// ======================================================================== -static Cursor CreateNullCursor(Display *display, Window root) +/////////////////////////////////////////////////////////////// + + +static qboolean mouse_avail; +static qboolean mouse_active = qfalse; + +static int mouse_accel_numerator; +static int mouse_accel_denominator; +static int mouse_threshold; + +#define KEY_MASK (KeyPressMask | KeyReleaseMask) +#define MOUSE_MASK (ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ButtonMotionMask ) +#define X_MASK (KEY_MASK | MOUSE_MASK | VisibilityChangeMask | StructureNotifyMask ) +cvar_t* in_nograb; + + +static Cursor CreateNullCursor() { - Pixmap cursormask; - XGCValues xgc; - GC gc; - XColor dummycolour; - Cursor cursor; + Pixmap cursormask = XCreatePixmap( dpy, win, 1, 1, 1 ); - 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; + XGCValues xgc; + xgc.function = GXclear; + GC gc = XCreateGC( dpy, cursormask, GCFunction, &xgc ); + XFillRectangle( dpy, cursormask, gc, 0, 0, 1, 1 ); + + XColor colour; + colour.pixel = 0; + colour.red = 0; + colour.flags = DoRed; + + Cursor cursor = XCreatePixmapCursor( dpy, cursormask, cursormask, &colour, &colour, 0, 0 ); + + XFreePixmap( dpy, cursormask ); + XFreeGC( dpy, gc ); + return cursor; } -static void install_grabs(void) + +static void install_grabs() { - // inviso cursor - XWarpPointer(dpy, None, win, - 0, 0, 0, 0, - glConfig.vidWidth / 2, glConfig.vidHeight / 2); - XSync(dpy, False); + XSync( dpy, False ); - XDefineCursor(dpy, win, CreateNullCursor(dpy, win)); + XDefineCursor( dpy, win, CreateNullCursor() ); - XGrabPointer(dpy, win, // bk010108 - do this earlier? - False, - MOUSE_MASK, - GrabModeAsync, GrabModeAsync, - win, - None, - CurrentTime); + XGrabPointer( dpy, win, False, MOUSE_MASK, GrabModeAsync, GrabModeAsync, win, None, CurrentTime ); - XGetPointerControl(dpy, &mouse_accel_numerator, &mouse_accel_denominator, - &mouse_threshold); + XGetPointerControl( dpy, &mouse_accel_numerator, &mouse_accel_denominator, &mouse_threshold ); + XChangePointerControl( dpy, True, True, 1, 1, 0 ); - XChangePointerControl(dpy, True, True, 1, 1, 0); + XWarpPointer( dpy, None, win, 0, 0, 0, 0, glConfig.vidWidth / 2, glConfig.vidHeight / 2 ); - XSync(dpy, False); + XGrabKeyboard( dpy, win, False, GrabModeAsync, GrabModeAsync, CurrentTime ); - mouseResetTime = Sys_Milliseconds (); - -#ifdef HAVE_XF86DGA - 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 -#endif /* HAVE_XF86DGA */ - { - mwx = glConfig.vidWidth / 2; - mwy = glConfig.vidHeight / 2; - mx = my = 0; - } - - XGrabKeyboard(dpy, win, - False, - GrabModeAsync, GrabModeAsync, - CurrentTime); - - XSync(dpy, False); + XSync( dpy, False ); } -static void uninstall_grabs(void) + +static void uninstall_grabs() { -#ifdef HAVE_XF86DGA - if (in_dgamouse->value) - { - if (com_developer->value) - ri.Printf( PRINT_ALL, "DGA Mouse - Disabling DGA DirectVideo\n" ); - XF86DGADirectVideo(dpy, DefaultScreen(dpy), 0); - } -#endif /* HAVE_XF86DGA */ + XChangePointerControl( dpy, True, True, mouse_accel_numerator, mouse_accel_denominator, mouse_threshold ); - XChangePointerControl(dpy, qtrue, qtrue, mouse_accel_numerator, - mouse_accel_denominator, mouse_threshold); + XUngrabPointer( dpy, CurrentTime ); + XUngrabKeyboard( dpy, CurrentTime ); - 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); + XUndefineCursor( dpy, win ); } -// drakkar -// Sys_MilliSeconds returns CPU usage time, -// this fuction returns real time milliseconds -unsigned long int realMilliSeconds() -{ - struct timeval tv; - gettimeofday( &tv, NULL ); - return (unsigned long int)tv.tv_sec*1000 + (unsigned long int)tv.tv_usec/1000; -} -// !drakkar - - // bk001206 - from Ryan's Fakk2 /** * XPending() actually performs a blocking read @@ -556,265 +724,159 @@ static qboolean X11_PendingInput(void) { return qfalse; } -// bk001206 - from Ryan's Fakk2. See above. -static qboolean repeated_press(XEvent *event) + +// X sends Release/Press pairs for auto-repeat even though the key hasn't actually been released + +static qboolean KeyRepeat( const XEvent* event ) { - XEvent peekevent; - qboolean repeated = qfalse; + if (!X11_PendingInput()) + return qfalse; - assert(dpy != NULL); + XEvent peek; + XPeekEvent( dpy, &peek ); - if (X11_PendingInput()) - { - XPeekEvent(dpy, &peekevent); + if ( (peek.type == KeyPress) && (peek.xkey.keycode == event->xkey.keycode) && (peek.xkey.time == event->xkey.time) ) { + XNextEvent( dpy, &peek ); // discard the CURRENT event, which is the RELEASE + return qtrue; + } - 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 qfalse; +} - return(repeated); -} // repeated_press -int Sys_XTimeToSysTime (Time xtime); -static void HandleEvents(void) +static void HandleEvents() { - 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 + XEvent event; + int key; + const char* p; - if (!dpy) - return; + 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; + while (XPending(dpy)) + { + XNextEvent(dpy, &event); - 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); + if (mouse && mouse->ProcessEvent(event)) + continue; - Sys_QueEvent( t, SE_KEY, key, qfalse, 0, NULL ); - break; + switch (event.type) + { + case KeyPress: + p = TranslateKey( &event.xkey, &key ); + if (key) { + Sys_QueEvent( 0, SE_KEY, key, qtrue, 0, NULL ); + } + if (p) { + while (*p) { + Sys_QueEvent( 0, SE_CHAR, *p++, 0, 0, NULL ); + } + } + break; - case MotionNotify: - t = Sys_XTimeToSysTime(event.xkey.time); - if (mouse_active) - { + case KeyRelease: + if (!cls.keyCatchers && KeyRepeat(&event)) { + continue; + } + TranslateKey( &event.xkey, &key ); + if (key) { + Sys_QueEvent( 0, SE_KEY, key, qfalse, 0, NULL ); + } + break; + } + } + +} + + + +static void IN_ActivateMouse() +{ + if (!mouse_avail || !dpy || !win) + return; + + if (!mouse_active) { + if (!in_nograb->value) + install_grabs(); + mouse_active = qtrue; + } + + mouse->Activate( qtrue ); +} + + +static void IN_DeactivateMouse() +{ + if (!mouse_avail || !dpy || !win) + return; + + if (mouse_active) { + if (!in_nograb->value) + uninstall_grabs(); + mouse_active = qfalse; + } +} + + +/////////////////////////////////////////////////////////////// + + + + + + + + +#ifdef _XF86DGA_H_ +#define HAVE_XF86DGA +#endif + +#define WINDOW_CLASS_NAME "CNQ3" + +// OpenGL driver +#define OPENGL_DRIVER_NAME "libGL.so.1" + +typedef enum +{ + RSERR_OK, + + RSERR_INVALID_FULLSCREEN, + RSERR_INVALID_MODE, + + RSERR_UNKNOWN +} rserr_t; + +glwstate_t glw_state; + +static GLXContext ctx = NULL; + + + +static cvar_t *in_mouse; + +// 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_fullscreen; + +qboolean vidmode_ext = qfalse; #ifdef HAVE_XF86DGA - 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 +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 = { -1,-1,-1 }; // drakkar - initialized to nonvalid values #endif /* HAVE_XF86DGA */ - { - // 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; +#ifdef HAVE_XF86DGA +static XF86VidModeModeInfo **vidmodes = NULL; // drakkar - initialized to NULL +#endif /* HAVE_XF86DGA */ +static int num_vidmodes; +static qboolean vidmode_active = qfalse; - mwx = event.xmotion.x; - mwy = event.xmotion.y; - dowarp = qtrue; - } - } - break; +static int scrnum; - 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 : - if( glInfo.isFullscreen ) break; - if( win_active ) break; - if( event.xcreatewindow.x < 1 ) break; - if( event.xcreatewindow.y < 1 ) break; - - 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 @@ -853,20 +915,21 @@ void GLimp_SetGamma( unsigned char red[256], unsigned char green[256], unsigned */ 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( visinfo ) // drakkar - XFree( visinfo ); - if (ctx) - qglXDestroyContext(dpy, ctx); - if (win) - XDestroyWindow(dpy, win); + if (!ctx || !dpy) + return; + + ri.Printf( PRINT_DEVELOPER, "Shutting down OpenGL subsystem\n" ); + +// IN_DeactivateMouse(); + + qglXDestroyContext(dpy, ctx); + ctx = NULL; + + if (win) { + XDestroyWindow(dpy, win); + win = 0; + } + #ifdef HAVE_XF86DGA if (vidmode_active) XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[0]); @@ -875,78 +938,30 @@ void GLimp_Shutdown( void ) XF86VidModeSetGamma(dpy, scrnum, &vidmode_InitialGamma); } #endif /* HAVE_XF86DGA */ - // 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); - } + + // 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); + dpy = NULL; + vidmode_active = qfalse; - dpy = NULL; - win = 0; - ctx = NULL; // drakkar - visinfo = NULL; #ifdef HAVE_XF86DGA if( vidmodes ) XFree( vidmodes ); vidmodes = NULL; #endif // !drakkar - memset( &glConfig, 0, sizeof( glConfig ) ); - memset( &glState, 0, sizeof( glState ) ); + QGL_Shutdown(); - QGL_Shutdown(); + memset( &glConfig, 0, sizeof( glConfig ) ); + memset( &glState, 0, sizeof( glState ) ); } -/* -** GLimp_LogComment -*/ -void GLimp_LogComment( const char* comment ) -{ - if ( glw_state.log_fp ) - { - fprintf( glw_state.log_fp, "%s", comment ); - } -} -/* -** GLW_StartDriverAndSetMode -*/ -// bk001204 - prototype needed -rserr_t GLW_SetMode( qboolean fullscreen ); -static qboolean GLW_StartDriverAndSetMode( qboolean fullscreen ) -{ - rserr_t err; - - 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( 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\n" ); - return qfalse; - default: - break; - } - return qtrue; -} - -/* -** GLW_SetMode -*/ -rserr_t GLW_SetMode( qboolean fullscreen ) +static rserr_t GLW_SetMode( qboolean fullscreen ) { int attrib[] = { GLX_RGBA, // 0 @@ -964,37 +979,35 @@ rserr_t GLW_SetMode( qboolean fullscreen ) #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, "Initializing OpenGL\n" ); - ri.Printf (PRINT_ALL, "...setting mode...\n" ); + if (!(dpy = XOpenDisplay(NULL))) { + ri.Error( ERR_FATAL, "GLW_SetMode - Couldn't open the X display" ); + return RSERR_INVALID_MODE; + } - if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect ) ) - { - ri.Printf( PRINT_ALL, " invalid mode\n" ); - return RSERR_INVALID_MODE; - } - ri.Printf( PRINT_ALL, " %d %d\n", glConfig.vidWidth, glConfig.vidHeight); + scrnum = DefaultScreen(dpy); + Window root = RootWindow(dpy, scrnum); + + if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect ) ) { + glConfig.vidWidth = XDisplayWidth(dpy, scrnum); + glConfig.vidHeight = XDisplayHeight(dpy, scrnum); + glConfig.windowAspect = (float)glConfig.vidWidth / glConfig.vidHeight; + vidmode_active = qtrue; + fullscreen = qfalse; + } + ri.Printf( PRINT_DEVELOPER, "...setting mode %dx%d %s\n", glConfig.vidWidth, glConfig.vidHeight, fullscreen ? "FS" : "W" ); - 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; @@ -1014,23 +1027,6 @@ rserr_t GLW_SetMode( qboolean fullscreen ) } #endif /* HAVE_XF86DGA */ - // Check for DGA - dga_MajorVersion = 0, dga_MinorVersion = 0; -#ifdef HAVE_XF86DGA - if (in_dgamouse->value) - { - if (!XF86DGAQueryVersion(dpy, &dga_MajorVersion, &dga_MinorVersion)) - { - // unable to query, probably 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); - } - } -#endif /* HAVE_XF86DGA */ #ifdef HAVE_XF86DGA if (vidmode_ext) @@ -1198,8 +1194,6 @@ rserr_t GLW_SetMode( qboolean fullscreen ) attr.background_pixel = BlackPixel(dpy, scrnum); attr.border_pixel = 0; attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone); - attr.backing_store = NotUseful; - attr.save_under = False; attr.event_mask = X_MASK; if (vidmode_active) { @@ -1215,12 +1209,8 @@ rserr_t GLW_SetMode( qboolean fullscreen ) actualWidth, actualHeight, 0, visinfo->depth, InputOutput, visinfo->visual, mask, &attr); - if( !win ) return RSERR_OK; - XStoreName( dpy, win, WINDOW_CLASS_NAME ); - XMapWindow( dpy, win ); - XFlush(dpy); + XStoreName( dpy, win, WINDOW_CLASS_NAME ); - XMoveWindow( dpy, win, 0, 0 ); /* GH: Don't let the window be resized */ sizehints.flags = PMinSize | PMaxSize; sizehints.min_width = sizehints.max_width = actualWidth; @@ -1228,7 +1218,7 @@ rserr_t GLW_SetMode( qboolean fullscreen ) XSetWMNormalHints( dpy, win, &sizehints ); - XFlush(dpy); + XMapWindow( dpy, win ); if (vidmode_active) XMoveWindow(dpy, win, 0, 0); @@ -1238,74 +1228,79 @@ rserr_t GLW_SetMode( qboolean fullscreen ) 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 = (char *)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", OPENGL_DRIVER_NAME ); - 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" ); - } - } + ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", (const char*)qglGetString( GL_RENDERER ) ); return RSERR_OK; } -/* -** GLW_InitExtensions -*/ -static void GLW_InitExtensions( void ) -{ - ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" ); - int maxAnisotropy = 0; - if ( strstr( glConfig.extensions_string, "GL_EXT_texture_filter_anisotropic" ) ) - { - if ( r_ext_max_anisotropy->integer > 1 ) - { - qglGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy ); - if ( maxAnisotropy <= 0 ) { - ri.Printf( PRINT_DEVELOPER, "...GL_EXT_texture_filter_anisotropic not properly supported!\n" ); - maxAnisotropy = 0; - } - else - { - ri.Printf( PRINT_DEVELOPER, "...using GL_EXT_texture_filter_anisotropic (max: %i)\n", maxAnisotropy ); - } - } - else - { - ri.Printf( PRINT_DEVELOPER, "...ignoring GL_EXT_texture_filter_anisotropic\n" ); - } - } - else - { - ri.Printf( PRINT_DEVELOPER, "...GL_EXT_texture_filter_anisotropic not found\n" ); - } - Cvar_Set( "r_ext_max_anisotropy", va("%i", maxAnisotropy) ); + +static qboolean GLW_StartDriverAndSetMode( qboolean fullscreen ) +{ + rserr_t err; + + 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( 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\n" ); + return qfalse; + default: + break; + } + return qtrue; } -static void GLW_InitGamma(void) + +static void GLW_InitExtensions() +{ + ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" ); + + int maxAnisotropy = 0; + if ( strstr( glConfig.extensions_string, "GL_EXT_texture_filter_anisotropic" ) ) + { + if ( r_ext_max_anisotropy->integer > 1 ) + { + qglGetIntegerv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy ); + if ( maxAnisotropy <= 0 ) { + ri.Printf( PRINT_DEVELOPER, "...GL_EXT_texture_filter_anisotropic not properly supported!\n" ); + maxAnisotropy = 0; + } + else + { + ri.Printf( PRINT_DEVELOPER, "...using GL_EXT_texture_filter_anisotropic (max: %i)\n", maxAnisotropy ); + } + } + else + { + ri.Printf( PRINT_DEVELOPER, "...ignoring GL_EXT_texture_filter_anisotropic\n" ); + } + } + else + { + ri.Printf( PRINT_DEVELOPER, "...GL_EXT_texture_filter_anisotropic not found\n" ); + } + Cvar_Set( "r_ext_max_anisotropy", va("%i", maxAnisotropy) ); +} + + +static void GLW_InitGamma() { /* Minimum extension version required */ #define GAMMA_MINMAJOR 2 @@ -1406,15 +1401,13 @@ void GLimp_Init( void ) } #endif - r_allowSoftwareGL = ri.Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH ); - InitSig(); - IN_Init(); // rcg08312005 moved into glimp. - // set up our custom error handler for X failures XSetErrorHandler(&qXErrorHandler); + in_nograb = Cvar_Get( "in_nograb", "0", 0 ); + // load appropriate DLL and initialize subsystem if (!GLW_LoadOpenGL() ) ri.Error( ERR_FATAL, "GLimp_Init()->GLW_LoadOpenGL() - could not load OpenGL subsystem (using '%s')\n", OPENGL_DRIVER_NAME ); @@ -1438,7 +1431,7 @@ void GLimp_Init( void ) InitSig(); // not clear why this is at begin & end of function - return; + IN_Init(); } @@ -1465,6 +1458,7 @@ void GLimp_EndFrame (void) } } + #ifdef SMP /* =========================================================== @@ -1597,22 +1591,47 @@ 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); - in_shiftedKeys = Cvar_Get ("in_shiftedKeys", "0", CVAR_ARCHIVE); +static void IN_StartupMouse() +{ + assert( !mouse ); + mouse = 0; - // turn on-off sub-frame timing of X events - in_subframe = Cvar_Get ("in_subframe", "1", CVAR_ARCHIVE); + cvar_t* in_mouse = Cvar_Get( "in_mouse", "1", CVAR_ARCHIVE|CVAR_LATCH ); + in_mouse->modified = qfalse; - // developer feature, allows to break without loosing mouse pointer - in_nograb = Cvar_Get ("in_nograb", "0", 0); + if (!in_mouse->integer) { + Com_Printf( "Mouse not active.\n" ); + return; + } + + if (in_mouse->integer == 1) { + if (rawmouse.Init()) { + mouse = &rawmouse; + Com_Printf( "Using XInput2\n" ); + return; + } + Com_Printf( "XInput2 mouse initialization failed\n" ); + } + + mouse = &xmouse; + mouse->Init(); + Com_Printf( "Using XWindows mouse input\n" ); +} + + + + + + +void IN_Init() +{ + QSUBSYSTEM_INIT_START( "Input" ); + //IN_InitKeyboard(); + IN_StartupMouse(); + + + in_mouse = Cvar_Get( "in_mouse", "1", CVAR_ARCHIVE|CVAR_LATCH ); // bk001130 - from cvs.17 (mkv), joystick variables in_joystick = Cvar_Get ("in_joystick", "0", CVAR_ARCHIVE|CVAR_LATCH); @@ -1620,22 +1639,31 @@ void IN_Init(void) { in_joystickDebug = Cvar_Get ("in_debugjoystick", "0", CVAR_TEMP); joy_threshold = Cvar_Get ("joy_threshold", "0.15", CVAR_ARCHIVE); // FIXME: in_joythreshold - Cvar_Set( "cl_platformSensitivity", "2.0" ); - +// fix this: it's crap AND wrong: the mouse is what decides if the mouse is available or not if (in_mouse->value) mouse_avail = qtrue; else mouse_avail = qfalse; - IN_StartupJoystick( ); // bk001130 - from cvs1.17 (mkv) - Com_Printf ("------------------------------------\n"); + IN_StartupJoystick(); + + QSUBSYSTEM_INIT_DONE( "Input" ); } + void IN_Shutdown(void) { mouse_avail = qfalse; + + //IN_Activate( qfalse ); + + if (mouse) { + mouse->Shutdown(); + mouse = 0; + } } + void IN_Frame (void) { // bk001130 - from cvs 1.17 (mkv) @@ -1652,15 +1680,21 @@ void IN_Frame (void) { } } - // drakkar -//- IN_ActivateMouse(); - if( win_active ) - IN_ActivateMouse(); - else - IN_DeactivateMouse(); - // !drakkar + IN_ActivateMouse(); + + if (!mouse) + return; + + int mx, my; + mouse->Read( &mx, &my ); + + if ( !mx && !my ) + return; + + Sys_QueEvent( 0, SE_MOUSE, mx, my, 0, NULL ); } + void IN_Activate(void) { } diff --git a/code/unix/linux_qgl.c b/code/unix/linux_qgl.c index 98ebc1f..91a310a 100644 --- a/code/unix/linux_qgl.c +++ b/code/unix/linux_qgl.c @@ -37,11 +37,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include "unix_glw.h" -// bk001129 - from cvs1.17 (mkv) -#if defined(__FX__) -#include -#endif - #if defined(USE_SDL_VIDEO) #include #include @@ -51,16 +46,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "../renderer/tr_local.h" -// bk001129 - from cvs1.17 (mkv) -#if defined(__FX__) -//FX Mesa Functions -fxMesaContext (*qfxMesaCreateContext)(GLuint win, GrScreenResolution_t, GrScreenRefresh_t, const GLint attribList[]); -fxMesaContext (*qfxMesaCreateBestContext)(GLuint win, GLint width, GLint height, const GLint attribList[]); -void (*qfxMesaDestroyContext)(fxMesaContext ctx); -void (*qfxMesaMakeCurrent)(fxMesaContext ctx); -fxMesaContext (*qfxMesaGetCurrentContext)(void); -void (*qfxMesaSwapBuffers)(void); -#endif //GLX Functions #if !defined(USE_SDL_VIDEO) @@ -1138,16 +1123,6 @@ void QGL_Shutdown( void ) qglVertexPointer = NULL; qglViewport = NULL; -// bk001129 - from cvs1.17 (mkv) -#if defined(__FX__) - qfxMesaCreateContext = NULL; - qfxMesaCreateBestContext = NULL; - qfxMesaDestroyContext = NULL; - qfxMesaMakeCurrent = NULL; - qfxMesaGetCurrentContext = NULL; - qfxMesaSwapBuffers = NULL; -#endif - #if !defined(USE_SDL_VIDEO) qglXGetProcAddress = NULL; qglXChooseVisual = NULL; @@ -1564,16 +1539,6 @@ qboolean QGL_Init( const char *dllname ) qglVertexPointer = dllVertexPointer =(void (*)(GLint, GLenum, GLsizei, const GLvoid*))GPA( "glVertexPointer" ); qglViewport = dllViewport =(void (*)(GLint, GLint, GLsizei, GLsizei))GPA( "glViewport" ); -// bk001129 - from cvs1.17 (mkv) -#if defined(__FX__) - qfxMesaCreateContext = GPA("fxMesaCreateContext"); - qfxMesaCreateBestContext = GPA("fxMesaCreateBestContext"); - qfxMesaDestroyContext = GPA("fxMesaDestroyContext"); - qfxMesaMakeCurrent = GPA("fxMesaMakeCurrent"); - qfxMesaGetCurrentContext = GPA("fxMesaGetCurrentContext"); - qfxMesaSwapBuffers = GPA("fxMesaSwapBuffers"); -#endif - #if !defined(USE_SDL_VIDEO) qglXChooseVisual = (XVisualInfo * (*)( Display *dpy, int screen, int *attribList ))GPA("glXChooseVisual"); qglXCreateContext = (GLXContext (*)( Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct ))GPA("glXCreateContext"); diff --git a/code/unix/unix_glw.h b/code/unix/unix_glw.h index 79c21d9..dc9b272 100644 --- a/code/unix/unix_glw.h +++ b/code/unix/unix_glw.h @@ -30,9 +30,7 @@ typedef struct { void *OpenGLLib; // instance of OpenGL library - int desktopWidth, desktopHeight, desktopBPP; - - FILE *log_fp; + int desktopWidth, desktopHeight, desktopBPP; } glwstate_t; extern glwstate_t glw_state; diff --git a/code/unix/unix_shared.cpp b/code/unix/unix_shared.cpp index a26c7ae..487a910 100644 --- a/code/unix/unix_shared.cpp +++ b/code/unix/unix_shared.cpp @@ -32,116 +32,33 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "../qcommon/q_shared.h" #include "../qcommon/qcommon.h" -//============================================================================= // Used to determine where to store user-specific files static char homePath[MAX_OSPATH]; -/* -================ -Sys_Milliseconds -================ -*/ -/* base time in seconds, that's our origin - timeval:tv_sec is an int: - assuming this wraps every 0x7fffffff - ~68 years since the Epoch (1970) - we're safe till 2038 - using unsigned long data type to work right with Sys_XTimeToSysTime */ -unsigned long sys_timeBase = 0; -/* current time in ms, using sys_timeBase as origin - NOTE: sys_timeBase*1000 + curtime -> ms since the Epoch - 0x7fffffff ms - ~24 days - although timeval:tv_usec is an int, I'm not sure wether it is actually used as an unsigned int - (which would affect the wrap period) -*/ + int Sys_Milliseconds() { - static int curtime; + static int sys_timeBase = 0; - struct timeval tp; - gettimeofday(&tp, NULL); + struct timeval tv; + gettimeofday( &tv, NULL ); - if (!sys_timeBase) - { - sys_timeBase = tp.tv_sec; - return tp.tv_usec/1000; + if (!sys_timeBase) { + sys_timeBase = tv.tv_sec; + return tv.tv_usec/1000; } - curtime = (tp.tv_sec - sys_timeBase)*1000 + tp.tv_usec/1000; - - return curtime; + return ((tv.tv_sec - sys_timeBase)*1000 + tv.tv_usec/1000); } -#if (defined(__linux__) || defined(__FreeBSD__) || defined(__sun)) && !defined(DEDICATED) -/* -================ -Sys_XTimeToSysTime -sub-frame timing of events returned by X -X uses the Time typedef - unsigned long -disable with in_subframe 0 - sys_timeBase*1000 is the number of ms since the Epoch of our origin - xtime is in ms and uses the Epoch as origin - Time data type is an unsigned long: 0xffffffff ms - ~49 days period - I didn't find much info in the XWindow documentation about the wrapping - we clamp sys_timeBase*1000 to unsigned long, that gives us the current origin for xtime - the computation will still work if xtime wraps (at ~49 days period since the Epoch) after we set sys_timeBase - -================ -*/ -extern cvar_t *in_subframe; -int Sys_XTimeToSysTime (unsigned long xtime) +void Sys_Mkdir( const char* path ) { - int ret, time, test; - - if (!in_subframe->value) - { - // if you don't want to do any event times corrections - return Sys_Milliseconds(); - } - - // test the wrap issue -#if 0 - // reference values for test: sys_timeBase 0x3dc7b5e9 xtime 0x541ea451 (read these from a test run) - // xtime will wrap in 0xabe15bae ms >~ 0x2c0056 s (33 days from Nov 5 2002 -> 8 Dec) - // NOTE: date -d '1970-01-01 UTC 1039384002 seconds' +%c - // use sys_timeBase 0x3dc7b5e9+0x2c0056 = 0x3df3b63f - // after around 5s, xtime would have wrapped around - // we get 7132, the formula handles the wrap safely - unsigned long xtime_aux,base_aux; - int test; -// Com_Printf("sys_timeBase: %p\n", sys_timeBase); -// Com_Printf("xtime: %p\n", xtime); - xtime_aux = 500; // 500 ms after wrap - base_aux = 0x3df3b63f; // the base a few seconds before wrap - test = xtime_aux - (unsigned long)(base_aux*1000); - Com_Printf("xtime wrap test: %d\n", test); -#endif - - // some X servers (like suse 8.1's) report weird event times - // if the game is loading, resolving DNS, etc. we are also getting old events - // so we only deal with subframe corrections that look 'normal' - ret = xtime - (unsigned long)(sys_timeBase*1000); - time = Sys_Milliseconds(); - test = time - ret; - //printf("delta: %d\n", test); - if (test < 0 || test > 30) // in normal conditions I've never seen this go above - { - return time; - } - - return ret; -} -#endif - - -void Sys_Mkdir( const char *path ) -{ - mkdir (path, 0777); + mkdir( path, 0777 ); } -//============================================ - #define MAX_FOUND_FILES 0x1000 // bk001129 - new in 1.26 @@ -239,7 +156,7 @@ char **Sys_ListFiles( const char *directory, const char *extension, const char * } extLen = strlen( extension ); - + // search nfiles = 0; @@ -258,7 +175,7 @@ char **Sys_ListFiles( const char *directory, const char *extension, const char * if (*extension) { if ( strlen( d->d_name ) < strlen( extension ) || - Q_stricmp( + Q_stricmp( d->d_name + strlen( d->d_name ) - strlen( extension ), extension ) ) { continue; // didn't match @@ -305,6 +222,7 @@ void Sys_FreeFileList( char **list ) { Z_Free( list ); } + const char* Sys_Cwd() { static char cwd[MAX_OSPATH]; @@ -315,10 +233,6 @@ const char* Sys_Cwd() return cwd; } -void Sys_SetDefaultHomePath(const char *path) -{ - Q_strncpyz(homePath, path, sizeof(homePath)); -} const char* Sys_DefaultHomePath() { @@ -334,7 +248,7 @@ const char* Sys_DefaultHomePath() Q_strcat(homePath, sizeof(homePath), "/.q3a"); #endif if (mkdir(homePath, 0777)) { - if (errno != EEXIST) + if (errno != EEXIST) Sys_Error("Unable to create directory \"%s\", error is %s(%d)\n", homePath, strerror(errno), errno); } return homePath; @@ -343,12 +257,12 @@ const char* Sys_DefaultHomePath() return ""; // assume current dir } -//============================================ void Sys_ShowConsole( int visLevel, qboolean quitOnClose ) { } + const char* Sys_GetCurrentUser() { const struct passwd* p;