KEYMAPPER: Allow mapped actions without modifier keys when no mapped actions exist for current modifier keys

This commit is contained in:
Matthew Jimenez 2020-05-26 22:00:49 -05:00 committed by Bastien Bouclet
parent bc3a642cb7
commit b4083476d5
4 changed files with 102 additions and 51 deletions

View File

@ -124,74 +124,108 @@ const Action *Keymap::findAction(const char *id) const {
return nullptr;
}
Keymap::ActionArray Keymap::getMappedActions(const Event &event) const {
Keymap::KeymapMatch Keymap::getMappedActions(const Event &event, ActionArray &actions) const {
switch (event.type) {
case EVENT_KEYDOWN:
case EVENT_KEYUP: {
KeyState normalizedKeystate = KeyboardHardwareInputSet::normalizeKeyState(event.kbd);
HardwareInput hardwareInput = HardwareInput::createKeyboard("", normalizedKeystate, "");
return _hwActionMap[hardwareInput];
actions.push_back(_hwActionMap[hardwareInput]);
if (!actions.empty()) {
return kKeymapMatchExact;
}
if (normalizedKeystate.flags & KBD_NON_STICKY) {
// If no matching actions and non-sticky keyboard modifiers are down,
// check again for matches without the exact keyboard modifiers
for (HardwareActionMap::const_iterator itInput = _hwActionMap.begin(); itInput != _hwActionMap.end(); ++itInput) {
if (itInput->_key.type == kHardwareInputTypeKeyboard && itInput->_key.key.keycode == normalizedKeystate.keycode) {
int flags = itInput->_key.key.flags;
if (flags & KBD_NON_STICKY && (flags & normalizedKeystate.flags) == flags) {
actions.push_back(itInput->_value);
return kKeymapMatchPartial;
}
}
}
// Lastly check again for matches no non-sticky keyboard modifiers
normalizedKeystate.flags &= ~KBD_NON_STICKY;
hardwareInput = HardwareInput::createKeyboard("", normalizedKeystate, "");
actions.push_back(_hwActionMap[hardwareInput]);
return actions.empty() ? kKeymapMatchNone : kKeymapMatchPartial;
}
break;
}
case EVENT_LBUTTONDOWN:
case EVENT_LBUTTONUP: {
HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_BUTTON_LEFT, "");
return _hwActionMap[hardwareInput];
actions.push_back(_hwActionMap[hardwareInput]);
break;
}
case EVENT_RBUTTONDOWN:
case EVENT_RBUTTONUP: {
HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_BUTTON_RIGHT, "");
return _hwActionMap[hardwareInput];
actions.push_back(_hwActionMap[hardwareInput]);
break;
}
case EVENT_MBUTTONDOWN:
case EVENT_MBUTTONUP: {
HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_BUTTON_MIDDLE, "");
return _hwActionMap[hardwareInput];
actions.push_back(_hwActionMap[hardwareInput]);
break;
}
case Common::EVENT_WHEELUP: {
HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_WHEEL_UP, "");
return _hwActionMap[hardwareInput];
actions.push_back(_hwActionMap[hardwareInput]);
break;
}
case Common::EVENT_WHEELDOWN: {
HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_WHEEL_DOWN, "");
return _hwActionMap[hardwareInput];
actions.push_back(_hwActionMap[hardwareInput]);
break;
}
case EVENT_X1BUTTONDOWN:
case EVENT_X1BUTTONUP: {
HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_BUTTON_X1, "");
return _hwActionMap[hardwareInput];
actions.push_back(_hwActionMap[hardwareInput]);
break;
}
case EVENT_X2BUTTONDOWN:
case EVENT_X2BUTTONUP: {
HardwareInput hardwareInput = HardwareInput::createMouse("", MOUSE_BUTTON_X2, "");
return _hwActionMap[hardwareInput];
actions.push_back(_hwActionMap[hardwareInput]);
break;
}
case EVENT_JOYBUTTON_DOWN:
case EVENT_JOYBUTTON_UP: {
HardwareInput hardwareInput = HardwareInput::createJoystickButton("", event.joystick.button, "");
return _hwActionMap[hardwareInput];
actions.push_back(_hwActionMap[hardwareInput]);
break;
}
case EVENT_JOYAXIS_MOTION: {
if (event.joystick.position != 0) {
bool positiveHalf = event.joystick.position >= 0;
HardwareInput hardwareInput = HardwareInput::createJoystickHalfAxis("", event.joystick.axis, positiveHalf, "");
return _hwActionMap[hardwareInput];
actions.push_back(_hwActionMap[hardwareInput]);
} else {
// Axis position zero is part of both half axes, and triggers actions bound to both
Keymap::ActionArray actions;
HardwareInput hardwareInputPos = HardwareInput::createJoystickHalfAxis("", event.joystick.axis, true, "");
HardwareInput hardwareInputNeg = HardwareInput::createJoystickHalfAxis("", event.joystick.axis, false, "");
actions.push_back(_hwActionMap[hardwareInputPos]);
actions.push_back(_hwActionMap[hardwareInputNeg]);
return actions;
}
break;
}
case EVENT_CUSTOM_BACKEND_HARDWARE: {
HardwareInput hardwareInput = HardwareInput::createCustom("", event.customType, "");
return _hwActionMap[hardwareInput];
actions.push_back(_hwActionMap[hardwareInput]);
break;
}
default:
return ActionArray();
break;
}
return actions.empty() ? kKeymapMatchNone : kKeymapMatchExact;
}
void Keymap::setConfigDomain(ConfigManager::Domain *configDomain) {

View File

@ -72,6 +72,12 @@ public:
kKeymapTypeGame
};
enum KeymapMatch {
kKeymapMatchNone,
kKeymapMatchPartial,
kKeymapMatchExact
};
typedef Array<Action *> ActionArray;
Keymap(KeymapType type, const String &id, const String &description);
@ -109,9 +115,10 @@ public:
/**
* Find the Actions that a hardware input is mapped to
* @param hardwareInput the input that is mapped to the required Action
* @return an array containing pointers to the actions
* @param actions an array containing pointers to the actions
* @return the matching status for the retieved actions
*/
ActionArray getMappedActions(const Event &event) const;
KeymapMatch getMappedActions(const Event &event, ActionArray &actions) const;
/**
* Adds a new Action to this Map

View File

@ -170,13 +170,40 @@ List<Event> Keymapper::mapEvent(const Event &ev) {
hardcodedEventMapping(ev);
List<Event> mappedEvents;
bool matchedAction = mapEvent(ev, _enabledKeymapType, mappedEvents);
if (!matchedAction) {
// If we found actions matching this input in the game / gui keymaps,
Keymap::ActionArray actions;
Keymap::KeymapMatch match = getMappedActions(ev, actions, _enabledKeymapType);
if (match != Keymap::kKeymapMatchExact) {
// If we found exact matching actions this input in the game / gui keymaps,
// no need to look at the global keymaps. An input resulting in actions
// from system and game keymaps would lead to unexpected user experience.
matchedAction = mapEvent(ev, Keymap::kKeymapTypeGlobal, mappedEvents);
Keymap::ActionArray globalActions;
match = getMappedActions(ev, globalActions, Keymap::kKeymapTypeGlobal);
if (match == Keymap::kKeymapMatchExact || actions.empty()) {
actions = globalActions;
}
}
bool matchedAction = !actions.empty();
List<Event> mappedEvents;
for (Keymap::ActionArray::const_iterator it = actions.begin(); it != actions.end(); it++) {
Event mappedEvent = executeAction(*it, ev);
if (mappedEvent.type == EVENT_INVALID) {
continue;
}
// In case we mapped a mouse event to something else, we need to generate an artificial
// mouse move event so event observers can keep track of the mouse position.
// Makes it possible to reliably use the mouse position from EventManager when consuming
// custom action events.
if (isMouseEvent(ev) && !isMouseEvent(mappedEvent)) {
Event fakeMouseEvent;
fakeMouseEvent.type = EVENT_MOUSEMOVE;
fakeMouseEvent.mouse = ev.mouse;
mappedEvents.push_back(fakeMouseEvent);
}
mappedEvents.push_back(mappedEvent);
}
if (ev.type == EVENT_JOYAXIS_MOTION && ev.joystick.axis < ARRAYSIZE(_joystickAxisPreviouslyPressed)) {
@ -195,42 +222,25 @@ List<Event> Keymapper::mapEvent(const Event &ev) {
return mappedEvents;
}
bool Keymapper::mapEvent(const Event &ev, Keymap::KeymapType keymapType, List<Event> &mappedEvents) {
bool matchedAction = false;
Keymap::KeymapMatch Keymapper::getMappedActions(const Event &event, Keymap::ActionArray &actions, Keymap::KeymapType keymapType) const {
Keymap::KeymapMatch match = Keymap::kKeymapMatchNone;
for (uint i = 0; i < _keymaps.size(); i++) {
if (!_keymaps[i]->isEnabled() || _keymaps[i]->getType() != keymapType) {
continue;
}
Keymap::ActionArray actions = _keymaps[i]->getMappedActions(ev);
if (!actions.empty()) {
matchedAction = true;
}
for (Keymap::ActionArray::const_iterator it = actions.begin(); it != actions.end(); it++) {
Event mappedEvent = executeAction(*it, ev);
if (mappedEvent.type == EVENT_INVALID) {
continue;
}
// In case we mapped a mouse event to something else, we need to generate an artificial
// mouse move event so event observers can keep track of the mouse position.
// Makes it possible to reliably use the mouse position from EventManager when consuming
// custom action events.
if (isMouseEvent(ev) && !isMouseEvent(mappedEvent)) {
Event fakeMouseEvent;
fakeMouseEvent.type = EVENT_MOUSEMOVE;
fakeMouseEvent.mouse = ev.mouse;
mappedEvents.push_back(fakeMouseEvent);
}
mappedEvents.push_back(mappedEvent);
Keymap::ActionArray array;
Keymap::KeymapMatch match2 = _keymaps[i]->getMappedActions(event, array);
if (match2 == match) {
actions.push_back(array);
} else if (match2 > match) {
match = match2;
actions.clear();
actions.push_back(array);
}
}
return matchedAction;
return match;
}
Keymapper::IncomingEventType Keymapper::convertToIncomingEventType(const Event &ev) const {

View File

@ -150,7 +150,7 @@ private:
bool _joystickAxisPreviouslyPressed[6];
bool mapEvent(const Event &ev, Keymap::KeymapType keymapType, List<Event> &mappedEvents);
Keymap::KeymapMatch getMappedActions(const Event &event, Keymap::ActionArray &actions, Keymap::KeymapType keymapType) const;
Event executeAction(const Action *act, const Event &incomingEvent);
EventType convertStartToEnd(EventType eventType);
IncomingEventType convertToIncomingEventType(const Event &ev) const;