mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-23 04:51:56 +00:00
Add absolute mouse mode and refactor mouse grabbing code
There were lots of places in the code that called Sys_GrabInput(), some of them each frame. Most of this is unified in events.cpp now, in handleMouseGrab() which is called once per frame by Sys_GenerateEvents() - this makes reasoning about when the mouse is grabbed and when not a lot easier. Sys_GrabInput(false) still is called in a few places, before operations that tend to take long (like loading a map or vid_restart), but (hopefully) not regularly anymore. The other big change is that the game now uses SDLs absolute mouse mode for fullscreen menus (except the PDA which is an ugly hack), so the ingame cursor is at the same position as the system cursor, which especially helps when debugging with `in_nograb 1` and should also help if someone wants to integrate an additional GUI toolkit like Dear ImGui.
This commit is contained in:
parent
00b58d1f0f
commit
ae63021d00
11 changed files with 114 additions and 54 deletions
|
@ -996,7 +996,6 @@ Activates or Deactivates a tool
|
|||
*/
|
||||
void idCommonLocal::ActivateTool( bool active ) {
|
||||
com_editorActive = active;
|
||||
Sys_GrabMouseCursor( !active );
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -826,7 +826,6 @@ bool idConsoleLocal::ProcessEvent( const sysEvent_t *event, bool forceAccept ) {
|
|||
// a down event will toggle the destination lines
|
||||
if ( keyCatching ) {
|
||||
Close();
|
||||
Sys_GrabMouseCursor( true );
|
||||
cvarSystem->SetCVarBool( "ui_chat", false );
|
||||
} else {
|
||||
consoleField.Clear();
|
||||
|
|
|
@ -2644,6 +2644,7 @@ void idSessionLocal::Frame() {
|
|||
return;
|
||||
}
|
||||
|
||||
#if 0 // handled via Sys_GenerateEvents() -> handleMouseGrab()
|
||||
// if the console is down, we don't need to hold
|
||||
// the mouse cursor
|
||||
if ( console->Active() || com_editorActive ) {
|
||||
|
@ -2651,6 +2652,7 @@ void idSessionLocal::Frame() {
|
|||
} else {
|
||||
Sys_GrabMouseCursor( true );
|
||||
}
|
||||
#endif
|
||||
|
||||
// save the screenshot and audio from the last draw if needed
|
||||
if ( aviCaptureMode ) {
|
||||
|
|
|
@ -172,10 +172,8 @@ idAsyncNetwork::RunFrame
|
|||
*/
|
||||
void idAsyncNetwork::RunFrame( void ) {
|
||||
if ( console->Active() ) {
|
||||
Sys_GrabMouseCursor( false );
|
||||
usercmdGen->InhibitUsercmd( INHIBIT_ASYNC, true );
|
||||
} else {
|
||||
Sys_GrabMouseCursor( true );
|
||||
usercmdGen->InhibitUsercmd( INHIBIT_ASYNC, false );
|
||||
}
|
||||
client.RunFrame();
|
||||
|
|
|
@ -1108,10 +1108,9 @@ void GLimp_DeactivateContext( void );
|
|||
// being immediate returns, which lets us guage how much time is
|
||||
// being spent inside OpenGL.
|
||||
|
||||
const int GRAB_ENABLE = (1 << 0);
|
||||
const int GRAB_REENABLE = (1 << 1);
|
||||
const int GRAB_HIDECURSOR = (1 << 2);
|
||||
const int GRAB_SETSTATE = (1 << 3);
|
||||
const int GRAB_GRABMOUSE = (1 << 0);
|
||||
const int GRAB_HIDECURSOR = (1 << 1);
|
||||
const int GRAB_RELATIVEMOUSE = (1 << 2);
|
||||
|
||||
void GLimp_GrabInput(int flags);
|
||||
/*
|
||||
|
|
|
@ -32,8 +32,9 @@ If you have questions concerning this license or the applicable additional terms
|
|||
#include "idlib/containers/List.h"
|
||||
#include "idlib/Heap.h"
|
||||
#include "framework/Common.h"
|
||||
#include "framework/Console.h"
|
||||
#include "framework/KeyInput.h"
|
||||
#include "framework/Session.h"
|
||||
#include "framework/Session_local.h"
|
||||
#include "renderer/RenderSystem.h"
|
||||
#include "renderer/tr_local.h"
|
||||
|
||||
|
@ -72,9 +73,15 @@ static idCVar in_kbd("in_kbd", _in_kbdNames[0], CVAR_SYSTEM | CVAR_ARCHIVE | CVA
|
|||
static idCVar in_ignoreConsoleKey("in_ignoreConsoleKey", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT | CVAR_BOOL,
|
||||
"Console only opens with Shift+Esc, not ` or ^ etc");
|
||||
|
||||
static idCVar in_nograb("in_nograb", "0", CVAR_SYSTEM | CVAR_NOCHEAT, "prevents input grabbing");
|
||||
static idCVar in_grabKeyboard("in_grabKeyboard", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT | CVAR_BOOL,
|
||||
"if enabled, grabs all keyboard input if mouse is grabbed (so keyboard shortcuts from the OS like Alt-Tab or Windows Key won't work)");
|
||||
|
||||
// set in handleMouseGrab(), used in Sys_GetEvent() to decide what kind of internal mouse event to generate
|
||||
static bool in_relativeMouseMode = true;
|
||||
// set in Sys_GetEvent() on window focus gained/lost events
|
||||
static bool in_hasFocus = true;
|
||||
|
||||
struct kbd_poll_t {
|
||||
int key;
|
||||
bool state;
|
||||
|
@ -648,15 +655,13 @@ unsigned char Sys_MapCharForKey(int key) {
|
|||
/*
|
||||
===============
|
||||
Sys_GrabMouseCursor
|
||||
Note: Usually grabbing is handled in idCommonLocal::Frame() -> Sys_GenerateEvents() -> handleMouseGrab()
|
||||
This function should only be used to release the mouse before long operations where
|
||||
common->Frame() won't be called for a while
|
||||
===============
|
||||
*/
|
||||
void Sys_GrabMouseCursor(bool grabIt) {
|
||||
int flags;
|
||||
|
||||
if (grabIt)
|
||||
flags = GRAB_ENABLE | GRAB_HIDECURSOR | GRAB_SETSTATE;
|
||||
else
|
||||
flags = GRAB_SETSTATE;
|
||||
int flags = grabIt ? (GRAB_GRABMOUSE | GRAB_HIDECURSOR | GRAB_RELATIVEMOUSE) : 0;
|
||||
|
||||
GLimp_GrabInput(flags);
|
||||
}
|
||||
|
@ -722,15 +727,14 @@ sysEvent_t Sys_GetEvent() {
|
|||
} // new context because visual studio complains about newmod and currentmod not initialized because of the case SDL_WINDOWEVENT_FOCUS_LOST
|
||||
|
||||
|
||||
common->ActivateTool( false );
|
||||
GLimp_GrabInput(GRAB_ENABLE | GRAB_REENABLE | GRAB_HIDECURSOR); // FIXME: not sure this is still needed after the ActivateTool()-call
|
||||
in_hasFocus = true;
|
||||
|
||||
// start playing the game sound world again (when coming from editor)
|
||||
session->SetPlayingSoundWorld();
|
||||
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
GLimp_GrabInput(0);
|
||||
in_hasFocus = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -738,10 +742,8 @@ sysEvent_t Sys_GetEvent() {
|
|||
#else
|
||||
case SDL_ACTIVEEVENT:
|
||||
{
|
||||
int flags = 0;
|
||||
|
||||
if (ev.active.gain) {
|
||||
flags = GRAB_ENABLE | GRAB_REENABLE | GRAB_HIDECURSOR;
|
||||
in_hasFocus = true;
|
||||
|
||||
// unset modifier, in case alt-tab was used to leave window and ALT is still set
|
||||
// as that can cause fullscreen-toggling when pressing enter...
|
||||
|
@ -751,9 +753,9 @@ sysEvent_t Sys_GetEvent() {
|
|||
newmod |= KMOD_CAPS;
|
||||
|
||||
SDL_SetModState((SDLMod)newmod);
|
||||
} else {
|
||||
in_hasFocus = false;
|
||||
}
|
||||
|
||||
GLimp_GrabInput(flags);
|
||||
}
|
||||
|
||||
continue; // handle next event
|
||||
|
@ -876,12 +878,18 @@ sysEvent_t Sys_GetEvent() {
|
|||
#endif
|
||||
|
||||
case SDL_MOUSEMOTION:
|
||||
res.evType = SE_MOUSE;
|
||||
res.evValue = ev.motion.xrel;
|
||||
res.evValue2 = ev.motion.yrel;
|
||||
if ( in_relativeMouseMode ) {
|
||||
res.evType = SE_MOUSE;
|
||||
res.evValue = ev.motion.xrel;
|
||||
res.evValue2 = ev.motion.yrel;
|
||||
|
||||
mouse_polls.Append(mouse_poll_t(M_DELTAX, ev.motion.xrel));
|
||||
mouse_polls.Append(mouse_poll_t(M_DELTAY, ev.motion.yrel));
|
||||
mouse_polls.Append(mouse_poll_t(M_DELTAX, ev.motion.xrel));
|
||||
mouse_polls.Append(mouse_poll_t(M_DELTAY, ev.motion.yrel));
|
||||
} else {
|
||||
res.evType = SE_MOUSE_ABS;
|
||||
res.evValue = ev.motion.x;
|
||||
res.evValue2 = ev.motion.y;
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
|
@ -892,7 +900,7 @@ sysEvent_t Sys_GetEvent() {
|
|||
if (ev.wheel.y > 0) {
|
||||
res.evValue = K_MWHEELUP;
|
||||
mouse_polls.Append(mouse_poll_t(M_DELTAZ, 1));
|
||||
} else {
|
||||
} else if (ev.wheel.y < 0) {
|
||||
res.evValue = K_MWHEELDOWN;
|
||||
mouse_polls.Append(mouse_poll_t(M_DELTAZ, -1));
|
||||
}
|
||||
|
@ -990,12 +998,67 @@ void Sys_ClearEvents() {
|
|||
mouse_polls.SetNum(0, false);
|
||||
}
|
||||
|
||||
static void handleMouseGrab() {
|
||||
|
||||
// these are the defaults for when the window does *not* have focus
|
||||
// (don't grab in any way)
|
||||
bool showCursor = true;
|
||||
bool grabMouse = false;
|
||||
bool relativeMouse = false;
|
||||
|
||||
// if com_editorActive, release everything, just like when we have no focus
|
||||
if ( in_hasFocus && !com_editorActive ) {
|
||||
// Note: this generally handles fullscreen menus, but not the PDA, because the PDA
|
||||
// is an ugly hack in gamecode that doesn't go through sessLocal.guiActive.
|
||||
// It goes through weapon input code or sth? That's also the reason only
|
||||
// leftclick (fire) works there (no mousewheel..)
|
||||
// So the PDA will continue to use relative mouse events to set its cursor position.
|
||||
const bool menuActive = ( sessLocal.GetActiveMenu() != NULL );
|
||||
|
||||
if ( menuActive ) {
|
||||
showCursor = false;
|
||||
relativeMouse = false;
|
||||
grabMouse = false; // TODO: or still grab to window? (maybe only if in exclusive fullscreen mode?)
|
||||
} else if ( console->Active() ) {
|
||||
showCursor = true;
|
||||
relativeMouse = grabMouse = false;
|
||||
} else { // in game
|
||||
showCursor = false;
|
||||
grabMouse = relativeMouse = true;
|
||||
}
|
||||
|
||||
in_relativeMouseMode = relativeMouse;
|
||||
|
||||
// if in_nograb is set, in_relativeMouseMode and relativeMouse can disagree
|
||||
// (=> don't enable relative mouse mode in SDL, but still use relative mouse events
|
||||
// in the game, unless we'd use absolute mousemode anyway)
|
||||
if ( in_nograb.GetBool() ) {
|
||||
grabMouse = relativeMouse = false;
|
||||
}
|
||||
} else {
|
||||
in_relativeMouseMode = false;
|
||||
}
|
||||
|
||||
int flags = 0;
|
||||
if ( !showCursor )
|
||||
flags |= GRAB_HIDECURSOR;
|
||||
if ( grabMouse )
|
||||
flags |= GRAB_GRABMOUSE;
|
||||
if ( relativeMouse )
|
||||
flags |= GRAB_RELATIVEMOUSE;
|
||||
|
||||
GLimp_GrabInput( flags );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
Sys_GenerateEvents
|
||||
================
|
||||
*/
|
||||
void Sys_GenerateEvents() {
|
||||
|
||||
handleMouseGrab();
|
||||
|
||||
char *s = Sys_ConsoleInput();
|
||||
|
||||
if (s)
|
||||
|
|
|
@ -97,11 +97,8 @@ If you have questions concerning this license or the applicable additional terms
|
|||
|
||||
#endif // _WIN32 and ID_ALLOW_TOOLS
|
||||
|
||||
idCVar in_nograb("in_nograb", "0", CVAR_SYSTEM | CVAR_NOCHEAT, "prevents input grabbing");
|
||||
idCVar r_waylandcompat("r_waylandcompat", "0", CVAR_SYSTEM | CVAR_NOCHEAT | CVAR_ARCHIVE, "wayland compatible framebuffer");
|
||||
|
||||
static bool grabbed = false;
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
static SDL_Window *window = NULL;
|
||||
static SDL_GLContext context = NULL;
|
||||
|
@ -644,28 +641,19 @@ GLExtension_t GLimp_ExtensionPointer(const char *name) {
|
|||
}
|
||||
|
||||
void GLimp_GrabInput(int flags) {
|
||||
bool grab = flags & GRAB_ENABLE;
|
||||
|
||||
if (grab && (flags & GRAB_REENABLE))
|
||||
grab = false;
|
||||
|
||||
if (flags & GRAB_SETSTATE)
|
||||
grabbed = grab;
|
||||
|
||||
if (in_nograb.GetBool())
|
||||
grab = false;
|
||||
|
||||
if (!window) {
|
||||
common->Warning("GLimp_GrabInput called without window");
|
||||
return;
|
||||
}
|
||||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_ShowCursor(flags & GRAB_HIDECURSOR ? SDL_DISABLE : SDL_ENABLE);
|
||||
SDL_SetRelativeMouseMode((grab && (flags & GRAB_HIDECURSOR)) ? SDL_TRUE : SDL_FALSE);
|
||||
SDL_SetWindowGrab(window, grab ? SDL_TRUE : SDL_FALSE);
|
||||
SDL_ShowCursor( (flags & GRAB_HIDECURSOR) ? SDL_DISABLE : SDL_ENABLE );
|
||||
SDL_SetRelativeMouseMode( (flags & GRAB_RELATIVEMOUSE) ? SDL_TRUE : SDL_FALSE );
|
||||
SDL_SetWindowGrab( window, (flags & GRAB_GRABMOUSE) ? SDL_TRUE : SDL_FALSE );
|
||||
#else
|
||||
SDL_ShowCursor(flags & GRAB_HIDECURSOR ? SDL_DISABLE : SDL_ENABLE);
|
||||
SDL_WM_GrabInput(grab ? SDL_GRAB_ON : SDL_GRAB_OFF);
|
||||
SDL_ShowCursor( (flags & GRAB_HIDECURSOR) ? SDL_DISABLE : SDL_ENABLE );
|
||||
// ignore GRAB_GRABMOUSE, SDL1.2 doesn't support grabbing without relative mode
|
||||
// so only grab if we want relative mode
|
||||
SDL_WM_GrabInput( (flags & GRAB_RELATIVEMOUSE) ? SDL_GRAB_ON : SDL_GRAB_OFF );
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -57,7 +57,8 @@ typedef enum {
|
|||
SE_NONE, // evTime is still valid
|
||||
SE_KEY, // evValue is a key code, evValue2 is the down flag
|
||||
SE_CHAR, // evValue is an ascii char
|
||||
SE_MOUSE, // evValue and evValue2 are reletive signed x / y moves
|
||||
SE_MOUSE, // evValue and evValue2 are relative signed x / y moves
|
||||
SE_MOUSE_ABS, // evValue and evValue2 are absolute x / y coordinates in the window
|
||||
SE_JOYSTICK_AXIS, // evValue is an axis number and evValue2 is the current state (-127 to 127)
|
||||
SE_CONSOLE // evPtr is a char*, from typing something at a non-game console
|
||||
} sysEventType_t;
|
||||
|
|
|
@ -51,7 +51,6 @@ void MaterialEditorInit( void ) {
|
|||
|
||||
com_editors = EDITOR_MATERIAL;
|
||||
|
||||
Sys_GrabMouseCursor( false );
|
||||
|
||||
InitAfx();
|
||||
|
||||
|
|
|
@ -344,7 +344,7 @@ const char *idUserInterfaceLocal::HandleEvent( const sysEvent_t *event, int _tim
|
|||
return ret;
|
||||
}
|
||||
|
||||
if ( event->evType == SE_MOUSE ) {
|
||||
if ( event->evType == SE_MOUSE || event->evType == SE_MOUSE_ABS ) {
|
||||
if ( !desktop || (desktop->GetFlags() & WIN_MENUGUI) ) {
|
||||
// DG: this is a fullscreen GUI, scale the mousedelta added to cursorX/Y
|
||||
// by 640/w, because the GUI pretends that everything is 640x480
|
||||
|
@ -374,8 +374,20 @@ const char *idUserInterfaceLocal::HandleEvent( const sysEvent_t *event, int _tim
|
|||
}
|
||||
}
|
||||
|
||||
cursorX += event->evValue * (float(VIRTUAL_WIDTH)/w);
|
||||
cursorY += event->evValue2 * (float(VIRTUAL_HEIGHT)/h);
|
||||
if( event->evType == SE_MOUSE ) {
|
||||
cursorX += event->evValue * (float(VIRTUAL_WIDTH)/w);
|
||||
cursorY += event->evValue2 * (float(VIRTUAL_HEIGHT)/h);
|
||||
} else { // SE_MOUSE_ABS
|
||||
// Note: In case of scaling to 4:3, w and h are already scaled down
|
||||
// to the 4:3 size that fits into the real resolution.
|
||||
// Otherwise xOffset/yOffset will just be 0
|
||||
float xOffset = (renderSystem->GetScreenWidth() - w) * 0.5f;
|
||||
float yOffset = (renderSystem->GetScreenHeight() - h) * 0.5f;
|
||||
// offset the mouse coordinates into 4:3 area and scale down to 640x480
|
||||
// yes, result could be negative, doesn't matter, code below checks that anyway
|
||||
cursorX = (event->evValue - xOffset) * (float(VIRTUAL_WIDTH)/w);
|
||||
cursorY = (event->evValue2 - yOffset) * (float(VIRTUAL_HEIGHT)/h);
|
||||
}
|
||||
} else {
|
||||
// not a fullscreen GUI but some ingame thing - no scaling needed
|
||||
cursorX += event->evValue;
|
||||
|
|
|
@ -927,7 +927,7 @@ const char *idWindow::HandleEvent(const sysEvent_t *event, bool *updateVisuals)
|
|||
}
|
||||
}
|
||||
|
||||
} else if (event->evType == SE_MOUSE) {
|
||||
} else if (event->evType == SE_MOUSE || event->evType == SE_MOUSE_ABS) {
|
||||
if (updateVisuals) {
|
||||
*updateVisuals = true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue