scummvm/backends/events/default/default-events.cpp
Colin Snover d1b77d4b68 BACKENDS: Fix missing mouse events when system cursor cannot be moved
Normally with SDL, a mouse motion event will be sent after the
system mouse cursor has been moved by a call to
SDL_WarpMouseInWindow, but if the system cursor cannot be moved
(e.g. because the window does not have mouse focus), games still
need to receive these mouse events so they can successfully update
the mouse position internally. Otherwise, games continue to think
the mouse is still in the original position and will continue to
try to perform whatever action is associated with that mouse
position.

Refs Trac#9689.
2017-10-15 13:24:21 -05:00

284 lines
7.5 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.
*
*/
#include "common/scummsys.h"
#if !defined(DISABLE_DEFAULT_EVENTMANAGER)
#include "common/system.h"
#include "common/config-manager.h"
#include "common/translation.h"
#include "backends/events/default/default-events.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/remap-dialog.h"
#include "backends/vkeybd/virtual-keyboard.h"
#include "engines/engine.h"
#include "gui/message.h"
DefaultEventManager::DefaultEventManager(Common::EventSource *boss) :
_buttonState(0),
_modifierState(0),
_shouldQuit(false),
_shouldRTL(false),
_confirmExitDialogActive(false) {
assert(boss);
_dispatcher.registerSource(boss, false);
_dispatcher.registerSource(&_artificialEventSource, false);
_dispatcher.registerObserver(this, kEventManPriority, false);
// Reset key repeat
_currentKeyDown.keycode = 0;
_currentKeyDown.ascii = 0;
_currentKeyDown.flags = 0;
_keyRepeatTime = 0;
#ifdef ENABLE_VKEYBD
_vk = new Common::VirtualKeyboard();
#endif
#ifdef ENABLE_KEYMAPPER
_keymapper = new Common::Keymapper(this);
// EventDispatcher will automatically free the keymapper
_dispatcher.registerMapper(_keymapper);
_remap = false;
#else
_dispatcher.registerMapper(new Common::DefaultEventMapper());
#endif
}
DefaultEventManager::~DefaultEventManager() {
#ifdef ENABLE_VKEYBD
delete _vk;
#endif
}
void DefaultEventManager::init() {
#ifdef ENABLE_VKEYBD
if (ConfMan.hasKey("vkeybd_pack_name")) {
_vk->loadKeyboardPack(ConfMan.get("vkeybd_pack_name"));
} else {
_vk->loadKeyboardPack("vkeybd_default");
}
#endif
}
bool DefaultEventManager::pollEvent(Common::Event &event) {
// Skip recording of these events
uint32 time = g_system->getMillis(true);
bool result = false;
_dispatcher.dispatch();
if (!_eventQueue.empty()) {
event = _eventQueue.pop();
result = true;
}
if (result) {
event.synthetic = false;
switch (event.type) {
case Common::EVENT_KEYDOWN:
_modifierState = event.kbd.flags;
// init continuous event stream
_currentKeyDown.ascii = event.kbd.ascii;
_currentKeyDown.keycode = event.kbd.keycode;
_currentKeyDown.flags = event.kbd.flags;
_keyRepeatTime = time + kKeyRepeatInitialDelay;
if (event.kbd.keycode == Common::KEYCODE_BACKSPACE) {
// WORKAROUND: Some engines incorrectly attempt to use the
// ascii value instead of the keycode to detect the backspace
// key (a non-portable behavior). This fails at least on
// Mac OS X, possibly also on other systems.
// As a workaround, we force the ascii value for backspace
// key pressed. A better fix would be for engines to stop
// making invalid assumptions about ascii values.
event.kbd.ascii = Common::KEYCODE_BACKSPACE;
_currentKeyDown.ascii = Common::KEYCODE_BACKSPACE;
}
break;
case Common::EVENT_KEYUP:
_modifierState = event.kbd.flags;
if (event.kbd.keycode == _currentKeyDown.keycode) {
// Only stop firing events if it's the current key
_currentKeyDown.keycode = 0;
}
break;
case Common::EVENT_MOUSEMOVE:
_mousePos = event.mouse;
break;
case Common::EVENT_LBUTTONDOWN:
_mousePos = event.mouse;
_buttonState |= LBUTTON;
break;
case Common::EVENT_LBUTTONUP:
_mousePos = event.mouse;
_buttonState &= ~LBUTTON;
break;
case Common::EVENT_RBUTTONDOWN:
_mousePos = event.mouse;
_buttonState |= RBUTTON;
break;
case Common::EVENT_RBUTTONUP:
_mousePos = event.mouse;
_buttonState &= ~RBUTTON;
break;
case Common::EVENT_MAINMENU:
if (g_engine && !g_engine->isPaused())
g_engine->openMainMenuDialog();
if (_shouldQuit)
event.type = Common::EVENT_QUIT;
else if (_shouldRTL)
event.type = Common::EVENT_RTL;
break;
#ifdef ENABLE_VKEYBD
case Common::EVENT_VIRTUAL_KEYBOARD:
if (_vk->isDisplaying()) {
_vk->close(true);
} else {
if (g_engine)
g_engine->pauseEngine(true);
_vk->show();
if (g_engine)
g_engine->pauseEngine(false);
result = false;
}
break;
#endif
#ifdef ENABLE_KEYMAPPER
case Common::EVENT_KEYMAPPER_REMAP:
if (!_remap) {
_remap = true;
Common::RemapDialog _remapDialog;
if (g_engine)
g_engine->pauseEngine(true);
_remapDialog.runModal();
if (g_engine)
g_engine->pauseEngine(false);
_remap = false;
}
break;
#endif
case Common::EVENT_RTL:
if (ConfMan.getBool("confirm_exit")) {
if (g_engine)
g_engine->pauseEngine(true);
GUI::MessageDialog alert(_("Do you really want to return to the Launcher?"), _("Launcher"), _("Cancel"));
result = _shouldRTL = (alert.runModal() == GUI::kMessageOK);
if (g_engine)
g_engine->pauseEngine(false);
} else
_shouldRTL = true;
break;
case Common::EVENT_MUTE:
if (g_engine)
g_engine->flipMute();
break;
case Common::EVENT_QUIT:
if (ConfMan.getBool("confirm_exit")) {
if (_confirmExitDialogActive) {
result = false;
break;
}
_confirmExitDialogActive = true;
if (g_engine)
g_engine->pauseEngine(true);
GUI::MessageDialog alert(_("Do you really want to quit?"), _("Quit"), _("Cancel"));
result = _shouldQuit = (alert.runModal() == GUI::kMessageOK);
if (g_engine)
g_engine->pauseEngine(false);
_confirmExitDialogActive = false;
} else
_shouldQuit = true;
break;
default:
break;
}
} else {
// Check if event should be sent again (keydown)
if (_currentKeyDown.keycode != 0 && _keyRepeatTime < time) {
// fire event
event.type = Common::EVENT_KEYDOWN;
event.synthetic = true;
event.kbd.ascii = _currentKeyDown.ascii;
event.kbd.keycode = (Common::KeyCode)_currentKeyDown.keycode;
event.kbd.flags = _currentKeyDown.flags;
_keyRepeatTime = time + kKeyRepeatSustainDelay;
result = true;
}
}
return result;
}
void DefaultEventManager::pushEvent(const Common::Event &event) {
// If already received an EVENT_QUIT, don't add another one
if (event.type == Common::EVENT_QUIT) {
if (!_shouldQuit)
_artificialEventSource.addEvent(event);
} else
_artificialEventSource.addEvent(event);
}
void DefaultEventManager::purgeMouseEvents() {
_dispatcher.dispatch();
Common::Queue<Common::Event> filteredQueue;
while (!_eventQueue.empty()) {
Common::Event event = _eventQueue.pop();
switch (event.type) {
case Common::EVENT_MOUSEMOVE:
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_LBUTTONUP:
case Common::EVENT_RBUTTONDOWN:
case Common::EVENT_RBUTTONUP:
case Common::EVENT_WHEELUP:
case Common::EVENT_WHEELDOWN:
case Common::EVENT_MBUTTONDOWN:
case Common::EVENT_MBUTTONUP:
// do nothing
break;
default:
filteredQueue.push(event);
break;
}
}
_eventQueue = filteredQueue;
}
#endif // !defined(DISABLE_DEFAULT_EVENTMANAGER)