diff options
Diffstat (limited to 'backends')
-rw-r--r-- | backends/common/hardware-key.h | 95 | ||||
-rw-r--r-- | backends/common/keymap-manager.cpp | 65 | ||||
-rw-r--r-- | backends/common/keymap-manager.h | 51 | ||||
-rw-r--r-- | backends/common/keymap.cpp | 83 | ||||
-rw-r--r-- | backends/common/keymap.h | 89 | ||||
-rw-r--r-- | backends/common/keymapper.cpp | 61 | ||||
-rw-r--r-- | backends/common/keymapper.h | 46 | ||||
-rw-r--r-- | backends/common/user-action.h | 70 | ||||
-rw-r--r-- | backends/common/virtual-keyboard-parser.cpp | 365 | ||||
-rw-r--r-- | backends/common/virtual-keyboard-parser.h | 212 | ||||
-rw-r--r-- | backends/common/virtual-keyboard.cpp | 385 | ||||
-rw-r--r-- | backends/common/virtual-keyboard.h | 185 | ||||
-rw-r--r-- | backends/events/default/default-events.cpp | 35 | ||||
-rw-r--r-- | backends/events/default/default-events.h | 11 | ||||
-rw-r--r-- | backends/module.mk | 4 |
15 files changed, 1754 insertions, 3 deletions
diff --git a/backends/common/hardware-key.h b/backends/common/hardware-key.h new file mode 100644 index 0000000000..de1984dc31 --- /dev/null +++ b/backends/common/hardware-key.h @@ -0,0 +1,95 @@ +#ifndef COMMON_HARDWAREKEY +#define COMMON_HARDWAREKEY + +#include "backends/common/user-action.h" + +namespace Common { + +/** +* Describes an available hardware key +*/ +struct HardwareKey { + /** unique id used for saving/loading to config */ + int32 id; + /** Human readable description */ + String description; + /** + * The KeyState that is generated by the back-end + * when this hardware key is pressed. + */ + KeyState key; + + UserActionCategory preferredCategory; + UserActionType preferredType; + int16 group; + + HardwareKey(KeyState ks = KeyState(), String des = "", + UserActionCategory cat = kGenericUserActionCategory, + UserActionType ty = kGenericUserActionType, int gr = 0) { + key = ks; + description = des; + preferredCategory = cat; + preferredType = ty; + group = gr; + } +}; + + +/** + * Simple class to encapsulate a device's set of HardwareKeys. + * Each device should extend this and call addHardwareKey a number of times + * in its constructor to define the device's available keys. + */ +class HardwareKeySet { +public: + + HardwareKeySet() {} + ~HardwareKeySet() { + List<HardwareKey*>::iterator it; + for (it = _keys.begin(); it != _keys.end(); it++) + delete *it; + } + + void addHardwareKey(HardwareKey *key) { + checkForKey(key); + _keys.push_back(key); + } + + const HardwareKey *findHardwareKey(int32 id) const { + List<HardwareKey*>::iterator it; + for (it = _keys.begin(); it != _keys.end(); it++) { + if ((*it)->id == id) + return (*it); + } + return 0; + } + + const HardwareKey *findHardwareKey(const KeyState& keystate) const { + List<HardwareKey*>::iterator it; + for (it = _keys.begin(); it != _keys.end(); it++) { + if ((*it)->key == keystate) + return (*it); + } + return 0; + } + + +private: + + void checkForKey(HardwareKey *key) { + List<HardwareKey*>::iterator it; + for (it = _keys.begin(); it != _keys.end(); it++) { + if ((*it)->id == key->id) + error("HardwareKey with id %d already given!\n", key->id); + else if ((*it)->key == key->key) + error("HardwareKey with same KeyState already given!\n"); + } + } + + List<HardwareKey*> _keys; +}; + + +} // end of namespace Common + +#endif
\ No newline at end of file diff --git a/backends/common/keymap-manager.cpp b/backends/common/keymap-manager.cpp new file mode 100644 index 0000000000..73ce705414 --- /dev/null +++ b/backends/common/keymap-manager.cpp @@ -0,0 +1,65 @@ +#include "backends/common/keymap-manager.h" + +#define GLOBAL_ID_STR "___GLOBAL" + +namespace Common { + + +void KeymapManager::Domain::addDefaultKeymap(Keymap *map) { + _defaultKeymap = map; +} + +void KeymapManager::Domain::addKeymap(const String& name, Keymap *map) { + if (_keymaps.contains(name)) + delete _keymaps[name]; + _keymaps[name] = map; +} + +void KeymapManager::Domain::deleteAllKeyMaps() { + KeymapMap::iterator it; + for (it = _keymaps.begin(); it != _keymaps.end(); it++) + delete it->_value; + _keymaps.clear(); +} + +Keymap *KeymapManager::Domain::getDefaultKeymap() { + return _defaultKeymap; +} + +Keymap *KeymapManager::Domain::getKeymap(const String& name) { + KeymapMap::iterator it = _keymaps.find(name); + if (it != _keymaps.end()) + return it->_value; + else + return 0; +} + + +void KeymapManager::registerDefaultGlobalKeymap(Keymap *map) { + _globalDomain.addDefaultKeymap(map); +} + +void KeymapManager::registerGlobalKeymap(const String& name, Keymap *map) { + _globalDomain.addKeymap(name, map); +} + +void KeymapManager::registerDefaultGameKeymap(Keymap *map) { + _gameDomain.addDefaultKeymap(map); +} + +void KeymapManager::registerGameKeymap(const String& name, Keymap *map) { + _gameDomain.addKeymap(name, map); +} + +void KeymapManager::unregisterAllGameKeymaps() { + _gameDomain.deleteAllKeyMaps(); +} + +Keymap *KeymapManager::getKeymap(const String& name) { + Keymap *keymap = _gameDomain.getKeymap(name); + if (!keymap) + _globalDomain.getKeymap(name); + return keymap; +} + +} // end of namespace Common
\ No newline at end of file diff --git a/backends/common/keymap-manager.h b/backends/common/keymap-manager.h new file mode 100644 index 0000000000..e5152d2426 --- /dev/null +++ b/backends/common/keymap-manager.h @@ -0,0 +1,51 @@ +#ifndef COMMON_KEYMAP_MANAGER +#define COMMON_KEYMAP_MANAGER + +#include "backends/common/keymap.h" +#include "common/hash-str.h" +#include "common/hashmap.h" + +namespace Common { + +class KeymapManager { +public: + + class Domain { + public: + Domain() : _defaultKeymap(0) {} + + void addDefaultKeymap(Keymap *map); + void addKeymap(const String& name, Keymap *map); + + void deleteAllKeyMaps(); + + Keymap *getDefaultKeymap(); + Keymap *getKeymap(const String& name); + + private: + typedef HashMap<String, Keymap*, + IgnoreCase_Hash, IgnoreCase_EqualTo> KeymapMap; + + Keymap *_defaultKeymap; + KeymapMap _keymaps; + }; + + void registerDefaultGlobalKeymap(Keymap *map); + void registerGlobalKeymap(const String& name, Keymap *map); + + void registerDefaultGameKeymap(Keymap *map); + void registerGameKeymap(const String& name, Keymap *map); + + void unregisterAllGameKeymaps(); + + Keymap *KeymapManager::getKeymap(const String& name); + +private: + + Domain _globalDomain; + Domain _gameDomain; +}; + +} // end of namespace Common + +#endif
\ No newline at end of file diff --git a/backends/common/keymap.cpp b/backends/common/keymap.cpp new file mode 100644 index 0000000000..9fb80bcc58 --- /dev/null +++ b/backends/common/keymap.cpp @@ -0,0 +1,83 @@ +#include "backends/common/keymap.h" + +namespace Common { + +Keymap::Keymap(const Keymap& km) : _actions(km._actions), _keymap() { + init(); + for (uint i = 0; i < _actions.size(); i++) { + if (_actions[i].hwKey) { + _keymap[_actions[i].hwKey->key] = &_actions[i]; + } + } +} + +void Keymap::init() { + _actions.reserve(20); +} + +void Keymap::addAction(const UserAction& action) { + if (findUserAction(action.id)) + error("UserAction with id %d already in KeyMap!\n", action.id); + _actions.push_back(action); + _actions[_actions.size()-1].hwKey = 0; +} + +void Keymap::mapKeyToAction(UserAction *action, HardwareKey *key) { + for (uint i = 0; i < _actions.size(); i++) { + if (&_actions[i] == action) { + internalMapKey(action, key); + return; + } + } + error("UserAction not contained in KeyMap\n"); +} + +void Keymap::mapKeyToAction(int32 id, HardwareKey *key) { + UserAction *act = findUserAction(id); + if (act) + internalMapKey(act, key); +} + +void Keymap::internalMapKey(UserAction *action, HardwareKey *hwKey) { + HashMap<KeyState, UserAction*>::iterator it; + it = _keymap.find(hwKey->key); + // if key is already mapped to an action then un-map it + if (it != _keymap.end()) + it->_value->hwKey = 0; + + action->hwKey = hwKey; + _keymap[hwKey->key] = action; +} + +const UserAction *Keymap::getUserAction(int32 id) const { + return findUserAction(id); +} + +UserAction *Keymap::findUserAction(int32 id) { + Array<UserAction>::iterator it; + for (it = _actions.begin(); it != _actions.end(); it++) { + if (it->id == id) + return &*it; + } + return 0; +} + +const UserAction *Keymap::findUserAction(int32 id) const { + Array<UserAction>::const_iterator it; + for (it = _actions.begin(); it != _actions.end(); it++) { + if (it->id == id) + return &*it; + } + return 0; +} + +UserAction *Keymap::getMappedAction(KeyState ks) const { + HashMap<KeyState, UserAction*>::iterator it; + it = _keymap.find(ks); + if (it == _keymap.end()) + return 0; + else + return it->_value; +} + +} // end of namespace Common
\ No newline at end of file diff --git a/backends/common/keymap.h b/backends/common/keymap.h new file mode 100644 index 0000000000..a2e204827e --- /dev/null +++ b/backends/common/keymap.h @@ -0,0 +1,89 @@ +#ifndef COMMON_KEYMAP +#define COMMON_KEYMAP + +#include "backends/common/hardware-key.h" +#include "backends/common/user-action.h" +#include "common/array.h" +#include "common/keyboard.h" +#include "common/func.h" +#include "common/hashmap.h" + +namespace Common { + +/** + * Hash function for KeyState + */ +template<> struct Hash<KeyState> + : public UnaryFunction<KeyState, uint> { + + uint operator()(const KeyState &val) const { + return (uint)(val.keycode * (val.flags << 1)); + } +}; + +class Keymap { +public: + Keymap() { init(); } + Keymap(const Keymap& km); +private: + void init(); + +public: + /** + * Adds a new UserAction to this Map, + * adding it at the back of the internal array + * @param action the UserAction to add + */ + void addAction(const UserAction& action); + + /** + * Maps a HardwareKey to the given UserAction + * @param action must point to a UserAction in this Keymap + * @param key pointer to HardwareKey to map + * @note if action does not point to a UserAction in this Keymap a + * fatal error will occur + */ + void mapKeyToAction(UserAction *action, HardwareKey *key); + + /** + * Maps a HardwareKey to the UserAction of the given id + * @param id id of the UserAction to map to + * @param key pointer to HardwareKey to map + */ + void mapKeyToAction(int32 id, HardwareKey *key); + + /** + * Retrieves the UserAction with the given id + * @param id id of UserAction to retrieve + * @return Pointer to the UserAction or 0 if not found + */ + const UserAction *getUserAction(int32 id) const; + + /** + * Get a read-only array of all the UserActions contained in this Keymap + */ + const Array<UserAction>& getUserActions() const { return _actions; } + + /** + * Find the UserAction that a key is mapped to + * @param key the key that is mapped to the required UserAction + * @return a pointer to the UserAction or 0 if no + */ + UserAction *getMappedAction(KeyState key) const; + +private: + + UserAction *findUserAction(int32 id); + const UserAction *findUserAction(int32 id) const; + + void internalMapKey(UserAction *action, HardwareKey *hwKey); + + Array<UserAction> _actions; + HashMap<KeyState, UserAction*> _keymap; + +}; + + +} // end of namespace Common + +#endif
\ No newline at end of file diff --git a/backends/common/keymapper.cpp b/backends/common/keymapper.cpp new file mode 100644 index 0000000000..a70a691907 --- /dev/null +++ b/backends/common/keymapper.cpp @@ -0,0 +1,61 @@ +#include "backends/common/keymapper.h" +#include "backends/common/keymap-manager.h" +#include "common/config-manager.h" +namespace Common { + +Keymapper::Keymapper(EventManager *evtMgr) { + _eventMan = evtMgr; + _keymapMan = new KeymapManager(); + _currentMap = 0; + _hardwareKeys = 0; +} + +void Keymapper::registerHardwareKeySet(HardwareKeySet *keys) { + if (_hardwareKeys) + error("Hardware key set already registered!\n"); + _hardwareKeys = keys; +} + +const HardwareKeySet *Keymapper::getHardwareKeySet() const { + return _hardwareKeys; +} + +void Keymapper::addGlobalKeyMap(const String& name, Keymap *keymap) { + _keymapMan->registerGlobalKeymap(name, keymap); +} + +void Keymapper::addGameKeyMap(const String& name, Keymap *keymap) { + if (_gameId.size() == 0) { + initGame(); + if (_gameId.size() == 0) + return; + } + _keymapMan->registerGameKeymap(name, keymap); +} + +void Keymapper::initGame() { + if (ConfMan.getActiveDomain() == 0) + error("Call to Keymapper::initGame when no game loaded\n"); + + if (_gameId.size() > 0) + deInitGame(); + _gameId = ConfMan.getActiveDomainName(); +} + +void Keymapper::deInitGame() { + _keymapMan->unregisterAllGameKeymaps(); + _gameId.clear(); +} + + +bool Keymapper::switchKeymap(const String& name) { + Keymap *new_map = _keymapMan->getKeymap(name); + if (!new_map) { + warning("Keymap '%s' could not be found\n", name.c_str()); + return false; + } + _currentMap = new_map; + return true; +} + +} // end of namespace Common diff --git a/backends/common/keymapper.h b/backends/common/keymapper.h new file mode 100644 index 0000000000..d9b1b050fb --- /dev/null +++ b/backends/common/keymapper.h @@ -0,0 +1,46 @@ +#ifndef COMMON_KEYMAPPER +#define COMMON_KEYMAPPER + +#include "backends/common/keymap.h" +#include "common/list.h" + +namespace Common { + +class KeymapManager; + +class Keymapper { +public: + + Keymapper(EventManager *eventMan); + + void registerHardwareKeySet(HardwareKeySet *keys); + + const HardwareKeySet *getHardwareKeySet() const; + + void addGlobalKeyMap(const String& name, Keymap *keymap); + + void addGameKeyMap(const String& name, Keymap *keymap); + + void initGame(); + void deInitGame(); + + bool switchKeymap(const String& name); + +private: + + typedef List<HardwareKey*>::iterator Iterator; + + EventManager *_eventMan; + KeymapManager *_keymapMan; + + String _gameId; + + Keymap *_currentMap; + + const HardwareKeySet *_hardwareKeys; + +}; + +} // end of namespace Common + +#endif
\ No newline at end of file diff --git a/backends/common/user-action.h b/backends/common/user-action.h new file mode 100644 index 0000000000..31b5473708 --- /dev/null +++ b/backends/common/user-action.h @@ -0,0 +1,70 @@ +#ifndef COMMON_USERACTION +#define COMMON_USERACTION + +#include "common/events.h" +#include "common/list.h" +#include "common/str.h" + +namespace Common { + +struct HardwareKey; + +enum UserActionType { + kGenericUserActionType, + + // common actions + kDirectionUpUserAction, + kDirectionDownUserAction, + kDirectionLeftUserAction, + kDirectionRightUserAction, + kLeftClickUserAction, + kRightClickUserAction, + kSaveUserAction, + kMenuUserAction, + + kUserActionTypeMax +}; + +enum UserActionCategory { + kGenericUserActionCategory, + // classes of action - probably need to be slightly more specific than this + kInGameUserAction, // effects the actual gameplay + kSystemUserAction, //show a menu / change volume / etc + + kUserActionCategoryMax +}; + +struct UserAction { + /** unique id used for saving/loading to config */ + int32 id; + /** Human readable description */ + String description; + /** Events to be sent when mapped key is pressed */ + List<Event> events; + + UserActionCategory category; + UserActionType type; + int priority; + int group; + int flags; + + /** Hardware key that is mapped to this UserAction */ + HardwareKey *hwKey; + + UserAction( String des = "", + UserActionCategory cat = kGenericUserActionCategory, + UserActionType ty = kGenericUserActionType, + int pr = 0, int gr = 0, int fl = 0 ) { + description = des; + category = cat; + type = ty; + priority = pr; + group = gr; + flags = fl; + hwKey = 0; + } +}; + +} // end of namespace Common + +#endif
\ No newline at end of file diff --git a/backends/common/virtual-keyboard-parser.cpp b/backends/common/virtual-keyboard-parser.cpp new file mode 100644 index 0000000000..7b1e79e937 --- /dev/null +++ b/backends/common/virtual-keyboard-parser.cpp @@ -0,0 +1,365 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "backends/common/virtual-keyboard-parser.h" + +#include "common/keyboard.h" +#include "graphics/imageman.h" +#include "common/util.h" + +namespace Common { + +VirtualKeyboardParser::VirtualKeyboardParser(VirtualKeyboard *kbd) : XMLParser() { + _keyboard = kbd; + + _callbacks["keyboard"] = &VirtualKeyboardParser::parserCallback_Keyboard; + _callbacks["mode"] = &VirtualKeyboardParser::parserCallback_Mode; + _callbacks["event"] = &VirtualKeyboardParser::parserCallback_Event; + _callbacks["layout"] = &VirtualKeyboardParser::parserCallback_Layout; + _callbacks["map"] = &VirtualKeyboardParser::parserCallback_Map; + _callbacks["area"] = &VirtualKeyboardParser::parserCallback_Area; + + _closedCallbacks["keyboard"] = &VirtualKeyboardParser::parserCallback_KeyboardClosed; + _closedCallbacks["mode"] = &VirtualKeyboardParser::parserCallback_ModeClosed; +} + +void VirtualKeyboardParser::cleanup() { + _mode = 0; + _kbdParsed = false; + _initialModeName.clear(); + if (_parseMode == kParseFull) { + // reset keyboard to remove existing config + _keyboard->reset(); + } +} + +bool VirtualKeyboardParser::keyCallback(Common::String keyName) { + if (!_callbacks.contains(_activeKey.top()->name)) + return parserError("%s is not a valid key name.", keyName.c_str()); + + return (this->*(_callbacks[_activeKey.top()->name]))(); +} + +bool VirtualKeyboardParser::closedKeyCallback(Common::String keyName) { + if (!_closedCallbacks.contains(_activeKey.top()->name)) + return true; + + return (this->*(_closedCallbacks[_activeKey.top()->name]))(); +} + +bool VirtualKeyboardParser::parserCallback_Keyboard() { + ParserNode *kbdNode = getActiveNode(); + + assert(kbdNode->name == "keyboard"); + + if (getParentNode(kbdNode) != 0) + return parserError("Keyboard element must be root"); + + if (_kbdParsed) + return parserError("Only a single keyboard element is allowed"); + + // if not full parse then we're done + if (_parseMode == kParseCheckResolutions) + return true; + + if (!kbdNode->values.contains("initial_mode")) + return parserError("Keyboard element must contain initial_mode attribute"); + + _initialModeName = kbdNode->values["initial_mode"]; + + if (kbdNode->values.contains("h_align")) { + Common::String h = kbdNode->values["h_align"]; + if (h == "left") + _keyboard->_hAlignment = VirtualKeyboard::kAlignLeft; + else if (h == "centre" || h == "center") + _keyboard->_hAlignment = VirtualKeyboard::kAlignCentre; + else if (h == "right") + _keyboard->_hAlignment = VirtualKeyboard::kAlignRight; + } + + if (kbdNode->values.contains("v_align")) { + Common::String v = kbdNode->values["h_align"]; + if (v == "top") + _keyboard->_vAlignment = VirtualKeyboard::kAlignTop; + else if (v == "middle" || v == "center") + _keyboard->_vAlignment = VirtualKeyboard::kAlignMiddle; + else if (v == "bottom") + _keyboard->_vAlignment = VirtualKeyboard::kAlignBottom; + } + + return true; +} + +bool VirtualKeyboardParser::parserCallback_KeyboardClosed() { + _kbdParsed = true; + if (!_keyboard->_initialMode) + return parserError("Initial mode of keyboard pack not defined"); + return true; +} + +bool VirtualKeyboardParser::parserCallback_Mode() { + ParserNode *modeNode = getActiveNode(); + + assert(modeNode->name == "mode"); + + if (getParentNode(modeNode) == 0 || getParentNode(modeNode)->name != "keyboard") + return parserError("Mode element must be child of keyboard element"); + + if (!modeNode->values.contains("name") || !modeNode->values.contains("resolutions")) + return parserError("Mode element must contain name and resolutions attributes"); + + Common::String name = modeNode->values["name"]; + + if (_parseMode == kParseFull) { + // if full parse then add new mode to keyboard + + if (_keyboard->_modes.contains(name)) + return parserError("Mode '%s' has already been defined", name.c_str()); + + VirtualKeyboard::Mode mode; + mode.name = name; + _keyboard->_modes[name] = mode; + _mode = &(_keyboard->_modes[name]); + + if (name == _initialModeName) + _keyboard->_initialMode = _mode; + } else + _mode = &(_keyboard->_modes[name]); + + Common::String resolutions = modeNode->values["resolutions"]; + Common::StringTokenizer tok (resolutions, " ,"); + + // select best resolution simply by minimising the difference between the + // overlay size and the resolution dimensions. + // TODO: improve this by giving preference to a resolution that is smaller + // than the overlay res (so the keyboard can't be too big for the screen) + uint16 scrW = g_system->getOverlayWidth(), scrH = g_system->getOverlayHeight(); + uint32 diff = 0xFFFFFFFF; + Common::String newResolution; + for (Common::String res = tok.nextToken(); res.size() > 0; res = tok.nextToken()) { + int resW, resH; + if (sscanf(res.c_str(), "%dx%d", &resW, &resH) != 2) { + return parserError("Invalid resolution specification"); + } else { + if (resW == scrW && resH == scrH) { + newResolution = res; + break; + } else { + uint32 newDiff = ABS(scrW - resW) + ABS(scrH - resH); + if (newDiff < diff) { + diff = newDiff; + newResolution = res; + } + } + } + } + + if (newResolution.empty()) + return parserError("No acceptable resolution was found"); + + if (_parseMode == kParseCheckResolutions) { + if (_mode->resolution == newResolution) { + modeNode->ignore = true; + return true; + } else { + // remove data relating to old resolution + ImageMan.unregisterSurface(_mode->bitmapName); + _mode->bitmapName.clear(); + _mode->image = 0; + _mode->imageMap.removeAllAreas(); + } + } + + _mode->resolution = newResolution; + _layoutParsed = false; + + return true; +} + +bool VirtualKeyboardParser::parserCallback_ModeClosed() { + if (!_layoutParsed) { + return parserError("'%s' layout missing from '%s' mode", _mode->resolution.c_str(), _mode->name.c_str()); + } + return true; +} + +bool VirtualKeyboardParser::parserCallback_Event() { + ParserNode *evtNode = getActiveNode(); + + assert(evtNode->name == "event"); + + if (getParentNode(evtNode) == 0 || getParentNode(evtNode)->name != "mode") + return parserError("Event element must be child of mode element"); + + if (!evtNode->values.contains("name") || !evtNode->values.contains("type")) + return parserError("Event element must contain name and type attributes"); + + assert(_mode); + + // if just checking resolutions we're done + if (_parseMode == kParseCheckResolutions) + return true; + + Common::String name = evtNode->values["name"]; + if (_mode->events.contains(name)) + return parserError("Event '%s' has already been defined", name.c_str()); + + VirtualKeyboard::Event evt; + evt.name = name; + + Common::String type = evtNode->values["type"]; + if (type == "key") { + if (!evtNode->values.contains("code") || !evtNode->values.contains("ascii")) + return parserError("Key event element must contain code and ascii attributes"); + + evt.type = VirtualKeyboard::kEventKey; + + Common::KeyCode code = (Common::KeyCode)atoi(evtNode->values["code"].c_str()); + uint16 ascii = atoi(evtNode->values["ascii"].c_str()); + + byte flags = 0; + if (evtNode->values.contains("flags")) { + Common::StringTokenizer tok(evtNode->values["flags"], ", "); + for (Common::String fl = tok.nextToken(); !fl.empty(); fl = tok.nextToken()) { + if (fl == "ctrl" || fl == "control") + flags &= Common::KBD_CTRL; + else if (fl == "alt") + flags &= Common::KBD_ALT; + else if (fl == "shift") + flags &= Common::KBD_SHIFT; + } + } + + evt.data = new Common::KeyState(code, ascii, flags); + + } else if (type == "switch_mode") { + if (!evtNode->values.contains("mode")) + return parserError("Switch mode event element must contain mode attribute"); + + evt.type = VirtualKeyboard::kEventSwitchMode; + evt.data = new Common::String(evtNode->values["mode"]); + } else if (type == "close") { + evt.type = VirtualKeyboard::kEventClose; + evt.data = 0; + } else + return parserError("Event type '%s' not known", type.c_str()); + + _mode->events[name] = evt; + + return true; +} + +bool VirtualKeyboardParser::parserCallback_Layout() { + ParserNode *layoutNode = getActiveNode(); + + assert(layoutNode->name == "layout"); + + if (getParentNode(layoutNode) == 0 || getParentNode(layoutNode)->name != "mode") + return parserError("Layout element must be child of mode element"); + + if (!layoutNode->values.contains("resolution") || !layoutNode->values.contains("bitmap")) + return parserError("Layout element must contain resolution and bitmap attributes"); + + assert(!_mode->resolution.empty()); + + Common::String res = layoutNode->values["resolution"]; + + if (res != _mode->resolution) { + layoutNode->ignore = true; + return true; + } + + _mode->bitmapName = layoutNode->values["bitmap"]; + + if (!ImageMan.registerSurface(_mode->bitmapName, 0)) + return parserError("Error loading bitmap '%s'", _mode->bitmapName.c_str()); + + _mode->image = ImageMan.getSurface(_mode->bitmapName); + if (!_mode->image) + return parserError("Error loading bitmap '%s'", _mode->bitmapName.c_str()); + + if (layoutNode->values.contains("transparent_color")) { + int r, g, b; + if (!parseIntegerKey(layoutNode->values["transparent_color"].c_str(), 3, &r, &g, &b)) + return parserError("Could not parse color value"); + _mode->transparentColor = g_system->RGBToColor(r, g, b); + } else + _mode->transparentColor = g_system->RGBToColor(255, 0, 255); // default to purple + + _layoutParsed = true; + + return true; +} + +bool VirtualKeyboardParser::parserCallback_Map() { + ParserNode *mapNode = getActiveNode(); + + assert(mapNode->name == "map"); + + if (getParentNode(mapNode) == 0 || getParentNode(mapNode)->name != "layout") + return parserError("Map element must be child of layout element"); + + return true; +} + +bool VirtualKeyboardParser::parserCallback_Area() { + ParserNode *areaNode = getActiveNode(); + + assert(areaNode->name == "area"); + + if (getParentNode(areaNode) == 0 || getParentNode(areaNode)->name != "map") + return parserError("Area element must be child of map element"); + + if (!areaNode->values.contains("shape") || !areaNode->values.contains("coords") || !areaNode->values.contains("target")) + return parserError("Area element must contain shape, coords and target attributes"); + + Common::String shape = areaNode->values["shape"]; + if (shape == "rect") { + Common::Rect *rect = _mode->imageMap.createRectArea(areaNode->values["target"]); + int x1, y1, x2, y2; + if (!parseIntegerKey(areaNode->values["coords"].c_str(), 4, &x1, &y1, &x2, &y2)) + return parserError("Invalid coords for rect area"); + rect->left = x1; rect->top = y1; rect->right = x2; rect->bottom = y2; + } else if (shape == "poly") { + Common::StringTokenizer tok (areaNode->values["coords"], ", "); + Common::Polygon *poly = _mode->imageMap.createPolygonArea(areaNode->values["target"]); + for (Common::String st = tok.nextToken(); !st.empty(); st = tok.nextToken()) { + int x, y; + if (sscanf(st.c_str(), "%d", &x) != 1) + return parserError("Invalid coords for polygon area"); + st = tok.nextToken(); + if (sscanf(st.c_str(), "%d", &y) != 1) + return parserError("Invalid coords for polygon area"); + poly->addPoint(x, y); + } + if (poly->getPointCount() < 3) + return parserError("Invalid coords for polygon area"); + } else + return parserError("Area shape '%s' not known", shape.c_str()); + + return true; +} + +} // end of namespace GUI diff --git a/backends/common/virtual-keyboard-parser.h b/backends/common/virtual-keyboard-parser.h new file mode 100644 index 0000000000..cd2ea28faf --- /dev/null +++ b/backends/common/virtual-keyboard-parser.h @@ -0,0 +1,212 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef COMMON_VIRTUAL_KEYBOARD_PARSER +#define COMMON_VIRTUAL_KEYBOARD_PARSER + +#include "common/xmlparser.h" +#include "backends/common/virtual-keyboard.h" + +/** + TODO - information about optional attributes and their default values + + + *************************************** + ** Virtual Keyboard Pack File Format ** + *************************************** + +The new virtual keyboard for ScummVM is implemented in the same way as a HTML +ImageMap. It uses a single bitmap of the entire keyboard layout and then a +image map description allows certain areas of the bitmap to be given special +actions. Most of these actions will be a virtual key press event, but there +will also be special keys that will change the keyboard layout or close the +keyboard. The HTML image map description is contained in a larger XML file that +can describe all the different modes of the keyboard, and also different +keyboard layouts for different screen resolutions. + + ******************************************** + ** Example keyboard pack description file ** + ******************************************** + +<keyboard modes="normal,caps" initial_mode="normal" v_align="bottom" h_align="centre"> + <mode name="normal" resolutions="640x400,320x200"> + <layout resolution="640x400" bitmap="normal_640x400.bmp" transparent_color="255,0,255"> + <map> + <area shape="poly" coords="65,50,67,48,94,48,96,50,96,77,94,79,67,79,65,77" target="q" /> + <area shape="poly" coords="105,50,107,48,134,48,136,50,136,77,134,79,107,79,105,77" target="w" /> + <area shape="poly" coords="146,50,148,48,174,48,176,50,176,77,174,79,148,79,146,77" target="e" /> + ... + <area shape="poly" coords="11,89,12,88,69,88,70,89,70,116,69,117,12,117,11,116" target="caps" /> + </map> + </layout> + <layout resolution="320x200" bitmap="normal_320x200.bmp" transparent_color="255,0,255"> + ... + </layout> + <event name="a" type="key" code="97" ascii="97" modifiers="" /> + <event name="b" type="key" code="98" ascii="98" modifiers="" /> + <event name="c" type="key" code="99" ascii="99" modifiers="" /> + ... + <event name="caps" type="switch_mode" mode="caps" /> + </mode> + + <mode name="caps" resolutions="640x400"> + <layout resolution="640x400" bitmap="caps_640x480.bmp" transparent_color="255,0,255"> + <map> + <area shape="poly" coords="65,50,67,48,94,48,96,50,96,77,94,79,67,79,65,77" target="Q" /> + ... + </map> + </layout> + <event name="A" type="key" code="97" ascii="65" modifiers="shift" /> + <event name="B" type="key" code="98" ascii="66" modifiers="shift" /> + <event name="C" type="key" code="99" ascii="67" modifiers="shift" /> + ... + </mode> +</keyboard> + +************************* +** Description of tags ** +************************* + +<keyboard> + +This is the required, root element of the file format. + +attributes: + - modes: lists all the modes that the keyboard pack contains + - initial_mode: which mode the keyboard should show initially + - v_align/h_align: where on the screen should the keyboard appear initially + +child tags: + - mode + +------------------------------------------------------------------------------- + +<mode> + +This tag encapsulates a single mode of the keyboard. Within are a number of +layouts, which provide the specific implementation at different resolutions. + +attributes: + - name: the name of the mode + - resolutions: list of the different layout resolutions + +child tags: + - layout + - event + +------------------------------------------------------------------------------- + +<event> + +These tags describe a particular event that will be triggered by a mouse click +on a particular area. The target attribute of each image map area should be the +same as an event's name. + +attributes: + - name: name of the event + - type: what sort of event is it (key | switch_mode | close) + - for key events + - code / ascii / modifiers: describe a key press in ScummVM KeyState format + - for switch_mode events + - mode: the mode that should be switched to + +------------------------------------------------------------------------------- + +<layout> + +These tags encapsulate an implementation of a mode at a particular resolution. + +attributes: + - resolution: the screen resolution that this layout is designed for + - bitmap: filename of the 24-bit bitmap that will be used for this layout + - transparent_color: color in r,b,g format that will be used for keycolor + transparency. + +child nodes: + - map: this describes the image map using the same format as html image maps + +------------------------------------------------------------------------------- + +<map> + +These tags describe the image map for a particular layout. It uses the exact +same format as HTML image maps. The only area shapes that are supported are +rectangles and polygons. The target attribute of each area should be the name +of an event for this mode (see <event> tag). For information on HTML image map +format see + - http://www.w3schools.com/TAGS/tag_map.asp + - http://www.w3schools.com/TAGS/tag_area.asp + +*/ + +namespace Common { + +enum ParseMode { + kParseFull, // when loading keyboard pack for first time + kParseCheckResolutions // when re-parsing following a change in screen size +}; + +class VirtualKeyboardParser : public Common::XMLParser { + + typedef bool (VirtualKeyboardParser::*ParserCallback)(); + +public: + + VirtualKeyboardParser(VirtualKeyboard *kbd); + void setParseMode(ParseMode m) { + _parseMode = m; + } + +protected: + VirtualKeyboard *_keyboard; + + /** internal state variables of parser */ + ParseMode _parseMode; + VirtualKeyboard::Mode *_mode; // pointer to mode currently being parsed + Common::String _initialModeName; + bool _kbdParsed; + bool _layoutParsed; + + bool keyCallback(Common::String keyName); + bool closedKeyCallback(Common::String keyName); + void cleanup(); + + bool parserCallback_Keyboard(); + bool parserCallback_Mode(); + bool parserCallback_Event(); + bool parserCallback_Layout(); + bool parserCallback_Map(); + bool parserCallback_Area(); + + bool parserCallback_KeyboardClosed(); + bool parserCallback_ModeClosed(); + + Common::HashMap<Common::String, ParserCallback, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _callbacks; + Common::HashMap<Common::String, ParserCallback, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> _closedCallbacks; +}; + +} // end of namespace GUI + +#endif diff --git a/backends/common/virtual-keyboard.cpp b/backends/common/virtual-keyboard.cpp new file mode 100644 index 0000000000..21d188dced --- /dev/null +++ b/backends/common/virtual-keyboard.cpp @@ -0,0 +1,385 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "backends/common/virtual-keyboard.h" +#include "backends/common/virtual-keyboard-parser.h" +#include "common/config-manager.h" +#include "common/events.h" +#include "common/unzip.h" +#include "graphics/cursorman.h" +#include "graphics/imageman.h" +#include "graphics/surface-keycolored.h" +#include "gui/newgui.h" + +namespace Common { + +VirtualKeyboard::VirtualKeyboard() : _currentMode(0), _keyDown(0) { + assert(g_system); + _system = g_system; + + _parser = new VirtualKeyboardParser(this); + _loaded = _displaying = _drag = false; + _lastScreenChanged = _system->getScreenChangeID(); + + memset(_cursor, 0xFF, sizeof(_cursor)); +} + +VirtualKeyboard::~VirtualKeyboard() { + // TODO: clean up event data pointers + deleteEventData(); + delete _parser; +} + +void VirtualKeyboard::reset() { + // TODO: clean up event data pointers + deleteEventData(); + _modes.clear(); + _initialMode = _currentMode = 0; + _kbdBound.left = _kbdBound.top + = _kbdBound.right = _kbdBound.bottom = 0; + _hAlignment = kAlignCentre; + _vAlignment = kAlignBottom; + _keyQueue.clear(); + _keyDown = 0; + _displaying = _drag = false; + _firstRun = true; + _lastScreenChanged = _system->getScreenChangeID(); +} + +void VirtualKeyboard::deleteEventData() { + ModeMap::iterator it_m; + EventMap::iterator it_e; + for (it_m = _modes.begin(); it_m != _modes.end(); it_m++) { + EventMap *evt = &(it_m->_value.events); + for (it_e = evt->begin(); it_e != evt->end(); it_e++) + delete it_e->_value.data; + } +} + +bool VirtualKeyboard::loadKeyboardPack(Common::String packName) { + + if (Common::File::exists(packName + ".xml")) { + // uncompressed keyboard pack + if (!_parser->loadFile(packName + ".xml")) + return false; + + } else if (Common::File::exists(packName + ".zip")) { + // compressed keyboard pack +#ifdef USE_ZLIB + unzFile zipFile = unzOpen((packName + ".zip").c_str()); + if (zipFile && unzLocateFile(zipFile, (packName + ".xml").c_str(), 2) == UNZ_OK) { + unz_file_info fileInfo; + unzOpenCurrentFile(zipFile); + unzGetCurrentFileInfo(zipFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0); + byte *buffer = new byte[fileInfo.uncompressed_size+1]; + assert(buffer); + memset(buffer, 0, (fileInfo.uncompressed_size+1)*sizeof(byte)); + unzReadCurrentFile(zipFile, buffer, fileInfo.uncompressed_size); + unzCloseCurrentFile(zipFile); + if (!_parser->loadBuffer(buffer, fileInfo.uncompressed_size+1, true)) { + unzClose(zipFile); + return false; + } + } else { + warning("Could not find %s.xml file in %s.zip keyboard pack\n", packName.c_str(), packName.c_str()); + unzClose(zipFile); + return false; + } + unzClose(zipFile); + + ImageMan.addArchive(packName + ".zip"); +#else + return false; +#endif + } else { + warning("Keyboard pack not found"); + return false; + } + + _parser->setParseMode(kParseFull); + _loaded = _parser->parse(); + if (_loaded) + printf("Keyboard pack '%s' loaded successfully!\n", packName.c_str()); + + return _loaded; +} + +void VirtualKeyboard::setDefaultPosition() +{ + int16 scrW = _system->getOverlayWidth(), scrH = _system->getOverlayHeight(); + int16 kbdW = _kbdBound.width(), kbdH = _kbdBound.height(); + int16 posX = 0, posY = 0; + if (scrW != kbdW) { + switch (_hAlignment) { + case kAlignLeft: + posX = 0; + break; + case kAlignCentre: + posX = (scrW - kbdW) / 2; + break; + case kAlignRight: + posX = scrW - kbdW; + break; + } + } + if (scrH != kbdH) { + switch (_vAlignment) { + case kAlignTop: + posY = 0; + break; + case kAlignMiddle: + posY = (scrH - kbdH) / 2; + break; + case kAlignBottom: + posY = scrH - kbdH; + break; + } + } + _kbdBound.moveTo(posX, posY); +} + +bool VirtualKeyboard::checkModeResolutions() +{ + _parser->setParseMode(kParseCheckResolutions); + _loaded = _parser->parse(); + return _loaded; +} + +void VirtualKeyboard::move(int16 x, int16 y) { + // snap to edge of screen + if (ABS(x) < SNAP_WIDTH) + x = 0; + int16 x2 = _system->getOverlayWidth() - _kbdBound.width(); + if (ABS(x - x2) < SNAP_WIDTH) + x = x2; + if (ABS(y) < SNAP_WIDTH) + y = 0; + int16 y2 = _system->getOverlayHeight() - _kbdBound.height(); + if (ABS(y - y2) < SNAP_WIDTH) + y = y2; + + _kbdBound.moveTo(x, y); +} + +Common::String VirtualKeyboard::findArea(int16 x, int16 y) { + x -= _kbdBound.left; + y -= _kbdBound.top; + if (x < 0 || x > _kbdBound.width()) return ""; + if (y < 0 || y > _kbdBound.height()) return ""; + return _currentMode->imageMap.findMapArea(x, y); +} + +void VirtualKeyboard::processClick(const Common::String& area) { + if (!_currentMode->events.contains(area)) return; + Event evt = _currentMode->events[area]; + + switch (evt.type) { + case kEventKey: + // add virtual keypress to queue + _keyQueue.push(*(Common::KeyState*)evt.data); + break; + case kEventSwitchMode: + // switch to new mode + switchMode(*(Common::String *)evt.data); + break; + case kEventClose: + // close virtual keyboard + _displaying = false; + break; + } +} + +void VirtualKeyboard::switchMode(Mode *newMode) { + _kbdBound.setWidth(newMode->image->w); + _kbdBound.setHeight(newMode->image->h); + _currentMode = newMode; + _needRedraw = true; +} + +void VirtualKeyboard::switchMode(const Common::String& newMode) { + if (!_modes.contains(newMode)) { + warning("Keyboard mode '%s' unknown", newMode.c_str()); + return; + } + switchMode(&_modes[newMode]); +} + +void VirtualKeyboard::show() { + if (!_loaded) { + // if not loaded then load default "vkeybd" pack + if (!loadKeyboardPack("vkeybd")) { + warning("Keyboard not loaded therefore can't be shown"); + return; + } + } + if (_lastScreenChanged != _system->getScreenChangeID()) + screenChanged(); + switchMode(_initialMode); + _displaying = true; + if (_firstRun) { + _firstRun = false; + setDefaultPosition(); + } + + if (!g_gui.isActive()) { + _system->showOverlay(); + _system->clearOverlay(); + } + + _overlayBackup.create(_system->getOverlayWidth(), _system->getOverlayHeight(), sizeof(OverlayColor)); + _system->grabOverlay((OverlayColor*)_overlayBackup.pixels, _overlayBackup.w); + setupCursor(); + + runLoop(); + + removeCursor(); + _system->copyRectToOverlay((OverlayColor*)_overlayBackup.pixels, _overlayBackup.w, 0, 0, _overlayBackup.w, _overlayBackup.h); + if (!g_gui.isActive()) _system->hideOverlay(); + _overlayBackup.free(); +} + +void VirtualKeyboard::hide() { + _displaying = false; +} + +void VirtualKeyboard::screenChanged() { + _lastScreenChanged = _system->getScreenChangeID(); + if (!checkModeResolutions()) + _displaying = false; +} + +void VirtualKeyboard::runLoop() { + Common::EventManager *eventMan = _system->getEventManager(); + + while (_displaying) { + if (_needRedraw) redraw(); + + animateCursor(); + _system->updateScreen(); + Common::Event event; + while (eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_LBUTTONDOWN: + if (_kbdBound.contains(event.mouse)) { + _areaDown = findArea(event.mouse.x, event.mouse.y); + if (_areaDown.empty()) { + _drag = true; + _dragPoint.x = event.mouse.x - _kbdBound.left; + _dragPoint.y = event.mouse.y - _kbdBound.top; + } + } else + _areaDown.clear(); + break; + case Common::EVENT_LBUTTONUP: + if (_drag) _drag = false; + if (!_areaDown.empty() && _areaDown == findArea(event.mouse.x, event.mouse.y)) { + processClick(_areaDown); + _areaDown.clear(); + } + break; + case Common::EVENT_MOUSEMOVE: + if (_drag) { + move(event.mouse.x - _dragPoint.x, + event.mouse.y - _dragPoint.y); + _needRedraw = true; + } + break; + case Common::EVENT_SCREEN_CHANGED: + screenChanged(); + break; + case Common::EVENT_QUIT: + _system->quit(); + return; + default: + break; + } + } + // Delay for a moment + _system->delayMillis(10); + } + + // push keydown & keyup events into the event manager + Common::Event evt; + evt.synthetic = false; + while (!_keyQueue.empty()) { + evt.kbd = _keyQueue.pop(); + evt.type = Common::EVENT_KEYDOWN; + eventMan->pushEvent(evt); + evt.type = Common::EVENT_KEYUP; + eventMan->pushEvent(evt); + } +} + +void VirtualKeyboard::redraw() { + Graphics::SurfaceKeyColored surf; + + surf.create(_system->getOverlayWidth(), _system->getOverlayHeight(), sizeof(OverlayColor)); + + memcpy(surf.pixels, _overlayBackup.pixels, surf.w * surf.h * sizeof(OverlayColor)); + surf.blit(_currentMode->image, _kbdBound.left, _kbdBound.top, _currentMode->transparentColor); + + _system->copyRectToOverlay((OverlayColor*)surf.pixels, surf.w, 0, 0, surf.w, surf.h); + + surf.free(); + + _needRedraw = false; +} + +void VirtualKeyboard::setupCursor() { + const byte palette[] = { + 255, 255, 255, 0, + 255, 255, 255, 0, + 171, 171, 171, 0, + 87, 87, 87, 0 + }; + + CursorMan.pushCursorPalette(palette, 0, 4); + CursorMan.pushCursor(NULL, 0, 0, 0, 0); + CursorMan.showMouse(true); +} + +void VirtualKeyboard::animateCursor() { + int time = _system->getMillis(); + if (time > _cursorAnimateTimer + kCursorAnimateDelay) { + for (int i = 0; i < 15; i++) { + if ((i < 6) || (i > 8)) { + _cursor[16 * 7 + i] = _cursorAnimateCounter; + _cursor[16 * i + 7] = _cursorAnimateCounter; + } + } + + CursorMan.replaceCursor(_cursor, 16, 16, 7, 7); + + _cursorAnimateTimer = time; + _cursorAnimateCounter = (_cursorAnimateCounter + 1) % 4; + } +} + +void VirtualKeyboard::removeCursor() { + CursorMan.popCursor(); + CursorMan.popCursorPalette(); +} + +} // end of namespace Common diff --git a/backends/common/virtual-keyboard.h b/backends/common/virtual-keyboard.h new file mode 100644 index 0000000000..fc2300c24c --- /dev/null +++ b/backends/common/virtual-keyboard.h @@ -0,0 +1,185 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef COMMON_VIRTUAL_KEYBOARD_H +#define COMMON_VIRTUAL_KEYBOARD_H + +class OSystem; + +#include "common/events.h" +#include "common/hashmap.h" +#include "common/hash-str.h" +#include "common/image-map.h" +#include "common/keyboard.h" +#include "common/queue.h" +#include "common/str.h" +#include "graphics/surface.h" + +namespace Common { + +class VirtualKeyboardParser; + +class VirtualKeyboard { + + enum EventType { + kEventKey, + kEventSwitchMode, + kEventClose + }; + + struct Event { + Common::String name; + EventType type; + Graphics::Surface *rollOverImage; + void *data; + }; + + typedef Common::HashMap<Common::String, Event, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> EventMap; + + struct Mode { + Common::String name; + Common::String resolution; + Common::String bitmapName; + Graphics::Surface *image; + OverlayColor transparentColor; + Common::ImageMap imageMap; + EventMap events; + Mode() : image(0) {} + }; + + typedef Common::HashMap<Common::String, Mode, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ModeMap; + + enum HorizontalAlignment { + kAlignLeft, + kAlignCentre, + kAlignRight + }; + + enum VerticalAlignment { + kAlignTop, + kAlignMiddle, + kAlignBottom + }; + +public: + VirtualKeyboard(); + virtual ~VirtualKeyboard(); + + /** + * Loads the keyboard pack with the given name. + * The system first looks for an uncompressed keyboard pack by searching + * for packName.xml in the filesystem, if this does not exist then it + * searches for a compressed keyboard pack by looking for packName.zip. + * @param packName name of the keyboard pack + */ + bool loadKeyboardPack(Common::String packName); + + /** + * Shows the keyboard, starting an event loop that will intercept all + * user input (like a modal GUI dialog). + * It is assumed that the game has been paused, before this is called + */ + void show(); + + /** + * Hides the keyboard, ending the event loop. + */ + void hide(); + + /** + * Returns true if the keyboard is currently being shown + */ + bool isDisplaying() { + return _displaying; + } + + /** + * Returns true if the keyboard is loaded and ready to be shown + */ + bool isLoaded() { + return _loaded; + } + +protected: + OSystem *_system; + + static const int SNAP_WIDTH = 10; + + friend class VirtualKeyboardParser; + VirtualKeyboardParser *_parser; + + // TODO : sort order of all this stuff + void reset(); + void deleteEventData(); + void screenChanged(); + bool checkModeResolutions(); + void setDefaultPosition(); + void move(int16 x, int16 y); + void switchMode(Mode *newMode); + void switchMode(const Common::String& newMode); + Common::String findArea(int16 x, int16 y); + void processClick(const Common::String &area); + void runLoop(); + void redraw(); + + Graphics::Surface _overlayBackup; + + bool _loaded; + bool _displaying; + bool _needRedraw; + bool _firstRun; + + ModeMap _modes; + Mode *_initialMode; + Mode *_currentMode; + + int _lastScreenChanged; + Common::Rect _kbdBound; + + HorizontalAlignment _hAlignment; + VerticalAlignment _vAlignment; + + Common::String _areaDown; + Common::Point _dragPoint; + bool _drag; + + Common::Queue<Common::KeyState> _keyQueue; + Common::KeyState *_keyDown; + + static const int kCursorAnimateDelay = 250; + int _cursorAnimateCounter; + int _cursorAnimateTimer; + byte _cursor[2048]; + void setupCursor(); + void removeCursor(); + void animateCursor(); + +}; + + +} // End of namespace GUI + + +#endif diff --git a/backends/events/default/default-events.cpp b/backends/events/default/default-events.cpp index 0caba25792..3a77b0114c 100644 --- a/backends/events/default/default-events.cpp +++ b/backends/events/default/default-events.cpp @@ -191,9 +191,13 @@ DefaultEventManager::DefaultEventManager(OSystem *boss) : _hasPlaybackEvent = false; } + + _vk = new Common::VirtualKeyboard(); + _artificialEventCounter = 0; } DefaultEventManager::~DefaultEventManager() { + delete _vk; _boss->lockMutex(_timeMutex); _boss->lockMutex(_recorderMutex); _recordMode = kPassthrough; @@ -348,8 +352,17 @@ void DefaultEventManager::processMillis(uint32 &millis) { bool DefaultEventManager::pollEvent(Common::Event &event) { uint32 time = _boss->getMillis(); bool result; - - result = _boss->pollEvent(event); + + if (!_artificialEventQueue.empty()) { + // delay the feeding of artificial events + if (++_artificialEventCounter % kArtificialEventDelay == 0) { + event = _artificialEventQueue.pop(); + result = true; + _artificialEventCounter = 0; + } else + result = _boss->pollEvent(event); + } else + result = _boss->pollEvent(event); if (_recordMode != kPassthrough) { @@ -384,6 +397,20 @@ bool DefaultEventManager::pollEvent(Common::Event &event) { _currentKeyDown.flags = event.kbd.flags; _keyRepeatTime = time + kKeyRepeatInitialDelay; #endif + + // HACK to show/hide keyboard (keyboard is not shown if gui is active) + if (event.kbd.keycode == Common::KEYCODE_F6 && event.kbd.flags == 0) { + if (_vk->isDisplaying()) { + _vk->hide(); + } else { + bool isPaused = (g_engine) ? g_engine->isPaused() : true; + if (!isPaused) g_engine->pauseEngine(true); + _vk->show(); + if (!isPaused) g_engine->pauseEngine(false); + result = false; + } + } + break; case Common::EVENT_KEYUP: _modifierState = event.kbd.flags; @@ -447,4 +474,8 @@ bool DefaultEventManager::pollEvent(Common::Event &event) { return result; } +void DefaultEventManager::pushEvent(const Common::Event &event) { + _artificialEventQueue.push(event); +} + #endif // !defined(DISABLE_DEFAULT_EVENTMANAGER) diff --git a/backends/events/default/default-events.h b/backends/events/default/default-events.h index 98dcd4b3de..891ee3e744 100644 --- a/backends/events/default/default-events.h +++ b/backends/events/default/default-events.h @@ -27,7 +27,9 @@ #define BACKEND_EVENTS_DEFAULT_H #include "common/events.h" +#include "common/queue.h" #include "common/savefile.h" +#include "backends/common/virtual-keyboard.h" /* At some point we will remove pollEvent from OSystem and change @@ -44,6 +46,14 @@ use a subclass of EventProvider. class DefaultEventManager : public Common::EventManager { OSystem *_boss; + Common::VirtualKeyboard *_vk; + + Common::Queue<Common::Event> _artificialEventQueue; + int _artificialEventCounter; + enum { + kArtificialEventDelay = 10 + }; + Common::Point _mousePos; int _buttonState; int _modifierState; @@ -107,6 +117,7 @@ public: ~DefaultEventManager(); virtual bool pollEvent(Common::Event &event); + virtual void pushEvent(const Common::Event &event); virtual void registerRandomSource(Common::RandomSource &rnd, const char *name); virtual void processMillis(uint32 &millis); diff --git a/backends/module.mk b/backends/module.mk index 6642a3a281..fba5bbe907 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -27,7 +27,9 @@ MODULE_OBJS := \ saves/savefile.o \ saves/default/default-saves.o \ saves/compressed/compressed-saves.o \ - timer/default/default-timer.o + timer/default/default-timer.o \ + common/virtual-keyboard.o \ + common/virtual-keyboard-parser.o # Include common rules include $(srcdir)/rules.mk |