Dhewm3SettingsMenu: Improve cursor handling and opening menu ingame

Pause the game (with g_stopTime) when the settings menu is opened
while ingame, unpause it when it's closed.
If the menu is open while ingame and an ImGui window has focus,
the mouse cursor is shown. If the player clicks outside an ImGui window,
it gets unfocused and the cursor is hidden and the player can look
around. Pressing F10 (or whatever key is bound to "dhewm3Settings")
will give focus back to an open ImGui window, pressing it again then
will close the settings window, pressing it once again afterwards will
open the settings window again.

handleMouseGrab() (in sys/events.cpp) now checks if sys_imgui thinks
that a cursor should be shown (via D3::ImGuiHooks::ShouldShowCursor())
and if so, shows it and ungrabs the mouse. This, together with
D3::ImGuiHooks::NewFrame() checking ShouldShowCursor() to (unset)
ImGuiConfigFlags_NoMouseCursorChange, should prevent flickering cursor
problems that sometimes occurred when ImGui's SDL2 backend and dhewm3
disagreed on whether the cursor should be visible.
This commit is contained in:
Daniel Gibson 2024-05-28 04:41:21 +02:00
parent c0b6660389
commit 4bdee4f638
4 changed files with 102 additions and 10 deletions

View file

@ -8,8 +8,8 @@
#include "KeyInput.h"
#include "UsercmdGen.h" // key bindings
//#include "Game.h" // idGameEdit to access entity definitions (player, weapons)
#include "DeclEntityDef.h"
#include "Session_local.h" // sessLocal.GetActiveMenu()
#include "sys/sys_imgui.h"
#include "../libs/imgui/imgui_internal.h"
@ -1697,24 +1697,59 @@ void Com_DrawDhewm3SettingsMenu()
}
}
void Com_InitDhewm3SettingsMenu()
static void InitDhewm3SettingsMenu()
{
InitOptions( controlOptions, IM_ARRAYSIZE(controlOptions) );
InitBindingEntries();
InitOptions( controlOptions, IM_ARRAYSIZE(controlOptions) );
}
// !! Don't call this function directly, always use !!
// !! D3::ImGuiHooks::OpenWindow( D3::ImGuiHooks::D3_ImGuiWin_Settings ) !!
// !! or D3::ImGuiHooks::CloseWindow( D3::ImGuiHooks::D3_ImGuiWin_Settings ) !!
// (unless you're implementing those two functions, they call this..)
void Com_OpenCloseDhewm3SettingsMenu( bool open )
{
if ( open ) {
if ( !sessLocal.IsMultiplayer() ) {
// if we're in a SP game, pause the game.
// g_stopTime is the best we have for this..
// advantage of this, compared to the main menu with its settings menu:
// gamma and brightness can be modified and the effect is visible in realtime,
// but (at least in SP..) the player is still safe from monsters while doing this
idCVar* stopTime = cvarSystem->Find( "g_stoptime" );
if ( stopTime != nullptr ) {
stopTime->SetBool( true );
}
}
InitDhewm3SettingsMenu();
} else {
// unset g_stopTime (no matter if we're in MP now, maybe we weren't
// when the menu was opened, just set it to false now to be sure)
idCVar* stopTime = cvarSystem->Find( "g_stoptime" );
if ( stopTime != nullptr ) {
stopTime->SetBool( false );
}
}
}
void Com_Dhewm3Settings_f( const idCmdArgs &args )
{
bool menuOpen = (D3::ImGuiHooks::GetOpenWindowsMask() & D3::ImGuiHooks::D3_ImGuiWin_Settings) != 0;
if ( !menuOpen ) {
// TODO: if in SP game, pause
Com_InitDhewm3SettingsMenu();
D3::ImGuiHooks::OpenWindow( D3::ImGuiHooks::D3_ImGuiWin_Settings );
} else {
D3::ImGuiHooks::CloseWindow( D3::ImGuiHooks::D3_ImGuiWin_Settings );
// TODO: if in SP game, unpause
if ( ImGui::IsWindowFocused( ImGuiFocusedFlags_AnyWindow ) ) {
// if the settings window is open and an ImGui window has focus,
// close the settings window when "dhewm3Settings" is executed
D3::ImGuiHooks::CloseWindow( D3::ImGuiHooks::D3_ImGuiWin_Settings );
} else {
// if the settings window is open but no ImGui window has focus,
// give focus to one of the ImGui windows.
// useful to get the cursor back when ingame..
ImGui::SetNextWindowFocus();
}
}
}

