aboutsummaryrefslogtreecommitdiff
path: root/backends/vkeybd
diff options
context:
space:
mode:
authorStephen Kennedy2008-08-07 16:38:39 +0000
committerStephen Kennedy2008-08-07 16:38:39 +0000
commit2645ca48ad9bc219b43ab88cc5b071952297a992 (patch)
treed1383869820354fb99560d630c0d694535d5ad61 /backends/vkeybd
parent66e4e3ec1a038bc653bb1c04893c39033ac3fb62 (diff)
downloadscummvm-rg350-2645ca48ad9bc219b43ab88cc5b071952297a992.tar.gz
scummvm-rg350-2645ca48ad9bc219b43ab88cc5b071952297a992.tar.bz2
scummvm-rg350-2645ca48ad9bc219b43ab88cc5b071952297a992.zip
* Moved VK code into backends/vkeybd
* Moved Keymapper code into backends/keymapper svn-id: r33681
Diffstat (limited to 'backends/vkeybd')
-rw-r--r--backends/vkeybd/image-map.cpp69
-rw-r--r--backends/vkeybd/image-map.h53
-rw-r--r--backends/vkeybd/polygon.cpp55
-rw-r--r--backends/vkeybd/polygon.h114
-rw-r--r--backends/vkeybd/virtual-keyboard-gui.cpp376
-rw-r--r--backends/vkeybd/virtual-keyboard-gui.h108
-rw-r--r--backends/vkeybd/virtual-keyboard-parser.cpp430
-rw-r--r--backends/vkeybd/virtual-keyboard-parser.h217
-rw-r--r--backends/vkeybd/virtual-keyboard.cpp347
-rw-r--r--backends/vkeybd/virtual-keyboard.h218
10 files changed, 1987 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/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..0dcea70292
--- /dev/null
+++ b/backends/vkeybd/virtual-keyboard-gui.cpp
@@ -0,0 +1,376 @@
+/* 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;
+
+ assert(g_system);
+ _system = g_system;
+
+ _lastScreenChanged = _system->getScreenChangeID();
+
+ memset(_cursor, 0xFF, sizeof(_cursor));
+
+ _displaying = _needRedraw = _drag = _drawCaret = _displayEnabled = false;
+ _firstRun = true;
+
+ _cursorAnimateTimer = 0;
+}
+
+void VirtualKeyboardGUI::initMode(VirtualKeyboard::Mode *mode) {
+ _kbdSurface = mode->image;
+ _kbdTransparentColor = mode->transparentColor;
+ _kbdBound.setWidth(_kbdSurface->w + 1);
+ _kbdBound.setHeight(_kbdSurface->h + 1);
+ _needRedraw = true;
+
+ _displayEnabled = false;
+ if (!mode->displayArea)
+ return;
+ Rect r = *(mode->displayArea);
+
+ // choose font
+ _dispFont = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
+ if (!fontIsSuitable(_dispFont, r)) {
+ _dispFont = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont);
+ if (!fontIsSuitable(_dispFont, r))
+ return;
+ }
+ _dispX = r.left;
+ _dispY = r.top + (_dispFont->getFontHeight() - r.height() + 1) / 2;
+ _dispSurface.free();
+ _dispSurface.create(r.width() + 1, _dispFont->getFontHeight(), sizeof(OverlayColor));
+ _dispI = 0;
+ _dispForeColor = mode->displayFontColor;
+ _dispBackColor = _dispForeColor + 0xFF;
+ _displayEnabled = true;
+}
+
+bool VirtualKeyboardGUI::fontIsSuitable(const Graphics::Font *font, const Rect& rect) {
+ return (font->getMaxCharWidth() < rect.width() &&
+ font->getFontHeight() < rect.height());
+}
+
+void VirtualKeyboardGUI::run() {
+ if (_lastScreenChanged != _system->getScreenChangeID())
+ screenChanged();
+
+ // TODO: set default position if position is somehow invalid
+ 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);
+ resetDirtyRect();
+
+ setupCursor();
+
+ _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();
+}
+
+void VirtualKeyboardGUI::hide() {
+ _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::setDefaultPosition()
+{
+ int16 scrW = _system->getOverlayWidth(), scrH = _system->getOverlayHeight();
+ int16 kbdW = _kbdBound.width(), kbdH = _kbdBound.height();
+ int16 posX = 0, posY = 0;
+ if (scrW != kbdW) {
+ switch (_kbd->_hAlignment) {
+ case VirtualKeyboard::kAlignLeft:
+ posX = 0;
+ break;
+ case VirtualKeyboard::kAlignCentre:
+ posX = (scrW - kbdW) / 2;
+ break;
+ case VirtualKeyboard::kAlignRight:
+ posX = scrW - kbdW;
+ break;
+ }
+ }
+ if (scrH != kbdH) {
+ switch (_kbd->_vAlignment) {
+ case VirtualKeyboard::kAlignTop:
+ posY = 0;
+ break;
+ case VirtualKeyboard::kAlignMiddle:
+ posY = (scrH - kbdH) / 2;
+ break;
+ case VirtualKeyboard::kAlignBottom:
+ posY = scrH - kbdH;
+ break;
+ }
+ }
+ _kbdBound.moveTo(posX, posY);
+}
+
+void VirtualKeyboardGUI::move(int16 x, int16 y) {
+ // add old position to dirty area
+ extendDirtyRect(_kbdBound);
+ _needRedraw = true;
+
+ // 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);
+}
+
+void VirtualKeyboardGUI::screenChanged() {
+ _lastScreenChanged = _system->getScreenChangeID();
+ if (!_kbd->checkModeResolutions())
+ _displaying = false;
+}
+
+
+void VirtualKeyboardGUI::mainLoop() {
+ Common::EventManager *eventMan = _system->getEventManager();
+
+ while (_displaying) {
+ if (_displayEnabled) {
+ if (_kbd->_keyQueue.hasStringChanged())
+ _refreshDisplay = true;
+ animateCaret();
+ if (_refreshDisplay) updateDisplay();;
+ }
+ 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)) {
+ _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(0, 0, _overlayBackup.w, _overlayBackup.h));
+}
+
+void VirtualKeyboardGUI::resetDirtyRect() {
+ _dirtyRect.setWidth(-1);
+}
+
+void VirtualKeyboardGUI::redraw() {
+ assert(_kbdSurface);
+
+ extendDirtyRect(_kbdBound);
+
+ Graphics::SurfaceKeyColored surf;
+ surf.create(_dirtyRect.width()+1, _dirtyRect.height()+1, sizeof(OverlayColor));
+
+ OverlayColor *scr = (OverlayColor *)surf.pixels;
+ const OverlayColor *ove = (OverlayColor *) _overlayBackup.getBasePtr(_dirtyRect.left, _dirtyRect.top);
+ int16 h = surf.h;
+
+ while (h-- > 0) {
+ memcpy(scr, ove, surf.w * sizeof(OverlayColor));
+ scr += surf.w;
+ ove += _overlayBackup.w;
+ }
+
+ int16 keyX = _kbdBound.left - _dirtyRect.left;
+ int16 keyY = _kbdBound.top - _dirtyRect.top;
+ surf.blit(_kbdSurface, keyX, keyY, _kbdTransparentColor);
+ if (_displayEnabled) surf.blit(&_dispSurface, keyX + _dispX, keyY + _dispY, _dispBackColor);
+ _system->copyRectToOverlay((OverlayColor*)surf.pixels, surf.w,
+ _dirtyRect.left, _dirtyRect.top, surf.w, surf.h);
+
+ surf.free();
+
+ _needRedraw = false;
+
+ 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 (_system->getMillis() % kCaretBlinkTime < kCaretBlinkTime / 2) {
+ if (!_drawCaret) {
+ _drawCaret = true;
+ _refreshDisplay = true;
+ }
+ } else {
+ if (_drawCaret) {
+ _drawCaret = false;
+ _refreshDisplay = true;
+ }
+ }
+}
+
+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(0, 0, _dispSurface.w, _dispSurface.h), _dispBackColor);
+ _dispFont->drawString(&_dispSurface, dispText, 0, 0, _dispSurface.w, _dispForeColor);
+ if (_drawCaret) {
+ String beforeCaret(wholeText.c_str() + _dispI, wholeText.c_str() + cursorPos);
+ int16 caretX = _dispFont->getStringWidth(beforeCaret);
+ _dispSurface.drawLine(caretX, 0, caretX, _dispSurface.h, _dispForeColor);
+ }
+
+ _needRedraw = true;
+}
+
+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..5836859262
--- /dev/null
+++ b/backends/vkeybd/virtual-keyboard-gui.h
@@ -0,0 +1,108 @@
+/* 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 VirtualKeyboardGUI {
+
+public:
+
+ VirtualKeyboardGUI(VirtualKeyboard *kbd);
+ void initMode(VirtualKeyboard::Mode *mode);
+ void run();
+ void hide();
+ bool isDisplaying() { return _displaying; }
+ void reset();
+ void startDrag(int16 x, int16 y);
+ void endDrag();
+
+private:
+
+ OSystem *_system;
+ VirtualKeyboard *_kbd;
+ Graphics::Surface *_kbdSurface;
+ OverlayColor _kbdTransparentColor;
+
+ static const int SNAP_WIDTH = 10;
+
+ Graphics::Surface _overlayBackup;
+
+ Rect _dirtyRect;
+
+ bool _displayEnabled;
+ bool _refreshDisplay;
+ Graphics::Surface _dispSurface;
+ const Graphics::Font *_dispFont;
+ int16 _dispX, _dispY;
+ uint _dispI;
+ OverlayColor _dispForeColor, _dispBackColor;
+
+ Rect _kbdBound;
+
+ Point _dragPoint;
+ bool _drag;
+
+ bool _displaying;
+ bool _firstRun;
+ bool _needRedraw;
+ int _lastScreenChanged;
+
+ void setDefaultPosition();
+ void move(int16 x, int16 y);
+ void screenChanged();
+ void mainLoop();
+ void extendDirtyRect(const Rect &r);
+ void resetDirtyRect();
+ void redraw();
+ void updateDisplay();
+ bool fontIsSuitable(const Graphics::Font *font, const Rect& rect);
+ uint calculateEndIndex(const String& str, uint startIndex);
+
+ bool _drawCaret;
+ 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..f3d71057b7
--- /dev/null
+++ b/backends/vkeybd/virtual-keyboard-parser.cpp
@@ -0,0 +1,430 @@
+/* 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;
+
+ _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(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(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")) {
+ 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")) {
+ 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");
+
+ 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]);
+
+ String resolutions = modeNode->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) {
+ 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;
+
+ String name = evtNode->values["name"];
+ if (_mode->events.contains(name))
+ return parserError("Event '%s' has already been defined", name.c_str());
+
+ VirtualKeyboard::Event *evt = new VirtualKeyboard::Event();
+ evt->name = name;
+
+ String type = evtNode->values["type"];
+ if (type == "key") {
+ if (!evtNode->values.contains("code") || !evtNode->values.contains("ascii")) {
+ delete evt;
+ return parserError("Key event element must contain code and ascii attributes");
+ }
+
+ evt->type = VirtualKeyboard::kEventKey;
+
+ KeyCode code = (KeyCode)atoi(evtNode->values["code"].c_str());
+ uint16 ascii = atoi(evtNode->values["ascii"].c_str());
+
+ byte flags = 0;
+ if (evtNode->values.contains("modifiers"))
+ flags = parseFlags(evtNode->values["modifiers"]);
+
+ evt->data = new KeyState(code, ascii, flags);
+
+ } else if (type == "modifier") {
+ if (!evtNode->values.contains("modifiers")) {
+ delete evt;
+ return parserError("Key modifier element must contain modifier attributes");
+ }
+
+ evt->type = VirtualKeyboard::kEventModifier;
+ byte *flags = new byte;
+ *(flags) = parseFlags(evtNode->values["modifiers"]);
+ evt->data = flags;
+
+ } else if (type == "switch_mode") {
+ if (!evtNode->values.contains("mode")) {
+ delete evt;
+ return parserError("Switch mode event element must contain mode attribute");
+ }
+
+ evt->type = VirtualKeyboard::kEventSwitchMode;
+ evt->data = new String(evtNode->values["mode"]);
+ } else if (type == "close") {
+ evt->type = VirtualKeyboard::kEventClose;
+ evt->data = 0;
+ } else {
+ delete evt;
+ 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());
+
+ String res = layoutNode->values["resolution"];
+
+ if (res != _mode->resolution) {
+ layoutNode->ignore = true;
+ return true;
+ }
+
+ _mode->bitmapName = layoutNode->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 (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
+
+ if (layoutNode->values.contains("display_font_color")) {
+ int r, g, b;
+ if (!parseIntegerKey(layoutNode->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 *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");
+
+ String& shape = areaNode->values["shape"];
+ String& target = areaNode->values["target"];
+ String& coords = areaNode->values["coords"];
+
+ if (target == "display_area") {
+ if (shape != "rect")
+ return parserError("display_area must be a rect area");
+ _mode->displayArea = new Rect();
+ return parseRect(_mode->displayArea, coords);
+ } else if (shape == "rect") {
+ Polygon *poly = _mode->imageMap.createArea(target);
+ return parseRectAsPolygon(poly, coords);
+ } else if (shape == "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..59a770bdce
--- /dev/null
+++ b/backends/vkeybd/virtual-keyboard-parser.h
@@ -0,0 +1,217 @@
+/* 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"
+
+/**
+ 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
+ String _initialModeName;
+ bool _kbdParsed;
+ bool _layoutParsed;
+
+ bool keyCallback(String keyName);
+ bool closedKeyCallback(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();
+
+ 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);
+
+ HashMap<String, ParserCallback, IgnoreCase_Hash, IgnoreCase_EqualTo> _callbacks;
+ HashMap<String, ParserCallback, IgnoreCase_Hash, IgnoreCase_EqualTo> _closedCallbacks;
+};
+
+} // 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..ae0fd86ee5
--- /dev/null
+++ b/backends/vkeybd/virtual-keyboard.cpp
@@ -0,0 +1,347 @@
+/* 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 "graphics/imageman.h"
+
+namespace Common {
+
+VirtualKeyboard::VirtualKeyboard() : _currentMode(0) {
+ assert(g_system);
+ _system = g_system;
+
+ _parser = new VirtualKeyboardParser(this);
+ _kbdGUI = new VirtualKeyboardGUI(this);
+ _loaded = false;
+}
+
+VirtualKeyboard::~VirtualKeyboard() {
+ deleteEvents();
+ delete _kbdGUI;
+ delete _parser;
+}
+
+void VirtualKeyboard::deleteEvents() {
+ 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;
+ }
+}
+
+void VirtualKeyboard::reset() {
+ deleteEvents();
+ _modes.clear();
+ _initialMode = _currentMode = 0;
+ _hAlignment = kAlignCentre;
+ _vAlignment = kAlignBottom;
+ _keyQueue.clear();
+ _loaded = false;
+ _kbdGUI->reset();
+}
+
+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 = (byte *)malloc(fileInfo.uncompressed_size+1 * sizeof(byte));
+ 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", 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;
+}
+
+bool VirtualKeyboard::checkModeResolutions()
+{
+ _parser->setParseMode(kParseCheckResolutions);
+ _loaded = _parser->parse();
+ return _loaded;
+}
+
+Common::String VirtualKeyboard::findArea(int16 x, int16 y) {
+ return _currentMode->imageMap.findMapArea(x, y);
+}
+
+void VirtualKeyboard::processAreaClick(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.insertKey(*(Common::KeyState*)evt->data);
+ break;
+ }
+ case kEventModifier:
+ _keyQueue.toggleFlags(*(byte*)(evt->data));
+ break;
+ case kEventSwitchMode:
+ // switch to new mode
+ switchMode(*(Common::String *)evt->data);
+ _keyQueue.clearFlags();
+ break;
+ case kEventClose:
+ // close virtual keyboard
+ _kbdGUI->hide();
+ break;
+ }
+}
+
+void VirtualKeyboard::switchMode(Mode *newMode) {
+ _kbdGUI->initMode(newMode);
+ _currentMode = newMode;
+}
+
+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::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) {
+ // if not loaded then load default "vkeybd" pack
+ if (!loadKeyboardPack("vkeybd")) {
+ warning("Keyboard not loaded therefore can't be shown");
+ return;
+ }
+ }
+ switchMode(_initialMode);
+
+ _kbdGUI->run();
+
+ EventManager *eventMan = _system->getEventManager();
+ assert(eventMan);
+
+ // 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::hide() {
+ _kbdGUI->hide();
+}
+
+bool VirtualKeyboard::isDisplaying() {
+ return _kbdGUI->isDisplaying();
+}
+
+VirtualKeyboard::KeyPressQueue::KeyPressQueue() {
+ _keyPos = _keys.end();
+ _strPos = 0;
+}
+
+void VirtualKeyboard::KeyPressQueue::toggleFlags(byte fl) {
+ _keyFlags ^= fl;
+ _strChanged = true;
+}
+
+void VirtualKeyboard::KeyPressQueue::clearFlags() {
+ _keyFlags = 0;
+ _strChanged = true;
+}
+
+void VirtualKeyboard::KeyPressQueue::insertKey(KeyState key) {
+ _strChanged = true;
+ switch (key.keycode) {
+ case KEYCODE_LEFT:
+ moveLeft();
+ return;
+ case KEYCODE_RIGHT:
+ moveRight();
+ return;
+ case KEYCODE_BACKSPACE:
+ deleteKey();
+ return;
+ default:
+ ;
+ }
+
+ key.flags ^= _keyFlags;
+ if ((key.keycode >= Common::KEYCODE_a) && (key.keycode <= Common::KEYCODE_z))
+ key.ascii = (key.flags & Common::KBD_SHIFT) ? key.keycode - 32 : key.keycode;
+ clearFlags();
+
+ String keyStr;
+ if (key.keycode >= 32 && key.keycode <= 126) {
+ if (key.flags & KBD_CTRL)
+ keyStr += "Ctrl+";
+ if (key.flags & KBD_ALT)
+ keyStr += "Alt+";
+ if (key.flags & KBD_SHIFT && (key.ascii < 65 || key.ascii > 90))
+ keyStr += "Shift+";
+ keyStr += (char)key.ascii;
+ }
+
+ const char *k = keyStr.c_str();
+ while (char ch = *k++)
+ _str.insertChar(ch, _strPos++);
+
+ VirtualKeyPress kp;
+ kp.key = key;
+ kp.strLen = keyStr.size();
+ _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)
+ _str.deleteChar(_strPos);
+ _keys.erase(it);
+}
+
+void VirtualKeyboard::KeyPressQueue::moveLeft() {
+ if (_keyPos == _keys.begin())
+ return;
+ _keyPos--;
+ _strPos -= _keyPos->strLen;
+}
+
+void VirtualKeyboard::KeyPressQueue::moveRight() {
+ if (_keyPos == _keys.end())
+ return;
+ _strPos += _keyPos->strLen;
+ _keyPos++;
+}
+
+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)
+ _str.deleteChar(0);
+
+ return kp.key;
+}
+
+void VirtualKeyboard::KeyPressQueue::clear() {
+ _keys.clear();
+ _keyPos = _keys.end();
+ _str.clear();
+ _strPos = 0;
+ _keyFlags = 0;
+}
+
+bool VirtualKeyboard::KeyPressQueue::empty()
+{
+ return _keys.empty();
+}
+
+String VirtualKeyboard::KeyPressQueue::getString()
+{
+ String flags;
+ if (_keyFlags & KBD_CTRL)
+ flags += "Ctrl+";
+ if (_keyFlags & KBD_ALT)
+ flags += "Alt+";
+ if (_keyFlags & KBD_SHIFT)
+ flags += "Shift+";
+ return _str + flags;
+}
+
+uint VirtualKeyboard::KeyPressQueue::getInsertIndex() {
+ return _strPos;
+}
+
+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..f15eb4aece
--- /dev/null
+++ b/backends/vkeybd/virtual-keyboard.h
@@ -0,0 +1,218 @@
+/* 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 VirtualKeyboard {
+protected:
+ enum EventType {
+ kEventKey,
+ kEventModifier,
+ kEventSwitchMode,
+ kEventClose
+ };
+
+ struct Event {
+ Common::String name;
+ EventType type;
+ void *data;
+
+ Event() : data(0) {}
+ ~Event() {
+ if (data) {
+ switch (type) {
+ case kEventKey:
+ delete (KeyState*)data;
+ break;
+ case kEventModifier:
+ delete (byte*)data;
+ break;
+ case kEventSwitchMode:
+ delete (String*)data;
+ break;
+ case kEventClose:
+ break;
+ }
+ }
+ }
+ };
+
+ typedef Common::HashMap<Common::String, Event*> EventMap;
+
+ struct Mode {
+ Common::String name;
+ Common::String resolution;
+ Common::String bitmapName;
+ Graphics::Surface *image;
+ OverlayColor transparentColor;
+ Common::ImageMap imageMap;
+ EventMap events;
+ Common::Rect *displayArea;
+ OverlayColor displayFontColor;
+
+ Mode() : image(0), displayArea(0) {}
+ ~Mode() { if (displayArea) delete displayArea; }
+ };
+
+ typedef Common::HashMap<Common::String, Mode, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ModeMap;
+
+ enum HorizontalAlignment {
+ kAlignLeft,
+ kAlignCentre,
+ kAlignRight
+ };
+
+ enum VerticalAlignment {
+ kAlignTop,
+ kAlignMiddle,
+ kAlignBottom
+ };
+
+ struct VirtualKeyPress {
+ Common::KeyState key;
+ uint strLen;
+ };
+
+ 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 _keyFlags;
+
+ List<VirtualKeyPress> _keys;
+ String _str;
+
+ 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(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();
+
+ /**
+ * Returns true if the keyboard is loaded and ready to be shown
+ */
+ bool isLoaded() {
+ return _loaded;
+ }
+
+protected: // TODO : clean up all this stuff
+
+ 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 Common::String& newMode);
+ void handleMouseDown(int16 x, int16 y);
+ void handleMouseUp(int16 x, int16 y);
+ String findArea(int16 x, int16 y);
+ void processAreaClick(const Common::String &area);
+
+ bool _loaded;
+
+ ModeMap _modes;
+ Mode *_initialMode;
+ Mode *_currentMode;
+
+ HorizontalAlignment _hAlignment;
+ VerticalAlignment _vAlignment;
+
+ String _areaDown;
+
+};
+
+
+} // End of namespace GUI
+
+
+#endif