mirror of https://github.com/stella-emu/stella.git
2522 lines
82 KiB
C++
2522 lines
82 KiB
C++
//============================================================================
|
|
//
|
|
// SSSS tt lll lll
|
|
// SS SS tt ll ll
|
|
// SS tttttt eeee ll ll aaaa
|
|
// SSSS tt ee ee ll ll aa
|
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
|
// SS SS tt ee ll ll aa aa
|
|
// SSSS ttt eeeee llll llll aaaaa
|
|
//
|
|
// Copyright (c) 1995-2014 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.
|
|
//
|
|
// $Id$
|
|
//============================================================================
|
|
|
|
#include <sstream>
|
|
#include <map>
|
|
|
|
#include "bspf.hxx"
|
|
|
|
#include "Base.hxx"
|
|
#include "CommandMenu.hxx"
|
|
#include "Console.hxx"
|
|
#include "DialogContainer.hxx"
|
|
#include "Event.hxx"
|
|
#include "FrameBuffer.hxx"
|
|
#include "FSNode.hxx"
|
|
#include "Launcher.hxx"
|
|
#include "Menu.hxx"
|
|
#include "OSystem.hxx"
|
|
#include "Joystick.hxx"
|
|
#include "Paddles.hxx"
|
|
#include "PropsSet.hxx"
|
|
#include "ListWidget.hxx"
|
|
#include "ScrollBarWidget.hxx"
|
|
#include "Settings.hxx"
|
|
#include "Sound.hxx"
|
|
#include "StateManager.hxx"
|
|
#include "M6532.hxx"
|
|
#include "MouseControl.hxx"
|
|
#include "Version.hxx"
|
|
|
|
#include "EventHandler.hxx"
|
|
|
|
#ifdef CHEATCODE_SUPPORT
|
|
#include "Cheat.hxx"
|
|
#include "CheatManager.hxx"
|
|
#endif
|
|
#ifdef DEBUGGER_SUPPORT
|
|
#include "Debugger.hxx"
|
|
#endif
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EventHandler::EventHandler(OSystem& osystem)
|
|
: myOSystem(osystem),
|
|
myOverlay(NULL),
|
|
myMouseControl(NULL),
|
|
myState(S_NONE),
|
|
myAllowAllDirectionsFlag(false),
|
|
myFryingFlag(false),
|
|
mySkipMouseMotion(true)
|
|
{
|
|
// 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;
|
|
|
|
// Erase the 'combo' array
|
|
for(int i = 0; i < kComboSize; ++i)
|
|
for(int j = 0; j < kEventsPerCombo; ++j)
|
|
myComboTable[i][j] = Event::NoType;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EventHandler::~EventHandler()
|
|
{
|
|
// Free strings created with strdup
|
|
for(uInt32 i = 0; i < kEmulActionListSize; ++i)
|
|
if(ourEmulActionList[i].key)
|
|
free(ourEmulActionList[i].key);
|
|
for(uInt32 i = 0; i < kMenuActionListSize; ++i)
|
|
if(ourMenuActionList[i].key)
|
|
free(ourMenuActionList[i].key);
|
|
|
|
delete myMouseControl;
|
|
for(uInt32 i = 0; i < myJoysticks.size(); ++i)
|
|
delete myJoysticks[i];
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
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"));
|
|
|
|
// Set quick select delay when typing characters in listwidgets
|
|
ListWidget::setQuickSelectDelay(myOSystem.settings().getInt("listdelay"));
|
|
|
|
// Set number of lines a mousewheel will scroll
|
|
ScrollBarWidget::setWheelLines(myOSystem.settings().getInt("mwheel"));
|
|
|
|
// Integer to string conversions (for HEX) use upper or lower-case
|
|
Common::Base::setHexUppercase(myOSystem.settings().getBool("dbg.uhex"));
|
|
|
|
// Joystick stuff
|
|
#ifdef JOYSTICK_SUPPORT
|
|
initializeJoysticks();
|
|
|
|
// Map the stelladaptors we've found according to the specified ports
|
|
mapStelladaptors(myOSystem.settings().getString("saport"));
|
|
|
|
setJoymap();
|
|
setActionMappings(kEmulationMode);
|
|
setActionMappings(kMenuMode);
|
|
|
|
ostringstream buf;
|
|
buf << "Joystick devices found:" << endl;
|
|
if(myJoysticks.size() == 0)
|
|
buf << "No joysticks present." << endl;
|
|
else
|
|
{
|
|
for(uInt32 i = 0; i < myJoysticks.size(); ++i)
|
|
buf << " " << i << ": " << myJoysticks[i]->about() << endl;
|
|
}
|
|
myOSystem.logMessage(buf.str(), 1);
|
|
#else
|
|
myOSystem.logMessage("Joystick support disabled.", 1);
|
|
#endif
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::reset(State state)
|
|
{
|
|
setEventState(state);
|
|
myOSystem.state().reset();
|
|
|
|
setContinuousSnapshots(0);
|
|
|
|
// Reset events almost immediately after starting emulation mode
|
|
// We wait a little while, since 'hold' events may be present, and we want
|
|
// time for the ROM to process them
|
|
if(state == S_EMULATE)
|
|
SDL_AddTimer(500, resetEventsCallback, (void*)this);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::addJoystick(StellaJoystick* stick, int idx)
|
|
{
|
|
#ifdef JOYSTICK_SUPPORT
|
|
myJoysticks.insert_at(idx, stick);
|
|
|
|
// Skip if we couldn't open it for any reason
|
|
if(stick->name == "None")
|
|
return;
|
|
|
|
// Figure out what type of joystick this is
|
|
if(stick->name.find("2600-daptor", 0) != string::npos)
|
|
{
|
|
// 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";
|
|
}
|
|
else if(stick->name.find("Stelladaptor", 0) != string::npos)
|
|
{
|
|
stick->name = "Stelladaptor";
|
|
}
|
|
else
|
|
{
|
|
// We need unique names for mappable devices
|
|
int count = 0;
|
|
for(uInt32 i = 0; i < myJoysticks.size(); ++i)
|
|
if(BSPF_startsWithIgnoreCase(myJoysticks[i]->name, stick->name))
|
|
++count;
|
|
|
|
if(count > 1)
|
|
{
|
|
ostringstream name;
|
|
name << stick->name << " " << count;
|
|
stick->name = name.str();
|
|
}
|
|
stick->type = StellaJoystick::JT_REGULAR;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::mapStelladaptors(const string& saport)
|
|
{
|
|
#ifdef JOYSTICK_SUPPORT
|
|
// 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];
|
|
if(BSPF_equalsIgnoreCase(saport, "lr"))
|
|
{
|
|
saOrder[0] = 1; saOrder[1] = 2;
|
|
}
|
|
else
|
|
{
|
|
saOrder[0] = 2; saOrder[1] = 1;
|
|
}
|
|
|
|
for(uInt32 i = 0; i < myJoysticks.size(); ++i)
|
|
{
|
|
if(BSPF_startsWithIgnoreCase(myJoysticks[i]->name, "Stelladaptor"))
|
|
{
|
|
saCount++;
|
|
if(saOrder[saCount-1] == 1)
|
|
{
|
|
myJoysticks[i]->name += " (emulates left joystick port)";
|
|
myJoysticks[i]->type = StellaJoystick::JT_STELLADAPTOR_LEFT;
|
|
}
|
|
else if(saOrder[saCount-1] == 2)
|
|
{
|
|
myJoysticks[i]->name += " (emulates right joystick port)";
|
|
myJoysticks[i]->type = StellaJoystick::JT_STELLADAPTOR_RIGHT;
|
|
}
|
|
}
|
|
else if(BSPF_startsWithIgnoreCase(myJoysticks[i]->name, "2600-daptor"))
|
|
{
|
|
saCount++;
|
|
if(saOrder[saCount-1] == 1)
|
|
{
|
|
myJoysticks[i]->name += " (emulates left joystick port)";
|
|
myJoysticks[i]->type = StellaJoystick::JT_2600DAPTOR_LEFT;
|
|
}
|
|
else if(saOrder[saCount-1] == 2)
|
|
{
|
|
myJoysticks[i]->name += " (emulates right joystick port)";
|
|
myJoysticks[i]->type = StellaJoystick::JT_2600DAPTOR_RIGHT;
|
|
}
|
|
}
|
|
}
|
|
myOSystem.settings().setValue("saport", saport);
|
|
|
|
// 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::toggleSAPortOrder()
|
|
{
|
|
const string& saport = myOSystem.settings().getString("saport");
|
|
if(saport == "lr")
|
|
{
|
|
mapStelladaptors("rl");
|
|
myOSystem.frameBuffer().showMessage("Stelladaptor ports right/left");
|
|
}
|
|
else
|
|
{
|
|
mapStelladaptors("lr");
|
|
myOSystem.frameBuffer().showMessage("Stelladaptor ports left/right");
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::poll(uInt64 time)
|
|
{
|
|
// Process events from the underlying hardware
|
|
pollEvent();
|
|
|
|
// Update controllers and console switches, and in general all other things
|
|
// related to emulation
|
|
if(myState == S_EMULATE)
|
|
{
|
|
myOSystem.console().riot().update();
|
|
|
|
#if 0
|
|
// Now check if the StateManager should be saving or loading state
|
|
// Per-frame cheats are disabled if the StateManager is active, since
|
|
// it would interfere with proper playback
|
|
if(myOSystem.state().isActive())
|
|
{
|
|
myOSystem.state().update();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#ifdef CHEATCODE_SUPPORT
|
|
const CheatList& cheats = myOSystem.cheat().perFrame();
|
|
for(uInt32 i = 0; i < cheats.size(); i++)
|
|
cheats[i]->evaluate();
|
|
#endif
|
|
|
|
// Handle continuous snapshots
|
|
if(myContSnapshotInterval > 0 &&
|
|
(++myContSnapshotCounter % myContSnapshotInterval == 0))
|
|
takeSnapshot((uInt32)time >> 10); // not quite milliseconds, but close enough
|
|
}
|
|
}
|
|
else if(myOverlay)
|
|
{
|
|
// Update the current dialog container at regular intervals
|
|
// Used to implement continuous events
|
|
myOverlay->updateTime(time);
|
|
}
|
|
|
|
// Turn off all mouse-related items; if they haven't been taken care of
|
|
// in the previous ::update() methods, they're now invalid
|
|
myEvent.set(Event::MouseAxisXValue, 0);
|
|
myEvent.set(Event::MouseAxisYValue, 0);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::handleTextEvent(char text)
|
|
{
|
|
// Text events are only used in GUI mode
|
|
if(myOverlay != NULL)
|
|
myOverlay->handleTextEvent(text);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::handleKeyEvent(StellaKey key, StellaMod mod, bool state)
|
|
{
|
|
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(kbdAlt(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_RETURN)
|
|
{
|
|
myOSystem.frameBuffer().toggleFullscreen();
|
|
}
|
|
// These only work when in emulation mode
|
|
else if(myState == S_EMULATE)
|
|
{
|
|
switch(key)
|
|
{
|
|
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_M:
|
|
myOSystem.console().toggleHMOVE();
|
|
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_P: // Alt-p toggles phosphor effect
|
|
myOSystem.console().togglePhosphor();
|
|
break;
|
|
|
|
case KBDK_L:
|
|
myOSystem.frameBuffer().toggleFrameStats();
|
|
break;
|
|
|
|
case KBDK_S:
|
|
if(myContSnapshotInterval == 0)
|
|
{
|
|
ostringstream buf;
|
|
uInt32 interval = myOSystem.settings().getInt("ssinterval");
|
|
buf << "Enabling shotshots in " << interval << " second intervals";
|
|
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(kbdControl(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 == S_EMULATE)
|
|
{
|
|
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();
|
|
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.c_str(), ios::out);
|
|
if(out)
|
|
{
|
|
myOSystem.console().properties().save(out);
|
|
out.close();
|
|
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;
|
|
|
|
// Handle keys which switch eventhandler state
|
|
// Arrange the logic to take advantage of short-circuit evaluation
|
|
if(!(kbdControl(mod) || kbdShift(mod) || kbdAlt(mod)) &&
|
|
state && eventStateChange(myKeyTable[key][kEmulationMode]))
|
|
return;
|
|
|
|
// Otherwise, let the event handler deal with it
|
|
if(myState == S_EMULATE)
|
|
handleEvent(myKeyTable[key][kEmulationMode], state);
|
|
else if(myOverlay != NULL)
|
|
myOverlay->handleKeyEvent(key, mod, state);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::handleMouseMotionEvent(int x, int y, int xrel, int yrel, int button)
|
|
{
|
|
// Determine which mode we're in, then send the event to the appropriate place
|
|
if(myState == S_EMULATE)
|
|
{
|
|
if(!mySkipMouseMotion)
|
|
{
|
|
myEvent.set(Event::MouseAxisXValue, xrel);
|
|
myEvent.set(Event::MouseAxisYValue, yrel);
|
|
}
|
|
mySkipMouseMotion = false;
|
|
}
|
|
else if(myOverlay)
|
|
myOverlay->handleMouseMotionEvent(x, y, button);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::handleMouseButtonEvent(MouseButton b, int x, int y)
|
|
{
|
|
// Determine which mode we're in, then send the event to the appropriate place
|
|
if(myState == S_EMULATE)
|
|
{
|
|
switch(b)
|
|
{
|
|
case EVENT_LBUTTONDOWN:
|
|
myEvent.set(Event::MouseButtonLeftValue, 1);
|
|
break;
|
|
case EVENT_LBUTTONUP:
|
|
myEvent.set(Event::MouseButtonLeftValue, 0);
|
|
break;
|
|
case EVENT_RBUTTONDOWN:
|
|
myEvent.set(Event::MouseButtonRightValue, 1);
|
|
break;
|
|
case EVENT_RBUTTONUP:
|
|
myEvent.set(Event::MouseButtonRightValue, 0);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
else if(myOverlay)
|
|
myOverlay->handleMouseButtonEvent(b, x, y);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::handleJoyEvent(int stick, int button, uInt8 state)
|
|
{
|
|
if(stick >= (int)myJoysticks.size())
|
|
return;
|
|
|
|
// Stelladaptors handle buttons differently than regular joysticks
|
|
const StellaJoystick& joy = *myJoysticks[stick];
|
|
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 == S_EMULATE)
|
|
handleEvent(joy.btnTable[button][kEmulationMode], state);
|
|
else if(myOverlay != NULL)
|
|
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 == S_EMULATE)
|
|
{
|
|
switch(myOSystem.console().controller(Controller::Left).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().controller(Controller::Right).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)
|
|
{
|
|
if(stick >= (int)myJoysticks.size())
|
|
return;
|
|
|
|
// Stelladaptors handle axis differently than regular joysticks
|
|
const StellaJoystick& joy = *myJoysticks[stick];
|
|
switch(joy.type)
|
|
{
|
|
case StellaJoystick::JT_REGULAR:
|
|
if(myState == S_EMULATE)
|
|
{
|
|
// 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 != NULL)
|
|
{
|
|
// 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; // 26000daptor axis
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::handleJoyHatEvent(int stick, int hat, int value)
|
|
{
|
|
if(stick >= (int)myJoysticks.size())
|
|
return;
|
|
|
|
const StellaJoystick& joy = *myJoysticks[stick];
|
|
|
|
// 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 == S_EMULATE)
|
|
{
|
|
handleEvent(joy.hatTable[hat][EVENT_HATUP][kEmulationMode],
|
|
value & EVENT_HATUP_M);
|
|
handleEvent(joy.hatTable[hat][EVENT_HATRIGHT][kEmulationMode],
|
|
value & EVENT_HATRIGHT_M);
|
|
handleEvent(joy.hatTable[hat][EVENT_HATDOWN][kEmulationMode],
|
|
value & EVENT_HATDOWN_M);
|
|
handleEvent(joy.hatTable[hat][EVENT_HATLEFT][kEmulationMode],
|
|
value & EVENT_HATLEFT_M);
|
|
}
|
|
else if(myOverlay != NULL)
|
|
{
|
|
if(value == EVENT_HATCENTER_M)
|
|
myOverlay->handleJoyHatEvent(stick, hat, EVENT_HATCENTER);
|
|
else
|
|
{
|
|
if(value & EVENT_HATUP_M)
|
|
myOverlay->handleJoyHatEvent(stick, hat, EVENT_HATUP);
|
|
if(value & EVENT_HATRIGHT_M)
|
|
myOverlay->handleJoyHatEvent(stick, hat, EVENT_HATRIGHT);
|
|
if(value & EVENT_HATDOWN_M)
|
|
myOverlay->handleJoyHatEvent(stick, hat, EVENT_HATDOWN);
|
|
if(value & EVENT_HATLEFT_M)
|
|
myOverlay->handleJoyHatEvent(stick, hat, EVENT_HATLEFT);
|
|
}
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::handleSystemEvent(SystemEvent e, int data1, int data2)
|
|
{
|
|
switch(e)
|
|
{
|
|
case EVENT_WINDOW_EXPOSED:
|
|
myOSystem.frameBuffer().refresh();
|
|
break;
|
|
case EVENT_WINDOW_MINIMIZED:
|
|
if(myState == S_EMULATE) enterMenuMode(S_MENU);
|
|
break;
|
|
default: // handle other events as testing requires
|
|
// cerr << "handleSystemEvent: " << e << endl;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::handleEvent(Event::Type event, int state)
|
|
{
|
|
// Take care of special events that aren't part of the emulation core
|
|
// or need to be preprocessed before passing them on
|
|
switch(event)
|
|
{
|
|
////////////////////////////////////////////////////////////////////////
|
|
// If enabled, make sure 'impossible' joystick directions aren't allowed
|
|
case Event::JoystickZeroUp:
|
|
if(!myAllowAllDirectionsFlag && state)
|
|
myEvent.set(Event::JoystickZeroDown, 0);
|
|
break;
|
|
|
|
case Event::JoystickZeroDown:
|
|
if(!myAllowAllDirectionsFlag && state)
|
|
myEvent.set(Event::JoystickZeroUp, 0);
|
|
break;
|
|
|
|
case Event::JoystickZeroLeft:
|
|
if(!myAllowAllDirectionsFlag && state)
|
|
myEvent.set(Event::JoystickZeroRight, 0);
|
|
break;
|
|
|
|
case Event::JoystickZeroRight:
|
|
if(!myAllowAllDirectionsFlag && state)
|
|
myEvent.set(Event::JoystickZeroLeft, 0);
|
|
break;
|
|
|
|
case Event::JoystickOneUp:
|
|
if(!myAllowAllDirectionsFlag && state)
|
|
myEvent.set(Event::JoystickOneDown, 0);
|
|
break;
|
|
|
|
case Event::JoystickOneDown:
|
|
if(!myAllowAllDirectionsFlag && state)
|
|
myEvent.set(Event::JoystickOneUp, 0);
|
|
break;
|
|
|
|
case Event::JoystickOneLeft:
|
|
if(!myAllowAllDirectionsFlag && state)
|
|
myEvent.set(Event::JoystickOneRight, 0);
|
|
break;
|
|
|
|
case Event::JoystickOneRight:
|
|
if(!myAllowAllDirectionsFlag && state)
|
|
myEvent.set(Event::JoystickOneLeft, 0);
|
|
break;
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
case Event::Fry:
|
|
if(myUseCtrlKeyFlag) myFryingFlag = bool(state);
|
|
return;
|
|
|
|
case Event::VolumeDecrease:
|
|
if(state) myOSystem.sound().adjustVolume(-1);
|
|
return;
|
|
|
|
case Event::VolumeIncrease:
|
|
if(state) myOSystem.sound().adjustVolume(+1);
|
|
return;
|
|
|
|
case Event::SaveState:
|
|
if(state) myOSystem.state().saveState();
|
|
return;
|
|
|
|
case Event::ChangeState:
|
|
if(state) myOSystem.state().changeState();
|
|
return;
|
|
|
|
case Event::LoadState:
|
|
if(state) myOSystem.state().loadState();
|
|
return;
|
|
|
|
case Event::TakeSnapshot:
|
|
if(state) takeSnapshot();
|
|
return;
|
|
|
|
case Event::LauncherMode:
|
|
if((myState == S_EMULATE || myState == S_CMDMENU ||
|
|
myState == S_DEBUGGER) && state)
|
|
{
|
|
// Go back to the launcher, or immediately quit
|
|
if(myOSystem.settings().getBool("exitlauncher") ||
|
|
myOSystem.launcherUsed())
|
|
{
|
|
myOSystem.deleteConsole();
|
|
myOSystem.createLauncher();
|
|
}
|
|
else
|
|
handleEvent(Event::Quit, 1);
|
|
}
|
|
return;
|
|
|
|
case Event::Quit:
|
|
if(state)
|
|
{
|
|
saveKeyMapping();
|
|
saveJoyMapping();
|
|
myOSystem.quit();
|
|
}
|
|
return;
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
// A combo event is simply multiple calls to handleEvent, once for
|
|
// each event it contains
|
|
case Event::Combo1:
|
|
case Event::Combo2:
|
|
case Event::Combo3:
|
|
case Event::Combo4:
|
|
case Event::Combo5:
|
|
case Event::Combo6:
|
|
case Event::Combo7:
|
|
case Event::Combo8:
|
|
case Event::Combo9:
|
|
case Event::Combo10:
|
|
case Event::Combo11:
|
|
case Event::Combo12:
|
|
case Event::Combo13:
|
|
case Event::Combo14:
|
|
case Event::Combo15:
|
|
case Event::Combo16:
|
|
for(int i = 0, combo = event - Event::Combo1; i < kEventsPerCombo; ++i)
|
|
if(myComboTable[combo][i] != Event::NoType)
|
|
handleEvent(myComboTable[combo][i], state);
|
|
return;
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
// Events which generate messages
|
|
case Event::ConsoleColor:
|
|
if(state) myOSystem.frameBuffer().showMessage("Color Mode");
|
|
break;
|
|
case Event::ConsoleBlackWhite:
|
|
if(state) myOSystem.frameBuffer().showMessage("BW Mode");
|
|
break;
|
|
case Event::ConsoleLeftDiffA:
|
|
if(state) myOSystem.frameBuffer().showMessage("Left Difficulty A");
|
|
break;
|
|
case Event::ConsoleLeftDiffB:
|
|
if(state) myOSystem.frameBuffer().showMessage("Left Difficulty B");
|
|
break;
|
|
case Event::ConsoleRightDiffA:
|
|
if(state) myOSystem.frameBuffer().showMessage("Right Difficulty A");
|
|
break;
|
|
case Event::ConsoleRightDiffB:
|
|
if(state) myOSystem.frameBuffer().showMessage("Right Difficulty B");
|
|
break;
|
|
|
|
case Event::NoType: // Ignore unmapped events
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Otherwise, pass it to the emulation core
|
|
myEvent.set(event, state);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool EventHandler::eventStateChange(Event::Type type)
|
|
{
|
|
bool handled = true;
|
|
|
|
switch(type)
|
|
{
|
|
case Event::PauseMode:
|
|
if(myState == S_EMULATE)
|
|
setEventState(S_PAUSE);
|
|
else if(myState == S_PAUSE)
|
|
setEventState(S_EMULATE);
|
|
else
|
|
handled = false;
|
|
break;
|
|
|
|
case Event::MenuMode:
|
|
if(myState == S_EMULATE)
|
|
enterMenuMode(S_MENU);
|
|
else
|
|
handled = false;
|
|
break;
|
|
|
|
case Event::CmdMenuMode:
|
|
if(myState == S_EMULATE)
|
|
enterMenuMode(S_CMDMENU);
|
|
else if(myState == S_CMDMENU)
|
|
leaveMenuMode();
|
|
else
|
|
handled = false;
|
|
break;
|
|
|
|
case Event::DebuggerMode:
|
|
if(myState == S_EMULATE)
|
|
enterDebugMode();
|
|
else if(myState == S_DEBUGGER)
|
|
leaveDebugMode();
|
|
else
|
|
handled = false;
|
|
break;
|
|
|
|
default:
|
|
handled = false;
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::setActionMappings(EventMode mode)
|
|
{
|
|
int listsize = 0;
|
|
ActionList* list = NULL;
|
|
|
|
switch(mode)
|
|
{
|
|
case kEmulationMode:
|
|
listsize = kEmulActionListSize;
|
|
list = ourEmulActionList;
|
|
break;
|
|
case kMenuMode:
|
|
listsize = kMenuActionListSize;
|
|
list = ourMenuActionList;
|
|
break;
|
|
default:
|
|
return;
|
|
break;
|
|
}
|
|
|
|
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;
|
|
free(list[i].key); list[i].key = NULL;
|
|
list[i].key = strdup("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);
|
|
}
|
|
}
|
|
|
|
#ifdef JOYSTICK_SUPPORT
|
|
for(uInt32 stick = 0; stick < myJoysticks.size(); ++stick)
|
|
{
|
|
const StellaJoystick& joy = *myJoysticks[stick];
|
|
|
|
// 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(dir)
|
|
{
|
|
case EVENT_HATUP: buf << "/up"; break;
|
|
case EVENT_HATDOWN: buf << "/down"; break;
|
|
case EVENT_HATLEFT: buf << "/left"; break;
|
|
case EVENT_HATRIGHT: buf << "/right"; break;
|
|
}
|
|
if(key == "")
|
|
key = key + buf.str();
|
|
else
|
|
key = key + ", " + buf.str();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// There are some keys which are hardcoded. These should be represented too.
|
|
string prepend = "";
|
|
if(event == Event::Quit)
|
|
#ifndef BSPF_MAC_OSX
|
|
prepend = "Ctrl Q";
|
|
#else
|
|
prepend = "Cmd Q";
|
|
#endif
|
|
else if(event == Event::UINavNext)
|
|
prepend = "TAB";
|
|
else if(event == Event::UINavPrev)
|
|
prepend = "Shift-TAB";
|
|
// else if ...
|
|
|
|
if(key == "")
|
|
key = prepend;
|
|
else if(prepend != "")
|
|
key = prepend + ", " + key;
|
|
|
|
if(key != "")
|
|
{
|
|
free(list[i].key); list[i].key = NULL;
|
|
list[i].key = strdup(key.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
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
|
|
IntArray::const_iterator event = map.begin();
|
|
for(int mode = 0; mode < kNumModes; ++mode)
|
|
for(int i = 0; i < KBDK_LAST; ++i)
|
|
myKeyTable[i][mode] = (Event::Type) *event++;
|
|
}
|
|
else
|
|
{
|
|
setDefaultKeymap(Event::NoType, kEmulationMode);
|
|
setDefaultKeymap(Event::NoType, kMenuMode);
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::setJoymap()
|
|
{
|
|
#ifdef JOYSTICK_SUPPORT
|
|
setDefaultJoymap(Event::NoType, kEmulationMode);
|
|
setDefaultJoymap(Event::NoType, kMenuMode);
|
|
|
|
// Get all mappings from the settings
|
|
istringstream buf(myOSystem.settings().getString("joymap"));
|
|
string joymap;
|
|
|
|
// First check the event type, and disregard the entire mapping if it's invalid
|
|
getline(buf, joymap, '^');
|
|
if(atoi(joymap.c_str()) != Event::LastType)
|
|
return;
|
|
|
|
// Otherwise, put each joystick mapping entry into a hashmap
|
|
while(getline(buf, joymap, '^'))
|
|
{
|
|
istringstream namebuf(joymap);
|
|
string joyname;
|
|
getline(namebuf, joyname, '|');
|
|
if(joyname.length() != 0)
|
|
myJoystickMap.insert(make_pair(joyname, joymap));
|
|
}
|
|
|
|
// Next try to match the mappings to the specific joystick (by name)
|
|
// We do it this way since a joystick may be unplugged and replugged,
|
|
// but it's settings should stay the same
|
|
for(uInt32 i = 0; i < myJoysticks.size(); ++i)
|
|
{
|
|
StellaJoystick& joy = *myJoysticks[i];
|
|
if(joy.type == StellaJoystick::JT_REGULAR)
|
|
{
|
|
map<string,string>::const_iterator iter = myJoystickMap.find(joy.name);
|
|
if(iter != myJoystickMap.end())
|
|
joy.setMap(iter->second);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::setComboMap()
|
|
{
|
|
// Since istringstream swallows whitespace, we have to make the
|
|
// delimiters be spaces
|
|
string list = myOSystem.settings().getString("combomap");
|
|
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
|
|
for(int i = 0; i < kComboSize; ++i)
|
|
for(int j = 0; j < kEventsPerCombo; ++j)
|
|
myComboTable[i][j] = Event::NoType;
|
|
}
|
|
|
|
saveComboMapping();
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
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;
|
|
setActionMappings(mode);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool EventHandler::addJoyAxisMapping(Event::Type event, EventMode mode,
|
|
int stick, int axis, int value,
|
|
bool updateMenus)
|
|
{
|
|
#ifdef JOYSTICK_SUPPORT
|
|
if(stick >= 0 && stick < (int)myJoysticks.size())
|
|
{
|
|
const StellaJoystick& joy = *myJoysticks[stick];
|
|
if(axis >= 0 && axis < joy.numAxes &&
|
|
event >= 0 && 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;
|
|
|
|
joy.axisTable[axis][(value > 0)][mode] = event;
|
|
}
|
|
if(updateMenus)
|
|
setActionMappings(mode);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool EventHandler::addJoyButtonMapping(Event::Type event, EventMode mode,
|
|
int stick, int button,
|
|
bool updateMenus)
|
|
{
|
|
#ifdef JOYSTICK_SUPPORT
|
|
if(stick >= 0 && stick < (int)myJoysticks.size() && !eventIsAnalog(event))
|
|
{
|
|
const StellaJoystick& joy = *myJoysticks[stick];
|
|
if(button >= 0 && button < joy.numButtons &&
|
|
event >= 0 && event < Event::LastType)
|
|
{
|
|
joy.btnTable[button][mode] = event;
|
|
if(updateMenus)
|
|
setActionMappings(mode);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool EventHandler::addJoyHatMapping(Event::Type event, EventMode mode,
|
|
int stick, int hat, int value,
|
|
bool updateMenus)
|
|
{
|
|
#ifdef JOYSTICK_SUPPORT
|
|
if(stick >= 0 && stick < (int)myJoysticks.size() && !eventIsAnalog(event))
|
|
{
|
|
const StellaJoystick& joy = *myJoysticks[stick];
|
|
if(hat >= 0 && hat < joy.numHats &&
|
|
event >= 0 && event < Event::LastType && value != EVENT_HATCENTER)
|
|
{
|
|
joy.hatTable[hat][value][mode] = event;
|
|
if(updateMenus)
|
|
setActionMappings(mode);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
#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;
|
|
|
|
#ifdef JOYSTICK_SUPPORT
|
|
// Erase the joystick mapping arrays
|
|
for(uInt32 i = 0; i < myJoysticks.size(); ++i)
|
|
myJoysticks[i]->eraseMap(mode);
|
|
#endif
|
|
|
|
setActionMappings(mode);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::setDefaultMapping(Event::Type event, EventMode mode)
|
|
{
|
|
setDefaultKeymap(event, mode);
|
|
setDefaultJoymap(event, mode);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::setDefaultKeymap(Event::Type event, EventMode mode)
|
|
{
|
|
#define SET_DEFAULT_KEY(sdk_key, sdk_mode, sdk_event, sdk_cmp_event) \
|
|
if(eraseAll || sdk_cmp_event == sdk_event) \
|
|
myKeyTable[sdk_key][sdk_mode] = sdk_event;
|
|
|
|
// 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;
|
|
}
|
|
|
|
switch(mode)
|
|
{
|
|
case kEmulationMode:
|
|
SET_DEFAULT_KEY(KBDK_1, mode, Event::KeyboardZero1, event);
|
|
SET_DEFAULT_KEY(KBDK_2, mode, Event::KeyboardZero2, event);
|
|
SET_DEFAULT_KEY(KBDK_3, mode, Event::KeyboardZero3, event);
|
|
SET_DEFAULT_KEY(KBDK_Q, mode, Event::KeyboardZero4, event);
|
|
SET_DEFAULT_KEY(KBDK_W, mode, Event::KeyboardZero5, event);
|
|
SET_DEFAULT_KEY(KBDK_E, mode, Event::KeyboardZero6, event);
|
|
SET_DEFAULT_KEY(KBDK_A, mode, Event::KeyboardZero7, event);
|
|
SET_DEFAULT_KEY(KBDK_S, mode, Event::KeyboardZero8, event);
|
|
SET_DEFAULT_KEY(KBDK_D, mode, Event::KeyboardZero9, event);
|
|
SET_DEFAULT_KEY(KBDK_Z, mode, Event::KeyboardZeroStar, event);
|
|
SET_DEFAULT_KEY(KBDK_X, mode, Event::KeyboardZero0, event);
|
|
SET_DEFAULT_KEY(KBDK_C, mode, Event::KeyboardZeroPound, event);
|
|
|
|
SET_DEFAULT_KEY(KBDK_8, mode, Event::KeyboardOne1, event);
|
|
SET_DEFAULT_KEY(KBDK_9, mode, Event::KeyboardOne2, event);
|
|
SET_DEFAULT_KEY(KBDK_0, mode, Event::KeyboardOne3, event);
|
|
SET_DEFAULT_KEY(KBDK_I, mode, Event::KeyboardOne4, event);
|
|
SET_DEFAULT_KEY(KBDK_O, mode, Event::KeyboardOne5, event);
|
|
SET_DEFAULT_KEY(KBDK_P, mode, Event::KeyboardOne6, event);
|
|
SET_DEFAULT_KEY(KBDK_K, mode, Event::KeyboardOne7, event);
|
|
SET_DEFAULT_KEY(KBDK_L, mode, Event::KeyboardOne8, event);
|
|
SET_DEFAULT_KEY(KBDK_SEMICOLON, mode, Event::KeyboardOne9, event);
|
|
SET_DEFAULT_KEY(KBDK_COMMA, mode, Event::KeyboardOneStar, event);
|
|
SET_DEFAULT_KEY(KBDK_PERIOD, mode, Event::KeyboardOne0, event);
|
|
SET_DEFAULT_KEY(KBDK_SLASH, mode, Event::KeyboardOnePound, event);
|
|
|
|
SET_DEFAULT_KEY(KBDK_UP, mode, Event::JoystickZeroUp, event);
|
|
SET_DEFAULT_KEY(KBDK_DOWN, mode, Event::JoystickZeroDown, event);
|
|
SET_DEFAULT_KEY(KBDK_LEFT, mode, Event::JoystickZeroLeft, event);
|
|
SET_DEFAULT_KEY(KBDK_RIGHT, mode, Event::JoystickZeroRight, event);
|
|
SET_DEFAULT_KEY(KBDK_SPACE, mode, Event::JoystickZeroFire, event);
|
|
SET_DEFAULT_KEY(KBDK_LCTRL, mode, Event::JoystickZeroFire, event);
|
|
SET_DEFAULT_KEY(KBDK_4, mode, Event::JoystickZeroFire5, event);
|
|
SET_DEFAULT_KEY(KBDK_5, mode, Event::JoystickZeroFire9, event);
|
|
|
|
SET_DEFAULT_KEY(KBDK_Y, mode, Event::JoystickOneUp, event);
|
|
SET_DEFAULT_KEY(KBDK_H, mode, Event::JoystickOneDown, event);
|
|
SET_DEFAULT_KEY(KBDK_G, mode, Event::JoystickOneLeft, event);
|
|
SET_DEFAULT_KEY(KBDK_J, mode, Event::JoystickOneRight, event);
|
|
SET_DEFAULT_KEY(KBDK_F, mode, Event::JoystickOneFire, event);
|
|
SET_DEFAULT_KEY(KBDK_6, mode, Event::JoystickOneFire5, event);
|
|
SET_DEFAULT_KEY(KBDK_7, mode, Event::JoystickOneFire9, event);
|
|
|
|
|
|
SET_DEFAULT_KEY(KBDK_F1, mode, Event::ConsoleSelect, event);
|
|
SET_DEFAULT_KEY(KBDK_F2, mode, Event::ConsoleReset, event);
|
|
SET_DEFAULT_KEY(KBDK_F3, mode, Event::ConsoleColor, event);
|
|
SET_DEFAULT_KEY(KBDK_F4, mode, Event::ConsoleBlackWhite, event);
|
|
SET_DEFAULT_KEY(KBDK_F5, mode, Event::ConsoleLeftDiffA, event);
|
|
SET_DEFAULT_KEY(KBDK_F6, mode, Event::ConsoleLeftDiffB, event);
|
|
SET_DEFAULT_KEY(KBDK_F7, mode, Event::ConsoleRightDiffA, event);
|
|
SET_DEFAULT_KEY(KBDK_F8, mode, Event::ConsoleRightDiffB, event);
|
|
SET_DEFAULT_KEY(KBDK_F9, mode, Event::SaveState, event);
|
|
SET_DEFAULT_KEY(KBDK_F10, mode, Event::ChangeState, event);
|
|
SET_DEFAULT_KEY(KBDK_F11, mode, Event::LoadState, event);
|
|
SET_DEFAULT_KEY(KBDK_F12, mode, Event::TakeSnapshot, event);
|
|
SET_DEFAULT_KEY(KBDK_BACKSPACE, mode, Event::Fry, event);
|
|
SET_DEFAULT_KEY(KBDK_PAUSE, mode, Event::PauseMode, event);
|
|
SET_DEFAULT_KEY(KBDK_TAB, mode, Event::MenuMode, event);
|
|
SET_DEFAULT_KEY(KBDK_BACKSLASH, mode, Event::CmdMenuMode, event);
|
|
SET_DEFAULT_KEY(KBDK_GRAVE, mode, Event::DebuggerMode, event);
|
|
SET_DEFAULT_KEY(KBDK_ESCAPE, mode, Event::LauncherMode, event);
|
|
break;
|
|
|
|
case kMenuMode:
|
|
SET_DEFAULT_KEY(KBDK_UP, mode, Event::UIUp, event);
|
|
SET_DEFAULT_KEY(KBDK_DOWN, mode, Event::UIDown, event);
|
|
SET_DEFAULT_KEY(KBDK_LEFT, mode, Event::UILeft, event);
|
|
SET_DEFAULT_KEY(KBDK_RIGHT, mode, Event::UIRight, event);
|
|
|
|
SET_DEFAULT_KEY(KBDK_HOME, mode, Event::UIHome, event);
|
|
SET_DEFAULT_KEY(KBDK_END, mode, Event::UIEnd, event);
|
|
SET_DEFAULT_KEY(KBDK_PAGEUP, mode, Event::UIPgUp, event);
|
|
SET_DEFAULT_KEY(KBDK_PAGEDOWN, mode, Event::UIPgDown, event);
|
|
|
|
SET_DEFAULT_KEY(KBDK_RETURN, mode, Event::UISelect, event);
|
|
SET_DEFAULT_KEY(KBDK_ESCAPE, mode, Event::UICancel, event);
|
|
|
|
SET_DEFAULT_KEY(KBDK_BACKSPACE, mode, Event::UIPrevDir, event);
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
break;
|
|
}
|
|
setActionMappings(mode);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::setDefaultJoymap(Event::Type event, EventMode mode)
|
|
{
|
|
#ifdef JOYSTICK_SUPPORT
|
|
// If event is 'NoType', erase and reset all mappings
|
|
// Otherwise, only reset the given event
|
|
for(uInt32 i = 0; i < myJoysticks.size(); ++i)
|
|
{
|
|
if(event == Event::NoType)
|
|
myJoysticks[i]->eraseMap(mode); // erase *all* mappings
|
|
else
|
|
myJoysticks[i]->eraseEvent(event, mode); // only reset the specific event
|
|
}
|
|
myOSystem.setDefaultJoymap(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());
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::saveJoyMapping()
|
|
{
|
|
#ifdef JOYSTICK_SUPPORT
|
|
// Don't update the joymap at all if it hasn't been modified during the
|
|
// program run
|
|
if(myJoysticks.size() == 0)
|
|
return;
|
|
|
|
// Save the joystick mapping hash table, making sure to update it with
|
|
// any changes that have been made during the program run
|
|
for(uInt32 i = 0; i < myJoysticks.size(); ++i)
|
|
{
|
|
const StellaJoystick& joy = *myJoysticks[i];
|
|
if(joy.type == StellaJoystick::JT_REGULAR)
|
|
{
|
|
// Update hashmap, removing the joystick entry (if it exists)
|
|
// and adding the most recent mapping from the joystick itself
|
|
map<string,string>::iterator iter = myJoystickMap.find(joy.name);
|
|
if(iter != myJoystickMap.end())
|
|
myJoystickMap.erase(iter);
|
|
myJoystickMap.insert(make_pair(joy.name, joy.getMap()));
|
|
}
|
|
}
|
|
|
|
// Now save the contents of the hashmap
|
|
ostringstream joybuf;
|
|
joybuf << Event::LastType;
|
|
map<string,string>::const_iterator iter;
|
|
for(iter = myJoystickMap.begin(); iter != myJoystickMap.end(); ++iter)
|
|
joybuf << "^" << iter->second;
|
|
|
|
myOSystem.settings().setValue("joymap", joybuf.str());
|
|
#endif
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::saveComboMapping()
|
|
{
|
|
// Iterate through the combomap table and create a colon-separated list
|
|
// For each combo event, create a comma-separated list of its events
|
|
// Prepend the event count, so we can check it on next load
|
|
ostringstream buf;
|
|
buf << kComboSize;
|
|
for(int i = 0; i < kComboSize; ++i)
|
|
{
|
|
buf << ":" << myComboTable[i][0];
|
|
for(int j = 1; j < kEventsPerCombo; ++j)
|
|
buf << "," << myComboTable[i][j];
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::getActionList(EventMode mode, StringList& l) const
|
|
{
|
|
switch(mode)
|
|
{
|
|
case kEmulationMode:
|
|
for(uInt32 i = 0; i < kEmulActionListSize; ++i)
|
|
l.push_back(EventHandler::ourEmulActionList[i].action);
|
|
break;
|
|
case kMenuMode:
|
|
for(uInt32 i = 0; i < kMenuActionListSize; ++i)
|
|
l.push_back(EventHandler::ourMenuActionList[i].action);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::getComboList(EventMode, VariantList& l) const
|
|
{
|
|
// For now, this only works in emulation mode
|
|
|
|
ostringstream buf;
|
|
|
|
l.push_back("None", "-1");
|
|
for(uInt32 i = 0; i < kEmulActionListSize; ++i)
|
|
if(EventHandler::ourEmulActionList[i].allow_combo)
|
|
{
|
|
buf << i;
|
|
l.push_back(EventHandler::ourEmulActionList[i].action, buf.str());
|
|
buf.str("");
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::getComboListForEvent(Event::Type event, StringList& l) const
|
|
{
|
|
ostringstream buf;
|
|
if(event >= Event::Combo1 && event <= Event::Combo16)
|
|
{
|
|
int combo = event - Event::Combo1;
|
|
for(uInt32 i = 0; i < kEventsPerCombo; ++i)
|
|
{
|
|
Event::Type e = myComboTable[combo][i];
|
|
for(uInt32 j = 0; j < kEmulActionListSize; ++j)
|
|
{
|
|
if(EventHandler::ourEmulActionList[j].event == e &&
|
|
EventHandler::ourEmulActionList[j].allow_combo)
|
|
{
|
|
buf << j;
|
|
l.push_back(buf.str());
|
|
buf.str("");
|
|
}
|
|
}
|
|
// Make sure entries are 1-to-1, using '-1' to indicate Event::NoType
|
|
if(i == l.size())
|
|
l.push_back("-1");
|
|
}
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::setComboListForEvent(Event::Type event, const StringList& events)
|
|
{
|
|
if(event >= Event::Combo1 && event <= Event::Combo16)
|
|
{
|
|
assert(events.size() == 8);
|
|
int combo = event - Event::Combo1;
|
|
for(int i = 0; i < 8; ++i)
|
|
{
|
|
int idx = atoi(events[i].c_str());
|
|
if(idx >=0 && idx < kEmulActionListSize)
|
|
myComboTable[combo][i] = EventHandler::ourEmulActionList[idx].event;
|
|
else
|
|
myComboTable[combo][i] = Event::NoType;
|
|
}
|
|
saveComboMapping();
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
Event::Type EventHandler::eventAtIndex(int idx, EventMode mode) const
|
|
{
|
|
switch(mode)
|
|
{
|
|
case kEmulationMode:
|
|
if(idx < 0 || idx >= kEmulActionListSize)
|
|
return Event::NoType;
|
|
else
|
|
return ourEmulActionList[idx].event;
|
|
break;
|
|
case kMenuMode:
|
|
if(idx < 0 || idx >= kMenuActionListSize)
|
|
return Event::NoType;
|
|
else
|
|
return ourMenuActionList[idx].event;
|
|
break;
|
|
default:
|
|
return Event::NoType;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
string EventHandler::actionAtIndex(int idx, EventMode mode) const
|
|
{
|
|
switch(mode)
|
|
{
|
|
case kEmulationMode:
|
|
if(idx < 0 || idx >= kEmulActionListSize)
|
|
return EmptyString;
|
|
else
|
|
return ourEmulActionList[idx].action;
|
|
break;
|
|
case kMenuMode:
|
|
if(idx < 0 || idx >= kMenuActionListSize)
|
|
return EmptyString;
|
|
else
|
|
return ourMenuActionList[idx].action;
|
|
break;
|
|
default:
|
|
return EmptyString;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
string EventHandler::keyAtIndex(int idx, EventMode mode) const
|
|
{
|
|
switch(mode)
|
|
{
|
|
case kEmulationMode:
|
|
if(idx < 0 || idx >= kEmulActionListSize)
|
|
return EmptyString;
|
|
else
|
|
return ourEmulActionList[idx].key;
|
|
break;
|
|
case kMenuMode:
|
|
if(idx < 0 || idx >= kMenuActionListSize)
|
|
return EmptyString;
|
|
else
|
|
return ourMenuActionList[idx].key;
|
|
break;
|
|
default:
|
|
return EmptyString;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
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 << "_" << hex << setw(8) << 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 << "]";
|
|
comments.push_back("Software", version.str());
|
|
comments.push_back("ROM Name", myOSystem.console().properties().get(Cartridge_Name));
|
|
comments.push_back("ROM MD5", myOSystem.console().properties().get(Cartridge_MD5));
|
|
comments.push_back("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 char* msg)
|
|
{
|
|
message = msg;
|
|
}
|
|
if(showmessage)
|
|
myOSystem.frameBuffer().showMessage(message);
|
|
}
|
|
else
|
|
{
|
|
// Make sure we have a 'clean' image, with no onscreen messages
|
|
myOSystem.frameBuffer().enableMessages(false);
|
|
|
|
string message = "Snapshot saved";
|
|
try
|
|
{
|
|
myOSystem.png().saveImage(filename, comments);
|
|
}
|
|
catch(const char* msg)
|
|
{
|
|
message = msg;
|
|
}
|
|
|
|
// Re-enable old messages
|
|
myOSystem.frameBuffer().enableMessages(true);
|
|
if(showmessage)
|
|
myOSystem.frameBuffer().showMessage(message);
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::setMouseControllerMode(const string& enable)
|
|
{
|
|
if(&myOSystem.console())
|
|
{
|
|
delete myMouseControl; myMouseControl = NULL;
|
|
|
|
bool usemouse = false;
|
|
if(BSPF_equalsIgnoreCase(enable, "always"))
|
|
usemouse = true;
|
|
else if(BSPF_equalsIgnoreCase(enable, "never"))
|
|
usemouse = false;
|
|
else // 'analog'
|
|
{
|
|
switch(myOSystem.console().controller(Controller::Left).type())
|
|
{
|
|
case Controller::Paddles:
|
|
case Controller::Driving:
|
|
case Controller::TrackBall22:
|
|
case Controller::TrackBall80:
|
|
case Controller::AmigaMouse:
|
|
case Controller::MindLink:
|
|
usemouse = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
switch(myOSystem.console().controller(Controller::Right).type())
|
|
{
|
|
case Controller::Paddles:
|
|
case Controller::Driving:
|
|
case Controller::TrackBall22:
|
|
case Controller::TrackBall80:
|
|
case Controller::AmigaMouse:
|
|
case Controller::MindLink:
|
|
usemouse = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
const string& control = usemouse ?
|
|
myOSystem.console().properties().get(Controller_MouseAxis) : "none";
|
|
|
|
myMouseControl = new MouseControl(myOSystem.console(), control);
|
|
myMouseControl->next(); // set first available mode
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::setContinuousSnapshots(uInt32 interval)
|
|
{
|
|
myContSnapshotInterval = (uInt32) myOSystem.frameRate() * interval;
|
|
myContSnapshotCounter = 0;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::enterMenuMode(State state)
|
|
{
|
|
setEventState(state);
|
|
myOverlay->reStack();
|
|
myOSystem.sound().mute(true);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::leaveMenuMode()
|
|
{
|
|
setEventState(S_EMULATE);
|
|
myOSystem.sound().mute(false);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
bool EventHandler::enterDebugMode()
|
|
{
|
|
#ifdef DEBUGGER_SUPPORT
|
|
if(myState == S_DEBUGGER || !(&myOSystem.console()))
|
|
return false;
|
|
|
|
// Make sure debugger starts in a consistent state
|
|
// This absolutely *has* to come before we actually change to debugger
|
|
// mode, since it takes care of locking the debugger state, which will
|
|
// probably be modified below
|
|
myOSystem.debugger().setStartState();
|
|
setEventState(S_DEBUGGER);
|
|
|
|
FBInitStatus fbstatus = myOSystem.createFrameBuffer();
|
|
if(fbstatus != kSuccess)
|
|
{
|
|
myOSystem.debugger().setQuitState();
|
|
setEventState(S_EMULATE);
|
|
if(fbstatus == kFailTooLarge)
|
|
myOSystem.frameBuffer().showMessage("Debugger window too large for screen",
|
|
kBottomCenter, true);
|
|
return false;
|
|
}
|
|
myOverlay->reStack();
|
|
myOSystem.sound().mute(true);
|
|
#else
|
|
myOSystem.frameBuffer().showMessage("Debugger support not included",
|
|
kBottomCenter, true);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::leaveDebugMode()
|
|
{
|
|
#ifdef DEBUGGER_SUPPORT
|
|
// paranoia: this should never happen:
|
|
if(myState != S_DEBUGGER)
|
|
return;
|
|
|
|
// Make sure debugger quits in a consistent state
|
|
myOSystem.debugger().setQuitState();
|
|
|
|
setEventState(S_EMULATE);
|
|
myOSystem.createFrameBuffer();
|
|
myOSystem.sound().mute(false);
|
|
#endif
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::setEventState(State 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");
|
|
|
|
// Only enable text input in GUI modes, since in emulation mode the
|
|
// keyboard acts as one large joystick with many (single) buttons
|
|
switch(myState)
|
|
{
|
|
case S_EMULATE:
|
|
myOverlay = NULL;
|
|
myOSystem.sound().mute(false);
|
|
enableTextEvents(false);
|
|
if(myOSystem.console().controller(Controller::Left).type() ==
|
|
Controller::CompuMate)
|
|
myUseCtrlKeyFlag = false;
|
|
break;
|
|
|
|
case S_PAUSE:
|
|
myOverlay = NULL;
|
|
myOSystem.sound().mute(true);
|
|
enableTextEvents(false);
|
|
break;
|
|
|
|
case S_MENU:
|
|
myOverlay = &myOSystem.menu();
|
|
enableTextEvents(true);
|
|
break;
|
|
|
|
case S_CMDMENU:
|
|
myOverlay = &myOSystem.commandMenu();
|
|
enableTextEvents(true);
|
|
break;
|
|
|
|
case S_LAUNCHER:
|
|
myOverlay = &myOSystem.launcher();
|
|
enableTextEvents(true);
|
|
break;
|
|
|
|
#ifdef DEBUGGER_SUPPORT
|
|
case S_DEBUGGER:
|
|
myOverlay = &myOSystem.debugger();
|
|
enableTextEvents(true);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
myOverlay = NULL;
|
|
break;
|
|
}
|
|
|
|
// Inform various subsystems about the new state
|
|
myOSystem.stateChanged(myState);
|
|
if(&myOSystem.frameBuffer())
|
|
{
|
|
myOSystem.frameBuffer().stateChanged(myState);
|
|
myOSystem.frameBuffer().setCursorState();
|
|
}
|
|
if(&myOSystem.console())
|
|
{
|
|
myOSystem.console().stateChanged(myState);
|
|
}
|
|
|
|
// Always clear any pending events when changing states
|
|
myEvent.clear();
|
|
|
|
// Sometimes an extraneous mouse motion event is generated
|
|
// after a state change, which should be supressed
|
|
mySkipMouseMotion = true;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
uInt32 EventHandler::resetEventsCallback(uInt32 interval, void* param)
|
|
{
|
|
((EventHandler*)param)->myEvent.clear();
|
|
return 0;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EventHandler::ActionList EventHandler::ourEmulActionList[kEmulActionListSize] = {
|
|
{ Event::ConsoleSelect, "Select", 0, true },
|
|
{ Event::ConsoleReset, "Reset", 0, true },
|
|
{ Event::ConsoleColor, "Color TV", 0, true },
|
|
{ Event::ConsoleBlackWhite, "Black & White TV", 0, true },
|
|
{ Event::ConsoleLeftDiffA, "P0 Difficulty A", 0, true },
|
|
{ Event::ConsoleLeftDiffB, "P0 Difficulty B", 0, true },
|
|
{ Event::ConsoleRightDiffA, "P1 Difficulty A", 0, true },
|
|
{ Event::ConsoleRightDiffB, "P1 Difficulty B", 0, true },
|
|
{ Event::SaveState, "Save State", 0, false },
|
|
{ Event::ChangeState, "Change State", 0, false },
|
|
{ Event::LoadState, "Load State", 0, false },
|
|
{ Event::TakeSnapshot, "Snapshot", 0, false },
|
|
{ Event::Fry, "Fry cartridge", 0, false },
|
|
{ Event::VolumeDecrease, "Decrease volume", 0, false },
|
|
{ Event::VolumeIncrease, "Increase volume", 0, false },
|
|
{ Event::PauseMode, "Pause", 0, false },
|
|
{ Event::MenuMode, "Enter options menu mode", 0, false },
|
|
{ Event::CmdMenuMode, "Toggle command menu mode", 0, false },
|
|
{ Event::DebuggerMode, "Toggle debugger mode", 0, false },
|
|
{ Event::LauncherMode, "Enter ROM launcher", 0, false },
|
|
{ Event::Quit, "Quit", 0, false },
|
|
|
|
{ Event::JoystickZeroUp, "P0 Joystick Up", 0, true },
|
|
{ Event::JoystickZeroDown, "P0 Joystick Down", 0, true },
|
|
{ Event::JoystickZeroLeft, "P0 Joystick Left", 0, true },
|
|
{ Event::JoystickZeroRight, "P0 Joystick Right", 0, true },
|
|
{ Event::JoystickZeroFire, "P0 Joystick Fire", 0, true },
|
|
{ Event::JoystickZeroFire5, "P0 Booster Top Trigger", 0, true },
|
|
{ Event::JoystickZeroFire9, "P0 Booster Handle Grip", 0, true },
|
|
|
|
{ Event::JoystickOneUp, "P1 Joystick Up", 0, true },
|
|
{ Event::JoystickOneDown, "P1 Joystick Down", 0, true },
|
|
{ Event::JoystickOneLeft, "P1 Joystick Left", 0, true },
|
|
{ Event::JoystickOneRight, "P1 Joystick Right", 0, true },
|
|
{ Event::JoystickOneFire, "P1 Joystick Fire", 0, true },
|
|
{ Event::JoystickOneFire5, "P1 Booster Top Trigger", 0, true },
|
|
{ Event::JoystickOneFire9, "P1 Booster Handle Grip", 0, true },
|
|
|
|
{ Event::PaddleZeroAnalog, "Paddle 0 Analog", 0, true },
|
|
{ Event::PaddleZeroDecrease, "Paddle 0 Decrease", 0, true },
|
|
{ Event::PaddleZeroIncrease, "Paddle 0 Increase", 0, true },
|
|
{ Event::PaddleZeroFire, "Paddle 0 Fire", 0, true },
|
|
|
|
{ Event::PaddleOneAnalog, "Paddle 1 Analog", 0, true },
|
|
{ Event::PaddleOneDecrease, "Paddle 1 Decrease", 0, true },
|
|
{ Event::PaddleOneIncrease, "Paddle 1 Increase", 0, true },
|
|
{ Event::PaddleOneFire, "Paddle 1 Fire", 0, true },
|
|
|
|
{ Event::PaddleTwoAnalog, "Paddle 2 Analog", 0, true },
|
|
{ Event::PaddleTwoDecrease, "Paddle 2 Decrease", 0, true },
|
|
{ Event::PaddleTwoIncrease, "Paddle 2 Increase", 0, true },
|
|
{ Event::PaddleTwoFire, "Paddle 2 Fire", 0, true },
|
|
|
|
{ Event::PaddleThreeAnalog, "Paddle 3 Analog", 0, true },
|
|
{ Event::PaddleThreeDecrease, "Paddle 3 Decrease", 0, true },
|
|
{ Event::PaddleThreeIncrease, "Paddle 3 Increase", 0, true },
|
|
{ Event::PaddleThreeFire, "Paddle 3 Fire", 0, true },
|
|
|
|
{ Event::KeyboardZero1, "P0 Keyboard 1", 0, true },
|
|
{ Event::KeyboardZero2, "P0 Keyboard 2", 0, true },
|
|
{ Event::KeyboardZero3, "P0 Keyboard 3", 0, true },
|
|
{ Event::KeyboardZero4, "P0 Keyboard 4", 0, true },
|
|
{ Event::KeyboardZero5, "P0 Keyboard 5", 0, true },
|
|
{ Event::KeyboardZero6, "P0 Keyboard 6", 0, true },
|
|
{ Event::KeyboardZero7, "P0 Keyboard 7", 0, true },
|
|
{ Event::KeyboardZero8, "P0 Keyboard 8", 0, true },
|
|
{ Event::KeyboardZero9, "P0 Keyboard 9", 0, true },
|
|
{ Event::KeyboardZeroStar, "P0 Keyboard *", 0, true },
|
|
{ Event::KeyboardZero0, "P0 Keyboard 0", 0, true },
|
|
{ Event::KeyboardZeroPound, "P0 Keyboard #", 0, true },
|
|
|
|
{ Event::KeyboardOne1, "P1 Keyboard 1", 0, true },
|
|
{ Event::KeyboardOne2, "P1 Keyboard 2", 0, true },
|
|
{ Event::KeyboardOne3, "P1 Keyboard 3", 0, true },
|
|
{ Event::KeyboardOne4, "P1 Keyboard 4", 0, true },
|
|
{ Event::KeyboardOne5, "P1 Keyboard 5", 0, true },
|
|
{ Event::KeyboardOne6, "P1 Keyboard 6", 0, true },
|
|
{ Event::KeyboardOne7, "P1 Keyboard 7", 0, true },
|
|
{ Event::KeyboardOne8, "P1 Keyboard 8", 0, true },
|
|
{ Event::KeyboardOne9, "P1 Keyboard 9", 0, true },
|
|
{ Event::KeyboardOneStar, "P1 Keyboard *", 0, true },
|
|
{ Event::KeyboardOne0, "P1 Keyboard 0", 0, true },
|
|
{ Event::KeyboardOnePound, "P1 Keyboard #", 0, true },
|
|
|
|
{ Event::Combo1, "Combo 1", 0, false },
|
|
{ Event::Combo2, "Combo 2", 0, false },
|
|
{ Event::Combo3, "Combo 3", 0, false },
|
|
{ Event::Combo4, "Combo 4", 0, false },
|
|
{ Event::Combo5, "Combo 5", 0, false },
|
|
{ Event::Combo6, "Combo 6", 0, false },
|
|
{ Event::Combo7, "Combo 7", 0, false },
|
|
{ Event::Combo8, "Combo 8", 0, false },
|
|
{ Event::Combo9, "Combo 9", 0, false },
|
|
{ Event::Combo10, "Combo 10", 0, false },
|
|
{ Event::Combo11, "Combo 11", 0, false },
|
|
{ Event::Combo12, "Combo 12", 0, false },
|
|
{ Event::Combo13, "Combo 13", 0, false },
|
|
{ Event::Combo14, "Combo 14", 0, false },
|
|
{ Event::Combo15, "Combo 15", 0, false },
|
|
{ Event::Combo16, "Combo 16", 0, false }
|
|
};
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EventHandler::ActionList EventHandler::ourMenuActionList[kMenuActionListSize] = {
|
|
{ Event::UIUp, "Move Up", 0, false },
|
|
{ Event::UIDown, "Move Down", 0, false },
|
|
{ Event::UILeft, "Move Left", 0, false },
|
|
{ Event::UIRight, "Move Right", 0, false },
|
|
|
|
{ Event::UIHome, "Home", 0, false },
|
|
{ Event::UIEnd, "End", 0, false },
|
|
{ Event::UIPgUp, "Page Up", 0, false },
|
|
{ Event::UIPgDown, "Page Down", 0, false },
|
|
|
|
{ Event::UIOK, "OK", 0, false },
|
|
{ Event::UICancel, "Cancel", 0, false },
|
|
{ Event::UISelect, "Select item", 0, false },
|
|
|
|
{ Event::UINavPrev, "Previous object", 0, false },
|
|
{ Event::UINavNext, "Next object", 0, false },
|
|
|
|
{ Event::UIPrevDir, "Parent directory", 0, 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 }
|
|
};
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EventHandler::StellaJoystick::StellaJoystick()
|
|
: type(JT_NONE),
|
|
name("None"),
|
|
numAxes(0),
|
|
numButtons(0),
|
|
numHats(0),
|
|
axisTable(NULL),
|
|
btnTable(NULL),
|
|
hatTable(NULL),
|
|
axisLastValue(NULL)
|
|
{
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
EventHandler::StellaJoystick::~StellaJoystick()
|
|
{
|
|
delete[] axisTable; axisTable = NULL;
|
|
delete[] btnTable; btnTable = NULL;
|
|
delete[] hatTable; hatTable = NULL;
|
|
delete[] axisLastValue; axisLastValue = NULL;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void EventHandler::StellaJoystick::initialize(const string& desc,
|
|
int axes, int buttons, int hats, int /*balls*/)
|
|
{
|
|
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& m)
|
|
{
|
|
istringstream buf(m);
|
|
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
|
|
IntArray::const_iterator event = map.begin();
|
|
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)
|
|
{
|
|
IntArray::const_iterator event = map.begin();
|
|
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)
|
|
{
|
|
IntArray::const_iterator event = map.begin();
|
|
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)
|
|
{
|
|
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:" << endl << " "
|
|
<< numAxes << " axes, "
|
|
<< numButtons << " buttons, "
|
|
<< numHats << " hats";
|
|
|
|
return buf.str();
|
|
}
|