Merge remote-tracking branch 'remotes/origin/feature/controller-mapping'

This commit is contained in:
Thomas Jentzsch 2019-08-05 10:01:43 +02:00
commit 38088619ee
36 changed files with 1286 additions and 756 deletions

248
src/common/JoyMap.cxx Normal file
View File

@ -0,0 +1,248 @@
//============================================================================
//
// 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 "JoyMap.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
JoyMap::JoyMap(void)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::add(const Event::Type event, const JoyMapping& mapping)
{
myMap[mapping] = event;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::add(const Event::Type event, const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir,
const int hat, const JoyHat hdir)
{
add(event, JoyMapping(mode, button, axis, adir, hat, hdir));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::add(const Event::Type event, const EventMode mode, const int button,
const int hat, const JoyHat hdir)
{
add(event, JoyMapping(mode, button, hat, hdir));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::erase(const JoyMapping& mapping)
{
myMap.erase(mapping);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::erase(const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir)
{
erase(JoyMapping(mode, button, axis, adir));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::erase(const EventMode mode, const int button,
const int hat, const JoyHat hdir)
{
erase(JoyMapping(mode, button, hat, hdir));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event::Type JoyMap::get(const JoyMapping& mapping) const
{
auto find = myMap.find(mapping);
if (find != myMap.end())
return find->second;
// try without button as modifier
JoyMapping m = mapping;
m.button = JOY_CTRL_NONE;
find = myMap.find(m);
if (find != myMap.end())
return find->second;
return Event::Type::NoType;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event::Type JoyMap::get(const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir) const
{
return get(JoyMapping(mode, button, axis, adir));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event::Type JoyMap::get(const EventMode mode, const int button,
const int hat, const JoyHat hdir) const
{
return get(JoyMapping(mode, button, hat, hdir));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool JoyMap::check(const JoyMapping & mapping) const
{
auto find = myMap.find(mapping);
return (find != myMap.end());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool JoyMap::check(const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir,
const int hat, const JoyHat hdir) const
{
return check(JoyMapping(mode, button, axis, adir, hat, hdir));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string JoyMap::getDesc(const Event::Type event, const JoyMapping& mapping) const
{
ostringstream buf;
// button description
if (mapping.button != JOY_CTRL_NONE)
buf << "/B" << mapping.button;
// axis description
if (mapping.axis != JoyAxis::NONE)
{
buf << "/A";
switch (mapping.axis)
{
case JoyAxis::X: buf << "X"; break;
case JoyAxis::Y: buf << "Y"; break;
case JoyAxis::Z: buf << "Z"; break;
default: buf << int(mapping.axis); break;
}
if (Event::isAnalog(event))
buf << "+|-";
else if (mapping.adir == JoyDir::NEG)
buf << "-";
else
buf << "+";
}
// hat description
if (mapping.hat != JOY_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 JoyMap::getEventMappingDesc(int stick, 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 << "J" << stick << getDesc(event, item.first);
}
}
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
JoyMap::JoyMappingArray JoyMap::getEventMapping(const Event::Type event, const EventMode mode) const
{
JoyMappingArray map;
for (auto item : myMap)
if (item.second == event && item.first.mode == mode)
map.push_back(item.first);
return map;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string JoyMap::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.button << ","
<< int(item.first.axis) << "," << int(item.first.adir) << ","
<< item.first.hat << "," << int(item.first.hdir);
}
}
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int JoyMap::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, button, axis, adir, hat, hdir, i = 0;
while (buf >> event && buf >> button
&& buf >> axis && buf >> adir
&& buf >> hat && buf >> hdir && ++i)
add(Event::Type(event), EventMode(mode), button, JoyAxis(axis), JoyDir(adir), hat, JoyHat(hdir));
return i;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JoyMap::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 JoyMap::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++;
}

147
src/common/JoyMap.hxx Normal file
View File

@ -0,0 +1,147 @@
//============================================================================
//
// 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 <unordered_map>
#include "Event.hxx"
#include "EventHandlerConstants.hxx"
/**
This class handles controller mappings in Stella.
@author Thomas Jentzsch
*/
class JoyMap
{
public:
struct JoyMapping
{
EventMode mode;
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)
JoyMapping()
: mode(EventMode(0)), button(0),
axis(JoyAxis(0)), adir(JoyDir(0)),
hat(0), hdir(JoyHat(0)) { }
JoyMapping(const JoyMapping& m)
: mode(m.mode), button(m.button),
axis(m.axis), adir(m.adir),
hat(m.hat), hdir(m.hdir) { }
explicit JoyMapping(EventMode c_mode, int c_button,
JoyAxis c_axis, JoyDir c_adir,
int c_hat, JoyHat c_hdir)
: mode(c_mode), button(c_button),
axis(c_axis), adir(c_adir),
hat(c_hat), hdir(c_hdir) { }
explicit JoyMapping(EventMode c_mode, int c_button,
JoyAxis c_axis, JoyDir c_adir)
: mode(c_mode), button(c_button),
axis(c_axis), adir(c_adir),
hat(JOY_CTRL_NONE), hdir(JoyHat::CENTER) { }
explicit JoyMapping(EventMode c_mode, int c_button,
int c_hat, JoyHat c_hdir)
: mode(c_mode), button(c_button),
axis(JoyAxis::NONE), adir(JoyDir::NONE),
hat(c_hat), hdir(c_hdir) { }
bool operator==(const JoyMapping& other) const
{
return (mode == other.mode
&& button == other.button
&& axis == other.axis
&& adir == other.adir
&& hat == other.hat
&& hdir == other.hdir
);
}
};
using JoyMappingArray = std::vector<JoyMapping>;
JoyMap();
virtual ~JoyMap() = default;
/** Add new mapping for given event */
void add(const Event::Type event, const JoyMapping& mapping);
void add(const Event::Type event, const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir,
const int hat = JOY_CTRL_NONE, const JoyHat hdir = JoyHat::CENTER);
void add(const Event::Type event, const EventMode mode, const int button,
const int hat, const JoyHat hdir);
/** Erase mapping */
void erase(const JoyMapping& mapping);
void erase(const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir);
void erase(const EventMode mode, const int button,
const int hat, const JoyHat hdir);
/** Get event for mapping */
Event::Type get(const JoyMapping& mapping) const;
Event::Type get(const EventMode mode, const int button,
const JoyAxis axis = JoyAxis::NONE, const JoyDir adir = JoyDir::NONE) const;
Event::Type get(const EventMode mode, const int button,
const int hat, const JoyHat hdir) const;
/** Check if a mapping exists */
bool check(const JoyMapping& mapping) const;
bool check(const EventMode mode, const int button,
const JoyAxis axis, const JoyDir adir,
const int hat = JOY_CTRL_NONE, const JoyHat hdir = JoyHat::CENTER) const;
/** Get mapping description */
string getEventMappingDesc(int stick, const Event::Type event, const EventMode mode) const;
JoyMappingArray 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:
string getDesc(const Event::Type event, const JoyMapping& mapping) const;
struct JoyHash {
size_t operator()(const JoyMapping& m)const {
return std::hash<uInt64>()((uInt64(m.mode)) // 3 bit
^ ((uInt64(m.button)) << 2) // 2 bits
^ ((uInt64(m.axis)) << 4) // 1 bit
^ ((uInt64(m.adir)) << 5) // 1 bit
^ ((uInt64(m.hat)) << 6) // 1 bit
^ ((uInt64(m.hdir)) << 7) // 2 bits
);
}
};
std::unordered_map<JoyMapping, Event::Type, JoyHash> myMap;
};
#endif

View File

@ -161,9 +161,9 @@ string KeyMap::getEventMappingDesc(const Event::Type event, const int mode) cons
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::vector<KeyMap::Mapping> KeyMap::getEventMapping(const Event::Type event, const int mode) const
KeyMap::MappingArray KeyMap::getEventMapping(const Event::Type event, const int mode) const
{
std::vector<KeyMap::Mapping> map;
MappingArray map;
for (auto item : myMap)
if (item.second == event && item.first.mode == mode)

View File

@ -55,6 +55,7 @@ class KeyMap
);
}
};
using MappingArray = std::vector<Mapping>;
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<Mapping> 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);

View File

@ -27,6 +27,8 @@
#include "DialogContainer.hxx"
#endif
static constexpr char CTRL_DELIM = '^';
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::PhysicalJoystickHandler(
OSystem& system, EventHandler& handler, Event& event)
@ -40,14 +42,14 @@ PhysicalJoystickHandler::PhysicalJoystickHandler(
string joymap, joyname;
// First compare if event list version has changed, and disregard the entire mapping if true
getline(buf, joymap, '^'); // event list size, ignore
getline(buf, joymap, CTRL_DELIM); // event list size, ignore
if(version == Event::VERSION)
{
// Otherwise, put each joystick mapping entry into the database
while(getline(buf, joymap, '^'))
while(getline(buf, joymap, CTRL_DELIM))
{
istringstream namebuf(joymap);
getline(namebuf, joyname, '|');
getline(namebuf, joyname, PhysicalJoystick::MODE_DELIM);
if(joyname.length() != 0)
myDatabase.emplace(joyname, StickInfo(joymap));
}
@ -120,21 +122,8 @@ int PhysicalJoystickHandler::add(PhysicalJoystickPtr stick)
{
StickInfo info("", stick);
myDatabase.emplace(stick->name, info);
setStickDefaultMapping(stick->ID, Event::NoType, kEmulationMode);
setStickDefaultMapping(stick->ID, Event::NoType, kMenuMode);
}
// We're potentially swapping out an input device behind the back of
// the Event system, so we make sure all Stelladaptor-generated events
// are reset
for(int i = 0; i < NUM_PORTS; ++i)
{
for(int j = 0; j < NUM_JOY_AXIS; ++j)
myEvent.set(SA_Axis[i][j], 0);
for(int j = 0; j < NUM_JOY_BTN; ++j)
myEvent.set(SA_Button[i][j], 0);
for(int j = 0; j < NUM_KEY_BTN; ++j)
myEvent.set(SA_Key[i][j], 0);
setStickDefaultMapping(stick->ID, Event::NoType, kEmulationMode, true);
setStickDefaultMapping(stick->ID, Event::NoType, kMenuMode, true);
}
return stick->ID;
@ -205,6 +194,12 @@ void PhysicalJoystickHandler::mapStelladaptors(const string& saport)
for(auto& stick: mySticks)
{
// remove previously added emulated ports
size_t pos = stick.second->name.find(" (emulates ");
if (pos != std::string::npos)
stick.second->name.erase(pos);
if(BSPF::startsWithIgnoreCase(stick.second->name, "Stelladaptor"))
{
if(saOrder[saCount] == 1)
@ -237,6 +232,83 @@ void PhysicalJoystickHandler::mapStelladaptors(const string& saport)
myOSystem.settings().setValue("saport", saport);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Depending on parameters, this method does the following:
// 1. update all events with default (event == Event::NoType, updateDefault == true)
// 2. reset all events to default (event == Event::NoType, updateDefault == false)
// 3. reset one event to default (event != Event::NoType)
void PhysicalJoystickHandler::setDefaultAction(const PhysicalJoystickPtr& j,
EventMapping map, Event::Type event,
EventMode mode, bool updateDefaults)
{
// If event is 'NoType', erase and reset all mappings
// Otherwise, only reset the given event
bool eraseAll = !updateDefaults && (event == Event::NoType);
if (updateDefaults)
{
// if there is no existing mapping for the event or
// the default mapping for the event is unused, set default key for event
if (j->joyMap.getEventMapping(map.event, mode).size() == 0 ||
!j->joyMap.check(mode, map.button, map.axis, map.adir, map.hat, map.hdir))
{
j->joyMap.add(map.event, mode, map.button, map.axis, map.adir, map.hat, map.hdir);
}
}
else if (eraseAll || map.event == event)
{
// TODO: allow for multiple defaults
//j->joyMap.eraseEvent(map.event, mode);
j->joyMap.add(map.event, mode, map.button, map.axis, map.adir, map.hat, map.hdir);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::setStickDefaultMapping(int stick, Event::Type event,
EventMode mode, bool updateDefaults)
{
const PhysicalJoystickPtr j = joy(stick);
if(j)
{
switch (mode)
{
case kEmulationMode:
if ((stick % 2) == 0) // even sticks
{
// put all controller events into their own mode's mappings
for (const auto& item : DefaultLeftJoystickMapping)
setDefaultAction(j, item, event, kJoystickMode, updateDefaults);
for (const auto& item : DefaultLeftPaddlesMapping)
setDefaultAction(j, item, event, kPaddlesMode, updateDefaults);
for (const auto& item : DefaultLeftKeypadMapping)
setDefaultAction(j, item, event, kKeypadMode, updateDefaults);
}
else // odd sticks
{
// put all controller events into their own mode's mappings
for (const auto& item : DefaultRightJoystickMapping)
setDefaultAction(j, item, event, kJoystickMode, updateDefaults);
for (const auto& item : DefaultRightPaddlesMapping)
setDefaultAction(j, item, event, kPaddlesMode, updateDefaults);
for (const auto& item : DefaultRightKeypadMapping)
setDefaultAction(j, item, event, kKeypadMode, updateDefaults);
}
break;
case kMenuMode:
for (const auto& item : DefaultMenuMapping)
setDefaultAction(j, item, event, kMenuMode, updateDefaults);
break;
default:
break;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::setDefaultMapping(Event::Type event, EventMode mode)
{
@ -246,113 +318,161 @@ void PhysicalJoystickHandler::setDefaultMapping(Event::Type event, EventMode mod
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::setStickDefaultMapping(int stick,
Event::Type event, EventMode mode)
void PhysicalJoystickHandler::defineControllerMappings(const string& controllerName, Controller::Jack port)
{
bool eraseAll = (event == Event::NoType);
auto setDefaultAxis = [&](int a_stick, JoyAxis a_axis, JoyDir a_value, Event::Type a_event)
// determine controller events to use
if ((controllerName == "KEYBOARD") || (controllerName == "KEYPAD"))
{
if(eraseAll || a_event == event)
myHandler.addJoyAxisMapping(a_event, mode, a_stick, int(a_axis), int(a_value), false);
};
auto setDefaultBtn = [&](int b_stick, int b_button, Event::Type b_event)
{
if(eraseAll || b_event == event)
myHandler.addJoyButtonMapping(b_event, mode, b_stick, b_button, false);
};
auto setDefaultHat = [&](int h_stick, int h_hat, JoyHat h_dir, Event::Type h_event)
{
if(eraseAll || h_event == event)
myHandler.addJoyHatMapping(h_event, mode, h_stick, h_hat, h_dir, false);
};
switch(mode)
{
case kEmulationMode: // Default emulation events
switch (stick)
{
case 0:
case 2:
// Left joystick left/right directions (assume joystick zero or two)
setDefaultAxis(stick, JoyAxis::X, JoyDir::NEG, Event::JoystickZeroLeft);
setDefaultAxis(stick, JoyAxis::X, JoyDir::POS, Event::JoystickZeroRight);
// Left joystick up/down directions (assume joystick zero or two)
setDefaultAxis(stick, JoyAxis::Y, JoyDir::NEG, Event::JoystickZeroUp);
setDefaultAxis(stick, JoyAxis::Y, JoyDir::POS, Event::JoystickZeroDown);
// Left joystick (assume joystick zero or two, buttons zero..two)
setDefaultBtn(stick, 0, Event::JoystickZeroFire);
setDefaultBtn(stick, 1, Event::JoystickZeroFire5);
setDefaultBtn(stick, 2, Event::JoystickZeroFire9);
// Left joystick left/right directions (assume joystick zero or two and hat 0)
setDefaultHat(stick, 0, JoyHat::LEFT, Event::JoystickZeroLeft);
setDefaultHat(stick, 0, JoyHat::RIGHT, Event::JoystickZeroRight);
// Left joystick up/down directions (assume joystick zero or two and hat 0)
setDefaultHat(stick, 0, JoyHat::UP, Event::JoystickZeroUp);
setDefaultHat(stick, 0, JoyHat::DOWN, Event::JoystickZeroDown);
#if defined(RETRON77)
// Left joystick (assume joystick zero or two, buttons two..four)
setDefaultBtn(stick, 2, Event::CmdMenuMode);
setDefaultBtn(stick, 3, Event::OptionsMenuMode);
setDefaultBtn(stick, 4, Event::ExitMode);
#endif
break;
case 1:
case 3:
// Right joystick left/right directions (assume joystick one or three)
setDefaultAxis(stick, JoyAxis::X, JoyDir::NEG, Event::JoystickOneLeft);
setDefaultAxis(stick, JoyAxis::X, JoyDir::POS, Event::JoystickOneRight);
// Right joystick left/right directions (assume joystick one or three)
setDefaultAxis(stick, JoyAxis::Y, JoyDir::NEG, Event::JoystickOneUp);
setDefaultAxis(stick, JoyAxis::Y, JoyDir::POS, Event::JoystickOneDown);
// Right joystick (assume joystick one or three, buttons zero..two)
setDefaultBtn(stick, 0, Event::JoystickOneFire);
setDefaultBtn(stick, 1, Event::JoystickOneFire5);
setDefaultBtn(stick, 2, Event::JoystickOneFire9);
// Right joystick left/right directions (assume joystick one or three and hat 0)
setDefaultHat(stick, 0, JoyHat::LEFT, Event::JoystickOneLeft);
setDefaultHat(stick, 0, JoyHat::RIGHT, Event::JoystickOneRight);
// Right joystick up/down directions (assume joystick one or three and hat 0)
setDefaultHat(stick, 0, JoyHat::UP, Event::JoystickOneUp);
setDefaultHat(stick, 0, JoyHat::DOWN, Event::JoystickOneDown);
#if defined(RETRON77)
// Right joystick (assume joystick one or three, buttons two..four)
setDefaultBtn(stick, 2, Event::CmdMenuMode);
setDefaultBtn(stick, 3, Event::OptionsMenuMode);
setDefaultBtn(stick, 4, Event::ExitMode);
setDefaultBtn(stick, 5, Event::RewindPause);
setDefaultBtn(stick, 7, Event::ConsoleSelect);
setDefaultBtn(stick, 8, Event::ConsoleReset);
#endif
break;
default:
break;
if (port == Controller::Jack::Left)
myLeftMode = kKeypadMode;
else
myRightMode = kKeypadMode;
}
else if (BSPF::startsWithIgnoreCase(controllerName, "PADDLES"))
{
if (port == Controller::Jack::Left)
myLeftMode = kPaddlesMode;
else
myRightMode = kPaddlesMode;
}
else if (controllerName == "CM")
{
myLeftMode = myRightMode = kCompuMateMode;
}
else
{
if (port == Controller::Jack::Left)
myLeftMode = kJoystickMode;
else
myRightMode = kJoystickMode;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::enableEmulationMappings()
{
for (auto& stick : mySticks)
{
const PhysicalJoystickPtr j = stick.second;
// start from scratch and enable common mappings
j->joyMap.eraseMode(kEmulationMode);
}
enableCommonMappings();
// enable right mode first, so that in case of mapping clashes the left controller has preference
switch (myRightMode)
{
case kPaddlesMode:
enableMappings(RightPaddlesEvents, kPaddlesMode);
break;
case kMenuMode: // Default menu/UI events
setDefaultAxis( stick, JoyAxis::X, JoyDir::NEG, Event::UINavPrev );
setDefaultAxis( stick, JoyAxis::X, JoyDir::POS, Event::UINavNext );
setDefaultAxis( stick, JoyAxis::Y, JoyDir::NEG, Event::UIUp );
setDefaultAxis( stick, JoyAxis::Y, JoyDir::POS, Event::UIDown );
// joystick (assume buttons zero..three)
setDefaultBtn( stick, 0, Event::UISelect );
setDefaultBtn( stick, 1, Event::UIOK );
setDefaultBtn( stick, 2, Event::UITabNext);
setDefaultBtn( stick, 3, Event::UITabPrev );
setDefaultBtn( stick, 5, Event::UICancel);
setDefaultHat( stick, 0, JoyHat::LEFT, Event::UINavPrev );
setDefaultHat( stick, 0, JoyHat::RIGHT, Event::UINavNext );
setDefaultHat( stick, 0, JoyHat::UP, Event::UIUp );
setDefaultHat( stick, 0, JoyHat::DOWN, Event::UIDown );
case kKeypadMode:
enableMappings(RightKeypadEvents, kKeypadMode);
break;
default:
enableMappings(RightJoystickEvents, kJoystickMode);
break;
}
switch (myLeftMode)
{
case kPaddlesMode:
enableMappings(LeftPaddlesEvents, kPaddlesMode);
break;
case kKeypadMode:
enableMappings(LeftKeypadEvents, kKeypadMode);
break;
default:
enableMappings(LeftJoystickEvents, kJoystickMode);
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::enableCommonMappings()
{
for (int i = Event::NoType + 1; i < Event::LastType; i++)
{
Event::Type event = static_cast<Event::Type>(i);
if (isCommonEvent(event))
enableMapping(event, kCommonMode);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::enableMappings(const Event::EventSet events, EventMode mode)
{
for (const auto& event : events)
enableMapping(event, mode);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::enableMapping(const Event::Type event, EventMode mode)
{
// copy from controller mode into emulation mode
for (auto& stick : mySticks)
{
const PhysicalJoystickPtr j = stick.second;
JoyMap::JoyMappingArray joyMappings = j->joyMap.getEventMapping(event, mode);
for (const auto& mapping : joyMappings)
j->joyMap.add(event, kEmulationMode, mapping.button, mapping.axis, mapping.adir);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventMode PhysicalJoystickHandler::getEventMode(const Event::Type event, const EventMode mode) const
{
if (mode == kEmulationMode)
{
if (isJoystickEvent(event))
return kJoystickMode;
if (isPaddleEvent(event))
return kPaddlesMode;
if (isKeypadEvent(event))
return kKeypadMode;
if (isCommonEvent(event))
return kCommonMode;
}
return mode;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::isJoystickEvent(const Event::Type event) const
{
return LeftJoystickEvents.find(event) != LeftJoystickEvents.end()
|| RightJoystickEvents.find(event) != RightJoystickEvents.end();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::isPaddleEvent(const Event::Type event) const
{
return LeftPaddlesEvents.find(event) != LeftPaddlesEvents.end()
|| RightPaddlesEvents.find(event) != RightPaddlesEvents.end();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::isKeypadEvent(const Event::Type event) const
{
return LeftKeypadEvents.find(event) != LeftKeypadEvents.end()
|| RightKeypadEvents.find(event) != RightKeypadEvents.end();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::isCommonEvent(const Event::Type event) const
{
return !(isJoystickEvent(event) || isPaddleEvent(event) || isKeypadEvent(event));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -363,12 +483,24 @@ void PhysicalJoystickHandler::eraseMapping(Event::Type event, EventMode mode)
if(event == Event::NoType)
{
for (auto& stick : mySticks)
{
stick.second->eraseMap(mode); // erase all events
if (mode == kEmulationMode)
{
stick.second->eraseMap(kCommonMode);
stick.second->eraseMap(kJoystickMode);
stick.second->eraseMap(kPaddlesMode);
stick.second->eraseMap(kKeypadMode);
}
}
}
else
{
for (auto& stick : mySticks)
{
stick.second->eraseEvent(event, mode); // only reset the specific event
stick.second->eraseEvent(event, getEventMode(event, mode));
}
}
}
@ -378,13 +510,12 @@ void PhysicalJoystickHandler::saveMapping()
// Save the joystick mapping hash table, making sure to update it with
// any changes that have been made during the program run
ostringstream joybuf;
joybuf << Event::LastType;
for(const auto& i: myDatabase)
{
const string& map = i.second.joy ? i.second.joy->getMap() : i.second.mapping;
if(map != "")
joybuf << "^" << map;
joybuf << CTRL_DELIM << map;
}
myOSystem.settings().setValue("joymap", joybuf.str());
}
@ -393,66 +524,21 @@ void PhysicalJoystickHandler::saveMapping()
string PhysicalJoystickHandler::getMappingDesc(Event::Type event, EventMode mode) const
{
ostringstream buf;
EventMode evMode = getEventMode(event, mode);
for(const auto& s: mySticks)
{
uInt32 stick = s.first;
const PhysicalJoystickPtr j = s.second;
if(!j) continue;
// Joystick button mapping/labeling
for(int button = 0; button < j->numButtons; ++button)
if (j)
{
if(j->btnTable[button][mode] == event)
//Joystick mapping / labeling
if (j->joyMap.getEventMapping(event, evMode).size())
{
if (buf.str() != "")
buf << ", ";
buf << "J" << stick << "/B" << button;
}
}
// Joystick axis mapping/labeling
for(int axis = 0; axis < j->numAxes; ++axis)
{
for(int dir = 0; dir < NUM_JOY_DIRS; ++dir)
{
if(j->axisTable[axis][dir][mode] == event)
{
if(buf.str() != "")
buf << ", ";
buf << "J" << stick << "/A" << axis;
if(Event::isAnalog(event))
{
dir = NUM_JOY_DIRS; // Immediately exit the inner loop after this iteration
buf << "/+|-";
}
else if(dir == 0)
buf << "/-";
else
buf << "/+";
}
}
}
// Joystick hat mapping/labeling
for(int hat = 0; hat < j->numHats; ++hat)
{
for(int dir = 0; dir < NUM_JOY_HAT_DIRS; ++dir)
{
if(j->hatTable[hat][dir][mode] == event)
{
if(buf.str() != "")
buf << ", ";
buf << "J" << stick << "/H" << hat;
switch(JoyHat(dir))
{
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;
}
}
buf << j->joyMap.getEventMappingDesc(stick, event, evMode);
}
}
}
@ -460,64 +546,49 @@ string PhysicalJoystickHandler::getMappingDesc(Event::Type event, EventMode mode
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::addAxisMapping(Event::Type event, EventMode mode,
int stick, int axis, int value)
bool PhysicalJoystickHandler::addJoyMapping(Event::Type event, EventMode mode, int stick,
int button, JoyAxis axis, int value)
{
const PhysicalJoystickPtr j = joy(stick);
if(j)
{
if(int(axis) >= 0 && int(axis) < j->numAxes && event < Event::LastType)
if (j && event < Event::LastType &&
button >= JOY_CTRL_NONE && button < j->numButtons &&
axis >= JoyAxis::NONE && int(axis) < j->numAxes)
{
EventMode evMode = getEventMode(event, mode);
// This confusing code is because each axis has two associated values,
// but analog events only affect one of the axis.
if (Event::isAnalog(event))
j->axisTable[axis][int(JoyDir::NEG)][mode] = j->axisTable[axis][int(JoyDir::POS)][mode] = event;
{
j->joyMap.add(event, evMode, button, axis, JoyDir::ANALOG);
}
else
{
// Otherwise, turn off the analog event(s) for this axis
if(Event::isAnalog(j->axisTable[axis][int(JoyDir::NEG)][mode]))
j->axisTable[axis][int(JoyDir::NEG)][mode] = Event::NoType;
if(Event::isAnalog(j->axisTable[axis][int(JoyDir::POS)][mode]))
j->axisTable[axis][int(JoyDir::POS)][mode] = Event::NoType;
if (Event::isAnalog(j->joyMap.get(evMode, button, axis, JoyDir::ANALOG)))
j->joyMap.erase(evMode, button, axis, JoyDir::ANALOG);
j->axisTable[axis][(value > int(JoyDir::NEG))][mode] = event;
j->joyMap.add(event, evMode, button, axis, convertAxisValue(value));
}
return true;
}
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::addBtnMapping(Event::Type event, EventMode mode,
int stick, int button)
bool PhysicalJoystickHandler::addJoyHatMapping(Event::Type event, EventMode mode, int stick,
int button, int hat, JoyHat dir)
{
const PhysicalJoystickPtr j = joy(stick);
if(j)
{
if(button >= 0 && button < j->numButtons && event < Event::LastType)
{
j->btnTable[button][mode] = event;
return true;
}
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::addHatMapping(Event::Type event, EventMode mode,
int stick, int hat, JoyHat value)
if (j && event < Event::LastType &&
button >= JOY_CTRL_NONE && button < j->numButtons &&
hat >= 0 && hat < j->numHats && dir != JoyHat::CENTER)
{
const PhysicalJoystickPtr j = joy(stick);
if(j)
{
if(hat >= 0 && hat < j->numHats && event < Event::LastType &&
value != JoyHat::CENTER)
{
j->hatTable[hat][int(value)][mode] = event;
j->joyMap.add(event, getEventMode(event, mode), button, hat, dir);
return true;
}
}
return false;
}
@ -525,38 +596,27 @@ bool PhysicalJoystickHandler::addHatMapping(Event::Type event, EventMode mode,
void PhysicalJoystickHandler::handleAxisEvent(int stick, int axis, int value)
{
const PhysicalJoystickPtr j = joy(stick);
if(!j) return;
// Stelladaptors handle axis differently than regular joysticks
switch(j->type)
if (j)
{
case PhysicalJoystick::JT_REGULAR:
int button = j->buttonLast[stick];
if (myHandler.state() == EventHandlerState::EMULATION)
{
// Every axis event has two associated values, negative and positive
Event::Type eventAxisNeg = j->axisTable[axis][int(JoyDir::NEG)][kEmulationMode];
Event::Type eventAxisPos = j->axisTable[axis][int(JoyDir::POS)][kEmulationMode];
Event::Type eventAxisAnalog = j->joyMap.get(kEmulationMode, button, JoyAxis(axis), JoyDir::ANALOG);
// Check for analog events, which are handled differently
// We'll pass them off as Stelladaptor events, and let the controllers
// handle it
switch(int(eventAxisNeg))
if (Event::isAnalog(eventAxisAnalog))
{
case Event::PaddleZeroAnalog:
myEvent.set(Event::SALeftAxis0Value, value);
break;
case Event::PaddleOneAnalog:
myEvent.set(Event::SALeftAxis1Value, value);
break;
case Event::PaddleTwoAnalog:
myEvent.set(Event::SARightAxis0Value, value);
break;
case Event::PaddleThreeAnalog:
myEvent.set(Event::SARightAxis1Value, value);
break;
default:
myHandler.handleEvent(eventAxisAnalog, value);
}
else
{
// Otherwise, we know the event is digital
// Every axis event has two associated values, negative and positive
Event::Type eventAxisNeg = j->joyMap.get(kEmulationMode, button, JoyAxis(axis), JoyDir::NEG);
Event::Type eventAxisPos = j->joyMap.get(kEmulationMode, button, JoyAxis(axis), JoyDir::POS);
if (value > Joystick::deadzone())
myHandler.handleEvent(eventAxisPos);
else if (value < -Joystick::deadzone())
@ -577,8 +637,6 @@ void PhysicalJoystickHandler::handleAxisEvent(int stick, int axis, int value)
}
}
j->axisLastValue[axis] = value;
break;
}
}
}
else if (myHandler.hasOverlay())
@ -597,30 +655,12 @@ void PhysicalJoystickHandler::handleAxisEvent(int stick, int axis, int value)
if (value != j->axisLastValue[axis])
{
#ifdef GUI_SUPPORT
myHandler.overlay().handleJoyAxisEvent(stick, axis, value);
cerr << "axis event" << endl;
myHandler.overlay().handleJoyAxisEvent(stick, axis, value, button);
#endif
j->axisLastValue[axis] = value;
}
}
break; // Regular joystick axis
// Since the various controller classes deal with Stelladaptor
// devices differently, we send the raw X and Y axis data directly,
// and let the controller handle it
// These events don't have to pass through handleEvent, since
// they can never be remapped
case PhysicalJoystick::JT_STELLADAPTOR_LEFT:
case PhysicalJoystick::JT_2600DAPTOR_LEFT:
if(axis < NUM_JOY_AXIS)
myEvent.set(SA_Axis[int(Controller::Jack::Left)][axis], value);
break; // axis on left controller (0)
case PhysicalJoystick::JT_STELLADAPTOR_RIGHT:
case PhysicalJoystick::JT_2600DAPTOR_RIGHT:
if(axis < NUM_JOY_AXIS)
myEvent.set(SA_Axis[int(Controller::Jack::Right)][axis], value);
break; // axis on right controller (1)
default:
break;
}
}
@ -628,64 +668,23 @@ void PhysicalJoystickHandler::handleAxisEvent(int stick, int axis, int value)
void PhysicalJoystickHandler::handleBtnEvent(int stick, int button, bool pressed)
{
const PhysicalJoystickPtr j = joy(stick);
if(!j) return;
// Stelladaptors handle buttons differently than regular joysticks
switch(j->type)
if (j)
{
case PhysicalJoystick::JT_REGULAR:
j->buttonLast[stick] = pressed ? button : JOY_CTRL_NONE;
// Handle buttons which switch eventhandler state
if(pressed && myHandler.changeStateByEvent(j->btnTable[button][kEmulationMode]))
if (pressed && myHandler.changeStateByEvent(j->joyMap.get(kEmulationMode, button)))
return;
// Determine which mode we're in, then send the event to the appropriate place
if (myHandler.state() == EventHandlerState::EMULATION)
myHandler.handleEvent(j->btnTable[button][kEmulationMode], pressed);
myHandler.handleEvent(j->joyMap.get(kEmulationMode, button), pressed);
#ifdef GUI_SUPPORT
else if (myHandler.hasOverlay())
myHandler.overlay().handleJoyBtnEvent(stick, button, pressed);
#endif
break; // Regular button
// These events don't have to pass through handleEvent, since
// they can never be remapped
case PhysicalJoystick::JT_STELLADAPTOR_LEFT:
case PhysicalJoystick::JT_STELLADAPTOR_RIGHT:
// The 'type-2' here refers to the fact that 'PhysicalJoystick::JT_STELLADAPTOR_LEFT'
// and 'PhysicalJoystick::JT_STELLADAPTOR_RIGHT' are at index 2 and 3 in the JoyType
// enum; subtracting two gives us Controller 0 and 1
if(button < 2)
myEvent.set(SA_Button[j->type-PhysicalJoystick::JT_STELLADAPTOR_LEFT][button], pressed ? 1 : 0);
break; // Stelladaptor button
case PhysicalJoystick::JT_2600DAPTOR_LEFT:
case PhysicalJoystick::JT_2600DAPTOR_RIGHT:
// The 'type-4' here refers to the fact that 'PhysicalJoystick::JT_2600DAPTOR_LEFT'
// and 'PhysicalJoystick::JT_2600DAPTOR_RIGHT' are at index 4 and 5 in the JoyType
// enum; subtracting four gives us Controller 0 and 1
if(myHandler.state() == EventHandlerState::EMULATION)
{
switch(myOSystem.console().leftController().type())
{
case Controller::Type::Keyboard:
if(button < NUM_KEY_BTN)
myEvent.set(SA_Key[j->type-PhysicalJoystick::JT_2600DAPTOR_LEFT][button], pressed ? 1 : 0);
break;
default:
if(button < NUM_JOY_BTN) myEvent.set(SA_Button[j->type-PhysicalJoystick::JT_2600DAPTOR_LEFT][button], pressed ? 1 : 0);
}
switch(myOSystem.console().rightController().type())
{
case Controller::Type::Keyboard:
if(button < NUM_KEY_BTN)
myEvent.set(SA_Key[j->type-PhysicalJoystick::JT_2600DAPTOR_LEFT][button], pressed ? 1 : 0);
break;
default:
if(button < NUM_JOY_BTN) myEvent.set(SA_Button[j->type-PhysicalJoystick::JT_2600DAPTOR_LEFT][button], pressed ? 1 : 0);
}
}
break; // 2600DAPTOR button
default:
break;
}
}
@ -695,35 +694,40 @@ void PhysicalJoystickHandler::handleHatEvent(int stick, int hat, int value)
// Preprocess all hat events, converting to Stella JoyHat type
// Generate multiple equivalent hat events representing combined direction
// when we get a diagonal hat event
const PhysicalJoystickPtr j = joy(stick);
if (j)
{
const int button = j->buttonLast[stick];
if (myHandler.state() == EventHandlerState::EMULATION)
{
const PhysicalJoystickPtr j = joy(stick);
if(!j) return;
myHandler.handleEvent(j->hatTable[hat][int(JoyHat::UP)][kEmulationMode],
myHandler.handleEvent(j->joyMap.get(kEmulationMode, button, hat, JoyHat::UP),
value & EVENT_HATUP_M);
myHandler.handleEvent(j->hatTable[hat][int(JoyHat::RIGHT)][kEmulationMode],
myHandler.handleEvent(j->joyMap.get(kEmulationMode, button, hat, JoyHat::RIGHT),
value & EVENT_HATRIGHT_M);
myHandler.handleEvent(j->hatTable[hat][int(JoyHat::DOWN)][kEmulationMode],
myHandler.handleEvent(j->joyMap.get(kEmulationMode, button, hat, JoyHat::DOWN),
value & EVENT_HATDOWN_M);
myHandler.handleEvent(j->hatTable[hat][int(JoyHat::LEFT)][kEmulationMode],
myHandler.handleEvent(j->joyMap.get(kEmulationMode, button, hat, JoyHat::LEFT),
value & EVENT_HATLEFT_M);
}
#ifdef GUI_SUPPORT
else if (myHandler.hasOverlay())
{
if (value == EVENT_HATCENTER_M)
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::CENTER);
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::CENTER, button);
else
{
if (value & EVENT_HATUP_M)
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::UP);
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::UP, button);
if (value & EVENT_HATRIGHT_M)
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::RIGHT);
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::RIGHT, button);
if (value & EVENT_HATDOWN_M)
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::DOWN);
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::DOWN, button);
if (value & EVENT_HATLEFT_M)
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::LEFT);
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::LEFT, button);
}
}
}
#endif
@ -758,31 +762,134 @@ ostream& operator<<(ostream& os, const PhysicalJoystickHandler& jh)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Used by the Stelladaptor to send absolute axis values
const Event::Type PhysicalJoystickHandler::SA_Axis[NUM_PORTS][NUM_JOY_AXIS] = {
{ Event::SALeftAxis0Value, Event::SALeftAxis1Value },
{ Event::SARightAxis0Value, Event::SARightAxis1Value }
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftJoystickMapping = {
// Left joystick (assume buttons zero..two)
{Event::JoystickZeroFire, 0},
{Event::JoystickZeroFire5, 1},
{Event::JoystickZeroFire9, 2},
#if defined(RETRON77)
// Left joystick (assume buttons two..four)
{Event::CmdMenuMode, 2),
{Event::OptionsMenuMode, 3),
{Event::ExitMode, 4),
#endif
// Left joystick left/right directions
{Event::JoystickZeroLeft, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
{Event::JoystickZeroRight, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
// Left joystick up/down directions
{Event::JoystickZeroUp, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG},
{Event::JoystickZeroDown, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS},
// Left joystick left/right directions (assume hat 0)
{Event::JoystickZeroLeft, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHat::LEFT},
{Event::JoystickZeroRight, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHat::RIGHT},
// Left joystick up/down directions (assume hat 0)
{Event::JoystickZeroUp, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHat::UP},
{Event::JoystickZeroDown, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHat::DOWN},
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Used by the Stelladaptor to map button presses to joystick or paddles
// (driving controllers and boostergrip are considered the same as joysticks)
const Event::Type PhysicalJoystickHandler::SA_Button[NUM_PORTS][NUM_JOY_BTN] = {
{ Event::JoystickZeroFire, Event::JoystickZeroFire9,
Event::JoystickZeroFire5, Event::JoystickZeroFire9 },
{ Event::JoystickOneFire, Event::JoystickOneFire9,
Event::JoystickOneFire5, Event::JoystickOneFire9 }
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRightJoystickMapping = {
// Right joystick (assume buttons zero..two)
{Event::JoystickOneFire, 0},
{Event::JoystickOneFire5, 1},
{Event::JoystickOneFire9, 2},
#if defined(RETRON77)
// Right joystick (assume buttons two..eight)
{Event::CmdMenuMode, 2},
{Event::OptionsMenuMode, 3},
{Event::ExitMode, 4},
{Event::RewindPause, 5},
{Event::ConsoleSelect, 7},
{Event::ConsoleReset, 8},
#endif
// Right joystick left/right directions
{Event::JoystickOneLeft, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
{Event::JoystickOneRight, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
// Right joystick up/down directions
{Event::JoystickOneUp, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG},
{Event::JoystickOneDown, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS},
// Right joystick left/right directions (assume hat 0)
{Event::JoystickOneLeft, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHat::LEFT},
{Event::JoystickOneRight, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHat::RIGHT},
// Right joystick up/down directions (assume hat 0)
{Event::JoystickOneUp, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHat::UP},
{Event::JoystickOneDown, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHat::DOWN},
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Used by the 2600-daptor to map button presses to keypad keys
const Event::Type PhysicalJoystickHandler::SA_Key[NUM_PORTS][NUM_KEY_BTN] = {
{ Event::KeyboardZero1, Event::KeyboardZero2, Event::KeyboardZero3,
Event::KeyboardZero4, Event::KeyboardZero5, Event::KeyboardZero6,
Event::KeyboardZero7, Event::KeyboardZero8, Event::KeyboardZero9,
Event::KeyboardZeroStar, Event::KeyboardZero0, Event::KeyboardZeroPound },
{ Event::KeyboardOne1, Event::KeyboardOne2, Event::KeyboardOne3,
Event::KeyboardOne4, Event::KeyboardOne5, Event::KeyboardOne6,
Event::KeyboardOne7, Event::KeyboardOne8, Event::KeyboardOne9,
Event::KeyboardOneStar, Event::KeyboardOne0, Event::KeyboardOnePound }
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftPaddlesMapping = {
{Event::PaddleZeroAnalog, JOY_CTRL_NONE, JoyAxis::X, JoyDir::ANALOG},
{Event::PaddleZeroDecrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
{Event::PaddleZeroIncrease, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
{Event::PaddleZeroFire, 0},
{Event::PaddleOneAnalog, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::ANALOG},
{Event::PaddleOneDecrease, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS},
{Event::PaddleOneIncrease, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG},
{Event::PaddleOneFire, 1},
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRightPaddlesMapping = {
{Event::PaddleTwoAnalog, JOY_CTRL_NONE, JoyAxis::Z, JoyDir::ANALOG},
{Event::PaddleTwoDecrease, JOY_CTRL_NONE, JoyAxis::Z, JoyDir::POS},
{Event::PaddleTwoIncrease, JOY_CTRL_NONE, JoyAxis::Z, JoyDir::NEG},
{Event::PaddleTwoFire, 2},
{Event::PaddleThreeAnalog, JOY_CTRL_NONE, JoyAxis(3), JoyDir::ANALOG},
{Event::PaddleThreeDecrease,JOY_CTRL_NONE, JoyAxis(3), JoyDir::POS},
{Event::PaddleThreeIncrease,JOY_CTRL_NONE, JoyAxis(3), JoyDir::NEG},
{Event::PaddleThreeFire, 3},
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultLeftKeypadMapping = {
{Event::KeyboardZero1, 0},
{Event::KeyboardZero2, 1},
{Event::KeyboardZero3, 2},
{Event::KeyboardZero4, 3},
{Event::KeyboardZero5, 4},
{Event::KeyboardZero6, 5},
{Event::KeyboardZero7, 6},
{Event::KeyboardZero8, 7},
{Event::KeyboardZero9, 8},
{Event::KeyboardZeroStar, 9},
{Event::KeyboardZero0, 10},
{Event::KeyboardZeroPound, 11},
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultRightKeypadMapping = {
{Event::KeyboardOne1, 0},
{Event::KeyboardOne2, 1},
{Event::KeyboardOne3, 2},
{Event::KeyboardOne4, 3},
{Event::KeyboardOne5, 4},
{Event::KeyboardOne6, 5},
{Event::KeyboardOne7, 6},
{Event::KeyboardOne8, 7},
{Event::KeyboardOne9, 8},
{Event::KeyboardOneStar, 9},
{Event::KeyboardOne0, 10},
{Event::KeyboardOnePound, 11},
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::EventMappingArray PhysicalJoystickHandler::DefaultMenuMapping = {
// valid for all joysticks
{Event::UISelect, 0},
{Event::UIOK, 1},
{Event::UITabNext, 2},
{Event::UITabPrev, 3},
{Event::UICancel, 5},
{Event::UINavPrev, JOY_CTRL_NONE, JoyAxis::X, JoyDir::NEG},
{Event::UITabPrev, 0, JoyAxis::X, JoyDir::NEG},
{Event::UINavNext, JOY_CTRL_NONE, JoyAxis::X, JoyDir::POS},
{Event::UITabNext, 0, JoyAxis::X, JoyDir::POS},
{Event::UIUp, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::NEG},
{Event::UIDown, JOY_CTRL_NONE, JoyAxis::Y, JoyDir::POS},
{Event::UINavPrev, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHat::LEFT},
{Event::UINavNext, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHat::RIGHT},
{Event::UIUp, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHat::UP},
{Event::UIDown, JOY_CTRL_NONE, JoyAxis::NONE, JoyDir::NONE, 0, JoyHat::DOWN},
};

View File

@ -41,7 +41,7 @@ using PhysicalJoystickPtr = shared_ptr<PhysicalJoystick>;
Essentially, this class is an extension of the EventHandler class, but
handling only joystick-specific functionality.
@author Stephen Anthony
@author Stephen Anthony, Thomas Jentzsch
*/
class PhysicalJoystickHandler
{
@ -69,32 +69,38 @@ class PhysicalJoystickHandler
bool remove(const string& name);
void mapStelladaptors(const string& saport);
void setDefaultMapping(Event::Type type, EventMode mode);
/** define mappings for current controllers */
void defineControllerMappings(const string& controllerName, Controller::Jack port);
/** 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 addAxisMapping(Event::Type event, EventMode mode, int stick, int axis, int value);
bool addBtnMapping(Event::Type event, EventMode mode, int stick, int button);
bool addHatMapping(Event::Type event, EventMode mode, int stick, int hat, JoyHat value);
bool addJoyMapping(Event::Type event, EventMode mode, int stick,
int button, JoyAxis axis, int value);
bool addJoyHatMapping(Event::Type event, EventMode mode, int stick,
int button, 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);
void handleHatEvent(int stick, int hat, int value);
Event::Type eventForAxis(int stick, int axis, int joyDir, EventMode mode) const {
Event::Type eventForAxis(EventMode mode, int stick, int axis, int value, int button) const {
const PhysicalJoystickPtr j = joy(stick);
return (j && joyDir != int(JoyDir::NEG))
? j->axisTable[axis][(joyDir > int(JoyDir::NEG))][mode] : Event::NoType;
return j->joyMap.get(mode, button, JoyAxis(axis), convertAxisValue(value));
}
Event::Type eventForButton(int stick, int button, EventMode mode) const {
Event::Type eventForButton(EventMode mode, int stick, int button) const {
const PhysicalJoystickPtr j = joy(stick);
return j ? j->btnTable[button][mode] : Event::NoType;
return j->joyMap.get(mode, button);
}
Event::Type eventForHat(int stick, int hat, JoyHat hatDir, EventMode mode) const {
Event::Type eventForHat(EventMode mode, int stick, int hat, JoyHat hatDir, int button) const {
const PhysicalJoystickPtr j = joy(stick);
return j ? j->hatTable[hat][int(hatDir)][mode] : Event::NoType;
return j->joyMap.get(mode, button, hat, hatDir);
}
/** Returns a list of pairs consisting of joystick name and associated ID. */
@ -122,17 +128,56 @@ class PhysicalJoystickHandler
}
// Set default mapping for given joystick when no mappings already exist
void setStickDefaultMapping(int stick, Event::Type type, EventMode mode);
void setStickDefaultMapping(int stick, Event::Type type, EventMode mode,
bool updateDefaults = false);
friend ostream& operator<<(ostream& os, const PhysicalJoystickHandler& jh);
// Static lookup tables for Stelladaptor/2600-daptor axis/button support
static const int NUM_JOY_BTN = 4;
static const int NUM_KEY_BTN = 12;
JoyDir convertAxisValue(int value) const {
return value == int(JoyDir::NONE) ? JoyDir::NONE : value > 0 ? JoyDir::POS : JoyDir::NEG;
}
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];
// Structures used for action menu items
struct EventMapping {
Event::Type event;
int button;
JoyAxis axis = JoyAxis::NONE;
JoyDir adir = JoyDir::NONE;
int hat = JOY_CTRL_NONE;
JoyHat hdir = JoyHat::CENTER;
};
using EventMappingArray = std::vector<EventMapping>;
void setDefaultAction(const PhysicalJoystickPtr& j,
EventMapping map, Event::Type event = Event::NoType,
EventMode mode = kEmulationMode, bool updateDefaults = false);
/** returns the event's controller mode */
EventMode getEventMode(const Event::Type event, const EventMode mode) const;
/** Checks event type. */
bool isJoystickEvent(const Event::Type event) const;
bool isPaddleEvent(const Event::Type event) const;
bool isKeypadEvent(const Event::Type event) const;
bool isCommonEvent(const Event::Type event) const;
void enableCommonMappings();
void enableMappings(const Event::EventSet events, EventMode mode);
void enableMapping(const Event::Type event, EventMode mode);
private:
EventMode myLeftMode;
EventMode myRightMode;
// Controller menu and common emulation mappings
static EventMappingArray DefaultMenuMapping;
// Controller specific mappings
static EventMappingArray DefaultLeftJoystickMapping;
static EventMappingArray DefaultRightJoystickMapping;
static EventMappingArray DefaultLeftPaddlesMapping;
static EventMappingArray DefaultRightPaddlesMapping;
static EventMappingArray DefaultLeftKeypadMapping;
static EventMappingArray DefaultRightKeypadMapping;
};
#endif

View File

@ -26,7 +26,6 @@
#include "PNGLibrary.hxx"
#include "PKeyboardHandler.hxx"
#ifdef DEBUGGER_SUPPORT
#include "Debugger.hxx"
#endif
@ -94,7 +93,7 @@ void PhysicalKeyboardHandler::setDefaultKey(EventMapping map, Event::Type event,
}
else if (eraseAll || map.event == event)
{
myKeyMap.eraseEvent(map.event, mode);
//myKeyMap.eraseEvent(map.event, mode);
myKeyMap.add(map.event, mode, map.key, map.mod);
}
}
@ -107,6 +106,12 @@ void PhysicalKeyboardHandler::setDefaultKey(EventMapping map, Event::Type event,
void PhysicalKeyboardHandler::setDefaultMapping(Event::Type event, EventMode mode,
bool updateDefaults)
{
if (!updateDefaults)
{
myKeyMap.eraseEvent(event, mode);
myKeyMap.eraseEvent(event, getEventMode(event, mode));
}
switch(mode)
{
case kEmulationMode:
@ -186,7 +191,6 @@ void PhysicalKeyboardHandler::enableEmulationMappings()
// see below
break;
default:
enableMappings(RightJoystickEvents, kJoystickMode);
break;
@ -226,7 +230,7 @@ void PhysicalKeyboardHandler::enableCommonMappings()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalKeyboardHandler::enableMappings(const EventSet events, EventMode mode)
void PhysicalKeyboardHandler::enableMappings(const Event::EventSet events, EventMode mode)
{
for (const auto& event : events)
enableMapping(event, mode);
@ -236,7 +240,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<KeyMap::Mapping> 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);
@ -387,46 +391,6 @@ void PhysicalKeyboardHandler::handleEvent(StellaKey key, StellaMod mod, bool pre
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventSet PhysicalKeyboardHandler::LeftJoystickEvents = {
Event::JoystickZeroUp, Event::JoystickZeroDown, Event::JoystickZeroLeft, Event::JoystickZeroRight,
Event::JoystickZeroFire, Event::JoystickZeroFire5, Event::JoystickZeroFire9,
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventSet PhysicalKeyboardHandler::RightJoystickEvents = {
Event::JoystickOneUp, Event::JoystickOneDown, Event::JoystickOneLeft, Event::JoystickOneRight,
Event::JoystickOneFire, Event::JoystickOneFire5, Event::JoystickOneFire9
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventSet PhysicalKeyboardHandler::LeftPaddlesEvents = {
Event::PaddleZeroDecrease, Event::PaddleZeroIncrease, Event::PaddleZeroAnalog, Event::PaddleZeroFire,
Event::PaddleOneDecrease, Event::PaddleOneIncrease, Event::PaddleOneAnalog, Event::PaddleOneFire,
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventSet PhysicalKeyboardHandler::RightPaddlesEvents = {
Event::PaddleTwoDecrease, Event::PaddleTwoIncrease, Event::PaddleTwoAnalog, Event::PaddleTwoFire,
Event::PaddleThreeDecrease, Event::PaddleThreeIncrease, Event::PaddleThreeAnalog, Event::PaddleThreeFire,
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventSet PhysicalKeyboardHandler::LeftKeypadEvents = {
Event::KeyboardZero1, Event::KeyboardZero2, Event::KeyboardZero3,
Event::KeyboardZero4, Event::KeyboardZero5, Event::KeyboardZero6,
Event::KeyboardZero7, Event::KeyboardZero8, Event::KeyboardZero9,
Event::KeyboardZeroStar, Event::KeyboardZero0, Event::KeyboardZeroPound,
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventSet PhysicalKeyboardHandler::RightKeypadEvents = {
Event::KeyboardOne1, Event::KeyboardOne2, Event::KeyboardOne3,
Event::KeyboardOne4, Event::KeyboardOne5, Event::KeyboardOne6,
Event::KeyboardOne7, Event::KeyboardOne8, Event::KeyboardOne9,
Event::KeyboardOneStar, Event::KeyboardOne0, Event::KeyboardOnePound,
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultCommonMapping = {
{Event::ConsoleSelect, KBDK_F1},
@ -544,6 +508,7 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultMenuM
{Event::UIPgDown, KBDK_PAGEDOWN},
{Event::UISelect, KBDK_RETURN},
{Event::UISelect, KBDK_KP_ENTER},
{Event::UICancel, KBDK_ESCAPE},
{Event::UINavPrev, KBDK_TAB, KBDM_SHIFT},
@ -579,11 +544,17 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultJoyst
{Event::JoystickZeroDown, KBDK_DOWN},
{Event::JoystickZeroLeft, KBDK_LEFT},
{Event::JoystickZeroRight, KBDK_RIGHT},
{Event::JoystickZeroUp, KBDK_KP_8},
{Event::JoystickZeroDown, KBDK_KP_2},
{Event::JoystickZeroLeft, KBDK_KP_4},
{Event::JoystickZeroRight, KBDK_KP_6},
{Event::JoystickZeroFire, KBDK_SPACE},
{Event::JoystickZeroFire, KBDK_LCTRL},
{Event::JoystickZeroFire, KBDK_KP_5},
{Event::JoystickZeroFire5, KBDK_4},
{Event::JoystickZeroFire5, KBDK_KP_9},
{Event::JoystickZeroFire9, KBDK_5},
{Event::JoystickZeroFire9, KBDK_KP_3},
{Event::JoystickOneUp, KBDK_Y},
{Event::JoystickOneDown, KBDK_H},
{Event::JoystickOneLeft, KBDK_G},
@ -641,7 +612,6 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultKeypa
{Event::KeyboardOnePound, KBDK_SLASH},
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::CompuMateMapping = {
{Event::CompuMateShift, KBDK_LSHIFT},

View File

@ -19,7 +19,6 @@
#define PHYSICAL_KEYBOARD_HANDLER_HXX
#include <map>
#include <set>
class OSystem;
class EventHandler;
@ -29,8 +28,6 @@ class EventHandler;
#include "EventHandlerConstants.hxx"
#include "KeyMap.hxx"
using EventSet = std::set<Event::Type>;
/**
This class handles all physical keyboard-related operations in Stella.
@ -40,7 +37,7 @@ using EventSet = std::set<Event::Type>;
Essentially, this class is an extension of the EventHandler class, but
handling only keyboard-specific functionality.
@author Stephen Anthony
@author Stephen Anthony, Thomas Jentzsch
*/
class PhysicalKeyboardHandler
{
@ -103,7 +100,7 @@ class PhysicalKeyboardHandler
void enableCommonMappings();
void enableMappings(const EventSet events, EventMode mode);
void enableMappings(const Event::EventSet events, EventMode mode);
void enableMapping(const Event::Type event, EventMode mode);
OSystem& myOSystem;
@ -130,16 +127,10 @@ class PhysicalKeyboardHandler
uInt8 myAltKeyCounter;
#endif
// Hold controller related events
static EventSet LeftJoystickEvents;
static EventSet RightJoystickEvents;
static EventSet LeftPaddlesEvents;
static EventSet RightPaddlesEvents;
static EventSet LeftKeypadEvents;
static EventSet RightKeypadEvents;
// Controller menu and common emulation mappings
static EventMappingArray DefaultMenuMapping;
static EventMappingArray DefaultCommonMapping;
// Controller specific mappings
static EventMappingArray DefaultJoystickMapping;
static EventMappingArray DefaultPaddleMapping;
static EventMappingArray DefaultKeypadMapping;

View File

@ -31,20 +31,16 @@ PhysicalJoystick::PhysicalJoystick()
numAxes(0),
numButtons(0),
numHats(0),
axisTable(nullptr),
btnTable(nullptr),
hatTable(nullptr),
axisLastValue(nullptr)
axisLastValue(nullptr),
buttonLast(nullptr)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystick::~PhysicalJoystick()
{
delete[] axisTable;
delete[] btnTable;
delete[] hatTable;
delete[] axisLastValue;
delete[] buttonLast;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -59,109 +55,66 @@ void PhysicalJoystick::initialize(int index, const string& desc,
numAxes = axes;
numButtons = buttons;
numHats = hats;
if(numAxes)
axisTable = new Event::Type[numAxes][NUM_JOY_DIRS][kNumModes];
if(numButtons)
btnTable = new Event::Type[numButtons][kNumModes];
if(numHats)
hatTable = new Event::Type[numHats][NUM_JOY_HAT_DIRS][kNumModes];
axisLastValue = new int[numAxes];
buttonLast = new int[numButtons];
// Erase the joystick axis mapping array and last axis value
for(int a = 0; a < numAxes; ++a)
{
axisLastValue[a] = 0;
for(int m = 0; m < kNumModes; ++m)
for (int d = 0; d < NUM_JOY_DIRS; ++d)
axisTable[a][d][m] = Event::NoType;
}
// Erase the joystick button mapping array
// Erase the last button
for (int b = 0; b < numButtons; ++b)
for(int m = 0; m < kNumModes; ++m)
btnTable[b][m] = Event::NoType;
buttonLast[b] = JOY_CTRL_NONE;
// Erase the last axis value
for(int a = 0; a < numAxes; ++a)
axisLastValue[a] = 0;
// Erase the mappings
eraseMap(kJoystickMode);
eraseMap(kPaddlesMode);
eraseMap(kKeypadMode);
eraseMap(kMenuMode);
// Erase the joystick hat mapping array
for(int h = 0; h < numHats; ++h)
for(int m = 0; m < kNumModes; ++m)
for (int d = 0; d < NUM_JOY_HAT_DIRS; ++d)
hatTable[h][d][m] = Event::NoType;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string PhysicalJoystick::getMap() const
{
// The mapping structure (for remappable devices) is defined as follows:
// NAME | AXIS # + values | BUTTON # + values | HAT # + values,
// where each subsection of values is separated by ':'
if(type == JT_REGULAR)
{
// <NAME>'$'<MODE>['|'(<EVENT>':'<BUTTON>','<AXIS>','<VALUE>)|(<EVENT>':'<BUTTON>','<HAT>','<HATDIR>)]
ostringstream joybuf;
joybuf << name << "|" << numAxes;
for(int m = 0; m < kNumModes; ++m)
for(int a = 0; a < numAxes; ++a)
for (int d = 0; d < NUM_JOY_DIRS; ++d)
joybuf << " " << axisTable[a][d][m];
joybuf << "|" << numButtons;
for(int m = 0; m < kNumModes; ++m)
for(int b = 0; b < numButtons; ++b)
joybuf << " " << btnTable[b][m];
joybuf << "|" << numHats;
for(int m = 0; m < kNumModes; ++m)
for(int h = 0; h < numHats; ++h)
for (int d = 0; d < NUM_JOY_HAT_DIRS; ++d)
joybuf << " " << hatTable[h][d][m];
joybuf << name;
joybuf << MODE_DELIM << int(kMenuMode) << "|" << joyMap.saveMapping(kMenuMode);
joybuf << MODE_DELIM << int(kJoystickMode) << "|" << joyMap.saveMapping(kJoystickMode);
joybuf << MODE_DELIM << int(kPaddlesMode) << "|" << joyMap.saveMapping(kPaddlesMode);
joybuf << MODE_DELIM << int(kKeypadMode) << "|" << joyMap.saveMapping(kKeypadMode);
joybuf << MODE_DELIM << int(kCommonMode) << "|" << joyMap.saveMapping(kCommonMode);
return joybuf.str();
}
return EmptyString;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystick::setMap(const string& mapString)
{
istringstream buf(mapString);
StringList items;
string item;
while(getline(buf, item, '|'))
items.push_back(item);
StringList mappings;
string map;
while (getline(buf, map, MODE_DELIM))
{
// remove leading "<mode>|" string
map.erase(0, 2);
mappings.push_back(map);
}
// Error checking
if(items.size() != 4)
if(mappings.size() != 1 + 5)
return false;
IntArray map;
// Parse axis/button/hat values
getValues(items[1], map);
if(int(map.size()) == numAxes * NUM_JOY_DIRS * kNumModes)
{
// Fill the axes table with events
auto event = map.cbegin();
for(int m = 0; m < kNumModes; ++m)
for(int a = 0; a < numAxes; ++a)
for (int d = 0; d < NUM_JOY_DIRS; ++d)
axisTable[a][d][m] = Event::Type(*event++);
}
getValues(items[2], map);
if(int(map.size()) == numButtons * kNumModes)
{
auto event = map.cbegin();
for(int m = 0; m < kNumModes; ++m)
for(int b = 0; b < numButtons; ++b)
btnTable[b][m] = Event::Type(*event++);
}
getValues(items[3], map);
if(int(map.size()) == numHats * NUM_JOY_HAT_DIRS * kNumModes)
{
auto event = map.cbegin();
for(int m = 0; m < kNumModes; ++m)
for(int h = 0; h < numHats; ++h)
for (int d = 0; d < NUM_JOY_HAT_DIRS; ++d)
hatTable[h][d][m] = Event::Type(*event++);
}
joyMap.loadMapping(mappings[1], kMenuMode);
joyMap.loadMapping(mappings[2], kJoystickMode);
joyMap.loadMapping(mappings[3], kPaddlesMode);
joyMap.loadMapping(mappings[4], kKeypadMode);
joyMap.loadMapping(mappings[5], kCommonMode);
return true;
}
@ -169,40 +122,13 @@ bool PhysicalJoystick::setMap(const string& mapString)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystick::eraseMap(EventMode mode)
{
// Erase axis mappings
for(int a = 0; a < numAxes; ++a)
for (int d = 0; d < NUM_JOY_DIRS; ++d)
axisTable[a][d][mode] = Event::NoType;
// Erase button mappings
for(int b = 0; b < numButtons; ++b)
btnTable[b][mode] = Event::NoType;
// Erase hat mappings
for(int h = 0; h < numHats; ++h)
for (int d = 0; d < NUM_JOY_HAT_DIRS; ++d)
hatTable[h][d][mode] = Event::NoType;
joyMap.eraseMode(mode);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystick::eraseEvent(Event::Type event, EventMode mode)
{
// Erase axis mappings
for(int a = 0; a < numAxes; ++a)
for (int d = 0; d < NUM_JOY_DIRS; ++d)
if(axisTable[a][d][mode] == event)
axisTable[a][d][mode] = Event::NoType;
// Erase button mappings
for(int b = 0; b < numButtons; ++b)
if(btnTable[b][mode] == event)
btnTable[b][mode] = Event::NoType;
// Erase hat mappings
for(int h = 0; h < numHats; ++h)
for (int d = 0; d < NUM_JOY_HAT_DIRS; ++d)
if(hatTable[h][d][mode] == event)
hatTable[h][d][mode] = Event::NoType;
joyMap.eraseEvent(event, mode);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -221,8 +147,6 @@ void PhysicalJoystick::getValues(const string& list, IntArray& map) const
string PhysicalJoystick::about() const
{
ostringstream buf;
buf << name;
if(type == JT_REGULAR)
buf << " with: " << numAxes << " axes, " << numButtons << " buttons, "
<< numHats << " hats";

View File

@ -20,6 +20,7 @@
#include "Event.hxx"
#include "EventHandlerConstants.hxx"
#include "JoyMap.hxx"
/**
An abstraction of a physical (real) joystick in Stella.
@ -31,12 +32,17 @@
Specific backend class(es) will inherit from this class, and implement
functionality specific to the device.
@author Stephen Anthony
@author Stephen Anthony, Thomas Jentzsch
*/
class PhysicalJoystick
{
friend class PhysicalJoystickHandler;
static constexpr char MODE_DELIM = '§'; // must not be '^', '|' or '#'
public:
PhysicalJoystick();
virtual ~PhysicalJoystick();
@ -65,10 +71,12 @@ class PhysicalJoystick
int ID;
string name;
int numAxes, numButtons, numHats;
Event::Type (*axisTable)[NUM_JOY_DIRS][kNumModes];
Event::Type (*btnTable)[kNumModes];
Event::Type (*hatTable)[NUM_JOY_HAT_DIRS][kNumModes];
int* axisLastValue;
int* buttonLast;
// Hashmaps of controller events
JoyMap joyMap;
private:
void getValues(const string& list, IntArray& map) const;

View File

@ -6,6 +6,7 @@ MODULE_OBJS := \
src/common/FBSurfaceSDL2.o \
src/common/FrameBufferSDL2.o \
src/common/FSNodeZIP.o \
src/common/JoyMap.o \
src/common/KeyMap.o \
src/common/Logger.o \
src/common/main.o \

View File

@ -32,8 +32,8 @@ BoosterGrip::BoosterGrip(Jack jack, const Event& event, const System& system)
myFireEvent = Event::JoystickZeroFire;
myTriggerEvent = Event::JoystickZeroFire5;
myBoosterEvent = Event::JoystickZeroFire9;
myXAxisValue = Event::SALeftAxis0Value;
myYAxisValue = Event::SALeftAxis1Value;
myXAxisValue = Event::PaddleZeroAnalog;
myYAxisValue = Event::PaddleOneAnalog;
}
else
{
@ -44,8 +44,8 @@ BoosterGrip::BoosterGrip(Jack jack, const Event& event, const System& system)
myFireEvent = Event::JoystickOneFire;
myTriggerEvent = Event::JoystickOneFire5;
myBoosterEvent = Event::JoystickOneFire9;
myXAxisValue = Event::SARightAxis0Value;
myYAxisValue = Event::SARightAxis1Value;
myXAxisValue = Event::PaddleTwoAnalog;
myYAxisValue = Event::PaddleThreeAnalog;
}
setPin(AnalogPin::Five, MAX_RESISTANCE);

View File

@ -135,6 +135,7 @@ Console::Console(OSystem& osystem, unique_ptr<Cartridge>& cart,
setControllers(md5);
// now that we know the controllers, enable the event mappings
myOSystem.eventHandler().enableEmulationKeyMappings();
myOSystem.eventHandler().enableEmulationJoyMappings();
// Mute audio and clear framebuffer while autodetection runs
myOSystem.sound().mute(1);
@ -804,6 +805,7 @@ void Console::setControllers(const string& rommd5)
myLeftControl = std::move(myCMHandler->leftController());
myRightControl = std::move(myCMHandler->rightController());
myOSystem.eventHandler().defineKeyControllerMappings("CM", Controller::Jack::Left);
myOSystem.eventHandler().defineJoyControllerMappings("CM", Controller::Jack::Left);
}
else
{
@ -849,6 +851,7 @@ unique_ptr<Controller> Console::getControllerPort(const string& rommd5,
unique_ptr<Controller> controller = std::move(myLeftControl);
myOSystem.eventHandler().defineKeyControllerMappings(controllerName, port);
myOSystem.eventHandler().defineJoyControllerMappings(controllerName, port);
if(controllerName == "JOYSTICK")
{

View File

@ -35,16 +35,16 @@ Driving::Driving(Jack jack, const Event& event, const System& system)
myCCWEvent = Event::JoystickZeroLeft;
myCWEvent = Event::JoystickZeroRight;
myFireEvent = Event::JoystickZeroFire;
myXAxisValue = Event::SALeftAxis0Value;
myYAxisValue = Event::SALeftAxis1Value;
myXAxisValue = Event::PaddleZeroAnalog;
myYAxisValue = Event::PaddleOneAnalog;
}
else
{
myCCWEvent = Event::JoystickOneLeft;
myCWEvent = Event::JoystickOneRight;
myFireEvent = Event::JoystickOneFire;
myXAxisValue = Event::SARightAxis0Value;
myYAxisValue = Event::SARightAxis1Value;
myXAxisValue = Event::PaddleTwoAnalog;
myYAxisValue = Event::PaddleThreeAnalog;
}
// Digital pins 3 and 4 are not connected

View File

@ -19,12 +19,13 @@
#define EVENT_HXX
#include <mutex>
#include <set>
#include "bspf.hxx"
#include "StellaKeys.hxx"
/**
@author Stephen Anthony, Christian Speckner
@author Stephen Anthony, Christian Speckner, Thomas Jentzsch
*/
class Event
{
@ -67,9 +68,6 @@ class Event
Combo1, Combo2, Combo3, Combo4, Combo5, Combo6, Combo7, Combo8,
Combo9, Combo10, Combo11, Combo12, Combo13, Combo14, Combo15, Combo16,
SALeftAxis0Value, SALeftAxis1Value,
SARightAxis0Value, SARightAxis1Value,
MouseAxisXValue, MouseAxisYValue,
MouseButtonLeftValue, MouseButtonRightValue,
@ -127,7 +125,9 @@ class Event
};
// Event list version, update if the id of existing event types changed
static constexpr Int32 VERSION = 2;
static constexpr Int32 VERSION = 3;
using EventSet = std::set<Event::Type>;
public:
/**
@ -200,4 +200,44 @@ class Event
Event& operator=(Event&&) = delete;
};
// Hold controller related events
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static const Event::EventSet LeftJoystickEvents = {
Event::JoystickZeroUp, Event::JoystickZeroDown, Event::JoystickZeroLeft, Event::JoystickZeroRight,
Event::JoystickZeroFire, Event::JoystickZeroFire5, Event::JoystickZeroFire9,
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static const Event::EventSet RightJoystickEvents = {
Event::JoystickOneUp, Event::JoystickOneDown, Event::JoystickOneLeft, Event::JoystickOneRight,
Event::JoystickOneFire, Event::JoystickOneFire5, Event::JoystickOneFire9
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static const Event::EventSet LeftPaddlesEvents = {
Event::PaddleZeroDecrease, Event::PaddleZeroIncrease, Event::PaddleZeroAnalog, Event::PaddleZeroFire,
Event::PaddleOneDecrease, Event::PaddleOneIncrease, Event::PaddleOneAnalog, Event::PaddleOneFire,
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static const Event::EventSet RightPaddlesEvents = {
Event::PaddleTwoDecrease, Event::PaddleTwoIncrease, Event::PaddleTwoAnalog, Event::PaddleTwoFire,
Event::PaddleThreeDecrease, Event::PaddleThreeIncrease, Event::PaddleThreeAnalog, Event::PaddleThreeFire,
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static const Event::EventSet LeftKeypadEvents = {
Event::KeyboardZero1, Event::KeyboardZero2, Event::KeyboardZero3,
Event::KeyboardZero4, Event::KeyboardZero5, Event::KeyboardZero6,
Event::KeyboardZero7, Event::KeyboardZero8, Event::KeyboardZero9,
Event::KeyboardZeroStar, Event::KeyboardZero0, Event::KeyboardZeroPound,
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static const Event::EventSet RightKeypadEvents = {
Event::KeyboardOne1, Event::KeyboardOne2, Event::KeyboardOne3,
Event::KeyboardOne4, Event::KeyboardOne5, Event::KeyboardOne6,
Event::KeyboardOne7, Event::KeyboardOne8, Event::KeyboardOne9,
Event::KeyboardOneStar, Event::KeyboardOne0, Event::KeyboardOnePound,
};
#endif

View File

@ -331,10 +331,12 @@ void EventHandler::handleSystemEvent(SystemEvent e, int, int)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::handleEvent(Event::Type event, bool pressed, bool repeated)
void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated)
{
// Take care of special events that aren't part of the emulation core
// or need to be preprocessed before passing them on
bool pressed = (value != 0);
switch(event)
{
////////////////////////////////////////////////////////////////////////
@ -896,7 +898,7 @@ void EventHandler::handleEvent(Event::Type event, bool pressed, bool repeated)
// Otherwise, pass it to the emulation core
if (!repeated)
myEvent.set(event, pressed);
myEvent.set(event, value);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1124,28 +1126,12 @@ bool EventHandler::addKeyMapping(Event::Type event, EventMode mode, StellaKey ke
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool EventHandler::addJoyAxisMapping(Event::Type event, EventMode mode,
int stick, int axis, int value,
bool EventHandler::addJoyMapping(Event::Type event, EventMode mode,
int stick, int button, JoyAxis axis, int value,
bool updateMenus)
{
#ifdef JOYSTICK_SUPPORT
bool mapped = myPJoyHandler->addAxisMapping(event, mode, stick, axis, value);
if(mapped && updateMenus)
setActionMappings(mode);
return mapped;
#else
return false;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool EventHandler::addJoyButtonMapping(Event::Type event, EventMode mode,
int stick, int button,
bool updateMenus)
{
#ifdef JOYSTICK_SUPPORT
bool mapped = myPJoyHandler->addBtnMapping(event, mode, stick, button);
bool mapped = myPJoyHandler->addJoyMapping(event, mode, stick, button, axis, value);
if (mapped && updateMenus)
setActionMappings(mode);
@ -1157,11 +1143,11 @@ bool EventHandler::addJoyButtonMapping(Event::Type event, EventMode mode,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool EventHandler::addJoyHatMapping(Event::Type event, EventMode mode,
int stick, int hat, JoyHat value,
int stick, int button, int hat, JoyHat dir,
bool updateMenus)
{
#ifdef JOYSTICK_SUPPORT
bool mapped = myPJoyHandler->addHatMapping(event, mode, stick, hat, value);
bool mapped = myPJoyHandler->addJoyHatMapping(event, mode, stick, button, hat, dir);
if (mapped && updateMenus)
setActionMappings(mode);
@ -1171,6 +1157,7 @@ bool EventHandler::addJoyHatMapping(Event::Type event, EventMode mode,
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::eraseMapping(Event::Type event, EventMode mode)
{

View File

@ -47,7 +47,7 @@ class PhysicalJoystick;
unchanged to the menu class, where (among other things) changing key
mapping can take place.
@author Stephen Anthony
@author Stephen Anthony, Thomas Jentzsch
*/
class EventHandler
{
@ -141,7 +141,7 @@ class EventHandler
@param pressed Pressed (true) or released (false)
@param repeated Repeated key (true) or first press/release (false)
*/
void handleEvent(Event::Type type, bool pressed = true, bool repeated = false);
void handleEvent(Event::Type type, Int32 value = 1, bool repeated = false);
/**
Handle events that must be processed each time a new console is
@ -162,14 +162,14 @@ class EventHandler
Event::Type eventForKey(EventMode mode, StellaKey key, StellaMod mod) const {
return myPKeyHandler->eventForKey(mode, key, mod);
}
Event::Type eventForJoyAxis(int stick, int axis, int value, EventMode mode) const {
return myPJoyHandler->eventForAxis(stick, axis, value, mode);
Event::Type eventForJoyAxis(EventMode mode, int stick, int axis, int value, int button) const {
return myPJoyHandler->eventForAxis(mode, stick, axis, value, button);
}
Event::Type eventForJoyButton(int stick, int button, EventMode mode) const {
return myPJoyHandler->eventForButton(stick, button, mode);
Event::Type eventForJoyButton(EventMode mode, int stick, int button) const {
return myPJoyHandler->eventForButton(mode, stick, button);
}
Event::Type eventForJoyHat(int stick, int hat, JoyHat value, EventMode mode) const {
return myPJoyHandler->eventForHat(stick, hat, value, mode);
Event::Type eventForJoyHat(EventMode mode, int stick, int hat, JoyHat value, int button) const {
return myPJoyHandler->eventForHat(mode, stick, hat, value, button);
}
/** Get description of given event and mode. */
@ -208,53 +208,55 @@ class EventHandler
/**
Bind a physical joystick axis direction to an event/action and regenerate
the mapping array(s).
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 value The value on 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 addJoyAxisMapping(Event::Type event, EventMode mode,
int stick, int axis, int value,
bool addJoyMapping(Event::Type event, EventMode mode, int stick,
int button, JoyAxis axis = JoyAxis::NONE, int value = 0,
bool updateMenus = true);
/**
Bind a physical joystick button to an event/action and regenerate the
mapping array(s).
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 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 addJoyButtonMapping(Event::Type event, EventMode mode, int stick, int button,
bool updateMenus = true);
/**
Bind a physical joystick hat direction 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 stick The joystick number
@param hat The joystick hat
@param value 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 hat, JoyHat value,
bool addJoyHatMapping(Event::Type event, EventMode mode, int stick,
int button, int hat, JoyHat dir,
bool updateMenus = true);
/**
Enable controller specific keyboard event mappings.
*/
void defineJoyControllerMappings(const string& controllerName, Controller::Jack port) {
myPJoyHandler->defineControllerMappings(controllerName, port);
}
/**
Enable emulation keyboard event mappings.
*/
void enableEmulationJoyMappings() {
myPJoyHandler->enableEmulationMappings();
}
/**
Erase the specified mapping.

View File

@ -38,14 +38,20 @@ enum class MouseButton {
NONE
};
static constexpr int JOY_CTRL_NONE = -1;
enum class JoyAxis {
X = 0,
Y = 1,
Z = 2,
NONE = JOY_CTRL_NONE
};
enum class JoyDir {
NEG = 0,
NEG = -1,
POS = 1,
NONE = 0,
ANALOG = 2
};

View File

@ -30,8 +30,8 @@ Joystick::Joystick(Jack jack, const Event& event, const System& system)
myLeftEvent = Event::JoystickZeroLeft;
myRightEvent = Event::JoystickZeroRight;
myFireEvent = Event::JoystickZeroFire;
myXAxisValue = Event::SALeftAxis0Value;
myYAxisValue = Event::SALeftAxis1Value;
myXAxisValue = Event::PaddleZeroAnalog;
myYAxisValue = Event::PaddleOneAnalog;
}
else
{
@ -40,8 +40,8 @@ Joystick::Joystick(Jack jack, const Event& event, const System& system)
myLeftEvent = Event::JoystickOneLeft;
myRightEvent = Event::JoystickOneRight;
myFireEvent = Event::JoystickOneFire;
myXAxisValue = Event::SARightAxis0Value;
myYAxisValue = Event::SARightAxis1Value;
myXAxisValue = Event::PaddleTwoAnalog;
myYAxisValue = Event::PaddleThreeAnalog;
}
}

View File

@ -53,8 +53,8 @@ Paddles::Paddles(Jack jack, const Event& event, const System& system,
if(!swappaddle) // First paddle is 0, second is 1
{
// These aren't affected by changes in axis orientation
myP0AxisValue = Event::SALeftAxis0Value;
myP1AxisValue = Event::SALeftAxis1Value;
myP0AxisValue = Event::PaddleZeroAnalog;
myP1AxisValue = Event::PaddleOneAnalog;
myP0FireEvent1 = Event::PaddleZeroFire;
myP0FireEvent2 = Event::JoystickZeroFire;
myP1FireEvent1 = Event::PaddleOneFire;
@ -89,8 +89,8 @@ Paddles::Paddles(Jack jack, const Event& event, const System& system,
else // First paddle is 1, second is 0
{
// These aren't affected by changes in axis orientation
myP0AxisValue = Event::SALeftAxis1Value;
myP1AxisValue = Event::SALeftAxis0Value;
myP0AxisValue = Event::PaddleOneAnalog;
myP1AxisValue = Event::PaddleZeroAnalog;
myP0FireEvent1 = Event::PaddleOneFire;
myP0FireEvent2 = Event::JoystickZeroFire9;
myP1FireEvent1 = Event::PaddleZeroFire;
@ -128,8 +128,8 @@ Paddles::Paddles(Jack jack, const Event& event, const System& system,
if(!swappaddle) // First paddle is 2, second is 3
{
// These aren't affected by changes in axis orientation
myP0AxisValue = Event::SARightAxis0Value;
myP1AxisValue = Event::SARightAxis1Value;
myP0AxisValue = Event::PaddleTwoAnalog;
myP1AxisValue = Event::PaddleThreeAnalog;
myP0FireEvent1 = Event::PaddleTwoFire;
myP0FireEvent2 = Event::JoystickOneFire;
myP1FireEvent1 = Event::PaddleThreeFire;
@ -164,8 +164,8 @@ Paddles::Paddles(Jack jack, const Event& event, const System& system,
else // First paddle is 3, second is 2
{
// These aren't affected by changes in axis orientation
myP0AxisValue = Event::SARightAxis1Value;
myP1AxisValue = Event::SARightAxis0Value;
myP0AxisValue = Event::PaddleThreeAnalog;
myP1AxisValue = Event::PaddleTwoAnalog;
myP0FireEvent1 = Event::PaddleThreeFire;
myP0FireEvent2 = Event::JoystickOneFire9;
myP1FireEvent1 = Event::PaddleTwoFire;

View File

@ -288,20 +288,20 @@ void ContextMenu::handleKeyDown(StellaKey key, StellaMod mod)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ContextMenu::handleJoyDown(int stick, int button)
{
handleEvent(instance().eventHandler().eventForJoyButton(stick, button, kMenuMode));
handleEvent(instance().eventHandler().eventForJoyButton(kMenuMode, stick, button));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ContextMenu::handleJoyAxis(int stick, int axis, int value)
void ContextMenu::handleJoyAxis(int stick, int axis, int value, int button)
{
if(value != 0) // we don't care about 'axis up' events
handleEvent(instance().eventHandler().eventForJoyAxis(stick, axis, value, kMenuMode));
handleEvent(instance().eventHandler().eventForJoyAxis(kMenuMode, stick, axis, value, button));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool ContextMenu::handleJoyHat(int stick, int hat, JoyHat value)
bool ContextMenu::handleJoyHat(int stick, int hat, JoyHat value, int button)
{
handleEvent(instance().eventHandler().eventForJoyHat(stick, hat, value, kMenuMode));
handleEvent(instance().eventHandler().eventForJoyHat(kMenuMode, stick, hat, value, button));
return true;
}

View File

@ -88,8 +88,8 @@ class ContextMenu : public Dialog, public CommandSender
void handleMouseWheel(int x, int y, int direction) override;
void handleKeyDown(StellaKey key, StellaMod mod) override;
void handleJoyDown(int stick, int button) override;
void handleJoyAxis(int stick, int axis, int value) override;
bool handleJoyHat(int stick, int hat, JoyHat value) override;
void handleJoyAxis(int stick, int axis, int value, int button) override;
bool handleJoyHat(int stick, int hat, JoyHat value, int button) override;
void handleEvent(Event::Type e);
void drawDialog() override;

View File

@ -591,7 +591,7 @@ bool Dialog::handleMouseClicks(int x, int y, MouseButton b)
void Dialog::handleJoyDown(int stick, int button)
{
Event::Type e =
instance().eventHandler().eventForJoyButton(stick, button, kMenuMode);
instance().eventHandler().eventForJoyButton(kMenuMode, stick, button);
// Unless a widget has claimed all responsibility for data, we assume
// that if an event exists for the given data, it should have priority.
@ -613,39 +613,39 @@ void Dialog::handleJoyUp(int stick, int button)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event::Type Dialog::getJoyAxisEvent(int stick, int axis, int value)
Event::Type Dialog::getJoyAxisEvent(int stick, int axis, int value, int button)
{
return instance().eventHandler().eventForJoyAxis(stick, axis, value, kMenuMode);
return instance().eventHandler().eventForJoyAxis(kMenuMode, stick, axis, value, button);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Dialog::handleJoyAxis(int stick, int axis, int value)
void Dialog::handleJoyAxis(int stick, int axis, int value, int button)
{
Event::Type e = getJoyAxisEvent(stick, axis, value);
Event::Type e = getJoyAxisEvent(stick, axis, value, button);
// Unless a widget has claimed all responsibility for data, we assume
// that if an event exists for the given data, it should have priority.
if(!handleNavEvent(e) && _focusedWidget)
{
if(_focusedWidget->wantsRaw() || e == Event::NoType)
_focusedWidget->handleJoyAxis(stick, axis, value);
_focusedWidget->handleJoyAxis(stick, axis, value, button);
else if(value != 0)
_focusedWidget->handleEvent(e);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Dialog::handleJoyHat(int stick, int hat, JoyHat value)
bool Dialog::handleJoyHat(int stick, int hat, JoyHat value, int button)
{
Event::Type e =
instance().eventHandler().eventForJoyHat(stick, hat, value, kMenuMode);
instance().eventHandler().eventForJoyHat(kMenuMode, stick, hat, value, button);
// Unless a widget has claimed all responsibility for data, we assume
// that if an event exists for the given data, it should have priority.
if(!handleNavEvent(e) && _focusedWidget)
{
if(_focusedWidget->wantsRaw() || e == Event::NoType)
return _focusedWidget->handleJoyHat(stick, hat, value);
return _focusedWidget->handleJoyHat(stick, hat, value, button);
else
return _focusedWidget->handleEvent(e);
}

View File

@ -139,10 +139,10 @@ class Dialog : public GuiObject
virtual bool handleMouseClicks(int x, int y, MouseButton b);
virtual void handleJoyDown(int stick, int button);
virtual void handleJoyUp(int stick, int button);
virtual void handleJoyAxis(int stick, int axis, int value);
virtual bool handleJoyHat(int stick, int hat, JoyHat value);
virtual void handleJoyAxis(int stick, int axis, int value, int button = JOY_CTRL_NONE);
virtual bool handleJoyHat(int stick, int hat, JoyHat value, int button = JOY_CTRL_NONE);
virtual void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
virtual Event::Type getJoyAxisEvent(int stick, int axis, int value);
virtual Event::Type getJoyAxisEvent(int stick, int axis, int value, int button);
Widget* findWidget(int x, int y) const; // Find the widget at pos x,y if any

View File

@ -299,7 +299,7 @@ void DialogContainer::handleJoyBtnEvent(int stick, int button, bool pressed)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DialogContainer::handleJoyAxisEvent(int stick, int axis, int value)
void DialogContainer::handleJoyAxisEvent(int stick, int axis, int value, int button)
{
if(myDialogStack.empty())
return;
@ -307,21 +307,23 @@ void DialogContainer::handleJoyAxisEvent(int stick, int axis, int value)
// Only stop firing events if it's the current stick
if(myCurrentAxisDown.stick == stick && value == 0)
{
cerr << "handleJoyAxisEvent 0" << endl;
myCurrentAxisDown.stick = myCurrentAxisDown.axis = -1;
}
else if(value != 0) // never repeat the 'off' event
{
cerr << "handleJoyAxisEvent repeat" << endl;
// Now account for repeated axis events (press and hold)
myCurrentAxisDown.stick = stick;
myCurrentAxisDown.axis = axis;
myCurrentAxisDown.value = value;
myAxisRepeatTime = myTime + kRepeatInitialDelay;
}
myDialogStack.top()->handleJoyAxis(stick, axis, value);
myDialogStack.top()->handleJoyAxis(stick, axis, value, button);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DialogContainer::handleJoyHatEvent(int stick, int hat, JoyHat value)
void DialogContainer::handleJoyHatEvent(int stick, int hat, JoyHat value, int button)
{
if(myDialogStack.empty())
return;
@ -339,7 +341,7 @@ void DialogContainer::handleJoyHatEvent(int stick, int hat, JoyHat value)
myCurrentHatDown.value = value;
myHatRepeatTime = myTime + kRepeatInitialDelay;
}
myDialogStack.top()->handleJoyHat(stick, hat, value);
myDialogStack.top()->handleJoyHat(stick, hat, value, button);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -108,7 +108,7 @@ class DialogContainer
@param axis The joystick axis
@param value Value associated with given axis
*/
void handleJoyAxisEvent(int stick, int axis, int value);
void handleJoyAxisEvent(int stick, int axis, int value, int button);
/**
Handle a joystick hat event.
@ -117,7 +117,7 @@ class DialogContainer
@param hat The joystick hat
@param value Value associated with given hat
*/
void handleJoyHatEvent(int stick, int hat, JoyHat value);
void handleJoyHatEvent(int stick, int hat, JoyHat value, int button);
/**
Draw the stack of menus (full indicates to redraw all items).

View File

@ -97,7 +97,7 @@ void EditTextWidget::drawWidget(bool hilite)
_changed && onTop && isEnabled()
? kDbgChangedTextColor
: onTop && isEnabled() ? _textcolor : kColor,
TextAlign::Left, -_editScrollOffset, false);
TextAlign::Left, isEditable() ? -_editScrollOffset : 0, !isEditable());
// Draw the caret
drawCaret();

View File

@ -47,6 +47,7 @@ EventMappingWidget::EventMappingWidget(GuiObject* boss, const GUI::Font& font,
myLastAxis(0),
myLastHat(0),
myLastValue(0),
myLastButton(JOY_CTRL_NONE),
myFirstTime(true)
{
const int fontHeight = font.getFontHeight(),
@ -151,6 +152,7 @@ void EventMappingWidget::setDefaults()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventMappingWidget::startRemapping()
{
cerr << "startRemapping" << endl;
if(myActionSelected < 0 || myRemapStatus)
return;
@ -159,9 +161,10 @@ void EventMappingWidget::startRemapping()
// Reset all previous events for determining correct axis/hat values
myLastStick = myLastAxis = myLastHat = myLastValue = -1;
myLastButton = JOY_CTRL_NONE;
// Reset the previously aggregated key mappings
myMod = myKey = 0;
myMod = myLastKey = 0;
// Disable all other widgets while in remap mode, except enable 'Cancel'
enableButtons(false);
@ -210,9 +213,11 @@ void EventMappingWidget::stopRemapping()
{
// Turn off remap mode
myRemapStatus = false;
cerr << "stopRemapping " << myRemapStatus << endl;
// Reset all previous events for determining correct axis/hat values
myLastStick = myLastAxis = myLastHat = myLastValue = -1;
myLastButton = JOY_CTRL_NONE;
// And re-enable all the widgets
enableButtons(true);
@ -257,7 +262,7 @@ bool EventMappingWidget::handleKeyDown(StellaKey key, StellaMod mod)
// Remap keys in remap mode
if (myRemapStatus && myActionSelected >= 0)
{
myKey = key;
myLastKey = key;
myMod |= mod;
}
return true;
@ -272,7 +277,7 @@ bool EventMappingWidget::handleKeyUp(StellaKey key, StellaMod mod)
{
Event::Type event =
instance().eventHandler().eventAtIndex(myActionSelected, myEventMode);
if (instance().eventHandler().addKeyMapping(event, myEventMode, StellaKey(myKey), StellaMod(myMod)))
if (instance().eventHandler().addKeyMapping(event, myEventMode, StellaKey(myLastKey), StellaMod(myMod)))
stopRemapping();
}
return true;
@ -281,19 +286,40 @@ bool EventMappingWidget::handleKeyUp(StellaKey key, StellaMod mod)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventMappingWidget::handleJoyDown(int stick, int button)
{
cerr << "handleJoyDown" << endl;
// Remap joystick buttons in remap mode
if(myRemapStatus && myActionSelected >= 0)
{
Event::Type event =
instance().eventHandler().eventAtIndex(myActionSelected, myEventMode);
if(instance().eventHandler().addJoyButtonMapping(event, myEventMode, stick, button))
stopRemapping();
cerr << "remap button start " << myRemapStatus << endl;
myLastStick = stick;
myLastButton = button;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventMappingWidget::handleJoyAxis(int stick, int axis, int value)
void EventMappingWidget::handleJoyUp(int stick, int button)
{
cerr << "handleJoyUp" << endl;
// Remap joystick buttons in remap mode
if (myRemapStatus && myActionSelected >= 0)
{
if (myLastStick == stick && myLastButton == button)
{
EventHandler& eh = instance().eventHandler();
Event::Type event = eh.eventAtIndex(myActionSelected, myEventMode);
cerr << "remap button stop" << endl;
// This maps solo button presses only
if (eh.addJoyMapping(event, myEventMode, stick, button))
stopRemapping();
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventMappingWidget::handleJoyAxis(int stick, int axis, int value, int button)
{
cerr << "handleJoyAxis:" << axis << ", " << value << ", (" << stick << ", " << myLastStick << "), (" << axis << ", " << myLastAxis << ")" << endl;
// Remap joystick axes in remap mode
// There are two phases to detection:
// First, detect an axis 'on' event
@ -301,8 +327,9 @@ void EventMappingWidget::handleJoyAxis(int stick, int axis, int value)
if(myRemapStatus && myActionSelected >= 0)
{
// Detect the first axis event that represents 'on'
if(myLastStick == -1 && myLastAxis == -1 && value != 0)
if((myLastStick == -1 || myLastStick == stick) && myLastAxis == -1 && value != 0)
{
cerr << "remap start" << endl;
myLastStick = stick;
myLastAxis = axis;
myLastValue = value;
@ -311,19 +338,18 @@ void EventMappingWidget::handleJoyAxis(int stick, int axis, int value)
// stick and axis, but turns the axis 'off'
else if(myLastStick == stick && axis == myLastAxis && value == 0)
{
value = myLastValue;
EventHandler& eh = instance().eventHandler();
Event::Type event = eh.eventAtIndex(myActionSelected, myEventMode);
Event::Type event =
instance().eventHandler().eventAtIndex(myActionSelected, myEventMode);
if(instance().eventHandler().addJoyAxisMapping(event, myEventMode,
stick, axis, value))
cerr << "remap stop" << endl;
if (eh.addJoyMapping(event, myEventMode, stick, myLastButton, JoyAxis(axis), myLastValue))
stopRemapping();
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool EventMappingWidget::handleJoyHat(int stick, int hat, JoyHat value)
bool EventMappingWidget::handleJoyHat(int stick, int hat, JoyHat value, int button)
{
// Remap joystick hats in remap mode
// There are two phases to detection:
@ -332,7 +358,7 @@ bool EventMappingWidget::handleJoyHat(int stick, int hat, JoyHat value)
if(myRemapStatus && myActionSelected >= 0)
{
// Detect the first hat event that represents a valid direction
if(myLastStick == -1 && myLastHat == -1 && value != JoyHat::CENTER)
if((myLastStick == -1 || myLastStick == stick) && myLastHat == -1 && value != JoyHat::CENTER)
{
myLastStick = stick;
myLastHat = hat;
@ -344,12 +370,10 @@ bool EventMappingWidget::handleJoyHat(int stick, int hat, JoyHat value)
// stick and hat, but centers the hat
else if(myLastStick == stick && hat == myLastHat && value == JoyHat::CENTER)
{
value = JoyHat(myLastValue);
EventHandler& eh = instance().eventHandler();
Event::Type event = eh.eventAtIndex(myActionSelected, myEventMode);
Event::Type event =
instance().eventHandler().eventAtIndex(myActionSelected, myEventMode);
if(instance().eventHandler().addJoyHatMapping(event, myEventMode,
stick, hat, value))
if (eh.addJoyHatMapping(event, myEventMode, stick, myLastButton, hat, JoyHat(myLastValue)))
{
stopRemapping();
return true;

View File

@ -60,8 +60,9 @@ class EventMappingWidget : public Widget, public CommandSender
bool handleKeyDown(StellaKey key, StellaMod mod) override;
bool handleKeyUp(StellaKey key, StellaMod mod) override;
void handleJoyDown(int stick, int button) override;
void handleJoyAxis(int stick, int axis, int value) override;
bool handleJoyHat(int stick, int hat, JoyHat value) override;
void handleJoyUp(int stick, int button) override;
void handleJoyAxis(int stick, int axis, int value, int button) override;
bool handleJoyHat(int stick, int hat, JoyHat value, int button) override;
void loadConfig() override;
void saveConfig();
@ -108,7 +109,9 @@ class EventMappingWidget : public Widget, public CommandSender
// Aggregates the modifier flags of the mapping
int myMod;
// Saves the last *pressed* key
int myKey;
int myLastKey;
// Saves the last *pressed* button
int myLastButton;
bool myFirstTime;

View File

@ -481,27 +481,39 @@ void InputDialog::handleJoyDown(int stick, int button)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void InputDialog::handleJoyAxis(int stick, int axis, int value)
void InputDialog::handleJoyUp(int stick, int button)
{
// Remap joystick axis in remap mode, otherwise pass to parent dialog
// Remap joystick buttons in remap mode, otherwise pass to parent dialog
if (myEmulEventMapper->remapMode())
myEmulEventMapper->handleJoyAxis(stick, axis, value);
myEmulEventMapper->handleJoyUp(stick, button);
else if (myMenuEventMapper->remapMode())
myMenuEventMapper->handleJoyAxis(stick, axis, value);
myMenuEventMapper->handleJoyUp(stick, button);
else
Dialog::handleJoyAxis(stick, axis, value);
Dialog::handleJoyUp(stick, button);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool InputDialog::handleJoyHat(int stick, int hat, JoyHat value)
void InputDialog::handleJoyAxis(int stick, int axis, int value, int button)
{
// Remap joystick axis in remap mode, otherwise pass to parent dialog
if(myEmulEventMapper->remapMode())
myEmulEventMapper->handleJoyAxis(stick, axis, value, button);
else if(myMenuEventMapper->remapMode())
myMenuEventMapper->handleJoyAxis(stick, axis, value, button);
else
Dialog::handleJoyAxis(stick, axis, value, button);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool InputDialog::handleJoyHat(int stick, int hat, JoyHat value, int button)
{
// Remap joystick hat in remap mode, otherwise pass to parent dialog
if(myEmulEventMapper->remapMode())
return myEmulEventMapper->handleJoyHat(stick, hat, value);
return myEmulEventMapper->handleJoyHat(stick, hat, value, button);
else if(myMenuEventMapper->remapMode())
return myMenuEventMapper->handleJoyHat(stick, hat, value);
return myMenuEventMapper->handleJoyHat(stick, hat, value, button);
else
return Dialog::handleJoyHat(stick, hat, value);
return Dialog::handleJoyHat(stick, hat, value, button);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -46,8 +46,9 @@ class InputDialog : public Dialog
void handleKeyDown(StellaKey key, StellaMod mod) override;
void handleKeyUp(StellaKey key, StellaMod mod) override;
void handleJoyDown(int stick, int button) override;
void handleJoyAxis(int stick, int axis, int value) override;
bool handleJoyHat(int stick, int hat, JoyHat value) override;
void handleJoyUp(int stick, int button) override;
void handleJoyAxis(int stick, int axis, int value, int button) override;
bool handleJoyHat(int stick, int hat, JoyHat value, int button) override;
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
void loadConfig() override;

View File

@ -469,7 +469,7 @@ void LauncherDialog::handleKeyDown(StellaKey key, StellaMod mod)
void LauncherDialog::handleJoyDown(int stick, int button)
{
// open power-up options and settings for 2nd and 4th button if not mapped otherwise
Event::Type e = instance().eventHandler().eventForJoyButton(stick, button, kMenuMode);
Event::Type e = instance().eventHandler().eventForJoyButton(kMenuMode, stick, button);
if (button == 1 && (e == Event::UIOK || e == Event::NoType))
myGlobalProps->open();
@ -480,9 +480,9 @@ void LauncherDialog::handleJoyDown(int stick, int button)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event::Type LauncherDialog::getJoyAxisEvent(int stick, int axis, int value)
Event::Type LauncherDialog::getJoyAxisEvent(int stick, int axis, int value, int button)
{
Event::Type e = instance().eventHandler().eventForJoyAxis(stick, axis, value, kMenuMode);
Event::Type e = instance().eventHandler().eventForJoyAxis(kMenuMode, stick, axis, value, button);
if(myUseMinimalUI)
{

View File

@ -92,7 +92,7 @@ class LauncherDialog : public Dialog
void handleMouseDown(int x, int y, MouseButton b, int clickCount) override;
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
void handleJoyDown(int stick, int button) override;
Event::Type getJoyAxisEvent(int stick, int axis, int value) override;
Event::Type getJoyAxisEvent(int stick, int axis, int value, int button) override;
void loadConfig() override;
void updateUI();

View File

@ -79,8 +79,8 @@ class Widget : public GuiObject
virtual bool handleMouseClicks(int x, int y, MouseButton b) { return false; }
virtual void handleJoyDown(int stick, int button) { }
virtual void handleJoyUp(int stick, int button) { }
virtual void handleJoyAxis(int stick, int axis, int value) { }
virtual bool handleJoyHat(int stick, int hat, JoyHat value) { return false; }
virtual void handleJoyAxis(int stick, int axis, int value, int button = JOY_CTRL_NONE) { }
virtual bool handleJoyHat(int stick, int hat, JoyHat value, int button = JOY_CTRL_NONE) { return false; }
virtual bool handleEvent(Event::Type event) { return false; }
void setDirty() override;

View File

@ -376,6 +376,7 @@
<ClCompile Include="..\common\FpsMeter.cxx" />
<ClCompile Include="..\common\FrameBufferSDL2.cxx" />
<ClCompile Include="..\common\FSNodeZIP.cxx" />
<ClCompile Include="..\common\JoyMap.cxx" />
<ClCompile Include="..\common\KeyMap.cxx" />
<ClCompile Include="..\common\Logger.cxx" />
<ClCompile Include="..\common\main.cxx" />
@ -1072,6 +1073,7 @@
<ClInclude Include="..\common\FrameBufferSDL2.hxx" />
<ClInclude Include="..\common\FSNodeFactory.hxx" />
<ClInclude Include="..\common\FSNodeZIP.hxx" />
<ClInclude Include="..\common\JoyMap.hxx" />
<ClInclude Include="..\common\KeyMap.hxx" />
<ClInclude Include="..\common\LinkedObjectPool.hxx" />
<ClInclude Include="..\common\Logger.hxx" />

View File

@ -975,6 +975,9 @@
<ClCompile Include="..\gui\R77HelpDialog.cxx">
<Filter>Source Files\gui</Filter>
</ClCompile>
<ClCompile Include="..\common\JoyMap.cxx">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\common\bspf.hxx">
@ -1994,6 +1997,9 @@
<ClInclude Include="..\common\KeyMap.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\common\JoyMap.hxx">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="stella.ico">