started adding playback mode (see #678)

This commit is contained in:
thrust26 2020-07-23 12:39:22 +02:00
parent 031df6aeaf
commit c3e156f9b5
10 changed files with 160 additions and 31 deletions

View File

@ -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")

View File

@ -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;
}

View File

@ -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
};

View File

@ -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,
};

View File

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

View File

@ -22,6 +22,7 @@
enum class EventHandlerState {
EMULATION,
TIMEMACHINE,
PLAYBACK,
PAUSE,
LAUNCHER,
OPTIONSMENU,

View File

@ -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();

View File

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

View File

@ -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);

View File

@ -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};