mirror of https://github.com/stella-emu/stella.git
Merge branch 'release/5.1'
This commit is contained in:
commit
87c59db4e0
2
Makefile
2
Makefile
|
@ -56,7 +56,7 @@ endif
|
|||
ifdef CLANG_WARNINGS
|
||||
CXXFLAGS+= -Weverything -Wno-c++17-extensions -Wno-c++98-compat -Wno-c++98-compat-pedantic \
|
||||
-Wno-double-promotion -Wno-switch-enum -Wno-conversion -Wno-covered-switch-default \
|
||||
-Wno-inconsistent-missing-destructor-override \
|
||||
-Wno-inconsistent-missing-destructor-override -Wno-float-equal \
|
||||
-Wno-exit-time-destructors -Wno-global-constructors -Wno-weak-vtables \
|
||||
-Wno-four-char-constants -Wno-padded
|
||||
endif
|
||||
|
|
|
@ -3191,8 +3191,7 @@
|
|||
going back further in time. To reach the horizon, save states
|
||||
will be compressed (*). This means that more and more intermediate
|
||||
states will be removed and the interval between save states
|
||||
becomes larger the further they are back in time. The very first
|
||||
save state will not be removed.<br>
|
||||
becomes larger the further they are back in time.<br>
|
||||
(*) Compresion only works if 'Uncompressed size' is smaller than
|
||||
'Buffer size'.
|
||||
</td>
|
||||
|
|
|
@ -129,6 +129,12 @@ class LinkedObjectPool
|
|||
*/
|
||||
const_iter next(const_iter i) const { return std::next(i, 1); }
|
||||
|
||||
/**
|
||||
Canonical iterators from C++ STL.
|
||||
*/
|
||||
const_iter cbegin() const { return myList.cbegin(); }
|
||||
const_iter cend() const { return myList.cend(); }
|
||||
|
||||
/**
|
||||
Answer whether 'current' is at the specified iterator.
|
||||
*/
|
||||
|
|
|
@ -42,7 +42,7 @@ void RewindManager::setup()
|
|||
|
||||
mySize = myOSystem.settings().getInt(prefix + "tm.size");
|
||||
if(mySize != myStateList.capacity())
|
||||
myStateList.resize(mySize);
|
||||
resize(mySize);
|
||||
|
||||
myUncompressed = myOSystem.settings().getInt(prefix + "tm.uncompressed");
|
||||
|
||||
|
@ -56,7 +56,7 @@ void RewindManager::setup()
|
|||
if(HOR_SETTINGS[i] == myOSystem.settings().getString(prefix + "tm.horizon"))
|
||||
myHorizon = HORIZON_CYCLES[i];
|
||||
|
||||
// calc interval growth factor
|
||||
// calc interval growth factor for compression
|
||||
// this factor defines the backward horizon
|
||||
const double MAX_FACTOR = 1E8;
|
||||
double minFactor = 0, maxFactor = MAX_FACTOR;
|
||||
|
@ -71,8 +71,8 @@ void RewindManager::setup()
|
|||
// horizon not reachable?
|
||||
if(myFactor == MAX_FACTOR)
|
||||
break;
|
||||
// sum up interval cycles (first and last state are not compressed)
|
||||
for(uInt32 i = myUncompressed + 1; i < mySize - 1; ++i)
|
||||
// sum up interval cycles (first state is not compressed)
|
||||
for(uInt32 i = myUncompressed + 1; i < mySize; ++i)
|
||||
{
|
||||
interval *= myFactor;
|
||||
cycleSum += interval;
|
||||
|
@ -88,7 +88,6 @@ void RewindManager::setup()
|
|||
else
|
||||
maxFactor = myFactor;
|
||||
}
|
||||
//cerr << "factor " << myFactor << endl;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -138,7 +137,7 @@ bool RewindManager::addState(const string& message, bool timeMachine)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 RewindManager::rewindState(uInt32 numStates)
|
||||
uInt32 RewindManager::rewindStates(uInt32 numStates)
|
||||
{
|
||||
uInt64 startCycles = myOSystem.console().tia().cycles();
|
||||
uInt32 i;
|
||||
|
@ -146,7 +145,7 @@ uInt32 RewindManager::rewindState(uInt32 numStates)
|
|||
|
||||
for(i = 0; i < numStates; ++i)
|
||||
{
|
||||
if(!atFirst())
|
||||
if(!atFirst())
|
||||
{
|
||||
if(!myLastTimeMachineAdd)
|
||||
// Set internal current iterator to previous state (back in time),
|
||||
|
@ -177,7 +176,7 @@ uInt32 RewindManager::rewindState(uInt32 numStates)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 RewindManager::unwindState(uInt32 numStates)
|
||||
uInt32 RewindManager::unwindStates(uInt32 numStates)
|
||||
{
|
||||
uInt64 startCycles = myOSystem.console().tia().cycles();
|
||||
uInt32 i;
|
||||
|
@ -210,44 +209,48 @@ uInt32 RewindManager::unwindState(uInt32 numStates)
|
|||
return i;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 RewindManager::windStates(uInt32 numStates, bool unwind)
|
||||
{
|
||||
if(unwind)
|
||||
return unwindStates(numStates);
|
||||
else
|
||||
return rewindStates(numStates);
|
||||
}
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void RewindManager::compressStates()
|
||||
{
|
||||
double expectedCycles = myInterval * myFactor * (1 + myFactor);
|
||||
double maxError = 1.5;
|
||||
uInt32 idx = myStateList.size() - 2;
|
||||
//uInt32 removeIdx = 0;
|
||||
// in case maxError is <= 1.5 remove first state by default:
|
||||
Common::LinkedObjectPool<RewindState>::const_iter removeIter = myStateList.first();
|
||||
if(myUncompressed < mySize)
|
||||
/*if(myUncompressed < mySize)
|
||||
// if compression is enabled, the first but one state is removed by default:
|
||||
removeIter++;
|
||||
removeIter++;*/
|
||||
|
||||
//cerr << "idx: " << idx << endl;
|
||||
// iterate from last but one to first but one
|
||||
for(auto it = myStateList.previous(myStateList.last()); it != myStateList.first(); --it)
|
||||
{
|
||||
if(idx < mySize - myUncompressed)
|
||||
{
|
||||
//cerr << *it << endl << endl; // debug code
|
||||
expectedCycles *= myFactor;
|
||||
|
||||
uInt64 prevCycles = myStateList.previous(it)->cycles;
|
||||
uInt64 nextCycles = myStateList.next(it)->cycles;
|
||||
double error = expectedCycles / (nextCycles - prevCycles);
|
||||
//cerr << "prevCycles: " << prevCycles << ", nextCycles: " << nextCycles << ", error: " << error << endl;
|
||||
|
||||
if(error > maxError)
|
||||
{
|
||||
maxError = error;
|
||||
removeIter = it;
|
||||
//removeIdx = idx;
|
||||
}
|
||||
}
|
||||
--idx;
|
||||
}
|
||||
myStateList.remove(removeIter); // remove
|
||||
//cerr << "remove " << removeIdx << endl;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -295,7 +298,7 @@ string RewindManager::getUnitString(Int64 cycles)
|
|||
{
|
||||
// use the lower unit up to twice the nextCycles unit, except for an exact match of the nextCycles unit
|
||||
// TODO: does the latter make sense, e.g. for ROMs with changing scanlines?
|
||||
if(cycles < UNIT_CYCLES[i + 1] * 2 && cycles % UNIT_CYCLES[i + 1] != 0)
|
||||
if(cycles == 0 || cycles < UNIT_CYCLES[i + 1] * 2 && cycles % UNIT_CYCLES[i + 1] != 0)
|
||||
break;
|
||||
}
|
||||
result << cycles / UNIT_CYCLES[i] << " " << UNIT_NAMES[i];
|
||||
|
@ -306,14 +309,14 @@ string RewindManager::getUnitString(Int64 cycles)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 RewindManager::getFirstCycles()
|
||||
uInt32 RewindManager::getFirstCycles() const
|
||||
{
|
||||
// TODO: check if valid
|
||||
return Common::LinkedObjectPool<RewindState>::const_iter(myStateList.first())->cycles;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 RewindManager::getCurrentCycles()
|
||||
uInt32 RewindManager::getCurrentCycles() const
|
||||
{
|
||||
if(myStateList.currentIsValid())
|
||||
return myStateList.current().cycles;
|
||||
|
@ -322,9 +325,20 @@ uInt32 RewindManager::getCurrentCycles()
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 RewindManager::getLastCycles()
|
||||
uInt32 RewindManager::getLastCycles() const
|
||||
{
|
||||
// TODO: check if valid
|
||||
return Common::LinkedObjectPool<RewindState>::const_iter(myStateList.last())->cycles;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
IntArray RewindManager::cyclesList() const
|
||||
{
|
||||
IntArray arr;
|
||||
|
||||
uInt64 firstCycle = getFirstCycles();
|
||||
for(auto it = myStateList.cbegin(); it != myStateList.cend(); ++it)
|
||||
arr.push_back(uInt32(it->cycles - firstCycle));
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
|
|
@ -38,6 +38,9 @@ class StateManager;
|
|||
to the end of the list (aka, all future states) are removed, and the internal
|
||||
iterator moves to the insertion point of the data (the end of the list).
|
||||
|
||||
If the list is full, states are either removed at the beginning (compression
|
||||
off) or at selective positions (compression on).
|
||||
|
||||
@author Stephen Anthony
|
||||
*/
|
||||
class RewindManager
|
||||
|
@ -106,22 +109,32 @@ class RewindManager
|
|||
bool addState(const string& message, bool timeMachine = false);
|
||||
|
||||
/**
|
||||
Rewind one level of the state list, and display the message associated
|
||||
Rewind numStates levels of the state list, and display the message associated
|
||||
with that state.
|
||||
|
||||
@param numStates Number of states to rewind
|
||||
@return Number of states to rewinded
|
||||
*/
|
||||
uInt32 rewindState(uInt32 numStates = 1);
|
||||
uInt32 rewindStates(uInt32 numStates = 1);
|
||||
|
||||
/**
|
||||
Unwind one level of the state list, and display the message associated
|
||||
Unwind numStates levels of the state list, and display the message associated
|
||||
with that state.
|
||||
|
||||
@param numStates Number of states to unwind
|
||||
@return Number of states to unwinded
|
||||
*/
|
||||
uInt32 unwindState(uInt32 numStates = 1);
|
||||
uInt32 unwindStates(uInt32 numStates = 1);
|
||||
|
||||
/**
|
||||
Rewind/unwind numStates levels of the state list, and display the message associated
|
||||
with that state.
|
||||
|
||||
@param numStates Number of states to wind
|
||||
@param unwind unwind or rewind
|
||||
@return Number of states to winded
|
||||
*/
|
||||
uInt32 windStates(uInt32 numStates, bool unwind);
|
||||
|
||||
bool atFirst() const { return myStateList.atFirst(); }
|
||||
bool atLast() const { return myStateList.atLast(); }
|
||||
|
@ -136,9 +149,15 @@ class RewindManager
|
|||
uInt32 getCurrentIdx() { return myStateList.currentIdx(); }
|
||||
uInt32 getLastIdx() { return myStateList.size(); }
|
||||
|
||||
uInt32 getFirstCycles();
|
||||
uInt32 getCurrentCycles();
|
||||
uInt32 getLastCycles();
|
||||
uInt32 getFirstCycles() const;
|
||||
uInt32 getCurrentCycles() const;
|
||||
uInt32 getLastCycles() const;
|
||||
|
||||
/**
|
||||
Get a collection of cycle timestamps, offset from the first one in
|
||||
the list. This also determines the number of states in the list.
|
||||
*/
|
||||
IntArray cyclesList() const;
|
||||
|
||||
private:
|
||||
OSystem& myOSystem;
|
||||
|
|
|
@ -142,17 +142,35 @@ void StateManager::toggleTimeMachine()
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool StateManager::rewindState(uInt32 numStates)
|
||||
bool StateManager::addExtraState(const string& message)
|
||||
{
|
||||
RewindManager& r = myOSystem.state().rewindManager();
|
||||
return r.rewindState(numStates);
|
||||
if(myActiveMode == Mode::TimeMachine)
|
||||
{
|
||||
RewindManager& r = myOSystem.state().rewindManager();
|
||||
return r.addState(message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool StateManager::unwindState(uInt32 numStates)
|
||||
bool StateManager::rewindStates(uInt32 numStates)
|
||||
{
|
||||
RewindManager& r = myOSystem.state().rewindManager();
|
||||
return r.unwindState(numStates);
|
||||
return r.rewindStates(numStates);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool StateManager::unwindStates(uInt32 numStates)
|
||||
{
|
||||
RewindManager& r = myOSystem.state().rewindManager();
|
||||
return r.unwindStates(numStates);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool StateManager::windStates(uInt32 numStates, bool unwind)
|
||||
{
|
||||
RewindManager& r = myOSystem.state().rewindManager();
|
||||
return r.windStates(numStates, unwind);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -72,14 +72,25 @@ class StateManager
|
|||
void setRewindMode(Mode mode) { myActiveMode = mode; }
|
||||
|
||||
/**
|
||||
Rewinds one state; this uses the RewindManager for its functionality.
|
||||
Optionally adds one extra state when entering the Time Machine dialog;
|
||||
this uses the RewindManager for its functionality.
|
||||
*/
|
||||
bool rewindState(uInt32 numStates = 1);
|
||||
bool addExtraState(const string& message);
|
||||
|
||||
/**
|
||||
Unwinds one state; this uses the RewindManager for its functionality.
|
||||
Rewinds states; this uses the RewindManager for its functionality.
|
||||
*/
|
||||
bool unwindState(uInt32 numStates = 1);
|
||||
bool rewindStates(uInt32 numStates = 1);
|
||||
|
||||
/**
|
||||
Unwinds states; this uses the RewindManager for its functionality.
|
||||
*/
|
||||
bool unwindStates(uInt32 numStates = 1);
|
||||
|
||||
/**
|
||||
Rewinds/unwinds states; this uses the RewindManager for its functionality.
|
||||
*/
|
||||
bool windStates(uInt32 numStates, bool unwind);
|
||||
|
||||
/**
|
||||
Updates the state of the system based on the currently active mode.
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#ifndef VERSION_HXX
|
||||
#define VERSION_HXX
|
||||
|
||||
#define STELLA_VERSION "5.1_a2"
|
||||
#define STELLA_VERSION "5.1_b1"
|
||||
#define STELLA_BUILD "3826"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -538,7 +538,7 @@ uInt16 Debugger::windStates(uInt16 numStates, bool unwind, string& message)
|
|||
unlockBankswitchState();
|
||||
|
||||
uInt64 startCycles = myOSystem.console().tia().cycles();
|
||||
uInt16 winds = unwind ? r.unwindState(numStates) : r.rewindState(numStates);
|
||||
uInt16 winds = r.windStates(numStates, unwind);
|
||||
message = r.getUnitString(myOSystem.console().tia().cycles() - startCycles);
|
||||
|
||||
lockBankswitchState();
|
||||
|
@ -620,7 +620,8 @@ void Debugger::setStartState()
|
|||
// Save initial state and add it to the rewind list (except when in currently rewinding)
|
||||
RewindManager& r = myOSystem.state().rewindManager();
|
||||
// avoid invalidating future states when entering the debugger e.g. during rewind
|
||||
if(myOSystem.eventHandler().state() == EventHandlerState::EMULATION)
|
||||
if(r.atLast() && (myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE
|
||||
|| myOSystem.state().mode() == StateManager::Mode::Off))
|
||||
addState("enter debugger");
|
||||
else
|
||||
updateRewindbuttons(r);
|
||||
|
|
|
@ -93,11 +93,39 @@ void DebuggerDialog::handleKeyDown(StellaKey key, StellaMod mod)
|
|||
else if(key == KBDK_F12)
|
||||
{
|
||||
instance().debugger().parser().run("savesnap");
|
||||
return;
|
||||
}
|
||||
else if(StellaModTest::isAlt(mod) && !StellaModTest::isControl(mod))
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
case KBDK_LEFT: // Alt-left(-shift) rewinds 1(10) states
|
||||
if(StellaModTest::isShift(mod))
|
||||
doRewind10();
|
||||
else
|
||||
doRewind();
|
||||
return;
|
||||
case KBDK_RIGHT: // Alt-right(-shift) unwinds 1(10) states
|
||||
if(StellaModTest::isShift(mod))
|
||||
doUnwind10();
|
||||
else
|
||||
doUnwind();
|
||||
return;
|
||||
case KBDK_DOWN: // Alt-down rewinds to start of list
|
||||
doRewindAll();
|
||||
return;
|
||||
case KBDK_UP: // Alt-up rewinds to end of list
|
||||
doUnwindAll();
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(StellaModTest::isControl(mod))
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
#if 0
|
||||
case KBDK_R:
|
||||
if(StellaModTest::isAlt(mod))
|
||||
doRewindAll();
|
||||
|
@ -105,7 +133,7 @@ void DebuggerDialog::handleKeyDown(StellaKey key, StellaMod mod)
|
|||
doRewind10();
|
||||
else
|
||||
doRewind();
|
||||
break;
|
||||
return;
|
||||
case KBDK_Y:
|
||||
if(StellaModTest::isAlt(mod))
|
||||
doUnwindAll();
|
||||
|
@ -113,19 +141,20 @@ void DebuggerDialog::handleKeyDown(StellaKey key, StellaMod mod)
|
|||
doUnwind10();
|
||||
else
|
||||
doUnwind();
|
||||
break;
|
||||
return;
|
||||
#endif
|
||||
case KBDK_S:
|
||||
doStep();
|
||||
break;
|
||||
return;
|
||||
case KBDK_T:
|
||||
doTrace();
|
||||
break;
|
||||
return;
|
||||
case KBDK_L:
|
||||
doScanlineAdvance();
|
||||
break;
|
||||
return;
|
||||
case KBDK_F:
|
||||
doAdvance();
|
||||
break;
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -294,33 +294,25 @@ void EventHandler::handleKeyEvent(StellaKey key, StellaMod mod, bool state)
|
|||
{
|
||||
myOSystem.frameBuffer().toggleFullscreen();
|
||||
}
|
||||
// state rewinding must work in pause mode too
|
||||
// State rewinding must work in pause mode too
|
||||
else if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE)
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
case KBDK_LEFT: // Alt-left(-shift) rewinds 1(10) states
|
||||
myOSystem.frameBuffer().setPauseDelay();
|
||||
setEventState(EventHandlerState::PAUSE);
|
||||
myOSystem.state().rewindState((StellaModTest::isShift(mod) && state) ? 10 : 1);
|
||||
enterTimeMachineMenuMode((StellaModTest::isShift(mod) && state) ? 10 : 1, false);
|
||||
break;
|
||||
|
||||
case KBDK_RIGHT: // Alt-right(-shift) unwinds 1(10) states
|
||||
myOSystem.frameBuffer().setPauseDelay();
|
||||
setEventState(EventHandlerState::PAUSE);
|
||||
myOSystem.state().unwindState((StellaModTest::isShift(mod) && state) ? 10 : 1);
|
||||
enterTimeMachineMenuMode((StellaModTest::isShift(mod) && state) ? 10 : 1, true);
|
||||
break;
|
||||
|
||||
case KBDK_DOWN: // Alt-down rewinds to start of list
|
||||
myOSystem.frameBuffer().setPauseDelay();
|
||||
setEventState(EventHandlerState::PAUSE);
|
||||
myOSystem.state().rewindState(1000);
|
||||
enterTimeMachineMenuMode(1000, false);
|
||||
break;
|
||||
|
||||
case KBDK_UP: // Alt-up rewinds to end of list
|
||||
myOSystem.frameBuffer().setPauseDelay();
|
||||
setEventState(EventHandlerState::PAUSE);
|
||||
myOSystem.state().unwindState(1000);
|
||||
enterTimeMachineMenuMode(1000, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1263,7 +1255,7 @@ bool EventHandler::eventStateChange(Event::Type type)
|
|||
|
||||
case Event::TimeMachineMode:
|
||||
if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE)
|
||||
enterMenuMode(EventHandlerState::TIMEMACHINE);
|
||||
enterTimeMachineMenuMode(0, false);
|
||||
else if(myState == EventHandlerState::TIMEMACHINE)
|
||||
leaveMenuMode();
|
||||
else
|
||||
|
@ -1271,8 +1263,7 @@ bool EventHandler::eventStateChange(Event::Type type)
|
|||
break;
|
||||
|
||||
case Event::DebuggerMode:
|
||||
if(myState == EventHandlerState::EMULATION
|
||||
|| myState == EventHandlerState::PAUSE
|
||||
if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE
|
||||
|| myState == EventHandlerState::TIMEMACHINE)
|
||||
enterDebugMode();
|
||||
else if(myState == EventHandlerState::DEBUGGER && myOSystem.debugger().canExit())
|
||||
|
@ -2164,6 +2155,17 @@ void EventHandler::leaveDebugMode()
|
|||
#endif
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void EventHandler::enterTimeMachineMenuMode(uInt32 numWinds, bool unwind)
|
||||
{
|
||||
// add one extra state if we are in Time Machine mode
|
||||
// TODO: maybe remove this state if we leave the menu at this new state
|
||||
myOSystem.state().addExtraState("enter Time Machine dialog"); // force new state
|
||||
|
||||
// TODO: display last wind message (numWinds != 0) in time machine dialog
|
||||
enterMenuMode(EventHandlerState::TIMEMACHINE);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void EventHandler::setEventState(EventHandlerState state)
|
||||
{
|
||||
|
|
|
@ -136,6 +136,7 @@ class EventHandler
|
|||
void leaveMenuMode();
|
||||
bool enterDebugMode();
|
||||
void leaveDebugMode();
|
||||
void enterTimeMachineMenuMode(uInt32 numWinds, bool unwind);
|
||||
void takeSnapshot(uInt32 number = 0);
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
//============================================================================
|
||||
//
|
||||
// SSSS tt lll lll
|
||||
// SS SS tt ll ll
|
||||
// SS tttttt eeee ll ll aaaa
|
||||
// SSSS tt ee ee ll ll aa
|
||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "Command.hxx"
|
||||
#include "Dialog.hxx"
|
||||
#include "Font.hxx"
|
||||
#include "FBSurface.hxx"
|
||||
#include "GuiObject.hxx"
|
||||
#include "OSystem.hxx"
|
||||
|
||||
#include "TimeLineWidget.hxx"
|
||||
|
||||
// TODO - remove all references to _stepValue__
|
||||
// - fix posToValue to use _stepValue
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
TimeLineWidget::TimeLineWidget(GuiObject* boss, const GUI::Font& font,
|
||||
int x, int y, int w, int h,
|
||||
const string& label, int labelWidth, int cmd)
|
||||
: ButtonWidget(boss, font, x, y, w, h, label, cmd),
|
||||
_value(0),
|
||||
_stepValue__(1),
|
||||
_valueMin(0),
|
||||
_valueMax(100),
|
||||
_isDragging(false),
|
||||
_labelWidth(labelWidth)
|
||||
{
|
||||
_flags = WIDGET_ENABLED | WIDGET_TRACK_MOUSE;
|
||||
_bgcolor = kDlgColor;
|
||||
_bgcolorhi = kDlgColor;
|
||||
|
||||
if(!_label.empty() && _labelWidth == 0)
|
||||
_labelWidth = _font.getStringWidth(_label);
|
||||
|
||||
_w = w + _labelWidth;
|
||||
|
||||
_stepValue.reserve(100);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void TimeLineWidget::setValue(int value)
|
||||
{
|
||||
if(value < _valueMin) value = _valueMin;
|
||||
else if(value > _valueMax) value = _valueMax;
|
||||
|
||||
if(value != _value)
|
||||
{
|
||||
_value = value;
|
||||
setDirty();
|
||||
sendCommand(_cmd, _value, _id);
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void TimeLineWidget::setMinValue(int value)
|
||||
{
|
||||
_valueMin = value;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void TimeLineWidget::setMaxValue(int value)
|
||||
{
|
||||
_valueMax = value;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void TimeLineWidget::setStepValues(const IntArray& steps)
|
||||
{
|
||||
// Try to allocate as infrequently as possible
|
||||
if(steps.size() > _stepValue.capacity())
|
||||
_stepValue.reserve(2 * steps.size());
|
||||
_stepValue.clear();
|
||||
|
||||
double scale = (_w - _labelWidth - 4) / double(steps.back());
|
||||
|
||||
// Skip the very last value; we take care of it outside the end of the loop
|
||||
for(uInt32 i = 0; i < steps.size() - 1; ++i)
|
||||
_stepValue.push_back(int(steps[i] * scale));
|
||||
|
||||
// Due to integer <-> double conversion, the last value is sometimes
|
||||
// slightly less than the maximum value; we assign it manually to fix this
|
||||
_stepValue.push_back(_w - _labelWidth - 4);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void TimeLineWidget::handleMouseMoved(int x, int y)
|
||||
{
|
||||
// TODO: when the mouse is dragged outside the widget, the slider should
|
||||
// snap back to the old value.
|
||||
if(isEnabled() && _isDragging && x >= int(_labelWidth))
|
||||
setValue(posToValue(x - _labelWidth));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void TimeLineWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount)
|
||||
{
|
||||
if(isEnabled() && b == MouseButton::LEFT)
|
||||
{
|
||||
_isDragging = true;
|
||||
handleMouseMoved(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void TimeLineWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount)
|
||||
{
|
||||
if(isEnabled() && _isDragging)
|
||||
sendCommand(_cmd, _value, _id);
|
||||
|
||||
_isDragging = false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void TimeLineWidget::handleMouseWheel(int x, int y, int direction)
|
||||
{
|
||||
if(isEnabled())
|
||||
{
|
||||
if(direction < 0)
|
||||
handleEvent(Event::UIUp);
|
||||
else if(direction > 0)
|
||||
handleEvent(Event::UIDown);
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool TimeLineWidget::handleEvent(Event::Type e)
|
||||
{
|
||||
if(!isEnabled())
|
||||
return false;
|
||||
|
||||
switch(e)
|
||||
{
|
||||
case Event::UIDown:
|
||||
case Event::UILeft:
|
||||
case Event::UIPgDown:
|
||||
setValue(_value - _stepValue__);
|
||||
break;
|
||||
|
||||
case Event::UIUp:
|
||||
case Event::UIRight:
|
||||
case Event::UIPgUp:
|
||||
setValue(_value + _stepValue__);
|
||||
break;
|
||||
|
||||
case Event::UIHome:
|
||||
setValue(_valueMin);
|
||||
break;
|
||||
|
||||
case Event::UIEnd:
|
||||
setValue(_valueMax);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void TimeLineWidget::drawWidget(bool hilite)
|
||||
{
|
||||
FBSurface& s = _boss->dialog().surface();
|
||||
|
||||
#ifndef FLAT_UI
|
||||
// Draw the label, if any
|
||||
if(_labelWidth > 0)
|
||||
s.drawString(_font, _label, _x, _y + 2, _labelWidth,
|
||||
isEnabled() ? kTextColor : kColor, TextAlign::Right);
|
||||
|
||||
// Draw the box
|
||||
s.box(_x + _labelWidth, _y, _w - _labelWidth, _h, kColor, kShadowColor);
|
||||
// Fill the box
|
||||
s.fillRect(_x + _labelWidth + 2, _y + 2, _w - _labelWidth - 4, _h - 4,
|
||||
!isEnabled() ? kBGColorHi : kWidColor);
|
||||
// Draw the 'bar'
|
||||
s.fillRect(_x + _labelWidth + 2, _y + 2, valueToPos(_value), _h - 4,
|
||||
!isEnabled() ? kColor : hilite ? kSliderColorHi : kSliderColor);
|
||||
#else
|
||||
// Draw the label, if any
|
||||
if(_labelWidth > 0)
|
||||
s.drawString(_font, _label, _x, _y + 2, _labelWidth,
|
||||
isEnabled() ? kTextColor : kColor, TextAlign::Left);
|
||||
|
||||
// Draw the box
|
||||
s.frameRect(_x + _labelWidth, _y, _w - _labelWidth, _h, isEnabled() && hilite ? kSliderColorHi : kShadowColor);
|
||||
// Fill the box
|
||||
s.fillRect(_x + _labelWidth + 1, _y + 1, _w - _labelWidth - 2, _h - 2,
|
||||
!isEnabled() ? kBGColorHi : kWidColor);
|
||||
// Draw the 'bar'
|
||||
s.fillRect(_x + _labelWidth + 2, _y + 2, valueToPos(_value), _h - 4,
|
||||
!isEnabled() ? kColor : hilite ? kSliderColorHi : kSliderColor);
|
||||
#endif
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
int TimeLineWidget::valueToPos(int value)
|
||||
{
|
||||
if(value < _valueMin) value = _valueMin;
|
||||
else if(value > _valueMax) value = _valueMax;
|
||||
|
||||
int real = _stepValue[BSPF::clamp(value, _valueMin, _valueMax)];
|
||||
#if 0
|
||||
int range = std::max(_valueMax - _valueMin, 1); // don't divide by zero
|
||||
int actual = ((_w - _labelWidth - 4) * (value - _valueMin) / range);
|
||||
cerr << "i=" << value << " real=" << real << endl << "actual=" << actual << endl << endl;
|
||||
#endif
|
||||
return real;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
int TimeLineWidget::posToValue(int pos)
|
||||
{
|
||||
int value = (pos) * (_valueMax - _valueMin) / (_w - _labelWidth - 4) + _valueMin;
|
||||
|
||||
// Scale the position to the correct interval (according to step value)
|
||||
return value - (value % _stepValue__);
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
//============================================================================
|
||||
//
|
||||
// SSSS tt lll lll
|
||||
// SS SS tt ll ll
|
||||
// SS tttttt eeee ll ll aaaa
|
||||
// SSSS tt ee ee ll ll aa
|
||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#ifndef TIMELINE_WIDGET_HXX
|
||||
#define TIMELINE_WIDGET_HXX
|
||||
|
||||
#include "Widget.hxx"
|
||||
|
||||
class TimeLineWidget : public ButtonWidget
|
||||
{
|
||||
public:
|
||||
TimeLineWidget(GuiObject* boss, const GUI::Font& font,
|
||||
int x, int y, int w, int h, const string& label = "",
|
||||
int labelWidth = 0, int cmd = 0);
|
||||
|
||||
void setValue(int value);
|
||||
int getValue() const { return _value; }
|
||||
|
||||
void setMinValue(int value);
|
||||
int getMinValue() const { return _valueMin; }
|
||||
void setMaxValue(int value);
|
||||
int getMaxValue() const { return _valueMax; }
|
||||
|
||||
/**
|
||||
Steps are not necessarily linear in a timeline, so we need info
|
||||
on each interval instead.
|
||||
*/
|
||||
void setStepValues(const IntArray& steps);
|
||||
|
||||
protected:
|
||||
void handleMouseMoved(int x, int y) override;
|
||||
void handleMouseDown(int x, int y, MouseButton b, int clickCount) override;
|
||||
void handleMouseUp(int x, int y, MouseButton b, int clickCount) override;
|
||||
void handleMouseWheel(int x, int y, int direction) override;
|
||||
bool handleEvent(Event::Type event) override;
|
||||
|
||||
void drawWidget(bool hilite) override;
|
||||
|
||||
int valueToPos(int value);
|
||||
int posToValue(int pos);
|
||||
|
||||
protected:
|
||||
int _value, _stepValue__;
|
||||
int _valueMin, _valueMax;
|
||||
bool _isDragging;
|
||||
int _labelWidth;
|
||||
|
||||
IntArray _stepValue;
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
TimeLineWidget() = delete;
|
||||
TimeLineWidget(const TimeLineWidget&) = delete;
|
||||
TimeLineWidget(TimeLineWidget&&) = delete;
|
||||
TimeLineWidget& operator=(const TimeLineWidget&) = delete;
|
||||
TimeLineWidget& operator=(TimeLineWidget&&) = delete;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -24,18 +24,16 @@
|
|||
#include "Widget.hxx"
|
||||
#include "StateManager.hxx"
|
||||
#include "RewindManager.hxx"
|
||||
|
||||
#include "TimeLineWidget.hxx"
|
||||
|
||||
#include "Console.hxx"
|
||||
#include "TIA.hxx"
|
||||
#include "System.hxx"
|
||||
|
||||
|
||||
#include "TimeMachineDialog.hxx"
|
||||
#include "Base.hxx"
|
||||
using Common::Base;
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent,
|
||||
int max_w, int max_h)
|
||||
|
@ -43,23 +41,6 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent,
|
|||
{
|
||||
const int BUTTON_W = 16, BUTTON_H = 14;
|
||||
|
||||
/*static uInt32 PAUSE[BUTTON_H] =
|
||||
{
|
||||
0b0001111001111000,
|
||||
0b0001111001111000,
|
||||
0b0001111001111000,
|
||||
0b0001111001111000,
|
||||
0b0001111001111000,
|
||||
0b0001111001111000,
|
||||
0b0001111001111000,
|
||||
0b0001111001111000,
|
||||
0b0001111001111000,
|
||||
0b0001111001111000,
|
||||
0b0001111001111000,
|
||||
0b0001111001111000,
|
||||
0b0001111001111000,
|
||||
0b0001111001111000
|
||||
};*/
|
||||
static uInt32 PLAY[BUTTON_H] =
|
||||
{
|
||||
0b0110000000000000,
|
||||
|
@ -181,12 +162,11 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent,
|
|||
};
|
||||
|
||||
const GUI::Font& font = instance().frameBuffer().font();
|
||||
const int H_BORDER = 6, BUTTON_GAP = 4, V_BORDER = 4; // FIXME, V_GAP = 4;
|
||||
const int H_BORDER = 6, BUTTON_GAP = 4, V_BORDER = 4;
|
||||
const int buttonWidth = BUTTON_W + 8,
|
||||
buttonHeight = BUTTON_H + 10,
|
||||
rowHeight = font.getLineHeight();
|
||||
|
||||
WidgetArray wid;
|
||||
int xpos, ypos;
|
||||
|
||||
// Set real dimensions
|
||||
|
@ -201,64 +181,61 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent,
|
|||
|
||||
// Add index info
|
||||
myCurrentIdxWidget = new StaticTextWidget(this, font, xpos, ypos, " ", TextAlign::Left, kBGColor);
|
||||
myCurrentIdxWidget->setTextColor(kWidColor);
|
||||
myCurrentIdxWidget->setTextColor(kColorInfo);
|
||||
myLastIdxWidget = new StaticTextWidget(this, font, _w - H_BORDER - font.getStringWidth("8888"), ypos,
|
||||
" ", TextAlign::Right, kBGColor);
|
||||
myLastIdxWidget->setTextColor(kWidColor);
|
||||
myLastIdxWidget->setTextColor(kColorInfo);
|
||||
|
||||
// Add timeline
|
||||
const uInt32 tl_h = myCurrentIdxWidget->getHeight() / 2,
|
||||
tl_x = xpos + myCurrentIdxWidget->getWidth() + 8,
|
||||
tl_y = ypos + (myCurrentIdxWidget->getHeight() - tl_h) / 2,
|
||||
tl_w = myLastIdxWidget->getAbsX() - tl_x - 8;
|
||||
myTimeline = new TimeLineWidget(this, font, tl_x, tl_y, tl_w, tl_h, "", 0, kTimeline);
|
||||
myTimeline->setMinValue(0);
|
||||
ypos += rowHeight;
|
||||
|
||||
// Add time info
|
||||
myCurrentTimeWidget = new StaticTextWidget(this, font, xpos, ypos + 3, "04:32 59", TextAlign::Left, kBGColor);
|
||||
myCurrentTimeWidget->setTextColor(kWidColor);
|
||||
myCurrentTimeWidget->setTextColor(kColorInfo);
|
||||
myLastTimeWidget = new StaticTextWidget(this, font, _w - H_BORDER - font.getStringWidth("XX:XX XX"), ypos + 3,
|
||||
"12:25 59", TextAlign::Right, kBGColor);
|
||||
myLastTimeWidget->setTextColor(kWidColor);
|
||||
myLastTimeWidget->setTextColor(kColorInfo);
|
||||
xpos = myCurrentTimeWidget->getRight() + BUTTON_GAP * 4;
|
||||
|
||||
// Add buttons
|
||||
myRewindAllWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, REWIND_ALL,
|
||||
BUTTON_W, BUTTON_H, kRewindAll);
|
||||
wid.push_back(myRewindAllWidget);
|
||||
xpos += buttonWidth + BUTTON_GAP;
|
||||
|
||||
myRewind10Widget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, REWIND_10,
|
||||
BUTTON_W, BUTTON_H, kRewind10);
|
||||
wid.push_back(myRewind10Widget);
|
||||
xpos += buttonWidth + BUTTON_GAP;
|
||||
|
||||
myRewind1Widget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, REWIND_1,
|
||||
BUTTON_W, BUTTON_H, kRewind1);
|
||||
wid.push_back(myRewind1Widget);
|
||||
xpos += buttonWidth + BUTTON_GAP*2;
|
||||
|
||||
/*myPauseWidget = new ButtonWidget(this, font, xpos, ypos - 2, buttonWidth + 4, buttonHeight + 4, PAUSE,
|
||||
BUTTON_W, BUTTON_H, kPause);
|
||||
wid.push_back(myPauseWidget);
|
||||
myPauseWidget->clearFlags(WIDGET_ENABLED);*/
|
||||
myPlayWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, PLAY,
|
||||
BUTTON_W, BUTTON_H, kPlay);
|
||||
wid.push_back(myPlayWidget);
|
||||
xpos += buttonWidth + BUTTON_GAP*2;
|
||||
|
||||
myUnwind1Widget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, UNWIND_1,
|
||||
BUTTON_W, BUTTON_H, kUnwind1);
|
||||
wid.push_back(myUnwind1Widget);
|
||||
xpos += buttonWidth + BUTTON_GAP;
|
||||
|
||||
myUnwind10Widget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, UNWIND_10,
|
||||
BUTTON_W, BUTTON_H, kUnwind10);
|
||||
wid.push_back(myUnwind10Widget);
|
||||
xpos += buttonWidth + BUTTON_GAP;
|
||||
|
||||
myUnwindAllWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, UNWIND_ALL,
|
||||
BUTTON_W, BUTTON_H, kUnwindAll);
|
||||
wid.push_back(myUnwindAllWidget);
|
||||
xpos = myUnwindAllWidget->getRight() + BUTTON_GAP * 3;
|
||||
|
||||
// Add message
|
||||
myMessageWidget = new StaticTextWidget(this, font, xpos, ypos + 3, " ",
|
||||
TextAlign::Left, kBGColor);
|
||||
myMessageWidget->setTextColor(kWidColor);
|
||||
myMessageWidget->setTextColor(kColorInfo);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -273,21 +250,75 @@ void TimeMachineDialog::center()
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void TimeMachineDialog::loadConfig()
|
||||
{
|
||||
surface().attributes().blending = true;
|
||||
surface().attributes().blendalpha = 80;
|
||||
surface().applyAttributes();
|
||||
RewindManager& r = instance().state().rewindManager();
|
||||
IntArray cycles = r.cyclesList();
|
||||
|
||||
// Set range and intervals for timeline
|
||||
myTimeline->setMaxValue(cycles.size() - 1);
|
||||
myTimeline->setStepValues(cycles);
|
||||
|
||||
// Enable blending (only once is necessary)
|
||||
if(!surface().attributes().blending)
|
||||
{
|
||||
surface().attributes().blending = true;
|
||||
surface().attributes().blendalpha = 80;
|
||||
surface().applyAttributes();
|
||||
}
|
||||
|
||||
handleWinds();
|
||||
myMessageWidget->setLabel("");
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void TimeMachineDialog::handleKeyDown(StellaKey key, StellaMod mod)
|
||||
{
|
||||
// The following 'Alt' shortcuts duplicate the shortcuts in EventHandler
|
||||
// It is best to keep them the same, so changes in EventHandler mean we
|
||||
// need to update the logic here too
|
||||
if(StellaModTest::isAlt(mod))
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
case KBDK_LEFT: // Alt-left(-shift) rewinds 1(10) states
|
||||
handleCommand(nullptr, StellaModTest::isShift(mod) ? kRewind10 : kRewind1, 0, 0);
|
||||
break;
|
||||
|
||||
case KBDK_RIGHT: // Alt-right(-shift) unwinds 1(10) states
|
||||
handleCommand(nullptr, StellaModTest::isShift(mod) ? kUnwind10 : kUnwind1, 0, 0);
|
||||
break;
|
||||
|
||||
case KBDK_DOWN: // Alt-down rewinds to start of list
|
||||
handleCommand(nullptr, kRewindAll, 0, 0);
|
||||
break;
|
||||
|
||||
case KBDK_UP: // Alt-up rewinds to end of list
|
||||
handleCommand(nullptr, kUnwindAll, 0, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
Dialog::handleKeyDown(key, mod);
|
||||
}
|
||||
}
|
||||
else if(key == KBDK_SPACE || key == KBDK_ESCAPE)
|
||||
handleCommand(nullptr, kPlay, 0, 0);
|
||||
else
|
||||
Dialog::handleKeyDown(key, mod);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void TimeMachineDialog::handleCommand(CommandSender* sender, int cmd,
|
||||
int data, int id)
|
||||
{
|
||||
//cerr << cmd << endl;
|
||||
switch(cmd)
|
||||
{
|
||||
case kTimeline:
|
||||
{
|
||||
Int32 winds = myTimeline->getValue() -
|
||||
instance().state().rewindManager().getCurrentIdx() + 1;
|
||||
handleWinds(winds);
|
||||
break;
|
||||
}
|
||||
|
||||
case kPlay:
|
||||
instance().eventHandler().leaveMenuMode();
|
||||
break;
|
||||
|
@ -321,12 +352,13 @@ void TimeMachineDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string TimeMachineDialog::getTimeString(uInt64 cycles)
|
||||
{
|
||||
const Int32 scanlines = std::max(instance().console().tia().scanlinesLastFrame(), 240u);
|
||||
const bool isNTSC = scanlines <= 287;
|
||||
const Int32 NTSC_FREQ = 1193182; // ~76*262*60
|
||||
const Int32 PAL_FREQ = 1182298; // ~76*312*50
|
||||
const Int32 PAL_FREQ = 1182298; // ~76*312*50
|
||||
const Int32 freq = isNTSC ? NTSC_FREQ : PAL_FREQ; // = cycles/second
|
||||
|
||||
uInt32 minutes = cycles / (freq * 60);
|
||||
|
@ -351,21 +383,26 @@ void TimeMachineDialog::handleWinds(Int32 numWinds)
|
|||
if(numWinds)
|
||||
{
|
||||
uInt64 startCycles = instance().console().tia().cycles();
|
||||
if(numWinds < 0)
|
||||
r.rewindState(-numWinds);
|
||||
else
|
||||
r.unwindState(numWinds);
|
||||
string message = r.getUnitString(instance().console().tia().cycles() - startCycles);
|
||||
if(numWinds < 0) r.rewindStates(-numWinds);
|
||||
else if(numWinds > 0) r.unwindStates(numWinds);
|
||||
|
||||
myMessageWidget->setLabel((numWinds < 0 ? "(-" : "(+") + message + ")");
|
||||
uInt64 elapsed = instance().console().tia().cycles() - startCycles;
|
||||
if(elapsed > 0)
|
||||
{
|
||||
string message = r.getUnitString(elapsed);
|
||||
|
||||
// TODO: add message text from addState()
|
||||
myMessageWidget->setLabel((numWinds < 0 ? "(-" : "(+") + message + ")");
|
||||
}
|
||||
}
|
||||
// Update time
|
||||
myCurrentTimeWidget->setLabel(getTimeString(r.getCurrentCycles() - r.getFirstCycles()));
|
||||
myLastTimeWidget->setLabel(getTimeString(r.getLastCycles() - r.getFirstCycles()));
|
||||
myTimeline->setValue(r.getCurrentIdx()-1);
|
||||
// Update index
|
||||
myCurrentIdxWidget->setValue(r.getCurrentIdx());
|
||||
myLastIdxWidget->setValue(r.getLastIdx());
|
||||
// enable/disable buttons
|
||||
// Enable/disable buttons
|
||||
myRewindAllWidget->setEnabled(!r.atFirst());
|
||||
myRewind10Widget->setEnabled(!r.atFirst());
|
||||
myRewind1Widget->setEnabled(!r.atFirst());
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
class CommandSender;
|
||||
class DialogContainer;
|
||||
class OSystem;
|
||||
class TimeLineWidget;
|
||||
|
||||
#include "Dialog.hxx"
|
||||
|
||||
|
@ -32,6 +33,7 @@ class TimeMachineDialog : public Dialog
|
|||
|
||||
private:
|
||||
void loadConfig() override;
|
||||
void handleKeyDown(StellaKey key, StellaMod mod) override;
|
||||
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
|
||||
|
||||
/** This dialog uses its own positioning, so we override Dialog::center() */
|
||||
|
@ -45,7 +47,7 @@ class TimeMachineDialog : public Dialog
|
|||
private:
|
||||
enum
|
||||
{
|
||||
kPause = 'TMps',
|
||||
kTimeline = 'TMtl',
|
||||
kPlay = 'TMpl',
|
||||
kRewindAll = 'TMra',
|
||||
kRewind10 = 'TMr1',
|
||||
|
@ -55,7 +57,8 @@ class TimeMachineDialog : public Dialog
|
|||
kUnwind1 = 'TMun',
|
||||
};
|
||||
|
||||
// FIXME ButtonWidget* myPauseWidget;
|
||||
TimeLineWidget* myTimeline;
|
||||
|
||||
ButtonWidget* myPlayWidget;
|
||||
ButtonWidget* myRewindAllWidget;
|
||||
ButtonWidget* myRewind10Widget;
|
||||
|
|
|
@ -43,6 +43,7 @@ MODULE_OBJS := \
|
|||
src/gui/SnapshotDialog.o \
|
||||
src/gui/StringListWidget.o \
|
||||
src/gui/TabWidget.o \
|
||||
src/gui/TimeLineWidget.o \
|
||||
src/gui/TimeMachineDialog.o \
|
||||
src/gui/TimeMachine.o \
|
||||
src/gui/UIDialog.o \
|
||||
|
|
|
@ -570,6 +570,8 @@
|
|||
DCE5CDE31BA10024005CD08A /* RiotRamWidget.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCE5CDE11BA10024005CD08A /* RiotRamWidget.cxx */; };
|
||||
DCE5CDE41BA10024005CD08A /* RiotRamWidget.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCE5CDE21BA10024005CD08A /* RiotRamWidget.hxx */; };
|
||||
DCE8B1871E7E03B300189864 /* FrameLayout.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCE8B1861E7E03B300189864 /* FrameLayout.hxx */; };
|
||||
DCE9158B201543B900960CC0 /* TimeLineWidget.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCE91589201543B900960CC0 /* TimeLineWidget.cxx */; };
|
||||
DCE9158C201543B900960CC0 /* TimeLineWidget.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCE9158A201543B900960CC0 /* TimeLineWidget.hxx */; };
|
||||
DCEC58581E945125002F0246 /* DelayQueueWidget.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DCEC58561E945125002F0246 /* DelayQueueWidget.cxx */; };
|
||||
DCEC58591E945125002F0246 /* DelayQueueWidget.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCEC58571E945125002F0246 /* DelayQueueWidget.hxx */; };
|
||||
DCEC585E1E945175002F0246 /* DelayQueueIterator.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DCEC585B1E945175002F0246 /* DelayQueueIterator.hxx */; };
|
||||
|
@ -1239,6 +1241,8 @@
|
|||
DCE5CDE11BA10024005CD08A /* RiotRamWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RiotRamWidget.cxx; sourceTree = "<group>"; };
|
||||
DCE5CDE21BA10024005CD08A /* RiotRamWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RiotRamWidget.hxx; sourceTree = "<group>"; };
|
||||
DCE8B1861E7E03B300189864 /* FrameLayout.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FrameLayout.hxx; sourceTree = "<group>"; };
|
||||
DCE91589201543B900960CC0 /* TimeLineWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimeLineWidget.cxx; sourceTree = "<group>"; };
|
||||
DCE9158A201543B900960CC0 /* TimeLineWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TimeLineWidget.hxx; sourceTree = "<group>"; };
|
||||
DCEC58561E945125002F0246 /* DelayQueueWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DelayQueueWidget.cxx; sourceTree = "<group>"; };
|
||||
DCEC58571E945125002F0246 /* DelayQueueWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DelayQueueWidget.hxx; sourceTree = "<group>"; };
|
||||
DCEC585B1E945175002F0246 /* DelayQueueIterator.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DelayQueueIterator.hxx; sourceTree = "<group>"; };
|
||||
|
@ -1903,6 +1907,8 @@
|
|||
2DEF21FB08BC033500B246B4 /* StringListWidget.hxx */,
|
||||
2DDBEAD0084578BF00812C11 /* TabWidget.cxx */,
|
||||
2DDBEAD1084578BF00812C11 /* TabWidget.hxx */,
|
||||
DCE91589201543B900960CC0 /* TimeLineWidget.cxx */,
|
||||
DCE9158A201543B900960CC0 /* TimeLineWidget.hxx */,
|
||||
DCA82C6D1FEB4E780059340F /* TimeMachine.cxx */,
|
||||
DCA82C6E1FEB4E780059340F /* TimeMachine.hxx */,
|
||||
DCA82C6F1FEB4E780059340F /* TimeMachineDialog.cxx */,
|
||||
|
@ -2168,6 +2174,7 @@
|
|||
2D91740509BA90380026E9FF /* DialogContainer.hxx in Headers */,
|
||||
DC6D39881A3CE65000171E71 /* CartWDWidget.hxx in Headers */,
|
||||
2D91740609BA90380026E9FF /* GameInfoDialog.hxx in Headers */,
|
||||
DCE9158C201543B900960CC0 /* TimeLineWidget.hxx in Headers */,
|
||||
2D91740709BA90380026E9FF /* GameList.hxx in Headers */,
|
||||
2D91740809BA90380026E9FF /* GuiObject.hxx in Headers */,
|
||||
DC73BD861915E5B1003FAFAD /* FBSurfaceSDL2.hxx in Headers */,
|
||||
|
@ -2581,6 +2588,7 @@
|
|||
2D9174B709BA90380026E9FF /* OptionsDialog.cxx in Sources */,
|
||||
2D9174B809BA90380026E9FF /* PopUpWidget.cxx in Sources */,
|
||||
DCBDDE9A1D6A5F0E009DF1E9 /* Cart3EPlusWidget.cxx in Sources */,
|
||||
DCE9158B201543B900960CC0 /* TimeLineWidget.cxx in Sources */,
|
||||
DCE5CDE31BA10024005CD08A /* RiotRamWidget.cxx in Sources */,
|
||||
2D9174B909BA90380026E9FF /* ProgressDialog.cxx in Sources */,
|
||||
2D9174BA09BA90380026E9FF /* ScrollBarWidget.cxx in Sources */,
|
||||
|
|
|
@ -346,6 +346,7 @@
|
|||
<ClCompile Include="..\gui\LoggerDialog.cxx" />
|
||||
<ClCompile Include="..\gui\RadioButtonWidget.cxx" />
|
||||
<ClCompile Include="..\gui\SnapshotDialog.cxx" />
|
||||
<ClCompile Include="..\gui\TimeLineWidget.cxx" />
|
||||
<ClCompile Include="..\gui\TimeMachine.cxx" />
|
||||
<ClCompile Include="..\gui\TimeMachineDialog.cxx" />
|
||||
<ClCompile Include="FSNodeWINDOWS.cxx" />
|
||||
|
@ -651,6 +652,7 @@
|
|||
<ClInclude Include="..\gui\LoggerDialog.hxx" />
|
||||
<ClInclude Include="..\gui\RadioButtonWidget.hxx" />
|
||||
<ClInclude Include="..\gui\SnapshotDialog.hxx" />
|
||||
<ClInclude Include="..\gui\TimeLineWidget.hxx" />
|
||||
<ClInclude Include="..\gui\TimeMachine.hxx" />
|
||||
<ClInclude Include="..\gui\TimeMachineDialog.hxx" />
|
||||
<ClInclude Include="..\libpng\pngdebug.h" />
|
||||
|
|
|
@ -891,6 +891,9 @@
|
|||
<ClCompile Include="..\emucore\tia\frame-manager\YStartDetector.cxx">
|
||||
<Filter>Source Files\emucore\tia</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\gui\TimeLineWidget.cxx">
|
||||
<Filter>Source Files\gui</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\common\bspf.hxx">
|
||||
|
@ -1823,6 +1826,9 @@
|
|||
<ClInclude Include="..\emucore\tia\TIAConstants.hxx">
|
||||
<Filter>Header Files\emucore\tia</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\gui\TimeLineWidget.hxx">
|
||||
<Filter>Header Files\gui</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="stella.ico">
|
||||
|
|
Loading…
Reference in New Issue