diff --git a/src/common/ControllerMap.cxx b/src/common/ControllerMap.cxx new file mode 100644 index 000000000..fca8511d4 --- /dev/null +++ b/src/common/ControllerMap.cxx @@ -0,0 +1,223 @@ +//============================================================================ +// +// 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-2019 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. +//============================================================================ + +#include "ControllerMap.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +ControllerMap::ControllerMap(void) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ControllerMap::add(const Event::Type event, const ControllerMapping& mapping) +{ + myMap[mapping] = event; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ControllerMap::add(const Event::Type event, const EventMode mode, const int stick, const int button, + const JoyAxis axis, const JoyDir adir, const int hat, const JoyHat hdir) +{ + add(event, ControllerMapping(mode, stick, button, axis, adir, hat, hdir)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ControllerMap::erase(const ControllerMapping& mapping) +{ + myMap.erase(mapping); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ControllerMap::erase(const EventMode mode, const int stick, const int button, + const JoyAxis axis, const JoyDir adir, const int hat, const JoyHat hdir) +{ + erase(ControllerMapping(mode, stick, button, axis, adir, hat, hdir)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Event::Type ControllerMap::get(const ControllerMapping& mapping) const +{ + auto find = myMap.find(mapping); + if (find != myMap.end()) + return find->second; + + return Event::Type::NoType; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Event::Type ControllerMap::get(const EventMode mode, const int stick, const int button, + const JoyAxis axis, const JoyDir adir, const int hat, const JoyHat hdir) const +{ + return get(ControllerMapping(mode, stick, button, axis, adir, hat, hdir)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool ControllerMap::check(const ControllerMapping & mapping) const +{ + auto find = myMap.find(mapping); + + return (find != myMap.end()); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool ControllerMap::check(const EventMode mode, const int stick, const int button, + const JoyAxis axis, const JoyDir adir, const int hat, const JoyHat hdir) const +{ + return check(ControllerMapping(mode, stick, button, axis, adir, hat, hdir)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string ControllerMap::getDesc(const Event::Type event, const ControllerMapping& mapping) const +{ + ostringstream buf; + + buf << "J" << mapping.stick; + + // button description + if (mapping.button != CTRL_NONE) + buf << "/B" << mapping.button; + + // axis description + if (int(mapping.axis) != CTRL_NONE) + { + buf << "/A" << mapping.hat; + switch (mapping.axis) + { + case JoyAxis::X: buf << "X"; break; + case JoyAxis::Y: buf << "Y"; break; + default: break; + } + + if (Event::isAnalog(event)) + buf << "+|-"; + else if (mapping.adir == JoyDir::NEG) + buf << "-"; + else + buf << "+"; + } + + // hat description + if (mapping.hat != CTRL_NONE) + { + buf << "/H" << mapping.hat; + switch (mapping.hdir) + { + case JoyHat::UP: buf << "/up"; break; + case JoyHat::DOWN: buf << "/down"; break; + case JoyHat::LEFT: buf << "/left"; break; + case JoyHat::RIGHT: buf << "/right"; break; + default: break; + } + } + + + return buf.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string ControllerMap::getDesc(const Event::Type event, const EventMode mode, const int stick, const int button, + const JoyAxis axis, const JoyDir adir, const int hat, const JoyHat hdir) const +{ + return getDesc(event, ControllerMapping(mode, stick, button, axis, adir, hat, hdir)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string ControllerMap::getEventMappingDesc(const Event::Type event, const EventMode mode) const +{ + ostringstream buf; + + for (auto item : myMap) + { + if (item.second == event && item.first.mode == mode) + { + if (buf.str() != "") + buf << ", "; + buf << getDesc(event, item.first); + } + } + return buf.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +ControllerMap::ControllerMappingArray ControllerMap::getEventMapping(const Event::Type event, const EventMode mode) const +{ + ControllerMappingArray map; + + for (auto item : myMap) + if (item.second == event && item.first.mode == mode) + map.push_back(item.first); + + return map; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string ControllerMap::saveMapping(const EventMode mode) const +{ + ostringstream buf; + + for (auto item : myMap) + { + if (item.first.mode == mode) + { + if (buf.str() != "") + buf << "|"; + buf << item.second << ":" << item.first.stick << "," << item.first.button << "," + << int(item.first.axis) << "," << int(item.first.adir) << "," << item.first.hat << "," << int(item.first.hdir); + } + } + return buf.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int ControllerMap::loadMapping(string& list, const EventMode mode) +{ + // Since istringstream swallows whitespace, we have to make the + // delimiters be spaces + std::replace(list.begin(), list.end(), '|', ' '); + std::replace(list.begin(), list.end(), ':', ' '); + std::replace(list.begin(), list.end(), ',', ' '); + istringstream buf(list); + int event, stick, button, axis, adir, hat, hdir, i = 0; + + while (buf >> event && buf >> stick && buf >> button + && buf >> axis && buf >> adir && buf >> hat && buf >> hdir && ++i) + add(Event::Type(event), EventMode(mode), stick, button, JoyAxis(axis), JoyDir(adir), hat, JoyHat(hdir)); + + return i; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ControllerMap::eraseMode(const EventMode mode) +{ + for (auto item = myMap.begin(); item != myMap.end();) + if (item->first.mode == mode) { + auto _item = item++; + erase(_item->first); + } + else item++; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void ControllerMap::eraseEvent(const Event::Type event, const EventMode mode) +{ + for (auto item = myMap.begin(); item != myMap.end();) + if (item->second == event && item->first.mode == mode) { + auto _item = item++; + erase(_item->first); + } + else item++; +} diff --git a/src/common/ControllerMap.hxx b/src/common/ControllerMap.hxx new file mode 100644 index 000000000..1afe6011a --- /dev/null +++ b/src/common/ControllerMap.hxx @@ -0,0 +1,129 @@ +//============================================================================ +// +// 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-2019 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 CONTROLLERMAP_HXX +#define CONTROLLERMAP_HXX + +#include +#include "Event.hxx" +#include "EventHandlerConstants.hxx" + +/** + This class handles controller mappings in Stella. + + @author Thomas Jentzsch +*/ +class ControllerMap +{ + public: + + struct ControllerMapping + { + EventMode mode; + int stick; // stick number + int button; // button number + JoyAxis axis; // horizontal/vertical + JoyDir adir; // axis direction (neg/pos) + int hat; // hat number + JoyHat hdir; // hat direction (left/right/up/down) + + ControllerMapping() + : mode(EventMode(0)), stick(0), button(0), + axis(JoyAxis(0)), adir(JoyDir(0)), hat(0), hdir(JoyHat(0)) { } + ControllerMapping(const ControllerMapping& m) + : mode(m.mode), stick(m.stick), button(m.button), + axis(m.axis), adir(m.adir), hat(m.hat), hdir(m.hdir) { } + explicit ControllerMapping(EventMode c_mode, int c_stick, int c_button, + JoyAxis c_axis, JoyDir c_adir, int c_hat, JoyHat c_hdir) + : mode(c_mode), stick(c_stick), button(c_button), + axis(c_axis), adir(c_adir), hat(c_hat), hdir(c_hdir) { } + + bool operator==(const ControllerMapping& other) const + { + return (mode == other.mode + && stick == other.stick + && button == other.button + && axis == other.axis + && adir == other.adir + && hat == other.hat + && hdir == other.hdir + ); + } + }; + using ControllerMappingArray = std::vector; + + ControllerMap(); + virtual ~ControllerMap() = default; + + /** Add new mapping for given event */ + void add(const Event::Type event, const ControllerMapping& mapping); + void add(const Event::Type event, const EventMode mode, const int stick, const int button, + const JoyAxis axis, const JoyDir adir, const int hat, const JoyHat hdir); + + /** Erase mapping */ + void erase(const ControllerMapping& mapping); + void erase(const EventMode mode, const int stick, const int button, + const JoyAxis axis, const JoyDir adir, const int hat, const JoyHat hdir); + + /** Get event for mapping */ + Event::Type get(const ControllerMapping& mapping) const; + Event::Type get(const EventMode mode, const int stick, const int button, + const JoyAxis axis, const JoyDir adir, const int hat, const JoyHat hdir) const; + + /** Check if a mapping exists */ + bool check(const ControllerMapping& mapping) const; + bool check(const EventMode mode, const int stick, const int button, + const JoyAxis axis, const JoyDir adir, const int hat, const JoyHat hdir) const; + + /** Get mapping description */ + string getDesc(const Event::Type event, const ControllerMapping& mapping) const; + string getDesc(const Event::Type event, const EventMode mode, const int stick, const int button, + const JoyAxis axis, const JoyDir adir, const int hat, const JoyHat hdir) const; + + /** Get the mapping description(s) for given event and mode */ + string getEventMappingDesc(const Event::Type event, const EventMode mode) const; + + ControllerMappingArray getEventMapping(const Event::Type event, const EventMode mode) const; + + string saveMapping(const EventMode mode) const; + int loadMapping(string& list, const EventMode mode); + + /** Erase all mappings for given mode */ + void eraseMode(const EventMode mode); + /** Erase given event's mapping for given mode */ + void eraseEvent(const Event::Type event, const EventMode mode); + /** clear all mappings for a modes */ + // void clear() { myMap.clear(); } + size_t size() { return myMap.size(); } + + private: + struct ControllerHash { + size_t operator()(const ControllerMapping& m)const { + return std::hash()((uInt64(m.mode)) // 3 bit + ^ ((uInt64(m.stick)) << 3) // 2 bits + ^ ((uInt64(m.button)) << 5) // 2 bits + ^ ((uInt64(m.axis)) << 7) // 1 bit + ^ ((uInt64(m.adir)) << 8) // 1 bit + ^ ((uInt64(m.hat)) << 9) // 1 bit + ^ ((uInt64(m.hdir)) << 10)); // 2 bits + } + }; + + std::unordered_map myMap; +}; + +#endif diff --git a/src/common/KeyMap.cxx b/src/common/KeyMap.cxx index 4f5515828..551b1a86e 100644 --- a/src/common/KeyMap.cxx +++ b/src/common/KeyMap.cxx @@ -161,9 +161,9 @@ string KeyMap::getEventMappingDesc(const Event::Type event, const int mode) cons } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -std::vector KeyMap::getEventMapping(const Event::Type event, const int mode) const +KeyMap::MappingArray KeyMap::getEventMapping(const Event::Type event, const int mode) const { - std::vector map; + MappingArray map; for (auto item : myMap) if (item.second == event && item.first.mode == mode) diff --git a/src/common/KeyMap.hxx b/src/common/KeyMap.hxx index 5b0d82ef5..f69d92b1e 100644 --- a/src/common/KeyMap.hxx +++ b/src/common/KeyMap.hxx @@ -55,6 +55,7 @@ class KeyMap ); } }; + using MappingArray = std::vector; KeyMap(); virtual ~KeyMap() = default; @@ -82,7 +83,7 @@ class KeyMap /** Get the mapping description(s) for given event and mode */ string getEventMappingDesc(const Event::Type event, const int mode) const; - std::vector getEventMapping(const Event::Type event, const int mode) const; + MappingArray getEventMapping(const Event::Type event, const int mode) const; string saveMapping(const int mode) const; int loadMapping(string& list, const int mode); diff --git a/src/common/PJoystickHandler.cxx b/src/common/PJoystickHandler.cxx index 7af501c8f..fb8f1e18c 100644 --- a/src/common/PJoystickHandler.cxx +++ b/src/common/PJoystickHandler.cxx @@ -426,7 +426,7 @@ string PhysicalJoystickHandler::getMappingDesc(Event::Type event, EventMode mode dir = NUM_JOY_DIRS; // Immediately exit the inner loop after this iteration buf << "/+|-"; } - else if(dir == 0) + else if(dir == int(JoyDir::NEG)) buf << "/-"; else buf << "/+"; @@ -521,6 +521,15 @@ bool PhysicalJoystickHandler::addHatMapping(Event::Type event, EventMode mode, return false; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PhysicalJoystickHandler::addMapping(Event::Type event, EventMode mode, int stick, + int button, JoyAxis axis, JoyDir adir, int hat, JoyHat hdir) +{ + myControllerMap.add(event, mode, stick, button, axis, adir, hat, hdir); + +} + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void PhysicalJoystickHandler::handleAxisEvent(int stick, int axis, int value) { diff --git a/src/common/PJoystickHandler.hxx b/src/common/PJoystickHandler.hxx index a300fdddf..b8bc447a6 100644 --- a/src/common/PJoystickHandler.hxx +++ b/src/common/PJoystickHandler.hxx @@ -28,6 +28,7 @@ class Event; #include "EventHandlerConstants.hxx" #include "PhysicalJoystick.hxx" #include "Variant.hxx" +#include "ControllerMap.hxx" using PhysicalJoystickPtr = shared_ptr; @@ -78,6 +79,12 @@ class PhysicalJoystickHandler bool addBtnMapping(Event::Type event, EventMode mode, int stick, int button); bool addHatMapping(Event::Type event, EventMode mode, int stick, int hat, JoyHat value); + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + void addMapping(Event::Type event, EventMode mode, int stick, + int button, JoyAxis axis, JoyDir adir, int hat, JoyHat hdir); + + + /** Handle a physical joystick event. */ void handleAxisEvent(int stick, int axis, int value); void handleBtnEvent(int stick, int button, bool pressed); @@ -133,6 +140,9 @@ class PhysicalJoystickHandler static const Event::Type SA_Axis[NUM_PORTS][NUM_JOY_AXIS]; static const Event::Type SA_Button[NUM_PORTS][NUM_JOY_BTN]; static const Event::Type SA_Key[NUM_PORTS][NUM_KEY_BTN]; + + // Hashmap of controller events + ControllerMap myControllerMap; }; #endif diff --git a/src/common/PKeyboardHandler.cxx b/src/common/PKeyboardHandler.cxx index d795f5589..8afcfdb0d 100644 --- a/src/common/PKeyboardHandler.cxx +++ b/src/common/PKeyboardHandler.cxx @@ -236,7 +236,7 @@ void PhysicalKeyboardHandler::enableMappings(const EventSet events, EventMode mo void PhysicalKeyboardHandler::enableMapping(const Event::Type event, EventMode mode) { // copy from controller mode into emulation mode - std::vector mappings = myKeyMap.getEventMapping(event, mode); + KeyMap::MappingArray mappings = myKeyMap.getEventMapping(event, mode); for (const auto& mapping : mappings) myKeyMap.add(event, kEmulationMode, mapping.key, mapping.mod); diff --git a/src/common/module.mk b/src/common/module.mk index dc24e6886..9c9f5c08a 100644 --- a/src/common/module.mk +++ b/src/common/module.mk @@ -6,6 +6,7 @@ MODULE_OBJS := \ src/common/FBSurfaceSDL2.o \ src/common/FrameBufferSDL2.o \ src/common/FSNodeZIP.o \ + src/common/ControllerMap.o \ src/common/KeyMap.o \ src/common/Logger.o \ src/common/main.o \ diff --git a/src/emucore/EventHandlerConstants.hxx b/src/emucore/EventHandlerConstants.hxx index 08ea4a96b..d0f7edaeb 100644 --- a/src/emucore/EventHandlerConstants.hxx +++ b/src/emucore/EventHandlerConstants.hxx @@ -38,6 +38,8 @@ enum class MouseButton { NONE }; +static constexpr int CTRL_NONE = -1; + enum class JoyAxis { X = 0, Y = 1, diff --git a/src/windows/Stella.vcxproj b/src/windows/Stella.vcxproj index 9ef2b4b25..a57bcf2c5 100644 --- a/src/windows/Stella.vcxproj +++ b/src/windows/Stella.vcxproj @@ -374,8 +374,9 @@ - + + @@ -1073,6 +1074,7 @@ + diff --git a/src/windows/Stella.vcxproj.filters b/src/windows/Stella.vcxproj.filters index 966e31a7f..bde453361 100644 --- a/src/windows/Stella.vcxproj.filters +++ b/src/windows/Stella.vcxproj.filters @@ -978,6 +978,9 @@ Source Files\gui + + Source Files + @@ -2000,6 +2003,9 @@ Header Files + + Header Files + @@ -2012,4 +2018,4 @@ Resource Files - + \ No newline at end of file