From adb891442f93512b1ded9a0235527d74a54ddac8 Mon Sep 17 00:00:00 2001 From: Robert Burnham Date: Thu, 2 Jul 2026 01:24:24 -0500 Subject: [PATCH] Rebuild the hotkey map on actual binding changes --- rts/Game/UI/KeyBindings.cpp | 69 ++++++++++++++++++++++++++----------- rts/Game/UI/KeyBindings.h | 10 +++++- 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/rts/Game/UI/KeyBindings.cpp b/rts/Game/UI/KeyBindings.cpp index 9bafd6ca542..634e86963da 100644 --- a/rts/Game/UI/KeyBindings.cpp +++ b/rts/Game/UI/KeyBindings.cpp @@ -611,6 +611,7 @@ void CKeyBindings::AddActionToKeyMap(KeyMap& bindings, Action& action) ActionList& al = bindings[ks]; action.bindingIndex = ++bindingsCount; al.push_back(action); + buildHotkeyMap = true; } else { ActionList& al = it->second; assert(it->first == ks); @@ -624,6 +625,7 @@ void CKeyBindings::AddActionToKeyMap(KeyMap& bindings, Action& action) // not yet bound, push it action.bindingIndex = ++bindingsCount; al.push_back(action); + buildHotkeyMap = true; } } } @@ -683,6 +685,9 @@ bool CKeyBindings::UnBind(const std::string& keystr, const std::string& command) if (al.empty()) bindings.erase(it); + if (success) + buildHotkeyMap = true; + return success; } @@ -707,6 +712,7 @@ bool CKeyBindings::UnBindKeyset(const std::string& keystr) return false; bindings.erase(it); + buildHotkeyMap = true; return true; } @@ -740,7 +746,12 @@ bool CKeyBindings::UnBindAction(const std::string& command) RECOIL_DETAILED_TRACY_ZONE; if (debugEnabled) LOG("[CKeyBindings::%s] command=%s", __func__, command.c_str()); - return RemoveActionFromKeyMap(command, codeBindings) || RemoveActionFromKeyMap(command, scanBindings); + const bool changed = RemoveActionFromKeyMap(command, codeBindings) || RemoveActionFromKeyMap(command, scanBindings); + + if (changed) + buildHotkeyMap = true; + + return changed; } @@ -811,9 +822,6 @@ void CKeyBindings::ConfigNotify(const std::string& key, const std::string& value void CKeyBindings::LoadDefaults() { RECOIL_DETAILED_TRACY_ZONE; - const bool tmpBuildHotkeyMap = buildHotkeyMap; - buildHotkeyMap = false; - if (debugEnabled) LOG("[CKeyBindings::%s]", __func__); @@ -822,8 +830,7 @@ void CKeyBindings::LoadDefaults() for (const auto& b: defaultBindings) { Bind(b.key, b.action); } - - buildHotkeyMap = tmpBuildHotkeyMap; + // no rebuild here: only ever used as a building block, the caller rebuilds } @@ -858,7 +865,7 @@ void CKeyBindings::PushAction(const Action& action) } } -bool CKeyBindings::ExecuteCommand(const std::string& line) +bool CKeyBindings::ExecuteCommandInternal(const std::string& line) { RECOIL_DETAILED_TRACY_ZONE; const std::vector words = CSimpleParser::Tokenize(line, 2); @@ -887,7 +894,7 @@ bool CKeyBindings::ExecuteCommand(const std::string& line) if (loadStack.empty() && words.size() == 1) LoadDefaults(); - Load(filename); + LoadInternal(filename); } else if (command == "keyreload") { const std::string& filename = words.size() > 1 ? words[1] : DEFAULT_FILENAME; @@ -895,13 +902,13 @@ bool CKeyBindings::ExecuteCommand(const std::string& line) if (debugEnabled) LOG("[CKeyBindings::%s] line=%s", __func__, line.c_str()); - ExecuteCommand("unbindall"); - ExecuteCommand("unbind enter chat"); + ExecuteCommandInternal("unbindall"); + ExecuteCommandInternal("unbind enter chat"); if (loadStack.empty() && words.size() == 1) LoadDefaults(); - Load(filename); + LoadInternal(filename); } else if (command == "keydefaults") { LoadDefaults(); @@ -930,6 +937,7 @@ bool CKeyBindings::ExecuteCommand(const std::string& line) keyCodes.Reset(); scanCodes.Reset(); bindingsCount = 0; + buildHotkeyMap = true; Bind("enter", "chat"); // bare minimum if (debugEnabled) @@ -939,14 +947,19 @@ bool CKeyBindings::ExecuteCommand(const std::string& line) return false; } - if (buildHotkeyMap) - BuildHotkeyMap(); - return false; } -bool CKeyBindings::Load(const std::string& filename) +bool CKeyBindings::ExecuteCommand(const std::string& line) +{ + const bool ret = ExecuteCommandInternal(line); + MaybeBuildHotkeyMap(); + return ret; +} + + +bool CKeyBindings::LoadInternal(const std::string& filename) { RECOIL_DETAILED_TRACY_ZONE; if (std::find(loadStack.begin(), loadStack.end(), filename) != loadStack.end()) { @@ -958,9 +971,6 @@ bool CKeyBindings::Load(const std::string& filename) return false; } - const bool tmpBuildHotkeyMap = buildHotkeyMap; - buildHotkeyMap = false; - if (debugEnabled) { LOG("[CKeyBindings::%s] filename=%s%s", __func__, filename.c_str(), loadStack.empty() ? "" : ", load stack:"); for (auto it = loadStack.rbegin(); it != loadStack.rend(); ++it) @@ -973,17 +983,34 @@ bool CKeyBindings::Load(const std::string& filename) CSimpleParser parser(ifs); while (!parser.Eof()) { - ExecuteCommand(parser.GetCleanLine()); + ExecuteCommandInternal(parser.GetCleanLine()); } loadStack.pop_back(); - buildHotkeyMap = tmpBuildHotkeyMap; - return true; } +bool CKeyBindings::Load(const std::string& filename) +{ + const bool ret = LoadInternal(filename); + MaybeBuildHotkeyMap(); + return ret; +} + + +void CKeyBindings::MaybeBuildHotkeyMap() +{ + RECOIL_DETAILED_TRACY_ZONE; + if (!buildHotkeyMap) + return; + + BuildHotkeyMap(); + buildHotkeyMap = false; +} + + void CKeyBindings::BuildHotkeyMap() { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Game/UI/KeyBindings.h b/rts/Game/UI/KeyBindings.h index 04260a7993e..9caf37e6102 100644 --- a/rts/Game/UI/KeyBindings.h +++ b/rts/Game/UI/KeyBindings.h @@ -58,8 +58,16 @@ class CKeyBindings : public CommandReceiver protected: void BuildHotkeyMap(); + // rebuilds the reverse map once, and only if a binding actually changed + void MaybeBuildHotkeyMap(); void DebugActionList(const ActionList& actionList) const; + // the *Internal variants do the work but never rebuild the reverse map; + // the public Load/ExecuteCommand wrappers rebuild once at the end, so every + // outside call path rebuilds exactly once regardless of how it recurses + bool LoadInternal(const std::string& filename = DEFAULT_FILENAME); + bool ExecuteCommandInternal(const std::string& line); + void AddActionToKeyMap(KeyMap& bindings, Action& action); static bool RemoveActionFromKeyMap(const std::string& command, KeyMap& bindings); @@ -90,7 +98,7 @@ class CKeyBindings : public CommandReceiver int fakeMetaKey = -1; int keyChainTimeout = 750; - bool buildHotkeyMap = true; + bool buildHotkeyMap = true; // reverse hotkey map is stale and needs rebuilding bool debugEnabled = false; };