stella/src/emucore/EventHandler.hxx

672 lines
21 KiB
C++

//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#ifndef EVENTHANDLER_HXX
#define EVENTHANDLER_HXX
#include <map>
class Console;
class OSystem;
class MouseControl;
class DialogContainer;
class PhysicalJoystick;
class Variant;
namespace GUI {
class Font;
}
#include "Event.hxx"
#include "EventHandlerConstants.hxx"
#include "Control.hxx"
#include "StellaKeys.hxx"
#include "PKeyboardHandler.hxx"
#include "PJoystickHandler.hxx"
#include "bspf.hxx"
/**
This class takes care of event remapping and dispatching for the
Stella core, as well as keeping track of the current 'mode'.
The frontend will send translated events here, and the handler will
check to see what the current 'mode' is.
If in emulation mode, events received from the frontend are remapped and
sent to the emulation core. If in menu mode, the events are sent
unchanged to the menu class, where (among other things) changing key
mapping can take place.
@author Stephen Anthony, Thomas Jentzsch
*/
class EventHandler
{
public:
/**
Create a new event handler object
*/
EventHandler(OSystem& osystem);
virtual ~EventHandler();
/**
Returns the event object associated with this handler class.
@return The event object
*/
const Event& event() const { return myEvent; }
/**
Initialize state of this eventhandler.
*/
void initialize();
/**
Maps the given Stelladaptor/2600-daptor(s) to specified ports on a real 2600.
@param saport How to map the ports ('lr' or 'rl')
*/
void mapStelladaptors(const string& saport);
/**
Toggles if all four joystick directions are allowed at once
*/
void toggleAllow4JoyDirections(bool toggle = true);
/**
Swaps the ordering of Stelladaptor/2600-daptor(s) devices.
*/
void toggleSAPortOrder(bool toggle = true);
/**
Toggle whether the console is in 2600 or 7800 mode.
Note that for now, this only affects whether the 7800 pause button is
supported; there is no further emulation of the 7800 itself.
*/
void set7800Mode();
/**
Collects and dispatches any pending events. This method should be
called regularly (at X times per second, where X is the game framerate).
@param time The current time in microseconds.
*/
void poll(uInt64 time);
/**
Get/set the current state of the EventHandler.
@return The EventHandlerState type
*/
EventHandlerState state() const { return myState; }
void setState(EventHandlerState state);
/**
Convenience method that checks if we're in TIA mode.
@return Whether TIA mode is active
*/
bool inTIAMode() const {
return !(myState == EventHandlerState::DEBUGGER ||
myState == EventHandlerState::LAUNCHER ||
myState == EventHandlerState::NONE);
}
/**
Resets the state machine of the EventHandler to the defaults.
@param state The current state to set
*/
void reset(EventHandlerState state);
/**
This method indicates that the system should terminate.
*/
void quit() { handleEvent(Event::Quit); }
/**
Sets the mouse axes and buttons to act as the controller specified in
the ROM properties, otherwise disable mouse control completely
@param enable Whether to use the mouse to emulate controllers
Currently, this will be one of the following values:
'always', 'analog', 'never'
*/
void setMouseControllerMode(const string& enable);
void changeMouseControllerMode(int direction = +1);
void changeMouseCursor(int direction = +1);
void enterMenuMode(EventHandlerState state);
void leaveMenuMode();
bool enterDebugMode();
void leaveDebugMode();
void enterTimeMachineMenuMode(uInt32 numWinds, bool unwind);
void enterPlayBackMode();
/**
Send an event directly to the event handler.
These events cannot be remapped.
@param type The event
@param value The value to use for the event
@param repeated Repeated key (true) or first press/release (false)
*/
void handleEvent(Event::Type type, Int32 value = 1, bool repeated = false);
/**
Handle events that must be processed each time a new console is
created. Typically, these are events set by commandline arguments.
*/
void handleConsoleStartupEvents();
bool frying() const { return myFryingFlag; }
StringList getActionList(Event::Group group) const;
VariantList getComboList() const;
/** Used to access the list of events assigned to a specific combo event. */
StringList getComboListForEvent(Event::Type event) const;
void setComboListForEvent(Event::Type event, const StringList& events);
/** Convert keys and physical joystick events into Stella events. */
Event::Type eventForKey(EventMode mode, StellaKey key, StellaMod mod) const {
return myPKeyHandler->eventForKey(mode, key, mod);
}
Event::Type eventForJoyAxis(EventMode mode, int stick, JoyAxis axis, JoyDir adir, int button) const {
return myPJoyHandler->eventForAxis(mode, stick, axis, adir, button);
}
Event::Type eventForJoyButton(EventMode mode, int stick, int button) const {
return myPJoyHandler->eventForButton(mode, stick, button);
}
Event::Type eventForJoyHat(EventMode mode, int stick, int hat, JoyHatDir hdir, int button) const {
return myPJoyHandler->eventForHat(mode, stick, hat, hdir, button);
}
/** Get description of given event and mode. */
string getMappingDesc(Event::Type event, EventMode mode) const {
return myPKeyHandler->getMappingDesc(event, mode);
}
Event::Type eventAtIndex(int idx, Event::Group group) const;
string actionAtIndex(int idx, Event::Group group) const;
string keyAtIndex(int idx, Event::Group group) const;
/**
Bind a key to an event/action and regenerate the mapping array(s).
@param event The event we are remapping
@param mode The mode where this event is active
@param key The key to bind to this event
@param mod The modifier to bind to this event
*/
bool addKeyMapping(Event::Type event, EventMode mode, StellaKey key, StellaMod mod);
/**
Enable controller specific keyboard event mappings.
*/
void defineKeyControllerMappings(const Controller::Type type, Controller::Jack port,
const Properties& properties) {
myPKeyHandler->defineControllerMappings(type, port, properties);
}
/**
Enable emulation keyboard event mappings.
*/
void enableEmulationKeyMappings() {
myPKeyHandler->enableEmulationMappings();
}
/**
Bind a physical joystick axis direction to an event/action and regenerate
the mapping array(s). The axis can be combined with a button. The button
can also be mapped without an axis.
@param event The event we are remapping
@param mode The mode where this event is active
@param stick The joystick number
@param button The joystick button
@param axis The joystick axis
@param adir The given axis
@param updateMenus Whether to update the action mappings (normally
we want to do this, unless there are a batch of
'adds', in which case it's delayed until the end
*/
bool addJoyMapping(Event::Type event, EventMode mode, int stick,
int button, JoyAxis axis = JoyAxis::NONE, JoyDir adir = JoyDir::NONE,
bool updateMenus = true);
/**
Bind a physical joystick hat direction to an event/action and regenerate
the mapping array(s). The hat can be combined with a button.
@param event The event we are remapping
@param mode The mode where this event is active
@param stick The joystick number
@param button The joystick button
@param hat The joystick hat
@param dir The value on the given hat
@param updateMenus Whether to update the action mappings (normally
we want to do this, unless there are a batch of
'adds', in which case it's delayed until the end
*/
bool addJoyHatMapping(Event::Type event, EventMode mode, int stick,
int button, int hat, JoyHatDir dir,
bool updateMenus = true);
/**
Enable controller specific keyboard event mappings.
*/
void defineJoyControllerMappings(const Controller::Type type, Controller::Jack port) {
myPJoyHandler->defineControllerMappings(type, port);
}
/**
Enable emulation keyboard event mappings.
*/
void enableEmulationJoyMappings() {
myPJoyHandler->enableEmulationMappings();
}
/**
Erase the specified mapping.
@param event The event for which we erase all mappings
@param mode The mode where this event is active
*/
void eraseMapping(Event::Type event, EventMode mode);
/**
Resets the event mappings to default values.
@param event The event which to (re)set (Event::NoType resets all)
@param mode The mode for which the defaults are set
*/
void setDefaultMapping(Event::Type event, EventMode mode);
/**
Joystick emulates 'impossible' directions (ie, left & right
at the same time).
@param allow Whether or not to allow impossible directions
*/
void allowAllDirections(bool allow) { myAllowAllDirectionsFlag = allow; }
/**
Changes to a new state based on the current state and the given event.
@param type The event
@return True if the state changed, else false
*/
bool changeStateByEvent(Event::Type type);
/**
Get the current overlay in use. The overlay won't always exist,
so we should test if it's available.
@return The overlay object
*/
DialogContainer& overlay() const { return *myOverlay; }
bool hasOverlay() const { return myOverlay != nullptr; }
/**
Return a list of all physical joysticks currently in the internal database
(first part of variant) and its internal ID (second part of variant).
*/
VariantList physicalJoystickDatabase() const {
return myPJoyHandler->database();
}
/**
Remove the physical joystick identified by 'name' from the joystick
database, only if it is not currently active.
*/
void removePhysicalJoystickFromDatabase(const string& name);
/**
Enable/disable text events (distinct from single-key events).
*/
virtual void enableTextEvents(bool enable) = 0;
#ifdef GUI_SUPPORT
/**
Check for QWERTZ keyboard layout
*/
bool isQwertz() { return myQwertz; }
/**
Clipboard methods.
*/
virtual void copyText(const string& text) const = 0;
virtual string pasteText(string& text) const = 0;
#endif
/**
Handle changing mouse modes.
*/
void changeMouseControl(int direction = +1);
bool hasMouseControl() const;
void saveKeyMapping();
void saveJoyMapping();
void exitEmulation(bool checkLauncher = false);
protected:
// Global OSystem object
OSystem& myOSystem;
#ifdef GUI_SUPPORT
// Keyboard layout
bool myQwertz{false};
#endif
/**
Sets the combo event mappings to those in the 'combomap' setting
*/
void setComboMap();
/**
Methods which are called by derived classes to handle specific types
of input.
*/
void handleTextEvent(char text);
void handleMouseMotionEvent(int x, int y, int xrel, int yrel);
void handleMouseButtonEvent(MouseButton b, bool pressed, int x, int y);
void handleKeyEvent(StellaKey key, StellaMod mod, bool pressed, bool repeated) {
myPKeyHandler->handleEvent(key, mod, pressed, repeated);
}
void handleJoyBtnEvent(int stick, int button, bool pressed) {
myPJoyHandler->handleBtnEvent(stick, button, pressed);
}
void handleJoyAxisEvent(int stick, int axis, int value) {
myPJoyHandler->handleAxisEvent(stick, axis, value);
}
void handleJoyHatEvent(int stick, int hat, int value) {
myPJoyHandler->handleHatEvent(stick, hat, value);
}
/**
Collects and dispatches any pending events.
*/
virtual void pollEvent() = 0;
// Other events that can be received from the underlying event handler
enum class SystemEvent {
WINDOW_SHOWN,
WINDOW_HIDDEN,
WINDOW_EXPOSED,
WINDOW_MOVED,
WINDOW_RESIZED,
WINDOW_MINIMIZED,
WINDOW_MAXIMIZED,
WINDOW_RESTORED,
WINDOW_ENTER,
WINDOW_LEAVE,
WINDOW_FOCUS_GAINED,
WINDOW_FOCUS_LOST
};
void handleSystemEvent(SystemEvent e, int data1 = 0, int data2 = 0);
/**
Add the given joystick to the list of physical joysticks available to the handler.
*/
void addPhysicalJoystick(const PhysicalJoystickPtr& stick);
/**
Remove physical joystick at the current index.
*/
void removePhysicalJoystick(int index);
private:
enum class AdjustSetting
{
NONE = -1,
// *** Audio & Video group ***
VOLUME,
ZOOM,
FULLSCREEN,
#ifdef ADAPTABLE_REFRESH_SUPPORT
ADAPT_REFRESH,
#endif
OVERSCAN,
TVFORMAT,
VCENTER,
ASPECT_RATIO,
VSIZE,
// Palette adjustables
PALETTE,
PALETTE_PHASE,
PALETTE_RED_SCALE,
PALETTE_RED_SHIFT,
PALETTE_GREEN_SCALE,
PALETTE_GREEN_SHIFT,
PALETTE_BLUE_SCALE,
PALETTE_BLUE_SHIFT,
PALETTE_HUE,
PALETTE_SATURATION,
PALETTE_CONTRAST,
PALETTE_BRIGHTNESS,
PALETTE_GAMMA,
// NTSC filter adjustables
NTSC_PRESET,
NTSC_SHARPNESS,
NTSC_RESOLUTION,
NTSC_ARTIFACTS,
NTSC_FRINGING,
NTSC_BLEEDING,
// Other TV effects adjustables
PHOSPHOR,
SCANLINES,
INTERPOLATION,
// *** Input group ***
DEADZONE,
ANALOG_DEADZONE,
ANALOG_SENSITIVITY,
ANALOG_LINEARITY,
DEJITTER_AVERAGING,
DEJITTER_REACTION,
DIGITAL_SENSITIVITY,
AUTO_FIRE,
FOUR_DIRECTIONS,
MOD_KEY_COMBOS,
SA_PORT_ORDER,
USE_MOUSE,
PADDLE_SENSITIVITY,
TRACKBALL_SENSITIVITY,
DRIVING_SENSITIVITY,
MOUSE_CURSOR,
GRAB_MOUSE,
LEFT_PORT,
RIGHT_PORT,
SWAP_PORTS,
SWAP_PADDLES,
PADDLE_CENTER_X,
PADDLE_CENTER_Y,
MOUSE_CONTROL,
MOUSE_RANGE,
// *** Debug group ***
STATS,
P0_ENAM,
P1_ENAM,
M0_ENAM,
M1_ENAM,
BL_ENAM,
PF_ENAM,
ALL_ENAM,
P0_CX,
P1_CX,
M0_CX,
M1_CX,
BL_CX,
PF_CX,
ALL_CX,
FIXED_COL,
COLOR_LOSS,
JITTER,
// *** Only used via direct hotkeys ***
STATE,
PALETTE_CHANGE_ATTRIBUTE,
NTSC_CHANGE_ATTRIBUTE,
CHANGE_SPEED,
// *** Ranges ***
NUM_ADJ,
START_AV_ADJ = VOLUME,
END_AV_ADJ = INTERPOLATION,
START_INPUT_ADJ = DEADZONE,
END_INPUT_ADJ = MOUSE_RANGE,
START_DEBUG_ADJ = STATS,
END_DEBUG_ADJ = JITTER,
};
enum class AdjustGroup
{
AV,
INPUT,
DEBUG,
NUM_GROUPS
};
private:
// Define event groups
static const Event::EventSet MiscEvents;
static const Event::EventSet AudioVideoEvents;
static const Event::EventSet StateEvents;
static const Event::EventSet ConsoleEvents;
static const Event::EventSet JoystickEvents;
static const Event::EventSet PaddlesEvents;
static const Event::EventSet KeyboardEvents;
static const Event::EventSet DevicesEvents;
static const Event::EventSet ComboEvents;
static const Event::EventSet DebugEvents;
/**
The following methods take care of assigning action mappings.
*/
void setActionMappings(EventMode mode);
void setDefaultKeymap(Event::Type, EventMode mode);
void setDefaultJoymap(Event::Type, EventMode mode);
static nlohmann::json convertLegacyComboMapping(string list);
void saveComboMapping();
StringList getActionList(EventMode mode) const;
StringList getActionList(const Event::EventSet& events, EventMode mode = EventMode::kEmulationMode) const;
// returns the action array index of the index in the provided group
int getEmulActionListIndex(int idx, const Event::EventSet& events) const;
int getActionListIndex(int idx, Event::Group group) const;
// The following two methods are used for adjusting several settings using global hotkeys
// They return the function used to adjust the currenly selected setting
AdjustGroup getAdjustGroup();
AdjustFunction cycleAdjustSetting(int direction);
AdjustFunction getAdjustSetting(AdjustSetting setting);
PhysicalJoystickHandler& joyHandler() { return *myPJoyHandler; }
PhysicalKeyboardHandler& keyHandler() { return *myPKeyHandler; }
bool isJoystick(const Controller& controller) const;
bool isPaddle(const Controller& controller) const;
bool isTrackball(const Controller& controller) const;
// Check if a currently non-relevant adjustment can be skipped
bool skipAVSetting() const;
bool skipInputSetting() const;
bool skipDebugSetting() const;
private:
// Structure used for action menu items
struct ActionList {
Event::Type event{Event::NoType};
string action;
string key;
};
// If true, the setting is visible and its value can be changed
bool myAdjustActive{false};
// ID of the currently selected global setting
AdjustSetting myAdjustSetting{AdjustSetting::START_AV_ADJ};
// ID of the currently selected direct hotkey setting (0 if none)
AdjustSetting myAdjustDirect{AdjustSetting::NONE};
// Global Event object
Event myEvent;
// Indicates current overlay object
DialogContainer* myOverlay{nullptr};
// Handler for all keyboard-related events
unique_ptr<PhysicalKeyboardHandler> myPKeyHandler;
// Handler for all joystick addition/removal/mapping
unique_ptr<PhysicalJoystickHandler> myPJoyHandler;
// MouseControl object, which takes care of switching the mouse between
// all possible controller modes
unique_ptr<MouseControl> myMouseControl;
// Indicates the current state of the system (ie, which mode is current)
EventHandlerState myState{EventHandlerState::NONE};
// Indicates whether the virtual joystick emulates 'impossible' directions
bool myAllowAllDirectionsFlag{false};
// Indicates whether or not we're in frying mode
bool myFryingFlag{false};
// Sometimes an extraneous mouse motion event occurs after a video
// state change; we detect when this happens and discard the event
bool mySkipMouseMotion{true};
// Whether the currently enabled console is emulating certain aspects
// of the 7800 (for now, only the switches are notified)
bool myIs7800{false};
// These constants are not meant to be used elsewhere; they are only used
// here to make it easier for the reader to correctly size the list(s)
static constexpr Int32
COMBO_SIZE = 16,
EVENTS_PER_COMBO = 8,
#ifdef PNG_SUPPORT
PNG_SIZE = 3,
#else
PNG_SIZE = 0,
#endif
#ifdef ADAPTABLE_REFRESH_SUPPORT
REFRESH_SIZE = 1,
#else
REFRESH_SIZE = 0,
#endif
EMUL_ACTIONLIST_SIZE = 216 + PNG_SIZE + COMBO_SIZE + REFRESH_SIZE,
MENU_ACTIONLIST_SIZE = 19
;
// The event(s) assigned to each combination event
BSPF::array2D<Event::Type, COMBO_SIZE, EVENTS_PER_COMBO> myComboTable;
// Holds static strings for the remap menu (emulation and menu events)
using EmulActionList = std::array<ActionList, EMUL_ACTIONLIST_SIZE>;
static EmulActionList ourEmulActionList;
using MenuActionList = std::array<ActionList, MENU_ACTIONLIST_SIZE>;
static MenuActionList ourMenuActionList;
// Following constructors and assignment operators not supported
EventHandler() = delete;
EventHandler(const EventHandler&) = delete;
EventHandler(EventHandler&&) = delete;
EventHandler& operator=(const EventHandler&) = delete;
EventHandler& operator=(EventHandler&&) = delete;
};
#endif