mirror of
https://github.com/luanti-org/luanti.git
synced 2025-09-30 19:22:14 +00:00
Merge afceefc985
into 274d8a7c65
This commit is contained in:
commit
e5fd709dc5
19 changed files with 411 additions and 179 deletions
|
@ -432,34 +432,21 @@ end
|
|||
|
||||
function make.key(setting)
|
||||
local btn_bind = "bind_" .. setting.name
|
||||
local btn_clear = "unbind_" .. setting.name
|
||||
local btn_edit = "edit_" .. setting.name
|
||||
local function add_conflict_warnings(fs, height)
|
||||
local value = core.settings:get(setting.name)
|
||||
local value = core.settings:get(setting.name):split("|")
|
||||
if value == "" then
|
||||
return height
|
||||
end
|
||||
|
||||
local critical_keys = {
|
||||
keymap_drop = true,
|
||||
keymap_dig = true,
|
||||
keymap_place = true,
|
||||
}
|
||||
|
||||
for _, o in ipairs(core.full_settingtypes) do
|
||||
if o.type == "key" and o.name ~= setting.name and
|
||||
core.are_keycodes_equal(core.settings:get(o.name), value) then
|
||||
is_keybinding_critical(setting.name, o.name) and
|
||||
has_keybinding_conflict(core.settings:get(o.name):split("|"), value) then
|
||||
|
||||
local is_current_close_world = setting.name == "keymap_close_world"
|
||||
local is_other_close_world = o.name == "keymap_close_world"
|
||||
local is_current_critical = critical_keys[setting.name]
|
||||
local is_other_critical = critical_keys[o.name]
|
||||
|
||||
if (is_other_critical or is_current_critical) or
|
||||
(not is_current_close_world and not is_other_close_world) then
|
||||
table.insert(fs, ("label[0,%f;%s]"):format(height + 0.3,
|
||||
core.colorize(mt_color_orange, fgettext([[Conflicts with "$1"]], fgettext(o.readable_name)))))
|
||||
height = height + 0.6
|
||||
end
|
||||
table.insert(fs, ("label[0,%f;%s]"):format(height + 0.3,
|
||||
core.colorize(mt_color_orange, fgettext([[Conflicts with "$1"]], fgettext(o.readable_name)))))
|
||||
height = height + 0.6
|
||||
end
|
||||
end
|
||||
return height
|
||||
|
@ -471,30 +458,32 @@ function make.key(setting)
|
|||
|
||||
get_formspec = function(self, avail_w)
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
local btn_bind_width = math.max(2.5, avail_w / 2)
|
||||
local value = core.settings:get(setting.name)
|
||||
local btn_width = math.max(2.5, avail_w / 2)
|
||||
local value = core.settings:get(setting.name):split("|")
|
||||
local fs = {
|
||||
("label[0,0.4;%s]"):format(get_label(setting)),
|
||||
("button_key[%f,0;%f,0.8;%s;%s]"):format(
|
||||
btn_bind_width, btn_bind_width - 0.8,
|
||||
btn_bind, core.formspec_escape(value)),
|
||||
("image_button[%f,0;0.8,0.8;%s;%s;]"):format(avail_w - 0.8,
|
||||
core.formspec_escape(defaulttexturedir .. "clear.png"),
|
||||
btn_clear),
|
||||
("tooltip[%s;%s]"):format(btn_clear, fgettext("Remove keybinding")),
|
||||
("tooltip[%s;%s]"):format(btn_edit, fgettext("Edit keybindings")),
|
||||
}
|
||||
if #value <= 1 then
|
||||
table.insert(fs, ("button_key[%f,0;%f,0.8;%s;%s]"):format(
|
||||
btn_width, btn_width-0.8, btn_bind, value[1] or ""))
|
||||
table.insert(fs, ("image_button[%f,0;0.8,0.8;%s;%s;]"):format(avail_w - 0.8,
|
||||
core.formspec_escape(defaulttexturedir.."overflow_btn.png"), btn_edit))
|
||||
else
|
||||
table.insert(fs, ("button[%f,0;%f,0.8;%s;%s]"):format(
|
||||
btn_width, btn_width, btn_edit, fgettext("Edit")))
|
||||
end
|
||||
local height = 0.8
|
||||
height = add_conflict_warnings(fs, height)
|
||||
return table.concat(fs), height
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields)
|
||||
on_submit = function(self, fields, tabview)
|
||||
if fields[btn_bind] then
|
||||
core.settings:set(setting.name, fields[btn_bind])
|
||||
return true
|
||||
elseif fields[btn_clear] then
|
||||
core.settings:set(setting.name, "")
|
||||
return true
|
||||
elseif fields[btn_edit] then
|
||||
return show_change_keybinding_dlg(setting, tabview)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
|
173
builtin/common/settings/dlg_change_keybinding.lua
Normal file
173
builtin/common/settings/dlg_change_keybinding.lua
Normal file
|
@ -0,0 +1,173 @@
|
|||
-- Luanti
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
local function get_formspec(dialogdata)
|
||||
local name = dialogdata.setting.name
|
||||
local readable_name = name
|
||||
if dialogdata.setting.readable_name then
|
||||
readable_name = ("%s (%s)"):format(fgettext(dialogdata.setting.readable_name), name)
|
||||
end
|
||||
|
||||
local value = dialogdata.value
|
||||
local selection = dialogdata.selection
|
||||
|
||||
local fs = {
|
||||
"formspec_version[4]",
|
||||
"size[6,9]",
|
||||
("label[0.5,0.8;%s]"):format(readable_name),
|
||||
("button[0.5,5.7;5,0.8;btn_add;%s]"):format(fgettext("Add keybinding")),
|
||||
("button_key[0.5,6.7;4.2,0.8;btn_bind;%s]"):format(core.formspec_escape(value[selection] or "")),
|
||||
("image_button[4.7,6.7;0.8,0.8;%s;btn_clear;]"):format(
|
||||
core.formspec_escape(defaulttexturedir .. "clear.png")),
|
||||
("tooltip[btn_clear;%s]"):format(fgettext("Remove keybinding")),
|
||||
("button[3.1,7.7;2.4,0.8;btn_close;%s]"):format(fgettext("Cancel")),
|
||||
("button[0.5,7.7;2.4,0.8;btn_save;%s]"):format(fgettext("Save")),
|
||||
}
|
||||
|
||||
local warning = ""
|
||||
local cells = {}
|
||||
for idx, key in ipairs(value) do
|
||||
local prefix = ""
|
||||
for _, o in ipairs(core.full_settingtypes) do
|
||||
if o.type == "key" and o.name ~= name and
|
||||
is_keybinding_critical(name, o.name) and
|
||||
has_keybinding_conflict(core.settings:get(o.name):split("|"), key) then
|
||||
prefix = mt_color_orange
|
||||
if idx == selection then
|
||||
warning = core.colorize(mt_color_orange, fgettext([[Conflicts with "$1"]], fgettext_ne(o.readable_name)))
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
table.insert(cells, core.formspec_escape(prefix .. core.get_keycode_name(key)))
|
||||
end
|
||||
table.insert(fs, ("textlist[0.5,1.3;5,3.8;keylist;%s;%d;false]"):format(table.concat(cells, ","), selection))
|
||||
table.insert(fs, ("label[0.5,5.4;%s]"):format(warning))
|
||||
|
||||
return table.concat(fs)
|
||||
end
|
||||
|
||||
local function buttonhandler(self, fields)
|
||||
local name = self.data.setting.name
|
||||
if fields.quit or fields.btn_close then
|
||||
self:delete()
|
||||
return true
|
||||
elseif fields.btn_save then
|
||||
local value = {}
|
||||
for _, v in ipairs(self.data.value) do
|
||||
if v ~= "" then -- filter out "empty" keybindings
|
||||
table.insert(value, v)
|
||||
end
|
||||
end
|
||||
core.settings:set(name, table.concat(value, "|"))
|
||||
self:delete()
|
||||
return true
|
||||
elseif fields.btn_clear then
|
||||
local selection = self.data.selection
|
||||
table.remove(self.data.value, selection)
|
||||
self.data.selection = math.max(1, math.min(selection, #self.data.value))
|
||||
return true
|
||||
elseif fields.btn_add then
|
||||
table.insert(self.data.value, "")
|
||||
self.data.selection = #self.data.value
|
||||
return true
|
||||
elseif fields.btn_bind then
|
||||
self.data.value[self.data.selection] = fields.btn_bind
|
||||
return true
|
||||
elseif fields.keylist then
|
||||
local event = core.explode_textlist_event(fields.keylist)
|
||||
if event.type == "CHG" or event.type == "DCL" then
|
||||
self.data.selection = event.index
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local formspec_handlers = {}
|
||||
if INIT == "pause_menu" then
|
||||
core.register_on_formspec_input(function(formname, fields)
|
||||
if formspec_handlers[formname] then
|
||||
formspec_handlers[formname](fields)
|
||||
return true
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local is_mainmenu = INIT == "mainmenu"
|
||||
function show_change_keybinding_dlg(setting, tabview)
|
||||
local dlg
|
||||
if is_mainmenu then
|
||||
dlg = dialog_create("dlg_change_keybinding",
|
||||
get_formspec, buttonhandler)
|
||||
else
|
||||
local name = "__builtin:rebind_" .. setting.name
|
||||
dlg = {
|
||||
show = function()
|
||||
if dlg.removed then
|
||||
--core.open_settings("controls_keyboard_and_mouse")
|
||||
tabview:show()
|
||||
else
|
||||
core.show_formspec(name, get_formspec(dlg.data))
|
||||
end
|
||||
end,
|
||||
delete = function()
|
||||
core.show_formspec(name, "")
|
||||
formspec_handlers[name] = nil
|
||||
dlg.removed = true
|
||||
end,
|
||||
data = {},
|
||||
}
|
||||
formspec_handlers[name] = function(fields)
|
||||
if buttonhandler(dlg, fields) then
|
||||
dlg:show()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
dlg.data.setting = setting
|
||||
dlg.data.value = core.settings:get(setting.name):split("|")
|
||||
dlg.data.selection = 1
|
||||
|
||||
if is_mainmenu then
|
||||
dlg:set_parent(tabview)
|
||||
tabview:hide()
|
||||
end
|
||||
dlg:show()
|
||||
|
||||
return is_mainmenu
|
||||
end
|
||||
|
||||
function has_keybinding_conflict(t1, t2) -- not local as it is also used by the make.key component
|
||||
if type(t2) == "string" then
|
||||
if type(t1) == "string" then
|
||||
return core.are_keycodes_equal(t1, t2)
|
||||
else
|
||||
for _, v1 in pairs(t1) do
|
||||
if core.are_keycodes_equal(v1, t2) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
for _, v2 in pairs(t2) do
|
||||
if has_keybinding_conflict(t1, v2) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local critical_keys = {
|
||||
keymap_drop = true,
|
||||
keymap_dig = true,
|
||||
keymap_place = true,
|
||||
}
|
||||
function is_keybinding_critical(n1, n2)
|
||||
local is_current_close_world = n1 == "keymap_close_world"
|
||||
local is_other_close_world = n2 == "keymap_close_world"
|
||||
local is_current_critical = critical_keys[n1]
|
||||
local is_other_critical = critical_keys[n2]
|
||||
return (is_other_critical or is_current_critical) or
|
||||
(not is_current_close_world and not is_other_close_world)
|
||||
end
|
|
@ -814,7 +814,9 @@ else
|
|||
-- case it's a no-op
|
||||
core.show_formspec("__builtin:settings", "")
|
||||
end
|
||||
|
||||
core.show_formspec("__builtin:settings", get_formspec(dialog.data))
|
||||
dialog.show = function() -- Used by the keybinding form
|
||||
core.show_formspec("__builtin:settings", get_formspec(dialog.data))
|
||||
end
|
||||
dialog:show()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ local path = core.get_builtin_path() .. "common" .. DIR_DELIM .. "settings" .. D
|
|||
|
||||
dofile(path .. "settingtypes.lua")
|
||||
dofile(path .. "dlg_change_mapgen_flags.lua")
|
||||
dofile(path .. "dlg_change_keybinding.lua")
|
||||
dofile(path .. "dlg_settings.lua")
|
||||
|
||||
-- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'.
|
||||
|
|
|
@ -74,6 +74,17 @@ local function create_rebind_keys_dlg()
|
|||
return dlg
|
||||
end
|
||||
|
||||
local function normalize_key_setting(str)
|
||||
if str == "|" then
|
||||
return core.normalize_keycode(str)
|
||||
end
|
||||
local t = string.split(str, "|")
|
||||
for k, v in pairs(t) do
|
||||
t[k] = core.normalize_keycode(v)
|
||||
end
|
||||
return table.concat(t, "|")
|
||||
end
|
||||
|
||||
function migrate_keybindings(parent)
|
||||
-- Show migration dialog if the user upgraded from an earlier version
|
||||
-- and this has not yet been shown before, *or* if keys settings had to be changed
|
||||
|
@ -86,7 +97,7 @@ function migrate_keybindings(parent)
|
|||
local settings = core.settings:to_table()
|
||||
for name, value in pairs(settings) do
|
||||
if name:match("^keymap_") then
|
||||
local normalized = core.normalize_keycode(value)
|
||||
local normalized = normalize_key_setting(value)
|
||||
if value ~= normalized then
|
||||
has_migration = true
|
||||
core.settings:set(name, normalized)
|
||||
|
|
|
@ -86,6 +86,17 @@ enum EEVENT_TYPE
|
|||
|
||||
};
|
||||
|
||||
//! Enumeration for "user" event types.
|
||||
enum UserEventType: s32
|
||||
{
|
||||
//! In-game touch buttons.
|
||||
/** This event is currently only fired by the in-game touch controller.
|
||||
UserData1: The GameKeyType of the button.
|
||||
UserData2: Whether the button is pressed or released.
|
||||
*/
|
||||
USER_EVENT_GAME_KEY = 1
|
||||
};
|
||||
|
||||
//! Enumeration for all mouse input events
|
||||
enum EMOUSE_INPUT_EVENT
|
||||
{
|
||||
|
@ -510,6 +521,9 @@ struct SEvent
|
|||
//! Any kind of user event.
|
||||
struct SUserEvent
|
||||
{
|
||||
//! Event code
|
||||
s32 code;
|
||||
|
||||
//! Some user specified data as int
|
||||
size_t UserData1;
|
||||
|
||||
|
|
|
@ -912,6 +912,7 @@ bool CIrrDeviceLinux::run()
|
|||
} else {
|
||||
// we assume it's a user message
|
||||
irrevent.EventType = EET_USER_EVENT;
|
||||
irrevent.UserEvent.code = 0;
|
||||
irrevent.UserEvent.UserData1 = static_cast<size_t>(event.xclient.data.l[0]);
|
||||
irrevent.UserEvent.UserData2 = static_cast<size_t>(event.xclient.data.l[1]);
|
||||
postEventFromUser(irrevent);
|
||||
|
|
|
@ -945,6 +945,7 @@ bool CIrrDeviceSDL::run()
|
|||
|
||||
case SDL_USEREVENT:
|
||||
irrevent.EventType = EET_USER_EVENT;
|
||||
irrevent.UserEvent.code = SDL_event.user.code;
|
||||
irrevent.UserEvent.UserData1 = reinterpret_cast<uintptr_t>(SDL_event.user.data1);
|
||||
irrevent.UserEvent.UserData2 = reinterpret_cast<uintptr_t>(SDL_event.user.data2);
|
||||
|
||||
|
|
|
@ -675,6 +675,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||
|
||||
case WM_USER:
|
||||
event.EventType = EET_USER_EVENT;
|
||||
event.UserEvent.code = 0;
|
||||
event.UserEvent.UserData1 = static_cast<size_t>(wParam);
|
||||
event.UserEvent.UserData2 = static_cast<size_t>(lParam);
|
||||
dev = getDeviceFromHWnd(hWnd);
|
||||
|
|
|
@ -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<KeyPress>{EscapeKey};
|
||||
|
||||
keybindings[KeyType::AUTOFORWARD] = getKeySetting("keymap_autoforward");
|
||||
|
||||
|
@ -76,7 +76,11 @@ 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<GameKeyType>(i));
|
||||
GameKeyType game_key = static_cast<GameKeyType>(i);
|
||||
keybindings[i].emplace_back(game_key);
|
||||
for (auto key: keybindings[i]) {
|
||||
listenForKey(key, game_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,13 +89,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 +111,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 +149,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 +163,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) {
|
||||
|
@ -216,6 +227,10 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
} else if (event.EventType == EET_USER_EVENT && event.UserEvent.code == USER_EVENT_GAME_KEY) {
|
||||
KeyPress keyCode(static_cast<GameKeyType>(event.UserEvent.UserData1));
|
||||
setKeyDown(keyCode, event.UserEvent.UserData2);
|
||||
return true;
|
||||
}
|
||||
|
||||
// tell Irrlicht to continue processing this event
|
||||
|
|
|
@ -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<KeyPress, KeyType::INTERNAL_ENUM_COUNT> keybindings;
|
||||
std::array<std::vector<KeyPress>, KeyType::INTERNAL_ENUM_COUNT> keybindings;
|
||||
|
||||
s32 mouse_wheel = 0;
|
||||
|
||||
|
|
|
@ -13,13 +13,6 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
struct table_key {
|
||||
std::string Name; // An EKEY_CODE 'symbol' name as a string
|
||||
EKEY_CODE Key;
|
||||
wchar_t Char; // L'\0' means no character assigned
|
||||
std::string LangName; // empty string means it doesn't have a human description
|
||||
};
|
||||
|
||||
#define DEFINEKEY1(x, lang) /* Irrlicht key without character */ \
|
||||
{ #x, x, L'\0', lang },
|
||||
#define DEFINEKEY2(x, ch, lang) /* Irrlicht key with character */ \
|
||||
|
@ -33,7 +26,7 @@ struct table_key {
|
|||
|
||||
#define N_(text) text
|
||||
|
||||
static std::vector<table_key> table = {
|
||||
std::vector<KeyPress::table_key> KeyPress::keycode_table = {
|
||||
// Keys that can be reliably mapped between Char and Key
|
||||
DEFINEKEY3(0)
|
||||
DEFINEKEY3(1)
|
||||
|
@ -224,18 +217,17 @@ static std::vector<table_key> table = {
|
|||
DEFINEKEY5("^")
|
||||
DEFINEKEY5("_")
|
||||
};
|
||||
|
||||
static const table_key invalid_key = {"", KEY_UNKNOWN, L'\0', ""};
|
||||
const KeyPress::table_key KeyPress::invalid_key = {"", KEY_UNKNOWN, L'\0', ""};
|
||||
|
||||
#undef N_
|
||||
|
||||
|
||||
static const table_key &lookup_keychar(wchar_t Char)
|
||||
const KeyPress::table_key &KeyPress::lookupKeychar(wchar_t Char)
|
||||
{
|
||||
if (Char == L'\0')
|
||||
return invalid_key;
|
||||
|
||||
for (const auto &table_key : table) {
|
||||
for (const auto &table_key : keycode_table) {
|
||||
if (table_key.Char == Char)
|
||||
return table_key;
|
||||
}
|
||||
|
@ -243,15 +235,15 @@ static const table_key &lookup_keychar(wchar_t Char)
|
|||
// Create a new entry in the lookup table if one is not available.
|
||||
auto newsym = wide_to_utf8(std::wstring_view(&Char, 1));
|
||||
table_key new_key {newsym, KEY_KEY_CODES_COUNT, Char, newsym};
|
||||
return table.emplace_back(std::move(new_key));
|
||||
return keycode_table.emplace_back(std::move(new_key));
|
||||
}
|
||||
|
||||
static const table_key &lookup_keykey(EKEY_CODE key)
|
||||
const KeyPress::table_key &KeyPress::lookupKeykey(EKEY_CODE key)
|
||||
{
|
||||
if (!Keycode::isValid(key))
|
||||
return invalid_key;
|
||||
|
||||
for (const auto &table_key : table) {
|
||||
for (const auto &table_key : keycode_table) {
|
||||
if (table_key.Key == key)
|
||||
return table_key;
|
||||
}
|
||||
|
@ -259,12 +251,12 @@ static const table_key &lookup_keykey(EKEY_CODE key)
|
|||
return invalid_key;
|
||||
}
|
||||
|
||||
static const table_key &lookup_keyname(std::string_view name)
|
||||
const KeyPress::table_key &KeyPress::lookupKeyname(std::string_view name)
|
||||
{
|
||||
if (name.empty())
|
||||
return invalid_key;
|
||||
|
||||
for (const auto &table_key : table) {
|
||||
for (const auto &table_key : keycode_table) {
|
||||
if (table_key.Name == name)
|
||||
return table_key;
|
||||
}
|
||||
|
@ -272,34 +264,43 @@ static const table_key &lookup_keyname(std::string_view name)
|
|||
auto wname = utf8_to_wide(name);
|
||||
if (wname.empty())
|
||||
return invalid_key;
|
||||
return lookup_keychar(wname[0]);
|
||||
return lookupKeychar(wname[0]);
|
||||
}
|
||||
|
||||
static const table_key &lookup_scancode(const u32 scancode)
|
||||
const KeyPress::table_key &KeyPress::lookupScancode(const u32 scancode)
|
||||
{
|
||||
auto key = RenderingEngine::get_raw_device()->getKeyFromScancode(scancode);
|
||||
return std::holds_alternative<EKEY_CODE>(key) ?
|
||||
lookup_keykey(std::get<EKEY_CODE>(key)) :
|
||||
lookup_keychar(std::get<wchar_t>(key));
|
||||
lookupKeykey(std::get<EKEY_CODE>(key)) :
|
||||
lookupKeychar(std::get<wchar_t>(key));
|
||||
}
|
||||
|
||||
static const table_key &lookup_scancode(const std::variant<u32, EKEY_CODE> &scancode)
|
||||
const KeyPress::table_key &KeyPress::lookupScancode() const
|
||||
{
|
||||
return std::holds_alternative<EKEY_CODE>(scancode) ?
|
||||
lookup_keykey(std::get<EKEY_CODE>(scancode)) :
|
||||
lookup_scancode(std::get<u32>(scancode));
|
||||
switch (getType()) {
|
||||
case KeyPress::KEYCODE_INPUT:
|
||||
return lookupKeykey(std::get<EKEY_CODE>(scancode));
|
||||
case KeyPress::SCANCODE_INPUT:
|
||||
return lookupScancode(std::get<u32>(scancode));
|
||||
default:
|
||||
return invalid_key;
|
||||
}
|
||||
}
|
||||
|
||||
void KeyPress::loadFromKey(EKEY_CODE keycode, wchar_t keychar)
|
||||
{
|
||||
scancode = RenderingEngine::get_raw_device()->getScancodeFromKey(Keycode(keycode, keychar));
|
||||
auto irr_scancode = RenderingEngine::get_raw_device()->getScancodeFromKey(Keycode(keycode, keychar));
|
||||
if (std::holds_alternative<EKEY_CODE>(irr_scancode))
|
||||
scancode.emplace<EKEY_CODE>(std::get<EKEY_CODE>(irr_scancode));
|
||||
else
|
||||
scancode.emplace<u32>(std::get<u32>(irr_scancode));
|
||||
}
|
||||
|
||||
KeyPress::KeyPress(const std::string &name)
|
||||
{
|
||||
if (loadFromScancode(name))
|
||||
return;
|
||||
const auto &key = lookup_keyname(name);
|
||||
const auto &key = lookupKeyname(name);
|
||||
loadFromKey(key.Key, key.Char);
|
||||
}
|
||||
|
||||
|
@ -326,7 +327,7 @@ std::string KeyPress::formatScancode() const
|
|||
|
||||
std::string KeyPress::sym() const
|
||||
{
|
||||
std::string name = lookup_scancode(scancode).Name;
|
||||
std::string name = lookupScancode().Name;
|
||||
if (USE_SDL2 || name.empty())
|
||||
if (auto newname = formatScancode(); !newname.empty())
|
||||
return newname;
|
||||
|
@ -335,7 +336,7 @@ std::string KeyPress::sym() const
|
|||
|
||||
std::string KeyPress::name() const
|
||||
{
|
||||
const auto &name = lookup_scancode(scancode).LangName;
|
||||
const auto &name = lookupScancode().LangName;
|
||||
if (!name.empty())
|
||||
return name;
|
||||
return formatScancode();
|
||||
|
@ -343,12 +344,12 @@ std::string KeyPress::name() const
|
|||
|
||||
EKEY_CODE KeyPress::getKeycode() const
|
||||
{
|
||||
return lookup_scancode(scancode).Key;
|
||||
return lookupScancode().Key;
|
||||
}
|
||||
|
||||
wchar_t KeyPress::getKeychar() const
|
||||
{
|
||||
return lookup_scancode(scancode).Char;
|
||||
return lookupScancode().Char;
|
||||
}
|
||||
|
||||
bool KeyPress::loadFromScancode(const std::string &name)
|
||||
|
@ -376,28 +377,51 @@ KeyPress KeyPress::getSpecialKey(const std::string &name)
|
|||
return key;
|
||||
}
|
||||
|
||||
KeyPress::operator bool() const
|
||||
{
|
||||
switch (getType()) {
|
||||
case KeyPress::SCANCODE_INPUT:
|
||||
return std::get<u32>(scancode) != 0;
|
||||
case KeyPress::KEYCODE_INPUT:
|
||||
return Keycode::isValid(std::get<EKEY_CODE>(scancode));
|
||||
case KeyPress::GAME_ACTION_INPUT:
|
||||
return std::get<GameKeyType>(scancode) < KeyType::INTERNAL_ENUM_COUNT;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Key config
|
||||
*/
|
||||
|
||||
// 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);
|
||||
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();
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "irrlichttypes.h"
|
||||
#include "keys.h"
|
||||
#include <Keycodes.h>
|
||||
#include <IEventReceiver.h>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
/* A key press, consisting of a scancode or a keycode.
|
||||
* This fits into 64 bits, so prefer passing this by value.
|
||||
|
@ -16,12 +18,20 @@
|
|||
class KeyPress
|
||||
{
|
||||
public:
|
||||
enum InputType {
|
||||
SCANCODE_INPUT, // Keyboard input (scancodes)
|
||||
KEYCODE_INPUT, // (Deprecated) EKEY_CODE-based keyboard and mouse input
|
||||
GAME_ACTION_INPUT, // GameKeyType input passed by touchscreen buttons
|
||||
};
|
||||
|
||||
KeyPress() = default;
|
||||
|
||||
KeyPress(const std::string &name);
|
||||
|
||||
KeyPress(const SEvent::SKeyInput &in);
|
||||
|
||||
KeyPress(GameKeyType key) : scancode(key) {}
|
||||
|
||||
// Get a string representation that is suitable for use in minetest.conf
|
||||
std::string sym() const;
|
||||
|
||||
|
@ -54,18 +64,32 @@ public:
|
|||
return scancode < o.scancode;
|
||||
}
|
||||
|
||||
// Check whether the keypress is valid
|
||||
operator bool() const
|
||||
{
|
||||
return std::holds_alternative<EKEY_CODE>(scancode) ?
|
||||
Keycode::isValid(std::get<EKEY_CODE>(scancode)) :
|
||||
std::get<u32>(scancode) != 0;
|
||||
InputType getType() const {
|
||||
return static_cast<InputType>(scancode.index());
|
||||
}
|
||||
|
||||
// Check whether the keypress is valid
|
||||
operator bool() const;
|
||||
|
||||
static KeyPress getSpecialKey(const std::string &name);
|
||||
|
||||
private:
|
||||
using value_type = std::variant<u32, EKEY_CODE>;
|
||||
struct table_key { // internal keycode lookup table
|
||||
std::string Name; // An EKEY_CODE 'symbol' name as a string
|
||||
EKEY_CODE Key;
|
||||
wchar_t Char; // L'\0' means no character assigned
|
||||
std::string LangName; // empty string means it doesn't have a human description
|
||||
};
|
||||
static const table_key invalid_key;
|
||||
static std::vector<table_key> keycode_table;
|
||||
static const table_key &lookupKeychar(wchar_t Char);
|
||||
static const table_key &lookupKeykey(EKEY_CODE key);
|
||||
static const table_key &lookupKeyname(std::string_view name);
|
||||
static const table_key &lookupScancode(const u32 scancode);
|
||||
const table_key &lookupScancode() const;
|
||||
|
||||
using value_type = std::variant<u32, EKEY_CODE, GameKeyType>;
|
||||
|
||||
bool loadFromScancode(const std::string &name);
|
||||
void loadFromKey(EKEY_CODE keycode, wchar_t keychar);
|
||||
std::string formatScancode() const;
|
||||
|
@ -92,7 +116,13 @@ struct std::hash<KeyPress>
|
|||
#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<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
|
||||
void clearKeyCache();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -3985,7 +3985,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) &&
|
||||
|
@ -4084,17 +4084,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;
|
||||
}
|
||||
|
|
|
@ -31,16 +31,13 @@
|
|||
|
||||
TouchControls *g_touchcontrols;
|
||||
|
||||
void TouchControls::emitKeyboardEvent(KeyPress key, bool pressed)
|
||||
void TouchControls::emitGameKeyEvent(GameKeyType key, bool pressed)
|
||||
{
|
||||
SEvent e{};
|
||||
e.EventType = EET_KEY_INPUT_EVENT;
|
||||
e.KeyInput.Key = key.getKeycode();
|
||||
e.KeyInput.Control = false;
|
||||
e.KeyInput.Shift = false;
|
||||
e.KeyInput.Char = key.getKeychar();
|
||||
e.KeyInput.SystemKeyCode = key.getScancode();
|
||||
e.KeyInput.PressedDown = pressed;
|
||||
e.EventType = EET_USER_EVENT;
|
||||
e.UserEvent.code = USER_EVENT_GAME_KEY;
|
||||
e.UserEvent.UserData1 = static_cast<size_t>(key);
|
||||
e.UserEvent.UserData2 = pressed;
|
||||
m_receiver->OnEvent(e);
|
||||
}
|
||||
|
||||
|
@ -55,10 +52,10 @@ void TouchControls::loadButtonTexture(IGUIImage *gui_button, const std::string &
|
|||
|
||||
void TouchControls::buttonEmitAction(button_info &btn, bool action)
|
||||
{
|
||||
if (!btn.keypress)
|
||||
if (!btn.game_key)
|
||||
return;
|
||||
|
||||
emitKeyboardEvent(btn.keypress, action);
|
||||
emitGameKeyEvent(btn.game_key, action);
|
||||
|
||||
if (action) {
|
||||
if (btn.toggleable == button_info::FIRST_TEXTURE) {
|
||||
|
@ -142,81 +139,48 @@ bool TouchControls::buttonsStep(std::vector<button_info> &buttons, float dtime)
|
|||
return has_pointers;
|
||||
}
|
||||
|
||||
static std::string id_to_setting(touch_gui_button_id id)
|
||||
static GameKeyType id_to_action(touch_gui_button_id id)
|
||||
{
|
||||
std::string key = "";
|
||||
switch (id) {
|
||||
case exit_id:
|
||||
return KeyType::ESC;
|
||||
case dig_id:
|
||||
key = "dig";
|
||||
break;
|
||||
return KeyType::DIG;
|
||||
case place_id:
|
||||
key = "place";
|
||||
break;
|
||||
return KeyType::PLACE;
|
||||
case jump_id:
|
||||
key = "jump";
|
||||
break;
|
||||
return KeyType::JUMP;
|
||||
case sneak_id:
|
||||
key = "sneak";
|
||||
break;
|
||||
return KeyType::SNEAK;
|
||||
case zoom_id:
|
||||
key = "zoom";
|
||||
break;
|
||||
return KeyType::ZOOM;
|
||||
case aux1_id:
|
||||
key = "aux1";
|
||||
break;
|
||||
return KeyType::AUX1;
|
||||
case fly_id:
|
||||
key = "freemove";
|
||||
break;
|
||||
return KeyType::FREEMOVE;
|
||||
case noclip_id:
|
||||
key = "noclip";
|
||||
break;
|
||||
return KeyType::NOCLIP;
|
||||
case fast_id:
|
||||
key = "fastmove";
|
||||
break;
|
||||
return KeyType::FASTMOVE;
|
||||
case debug_id:
|
||||
key = "toggle_debug";
|
||||
break;
|
||||
return KeyType::TOGGLE_DEBUG;
|
||||
case camera_id:
|
||||
key = "camera_mode";
|
||||
break;
|
||||
return KeyType::CAMERA_MODE;
|
||||
case range_id:
|
||||
key = "rangeselect";
|
||||
break;
|
||||
return KeyType::RANGESELECT;
|
||||
case minimap_id:
|
||||
key = "minimap";
|
||||
break;
|
||||
return KeyType::MINIMAP;
|
||||
case toggle_chat_id:
|
||||
key = "toggle_chat";
|
||||
break;
|
||||
return KeyType::TOGGLE_CHAT;
|
||||
case chat_id:
|
||||
key = "chat";
|
||||
break;
|
||||
return KeyType::CHAT;
|
||||
case inventory_id:
|
||||
key = "inventory";
|
||||
break;
|
||||
return KeyType::INVENTORY;
|
||||
case drop_id:
|
||||
key = "drop";
|
||||
break;
|
||||
return KeyType::DROP;
|
||||
default:
|
||||
break;
|
||||
return KeyType::INTERNAL_ENUM_COUNT;
|
||||
}
|
||||
return key.empty() ? key : "keymap_" + key;
|
||||
}
|
||||
|
||||
static KeyPress id_to_keypress(touch_gui_button_id id)
|
||||
{
|
||||
// ESC isn't part of the keymap.
|
||||
if (id == exit_id)
|
||||
return EscapeKey;
|
||||
|
||||
auto setting_name = id_to_setting(id);
|
||||
|
||||
assert(!setting_name.empty());
|
||||
auto kp = getKeySetting(setting_name);
|
||||
if (!kp)
|
||||
warningstream << "TouchControls: Unbound or invalid key for "
|
||||
<< setting_name << ", hiding button." << std::endl;
|
||||
return kp;
|
||||
}
|
||||
|
||||
|
||||
|
@ -239,11 +203,6 @@ TouchControls::TouchControls(IrrlichtDevice *device, ISimpleTextureSource *tsrc)
|
|||
readSettings();
|
||||
for (auto name : setting_names)
|
||||
g_settings->registerChangedCallback(name, settingChangedCallback, this);
|
||||
|
||||
// Also update layout when keybindings change (e.g. for convertibles)
|
||||
for (u8 id = 0; id < touch_gui_button_id_END; id++)
|
||||
if (auto name = id_to_setting((touch_gui_button_id)id); !name.empty())
|
||||
g_settings->registerChangedCallback(name, settingChangedCallback, this);
|
||||
}
|
||||
|
||||
void TouchControls::settingChangedCallback(const std::string &name, void *data)
|
||||
|
@ -376,7 +335,7 @@ bool TouchControls::mayAddButton(touch_gui_button_id id)
|
|||
assert(ButtonLayout::isButtonValid(id));
|
||||
assert(ButtonLayout::isButtonAllowed(id));
|
||||
// The overflow button doesn't need a keycode to be valid.
|
||||
return id == overflow_id || id_to_keypress(id);
|
||||
return id == overflow_id || id_to_action(id) < KeyType::INTERNAL_ENUM_COUNT;
|
||||
}
|
||||
|
||||
void TouchControls::addButton(std::vector<button_info> &buttons, touch_gui_button_id id,
|
||||
|
@ -388,7 +347,7 @@ void TouchControls::addButton(std::vector<button_info> &buttons, touch_gui_butto
|
|||
|
||||
button_info &btn = buttons.emplace_back();
|
||||
btn.id = id;
|
||||
btn.keypress = id_to_keypress(id);
|
||||
btn.game_key = id_to_action(id);
|
||||
btn.gui_button = grab_gui_element<IGUIImage>(btn_gui_button);
|
||||
}
|
||||
|
||||
|
@ -655,10 +614,10 @@ void TouchControls::translateEvent(const SEvent &event)
|
|||
void TouchControls::applyJoystickStatus()
|
||||
{
|
||||
if (m_joystick_triggers_aux1) {
|
||||
auto key = id_to_keypress(aux1_id);
|
||||
emitKeyboardEvent(key, false);
|
||||
auto key = id_to_action(aux1_id);
|
||||
emitGameKeyEvent(key, false);
|
||||
if (m_joystick_status_aux1)
|
||||
emitKeyboardEvent(key, true);
|
||||
emitGameKeyEvent(key, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -764,11 +723,11 @@ void TouchControls::releaseAll()
|
|||
// Release those manually too since the change initiated by
|
||||
// handleReleaseEvent will only be applied later by applyContextControls.
|
||||
if (m_dig_pressed) {
|
||||
emitKeyboardEvent(id_to_keypress(dig_id), false);
|
||||
emitGameKeyEvent(id_to_action(dig_id), false);
|
||||
m_dig_pressed = false;
|
||||
}
|
||||
if (m_place_pressed) {
|
||||
emitKeyboardEvent(id_to_keypress(place_id), false);
|
||||
emitGameKeyEvent(id_to_action(place_id), false);
|
||||
m_place_pressed = false;
|
||||
}
|
||||
}
|
||||
|
@ -852,20 +811,20 @@ void TouchControls::applyContextControls(const TouchInteractionMode &mode)
|
|||
target_place_pressed |= now < m_place_pressed_until;
|
||||
|
||||
if (target_dig_pressed && !m_dig_pressed) {
|
||||
emitKeyboardEvent(id_to_keypress(dig_id), true);
|
||||
emitGameKeyEvent(id_to_action(dig_id), true);
|
||||
m_dig_pressed = true;
|
||||
|
||||
} else if (!target_dig_pressed && m_dig_pressed) {
|
||||
emitKeyboardEvent(id_to_keypress(dig_id), false);
|
||||
emitGameKeyEvent(id_to_action(dig_id), false);
|
||||
m_dig_pressed = false;
|
||||
}
|
||||
|
||||
if (target_place_pressed && !m_place_pressed) {
|
||||
emitKeyboardEvent(id_to_keypress(place_id), true);
|
||||
emitGameKeyEvent(id_to_action(place_id), true);
|
||||
m_place_pressed = true;
|
||||
|
||||
} else if (!target_place_pressed && m_place_pressed) {
|
||||
emitKeyboardEvent(id_to_keypress(place_id), false);
|
||||
emitGameKeyEvent(id_to_action(place_id), false);
|
||||
m_place_pressed = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "itemdef.h"
|
||||
#include "touchscreenlayout.h"
|
||||
#include "util/basic_macros.h"
|
||||
#include "client/keycode.h"
|
||||
#include "client/keys.h"
|
||||
|
||||
class IrrlichtDevice;
|
||||
namespace gui
|
||||
|
@ -53,7 +53,7 @@ struct button_info
|
|||
{
|
||||
touch_gui_button_id id;
|
||||
float repeat_counter;
|
||||
KeyPress keypress;
|
||||
GameKeyType game_key;
|
||||
std::vector<size_t> pointer_ids;
|
||||
std::shared_ptr<IGUIImage> gui_button = nullptr;
|
||||
|
||||
|
@ -198,7 +198,7 @@ private:
|
|||
// for its buttons. We only want static image display, not interactivity,
|
||||
// from Irrlicht.
|
||||
|
||||
void emitKeyboardEvent(KeyPress keycode, bool pressed);
|
||||
void emitGameKeyEvent(GameKeyType, bool pressed);
|
||||
|
||||
void loadButtonTexture(IGUIImage *gui_button, const std::string &path);
|
||||
void buttonEmitAction(button_info &btn, bool action);
|
||||
|
|
|
@ -42,6 +42,14 @@ int ModApiMenuCommon::l_normalize_keycode(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int ModApiMenuCommon::l_get_keycode_name(lua_State *L)
|
||||
{
|
||||
auto keystr = luaL_checkstring(L, 1);
|
||||
auto name = KeyPress(keystr).name();
|
||||
lua_pushstring(L, name.empty() ? "" : gettext(name.c_str()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void ModApiMenuCommon::Initialize(lua_State *L, int top)
|
||||
{
|
||||
|
@ -49,6 +57,7 @@ void ModApiMenuCommon::Initialize(lua_State *L, int top)
|
|||
API_FCT(get_active_driver);
|
||||
API_FCT(irrlicht_device_supports_touch);
|
||||
API_FCT(normalize_keycode);
|
||||
API_FCT(get_keycode_name);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ private:
|
|||
static int l_get_active_driver(lua_State *L);
|
||||
static int l_irrlicht_device_supports_touch(lua_State *L);
|
||||
static int l_normalize_keycode(lua_State *L);
|
||||
static int l_get_keycode_name(lua_State *L);
|
||||
|
||||
public:
|
||||
static void Initialize(lua_State *L, int top);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue