diff options
Diffstat (limited to 'backends/vkeybd')
-rw-r--r-- | backends/vkeybd/image-map.cpp | 69 | ||||
-rw-r--r-- | backends/vkeybd/image-map.h | 53 | ||||
-rw-r--r-- | backends/vkeybd/keycode-descriptions.h | 331 | ||||
-rw-r--r-- | backends/vkeybd/polygon.cpp | 55 | ||||
-rw-r--r-- | backends/vkeybd/polygon.h | 114 | ||||
-rw-r--r-- | backends/vkeybd/virtual-keyboard-gui.cpp | 416 | ||||
-rw-r--r-- | backends/vkeybd/virtual-keyboard-gui.h | 153 | ||||
-rw-r--r-- | backends/vkeybd/virtual-keyboard-parser.cpp | 363 | ||||
-rw-r--r-- | backends/vkeybd/virtual-keyboard-parser.h | 267 | ||||
-rw-r--r-- | backends/vkeybd/virtual-keyboard.cpp | 390 | ||||
-rw-r--r-- | backends/vkeybd/virtual-keyboard.h | 253 | ||||
-rw-r--r-- | backends/vkeybd/vkeybd.zip | bin | 0 -> 641277 bytes |
12 files changed, 2464 insertions, 0 deletions
diff --git a/backends/vkeybd/image-map.cpp b/backends/vkeybd/image-map.cpp new file mode 100644 index 0000000000..d97b662c7d --- /dev/null +++ b/backends/vkeybd/image-map.cpp @@ -0,0 +1,69 @@ +/* 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/vkeybd/image-map.h" + +namespace Common { + +ImageMap::~ImageMap() { + removeAllAreas(); +} + +Polygon *ImageMap::createArea(const String& id) { + if (_areas.contains(id)) { + warning("Image map already contains an area with target of '%s'", id.c_str()); + return 0; + } + Polygon *p = new Polygon(); + _areas[id] = p; + return p; +} + +void ImageMap::removeArea(const String& id) { + if (!_areas.contains(id)) + return; + delete _areas[id]; + _areas.erase(id); +} + +void ImageMap::removeAllAreas() { + HashMap<String, Polygon*>::iterator it; + for (it = _areas.begin(); it != _areas.end(); it++) { + delete it->_value; + } + _areas.clear(); +} + +String ImageMap::findMapArea(int16 x, int16 y) { + HashMap<String, Polygon*>::iterator it; + for (it = _areas.begin(); it != _areas.end(); it++) { + if (it->_value->contains(x, y)) + return it->_key; + } + return ""; +} + + +} // End of namespace Common diff --git a/backends/vkeybd/image-map.h b/backends/vkeybd/image-map.h new file mode 100644 index 0000000000..ed6feaa26e --- /dev/null +++ b/backends/vkeybd/image-map.h @@ -0,0 +1,53 @@ +/* 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_IMAGEMAP_H +#define COMMON_IMAGEMAP_H + +#include "common/hashmap.h" +#include "common/hash-str.h" +#include "backends/vkeybd/polygon.h" + +namespace Common { + +class ImageMap { + +public: + + ~ImageMap(); + + Polygon *createArea(const String& id); + void removeArea(const String& id); + void removeAllAreas(); + String findMapArea(int16 x, int16 y); + +protected: + HashMap<String, Polygon*> _areas; +}; + + +} // End of namespace Common + +#endif diff --git a/backends/vkeybd/keycode-descriptions.h b/backends/vkeybd/keycode-descriptions.h new file mode 100644 index 0000000000..e31cc562be --- /dev/null +++ b/backends/vkeybd/keycode-descriptions.h @@ -0,0 +1,331 @@ +#ifndef KEYCODE_DESCRIPTIONS +#define KEYCODE_DESCRIPTIONS + +static const char *keycodeDescTable[] = { + "", + "", + "", + "", + "", + "", + "", + "", + "Backspace", + "Tab", + "", + "", + "Clear", + "Return", + "", + "", + "", + "", + "", + "Pause", + "", + "", + "", + "", + "", + "", + "", + "Escape", + "", + "", + "", + "", + " ", + "!", + "\"", + "#", + "$", + "%", + "&", + "'", + "(", + ")", + "*", + "+", + ",", + "-", + ".", + "/", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + ":", + ";", + "<", + "=", + ">", + "?", + "@", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "[", + "\\", + "]", + "^", + "_", + "`", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "{", + "|", + "}", + "~", + "Delete", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + ".", + "/", + "*", + "-", + "+", + "Enter", + "=", + "Up", + "Down", + "Right", + "Left", + "Ins", + "Home", + "End", + "Page Up", + "Page Down", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "F13", + "F14", + "F15", + "", + "", + "", + "Num Lock", + "Caps Lock", + "Scroll Lock", + "Shift", + "Shift", + "Ctrl", + "Ctrl", + "Alt", + "Alt", + "Meta", + "Meta", + "Super", + "Super", + "Mode", + "Compose", + "Help", + "Print", + "SysReq", + "Break", + "Menu", + "Power", + "€", + "Undo" +}; +static const int keycodeDescTableSize = 322; + +#endif diff --git a/backends/vkeybd/polygon.cpp b/backends/vkeybd/polygon.cpp new file mode 100644 index 0000000000..77ef3f0f44 --- /dev/null +++ b/backends/vkeybd/polygon.cpp @@ -0,0 +1,55 @@ +/* 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/vkeybd/polygon.h" + +namespace Common { + +bool Polygon::contains(int16 x, int16 y) const { + int yflag0; + int yflag1; + bool inside_flag = false; + unsigned int pt; + + const Point *vtx0 = &_points[_points.size() - 1]; + const Point *vtx1 = &_points[0]; + + yflag0 = (vtx0->y >= y); + for (pt = 0; pt < _points.size(); pt++, vtx1++) { + yflag1 = (vtx1->y >= y); + if (yflag0 != yflag1) { + if (((vtx1->y - y) * (vtx0->x - vtx1->x) >= + (vtx1->x - x) * (vtx0->y - vtx1->y)) == yflag1) { + inside_flag = !inside_flag; + } + } + yflag0 = yflag1; + vtx0 = vtx1; + } + + return inside_flag; +} + +} // end of namespace Common diff --git a/backends/vkeybd/polygon.h b/backends/vkeybd/polygon.h new file mode 100644 index 0000000000..69df2c0ca3 --- /dev/null +++ b/backends/vkeybd/polygon.h @@ -0,0 +1,114 @@ +/* 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_POLYGON_H +#define COMMON_POLYGON_H + +#include "common/array.h" +#include "common/rect.h" + +namespace Common { + +struct Polygon { + + + Polygon() {} + Polygon(const Polygon& p) : _points(p._points), _bound(p._bound) {} + Polygon(Array<Point> p) : _points(p) { + if (p.empty()) return; + _bound = Rect(p[0].x, p[0].y, p[0].x, p[0].y); + for (uint i = 1; i < p.size(); i++) { + _bound.extend(Rect(p[i].x, p[i].y, p[i].x, p[i].y)); + } + } + Polygon(Point *p, int n) { + for (int i = 0; i < n; i++) { + addPoint(p[i]); + } + } + virtual ~Polygon() {} + + void addPoint(const Point& p) { + _points.push_back(p); + _bound.extend(Rect(p.x, p.y, p.x, p.y)); + } + + void addPoint(int16 x, int16 y) { + addPoint(Point(x,y)); + } + + uint getPointCount() { + return _points.size(); + } + + /*! @brief check if given position is inside this polygon + + @param x the horizontal position to check + @param y the vertical position to check + + @return true if the given position is inside this polygon, false otherwise + */ + virtual bool contains(int16 x, int16 y) const; + + /*! @brief check if given point is inside this polygon + + @param p the point to check + + @return true if the given point is inside this polygon, false otherwise + */ + virtual bool contains(const Point &p) const { + return contains(p.x, p.y); + } + + virtual void moveTo(int16 x, int16 y) { + int16 dx = x - ((_bound.right + _bound.left) / 2); + int16 dy = y - ((_bound.bottom + _bound.top) / 2); + translate(dx, dy); + } + + virtual void moveTo(const Point &p) { + moveTo(p.x, p.y); + } + + virtual void translate(int16 dx, int16 dy) { + Array<Point>::iterator it; + for (it = _points.begin(); it != _points.end(); it++) { + it->x += dx; + it->y += dy; + } + } + + virtual Rect getBoundingRect() const { + return _bound; + } + +private: + Array<Point> _points; + Rect _bound; +}; + +} // end of namespace Common + +#endif diff --git a/backends/vkeybd/virtual-keyboard-gui.cpp b/backends/vkeybd/virtual-keyboard-gui.cpp new file mode 100644 index 0000000000..a66e0721f8 --- /dev/null +++ b/backends/vkeybd/virtual-keyboard-gui.cpp @@ -0,0 +1,416 @@ +/* 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/vkeybd/virtual-keyboard-gui.h" +#include "graphics/cursorman.h" +#include "gui/newgui.h" + +namespace Common { + +VirtualKeyboardGUI::VirtualKeyboardGUI(VirtualKeyboard *kbd) + : _kbd(kbd), _displaying(false), _drag(false), + _drawCaret(false), _displayEnabled(false), _firstRun(true), + _cursorAnimateTimer(0), _cursorAnimateCounter(0) { + + assert(_kbd); + assert(g_system); + _system = g_system; + + _lastScreenChanged = _system->getScreenChangeID(); + _screenW = _system->getOverlayWidth(); + _screenH = _system->getOverlayHeight(); + + + memset(_cursor, 0xFF, sizeof(_cursor)); +} + +VirtualKeyboardGUI::~VirtualKeyboardGUI() { + _overlayBackup.free(); + _dispSurface.free(); +} + +void VirtualKeyboardGUI::initMode(VirtualKeyboard::Mode *mode) { + _kbdSurface = mode->image; + _kbdTransparentColor = mode->transparentColor; + _kbdBound.setWidth(_kbdSurface->w); + _kbdBound.setHeight(_kbdSurface->h); + + if (mode->displayArea) + setupDisplayArea(*(mode->displayArea), mode->displayFontColor); + + if (_displaying) { + extendDirtyRect(_kbdBound); + redraw(); + } +} + +void VirtualKeyboardGUI::setupDisplayArea(Rect& r, OverlayColor forecolor) { + + _dispFont = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont); + if (!fontIsSuitable(_dispFont, r)) { + _dispFont = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont); + if (!fontIsSuitable(_dispFont, r)) { + _displayEnabled = false; + return; + } + } + _dispX = _kbdBound.left + r.left; + _dispY = _kbdBound.top + r.top + (r.height() - _dispFont->getFontHeight()) / 2; + _dispI = 0; + _dispForeColor = forecolor; + _dispBackColor = _dispForeColor + 0xFF; + _dispSurface.create(r.width(), _dispFont->getFontHeight(), sizeof(OverlayColor)); + _dispSurface.fillRect(Rect(_dispSurface.w, _dispSurface.h), _dispBackColor); + _displayEnabled = true; +} + +bool VirtualKeyboardGUI::fontIsSuitable(const Graphics::Font *font, const Rect& rect) { + return (font->getMaxCharWidth() < rect.width() && + font->getFontHeight() < rect.height()); +} + +void VirtualKeyboardGUI::checkScreenChanged() { + if (_lastScreenChanged != _system->getScreenChangeID()) + screenChanged(); +} + +void VirtualKeyboardGUI::initSize(int16 w, int16 h) { + _screenW = w; + _screenH = h; +} + +void VirtualKeyboardGUI::run() { + if (_firstRun) { + _firstRun = false; + moveToDefaultPosition(); + } + + if (!g_gui.isActive()) { + _system->showOverlay(); + _system->clearOverlay(); + } + _overlayBackup.create(_screenW, _screenH, sizeof(OverlayColor)); + _system->grabOverlay((OverlayColor*)_overlayBackup.pixels, _overlayBackup.w); + + setupCursor(); + + forceRedraw(); + _displaying = true; + mainLoop(); + + removeCursor(); + + _system->copyRectToOverlay((OverlayColor*)_overlayBackup.pixels, _overlayBackup.w, 0, 0, _overlayBackup.w, _overlayBackup.h); + if (!g_gui.isActive()) _system->hideOverlay(); + + _overlayBackup.free(); + _dispSurface.free(); +} + +void VirtualKeyboardGUI::close() { + _displaying = false; +} + +void VirtualKeyboardGUI::reset() { + _kbdBound.left = _kbdBound.top + = _kbdBound.right = _kbdBound.bottom = 0; + _displaying = _drag = false; + _firstRun = true; + _lastScreenChanged = _system->getScreenChangeID(); + _kbdSurface = 0; +} + +void VirtualKeyboardGUI::moveToDefaultPosition() +{ + int16 kbdW = _kbdBound.width(), kbdH = _kbdBound.height(); + int16 x = 0, y = 0; + if (_screenW != kbdW) { + switch (_kbd->_hAlignment) { + case VirtualKeyboard::kAlignLeft: + x = 0; + break; + case VirtualKeyboard::kAlignCentre: + x = (_screenW - kbdW) / 2; + break; + case VirtualKeyboard::kAlignRight: + x = _screenW - kbdW; + break; + } + } + if (_screenH != kbdH) { + switch (_kbd->_vAlignment) { + case VirtualKeyboard::kAlignTop: + y = 0; + break; + case VirtualKeyboard::kAlignMiddle: + y = (_screenH - kbdH) / 2; + break; + case VirtualKeyboard::kAlignBottom: + y = _screenH - kbdH; + break; + } + } + move(x, y); +} + +void VirtualKeyboardGUI::move(int16 x, int16 y) { + // add old position to dirty area + if (_displaying) extendDirtyRect(_kbdBound); + + // snap to edge of screen + if (ABS(x) < SNAP_WIDTH) + x = 0; + int16 x2 = _screenW - _kbdBound.width(); + if (ABS(x - x2) < SNAP_WIDTH) + x = x2; + if (ABS(y) < SNAP_WIDTH) + y = 0; + int16 y2 = _screenH - _kbdBound.height(); + if (ABS(y - y2) < SNAP_WIDTH) + y = y2; + + _dispX += x - _kbdBound.left; + _dispY += y - _kbdBound.top; + _kbdBound.moveTo(x, y); + + if (_displaying) { + // add new position to dirty area + extendDirtyRect(_kbdBound); + redraw(); + } +} + +void VirtualKeyboardGUI::screenChanged() { + _lastScreenChanged = _system->getScreenChangeID(); + int16 newScreenW = _system->getOverlayWidth(); + int16 newScreenH = _system->getOverlayHeight(); + if (_screenW != newScreenW || _screenH != newScreenH) { + _screenW = newScreenW; + _screenH = newScreenH; + if (!_kbd->checkModeResolutions()) { + _displaying = false; + return; + } + moveToDefaultPosition(); + } +} + + +void VirtualKeyboardGUI::mainLoop() { + Common::EventManager *eventMan = _system->getEventManager(); + + while (_displaying) { + if (_kbd->_keyQueue.hasStringChanged()) + updateDisplay(); + animateCaret(); + animateCursor(); + redraw(); + _system->updateScreen(); + Common::Event event; + while (eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_LBUTTONDOWN: + if (_kbdBound.contains(event.mouse)) { + _kbd->handleMouseDown(event.mouse.x - _kbdBound.left, + event.mouse.y - _kbdBound.top); + } + break; + case Common::EVENT_LBUTTONUP: + if (_kbdBound.contains(event.mouse)) { + _kbd->handleMouseUp(event.mouse.x - _kbdBound.left, + event.mouse.y - _kbdBound.top); + } + break; + case Common::EVENT_MOUSEMOVE: + if (_drag) + move(event.mouse.x - _dragPoint.x, + event.mouse.y - _dragPoint.y); + break; + case Common::EVENT_SCREEN_CHANGED: + screenChanged(); + break; + case Common::EVENT_QUIT: + _system->quit(); + return; + default: + break; + } + } + // Delay for a moment + _system->delayMillis(10); + } +} + +void VirtualKeyboardGUI::startDrag(int16 x, int16 y) { + _drag = true; + _dragPoint.x = x; + _dragPoint.y = y; +} + +void VirtualKeyboardGUI::endDrag() { + _drag = false; +} + +void VirtualKeyboardGUI::extendDirtyRect(const Rect &r) { + if (_dirtyRect.isValidRect()) { + _dirtyRect.extend(r); + } else { + _dirtyRect = r; + } + _dirtyRect.clip(Rect(_overlayBackup.w, _overlayBackup.h)); +} + +void VirtualKeyboardGUI::resetDirtyRect() { + _dirtyRect.setWidth(-1); +} + +void VirtualKeyboardGUI::forceRedraw() { + updateDisplay(); + extendDirtyRect(Rect(_overlayBackup.w, _overlayBackup.h)); + redraw(); +} + +void VirtualKeyboardGUI::redraw() { + assert(_kbdSurface); + int16 w = _dirtyRect.width(); + int16 h = _dirtyRect.height(); + if (w <= 0 || h <= 0) return; + + Graphics::SurfaceKeyColored surf; + surf.create(w, h, sizeof(OverlayColor)); + + OverlayColor *dst = (OverlayColor *)surf.pixels; + const OverlayColor *src = (OverlayColor *) _overlayBackup.getBasePtr(_dirtyRect.left, _dirtyRect.top); + + while (h--) { + memcpy(dst, src, surf.w * sizeof(OverlayColor)); + dst += surf.w; + src += _overlayBackup.w; + } + + surf.blit(_kbdSurface, _kbdBound.left - _dirtyRect.left, + _kbdBound.top - _dirtyRect.top, _kbdTransparentColor); + if (_displayEnabled) { + surf.blit(&_dispSurface, _dispX - _dirtyRect.left, + _dispY - _dirtyRect.top, _dispBackColor); + } + _system->copyRectToOverlay((OverlayColor*)surf.pixels, surf.w, + _dirtyRect.left, _dirtyRect.top, surf.w, surf.h); + + surf.free(); + + resetDirtyRect(); +} + +uint VirtualKeyboardGUI::calculateEndIndex(const String& str, uint startIndex) { + int16 w = 0; + while (w <= _dispSurface.w && startIndex < str.size()) { + w += _dispFont->getCharWidth(str[startIndex++]); + } + if (w > _dispSurface.w) startIndex--; + return startIndex; +} + +void VirtualKeyboardGUI::animateCaret() { + if (!_displayEnabled) return; + + if (_system->getMillis() % kCaretBlinkTime < kCaretBlinkTime / 2) { + if (!_drawCaret) { + _drawCaret = true; + _dispSurface.drawLine(_caretX, 0, _caretX, _dispSurface.h, _dispForeColor); + extendDirtyRect(Rect(_dispX + _caretX, _dispY, _dispX + _caretX + 1, _dispY + _dispSurface.h)); + } + } else { + if (_drawCaret) { + _drawCaret = false; + _dispSurface.drawLine(_caretX, 0, _caretX, _dispSurface.h, _dispBackColor); + extendDirtyRect(Rect(_dispX + _caretX, _dispY, _dispX + _caretX + 1, _dispY + _dispSurface.h)); + } + } +} + +void VirtualKeyboardGUI::updateDisplay() { + if (!_displayEnabled) return; + + // calculate the text to display + uint cursorPos = _kbd->_keyQueue.getInsertIndex(); + String wholeText = _kbd->_keyQueue.getString(); + uint dispTextEnd; + if (_dispI > cursorPos) + _dispI = cursorPos; + + dispTextEnd = calculateEndIndex(wholeText, _dispI); + while (cursorPos > dispTextEnd) + dispTextEnd = calculateEndIndex(wholeText, ++_dispI); + + String dispText = String(wholeText.c_str() + _dispI, wholeText.c_str() + dispTextEnd); + + // draw to display surface + _dispSurface.fillRect(Rect(_dispSurface.w, _dispSurface.h), _dispBackColor); + _dispFont->drawString(&_dispSurface, dispText, 0, 0, _dispSurface.w, _dispForeColor); + + String beforeCaret(wholeText.c_str() + _dispI, wholeText.c_str() + cursorPos); + _caretX = _dispFont->getStringWidth(beforeCaret); + if (_drawCaret) _dispSurface.drawLine(_caretX, 0, _caretX, _dispSurface.h, _dispForeColor); + + extendDirtyRect(Rect(_dispX, _dispY, _dispX + _dispSurface.w, _dispY + _dispSurface.h)); +} + +void VirtualKeyboardGUI::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 VirtualKeyboardGUI::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 VirtualKeyboardGUI::removeCursor() { + CursorMan.popCursor(); + CursorMan.popCursorPalette(); +} + +} // end of namespace Common diff --git a/backends/vkeybd/virtual-keyboard-gui.h b/backends/vkeybd/virtual-keyboard-gui.h new file mode 100644 index 0000000000..e99d552479 --- /dev/null +++ b/backends/vkeybd/virtual-keyboard-gui.h @@ -0,0 +1,153 @@ +/* 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_GUI +#define COMMON_VIRTUAL_KEYBOARD_GUI + +#include "backends/vkeybd/virtual-keyboard.h" +#include "common/rect.h" +#include "common/system.h" +#include "graphics/fontman.h" +#include "graphics/surface-keycolored.h" + +namespace Common { + +/** + * Class to handle the drawing of the virtual keyboard to the overlay, and the + * execution of the keyboard's main loop. + * This includes the blitting of the appropriate bitmap in the correct location, + * as well as the drawing of the virtual keyboard display. + */ +class VirtualKeyboardGUI { + +public: + + VirtualKeyboardGUI(VirtualKeyboard *kbd); + ~VirtualKeyboardGUI(); + + /** + * Updates the GUI when the Mode of the keyboard is changes + */ + void initMode(VirtualKeyboard::Mode *mode); + + /** + * Starts the drawing of the keyboard, and runs the main event loop. + */ + void run(); + + /** + * Interrupts the event loop and resets the overlay to its initial state. + */ + void close(); + + bool isDisplaying() { return _displaying; } + + /** + * Reset the class to an initial state + */ + void reset(); + + /** + * Activates drag mode. Takes the keyboard-relative coordinates of the + * cursor as an argument. + */ + void startDrag(int16 x, int16 y); + + /** + * Deactivates drag mode + * */ + void endDrag(); + + /** + * Checks for a screen change in the backend and re-inits the virtual + * keyboard if it has. + */ + void checkScreenChanged(); + + /** + * Sets the GUI's internal screen size variables + */ + void initSize(int16 w, int16 h); + +private: + + OSystem *_system; + + VirtualKeyboard *_kbd; + Rect _kbdBound; + Graphics::Surface *_kbdSurface; + OverlayColor _kbdTransparentColor; + + Point _dragPoint; + bool _drag; + static const int SNAP_WIDTH = 10; + + Graphics::Surface _overlayBackup; + Rect _dirtyRect; + + bool _displayEnabled; + Graphics::Surface _dispSurface; + const Graphics::Font *_dispFont; + int16 _dispX, _dispY; + uint _dispI; + OverlayColor _dispForeColor, _dispBackColor; + + int _lastScreenChanged; + int16 _screenW, _screenH; + + bool _displaying; + bool _firstRun; + + void setupDisplayArea(Rect& r, OverlayColor forecolor); + void move(int16 x, int16 y); + void moveToDefaultPosition(); + void screenChanged(); + void mainLoop(); + void extendDirtyRect(const Rect &r); + void resetDirtyRect(); + void redraw(); + void forceRedraw(); + void updateDisplay(); + bool fontIsSuitable(const Graphics::Font *font, const Rect& rect); + uint calculateEndIndex(const String& str, uint startIndex); + + bool _drawCaret; + int16 _caretX; + static const int kCaretBlinkTime = 500; + void animateCaret(); + + static const int kCursorAnimateDelay = 250; + int _cursorAnimateCounter; + int _cursorAnimateTimer; + byte _cursor[2048]; + void setupCursor(); + void removeCursor(); + void animateCursor(); + +}; + +} // end of namespace Common + +#endif diff --git a/backends/vkeybd/virtual-keyboard-parser.cpp b/backends/vkeybd/virtual-keyboard-parser.cpp new file mode 100644 index 0000000000..a2b035f1b5 --- /dev/null +++ b/backends/vkeybd/virtual-keyboard-parser.cpp @@ -0,0 +1,363 @@ +/* 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/vkeybd/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) { +} + +void VirtualKeyboardParser::cleanup() { + _mode = 0; + _kbdParsed = false; + _initialModeName.clear(); + if (_parseMode == kParseFull) { + // reset keyboard to remove existing config + _keyboard->reset(); + } +} + +bool VirtualKeyboardParser::closedKeyCallback(ParserNode *node) { + if (node->name.equalsIgnoreCase("keyboard")) { + _kbdParsed = true; + if (!_keyboard->_initialMode) + return parserError("Initial mode of keyboard pack not defined"); + } else if (node->name.equalsIgnoreCase("mode")) { + if (!_layoutParsed) { + return parserError("'%s' layout missing from '%s' mode", + _mode->resolution.c_str(), _mode->name.c_str()); + } + } + return true; +} + +bool VirtualKeyboardParser::parserCallback_keyboard(ParserNode *node) { + + if (_kbdParsed) + return parserError("Only a single keyboard element is allowed"); + + // if not full parse then we're done + if (_parseMode == kParseCheckResolutions) + return true; + + _initialModeName = node->values["initial_mode"]; + + if (node->values.contains("h_align")) { + String h = node->values["h_align"]; + if (h.equalsIgnoreCase("left")) + _keyboard->_hAlignment = VirtualKeyboard::kAlignLeft; + else if (h.equalsIgnoreCase("centre") || h.equalsIgnoreCase("center")) + _keyboard->_hAlignment = VirtualKeyboard::kAlignCentre; + else if (h.equalsIgnoreCase("right")) + _keyboard->_hAlignment = VirtualKeyboard::kAlignRight; + } + + if (node->values.contains("v_align")) { + String v = node->values["h_align"]; + if (v.equalsIgnoreCase("top")) + _keyboard->_vAlignment = VirtualKeyboard::kAlignTop; + else if (v.equalsIgnoreCase("middle") || v.equalsIgnoreCase("center")) + _keyboard->_vAlignment = VirtualKeyboard::kAlignMiddle; + else if (v.equalsIgnoreCase("bottom")) + _keyboard->_vAlignment = VirtualKeyboard::kAlignBottom; + } + + return true; +} + +bool VirtualKeyboardParser::parserCallback_mode(ParserNode *node) { + + String name = node->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; + + String resolutions = node->values["resolutions"]; + 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; + String newResolution; + for (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) { + node->ignore = true; + return true; + } else { + // remove data relating to old resolution + ImageMan.unregisterSurface(_mode->bitmapName); + _mode->bitmapName.clear(); + _mode->image = 0; + _mode->imageMap.removeAllAreas(); + delete _mode->displayArea; + _mode->displayArea = 0; + } + } + + _mode->resolution = newResolution; + _layoutParsed = false; + + return true; +} + +bool VirtualKeyboardParser::parserCallback_event(ParserNode *node) { + + // if just checking resolutions we're done + if (_parseMode == kParseCheckResolutions) + return true; + + String name = node->values["name"]; + if (_mode->events.contains(name)) + return parserError("Event '%s' has already been defined", name.c_str()); + + VirtualKeyboard::VKEvent *evt = new VirtualKeyboard::VKEvent(); + evt->name = name; + + String type = node->values["type"]; + if (type.equalsIgnoreCase("key")) { + if (!node->values.contains("code") || !node->values.contains("ascii")) { + delete evt; + return parserError("Key event element must contain code and ascii attributes"); + } + evt->type = VirtualKeyboard::kVKEventKey; + + KeyState *ks = (KeyState*) malloc(sizeof(KeyState)); + ks->keycode = (KeyCode)atoi(node->values["code"].c_str()); + ks->ascii = atoi(node->values["ascii"].c_str()); + ks->flags = 0; + if (node->values.contains("modifiers")) + ks->flags = parseFlags(node->values["modifiers"]); + evt->data = ks; + + } else if (type.equalsIgnoreCase("modifier")) { + if (!node->values.contains("modifiers")) { + delete evt; + return parserError("Key modifier element must contain modifier attributes"); + } + + evt->type = VirtualKeyboard::kVKEventModifier; + byte *flags = (byte*) malloc(sizeof(byte)); + *(flags) = parseFlags(node->values["modifiers"]); + evt->data = flags; + + } else if (type.equalsIgnoreCase("switch_mode")) { + if (!node->values.contains("mode")) { + delete evt; + return parserError("Switch mode event element must contain mode attribute"); + } + + evt->type = VirtualKeyboard::kVKEventSwitchMode; + String& mode = node->values["mode"]; + char *str = (char*) malloc(sizeof(char) * mode.size() + 1); + memcpy(str, mode.c_str(), sizeof(char) * mode.size()); + str[mode.size()] = 0; + evt->data = str; + } else if (type.equalsIgnoreCase("submit")) { + evt->type = VirtualKeyboard::kVKEventSubmit; + } else if (type.equalsIgnoreCase("cancel")) { + evt->type = VirtualKeyboard::kVKEventCancel; + } else if (type.equalsIgnoreCase("clear")) { + evt->type = VirtualKeyboard::kVKEventClear; + } else if (type.equalsIgnoreCase("delete")) { + evt->type = VirtualKeyboard::kVKEventDelete; + } else if (type.equalsIgnoreCase("move_left")) { + evt->type = VirtualKeyboard::kVKEventMoveLeft; + } else if (type.equalsIgnoreCase("move_right")) { + evt->type = VirtualKeyboard::kVKEventMoveRight; + } else { + delete evt; + return parserError("Event type '%s' not known", type.c_str()); + } + + _mode->events[name] = evt; + + return true; +} + +bool VirtualKeyboardParser::parserCallback_layout(ParserNode *node) { + + assert(!_mode->resolution.empty()); + + String res = node->values["resolution"]; + + if (res != _mode->resolution) { + node->ignore = true; + return true; + } + + _mode->bitmapName = node->values["bitmap"]; + _mode->image = ImageMan.getSurface(_mode->bitmapName); + if (!_mode->image) { + 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 (node->values.contains("transparent_color")) { + int r, g, b; + if (!parseIntegerKey(node->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 // default to purple + _mode->transparentColor = g_system->RGBToColor(255, 0, 255); + + if (node->values.contains("display_font_color")) { + int r, g, b; + if (!parseIntegerKey(node->values["display_font_color"].c_str(), 3, &r, &g, &b)) + return parserError("Could not parse color value"); + _mode->displayFontColor = g_system->RGBToColor(r, g, b); + } else + _mode->displayFontColor = g_system->RGBToColor(0, 0, 0); // default to black + + _layoutParsed = true; + + return true; +} + +bool VirtualKeyboardParser::parserCallback_map(ParserNode *node) { + return true; +} + +bool VirtualKeyboardParser::parserCallback_area(ParserNode *node) { + + String& shape = node->values["shape"]; + String& target = node->values["target"]; + String& coords = node->values["coords"]; + + if (target.equalsIgnoreCase("display_area")) { + if (! shape.equalsIgnoreCase("rect")) + return parserError("display_area must be a rect area"); + _mode->displayArea = new Rect(); + return parseRect(_mode->displayArea, coords); + } else if (shape.equalsIgnoreCase("rect")) { + Polygon *poly = _mode->imageMap.createArea(target); + return parseRectAsPolygon(poly, coords); + } else if (shape.equalsIgnoreCase("poly")) { + Polygon *poly = _mode->imageMap.createArea(target); + return parsePolygon(poly, coords); + } + return parserError("Area shape '%s' not known", shape.c_str()); +} + +byte VirtualKeyboardParser::parseFlags(const String& flags) { + if (flags.empty()) + return 0; + + StringTokenizer tok(flags, ", "); + byte val = 0; + for (String fl = tok.nextToken(); !fl.empty(); fl = tok.nextToken()) { + if (fl == "ctrl" || fl == "control") + val |= KBD_CTRL; + else if (fl == "alt") + val |= KBD_ALT; + else if (fl == "shift") + val |= KBD_SHIFT; + } + return val; +} + +bool VirtualKeyboardParser::parseRect(Rect *rect, const String& coords) { + int x1, y1, x2, y2; + if (!parseIntegerKey(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; + if (!rect->isValidRect()) + return parserError("Rect area is not a valid rectangle"); + return true; +} + +bool VirtualKeyboardParser::parsePolygon(Polygon *poly, const String& coords) { + StringTokenizer tok (coords, ", "); + for (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"); + + return true; +} + +bool VirtualKeyboardParser::parseRectAsPolygon(Polygon *poly, const String& coords) { + Rect rect; + if (!parseRect(&rect, coords)) + return false; + poly->addPoint(rect.left, rect.top); + poly->addPoint(rect.right, rect.top); + poly->addPoint(rect.right, rect.bottom); + poly->addPoint(rect.left, rect.bottom); + return true; +} + +} // end of namespace GUI diff --git a/backends/vkeybd/virtual-keyboard-parser.h b/backends/vkeybd/virtual-keyboard-parser.h new file mode 100644 index 0000000000..5ad353c516 --- /dev/null +++ b/backends/vkeybd/virtual-keyboard-parser.h @@ -0,0 +1,267 @@ +/* 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/vkeybd/virtual-keyboard.h" + +/** + + *************************************** + ** 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. + +required attributes: + - initial_mode: name of the mode the keyboard will show initially + +optional attributes: + - v_align/h_align: where on the screen should the keyboard appear initially + (defaults to bottom/center). + +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. + +required 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. + +required attributes: + - name: name of the event + - type: key | modifier | switch_mode | submit | cancel | clear | delete | + move_left | move_right - see VirtualKeyboard::EventType for explanation +for key events + - code / ascii: describe a key press in ScummVM KeyState format +for key and modifier events + - modifiers: modifier keystate as comma-separated list of shift, ctrl and/or + alt. +for switch_mode events + - mode: name of the mode that should be switched to +------------------------------------------------------------------------------- + +<layout> + +These tags encapsulate an implementation of a mode at a particular resolution. + +required 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 + +optional attributes: + - transparent_color: color in r,g,b format that will be used for keycolor + transparency (defaults to (255,0,255). + - display_font_color: color in r,g,b format that will be used for the text of + the keyboard display (defaults to (0,0,0). + +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 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). They will usually be generated by +an external tool such as GIMP's Image Map plugin, and so will not be written +by hand, but for more 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 { + +/** + * Subclass of Common::XMLParser that parses the virtual keyboard pack + * description file + */ +class VirtualKeyboardParser : public XMLParser { + +public: + + /** + * Enum dictating how extensive a parse will be + */ + enum ParseMode { + /** + * Full parse - when loading keyboard pack for first time + */ + kParseFull, + /** + * Just check resolutions and reload layouts if needed - following a + * change in screen size + */ + kParseCheckResolutions + }; + + VirtualKeyboardParser(VirtualKeyboard *kbd); + void setParseMode(ParseMode m) { + _parseMode = m; + } + +protected: + CUSTOM_XML_PARSER(VirtualKeyboardParser) { + XML_KEY(keyboard) + XML_PROP(initial_mode, true) + XML_PROP(v_align, false) + XML_PROP(h_align, false) + XML_KEY(mode) + XML_PROP(name, true) + XML_PROP(resolutions, true) + XML_KEY(layout) + XML_PROP(resolution, true) + XML_PROP(bitmap, true) + XML_PROP(transparent_color, false) + XML_PROP(display_font_color, false) + XML_KEY(map) + XML_KEY(area) + XML_PROP(shape, true) + XML_PROP(coords, true) + XML_PROP(target, true) + KEY_END() + KEY_END() + KEY_END() + XML_KEY(event) + XML_PROP(name, true) + XML_PROP(type, true) + XML_PROP(code, false) + XML_PROP(ascii, false) + XML_PROP(modifiers, false) + XML_PROP(mode, false) + KEY_END() + KEY_END() + KEY_END() + } PARSER_END() + +protected: + VirtualKeyboard *_keyboard; + + /** internal state variables of parser */ + ParseMode _parseMode; + VirtualKeyboard::Mode *_mode; + String _initialModeName; + bool _kbdParsed; + bool _layoutParsed; + + /** Cleanup internal state before parse */ + virtual void cleanup(); + + /** Parser callback function */ + bool parserCallback_keyboard(ParserNode *node); + bool parserCallback_mode(ParserNode *node); + bool parserCallback_event(ParserNode *node); + bool parserCallback_layout(ParserNode *node); + bool parserCallback_map(ParserNode *node); + bool parserCallback_area(ParserNode *node); + virtual bool closedKeyCallback(ParserNode *node); + + /** Parse helper functions */ + byte parseFlags(const String& flags); + bool parseRect(Rect *rect, const String& coords); + bool parsePolygon(Polygon *poly, const String& coords); + bool parseRectAsPolygon(Polygon *poly, const String& coords); +}; + +} // end of namespace GUI + +#endif diff --git a/backends/vkeybd/virtual-keyboard.cpp b/backends/vkeybd/virtual-keyboard.cpp new file mode 100644 index 0000000000..fab2d80d30 --- /dev/null +++ b/backends/vkeybd/virtual-keyboard.cpp @@ -0,0 +1,390 @@ +/* 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/vkeybd/virtual-keyboard.h" +#include "backends/vkeybd/virtual-keyboard-gui.h" +#include "backends/vkeybd/virtual-keyboard-parser.h" +#include "backends/vkeybd/keycode-descriptions.h" +#include "common/config-manager.h" +#include "common/fs.h" +#include "graphics/imageman.h" +#include "common/unzip.h" + +#define KEY_START_CHAR ('[') +#define KEY_END_CHAR (']') + +namespace Common { + +VirtualKeyboard::VirtualKeyboard() : _currentMode(0) { + assert(g_system); + _system = g_system; + + _parser = new VirtualKeyboardParser(this); + _kbdGUI = new VirtualKeyboardGUI(this); + _submitKeys = _loaded = false; + +} + +VirtualKeyboard::~VirtualKeyboard() { + deleteEvents(); + delete _kbdGUI; + delete _parser; +} + +void VirtualKeyboard::deleteEvents() { + ModeMap::iterator it_m; + VKEventMap::iterator it_e; + for (it_m = _modes.begin(); it_m != _modes.end(); it_m++) { + VKEventMap *evt = &(it_m->_value.events); + for (it_e = evt->begin(); it_e != evt->end(); it_e++) + delete it_e->_value; + } +} + +void VirtualKeyboard::reset() { + deleteEvents(); + _modes.clear(); + _initialMode = _currentMode = 0; + _hAlignment = kAlignCentre; + _vAlignment = kAlignBottom; + _keyQueue.clear(); + _loaded = false; + _kbdGUI->reset(); +} + +bool VirtualKeyboard::loadKeyboardPack(String packName) { + + _kbdGUI->initSize(_system->getOverlayWidth(), _system->getOverlayHeight()); + + FilesystemNode *vkDir = 0; + if (ConfMan.hasKey("vkeybdpath")) { + vkDir = new FilesystemNode(ConfMan.get("vkeybdpath")); + } else if (ConfMan.hasKey("extrapath")) { + vkDir = new FilesystemNode(ConfMan.get("extrapath")); + } else { // use current directory + vkDir = new FilesystemNode("."); + } + + if (vkDir->getChild(packName + ".xml").exists()) { + // uncompressed keyboard pack + + if (!_parser->loadFile(vkDir->getChild(packName + ".xml"))) + return false; + + } else if (vkDir->getChild(packName + ".zip").exists()) { + // compressed keyboard pack +#ifdef USE_ZLIB + ZipArchive arch(vkDir->getChild(packName + ".zip").getPath().c_str()); + if (arch.hasFile(packName + ".xml")) { + if (!_parser->loadStream(arch.openFile(packName + ".xml"))) + return false; + } else { + warning("Could not find %s.xml file in %s.zip keyboard pack", packName.c_str(), packName.c_str()); + return false; + } + ImageMan.addArchive(vkDir->getChild(packName + ".zip").getPath().c_str()); +#else + return false; +#endif + } else { + warning("Keyboard pack not found"); + return false; + } + + _parser->setParseMode(VirtualKeyboardParser::kParseFull); + _loaded = _parser->parse(); + if (_loaded) + printf("Keyboard pack '%s' loaded successfully!\n", packName.c_str()); + + return _loaded; +} + +bool VirtualKeyboard::checkModeResolutions() +{ + _parser->setParseMode(VirtualKeyboardParser::kParseCheckResolutions); + _loaded = _parser->parse(); + if (_currentMode) _kbdGUI->initMode(_currentMode); + return _loaded; +} + +String VirtualKeyboard::findArea(int16 x, int16 y) { + return _currentMode->imageMap.findMapArea(x, y); +} + +void VirtualKeyboard::processAreaClick(const String& area) { + if (!_currentMode->events.contains(area)) return; + VKEvent *evt = _currentMode->events[area]; + + switch (evt->type) { + case kVKEventKey: { + // add virtual keypress to queue + _keyQueue.insertKey(*(KeyState*)evt->data); + break; + } + case kVKEventModifier: + _keyQueue.toggleFlags(*(byte*)(evt->data)); + break; + case kVKEventSwitchMode: + // switch to new mode + switchMode((char *)evt->data); + _keyQueue.clearFlags(); + break; + case kVKEventSubmit: + close(true); + break; + case kVKEventCancel: + close(false); + break; + case kVKEventClear: + _keyQueue.clear(); + break; + case kVKEventDelete: + _keyQueue.deleteKey(); + break; + case kVKEventMoveLeft: + _keyQueue.moveLeft(); + break; + case kVKEventMoveRight: + _keyQueue.moveRight(); + break; + } +} + +void VirtualKeyboard::switchMode(Mode *newMode) { + _kbdGUI->initMode(newMode); + _currentMode = newMode; +} + +void VirtualKeyboard::switchMode(const String& newMode) { + if (!_modes.contains(newMode)) { + warning("Keyboard mode '%s' unknown", newMode.c_str()); + return; + } + switchMode(&_modes[newMode]); +} + +void VirtualKeyboard::handleMouseDown(int16 x, int16 y) { + _areaDown = findArea(x, y); + if (_areaDown.empty()) + _kbdGUI->startDrag(x, y); +} + +void VirtualKeyboard::handleMouseUp(int16 x, int16 y) { + if (!_areaDown.empty() && _areaDown == findArea(x, y)) { + processAreaClick(_areaDown); + _areaDown.clear(); + } + _kbdGUI->endDrag(); +} + +void VirtualKeyboard::show() { + if (_loaded) _kbdGUI->checkScreenChanged(); + if (!_loaded) { + warning("Virtual keyboard not loaded"); + return; + } + + switchMode(_initialMode); + _kbdGUI->run(); + + if (_submitKeys) { + EventManager *eventMan = _system->getEventManager(); + assert(eventMan); + + // push keydown & keyup events into the event manager + Event evt; + evt.synthetic = false; + while (!_keyQueue.empty()) { + evt.kbd = _keyQueue.pop(); + evt.type = EVENT_KEYDOWN; + eventMan->pushEvent(evt); + evt.type = EVENT_KEYUP; + eventMan->pushEvent(evt); + } + } else { + _keyQueue.clear(); + } +} + +void VirtualKeyboard::close(bool submit) { + _submitKeys = submit; + _kbdGUI->close(); +} + +bool VirtualKeyboard::isDisplaying() { + return _kbdGUI->isDisplaying(); +} + +VirtualKeyboard::KeyPressQueue::KeyPressQueue() { + _keyPos = _keys.end(); + _strPos = 0; + _strChanged = false; + _flags = 0; +} + +void VirtualKeyboard::KeyPressQueue::toggleFlags(byte fl) { + _flags ^= fl; + _flagsStr.clear(); + if (_flags) { + _flagsStr = KEY_START_CHAR; + if (_flags & KBD_CTRL) + _flagsStr += "Ctrl+"; + if (_flags & KBD_ALT) + _flagsStr += "Alt+"; + if (_flags & KBD_SHIFT) + _flagsStr += "Shift+"; + } + _strChanged = true; +} + +void VirtualKeyboard::KeyPressQueue::clearFlags() { + _flags = 0; + _flagsStr.clear(); + _strChanged = true; +} + +void VirtualKeyboard::KeyPressQueue::insertKey(KeyState key) { + _strChanged = true; + key.flags ^= _flags; + if ((key.keycode >= KEYCODE_a) && (key.keycode <= KEYCODE_z)) + key.ascii = (key.flags & KBD_SHIFT) ? key.keycode - 32 : key.keycode; + clearFlags(); + + String keyStr; + if (key.flags & KBD_CTRL) keyStr += "Ctrl+"; + if (key.flags & KBD_ALT) keyStr += "Alt+"; + + if (key.ascii >= 32 && key.ascii <= 255) { + if (key.flags & KBD_SHIFT && (key.ascii < 65 || key.ascii > 90)) + keyStr += "Shift+"; + keyStr += (char)key.ascii; + } else { + if (key.flags & KBD_SHIFT) keyStr += "Shift+"; + if (key.keycode >= 0 && key.keycode < keycodeDescTableSize) + keyStr += keycodeDescTable[key.keycode]; + } + + if (keyStr.empty()) keyStr += "???"; + + _keysStr.insertChar(KEY_START_CHAR, _strPos++); + const char *k = keyStr.c_str(); + while (char ch = *k++) + _keysStr.insertChar(ch, _strPos++); + _keysStr.insertChar(KEY_END_CHAR, _strPos++); + + VirtualKeyPress kp; + kp.key = key; + kp.strLen = keyStr.size() + 2; + _keys.insert(_keyPos, kp); +} + +void VirtualKeyboard::KeyPressQueue::deleteKey() { + if (_keyPos == _keys.begin()) + return; + List<VirtualKeyPress>::iterator it = _keyPos; + it--; + _strPos -= it->strLen; + while((it->strLen)-- > 0) + _keysStr.deleteChar(_strPos); + _keys.erase(it); + _strChanged = true; +} + +void VirtualKeyboard::KeyPressQueue::moveLeft() { + if (_keyPos == _keys.begin()) + return; + _keyPos--; + _strPos -= _keyPos->strLen; + _strChanged = true; +} + +void VirtualKeyboard::KeyPressQueue::moveRight() { + if (_keyPos == _keys.end()) + return; + _strPos += _keyPos->strLen; + _keyPos++; + _strChanged = true; +} + +KeyState VirtualKeyboard::KeyPressQueue::pop() { + bool front = (_keyPos == _keys.begin()); + VirtualKeyPress kp = *(_keys.begin()); + _keys.pop_front(); + + if (front) + _keyPos = _keys.begin(); + else + _strPos -= kp.strLen; + + while (kp.strLen-- > 0) + _keysStr.deleteChar(0); + + return kp.key; +} + +void VirtualKeyboard::KeyPressQueue::clear() { + _keys.clear(); + _keyPos = _keys.end(); + _keysStr.clear(); + _strPos = 0; + clearFlags(); + _strChanged = true; +} + +bool VirtualKeyboard::KeyPressQueue::empty() +{ + return _keys.empty(); +} + +String VirtualKeyboard::KeyPressQueue::getString() +{ + if (_keysStr.empty()) + return _flagsStr; + if (_flagsStr.empty()) + return _keysStr; + if (_strPos == _keysStr.size()) + return _keysStr + _flagsStr; + + uint len = _keysStr.size() + _flagsStr.size(); + char *str = new char[len]; + memcpy(str, _keysStr.c_str(), _strPos); + memcpy(str + _strPos, _flagsStr.c_str(), _flagsStr.size()); + memcpy(str + _strPos + _flagsStr.size(), _keysStr.c_str() + _strPos, _keysStr.size() - _strPos); + String ret(str, len); + delete[] str; + return ret; +} + +uint VirtualKeyboard::KeyPressQueue::getInsertIndex() { + return _strPos + _flagsStr.size(); +} + +bool VirtualKeyboard::KeyPressQueue::hasStringChanged() { + bool ret = _strChanged; + _strChanged = false; + return ret; +} + +} // end of namespace Common diff --git a/backends/vkeybd/virtual-keyboard.h b/backends/vkeybd/virtual-keyboard.h new file mode 100644 index 0000000000..f2f7485c6d --- /dev/null +++ b/backends/vkeybd/virtual-keyboard.h @@ -0,0 +1,253 @@ +/* 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 "backends/vkeybd/image-map.h" +#include "common/keyboard.h" +#include "common/list.h" +#include "common/str.h" + +namespace Common { + +class VirtualKeyboardGUI; +class VirtualKeyboardParser; + +/** + * Class that handles the functionality of the virtual keyboard. + * This includes storage of the virtual key press events when the user clicks + * a key and delivery of them when the keyboard is closed, as well as managing + * the internal state of the keyboard, such as its active mode. + */ +class VirtualKeyboard { +protected: + + /** + * Enum to describe the different types of events that can be associated + * with an area of the virtual keyboard bitmap. + */ + enum VKEventType { + /** Standard key press event */ + kVKEventKey, + /** Modifier key press event */ + kVKEventModifier, + /** Switch the mode of the keyboard */ + kVKEventSwitchMode, + /** Close the keyboard, submitting all keypresses */ + kVKEventSubmit, + /** Close the keyboard, without submitting keypresses */ + kVKEventCancel, + /** Clear the virtual keypress queue */ + kVKEventClear, + /** Move the keypress queue insert position backwards */ + kVKEventMoveLeft, + /** Move the keypress queue insert position forwards */ + kVKEventMoveRight, + /** Delete keypress from queue at the current insert position */ + kVKEventDelete + }; + + /** VKEvent struct encapsulates data on a virtual keyboard event */ + struct VKEvent { + String name; + VKEventType type; + /** + * Void pointer that will point to different types of data depending + * on the type of the event, these are: + * - KeyState struct for kVKEventKey events + * - a flags byte for kVKEventModifier events + * - c-string stating the name of the new mode for kSwitchMode events + */ + void *data; + + VKEvent() : data(0) {} + ~VKEvent() { + if (data) free(data); + } + }; + + typedef HashMap<String, VKEvent*> VKEventMap; + + /** + * Mode struct encapsulates all the data for each mode of the keyboard + */ + struct Mode { + String name; + String resolution; + String bitmapName; + Graphics::Surface *image; + OverlayColor transparentColor; + ImageMap imageMap; + VKEventMap events; + Rect *displayArea; + OverlayColor displayFontColor; + + Mode() : image(0), displayArea(0) {} + ~Mode() { delete displayArea; } + }; + + typedef HashMap<String, Mode, IgnoreCase_Hash, IgnoreCase_EqualTo> ModeMap; + + enum HorizontalAlignment { + kAlignLeft, + kAlignCentre, + kAlignRight + }; + + enum VerticalAlignment { + kAlignTop, + kAlignMiddle, + kAlignBottom + }; + + struct VirtualKeyPress { + KeyState key; + /** length of the key presses description string */ + uint strLen; + }; + + /** + * Class that stores the queue of virtual key presses, as well as + * maintaining a string that represents a preview of the queue + */ + class KeyPressQueue { + public: + KeyPressQueue(); + void toggleFlags(byte fl); + void clearFlags(); + void insertKey(KeyState key); + void deleteKey(); + void moveLeft(); + void moveRight(); + KeyState pop(); + void clear(); + bool empty(); + String getString(); + uint getInsertIndex(); + bool hasStringChanged(); + + private: + byte _flags; + String _flagsStr; + + + List<VirtualKeyPress> _keys; + String _keysStr; + + bool _strChanged; + + List<VirtualKeyPress>::iterator _keyPos; + uint _strPos; + }; + +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(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. + * @param submit if true all accumulated key presses are submitted to + * the event manager + */ + void close(bool submit); + + /** + * Returns true if the keyboard is currently being shown + */ + bool isDisplaying(); + + /** + * Returns true if the keyboard is loaded and ready to be shown + */ + bool isLoaded() { + return _loaded; + } + +protected: + + OSystem *_system; + + friend class VirtualKeyboardGUI; + VirtualKeyboardGUI *_kbdGUI; + + KeyPressQueue _keyQueue; + + friend class VirtualKeyboardParser; + VirtualKeyboardParser *_parser; + + void reset(); + void deleteEvents(); + bool checkModeResolutions(); + void switchMode(Mode *newMode); + void switchMode(const String& newMode); + void handleMouseDown(int16 x, int16 y); + void handleMouseUp(int16 x, int16 y); + String findArea(int16 x, int16 y); + void processAreaClick(const String &area); + + bool _loaded; + + ModeMap _modes; + Mode *_initialMode; + Mode *_currentMode; + + HorizontalAlignment _hAlignment; + VerticalAlignment _vAlignment; + + String _areaDown; + + bool _submitKeys; + +}; + + +} // End of namespace Common + + +#endif diff --git a/backends/vkeybd/vkeybd.zip b/backends/vkeybd/vkeybd.zip Binary files differnew file mode 100644 index 0000000000..216512fef2 --- /dev/null +++ b/backends/vkeybd/vkeybd.zip |