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
#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<const HardwareInput *> Keymap::getActionMapping(Action *action) const {
Array<const HardwareInput *> 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<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
*/
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
* @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<const HardwareInput *, Action *> HardwareActionMap;
typedef HashMap<const HardwareInput *, ActionArray> HardwareActionMap;
KeymapType _type;
String _name;

View File

@ -135,6 +135,8 @@ List<Event> Keymapper::mapEvent(const Event &ev, EventSource *source) {
return DefaultEventMapper::mapEvent(ev, source);
}
IncomingEventType incomingEventType = convertToIncomingEventType(ev);
List<Event> mappedEvents;
for (int i = _keymaps.size() - 1; i >= 0; --i) {
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());
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;
}
}

View File

@ -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<const HardwareInput *> 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("-");
}