stella/src/common/PJoystickHandler.hxx

249 lines
9.6 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-2025 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 PHYSICAL_JOYSTICK_HANDLER_HXX
#define PHYSICAL_JOYSTICK_HANDLER_HXX
#include <map>
class OSystem;
class EventHandler;
class Event;
#include "bspf.hxx"
#include "EventHandlerConstants.hxx"
#include "PhysicalJoystick.hxx"
#include "Variant.hxx"
#include "json_lib.hxx"
using PhysicalJoystickPtr = shared_ptr<PhysicalJoystick>;
/**
This class handles all physical joystick-related operations in Stella.
It is responsible for adding/accessing/removing PhysicalJoystick objects,
and getting/setting events associated with joystick actions (button presses,
axis/hat actions, etc).
Essentially, this class is an extension of the EventHandler class, but
handling only joystick-specific functionality.
@author Stephen Anthony, Thomas Jentzsch
*/
class PhysicalJoystickHandler
{
public:
struct MinStrickInfo
{
string name;
int ID;
PhysicalJoystick::Port port;
explicit MinStrickInfo(string_view _name, int _id, PhysicalJoystick::Port _port)
: name{_name}, ID{_id}, port{_port} {}
};
using MinStrickInfoList = std::vector<MinStrickInfo>;
private:
struct StickInfo
{
// NOTE: For all future linting exercises
// Make sure to *NEVER* use brace initialization or std::move
// on the 'mapping' instance variable; there lay dragons ...
// https://json.nlohmann.me/home/faq/#brace-initialization-yields-arrays
explicit StickInfo(nlohmann::json map, PhysicalJoystickPtr stick = nullptr)
: mapping(map), joy{std::move(stick)} {} // NOLINT
nlohmann::json mapping;
PhysicalJoystickPtr joy;
friend ostream& operator<<(ostream& os, const StickInfo& si) {
os << " joy: " << si.joy << "\n map: " << si.mapping;
return os;
}
};
public:
PhysicalJoystickHandler(OSystem& system, EventHandler& handler, Event& event);
static nlohmann::json convertLegacyMapping(string_view mapping);
/** Return stick ID on success, -1 on failure. */
int add(const PhysicalJoystickPtr& stick);
bool remove(int id);
bool remove(string_view name);
void setPort(string_view name, PhysicalJoystick::Port port);
bool mapStelladaptors(string_view saport, int ID = -1);
bool hasStelladaptors() const;
void setDefaultMapping(Event::Type event, EventMode mode);
/** define mappings for current controllers */
void defineControllerMappings(Controller::Type type, Controller::Jack port,
const Properties& properties,
Controller::Type qtType1 = Controller::Type::Unknown,
Controller::Type qtType2 = Controller::Type::Unknown);
/** enable mappings for emulation mode */
void enableEmulationMappings();
void eraseMapping(Event::Type event, EventMode mode);
void saveMapping();
string getMappingDesc(Event::Type, EventMode mode) const;
/** Bind a physical joystick event to a virtual event/action. */
bool addJoyMapping(Event::Type event, EventMode mode, int stick,
int button, JoyAxis axis, JoyDir adir);
bool addJoyHatMapping(Event::Type event, EventMode mode, int stick,
int button, int hat, JoyHatDir hdir);
/** Handle a physical joystick event. */
void handleAxisEvent(int stick, int axis, int value);
void handleBtnEvent(int stick, int button, bool pressed);
void handleHatEvent(int stick, int hat, int value);
Event::Type eventForAxis(EventMode mode, int stick, JoyAxis axis, JoyDir adir, int button) const {
const PhysicalJoystickPtr& j = joy(stick);
return j->joyMap.get(mode, button, axis, adir);
}
Event::Type eventForButton(EventMode mode, int stick, int button) const {
const PhysicalJoystickPtr& j = joy(stick);
return j->joyMap.get(mode, button);
}
Event::Type eventForHat(EventMode mode, int stick, int hat, JoyHatDir hatDir, int button) const {
const PhysicalJoystickPtr& j = joy(stick);
return j->joyMap.get(mode, button, hat, hatDir);
}
/** Returns a list containing minimal controller info (name, ID, port). */
MinStrickInfoList minStickList() const;
void changeDigitalDeadZone(int direction = +1);
void changeAnalogPaddleDeadZone(int direction = +1);
void changeAnalogPaddleSensitivity(int direction = +1);
void changeAnalogPaddleLinearity(int direction = +1);
void changePaddleDejitterAveraging(int direction = +1);
void changePaddleDejitterReaction(int direction = +1);
void changeDigitalPaddleSensitivity(int direction = +1);
void changeMousePaddleSensitivity(int direction = +1);
void changeMouseTrackballSensitivity(int direction = +1);
void changeDrivingSensitivity(int direction = +1);
private:
using StickDatabase = std::map<string, StickInfo, std::less<>>;
using StickList = std::map<int, PhysicalJoystickPtr>;
OSystem& myOSystem; // NOLINT: we want a reference here
EventHandler& myHandler; // NOLINT: we want a reference here
Event& myEvent; // NOLINT: we want a reference here
// Contains all joysticks that Stella knows about, indexed by name
StickDatabase myDatabase;
// Contains only joysticks that are currently available, indexed by id
StickList mySticks;
// Get joystick corresponding to given id (or nullptr if it doesn't exist)
// Make this inline so it's as fast as possible
PhysicalJoystickPtr joy(int id) const {
const auto& i = mySticks.find(id);
return i != mySticks.cend() ? i->second : nullptr;
}
// Add stick to stick database
void addToDatabase(const PhysicalJoystickPtr& stick);
// Set default mapping for given joystick when no mappings already exist
void setStickDefaultMapping(int stick, Event::Type event, EventMode mode,
bool updateDefaults = false);
friend ostream& operator<<(ostream& os, const PhysicalJoystickHandler& jh);
static constexpr JoyDir convertAxisValue(int value) {
return value == static_cast<int>(JoyDir::NONE)
? JoyDir::NONE
: value > 0 ? JoyDir::POS : JoyDir::NEG;
}
// Handle regular axis events (besides special Stelladaptor handling)
void handleRegularAxisEvent(const PhysicalJoystickPtr& j,
int stick, int axis, int value);
// Structures used for action menu items
struct EventMapping {
Event::Type event{Event::NoType};
int button{0};
JoyAxis axis{JoyAxis::NONE};
JoyDir adir{JoyDir::NONE};
int hat{JOY_CTRL_NONE};
JoyHatDir hdir{JoyHatDir::CENTER};
};
using EventMappingArray = std::vector<EventMapping>;
void setDefaultAction(int stick,
EventMapping map, Event::Type event = Event::NoType,
EventMode mode = EventMode::kEmulationMode,
bool updateDefaults = false);
/** return event mode for given property */
static EventMode getMode(const Properties& properties, PropType propType);
/** return event mode for given controller type */
static EventMode getMode(Controller::Type type);
/** returns the event's controller mode */
static EventMode getEventMode(Event::Type event, EventMode mode);
/** Checks event type. */
static bool isJoystickEvent(Event::Type event);
static bool isPaddleEvent(Event::Type event);
static bool isKeyboardEvent(Event::Type event);
static bool isDrivingEvent(Event::Type event);
static bool isCommonEvent(Event::Type event);
void enableCommonMappings();
void enableMappings(const Event::EventSet& events, EventMode mode);
void enableMapping(Event::Type event, EventMode mode);
private:
EventMode myLeftMode{EventMode::kEmulationMode};
EventMode myRightMode{EventMode::kEmulationMode};
// Additional modes for QuadTari controller
EventMode myLeft2ndMode{EventMode::kEmulationMode};
EventMode myRight2ndMode{EventMode::kEmulationMode};
// Controller menu and common emulation mappings
static EventMappingArray DefaultMenuMapping;
static EventMappingArray DefaultCommonMapping;
// Controller specific mappings
static EventMappingArray DefaultLeftJoystickMapping;
static EventMappingArray DefaultRightJoystickMapping;
static EventMappingArray DefaultLeftPaddlesMapping;
static EventMappingArray DefaultRightPaddlesMapping;
static EventMappingArray DefaultLeftAPaddlesMapping;
static EventMappingArray DefaultLeftBPaddlesMapping;
static EventMappingArray DefaultRightAPaddlesMapping;
static EventMappingArray DefaultRightBPaddlesMapping;
static EventMappingArray DefaultLeftKeyboardMapping;
static EventMappingArray DefaultRightKeyboardMapping;
static EventMappingArray DefaultLeftDrivingMapping;
static EventMappingArray DefaultRightDrivingMapping;
static constexpr int NUM_PORTS = 2;
static constexpr int NUM_SA_AXIS = 2;
static const Event::Type SA_Axis[NUM_PORTS][NUM_SA_AXIS];
};
#endif