KEYMAPPER: Multiple inputs can map to the same action

This commit is contained in:
Bastien Bouclet 2017-08-14 13:58:43 +02:00
parent d5e2b5d8f2
commit ade0efa762
4 changed files with 84 additions and 41 deletions

View file

@ -25,6 +25,7 @@
#ifdef ENABLE_KEYMAPPER #ifdef ENABLE_KEYMAPPER
#include "common/system.h" #include "common/system.h"
#include "common/tokenizer.h"
#include "backends/keymapper/action.h" #include "backends/keymapper/action.h"
#include "backends/keymapper/hardware-input.h" #include "backends/keymapper/hardware-input.h"
@ -54,27 +55,43 @@ void Keymap::addAction(Action *action) {
} }
void Keymap::registerMapping(Action *action, const HardwareInput *hwInput) { void Keymap::registerMapping(Action *action, const HardwareInput *hwInput) {
unregisterMapping(action); ActionArray &actionArray = _hwActionMap.getVal(hwInput);
_hwActionMap[hwInput] = action; // Don't allow an input to map to the same action multiple times
ActionArray::const_iterator found = find(actionArray.begin(), actionArray.end(), action);
if (found == actionArray.end()) {
actionArray.push_back(action);
}
} }
void Keymap::unregisterMapping(Action *action) { void Keymap::unregisterMapping(Action *action) {
for (HardwareActionMap::iterator it = _hwActionMap.begin(); it != _hwActionMap.end(); it++) { // Remove the action from all the input mappings
if (it->_value == action) { for (HardwareActionMap::iterator itInput = _hwActionMap.begin(); itInput != _hwActionMap.end(); itInput++) {
_hwActionMap.erase(it); for (ActionArray::iterator itAction = itInput->_value.begin(); itAction != itInput->_value.end(); itAction++) {
if (*itAction == action) {
itInput->_value.erase(itAction);
break;
}
}
if (itInput->_value.empty()) {
_hwActionMap.erase(itInput);
} }
} }
} }
const HardwareInput *Keymap::getActionMapping(Action *action) const { Array<const HardwareInput *> Keymap::getActionMapping(Action *action) const {
for (HardwareActionMap::const_iterator it = _hwActionMap.begin(); it != _hwActionMap.end(); it++) { Array<const HardwareInput *> inputs;
if (it->_value == action) {
return it->_key; for (HardwareActionMap::iterator itInput = _hwActionMap.begin(); itInput != _hwActionMap.end(); itInput++) {
for (ActionArray::iterator itAction = itInput->_value.begin(); itAction != itInput->_value.end(); itAction++) {
if (*itAction == action) {
inputs.push_back(itInput->_key);
break;
}
} }
} }
return nullptr; return inputs;
} }
const Action *Keymap::findAction(const char *id) const { const Action *Keymap::findAction(const char *id) const {
@ -86,7 +103,7 @@ const Action *Keymap::findAction(const char *id) const {
return nullptr; return nullptr;
} }
Action *Keymap::getMappedAction(const HardwareInput *hardwareInput) const { const Keymap::ActionArray &Keymap::getMappedActions(const HardwareInput *hardwareInput) const {
return _hwActionMap[hardwareInput]; return _hwActionMap[hardwareInput];
} }
@ -95,25 +112,25 @@ void Keymap::setConfigDomain(ConfigManager::Domain *dom) {
} }
void Keymap::loadMappings(const HardwareInputSet *hwKeys) { void Keymap::loadMappings(const HardwareInputSet *hwKeys) {
if (!_configDomain) assert(_configDomain);
return;
if (_actions.empty()) if (_actions.empty()) {
return; return;
}
String prefix = KEYMAP_KEY_PREFIX + _name + "_"; String prefix = KEYMAP_KEY_PREFIX + _name + "_";
_hwActionMap.clear();
for (ActionArray::const_iterator it = _actions.begin(); it != _actions.end(); ++it) { for (ActionArray::const_iterator it = _actions.begin(); it != _actions.end(); ++it) {
Action* ua = *it; Action* ua = *it;
String actionId(ua->id); String actionId(ua->id);
String confKey = prefix + actionId; String confKey = prefix + actionId;
String hwInputId = _configDomain->getVal(confKey); // The configuration value is a list of space separated hardware input ids
StringTokenizer hwInputIds = _configDomain->getVal(confKey);
// there's no mapping
if (hwInputId.empty())
continue;
String hwInputId;
while ((hwInputId = hwInputIds.nextToken()) != "") {
const HardwareInput *hwInput = hwKeys->findHardwareInput(hwInputId.c_str()); const HardwareInput *hwInput = hwKeys->findHardwareInput(hwInputId.c_str());
if (!hwInput) { if (!hwInput) {
@ -122,7 +139,8 @@ void Keymap::loadMappings(const HardwareInputSet *hwKeys) {
} }
// map the key // map the key
_hwActionMap[hwInput] = ua; registerMapping(ua, hwInput);
}
} }
} }
@ -132,11 +150,21 @@ void Keymap::saveMappings() {
String prefix = KEYMAP_KEY_PREFIX + _name + "_"; String prefix = KEYMAP_KEY_PREFIX + _name + "_";
for (HardwareActionMap::const_iterator it = _hwActionMap.begin(); it != _hwActionMap.end(); it++) { for (ActionArray::const_iterator it = _actions.begin(); it != _actions.end(); it++) {
const Action *action = it->_value; Action *action = *it;
const HardwareInput *input = it->_key; Array<const HardwareInput *> mappedInputs = getActionMapping(action);
_configDomain->setVal(prefix + action->id, input->id); // The configuration value is a list of space separated hardware input ids
String confValue;
for (uint j = 0; j < mappedInputs.size(); j++) {
if (!confValue.empty()) {
confValue += " ";
}
confValue += mappedInputs[j]->id;
}
_configDomain->setVal(prefix + action->id, confValue);
} }
} }

