ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

virtual-keyboard-gui.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/scummsys.h"
00024 
00025 #ifdef ENABLE_VKEYBD
00026 
00027 #include "backends/vkeybd/virtual-keyboard-gui.h"
00028 
00029 #include "graphics/cursorman.h"
00030 #include "graphics/fontman.h"
00031 #include "gui/gui-manager.h"
00032 
00033 namespace Common {
00034 
00035 template<typename ColorType>
00036 static void blitImplementation(Graphics::Surface *surf_dst, Graphics::Surface *surf_src, int16 x, int16 y, ColorType transparent) {
00037     const ColorType *src = (const ColorType *)surf_src->getPixels();
00038     int blitW = surf_src->w;
00039     int blitH = surf_src->h;
00040 
00041     // clip co-ordinates
00042     if (x < 0) {
00043         blitW += x;
00044         src -= x;
00045         x = 0;
00046     }
00047     if (y < 0) {
00048         blitH += y;
00049         src -= y * surf_src->w;
00050         y = 0;
00051     }
00052     if (blitW > surf_dst->w - x)
00053         blitW = surf_dst->w - x;
00054     if (blitH > surf_dst->h - y)
00055         blitH = surf_dst->h - y;
00056     if (blitW <= 0 || blitH <= 0)
00057         return;
00058 
00059     ColorType *dst = (ColorType *)surf_dst->getBasePtr(x, y);
00060     int dstAdd = surf_dst->w - blitW;
00061     int srcAdd = surf_src->w - blitW;
00062 
00063     for (int i = 0; i < blitH; ++i) {
00064         for (int j = 0; j < blitW; ++j, ++dst, ++src) {
00065             ColorType col = *src;
00066             if (col != transparent)
00067                 *dst = col;
00068         }
00069         dst += dstAdd;
00070         src += srcAdd;
00071     }
00072 }
00073 
00074 static void blit(Graphics::Surface *surf_dst, Graphics::Surface *surf_src, int16 x, int16 y, uint32 transparent) {
00075     if (surf_dst->format.bytesPerPixel != surf_src->format.bytesPerPixel)
00076         return;
00077 
00078     if (surf_dst->format.bytesPerPixel == 2)
00079         blitImplementation<uint16>(surf_dst, surf_src, x, y, transparent);
00080     else if (surf_dst->format.bytesPerPixel == 4)
00081         blitImplementation<uint32>(surf_dst, surf_src, x, y, transparent);
00082 }
00083 
00084 VirtualKeyboardGUI::VirtualKeyboardGUI(VirtualKeyboard *kbd)
00085     : _kbd(kbd), _displaying(false), _drag(false),
00086       _drawCaret(false), _displayEnabled(false), _firstRun(true),
00087       _cursorAnimateTimer(0), _cursorAnimateCounter(0) {
00088 
00089     assert(_kbd);
00090     assert(g_system);
00091     _system = g_system;
00092 
00093     _lastScreenChanged = _system->getScreenChangeID();
00094     _screenW = _system->getOverlayWidth();
00095     _screenH = _system->getOverlayHeight();
00096 
00097 
00098     memset(_cursor, 0xFF, sizeof(_cursor));
00099 }
00100 
00101 VirtualKeyboardGUI::~VirtualKeyboardGUI() {
00102     _overlayBackup.free();
00103     _dispSurface.free();
00104 }
00105 
00106 void VirtualKeyboardGUI::initMode(VirtualKeyboard::Mode *mode) {
00107     assert(mode->image);
00108 
00109     _kbdSurface = mode->image;
00110     _kbdTransparentColor = mode->transparentColor;
00111     _kbdBound.setWidth(_kbdSurface->w);
00112     _kbdBound.setHeight(_kbdSurface->h);
00113 
00114     setupDisplayArea(mode->displayArea, mode->displayFontColor);
00115 
00116     if (_displaying) {
00117         extendDirtyRect(_kbdBound);
00118         redraw();
00119     }
00120 }
00121 
00122 void VirtualKeyboardGUI::setupDisplayArea(Rect &r, uint32 forecolor) {
00123 
00124     _dispFont = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
00125     if (!fontIsSuitable(_dispFont, r)) {
00126         _dispFont = FontMan.getFontByUsage(Graphics::FontManager::kGUIFont);
00127         if (!fontIsSuitable(_dispFont, r)) {
00128             /* FIXME: We 'ab'use the kConsoleFont to get a font that fits in a small display_area on 320*240 keyboard images */
00129             _dispFont = FontMan.getFontByUsage(Graphics::FontManager::kConsoleFont);
00130             if (!fontIsSuitable(_dispFont, r)) {
00131                 _displayEnabled = false;
00132                 return;
00133             }
00134         }
00135     }
00136     _dispX = _kbdBound.left + r.left;
00137     _dispY = _kbdBound.top + r.top + (r.height() - _dispFont->getFontHeight()) / 2;
00138     _dispI = 0;
00139     _dispForeColor = forecolor;
00140     _dispBackColor = _dispForeColor + 0xFF;
00141     _dispSurface.create(r.width(), _dispFont->getFontHeight(), _system->getOverlayFormat());
00142     _dispSurface.fillRect(Rect(_dispSurface.w, _dispSurface.h), _dispBackColor);
00143     _displayEnabled = true;
00144 }
00145 
00146 bool VirtualKeyboardGUI::fontIsSuitable(const Graphics::Font *font, const Rect &rect) {
00147     return (font->getMaxCharWidth() < rect.width() &&
00148             font->getFontHeight() < rect.height());
00149 }
00150 
00151 void VirtualKeyboardGUI::checkScreenChanged() {
00152     if (_lastScreenChanged != _system->getScreenChangeID())
00153         screenChanged();
00154 }
00155 
00156 void VirtualKeyboardGUI::initSize(int16 w, int16 h) {
00157     _screenW = w;
00158     _screenH = h;
00159 }
00160 
00161 void VirtualKeyboardGUI::run() {
00162     if (_firstRun) {
00163         _firstRun = false;
00164         moveToDefaultPosition();
00165     }
00166 
00167     if (!g_gui.isActive()) {
00168         _system->showOverlay();
00169         _system->clearOverlay();
00170     }
00171     _overlayBackup.create(_screenW, _screenH, _system->getOverlayFormat());
00172     _system->grabOverlay(_overlayBackup.getPixels(), _overlayBackup.pitch);
00173 
00174     setupCursor();
00175 
00176     forceRedraw();
00177     _displaying = true;
00178     mainLoop();
00179 
00180     removeCursor();
00181 
00182     _system->copyRectToOverlay(_overlayBackup.getPixels(), _overlayBackup.pitch, 0, 0, _overlayBackup.w, _overlayBackup.h);
00183     if (!g_gui.isActive()) _system->hideOverlay();
00184 
00185     _overlayBackup.free();
00186     _dispSurface.free();
00187 }
00188 
00189 void VirtualKeyboardGUI::close() {
00190     _displaying = false;
00191 }
00192 
00193 void VirtualKeyboardGUI::reset() {
00194     _kbdBound.left = _kbdBound.top = 0;
00195     _kbdBound.right = _kbdBound.bottom = 0;
00196     _displaying = _drag = false;
00197     _firstRun = true;
00198     _lastScreenChanged = _system->getScreenChangeID();
00199     _kbdSurface = 0;
00200 }
00201 
00202 void VirtualKeyboardGUI::moveToDefaultPosition() {
00203     int16 kbdW = _kbdBound.width(), kbdH = _kbdBound.height();
00204     int16 x = 0, y = 0;
00205     if (_screenW != kbdW) {
00206         switch (_kbd->_hAlignment) {
00207         case VirtualKeyboard::kAlignLeft:
00208             x = 0;
00209             break;
00210         case VirtualKeyboard::kAlignCenter:
00211             x = (_screenW - kbdW) / 2;
00212             break;
00213         case VirtualKeyboard::kAlignRight:
00214             x = _screenW - kbdW;
00215             break;
00216         }
00217     }
00218     if (_screenH != kbdH) {
00219         switch (_kbd->_vAlignment) {
00220         case VirtualKeyboard::kAlignTop:
00221             y = 0;
00222             break;
00223         case VirtualKeyboard::kAlignMiddle:
00224             y = (_screenH - kbdH) / 2;
00225             break;
00226         case VirtualKeyboard::kAlignBottom:
00227             y = _screenH - kbdH;
00228             break;
00229         }
00230     }
00231     move(x, y);
00232 }
00233 
00234 void VirtualKeyboardGUI::move(int16 x, int16 y) {
00235     // add old position to dirty area
00236     if (_displaying) extendDirtyRect(_kbdBound);
00237 
00238     // snap to edge of screen
00239     if (ABS(x) < SNAP_WIDTH)
00240         x = 0;
00241     int16 x2 = _screenW - _kbdBound.width();
00242     if (ABS(x - x2) < SNAP_WIDTH)
00243         x = x2;
00244     if (ABS(y) < SNAP_WIDTH)
00245         y = 0;
00246     int16 y2 = _screenH - _kbdBound.height();
00247     if (ABS(y - y2) < SNAP_WIDTH)
00248         y = y2;
00249 
00250     _dispX += x - _kbdBound.left;
00251     _dispY += y - _kbdBound.top;
00252     _kbdBound.moveTo(x, y);
00253 
00254     if (_displaying) {
00255         // add new position to dirty area
00256         extendDirtyRect(_kbdBound);
00257         redraw();
00258     }
00259 }
00260 
00261 void VirtualKeyboardGUI::screenChanged() {
00262     g_gui.checkScreenChange();
00263 
00264     _lastScreenChanged = _system->getScreenChangeID();
00265     int16 newScreenW = _system->getOverlayWidth();
00266     int16 newScreenH = _system->getOverlayHeight();
00267 
00268     if (_screenW != newScreenW || _screenH != newScreenH) {
00269         _screenW = newScreenW;
00270         _screenH = newScreenH;
00271 
00272         _overlayBackup.create(_screenW, _screenH, _system->getOverlayFormat());
00273         _system->grabOverlay(_overlayBackup.getPixels(), _overlayBackup.pitch);
00274 
00275         if (!_kbd->checkModeResolutions()) {
00276             _displaying = false;
00277             return;
00278         }
00279         moveToDefaultPosition();
00280     }
00281 }
00282 
00283 
00284 void VirtualKeyboardGUI::mainLoop() {
00285     Common::EventManager *eventMan = _system->getEventManager();
00286 
00287     while (_displaying) {
00288         if (_kbd->_keyQueue.hasStringChanged())
00289             updateDisplay();
00290         animateCaret();
00291         animateCursor();
00292         redraw();
00293         _system->updateScreen();
00294         Common::Event event;
00295         while (eventMan->pollEvent(event)) {
00296             switch (event.type) {
00297             case Common::EVENT_LBUTTONDOWN:
00298                 if (_kbdBound.contains(event.mouse)) {
00299                     _kbd->handleMouseDown(event.mouse.x - _kbdBound.left,
00300                                           event.mouse.y - _kbdBound.top);
00301                 }
00302                 break;
00303             case Common::EVENT_LBUTTONUP:
00304                 if (_kbdBound.contains(event.mouse)) {
00305                     _kbd->handleMouseUp(event.mouse.x - _kbdBound.left,
00306                                         event.mouse.y - _kbdBound.top);
00307                 }
00308                 break;
00309             case Common::EVENT_MOUSEMOVE:
00310                 if (_drag)
00311                     move(event.mouse.x - _dragPoint.x,
00312                          event.mouse.y - _dragPoint.y);
00313                 break;
00314             case Common::EVENT_SCREEN_CHANGED:
00315                 screenChanged();
00316                 break;
00317             case Common::EVENT_QUIT:
00318                 _system->quit();
00319                 return;
00320             default:
00321                 break;
00322             }
00323         }
00324         // Delay for a moment
00325         _system->delayMillis(10);
00326     }
00327 }
00328 
00329 void VirtualKeyboardGUI::startDrag(int16 x, int16 y) {
00330     _drag = true;
00331     _dragPoint.x = x;
00332     _dragPoint.y = y;
00333 }
00334 
00335 void VirtualKeyboardGUI::endDrag() {
00336     _drag = false;
00337 }
00338 
00339 void VirtualKeyboardGUI::extendDirtyRect(const Rect &r) {
00340     if (_dirtyRect.isValidRect()) {
00341         _dirtyRect.extend(r);
00342     } else {
00343         _dirtyRect = r;
00344     }
00345     _dirtyRect.clip(Rect(_overlayBackup.w, _overlayBackup.h));
00346 }
00347 
00348 void VirtualKeyboardGUI::resetDirtyRect() {
00349     _dirtyRect.setWidth(-1);
00350 }
00351 
00352 void VirtualKeyboardGUI::forceRedraw() {
00353     updateDisplay();
00354     extendDirtyRect(Rect(_overlayBackup.w, _overlayBackup.h));
00355     redraw();
00356 }
00357 
00358 void VirtualKeyboardGUI::redraw() {
00359     assert(_kbdSurface);
00360     int16 w = _dirtyRect.width();
00361     int16 h = _dirtyRect.height();
00362     if (w <= 0 || h <= 0) return;
00363 
00364     Graphics::Surface surf;
00365     surf.create(w, h, _system->getOverlayFormat());
00366 
00367     byte *dst = (byte *)surf.getPixels();
00368     const byte *src = (const byte *)_overlayBackup.getBasePtr(_dirtyRect.left, _dirtyRect.top);
00369 
00370     while (h--) {
00371         memcpy(dst, src, surf.pitch);
00372         dst += surf.pitch;
00373         src += _overlayBackup.pitch;
00374     }
00375 
00376     blit(&surf, _kbdSurface, _kbdBound.left - _dirtyRect.left,
00377          _kbdBound.top - _dirtyRect.top, _kbdTransparentColor);
00378     if (_displayEnabled) {
00379         blit(&surf, &_dispSurface, _dispX - _dirtyRect.left,
00380              _dispY - _dirtyRect.top, _dispBackColor);
00381     }
00382     _system->copyRectToOverlay(surf.getPixels(), surf.pitch,
00383                                _dirtyRect.left, _dirtyRect.top, surf.w, surf.h);
00384 
00385     surf.free();
00386 
00387     resetDirtyRect();
00388 }
00389 
00390 uint VirtualKeyboardGUI::calculateEndIndex(const String &str, uint startIndex) {
00391     int16 w = 0;
00392     while (w <= _dispSurface.w && startIndex < str.size()) {
00393         w += _dispFont->getCharWidth(str[startIndex++]);
00394     }
00395     if (w > _dispSurface.w) startIndex--;
00396     return startIndex;
00397 }
00398 
00399 void VirtualKeyboardGUI::animateCaret() {
00400     if (!_displayEnabled) return;
00401 
00402     if (_system->getMillis() % kCaretBlinkTime < kCaretBlinkTime / 2) {
00403         if (!_drawCaret) {
00404             _drawCaret = true;
00405             _dispSurface.drawLine(_caretX, 0, _caretX, _dispSurface.h, _dispForeColor);
00406             extendDirtyRect(Rect(_dispX + _caretX, _dispY, _dispX + _caretX + 1, _dispY + _dispSurface.h));
00407         }
00408     } else {
00409         if (_drawCaret) {
00410             _drawCaret = false;
00411             _dispSurface.drawLine(_caretX, 0, _caretX, _dispSurface.h, _dispBackColor);
00412             extendDirtyRect(Rect(_dispX + _caretX, _dispY, _dispX + _caretX + 1, _dispY + _dispSurface.h));
00413         }
00414     }
00415 }
00416 
00417 void VirtualKeyboardGUI::updateDisplay() {
00418     if (!_displayEnabled) return;
00419 
00420     // calculate the text to display
00421     uint cursorPos = _kbd->_keyQueue.getInsertIndex();
00422     String wholeText = _kbd->_keyQueue.getString();
00423     uint dispTextEnd;
00424     if (_dispI > cursorPos)
00425         _dispI = cursorPos;
00426 
00427     dispTextEnd = calculateEndIndex(wholeText, _dispI);
00428     while (cursorPos > dispTextEnd)
00429         dispTextEnd = calculateEndIndex(wholeText, ++_dispI);
00430 
00431     String dispText = String(wholeText.c_str() + _dispI, wholeText.c_str() + dispTextEnd);
00432 
00433     // draw to display surface
00434     _dispSurface.fillRect(Rect(_dispSurface.w, _dispSurface.h), _dispBackColor);
00435     _dispFont->drawString(&_dispSurface, dispText, 0, 0, _dispSurface.w, _dispForeColor);
00436 
00437     String beforeCaret(wholeText.c_str() + _dispI, wholeText.c_str() + cursorPos);
00438     _caretX = _dispFont->getStringWidth(beforeCaret);
00439     if (_drawCaret) _dispSurface.drawLine(_caretX, 0, _caretX, _dispSurface.h, _dispForeColor);
00440 
00441     extendDirtyRect(Rect(_dispX, _dispY, _dispX + _dispSurface.w, _dispY + _dispSurface.h));
00442 }
00443 
00444 void VirtualKeyboardGUI::setupCursor() {
00445     const byte palette[] = {
00446         255, 255, 255,
00447         255, 255, 255,
00448         171, 171, 171,
00449         87,  87,  87
00450     };
00451 
00452     CursorMan.pushCursorPalette(palette, 0, 4);
00453     CursorMan.pushCursor(NULL, 0, 0, 0, 0, 0);
00454     CursorMan.showMouse(true);
00455 }
00456 
00457 void VirtualKeyboardGUI::animateCursor() {
00458     int time = _system->getMillis();
00459     if (time > _cursorAnimateTimer + kCursorAnimateDelay) {
00460         for (int i = 0; i < 15; i++) {
00461             if ((i < 6) || (i > 8)) {
00462                 _cursor[16 * 7 + i] = _cursorAnimateCounter;
00463                 _cursor[16 * i + 7] = _cursorAnimateCounter;
00464             }
00465         }
00466 
00467         CursorMan.replaceCursor(_cursor, 16, 16, 7, 7, 255);
00468 
00469         _cursorAnimateTimer = time;
00470         _cursorAnimateCounter = (_cursorAnimateCounter + 1) % 4;
00471     }
00472 }
00473 
00474 void VirtualKeyboardGUI::removeCursor() {
00475     CursorMan.popCursor();
00476     CursorMan.popCursorPalette();
00477 }
00478 
00479 } // End of namespace Common
00480 
00481 #endif // #ifdef ENABLE_VKEYBD


Generated on Sat Feb 16 2019 05:01:11 for ResidualVM by doxygen 1.7.1
curved edge   curved edge