stella/src/common/PJoystickHandler.cxx

750 lines
26 KiB
C++

//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-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 "OSystem.hxx"
#include "Console.hxx"
#include "Joystick.hxx"
#include "Settings.hxx"
#include "EventHandler.hxx"
#include "DialogContainer.hxx"
#include "PJoystickHandler.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::PhysicalJoystickHandler(
OSystem& system, EventHandler& handler, Event& event)
: myOSystem(system),
myHandler(handler),
myEvent(event)
{
// Load previously saved joystick mapping (if any) from settings
istringstream buf(myOSystem.settings().getString("joymap"));
string joymap, joyname;
// First check the event type, and disregard the entire mapping if it's invalid
getline(buf, joymap, '^');
if(atoi(joymap.c_str()) == Event::LastType)
{
// Otherwise, put each joystick mapping entry into the database
while(getline(buf, joymap, '^'))
{
istringstream namebuf(joymap);
getline(namebuf, joyname, '|');
if(joyname.length() != 0)
myDatabase.emplace(joyname, StickInfo(joymap));
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int PhysicalJoystickHandler::add(PhysicalJoystickPtr stick)
{
// Skip if we couldn't open it for any reason
if(stick->ID < 0)
return -1;
// Figure out what type of joystick this is
bool specialAdaptor = false;
if(BSPF::containsIgnoreCase(stick->name, "2600-daptor"))
{
specialAdaptor = true;
if(stick->numAxes == 4)
{
// TODO - detect controller type based on z-axis
stick->name = "2600-daptor D9";
}
else if(stick->numAxes == 3)
{
stick->name = "2600-daptor II";
}
else
stick->name = "2600-daptor";
}
else if(BSPF::containsIgnoreCase(stick->name, "Stelladaptor"))
{
stick->name = "Stelladaptor";
specialAdaptor = true;
}
else
{
// We need unique names for mappable devices
// For non-unique names that already have a database entry,
// we append ' #x', where 'x' increases consecutively
int count = 0;
for(const auto& i: myDatabase)
if(BSPF::startsWithIgnoreCase(i.first, stick->name) && i.second.joy)
++count;
if(count > 0)
{
ostringstream name;
name << stick->name << " #" << count+1;
stick->name = name.str();
}
stick->type = PhysicalJoystick::JT_REGULAR;
}
// The stick *must* be inserted here, since it may be used below
mySticks[stick->ID] = stick;
// Map the stelladaptors we've found according to the specified ports
if(specialAdaptor)
mapStelladaptors(myOSystem.settings().getString("saport"));
// Add stick to database
auto it = myDatabase.find(stick->name);
if(it != myDatabase.end()) // already present
{
it->second.joy = stick;
stick->setMap(it->second.mapping);
}
else // adding for the first time
{
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 < 2; ++i)
{
for(int j = 0; j < 2; ++j)
myEvent.set(SA_Axis[i][j], 0);
for(int j = 0; j < 4; ++j)
myEvent.set(SA_Button[i][j], 0);
for(int j = 0; j < 12; ++j)
myEvent.set(SA_Key[i][j], 0);
}
return stick->ID;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::remove(int id)
{
// When a joystick is removed, we delete the actual joystick object but
// remember its mapping, since it will eventually be saved to settings
// Sticks that are removed must have initially been added
// So we use the 'active' joystick list to access them
try
{
PhysicalJoystickPtr stick = mySticks.at(id);
auto it = myDatabase.find(stick->name);
if(it != myDatabase.end() && it->second.joy == stick)
{
ostringstream buf;
buf << "Removed joystick " << mySticks[id]->ID << ":" << endl
<< " " << mySticks[id]->about() << endl;
myOSystem.logMessage(buf.str(), 1);
// Remove joystick, but remember mapping
it->second.mapping = stick->getMap();
it->second.joy = nullptr;
mySticks.erase(id);
return true;
}
}
catch(const std::out_of_range&)
{
// fall through to indicate remove failed
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::remove(const string& name)
{
auto it = myDatabase.find(name);
if(it != myDatabase.end() && it->second.joy == nullptr)
{
myDatabase.erase(it);
return true;
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::mapStelladaptors(const string& saport)
{
// saport will have two values:
// 'lr' means treat first valid adaptor as left port, second as right port
// 'rl' means treat first valid adaptor as right port, second as left port
// We know there will be only two such devices (at most), since the logic
// in setupJoysticks take care of that
int saCount = 0;
int saOrder[2] = { 1, 2 };
if(BSPF::equalsIgnoreCase(saport, "rl"))
{
saOrder[0] = 2; saOrder[1] = 1;
}
for(auto& stick: mySticks)
{
if(BSPF::startsWithIgnoreCase(stick.second->name, "Stelladaptor"))
{
if(saOrder[saCount] == 1)
{
stick.second->name += " (emulates left joystick port)";
stick.second->type = PhysicalJoystick::JT_STELLADAPTOR_LEFT;
}
else if(saOrder[saCount] == 2)
{
stick.second->name += " (emulates right joystick port)";
stick.second->type = PhysicalJoystick::JT_STELLADAPTOR_RIGHT;
}
saCount++;
}
else if(BSPF::startsWithIgnoreCase(stick.second->name, "2600-daptor"))
{
if(saOrder[saCount] == 1)
{
stick.second->name += " (emulates left joystick port)";
stick.second->type = PhysicalJoystick::JT_2600DAPTOR_LEFT;
}
else if(saOrder[saCount] == 2)
{
stick.second->name += " (emulates right joystick port)";
stick.second->type = PhysicalJoystick::JT_2600DAPTOR_RIGHT;
}
saCount++;
}
}
myOSystem.settings().setValue("saport", saport);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::setDefaultMapping(Event::Type event, EventMode mode)
{
eraseMapping(event, mode);
for(auto& i: mySticks)
setStickDefaultMapping(i.first, event, mode);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::setStickDefaultMapping(int stick,
Event::Type event, EventMode mode)
{
bool eraseAll = (event == Event::NoType);
auto setDefaultAxis = [&](int a_stick, int a_axis, int a_value, Event::Type a_event)
{
if(eraseAll || a_event == event)
myHandler.addJoyAxisMapping(a_event, mode, a_stick, a_axis, 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
if(stick == 0)
{
// Left joystick left/right directions (assume joystick zero)
setDefaultAxis( 0, 0, 0, Event::JoystickZeroLeft );
setDefaultAxis( 0, 0, 1, Event::JoystickZeroRight );
// Left joystick up/down directions (assume joystick zero)
setDefaultAxis( 0, 1, 0, Event::JoystickZeroUp );
setDefaultAxis( 0, 1, 1, Event::JoystickZeroDown );
// Left joystick (assume joystick zero, button zero)
setDefaultBtn( 0, 0, Event::JoystickZeroFire );
// Left joystick left/right directions (assume joystick zero and hat 0)
setDefaultHat( 0, 0, JoyHat::LEFT, Event::JoystickZeroLeft );
setDefaultHat( 0, 0, JoyHat::RIGHT, Event::JoystickZeroRight );
// Left joystick up/down directions (assume joystick zero and hat 0)
setDefaultHat( 0, 0, JoyHat::UP, Event::JoystickZeroUp );
setDefaultHat( 0, 0, JoyHat::DOWN, Event::JoystickZeroDown );
}
else if(stick == 1)
{
// Right joystick left/right directions (assume joystick one)
setDefaultAxis( 1, 0, 0, Event::JoystickOneLeft );
setDefaultAxis( 1, 0, 1, Event::JoystickOneRight );
// Right joystick left/right directions (assume joystick one)
setDefaultAxis( 1, 1, 0, Event::JoystickOneUp );
setDefaultAxis( 1, 1, 1, Event::JoystickOneDown );
// Right joystick (assume joystick one, button zero)
setDefaultBtn( 1, 0, Event::JoystickOneFire );
// Right joystick left/right directions (assume joystick one and hat 0)
setDefaultHat( 1, 0, JoyHat::LEFT, Event::JoystickOneLeft );
setDefaultHat( 1, 0, JoyHat::RIGHT, Event::JoystickOneRight );
// Right joystick up/down directions (assume joystick one and hat 0)
setDefaultHat( 1, 0, JoyHat::UP, Event::JoystickOneUp );
setDefaultHat( 1, 0, JoyHat::DOWN, Event::JoystickOneDown );
}
break;
case kMenuMode: // Default menu/UI events
if(stick == 0)
{
setDefaultAxis( 0, 0, 0, Event::UILeft );
setDefaultAxis( 0, 0, 1, Event::UIRight );
setDefaultAxis( 0, 1, 0, Event::UIUp );
setDefaultAxis( 0, 1, 1, Event::UIDown );
// Left joystick (assume joystick zero, button zero)
setDefaultBtn( 0, 0, Event::UISelect );
setDefaultHat( 0, 0, JoyHat::LEFT, Event::UILeft );
setDefaultHat( 0, 0, JoyHat::RIGHT, Event::UIRight );
setDefaultHat( 0, 0, JoyHat::UP, Event::UIUp );
setDefaultHat( 0, 0, JoyHat::DOWN, Event::UIDown );
}
break;
default:
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::eraseMapping(Event::Type event, EventMode mode)
{
// If event is 'NoType', erase and reset all mappings
// Otherwise, only reset the given event
if(event == Event::NoType)
{
for(auto& stick: mySticks)
stick.second->eraseMap(mode); // erase all events
}
else
{
for(auto& stick: mySticks)
stick.second->eraseEvent(event, mode); // only reset the specific event
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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;
}
myOSystem.settings().setValue("joymap", joybuf.str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string PhysicalJoystickHandler::getMappingDesc(Event::Type event, EventMode mode) const
{
ostringstream buf;
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->btnTable[button][mode] == event)
{
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 < 2; ++dir)
{
if(j->axisTable[axis][dir][mode] == event)
{
if(buf.str() != "")
buf << ", ";
buf << "J" << stick << "/A" << axis;
if(Event::isAnalog(event))
{
dir = 2; // 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 < 4; ++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;
}
}
}
}
}
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::addAxisMapping(Event::Type event, EventMode mode,
int stick, int axis, int value)
{
const PhysicalJoystickPtr j = joy(stick);
if(j)
{
if(axis >= 0 && axis < j->numAxes && event < Event::LastType)
{
// 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][0][mode] = j->axisTable[axis][1][mode] = event;
else
{
// Otherwise, turn off the analog event(s) for this axis
if(Event::isAnalog(j->axisTable[axis][0][mode]))
j->axisTable[axis][0][mode] = Event::NoType;
if(Event::isAnalog(j->axisTable[axis][1][mode]))
j->axisTable[axis][1][mode] = Event::NoType;
j->axisTable[axis][(value > 0)][mode] = event;
}
return true;
}
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::addBtnMapping(Event::Type event, EventMode mode,
int stick, int button)
{
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)
{
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;
return true;
}
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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)
{
case PhysicalJoystick::JT_REGULAR:
if(myHandler.state() == EventHandlerState::EMULATION)
{
// Every axis event has two associated values, negative and positive
Event::Type eventAxisNeg = j->axisTable[axis][0][kEmulationMode];
Event::Type eventAxisPos = j->axisTable[axis][1][kEmulationMode];
// 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))
{
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:
{
// Otherwise, we know the event is digital
if(value > Joystick::deadzone())
myHandler.handleEvent(eventAxisPos);
else if(value < -Joystick::deadzone())
myHandler.handleEvent(eventAxisNeg);
else
{
// Treat any deadzone value as zero
value = 0;
// Now filter out consecutive, similar values
// (only pass on the event if the state has changed)
if(j->axisLastValue[axis] != value)
{
// Turn off both events, since we don't know exactly which one
// was previously activated.
myHandler.handleEvent(eventAxisNeg, false);
myHandler.handleEvent(eventAxisPos, false);
}
}
j->axisLastValue[axis] = value;
break;
}
}
}
else if(myHandler.hasOverlay())
{
// First, clamp the values to simulate digital input
// (the only thing that the underlying code understands)
if(value > Joystick::deadzone())
value = 32000;
else if(value < -Joystick::deadzone())
value = -32000;
else
value = 0;
// Now filter out consecutive, similar values
// (only pass on the event if the state has changed)
if(value != j->axisLastValue[axis])
{
myHandler.overlay().handleJoyAxisEvent(stick, axis, value);
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 < 2)
myEvent.set(SA_Axis[0][axis], value);
break; // axis on left controller (0)
case PhysicalJoystick::JT_STELLADAPTOR_RIGHT:
case PhysicalJoystick::JT_2600DAPTOR_RIGHT:
if(axis < 2)
myEvent.set(SA_Axis[1][axis], value);
break; // axis on right controller (1)
default:
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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)
{
case PhysicalJoystick::JT_REGULAR:
// Handle buttons which switch eventhandler state
if(pressed && myHandler.changeStateByEvent(j->btnTable[button][kEmulationMode]))
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);
else if(myHandler.hasOverlay())
myHandler.overlay().handleJoyBtnEvent(stick, button, pressed);
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-2][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::Keyboard:
if(button < 12) myEvent.set(SA_Key[j->type-4][button], pressed ? 1 : 0);
break;
default:
if(button < 4) myEvent.set(SA_Button[j->type-4][button], pressed ? 1 : 0);
}
switch(myOSystem.console().rightController().type())
{
case Controller::Keyboard:
if(button < 12) myEvent.set(SA_Key[j->type-4][button], pressed ? 1 : 0);
break;
default:
if(button < 4) myEvent.set(SA_Button[j->type-4][button], pressed ? 1 : 0);
}
}
break; // 2600DAPTOR button
default:
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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
if(myHandler.state() == EventHandlerState::EMULATION)
{
const PhysicalJoystickPtr j = joy(stick);
if(!j) return;
myHandler.handleEvent(j->hatTable[hat][int(JoyHat::UP)][kEmulationMode],
value & EVENT_HATUP_M);
myHandler.handleEvent(j->hatTable[hat][int(JoyHat::RIGHT)][kEmulationMode],
value & EVENT_HATRIGHT_M);
myHandler.handleEvent(j->hatTable[hat][int(JoyHat::DOWN)][kEmulationMode],
value & EVENT_HATDOWN_M);
myHandler.handleEvent(j->hatTable[hat][int(JoyHat::LEFT)][kEmulationMode],
value & EVENT_HATLEFT_M);
}
else if(myHandler.hasOverlay())
{
if(value == EVENT_HATCENTER_M)
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::CENTER);
else
{
if(value & EVENT_HATUP_M)
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::UP);
if(value & EVENT_HATRIGHT_M)
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::RIGHT);
if(value & EVENT_HATDOWN_M)
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::DOWN);
if(value & EVENT_HATLEFT_M)
myHandler.overlay().handleJoyHatEvent(stick, hat, JoyHat::LEFT);
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VariantList PhysicalJoystickHandler::database() const
{
VariantList db;
for(const auto& i: myDatabase)
VarList::push_back(db, i.first, i.second.joy ? i.second.joy->ID : -1);
return db;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ostream& operator<<(ostream& os, const PhysicalJoystickHandler& jh)
{
os << "---------------------------------------------------------" << endl
<< "joy database:" << endl;
for(const auto& i: jh.myDatabase)
os << i.first << endl << i.second << endl << endl;
os << "---------------------" << endl
<< "joy active:" << endl;
for(const auto& i: jh.mySticks)
os << i.first << ": " << *i.second << endl;
os << "---------------------------------------------------------"
<< endl << endl << endl;
return os;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Used by the Stelladaptor to send absolute axis values
const Event::Type PhysicalJoystickHandler::SA_Axis[2][2] = {
{ Event::SALeftAxis0Value, Event::SALeftAxis1Value },
{ Event::SARightAxis0Value, Event::SARightAxis1Value }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 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[2][4] = {
{ Event::JoystickZeroFire, Event::JoystickZeroFire9,
Event::JoystickZeroFire5, Event::JoystickZeroFire9 },
{ Event::JoystickOneFire, Event::JoystickOneFire9,
Event::JoystickOneFire5, Event::JoystickOneFire9 }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Used by the 2600-daptor to map button presses to keypad keys
const Event::Type PhysicalJoystickHandler::SA_Key[2][12] = {
{ 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 }
};