mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-11 03:34:13 +00:00
KEYMAPPER: Introduce a Virtual Mouse event source
The Virtual Mouse is meant to provide a way to control the mouse cursor on system without a physical mouse. It provides keymapper actions that are expected to be bound to game controller axes or buttons.
This commit is contained in:
parent
e66e35a3fd
commit
568d882e80
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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, "");
|
||||
|
@ -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;
|
||||
|
219
backends/keymapper/virtual-mouse.cpp
Normal file
219
backends/keymapper/virtual-mouse.cpp
Normal file
@ -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<int16>(event.mouse.x, 0, screenSize.width());
|
||||
event.mouse.y = CLIP<int16>(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
|
91
backends/keymapper/virtual-mouse.h
Normal file
91
backends/keymapper/virtual-mouse.h
Normal file
@ -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
|
@ -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 \
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user