View file

@ -1618,7 +1618,7 @@ static void handleMouseGrab() {
bool relativeMouse = false;
// if com_editorActive, release everything, just like when we have no focus
if ( in_hasFocus && !com_editorActive ) {
if ( in_hasFocus && !com_editorActive && !D3::ImGuiHooks::ShouldShowCursor() ) {
// 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

View file

@ -17,10 +17,12 @@ typedef char* (*MY_XGETDEFAULTFUN)(Display*, const char*, const char*);
#include "framework/Common.h"
#include "framework/KeyInput.h"
#include "framework/Session_local.h" // sessLocal.GetActiveMenu()
#include "renderer/qgl.h"
#include "renderer/tr_local.h" // glconfig
extern void Com_DrawDhewm3SettingsMenu(); // in framework/dhewm3SettingsMenu.cpp
extern void Com_OpenCloseDhewm3SettingsMenu( bool open ); // ditto
static idCVar imgui_scale( "imgui_scale", "-1.0", CVAR_SYSTEM|CVAR_FLOAT|CVAR_ARCHIVE, "factor to scale ImGUI menus by (-1: auto)" ); // TODO: limit values?
@ -309,6 +311,12 @@ void NewFrame()
// Start the Dear ImGui frame
ImGui_ImplOpenGL2_NewFrame();
if ( ShouldShowCursor() )
ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange;
else
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
haveNewFrame = true;
@ -409,6 +417,28 @@ void SetKeyBindMode( bool enable )
idKeyInput::ClearStates();
}
bool ShouldShowCursor()
{
if ( sessLocal.GetActiveMenu() != nullptr ) {
// if we're in a menu (probably main menu), dhewm3 renders a cursor for it,
// so only show the ImGui cursor when the mouse cursor is over an ImGui window
return openImguiWindows != 0 && ImGui::GetIO().WantCaptureMouse;
} else {
// when ingame, render the ImGui/SDL/system cursor if an ImGui window is open
// because dhewm3 does *not* render its own cursor outside ImGui windows.
// additionally, only show it if an ImGui window has focus - this allows you
// to click outside the ImGui window to give Doom3 focus and look around.
// You can get focus on the ImGui window again by clicking while the invisible
// cursor is over the window (things in it still get highlighted), or by
// opening the main (Esc) or by opening the Dhewm3 Settings window (F10, usually),
// which will either open it focused or give an ImGui window focus if it
// was open but unfocused.
// Might be nice to have a keyboard shortcut to give focus to any open
// ImGui window, maybe Pause?
return openImguiWindows != 0 && ImGui::IsWindowFocused( ImGuiFocusedFlags_AnyWindow );
}
}
void EndFrame()
{
if (openImguiWindows == 0 && !haveNewFrame)
@ -464,11 +494,33 @@ void EndFrame()
void OpenWindow( D3ImGuiWindow win )
{
if ( openImguiWindows & win )
return; // already open
ImGui::SetNextWindowFocus();
switch ( win ) {
case D3_ImGuiWin_Settings:
Com_OpenCloseDhewm3SettingsMenu( true );
break;
// TODO: other windows that need explicit opening
}
openImguiWindows |= win;
}
void CloseWindow( D3ImGuiWindow win )
{
if ( (openImguiWindows & win) == 0 )
return; // already closed
switch ( win ) {
case D3_ImGuiWin_Settings:
Com_OpenCloseDhewm3SettingsMenu( false );
break;
// TODO: other windows that need explicit closing
}
openImguiWindows &= ~win;
}

View file

@ -45,6 +45,9 @@ extern bool ProcessEvent(const void* sdlEvent);
// even if ImGui window has focus
extern void SetKeyBindMode( bool enable );
// returns true if the system cursor should be shown because an ImGui menu is active
extern bool ShouldShowCursor();
// NewFrame() is called once per D3 frame, after all events have been gotten
// => ProcessEvent() has already been called (probably multiple times)
extern void NewFrame();
@ -99,6 +102,8 @@ inline bool ProcessEvent(const void* sdlEvent) { return false; }
inline void SetKeyBindMode( bool enable ) {}
inline bool ShouldShowCursor() { return false; }
inline void NewFrame() {}
inline void EndFrame() {}