diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index 52e73356cb..8b62c74c2a 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -26,7 +26,7 @@ void MyEventReceiver::reloadKeybindings() keybindings[KeyType::DIG] = getKeySetting("keymap_dig"); keybindings[KeyType::PLACE] = getKeySetting("keymap_place"); - keybindings[KeyType::ESC] = EscapeKey; + keybindings[KeyType::ESC] = std::vector{EscapeKey}; keybindings[KeyType::AUTOFORWARD] = getKeySetting("keymap_autoforward"); @@ -76,7 +76,9 @@ void MyEventReceiver::reloadKeybindings() // First clear all keys, then re-add the ones we listen for keysListenedFor.clear(); for (int i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) { - listenForKey(keybindings[i], static_cast(i)); + for (auto key: keybindings[i]) { + listenForKey(key, static_cast(i)); + } } } @@ -85,13 +87,11 @@ bool MyEventReceiver::setKeyDown(KeyPress keyCode, bool is_down) if (keysListenedFor.find(keyCode) == keysListenedFor.end()) // ignore irrelevant key input return false; auto action = keysListenedFor[keyCode]; - if (is_down) { + if (is_down) physicalKeyDown.insert(keyCode); - setKeyDown(action, true); - } else { + else physicalKeyDown.erase(keyCode); - setKeyDown(action, false); - } + setKeyDown(action, checkKeyDown(action)); return true; } @@ -109,6 +109,15 @@ void MyEventReceiver::setKeyDown(GameKeyType action, bool is_down) } } +bool MyEventReceiver::checkKeyDown(GameKeyType action) +{ + for (auto key: keybindings[action]) { + if (physicalKeyDown.find(key) != physicalKeyDown.end()) + return true; + } + return false; +} + bool MyEventReceiver::OnEvent(const SEvent &event) { if (event.EventType == EET_LOG_TEXT_EVENT) { @@ -138,7 +147,7 @@ bool MyEventReceiver::OnEvent(const SEvent &event) if (event.EventType == EET_KEY_INPUT_EVENT) { KeyPress keyCode(event.KeyInput); - if (keyCode == getKeySetting("keymap_fullscreen")) { + if (keySettingHasMatch("keymap_fullscreen", keyCode)) { if (event.KeyInput.PressedDown && !fullscreen_is_down) { IrrlichtDevice *device = RenderingEngine::get_raw_device(); @@ -152,7 +161,7 @@ bool MyEventReceiver::OnEvent(const SEvent &event) fullscreen_is_down = event.KeyInput.PressedDown; return true; - } else if (keyCode == getKeySetting("keymap_close_world")) { + } else if (keySettingHasMatch("keymap_close_world", keyCode)) { close_world_down = event.KeyInput.PressedDown; } else if (keyCode == EscapeKey) { diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index ce2c41a637..e273f65a0e 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -97,13 +97,14 @@ private: bool setKeyDown(KeyPress keyCode, bool is_down); void setKeyDown(GameKeyType action, bool is_down); + bool checkKeyDown(GameKeyType action); /* This is faster than using getKeySetting with the tradeoff that functions * using it must make sure that it's initialised before using it and there is * no error handling (for example bounds checking). This is useful here as the * faster (up to 10x faster) key lookup is an asset. */ - std::array keybindings; + std::array, KeyType::INTERNAL_ENUM_COUNT> keybindings; s32 mouse_wheel = 0; diff --git a/src/client/keycode.cpp b/src/client/keycode.cpp index e21bc6308c..eff198c1aa 100644 --- a/src/client/keycode.cpp +++ b/src/client/keycode.cpp @@ -380,23 +380,32 @@ KeyPress KeyPress::getSpecialKey(const std::string &name) */ // A simple cache for quicker lookup -static std::unordered_map g_key_setting_cache; +static std::unordered_map> g_key_setting_cache; -KeyPress getKeySetting(const std::string &settingname) +const std::vector &getKeySetting(const std::string &settingname) { auto n = g_key_setting_cache.find(settingname); if (n != g_key_setting_cache.end()) return n->second; - auto keysym = g_settings->get(settingname); + auto setting_value = g_settings->get(settingname); auto &ref = g_key_setting_cache[settingname]; - ref = KeyPress(keysym); - if (!keysym.empty() && !ref) { - warningstream << "Invalid key '" << keysym << "' for '" << settingname << "'." << std::endl; + for (const auto &keysym: str_split(setting_value, '|')) { + if (KeyPress kp = keysym) { + ref.push_back(kp); + } else { + warningstream << "Invalid key '" << keysym << "' for '" << settingname << "'." << std::endl; + } } return ref; } +bool keySettingHasMatch(const std::string &settingname, KeyPress kp) +{ + const auto &keylist = getKeySetting(settingname); + return std::find(keylist.begin(), keylist.end(), kp) != keylist.end(); +} + void clearKeyCache() { g_key_setting_cache.clear(); diff --git a/src/client/keycode.h b/src/client/keycode.h index a62e7822cd..23608938f1 100644 --- a/src/client/keycode.h +++ b/src/client/keycode.h @@ -9,6 +9,7 @@ #include #include #include +#include /* A key press, consisting of a scancode or a keycode. * This fits into 64 bits, so prefer passing this by value. @@ -92,7 +93,13 @@ struct std::hash #define RMBKey KeyPress::getSpecialKey("KEY_RBUTTON") // Key configuration getter -KeyPress getKeySetting(const std::string &settingname); +// Note that the reference may be invalidated by a next call to getKeySetting +// or a related function, so the value should either be used immediately or +// copied elsewhere before calling this again. +const std::vector &getKeySetting(const std::string &settingname); + +// Check whether the key setting includes a key. +bool keySettingHasMatch(const std::string &settingname, KeyPress kp); // Clear fast lookup cache void clearKeyCache(); diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp index db90368cc3..64b9b18d48 100644 --- a/src/gui/guiChatConsole.cpp +++ b/src/gui/guiChatConsole.cpp @@ -436,7 +436,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event) } // Key input - if (KeyPress(event.KeyInput) == getKeySetting("keymap_console")) { + if (keySettingHasMatch("keymap_console", event.KeyInput)) { closeConsole(); // inhibit open so the_game doesn't reopen immediately diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 7d38376897..4604554691 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3995,7 +3995,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) if (event.EventType == EET_KEY_INPUT_EVENT) { KeyPress kp(event.KeyInput); if (kp == EscapeKey - || kp == getKeySetting("keymap_inventory") + || keySettingHasMatch("keymap_inventory", kp) || event.KeyInput.Key==KEY_RETURN) { gui::IGUIElement *focused = Environment->getFocus(); if (focused && isMyChild(focused) && @@ -4094,17 +4094,17 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } if (event.KeyInput.PressedDown && ( (kp == EscapeKey) || - ((m_client != NULL) && (kp == getKeySetting("keymap_inventory"))))) { + ((m_client != NULL) && (keySettingHasMatch("keymap_inventory", kp))))) { tryClose(); return true; } if (m_client != NULL && event.KeyInput.PressedDown && - (kp == getKeySetting("keymap_screenshot"))) { + (keySettingHasMatch("keymap_screenshot", kp))) { m_client->makeScreenshot(); } - if (event.KeyInput.PressedDown && kp == getKeySetting("keymap_toggle_debug")) { + if (event.KeyInput.PressedDown && keySettingHasMatch("keymap_toggle_debug", kp)) { if (!m_client || m_client->checkPrivilege("debug")) m_show_debug = !m_show_debug; } diff --git a/src/gui/touchcontrols.cpp b/src/gui/touchcontrols.cpp index 6a3aeb108b..c6a6d8b88e 100644 --- a/src/gui/touchcontrols.cpp +++ b/src/gui/touchcontrols.cpp @@ -212,11 +212,11 @@ static KeyPress id_to_keypress(touch_gui_button_id id) auto setting_name = id_to_setting(id); assert(!setting_name.empty()); - auto kp = getKeySetting(setting_name); - if (!kp) + const auto &keylist = getKeySetting(setting_name); + if (keylist.empty()) warningstream << "TouchControls: Unbound or invalid key for " << setting_name << ", hiding button." << std::endl; - return kp; + return keylist[0]; }