Merge branch 'master' into feature/precise-audio

This commit is contained in:
Christian Speckner 2018-04-25 22:49:00 +02:00
commit c905b01fca
71 changed files with 2646 additions and 2112 deletions

View File

@ -1,4 +1,18 @@
5.1 to 5.2: (MMM d, 2018)
===========================================================================
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
===========================================================================
Release History
===========================================================================
5.1.1 to 5.2: (MMM d, 2018)
* Extra functionality for Time Machine dialog (start/stop recording;
minor fixes; TODO button and initial key repeats...)
@ -7,6 +21,21 @@
* UI modernization (new widget look, dialog titles added, dialogs refactored)
* Fixed bug in UI navigation with joystick hat movement.
* The Linux builds now use the system-installed PNG and ZLIB libraries
by default.
* Updated version of SDL included with the Windows build to 2.0.8.
-Have fun!
5.1 to 5.1.1: (February 21, 2018)
* Fixed bug in Stargunner ROM starting with a blank screen.
5.0.2 to 5.1: (February 4, 2018)
* Added "Time Machine" mode, which automatically creates save states
@ -141,8 +170,6 @@
* Updated included PNG library to latest stable version.
-Have fun!
5.0.1 to 5.0.2: (August 20, 2017)

2
debian/compat vendored
View File

@ -1 +1 @@
5
9

2
debian/control vendored
View File

@ -3,7 +3,7 @@ Section: games
Priority: optional
Maintainer: Stephen Anthony <sa666666@gmail.com>
Standards-Version: 3.7.2
Build-Depends: debhelper (>= 5.0.0), libsdl2-dev
Build-Depends: debhelper (>= 5.0.0), libsdl2-dev, libpng-dev
Package: stella
Architecture: any

2
debian/rules vendored
View File

