KEYMAPPER: Map non-key custom input events

This also makes the keymapper be in charge of keymapping logic
This commit is contained in:
Tarek Soliman 2012-02-22 18:30:47 -06:00
parent 4ee1a3acea
commit d12f21b31d
11 changed files with 238 additions and 84 deletions

View File

@ -33,15 +33,17 @@
namespace Common {
Keymap::Keymap(const Keymap& km) : _actions(km._actions), _keymap(), _configDomain(0) {
Keymap::Keymap(const Keymap& km) : _actions(km._actions), _keymap(), _nonkeymap(), _configDomain(0) {
List<Action *>::iterator it;
for (it = _actions.begin(); it != _actions.end(); ++it) {
const HardwareInput *hwInput = (*it)->getMappedInput();
//FIXME: Add support for kHardwareInputTypeGeneric
if (hwInput && hwInput->type == kHardwareInputTypeKeyboard) {
_keymap[hwInput->key] = *it;
if (hwInput) {
if (hwInput->type == kHardwareInputTypeKeyboard)
_keymap[hwInput->key] = *it;
else if (hwInput->type == kHardwareInputTypeGeneric)
_nonkeymap[hwInput->inputCode] = *it;
}
}
}
@ -61,17 +63,20 @@ void Keymap::addAction(Action *action) {
}
void Keymap::registerMapping(Action *action, const HardwareInput *hwInput) {
HashMap<KeyState, Action *>::iterator it;
//FIXME: Add support for kHardwareInputTypeGeneric
if (hwInput->type == kHardwareInputTypeKeyboard) {
it = _keymap.find(hwInput->key);
// if key is already mapped to a different action then un-map it
if (it != _keymap.end() && action != it->_value) {
HashMap<KeyState, Action *>::iterator it = _keymap.find(hwInput->key);
// if input is already mapped to a different action then unmap it from there
if (it != _keymap.end() && action != it->_value)
it->_value->mapInput(0);
}
// now map it
_keymap[hwInput->key] = action;
} else if (hwInput->type == kHardwareInputTypeGeneric) {
HashMap<HardwareInputCode, Action *>::iterator it = _nonkeymap.find(hwInput->inputCode);
// if input is already mapped to a different action then unmap it from there
if (it != _nonkeymap.end() && action != it->_value)
it->_value->mapInput(0);
// now map it
_nonkeymap[hwInput->inputCode] = action;
}
}
@ -79,9 +84,10 @@ void Keymap::unregisterMapping(Action *action) {
const HardwareInput *hwInput = action->getMappedInput();
if (hwInput) {
//FIXME: Add support for kHardwareInputTypeGeneric
if (hwInput->type == kHardwareInputTypeKeyboard)
_keymap.erase(hwInput->key);
else if (hwInput->type == kHardwareInputTypeGeneric)
_nonkeymap.erase(hwInput->inputCode);
}
}
@ -121,6 +127,17 @@ Action *Keymap::getMappedAction(const KeyState& ks) const {
return it->_value;
}
Action *Keymap::getMappedAction(const HardwareInputCode code) const {
HashMap<HardwareInputCode, Action *>::iterator it;
it = _nonkeymap.find(code);
if (it == _nonkeymap.end())
return 0;
else
return it->_value;
}
void Keymap::setConfigDomain(ConfigManager::Domain *dom) {
_configDomain = dom;
}

View File

@ -33,12 +33,10 @@
#include "common/keyboard.h"
#include "common/list.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/hardware-input.h"
namespace Common {
struct HardwareInput;
class HardwareInputSet;
/**
* Hash function for KeyState
*/
@ -76,6 +74,13 @@ public:
*/
Action *getMappedAction(const KeyState& ks) const;
/**
* Find the Action that a generic input is mapped to
* @param code the input code that is mapped to the required Action
* @return a pointer to the Action or 0 if no
*/
Action *getMappedAction(const HardwareInputCode code) const;
void setConfigDomain(ConfigManager::Domain *dom);
/**
@ -130,6 +135,7 @@ private:
String _name;
List<Action *> _actions;
HashMap<KeyState, Action *> _keymap;
HashMap<HardwareInputCode, Action *> _nonkeymap;
ConfigManager::Domain *_configDomain;
};

View File

@ -54,7 +54,7 @@ Keymap *Keymapper::Domain::getKeymap(const String& name) {
}
Keymapper::Keymapper(EventManager *evtMgr)
: _eventMan(evtMgr), _enabled(true), _hardwareInputs(0) {
: _eventMan(evtMgr), _enabled(true), _remapping(false), _hardwareInputs(0), _actionToRemap(0) {
ConfigManager::Domain *confDom = ConfMan.getDomain(ConfigManager::kKeymapperDomain);
_globalDomain.setConfigDomain(confDom);
@ -183,13 +183,16 @@ List<Event> Keymapper::mapEvent(const Event &ev, EventSource *source) {
if (source && !source->allowMapping()) {
return DefaultEventMapper::mapEvent(ev, source);
}
List<Event> mappedEvents;
if (ev.type == Common::EVENT_KEYDOWN)
if (_remapping)
mappedEvents = remap(ev);
else if (ev.type == Common::EVENT_KEYDOWN)
mappedEvents = mapKeyDown(ev.kbd);
else if (ev.type == Common::EVENT_KEYUP)
mappedEvents = mapKeyUp(ev.kbd);
else if (ev.type == Common::EVENT_CUSTOM_BACKEND_HARDWARE)
mappedEvents = mapNonKey(ev.customType);
if (!mappedEvents.empty())
return mappedEvents;
@ -197,6 +200,13 @@ List<Event> Keymapper::mapEvent(const Event &ev, EventSource *source) {
return DefaultEventMapper::mapEvent(ev, source);
}
void Keymapper::startRemappingMode(Action *actionToRemap) {
assert(!_remapping);
_remapping = true;
_actionToRemap = actionToRemap;
}
List<Event> Keymapper::mapKeyDown(const KeyState& key) {
return mapKey(key, true);
}
@ -236,7 +246,30 @@ List<Event> Keymapper::mapKey(const KeyState& key, bool keyDown) {
if (!action)
return List<Event>();
return executeAction(action, keyDown);
return executeAction(action, keyDown ? kIncomingKeyDown : kIncomingKeyUp);
}
List<Event> Keymapper::mapNonKey(const HardwareInputCode code) {
if (!_enabled || _activeMaps.empty())
return List<Event>();
Action *action = 0;
// Search for nonkey in active keymap stack
for (int i = _activeMaps.size() - 1; i >= 0; --i) {
MapRecord mr = _activeMaps[i];
debug(5, "Keymapper::mapKey keymap: %s", mr.keymap->getName().c_str());
action = mr.keymap->getMappedAction(code);
if (action || !mr.transparent)
break;
}
if (!action)
return List<Event>();
return executeAction(action);
}
Action *Keymapper::getAction(const KeyState& key) {
@ -245,52 +278,57 @@ Action *Keymapper::getAction(const KeyState& key) {
return action;
}
List<Event> Keymapper::executeAction(const Action *action, bool keyDown) {
List<Event> Keymapper::executeAction(const Action *action, IncomingEventType incomingType) {
List<Event> mappedEvents;
List<Event>::const_iterator it;
for (it = action->events.begin(); it != action->events.end(); ++it) {
Event evt = *it;
Event evt = Event(*it);
EventType convertedType = convertDownToUp(evt.type);
switch (evt.type) {
case EVENT_KEYDOWN:
if (!keyDown) evt.type = EVENT_KEYUP;
break;
case EVENT_KEYUP:
if (keyDown) evt.type = EVENT_KEYDOWN;
break;
case EVENT_LBUTTONDOWN:
if (!keyDown) evt.type = EVENT_LBUTTONUP;
break;
case EVENT_LBUTTONUP:
if (keyDown) evt.type = EVENT_LBUTTONDOWN;
break;
case EVENT_RBUTTONDOWN:
if (!keyDown) evt.type = EVENT_RBUTTONUP;
break;
case EVENT_RBUTTONUP:
if (keyDown) evt.type = EVENT_RBUTTONDOWN;
break;
case EVENT_MBUTTONDOWN:
if (!keyDown) evt.type = EVENT_MBUTTONUP;
break;
case EVENT_MBUTTONUP:
if (keyDown) evt.type = EVENT_MBUTTONDOWN;
break;
case EVENT_MAINMENU:
if (!keyDown) evt.type = EVENT_MAINMENU;
break;
default:
// don't deliver other events on key up
if (!keyDown) continue;
// hardware keys need to send up instead when they are up
if (incomingType == kIncomingKeyUp) {
if (convertedType == EVENT_INVALID)
continue; // don't send any non-down-converted events on up they were already sent on down
evt.type = convertedType;
}
evt.mouse = _eventMan->getMousePos();
mappedEvents.push_back(evt);
// non-keys need to send up as well
// TODO: implement a way to add a delay
if (incomingType == kIncomingNonKey) {
if (convertedType == EVENT_INVALID)
continue; // don't send any non-down-converted events on up they were already sent on down
evt.type = convertedType;
mappedEvents.push_back(evt);
}
}
return mappedEvents;
}
EventType Keymapper::convertDownToUp(EventType type) {
EventType result = EVENT_INVALID;
switch (type) {
case EVENT_KEYDOWN:
result = EVENT_KEYUP;
break;
case EVENT_LBUTTONDOWN:
result = EVENT_LBUTTONUP;
break;
case EVENT_RBUTTONDOWN:
result = EVENT_RBUTTONUP;
break;
case EVENT_MBUTTONDOWN:
result = EVENT_MBUTTONUP;
break;
default:
break;
}
return result;
}
const HardwareInput *Keymapper::findHardwareInput(const KeyState& key) {
return (_hardwareInputs) ? _hardwareInputs->findHardwareInput(key) : 0;
}
@ -299,6 +337,41 @@ const HardwareInput *Keymapper::findHardwareInput(const HardwareInputCode code)
return (_hardwareInputs) ? _hardwareInputs->findHardwareInput(code) : 0;
}
List<Event> Keymapper::remap(const Event &ev) {
assert(_remapping);
assert(_actionToRemap);
List<Event> list;
const HardwareInput *hwInput = 0;
Event mappedEvent;
switch (ev.type) {
case EVENT_KEYDOWN:
// eat the event by returning an event invalid
mappedEvent.type = EVENT_INVALID;
list.push_back(mappedEvent);
break;
case EVENT_KEYUP:
hwInput = findHardwareInput(ev.kbd);
break;
case EVENT_CUSTOM_BACKEND_HARDWARE:
hwInput = findHardwareInput(ev.customType);
break;
default:
break;
}
if (hwInput) {
_actionToRemap->mapInput(hwInput);
_actionToRemap->getParent()->saveMappings();
_remapping = false;
_actionToRemap = 0;
mappedEvent.type = EVENT_GUI_REMAP_COMPLETE_ACTION;
list.push_back(mappedEvent);
}
return list;
}
} // End of namespace Common
#endif // #ifdef ENABLE_KEYMAPPER

View File

@ -149,6 +149,7 @@ public:
* @return mapped events
*/
List<Event> mapKey(const KeyState& key, bool keyDown);
List<Event> mapNonKey(const HardwareInputCode code);
/**
* @brief Map a key down event.
@ -167,6 +168,24 @@ public:
*/
void setEnabled(bool enabled) { _enabled = enabled; }
/**
* @brief Activate remapping mode
* While this mode is active, any mappable event will be bound to the action
* provided.
* @param actionToRemap Action that is the target of the remap
*/
void startRemappingMode(Action *actionToRemap);
/**
* @brief Force-stop the remapping mode
*/
void stopRemappingMode() { _remapping = false; }
/**
* Query whether the keymapper is currently in the remapping mode
*/
bool isRemapping() const { return _remapping; }
/**
* Return a HardwareInput pointer for the given key state
*/
@ -183,6 +202,12 @@ public:
private:
enum IncomingEventType {
kIncomingKeyDown,
kIncomingKeyUp,
kIncomingNonKey
};
void initKeymap(Domain &domain, Keymap *keymap);
Domain _globalDomain;
@ -193,12 +218,16 @@ private:
void pushKeymap(Keymap *newMap, bool transparent, bool global);
Action *getAction(const KeyState& key);
List<Event> executeAction(const Action *act, bool keyDown);
List<Event> executeAction(const Action *act, IncomingEventType incomingType = kIncomingNonKey);
EventType convertDownToUp(EventType eventType);
List<Event> remap(const Event &ev);
EventManager *_eventMan;
bool _enabled;
bool _remapping;
Action *_actionToRemap;
Stack<MapRecord> _activeMaps;
HashMap<KeyState, Action *> _keysDown;

View File

@ -39,7 +39,7 @@ enum {
};
RemapDialog::RemapDialog()
: Dialog("KeyMapper"), _keymapTable(0), _activeRemapAction(0), _topAction(0), _remapTimeout(0), _topKeymapIsGui(false) {
: Dialog("KeyMapper"), _keymapTable(0), _topAction(0), _remapTimeout(0), _topKeymapIsGui(false) {
_keymapper = g_system->getEventManager()->getKeymapper();
assert(_keymapper);
@ -243,16 +243,14 @@ void RemapDialog::clearMapping(uint i) {
return;
debug(3, "clear the mapping %u", i);
_activeRemapAction = _currentActions[_topAction + i].action;
_activeRemapAction->mapInput(0);
_activeRemapAction->getParent()->saveMappings();
Action *activeRemapAction = _currentActions[_topAction + i].action;
activeRemapAction->mapInput(0);
activeRemapAction->getParent()->saveMappings();
_changes = true;
// force refresh
_topAction = -1;
stopRemapping(true);
refreshKeymap();
_activeRemapAction = 0;
}
void RemapDialog::startRemapping(uint i) {
@ -260,57 +258,56 @@ void RemapDialog::startRemapping(uint i) {
return;
_remapTimeout = g_system->getMillis() + kRemapTimeoutDelay;
_activeRemapAction = _currentActions[_topAction + i].action;
Action *activeRemapAction = _currentActions[_topAction + i].action;
_keymapWidgets[i].keyButton->setLabel("...");
_keymapWidgets[i].keyButton->draw();
_keymapper->setEnabled(false);
_keymapper->startRemappingMode(activeRemapAction);
}
void RemapDialog::stopRemapping() {
void RemapDialog::stopRemapping(bool force) {
_topAction = -1;
refreshKeymap();
_activeRemapAction = 0;
_keymapper->setEnabled(true);
if (force)
_keymapper->stopRemappingMode();
}
void RemapDialog::handleKeyDown(Common::KeyState state) {
if (_activeRemapAction)
if (_keymapper->isRemapping())
return;
GUI::Dialog::handleKeyDown(state);
}
void RemapDialog::handleKeyUp(Common::KeyState state) {
if (_activeRemapAction) {
const HardwareInput *hwInput = _keymapper->findHardwareInput(state);
if (_keymapper->isRemapping())
return;
debug(4, "RemapDialog::handleKeyUp Key: %d, %d (%c), %x", state.keycode, state.ascii, (state.ascii ? state.ascii : ' '), state.flags);
GUI::Dialog::handleKeyUp(state);
}
if (hwInput) {
_activeRemapAction->mapInput(hwInput);
_activeRemapAction->getParent()->saveMappings();
_changes = true;
stopRemapping();
}
void RemapDialog::handleOtherEvent(Event ev) {
if (ev.type == EVENT_GUI_REMAP_COMPLETE_ACTION) {
// _keymapper is telling us that something changed
_changes = true;
stopRemapping();
} else {
GUI::Dialog::handleKeyUp(state);
GUI::Dialog::handleOtherEvent(ev);
}
}
void RemapDialog::handleMouseDown(int x, int y, int button, int clickCount) {
if (_activeRemapAction)
if (_keymapper->isRemapping())
stopRemapping();
else
Dialog::handleMouseDown(x, y, button, clickCount);
}
void RemapDialog::handleTickle() {
if (_activeRemapAction && g_system->getMillis() > _remapTimeout)
stopRemapping();
if (_keymapper->isRemapping() && g_system->getMillis() > _remapTimeout)
stopRemapping(true);
Dialog::handleTickle();
}
@ -358,9 +355,10 @@ void RemapDialog::loadKeymap() {
while (inputIt != freeInputs.end()) {
Action *act = 0;
// FIXME: Add support for kHardwareInputTypeGeneric
if (input->type == kHardwareInputTypeKeyboard)
act = mr.keymap->getMappedAction(input->key);
else if (input->type == kHardwareInputTypeGeneric)
act = mr.keymap->getMappedAction(input->inputCode);
if (act) {
ActionInfo info = {act, true, act->description + " (" + mr.keymap->getName() + ")"};

View File

@ -50,6 +50,7 @@ public:
virtual void handleKeyUp(Common::KeyState state);
virtual void handleMouseDown(int x, int y, int button, int clickCount);
virtual void handleTickle();
virtual void handleOtherEvent(Common::Event ev);
protected:
struct ActionWidgets {
@ -67,7 +68,7 @@ protected:
void refreshKeymap();
void clearMapping(uint i);
void startRemapping(uint i);
void stopRemapping();
void stopRemapping(bool force = false);
Keymapper *_keymapper;
Keymap** _keymapTable;
@ -85,7 +86,6 @@ protected:
uint _rowCount;
Array<ActionWidgets> _keymapWidgets;
Action *_activeRemapAction;
uint32 _remapTimeout;
static const uint32 kRemapTimeoutDelay = 3000;

View File

@ -21,6 +21,7 @@
*/
#include "common/events.h"
#include "common/textconsole.h"
namespace Common {
@ -46,6 +47,15 @@ List<Event> DefaultEventMapper::mapEvent(const Event &ev, EventSource *source) {
// if it didn't get mapped, just pass it through
if (mappedEvent.type == EVENT_INVALID)
mappedEvent = ev;
#ifdef ENABLE_VKEYBD
// TODO: this check is not needed post-split
if (mappedEvent.type == EVENT_CUSTOM_BACKEND_HARDWARE) {
warning("EVENT_CUSTOM_BACKEND_HARDWARE was not mapped");
return List<Event>();
}
#endif
events.push_back(mappedEvent);
return events;
}

View File

@ -79,6 +79,8 @@ enum EventType {
// IMPORTANT NOTE: This is part of the WIP Keymapper. If you plan to use
// this, please talk to tsoliman and/or LordHoto.
EVENT_CUSTOM_BACKEND_ACTION = 18,
EVENT_CUSTOM_BACKEND_HARDWARE = 21,
EVENT_GUI_REMAP_COMPLETE_ACTION = 22,
EVENT_KEYMAPPER_REMAP = 19
#endif
#ifdef ENABLE_VKEYBD

View File

@ -21,6 +21,10 @@
#include "common/rect.h"
#ifdef ENABLE_KEYMAPPER
#include "common/events.h"
#endif
#include "gui/gui-manager.h"
#include "gui/dialog.h"
#include "gui/widget.h"
@ -314,6 +318,9 @@ void Dialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
}
}
#ifdef ENABLE_KEYMAPPER
void Dialog::handleOtherEvent(Common::Event evt) { }
#endif
/*
* Determine the widget at location (x,y) if any. Assumes the coordinates are
* in the local coordinate system, i.e. relative to the top left of the dialog.

View File

@ -29,6 +29,12 @@
#include "gui/object.h"
#include "gui/ThemeEngine.h"
#ifdef ENABLE_KEYMAPPER
namespace Common {
struct Event;
}
#endif
namespace GUI {
class Widget;
@ -82,6 +88,9 @@ protected:
virtual void handleKeyUp(Common::KeyState state);
virtual void handleMouseMoved(int x, int y, int button);
virtual void handleCommand(CommandSender *sender, uint32 cmd, uint32 data);
#ifdef ENABLE_KEYMAPPER
virtual void handleOtherEvent(Common::Event evt);
#endif
Widget *findWidget(int x, int y); // Find the widget at pos x,y if any
Widget *findWidget(const char *name);

View File

@ -366,6 +366,9 @@ void GuiManager::runLoop() {
screenChange();
break;
default:
#ifdef ENABLE_KEYMAPPER
activeDialog->handleOtherEvent(event);
#endif
break;
}