First pass at reworking EventHandler, splitting it up into more manageable pieces.

For now, joystick-related stuff is moved into its own classes.
No improvements are made yet; just refactoring and moving code around.
This reduces EventHandler from ~2500 lines to ~2000 lines.
This commit is contained in:
Stephen Anthony 2018-03-17 18:00:44 -02:30
parent 593ec2fdc9
commit 76b6026d30
15 changed files with 1348 additions and 1169 deletions

View File

@ -96,8 +96,8 @@ void EventHandlerSDL2::pollEvent()
case SDL_JOYBUTTONUP:
case SDL_JOYBUTTONDOWN:
{
handleJoyEvent(myEvent.jbutton.which, myEvent.jbutton.button,
myEvent.jbutton.state == SDL_PRESSED ? 1 : 0);
handleJoyBtnEvent(myEvent.jbutton.which, myEvent.jbutton.button,
myEvent.jbutton.state == SDL_PRESSED ? 1 : 0);
break;
}
@ -127,12 +127,12 @@ void EventHandlerSDL2::pollEvent()
case SDL_JOYDEVICEADDED:
{
addJoystick(new JoystickSDL2(myEvent.jdevice.which));
addPhysicalJoystick(new JoystickSDL2(myEvent.jdevice.which));
break; // SDL_JOYDEVICEADDED
}
case SDL_JOYDEVICEREMOVED:
{
removeJoystick(myEvent.jdevice.which);
removePhysicalJoystick(myEvent.jdevice.which);
break; // SDL_JOYDEVICEREMOVED
}
#endif

View File

@ -20,6 +20,7 @@
#include "SDL_lib.hxx"
#include "EventHandler.hxx"
#include "PhysicalJoystick.hxx"
/**
This class handles event collection from the point of view of the specific
@ -58,9 +59,9 @@ class EventHandlerSDL2 : public EventHandler
private:
SDL_Event myEvent;
// A thin wrapper around a basic StellaJoystick, holding the pointer to
// the underlying SDL stick.
class JoystickSDL2 : public StellaJoystick
// A thin wrapper around a basic PhysicalJoystick, holding the pointer to
// the underlying SDL joystick device.
class JoystickSDL2 : public PhysicalJoystick
{
public:
JoystickSDL2(int idx);

View File

@ -0,0 +1,759 @@
//============================================================================
//
// 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-2018 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));
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystickHandler::~PhysicalJoystickHandler()
{
for(const auto& i: myDatabase)
delete i.second.joy;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::printDatabase() const
{
cerr << "---------------------------------------------------------" << endl
<< "joy database:" << endl;
for(const auto& i: myDatabase)
cerr << i.first << endl << i.second << endl << endl;
cerr << "---------------------" << endl
<< "joy active:" << endl;
for(const auto& i: mySticks)
cerr << i.first << ": " << *i.second << endl;
cerr << "---------------------------------------------------------" << endl << endl << endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int PhysicalJoystickHandler::add(PhysicalJoystick* 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"))
{
// 2600-daptorII devices have 3 axes and 12 buttons, and the value of the z-axis
// determines how those 12 buttons are used (not all buttons are used in all modes)
if(stick->numAxes == 3)
{
// TODO - stubbed out for now, until we find a way to reliably get info
// from the Z axis
stick->name = "2600-daptor II";
}
else
stick->name = "2600-daptor";
specialAdaptor = true;
}
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
{
PhysicalJoystick* 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();
delete it->second.joy; 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 PhysicalJoystick* 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;
case JoyHat::CENTER: break;
}
}
}
}
}
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalJoystickHandler::addAxisMapping(Event::Type event, EventMode mode,
int stick, int axis, int value)
{
const PhysicalJoystick* 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 PhysicalJoystick* 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 PhysicalJoystick* 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 PhysicalJoystick* 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, 1);
else if(value < -Joystick::deadzone())
myHandler.handleEvent(eventAxisNeg, 1);
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, 0);
myHandler.handleEvent(eventAxisPos, 0);
}
}
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_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(axis < 2)
myEvent.set(SA_Axis[j->type-2][axis], value);
break; // Stelladaptor axis
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(axis < 2)
myEvent.set(SA_Axis[j->type-4][axis], value);
break; // 2600-daptor axis
default:
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystickHandler::handleBtnEvent(int stick, int button, uInt8 state)
{
const PhysicalJoystick* 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(state && myHandler.eventStateChange(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], state);
else if(myHandler.hasOverlay())
myHandler.overlay().handleJoyBtnEvent(stick, button, state);
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], state);
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], state);
break;
default:
if(button < 4) myEvent.set(SA_Button[j->type-4][button], state);
}
switch(myOSystem.console().rightController().type())
{
case Controller::Keyboard:
if(button < 12) myEvent.set(SA_Key[j->type-4][button], state);
break;
default:
if(button < 4) myEvent.set(SA_Button[j->type-4][button], state);
}
}
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 PhysicalJoystick* 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;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 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 }
};

View File

@ -0,0 +1,130 @@
//============================================================================
//
// 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-2018 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#ifndef PHYSICAL_JOYSTICK_HANDLER_HXX
#define PHYSICAL_JOYSTICK_HANDLER_HXX
#include <map>
class OSystem;
class EventHandler;
class Event;
#include "bspf.hxx"
#include "EventHandlerConstants.hxx"
#include "PhysicalJoystick.hxx"
#include "Variant.hxx"
/**
This class handles all physical joystick-related operations in Stella.
It is responsible for adding/accessing/removing PhysicalJoystick objects,
and getting/setting events associated with joystick actions (button presses,
axis/hat actions, etc).
Essentially, this class is an extension of the EventHandler class, but
handling only joystick-specific functionality.
@author Stephen Anthony
*/
class PhysicalJoystickHandler
{
private:
struct StickInfo
{
StickInfo(const string& map = EmptyString, PhysicalJoystick* stick = nullptr)
: mapping(map), joy(stick) {}
string mapping;
PhysicalJoystick* joy;
friend ostream& operator<<(ostream& os, const StickInfo& si) {
os << " joy: " << si.joy << endl << " map: " << si.mapping;
return os;
}
};
public:
using StickDatabase = std::map<string,StickInfo>;
using StickList = std::map<int, PhysicalJoystick*>;
PhysicalJoystickHandler(OSystem& system, EventHandler& handler, Event& event);
~PhysicalJoystickHandler();
/** Return stick ID on success, -1 on failure. */
int add(PhysicalJoystick* stick);
bool remove(int id);
bool remove(const string& name);
void mapStelladaptors(const string& saport);
void setDefaultMapping(Event::Type type, EventMode mode);
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);
/** Handle a physical joystick event. */
void handleAxisEvent(int stick, int axis, int value);
void handleBtnEvent(int stick, int button, uInt8 state);
void handleHatEvent(int stick, int hat, int value);
Event::Type eventForAxis(int stick, int axis, int value, EventMode mode) const {
const PhysicalJoystick* j = joy(stick);
return j ? j->axisTable[axis][(value > 0)][mode] : Event::NoType;
}
Event::Type eventForButton(int stick, int button, EventMode mode) const {
const PhysicalJoystick* j = joy(stick);
return j ? j->btnTable[button][mode] : Event::NoType;
}
Event::Type eventForHat(int stick, int hat, JoyHat value, EventMode mode) const {
const PhysicalJoystick* j = joy(stick);
return j ? j->hatTable[hat][int(value)][mode] : Event::NoType;
}
VariantList database() const;
private:
OSystem& myOSystem;
EventHandler& myHandler;
Event& myEvent;
// Contains all joysticks that Stella knows about, indexed by name
StickDatabase myDatabase;
// Contains only joysticks that are currently available, indexed by id
StickList mySticks;
// Get joystick corresponding to given id (or nullptr if it doesn't exist)
// Make this inline so it's as fast as possible
const PhysicalJoystick* joy(int id) const {
const auto& i = mySticks.find(id);
return i != mySticks.cend() ? i->second : nullptr;
}
void setStickDefaultMapping(int stick, Event::Type type, EventMode mode);
void printDatabase() const;
// Static lookup tables for Stelladaptor/2600-daptor axis/button support
static const Event::Type SA_Axis[2][2];
static const Event::Type SA_Button[2][4];
static const Event::Type SA_Key[2][12];
};
#endif