@ -20,7 +20,7 @@ endif
config.status: configure
dh_testdir
CXXFLAGS="$(CXXFLAGS)" ./configure --prefix=/usr --force-builtin-libpng
CXXFLAGS="$(CXXFLAGS)" ./configure --prefix=/usr
build: build-stamp

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;
}
@ -111,7 +111,7 @@ void EventHandlerSDL2::pollEvent()
case SDL_JOYHATMOTION:
{
int v = myEvent.jhat.value, value = 0;
if(v & SDL_HAT_CENTERED)
if(v == SDL_HAT_CENTERED)
value = EVENT_HATCENTER_M;
else
{
@ -127,12 +127,12 @@ void EventHandlerSDL2::pollEvent()
case SDL_JOYDEVICEADDED:
{
addJoystick(new JoystickSDL2(myEvent.jdevice.which));
addPhysicalJoystick(make_shared<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
@ -43,13 +44,6 @@ class EventHandlerSDL2 : public EventHandler
*/
void enableTextEvents(bool enable) override;
/**
Returns the human-readable name for a StellaKey.
*/
const char* const nameForKey(StellaKey key) const override {
return SDL_GetScancodeName(SDL_Scancode(key));
}
/**
Collects and dispatches any pending SDL2 events.
*/
@ -58,9 +52,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,749 @@
//============================================================================
//
// 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));
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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, 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_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, uInt8 state)
{
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(state && 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], 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 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 }
};

View File

@ -0,0 +1,134 @@
//============================================================================
//
// 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"
using PhysicalJoystickPtr = shared_ptr<PhysicalJoystick>;
/**
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, PhysicalJoystickPtr stick = nullptr)
: mapping(map), joy(stick) {}
string mapping;
PhysicalJoystickPtr joy;
friend ostream& operator<<(ostream& os, const StickInfo& si) {
os << " joy: " << si.joy << endl << " map: " << si.mapping;
return os;
}
};
public:
PhysicalJoystickHandler(OSystem& system, EventHandler& handler, Event& event);
/** Return stick ID on success, -1 on failure. */
int add(PhysicalJoystickPtr 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 PhysicalJoystickPtr j = joy(stick);
return j ? j->axisTable[axis][(value > 0)][mode] : Event::NoType;
}
Event::Type eventForButton(int stick, int button, EventMode mode) const {
const PhysicalJoystickPtr j = joy(stick);
return j ? j->btnTable[button][mode] : Event::NoType;
}
Event::Type eventForHat(int stick, int hat, JoyHat value, EventMode mode) const {
const PhysicalJoystickPtr j = joy(stick);
return j ? j->hatTable[hat][int(value)][mode] : Event::NoType;
}
/** Returns a list of pairs consisting of joystick name and associated ID. */
VariantList database() const;
private:
using StickDatabase = std::map<string,StickInfo>;
using StickList = std::map<int, PhysicalJoystickPtr>;
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 PhysicalJoystickPtr joy(int id) const {
const auto& i = mySticks.find(id);
return i != mySticks.cend() ? i->second : nullptr;
}
// Set default mapping for given joystick when no mappings already exist
void setStickDefaultMapping(int stick, Event::Type type, EventMode mode);
friend ostream& operator<<(ostream& os, const PhysicalJoystickHandler& jh);
// 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,614 @@
//============================================================================
//
// 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 "Settings.hxx"
#include "EventHandler.hxx"
#include "Event.hxx"
#include "Sound.hxx"
#include "StateManager.hxx"
#include "StellaKeys.hxx"
#include "TIASurface.hxx"
#include "PNGLibrary.hxx"
#include "DialogContainer.hxx"
#include "PKeyboardHandler.hxx"
#ifdef DEBUGGER_SUPPORT
#include "Debugger.hxx"
#endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PhysicalKeyboardHandler::PhysicalKeyboardHandler(
OSystem& system, EventHandler& handler, Event& event)
: myOSystem(system),
myHandler(handler),
myEvent(event),
myAltKeyCounter(0),
myUseCtrlKeyFlag(myOSystem.settings().getBool("ctrlcombo"))
{
// Since istringstream swallows whitespace, we have to make the
// delimiters be spaces
string list = myOSystem.settings().getString("keymap");
replace(list.begin(), list.end(), ':', ' ');
istringstream buf(list);
IntArray map;
int value;
Event::Type e;
// Get event count, which should be the first int in the list
buf >> value;
e = Event::Type(value);
if(e == Event::LastType)
while(buf >> value)
map.push_back(value);
// Only fill the key mapping array if the data is valid
if(e == Event::LastType && map.size() == KBDK_LAST * kNumModes)
{
// Fill the keymap table with events
auto ev = map.cbegin();
for(int mode = 0; mode < kNumModes; ++mode)
for(int i = 0; i < KBDK_LAST; ++i)
myKeyTable[i][mode] = Event::Type(*ev++);
}
else
{
setDefaultMapping(Event::NoType, kEmulationMode);
setDefaultMapping(Event::NoType, kMenuMode);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalKeyboardHandler::setDefaultMapping(Event::Type event, EventMode mode)
{
// If event is 'NoType', erase and reset all mappings
// Otherwise, only reset the given event
bool eraseAll = (event == Event::NoType);
if(eraseAll)
{
// Erase all mappings
for(int i = 0; i < KBDK_LAST; ++i)
myKeyTable[i][mode] = Event::NoType;
}
auto setDefaultKey = [&](StellaKey key, Event::Type k_event)
{
if(eraseAll || k_event == event)
myKeyTable[key][mode] = k_event;
};
switch(mode)
{
case kEmulationMode:
setDefaultKey( KBDK_1, Event::KeyboardZero1 );
setDefaultKey( KBDK_2, Event::KeyboardZero2 );
setDefaultKey( KBDK_3, Event::KeyboardZero3 );
setDefaultKey( KBDK_Q, Event::KeyboardZero4 );
setDefaultKey( KBDK_W, Event::KeyboardZero5 );
setDefaultKey( KBDK_E, Event::KeyboardZero6 );
setDefaultKey( KBDK_A, Event::KeyboardZero7 );
setDefaultKey( KBDK_S, Event::KeyboardZero8 );
setDefaultKey( KBDK_D, Event::KeyboardZero9 );
setDefaultKey( KBDK_Z, Event::KeyboardZeroStar );
setDefaultKey( KBDK_X, Event::KeyboardZero0 );
setDefaultKey( KBDK_C, Event::KeyboardZeroPound );
setDefaultKey( KBDK_8, Event::KeyboardOne1 );
setDefaultKey( KBDK_9, Event::KeyboardOne2 );
setDefaultKey( KBDK_0, Event::KeyboardOne3 );
setDefaultKey( KBDK_I, Event::KeyboardOne4 );
setDefaultKey( KBDK_O, Event::KeyboardOne5 );
setDefaultKey( KBDK_P, Event::KeyboardOne6 );
setDefaultKey( KBDK_K, Event::KeyboardOne7 );
setDefaultKey( KBDK_L, Event::KeyboardOne8 );
setDefaultKey( KBDK_SEMICOLON, Event::KeyboardOne9 );
setDefaultKey( KBDK_COMMA, Event::KeyboardOneStar );
setDefaultKey( KBDK_PERIOD, Event::KeyboardOne0 );
setDefaultKey( KBDK_SLASH, Event::KeyboardOnePound );
setDefaultKey( KBDK_UP, Event::JoystickZeroUp );
setDefaultKey( KBDK_DOWN, Event::JoystickZeroDown );
setDefaultKey( KBDK_LEFT, Event::JoystickZeroLeft );
setDefaultKey( KBDK_RIGHT, Event::JoystickZeroRight );
setDefaultKey( KBDK_SPACE, Event::JoystickZeroFire );
setDefaultKey( KBDK_LCTRL, Event::JoystickZeroFire );
setDefaultKey( KBDK_4, Event::JoystickZeroFire5 );
setDefaultKey( KBDK_5, Event::JoystickZeroFire9 );
setDefaultKey( KBDK_Y, Event::JoystickOneUp );
setDefaultKey( KBDK_H, Event::JoystickOneDown );
setDefaultKey( KBDK_G, Event::JoystickOneLeft );
setDefaultKey( KBDK_J, Event::JoystickOneRight );
setDefaultKey( KBDK_F, Event::JoystickOneFire );
setDefaultKey( KBDK_6, Event::JoystickOneFire5 );
setDefaultKey( KBDK_7, Event::JoystickOneFire9 );
setDefaultKey( KBDK_F1, Event::ConsoleSelect );
setDefaultKey( KBDK_F2, Event::ConsoleReset );
setDefaultKey( KBDK_F3, Event::ConsoleColor );
setDefaultKey( KBDK_F4, Event::ConsoleBlackWhite );
setDefaultKey( KBDK_F5, Event::ConsoleLeftDiffA );
setDefaultKey( KBDK_F6, Event::ConsoleLeftDiffB );
setDefaultKey( KBDK_F7, Event::ConsoleRightDiffA );
setDefaultKey( KBDK_F8, Event::ConsoleRightDiffB );
setDefaultKey( KBDK_F9, Event::SaveState );
setDefaultKey( KBDK_F10, Event::ChangeState );
setDefaultKey( KBDK_F11, Event::LoadState );
setDefaultKey( KBDK_F12, Event::TakeSnapshot );
setDefaultKey( KBDK_BACKSPACE, Event::Fry );
setDefaultKey( KBDK_PAUSE, Event::PauseMode );
setDefaultKey( KBDK_TAB, Event::OptionsMenuMode );
setDefaultKey( KBDK_BACKSLASH, Event::CmdMenuMode );
setDefaultKey( KBDK_T, Event::TimeMachineMode );
setDefaultKey( KBDK_GRAVE, Event::DebuggerMode );
setDefaultKey( KBDK_ESCAPE, Event::LauncherMode );
break;
case kMenuMode:
setDefaultKey( KBDK_UP, Event::UIUp );
setDefaultKey( KBDK_DOWN, Event::UIDown );
setDefaultKey( KBDK_LEFT, Event::UILeft );
setDefaultKey( KBDK_RIGHT, Event::UIRight );
setDefaultKey( KBDK_HOME, Event::UIHome );
setDefaultKey( KBDK_END, Event::UIEnd );
setDefaultKey( KBDK_PAGEUP, Event::UIPgUp );
setDefaultKey( KBDK_PAGEDOWN, Event::UIPgDown );
setDefaultKey( KBDK_RETURN, Event::UISelect );
setDefaultKey( KBDK_ESCAPE, Event::UICancel );
setDefaultKey( KBDK_BACKSPACE, Event::UIPrevDir );
break;
default:
return;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalKeyboardHandler::eraseMapping(Event::Type event, EventMode mode)
{
for(int i = 0; i < KBDK_LAST; ++i)
if(myKeyTable[i][mode] == event && i != KBDK_TAB)
myKeyTable[i][mode] = Event::NoType;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalKeyboardHandler::saveMapping()
{
// Iterate through the keymap table and create a colon-separated list
// Prepend the event count, so we can check it on next load
ostringstream keybuf;
keybuf << Event::LastType;
for(int mode = 0; mode < kNumModes; ++mode)
for(int i = 0; i < KBDK_LAST; ++i)
keybuf << ":" << myKeyTable[i][mode];
myOSystem.settings().setValue("keymap", keybuf.str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string PhysicalKeyboardHandler::getMappingDesc(Event::Type event, EventMode mode) const
{
ostringstream buf;
for(int k = 0; k < KBDK_LAST; ++k)
{
if(myKeyTable[k][mode] == event)
{
if(buf.str() != "")
buf << ", ";
buf << StellaKeyName::forKey(StellaKey(k));
}
}
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PhysicalKeyboardHandler::addMapping(Event::Type event, EventMode mode,
StellaKey key)
{
// These keys cannot be remapped
if(key == KBDK_TAB || Event::isAnalog(event))
return false;
else
myKeyTable[key][mode] = event;
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PhysicalKeyboardHandler::handleEvent(StellaKey key, StellaMod mod, bool state)
{
// Swallow KBDK_TAB under certain conditions
// See commments on 'myAltKeyCounter' for more information
#ifdef BSPF_UNIX
if(myAltKeyCounter > 1 && key == KBDK_TAB)
{
myAltKeyCounter = 0;
return;
}
#endif
bool handled = true;
EventHandlerState estate = myHandler.state();
// Immediately store the key state
myEvent.setKey(key, state);
// An attempt to speed up event processing; we quickly check for
// Control or Alt/Cmd combos first
if(StellaModTest::isAlt(mod) && state)
{
#ifdef BSPF_MAC_OSX
// These keys work in all states
if(key == KBDK_Q)
{
myHandler.handleEvent(Event::Quit, 1);
}
else
#endif
if(key == KBDK_TAB)
{
// Swallow Alt-Tab, but remember that it happened
myAltKeyCounter = 1;
return;
}
else if(key == KBDK_RETURN)
{
myOSystem.frameBuffer().toggleFullscreen();
}
// State rewinding must work in pause mode too
else if(estate == EventHandlerState::EMULATION || estate == EventHandlerState::PAUSE)
{
switch(key)
{
case KBDK_LEFT: // Alt-left(-shift) rewinds 1(10) states
myHandler.enterTimeMachineMenuMode((StellaModTest::isShift(mod) && state) ? 10 : 1, false);
break;
case KBDK_RIGHT: // Alt-right(-shift) unwinds 1(10) states
myHandler.enterTimeMachineMenuMode((StellaModTest::isShift(mod) && state) ? 10 : 1, true);
break;
case KBDK_DOWN: // Alt-down rewinds to start of list
myHandler.enterTimeMachineMenuMode(1000, false);
break;
case KBDK_UP: // Alt-up rewinds to end of list
myHandler.enterTimeMachineMenuMode(1000, true);
break;
// These can work in pause mode too
case KBDK_EQUALS:
myOSystem.frameBuffer().changeWindowedVidMode(+1);
break;
case KBDK_MINUS:
myOSystem.frameBuffer().changeWindowedVidMode(-1);
break;
case KBDK_LEFTBRACKET:
myOSystem.sound().adjustVolume(-1);
break;
case KBDK_RIGHTBRACKET:
myOSystem.sound().adjustVolume(+1);
break;
case KBDK_PAGEUP: // Alt-PageUp increases YStart
myOSystem.console().changeYStart(+1);
break;
case KBDK_PAGEDOWN: // Alt-PageDown decreases YStart
myOSystem.console().changeYStart(-1);
break;
case KBDK_1: // Alt-1 turns off NTSC filtering
myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::PRESET_OFF);
break;
case KBDK_2: // Alt-2 turns on 'composite' NTSC filtering
myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::PRESET_COMPOSITE);
break;
case KBDK_3: // Alt-3 turns on 'svideo' NTSC filtering
myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::PRESET_SVIDEO);
break;
case KBDK_4: // Alt-4 turns on 'rgb' NTSC filtering
myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::PRESET_RGB);
break;
case KBDK_5: // Alt-5 turns on 'bad' NTSC filtering
myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::PRESET_BAD);
break;
case KBDK_6: // Alt-6 turns on 'custom' NTSC filtering
myOSystem.frameBuffer().tiaSurface().setNTSC(NTSCFilter::PRESET_CUSTOM);
break;
case KBDK_7: // Alt-7 changes scanline intensity for NTSC filtering
if(StellaModTest::isShift(mod))
myOSystem.frameBuffer().tiaSurface().setScanlineIntensity(-5);
else
myOSystem.frameBuffer().tiaSurface().setScanlineIntensity(+5);
break;
case KBDK_8: // Alt-8 turns toggles scanline interpolation
myOSystem.frameBuffer().tiaSurface().toggleScanlineInterpolation();
break;
case KBDK_9: // Alt-9 selects various custom adjustables for NTSC filtering
if(myOSystem.frameBuffer().tiaSurface().ntscEnabled())
{
if(StellaModTest::isShift(mod))
myOSystem.frameBuffer().showMessage(
myOSystem.frameBuffer().tiaSurface().ntsc().setPreviousAdjustable());
else
myOSystem.frameBuffer().showMessage(
myOSystem.frameBuffer().tiaSurface().ntsc().setNextAdjustable());
}
break;
case KBDK_0: // Alt-0 changes custom adjustables for NTSC filtering
if(myOSystem.frameBuffer().tiaSurface().ntscEnabled())
{
if(StellaModTest::isShift(mod))
myOSystem.frameBuffer().showMessage(
myOSystem.frameBuffer().tiaSurface().ntsc().decreaseAdjustable());
else
myOSystem.frameBuffer().showMessage(
myOSystem.frameBuffer().tiaSurface().ntsc().increaseAdjustable());
}
break;
case KBDK_Z:
if(StellaModTest::isShift(mod))
myOSystem.console().toggleP0Collision();
else
myOSystem.console().toggleP0Bit();
break;
case KBDK_X:
if(StellaModTest::isShift(mod))
myOSystem.console().toggleP1Collision();
else
myOSystem.console().toggleP1Bit();
break;
case KBDK_C:
if(StellaModTest::isShift(mod))
myOSystem.console().toggleM0Collision();
else
myOSystem.console().toggleM0Bit();
break;
case KBDK_V:
if(StellaModTest::isShift(mod))
myOSystem.console().toggleM1Collision();
else
myOSystem.console().toggleM1Bit();
break;
case KBDK_B:
if(StellaModTest::isShift(mod))
myOSystem.console().toggleBLCollision();
else
myOSystem.console().toggleBLBit();
break;
case KBDK_N:
if(StellaModTest::isShift(mod))
myOSystem.console().togglePFCollision();
else
myOSystem.console().togglePFBit();
break;
case KBDK_COMMA:
myOSystem.console().toggleFixedColors();
break;
case KBDK_PERIOD:
if(StellaModTest::isShift(mod))
myOSystem.console().toggleCollisions();
else
myOSystem.console().toggleBits();
break;
case KBDK_I: // Alt-i decreases phosphor blend
myOSystem.console().changePhosphor(-1);
break;
case KBDK_O: // Alt-o increases phosphor blend
myOSystem.console().changePhosphor(+1);
break;
case KBDK_P: // Alt-p toggles phosphor effect
myOSystem.console().togglePhosphor();
break;
case KBDK_J: // Alt-j toggles scanline jitter
myOSystem.console().toggleJitter();
break;
case KBDK_L:
myOSystem.frameBuffer().toggleFrameStats();
break;
case KBDK_T: // Alt-t toggles Time Machine
myOSystem.state().toggleTimeMachine();
break;
case KBDK_S:
myOSystem.png().toggleContinuousSnapshots(StellaModTest::isShift(mod));
break;
default:
handled = false;
break;
}
}
else
handled = false;
}
else if(StellaModTest::isControl(mod) && state && myUseCtrlKeyFlag)
{
// These keys work in all states
if(key == KBDK_Q)
{
myHandler.handleEvent(Event::Quit, 1);
}
// These only work when in emulation mode
else if(estate == EventHandlerState::EMULATION || estate == EventHandlerState::PAUSE)
{
switch(key)
{
case KBDK_0: // Ctrl-0 switches between mouse control modes
myHandler.handleMouseControl();
break;
case KBDK_1: // Ctrl-1 swaps Stelladaptor/2600-daptor ports
myHandler.toggleSAPortOrder();
break;
case KBDK_F: // (Shift) Ctrl-f toggles NTSC/PAL/SECAM mode
myOSystem.console().toggleFormat(StellaModTest::isShift(mod) ? -1 : 1);
break;
case KBDK_G: // Ctrl-g (un)grabs mouse
if(!myOSystem.frameBuffer().fullScreen())
{
myOSystem.frameBuffer().toggleGrabMouse();
myOSystem.frameBuffer().showMessage(myOSystem.frameBuffer().grabMouseEnabled()
? "Grab mouse enabled" : "Grab mouse disabled");
}
break;
case KBDK_L: // Ctrl-l toggles PAL color-loss effect
myOSystem.console().toggleColorLoss();
break;
case KBDK_P: // Ctrl-p toggles different palettes
myOSystem.console().togglePalette();
break;
case KBDK_R: // Ctrl-r reloads the currently loaded ROM
myOSystem.reloadConsole();
break;
case KBDK_PAGEUP: // Ctrl-PageUp increases Height
myOSystem.console().changeHeight(+1);
break;
case KBDK_PAGEDOWN: // Ctrl-PageDown decreases Height
myOSystem.console().changeHeight(-1);
break;
case KBDK_S: // Ctrl-s saves properties to a file
{
string filename = myOSystem.baseDir() +
myOSystem.console().properties().get(Cartridge_Name) + ".pro";
ofstream out(filename);
if(out)
{
out << myOSystem.console().properties();
myOSystem.frameBuffer().showMessage("Properties saved");
}
else
myOSystem.frameBuffer().showMessage("Error saving properties");
break;
}
default:
handled = false;
break;
}
}
else
handled = false;
}
else
handled = false;
// Don't pass the key on if we've already taken care of it
if(handled) return;
// Arrange the logic to take advantage of short-circuit evaluation
if(!(StellaModTest::isControl(mod) || StellaModTest::isShift(mod) || StellaModTest::isAlt(mod)))
{
// Special handling for Escape key
// Basically, exit whichever mode we're currently in
if(state && key == KBDK_ESCAPE)
{
switch(estate)
{
case EventHandlerState::PAUSE:
myHandler.changeStateByEvent(Event::PauseMode);
return;
case EventHandlerState::CMDMENU:
myHandler.changeStateByEvent(Event::CmdMenuMode);
return;
case EventHandlerState::TIMEMACHINE:
myHandler.changeStateByEvent(Event::TimeMachineMode);
return;
#if 0 // FIXME - exits ROM too, when it should just go back to ROM
case EventHandlerState::DEBUGGER:
myHandler.changeStateByEvent(Event::DebuggerMode);
return;
#endif
default:
break;
}
}
// Handle keys which switch eventhandler state
if(!state && myHandler.changeStateByEvent(myKeyTable[key][kEmulationMode]))
return;
}
// Otherwise, let the event handler deal with it
switch(estate)
{
case EventHandlerState::EMULATION:
myHandler.handleEvent(myKeyTable[key][kEmulationMode], state);
break;
case EventHandlerState::PAUSE:
switch(myKeyTable[key][kEmulationMode])
{
case Event::TakeSnapshot:
case Event::DebuggerMode:
myHandler.handleEvent(myKeyTable[key][kEmulationMode], state);
break;
default:
break;
}
break;
default:
if(myHandler.hasOverlay())
myHandler.overlay().handleKeyEvent(key, mod, state);
break;
}
}

View File

@ -0,0 +1,95 @@
//============================================================================
//
// 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_KEYBOARD_HANDLER_HXX
#define PHYSICAL_KEYBOARD_HANDLER_HXX
#include <map>
class OSystem;
class EventHandler;
class Event;
#include "bspf.hxx"
#include "EventHandlerConstants.hxx"
/**
This class handles all physical keyboard-related operations in Stella.
It is responsible for getting/setting events associated with keyboard
actions.
Essentially, this class is an extension of the EventHandler class, but
handling only keyboard-specific functionality.
@author Stephen Anthony
*/
class PhysicalKeyboardHandler
{
public:
PhysicalKeyboardHandler(OSystem& system, EventHandler& handler, Event& event);
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 keyboard event to a virtual event/action. */
bool addMapping(Event::Type event, EventMode mode, StellaKey key);
/** Handle a physical keyboard event. */
void handleEvent(StellaKey key, StellaMod mod, bool state);
Event::Type eventForKey(StellaKey key, EventMode mode) const {
return myKeyTable[key][mode];
}
/** See comments on 'myAltKeyCounter' for more information. */
uInt8& altKeyCount() { return myAltKeyCounter; }
/** See comments on 'myUseCtrlKeyFlag' for more information. */
bool& useCtrlKey() { return myUseCtrlKeyFlag; }
private:
OSystem& myOSystem;
EventHandler& myHandler;
Event& myEvent;
// Array of key events, indexed by StellaKey
Event::Type myKeyTable[KBDK_LAST][kNumModes];
// Sometimes key combos with the Alt key become 'stuck' after the
// window changes state, and we want to ignore that event
// For example, press Alt-Tab and then upon re-entering the window,
// the app receives 'tab'; obviously the 'tab' shouldn't be happening
// So we keep track of the cases that matter (for now, Alt-Tab)
// and swallow the event afterwards
// Basically, the initial event sets the variable to 1, and upon
// returning to the app (ie, receiving EVENT_WINDOW_FOCUS_GAINED),
// the count is updated to 2, but only if it was already updated to 1
// TODO - This may be a bug in SDL, and might be removed in the future
// It only seems to be an issue in Linux
uInt8 myAltKeyCounter;
// Indicates whether the key-combos tied to the Control key are
// being used or not (since Ctrl by default is the fire button,
// pressing it with a movement key could inadvertantly activate
// a Ctrl combo when it isn't wanted)
bool myUseCtrlKeyFlag;
};
#endif

View File

@ -18,14 +18,21 @@
#include <cmath>
#include "bspf.hxx"
#include "OSystem.hxx"
#include "Console.hxx"
#include "FrameBuffer.hxx"
#include "FBSurface.hxx"
#include "Props.hxx"
#include "Settings.hxx"
#include "TIASurface.hxx"
#include "Version.hxx"
#include "PNGLibrary.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PNGLibrary::PNGLibrary(const FrameBuffer& fb)
: myFB(fb)
PNGLibrary::PNGLibrary(OSystem& osystem)
: myOSystem(osystem),
mySnapInterval(0),
mySnapCounter(0)
{
}
@ -121,12 +128,13 @@ void PNGLibrary::saveImage(const string& filename, const VariantList& comments)
if(!out.is_open())
throw runtime_error("ERROR: Couldn't create snapshot file");
const GUI::Rect& rect = myFB.imageRect();
const FrameBuffer& fb = myOSystem.frameBuffer();
const GUI::Rect& rect = fb.imageRect();
png_uint_32 width = rect.width(), height = rect.height();
// Get framebuffer pixel data (we get ABGR format)
unique_ptr<png_byte[]> buffer = make_unique<png_byte[]>(width * height * 4);
myFB.readPixels(buffer.get(), width*4, rect);
fb.readPixels(buffer.get(), width*4, rect);
// Set up pointers into "buffer" byte array
unique_ptr<png_bytep[]> rows = make_unique<png_bytep[]>(height);
@ -227,6 +235,146 @@ done:
throw runtime_error(err_message);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::updateTime(uInt64 time)
{
if(++mySnapCounter % mySnapInterval == 0)
takeSnapshot(uInt32(time) >> 10); // not quite milliseconds, but close enough
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::toggleContinuousSnapshots(bool perFrame)
{
if(mySnapInterval == 0)
{
ostringstream buf;
uInt32 interval = myOSystem.settings().getInt("ssinterval");
if(perFrame)
{
buf << "Enabling snapshots every frame";
interval = 1;
}
else
{
buf << "Enabling snapshots in " << interval << " second intervals";
interval *= uInt32(myOSystem.frameRate());
}
myOSystem.frameBuffer().showMessage(buf.str());
setContinuousSnapInterval(interval);
}
else
{
ostringstream buf;
buf << "Disabling snapshots, generated "
<< (mySnapCounter / mySnapInterval)
<< " files";
myOSystem.frameBuffer().showMessage(buf.str());
setContinuousSnapInterval(0);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::setContinuousSnapInterval(uInt32 interval)
{
mySnapInterval = interval;
mySnapCounter = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::takeSnapshot(uInt32 number)
{
if(!myOSystem.hasConsole())
return;
// Figure out the correct snapshot name
string filename;
bool showmessage = number == 0;
string sspath = myOSystem.snapshotSaveDir() +
(myOSystem.settings().getString("snapname") != "int" ?
myOSystem.romFile().getNameWithExt("")
: myOSystem.console().properties().get(Cartridge_Name));
// Check whether we want multiple snapshots created
if(number > 0)
{
ostringstream buf;
buf << sspath << "_" << std::hex << std::setw(8) << std::setfill('0')
<< number << ".png";
filename = buf.str();
}
else if(!myOSystem.settings().getBool("sssingle"))
{
// Determine if the file already exists, checking each successive filename
// until one doesn't exist
filename = sspath + ".png";
FilesystemNode node(filename);
if(node.exists())
{
ostringstream buf;
for(uInt32 i = 1; ;++i)
{
buf.str("");
buf << sspath << "_" << i << ".png";
FilesystemNode next(buf.str());
if(!next.exists())
break;
}
filename = buf.str();
}
}
else
filename = sspath + ".png";
// Some text fields to add to the PNG snapshot
VariantList comments;
ostringstream version;
version << "Stella " << STELLA_VERSION << " (Build " << STELLA_BUILD << ") ["
<< BSPF::ARCH << "]";
VarList::push_back(comments, "Software", version.str());
VarList::push_back(comments, "ROM Name", myOSystem.console().properties().get(Cartridge_Name));
VarList::push_back(comments, "ROM MD5", myOSystem.console().properties().get(Cartridge_MD5));
VarList::push_back(comments, "TV Effects", myOSystem.frameBuffer().tiaSurface().effectsInfo());
// Now create a PNG snapshot
if(myOSystem.settings().getBool("ss1x"))
{
string message = "Snapshot saved";
try
{
GUI::Rect rect;
const FBSurface& surface = myOSystem.frameBuffer().tiaSurface().baseSurface(rect);
myOSystem.png().saveImage(filename, surface, rect, comments);
}
catch(const runtime_error& e)
{
message = e.what();
}
if(showmessage)
myOSystem.frameBuffer().showMessage(message);
}
else
{
// Make sure we have a 'clean' image, with no onscreen messages
myOSystem.frameBuffer().enableMessages(false);
myOSystem.frameBuffer().tiaSurface().reRender();
string message = "Snapshot saved";
try
{
myOSystem.png().saveImage(filename, comments);
}
catch(const runtime_error& e)
{
message = e.what();
}
// Re-enable old messages
myOSystem.frameBuffer().enableMessages(true);
if(showmessage)
myOSystem.frameBuffer().showMessage(message);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool PNGLibrary::allocateStorage(png_uint_32 w, png_uint_32 h)
{
@ -276,12 +424,13 @@ void PNGLibrary::loadImagetoSurface(FBSurface& surface)
uInt8* i_buf = ReadInfo.buffer.get();
uInt32 i_pitch = ReadInfo.pitch;
const FrameBuffer& fb = myOSystem.frameBuffer();
for(uInt32 irow = 0; irow < ih; ++irow, i_buf += i_pitch, s_buf += s_pitch)
{
uInt8* i_ptr = i_buf;
uInt32* s_ptr = s_buf;
for(uInt32 icol = 0; icol < ReadInfo.width; ++icol, i_ptr += 3)
*s_ptr++ = myFB.mapRGB(*i_ptr, *(i_ptr+1), *(i_ptr+2));
*s_ptr++ = fb.mapRGB(*i_ptr, *(i_ptr+1), *(i_ptr+2));
}
}

View File

@ -20,6 +20,7 @@
#include <png.h>
class OSystem;
class FrameBuffer;
class FBSurface;
class Properties;
@ -36,7 +37,7 @@ class Properties;
class PNGLibrary
{
public:
PNGLibrary(const FrameBuffer& fb);
PNGLibrary(OSystem& osystem);
/**
Read a PNG image from the specified file into a FBSurface structure,
@ -82,8 +83,51 @@ class PNGLibrary
const GUI::Rect& rect = GUI::EmptyRect,
const VariantList& comments = EmptyVarList);
/**
Called at regular intervals, and used to determine whether a
continuous snapshot is due to be taken.
@param time The current time in microseconds
*/
void updateTime(uInt64 time);
/**
Answer whether continuous snapshot mode is enabled.
*/
bool continuousSnapEnabled() const { return mySnapInterval > 0; }
/**
Enable/disable continuous snapshot mode.
@param perFrame Toggle snapshots every frame, or that specified by
'ssinterval' setting.
*/
void toggleContinuousSnapshots(bool perFrame);
/**
Set the number of seconds between taking a snapshot in
continuous snapshot mode. Setting an interval of 0 disables
continuous snapshots.
@param interval Interval in seconds between snapshots
*/
void setContinuousSnapInterval(uInt32 interval);
/**
Create a new snapshot based on the name of the ROM, and also
optionally using the number given as a parameter.
@param number Optional number to append to the snapshot name
*/
void takeSnapshot(uInt32 number = 0);
private:
const FrameBuffer& myFB;
// Global OSystem object
OSystem& myOSystem;
// Used for continuous snapshot mode
uInt32 mySnapInterval;
uInt32 mySnapCounter;
// The following data remains between invocations of allocateStorage,
// and is only changed when absolutely necessary.

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 PHYSICAL_JOYSTICK_HXX
#define PHYSICAL_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

@ -177,8 +177,8 @@ class RewindManager
// We do nothing on object instantiation or copy
// The goal of LinkedObjectPool is to not do any allocations at all
RewindState() { }
RewindState(const RewindState&) { }
RewindState() : cycles(0) { }
RewindState(const RewindState& rs) : cycles(rs.cycles) { }
// Output object info; used for debugging only
friend ostream& operator<<(ostream& os, const RewindState& s) {

View File

@ -49,6 +49,13 @@ SoundSDL2::SoundSDL2(OSystem& osystem)
{
myOSystem.logMessage("SoundSDL2::SoundSDL2 started ...", 2);
#ifdef BSPF_WINDOWS
// TODO - remove the following code once we convert to the new sound
// core, and use 32-bit floating point samples and do
// our own resampling
SDL_setenv("SDL_AUDIODRIVER", "directsound", true);
#endif
// The sound system is opened only once per program run, to eliminate
// issues with opening and closing it multiple times
// This fixes a bug most prevalent with ATI video cards in Windows,

View File

@ -435,4 +435,12 @@ namespace StellaModTest
}
};
namespace StellaKeyName
{
inline const char* const forKey(StellaKey key)
{
return SDL_GetScancodeName(SDL_Scancode(key));
}
};
#endif /* StellaKeys */

View File

@ -28,6 +28,7 @@
#include "Settings.hxx"
#include "FSNode.hxx"
#include "OSystem.hxx"
#include "PNGLibrary.hxx"
#include "System.hxx"
#ifdef DEBUGGER_SUPPORT
@ -124,15 +125,22 @@ int main(int argc, char* argv[])
}
else
{
const string& result = theOSystem->createConsole(romnode);
if(result != EmptyString)
return Cleanup();
if(theOSystem->settings().getBool("takesnapshot"))
try
{
theOSystem->logMessage("Taking snapshots with 'takesnapshot' ...", 2);
for(int i = 0; i < 30; ++i) theOSystem->frameBuffer().update();
theOSystem->eventHandler().takeSnapshot();
const string& result = theOSystem->createConsole(romnode);
if(result != EmptyString)
return Cleanup();
if(theOSystem->settings().getBool("takesnapshot"))
{
for(int i = 0; i < 30; ++i) theOSystem->frameBuffer().update();
theOSystem->png().takeSnapshot();
return Cleanup();
}
}
catch(const runtime_error& e)
{
theOSystem->logMessage(e.what(), 0);
return Cleanup();
}

View File

@ -1,16 +1,19 @@
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/PKeyboardHandler.o \
src/common/PNGLibrary.o \
src/common/RewindManager.o \
src/common/SoundSDL2.o \
src/common/StateManager.o \
src/common/ZipHandler.o \
src/common/AudioQueue.o

View File

@ -184,6 +184,8 @@ class AtariNTSC
float artifacts;
float fringing;
float kernel [rescale_out * kernel_size * 2];
init_t() : contrast(0.0), brightness(0.0), artifacts(0.0), fringing(0.0) { }
};
init_t myImpl;

View File

@ -735,7 +735,7 @@ string CartDebug::loadListFile()
getline(in, line);
if(line.length() == 0 || line[0] == '-')
if(!in.good() || line == "" || line[0] == '-')
continue;
else // Search for constants
{
@ -798,6 +798,7 @@ string CartDebug::loadSymbolFile()
int value = -1;
getline(in, label);
if(!in.good()) continue;
stringstream buf(label);
buf >> label >> hex >> value;

View File

@ -226,9 +226,9 @@ const string Debugger::invIfChanged(int reg, int oldReg)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::reset()
{
unlockBankswitchState();
unlockSystem();
mySystem.reset();
lockBankswitchState();
lockSystem();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -253,21 +253,21 @@ string Debugger::setRAM(IntArray& args)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::saveState(int state)
{
mySystem.clearDirtyPages();
unlockBankswitchState();
// Saving a state is implicitly a read-only operation, so we keep the
// system locked, so no changes can occur
myOSystem.state().saveState(state);
lockBankswitchState();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::loadState(int state)
{
// We're loading a new state, so we start with a clean slate
mySystem.clearDirtyPages();
unlockBankswitchState();
// State loading could initiate a bankswitch, so we allow it temporarily
unlockSystem();
myOSystem.state().loadState(state);
lockBankswitchState();
lockSystem();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -277,9 +277,9 @@ int Debugger::step()
uInt64 startCycle = mySystem.cycles();
unlockBankswitchState();
unlockSystem();
myOSystem.console().tia().updateScanlineByStep().flushLineCache();
lockBankswitchState();
lockSystem();
addState("step");
return int(mySystem.cycles() - startCycle);
@ -305,9 +305,9 @@ int Debugger::trace()
uInt64 startCycle = mySystem.cycles();
int targetPC = myCpuDebug->pc() + 3; // return address
unlockBankswitchState();
unlockSystem();
myOSystem.console().tia().updateScanlineByTrace(targetPC).flushLineCache();
lockBankswitchState();
lockSystem();
addState("trace");
return int(mySystem.cycles() - startCycle);
@ -490,13 +490,13 @@ void Debugger::nextScanline(int lines)
saveOldState();
unlockBankswitchState();
unlockSystem();
while(lines)
{
myOSystem.console().tia().updateScanline();
--lines;
}
lockBankswitchState();
lockSystem();
addState(buf.str());
myOSystem.console().tia().flushLineCache();
@ -510,13 +510,13 @@ void Debugger::nextFrame(int frames)
saveOldState();
unlockBankswitchState();
unlockSystem();
while(frames)
{
myOSystem.console().tia().update();
--frames;
}
lockBankswitchState();
lockSystem();
addState(buf.str());
}
@ -535,13 +535,13 @@ uInt16 Debugger::windStates(uInt16 numStates, bool unwind, string& message)
saveOldState();
unlockBankswitchState();
unlockSystem();
uInt64 startCycles = myOSystem.console().tia().cycles();
uInt16 winds = r.windStates(numStates, unwind);
message = r.getUnitString(myOSystem.console().tia().cycles() - startCycles);
lockBankswitchState();
lockSystem();
updateRewindbuttons(r);
return winds;
@ -593,13 +593,15 @@ bool Debugger::patchROM(uInt16 addr, uInt8 value)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::saveOldState(bool clearDirtyPages)
{
if (clearDirtyPages)
if(clearDirtyPages)
mySystem.clearDirtyPages();
lockSystem();
myCartDebug->saveOldState();
myCpuDebug->saveOldState();
myRiotDebug->saveOldState();
myTiaDebug->saveOldState();
unlockSystem();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -615,7 +617,7 @@ void Debugger::addState(string rewindMsg)
void Debugger::setStartState()
{
// Lock the bus each time the debugger is entered, so we don't disturb anything
lockBankswitchState();
lockSystem();
// Save initial state and add it to the rewind list (except when in currently rewinding)
RewindManager& r = myOSystem.state().rewindManager();
@ -636,7 +638,7 @@ void Debugger::setQuitState()
saveOldState();
// Bus must be unlocked for normal operation when leaving debugger mode
unlockBankswitchState();
unlockSystem();
// execute one instruction on quit. If we're
// sitting at a breakpoint/trap, this will get us past it.
@ -771,14 +773,14 @@ void Debugger::getCompletions(const char* in, StringList& list) const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::lockBankswitchState()
void Debugger::lockSystem()
{
mySystem.lockDataBus();
myConsole.cartridge().lockBank();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::unlockBankswitchState()
void Debugger::unlockSystem()
{
mySystem.unlockDataBus();
myConsole.cartridge().unlockBank();

View File

@ -231,14 +231,13 @@ class Debugger : public DialogContainer
/**
Normally, accessing RAM or ROM during emulation can possibly trigger
bankswitching. However, when we're in the debugger, we'd like to
inspect values without actually triggering bankswitches. The
bankswitching or other inadvertent changes. However, when we're in
the debugger, we'd like to inspect values without restriction. The
read/write state must therefore be locked before accessing values,
and unlocked for normal emulation to occur.
(takes mediasource into account)
*/
void lockBankswitchState();
void unlockBankswitchState();
void lockSystem();
void unlockSystem();
/**
Answers whether the debugger can be exited. Currently this only

View File

@ -58,6 +58,7 @@ using std::right;
DebuggerParser::DebuggerParser(Debugger& d, Settings& s)
: debugger(d),
settings(s),
myCommand(0),
argCount(0),
execDepth(0),
execPrefix("")
@ -1459,13 +1460,10 @@ void DebuggerParser::executeListfunctions()
void DebuggerParser::executeListsavestateifs()
{
ostringstream buf;
int count = 0;
StringList conds = debugger.m6502().getCondSaveStateNames();
if(conds.size() > 0)
{
if(count)
commandResult << endl;
commandResult << "savestateif:" << endl;
for(uInt32 i = 0; i < conds.size(); i++)
{

View File

@ -26,8 +26,11 @@ CartridgeMNetworkWidget::CartridgeMNetworkWidget(
int x, int y, int w, int h,
CartridgeMNetwork& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart)
{}
myCart(cart),
myLower2K(nullptr),
myUpper256B(nullptr)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeMNetworkWidget::initialize(GuiObject* boss, CartridgeMNetwork& cart, ostringstream& info)

View File

@ -34,11 +34,11 @@ class CartridgeMNetworkWidget : public CartDebugWidget
virtual ~CartridgeMNetworkWidget() = default;
protected:
PopUpWidget *myLower2K, *myUpper256B;
//CartridgeE7& myCart;
CartridgeMNetwork& myCart;
PopUpWidget *myLower2K, *myUpper256B;
struct CartState
{
ByteArray internalram;

View File

@ -64,11 +64,15 @@ void DrivingWidget::loadConfig()
if(myController.read(Controller::Two)) gray += 2;
for(myGrayIndex = 0; myGrayIndex < 4; ++myGrayIndex)
{
if(ourGrayTable[myGrayIndex] == gray)
{
setValue(myGrayIndex);
break;
}
}
myFire->setState(!myController.read(Controller::Six));
setValue();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -81,13 +85,13 @@ void DrivingWidget::handleCommand(
myGrayIndex = (myGrayIndex + 1) % 4;
myController.set(Controller::One, (ourGrayTable[myGrayIndex] & 0x1) != 0);
myController.set(Controller::Two, (ourGrayTable[myGrayIndex] & 0x2) != 0);
setValue();
setValue(myGrayIndex);
break;
case kGrayDownCmd:
myGrayIndex = myGrayIndex == 0 ? 3 : myGrayIndex - 1;
myController.set(Controller::One, (ourGrayTable[myGrayIndex] & 0x1) != 0);
myController.set(Controller::Two, (ourGrayTable[myGrayIndex] & 0x2) != 0);
setValue();
setValue(myGrayIndex);
break;
case kFireCmd:
myController.set(Controller::Six, !myFire->getState());
@ -96,9 +100,9 @@ void DrivingWidget::handleCommand(
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DrivingWidget::setValue()
void DrivingWidget::setValue(int idx)
{
int grayCode = ourGrayTable[myGrayIndex];
int grayCode = ourGrayTable[idx];
// FIXME * 8 = a nasty hack, because the DataGridWidget does not support 2 digit binary output
myGrayValue->setList(0, (grayCode & 0b01) + (grayCode & 0b10) * 8);
}

View File

@ -50,7 +50,7 @@ class DrivingWidget : public ControllerWidget
void loadConfig() override;
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
void setValue();
void setValue(int idx);
// Following constructors and assignment operators not supported
DrivingWidget() = delete;

View File

@ -22,7 +22,8 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FlashWidget::FlashWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, Controller& controller)
: ControllerWidget(boss, font, x, y, controller)
: ControllerWidget(boss, font, x, y, controller),
myEEPROMEraseCurrent(nullptr)
{
}
@ -34,7 +35,7 @@ void FlashWidget::init(GuiObject* boss, const GUI::Font& font, int x, int y)
int xpos = x, ypos = y;
new StaticTextWidget(boss, font, xpos, ypos + 2, getHeader());
ypos += lineHeight + 6;
new StaticTextWidget(boss, ifont, xpos, ypos, "Pages/Ranges used:");

View File

@ -34,7 +34,9 @@ PromptWidget::PromptWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h)
: Widget(boss, font, x, y, w - kScrollBarWidth, h),
CommandSender(boss),
_historySize(0),
_historyIndex(0),
_historyLine(0),
_makeDirty(false),
_firstTime(true),
_exitedEarly(false)

View File

@ -127,7 +127,7 @@ TiaWidget::TiaWidget(GuiObject* boss, const GUI::Font& lfont,
2*fontWidth, fontHeight,
rowLabel[row], TextAlign::Left);
for(uInt32 col = 0; col < 5 - row; ++col)
for(uInt32 col = 0; col < 5 - row; ++col, ++idx)
{
myCollision[idx] = new CheckboxWidget(boss, lfont, collX, collY, "", CheckboxWidget::kCheckActionCmd);
myCollision[idx]->setTarget(this);
@ -149,7 +149,6 @@ TiaWidget::TiaWidget(GuiObject* boss, const GUI::Font& lfont,
colLabel[col], TextAlign::Left);
collX += myCollision[idx]->getWidth() + 10;
idx++;
}
collX = xpos + lwidth + 5;
collY += lineHeight+3;

View File

@ -102,6 +102,11 @@ void CartridgeBUS::setInitialState()
// Assuming mode starts out with Fast Fetch off and 3-Voice music,
// need to confirm with Chris
myMode = 0xFF;
myBankOffset = myBusOverdriveAddress =
mySTYZeroPageAddress = myJMPoperandAddress = 0;
myFastJumpActive = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -105,6 +105,7 @@ void CartridgeCDF::setInitialState()
// need to confirm with Chris
myMode = 0xFF;
myBankOffset = myLDAimmediateOperandAddress = myJMPoperandAddress = 0;
myFastJumpActive = 0;
}

View File

@ -273,8 +273,6 @@ class CartridgeCDF : public Cartridge
// *and* the next two bytes in ROM are 00 00
uInt16 myJMPoperandAddress;
TIA* myTIA;
uInt8 myFastJumpActive;
// version of CDF

View File

@ -23,7 +23,8 @@ CartridgeMNetwork::CartridgeMNetwork(const BytePtr& image, uInt32 size,
const Settings& settings)
: Cartridge(settings),
mySize(size),
myCurrentRAM(0)
myCurrentRAM(0),
myRAMSlice(0)
{
}

View File

@ -94,6 +94,9 @@ class Console : public Serializable
*/
Controller& leftController() const { return *myLeftControl; }
Controller& rightController() const { return *myRightControl; }
Controller& controller(Controller::Jack jack) const {
return jack == Controller::Left ? leftController() : rightController();
}
/**
Get the TIA for this console

View File

@ -171,6 +171,12 @@ class Controller : public Serializable
*/
virtual void update() = 0;
/**
Answers whether the controller is intrinsically an analog controller.
Specific controllers should override and implement this method.
*/
virtual bool isAnalog() const { return false; }
/**
Notification method invoked by the system after its reset method has
been called. It may be necessary to override this method for

View File

@ -47,6 +47,11 @@ class Driving : public Controller
*/
void update() override;
/**
Answers whether the controller is intrinsically an analog controller.
*/
bool isAnalog() const override { return true; }
/**
Determines how this controller will treat values received from the
X/Y axis and left/right buttons of the mouse. Since not all controllers

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];

File diff suppressed because it is too large Load Diff

View File

@ -24,11 +24,14 @@ class Console;
class OSystem;
class MouseControl;
class DialogContainer;
class PhysicalJoystick;
#include "Event.hxx"
#include "EventHandlerConstants.hxx"
#include "Control.hxx"
#include "StellaKeys.hxx"
#include "PKeyboardHandler.hxx"
#include "PJoystickHandler.hxx"
#include "Variant.hxx"
#include "bspf.hxx"
@ -95,11 +98,12 @@ class EventHandler
void poll(uInt64 time);
/**
Returns the current state of the EventHandler
Get/set the current state of the EventHandler
@return The EventHandlerState type
*/
EventHandlerState state() const { return myState; }
void setState(EventHandlerState state);
/**
Resets the state machine of the EventHandler to the defaults
@ -123,21 +127,11 @@ class EventHandler
*/
void setMouseControllerMode(const string& enable);
/**
Set the number of seconds between taking a snapshot in
continuous snapshot mode. Setting an interval of 0 disables
continuous snapshots.
@param interval Interval in seconds between snapshots
*/
void setContinuousSnapshots(uInt32 interval);
void enterMenuMode(EventHandlerState state);
void leaveMenuMode();
bool enterDebugMode();
void leaveDebugMode();
void enterTimeMachineMenuMode(uInt32 numWinds, bool unwind);
void takeSnapshot(uInt32 number = 0);
/**
Send an event directly to the event handler.
@ -163,19 +157,18 @@ class EventHandler
StringList getComboListForEvent(Event::Type event) const;
void setComboListForEvent(Event::Type event, const StringList& events);
Event::Type eventForKey(StellaKey key, EventMode mode) const
{ return myKeyTable[key][mode]; }
/** Convert keys and physical joystick events into Stella events. */
Event::Type eventForKey(StellaKey key, EventMode mode) const {
return myPKeyHandler->eventForKey(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 +185,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 +202,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 +217,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
@ -270,30 +263,46 @@ class EventHandler
void allowAllDirections(bool allow) { myAllowAllDirectionsFlag = allow; }
/**
Determines whether the given controller must use the mouse (aka,
whether the controller generates analog output).
Changes to a new state based on the current state and the given event.
@param jack The controller to query
@param type The event
@return True if the state changed, else false
*/
bool controllerIsAnalog(Controller::Jack jack) const;
bool changeStateByEvent(Event::Type type);
/**
Return a list of all joysticks currently in the internal database
Get the current overlay in use. The overlay won't always exist,
so we should test if it's available.
@return The overlay object
*/
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 joystickDatabase() const;
VariantList physicalJoystickDatabase() const {
return myPJoyHandler->database();
}
/**
Remove the joystick identified by 'name' from the joystick database,
only if it is not currently active.
Remove the physical joystick identified by 'name' from the joystick
database, only if it is not currently active.
*/
void removeJoystickFromDatabase(const string& name);
void removePhysicalJoystickFromDatabase(const string& name);
/**
Enable/disable text events (distinct from single-key events).
*/
virtual void enableTextEvents(bool enable) = 0;
/**
Handle changing mouse modes.
*/
void handleMouseControl();
protected:
// Global OSystem object
OSystem& myOSystem;
@ -303,18 +312,20 @@ class EventHandler
of input.
*/
void handleTextEvent(char text);
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);
/**
Returns the human-readable name for a StellaKey.
*/
virtual const char* const nameForKey(StellaKey key) const
{ return EmptyString.c_str(); }
void handleKeyEvent(StellaKey key, StellaMod mod, bool state) {
myPKeyHandler->handleEvent(key, mod, state);
}
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);
}
/**
Collects and dispatches any pending events.
@ -338,120 +349,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(PhysicalJoystickPtr 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,35 +367,16 @@ 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.
*/
void setActionMappings(EventMode mode);
void setKeymap();
void setDefaultKeymap(Event::Type, EventMode mode);
void setDefaultJoymap(Event::Type, EventMode mode);
void saveKeyMapping();
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:
// Structure used for action menu items
struct ActionList {
@ -505,31 +392,28 @@ class EventHandler
// Indicates current overlay object
DialogContainer* myOverlay;
// Handler for all keyboard-related events
unique_ptr<PhysicalKeyboardHandler> myPKeyHandler;
// Handler for all joystick addition/removal/mapping
unique_ptr<PhysicalJoystickHandler> myPJoyHandler;
// MouseControl object, which takes care of switching the mouse between
// all possible controller modes
unique_ptr<MouseControl> myMouseControl;
// Array of key events, indexed by StellaKey
Event::Type myKeyTable[KBDK_LAST][kNumModes];
// The event(s) assigned to each combination event
Event::Type myComboTable[kComboSize][kEventsPerCombo];
// 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
bool myFryingFlag;
// Indicates whether the key-combos tied to the Control key are
// being used or not (since Ctrl by default is the fire button,
// pressing it with a movement key could inadvertantly activate
// a Ctrl combo when it isn't wanted)
bool myUseCtrlKeyFlag;
// Sometimes an extraneous mouse motion event occurs after a video
// state change; we detect when this happens and discard the event
bool mySkipMouseMotion;
@ -538,35 +422,10 @@ class EventHandler
// of the 7800 (for now, only the switches are notified)
bool myIs7800;
// Sometimes key combos with the Alt key become 'stuck' after the
// window changes state, and we want to ignore that event
// For example, press Alt-Tab and then upon re-entering the window,
// the app receives 'tab'; obviously the 'tab' shouldn't be happening
// So we keep track of the cases that matter (for now, Alt-Tab)
// and swallow the event afterwards
// Basically, the initial event sets the variable to 1, and upon
// returning to the app (ie, receiving EVENT_WINDOW_FOCUS_GAINED),
// the count is updated to 2, but only if it was already updated to 1
// TODO - This may be a bug in SDL, and might be removed in the future
// It only seems to be an issue in Linux
uInt8 myAltKeyCounter;
// Used for continuous snapshot mode
uInt32 myContSnapshotInterval;
uInt32 myContSnapshotCounter;
// Holds static strings for the remap menu (emulation and menu events)
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

@ -47,9 +47,10 @@ FrameBuffer::FrameBuffer(OSystem& osystem)
myInitializedCount(0),
myPausedCount(0),
myStatsEnabled(false),
myLastScanlines(0),
myGrabMouse(false),
myCurrentModeList(nullptr)
{
}
{}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBuffer::~FrameBuffer()
@ -649,8 +650,8 @@ void FrameBuffer::setCursorState()
bool emulation =
myOSystem.eventHandler().state() == EventHandlerState::EMULATION;
bool analog = myOSystem.hasConsole() ?
(myOSystem.eventHandler().controllerIsAnalog(Controller::Left) ||
myOSystem.eventHandler().controllerIsAnalog(Controller::Right)) : false;
(myOSystem.console().controller(Controller::Left).isAnalog() ||
myOSystem.console().controller(Controller::Right).isAnalog()) : false;
bool alwaysUseMouse = BSPF::equalsIgnoreCase("always", myOSystem.settings().getString("usemouse"));
grabMouse(emulation && (analog || alwaysUseMouse) && myGrabMouse);

View File

@ -506,7 +506,9 @@ class FrameBuffer
shared_ptr<FBSurface> surface;
bool enabled;
Message() : counter(0), x(0), y(0), w(0), h(0), color(0), enabled(false) { }
Message()
: counter(0), x(0), y(0), w(0), h(0), position(MessagePosition::BottomCenter),
color(0), enabled(false) { }
};
Message myMsg;
Message myStatsMsg;

View File

@ -207,15 +207,6 @@ inline void M6502::handleHalt()
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void M6502::updateStepStateByInstruction()
{
// Currently only used in debugger mode
#ifdef DEBUGGER_SUPPORT
myStepStateByInstruction = myCondBreaks.size() || myCondSaveStates.size() || myTrapConds.size();
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool M6502::execute(uInt32 number)
{
@ -258,23 +249,19 @@ inline bool M6502::_execute(uInt32 number)
{
bool read = myJustHitReadTrapFlag;
myJustHitReadTrapFlag = myJustHitWriteTrapFlag = false;
if(myDebugger && myDebugger->start(myHitTrapInfo.message, myHitTrapInfo.address, read))
{
return true;
}
if (startDebugger(myHitTrapInfo.message, myHitTrapInfo.address, read)) return true;
}
if(myBreakPoints.isInitialized() && myBreakPoints.isSet(PC))
if(myDebugger && myDebugger->start("BP: ", PC))
return true;
if(myBreakPoints.isInitialized() && myBreakPoints.isSet(PC) && startDebugger("BP: ", PC))
return true;
int cond = evalCondBreaks();
if(cond > -1)
{
stringstream msg;
msg << "CBP[" << Common::Base::HEX2 << cond << "]: " << myCondBreakNames[cond];
if(myDebugger && myDebugger->start(msg.str()))
return true;
if (startDebugger(msg.str())) return true;
}
cond = evalCondSaveStates();
@ -611,4 +598,23 @@ const StringList& M6502::getCondTrapNames() const
{
return myTrapCondNames;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void M6502::updateStepStateByInstruction()
{
myStepStateByInstruction = myCondBreaks.size() || myCondSaveStates.size() ||
myTrapConds.size();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool M6502::startDebugger(const string& message, int address, bool read)
{
handleHalt();
mySystem->tia().updateEmulation();
mySystem->m6532().updateEmulation();
return myDebugger->start(message, address, read);
}
#endif // DEBUGGER_SUPPORT

View File

@ -314,6 +314,13 @@ class M6502 : public Serializable
*/
void handleHalt();
/**
This is the actual dispatch function that does the grunt work. M6502::execute
wraps it and makes sure that any pending halt is processed before returning.
*/
bool _execute(uInt32 number);
#ifdef DEBUGGER_SUPPORT
/**
Check whether we are required to update hardware (TIA + RIOT) in lockstep
with the CPU and update the flag accordingly.
@ -321,10 +328,11 @@ class M6502 : public Serializable
void updateStepStateByInstruction();
/**
This is the actual dispatch function that does the grunt work. M6502::execute
wraps it and makes sure that any pending halt is processed before returning.
Make sure that the current hardware state is up to date (TIA & RIOT) and dispatch
debugger.
*/
bool _execute(uInt32 number);
bool startDebugger(const string& message = "", int address = -1, bool read = true);
#endif // DEBUGGER_SUPPORT
private:
/**

View File

@ -72,6 +72,11 @@ class MindLink : public Controller
*/
void update() override;
/**
Answers whether the controller is intrinsically an analog controller.
*/
bool isAnalog() const override { return true; }
/**
Determines how this controller will treat values received from the
X/Y axis and left/right buttons of the mouse. Since not all controllers

View File

@ -154,7 +154,7 @@ bool OSystem::create()
myRandom->initSeed();
// Create PNG handler
myPNGLib = make_unique<PNGLibrary>(*myFrameBuffer);
myPNGLib = make_unique<PNGLibrary>(*this);
return true;
}

View File

@ -54,6 +54,11 @@ class Paddles : public Controller
*/
void update() override;
/**
Answers whether the controller is intrinsically an analog controller.
*/
bool isAnalog() const override { return true; }
/**
Determines how this controller will treat values received from the
X/Y axis and left/right buttons of the mouse. Since not all controllers

View File

@ -57,6 +57,11 @@ class PointingDevice : public Controller
*/
void update() override;
/**
Answers whether the controller is intrinsically an analog controller.
*/
bool isAnalog() const override { return true; }
/**
Determines how this controller will treat values received from the
X/Y axis and left/right buttons of the mouse. Since not all controllers

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

@ -20,6 +20,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Background::Background()
: myTIA(nullptr)
{
reset();
}

View File

@ -26,7 +26,8 @@ enum Count: Int8 {
Ball::Ball(uInt32 collisionMask)
: myCollisionMaskDisabled(collisionMask),
myCollisionMaskEnabled(0xFFFF),
myIsSuppressed(false)
myIsSuppressed(false),
myTIA(nullptr)
{
reset();
}

View File

@ -83,7 +83,7 @@ void DelayQueue<length, capacity>::push(uInt8 address, uInt8 value, uInt8 delay)
uInt8 currentIndex = myIndices[address];
if (currentIndex < 0xFF)
if (currentIndex < length)
myMembers[currentIndex].remove(address);
uInt8 index = smartmod<length>(myIndex + delay);

View File

@ -28,6 +28,8 @@ class DelayQueueMember : public Serializable {
struct Entry {
uInt8 address;
uInt8 value;
Entry() : address(0), value(0) { }
};
public:
@ -52,7 +54,6 @@ class DelayQueueMember : public Serializable {
uInt8 mySize;
private:
DelayQueueMember(const DelayQueueMember<capacity>&) = delete;
DelayQueueMember(DelayQueueMember<capacity>&&) = delete;
DelayQueueMember<capacity>& operator=(const DelayQueueMember<capacity>&) = delete;
@ -68,7 +69,8 @@ class DelayQueueMember : public Serializable {
template<unsigned capacity>
DelayQueueMember<capacity>::DelayQueueMember()
: mySize(0)
{}
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
template<unsigned capacity>
@ -110,7 +112,7 @@ void DelayQueueMember<capacity>::clear()
template<unsigned capacity>
bool DelayQueueMember<capacity>::save(Serializer& out) const
{
try
try
{
out.putInt(mySize);
for(uInt8 i = 0; i < mySize; ++i)

View File

@ -28,7 +28,8 @@ Missile::Missile(uInt32 collisionMask)
: myCollisionMaskDisabled(collisionMask),
myCollisionMaskEnabled(0xFFFF),
myIsSuppressed(false),
myDecodesOffset(0)
myDecodesOffset(0),
myTIA(nullptr)
{
reset();
}

View File

@ -28,7 +28,8 @@ Player::Player(uInt32 collisionMask)
: myCollisionMaskDisabled(collisionMask),
myCollisionMaskEnabled(0xFFFF),
myIsSuppressed(false),
myDecodesOffset(0)
myDecodesOffset(0),
myTIA(nullptr)
{
reset();
}

View File

@ -22,7 +22,8 @@
Playfield::Playfield(uInt32 collisionMask)
: myCollisionMaskDisabled(collisionMask),
myCollisionMaskEnabled(0xFFFF),
myIsSuppressed(false)
myIsSuppressed(false),
myTIA(nullptr)
{
reset();
}
@ -38,9 +39,11 @@ void Playfield::reset()
myPf1 = 0;
myPf2 = 0;
myX = 0;
myObjectColor = myDebugColor = 0;
myColorP0 = 0;
myColorP1 = 0;
myColorLeft = myColorRight = 0;
myColorP0 = myColorP1 = 0;
myColorMode = ColorMode::normal;
myDebugEnabled = false;

View File

@ -170,11 +170,13 @@ void TIA::reset()
myDelayQueue.reset();
if (myFrameManager) myFrameManager->reset();
myCyclesAtFrameStart = 0;
frameReset(); // Recalculate the size of the display
if (myFrameManager)
{
myFrameManager->reset();
frameReset(); // Recalculate the size of the display
}
// Must be done last, after all other items have reset
enableFixedColors(mySettings.getBool(mySettings.getBool("dev.settings") ? "dev.debugcolors" : "plr.debugcolors"));

View File

@ -35,9 +35,21 @@ enum Metrics: uInt32 {
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameManager::FrameManager() :
myHeight(0),
myYStart(0)
FrameManager::FrameManager()
: myState(State::waitForVsyncStart),
myLineInState(0),
myVsyncLines(0),
myY(0), myLastY(0),
myVblankLines(0),
myKernelLines(0),
myOverscanLines(0),
myFrameLines(0),
myHeight(0),
myFixedHeight(0),
myYStart(0),
myJitterEnabled(false),
myStableFrameLines(-1),
myStableFrameHeightCountdown(0)
{
onLayoutChange();
}

View File

@ -25,9 +25,12 @@ enum Metrics: uInt32 {
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
JitterEmulation::JitterEmulation() :
myYStart(0)
{}
JitterEmulation::JitterEmulation()
: myJitterFactor(0),
myYStart(0)
{
reset();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JitterEmulation::reset()

View File

@ -51,6 +51,7 @@ Dialog::Dialog(OSystem& instance, DialogContainer& parent, const GUI::Font& font
_mouseWidget(nullptr),
_focusedWidget(nullptr),
_dragWidget(nullptr),
_defaultWidget(nullptr),
_okWidget(nullptr),
_cancelWidget(nullptr),
_visible(false),

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;

View File

@ -89,7 +89,7 @@ RadioButtonWidget::RadioButtonWidget(GuiObject* boss, const GUI::Font& font,
RadioButtonGroup* group,
int cmd)
: CheckboxWidget(boss, font, x, y, label, cmd),
myGroup(group)
myGroup(group)
{
_flags = WIDGET_ENABLED;
_bgcolor = _bgcolorhi = kWidColor;
@ -164,7 +164,7 @@ void RadioButtonWidget::drawWidget(bool hilite)
// Draw the inner bounding circle with enabled color
s.drawBitmap(radio_img_innercircle, _x + 1, _y + _boxY + 1, isEnabled()
? _bgcolor : kColor, 12, 12);
? _bgcolor : uInt32(kColor), 12, 12);
// draw state
if(_state)

View File

@ -56,7 +56,7 @@ class RadioButtonWidget : public CheckboxWidget
class RadioButtonGroup
{
public:
RadioButtonGroup() = default;
RadioButtonGroup() : mySelected(0) { }
// add widget to group
void addWidget(RadioButtonWidget* widget);

View File

@ -39,7 +39,8 @@ Widget::Widget(GuiObject* boss, const GUI::Font& font,
_bgcolor(kWidColor),
_bgcolorhi(kWidColor),
_textcolor(kTextColor),
_textcolorhi(kTextColorHi)
_textcolorhi(kTextColorHi),
_shadowcolor(kShadowColor)
{
// Insert into the widget list of the boss
_next = _boss->_firstWidget;
@ -354,7 +355,10 @@ ButtonWidget::ButtonWidget(GuiObject* boss, const GUI::Font& font,
: StaticTextWidget(boss, font, x, y, w, h, label, TextAlign::Center),
CommandSender(boss),
_cmd(cmd),
_useBitmap(false)
_useBitmap(false),
_bitmap(nullptr),
_bmw(0),
_bmh(0)
{
_flags = WIDGET_ENABLED | WIDGET_CLEARBG;
_bgcolor = kBtnColor;
@ -621,8 +625,8 @@ void CheckboxWidget::drawWidget(bool hilite)
if(_drawBox)
s.frameRect(_x, _y + _boxY, 14, 14, hilite && isEnabled() && isEditable() ? kWidColorHi : kColor);
// Do we draw a square or cross?
s.fillRect(_x + 1, _y + _boxY + 1, 12, 12, _changed ? kDbgChangedColor
: isEnabled() ? _bgcolor : kColor);
s.fillRect(_x + 1, _y + _boxY + 1, 12, 12, _changed ? uInt32(kDbgChangedColor)
: isEnabled() ? _bgcolor : uInt32(kColor));
if(_state)
s.drawBitmap(_img, _x + 2, _y + _boxY + 2, isEnabled() ? hilite && isEditable() ? kWidColorHi : kCheckColor
: kColor, 10);

View File

@ -233,6 +233,8 @@
DC1B2EC41E50036100F62837 /* AmigaMouse.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC1B2EBE1E50036100F62837 /* AmigaMouse.hxx */; };
DC1B2EC61E50036100F62837 /* AtariMouse.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC1B2EC01E50036100F62837 /* AtariMouse.hxx */; };
DC1B2EC81E50036100F62837 /* TrakBall.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC1B2EC21E50036100F62837 /* TrakBall.hxx */; };
DC1BC6662066B4390076F74A /* PKeyboardHandler.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC1BC6642066B4390076F74A /* PKeyboardHandler.cxx */; };
DC1BC6672066B4390076F74A /* PKeyboardHandler.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC1BC6652066B4390076F74A /* PKeyboardHandler.hxx */; };
DC1FC18A0DB3B2C7009B3DF7 /* SerialPortMACOSX.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC1FC1880DB3B2C7009B3DF7 /* SerialPortMACOSX.cxx */; };
DC1FC18B0DB3B2C7009B3DF7 /* SerialPortMACOSX.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC1FC1890DB3B2C7009B3DF7 /* SerialPortMACOSX.hxx */; };
DC2874071F8F2278004BF21A /* TrapArray.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC2874061F8F2278004BF21A /* TrapArray.hxx */; };
@ -319,7 +321,6 @@
DC5D2C550F117CFD004D1660 /* StellaMediumFont.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC5D2C510F117CFD004D1660 /* StellaMediumFont.hxx */; };
DC5D2C600F129B1E004D1660 /* LauncherFilterDialog.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC5D2C5E0F129B1E004D1660 /* LauncherFilterDialog.cxx */; };
DC5D2C610F129B1E004D1660 /* LauncherFilterDialog.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC5D2C5F0F129B1E004D1660 /* LauncherFilterDialog.hxx */; };
DC5E473B19EC9A14000E45DF /* EventJoyHandler.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC5E473A19EC9A14000E45DF /* EventJoyHandler.cxx */; };
DC5EE7C214F7C165001C628C /* NTSCFilter.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC5EE7C014F7C165001C628C /* NTSCFilter.cxx */; };
DC5EE7C314F7C165001C628C /* NTSCFilter.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC5EE7C114F7C165001C628C /* NTSCFilter.hxx */; };
DC62E6471960E87B007AEF05 /* AtariVoxWidget.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC62E6431960E87B007AEF05 /* AtariVoxWidget.cxx */; };
@ -372,6 +373,10 @@
DC6C726313CDEA0A008A5975 /* LoggerDialog.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC6C726113CDEA0A008A5975 /* LoggerDialog.hxx */; };
DC6D39871A3CE65000171E71 /* CartWDWidget.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC6D39851A3CE65000171E71 /* CartWDWidget.cxx */; };
DC6D39881A3CE65000171E71 /* CartWDWidget.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC6D39861A3CE65000171E71 /* CartWDWidget.hxx */; };
DC6DC91E205DB879004A5FC3 /* PhysicalJoystick.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC6DC91A205DB879004A5FC3 /* PhysicalJoystick.cxx */; };
DC6DC91F205DB879004A5FC3 /* PhysicalJoystick.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC6DC91B205DB879004A5FC3 /* PhysicalJoystick.hxx */; };
DC6DC920205DB879004A5FC3 /* PJoystickHandler.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC6DC91C205DB879004A5FC3 /* PJoystickHandler.cxx */; };
DC6DC921205DB879004A5FC3 /* PJoystickHandler.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC6DC91D205DB879004A5FC3 /* PJoystickHandler.hxx */; };
DC71EA9D1FDA06D2008827CB /* CartE78K.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC71EA991FDA06D2008827CB /* CartE78K.cxx */; };
DC71EA9E1FDA06D2008827CB /* CartE78K.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC71EA9A1FDA06D2008827CB /* CartE78K.hxx */; };
DC71EA9F1FDA06D2008827CB /* CartMNetwork.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC71EA9B1FDA06D2008827CB /* CartMNetwork.cxx */; };
@ -905,6 +910,8 @@
DC1B2EBE1E50036100F62837 /* AmigaMouse.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AmigaMouse.hxx; sourceTree = "<group>"; };
DC1B2EC01E50036100F62837 /* AtariMouse.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AtariMouse.hxx; sourceTree = "<group>"; };
DC1B2EC21E50036100F62837 /* TrakBall.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrakBall.hxx; sourceTree = "<group>"; };
DC1BC6642066B4390076F74A /* PKeyboardHandler.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PKeyboardHandler.cxx; sourceTree = "<group>"; };
DC1BC6652066B4390076F74A /* PKeyboardHandler.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PKeyboardHandler.hxx; sourceTree = "<group>"; };
DC1FC1880DB3B2C7009B3DF7 /* SerialPortMACOSX.cxx */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SerialPortMACOSX.cxx; sourceTree = "<group>"; };
DC1FC1890DB3B2C7009B3DF7 /* SerialPortMACOSX.hxx */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = SerialPortMACOSX.hxx; sourceTree = "<group>"; };
DC2874061F8F2278004BF21A /* TrapArray.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrapArray.hxx; sourceTree = "<group>"; };
@ -991,7 +998,6 @@
DC5D2C510F117CFD004D1660 /* StellaMediumFont.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StellaMediumFont.hxx; sourceTree = "<group>"; };
DC5D2C5E0F129B1E004D1660 /* LauncherFilterDialog.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LauncherFilterDialog.cxx; sourceTree = "<group>"; };
DC5D2C5F0F129B1E004D1660 /* LauncherFilterDialog.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LauncherFilterDialog.hxx; sourceTree = "<group>"; };
DC5E473A19EC9A14000E45DF /* EventJoyHandler.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventJoyHandler.cxx; sourceTree = "<group>"; };
DC5EE7C014F7C165001C628C /* NTSCFilter.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NTSCFilter.cxx; sourceTree = "<group>"; };
DC5EE7C114F7C165001C628C /* NTSCFilter.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = NTSCFilter.hxx; sourceTree = "<group>"; };
DC62E6431960E87B007AEF05 /* AtariVoxWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AtariVoxWidget.cxx; sourceTree = "<group>"; };
@ -1044,6 +1050,10 @@
DC6C726113CDEA0A008A5975 /* LoggerDialog.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LoggerDialog.hxx; sourceTree = "<group>"; };
DC6D39851A3CE65000171E71 /* CartWDWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CartWDWidget.cxx; sourceTree = "<group>"; };
DC6D39861A3CE65000171E71 /* CartWDWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartWDWidget.hxx; sourceTree = "<group>"; };
DC6DC91A205DB879004A5FC3 /* PhysicalJoystick.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PhysicalJoystick.cxx; sourceTree = "<group>"; };
DC6DC91B205DB879004A5FC3 /* PhysicalJoystick.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PhysicalJoystick.hxx; sourceTree = "<group>"; };
DC6DC91C205DB879004A5FC3 /* PJoystickHandler.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PJoystickHandler.cxx; sourceTree = "<group>"; };
DC6DC91D205DB879004A5FC3 /* PJoystickHandler.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PJoystickHandler.hxx; sourceTree = "<group>"; };
DC71EA991FDA06D2008827CB /* CartE78K.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CartE78K.cxx; sourceTree = "<group>"; };
DC71EA9A1FDA06D2008827CB /* CartE78K.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartE78K.hxx; sourceTree = "<group>"; };
DC71EA9B1FDA06D2008827CB /* CartMNetwork.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CartMNetwork.cxx; sourceTree = "<group>"; };
@ -1593,6 +1603,12 @@
DCB87E571A104C1E00BF2A3B /* MediaFactory.hxx */,
DC56FCDC14CCCC4900A31CC3 /* MouseControl.cxx */,
DC56FCDD14CCCC4900A31CC3 /* MouseControl.hxx */,
DC6DC91A205DB879004A5FC3 /* PhysicalJoystick.cxx */,
DC6DC91B205DB879004A5FC3 /* PhysicalJoystick.hxx */,
DC6DC91C205DB879004A5FC3 /* PJoystickHandler.cxx */,
DC6DC91D205DB879004A5FC3 /* PJoystickHandler.hxx */,
DC1BC6642066B4390076F74A /* PKeyboardHandler.cxx */,
DC1BC6652066B4390076F74A /* PKeyboardHandler.hxx */,
DCD6FC9111C28C6F005DA767 /* PNGLibrary.cxx */,
DCD6FC9211C28C6F005DA767 /* PNGLibrary.hxx */,
DCDDEAC01F5DBF0400C67366 /* RewindManager.cxx */,
@ -1754,7 +1770,6 @@
2D733D6E062895B2006265D9 /* EventHandler.cxx */,
2D733D6F062895B2006265D9 /* EventHandler.hxx */,
DC5AAC261FCB24AB00C420A6 /* EventHandlerConstants.hxx */,
DC5E473A19EC9A14000E45DF /* EventJoyHandler.cxx */,
DC73BD871915E5E3003FAFAD /* FBSurface.cxx */,
DC73BD881915E5E3003FAFAD /* FBSurface.hxx */,
2D733D70062895B2006265D9 /* FrameBuffer.cxx */,
@ -2264,6 +2279,7 @@
2D91746109BA90380026E9FF /* TogglePixelWidget.hxx in Headers */,
2D91746209BA90380026E9FF /* ToggleWidget.hxx in Headers */,
2D91746409BA90380026E9FF /* TiaZoomWidget.hxx in Headers */,
DC1BC6672066B4390076F74A /* PKeyboardHandler.hxx in Headers */,
2D91746609BA90380026E9FF /* AudioWidget.hxx in Headers */,
2D91746909BA90380026E9FF /* EventMappingWidget.hxx in Headers */,
2D91746A09BA90380026E9FF /* InputDialog.hxx in Headers */,
@ -2280,6 +2296,7 @@
DC8078EB0B4BD697005E9305 /* UIDialog.hxx in Headers */,
DCEECE570B5E5E540021D754 /* Cart0840.hxx in Headers */,
DCE3BBFA0C95CEDC00A671DF /* RomInfoWidget.hxx in Headers */,
DC6DC91F205DB879004A5FC3 /* PhysicalJoystick.hxx in Headers */,
DC0984860D3985160073C852 /* CartSB.hxx in Headers */,
DCEC585E1E945175002F0246 /* DelayQueueIterator.hxx in Headers */,
DCA23AEA0D75B22500F77B33 /* CartX07.hxx in Headers */,
@ -2376,6 +2393,7 @@
DCAAE5DA1715887B0080BB82 /* Cart0840Widget.hxx in Headers */,
DCAAE5DC1715887B0080BB82 /* CartCVWidget.hxx in Headers */,
DCAAE5DD1715887B0080BB82 /* CartDebugWidget.hxx in Headers */,
DC6DC921205DB879004A5FC3 /* PJoystickHandler.hxx in Headers */,
DCAAE5DF1715887B0080BB82 /* CartEFSCWidget.hxx in Headers */,
DCAAE5E11715887B0080BB82 /* CartEFWidget.hxx in Headers */,
DCAAE5E31715887B0080BB82 /* CartF0Widget.hxx in Headers */,
@ -2562,6 +2580,7 @@
DCF3A6FA1DFC75E3008A8AF3 /* Player.cxx in Sources */,
2D91748609BA90380026E9FF /* CartFE.cxx in Sources */,
2D91748909BA90380026E9FF /* Console.cxx in Sources */,
DC6DC91E205DB879004A5FC3 /* PhysicalJoystick.cxx in Sources */,
2D91748A09BA90380026E9FF /* Control.cxx in Sources */,
2D91748C09BA90380026E9FF /* Driving.cxx in Sources */,
E0306E101F93E916003DDD52 /* FrameLayoutDetector.cxx in Sources */,
@ -2594,7 +2613,6 @@
2D9174B309BA90380026E9FF /* LauncherDialog.cxx in Sources */,
2D9174B509BA90380026E9FF /* ListWidget.cxx in Sources */,
2D9174B609BA90380026E9FF /* Menu.cxx in Sources */,
DC5E473B19EC9A14000E45DF /* EventJoyHandler.cxx in Sources */,
CFE3F60D1E84A9A200A8204E /* CartCDFWidget.cxx in Sources */,
2D9174B709BA90380026E9FF /* OptionsDialog.cxx in Sources */,
2D9174B809BA90380026E9FF /* PopUpWidget.cxx in Sources */,
@ -2630,6 +2648,7 @@
2D9174CE09BA90380026E9FF /* Cart3E.cxx in Sources */,
2D9174CF09BA90380026E9FF /* CpuDebug.cxx in Sources */,
2D9174F109BA90380026E9FF /* InputTextDialog.cxx in Sources */,
DC6DC920205DB879004A5FC3 /* PJoystickHandler.cxx in Sources */,
DC74E5C6198AF12700F37E36 /* CartDASHWidget.cxx in Sources */,
2D9174F209BA90380026E9FF /* CheckListWidget.cxx in Sources */,
2D9174F309BA90380026E9FF /* StringListWidget.cxx in Sources */,
@ -2740,6 +2759,7 @@
DC9616301F817830008A2206 /* FlashWidget.cxx in Sources */,
DCFFE59D12100E1400DFA000 /* ComboDialog.cxx in Sources */,
DCD2839812E39F1200A808DC /* Thumbulator.cxx in Sources */,
DC1BC6662066B4390076F74A /* PKeyboardHandler.cxx in Sources */,
DC6C726213CDEA0A008A5975 /* LoggerDialog.cxx in Sources */,
DC8C1BAD14B25DE7006440EE /* CartCM.cxx in Sources */,
DCDDEAC61F5DBF0400C67366 /* StateManager.cxx in Sources */,

