This commit is contained in:
y5nw 2024-12-30 21:39:50 +01:00 committed by GitHub
commit 0488f0c640
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 351 additions and 93 deletions

View File

@ -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;

View File

@ -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();

View File

@ -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");

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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)

View 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)]);
}
}

View File

@ -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));