/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "backends/keymapper/keymapper.h" #ifdef ENABLE_KEYMAPPER #include "common/config-manager.h" #include "common/system.h" namespace Common { // These magic numbers are provided by fuzzie and WebOS static const uint32 kDelayKeyboardEventMillis = 250; static const uint32 kDelayMouseEventMillis = 50; void Keymapper::Domain::addKeymap(Keymap *map) { iterator it = find(map->getName()); if (it != end()) delete it->_value; setVal(map->getName(), map); } void Keymapper::Domain::deleteAllKeyMaps() { for (iterator it = begin(); it != end(); ++it) delete it->_value; clear(); } Keymap *Keymapper::Domain::getKeymap(const String& name) { iterator it = find(name); if (it != end()) return it->_value; else return 0; } Keymapper::Keymapper(EventManager *evtMgr) : _eventMan(evtMgr), _enabled(true), _remapping(false), _hardwareInputs(0), _actionToRemap(0) { ConfigManager::Domain *confDom = ConfMan.getDomain(ConfigManager::kKeymapperDomain); _globalDomain.setConfigDomain(confDom); } Keymapper::~Keymapper() { delete _hardwareInputs; } void Keymapper::registerHardwareInputSet(HardwareInputSet *inputs) { if (_hardwareInputs) error("Hardware input set already registered"); if (!inputs) { warning("No hardware input were defined, using defaults"); inputs = new HardwareInputSet(true); } _hardwareInputs = inputs; } void Keymapper::addGlobalKeymap(Keymap *keymap) { initKeymap(_globalDomain, keymap); } void Keymapper::addGameKeymap(Keymap *keymap) { if (ConfMan.getActiveDomain() == 0) error("Call to Keymapper::addGameKeymap when no game loaded"); // Detect whether the active game changed since last call. // If so, flush the game key configuration. if (_gameDomain.getConfigDomain() != ConfMan.getActiveDomain()) { cleanupGameKeymaps(); _gameDomain.setConfigDomain(ConfMan.getActiveDomain()); } initKeymap(_gameDomain, keymap); } void Keymapper::initKeymap(Domain &domain, Keymap *map) { if (!_hardwareInputs) { warning("No hardware inputs were registered yet (%s)", map->getName().c_str()); return; } map->setConfigDomain(domain.getConfigDomain()); map->loadMappings(_hardwareInputs); if (map->isComplete(_hardwareInputs) == false) { map->saveMappings(); ConfMan.flushToDisk(); } domain.addKeymap(map); } void Keymapper::cleanupGameKeymaps() { // Flush all game specific keymaps _gameDomain.deleteAllKeyMaps(); // Now restore the stack of active maps. Re-add all global keymaps, drop // the game specific (=deleted) ones. Stack newStack; for (Stack::size_type i = 0; i < _activeMaps.size(); i++) { if (_activeMaps[i].global) newStack.push(_activeMaps[i]); } _activeMaps = newStack; } Keymap *Keymapper::getKeymap(const String& name, bool *globalReturn) { Keymap *keymap = _gameDomain.getKeymap(name); bool global = false; if (!keymap) { keymap = _globalDomain.getKeymap(name); global = true; } if (globalReturn) *globalReturn = global; return keymap; } bool Keymapper::pushKeymap(const String& name, bool transparent) { bool global; assert(!name.empty()); Keymap *newMap = getKeymap(name, &global); if (!newMap) { warning("Keymap '%s' not registered", name.c_str()); return false; } pushKeymap(newMap, transparent, global); return true; } void Keymapper::pushKeymap(Keymap *newMap, bool transparent, bool global) { MapRecord mr = {newMap, transparent, global}; _activeMaps.push(mr); } void Keymapper::popKeymap(const char *name) { if (!_activeMaps.empty()) { if (name) { String topKeymapName = _activeMaps.top().keymap->getName(); if (topKeymapName.equals(name)) _activeMaps.pop(); else warning("An attempt to pop wrong keymap was blocked (expected %s but was %s)", name, topKeymapName.c_str()); } else { _activeMaps.pop(); } } } List Keymapper::mapEvent(const Event &ev, EventSource *source) { if (source && !source->allowMapping()) { return DefaultEventMapper::mapEvent(ev, source); } List mappedEvents; 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; else return DefaultEventMapper::mapEvent(ev, source); } void Keymapper::startRemappingMode(Action *actionToRemap) { assert(!_remapping); _remapping = true; _actionToRemap = actionToRemap; } List Keymapper::mapKeyDown(const KeyState& key) { return mapKey(key, true); } List Keymapper::mapKeyUp(const KeyState& key) { return mapKey(key, false); } List Keymapper::mapKey(const KeyState& key, bool keyDown) { if (!_enabled || _activeMaps.empty()) return List(); Action *action = 0; if (keyDown) { // Search for key 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(key); if (action || !mr.transparent) break; } if (action) _keysDown[key] = action; } else { HashMap::iterator it = _keysDown.find(key); if (it != _keysDown.end()) { action = it->_value; _keysDown.erase(key); } } if (!action) return List(); return executeAction(action, keyDown ? kIncomingKeyDown : kIncomingKeyUp); } List Keymapper::mapNonKey(const HardwareInputCode code) { if (!_enabled || _activeMaps.empty()) return List(); 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(); return executeAction(action); } Action *Keymapper::getAction(const KeyState& key) { Action *action = 0; return action; } List Keymapper::executeAction(const Action *action, IncomingEventType incomingType) { List mappedEvents; List::const_iterator it; Event evt; for (it = action->events.begin(); it != action->events.end(); ++it) { evt = Event(*it); EventType convertedType = convertDownToUp(evt.type); // 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(); // Check if the event is coming from a non-key hardware event // that is mapped to a key event if (incomingType == kIncomingNonKey && convertedType != EVENT_INVALID) // WORKAROUND: Delay the down events coming from non-key hardware events // with a zero delay. This is to prevent DOWN1 DOWN2 UP1 UP2. addDelayedEvent(0, evt); else mappedEvents.push_back(evt); // non-keys need to send up as well if (incomingType == kIncomingNonKey && convertedType != EVENT_INVALID) { // WORKAROUND: Delay the up events coming from non-key hardware events // This is for engines that run scripts that check on key being down evt.type = convertedType; const uint32 delay = (convertedType == EVENT_KEYUP ? kDelayKeyboardEventMillis : kDelayMouseEventMillis); addDelayedEvent(delay, 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; } const HardwareInput *Keymapper::findHardwareInput(const HardwareInputCode code) { return (_hardwareInputs) ? _hardwareInputs->findHardwareInput(code) : 0; } List Keymapper::remap(const Event &ev) { assert(_remapping); assert(_actionToRemap); List 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