mirror of https://github.com/stella-emu/stella.git
750 lines
26 KiB
C++
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 }
|
|
};
|