diff --git a/backends/keymapper/keymap.cpp b/backends/keymapper/keymap.cpp index bfb6416683d..101ae1f4047 100644 --- a/backends/keymapper/keymap.cpp +++ b/backends/keymapper/keymap.cpp @@ -25,6 +25,7 @@ #ifdef ENABLE_KEYMAPPER #include "common/system.h" +#include "common/tokenizer.h" #include "backends/keymapper/action.h" #include "backends/keymapper/hardware-input.h" @@ -54,27 +55,43 @@ void Keymap::addAction(Action *action) { } 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) { - for (HardwareActionMap::iterator it = _hwActionMap.begin(); it != _hwActionMap.end(); it++) { - if (it->_value == action) { - _hwActionMap.erase(it); + // Remove the action from all the input mappings + 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) { + itInput->_value.erase(itAction); + break; + } + } + if (itInput->_value.empty()) { + _hwActionMap.erase(itInput); } } } -const HardwareInput *Keymap::getActionMapping(Action *action) const { - for (HardwareActionMap::const_iterator it = _hwActionMap.begin(); it != _hwActionMap.end(); it++) { - if (it->_value == action) { - return it->_key; +Array Keymap::getActionMapping(Action *action) const { + Array inputs; + + 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 { @@ -86,7 +103,7 @@ const Action *Keymap::findAction(const char *id) const { return nullptr; } -Action *Keymap::getMappedAction(const HardwareInput *hardwareInput) const { +const Keymap::ActionArray &Keymap::getMappedActions(const HardwareInput *hardwareInput) const { return _hwActionMap[hardwareInput]; } @@ -95,34 +112,35 @@ void Keymap::setConfigDomain(ConfigManager::Domain *dom) { } void Keymap::loadMappings(const HardwareInputSet *hwKeys) { - if (!_configDomain) - return; + assert(_configDomain); - if (_actions.empty()) + if (_actions.empty()) { return; + } String prefix = KEYMAP_KEY_PREFIX + _name + "_"; + _hwActionMap.clear(); for (ActionArray::const_iterator it = _actions.begin(); it != _actions.end(); ++it) { Action* ua = *it; String actionId(ua->id); 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) { + warning("HardwareInput with ID '%s' not known", hwInputId.c_str()); + continue; + } - if (!hwInput) { - warning("HardwareInput with ID '%s' not known", hwInputId.c_str()); - continue; + // map the key + registerMapping(ua, hwInput); } - - // map the key - _hwActionMap[hwInput] = ua; } } @@ -132,11 +150,21 @@ void Keymap::saveMappings() { String prefix = KEYMAP_KEY_PREFIX + _name + "_"; - for (HardwareActionMap::const_iterator it = _hwActionMap.begin(); it != _hwActionMap.end(); it++) { - const Action *action = it->_value; - const HardwareInput *input = it->_key; + for (ActionArray::const_iterator it = _actions.begin(); it != _actions.end(); it++) { + Action *action = *it; + Array 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); } } diff --git a/backends/keymapper/keymap.h b/backends/keymapper/keymap.h index fd72c6f7732..deb3c3e3f88 100644 --- a/backends/keymapper/keymap.h +++ b/backends/keymapper/keymap.h @@ -70,14 +70,14 @@ public: /** * Find the hardware input an action is mapped to, if any */ - const HardwareInput *getActionMapping(Action *action) const; + Array 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 - * @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 @@ -120,7 +120,7 @@ private: const Action *findAction(const char *id) const; - typedef HashMap HardwareActionMap; + typedef HashMap HardwareActionMap; KeymapType _type; String _name; diff --git a/backends/keymapper/keymapper.cpp b/backends/keymapper/keymapper.cpp index 2fbd57116af..bf320143e8c 100644 --- a/backends/keymapper/keymapper.cpp +++ b/backends/keymapper/keymapper.cpp @@ -135,6 +135,8 @@ List Keymapper::mapEvent(const Event &ev, EventSource *source) { return DefaultEventMapper::mapEvent(ev, source); } + IncomingEventType incomingEventType = convertToIncomingEventType(ev); + List mappedEvents; for (int i = _keymaps.size() - 1; i >= 0; --i) { if (!_keymaps[i]->isEnabled()) { @@ -148,10 +150,13 @@ List Keymapper::mapEvent(const Event &ev, EventSource *source) { debug(5, "Keymapper::mapKey keymap: %s", _keymaps[i]->getName().c_str()); - Action *action = _keymaps[i]->getMappedAction(hwInput); - if (action) { - IncomingEventType incomingEventType = convertToIncomingEventType(ev); - mappedEvents.push_back(executeAction(action, incomingEventType)); + const Keymap::ActionArray &actions = _keymaps[i]->getMappedActions(hwInput); + for (Keymap::ActionArray::const_iterator it = actions.begin(); it != actions.end(); it++) { + mappedEvents.push_back(executeAction(*it, 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; } } diff --git a/backends/keymapper/remap-dialog.cpp b/backends/keymapper/remap-dialog.cpp index 82f402c970d..b78acab374c 100644 --- a/backends/keymapper/remap-dialog.cpp +++ b/backends/keymapper/remap-dialog.cpp @@ -241,9 +241,19 @@ void RemapDialog::refreshKeymap() { Keymap *keymap = row.action->getParent(); - const HardwareInput *mappedInput = keymap->getActionMapping(row.action); - if (mappedInput) - row.keyButton->setLabel(mappedInput->description); + Array mappedInputs = keymap->getActionMapping(row.action); + + 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 row.keyButton->setLabel("-"); }