View file

@ -70,14 +70,14 @@ public:
/** /**
* Find the hardware input an action is mapped to, if any * Find the hardware input an action is mapped to, if any
*/ */
const HardwareInput *getActionMapping(Action *action) const; Array<const HardwareInput *> getActionMapping(Action *action) const;
/** /**
* Find the Action that a hardware input is mapped to * Find the Actions that a hardware input is mapped to
* @param hardwareInput the input that is mapped to the required Action * @param hardwareInput the input that is mapped to the required Action
* @return a pointer to the Action or 0 if no * @return an array containing pointers to the actions
*/ */
Action *getMappedAction(const HardwareInput *hardwareInput) const; const ActionArray &getMappedActions(const HardwareInput *hardwareInput) const;
/** /**
* Get the list of all the Actions contained in this Keymap * Get the list of all the Actions contained in this Keymap
@ -120,7 +120,7 @@ private:
const Action *findAction(const char *id) const; const Action *findAction(const char *id) const;
typedef HashMap<const HardwareInput *, Action *> HardwareActionMap; typedef HashMap<const HardwareInput *, ActionArray> HardwareActionMap;
KeymapType _type; KeymapType _type;
String _name; String _name;

View file

@ -135,6 +135,8 @@ List<Event> Keymapper::mapEvent(const Event &ev, EventSource *source) {
return DefaultEventMapper::mapEvent(ev, source); return DefaultEventMapper::mapEvent(ev, source);
} }
IncomingEventType incomingEventType = convertToIncomingEventType(ev);
List<Event> mappedEvents; List<Event> mappedEvents;
for (int i = _keymaps.size() - 1; i >= 0; --i) { for (int i = _keymaps.size() - 1; i >= 0; --i) {
if (!_keymaps[i]->isEnabled()) { if (!_keymaps[i]->isEnabled()) {
@ -148,10 +150,13 @@ List<Event> Keymapper::mapEvent(const Event &ev, EventSource *source) {
debug(5, "Keymapper::mapKey keymap: %s", _keymaps[i]->getName().c_str()); debug(5, "Keymapper::mapKey keymap: %s", _keymaps[i]->getName().c_str());
Action *action = _keymaps[i]->getMappedAction(hwInput); const Keymap::ActionArray &actions = _keymaps[i]->getMappedActions(hwInput);
if (action) { for (Keymap::ActionArray::const_iterator it = actions.begin(); it != actions.end(); it++) {
IncomingEventType incomingEventType = convertToIncomingEventType(ev); mappedEvents.push_back(executeAction(*it, incomingEventType));
mappedEvents.push_back(executeAction(action, incomingEventType)); }
if (!actions.empty()) {
// If we found actions matching this input in a keymap, no need to look at the other keymaps.
// An input resulting in actions from system and game keymaps would lead to unexpected user experience.
break; break;
} }
} }

View file

@ -241,9 +241,19 @@ void RemapDialog::refreshKeymap() {
Keymap *keymap = row.action->getParent(); Keymap *keymap = row.action->getParent();
const HardwareInput *mappedInput = keymap->getActionMapping(row.action); Array<const HardwareInput *> mappedInputs = keymap->getActionMapping(row.action);
if (mappedInput)
row.keyButton->setLabel(mappedInput->description); String keysLabel;
for (uint j = 0; j < mappedInputs.size(); j++) {
if (!keysLabel.empty()) {
keysLabel += ", ";
}
keysLabel += mappedInputs[j]->description;
}
if (!keysLabel.empty())
row.keyButton->setLabel(keysLabel);
else else
row.keyButton->setLabel("-"); row.keyButton->setLabel("-");
} }