diff --git a/Changes.txt b/Changes.txt index 95620c946..9636cc60e 100644 --- a/Changes.txt +++ b/Changes.txt @@ -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) diff --git a/debian/compat b/debian/compat index 7ed6ff82d..ec635144f 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -5 +9 diff --git a/debian/control b/debian/control index 07eabba18..4d19b476f 100644 --- a/debian/control +++ b/debian/control @@ -3,7 +3,7 @@ Section: games Priority: optional Maintainer: Stephen Anthony 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 diff --git a/debian/rules b/debian/rules index b2260d00c..35b3c2ff4 100755 --- a/debian/rules +++ b/debian/rules @@ -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 diff --git a/src/common/EventHandlerSDL2.cxx b/src/common/EventHandlerSDL2.cxx index 5d821325c..dc9382bc3 100644 --- a/src/common/EventHandlerSDL2.cxx +++ b/src/common/EventHandlerSDL2.cxx @@ -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(myEvent.jdevice.which)); break; // SDL_JOYDEVICEADDED } case SDL_JOYDEVICEREMOVED: { - removeJoystick(myEvent.jdevice.which); + removePhysicalJoystick(myEvent.jdevice.which); break; // SDL_JOYDEVICEREMOVED } #endif diff --git a/src/common/EventHandlerSDL2.hxx b/src/common/EventHandlerSDL2.hxx index c826c3c4b..76f4b04f1 100644 --- a/src/common/EventHandlerSDL2.hxx +++ b/src/common/EventHandlerSDL2.hxx @@ -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); diff --git a/src/common/PJoystickHandler.cxx b/src/common/PJoystickHandler.cxx new file mode 100644 index 000000000..60f7ceb62 --- /dev/null +++ b/src/common/PJoystickHandler.cxx @@ -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 } +}; diff --git a/src/common/PJoystickHandler.hxx b/src/common/PJoystickHandler.hxx new file mode 100644 index 000000000..94a13fb68 --- /dev/null +++ b/src/common/PJoystickHandler.hxx @@ -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 + +class OSystem; +class EventHandler; +class Event; + +#include "bspf.hxx" +#include "EventHandlerConstants.hxx" +#include "PhysicalJoystick.hxx" +#include "Variant.hxx" + +using PhysicalJoystickPtr = shared_ptr; + +/** + 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; + using StickList = std::map; + + 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 diff --git a/src/common/PKeyboardHandler.cxx b/src/common/PKeyboardHandler.cxx new file mode 100644 index 000000000..779a01566 --- /dev/null +++ b/src/common/PKeyboardHandler.cxx @@ -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; + } +} diff --git a/src/common/PKeyboardHandler.hxx b/src/common/PKeyboardHandler.hxx new file mode 100644 index 000000000..ef5ab5b4e --- /dev/null +++ b/src/common/PKeyboardHandler.hxx @@ -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 + +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 diff --git a/src/common/PNGLibrary.cxx b/src/common/PNGLibrary.cxx index 476a161b1..0a3f22353 100644 --- a/src/common/PNGLibrary.cxx +++ b/src/common/PNGLibrary.cxx @@ -18,14 +18,21 @@ #include #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 buffer = make_unique(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 rows = make_unique(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)); } } diff --git a/src/common/PNGLibrary.hxx b/src/common/PNGLibrary.hxx index d6950c350..b2fc3eb79 100644 --- a/src/common/PNGLibrary.hxx +++ b/src/common/PNGLibrary.hxx @@ -20,6 +20,7 @@ #include +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. diff --git a/src/common/PhysicalJoystick.cxx b/src/common/PhysicalJoystick.cxx new file mode 100644 index 000000000..33a8c6911 --- /dev/null +++ b/src/common/PhysicalJoystick.cxx @@ -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 + +#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(); +} diff --git a/src/common/PhysicalJoystick.hxx b/src/common/PhysicalJoystick.hxx new file mode 100644 index 000000000..fc6e7b3bb --- /dev/null +++ b/src/common/PhysicalJoystick.hxx @@ -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 diff --git a/src/common/RewindManager.hxx b/src/common/RewindManager.hxx index 5ce4959a6..6cd8ee116 100644 --- a/src/common/RewindManager.hxx +++ b/src/common/RewindManager.hxx @@ -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) { diff --git a/src/common/SoundSDL2.cxx b/src/common/SoundSDL2.cxx index 1116d6b48..0d33e3628 100644 --- a/src/common/SoundSDL2.cxx +++ b/src/common/SoundSDL2.cxx @@ -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, diff --git a/src/common/StellaKeys.hxx b/src/common/StellaKeys.hxx index 7088e6cc8..1bb99d8f5 100644 --- a/src/common/StellaKeys.hxx +++ b/src/common/StellaKeys.hxx @@ -435,4 +435,12 @@ namespace StellaModTest } }; +namespace StellaKeyName +{ + inline const char* const forKey(StellaKey key) + { + return SDL_GetScancodeName(SDL_Scancode(key)); + } +}; + #endif /* StellaKeys */ diff --git a/src/common/main.cxx b/src/common/main.cxx index fca86956d..c275d5bc7 100644 --- a/src/common/main.cxx +++ b/src/common/main.cxx @@ -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(); } diff --git a/src/common/module.mk b/src/common/module.mk index beea041b5..e482cb9b9 100644 --- a/src/common/module.mk +++ b/src/common/module.mk @@ -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 diff --git a/src/common/tv_filters/AtariNTSC.hxx b/src/common/tv_filters/AtariNTSC.hxx index ce868948c..f492c6cb3 100644 --- a/src/common/tv_filters/AtariNTSC.hxx +++ b/src/common/tv_filters/AtariNTSC.hxx @@ -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; diff --git a/src/debugger/CartDebug.cxx b/src/debugger/CartDebug.cxx index 552a985a2..3faaa2006 100644 --- a/src/debugger/CartDebug.cxx +++ b/src/debugger/CartDebug.cxx @@ -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; diff --git a/src/debugger/Debugger.cxx b/src/debugger/Debugger.cxx index c840e9a78..65b440469 100644 --- a/src/debugger/Debugger.cxx +++ b/src/debugger/Debugger.cxx @@ -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(); diff --git a/src/debugger/Debugger.hxx b/src/debugger/Debugger.hxx index 76b32e9e5..7da6bc580 100644 --- a/src/debugger/Debugger.hxx +++ b/src/debugger/Debugger.hxx @@ -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 diff --git a/src/debugger/DebuggerParser.cxx b/src/debugger/DebuggerParser.cxx index f662132c2..53c4596e3 100644 --- a/src/debugger/DebuggerParser.cxx +++ b/src/debugger/DebuggerParser.cxx @@ -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++) { diff --git a/src/debugger/gui/CartMNetworkWidget.cxx b/src/debugger/gui/CartMNetworkWidget.cxx index d1bebc552..df50a1740 100644 --- a/src/debugger/gui/CartMNetworkWidget.cxx +++ b/src/debugger/gui/CartMNetworkWidget.cxx @@ -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) diff --git a/src/debugger/gui/CartMNetworkWidget.hxx b/src/debugger/gui/CartMNetworkWidget.hxx index 608bf15a7..d703b1ea3 100644 --- a/src/debugger/gui/CartMNetworkWidget.hxx +++ b/src/debugger/gui/CartMNetworkWidget.hxx @@ -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; diff --git a/src/debugger/gui/DrivingWidget.cxx b/src/debugger/gui/DrivingWidget.cxx index 6b924d7e8..2b83292d1 100644 --- a/src/debugger/gui/DrivingWidget.cxx +++ b/src/debugger/gui/DrivingWidget.cxx @@ -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); } diff --git a/src/debugger/gui/DrivingWidget.hxx b/src/debugger/gui/DrivingWidget.hxx index 3fd38a092..8eddf778e 100644 --- a/src/debugger/gui/DrivingWidget.hxx +++ b/src/debugger/gui/DrivingWidget.hxx @@ -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; diff --git a/src/debugger/gui/FlashWidget.cxx b/src/debugger/gui/FlashWidget.cxx index f2e980f5b..31672a7a6 100644 --- a/src/debugger/gui/FlashWidget.cxx +++ b/src/debugger/gui/FlashWidget.cxx @@ -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:"); diff --git a/src/debugger/gui/PromptWidget.cxx b/src/debugger/gui/PromptWidget.cxx index e723755ae..ede0d20ad 100644 --- a/src/debugger/gui/PromptWidget.cxx +++ b/src/debugger/gui/PromptWidget.cxx @@ -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) diff --git a/src/debugger/gui/TiaWidget.cxx b/src/debugger/gui/TiaWidget.cxx index 17df7b5b4..0fad9a0d9 100644 --- a/src/debugger/gui/TiaWidget.cxx +++ b/src/debugger/gui/TiaWidget.cxx @@ -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; diff --git a/src/emucore/CartBUS.cxx b/src/emucore/CartBUS.cxx index 622caedcf..fa40667b2 100644 --- a/src/emucore/CartBUS.cxx +++ b/src/emucore/CartBUS.cxx @@ -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; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/CartCDF.cxx b/src/emucore/CartCDF.cxx index 81029e7af..1e7268ee0 100644 --- a/src/emucore/CartCDF.cxx +++ b/src/emucore/CartCDF.cxx @@ -105,6 +105,7 @@ void CartridgeCDF::setInitialState() // need to confirm with Chris myMode = 0xFF; + myBankOffset = myLDAimmediateOperandAddress = myJMPoperandAddress = 0; myFastJumpActive = 0; } diff --git a/src/emucore/CartCDF.hxx b/src/emucore/CartCDF.hxx index ca37e5bc5..b943dda2b 100644 --- a/src/emucore/CartCDF.hxx +++ b/src/emucore/CartCDF.hxx @@ -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 diff --git a/src/emucore/CartMNetwork.cxx b/src/emucore/CartMNetwork.cxx index ffd2d2538..11240abd8 100644 --- a/src/emucore/CartMNetwork.cxx +++ b/src/emucore/CartMNetwork.cxx @@ -23,7 +23,8 @@ CartridgeMNetwork::CartridgeMNetwork(const BytePtr& image, uInt32 size, const Settings& settings) : Cartridge(settings), mySize(size), - myCurrentRAM(0) + myCurrentRAM(0), + myRAMSlice(0) { } diff --git a/src/emucore/Console.hxx b/src/emucore/Console.hxx index d3b1082f8..cd5608ccb 100644 --- a/src/emucore/Console.hxx +++ b/src/emucore/Console.hxx @@ -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 diff --git a/src/emucore/Control.hxx b/src/emucore/Control.hxx index c632c92d0..7d9eb13ef 100644 --- a/src/emucore/Control.hxx +++ b/src/emucore/Control.hxx @@ -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 diff --git a/src/emucore/Driving.hxx b/src/emucore/Driving.hxx index 1f905d16e..0925cef25 100644 --- a/src/emucore/Driving.hxx +++ b/src/emucore/Driving.hxx @@ -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 diff --git a/src/emucore/Event.hxx b/src/emucore/Event.hxx index 1d4b20603..f286acf18 100644 --- a/src/emucore/Event.hxx +++ b/src/emucore/Event.hxx @@ -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]; diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index 76d045e06..4b8c5388a 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -26,7 +26,6 @@ #include "DialogContainer.hxx" #include "Event.hxx" #include "FrameBuffer.hxx" -#include "TIASurface.hxx" #include "FSNode.hxx" #include "Launcher.hxx" #include "TimeMachine.hxx" @@ -34,6 +33,7 @@ #include "OSystem.hxx" #include "Joystick.hxx" #include "Paddles.hxx" +#include "PJoystickHandler.hxx" #include "PointingDevice.hxx" #include "PropsSet.hxx" #include "ListWidget.hxx" @@ -45,7 +45,6 @@ #include "M6532.hxx" #include "MouseControl.hxx" #include "PNGLibrary.hxx" -#include "Version.hxx" #include "EventHandler.hxx" @@ -64,25 +63,19 @@ EventHandler::EventHandler(OSystem& osystem) myState(EventHandlerState::NONE), myAllowAllDirectionsFlag(false), myFryingFlag(false), - myUseCtrlKeyFlag(true), mySkipMouseMotion(true), - myIs7800(false), - myAltKeyCounter(0), - myContSnapshotInterval(0), - myContSnapshotCounter(0) + myIs7800(false) { - // Erase the key mapping array - for(int i = 0; i < KBDK_LAST; ++i) - for(int m = 0; m < kNumModes; ++m) - myKeyTable[i][m] = Event::NoType; + // Create keyboard handler (to handle all physical keyboard functionality) + myPKeyHandler = make_unique(osystem, *this, myEvent); + + // Create joystick handler (to handle all physical joystick functionality) + myPJoyHandler = make_unique(osystem, *this, myEvent); // Erase the 'combo' array for(int i = 0; i < kComboSize; ++i) for(int j = 0; j < kEventsPerCombo; ++j) myComboTable[i][j] = Event::NoType; - - // Create joystick handler (to handle all joystick functionality) - myJoyHandler = make_unique(osystem); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -95,13 +88,10 @@ void EventHandler::initialize() { // Make sure the event/action mappings are correctly set, // and fill the ActionList structure with valid values - setKeymap(); setComboMap(); setActionMappings(kEmulationMode); setActionMappings(kMenuMode); - myUseCtrlKeyFlag = myOSystem.settings().getBool("ctrlcombo"); - Joystick::setDeadZone(myOSystem.settings().getInt("joydeadzone")); Paddles::setDigitalSensitivity(myOSystem.settings().getInt("dsense")); Paddles::setMouseSensitivity(myOSystem.settings().getInt("msense")); @@ -127,47 +117,34 @@ void EventHandler::initialize() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::reset(EventHandlerState state) { - setEventState(state); + setState(state); myOSystem.state().reset(); - - setContinuousSnapshots(0); + myOSystem.png().setContinuousSnapInterval(0); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::addJoystick(StellaJoystick* stick) +void EventHandler::addPhysicalJoystick(PhysicalJoystickPtr joy) { #ifdef JOYSTICK_SUPPORT - if(!myJoyHandler->add(stick)) + int ID = myPJoyHandler->add(joy); + if(ID < 0) return; setActionMappings(kEmulationMode); setActionMappings(kMenuMode); ostringstream buf; - buf << "Added joystick " << stick->ID << ":" << endl - << " " << stick->about() << endl; + buf << "Added joystick " << ID << ":" << endl + << " " << joy->about() << endl; myOSystem.logMessage(buf.str(), 1); - - // We're potentially swapping out an input device behind the back of - // the Event system, so we make sure all Stelladaptor-generated events - // are reset - for(int i = 0; i < 2; ++i) - { - for(int j = 0; j < 2; ++j) - myEvent.set(SA_Axis[i][j], 0); - for(int j = 0; j < 4; ++j) - myEvent.set(SA_Button[i][j], 0); - for(int j = 0; j < 12; ++j) - myEvent.set(SA_Key[i][j], 0); - } #endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::removeJoystick(int id) +void EventHandler::removePhysicalJoystick(int id) { #ifdef JOYSTICK_SUPPORT - myJoyHandler->remove(id); + myPJoyHandler->remove(id); #endif } @@ -175,7 +152,7 @@ void EventHandler::removeJoystick(int id) void EventHandler::mapStelladaptors(const string& saport) { #ifdef JOYSTICK_SUPPORT - myJoyHandler->mapStelladaptors(saport); + myPJoyHandler->mapStelladaptors(saport); #endif } @@ -206,6 +183,13 @@ void EventHandler::set7800Mode() myIs7800 = false; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void EventHandler::handleMouseControl() +{ + if(myMouseControl) + myOSystem.frameBuffer().showMessage(myMouseControl->next()); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::poll(uInt64 time) { @@ -229,9 +213,8 @@ void EventHandler::poll(uInt64 time) #endif // Handle continuous snapshots - if(myContSnapshotInterval > 0 && - (++myContSnapshotCounter % myContSnapshotInterval == 0)) - takeSnapshot(uInt32(time) >> 10); // not quite milliseconds, but close enough + if(myOSystem.png().continuousSnapEnabled()) + myOSystem.png().updateTime(time); } else if(myOverlay) { @@ -254,404 +237,6 @@ void EventHandler::handleTextEvent(char text) myOverlay->handleTextEvent(text); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::handleKeyEvent(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 = false; - return; - } -#endif - - bool handled = true; - - // 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) - { - 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(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE) - { - switch(key) - { - case KBDK_LEFT: // Alt-left(-shift) rewinds 1(10) states - enterTimeMachineMenuMode((StellaModTest::isShift(mod) && state) ? 10 : 1, false); - break; - - case KBDK_RIGHT: // Alt-right(-shift) unwinds 1(10) states - enterTimeMachineMenuMode((StellaModTest::isShift(mod) && state) ? 10 : 1, true); - break; - - case KBDK_DOWN: // Alt-down rewinds to start of list - enterTimeMachineMenuMode(1000, false); - break; - - case KBDK_UP: // Alt-up rewinds to end of list - 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(mod & KBDM_SHIFT) - 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(mod & KBDM_SHIFT) - 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(mod & KBDM_SHIFT) - myOSystem.frameBuffer().showMessage( - myOSystem.frameBuffer().tiaSurface().ntsc().decreaseAdjustable()); - else - myOSystem.frameBuffer().showMessage( - myOSystem.frameBuffer().tiaSurface().ntsc().increaseAdjustable()); - } - break; - - case KBDK_Z: - if(mod & KBDM_SHIFT) - myOSystem.console().toggleP0Collision(); - else - myOSystem.console().toggleP0Bit(); - break; - - case KBDK_X: - if(mod & KBDM_SHIFT) - myOSystem.console().toggleP1Collision(); - else - myOSystem.console().toggleP1Bit(); - break; - - case KBDK_C: - if(mod & KBDM_SHIFT) - myOSystem.console().toggleM0Collision(); - else - myOSystem.console().toggleM0Bit(); - break; - - case KBDK_V: - if(mod & KBDM_SHIFT) - myOSystem.console().toggleM1Collision(); - else - myOSystem.console().toggleM1Bit(); - break; - - case KBDK_B: - if(mod & KBDM_SHIFT) - myOSystem.console().toggleBLCollision(); - else - myOSystem.console().toggleBLBit(); - break; - - case KBDK_N: - if(mod & KBDM_SHIFT) - myOSystem.console().togglePFCollision(); - else - myOSystem.console().togglePFBit(); - break; - - case KBDK_COMMA: - myOSystem.console().toggleFixedColors(); - break; - - case KBDK_PERIOD: - if(mod & KBDM_SHIFT) - 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: - if(myContSnapshotInterval == 0) - { - ostringstream buf; - uInt32 interval = myOSystem.settings().getInt("ssinterval"); - if(mod & KBDM_SHIFT) - { - buf << "Enabling snapshots every frame"; - interval = 1; - } - else - { - buf << "Enabling snapshots in " << interval << " second intervals"; - interval *= uInt32(myOSystem.frameRate()); - } - myOSystem.frameBuffer().showMessage(buf.str()); - setContinuousSnapshots(interval); - } - else - { - ostringstream buf; - buf << "Disabling snapshots, generated " - << (myContSnapshotCounter / myContSnapshotInterval) - << " files"; - myOSystem.frameBuffer().showMessage(buf.str()); - setContinuousSnapshots(0); - } - 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) - { - handleEvent(Event::Quit, 1); - } - // These only work when in emulation mode - else if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE) - { - switch(key) - { - case KBDK_0: // Ctrl-0 switches between mouse control modes - if(myMouseControl) - myOSystem.frameBuffer().showMessage(myMouseControl->next()); - break; - - case KBDK_1: // Ctrl-1 swaps Stelladaptor/2600-daptor ports - toggleSAPortOrder(); - break; - - case KBDK_F: // (Shift) Ctrl-f toggles NTSC/PAL/SECAM mode - myOSystem.console().toggleFormat(mod & KBDM_SHIFT ? -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 - if(state && key == KBDK_ESCAPE) - { - if(myState == EventHandlerState::PAUSE) - { - setEventState(EventHandlerState::EMULATION); - return; - } - else if(myState == EventHandlerState::CMDMENU || - myState == EventHandlerState::TIMEMACHINE) - { - leaveMenuMode(); - return; - } - else if(myState == EventHandlerState::DEBUGGER && myOSystem.debugger().canExit()) - { - leaveDebugMode(); - return; - } - } - - // Handle keys which switch eventhandler state - if(!state && eventStateChange(myKeyTable[key][kEmulationMode])) - return; - } - - // Otherwise, let the event handler deal with it - switch(myState) - { - case EventHandlerState::EMULATION: - handleEvent(myKeyTable[key][kEmulationMode], state); - break; - - case EventHandlerState::PAUSE: - switch(myKeyTable[key][kEmulationMode]) - { - case Event::TakeSnapshot: - case Event::DebuggerMode: - handleEvent(myKeyTable[key][kEmulationMode], state); - break; - - default: - break; - } - break; - - default: - if(myOverlay) - myOverlay->handleKeyEvent(key, mod, state); - break; - } -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::handleMouseMotionEvent(int x, int y, int xrel, int yrel) { @@ -692,211 +277,6 @@ void EventHandler::handleMouseButtonEvent(MouseButton b, bool pressed, myOverlay->handleMouseButtonEvent(b, pressed, x, y); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::handleJoyEvent(int stick, int button, uInt8 state) -{ - const StellaJoystick* joy = myJoyHandler->joy(stick); - if(!joy) return; - - // Stelladaptors handle buttons differently than regular joysticks - switch(joy->type) - { - case StellaJoystick::JT_REGULAR: - // Handle buttons which switch eventhandler state - if(state && eventStateChange(joy->btnTable[button][kEmulationMode])) - return; - - // Determine which mode we're in, then send the event to the appropriate place - if(myState == EventHandlerState::EMULATION) - handleEvent(joy->btnTable[button][kEmulationMode], state); - else if(myOverlay) - myOverlay->handleJoyEvent(stick, button, state); - break; // Regular button - - // These events don't have to pass through handleEvent, since - // they can never be remapped - case StellaJoystick::JT_STELLADAPTOR_LEFT: - case StellaJoystick::JT_STELLADAPTOR_RIGHT: - // The 'type-2' here refers to the fact that 'StellaJoystick::JT_STELLADAPTOR_LEFT' - // and 'StellaJoystick::JT_STELLADAPTOR_RIGHT' are at index 2 and 3 in the JoyType - // enum; subtracting two gives us Controller 0 and 1 - if(button < 2) myEvent.set(SA_Button[joy->type-2][button], state); - break; // Stelladaptor button - case StellaJoystick::JT_2600DAPTOR_LEFT: - case StellaJoystick::JT_2600DAPTOR_RIGHT: - // The 'type-4' here refers to the fact that 'StellaJoystick::JT_2600DAPTOR_LEFT' - // and 'StellaJoystick::JT_2600DAPTOR_RIGHT' are at index 4 and 5 in the JoyType - // enum; subtracting four gives us Controller 0 and 1 - if(myState == EventHandlerState::EMULATION) - { - switch(myOSystem.console().leftController().type()) - { - case Controller::Keyboard: - if(button < 12) myEvent.set(SA_Key[joy->type-4][button], state); - break; - default: - if(button < 4) myEvent.set(SA_Button[joy->type-4][button], state); - } - switch(myOSystem.console().rightController().type()) - { - case Controller::Keyboard: - if(button < 12) myEvent.set(SA_Key[joy->type-4][button], state); - break; - default: - if(button < 4) myEvent.set(SA_Button[joy->type-4][button], state); - } - } - break; // 2600DAPTOR button - default: - break; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::handleJoyAxisEvent(int stick, int axis, int value) -{ - const StellaJoystick* joy = myJoyHandler->joy(stick); - if(!joy) return; - - // Stelladaptors handle axis differently than regular joysticks - switch(joy->type) - { - case StellaJoystick::JT_REGULAR: - if(myState == EventHandlerState::EMULATION) - { - // Every axis event has two associated values, negative and positive - Event::Type eventAxisNeg = joy->axisTable[axis][0][kEmulationMode]; - Event::Type eventAxisPos = joy->axisTable[axis][1][kEmulationMode]; - - // Check for analog events, which are handled differently - // We'll pass them off as Stelladaptor events, and let the controllers - // handle it - switch(int(eventAxisNeg)) - { - case Event::PaddleZeroAnalog: - myEvent.set(Event::SALeftAxis0Value, value); - break; - case Event::PaddleOneAnalog: - myEvent.set(Event::SALeftAxis1Value, value); - break; - case Event::PaddleTwoAnalog: - myEvent.set(Event::SARightAxis0Value, value); - break; - case Event::PaddleThreeAnalog: - myEvent.set(Event::SARightAxis1Value, value); - break; - default: - { - // Otherwise, we know the event is digital - if(value > Joystick::deadzone()) - handleEvent(eventAxisPos, 1); - else if(value < -Joystick::deadzone()) - handleEvent(eventAxisNeg, 1); - else - { - // Treat any deadzone value as zero - value = 0; - - // Now filter out consecutive, similar values - // (only pass on the event if the state has changed) - if(joy->axisLastValue[axis] != value) - { - // Turn off both events, since we don't know exactly which one - // was previously activated. - handleEvent(eventAxisNeg, 0); - handleEvent(eventAxisPos, 0); - } - } - joy->axisLastValue[axis] = value; - break; - } - } - } - else if(myOverlay) - { - // First, clamp the values to simulate digital input - // (the only thing that the underlying code understands) - if(value > Joystick::deadzone()) - value = 32000; - else if(value < -Joystick::deadzone()) - value = -32000; - else - value = 0; - - // Now filter out consecutive, similar values - // (only pass on the event if the state has changed) - if(value != joy->axisLastValue[axis]) - { - myOverlay->handleJoyAxisEvent(stick, axis, value); - joy->axisLastValue[axis] = value; - } - } - break; // Regular joystick axis - - // Since the various controller classes deal with Stelladaptor - // devices differently, we send the raw X and Y axis data directly, - // and let the controller handle it - // These events don't have to pass through handleEvent, since - // they can never be remapped - case StellaJoystick::JT_STELLADAPTOR_LEFT: - case StellaJoystick::JT_STELLADAPTOR_RIGHT: - // The 'type-2' here refers to the fact that 'StellaJoystick::JT_STELLADAPTOR_LEFT' - // and 'StellaJoystick::JT_STELLADAPTOR_RIGHT' are at index 2 and 3 in the JoyType - // enum; subtracting two gives us Controller 0 and 1 - if(axis < 2) - myEvent.set(SA_Axis[joy->type-2][axis], value); - break; // Stelladaptor axis - case StellaJoystick::JT_2600DAPTOR_LEFT: - case StellaJoystick::JT_2600DAPTOR_RIGHT: - // The 'type-4' here refers to the fact that 'StellaJoystick::JT_2600DAPTOR_LEFT' - // and 'StellaJoystick::JT_2600DAPTOR_RIGHT' are at index 4 and 5 in the JoyType - // enum; subtracting four gives us Controller 0 and 1 - if(axis < 2) - myEvent.set(SA_Axis[joy->type-4][axis], value); - break; // 2600-daptor axis - default: - break; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::handleJoyHatEvent(int stick, int hat, int value) -{ - const StellaJoystick* joy = myJoyHandler->joy(stick); - if(!joy) return; - - // Preprocess all hat events, converting to Stella JoyHat type - // Generate multiple equivalent hat events representing combined direction - // when we get a diagonal hat event - if(myState == EventHandlerState::EMULATION) - { - handleEvent(joy->hatTable[hat][int(JoyHat::UP)][kEmulationMode], - value & EVENT_HATUP_M); - handleEvent(joy->hatTable[hat][int(JoyHat::RIGHT)][kEmulationMode], - value & EVENT_HATRIGHT_M); - handleEvent(joy->hatTable[hat][int(JoyHat::DOWN)][kEmulationMode], - value & EVENT_HATDOWN_M); - handleEvent(joy->hatTable[hat][int(JoyHat::LEFT)][kEmulationMode], - value & EVENT_HATLEFT_M); - } - else if(myOverlay) - { - if(value == EVENT_HATCENTER_M) - myOverlay->handleJoyHatEvent(stick, hat, JoyHat::CENTER); - else - { - if(value & EVENT_HATUP_M) - myOverlay->handleJoyHatEvent(stick, hat, JoyHat::UP); - if(value & EVENT_HATRIGHT_M) - myOverlay->handleJoyHatEvent(stick, hat, JoyHat::RIGHT); - if(value & EVENT_HATDOWN_M) - myOverlay->handleJoyHatEvent(stick, hat, JoyHat::DOWN); - if(value & EVENT_HATLEFT_M) - myOverlay->handleJoyHatEvent(stick, hat, JoyHat::LEFT); - } - } -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::handleSystemEvent(SystemEvent e, int, int) { @@ -909,8 +289,8 @@ void EventHandler::handleSystemEvent(SystemEvent e, int, int) case SystemEvent::WINDOW_FOCUS_GAINED: // Used to handle Alt-x key combos; sometimes the key associated with // Alt gets 'stuck' and is passed to the core for processing - if(myAltKeyCounter > 0) - myAltKeyCounter = 2; + if(myPKeyHandler->altKeyCount() > 0) + myPKeyHandler->altKeyCount() = 2; break; #if 0 case SystemEvent::WINDOW_MINIMIZED: @@ -974,7 +354,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 state) //////////////////////////////////////////////////////////////////////// case Event::Fry: - if(myUseCtrlKeyFlag) myFryingFlag = bool(state); + if(myPKeyHandler->useCtrlKey()) myFryingFlag = bool(state); return; case Event::VolumeDecrease: @@ -998,7 +378,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 state) return; case Event::TakeSnapshot: - if(state) takeSnapshot(); + if(state) myOSystem.png().takeSnapshot(); return; case Event::LauncherMode: @@ -1212,7 +592,7 @@ void EventHandler::handleConsoleStartupEvents() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool EventHandler::eventStateChange(Event::Type type) +bool EventHandler::changeStateByEvent(Event::Type type) { bool handled = true; @@ -1220,9 +600,9 @@ bool EventHandler::eventStateChange(Event::Type type) { case Event::PauseMode: if(myState == EventHandlerState::EMULATION) - setEventState(EventHandlerState::PAUSE); + setState(EventHandlerState::PAUSE); else if(myState == EventHandlerState::PAUSE) - setEventState(EventHandlerState::EMULATION); + setState(EventHandlerState::EMULATION); else handled = false; break; @@ -1253,6 +633,7 @@ bool EventHandler::eventStateChange(Event::Type type) break; case Event::DebuggerMode: + #ifdef DEBUGGER_SUPPORT if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE || myState == EventHandlerState::TIMEMACHINE) enterDebugMode(); @@ -1260,6 +641,7 @@ bool EventHandler::eventStateChange(Event::Type type) leaveDebugMode(); else handled = false; + #endif break; default: @@ -1289,97 +671,20 @@ void EventHandler::setActionMappings(EventMode mode) return; } - ostringstream buf; - // Fill the ActionList with the current key and joystick mappings for(int i = 0; i < listsize; ++i) { Event::Type event = list[i].event; list[i].key = "None"; - string key = ""; - for(int j = 0; j < KBDK_LAST; ++j) // key mapping - { - if(myKeyTable[j][mode] == event) - { - if(key == "") - key = key + nameForKey(StellaKey(j)); - else - key = key + ", " + nameForKey(StellaKey(j)); - } - } + string key = myPKeyHandler->getMappingDesc(event, mode); #ifdef JOYSTICK_SUPPORT - for(const auto& st: myJoyHandler->sticks()) + string joydesc = myPJoyHandler->getMappingDesc(event, mode); + if(joydesc != "") { - uInt32 stick = st.first; - const StellaJoystick* joy = st.second; - if(!joy) continue; - - // Joystick button mapping/labeling - for(int button = 0; button < joy->numButtons; ++button) - { - if(joy->btnTable[button][mode] == event) - { - buf.str(""); - buf << "J" << stick << "/B" << button; - if(key == "") - key = key + buf.str(); - else - key = key + ", " + buf.str(); - } - } - - // Joystick axis mapping/labeling - for(int axis = 0; axis < joy->numAxes; ++axis) - { - for(int dir = 0; dir < 2; ++dir) - { - if(joy->axisTable[axis][dir][mode] == event) - { - buf.str(""); - buf << "J" << stick << "/A" << axis; - if(eventIsAnalog(event)) - { - dir = 2; // Immediately exit the inner loop after this iteration - buf << "/+|-"; - } - else if(dir == 0) - buf << "/-"; - else - buf << "/+"; - - if(key == "") - key = key + buf.str(); - else - key = key + ", " + buf.str(); - } - } - } - - // Joystick hat mapping/labeling - for(int hat = 0; hat < joy->numHats; ++hat) - { - for(int dir = 0; dir < 4; ++dir) - { - if(joy->hatTable[hat][dir][mode] == event) - { - buf.str(""); - buf << "J" << stick << "/H" << hat; - switch(JoyHat(dir)) - { - case JoyHat::UP: buf << "/up"; break; - case JoyHat::DOWN: buf << "/down"; break; - case JoyHat::LEFT: buf << "/left"; break; - case JoyHat::RIGHT: buf << "/right"; break; - case JoyHat::CENTER: break; - } - if(key == "") - key = key + buf.str(); - else - key = key + ", " + buf.str(); - } - } - } + if(key != "") + key += ", "; + key += joydesc; } #endif @@ -1407,42 +712,6 @@ void EventHandler::setActionMappings(EventMode mode) } } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::setKeymap() -{ - // 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 event; - - // Get event count, which should be the first int in the list - buf >> value; - event = Event::Type(value); - if(event == Event::LastType) - while(buf >> value) - map.push_back(value); - - // Only fill the key mapping array if the data is valid - if(event == Event::LastType && map.size() == KBDK_LAST * kNumModes) - { - // Fill the keymap table with events - auto e = map.cbegin(); - for(int mode = 0; mode < kNumModes; ++mode) - for(int i = 0; i < KBDK_LAST; ++i) - myKeyTable[i][mode] = Event::Type(*e++); - } - else - { - setDefaultKeymap(Event::NoType, kEmulationMode); - setDefaultKeymap(Event::NoType, kMenuMode); - } -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::setComboMap() { @@ -1452,69 +721,63 @@ void EventHandler::setComboMap() replace(list.begin(), list.end(), ':', ' '); istringstream buf(list); - // Get combo count, which should be the first int in the list - // If it isn't, then we treat the entire list as invalid - string key; - buf >> key; - if(atoi(key.c_str()) == kComboSize) - { - // Fill the combomap table with events for as long as they exist - int combocount = 0; - while(buf >> key && combocount < kComboSize) - { - // Each event in a comboevent is separated by a comma - replace(key.begin(), key.end(), ',', ' '); - istringstream buf2(key); - - int eventcount = 0; - while(buf2 >> key && eventcount < kEventsPerCombo) - { - myComboTable[combocount][eventcount] = Event::Type(atoi(key.c_str())); - ++eventcount; - } - ++combocount; - } - } - else - { - // Erase the 'combo' array + // Erase the 'combo' array + auto ERASE_ALL = [&]() { for(int i = 0; i < kComboSize; ++i) for(int j = 0; j < kEventsPerCombo; ++j) myComboTable[i][j] = Event::NoType; + }; + + // Get combo count, which should be the first int in the list + // If it isn't, then we treat the entire list as invalid + if(!buf.good()) + ERASE_ALL(); + else + { + string key; + buf >> key; + if(atoi(key.c_str()) == kComboSize) + { + // Fill the combomap table with events for as long as they exist + int combocount = 0; + while(buf >> key && combocount < kComboSize) + { + // Each event in a comboevent is separated by a comma + replace(key.begin(), key.end(), ',', ' '); + istringstream buf2(key); + + int eventcount = 0; + while(buf2 >> key && eventcount < kEventsPerCombo) + { + myComboTable[combocount][eventcount] = Event::Type(atoi(key.c_str())); + ++eventcount; + } + ++combocount; + } + } + else + ERASE_ALL(); } saveComboMapping(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -VariantList EventHandler::joystickDatabase() const +void EventHandler::removePhysicalJoystickFromDatabase(const string& name) { - VariantList db; - for(const auto& i: myJoyHandler->database()) - VarList::push_back(db, i.first, i.second.joy ? i.second.joy->ID : -1); - - return db; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::removeJoystickFromDatabase(const string& name) -{ - myJoyHandler->remove(name); +#ifdef JOYSTICK_SUPPORT + myPJoyHandler->remove(name); +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool EventHandler::addKeyMapping(Event::Type event, EventMode mode, StellaKey key) { - // These keys cannot be remapped - if(key == KBDK_TAB || eventIsAnalog(event)) - return false; - else - { - myKeyTable[key][mode] = event; + bool mapped = myPKeyHandler->addMapping(event, mode, key); + if(mapped) setActionMappings(mode); - return true; - } + return mapped; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1523,33 +786,14 @@ bool EventHandler::addJoyAxisMapping(Event::Type event, EventMode mode, bool updateMenus) { #ifdef JOYSTICK_SUPPORT - const StellaJoystick* joy = myJoyHandler->joy(stick); - if(joy) - { - if(axis >= 0 && axis < joy->numAxes && event < Event::LastType) - { - // This confusing code is because each axis has two associated values, - // but analog events only affect one of the axis. - if(eventIsAnalog(event)) - joy->axisTable[axis][0][mode] = - joy->axisTable[axis][1][mode] = event; - else - { - // Otherwise, turn off the analog event(s) for this axis - if(eventIsAnalog(joy->axisTable[axis][0][mode])) - joy->axisTable[axis][0][mode] = Event::NoType; - if(eventIsAnalog(joy->axisTable[axis][1][mode])) - joy->axisTable[axis][1][mode] = Event::NoType; + bool mapped = myPJoyHandler->addAxisMapping(event, mode, stick, axis, value); + if(mapped && updateMenus) + setActionMappings(mode); - joy->axisTable[axis][(value > 0)][mode] = event; - } - if(updateMenus) - setActionMappings(mode); - return true; - } - } -#endif + return mapped; +#else return false; +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1558,19 +802,14 @@ bool EventHandler::addJoyButtonMapping(Event::Type event, EventMode mode, bool updateMenus) { #ifdef JOYSTICK_SUPPORT - const StellaJoystick* joy = myJoyHandler->joy(stick); - if(joy) - { - if(button >= 0 && button < joy->numButtons && event < Event::LastType) - { - joy->btnTable[button][mode] = event; - if(updateMenus) - setActionMappings(mode); - return true; - } - } -#endif + bool mapped = myPJoyHandler->addBtnMapping(event, mode, stick, button); + if(mapped && updateMenus) + setActionMappings(mode); + + return mapped; +#else return false; +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1579,33 +818,25 @@ bool EventHandler::addJoyHatMapping(Event::Type event, EventMode mode, bool updateMenus) { #ifdef JOYSTICK_SUPPORT - const StellaJoystick* joy = myJoyHandler->joy(stick); - if(joy) - { - if(hat >= 0 && hat < joy->numHats && event < Event::LastType && - value != JoyHat::CENTER) - { - joy->hatTable[hat][int(value)][mode] = event; - if(updateMenus) - setActionMappings(mode); - return true; - } - } -#endif + bool mapped = myPJoyHandler->addHatMapping(event, mode, stick, hat, value); + if(mapped && updateMenus) + setActionMappings(mode); + + return mapped; +#else return false; +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::eraseMapping(Event::Type event, EventMode mode) { - // Erase the KeyEvent arrays - for(int i = 0; i < KBDK_LAST; ++i) - if(myKeyTable[i][mode] == event && i != KBDK_TAB) - myKeyTable[i][mode] = Event::NoType; + // Erase the KeyEvent array + myPKeyHandler->eraseMapping(event, mode); #ifdef JOYSTICK_SUPPORT // Erase the joystick mapping arrays - myJoyHandler->eraseMapping(event, mode); + myPJoyHandler->eraseMapping(event, mode); #endif setActionMappings(mode); @@ -1621,138 +852,31 @@ void EventHandler::setDefaultMapping(Event::Type event, EventMode mode) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::setDefaultKeymap(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; - } + myPKeyHandler->setDefaultMapping(event, mode); setActionMappings(mode); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::setDefaultJoymap(Event::Type event, EventMode mode) { - myJoyHandler->setDefaultMapping(event, mode); +#ifdef JOYSTICK_SUPPORT + myPJoyHandler->setDefaultMapping(event, mode); setActionMappings(mode); +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::saveKeyMapping() { - // 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()); + myPKeyHandler->saveMapping(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::saveJoyMapping() { - myJoyHandler->saveMapping(); +#ifdef JOYSTICK_SUPPORT + myPJoyHandler->saveMapping(); +#endif } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1772,21 +896,6 @@ void EventHandler::saveComboMapping() myOSystem.settings().setValue("combomap", buf.str()); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -inline bool EventHandler::eventIsAnalog(Event::Type event) const -{ - switch(event) - { - case Event::PaddleZeroAnalog: - case Event::PaddleOneAnalog: - case Event::PaddleTwoAnalog: - case Event::PaddleThreeAnalog: - return true; - default: - return false; - } -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - StringList EventHandler::getActionList(EventMode mode) const { @@ -1935,118 +1044,6 @@ string EventHandler::keyAtIndex(int idx, EventMode mode) const } } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::takeSnapshot(uInt32 number) -{ - // 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 EventHandler::controllerIsAnalog(Controller::Jack jack) const -{ - const Controller& controller = jack == Controller::Left ? - myOSystem.console().leftController() : myOSystem.console().rightController(); - - switch(controller.type()) - { - case Controller::Paddles: - case Controller::Driving: - case Controller::AmigaMouse: - case Controller::AtariMouse: - case Controller::TrakBall: - case Controller::MindLink: - return true; - default: - return false; - } -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::setMouseControllerMode(const string& enable) { @@ -2059,9 +1056,8 @@ void EventHandler::setMouseControllerMode(const string& enable) usemouse = false; else // 'analog' { - if(controllerIsAnalog(Controller::Left) || - controllerIsAnalog(Controller::Right)) - usemouse = true; + usemouse = myOSystem.console().controller(Controller::Left).isAnalog() || + myOSystem.console().controller(Controller::Right).isAnalog(); } const string& control = usemouse ? @@ -2072,17 +1068,10 @@ void EventHandler::setMouseControllerMode(const string& enable) } } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::setContinuousSnapshots(uInt32 interval) -{ - myContSnapshotInterval = interval; - myContSnapshotCounter = 0; -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::enterMenuMode(EventHandlerState state) { - setEventState(state); + setState(state); myOverlay->reStack(); myOSystem.sound().mute(true); } @@ -2090,7 +1079,7 @@ void EventHandler::enterMenuMode(EventHandlerState state) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::leaveMenuMode() { - setEventState(EventHandlerState::EMULATION); + setState(EventHandlerState::EMULATION); myOSystem.sound().mute(false); } @@ -2106,13 +1095,13 @@ bool EventHandler::enterDebugMode() // mode, since it takes care of locking the debugger state, which will // probably be modified below myOSystem.debugger().setStartState(); - setEventState(EventHandlerState::DEBUGGER); + setState(EventHandlerState::DEBUGGER); FBInitStatus fbstatus = myOSystem.createFrameBuffer(); if(fbstatus != FBInitStatus::Success) { myOSystem.debugger().setQuitState(); - setEventState(EventHandlerState::EMULATION); + setState(EventHandlerState::EMULATION); if(fbstatus == FBInitStatus::FailTooLarge) myOSystem.frameBuffer().showMessage("Debugger window too large for screen", MessagePosition::BottomCenter, true); @@ -2139,7 +1128,7 @@ void EventHandler::leaveDebugMode() // Make sure debugger quits in a consistent state myOSystem.debugger().setQuitState(); - setEventState(EventHandlerState::EMULATION); + setState(EventHandlerState::EMULATION); myOSystem.createFrameBuffer(); myOSystem.sound().mute(false); #endif @@ -2160,28 +1149,27 @@ void EventHandler::enterTimeMachineMenuMode(uInt32 numWinds, bool unwind) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void EventHandler::setEventState(EventHandlerState state) +void EventHandler::setState(EventHandlerState state) { myState = state; // Normally, the usage of Control key is determined by 'ctrlcombo' // For certain ROMs it may be forced off, whatever the setting - myUseCtrlKeyFlag = myOSystem.settings().getBool("ctrlcombo"); + myPKeyHandler->useCtrlKey() = myOSystem.settings().getBool("ctrlcombo"); // Only enable text input in GUI modes, since in emulation mode the // keyboard acts as one large joystick with many (single) buttons + myOverlay = nullptr; switch(myState) { case EventHandlerState::EMULATION: - myOverlay = nullptr; myOSystem.sound().mute(false); enableTextEvents(false); if(myOSystem.console().leftController().type() == Controller::CompuMate) - myUseCtrlKeyFlag = false; + myPKeyHandler->useCtrlKey() = false; break; case EventHandlerState::PAUSE: - myOverlay = nullptr; myOSystem.sound().mute(true); enableTextEvents(false); break; @@ -2216,7 +1204,6 @@ void EventHandler::setEventState(EventHandlerState state) break; case EventHandlerState::NONE: - myOverlay = nullptr; break; } @@ -2362,33 +1349,3 @@ EventHandler::ActionList EventHandler::ourMenuActionList[kMenuActionListSize] = { Event::UIPrevDir, "Parent directory", "", false } }; - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Used by the Stelladaptor to send absolute axis values -const Event::Type EventHandler::SA_Axis[2][2] = { - { Event::SALeftAxis0Value, Event::SALeftAxis1Value }, - { Event::SARightAxis0Value, Event::SARightAxis1Value } -}; - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Used by the Stelladaptor to map button presses to joystick or paddles -// (driving controllers and boostergrip are considered the same as joysticks) -const Event::Type EventHandler::SA_Button[2][4] = { - { Event::JoystickZeroFire, Event::JoystickZeroFire9, - Event::JoystickZeroFire5, Event::JoystickZeroFire9 }, - { Event::JoystickOneFire, Event::JoystickOneFire9, - Event::JoystickOneFire5, Event::JoystickOneFire9 } -}; - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Used by the 2600-daptor to map button presses to keypad keys -const Event::Type EventHandler::SA_Key[2][12] = { - { Event::KeyboardZero1, Event::KeyboardZero2, Event::KeyboardZero3, - Event::KeyboardZero4, Event::KeyboardZero5, Event::KeyboardZero6, - Event::KeyboardZero7, Event::KeyboardZero8, Event::KeyboardZero9, - Event::KeyboardZeroStar, Event::KeyboardZero0, Event::KeyboardZeroPound }, - { Event::KeyboardOne1, Event::KeyboardOne2, Event::KeyboardOne3, - Event::KeyboardOne4, Event::KeyboardOne5, Event::KeyboardOne6, - Event::KeyboardOne7, Event::KeyboardOne8, Event::KeyboardOne9, - Event::KeyboardOneStar, Event::KeyboardOne0, Event::KeyboardOnePound } -}; diff --git a/src/emucore/EventHandler.hxx b/src/emucore/EventHandler.hxx index 4821f4ca2..a6d32ce98 100644 --- a/src/emucore/EventHandler.hxx +++ b/src/emucore/EventHandler.hxx @@ -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; - using StickList = std::map; - - 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 myPKeyHandler; + + // Handler for all joystick addition/removal/mapping + unique_ptr myPJoyHandler; + // MouseControl object, which takes care of switching the mouse between // all possible controller modes unique_ptr 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 myJoyHandler; - // Following constructors and assignment operators not supported EventHandler() = delete; EventHandler(const EventHandler&) = delete; diff --git a/src/emucore/EventJoyHandler.cxx b/src/emucore/EventJoyHandler.cxx deleted file mode 100644 index 295c72b69..000000000 --- a/src/emucore/EventJoyHandler.cxx +++ /dev/null @@ -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 - -#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()); -} diff --git a/src/emucore/FrameBuffer.cxx b/src/emucore/FrameBuffer.cxx index ad517f37c..4fa8d950c 100644 --- a/src/emucore/FrameBuffer.cxx +++ b/src/emucore/FrameBuffer.cxx @@ -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); diff --git a/src/emucore/FrameBuffer.hxx b/src/emucore/FrameBuffer.hxx index 998c54f6b..29df6d441 100644 --- a/src/emucore/FrameBuffer.hxx +++ b/src/emucore/FrameBuffer.hxx @@ -506,7 +506,9 @@ class FrameBuffer shared_ptr 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; diff --git a/src/emucore/M6502.cxx b/src/emucore/M6502.cxx index be39a27e6..19cf07ed8 100644 --- a/src/emucore/M6502.cxx +++ b/src/emucore/M6502.cxx @@ -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 diff --git a/src/emucore/M6502.hxx b/src/emucore/M6502.hxx index 3c88674ac..c0512d658 100644 --- a/src/emucore/M6502.hxx +++ b/src/emucore/M6502.hxx @@ -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: /** diff --git a/src/emucore/MindLink.hxx b/src/emucore/MindLink.hxx index f0697ee15..15c7c96ba 100644 --- a/src/emucore/MindLink.hxx +++ b/src/emucore/MindLink.hxx @@ -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 diff --git a/src/emucore/OSystem.cxx b/src/emucore/OSystem.cxx index 4b7f52512..f9b2ec853 100644 --- a/src/emucore/OSystem.cxx +++ b/src/emucore/OSystem.cxx @@ -154,7 +154,7 @@ bool OSystem::create() myRandom->initSeed(); // Create PNG handler - myPNGLib = make_unique(*myFrameBuffer); + myPNGLib = make_unique(*this); return true; } diff --git a/src/emucore/Paddles.hxx b/src/emucore/Paddles.hxx index 3e198f294..4a206a5d7 100644 --- a/src/emucore/Paddles.hxx +++ b/src/emucore/Paddles.hxx @@ -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 diff --git a/src/emucore/PointingDevice.hxx b/src/emucore/PointingDevice.hxx index 8c6b4f16b..8ff501c11 100644 --- a/src/emucore/PointingDevice.hxx +++ b/src/emucore/PointingDevice.hxx @@ -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 diff --git a/src/emucore/module.mk b/src/emucore/module.mk index f9a4d0903..3c909aa48 100644 --- a/src/emucore/module.mk +++ b/src/emucore/module.mk @@ -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 \ diff --git a/src/emucore/tia/Background.cxx b/src/emucore/tia/Background.cxx index b432a2ef6..6e522e290 100644 --- a/src/emucore/tia/Background.cxx +++ b/src/emucore/tia/Background.cxx @@ -20,6 +20,7 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Background::Background() + : myTIA(nullptr) { reset(); } diff --git a/src/emucore/tia/Ball.cxx b/src/emucore/tia/Ball.cxx index 48f6946bf..2a492d22b 100644 --- a/src/emucore/tia/Ball.cxx +++ b/src/emucore/tia/Ball.cxx @@ -26,7 +26,8 @@ enum Count: Int8 { Ball::Ball(uInt32 collisionMask) : myCollisionMaskDisabled(collisionMask), myCollisionMaskEnabled(0xFFFF), - myIsSuppressed(false) + myIsSuppressed(false), + myTIA(nullptr) { reset(); } diff --git a/src/emucore/tia/DelayQueue.hxx b/src/emucore/tia/DelayQueue.hxx index 6d3453e70..d2d3ce069 100644 --- a/src/emucore/tia/DelayQueue.hxx +++ b/src/emucore/tia/DelayQueue.hxx @@ -83,7 +83,7 @@ void DelayQueue::push(uInt8 address, uInt8 value, uInt8 delay) uInt8 currentIndex = myIndices[address]; - if (currentIndex < 0xFF) + if (currentIndex < length) myMembers[currentIndex].remove(address); uInt8 index = smartmod(myIndex + delay); diff --git a/src/emucore/tia/DelayQueueMember.hxx b/src/emucore/tia/DelayQueueMember.hxx index cc0e9a5fe..a280d61ff 100644 --- a/src/emucore/tia/DelayQueueMember.hxx +++ b/src/emucore/tia/DelayQueueMember.hxx @@ -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&) = delete; DelayQueueMember(DelayQueueMember&&) = delete; DelayQueueMember& operator=(const DelayQueueMember&) = delete; @@ -68,7 +69,8 @@ class DelayQueueMember : public Serializable { template DelayQueueMember::DelayQueueMember() : mySize(0) -{} +{ +} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - template @@ -110,7 +112,7 @@ void DelayQueueMember::clear() template bool DelayQueueMember::save(Serializer& out) const { - try + try { out.putInt(mySize); for(uInt8 i = 0; i < mySize; ++i) diff --git a/src/emucore/tia/Missile.cxx b/src/emucore/tia/Missile.cxx index 93f824f1b..e70579c06 100644 --- a/src/emucore/tia/Missile.cxx +++ b/src/emucore/tia/Missile.cxx @@ -28,7 +28,8 @@ Missile::Missile(uInt32 collisionMask) : myCollisionMaskDisabled(collisionMask), myCollisionMaskEnabled(0xFFFF), myIsSuppressed(false), - myDecodesOffset(0) + myDecodesOffset(0), + myTIA(nullptr) { reset(); } diff --git a/src/emucore/tia/Player.cxx b/src/emucore/tia/Player.cxx index 70c6fbcaf..bcfa9c4ab 100644 --- a/src/emucore/tia/Player.cxx +++ b/src/emucore/tia/Player.cxx @@ -28,7 +28,8 @@ Player::Player(uInt32 collisionMask) : myCollisionMaskDisabled(collisionMask), myCollisionMaskEnabled(0xFFFF), myIsSuppressed(false), - myDecodesOffset(0) + myDecodesOffset(0), + myTIA(nullptr) { reset(); } diff --git a/src/emucore/tia/Playfield.cxx b/src/emucore/tia/Playfield.cxx index 9d3adba74..72137a008 100644 --- a/src/emucore/tia/Playfield.cxx +++ b/src/emucore/tia/Playfield.cxx @@ -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; diff --git a/src/emucore/tia/TIA.cxx b/src/emucore/tia/TIA.cxx index 50eacf054..77cf0df49 100644 --- a/src/emucore/tia/TIA.cxx +++ b/src/emucore/tia/TIA.cxx @@ -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")); diff --git a/src/emucore/tia/frame-manager/FrameManager.cxx b/src/emucore/tia/frame-manager/FrameManager.cxx index b74a23a10..18b1a54c1 100644 --- a/src/emucore/tia/frame-manager/FrameManager.cxx +++ b/src/emucore/tia/frame-manager/FrameManager.cxx @@ -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(); } diff --git a/src/emucore/tia/frame-manager/JitterEmulation.cxx b/src/emucore/tia/frame-manager/JitterEmulation.cxx index 18b53951e..8daa28228 100644 --- a/src/emucore/tia/frame-manager/JitterEmulation.cxx +++ b/src/emucore/tia/frame-manager/JitterEmulation.cxx @@ -25,9 +25,12 @@ enum Metrics: uInt32 { }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -JitterEmulation::JitterEmulation() : - myYStart(0) -{} +JitterEmulation::JitterEmulation() + : myJitterFactor(0), + myYStart(0) +{ + reset(); +} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void JitterEmulation::reset() diff --git a/src/gui/Dialog.cxx b/src/gui/Dialog.cxx index 073748367..7789d4541 100644 --- a/src/gui/Dialog.cxx +++ b/src/gui/Dialog.cxx @@ -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), diff --git a/src/gui/DialogContainer.cxx b/src/gui/DialogContainer.cxx index f603c1fbd..a25a8fc4e 100644 --- a/src/gui/DialogContainer.cxx +++ b/src/gui/DialogContainer.cxx @@ -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; diff --git a/src/gui/DialogContainer.hxx b/src/gui/DialogContainer.hxx index 0d6b44f01..776f917bb 100644 --- a/src/gui/DialogContainer.hxx +++ b/src/gui/DialogContainer.hxx @@ -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. diff --git a/src/gui/JoystickDialog.cxx b/src/gui/JoystickDialog.cxx index 791f80597..cf02adef6 100644 --- a/src/gui/JoystickDialog.cxx +++ b/src/gui/JoystickDialog.cxx @@ -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; diff --git a/src/gui/RadioButtonWidget.cxx b/src/gui/RadioButtonWidget.cxx index e0549ee1d..aa129461b 100644 --- a/src/gui/RadioButtonWidget.cxx +++ b/src/gui/RadioButtonWidget.cxx @@ -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) diff --git a/src/gui/RadioButtonWidget.hxx b/src/gui/RadioButtonWidget.hxx index d86b32e80..15264bab2 100644 --- a/src/gui/RadioButtonWidget.hxx +++ b/src/gui/RadioButtonWidget.hxx @@ -56,7 +56,7 @@ class RadioButtonWidget : public CheckboxWidget class RadioButtonGroup { public: - RadioButtonGroup() = default; + RadioButtonGroup() : mySelected(0) { } // add widget to group void addWidget(RadioButtonWidget* widget); diff --git a/src/gui/Widget.cxx b/src/gui/Widget.cxx index 89d3c61af..ff276a95c 100644 --- a/src/gui/Widget.cxx +++ b/src/gui/Widget.cxx @@ -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); diff --git a/src/macosx/stella.xcodeproj/project.pbxproj b/src/macosx/stella.xcodeproj/project.pbxproj index 459b7ba78..9766500da 100644 --- a/src/macosx/stella.xcodeproj/project.pbxproj +++ b/src/macosx/stella.xcodeproj/project.pbxproj @@ -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 = ""; }; DC1B2EC01E50036100F62837 /* AtariMouse.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AtariMouse.hxx; sourceTree = ""; }; DC1B2EC21E50036100F62837 /* TrakBall.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrakBall.hxx; sourceTree = ""; }; + DC1BC6642066B4390076F74A /* PKeyboardHandler.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PKeyboardHandler.cxx; sourceTree = ""; }; + DC1BC6652066B4390076F74A /* PKeyboardHandler.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PKeyboardHandler.hxx; sourceTree = ""; }; DC1FC1880DB3B2C7009B3DF7 /* SerialPortMACOSX.cxx */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = SerialPortMACOSX.cxx; sourceTree = ""; }; DC1FC1890DB3B2C7009B3DF7 /* SerialPortMACOSX.hxx */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = SerialPortMACOSX.hxx; sourceTree = ""; }; DC2874061F8F2278004BF21A /* TrapArray.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TrapArray.hxx; sourceTree = ""; }; @@ -991,7 +998,6 @@ DC5D2C510F117CFD004D1660 /* StellaMediumFont.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = StellaMediumFont.hxx; sourceTree = ""; }; DC5D2C5E0F129B1E004D1660 /* LauncherFilterDialog.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LauncherFilterDialog.cxx; sourceTree = ""; }; DC5D2C5F0F129B1E004D1660 /* LauncherFilterDialog.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LauncherFilterDialog.hxx; sourceTree = ""; }; - DC5E473A19EC9A14000E45DF /* EventJoyHandler.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventJoyHandler.cxx; sourceTree = ""; }; DC5EE7C014F7C165001C628C /* NTSCFilter.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NTSCFilter.cxx; sourceTree = ""; }; DC5EE7C114F7C165001C628C /* NTSCFilter.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = NTSCFilter.hxx; sourceTree = ""; }; DC62E6431960E87B007AEF05 /* AtariVoxWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AtariVoxWidget.cxx; sourceTree = ""; }; @@ -1044,6 +1050,10 @@ DC6C726113CDEA0A008A5975 /* LoggerDialog.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LoggerDialog.hxx; sourceTree = ""; }; DC6D39851A3CE65000171E71 /* CartWDWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CartWDWidget.cxx; sourceTree = ""; }; DC6D39861A3CE65000171E71 /* CartWDWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartWDWidget.hxx; sourceTree = ""; }; + DC6DC91A205DB879004A5FC3 /* PhysicalJoystick.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PhysicalJoystick.cxx; sourceTree = ""; }; + DC6DC91B205DB879004A5FC3 /* PhysicalJoystick.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PhysicalJoystick.hxx; sourceTree = ""; }; + DC6DC91C205DB879004A5FC3 /* PJoystickHandler.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PJoystickHandler.cxx; sourceTree = ""; }; + DC6DC91D205DB879004A5FC3 /* PJoystickHandler.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PJoystickHandler.hxx; sourceTree = ""; }; DC71EA991FDA06D2008827CB /* CartE78K.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CartE78K.cxx; sourceTree = ""; }; DC71EA9A1FDA06D2008827CB /* CartE78K.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartE78K.hxx; sourceTree = ""; }; DC71EA9B1FDA06D2008827CB /* CartMNetwork.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CartMNetwork.cxx; sourceTree = ""; }; @@ -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 */, diff --git a/src/windows/Stella.vcxproj b/src/windows/Stella.vcxproj index 15ea0f4a4..623958114 100644 --- a/src/windows/Stella.vcxproj +++ b/src/windows/Stella.vcxproj @@ -236,6 +236,9 @@ + + + @@ -321,7 +324,6 @@ - @@ -522,6 +524,9 @@ + + + diff --git a/src/windows/Stella.vcxproj.filters b/src/windows/Stella.vcxproj.filters index 50126d528..f2c059592 100644 --- a/src/windows/Stella.vcxproj.filters +++ b/src/windows/Stella.vcxproj.filters @@ -741,9 +741,6 @@ Source Files\debugger - - Source Files\emucore - Source Files @@ -899,6 +896,15 @@ Source Files\gui + + Source Files + + + Source Files + + + Source Files + @@ -1839,6 +1845,15 @@ Header Files\gui + + Header Files + + + Header Files + + + Header Files +