View File

@ -236,6 +236,9 @@
<ClCompile Include="..\common\FSNodeZIP.cxx" />
<ClCompile Include="..\common\main.cxx" />
<ClCompile Include="..\common\MouseControl.cxx" />
<ClCompile Include="..\common\PhysicalJoystick.cxx" />
<ClCompile Include="..\common\PJoystickHandler.cxx" />
<ClCompile Include="..\common\PKeyboardHandler.cxx" />
<ClCompile Include="..\common\RewindManager.cxx" />
<ClCompile Include="..\common\StateManager.cxx" />
<ClCompile Include="..\common\tv_filters\AtariNTSC.cxx" />
@ -321,7 +324,6 @@
<ClCompile Include="..\emucore\CartE78K.cxx" />
<ClCompile Include="..\emucore\CartWD.cxx" />
<ClCompile Include="..\emucore\CompuMate.cxx" />
<ClCompile Include="..\emucore\EventJoyHandler.cxx" />
<ClCompile Include="..\emucore\FBSurface.cxx" />
<ClCompile Include="..\emucore\MindLink.cxx" />
<ClCompile Include="..\emucore\PointingDevice.cxx" />
@ -522,6 +524,9 @@
<ClInclude Include="..\common\LinkedObjectPool.hxx" />
<ClInclude Include="..\common\MediaFactory.hxx" />
<ClInclude Include="..\common\MouseControl.hxx" />
<ClInclude Include="..\common\PhysicalJoystick.hxx" />
<ClInclude Include="..\common\PJoystickHandler.hxx" />
<ClInclude Include="..\common\PKeyboardHandler.hxx" />
<ClInclude Include="..\common\RewindManager.hxx" />
<ClInclude Include="..\common\StateManager.hxx" />
<ClInclude Include="..\common\StellaKeys.hxx" />

View File

@ -741,9 +741,6 @@
<ClCompile Include="..\debugger\gui\CartMDMWidget.cxx">
<Filter>Source Files\debugger</Filter>
</ClCompile>
<ClCompile Include="..\emucore\EventJoyHandler.cxx">
<Filter>Source Files\emucore</Filter>
</ClCompile>
<ClCompile Include="..\common\main.cxx">
<Filter>Source Files</Filter>
</ClCompile>
@ -899,6 +896,15 @@
<ClCompile Include="..\gui\TimeLineWidget.cxx">
<Filter>Source Files\gui</Filter>
</ClCompile>
<ClCompile Include="..\common\PhysicalJoystick.cxx">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\PJoystickHandler.cxx">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\PKeyboardHandler.cxx">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\common\bspf.hxx">
@ -1839,6 +1845,15 @@
<ClInclude Include="..\gui\TimeLineWidget.hxx">
<Filter>Header Files\gui</Filter>
</ClInclude>
<ClInclude Include="..\common\PhysicalJoystick.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\common\PJoystickHandler.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\common\PKeyboardHandler.hxx">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="stella.ico">