diff --git a/backends/events/default/default-events.cpp b/backends/events/default/default-events.cpp index e29d9c3da50..8781b1ce7f7 100644 --- a/backends/events/default/default-events.cpp +++ b/backends/events/default/default-events.cpp @@ -30,7 +30,7 @@ #include "backends/events/default/default-events.h" #include "backends/keymapper/action.h" #include "backends/keymapper/keymapper.h" -#include "backends/keymapper/remap-widget.h" +#include "backends/keymapper/virtual-mouse.h" #include "backends/vkeybd/virtual-keyboard.h" #include "engines/engine.h" @@ -58,13 +58,15 @@ DefaultEventManager::DefaultEventManager(Common::EventSource *boss) : #ifdef ENABLE_VKEYBD _vk = nullptr; #endif + + _virtualMouse = new Common::VirtualMouse(&_dispatcher); + _keymapper = new Common::Keymapper(this); - // EventDispatcher will automatically free the keymapper _dispatcher.registerMapper(_keymapper); - _remap = false; } DefaultEventManager::~DefaultEventManager() { + delete _virtualMouse; #ifdef ENABLE_VKEYBD delete _vk; #endif @@ -372,6 +374,8 @@ Common::Keymap *DefaultEventManager::getGlobalKeymap() { act->setEvent(EVENT_DEBUGGER); globalKeymap->addAction(act); + _virtualMouse->addActionsToKeymap(globalKeymap); + return globalKeymap; } diff --git a/backends/events/default/default-events.h b/backends/events/default/default-events.h index 11d523af043..7696c08303f 100644 --- a/backends/events/default/default-events.h +++ b/backends/events/default/default-events.h @@ -31,6 +31,7 @@ class Keymapper; #ifdef ENABLE_VKEYBD class VirtualKeyboard; #endif +class VirtualMouse; } @@ -39,8 +40,9 @@ class DefaultEventManager : public Common::EventManager, Common::EventObserver { Common::VirtualKeyboard *_vk; #endif + Common::VirtualMouse *_virtualMouse; + Common::Keymapper *_keymapper; - bool _remap; Common::ArtificialEventSource _artificialEventSource; diff --git a/backends/keymapper/action.h b/backends/keymapper/action.h index a8e46e8d9c0..f798d2f50a8 100644 --- a/backends/keymapper/action.h +++ b/backends/keymapper/action.h @@ -68,6 +68,12 @@ public: event.customType = evtType; } + void setCustomBackendActionAxisEvent(const CustomEventType evtType) { + event = Event(); + event.type = EVENT_CUSTOM_BACKEND_ACTION_AXIS; + event.customType = evtType; + } + void setCustomEngineActionEvent(const CustomEventType evtType) { event = Event(); event.type = EVENT_CUSTOM_ENGINE_ACTION_START; diff --git a/backends/keymapper/keymap.cpp b/backends/keymapper/keymap.cpp index 409aff6a2c5..64b8c3ddaa4 100644 --- a/backends/keymapper/keymap.cpp +++ b/backends/keymapper/keymap.cpp @@ -171,9 +171,19 @@ Keymap::ActionArray Keymap::getMappedActions(const Event &event) const { return _hwActionMap[hardwareInput]; } case EVENT_JOYAXIS_MOTION: { - bool positiveHalf = event.joystick.position >= 0; - HardwareInput hardwareInput = HardwareInput::createJoystickHalfAxis("", event.joystick.axis, positiveHalf, ""); - return _hwActionMap[hardwareInput]; + if (event.joystick.position != 0) { + bool positiveHalf = event.joystick.position >= 0; + HardwareInput hardwareInput = HardwareInput::createJoystickHalfAxis("", event.joystick.axis, positiveHalf, ""); + return _hwActionMap[hardwareInput]; + } else { + // Axis position zero is part of both half axes, and triggers actions bound to both + Keymap::ActionArray actions; + HardwareInput hardwareInputPos = HardwareInput::createJoystickHalfAxis("", event.joystick.axis, true, ""); + HardwareInput hardwareInputNeg = HardwareInput::createJoystickHalfAxis("", event.joystick.axis, false, ""); + actions.push_back(_hwActionMap[hardwareInputPos]); + actions.push_back(_hwActionMap[hardwareInputNeg]); + return actions; + } } case EVENT_CUSTOM_BACKEND_HARDWARE: { HardwareInput hardwareInput = HardwareInput::createCustom("", event.customType, ""); diff --git a/backends/keymapper/keymapper.cpp b/backends/keymapper/keymapper.cpp index 6dc5f366595..c3d5b9ed0ca 100644 --- a/backends/keymapper/keymapper.cpp +++ b/backends/keymapper/keymapper.cpp @@ -261,6 +261,23 @@ Event Keymapper::executeAction(const Action *action, const Event &incomingEvent) Event outgoingEvent = Event(action->event); IncomingEventType incomingType = convertToIncomingEventType(incomingEvent); + + if (outgoingEvent.type == EVENT_JOYAXIS_MOTION + || outgoingEvent.type == EVENT_CUSTOM_BACKEND_ACTION_AXIS) { + if (incomingEvent.type == EVENT_JOYAXIS_MOTION) { + // At the moment only half-axes can be bound to actions, hence taking + // the absolute value. If full axes were to be mappable, the action + // could carry the information allowing to distinguish cases here. + outgoingEvent.joystick.position = ABS(incomingEvent.joystick.position); + } else if (incomingType == kIncomingEventStart) { + outgoingEvent.joystick.position = JOYAXIS_MAX; + } else if (incomingType == kIncomingEventEnd) { + outgoingEvent.joystick.position = 0; + } + + return outgoingEvent; + } + if (incomingType == kIncomingEventIgnored) { outgoingEvent.type = EVENT_INVALID; return outgoingEvent; diff --git a/backends/keymapper/virtual-mouse.cpp b/backends/keymapper/virtual-mouse.cpp new file mode 100644 index 00000000000..0adf3a2a922 --- /dev/null +++ b/backends/keymapper/virtual-mouse.cpp @@ -0,0 +1,219 @@ +/* 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 "backends/keymapper/virtual-mouse.h" + +#include "backends/keymapper/action.h" +#include "backends/keymapper/keymap.h" + +#include "common/config-manager.h" +#include "common/system.h" +#include "common/translation.h" + +#include "gui/gui-manager.h" + +namespace Common { + +VirtualMouse::VirtualMouse(EventDispatcher *eventDispatcher) : + _eventDispatcher(eventDispatcher), + _inputAxisPositionX(0), + _inputAxisPositionY(0), + _mouseVelocityX(0.f), + _mouseVelocityY(0.f), + _slowModifier(1.f), + _subPixelRemainderX(0.f), + _subPixelRemainderY(0.f), + _lastUpdateMillis(0) { + _eventDispatcher->registerSource(this, false); + _eventDispatcher->registerObserver(this, 10, false); +} + +VirtualMouse::~VirtualMouse() { + _eventDispatcher->unregisterObserver(this); + _eventDispatcher->unregisterSource(this); +} + +bool VirtualMouse::pollEvent(Event &event) { + // Update the virtual mouse once per frame (assuming 60Hz) + uint32 curTime = g_system->getMillis(true); + if (curTime < _lastUpdateMillis + kUpdateDelay) { + return false; + } + _lastUpdateMillis = curTime; + + // Adjust the speed of the cursor according to the virtual screen resolution + Common::Rect screenSize; + if (g_gui.isActive()) { + screenSize = Common::Rect(g_system->getOverlayWidth(), g_system->getOverlayHeight()); + } else { + screenSize = Common::Rect(g_system->getWidth(), g_system->getHeight()); + } + + float screenSizeSpeedModifier = screenSize.width() / (float)kDefaultScreenWidth; + + // Compute the movement delta when compared to the previous update + float deltaX = _subPixelRemainderX + _mouseVelocityX * _slowModifier * screenSizeSpeedModifier * 10.f; + float deltaY = _subPixelRemainderY + _mouseVelocityY * _slowModifier * screenSizeSpeedModifier * 10.f; + + Common::Point delta; + delta.x = deltaX; + delta.y = deltaY; + + // Keep track of sub-pixel movement so the cursor ultimately moves, + // even when configured at very low speeds. + _subPixelRemainderX = deltaX - delta.x; + _subPixelRemainderY = deltaY - delta.y; + + if (delta.x == 0 && delta.y == 0) { + return false; + } + + // Send a mouse event + Common::Point oldPos = g_system->getEventManager()->getMousePos(); + + event.type = Common::EVENT_MOUSEMOVE; + event.mouse = oldPos + delta; + + event.mouse.x = CLIP(event.mouse.x, 0, screenSize.width()); + event.mouse.y = CLIP(event.mouse.y, 0, screenSize.height()); + + g_system->warpMouse(event.mouse.x, event.mouse.y); + + return true; +} + +bool VirtualMouse::notifyEvent(const Event &event) { + if (event.type != EVENT_CUSTOM_BACKEND_ACTION_AXIS) { + return false; + } + + switch (event.customType) { + case kCustomActionVirtualAxisUp: + if (event.joystick.position == 0 && _inputAxisPositionY > 0) { + return true; // Ignore axis reset events if we are already going in the other direction + } + + handleAxisMotion(_inputAxisPositionX, -event.joystick.position); + return true; + case kCustomActionVirtualAxisDown: + if (event.joystick.position == 0 && _inputAxisPositionY < 0) { + return true; + } + + handleAxisMotion(_inputAxisPositionX, event.joystick.position); + return true; + case kCustomActionVirtualAxisLeft: + if (event.joystick.position == 0 && _inputAxisPositionX > 0) { + return true; + } + + handleAxisMotion(-event.joystick.position, _inputAxisPositionY); + return true; + case kCustomActionVirtualAxisRight: + if (event.joystick.position == 0 && _inputAxisPositionX < 0) { + return true; + } + + handleAxisMotion(event.joystick.position, _inputAxisPositionY); + return true; + case kCustomActionVirtualMouseSlow: + _slowModifier = 0.9f * (1.f - event.joystick.position / (float)JOYAXIS_MAX) + 0.1f; + return true; + } + + return false; +} + +void VirtualMouse::addActionsToKeymap(Keymap *keymap) { + Action *act; + + act = new Action("VMOUSEUP", _("Virtual mouse up")); + act->addDefaultInputMapping("JOY_LEFT_STICK_Y-"); + act->setCustomBackendActionAxisEvent(VirtualMouse::kCustomActionVirtualAxisUp); + keymap->addAction(act); + + act = new Action("VMOUSEDOWN", _("Virtual mouse down")); + act->addDefaultInputMapping("JOY_LEFT_STICK_Y+"); + act->setCustomBackendActionAxisEvent(VirtualMouse::kCustomActionVirtualAxisDown); + keymap->addAction(act); + + act = new Action("VMOUSELEFT", _("Virtual mouse left")); + act->addDefaultInputMapping("JOY_LEFT_STICK_X-"); + act->setCustomBackendActionAxisEvent(VirtualMouse::kCustomActionVirtualAxisLeft); + keymap->addAction(act); + + act = new Action("VMOUSERIGHT", _("Virtual mouse right")); + act->addDefaultInputMapping("JOY_LEFT_STICK_X+"); + act->setCustomBackendActionAxisEvent(VirtualMouse::kCustomActionVirtualAxisRight); + keymap->addAction(act); + + act = new Action("VMOUSESLOW", _("Slow down virtual mouse")); + act->addDefaultInputMapping("JOY_RIGHT_SHOULDER"); + act->setCustomBackendActionAxisEvent(VirtualMouse::kCustomActionVirtualMouseSlow); + keymap->addAction(act); +} + +void VirtualMouse::handleAxisMotion(int16 axisPositionX, int16 axisPositionY) { + _inputAxisPositionX = axisPositionX; + _inputAxisPositionY = axisPositionY; + + float analogX = (float)_inputAxisPositionX; + float analogY = (float)_inputAxisPositionY; + float deadZone = (float)ConfMan.getInt("joystick_deadzone") * 1000.0f; + + float magnitude = sqrtf(analogX * analogX + analogY * analogY); + + if (magnitude >= deadZone) { + float scalingFactor = 1.0f / magnitude * (magnitude - deadZone) / (JOYAXIS_MAX - deadZone); + float speedFactor = computeJoystickMouseSpeedFactor(); + _mouseVelocityX = analogX * scalingFactor * speedFactor; + _mouseVelocityY = analogY * scalingFactor * speedFactor; + } else { + _mouseVelocityX = 0.f; + _mouseVelocityY = 0.f; + } +} + +float VirtualMouse::computeJoystickMouseSpeedFactor() const { + switch (ConfMan.getInt("kbdmouse_speed")) { + case 0: + return 0.25; // 0.25 keyboard pointer speed + case 1: + return 0.5; // 0.5 speed + case 2: + return 0.75; // 0.75 speed + case 3: + return 1.0; // 1.0 speed + case 4: + return 1.25; // 1.25 speed + case 5: + return 1.5; // 1.5 speed + case 6: + return 1.75; // 1.75 speed + case 7: + return 2.0; // 2.0 speed + default: + return 1.0; + } +} + +} // End of namespace Common diff --git a/backends/keymapper/virtual-mouse.h b/backends/keymapper/virtual-mouse.h new file mode 100644 index 00000000000..58c0f29799a --- /dev/null +++ b/backends/keymapper/virtual-mouse.h @@ -0,0 +1,91 @@ +/* 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. + * + */ + +#ifndef BACKENDS_KEYMAPPER_VIRTUAL_MOUSE_H +#define BACKENDS_KEYMAPPER_VIRTUAL_MOUSE_H + +#include "common/scummsys.h" + +#include "common/events.h" + +namespace Common { + +class EventDispatcher; +class Keymap; + +/** + * The Virtual Mouse can produce mouse move events on systems without a physical mouse. + * + * It is useful for moving the mouse cursor using a gamepad or a keyboard. + * + * This class defines a keymap with actions for moving the cursor in all four directions. + * The keymapper produces custom backend events whenever keys bound to these actions are + * pressed. This class handles the events through its EventObserver interface and produces + * mouse move events when necesssary through its EventSource interface. + */ +class VirtualMouse : public EventSource, public EventObserver { +public: + VirtualMouse(EventDispatcher *eventDispatcher); + ~VirtualMouse() override; + + // EventSource API + bool pollEvent(Event &event) override; + + // EventObserver API + bool notifyEvent(const Event &event) override; + + /** Add the virtual mouse keymapper actions to a keymap */ + void addActionsToKeymap(Keymap *keymap); + +private: + static const int32 kUpdateDelay = 12; + static const int32 kDefaultScreenWidth = 640; + + enum { + kCustomActionVirtualAxisUp = 10000, + kCustomActionVirtualAxisDown = 10001, + kCustomActionVirtualAxisLeft = 10002, + kCustomActionVirtualAxisRight = 10003, + kCustomActionVirtualMouseSlow = 10004 + }; + + void handleAxisMotion(int16 axisPositionX, int16 axisPositionY); + float computeJoystickMouseSpeedFactor() const; + + EventDispatcher *_eventDispatcher; + + int16 _inputAxisPositionX; + int16 _inputAxisPositionY; + + float _mouseVelocityX; + float _mouseVelocityY; + float _slowModifier; + + float _subPixelRemainderX; + float _subPixelRemainderY; + + uint32 _lastUpdateMillis; +}; + +} // End of namespace Common + +#endif // #ifndef BACKENDS_KEYMAPPER_VIRTUAL_MOUSE_H diff --git a/backends/module.mk b/backends/module.mk index b5d7736698d..caea5bb11cf 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -15,6 +15,7 @@ MODULE_OBJS := \ keymapper/keymapper.o \ keymapper/remap-widget.o \ keymapper/standard-actions.o \ + keymapper/virtual-mouse.o \ log/log.o \ midi/alsa.o \ midi/dmedia.o \ diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index de1fd3380ec..79b9ae50067 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -203,7 +203,7 @@ void OSystem_SDL::initBackend() { // Create the default event source, in case a custom backend // manager didn't provide one yet. if (_eventSource == 0) - _eventSource = new LegacySdlEventSource(); + _eventSource = new SdlEventSource(); if (_eventManager == nullptr) { DefaultEventManager *eventManager = new DefaultEventManager(_eventSource); diff --git a/common/events.h b/common/events.h index b781f35cca3..d314acf0d2e 100644 --- a/common/events.h +++ b/common/events.h @@ -76,6 +76,7 @@ enum EventType { EVENT_CUSTOM_BACKEND_ACTION_START = 18, EVENT_CUSTOM_BACKEND_ACTION_END = 19, + EVENT_CUSTOM_BACKEND_ACTION_AXIS = 34, EVENT_CUSTOM_ENGINE_ACTION_START = 20, EVENT_CUSTOM_ENGINE_ACTION_END = 21, diff --git a/po/POTFILES b/po/POTFILES index b72a56ae467..abdef12d73e 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -65,6 +65,7 @@ backends/graphics/surfacesdl/surfacesdl-graphics.cpp backends/graphics/sdl/sdl-graphics.cpp backends/keymapper/hardware-input.cpp backends/keymapper/remap-widget.cpp +backends/keymapper/virtual-mouse.cpp backends/midi/windows.cpp backends/networking/sdl_net/handlers/createdirectoryhandler.cpp backends/networking/sdl_net/handlers/downloadfilehandler.cpp