1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-09-30 19:22:14 +00:00

Implement secondary keybindings

This commit is contained in:
y5nw 2025-03-23 21:55:45 +01:00 committed by y5nw
parent 4f42b4308c
commit f767a7a529
7 changed files with 51 additions and 25 deletions

View file

@ -26,7 +26,7 @@ void MyEventReceiver::reloadKeybindings()
keybindings[KeyType::DIG] = getKeySetting("keymap_dig"); keybindings[KeyType::DIG] = getKeySetting("keymap_dig");
keybindings[KeyType::PLACE] = getKeySetting("keymap_place"); keybindings[KeyType::PLACE] = getKeySetting("keymap_place");
keybindings[KeyType::ESC] = EscapeKey; keybindings[KeyType::ESC] = std::vector<KeyPress>{EscapeKey};
keybindings[KeyType::AUTOFORWARD] = getKeySetting("keymap_autoforward"); 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 // First clear all keys, then re-add the ones we listen for
keysListenedFor.clear(); keysListenedFor.clear();
for (int i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) { for (int i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) {
listenForKey(keybindings[i], static_cast<GameKeyType>(i)); for (auto key: keybindings[i]) {
listenForKey(key, static_cast<GameKeyType>(i));
}
} }
} }
@ -85,13 +87,11 @@ bool MyEventReceiver::setKeyDown(KeyPress keyCode, bool is_down)
if (keysListenedFor.find(keyCode) == keysListenedFor.end()) // ignore irrelevant key input if (keysListenedFor.find(keyCode) == keysListenedFor.end()) // ignore irrelevant key input
return false; return false;
auto action = keysListenedFor[keyCode]; auto action = keysListenedFor[keyCode];
if (is_down) { if (is_down)
physicalKeyDown.insert(keyCode); physicalKeyDown.insert(keyCode);
setKeyDown(action, true); else
} else {
physicalKeyDown.erase(keyCode); physicalKeyDown.erase(keyCode);
setKeyDown(action, false); setKeyDown(action, checkKeyDown(action));
}
return true; 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) bool MyEventReceiver::OnEvent(const SEvent &event)
{ {
if (event.EventType == EET_LOG_TEXT_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) { if (event.EventType == EET_KEY_INPUT_EVENT) {
KeyPress keyCode(event.KeyInput); KeyPress keyCode(event.KeyInput);
if (keyCode == getKeySetting("keymap_fullscreen")) { if (keySettingHasMatch("keymap_fullscreen", keyCode)) {
if (event.KeyInput.PressedDown && !fullscreen_is_down) { if (event.KeyInput.PressedDown && !fullscreen_is_down) {
IrrlichtDevice *device = RenderingEngine::get_raw_device(); IrrlichtDevice *device = RenderingEngine::get_raw_device();
@ -152,7 +161,7 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
fullscreen_is_down = event.KeyInput.PressedDown; fullscreen_is_down = event.KeyInput.PressedDown;
return true; return true;
} else if (keyCode == getKeySetting("keymap_close_world")) { } else if (keySettingHasMatch("keymap_close_world", keyCode)) {
close_world_down = event.KeyInput.PressedDown; close_world_down = event.KeyInput.PressedDown;
} else if (keyCode == EscapeKey) { } else if (keyCode == EscapeKey) {

View file

@ -97,13 +97,14 @@ private:
bool setKeyDown(KeyPress keyCode, bool is_down); bool setKeyDown(KeyPress keyCode, bool is_down);
void setKeyDown(GameKeyType action, 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 /* 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 * 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 * no error handling (for example bounds checking). This is useful here as the
* faster (up to 10x faster) key lookup is an asset. * faster (up to 10x faster) key lookup is an asset.
*/ */
std::array<KeyPress, KeyType::INTERNAL_ENUM_COUNT> keybindings; std::array<std::vector<KeyPress>, KeyType::INTERNAL_ENUM_COUNT> keybindings;
s32 mouse_wheel = 0; s32 mouse_wheel = 0;

View file

@ -380,23 +380,32 @@ KeyPress KeyPress::getSpecialKey(const std::string &name)
*/ */
// A simple cache for quicker lookup // A simple cache for quicker lookup
static std::unordered_map<std::string, KeyPress> g_key_setting_cache; static std::unordered_map<std::string, std::vector<KeyPress>> g_key_setting_cache;
KeyPress getKeySetting(const std::string &settingname) const std::vector<KeyPress> &getKeySetting(const std::string &settingname)
{ {
auto n = g_key_setting_cache.find(settingname); auto n = g_key_setting_cache.find(settingname);
if (n != g_key_setting_cache.end()) if (n != g_key_setting_cache.end())
return n->second; return n->second;
auto keysym = g_settings->get(settingname); auto setting_value = g_settings->get(settingname);
auto &ref = g_key_setting_cache[settingname]; auto &ref = g_key_setting_cache[settingname];
ref = KeyPress(keysym); for (const auto &keysym: str_split(setting_value, '|')) {
if (!keysym.empty() && !ref) { if (KeyPress kp = keysym) {
warningstream << "Invalid key '" << keysym << "' for '" << settingname << "'." << std::endl; ref.push_back(kp);
} else {
warningstream << "Invalid key '" << keysym << "' for '" << settingname << "'." << std::endl;
}
} }
return ref; 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() void clearKeyCache()
{ {
g_key_setting_cache.clear(); g_key_setting_cache.clear();

View file

@ -9,6 +9,7 @@
#include <IEventReceiver.h> #include <IEventReceiver.h>
#include <string> #include <string>
#include <variant> #include <variant>
#include <vector>
/* A key press, consisting of a scancode or a keycode. /* A key press, consisting of a scancode or a keycode.
* This fits into 64 bits, so prefer passing this by value. * This fits into 64 bits, so prefer passing this by value.
@ -92,7 +93,13 @@ struct std::hash<KeyPress>
#define RMBKey KeyPress::getSpecialKey("KEY_RBUTTON") #define RMBKey KeyPress::getSpecialKey("KEY_RBUTTON")
// Key configuration getter // 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<KeyPress> &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 // Clear fast lookup cache
void clearKeyCache(); void clearKeyCache();

View file

@ -436,7 +436,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
} }
// Key input // Key input
if (KeyPress(event.KeyInput) == getKeySetting("keymap_console")) { if (keySettingHasMatch("keymap_console", event.KeyInput)) {
closeConsole(); closeConsole();
// inhibit open so the_game doesn't reopen immediately // inhibit open so the_game doesn't reopen immediately

View file

@ -3995,7 +3995,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
if (event.EventType == EET_KEY_INPUT_EVENT) { if (event.EventType == EET_KEY_INPUT_EVENT) {
KeyPress kp(event.KeyInput); KeyPress kp(event.KeyInput);
if (kp == EscapeKey if (kp == EscapeKey
|| kp == getKeySetting("keymap_inventory") || keySettingHasMatch("keymap_inventory", kp)
|| event.KeyInput.Key==KEY_RETURN) { || event.KeyInput.Key==KEY_RETURN) {
gui::IGUIElement *focused = Environment->getFocus(); gui::IGUIElement *focused = Environment->getFocus();
if (focused && isMyChild(focused) && if (focused && isMyChild(focused) &&
@ -4094,17 +4094,17 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
} }
if (event.KeyInput.PressedDown && ( if (event.KeyInput.PressedDown && (
(kp == EscapeKey) || (kp == EscapeKey) ||
((m_client != NULL) && (kp == getKeySetting("keymap_inventory"))))) { ((m_client != NULL) && (keySettingHasMatch("keymap_inventory", kp))))) {
tryClose(); tryClose();
return true; return true;
} }
if (m_client != NULL && event.KeyInput.PressedDown && if (m_client != NULL && event.KeyInput.PressedDown &&
(kp == getKeySetting("keymap_screenshot"))) { (keySettingHasMatch("keymap_screenshot", kp))) {
m_client->makeScreenshot(); 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")) if (!m_client || m_client->checkPrivilege("debug"))
m_show_debug = !m_show_debug; m_show_debug = !m_show_debug;
} }

View file

@ -212,11 +212,11 @@ static KeyPress id_to_keypress(touch_gui_button_id id)
auto setting_name = id_to_setting(id); auto setting_name = id_to_setting(id);
assert(!setting_name.empty()); assert(!setting_name.empty());
auto kp = getKeySetting(setting_name); const auto &keylist = getKeySetting(setting_name);
if (!kp) if (keylist.empty())
warningstream << "TouchControls: Unbound or invalid key for " warningstream << "TouchControls: Unbound or invalid key for "
<< setting_name << ", hiding button." << std::endl; << setting_name << ", hiding button." << std::endl;
return kp; return keylist[0];
} }