scummvm/backends/vkeybd/virtual-keyboard-gui.cpp
Marcus Comstedt 40f9506163 The default keycolor for mouse pointers used to be 255.
This makes sense as a default for CLUT8 modes, but not really
for anything else.  As part of the gsoc2009-16bit merge, the
default was changed to "all ones", with extra code in the SDL
backend to truncate this to the depth of the mode.  However,
"all ones" (white) still isn't a very useful default for RGB modes.
So rather than jumping through hoops to provide a bad default,
it's better to remove the default altogether.  Engines which relied
on the old default of 255 have been updated to specify it explicitly.

svn-id: r47118
2010-01-07 15:07:36 +00:00

476 lines
12 KiB
C++

/* 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$
*
*/
#ifdef ENABLE_VKEYBD
#include "backends/vkeybd/virtual-keyboard-gui.h"
#include "graphics/cursorman.h"
#include "graphics/fontman.h"
#include "gui/GuiManager.h"
namespace Common {
static void blit(Graphics::Surface *surf_dst, Graphics::Surface *surf_src, int16 x, int16 y, OverlayColor transparent) {
if (surf_dst->bytesPerPixel != sizeof(OverlayColor) || surf_src->bytesPerPixel != sizeof(OverlayColor))
return;
const OverlayColor *src = (const OverlayColor *)surf_src->pixels;
int blitW = surf_src->w;
int blitH = surf_src->h;
// clip co-ordinates
if (x < 0) {
blitW += x;
src -= x;
x = 0;
}
if (y < 0) {
blitH += y;
src -= y * surf_src->w;
y = 0;
}
if (blitW > surf_dst->w - x)
blitW = surf_dst->w - x;
if (blitH > surf_dst->h - y)
blitH = surf_dst->h - y;
if (blitW <= 0 || blitH <= 0)
return;
OverlayColor *dst = (OverlayColor *)surf_dst->getBasePtr(x, y);
int dstAdd = surf_dst->w - blitW;
int srcAdd = surf_src->w - blitW;
for (int i = 0; i < blitH; ++i) {
for (int j = 0; j < blitW; ++j, ++dst, ++src) {
OverlayColor col = *src;
if (col != transparent)
*dst = col;
}
dst += dstAdd;
src += srcAdd;
}
}
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) {
assert(mode->image);
_kbdSurface = mode->image;
_kbdTransparentColor = mode->transparentColor;
_kbdBound.setWidth(_kbdSurface->w);
_kbdBound.setHeight(_kbdSurface->h);
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)) {
/* FIXME: We 'ab'use the kConsoleFont to get a font that fits in a small display_area on 320*240 keyboard images */
_dispFont = FontMan.getFontByUsage(Graphics::FontManager::kConsoleFont);
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() {
g_gui.checkScreenChange();
_lastScreenChanged = _system->getScreenChangeID();
int16 newScreenW = _system->getOverlayWidth();
int16 newScreenH = _system->getOverlayHeight();
if (_screenW != newScreenW || _screenH != newScreenH) {
_screenW = newScreenW;
_screenH = newScreenH;
_overlayBackup.create(_screenW, _screenH, sizeof(OverlayColor));
_system->grabOverlay((OverlayColor*)_overlayBackup.pixels, _overlayBackup.w);
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::Surface 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;
}
blit(&surf, _kbdSurface, _kbdBound.left - _dirtyRect.left,
_kbdBound.top - _dirtyRect.top, _kbdTransparentColor);
if (_displayEnabled) {
blit(&surf, &_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, 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, 255);
_cursorAnimateTimer = time;
_cursorAnimateCounter = (_cursorAnimateCounter + 1) % 4;
}
}
void VirtualKeyboardGUI::removeCursor() {
CursorMan.popCursor();
CursorMan.popCursorPalette();
}
} // End of namespace Common
#endif // #ifdef ENABLE_VKEYBD