mirror of https://github.com/stella-emu/stella.git
started adding playback mode (see #678)
This commit is contained in:
parent
031df6aeaf
commit
c3e156f9b5
|
@ -416,6 +416,7 @@ void PhysicalKeyboardHandler::handleEvent(StellaKey key, StellaMod mod, bool pre
|
|||
{
|
||||
case EventHandlerState::EMULATION:
|
||||
case EventHandlerState::PAUSE:
|
||||
case EventHandlerState::PLAYBACK:
|
||||
myHandler.handleEvent(myKeyMap.get(EventMode::kEmulationMode, key, mod), pressed, repeated);
|
||||
break;
|
||||
|
||||
|
@ -563,6 +564,7 @@ PhysicalKeyboardHandler::EventMappingArray PhysicalKeyboardHandler::DefaultCommo
|
|||
{Event::Unwind1Menu, KBDK_RIGHT, MOD3},
|
||||
{Event::Unwind10Menu, KBDK_RIGHT, KBDM_SHIFT | MOD3},
|
||||
{Event::UnwindAllMenu, KBDK_UP, MOD3},
|
||||
{Event::TogglePlayBackMode, KBDK_SPACE, KBDM_SHIFT},
|
||||
|
||||
#if defined(RETRON77)
|
||||
{Event::ConsoleColorToggle, KBDK_F4}, // back ("COLOR","B/W")
|
||||
|
|
|
@ -181,7 +181,8 @@ uInt32 RewindManager::rewindStates(uInt32 numStates)
|
|||
else
|
||||
message = "Rewind not possible";
|
||||
|
||||
if(myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE)
|
||||
if(myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE
|
||||
&& myOSystem.eventHandler().state() != EventHandlerState::PLAYBACK)
|
||||
myOSystem.frameBuffer().showMessage(message);
|
||||
return i;
|
||||
}
|
||||
|
@ -215,7 +216,8 @@ uInt32 RewindManager::unwindStates(uInt32 numStates)
|
|||
else
|
||||
message = "Unwind not possible";
|
||||
|
||||
if(myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE)
|
||||
if(myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE
|
||||
&& myOSystem.eventHandler().state() != EventHandlerState::PLAYBACK)
|
||||
myOSystem.frameBuffer().showMessage(message);
|
||||
return i;
|
||||
}
|
||||
|
|
|
@ -125,6 +125,7 @@ class Event
|
|||
ToggleAdaptRefresh, PreviousMultiCartRom,
|
||||
// add new events from here to avoid that user remapped events get overwritten
|
||||
PreviousSettingGroup, NextSettingGroup,
|
||||
TogglePlayBackMode,
|
||||
|
||||
LastType
|
||||
};
|
||||
|
|
|
@ -1283,6 +1283,10 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated)
|
|||
if (pressed && !repeated) changeStateByEvent(Event::TimeMachineMode);
|
||||
return;
|
||||
|
||||
case EventHandlerState::PLAYBACK:
|
||||
if (pressed && !repeated) changeStateByEvent(Event::TogglePlayBackMode);
|
||||
return;
|
||||
|
||||
// this event is called when exiting a ROM from the debugger, so it acts like pressing ESC in emulation
|
||||
case EventHandlerState::EMULATION:
|
||||
case EventHandlerState::DEBUGGER:
|
||||
|
@ -1556,7 +1560,7 @@ bool EventHandler::changeStateByEvent(Event::Type type)
|
|||
switch(type)
|
||||
{
|
||||
case Event::TogglePauseMode:
|
||||
if(myState == EventHandlerState::EMULATION)
|
||||
if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PLAYBACK)
|
||||
setState(EventHandlerState::PAUSE);
|
||||
else if(myState == EventHandlerState::PAUSE)
|
||||
setState(EventHandlerState::EMULATION);
|
||||
|
@ -1565,14 +1569,16 @@ bool EventHandler::changeStateByEvent(Event::Type type)
|
|||
break;
|
||||
|
||||
case Event::OptionsMenuMode:
|
||||
if (myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE)
|
||||
if (myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE
|
||||
|| myState == EventHandlerState::PLAYBACK)
|
||||
enterMenuMode(EventHandlerState::OPTIONSMENU);
|
||||
else
|
||||
handled = false;
|
||||
break;
|
||||
|
||||
case Event::CmdMenuMode:
|
||||
if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE)
|
||||
if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE
|
||||
|| myState == EventHandlerState::PLAYBACK)
|
||||
enterMenuMode(EventHandlerState::CMDMENU);
|
||||
else if(myState == EventHandlerState::CMDMENU && !myOSystem.settings().getBool("minimal_ui"))
|
||||
// The extra check for "minimal_ui" allows mapping e.g. right joystick fire
|
||||
|
@ -1583,7 +1589,8 @@ bool EventHandler::changeStateByEvent(Event::Type type)
|
|||
break;
|
||||
|
||||
case Event::TimeMachineMode:
|
||||
if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE)
|
||||
if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE
|
||||
|| myState == EventHandlerState::PLAYBACK)
|
||||
enterTimeMachineMenuMode(0, false);
|
||||
else if(myState == EventHandlerState::TIMEMACHINE)
|
||||
leaveMenuMode();
|
||||
|
@ -1591,10 +1598,24 @@ bool EventHandler::changeStateByEvent(Event::Type type)
|
|||
handled = false;
|
||||
break;
|
||||
|
||||
case Event::TogglePlayBackMode:
|
||||
if (myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE
|
||||
|| myState == EventHandlerState::TIMEMACHINE)
|
||||
enterPlayBackMode();
|
||||
else if (myState == EventHandlerState::PLAYBACK)
|
||||
#ifdef GUI_SUPPORT
|
||||
enterMenuMode(EventHandlerState::TIMEMACHINE);
|
||||
#else
|
||||
setState(EventHandlerState::PAUSE);
|
||||
#endif
|
||||
else
|
||||
handled = false;
|
||||
break;
|
||||
|
||||
case Event::DebuggerMode:
|
||||
#ifdef DEBUGGER_SUPPORT
|
||||
if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE
|
||||
|| myState == EventHandlerState::TIMEMACHINE)
|
||||
|| myState == EventHandlerState::TIMEMACHINE || myState == EventHandlerState::PLAYBACK)
|
||||
enterDebugMode();
|
||||
else if(myState == EventHandlerState::DEBUGGER && myOSystem.debugger().canExit())
|
||||
leaveDebugMode();
|
||||
|
@ -2248,6 +2269,15 @@ void EventHandler::enterTimeMachineMenuMode(uInt32 numWinds, bool unwind)
|
|||
#endif
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void EventHandler::enterPlayBackMode()
|
||||
{
|
||||
#ifdef GUI_SUPPORT
|
||||
setState(EventHandlerState::PLAYBACK);
|
||||
myOSystem.sound().mute(true); // sound does not work in playback mode
|
||||
#endif
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void EventHandler::setState(EventHandlerState state)
|
||||
{
|
||||
|
@ -2263,6 +2293,7 @@ void EventHandler::setState(EventHandlerState state)
|
|||
switch(myState)
|
||||
{
|
||||
case EventHandlerState::EMULATION:
|
||||
case EventHandlerState::PLAYBACK:
|
||||
myOSystem.sound().mute(false);
|
||||
enableTextEvents(false);
|
||||
break;
|
||||
|
@ -2541,6 +2572,7 @@ EventHandler::EmulActionList EventHandler::ourEmulActionList = { {
|
|||
{ Event::Unwind1Menu, "Unwind one state & enter TM UI", "" },
|
||||
{ Event::Unwind10Menu, "Unwind 10 states & enter TM UI", "" },
|
||||
{ Event::UnwindAllMenu, "Unwind all states & enter TM UI", "" },
|
||||
{ Event::TogglePlayBackMode, "Toggle 'Time Machine' playback mode", "" },
|
||||
|
||||
{ Event::Combo1, "Combo 1", "" },
|
||||
{ Event::Combo2, "Combo 2", "" },
|
||||
|
@ -2628,6 +2660,7 @@ const Event::EventSet EventHandler::StateEvents = {
|
|||
Event::TimeMachineMode, Event::RewindPause, Event::UnwindPause, Event::ToggleTimeMachine,
|
||||
Event::Rewind1Menu, Event::Rewind10Menu, Event::RewindAllMenu,
|
||||
Event::Unwind1Menu, Event::Unwind10Menu, Event::UnwindAllMenu,
|
||||
Event::TogglePlayBackMode,
|
||||
Event::SaveAllStates, Event::LoadAllStates, Event::ToggleAutoSlot,
|
||||
};
|
||||
|
||||
|
|
|
@ -136,6 +136,7 @@ class EventHandler
|
|||
bool enterDebugMode();
|
||||
void leaveDebugMode();
|
||||
void enterTimeMachineMenuMode(uInt32 numWinds, bool unwind);
|
||||
void enterPlayBackMode();
|
||||
|
||||
/**
|
||||
Send an event directly to the event handler.
|
||||
|
@ -557,7 +558,7 @@ class EventHandler
|
|||
#else
|
||||
REFRESH_SIZE = 0,
|
||||
#endif
|
||||
EMUL_ACTIONLIST_SIZE = 159 + PNG_SIZE + COMBO_SIZE + REFRESH_SIZE,
|
||||
EMUL_ACTIONLIST_SIZE = 160 + PNG_SIZE + COMBO_SIZE + REFRESH_SIZE,
|
||||
MENU_ACTIONLIST_SIZE = 18
|
||||
;
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
enum class EventHandlerState {
|
||||
EMULATION,
|
||||
TIMEMACHINE,
|
||||
PLAYBACK,
|
||||
PAUSE,
|
||||
LAUNCHER,
|
||||
OPTIONSMENU,
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "FBSurface.hxx"
|
||||
#include "TIASurface.hxx"
|
||||
#include "FrameBuffer.hxx"
|
||||
#include "StateManager.hxx"
|
||||
#include "RewindManager.hxx"
|
||||
|
||||
#ifdef DEBUGGER_SUPPORT
|
||||
#include "Debugger.hxx"
|
||||
|
@ -422,6 +424,44 @@ void FrameBuffer::update(bool force)
|
|||
break; // EventHandlerState::TIMEMACHINE
|
||||
}
|
||||
|
||||
case EventHandlerState::PLAYBACK:
|
||||
{
|
||||
static Int32 frames = 0;
|
||||
RewindManager& r = myOSystem.state().rewindManager();
|
||||
bool success = true;
|
||||
Int64 frameCycles = 76 * std::max<Int32>(myOSystem.console().tia().scanlinesLastFrame(), 240);
|
||||
|
||||
if(--frames <= 0)
|
||||
{
|
||||
r.unwindStates(1);
|
||||
// get time between current and next state
|
||||
uInt64 startCycles = r.getCurrentCycles();
|
||||
success = r.unwindStates(1);
|
||||
// display larger state gaps faster
|
||||
frames = std::sqrt((myOSystem.console().tia().cycles() - startCycles) / frameCycles);
|
||||
|
||||
if(success)
|
||||
r.rewindStates(1);
|
||||
}
|
||||
|
||||
force = force || success;
|
||||
if (force)
|
||||
myTIASurface->render();
|
||||
|
||||
// Stop playback mode at the end of the state buffer
|
||||
// and switch to Time Machine or Pause mode
|
||||
if (!success)
|
||||
{
|
||||
frames = 0;
|
||||
#ifdef GUI_SUPPORT
|
||||
myOSystem.eventHandler().enterMenuMode(EventHandlerState::TIMEMACHINE);
|
||||
#else
|
||||
myOSystem.eventHandler().changeStateByEvent(Event::TogglePauseMode);
|
||||
#endif
|
||||
}
|
||||
break; // EventHandlerState::PLAYBACK
|
||||
}
|
||||
|
||||
case EventHandlerState::LAUNCHER:
|
||||
{
|
||||
force = force || myOSystem.launcher().needsRedraw();
|
||||
|
|
|
@ -343,6 +343,7 @@ FBInitStatus OSystem::createFrameBuffer()
|
|||
case EventHandlerState::OPTIONSMENU:
|
||||
case EventHandlerState::CMDMENU:
|
||||
case EventHandlerState::TIMEMACHINE:
|
||||
case EventHandlerState::PLAYBACK:
|
||||
#endif
|
||||
if((fbstatus = myConsole->initializeVideo()) != FBInitStatus::Success)
|
||||
return fbstatus;
|
||||
|
|
|
@ -69,22 +69,23 @@ static constexpr std::array<uInt32, BUTTON_H> STOP = {
|
|||
0b11111111111111,
|
||||
0b11111111111111
|
||||
};
|
||||
static constexpr std::array<uInt32, BUTTON_H> PLAY = {
|
||||
0b11000000000000,
|
||||
0b11110000000000,
|
||||
0b11111100000000,
|
||||
0b11111111000000,
|
||||
0b11111111110000,
|
||||
0b11111111111100,
|
||||
0b11111111111111,
|
||||
0b11111111111111,
|
||||
0b11111111111100,
|
||||
0b11111111110000,
|
||||
0b11111111000000,
|
||||
0b11111100000000,
|
||||
0b11110000000000,
|
||||
0b11000000000000
|
||||
static constexpr std::array<uInt32, BUTTON_H> EXIT = {
|
||||
0b01100000000110,
|
||||
0b11110000001111,
|
||||
0b11111000011111,
|
||||
0b01111100111110,
|
||||
0b00111111111100,
|
||||
0b00011111111000,
|
||||
0b00001111110000,
|
||||
0b00001111110000,
|
||||
0b00011111111000,
|
||||
0b00111111111100,
|
||||
0b01111100111110,
|
||||
0b11111000011111,
|
||||
0b11110000001111,
|
||||
0b01100000000110
|
||||
};
|
||||
|
||||
static constexpr std::array<uInt32, BUTTON_H> REWIND_ALL = {
|
||||
0,
|
||||
0b11000011000011,
|
||||
|
@ -117,6 +118,22 @@ static constexpr std::array<uInt32, BUTTON_H> REWIND_1 = {
|
|||
0b00000110001110,
|
||||
0
|
||||
};
|
||||
static constexpr std::array<uInt32, BUTTON_H> PLAYBACK = {
|
||||
0b11000000000000,
|
||||
0b11110000000000,
|
||||
0b11111100000000,
|
||||
0b11111111000000,
|
||||
0b11111111110000,
|
||||
0b11111111111100,
|
||||
0b11111111111111,
|
||||
0b11111111111111,
|
||||
0b11111111111100,
|
||||
0b11111111110000,
|
||||
0b11111111000000,
|
||||
0b11111100000000,
|
||||
0b11110000000000,
|
||||
0b11000000000000
|
||||
};
|
||||
static constexpr std::array<uInt32, BUTTON_H> UNWIND_1 = {
|
||||
0,
|
||||
0b01110001100000,
|
||||
|
@ -235,8 +252,8 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent,
|
|||
STOP.data(), BUTTON_W, BUTTON_H, kToggle);
|
||||
xpos += buttonWidth + BUTTON_GAP;
|
||||
|
||||
myPlayWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
||||
PLAY.data(), BUTTON_W, BUTTON_H, kPlay);
|
||||
myExitWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
||||
EXIT.data(), BUTTON_W, BUTTON_H, kExit);
|
||||
xpos += buttonWidth + BUTTON_GAP * 4;
|
||||
|
||||
myRewindAllWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
||||
|
@ -247,6 +264,10 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent,
|
|||
REWIND_1.data(), BUTTON_W, BUTTON_H, kRewind1, true);
|
||||
xpos += buttonWidth + BUTTON_GAP;
|
||||
|
||||
myPlayBackWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
||||
PLAYBACK.data(), BUTTON_W, BUTTON_H, kPlayBack);
|
||||
xpos += buttonWidth + BUTTON_GAP;
|
||||
|
||||
myUnwind1Widget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight,
|
||||
UNWIND_1.data(), BUTTON_W, BUTTON_H, kUnwind1, true);
|
||||
xpos += buttonWidth + BUTTON_GAP;
|
||||
|
@ -331,14 +352,33 @@ void TimeMachineDialog::handleKeyDown(StellaKey key, StellaMod mod, bool repeate
|
|||
break;
|
||||
|
||||
case Event::ExitMode:
|
||||
handleCommand(nullptr, kPlay, 0, 0);
|
||||
handleCommand(nullptr, kExit, 0, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
Dialog::handleKeyDown(key, mod);
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void TimeMachineDialog::handleKeyUp(StellaKey key, StellaMod mod)
|
||||
{
|
||||
// The following shortcuts duplicate the shortcuts in EventHandler
|
||||
// Note: mode switches must happen in key UP
|
||||
|
||||
Event::Type event = instance().eventHandler().eventForKey(EventMode::kEmulationMode, key, mod);
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case Event::TogglePlayBackMode:
|
||||
handleCommand(nullptr, kPlayBack, 0, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (key == KBDK_SPACE)
|
||||
handleCommand(nullptr, kPlay, 0, 0);
|
||||
handleCommand(nullptr, kPlayBack, 0, 0);
|
||||
else
|
||||
Dialog::handleKeyDown(key, mod);
|
||||
Dialog::handleKeyUp(key, mod);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,7 +401,7 @@ void TimeMachineDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
handleToggle();
|
||||
break;
|
||||
|
||||
case kPlay:
|
||||
case kExit:
|
||||
instance().eventHandler().leaveMenuMode();
|
||||
break;
|
||||
|
||||
|
@ -377,6 +417,10 @@ void TimeMachineDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
handleWinds(-1000);
|
||||
break;
|
||||
|
||||
case kPlayBack:
|
||||
instance().eventHandler().enterPlayBackMode();
|
||||
break;
|
||||
|
||||
case kUnwind1:
|
||||
handleWinds(1);
|
||||
break;
|
||||
|
@ -479,6 +523,7 @@ void TimeMachineDialog::handleWinds(Int32 numWinds)
|
|||
// Enable/disable buttons
|
||||
myRewindAllWidget->setEnabled(!r.atFirst());
|
||||
myRewind1Widget->setEnabled(!r.atFirst());
|
||||
myPlayBackWidget->setEnabled(!r.atLast());
|
||||
myUnwindAllWidget->setEnabled(!r.atLast());
|
||||
myUnwind1Widget->setEnabled(!r.atLast());
|
||||
mySaveAllWidget->setEnabled(r.getLastIdx() != 0);
|
||||
|
|
|
@ -38,6 +38,7 @@ class TimeMachineDialog : public Dialog
|
|||
private:
|
||||
void loadConfig() override;
|
||||
void handleKeyDown(StellaKey key, StellaMod mod, bool repeated) override;
|
||||
void handleKeyUp(StellaKey key, StellaMod mod) override;
|
||||
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
|
||||
|
||||
/** initialize timeline bar */
|
||||
|
@ -58,7 +59,8 @@ class TimeMachineDialog : public Dialog
|
|||
{
|
||||
kTimeline = 'TMtl',
|
||||
kToggle = 'TMtg',
|
||||
kPlay = 'TMpl',
|
||||
kExit = 'TMex',
|
||||
kPlayBack = 'TMpb',
|
||||
kRewindAll = 'TMra',
|
||||
kRewind10 = 'TMr1',
|
||||
kRewind1 = 'TMre',
|
||||
|
@ -73,7 +75,8 @@ class TimeMachineDialog : public Dialog
|
|||
TimeLineWidget* myTimeline{nullptr};
|
||||
|
||||
ButtonWidget* myToggleWidget{nullptr};
|
||||
ButtonWidget* myPlayWidget{nullptr};
|
||||
ButtonWidget* myExitWidget{ nullptr };
|
||||
ButtonWidget* myPlayBackWidget{nullptr};
|
||||
ButtonWidget* myRewindAllWidget{nullptr};
|
||||
ButtonWidget* myRewind1Widget{nullptr};
|
||||
ButtonWidget* myUnwind1Widget{nullptr};
|
||||
|
|
Loading…
Reference in New Issue