Apple's desktop operating system was formerly called "Mac OS X" and "OS X", but since 2016 it has been called "macOS" (starting with version 10.12). Changing across all comments and documentation to use this current terminology, except in cases where the historical versions are explicitly referenced. No code changes are made; we should consider changing those in future PRs.
391 lines
10 KiB
C++
391 lines
10 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#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/action.h"
|
|
#include "backends/keymapper/keymapper.h"
|
|
#include "backends/keymapper/virtual-mouse.h"
|
|
#include "backends/vkeybd/virtual-keyboard.h"
|
|
|
|
#include "engines/engine.h"
|
|
#include "gui/debugger.h"
|
|
#include "gui/message.h"
|
|
|
|
DefaultEventManager::DefaultEventManager(Common::EventSource *boss) :
|
|
_buttonState(0),
|
|
_modifierState(0),
|
|
_shouldQuit(false),
|
|
_shouldReturnToLauncher(false),
|
|
_confirmExitDialogActive(false) {
|
|
|
|
assert(boss);
|
|
|
|
_dispatcher.registerSource(boss, false);
|
|
_dispatcher.registerSource(&_artificialEventSource, false);
|
|
|
|
_dispatcher.registerObserver(this, kEventManPriority, false);
|
|
|
|
#ifdef ENABLE_VKEYBD
|
|
_vk = nullptr;
|
|
#endif
|
|
|
|
_virtualMouse = new Common::VirtualMouse(&_dispatcher);
|
|
|
|
_keymapper = new Common::Keymapper(this);
|
|
_dispatcher.registerMapper(_keymapper);
|
|
}
|
|
|
|
DefaultEventManager::~DefaultEventManager() {
|
|
delete _virtualMouse;
|
|
#ifdef ENABLE_VKEYBD
|
|
delete _vk;
|
|
#endif
|
|
delete _keymapper;
|
|
}
|
|
|
|
void DefaultEventManager::init() {
|
|
#ifdef ENABLE_VKEYBD
|
|
_vk = new Common::VirtualKeyboard();
|
|
|
|
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) {
|
|
_dispatcher.dispatch();
|
|
|
|
if (g_engine)
|
|
// Handle autosaves if enabled
|
|
g_engine->handleAutoSave();
|
|
|
|
if (_eventQueue.empty()) {
|
|
return false;
|
|
}
|
|
|
|
event = _eventQueue.pop();
|
|
bool forwardEvent = true;
|
|
|
|
// If the backend has the kFeatureNoQuit or the "Return to Launcher at Exit" option is enabled,
|
|
// replace "Quit" event with "Return to Launcher". This is also handled in scummvm_main, but
|
|
// doing it here allows getting the correct confirmation dialog if the "confirm_exit" setting
|
|
// is set to true.
|
|
if (event.type == Common::EVENT_QUIT && (g_system->hasFeature(OSystem::kFeatureNoQuit) || (ConfMan.getBool("gui_return_to_launcher_at_exit") && g_engine && g_engine->hasFeature(Engine::kSupportsReturnToLauncher))))
|
|
event.type = Common::EVENT_RETURN_TO_LAUNCHER;
|
|
|
|
switch (event.type) {
|
|
case Common::EVENT_KEYDOWN:
|
|
_modifierState = event.kbd.flags;
|
|
|
|
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
|
|
// macOS, 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;
|
|
}
|
|
break;
|
|
|
|
case Common::EVENT_KEYUP:
|
|
_modifierState = event.kbd.flags;
|
|
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 (_shouldReturnToLauncher)
|
|
event.type = Common::EVENT_RETURN_TO_LAUNCHER;
|
|
break;
|
|
|
|
case Common::EVENT_VIRTUAL_KEYBOARD:
|
|
#ifdef ENABLE_VKEYBD
|
|
if (!_vk)
|
|
break;
|
|
|
|
if (_vk->isDisplaying()) {
|
|
_vk->close(true);
|
|
} else {
|
|
PauseToken pt;
|
|
if (g_engine)
|
|
pt = g_engine->pauseEngine();
|
|
_vk->show();
|
|
forwardEvent = false;
|
|
}
|
|
#else
|
|
// TODO: Support switching between virtual keyboards at runtime
|
|
if (g_system->hasFeature(OSystem::kFeatureVirtualKeyboard)) {
|
|
if (g_system->getFeatureState(OSystem::kFeatureVirtualKeyboard))
|
|
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
|
|
else
|
|
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case Common::EVENT_RETURN_TO_LAUNCHER:
|
|
if (ConfMan.getBool("confirm_exit")) {
|
|
if (_confirmExitDialogActive) {
|
|
forwardEvent = false;
|
|
break;
|
|
}
|
|
_confirmExitDialogActive = true;
|
|
{
|
|
PauseToken pt;
|
|
if (g_engine)
|
|
pt = g_engine->pauseEngine();
|
|
GUI::MessageDialog alert(_("Do you really want to return to the Launcher?\nAny unsaved progress will be lost."), _("Yes"), _("Cancel"));
|
|
forwardEvent = _shouldReturnToLauncher = (alert.runModal() == GUI::kMessageOK);
|
|
}
|
|
_confirmExitDialogActive = false;
|
|
} else
|
|
_shouldReturnToLauncher = true;
|
|
break;
|
|
|
|
case Common::EVENT_MUTE:
|
|
if (g_engine)
|
|
g_engine->flipMute();
|
|
break;
|
|
|
|
case Common::EVENT_QUIT:
|
|
if (g_engine && ConfMan.getBool("confirm_exit")) {
|
|
if (_confirmExitDialogActive) {
|
|
forwardEvent = false;
|
|
break;
|
|
}
|
|
_confirmExitDialogActive = true;
|
|
|
|
{
|
|
PauseToken pt;
|
|
pt = g_engine->pauseEngine();
|
|
GUI::MessageDialog alert(_("Do you really want to quit?\nAny unsaved progress will be lost."), _("Quit"), _("Cancel"));
|
|
forwardEvent = _shouldQuit = (alert.runModal() == GUI::kMessageOK);
|
|
}
|
|
_confirmExitDialogActive = false;
|
|
} else {
|
|
_shouldQuit = true;
|
|
}
|
|
break;
|
|
|
|
case Common::EVENT_DEBUGGER: {
|
|
GUI::Debugger *debugger = g_engine ? g_engine->getOrCreateDebugger() : nullptr;
|
|
if (debugger && !debugger->isActive()) {
|
|
debugger->attach();
|
|
debugger->onFrame();
|
|
forwardEvent = false;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Common::EVENT_INPUT_CHANGED: {
|
|
Common::HardwareInputSet *inputSet = g_system->getHardwareInputSet();
|
|
Common::KeymapperDefaultBindings *backendDefaultBindings = g_system->getKeymapperDefaultBindings();
|
|
|
|
_keymapper->registerHardwareInputSet(inputSet, backendDefaultBindings);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return forwardEvent;
|
|
}
|
|
|
|
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) {
|
|
// Update button state even when purging events to avoid desynchronisation with real button state
|
|
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_WHEELUP:
|
|
case Common::EVENT_WHEELDOWN:
|
|
case Common::EVENT_MBUTTONDOWN:
|
|
case Common::EVENT_MBUTTONUP:
|
|
case Common::EVENT_X1BUTTONDOWN:
|
|
case Common::EVENT_X1BUTTONUP:
|
|
case Common::EVENT_X2BUTTONDOWN:
|
|
case Common::EVENT_X2BUTTONUP:
|
|
case Common::EVENT_MOUSEMOVE:
|
|
// do nothing
|
|
break;
|
|
default:
|
|
filteredQueue.push(event);
|
|
break;
|
|
}
|
|
}
|
|
_eventQueue = filteredQueue;
|
|
}
|
|
|
|
void DefaultEventManager::purgeKeyboardEvents() {
|
|
_dispatcher.dispatch();
|
|
|
|
Common::Queue<Common::Event> filteredQueue;
|
|
while (!_eventQueue.empty()) {
|
|
Common::Event event = _eventQueue.pop();
|
|
switch (event.type) {
|
|
// Update keyboard state even when purging events to avoid desynchronisation with real keyboard state
|
|
case Common::EVENT_KEYDOWN:
|
|
case Common::EVENT_KEYUP:
|
|
_modifierState = event.kbd.flags;
|
|
break;
|
|
|
|
default:
|
|
filteredQueue.push(event);
|
|
break;
|
|
}
|
|
}
|
|
_eventQueue = filteredQueue;
|
|
}
|
|
|
|
Common::Keymap *DefaultEventManager::getGlobalKeymap() {
|
|
using namespace Common;
|
|
|
|
// Now create the global keymap
|
|
Keymap *globalKeymap = new Keymap(Keymap::kKeymapTypeGlobal, kGlobalKeymapName, _("Global"));
|
|
|
|
Action *act;
|
|
act = new Action("MENU", _("Global Main Menu"));
|
|
act->addDefaultInputMapping("C+F5");
|
|
act->addDefaultInputMapping("JOY_START");
|
|
act->setEvent(EVENT_MAINMENU);
|
|
globalKeymap->addAction(act);
|
|
|
|
#ifndef ENABLE_VKEYBD
|
|
if (g_system->hasFeature(OSystem::kFeatureVirtualKeyboard))
|
|
#endif
|
|
{
|
|
act = new Action("VIRT", _("Display keyboard"));
|
|
act->addDefaultInputMapping("C+F7");
|
|
act->addDefaultInputMapping("JOY_BACK");
|
|
act->setEvent(EVENT_VIRTUAL_KEYBOARD);
|
|
globalKeymap->addAction(act);
|
|
}
|
|
|
|
act = new Action("MUTE", _("Toggle mute"));
|
|
act->addDefaultInputMapping("C+u");
|
|
act->setEvent(EVENT_MUTE);
|
|
globalKeymap->addAction(act);
|
|
|
|
if (!g_system->hasFeature(OSystem::kFeatureNoQuit)) {
|
|
act = new Action("QUIT", _("Quit"));
|
|
act->setEvent(EVENT_QUIT);
|
|
|
|
#if defined(MACOSX)
|
|
// On Macintosh, Cmd-Q quits
|
|
act->addDefaultInputMapping("M+q");
|
|
#elif defined(POSIX)
|
|
// On other *nix systems, Control-Q quits
|
|
act->addDefaultInputMapping("C+q");
|
|
#else
|
|
// Ctrl-z quits
|
|
act->addDefaultInputMapping("C+z");
|
|
|
|
#ifdef WIN32
|
|
// On Windows, also use the default Alt-F4 quit combination
|
|
act->addDefaultInputMapping("A+F4");
|
|
#endif
|
|
#endif
|
|
|
|
globalKeymap->addAction(act);
|
|
}
|
|
|
|
act = new Action("DEBUGGER", _("Open Debugger"));
|
|
act->addDefaultInputMapping("C+A+d");
|
|
act->setEvent(EVENT_DEBUGGER);
|
|
globalKeymap->addAction(act);
|
|
|
|
_virtualMouse->addActionsToKeymap(globalKeymap);
|
|
|
|
return globalKeymap;
|
|
}
|
|
|
|
#endif // !defined(DISABLE_DEFAULT_EVENTMANAGER)
|