Some functions that will be used by the ImGui keybinding menu

This commit is contained in:
Daniel Gibson 2024-05-22 16:39:37 +02:00
parent 8554487bf5
commit 6d508eac17
4 changed files with 209 additions and 15 deletions

View file

@ -278,6 +278,28 @@ static bool utf8ToISO8859_1(const char* inbuf, char* outbuf, size_t outsize) {
}
#endif // SDL2
// start button isn't bindable, but I want to use its name in the imgui-based menu
const char* D3_GetGamepadStartButtonName() {
int layout = joy_gamepadLayout.GetInteger();
if ( layout == -1 ) {
layout = gamepadType;
}
switch( layout ) {
default:
common->Warning( "joy_gamepadLayout has invalid value %d !\n", joy_gamepadLayout.GetInteger() );
// fall-through
case D3_GAMEPAD_PLAYSTATION_OLD:
case D3_GAMEPAD_XINPUT:
return "Pad Start";
case D3_GAMEPAD_NINTENDO:
return "Pad (+)";
case D3_GAMEPAD_PLAYSTATION:
return "Pad Options";
}
}
const char* Sys_GetLocalizedJoyKeyName( int key ) {
// Note: trying to keep the returned names short, because the Doom3 binding window doesn't have much space for names..
@ -409,28 +431,41 @@ const char* Sys_GetLocalizedJoyKeyName( int key ) {
return NULL;
}
// returns localized name of the key (between K_FIRST_SCANCODE and K_LAST_SCANCODE),
// regarding the current keyboard layout - if that name is in ASCII or corresponds
// to a "High-ASCII" char supported by Doom3.
// Otherwise return same name as Sys_GetScancodeName()
// !! Returned string is only valid until next call to this function !!
const char* Sys_GetLocalizedScancodeName( int key ) {
static const char* getLocalizedScancodeName( int key, bool useUtf8 )
{
if ( key >= K_FIRST_SCANCODE && key <= K_LAST_SCANCODE ) {
int scIdx = key - K_FIRST_SCANCODE;
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_Scancode sc = ( SDL_Scancode ) scancodemappings[scIdx].sdlScancode;
SDL_Keycode k = SDL_GetKeyFromScancode( sc );
if ( k >= 0xA1 && k <= 0xFF ) {
// luckily, the "High-ASCII" (ISO-8559-1) chars supported by Doom3
// have the same values as the corresponding SDL_Keycodes.
static char oneCharStr[2] = {0, 0};
oneCharStr[0] = (unsigned char)k;
return oneCharStr;
static char shortStr[3] = {};
if ( useUtf8 ) {
// SDL_Keycodes are unicode chars (where applicable),
// so at least for Latin-1 turn them directly into UTF-8
if ( k >= 0xE0 && k <= 0xFE && k != 0xF7 ) {
// turn lowercase chars into their uppercase equivalents
k -= 32;
}
shortStr[0] = (unsigned char)( 0xC0 + (k >> 6) );
shortStr[1] = (unsigned char)( 0x80 + (k & 0x3F) );
return shortStr;
} else {
// luckily, the "High-ASCII" (ISO-8559-1) chars supported by Doom3
// have the same values as the corresponding SDL_Keycodes.
shortStr[0] = (unsigned char)k;
shortStr[1] = 0;
return shortStr;
}
} else if ( k != SDLK_UNKNOWN ) {
const char *ret = SDL_GetKeyName( k );
// the keyname from SDL2 is in UTF-8, which Doom3 can't print,
// so only return the name if it's ASCII, otherwise fall back to "SC_bla"
if ( ret && *ret != '\0' ) {
if( useUtf8 ) {
return ret;
}
if( isAscii( ret ) ) {
return ret;
}
@ -448,6 +483,19 @@ const char* Sys_GetLocalizedScancodeName( int key ) {
return NULL;
}
// returns localized name of the key (between K_FIRST_SCANCODE and K_LAST_SCANCODE),
// regarding the current keyboard layout - if that name is in ASCII or corresponds
// to a "High-ASCII" char supported by Doom3.
// Otherwise return same name as Sys_GetScancodeName()
// !! Returned string is only valid until next call to this function !!
const char* Sys_GetLocalizedScancodeName( int key ) {
return getLocalizedScancodeName( key, false );
}
const char* Sys_GetLocalizedScancodeNameUTF8( int key ) {
return getLocalizedScancodeName( key, true );
}
// returns keyNum_t (K_SC_* constant) for given scancode name (like "SC_A")
// only makes sense to call it if name starts with "SC_" (or "sc_")
// returns -1 if not found

View file

@ -1,4 +1,6 @@
#define IMGUI_DEFINE_MATH_OPERATORS
#include <SDL.h>
#include "sys_imgui.h"
@ -14,6 +16,7 @@ typedef char* (*MY_XGETDEFAULTFUN)(Display*, const char*, const char*);
#include "../libs/imgui/backends/imgui_impl_sdl2.h"
#include "framework/Common.h"
#include "framework/KeyInput.h"
#include "renderer/qgl.h"
#include "renderer/tr_local.h" // glconfig
@ -31,6 +34,82 @@ ImGuiContext* imguiCtx = NULL;
static bool haveNewFrame = false;
static int openImguiWindows = 0; // or-ed enum D3ImGuiWindow values
// was there a key down or button (mouse/gamepad) down event this frame?
// used to make the warning overlay disappear
static bool hadKeyDownEvent = false;
static idStr warningOverlayText;
static double warningOverlayStartTime = -100.0;
static ImVec2 warningOverlayStartPos;
static void UpdateWarningOverlay()
{
double timeNow = ImGui::GetTime();
if ( timeNow - warningOverlayStartTime > 4.0f ) {
warningOverlayStartTime = -100.0f;
return;
}
// also hide if a key was pressed or maybe even if the mouse was moved (too much)
ImVec2 mdv = ImGui::GetMousePos() - warningOverlayStartPos; // Mouse Delta Vector
float mouseDelta = sqrtf( mdv.x * mdv.x + mdv.y * mdv.y );
const float fontSize = ImGui::GetFontSize();
if ( mouseDelta > fontSize * 4.0f || hadKeyDownEvent ) {
warningOverlayStartTime = -100.0f;
return;
}
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::PushStyleColor( ImGuiCol_WindowBg, ImVec4(1.0f, 0.4f, 0.4f, 0.4f) );
float padSize = fontSize * 2.0f;
ImGui::PushStyleVar( ImGuiStyleVar_WindowPadding, ImVec2(padSize, padSize) );
int winFlags = ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;
ImGui::Begin("WarningOverlay", NULL, winFlags);
ImDrawList* drawList = ImGui::GetWindowDrawList();
ImVec2 points[] = {
{0, 40}, {40, 40}, {20, 0}, // triangle
{20, 12}, {20, 28}, // line
{20, 33} // dot
};
float iconScale = 1.0f; // TODO: global scale also used for fontsize
ImVec2 offset = ImGui::GetWindowPos() + ImVec2(fontSize, fontSize);
for ( ImVec2& v : points ) {
v.x = roundf( v.x * iconScale );
v.y = roundf( v.y * iconScale );
v += offset;
}
ImU32 color = ImGui::GetColorU32( ImVec4(0.1f, 0.1f, 0.1f, 1.0f) );
drawList->AddTriangle( points[0], points[1], points[2], color, roundf( iconScale * 4.0f ) );
drawList->AddPolyline( points+3, 2, color, 0, roundf( iconScale * 3.0f ) );
float dotRadius = 2.0f * iconScale;
drawList->AddEllipseFilled( points[5], ImVec2(dotRadius, dotRadius), color, 0, 6 );
ImGui::Indent( 40.0f * iconScale );
ImGui::TextUnformatted( warningOverlayText.c_str() );
ImGui::End();
ImGui::PopStyleVar(); // WindowPadding
ImGui::PopStyleColor(); // WindowBg
}
void ShowWarningOverlay( const char* text )
{
warningOverlayText = text;
warningOverlayStartTime = ImGui::GetTime();
warningOverlayStartPos = ImGui::GetMousePos();
}
static float GetDefaultDPI()
{
SDL_Window* win = sdlWindow;
@ -203,6 +282,8 @@ void NewFrame()
ImGui::NewFrame();
haveNewFrame = true;
UpdateWarningOverlay();
if (openImguiWindows & D3_ImGuiWin_Settings) {
Com_DrawDhewm3SettingsMenu();
}
@ -215,6 +296,8 @@ void NewFrame()
}
}
bool keybindModeEnabled = false;
// called with every SDL event by Sys_GetEvent()
// returns true if ImGui has handled the event (so it shouldn't be handled by D3)
bool ProcessEvent(const void* sdlEvent)
@ -228,14 +311,42 @@ bool ProcessEvent(const void* sdlEvent)
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
if( ImGui_ImplSDL2_ProcessEvent( ev ) ) {
bool imguiUsedEvent = ImGui_ImplSDL2_ProcessEvent( ev );
if ( keybindModeEnabled ) {
// in keybind mode, all input events are passed to Doom3 so it can translate them
// to internal events and we can access and use them to create a new binding
return false;
}
if ( ImGui::IsWindowFocused( ImGuiFocusedFlags_AnyWindow )
&& (ev->type == SDL_CONTROLLERAXISMOTION || ev->type == SDL_CONTROLLERBUTTONDOWN) ) {
// don't pass on controller axis events to avoid moving the mouse cursor
// and controller button events to avoid emulating mouse clicks
return true;
}
switch( ev->type ) {
// Hack: detect if any key was pressed to close the warning overlay
case SDL_CONTROLLERBUTTONDOWN:
case SDL_MOUSEWHEEL:
case SDL_MOUSEBUTTONDOWN:
case SDL_KEYDOWN:
// TODO: controller trigger?
hadKeyDownEvent = true;
}
if( imguiUsedEvent ) {
ImGuiIO& io = ImGui::GetIO();
if ( io.WantCaptureMouse ) {
switch( ev->type ) {
case SDL_MOUSEMOTION:
case SDL_MOUSEWHEEL:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
// Note: still pass button up events to the engine, so if they were pressed down
// before an imgui window got focus they don't remain pressed indefinitely
//case SDL_MOUSEBUTTONUP:
return true;
}
}
@ -243,15 +354,30 @@ bool ProcessEvent(const void* sdlEvent)
if ( io.WantCaptureKeyboard ) {
switch( ev->type ) {
case SDL_TEXTINPUT:
case SDL_KEYDOWN:
case SDL_KEYUP:
return true;
case SDL_KEYDOWN:
//case SDL_KEYUP: NOTE: see above why key up events are passed to the engine
if ( ev->key.keysym.sym < SDLK_F1 || ev->key.keysym.sym > SDLK_F12) {
// F1 - F12 are passed to the engine so its shortcuts
// (like quickload or screenshot) still work
// Doom3's menu does the same
return true;
}
}
}
}
return false;
}
void SetKeyBindMode( bool enable )
{
keybindModeEnabled = enable;
// make sure no keys are registered as down, neither when entering nor when exiting keybind mode
idKeyInput::ClearStates();
}
void EndFrame()
{
if (openImguiWindows == 0 && !haveNewFrame)
@ -297,6 +423,11 @@ void EndFrame()
if ( curArrayBuffer != 0 ) {
qglBindBufferARB( GL_ARRAY_BUFFER_ARB, curArrayBuffer );
}
// reset this at the end of each frame, will be set again by ProcessEvent()
if ( hadKeyDownEvent ) {
hadKeyDownEvent = false;
}
}

View file

@ -41,6 +41,10 @@ extern int GetOpenWindowsMask();
// returns true if ImGui has handled the event (so it shouldn't be handled by D3)
extern bool ProcessEvent(const void* sdlEvent);
// for binding keys from an ImGui-based menu: send input events to dhewm3
// even if ImGui window has focus
extern void SetKeyBindMode( bool enable );
// NewFrame() is called once per D3 frame, after all events have been gotten
// => ProcessEvent() has already been called (probably multiple times)
extern void NewFrame();
@ -52,6 +56,11 @@ extern void EndFrame();
extern float GetScale();
extern void SetScale( float scale );
// show a red overlay-window at the center of the screen that contains
// a warning symbol (triangle with !) and the given text
// disappears after a few seconds or when a key is pressed or the mouse is moved
extern void ShowWarningOverlay( const char* text );
#else // IMGUI_DISABLE - just stub out everything
inline bool IsImguiEnabled()
@ -69,6 +78,8 @@ inline void Shutdown() {}
inline bool ProcessEvent(const void* sdlEvent) { return false; }
inline void SetKeyBindMode( bool enable ) {}
inline void NewFrame() {}
inline void EndFrame() {}
@ -82,6 +93,8 @@ inline int GetOpenWindowsMask() { return 0; }
inline float GetScale() { return 1.0f; }
inline void SetScale( float scale ) {}
inline void ShowWarningOverlay( const char* text ) {}
#endif
}} //namespace D3::ImGuiHooks

View file

@ -218,6 +218,8 @@ const char* Sys_GetScancodeName( int key );
// Otherwise return same name as Sys_GetScancodeName()
// !! Returned string is only valid until next call to this function !!
const char* Sys_GetLocalizedScancodeName( int key );
// the same, but using UTF-8 instead of "High-ASCII"
const char* Sys_GetLocalizedScancodeNameUTF8( int key );
// returns keyNum_t (K_SC_* constant) for given scancode name (like "SC_A")
int Sys_GetKeynumForScancodeName( const char* name );