diff --git a/backends/keymapper/hardware-input.cpp b/backends/keymapper/hardware-input.cpp index b3ab98d19b3..01a62a2ebb5 100644 --- a/backends/keymapper/hardware-input.cpp +++ b/backends/keymapper/hardware-input.cpp @@ -205,6 +205,19 @@ const KeyTableEntry defaultKeys[] = { {"AUDIOREWIND", KEYCODE_AUDIOREWIND, "Audio Rewind"}, {"AUDIOFASTFORWARD", KEYCODE_AUDIOFASTFORWARD, "Audio Fast-Forward"}, + // Modifier keys + {"SCROLLOCK", KEYCODE_SCROLLOCK, "Scroll Lock" }, + {"CAPSLOCK", KEYCODE_CAPSLOCK, "Caps Lock" }, + {"NUMLOCK", KEYCODE_NUMLOCK, "Num Lock" }, + {"LSHIFT", KEYCODE_LSHIFT, "Left Shift" }, + {"RSHIFT", KEYCODE_RSHIFT, "Right Shift" }, + {"LALT", KEYCODE_LALT, "Left Alt" }, + {"RALT", KEYCODE_RALT, "Right Alt" }, + {"LCTRL", KEYCODE_LCTRL, "Left Control" }, + {"RCTRL", KEYCODE_RCTRL, "Right Control" }, + {"LMETA", KEYCODE_LMETA, "Left Meta" }, + {"RMETA", KEYCODE_RMETA, "Right Meta" }, + {0, KEYCODE_INVALID, 0} }; @@ -314,9 +327,11 @@ HardwareInput KeyboardHardwareInputSet::findHardwareInput(const Event &event) co switch (event.type) { case EVENT_KEYDOWN: case EVENT_KEYUP: { + KeyState normalizedKeystate = normalizeKeyState(event.kbd); + const KeyTableEntry *key = nullptr; for (key = _keys; key->hwId; key++) { - if (event.kbd.keycode == key->keycode) { + if (normalizedKeystate.keycode == key->keycode) { break; } } @@ -330,7 +345,7 @@ HardwareInput KeyboardHardwareInputSet::findHardwareInput(const Event &event) co byte modifierFlags = 0; for (const ModifierTableEntry *modifier = _modifiers; modifier->id; modifier++) { - if (event.kbd.flags & modifier->flag) { + if (normalizedKeystate.flags & modifier->flag) { id += modifier->id; id += "+"; fullKeyDesc += modifier->desc; @@ -346,6 +361,50 @@ HardwareInput KeyboardHardwareInputSet::findHardwareInput(const Event &event) co } } +KeyState KeyboardHardwareInputSet::normalizeKeyState(const KeyState &keystate) { + KeyState normalizedKeystate = keystate; + + // We ignore the sticky modifiers as they traditionally + // have no impact on the outcome of key presses. + // TODO: Maybe Num Lock should act as a modifier for the keypad. + normalizedKeystate.flags &= ~KBD_STICKY; + + // Modifier keypresses ignore the corresponding modifier flag. + // That way, for example, `Left Shift` is not identified + // as `Shift+Left Shift` by the keymapper. + switch (normalizedKeystate.keycode) { + case KEYCODE_LSHIFT: + case KEYCODE_RSHIFT: + normalizedKeystate.flags &= ~KBD_SHIFT; + break; + case KEYCODE_LCTRL: + case KEYCODE_RCTRL: + normalizedKeystate.flags &= ~KBD_CTRL; + break; + case KEYCODE_LALT: + case KEYCODE_RALT: + normalizedKeystate.flags &= ~KBD_ALT; + break; + case KEYCODE_LMETA: + case KEYCODE_RMETA: + normalizedKeystate.flags &= ~KBD_META; + break; + case KEYCODE_SCROLLOCK: + normalizedKeystate.flags &= ~KBD_SCRL; + break; + case KEYCODE_CAPSLOCK: + normalizedKeystate.flags &= ~KBD_CAPS; + break; + case KEYCODE_NUMLOCK: + normalizedKeystate.flags &= ~KBD_NUM; + break; + default: + break; + } + + return normalizedKeystate; +} + MouseHardwareInputSet::MouseHardwareInputSet(const HardwareInputTableEntry *buttonEntries) : _buttonEntries(buttonEntries) { assert(_buttonEntries); diff --git a/backends/keymapper/hardware-input.h b/backends/keymapper/hardware-input.h index bb641690010..c72f93758d3 100644 --- a/backends/keymapper/hardware-input.h +++ b/backends/keymapper/hardware-input.h @@ -234,6 +234,9 @@ public: HardwareInput findHardwareInput(const String &id) const override; HardwareInput findHardwareInput(const Event &event) const override; + /** Transform a keystate into a canonical form that can be used to unambiguously identify the keypress */ + static KeyState normalizeKeyState(const KeyState &keystate); + private: const KeyTableEntry *_keys; const ModifierTableEntry *_modifiers; diff --git a/backends/keymapper/keymap.cpp b/backends/keymapper/keymap.cpp index d3a787804da..055b4c0329e 100644 --- a/backends/keymapper/keymap.cpp +++ b/backends/keymapper/keymap.cpp @@ -128,7 +128,8 @@ Keymap::ActionArray Keymap::getMappedActions(const Event &event) const { switch (event.type) { case EVENT_KEYDOWN: case EVENT_KEYUP: { - HardwareInput hardwareInput = HardwareInput::createKeyboard("", event.kbd, ""); + KeyState normalizedKeystate = KeyboardHardwareInputSet::normalizeKeyState(event.kbd); + HardwareInput hardwareInput = HardwareInput::createKeyboard("", normalizedKeystate, ""); return _hwActionMap[hardwareInput]; } case EVENT_LBUTTONDOWN: diff --git a/backends/keymapper/keymap.h b/backends/keymapper/keymap.h index a5f7df69277..d860c4107da 100644 --- a/backends/keymapper/keymap.h +++ b/backends/keymapper/keymap.h @@ -44,20 +44,21 @@ struct HardwareInput; class HardwareInputSet; class KeymapperDefaultBindings; -struct Event_EqualTo { +struct HardwareInput_EqualTo { bool operator()(const HardwareInput& x, const HardwareInput& y) const { return (x.type == y.type) - && (x.key == y.key) // TODO: Remove the equality operator from KeyState + && (x.key.keycode == y.key.keycode) + && (x.key.flags == y.key.flags) && (x.inputCode == y.inputCode); } }; -struct Event_Hash { +struct HardwareInput_Hash { uint operator()(const HardwareInput& x) const { uint hash = 7; hash = 31 * hash + x.type; hash = 31 * hash + x.key.keycode; - hash = 31 * hash + (x.key.flags & ~KBD_STICKY); + hash = 31 * hash + x.key.flags; hash = 31 * hash + x.inputCode; return hash; } @@ -169,7 +170,7 @@ private: void registerMappings(Action *action, const StringArray &hwInputIds); bool areMappingsIdentical(const Array &inputs, const StringArray &mapping); - typedef HashMap HardwareActionMap; + typedef HashMap HardwareActionMap; KeymapType _type; String _id; diff --git a/common/keyboard.h b/common/keyboard.h index ca26e6bbfc3..86ab6f9d08a 100644 --- a/common/keyboard.h +++ b/common/keyboard.h @@ -346,8 +346,8 @@ struct KeyState { /** * Check if two key states are equal. This implementation ignores the state - * of the sticky flags (caps lock, num lock, scroll lock) completely. This - * functionality is currently only used by the keymapper. + * of the sticky flags (caps lock, num lock, scroll lock) completely. + * @todo is this still being used? */ bool operator==(const KeyState &x) const { // Intentionally ignore ASCII, as the keycode and non-sticky flag