Merge c6a1e31254 into 27c3aade5d
This commit is contained in:
commit
0488f0c640
@ -77,10 +77,22 @@ void KeyCache::populate()
|
||||
if (handler) {
|
||||
// First clear all keys, then re-add the ones we listen for
|
||||
handler->dontListenForKeys();
|
||||
avoidModifiers.fill({});
|
||||
for (const KeyPress &k : key) {
|
||||
handler->listenForKey(k);
|
||||
|
||||
for (auto j = 0; j < KeyType::INTERNAL_ENUM_COUNT; j++) {
|
||||
const auto &k2 = key[j];
|
||||
if (!k2.empty() && k != k2 && k.matches(k2) > 0) {
|
||||
// warningstream << k.sym() << " matches " << k2.sym() << std::endl;
|
||||
// k2 involves a modifier key that is not used by k
|
||||
avoidModifiers[j].emplace_front(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
handler->listenForKey(EscapeKey);
|
||||
handler->listenForKey(KeyPress("KEY_SHIFT", false));
|
||||
handler->listenForKey(KeyPress("KEY_CONTROL", false));
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,7 +154,7 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
|
||||
|
||||
// Remember whether each key is down or up
|
||||
if (event.EventType == irr::EET_KEY_INPUT_EVENT) {
|
||||
const KeyPress keyCode(event.KeyInput);
|
||||
const KeyPress keyCode(event.KeyInput, false, false);
|
||||
if (keysListenedFor[keyCode]) {
|
||||
if (event.KeyInput.PressedDown) {
|
||||
if (!IsKeyDown(keyCode))
|
||||
@ -155,6 +167,7 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
|
||||
keyWasReleased.set(keyCode);
|
||||
|
||||
keyIsDown.unset(keyCode);
|
||||
keyWasDown.unset(keyCode);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@ -6,7 +6,10 @@
|
||||
|
||||
#include "irrlichttypes.h"
|
||||
#include "joystick_controller.h"
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <unordered_set>
|
||||
#include "keycode.h"
|
||||
|
||||
class InputHandler;
|
||||
@ -43,7 +46,18 @@ struct KeyCache
|
||||
void populate_nonchanging();
|
||||
|
||||
KeyPress key[KeyType::INTERNAL_ENUM_COUNT];
|
||||
std::array<std::list<KeyPress>, KeyType::INTERNAL_ENUM_COUNT> avoidModifiers;
|
||||
InputHandler *handler;
|
||||
|
||||
bool matches(const GameKeyType k, const std::function<bool(KeyPress)> &matcher)
|
||||
{
|
||||
if (!matcher(key[k]))
|
||||
return false;
|
||||
for (const auto &other: avoidModifiers[k])
|
||||
if (matcher(other))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class KeyList : private std::list<KeyPress>
|
||||
@ -51,6 +65,7 @@ class KeyList : private std::list<KeyPress>
|
||||
typedef std::list<KeyPress> super;
|
||||
typedef super::iterator iterator;
|
||||
typedef super::const_iterator const_iterator;
|
||||
using ModifierSet = std::unordered_set<std::string>;
|
||||
|
||||
virtual const_iterator find(const KeyPress &key) const
|
||||
{
|
||||
@ -82,41 +97,103 @@ class KeyList : private std::list<KeyPress>
|
||||
return e;
|
||||
}
|
||||
|
||||
static void toggleModifier(ModifierSet &set, const KeyPress &base)
|
||||
{
|
||||
auto sym = base.sym();
|
||||
if (set.find(sym) != set.end())
|
||||
set.erase(sym);
|
||||
else
|
||||
set.emplace(sym);
|
||||
}
|
||||
|
||||
ModifierSet shift;
|
||||
ModifierSet control;
|
||||
|
||||
public:
|
||||
void clear() { super::clear(); }
|
||||
KeyList() = default;
|
||||
|
||||
// Used for unittests
|
||||
KeyList(std::initializer_list<KeyPress> init): super(init.size())
|
||||
{
|
||||
for (const auto &key: init)
|
||||
set(key);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
super::clear();
|
||||
shift.clear();
|
||||
control.clear();
|
||||
}
|
||||
|
||||
void set(const KeyPress &key)
|
||||
{
|
||||
if (find(key) == end())
|
||||
push_back(key);
|
||||
KeyPress base = key.base();
|
||||
if (base.is_shift_base())
|
||||
shift.emplace(base.sym());
|
||||
else if (base.is_control_base())
|
||||
control.emplace(base.sym());
|
||||
else if (find(base) == end())
|
||||
push_back(std::move(base));
|
||||
}
|
||||
|
||||
void unset(const KeyPress &key)
|
||||
{
|
||||
iterator p(find(key));
|
||||
|
||||
if (p != end())
|
||||
erase(p);
|
||||
KeyPress base = key.base();
|
||||
if (base.is_shift_base())
|
||||
shift.erase(base.sym());
|
||||
else if (base.is_control_base())
|
||||
control.erase(base.sym());
|
||||
else {
|
||||
iterator p(find(base));
|
||||
if (p != end())
|
||||
erase(p);
|
||||
}
|
||||
}
|
||||
|
||||
void toggle(const KeyPress &key)
|
||||
{
|
||||
iterator p(this->find(key));
|
||||
|
||||
if (p != end())
|
||||
erase(p);
|
||||
else
|
||||
push_back(key);
|
||||
KeyPress base = key.base();
|
||||
if (base.is_shift_base())
|
||||
toggleModifier(shift, base);
|
||||
else if (base.is_control_base())
|
||||
toggleModifier(control, base);
|
||||
else {
|
||||
iterator p(this->find(key));
|
||||
if (p != end())
|
||||
erase(p);
|
||||
else
|
||||
push_back(key);
|
||||
}
|
||||
}
|
||||
|
||||
void append(const KeyList &other)
|
||||
void append(KeyList &other)
|
||||
{
|
||||
for (const KeyPress &key : other) {
|
||||
set(key);
|
||||
}
|
||||
shift.merge(other.shift);
|
||||
control.merge(other.control);
|
||||
}
|
||||
|
||||
bool operator[](const KeyPress &key) const { return find(key) != end(); }
|
||||
bool operator[](const KeyPress &key) const
|
||||
{
|
||||
auto base = key.base();
|
||||
if (key.is_shift_base()) {
|
||||
if (shift.empty())
|
||||
return false;
|
||||
} else if (key.is_control_base()) {
|
||||
if (control.empty())
|
||||
return false;
|
||||
} else if (base.valid_base() && find(base) == end())
|
||||
return false;
|
||||
if (!(key.valid_base() || key.has_modifier()))
|
||||
return false;
|
||||
if (key.shift && shift.empty())
|
||||
return false;
|
||||
if (key.control && control.empty())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class MyEventReceiver : public IEventReceiver
|
||||
@ -128,10 +205,10 @@ public:
|
||||
bool IsKeyDown(const KeyPress &keyCode) const { return keyIsDown[keyCode]; }
|
||||
|
||||
// Checks whether a key was down and resets the state
|
||||
bool WasKeyDown(const KeyPress &keyCode)
|
||||
bool WasKeyDown(const KeyPress &keyCode, const bool reset = true)
|
||||
{
|
||||
bool b = keyWasDown[keyCode];
|
||||
if (b)
|
||||
if (reset && b)
|
||||
keyWasDown.unset(keyCode);
|
||||
return b;
|
||||
}
|
||||
@ -282,19 +359,22 @@ public:
|
||||
|
||||
virtual bool isKeyDown(GameKeyType k)
|
||||
{
|
||||
return m_receiver->IsKeyDown(keycache.key[k]) || joystick.isKeyDown(k);
|
||||
return keycache.matches(k, [=](const auto &key) { return m_receiver->IsKeyDown(key); }) || joystick.isKeyDown(k);
|
||||
}
|
||||
virtual bool wasKeyDown(GameKeyType k)
|
||||
{
|
||||
return m_receiver->WasKeyDown(keycache.key[k]) || joystick.wasKeyDown(k);
|
||||
bool result = keycache.matches(k, [=](const auto &key) { return m_receiver->WasKeyDown(key, false); }) || joystick.wasKeyDown(k);
|
||||
if (result) // reset WasKeyDown state
|
||||
m_receiver->WasKeyDown(keycache.key[k]);
|
||||
return result;
|
||||
}
|
||||
virtual bool wasKeyPressed(GameKeyType k)
|
||||
{
|
||||
return m_receiver->WasKeyPressed(keycache.key[k]) || joystick.wasKeyPressed(k);
|
||||
return keycache.matches(k, [=](const auto &key) { return m_receiver->WasKeyPressed(key); }) || joystick.wasKeyPressed(k);
|
||||
}
|
||||
virtual bool wasKeyReleased(GameKeyType k)
|
||||
{
|
||||
return m_receiver->WasKeyReleased(keycache.key[k]) || joystick.wasKeyReleased(k);
|
||||
return keycache.matches(k, [=](const auto &key) { return m_receiver->WasKeyReleased(key); }) || joystick.wasKeyReleased(k);
|
||||
}
|
||||
|
||||
virtual float getJoystickSpeed();
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
// Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
#include "gettext.h"
|
||||
#include "keycode.h"
|
||||
#include "settings.h"
|
||||
#include "log.h"
|
||||
@ -28,8 +29,6 @@ struct table_key {
|
||||
#define DEFINEKEY5(ch) /* key without Irrlicht keycode */ \
|
||||
{ ch, irr::KEY_KEY_CODES_COUNT, (wchar_t) *ch, ch },
|
||||
|
||||
#define N_(text) text
|
||||
|
||||
static const struct table_key table[] = {
|
||||
// Keys that can be reliably mapped between Char and Key
|
||||
DEFINEKEY3(0)
|
||||
@ -224,10 +223,10 @@ static const struct table_key table[] = {
|
||||
#undef N_
|
||||
|
||||
|
||||
static const table_key &lookup_keyname(const char *name)
|
||||
static const table_key &lookup_keyname(const std::string_view &name)
|
||||
{
|
||||
for (const auto &table_key : table) {
|
||||
if (strcmp(table_key.Name, name) == 0)
|
||||
if (table_key.Name == name)
|
||||
return table_key;
|
||||
}
|
||||
|
||||
@ -258,19 +257,39 @@ static const table_key &lookup_keychar(wchar_t Char)
|
||||
throw UnknownKeycode(os.str().c_str());
|
||||
}
|
||||
|
||||
KeyPress::KeyPress(const char *name)
|
||||
std::string_view KeyPress::parseModifiers(const std::string_view &name, bool merge_modifiers)
|
||||
{
|
||||
if (strlen(name) == 0) {
|
||||
if (str_starts_with(name, "KEY_CONTROL-")) {
|
||||
control = true;
|
||||
return parseModifiers(name.substr(12), merge_modifiers);
|
||||
} else if (str_starts_with(name, "KEY_SHIFT-")) {
|
||||
shift = true;
|
||||
return parseModifiers(name.substr(10), merge_modifiers);
|
||||
}
|
||||
if (merge_modifiers) {
|
||||
if (is_control_base(name)) {
|
||||
control = true;
|
||||
return "";
|
||||
} else if (is_shift_base(name)) {
|
||||
shift = true;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
KeyPress::KeyPress(const std::string_view &name, bool merge_modifiers)
|
||||
{
|
||||
auto keyname = parseModifiers(name, merge_modifiers);
|
||||
auto wkeyname = utf8_to_wide(keyname);
|
||||
if (wkeyname.empty()) {
|
||||
Key = irr::KEY_KEY_CODES_COUNT;
|
||||
Char = L'\0';
|
||||
m_name = "";
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen(name) <= 4) {
|
||||
} else if (wkeyname.size() == 1) {
|
||||
// Lookup by resulting character
|
||||
int chars_read = mbtowc(&Char, name, 1);
|
||||
FATAL_ERROR_IF(chars_read != 1, "Unexpected multibyte character");
|
||||
Char = wkeyname[0];
|
||||
try {
|
||||
auto &k = lookup_keychar(Char);
|
||||
m_name = k.Name;
|
||||
@ -279,9 +298,9 @@ KeyPress::KeyPress(const char *name)
|
||||
} catch (UnknownKeycode &e) {};
|
||||
} else {
|
||||
// Lookup by name
|
||||
m_name = name;
|
||||
m_name = keyname;
|
||||
try {
|
||||
auto &k = lookup_keyname(name);
|
||||
auto &k = lookup_keyname(keyname);
|
||||
Key = k.Key;
|
||||
Char = k.Char;
|
||||
return;
|
||||
@ -290,15 +309,28 @@ KeyPress::KeyPress(const char *name)
|
||||
|
||||
// It's not a known key, complain and try to do something
|
||||
Key = irr::KEY_KEY_CODES_COUNT;
|
||||
int chars_read = mbtowc(&Char, name, 1);
|
||||
FATAL_ERROR_IF(chars_read != 1, "Unexpected multibyte character");
|
||||
Char = wkeyname[0];
|
||||
m_name = "";
|
||||
warningstream << "KeyPress: Unknown key '" << name
|
||||
warningstream << "KeyPress: Unknown key '" << keyname
|
||||
<< "', falling back to first char." << std::endl;
|
||||
}
|
||||
|
||||
KeyPress::KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character)
|
||||
KeyPress::KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character, bool merge_modifiers):
|
||||
shift(in.Shift), control(in.Control)
|
||||
{
|
||||
if (merge_modifiers) {
|
||||
switch (in.Key) {
|
||||
case irr::KEY_SHIFT: case irr::KEY_LSHIFT: case irr::KEY_RSHIFT:
|
||||
shift = true;
|
||||
return;
|
||||
case irr::KEY_CONTROL: case irr::KEY_LCONTROL: case irr::KEY_RCONTROL:
|
||||
control = true;
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (prefer_character)
|
||||
Key = irr::KEY_KEY_CODES_COUNT;
|
||||
else
|
||||
@ -315,21 +347,47 @@ KeyPress::KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character)
|
||||
};
|
||||
}
|
||||
|
||||
const char *KeyPress::sym() const
|
||||
const std::string KeyPress::sym() const
|
||||
{
|
||||
return m_name.c_str();
|
||||
std::string sym = m_name;
|
||||
if (shift)
|
||||
sym = sym.empty() ? "KEY_SHIFT" : "KEY_SHIFT-" + sym;
|
||||
if (control)
|
||||
sym = sym.empty() ? "KEY_CONTROL" : "KEY_CONTROL-" + sym;
|
||||
return sym;
|
||||
}
|
||||
|
||||
const char *KeyPress::name() const
|
||||
const std::string KeyPress::name() const
|
||||
{
|
||||
if (m_name.empty())
|
||||
if (m_name.empty() && !has_modifier())
|
||||
return "";
|
||||
const char *ret;
|
||||
std::string ret;
|
||||
if (valid_kcode(Key))
|
||||
ret = lookup_keykey(Key).LangName;
|
||||
else
|
||||
else if (Char != L'\0')
|
||||
ret = lookup_keychar(Char).LangName;
|
||||
return ret ? ret : "<Unnamed key>";
|
||||
if (!ret.empty())
|
||||
ret = strgettext(ret);
|
||||
if (shift)
|
||||
ret = ret.empty() ? gettext("Shift Key") : fmtgettext("Shift-%s", ret.c_str());
|
||||
if (control)
|
||||
ret = ret.empty() ? gettext("Control Key") : fmtgettext("Control-%s", ret.c_str());
|
||||
if (ret.empty())
|
||||
ret = gettext("<Unnamed key>");
|
||||
return ret;
|
||||
}
|
||||
|
||||
int KeyPress::matches(const KeyPress &p) const {
|
||||
if (!basic_equals(p) && p.valid_base())
|
||||
return 0;
|
||||
if ((p.shift && !shift) || (p.control && !control))
|
||||
return 0;
|
||||
int score = basic_equals(p);
|
||||
if (p.shift == shift)
|
||||
score++;
|
||||
if (p.control == control)
|
||||
score++;
|
||||
return score;
|
||||
}
|
||||
|
||||
const KeyPress EscapeKey("KEY_ESCAPE");
|
||||
|
||||
@ -13,8 +13,8 @@
|
||||
class UnknownKeycode : public BaseException
|
||||
{
|
||||
public:
|
||||
UnknownKeycode(const char *s) :
|
||||
BaseException(s) {};
|
||||
UnknownKeycode(const std::string_view &s):
|
||||
BaseException(std::string(s)) {}
|
||||
};
|
||||
|
||||
/* A key press, consisting of either an Irrlicht keycode
|
||||
@ -25,19 +25,80 @@ class KeyPress
|
||||
public:
|
||||
KeyPress() = default;
|
||||
|
||||
KeyPress(const char *name);
|
||||
KeyPress(const std::string_view &name, bool merge_modifiers = true);
|
||||
|
||||
KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character = false);
|
||||
KeyPress(const char *name, bool merge_modifiers = true): KeyPress(std::string_view(name), merge_modifiers) {};
|
||||
|
||||
KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character = false, bool merge_modifiers = true);
|
||||
|
||||
bool operator==(const KeyPress &o) const
|
||||
{
|
||||
return (Char > 0 && Char == o.Char) || (valid_kcode(Key) && Key == o.Key);
|
||||
return basic_equals(o) && shift == o.shift && control == o.control;
|
||||
}
|
||||
bool operator!=(const KeyPress &o) const
|
||||
{
|
||||
return !operator==(o);
|
||||
}
|
||||
|
||||
const char *sym() const;
|
||||
const char *name() const;
|
||||
bool basic_equals(const KeyPress &o) const
|
||||
{
|
||||
return (Char > 0 && Char == o.Char) || (valid_kcode(Key) && Key == o.Key)
|
||||
|| (Char == o.Char && Key == o.Key);
|
||||
}
|
||||
|
||||
bool has_modifier() const
|
||||
{
|
||||
return shift || control;
|
||||
}
|
||||
|
||||
bool valid_base() const
|
||||
{
|
||||
return valid_kcode(Key) || Char > 0;
|
||||
}
|
||||
|
||||
KeyPress base() const
|
||||
{
|
||||
KeyPress kp = *this;
|
||||
kp.control = kp.shift = false;
|
||||
return kp;
|
||||
}
|
||||
|
||||
bool is_shift_base() const
|
||||
{
|
||||
return is_shift_base(m_name);
|
||||
}
|
||||
|
||||
static bool is_shift_base(const std::string_view &name)
|
||||
{
|
||||
return (name == "KEY_LSHIFT" || name == "KEY_RSHIFT" || name == "KEY_SHIFT");
|
||||
}
|
||||
|
||||
bool is_control_base() const
|
||||
{
|
||||
return is_control_base(m_name);
|
||||
}
|
||||
|
||||
static bool is_control_base(const std::string_view &name)
|
||||
{
|
||||
return (name == "KEY_LCONTROL" || name == "KEY_RCONTROL" || name == "KEY_CONTROL");
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return !(valid_base() || has_modifier());
|
||||
}
|
||||
|
||||
int matches(const KeyPress &p) const;
|
||||
|
||||
const std::string sym() const;
|
||||
const std::string name() const;
|
||||
|
||||
bool shift = false;
|
||||
bool control = false;
|
||||
|
||||
protected:
|
||||
std::string_view parseModifiers(const std::string_view &str, bool merge_modifiers);
|
||||
|
||||
static bool valid_kcode(irr::EKEY_CODE k)
|
||||
{
|
||||
return k > 0 && k < irr::KEY_KEY_CODES_COUNT;
|
||||
|
||||
@ -415,7 +415,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event)
|
||||
}
|
||||
|
||||
// Key input
|
||||
if (KeyPress(event.KeyInput) == getKeySetting("keymap_console")) {
|
||||
if (KeyPress(event.KeyInput).matches(getKeySetting("keymap_console"))) {
|
||||
closeConsole();
|
||||
|
||||
// inhibit open so the_game doesn't reopen immediately
|
||||
|
||||
@ -128,7 +128,7 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
|
||||
core::rect<s32> rect(0, 0, 100 * s, 30 * s);
|
||||
rect += topleft + v2s32(offset.X + 150 * s, offset.Y - 5 * s);
|
||||
k->button = GUIButton::addButton(Environment, rect, m_tsrc, this, k->id,
|
||||
wstrgettext(k->key.name()).c_str());
|
||||
utf8_to_wide(k->key.name()).c_str());
|
||||
}
|
||||
if ((i + 1) % KMaxButtonPerColumns == 0) {
|
||||
offset.X += 260 * s;
|
||||
@ -242,7 +242,7 @@ bool GUIKeyChangeMenu::acceptInput()
|
||||
bool GUIKeyChangeMenu::resetMenu()
|
||||
{
|
||||
if (active_key) {
|
||||
active_key->button->setText(wstrgettext(active_key->key.name()).c_str());
|
||||
active_key->button->setText(utf8_to_wide(active_key->key.name()).c_str());
|
||||
active_key = nullptr;
|
||||
return false;
|
||||
}
|
||||
@ -250,10 +250,13 @@ bool GUIKeyChangeMenu::resetMenu()
|
||||
}
|
||||
bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
|
||||
{
|
||||
if (event.EventType == EET_KEY_INPUT_EVENT && active_key
|
||||
&& event.KeyInput.PressedDown) {
|
||||
if (event.EventType == EET_KEY_INPUT_EVENT && active_key) {
|
||||
if (!event.KeyInput.PressedDown) {
|
||||
active_key = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool prefer_character = shift_down;
|
||||
bool prefer_character = false;
|
||||
KeyPress kp(event.KeyInput, prefer_character);
|
||||
|
||||
if (event.KeyInput.Key == irr::KEY_DELETE)
|
||||
@ -261,16 +264,9 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
|
||||
else if (event.KeyInput.Key == irr::KEY_ESCAPE)
|
||||
kp = active_key->key; // Cancel
|
||||
|
||||
bool shift_went_down = false;
|
||||
if(!shift_down &&
|
||||
(event.KeyInput.Key == irr::KEY_SHIFT ||
|
||||
event.KeyInput.Key == irr::KEY_LSHIFT ||
|
||||
event.KeyInput.Key == irr::KEY_RSHIFT))
|
||||
shift_went_down = true;
|
||||
|
||||
// Display Key already in use message
|
||||
bool key_in_use = false;
|
||||
if (strcmp(kp.sym(), "") != 0) {
|
||||
if (!kp.sym().empty()) {
|
||||
for (key_setting *ks : key_settings) {
|
||||
if (ks != active_key && ks->key == kp) {
|
||||
key_in_use = true;
|
||||
@ -293,15 +289,8 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
|
||||
// But go on
|
||||
{
|
||||
active_key->key = kp;
|
||||
active_key->button->setText(wstrgettext(kp.name()).c_str());
|
||||
active_key->button->setText(utf8_to_wide(kp.name()).c_str());
|
||||
|
||||
// Allow characters made with shift
|
||||
if (shift_went_down){
|
||||
shift_down = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
active_key = nullptr;
|
||||
return true;
|
||||
}
|
||||
} else if (event.EventType == EET_KEY_INPUT_EVENT && !active_key
|
||||
@ -342,7 +331,6 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
|
||||
}
|
||||
FATAL_ERROR_IF(!active_key, "Key setting not found");
|
||||
|
||||
shift_down = false;
|
||||
active_key->button->setText(wstrgettext("press key").c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
@ -55,8 +55,6 @@ private:
|
||||
|
||||
void add_key(int id, std::wstring button_name, const std::string &setting_name);
|
||||
|
||||
bool shift_down = false;
|
||||
|
||||
key_setting *active_key = nullptr;
|
||||
gui::IGUIStaticText *key_used_text = nullptr;
|
||||
std::vector<key_setting *> key_settings;
|
||||
|
||||
@ -56,4 +56,5 @@ set (UNITTEST_CLIENT_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_irr_matrix4.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_mesh_compare.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_keycode.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_inputhandler.cpp
|
||||
PARENT_SCOPE)
|
||||
|
||||
37
src/unittest/test_inputhandler.cpp
Normal file
37
src/unittest/test_inputhandler.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
// Minetest
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "client/inputhandler.h"
|
||||
#include "catch.h"
|
||||
|
||||
TEST_CASE("test KeyList")
|
||||
{
|
||||
SECTION("KeyList inclusion")
|
||||
{
|
||||
KeyList list{"KEY_KEY_5", KeyPress("KEY_LSHIFT", false)};
|
||||
CHECK(list["KEY_KEY_5"]);
|
||||
CHECK(list["KEY_SHIFT"]);
|
||||
CHECK(list["KEY_SHIFT-KEY_KEY_5"]);
|
||||
CHECK(!list["KEY_NUMPAD5"]);
|
||||
CHECK(!list["KEY_CONTROL-KEY_KEY_5"]);
|
||||
}
|
||||
SECTION("KeyList insertion")
|
||||
{
|
||||
KeyList list;
|
||||
list.set("KEY_KEY_5");
|
||||
list.set(KeyPress("KEY_LSHIFT", false));
|
||||
list.unset(KeyPress("KEY_RSHIFT", false));
|
||||
CHECK(list["KEY_SHIFT-KEY_KEY_5"]);
|
||||
list.toggle(KeyPress("KEY_RSHIFT", false));
|
||||
list.toggle(KeyPress("KEY_LSHIFT", false));
|
||||
list.set("KEY_KEY_6");
|
||||
CHECK(list["KEY_SHIFT-KEY_KEY_6"]);
|
||||
list.toggle(KeyPress("KEY_RSHIFT", false));
|
||||
CHECK(list["KEY_KEY_5"]);
|
||||
CHECK(!list["KEY_SHIFT-KEY_KEY_5"]);
|
||||
list.toggle(KeyPress("KEY_SHIFT", false));
|
||||
list.clear();
|
||||
CHECK(!list["KEY_KEY_5"]);
|
||||
CHECK(!list[KeyPress("KEY_SHIFT", false)]);
|
||||
}
|
||||
}
|
||||
@ -31,7 +31,7 @@ void TestKeycode::runTests(IGameDef *gamedef)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define UASSERTEQ_STR(one, two) UASSERT(strcmp(one, two) == 0)
|
||||
#define UASSERT_HAS_NAME(key) UASSERTCMP(size_t, >, key.name().size(), 0)
|
||||
|
||||
void TestKeycode::testCreateFromString()
|
||||
{
|
||||
@ -39,57 +39,62 @@ void TestKeycode::testCreateFromString()
|
||||
|
||||
// Character key, from char
|
||||
k = KeyPress("R");
|
||||
UASSERTEQ_STR(k.sym(), "KEY_KEY_R");
|
||||
UASSERTCMP(int, >, strlen(k.name()), 0); // should have human description
|
||||
UASSERT(k.sym() == "KEY_KEY_R");
|
||||
UASSERT_HAS_NAME(k); // should have human description
|
||||
|
||||
// Character key, from identifier
|
||||
k = KeyPress("KEY_KEY_B");
|
||||
UASSERTEQ_STR(k.sym(), "KEY_KEY_B");
|
||||
UASSERTCMP(int, >, strlen(k.name()), 0);
|
||||
UASSERT(k.sym() == "KEY_KEY_B");
|
||||
UASSERT_HAS_NAME(k);
|
||||
|
||||
// Non-Character key, from identifier
|
||||
k = KeyPress("KEY_UP");
|
||||
UASSERTEQ_STR(k.sym(), "KEY_UP");
|
||||
UASSERTCMP(int, >, strlen(k.name()), 0);
|
||||
UASSERT(k.sym() == "KEY_UP");
|
||||
UASSERT_HAS_NAME(k);
|
||||
|
||||
k = KeyPress("KEY_F6");
|
||||
UASSERTEQ_STR(k.sym(), "KEY_F6");
|
||||
UASSERTCMP(int, >, strlen(k.name()), 0);
|
||||
UASSERT(k.sym() == "KEY_F6");
|
||||
UASSERT_HAS_NAME(k);
|
||||
|
||||
// Irrlicht-unknown key, from char
|
||||
k = KeyPress("/");
|
||||
UASSERTEQ_STR(k.sym(), "/");
|
||||
UASSERTCMP(int, >, strlen(k.name()), 0);
|
||||
UASSERT(k.sym() == "/");
|
||||
UASSERT_HAS_NAME(k);
|
||||
|
||||
// Key with modifier
|
||||
k = KeyPress("KEY_SHIFT-KEY_CONTROL-2");
|
||||
UASSERT(k.sym() == "KEY_CONTROL-KEY_SHIFT-KEY_KEY_2");
|
||||
}
|
||||
|
||||
void TestKeycode::testCreateFromSKeyInput()
|
||||
{
|
||||
KeyPress k;
|
||||
irr::SEvent::SKeyInput in;
|
||||
in.Control = in.Shift = false;
|
||||
|
||||
// Character key
|
||||
in.Key = irr::KEY_KEY_3;
|
||||
in.Char = L'3';
|
||||
k = KeyPress(in);
|
||||
UASSERTEQ_STR(k.sym(), "KEY_KEY_3");
|
||||
UASSERT(k.sym() == "KEY_KEY_3");
|
||||
|
||||
// Non-Character key
|
||||
in.Key = irr::KEY_RSHIFT;
|
||||
in.Char = L'\0';
|
||||
k = KeyPress(in);
|
||||
UASSERTEQ_STR(k.sym(), "KEY_RSHIFT");
|
||||
UASSERT(k.sym() == "KEY_SHIFT");
|
||||
|
||||
// Irrlicht-unknown key
|
||||
in.Key = irr::KEY_KEY_CODES_COUNT;
|
||||
in.Char = L'?';
|
||||
k = KeyPress(in);
|
||||
UASSERTEQ_STR(k.sym(), "?");
|
||||
UASSERT(k.sym() == "?");
|
||||
|
||||
// prefer_character mode
|
||||
in.Key = irr::KEY_COMMA;
|
||||
in.Char = L'G';
|
||||
k = KeyPress(in, true);
|
||||
UASSERTEQ_STR(k.sym(), "KEY_KEY_G");
|
||||
UASSERT(k.sym() == "KEY_KEY_G");
|
||||
}
|
||||
|
||||
void TestKeycode::testCompare()
|
||||
@ -98,9 +103,25 @@ void TestKeycode::testCompare()
|
||||
UASSERT(KeyPress("5") == KeyPress("KEY_KEY_5"));
|
||||
UASSERT(!(KeyPress("5") == KeyPress("KEY_NUMPAD5")));
|
||||
|
||||
// Test modifiers
|
||||
UASSERT(KeyPress("KEY_CONTROL-KEY_SHIFT-5") == KeyPress("KEY_SHIFT-KEY_CONTROL-KEY_KEY_5"));
|
||||
UASSERT(KeyPress("KEY_SHIFT--") == KeyPress("KEY_SHIFT-KEY_MINUS"));
|
||||
UASSERT(KeyPress("KEY_CONTROL-KEY_LSHIFT") == KeyPress("KEY_SHIFT-KEY_RCONTROL"));
|
||||
UASSERT(KeyPress("KEY_RSHIFT") == KeyPress("KEY_SHIFT-"));
|
||||
|
||||
// Test matching
|
||||
UASSERT(!KeyPress("5").matches(KeyPress("KEY_CONTROL-5")));
|
||||
UASSERT(!KeyPress("5").matches(KeyPress("KEY_CONTROL")));
|
||||
UASSERT(KeyPress("KEY_SHIFT-5").matches(KeyPress("KEY_SHIFT")));
|
||||
UASSERT(KeyPress("KEY_SHIFT-5").matches(KeyPress("5")));
|
||||
UASSERT(KeyPress("KEY_SHIFT-5").matches(KeyPress("KEY_SHIFT-5")) > KeyPress("KEY_SHIFT-5").matches("5"));
|
||||
UASSERT(!KeyPress("5").matches(KeyPress("$")));
|
||||
UASSERT(!KeyPress("5").matches(KeyPress("KEY_F10")));
|
||||
|
||||
// Matching char suffices
|
||||
// note: This is a real-world example, Irrlicht maps XK_equal to irr::KEY_PLUS on Linux
|
||||
irr::SEvent::SKeyInput in;
|
||||
in.Shift = in.Control = false;
|
||||
in.Key = irr::KEY_PLUS;
|
||||
in.Char = L'=';
|
||||
UASSERT(KeyPress("=") == KeyPress(in));
|
||||
@ -108,6 +129,7 @@ void TestKeycode::testCompare()
|
||||
// Matching keycode suffices
|
||||
irr::SEvent::SKeyInput in2;
|
||||
in.Key = in2.Key = irr::KEY_OEM_CLEAR;
|
||||
in2.Shift = in2.Control = false;
|
||||
in.Char = L'\0';
|
||||
in2.Char = L';';
|
||||
UASSERT(KeyPress(in) == KeyPress(in2));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user