From b28bac89d4832b2277183e26f3952e01396ba402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 4 Aug 2024 12:17:47 +0200 Subject: [PATCH 1/7] Move `CBinds::GetKeyId` function to `IInput::FindKeyByName` --- src/engine/client/input.cpp | 20 ++++++++++++++++++++ src/engine/client/input.h | 1 + src/engine/input.h | 1 + src/game/client/components/binds.cpp | 22 +--------------------- src/game/client/components/binds.h | 2 -- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/engine/client/input.cpp b/src/engine/client/input.cpp index 8e3da5c42..5711a5119 100644 --- a/src/engine/client/input.cpp +++ b/src/engine/client/input.cpp @@ -367,6 +367,26 @@ bool CInput::KeyState(int Key) const return m_aInputState[Key]; } +int CInput::FindKeyByName(const char *pKeyName) const +{ + // check for numeric + if(pKeyName[0] == '&') + { + int Key = str_toint(pKeyName + 1); + if(Key > KEY_FIRST && Key < KEY_LAST) + return Key; // numeric + } + + // search for key + for(int Key = KEY_FIRST; Key < KEY_LAST; Key++) + { + if(str_comp_nocase(pKeyName, KeyName(Key)) == 0) + return Key; + } + + return KEY_UNKNOWN; +} + void CInput::UpdateMouseState() { const int MouseState = SDL_GetMouseState(nullptr, nullptr); diff --git a/src/engine/client/input.h b/src/engine/client/input.h index bc94ad707..908e133e1 100644 --- a/src/engine/client/input.h +++ b/src/engine/client/input.h @@ -136,6 +136,7 @@ public: bool AltIsPressed() const override { return KeyState(KEY_LALT) || KeyState(KEY_RALT); } bool KeyIsPressed(int Key) const override { return KeyState(Key); } bool KeyPress(int Key, bool CheckCounter) const override { return CheckCounter ? (m_aInputCount[Key] == m_InputCounter) : m_aInputCount[Key]; } + int FindKeyByName(const char *pKeyName) const override; size_t NumJoysticks() const override { return m_vJoysticks.size(); } CJoystick *GetJoystick(size_t Index) override { return &m_vJoysticks[Index]; } diff --git a/src/engine/input.h b/src/engine/input.h index f80a210f4..4bb6e67a7 100644 --- a/src/engine/input.h +++ b/src/engine/input.h @@ -58,6 +58,7 @@ public: virtual bool KeyIsPressed(int Key) const = 0; virtual bool KeyPress(int Key, bool CheckCounter = false) const = 0; const char *KeyName(int Key) const { return (Key >= 0 && Key < g_MaxKeys) ? g_aaKeyStrings[Key] : g_aaKeyStrings[0]; } + virtual int FindKeyByName(const char *pKeyName) const = 0; // joystick class IJoystick diff --git a/src/game/client/components/binds.cpp b/src/game/client/components/binds.cpp index 2370b8afb..8f123a1a5 100644 --- a/src/game/client/components/binds.cpp +++ b/src/game/client/components/binds.cpp @@ -376,26 +376,6 @@ void CBinds::ConUnbindAll(IConsole::IResult *pResult, void *pUserData) pBinds->UnbindAll(); } -int CBinds::GetKeyId(const char *pKeyName) -{ - // check for numeric - if(pKeyName[0] == '&') - { - int i = str_toint(pKeyName + 1); - if(i > 0 && i < KEY_LAST) - return i; // numeric - } - - // search for key - for(int i = 0; i < KEY_LAST; i++) - { - if(str_comp_nocase(pKeyName, Input()->KeyName(i)) == 0) - return i; - } - - return 0; -} - int CBinds::GetBindSlot(const char *pBindString, int *pModifierCombination) { *pModifierCombination = MODIFIER_NONE; @@ -420,7 +400,7 @@ int CBinds::GetBindSlot(const char *pBindString, int *pModifierCombination) else break; } - return GetKeyId(*pModifierCombination == MODIFIER_NONE ? aMod : pKey + 1); + return Input()->FindKeyByName(*pModifierCombination == MODIFIER_NONE ? aMod : pKey + 1); } const char *CBinds::GetModifierName(int Modifier) diff --git a/src/game/client/components/binds.h b/src/game/client/components/binds.h index 39a0b8aca..471d115f6 100644 --- a/src/game/client/components/binds.h +++ b/src/game/client/components/binds.h @@ -12,8 +12,6 @@ class IConfigManager; class CBinds : public CComponent { - int GetKeyId(const char *pKeyName); - static void ConBind(IConsole::IResult *pResult, void *pUserData); static void ConBinds(IConsole::IResult *pResult, void *pUserData); static void ConUnbind(IConsole::IResult *pResult, void *pUserData); From d2222f55e76af02fe9ab5f1c923d67991fa534f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 4 Aug 2024 21:23:00 +0200 Subject: [PATCH 2/7] Add `CBindSlot` as return value for `GetBindSlot` function --- src/game/client/components/binds.cpp | 48 +++++++++++++--------------- src/game/client/components/binds.h | 15 ++++++++- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/game/client/components/binds.cpp b/src/game/client/components/binds.cpp index 8f123a1a5..50ef83e90 100644 --- a/src/game/client/components/binds.cpp +++ b/src/game/client/components/binds.cpp @@ -282,10 +282,9 @@ void CBinds::ConBind(IConsole::IResult *pResult, void *pUserData) { CBinds *pBinds = (CBinds *)pUserData; const char *pBindStr = pResult->GetString(0); - int Modifier; - int KeyId = pBinds->GetBindSlot(pBindStr, &Modifier); + const CBindSlot BindSlot = pBinds->GetBindSlot(pBindStr); - if(!KeyId) + if(!BindSlot.m_Key) { char aBuf[256]; str_format(aBuf, sizeof(aBuf), "key %s not found", pBindStr); @@ -298,16 +297,16 @@ void CBinds::ConBind(IConsole::IResult *pResult, void *pUserData) char aBuf[256]; const char *pKeyName = pResult->GetString(0); - if(!pBinds->m_aapKeyBindings[Modifier][KeyId]) - str_format(aBuf, sizeof(aBuf), "%s (%d) is not bound", pKeyName, KeyId); + if(!pBinds->m_aapKeyBindings[BindSlot.m_ModifierMask][BindSlot.m_Key]) + str_format(aBuf, sizeof(aBuf), "%s (%d) is not bound", pKeyName, BindSlot.m_Key); else - str_format(aBuf, sizeof(aBuf), "%s (%d) = %s", pKeyName, KeyId, pBinds->m_aapKeyBindings[Modifier][KeyId]); + str_format(aBuf, sizeof(aBuf), "%s (%d) = %s", pKeyName, BindSlot.m_Key, pBinds->m_aapKeyBindings[BindSlot.m_ModifierMask][BindSlot.m_Key]); pBinds->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "binds", aBuf, gs_BindPrintColor); return; } - pBinds->Bind(KeyId, pResult->GetString(1), false, Modifier); + pBinds->Bind(BindSlot.m_Key, pResult->GetString(1), false, BindSlot.m_ModifierMask); } void CBinds::ConBinds(IConsole::IResult *pResult, void *pUserData) @@ -317,20 +316,18 @@ void CBinds::ConBinds(IConsole::IResult *pResult, void *pUserData) { char aBuf[256]; const char *pKeyName = pResult->GetString(0); - - int Modifier; - int KeyId = pBinds->GetBindSlot(pKeyName, &Modifier); - if(!KeyId) + const CBindSlot BindSlot = pBinds->GetBindSlot(pKeyName); + if(!BindSlot.m_Key) { str_format(aBuf, sizeof(aBuf), "key '%s' not found", pKeyName); pBinds->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "binds", aBuf, gs_BindPrintColor); } else { - if(!pBinds->m_aapKeyBindings[Modifier][KeyId]) - str_format(aBuf, sizeof(aBuf), "%s (%d) is not bound", pKeyName, KeyId); + if(!pBinds->m_aapKeyBindings[BindSlot.m_ModifierMask][BindSlot.m_Key]) + str_format(aBuf, sizeof(aBuf), "%s (%d) is not bound", pKeyName, BindSlot.m_Key); else - str_format(aBuf, sizeof(aBuf), "%s (%d) = %s", pKeyName, KeyId, pBinds->m_aapKeyBindings[Modifier][KeyId]); + str_format(aBuf, sizeof(aBuf), "%s (%d) = %s", pKeyName, BindSlot.m_Key, pBinds->m_aapKeyBindings[BindSlot.m_ModifierMask][BindSlot.m_Key]); pBinds->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "binds", aBuf, gs_BindPrintColor); } @@ -356,10 +353,9 @@ void CBinds::ConUnbind(IConsole::IResult *pResult, void *pUserData) { CBinds *pBinds = (CBinds *)pUserData; const char *pKeyName = pResult->GetString(0); - int Modifier; - int KeyId = pBinds->GetBindSlot(pKeyName, &Modifier); + const CBindSlot BindSlot = pBinds->GetBindSlot(pKeyName); - if(!KeyId) + if(!BindSlot.m_Key) { char aBuf[256]; str_format(aBuf, sizeof(aBuf), "key %s not found", pKeyName); @@ -367,7 +363,7 @@ void CBinds::ConUnbind(IConsole::IResult *pResult, void *pUserData) return; } - pBinds->Bind(KeyId, "", false, Modifier); + pBinds->Bind(BindSlot.m_Key, "", false, BindSlot.m_ModifierMask); } void CBinds::ConUnbindAll(IConsole::IResult *pResult, void *pUserData) @@ -376,31 +372,31 @@ void CBinds::ConUnbindAll(IConsole::IResult *pResult, void *pUserData) pBinds->UnbindAll(); } -int CBinds::GetBindSlot(const char *pBindString, int *pModifierCombination) +CBinds::CBindSlot CBinds::GetBindSlot(const char *pBindString) const { - *pModifierCombination = MODIFIER_NONE; + int ModifierMask = MODIFIER_NONE; char aMod[32]; aMod[0] = '\0'; const char *pKey = str_next_token(pBindString, "+", aMod, sizeof(aMod)); while(aMod[0] && *(pKey)) { if(!str_comp_nocase(aMod, "shift")) - *pModifierCombination |= (1 << MODIFIER_SHIFT); + ModifierMask |= (1 << MODIFIER_SHIFT); else if(!str_comp_nocase(aMod, "ctrl")) - *pModifierCombination |= (1 << MODIFIER_CTRL); + ModifierMask |= (1 << MODIFIER_CTRL); else if(!str_comp_nocase(aMod, "alt")) - *pModifierCombination |= (1 << MODIFIER_ALT); + ModifierMask |= (1 << MODIFIER_ALT); else if(!str_comp_nocase(aMod, "gui")) - *pModifierCombination |= (1 << MODIFIER_GUI); + ModifierMask |= (1 << MODIFIER_GUI); else - return 0; + return {KEY_UNKNOWN, MODIFIER_NONE}; if(str_find(pKey + 1, "+")) pKey = str_next_token(pKey + 1, "+", aMod, sizeof(aMod)); else break; } - return Input()->FindKeyByName(*pModifierCombination == MODIFIER_NONE ? aMod : pKey + 1); + return {Input()->FindKeyByName(ModifierMask == MODIFIER_NONE ? aMod : pKey + 1), ModifierMask}; } const char *CBinds::GetModifierName(int Modifier) diff --git a/src/game/client/components/binds.h b/src/game/client/components/binds.h index 471d115f6..164640224 100644 --- a/src/game/client/components/binds.h +++ b/src/game/client/components/binds.h @@ -20,6 +20,20 @@ class CBinds : public CComponent static void ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData); + class CBindSlot + { + public: + int m_Key; + int m_ModifierMask; + + CBindSlot(int Key, int ModifierMask) : + m_Key(Key), + m_ModifierMask(ModifierMask) + { + } + }; + CBindSlot GetBindSlot(const char *pBindString) const; + public: CBinds(); ~CBinds(); @@ -53,7 +67,6 @@ public: void UnbindAll(); const char *Get(int KeyId, int ModifierCombination); void GetKey(const char *pBindStr, char *pBuf, size_t BufSize); - int GetBindSlot(const char *pBindString, int *pModifierCombination); static int GetModifierMask(IInput *pInput); static int GetModifierMaskOfKey(int Key); static const char *GetModifierName(int Modifier); From 8fd40efe362f7fcd371e30b66a815e4261d86dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 4 Aug 2024 21:25:04 +0200 Subject: [PATCH 3/7] Fix key names for composite binds with multiple modifiers This was only affecting the key names displayed in the voting HUD, e.g. with `bind "ctrl+alt+f3" "vote yes"` the voting HUD previously showed `+f3` as the key name. --- src/game/client/components/binds.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/game/client/components/binds.cpp b/src/game/client/components/binds.cpp index 50ef83e90..90efe3269 100644 --- a/src/game/client/components/binds.cpp +++ b/src/game/client/components/binds.cpp @@ -196,21 +196,18 @@ const char *CBinds::Get(int KeyId, int ModifierCombination) void CBinds::GetKey(const char *pBindStr, char *pBuf, size_t BufSize) { - pBuf[0] = 0; - for(int Mod = 0; Mod < MODIFIER_COMBINATION_COUNT; Mod++) + pBuf[0] = '\0'; + for(int Modifier = MODIFIER_NONE; Modifier < MODIFIER_COMBINATION_COUNT; Modifier++) { - for(int KeyId = 0; KeyId < KEY_LAST; KeyId++) + for(int KeyId = KEY_FIRST; KeyId < KEY_LAST; KeyId++) { - const char *pBind = Get(KeyId, Mod); + const char *pBind = Get(KeyId, Modifier); if(!pBind[0]) continue; if(str_comp(pBind, pBindStr) == 0) { - if(Mod) - str_format(pBuf, BufSize, "%s+%s", GetModifierName(Mod), Input()->KeyName(KeyId)); - else - str_copy(pBuf, Input()->KeyName(KeyId), BufSize); + str_format(pBuf, BufSize, "%s%s", GetKeyBindModifiersName(Modifier), Input()->KeyName(KeyId)); return; } } From 9f6cb92cb9610e2d5e556c84e280e767802d426d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 4 Aug 2024 21:27:08 +0200 Subject: [PATCH 4/7] Add assertions to `CBinds::Bind` and `Get` functions --- src/game/client/components/binds.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/game/client/components/binds.cpp b/src/game/client/components/binds.cpp index 90efe3269..e62d1d4c8 100644 --- a/src/game/client/components/binds.cpp +++ b/src/game/client/components/binds.cpp @@ -51,8 +51,8 @@ CBinds::~CBinds() void CBinds::Bind(int KeyId, const char *pStr, bool FreeOnly, int ModifierCombination) { - if(KeyId < 0 || KeyId >= KEY_LAST) - return; + dbg_assert(KeyId >= KEY_FIRST && KeyId < KEY_LAST, "KeyId invalid"); + dbg_assert(ModifierCombination >= MODIFIER_NONE && ModifierCombination < MODIFIER_COMBINATION_COUNT, "ModifierCombination invalid"); if(FreeOnly && Get(KeyId, ModifierCombination)[0]) return; @@ -189,9 +189,9 @@ void CBinds::UnbindAll() const char *CBinds::Get(int KeyId, int ModifierCombination) { - if(KeyId > 0 && KeyId < KEY_LAST && m_aapKeyBindings[ModifierCombination][KeyId]) - return m_aapKeyBindings[ModifierCombination][KeyId]; - return ""; + dbg_assert(KeyId >= KEY_FIRST && KeyId < KEY_LAST, "KeyId invalid"); + dbg_assert(ModifierCombination >= MODIFIER_NONE && ModifierCombination < MODIFIER_COMBINATION_COUNT, "ModifierCombination invalid"); + return m_aapKeyBindings[ModifierCombination][KeyId] ? m_aapKeyBindings[ModifierCombination][KeyId] : ""; } void CBinds::GetKey(const char *pBindStr, char *pBuf, size_t BufSize) From 12e5f7f1d3bf737e3d275e0f67a77ad8d003f97f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 4 Aug 2024 21:31:54 +0200 Subject: [PATCH 5/7] Various minor refactoring of `CBinds` - Use `UnbindAll` function in destructor to reduce duplicate code. - Use appropriate constants instead of magic number `0`. - Remove redundant comments. - Remove redundant `else if` condition. - Rename generic loop variables `i` and `j` to `Modifier` and `Key` for clarity. --- src/game/client/components/binds.cpp | 36 +++++++++++++--------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/game/client/components/binds.cpp b/src/game/client/components/binds.cpp index e62d1d4c8..5fe69b6f8 100644 --- a/src/game/client/components/binds.cpp +++ b/src/game/client/components/binds.cpp @@ -44,9 +44,7 @@ CBinds::CBinds() CBinds::~CBinds() { - for(int i = 0; i < KEY_LAST; i++) - for(auto &apKeyBinding : m_aapKeyBindings) - free(apKeyBinding[i]); + UnbindAll(); } void CBinds::Bind(int KeyId, const char *pStr, bool FreeOnly, int ModifierCombination) @@ -58,7 +56,7 @@ void CBinds::Bind(int KeyId, const char *pStr, bool FreeOnly, int ModifierCombin return; free(m_aapKeyBindings[ModifierCombination][KeyId]); - m_aapKeyBindings[ModifierCombination][KeyId] = 0; + m_aapKeyBindings[ModifierCombination][KeyId] = nullptr; // skip modifiers for +xxx binds if(pStr[0] == '+') @@ -120,7 +118,7 @@ int CBinds::GetModifierMaskOfKey(int Key) case KEY_RGUI: return 1 << CBinds::MODIFIER_GUI; default: - return 0; + return CBinds::MODIFIER_NONE; } } @@ -182,7 +180,7 @@ void CBinds::UnbindAll() for(auto &pKeyBinding : apKeyBinding) { free(pKeyBinding); - pKeyBinding = 0; + pKeyBinding = nullptr; } } } @@ -216,8 +214,8 @@ void CBinds::GetKey(const char *pBindStr, char *pBuf, size_t BufSize) void CBinds::SetDefaults() { - // set default key bindings UnbindAll(); + Bind(KEY_F1, "toggle_local_console"); Bind(KEY_F2, "toggle_remote_console"); Bind(KEY_TAB, "+scoreboard"); @@ -257,7 +255,6 @@ void CBinds::SetDefaults() Bind(KEY_Q, "say /pause"); Bind(KEY_P, "say /pause"); - // DDRace g_Config.m_ClDDRaceBindsSet = 0; SetDDRaceBinds(false); } @@ -271,7 +268,6 @@ void CBinds::OnConsoleInit() Console()->Register("unbind", "s[key]", CFGFLAG_CLIENT, ConUnbind, this, "Unbind key"); Console()->Register("unbindall", "", CFGFLAG_CLIENT, ConUnbindAll, this, "Unbind all keys"); - // default bindings SetDefaults(); } @@ -329,17 +325,17 @@ void CBinds::ConBinds(IConsole::IResult *pResult, void *pUserData) pBinds->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "binds", aBuf, gs_BindPrintColor); } } - else if(pResult->NumArguments() == 0) + else { char aBuf[1024]; - for(int i = 0; i < MODIFIER_COMBINATION_COUNT; i++) + for(int Modifier = MODIFIER_NONE; Modifier < MODIFIER_COMBINATION_COUNT; Modifier++) { - for(int j = 0; j < KEY_LAST; j++) + for(int Key = KEY_FIRST; Key < KEY_LAST; Key++) { - if(!pBinds->m_aapKeyBindings[i][j]) + if(!pBinds->m_aapKeyBindings[Modifier][Key]) continue; - str_format(aBuf, sizeof(aBuf), "%s%s (%d) = %s", GetKeyBindModifiersName(i), pBinds->Input()->KeyName(j), j, pBinds->m_aapKeyBindings[i][j]); + str_format(aBuf, sizeof(aBuf), "%s%s (%d) = %s", GetKeyBindModifiersName(Modifier), pBinds->Input()->KeyName(Key), Key, pBinds->m_aapKeyBindings[Modifier][Key]); pBinds->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "binds", aBuf, gs_BindPrintColor); } } @@ -434,22 +430,22 @@ void CBinds::ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData) CBinds *pSelf = (CBinds *)pUserData; pConfigManager->WriteLine("unbindall"); - for(int i = 0; i < MODIFIER_COMBINATION_COUNT; i++) + for(int Modifier = MODIFIER_NONE; Modifier < MODIFIER_COMBINATION_COUNT; Modifier++) { - for(int j = 0; j < KEY_LAST; j++) + for(int Key = KEY_FIRST; Key < KEY_LAST; Key++) { - if(!pSelf->m_aapKeyBindings[i][j]) + if(!pSelf->m_aapKeyBindings[Modifier][Key]) continue; // worst case the str_escape can double the string length - int Size = str_length(pSelf->m_aapKeyBindings[i][j]) * 2 + 30; + int Size = str_length(pSelf->m_aapKeyBindings[Modifier][Key]) * 2 + 30; char *pBuffer = (char *)malloc(Size); char *pEnd = pBuffer + Size; - str_format(pBuffer, Size, "bind %s%s \"", GetKeyBindModifiersName(i), pSelf->Input()->KeyName(j)); + str_format(pBuffer, Size, "bind %s%s \"", GetKeyBindModifiersName(Modifier), pSelf->Input()->KeyName(Key)); // process the string. we need to escape some characters char *pDst = pBuffer + str_length(pBuffer); - str_escape(&pDst, pSelf->m_aapKeyBindings[i][j], pEnd); + str_escape(&pDst, pSelf->m_aapKeyBindings[Modifier][Key], pEnd); str_append(pBuffer, "\"", Size); pConfigManager->WriteLine(pBuffer); From dc1a483b538f0f62a916a84d16ec283ef6698c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 4 Aug 2024 21:37:28 +0200 Subject: [PATCH 6/7] Make handling of F1-F24 keys binds consistent with regular binds The handling of F1-F24 keys in `CBindsSpecial` was not correct for composite binds, leading to stuck inputs if modifier keys are released first. Now, the `CBindsSpecial::OnInput` function simply delegates to `CBinds::OnInput` for F-key presses/releases so the behavior is consistent with regular binds. --- src/game/client/components/binds.cpp | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/game/client/components/binds.cpp b/src/game/client/components/binds.cpp index 5fe69b6f8..7f82aa485 100644 --- a/src/game/client/components/binds.cpp +++ b/src/game/client/components/binds.cpp @@ -11,26 +11,15 @@ static const ColorRGBA gs_BindPrintColor{1.0f, 1.0f, 0.8f, 1.0f}; bool CBinds::CBindsSpecial::OnInput(const IInput::CEvent &Event) { + if((Event.m_Flags & (IInput::FLAG_PRESS | IInput::FLAG_RELEASE)) == 0) + return false; + // only handle F and composed F binds - if(((Event.m_Key >= KEY_F1 && Event.m_Key <= KEY_F12) || (Event.m_Key >= KEY_F13 && Event.m_Key <= KEY_F24)) && (Event.m_Key != KEY_F5 || !m_pClient->m_Menus.IsActive())) + // do not handle F5 bind while menu is active + if(((Event.m_Key >= KEY_F1 && Event.m_Key <= KEY_F12) || (Event.m_Key >= KEY_F13 && Event.m_Key <= KEY_F24)) && + (Event.m_Key != KEY_F5 || !m_pClient->m_Menus.IsActive())) { - int Mask = CBinds::GetModifierMask(Input()); - - // Look for a composed bind - bool ret = false; - if(m_pBinds->m_aapKeyBindings[Mask][Event.m_Key]) - { - m_pBinds->GetConsole()->ExecuteLineStroked(Event.m_Flags & IInput::FLAG_PRESS, m_pBinds->m_aapKeyBindings[Mask][Event.m_Key]); - ret = true; - } - // Look for a non composed bind - if(!ret && m_pBinds->m_aapKeyBindings[0][Event.m_Key]) - { - m_pBinds->GetConsole()->ExecuteLineStroked(Event.m_Flags & IInput::FLAG_PRESS, m_pBinds->m_aapKeyBindings[0][Event.m_Key]); - ret = true; - } - - return ret; + return m_pBinds->OnInput(Event); } return false; From 3eb8122eea80ceeedc2e7654ad70e803c72ba3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Sun, 4 Aug 2024 21:58:08 +0200 Subject: [PATCH 7/7] Support composite binds with `+` commands Previously, binding `+` commands like `+hook` to composite keys like `shift+x` or `ctrl+alt+mouse1` was not possible, as it would lead to stuck inputs when one of the modifiers is released first before the primary key. When binding `+` commands to composite binds the modifier was instead ignored to prevent this. Now, binding `+` commands to composite keys is also possible. Active binds (key, modifier) are stored and when a modifier key is released all binds using that modifier are also released. Closes #8314. --- src/game/client/components/binds.cpp | 113 +++++++++++++++++---------- src/game/client/components/binds.h | 3 + 2 files changed, 75 insertions(+), 41 deletions(-) diff --git a/src/game/client/components/binds.cpp b/src/game/client/components/binds.cpp index 7f82aa485..eb01398c1 100644 --- a/src/game/client/components/binds.cpp +++ b/src/game/client/components/binds.cpp @@ -47,10 +47,6 @@ void CBinds::Bind(int KeyId, const char *pStr, bool FreeOnly, int ModifierCombin free(m_aapKeyBindings[ModifierCombination][KeyId]); m_aapKeyBindings[ModifierCombination][KeyId] = nullptr; - // skip modifiers for +xxx binds - if(pStr[0] == '+') - ModifierCombination = 0; - char aBuf[256]; if(!pStr[0]) { @@ -113,53 +109,88 @@ int CBinds::GetModifierMaskOfKey(int Key) bool CBinds::OnInput(const IInput::CEvent &Event) { - // don't handle invalid events - if(Event.m_Key <= KEY_FIRST || Event.m_Key >= KEY_LAST) + if((Event.m_Flags & (IInput::FLAG_PRESS | IInput::FLAG_RELEASE)) == 0) return false; - int Mask = GetModifierMask(Input()); - int KeyModifierMask = GetModifierMaskOfKey(Event.m_Key); - Mask &= ~KeyModifierMask; + const int KeyModifierMask = GetModifierMaskOfKey(Event.m_Key); + const int ModifierMask = GetModifierMask(Input()) & ~KeyModifierMask; - bool ret = false; - const char *pKey = nullptr; + bool Handled = false; - if(m_aapKeyBindings[Mask][Event.m_Key]) + if(Event.m_Flags & IInput::FLAG_PRESS) { - if(Event.m_Flags & IInput::FLAG_PRESS) + auto ActiveBind = std::find_if(m_vActiveBinds.begin(), m_vActiveBinds.end(), [&](const CBindSlot &Bind) { + return Event.m_Key == Bind.m_Key; + }); + if(ActiveBind == m_vActiveBinds.end()) { - Console()->ExecuteLineStroked(1, m_aapKeyBindings[Mask][Event.m_Key]); - pKey = m_aapKeyBindings[Mask][Event.m_Key]; - } - // Have to check for nullptr again because the previous execute can unbind itself - if(Event.m_Flags & IInput::FLAG_RELEASE && m_aapKeyBindings[Mask][Event.m_Key]) - Console()->ExecuteLineStroked(0, m_aapKeyBindings[Mask][Event.m_Key]); - ret = true; - } + const auto &&OnPress = [&](int Mask) { + const char *pBind = m_aapKeyBindings[Mask][Event.m_Key]; + if(g_Config.m_ClSubTickAiming) + { + if(str_comp("+fire", pBind) == 0 || str_comp("+hook", pBind) == 0) + { + m_MouseOnAction = true; + } + } + Console()->ExecuteLineStroked(1, pBind); + m_vActiveBinds.emplace_back(Event.m_Key, Mask); + }; - if(m_aapKeyBindings[0][Event.m_Key] && !ret) - { - // When ctrl+shift are pressed (ctrl+shift binds and also the hard-coded ctrl+shift+d, ctrl+shift+g, ctrl+shift+e), ignore other +xxx binds - if(Event.m_Flags & IInput::FLAG_PRESS && Mask != ((1 << MODIFIER_CTRL) | (1 << MODIFIER_SHIFT)) && Mask != ((1 << MODIFIER_GUI) | (1 << MODIFIER_SHIFT))) - { - Console()->ExecuteLineStroked(1, m_aapKeyBindings[0][Event.m_Key]); - pKey = m_aapKeyBindings[Mask][Event.m_Key]; - } - // Have to check for nullptr again because the previous execute can unbind itself - if(Event.m_Flags & IInput::FLAG_RELEASE && m_aapKeyBindings[0][Event.m_Key]) - Console()->ExecuteLineStroked(0, m_aapKeyBindings[0][Event.m_Key]); - ret = true; - } - - if(g_Config.m_ClSubTickAiming && pKey) - { - if(str_comp("+fire", pKey) == 0 || str_comp("+hook", pKey) == 0) - { - m_MouseOnAction = true; + if(m_aapKeyBindings[ModifierMask][Event.m_Key]) + { + OnPress(ModifierMask); + Handled = true; + } + else if(m_aapKeyBindings[MODIFIER_NONE][Event.m_Key] && + ModifierMask != ((1 << MODIFIER_CTRL) | (1 << MODIFIER_SHIFT)) && + ModifierMask != ((1 << MODIFIER_GUI) | (1 << MODIFIER_SHIFT))) + { + OnPress(MODIFIER_NONE); + Handled = true; + } } } - return ret; + if(Event.m_Flags & IInput::FLAG_RELEASE) + { + // Release active bind that uses this primary key + auto ActiveBind = std::find_if(m_vActiveBinds.begin(), m_vActiveBinds.end(), [&](const CBindSlot &Bind) { + return Event.m_Key == Bind.m_Key; + }); + if(ActiveBind != m_vActiveBinds.end()) + { + // Have to check for nullptr again because the previous execute can unbind itself + if(m_aapKeyBindings[ActiveBind->m_ModifierMask][ActiveBind->m_Key]) + { + Console()->ExecuteLineStroked(0, m_aapKeyBindings[ActiveBind->m_ModifierMask][ActiveBind->m_Key]); + } + m_vActiveBinds.erase(ActiveBind); + Handled = true; + } + + // Release all active binds that use this modifier key + if(KeyModifierMask != MODIFIER_NONE) + { + while(true) + { + auto ActiveModifierBind = std::find_if(m_vActiveBinds.begin(), m_vActiveBinds.end(), [&](const CBindSlot &Bind) { + return (Bind.m_ModifierMask & KeyModifierMask) != 0; + }); + if(ActiveModifierBind == m_vActiveBinds.end()) + break; + // Have to check for nullptr again because the previous execute can unbind itself + if(m_aapKeyBindings[ActiveModifierBind->m_ModifierMask][ActiveModifierBind->m_Key]) + { + Console()->ExecuteLineStroked(0, m_aapKeyBindings[ActiveModifierBind->m_ModifierMask][ActiveModifierBind->m_Key]); + } + m_vActiveBinds.erase(ActiveModifierBind); + Handled = true; + } + } + } + + return Handled; } void CBinds::UnbindAll() diff --git a/src/game/client/components/binds.h b/src/game/client/components/binds.h index 164640224..32db85336 100644 --- a/src/game/client/components/binds.h +++ b/src/game/client/components/binds.h @@ -8,6 +8,8 @@ #include +#include + class IConfigManager; class CBinds : public CComponent @@ -81,5 +83,6 @@ public: private: char *m_aapKeyBindings[MODIFIER_COMBINATION_COUNT][KEY_LAST]; + std::vector m_vActiveBinds; }; #endif