View File

@ -0,0 +1,229 @@
//============================================================================
//
// 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-2018 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 <map>
#include "OSystem.hxx"
#include "Settings.hxx"
#include "Vec.hxx"
#include "bspf.hxx"
#include "PhysicalJoystick.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystick::PhysicalJoystick()
: type(JT_NONE),
ID(-1),
name("None"),
numAxes(0),
numButtons(0),
numHats(0),
axisTable(nullptr),
btnTable(nullptr),
hatTable(nullptr),
axisLastValue(nullptr)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalJoystick::~PhysicalJoystick()
{
delete[] axisTable;
delete[] btnTable;
delete[] hatTable;
delete[] axisLastValue;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystick::initialize(int index, const string& desc,
int axes, int buttons, int hats, int /*balls*/)
{
ID = index;
name = desc;
// Dynamically create the various mapping arrays for this joystick,
// based on its specific attributes
numAxes = axes;
numButtons = buttons;
numHats = hats;
if(numAxes)
axisTable = new Event::Type[numAxes][2][kNumModes];
if(numButtons)
btnTable = new Event::Type[numButtons][kNumModes];
if(numHats)
hatTable = new Event::Type[numHats][4][kNumModes];
axisLastValue = new int[numAxes];
// 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)
axisTable[a][0][m] = axisTable[a][1][m] = Event::NoType;
}
// Erase the joystick button mapping array
for(int b = 0; b < numButtons; ++b)
for(int m = 0; m < kNumModes; ++m)
btnTable[b][m] = Event::NoType;
// Erase the joystick hat mapping array
for(int h = 0; h < numHats; ++h)
for(int m = 0; m < kNumModes; ++m)
hatTable[h][0][m] = hatTable[h][1][m] =
hatTable[h][2][m] = hatTable[h][3][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)
{
ostringstream joybuf;
joybuf << name << "|" << numAxes;
for(int m = 0; m < kNumModes; ++m)
for(int a = 0; a < numAxes; ++a)
for(int k = 0; k < 2; ++k)
joybuf << " " << axisTable[a][k][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 k = 0; k < 4; ++k)
joybuf << " " << hatTable[h][k][m];
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);
// Error checking
if(items.size() != 4)
return false;
IntArray map;
// Parse axis/button/hat values
getValues(items[1], map);
if(int(map.size()) == numAxes * 2 * 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 k = 0; k < 2; ++k)
axisTable[a][k][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 * 4 * kNumModes)
{
auto event = map.cbegin();
for(int m = 0; m < kNumModes; ++m)
for(int h = 0; h < numHats; ++h)
for(int k = 0; k < 4; ++k)
hatTable[h][k][m] = Event::Type(*event++);
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystick::eraseMap(EventMode mode)
{
// Erase axis mappings
for(int a = 0; a < numAxes; ++a)
axisTable[a][0][mode] = axisTable[a][1][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)
hatTable[h][0][mode] = hatTable[h][1][mode] =
hatTable[h][2][mode] = hatTable[h][3][mode] = Event::NoType;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystick::eraseEvent(Event::Type event, EventMode mode)
{
// Erase axis mappings
for(int a = 0; a < numAxes; ++a)
{
if(axisTable[a][0][mode] == event) axisTable[a][0][mode] = Event::NoType;
if(axisTable[a][1][mode] == event) axisTable[a][1][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)
{
if(hatTable[h][0][mode] == event) hatTable[h][0][mode] = Event::NoType;
if(hatTable[h][1][mode] == event) hatTable[h][1][mode] = Event::NoType;
if(hatTable[h][2][mode] == event) hatTable[h][2][mode] = Event::NoType;
if(hatTable[h][3][mode] == event) hatTable[h][3][mode] = Event::NoType;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalJoystick::getValues(const string& list, IntArray& map) const
{
map.clear();
istringstream buf(list);
int value;
buf >> value; // we don't need to know the # of items at this point
while(buf >> value)
map.push_back(value);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string PhysicalJoystick::about() const
{
ostringstream buf;
buf << name;
if(type == JT_REGULAR)
buf << " with: " << numAxes << " axes, " << numButtons << " buttons, "
<< numHats << " hats";
return buf.str();
}

View File

@ -0,0 +1,83 @@
//============================================================================
//
// 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-2018 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 STELLA_JOYSTICK_HXX
#define STELLA_JOYSTICK_HXX
#include "Event.hxx"
#include "EventHandlerConstants.hxx"
/**
An abstraction of a physical (real) joystick in Stella.
A PhysicalJoystick holds its own event mapping information, space for
which is dynamically allocated based on the actual number of buttons,
axes, etc that the device contains.
Specific backend class(es) will inherit from this class, and implement
functionality specific to the device.
@author Stephen Anthony
*/
class PhysicalJoystick
{
friend class PhysicalJoystickHandler;
public:
PhysicalJoystick();
virtual ~PhysicalJoystick();
string getMap() const;
bool setMap(const string& map);
void eraseMap(EventMode mode);
void eraseEvent(Event::Type event, EventMode mode);
string about() const;
protected:
void initialize(int index, const string& desc,
int axes, int buttons, int hats, int balls);
private:
enum JoyType {
JT_NONE = 0,
JT_REGULAR = 1,
JT_STELLADAPTOR_LEFT = 2,
JT_STELLADAPTOR_RIGHT = 3,
JT_2600DAPTOR_LEFT = 4,
JT_2600DAPTOR_RIGHT = 5
};
JoyType type;
int ID;
string name;
int numAxes, numButtons, numHats;
Event::Type (*axisTable)[2][kNumModes];
Event::Type (*btnTable)[kNumModes];
Event::Type (*hatTable)[4][kNumModes];
int* axisLastValue;
private:
void getValues(const string& list, IntArray& map) const;
friend ostream& operator<<(ostream& os, const PhysicalJoystick& s) {
os << " ID: " << s.ID << ", name: " << s.name << ", numaxis: " << s.numAxes
<< ", numbtns: " << s.numButtons << ", numhats: " << s.numHats;
return os;
}
};
#endif

View File

@ -1,16 +1,18 @@
MODULE := src/common
MODULE_OBJS := \
src/common/main.o \
src/common/Base.o \
src/common/EventHandlerSDL2.o \
src/common/FrameBufferSDL2.o \
src/common/FBSurfaceSDL2.o \
src/common/SoundSDL2.o \
src/common/FrameBufferSDL2.o \
src/common/FSNodeZIP.o \
src/common/PNGLibrary.o \
src/common/main.o \
src/common/MouseControl.o \
src/common/PhysicalJoystick.o \
src/common/PJoystickHandler.o \
src/common/PNGLibrary.o \
src/common/RewindManager.o \
src/common/SoundSDL2.o \
src/common/StateManager.o \
src/common/ZipHandler.o

View File

@ -22,7 +22,7 @@
#include "StellaKeys.hxx"
/**
@author Bradford W. Mott
@author Bradford W. Mott, Stephen Anthony
*/
class Event
{
@ -82,23 +82,23 @@ class Event
public:
/**
Create a new event object
Create a new event object.
*/
Event() { clear(); }
public:
/**
Get the value associated with the event of the specified type
Get the value associated with the event of the specified type.
*/
Int32 get(Type type) const { return myValues[type]; }
/**
Set the value associated with the event of the specified type
Set the value associated with the event of the specified type.
*/
void set(Type type, Int32 value) { myValues[type] = value; }
/**
Clears the event array (resets to initial state)
Clears the event array (resets to initial state).
*/
void clear()
{
@ -110,15 +110,32 @@ class Event
}
/**
Get the keytable associated with this event
Get the keytable associated with this event.
*/
const bool* getKeys() const { return myKeyTable; }
/**
Set the value associated with the event of the specified type
Set the value associated with the event of the specified type.
*/
void setKey(StellaKey key, bool state) { myKeyTable[key] = state; }
/**
Tests if a given event represents continuous or analog values.
*/
static bool isAnalog(Type type)
{
switch(type)
{
case Event::PaddleZeroAnalog:
case Event::PaddleOneAnalog:
case Event::PaddleTwoAnalog:
case Event::PaddleThreeAnalog:
return true;
default:
return false;
}
}
private:
// Array of values associated with each event type
Int32 myValues[LastType];

View File

@ -34,6 +34,7 @@
#include "OSystem.hxx"
#include "Joystick.hxx"
#include "Paddles.hxx"
#include "PJoystickHandler.hxx"
#include "PointingDevice.hxx"
#include "PropsSet.hxx"
#include "ListWidget.hxx"
@ -81,8 +82,8 @@ EventHandler::EventHandler(OSystem& osystem)
for(int j = 0; j < kEventsPerCombo; ++j)
myComboTable[i][j] = Event::NoType;
// Create joystick handler (to handle all joystick functionality)
myJoyHandler = make_unique<JoystickHandler>(osystem);
// Create joystick handler (to handle all physical joystick functionality)
myPJoyHandler = make_unique<PhysicalJoystickHandler>(osystem, *this, myEvent);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -134,40 +135,28 @@ void EventHandler::reset(EventHandlerState state)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::addJoystick(StellaJoystick* stick)
void EventHandler::addPhysicalJoystick(PhysicalJoystick* joy)
{
#ifdef JOYSTICK_SUPPORT
if(!myJoyHandler->add(stick))
int ID = myPJoyHandler->add(joy);
if(ID < 0)
return;
setActionMappings(kEmulationMode);
setActionMappings(kMenuMode);
ostringstream buf;
buf << "Added joystick " << stick->ID << ":" << endl
<< " " << stick->about() << endl;
buf << "Added joystick " << ID << ":" << endl
<< " " << joy->about() << endl;
myOSystem.logMessage(buf.str(), 1);
// 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);
}
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::removeJoystick(int id)
void EventHandler::removePhysicalJoystick(int id)
{
#ifdef JOYSTICK_SUPPORT
myJoyHandler->remove(id);
myPJoyHandler->remove(id);
#endif
}
@ -175,7 +164,7 @@ void EventHandler::removeJoystick(int id)
void EventHandler::mapStelladaptors(const string& saport)
{
#ifdef JOYSTICK_SUPPORT
myJoyHandler->mapStelladaptors(saport);
myPJoyHandler->mapStelladaptors(saport);
#endif
}
@ -692,211 +681,6 @@ void EventHandler::handleMouseButtonEvent(MouseButton b, bool pressed,
myOverlay->handleMouseButtonEvent(b, pressed, x, y);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::handleJoyEvent(int stick, int button, uInt8 state)
{
const StellaJoystick* joy = myJoyHandler->joy(stick);
if(!joy) return;
// Stelladaptors handle buttons differently than regular joysticks
switch(joy->type)
{
case StellaJoystick::JT_REGULAR:
// Handle buttons which switch eventhandler state
if(state && eventStateChange(joy->btnTable[button][kEmulationMode]))
return;
// Determine which mode we're in, then send the event to the appropriate place
if(myState == EventHandlerState::EMULATION)
handleEvent(joy->btnTable[button][kEmulationMode], state);
else if(myOverlay)
myOverlay->handleJoyEvent(stick, button, state);
break; // Regular button
// These events don't have to pass through handleEvent, since
// they can never be remapped
case StellaJoystick::JT_STELLADAPTOR_LEFT:
case StellaJoystick::JT_STELLADAPTOR_RIGHT:
// The 'type-2' here refers to the fact that 'StellaJoystick::JT_STELLADAPTOR_LEFT'
// and 'StellaJoystick::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[joy->type-2][button], state);
break; // Stelladaptor button
case StellaJoystick::JT_2600DAPTOR_LEFT:
case StellaJoystick::JT_2600DAPTOR_RIGHT:
// The 'type-4' here refers to the fact that 'StellaJoystick::JT_2600DAPTOR_LEFT'
// and 'StellaJoystick::JT_2600DAPTOR_RIGHT' are at index 4 and 5 in the JoyType
// enum; subtracting four gives us Controller 0 and 1
if(myState == EventHandlerState::EMULATION)
{
switch(myOSystem.console().leftController().type())
{
case Controller::Keyboard:
if(button < 12) myEvent.set(SA_Key[joy->type-4][button], state);
break;
default:
if(button < 4) myEvent.set(SA_Button[joy->type-4][button], state);
}
switch(myOSystem.console().rightController().type())
{
case Controller::Keyboard:
if(button < 12) myEvent.set(SA_Key[joy->type-4][button], state);
break;
default:
if(button < 4) myEvent.set(SA_Button[joy->type-4][button], state);
}
}
break; // 2600DAPTOR button
default:
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::handleJoyAxisEvent(int stick, int axis, int value)
{
const StellaJoystick* joy = myJoyHandler->joy(stick);
if(!joy) return;
// Stelladaptors handle axis differently than regular joysticks
switch(joy->type)
{
case StellaJoystick::JT_REGULAR:
if(myState == EventHandlerState::EMULATION)
{
// Every axis event has two associated values, negative and positive
Event::Type eventAxisNeg = joy->axisTable[axis][0][kEmulationMode];
Event::Type eventAxisPos = joy->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())
handleEvent(eventAxisPos, 1);
else if(value < -Joystick::deadzone())
handleEvent(eventAxisNeg, 1);
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(joy->axisLastValue[axis] != value)
{
// Turn off both events, since we don't know exactly which one
// was previously activated.
handleEvent(eventAxisNeg, 0);
handleEvent(eventAxisPos, 0);
}
}
joy->axisLastValue[axis] = value;
break;
}
}
}
else if(myOverlay)
{
// 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 != joy->axisLastValue[axis])
{
myOverlay->handleJoyAxisEvent(stick, axis, value);
joy->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 StellaJoystick::JT_STELLADAPTOR_LEFT:
case StellaJoystick::JT_STELLADAPTOR_RIGHT:
// The 'type-2' here refers to the fact that 'StellaJoystick::JT_STELLADAPTOR_LEFT'
// and 'StellaJoystick::JT_STELLADAPTOR_RIGHT' are at index 2 and 3 in the JoyType
// enum; subtracting two gives us Controller 0 and 1
if(axis < 2)
myEvent.set(SA_Axis[joy->type-2][axis], value);
break; // Stelladaptor axis
case StellaJoystick::JT_2600DAPTOR_LEFT:
case StellaJoystick::JT_2600DAPTOR_RIGHT:
// The 'type-4' here refers to the fact that 'StellaJoystick::JT_2600DAPTOR_LEFT'
// and 'StellaJoystick::JT_2600DAPTOR_RIGHT' are at index 4 and 5 in the JoyType
// enum; subtracting four gives us Controller 0 and 1
if(axis < 2)
myEvent.set(SA_Axis[joy->type-4][axis], value);
break; // 2600-daptor axis
default:
break;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::handleJoyHatEvent(int stick, int hat, int value)
{
const StellaJoystick* joy = myJoyHandler->joy(stick);
if(!joy) return;
// 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(myState == EventHandlerState::EMULATION)
{
handleEvent(joy->hatTable[hat][int(JoyHat::UP)][kEmulationMode],
value & EVENT_HATUP_M);
handleEvent(joy->hatTable[hat][int(JoyHat::RIGHT)][kEmulationMode],
value & EVENT_HATRIGHT_M);
handleEvent(joy->hatTable[hat][int(JoyHat::DOWN)][kEmulationMode],
value & EVENT_HATDOWN_M);
handleEvent(joy->hatTable[hat][int(JoyHat::LEFT)][kEmulationMode],
value & EVENT_HATLEFT_M);
}
else if(myOverlay)
{
if(value == EVENT_HATCENTER_M)
myOverlay->handleJoyHatEvent(stick, hat, JoyHat::CENTER);
else
{
if(value & EVENT_HATUP_M)
myOverlay->handleJoyHatEvent(stick, hat, JoyHat::UP);
if(value & EVENT_HATRIGHT_M)
myOverlay->handleJoyHatEvent(stick, hat, JoyHat::RIGHT);
if(value & EVENT_HATDOWN_M)
myOverlay->handleJoyHatEvent(stick, hat, JoyHat::DOWN);
if(value & EVENT_HATLEFT_M)
myOverlay->handleJoyHatEvent(stick, hat, JoyHat::LEFT);
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::handleSystemEvent(SystemEvent e, int, int)
{
@ -1289,8 +1073,6 @@ void EventHandler::setActionMappings(EventMode mode)
return;
}
ostringstream buf;
// Fill the ActionList with the current key and joystick mappings
for(int i = 0; i < listsize; ++i)
{
@ -1309,77 +1091,12 @@ void EventHandler::setActionMappings(EventMode mode)
}
#ifdef JOYSTICK_SUPPORT
for(const auto& st: myJoyHandler->sticks())
string joydesc = myPJoyHandler->getMappingDesc(event, mode);
if(joydesc != "")
{
uInt32 stick = st.first;
const StellaJoystick* joy = st.second;
if(!joy) continue;
// Joystick button mapping/labeling
for(int button = 0; button < joy->numButtons; ++button)
{
if(joy->btnTable[button][mode] == event)
{
buf.str("");
buf << "J" << stick << "/B" << button;
if(key == "")
key = key + buf.str();
else
key = key + ", " + buf.str();
}
}
// Joystick axis mapping/labeling
for(int axis = 0; axis < joy->numAxes; ++axis)
{
for(int dir = 0; dir < 2; ++dir)
{
if(joy->axisTable[axis][dir][mode] == event)
{
buf.str("");
buf << "J" << stick << "/A" << axis;
if(eventIsAnalog(event))
{
dir = 2; // Immediately exit the inner loop after this iteration
buf << "/+|-";
}
else if(dir == 0)
buf << "/-";
else
buf << "/+";
if(key == "")
key = key + buf.str();
else
key = key + ", " + buf.str();
}
}
}
// Joystick hat mapping/labeling
for(int hat = 0; hat < joy->numHats; ++hat)
{
for(int dir = 0; dir < 4; ++dir)
{
if(joy->hatTable[hat][dir][mode] == event)
{
buf.str("");
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;
case JoyHat::CENTER: break;
}
if(key == "")
key = key + buf.str();
else
key = key + ", " + buf.str();
}
}
}
if(key != "")
key += ", ";
key += joydesc;
}
#endif
@ -1487,26 +1204,18 @@ void EventHandler::setComboMap()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VariantList EventHandler::joystickDatabase() const
void EventHandler::removePhysicalJoystickFromDatabase(const string& name)
{
VariantList db;
for(const auto& i: myJoyHandler->database())
VarList::push_back(db, i.first, i.second.joy ? i.second.joy->ID : -1);
return db;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::removeJoystickFromDatabase(const string& name)
{
myJoyHandler->remove(name);
#ifdef JOYSTICK_SUPPORT
myPJoyHandler->remove(name);
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool EventHandler::addKeyMapping(Event::Type event, EventMode mode, StellaKey key)
{
// These keys cannot be remapped
if(key == KBDK_TAB || eventIsAnalog(event))
if(key == KBDK_TAB || Event::isAnalog(event))
return false;
else
{
@ -1523,33 +1232,14 @@ bool EventHandler::addJoyAxisMapping(Event::Type event, EventMode mode,
bool updateMenus)
{
#ifdef JOYSTICK_SUPPORT
const StellaJoystick* joy = myJoyHandler->joy(stick);
if(joy)
{
if(axis >= 0 && axis < joy->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(eventIsAnalog(event))
joy->axisTable[axis][0][mode] =
joy->axisTable[axis][1][mode] = event;
else
{
// Otherwise, turn off the analog event(s) for this axis
if(eventIsAnalog(joy->axisTable[axis][0][mode]))
joy->axisTable[axis][0][mode] = Event::NoType;
if(eventIsAnalog(joy->axisTable[axis][1][mode]))
joy->axisTable[axis][1][mode] = Event::NoType;
bool mapped = myPJoyHandler->addAxisMapping(event, mode, stick, axis, value);
if(mapped && updateMenus)
setActionMappings(mode);
joy->axisTable[axis][(value > 0)][mode] = event;
}
if(updateMenus)
setActionMappings(mode);
return true;
}
}
#endif
return mapped;
#else
return false;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1558,19 +1248,14 @@ bool EventHandler::addJoyButtonMapping(Event::Type event, EventMode mode,
bool updateMenus)
{
#ifdef JOYSTICK_SUPPORT
const StellaJoystick* joy = myJoyHandler->joy(stick);
if(joy)
{
if(button >= 0 && button < joy->numButtons && event < Event::LastType)
{
joy->btnTable[button][mode] = event;
if(updateMenus)
setActionMappings(mode);
return true;
}
}
#endif
bool mapped = myPJoyHandler->addBtnMapping(event, mode, stick, button);
if(mapped && updateMenus)
setActionMappings(mode);
return mapped;
#else
return false;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1579,20 +1264,14 @@ bool EventHandler::addJoyHatMapping(Event::Type event, EventMode mode,
bool updateMenus)
{
#ifdef JOYSTICK_SUPPORT
const StellaJoystick* joy = myJoyHandler->joy(stick);
if(joy)
{
if(hat >= 0 && hat < joy->numHats && event < Event::LastType &&
value != JoyHat::CENTER)
{
joy->hatTable[hat][int(value)][mode] = event;
if(updateMenus)
setActionMappings(mode);
return true;
}
}
#endif
bool mapped = myPJoyHandler->addHatMapping(event, mode, stick, hat, value);
if(mapped && updateMenus)
setActionMappings(mode);
return mapped;
#else
return false;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1605,7 +1284,7 @@ void EventHandler::eraseMapping(Event::Type event, EventMode mode)
#ifdef JOYSTICK_SUPPORT
// Erase the joystick mapping arrays
myJoyHandler->eraseMapping(event, mode);
myPJoyHandler->eraseMapping(event, mode);
#endif
setActionMappings(mode);
@ -1731,8 +1410,10 @@ void EventHandler::setDefaultKeymap(Event::Type event, EventMode mode)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::setDefaultJoymap(Event::Type event, EventMode mode)
{
myJoyHandler->setDefaultMapping(event, mode);
#ifdef JOYSTICK_SUPPORT
myPJoyHandler->setDefaultMapping(event, mode);
setActionMappings(mode);
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1752,7 +1433,9 @@ void EventHandler::saveKeyMapping()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::saveJoyMapping()
{
myJoyHandler->saveMapping();
#ifdef JOYSTICK_SUPPORT
myPJoyHandler->saveMapping();
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1772,21 +1455,6 @@ void EventHandler::saveComboMapping()
myOSystem.settings().setValue("combomap", buf.str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline bool EventHandler::eventIsAnalog(Event::Type event) const
{
switch(event)
{
case Event::PaddleZeroAnalog:
case Event::PaddleOneAnalog:
case Event::PaddleTwoAnalog:
case Event::PaddleThreeAnalog:
return true;
default:
return false;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
StringList EventHandler::getActionList(EventMode mode) const
{
@ -2059,9 +1727,8 @@ void EventHandler::setMouseControllerMode(const string& enable)
usemouse = false;
else // 'analog'
{
if(controllerIsAnalog(Controller::Left) ||
controllerIsAnalog(Controller::Right))
usemouse = true;
usemouse = controllerIsAnalog(Controller::Left) ||
controllerIsAnalog(Controller::Right);
}
const string& control = usemouse ?
@ -2170,10 +1837,10 @@ void EventHandler::setEventState(EventHandlerState state)
// Only enable text input in GUI modes, since in emulation mode the
// keyboard acts as one large joystick with many (single) buttons
myOverlay = nullptr;
switch(myState)
{
case EventHandlerState::EMULATION:
myOverlay = nullptr;
myOSystem.sound().mute(false);
enableTextEvents(false);
if(myOSystem.console().leftController().type() == Controller::CompuMate)
@ -2181,7 +1848,6 @@ void EventHandler::setEventState(EventHandlerState state)
break;
case EventHandlerState::PAUSE:
myOverlay = nullptr;
myOSystem.sound().mute(true);
enableTextEvents(false);
break;
@ -2216,7 +1882,6 @@ void EventHandler::setEventState(EventHandlerState state)
break;
case EventHandlerState::NONE:
myOverlay = nullptr;
break;
}
@ -2362,33 +2027,3 @@ EventHandler::ActionList EventHandler::ourMenuActionList[kMenuActionListSize] =
{ Event::UIPrevDir, "Parent directory", "", false }
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Used by the Stelladaptor to send absolute axis values
const Event::Type EventHandler::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 EventHandler::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 EventHandler::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 }
};

View File

@ -24,11 +24,13 @@ class Console;
class OSystem;
class MouseControl;
class DialogContainer;
class PhysicalJoystick;
#include "Event.hxx"
#include "EventHandlerConstants.hxx"
#include "Control.hxx"
#include "StellaKeys.hxx"
#include "PJoystickHandler.hxx"
#include "Variant.hxx"
#include "bspf.hxx"
@ -163,19 +165,17 @@ class EventHandler
StringList getComboListForEvent(Event::Type event) const;
void setComboListForEvent(Event::Type event, const StringList& events);
/** Convert keys and physical joystick events into Stella events. */
Event::Type eventForKey(StellaKey key, EventMode mode) const
{ return myKeyTable[key][mode]; }
Event::Type eventForJoyAxis(int stick, int axis, int value, EventMode mode) const {
const StellaJoystick* joy = myJoyHandler->joy(stick);
return joy ? joy->axisTable[axis][(value > 0)][mode] : Event::NoType;
return myPJoyHandler->eventForAxis(stick, axis, value, mode);
}
Event::Type eventForJoyButton(int stick, int button, EventMode mode) const {
const StellaJoystick* joy = myJoyHandler->joy(stick);
return joy ? joy->btnTable[button][mode] : Event::NoType;
return myPJoyHandler->eventForButton(stick, button, mode);
}
Event::Type eventForJoyHat(int stick, int hat, JoyHat value, EventMode mode) const {
const StellaJoystick* joy = myJoyHandler->joy(stick);
return joy ? joy->hatTable[hat][int(value)][mode] : Event::NoType;
return myPJoyHandler->eventForHat(stick, hat, value, mode);
}
Event::Type eventAtIndex(int idx, EventMode mode) const;
@ -192,7 +192,7 @@ class EventHandler
bool addKeyMapping(Event::Type event, EventMode mode, StellaKey key);
/**
Bind a joystick axis direction to an event/action and regenerate
Bind a physical joystick axis direction to an event/action and regenerate
the mapping array(s).
@param event The event we are remapping
@ -209,7 +209,7 @@ class EventHandler
bool updateMenus = true);
/**
Bind a joystick button to an event/action and regenerate the
Bind a physical joystick button to an event/action and regenerate the
mapping array(s).
@param event The event we are remapping
@ -224,7 +224,7 @@ class EventHandler
bool updateMenus = true);
/**
Bind a joystick hat direction to an event/action and regenerate
Bind a physical joystick hat direction to an event/action and regenerate
the mapping array(s).
@param event The event we are remapping
@ -278,16 +278,35 @@ class EventHandler
bool controllerIsAnalog(Controller::Jack jack) const;
/**
Return a list of all joysticks currently in the internal database
(first part of variant) and its internal ID (second part of variant).
Detects and changes the eventhandler state.
@param type The event
@return True if the state changed, else false
*/
VariantList joystickDatabase() const;
bool eventStateChange(Event::Type type);
/**
Remove the joystick identified by 'name' from the joystick database,
only if it is not currently active.
Get the current overlay in use. The overlay won't always exist,
so we should test if it's available.
@return The overlay object
*/
void removeJoystickFromDatabase(const string& name);
DialogContainer& overlay() const { return *myOverlay; }
bool hasOverlay() const { return myOverlay != nullptr; }
/**
Return a list of all physical joysticks currently in the internal database
(first part of variant) and its internal ID (second part of variant).
*/
VariantList physicalJoystickDatabase() const {
return myPJoyHandler->database();
}
/**
Remove the physical joystick identified by 'name' from the joystick
database, only if it is not currently active.
*/
void removePhysicalJoystickFromDatabase(const string& name);
/**
Enable/disable text events (distinct from single-key events).
@ -306,9 +325,15 @@ class EventHandler
void handleKeyEvent(StellaKey key, StellaMod mod, bool state);
void handleMouseMotionEvent(int x, int y, int xrel, int yrel);
void handleMouseButtonEvent(MouseButton b, bool pressed, int x, int y);
void handleJoyEvent(int stick, int button, uInt8 state);
void handleJoyAxisEvent(int stick, int axis, int value);
void handleJoyHatEvent(int stick, int hat, int value);
void handleJoyBtnEvent(int stick, int button, uInt8 state) {
myPJoyHandler->handleBtnEvent(stick, button, state);
}
void handleJoyAxisEvent(int stick, int axis, int value) {
myPJoyHandler->handleAxisEvent(stick, axis, value);
}
void handleJoyHatEvent(int stick, int hat, int value) {
myPJoyHandler->handleHatEvent(stick, hat, value);
}
/**
Returns the human-readable name for a StellaKey.
@ -338,120 +363,15 @@ class EventHandler
};
void handleSystemEvent(SystemEvent e, int data1 = 0, int data2 = 0);
// An abstraction of a joystick in Stella.
// A StellaJoystick holds its own event mapping information, space for
// which is dynamically allocated based on the actual number of buttons,
// axes, etc that the device contains.
// Specific backend class(es) will inherit from this class, and implement
// functionality specific to the device.
class StellaJoystick
{
friend class EventHandler;
public:
StellaJoystick();
virtual ~StellaJoystick();
string getMap() const;
bool setMap(const string& map);
void eraseMap(EventMode mode);
void eraseEvent(Event::Type event, EventMode mode);
string about() const;
protected:
void initialize(int index, const string& desc,
int axes, int buttons, int hats, int balls);
private:
enum JoyType {
JT_NONE = 0,
JT_REGULAR = 1,
JT_STELLADAPTOR_LEFT = 2,
JT_STELLADAPTOR_RIGHT = 3,
JT_2600DAPTOR_LEFT = 4,
JT_2600DAPTOR_RIGHT = 5
};
JoyType type;
int ID;
string name;
int numAxes, numButtons, numHats;
Event::Type (*axisTable)[2][kNumModes];
Event::Type (*btnTable)[kNumModes];
Event::Type (*hatTable)[4][kNumModes];
int* axisLastValue;
private:
void getValues(const string& list, IntArray& map) const;
friend ostream& operator<<(ostream& os, const StellaJoystick& s) {
os << " ID: " << s.ID << ", name: " << s.name << ", numaxis: " << s.numAxes
<< ", numbtns: " << s.numButtons << ", numhats: " << s.numHats;
return os;
}
};
class JoystickHandler
{
private:
struct StickInfo
{
StickInfo(const string& map = EmptyString, StellaJoystick* stick = nullptr)
: mapping(map), joy(stick) {}
string mapping;
StellaJoystick* joy;
friend ostream& operator<<(ostream& os, const StickInfo& si) {
os << " joy: " << si.joy << endl << " map: " << si.mapping;
return os;
}
};
public:
using StickDatabase = std::map<string,StickInfo>;
using StickList = std::map<int, StellaJoystick*>;
JoystickHandler(OSystem& system);
~JoystickHandler();
bool add(StellaJoystick* stick);
bool remove(int id);
bool remove(const string& name);
void mapStelladaptors(const string& saport);
void setDefaultMapping(Event::Type type, EventMode mode);
void eraseMapping(Event::Type event, EventMode mode);
void saveMapping();
const StellaJoystick* joy(int id) const {
const auto& i = mySticks.find(id);
return i != mySticks.cend() ? i->second : nullptr;
}
const StickDatabase& database() const { return myDatabase; }
const StickList& sticks() const { return mySticks; }
private:
OSystem& myOSystem;
// Contains all joysticks that Stella knows about, indexed by name
StickDatabase myDatabase;
// Contains only joysticks that are currently available, indexed by id
StickList mySticks;
void setStickDefaultMapping(int stick, Event::Type type, EventMode mode);
void printDatabase() const;
};
/**
Add the given joystick to the list of physical joysticks available to the handler.
*/
void addPhysicalJoystick(PhysicalJoystick* stick);
/**
Add the given joystick to the list of sticks available to the handler.
Remove physical joystick at the current index.
*/
void addJoystick(StellaJoystick* stick);
/**
Remove joystick at the current index.
*/
void removeJoystick(int index);
void removePhysicalJoystick(int index);
private:
enum {
@ -461,14 +381,6 @@ class EventHandler
kMenuActionListSize = 14
};
/**
Detects and changes the eventhandler state
@param type The event
@return True if the state changed, else false
*/
bool eventStateChange(Event::Type type);
/**
The following methods take care of assigning action mappings.
*/
@ -480,14 +392,6 @@ class EventHandler
void saveJoyMapping();
void saveComboMapping();
/**
Tests if a given event should use continuous/analog values.
@param event The event to test for analog processing
@return True if analog, else false
*/
bool eventIsAnalog(Event::Type event) const;
void setEventState(EventHandlerState state);
private:
@ -509,6 +413,9 @@ class EventHandler
// all possible controller modes
unique_ptr<MouseControl> myMouseControl;
// Handler for all joystick addition/removal/mapping
unique_ptr<PhysicalJoystickHandler> myPJoyHandler;
// Array of key events, indexed by StellaKey
Event::Type myKeyTable[KBDK_LAST][kNumModes];
@ -518,7 +425,7 @@ class EventHandler
// Indicates the current state of the system (ie, which mode is current)
EventHandlerState myState;
// Indicates whether the joystick emulates 'impossible' directions
// Indicates whether the virtual joystick emulates 'impossible' directions
bool myAllowAllDirectionsFlag;
// Indicates whether or not we're in frying mode
@ -559,14 +466,6 @@ class EventHandler
static ActionList ourEmulActionList[kEmulActionListSize];
static ActionList ourMenuActionList[kMenuActionListSize];
// Static lookup tables for Stelladaptor/2600-daptor axis/button support
static const Event::Type SA_Axis[2][2];
static const Event::Type SA_Button[2][4];
static const Event::Type SA_Key[2][12];
// Handler for all joystick addition/removal/mapping
unique_ptr<JoystickHandler> myJoyHandler;
// Following constructors and assignment operators not supported
EventHandler() = delete;
EventHandler(const EventHandler&) = delete;

View File

@ -1,576 +0,0 @@
//============================================================================
//
// 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-2018 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 <map>
#include "OSystem.hxx"
#include "Settings.hxx"
#include "Vec.hxx"
#include "bspf.hxx"
#include "EventHandler.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandler::StellaJoystick::StellaJoystick()
: type(JT_NONE),
ID(-1),
name("None"),
numAxes(0),
numButtons(0),
numHats(0),
axisTable(nullptr),
btnTable(nullptr),
hatTable(nullptr),
axisLastValue(nullptr)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandler::StellaJoystick::~StellaJoystick()
{
delete[] axisTable;
delete[] btnTable;
delete[] hatTable;
delete[] axisLastValue;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::StellaJoystick::initialize(int index, const string& desc,
int axes, int buttons, int hats, int /*balls*/)
{
ID = index;
name = desc;
// Dynamically create the various mapping arrays for this joystick,
// based on its specific attributes
numAxes = axes;
numButtons = buttons;
numHats = hats;
if(numAxes)
axisTable = new Event::Type[numAxes][2][kNumModes];
if(numButtons)
btnTable = new Event::Type[numButtons][kNumModes];
if(numHats)
hatTable = new Event::Type[numHats][4][kNumModes];
axisLastValue = new int[numAxes];
// 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)
axisTable[a][0][m] = axisTable[a][1][m] = Event::NoType;
}
// Erase the joystick button mapping array
for(int b = 0; b < numButtons; ++b)
for(int m = 0; m < kNumModes; ++m)
btnTable[b][m] = Event::NoType;
// Erase the joystick hat mapping array
for(int h = 0; h < numHats; ++h)
for(int m = 0; m < kNumModes; ++m)
hatTable[h][0][m] = hatTable[h][1][m] =
hatTable[h][2][m] = hatTable[h][3][m] = Event::NoType;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string EventHandler::StellaJoystick::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)
{
ostringstream joybuf;
joybuf << name << "|" << numAxes;
for(int m = 0; m < kNumModes; ++m)
for(int a = 0; a < numAxes; ++a)
for(int k = 0; k < 2; ++k)
joybuf << " " << axisTable[a][k][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 k = 0; k < 4; ++k)
joybuf << " " << hatTable[h][k][m];
return joybuf.str();
}
return EmptyString;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool EventHandler::StellaJoystick::setMap(const string& mapString)
{
istringstream buf(mapString);
StringList items;
string item;
while(getline(buf, item, '|'))
items.push_back(item);
// Error checking
if(items.size() != 4)
return false;
IntArray map;
// Parse axis/button/hat values
getValues(items[1], map);
if(int(map.size()) == numAxes * 2 * 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 k = 0; k < 2; ++k)
axisTable[a][k][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 * 4 * kNumModes)
{
auto event = map.cbegin();
for(int m = 0; m < kNumModes; ++m)
for(int h = 0; h < numHats; ++h)
for(int k = 0; k < 4; ++k)
hatTable[h][k][m] = Event::Type(*event++);
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::StellaJoystick::eraseMap(EventMode mode)
{
// Erase axis mappings
for(int a = 0; a < numAxes; ++a)
axisTable[a][0][mode] = axisTable[a][1][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)
hatTable[h][0][mode] = hatTable[h][1][mode] =
hatTable[h][2][mode] = hatTable[h][3][mode] = Event::NoType;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::StellaJoystick::eraseEvent(Event::Type event, EventMode mode)
{
// Erase axis mappings
for(int a = 0; a < numAxes; ++a)
{
if(axisTable[a][0][mode] == event) axisTable[a][0][mode] = Event::NoType;
if(axisTable[a][1][mode] == event) axisTable[a][1][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)
{
if(hatTable[h][0][mode] == event) hatTable[h][0][mode] = Event::NoType;
if(hatTable[h][1][mode] == event) hatTable[h][1][mode] = Event::NoType;
if(hatTable[h][2][mode] == event) hatTable[h][2][mode] = Event::NoType;
if(hatTable[h][3][mode] == event) hatTable[h][3][mode] = Event::NoType;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::StellaJoystick::getValues(const string& list, IntArray& map) const
{
map.clear();
istringstream buf(list);
int value;
buf >> value; // we don't need to know the # of items at this point
while(buf >> value)
map.push_back(value);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string EventHandler::StellaJoystick::about() const
{
ostringstream buf;
buf << name;
if(type == JT_REGULAR)
buf << " with: " << numAxes << " axes, " << numButtons << " buttons, "
<< numHats << " hats";
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandler::JoystickHandler::JoystickHandler(OSystem& system)
: myOSystem(system)
{
// 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)
{
StickInfo info(joymap);
myDatabase.emplace(joyname, info);
}
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandler::JoystickHandler::~JoystickHandler()
{
for(const auto& i: myDatabase)
delete i.second.joy;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::JoystickHandler::printDatabase() const
{
cerr << "---------------------------------------------------------" << endl
<< "joy database:" << endl;
for(const auto& i: myDatabase)
cerr << i.first << endl << i.second << endl << endl;
cerr << "---------------------" << endl
<< "joy active:" << endl;
for(const auto& i: mySticks)
cerr << i.first << ": " << *i.second << endl;
cerr << "---------------------------------------------------------" << endl << endl << endl;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool EventHandler::JoystickHandler::add(StellaJoystick* stick)
{
// Skip if we couldn't open it for any reason
if(stick->ID < 0)
return false;
// Figure out what type of joystick this is
bool specialAdaptor = false;
if(BSPF::containsIgnoreCase(stick->name, "2600-daptor"))
{
// 2600-daptorII devices have 3 axes and 12 buttons, and the value of the z-axis
// determines how those 12 buttons are used (not all buttons are used in all modes)
if(stick->numAxes == 3)
{
// TODO - stubbed out for now, until we find a way to reliably get info
// from the Z axis
stick->name = "2600-daptor II";
}
else
stick->name = "2600-daptor";
specialAdaptor = true;
}
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 = StellaJoystick::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);
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool EventHandler::JoystickHandler::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
{
StellaJoystick* 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();
delete it->second.joy; it->second.joy = nullptr;
mySticks.erase(id);
return true;
}
}
catch(std::out_of_range)
{
// fall through to indicate remove failed
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool EventHandler::JoystickHandler::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 EventHandler::JoystickHandler::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 = StellaJoystick::JT_STELLADAPTOR_LEFT;
}
else if(saOrder[saCount] == 2)
{
stick.second->name += " (emulates right joystick port)";
stick.second->type = StellaJoystick::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 = StellaJoystick::JT_2600DAPTOR_LEFT;
}
else if(saOrder[saCount] == 2)
{
stick.second->name += " (emulates right joystick port)";
stick.second->type = StellaJoystick::JT_2600DAPTOR_RIGHT;
}
saCount++;
}
}
myOSystem.settings().setValue("saport", saport);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::JoystickHandler::setDefaultMapping(Event::Type event, EventMode mode)
{
eraseMapping(event, mode);
for(auto& i: mySticks)
setStickDefaultMapping(i.first, event, mode);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::JoystickHandler::setStickDefaultMapping(int stick,
Event::Type event, EventMode mode)
{
EventHandler& handler = myOSystem.eventHandler();
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)
handler.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)
handler.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)
handler.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 EventHandler::JoystickHandler::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 EventHandler::JoystickHandler::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());
}

View File

@ -53,7 +53,6 @@ MODULE_OBJS := \
src/emucore/Control.o \
src/emucore/Driving.o \
src/emucore/EventHandler.o \
src/emucore/EventJoyHandler.o \
src/emucore/FrameBuffer.o \
src/emucore/FBSurface.o \
src/emucore/FSNode.o \

View File

@ -271,7 +271,7 @@ void DialogContainer::handleMouseButtonEvent(MouseButton b, bool pressed,
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DialogContainer::handleJoyEvent(int stick, int button, uInt8 state)
void DialogContainer::handleJoyBtnEvent(int stick, int button, uInt8 state)
{
if(myDialogStack.empty())
return;

View File

@ -99,7 +99,7 @@ class DialogContainer
@param button The joystick button
@param state The state (pressed or released)
*/
void handleJoyEvent(int stick, int button, uInt8 state);
void handleJoyBtnEvent(int stick, int button, uInt8 state);
/**
Handle a joystick axis event.

View File

@ -75,7 +75,7 @@ void JoystickDialog::loadConfig()
myJoyIDs.clear();
StringList sticks;
for(const auto& i: instance().eventHandler().joystickDatabase())
for(const auto& i: instance().eventHandler().physicalJoystickDatabase())
{
sticks.push_back(i.first);
myJoyIDs.push_back(i.second.toInt());
@ -99,7 +99,8 @@ void JoystickDialog::handleCommand(CommandSender* sender, int cmd, int data, int
break;
case kRemoveCmd:
instance().eventHandler().removeJoystickFromDatabase(myJoyList->getSelectedString());
instance().eventHandler().removePhysicalJoystickFromDatabase(
myJoyList->getSelectedString());
loadConfig();
break;