Skip to content

Commit

Permalink
SCUMM: Improve Mac "beam" cursor handling
Browse files Browse the repository at this point in the history
The beam cursor should be drawn inverted. ScummVM does not have support
for such cursors as far as I know (except maybe in the OpenGL backend?)
so we draw it manually.

I still need to deal gracefully with colors other than black and white,
because it's possible to drag the cursor outside the dialog window while
pressing the mouse button to select text.
  • Loading branch information
Torbjörn Andersson committed Nov 4, 2023
1 parent 82ddb70 commit 8f9b2be
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 24 deletions.
145 changes: 124 additions & 21 deletions engines/scumm/gfx_mac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -561,16 +561,6 @@ MacGui::MacEditText::MacEditText(MacGui::MacDialogWindow *window, Common::Rect b
_textSurface = _window->innerSurface()->getSubArea(textBounds);
}

Graphics::MacGUIConstants::MacCursorType MacGui::MacEditText::getCursorType() const {
// If the widget is found, that implies that it's active.

// Note: The text widget in the Indy 3 save dialog does not change the
// cursor in the original. But since we're not going to emulate it
// that closely, I don't think we need to mark this as an enhancement.

return Graphics::MacGUIConstants::kMacCursorBeam;
}

bool MacGui::MacEditText::findWidget(int x, int y) const {
if (!_visible || !_enabled)
return false;
Expand Down Expand Up @@ -786,8 +776,6 @@ bool MacGui::MacEditText::handleKeyDown(Common::Event &event) {
if (event.kbd.flags & (Common::KBD_CTRL | Common::KBD_ALT | Common::KBD_META))
return false;

CursorMan.showMouse(false);

switch (event.kbd.keycode) {
case Common::KEYCODE_LEFT:
if (_selectLen < 0) {
Expand Down Expand Up @@ -1033,8 +1021,8 @@ MacGui::MacDialogWindow::~MacDialogWindow() {
if (!CursorMan.isVisible())
CursorMan.showMouse(true);

if (_gui->_windowManager->getCursorType() != Graphics::MacGUIConstants::kMacCursorArrow)
_gui->_windowManager->replaceCursor(Graphics::MacGUIConstants::kMacCursorArrow);
CursorMan.showMouse(true);
_gui->_windowManager->replaceCursor(Graphics::MacGUIConstants::kMacCursorArrow);

copyToScreen(_backup);
_backup->free();
Expand Down Expand Up @@ -1123,6 +1111,72 @@ void MacGui::MacDialogWindow::markRectAsDirty(Common::Rect r) {
_dirtyRects.push_back(r);
}

void MacGui::MacDialogWindow::drawBeamCursor() {
int x0 = _beamCursorPos.x - _beamCursorHotspotX;
int y0 = _beamCursorPos.y - _beamCursorHotspotY;
int x1 = x0 + _beamCursor->w;
int y1 = y0 + _beamCursor->h;

_beamCursor->copyRectToSurface(*(_gui->surface()), 0, 0, Common::Rect(x0, y0, x1, y1));

const Common::Point beam[] = {
Common::Point(0, 0), Common::Point(1, 0), Common::Point(5, 0),
Common::Point(6, 0), Common::Point(2, 1), Common::Point(4, 1),
Common::Point(3, 2), Common::Point(3, 3), Common::Point(3, 4),
Common::Point(3, 5), Common::Point(3, 6), Common::Point(3, 7),
Common::Point(3, 8), Common::Point(3, 9), Common::Point(3, 10),
Common::Point(3, 11), Common::Point(3, 12), Common::Point(3, 13),
Common::Point(2, 14), Common::Point(4, 14), Common::Point(0, 15),
Common::Point(1, 15), Common::Point(5, 15), Common::Point(6, 15)
};

for (int i = 0; i < ARRAYSIZE(beam); i++) {
uint32 color = _beamCursor->getPixel(beam[i].x, beam[i].y);

if (color == kBlack)
color = kWhite;
else
color = kBlack;

_beamCursor->setPixel(beam[i].x, beam[i].y, color);
}

int srcX = 0;
int srcY = 0;

if (x0 < 0) {
srcX = -x0;
x0 = 0;
}

x1 = MIN(x1, 640);

if (y0 < 0) {
srcY = -y0;
y0 = 0;
}

y1 = MIN(y1, 400);

_system->copyRectToScreen(_beamCursor->getBasePtr(srcX, srcY), _beamCursor->pitch, x0, y0, x1 - x0, y1 - y0);
}

void MacGui::MacDialogWindow::undrawBeamCursor() {
int x0 = _beamCursorPos.x - _beamCursorHotspotX;
int y0 = _beamCursorPos.y - _beamCursorHotspotY;
int x1 = x0 + _beamCursor->w;
int y1 = y0 + _beamCursor->h;

x0 = MAX(x0, 0);
x1 = MIN(x1, 640);
y0 = MAX(y0, 0);
y1 = MIN(y1, 400);

Graphics::Surface *screen = _gui->surface();

_system->copyRectToScreen(screen->getBasePtr(x0, y0), screen->pitch, x0, y0, x1 - x0, y1 - y0);
}

void MacGui::MacDialogWindow::update(bool fullRedraw) {
for (uint i = 0; i < _widgets.size(); i++)
_widgets[i]->draw();
Expand All @@ -1141,6 +1195,12 @@ void MacGui::MacDialogWindow::update(bool fullRedraw) {
}

_dirtyRects.clear();

if (_beamCursor) {
undrawBeamCursor();
_beamCursorPos = _realMousePos;
drawBeamCursor();
}
}

void MacGui::MacDialogWindow::fillPattern(Common::Rect r, uint16 pattern) {
Expand Down Expand Up @@ -1180,6 +1240,9 @@ int MacGui::MacDialogWindow::runDialog() {

while (_system->getEventManager()->pollEvent(event)) {
if (Common::isMouseEvent(event)) {
_realMousePos.x = event.mouse.x;
_realMousePos.y = event.mouse.y;

event.mouse.x -= (_bounds.left + _margin);
event.mouse.y -= (_bounds.top + _margin);

Expand Down Expand Up @@ -1212,8 +1275,8 @@ int MacGui::MacDialogWindow::runDialog() {
break;

case Common::EVENT_MOUSEMOVE:
if (!CursorMan.isVisible())
CursorMan.showMouse(true);
if (_beamCursor && !_beamCursorVisible)
_beamCursorVisible = true;

if (_focusedWidget) {
if (_focusedWidget->findWidget(_oldMousePos.x, _oldMousePos.y) != _focusedWidget->findWidget(_mousePos.x, _mousePos.y)) {
Expand All @@ -1223,12 +1286,24 @@ int MacGui::MacDialogWindow::runDialog() {
_focusedWidget->handleMouseMove(event);
} else {
int mouseOverWidget = findWidget(_mousePos.x, _mousePos.y);
Graphics::MacGUIConstants::MacCursorType cursorType = (mouseOverWidget >= 0) ? _widgets[mouseOverWidget]->getCursorType() : Graphics::MacGUIConstants::kMacCursorArrow;

if (_gui->_windowManager->getCursorType() != cursorType)
_gui->_windowManager->replaceCursor(cursorType);
bool useBeamCursor = (mouseOverWidget >= 0 && _widgets[mouseOverWidget]->useBeamCursor());

if (useBeamCursor && !_beamCursor) {
CursorMan.showMouse(false);
_beamCursor = new Graphics::Surface();
_beamCursor->create(7, 16, Graphics::PixelFormat::createFormatCLUT8());
_beamCursorVisible = true;
_beamCursorPos = _realMousePos;
} else if (!useBeamCursor && _beamCursor) {
CursorMan.showMouse(true);
undrawBeamCursor();
_beamCursor->free();
delete _beamCursor;
_beamCursor = nullptr;
_beamCursorVisible = false;
}
}

break;

case Common::EVENT_KEYDOWN:
Expand Down Expand Up @@ -1259,6 +1334,8 @@ int MacGui::MacDialogWindow::runDialog() {
// to key presses.
for (uint i = 0; i < _widgets.size(); i++) {
if (_widgets[i]->handleKeyDown(event)) {
if (_beamCursor)
_beamCursorVisible = true;
_widgets[i]->setRedraw();
break;
}
Expand Down Expand Up @@ -1593,13 +1670,39 @@ Graphics::Surface *MacGui::loadPict(int id) {
// The palette doesn't match the game's palette at all, so remap
// the colors to the custom area of the palette. It's assumed that
// only one such picture will be loaded at a time.
//
// But we still match black and white to 0 and 15 to make sure they
// mach exactly.

int black = -1;
int white = -1;

for (int i = 0; i < pict.getPaletteColorCount(); i++) {
int r = palette[3 * i];
int g = palette[3 * i + 1];
int b = palette[3 * i + 2];

if (r == 0 && g == 0 && b == 0)
black = i;
else if (r == 255 && g == 255 && b == 255)
white = i;
}

if (palette) {
_system->getPaletteManager()->setPalette(palette, kCustomColor, pict.getPaletteColorCount());

for (int y = 0; y < s->h; y++) {
for (int x = 0; x < s->w; x++) {
s->setPixel(x, y, kCustomColor + s1->getPixel(x, y));
int color = s1->getPixel(x, y);

if (color == black)
color = kBlack;
else if (color == white)
color = kWhite;
else
color = kCustomColor + color;

s->setPixel(x, y, color);
}
}
} else
Expand Down
17 changes: 14 additions & 3 deletions engines/scumm/gfx_mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#define SCUMM_GFX_MAC_H

#include "graphics/font.h"
#include "graphics/macgui/macwindowmanager.h"

class OSystem;

Expand Down Expand Up @@ -181,7 +180,7 @@ class MacGui {
virtual void setValue(int value);
int getValue() const { return _value; }

virtual Graphics::MacGUIConstants::MacCursorType getCursorType() const { return Graphics::MacGUIConstants::kMacCursorArrow; }
virtual bool useBeamCursor() { return false; }
virtual bool findWidget(int x, int y) const;

virtual void draw(bool drawFocused = false) = 0;
Expand Down Expand Up @@ -257,7 +256,7 @@ class MacGui {
public:
MacEditText(MacGui::MacDialogWindow *window, Common::Rect bounds, Common::String text, bool enabled);

Graphics::MacGUIConstants::MacCursorType getCursorType() const;
bool useBeamCursor() { return true; }
bool findWidget(int x, int y) const;

void draw(bool drawFocused = false);
Expand Down Expand Up @@ -317,6 +316,15 @@ class MacGui {

bool _visible = false;

Graphics::Surface *_beamCursor = nullptr;
Common::Point _beamCursorPos;
bool _beamCursorVisible = false;
int _beamCursorHotspotX = 3;
int _beamCursorHotspotY = 4;

void drawBeamCursor();
void undrawBeamCursor();

PauseToken _pauseToken;

Graphics::Surface *_from = nullptr;
Expand All @@ -333,6 +341,7 @@ class MacGui {
Common::Point _focusClick;
Common::Point _oldMousePos;
Common::Point _mousePos;
Common::Point _realMousePos;

Common::StringArray _substitutions;
Common::Array<Common::Rect> _dirtyRects;
Expand Down Expand Up @@ -401,6 +410,8 @@ class MacGui {
MacGui(ScummEngine *vm, Common::String resourceFile);
virtual ~MacGui();

Graphics::Surface *surface() { return _surface; }

virtual const Common::String name() const = 0;

virtual bool handleEvent(Common::Event &event);
Expand Down

0 comments on commit 8f9b2be

Please sign in to comment.