From 69e41a1434797ba3ad75e0278e7a4ffe69a413ca Mon Sep 17 00:00:00 2001 From: Stephen Anthony Date: Sun, 21 Jan 2018 17:37:07 -0330 Subject: [PATCH 01/13] Initial check-in of Time Machine timeline: - currently, TimeLineWidget is just a copy of SliderWidget; still have to add a round 'button' to grab the line - at some point, we may merge SliderWidget and TimeLineWidget; for now they are separate - absolutely no functionality yet; just here for you guys to see how it will look - we still need to discuss gridmarks, and how the timeline will change (by state file, by time, etc) --- src/gui/TimeLineWidget.cxx | 209 ++++++++++++++++++++++++++++++++++ src/gui/TimeLineWidget.hxx | 67 +++++++++++ src/gui/TimeMachineDialog.cxx | 30 +++-- src/gui/TimeMachineDialog.hxx | 6 +- src/gui/module.mk | 1 + 5 files changed, 302 insertions(+), 11 deletions(-) create mode 100644 src/gui/TimeLineWidget.cxx create mode 100644 src/gui/TimeLineWidget.hxx diff --git a/src/gui/TimeLineWidget.cxx b/src/gui/TimeLineWidget.cxx new file mode 100644 index 000000000..b9e84fe27 --- /dev/null +++ b/src/gui/TimeLineWidget.cxx @@ -0,0 +1,209 @@ +//============================================================================ +// +// 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" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +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; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +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::setStepValue(int value) +{ + _stepValue = value; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +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 range = std::max(_valueMax - _valueMin, 1); // don't divide by zero + + return ((_w - _labelWidth - 4) * (value - _valueMin) / range); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +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); +} diff --git a/src/gui/TimeLineWidget.hxx b/src/gui/TimeLineWidget.hxx new file mode 100644 index 000000000..475fd5b7c --- /dev/null +++ b/src/gui/TimeLineWidget.hxx @@ -0,0 +1,67 @@ +//============================================================================ +// +// 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; } + void setStepValue(int value); + int getStepValue() const { return _stepValue; } + + 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; + + 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 diff --git a/src/gui/TimeMachineDialog.cxx b/src/gui/TimeMachineDialog.cxx index cd009330d..357432d8f 100644 --- a/src/gui/TimeMachineDialog.cxx +++ b/src/gui/TimeMachineDialog.cxx @@ -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) @@ -205,6 +203,14 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, myLastIdxWidget = new StaticTextWidget(this, font, _w - H_BORDER - font.getStringWidth("8888"), ypos, " ", TextAlign::Right, kBGColor); myLastIdxWidget->setTextColor(kWidColor); + + // 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); + wid.push_back(myTimeline); ypos += rowHeight; // Add time info @@ -231,10 +237,6 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, 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); @@ -259,6 +261,8 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, myMessageWidget = new StaticTextWidget(this, font, xpos, ypos + 3, " ", TextAlign::Left, kBGColor); myMessageWidget->setTextColor(kWidColor); + + // FIXME - add wid list to focus list } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -273,6 +277,10 @@ void TimeMachineDialog::center() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TimeMachineDialog::loadConfig() { +cerr << "loadConfig()\n"; + // FIXME - set range for timeline + //myTimeline->setMinValue(..); myTimeline->setMaxValue(..); + surface().attributes().blending = true; surface().attributes().blendalpha = 80; surface().applyAttributes(); @@ -285,9 +293,12 @@ void TimeMachineDialog::loadConfig() void TimeMachineDialog::handleCommand(CommandSender* sender, int cmd, int data, int id) { -//cerr << cmd << endl; switch(cmd) { + case kTimeline: + cerr << "timeline: " << myTimeline->getValue() << endl; + break; + case kPlay: instance().eventHandler().leaveMenuMode(); break; @@ -321,6 +332,7 @@ void TimeMachineDialog::handleCommand(CommandSender* sender, int cmd, } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - string TimeMachineDialog::getTimeString(uInt64 cycles) { const Int32 scanlines = std::max(instance().console().tia().scanlinesLastFrame(), 240u); @@ -365,7 +377,7 @@ void TimeMachineDialog::handleWinds(Int32 numWinds) // 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()); diff --git a/src/gui/TimeMachineDialog.hxx b/src/gui/TimeMachineDialog.hxx index 05b49ce25..75d6da1a1 100644 --- a/src/gui/TimeMachineDialog.hxx +++ b/src/gui/TimeMachineDialog.hxx @@ -21,6 +21,7 @@ class CommandSender; class DialogContainer; class OSystem; +class TimeLineWidget; #include "Dialog.hxx" @@ -45,7 +46,7 @@ class TimeMachineDialog : public Dialog private: enum { - kPause = 'TMps', + kTimeline = 'TMtl', kPlay = 'TMpl', kRewindAll = 'TMra', kRewind10 = 'TMr1', @@ -55,7 +56,8 @@ class TimeMachineDialog : public Dialog kUnwind1 = 'TMun', }; - // FIXME ButtonWidget* myPauseWidget; + TimeLineWidget* myTimeline; + ButtonWidget* myPlayWidget; ButtonWidget* myRewindAllWidget; ButtonWidget* myRewind10Widget; diff --git a/src/gui/module.mk b/src/gui/module.mk index 3058ac9c8..b598334c7 100644 --- a/src/gui/module.mk +++ b/src/gui/module.mk @@ -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 \ From 63de71d7f53aa6246a63228ccedee43c1975c74c Mon Sep 17 00:00:00 2001 From: Stephen Anthony Date: Sun, 21 Jan 2018 18:09:01 -0330 Subject: [PATCH 02/13] Updated VS project file for TimeLineWidget class. --- src/windows/Stella.vcxproj | 2 ++ src/windows/Stella.vcxproj.filters | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/windows/Stella.vcxproj b/src/windows/Stella.vcxproj index c9225f627..0318c47a2 100644 --- a/src/windows/Stella.vcxproj +++ b/src/windows/Stella.vcxproj @@ -346,6 +346,7 @@ + @@ -651,6 +652,7 @@ + diff --git a/src/windows/Stella.vcxproj.filters b/src/windows/Stella.vcxproj.filters index 7997369dd..592651d33 100644 --- a/src/windows/Stella.vcxproj.filters +++ b/src/windows/Stella.vcxproj.filters @@ -891,6 +891,9 @@ Source Files\emucore\tia + + Source Files\gui + @@ -1823,6 +1826,9 @@ Header Files\emucore\tia + + Header Files\gui + From 307e496109f19f69f89f84903df07f981f1c9498 Mon Sep 17 00:00:00 2001 From: Stephen Anthony Date: Sun, 21 Jan 2018 18:28:15 -0330 Subject: [PATCH 03/13] Updated Xcode project file for TimeLineWidget class. --- src/macosx/stella.xcodeproj/project.pbxproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/macosx/stella.xcodeproj/project.pbxproj b/src/macosx/stella.xcodeproj/project.pbxproj index 0358d89a8..6942a8c56 100644 --- a/src/macosx/stella.xcodeproj/project.pbxproj +++ b/src/macosx/stella.xcodeproj/project.pbxproj @@ -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 = ""; }; DCE5CDE21BA10024005CD08A /* RiotRamWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RiotRamWidget.hxx; sourceTree = ""; }; DCE8B1861E7E03B300189864 /* FrameLayout.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FrameLayout.hxx; sourceTree = ""; }; + DCE91589201543B900960CC0 /* TimeLineWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimeLineWidget.cxx; sourceTree = ""; }; + DCE9158A201543B900960CC0 /* TimeLineWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TimeLineWidget.hxx; sourceTree = ""; }; DCEC58561E945125002F0246 /* DelayQueueWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DelayQueueWidget.cxx; sourceTree = ""; }; DCEC58571E945125002F0246 /* DelayQueueWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DelayQueueWidget.hxx; sourceTree = ""; }; DCEC585B1E945175002F0246 /* DelayQueueIterator.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = DelayQueueIterator.hxx; sourceTree = ""; }; @@ -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 */, From 2c90aaa7c7612d9665c338ac67b2ae6bfa0a2ed5 Mon Sep 17 00:00:00 2001 From: Stephen Anthony Date: Sat, 27 Jan 2018 19:34:26 -0330 Subject: [PATCH 04/13] Timeline and buttons in TimeMachineDialog are now syncronized. --- Makefile | 2 +- src/common/RewindManager.cxx | 21 ++++++++--- src/common/RewindManager.hxx | 12 +++++-- src/common/Version.hxx | 2 +- src/gui/TimeLineWidget.cxx | 2 ++ src/gui/TimeLineWidget.hxx | 2 ++ src/gui/TimeMachineDialog.cxx | 67 ++++++++++++++++++++++------------- 7 files changed, 74 insertions(+), 34 deletions(-) diff --git a/Makefile b/Makefile index 2b0c212c8..35a1e3606 100644 --- a/Makefile +++ b/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 diff --git a/src/common/RewindManager.cxx b/src/common/RewindManager.cxx index f6d6d0b62..1a0194449 100644 --- a/src/common/RewindManager.cxx +++ b/src/common/RewindManager.cxx @@ -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"); @@ -146,7 +146,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), @@ -306,14 +306,14 @@ string RewindManager::getUnitString(Int64 cycles) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 RewindManager::getFirstCycles() +uInt32 RewindManager::getFirstCycles() const { // TODO: check if valid return Common::LinkedObjectPool::const_iter(myStateList.first())->cycles; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 RewindManager::getCurrentCycles() +uInt32 RewindManager::getCurrentCycles() const { if(myStateList.currentIsValid()) return myStateList.current().cycles; @@ -322,9 +322,20 @@ uInt32 RewindManager::getCurrentCycles() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 RewindManager::getLastCycles() +uInt32 RewindManager::getLastCycles() const { // TODO: check if valid return Common::LinkedObjectPool::const_iter(myStateList.last())->cycles; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +IntArray RewindManager::cyclesList() const +{ + IntArray arr; + + uInt64 firstCycle = getFirstCycles(); + for(auto it = myStateList.first(); it != myStateList.last(); ++it) + arr.push_back(uInt32(it->cycles - firstCycle)); + + return arr; +} diff --git a/src/common/RewindManager.hxx b/src/common/RewindManager.hxx index 8749aa109..3c37aaf19 100644 --- a/src/common/RewindManager.hxx +++ b/src/common/RewindManager.hxx @@ -136,9 +136,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; diff --git a/src/common/Version.hxx b/src/common/Version.hxx index 44ab97567..c8a5972f8 100644 --- a/src/common/Version.hxx +++ b/src/common/Version.hxx @@ -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 diff --git a/src/gui/TimeLineWidget.cxx b/src/gui/TimeLineWidget.cxx index b9e84fe27..6829ee4e1 100644 --- a/src/gui/TimeLineWidget.cxx +++ b/src/gui/TimeLineWidget.cxx @@ -73,11 +73,13 @@ void TimeLineWidget::setMaxValue(int value) _valueMax = value; } +#if 0 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TimeLineWidget::setStepValue(int value) { _stepValue = value; } +#endif // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TimeLineWidget::handleMouseMoved(int x, int y) diff --git a/src/gui/TimeLineWidget.hxx b/src/gui/TimeLineWidget.hxx index 475fd5b7c..c2070500b 100644 --- a/src/gui/TimeLineWidget.hxx +++ b/src/gui/TimeLineWidget.hxx @@ -34,8 +34,10 @@ class TimeLineWidget : public ButtonWidget int getMinValue() const { return _valueMin; } void setMaxValue(int value); int getMaxValue() const { return _valueMax; } +#if 0 void setStepValue(int value); int getStepValue() const { return _stepValue; } +#endif protected: void handleMouseMoved(int x, int y) override; diff --git a/src/gui/TimeMachineDialog.cxx b/src/gui/TimeMachineDialog.cxx index 357432d8f..d44a59ad5 100644 --- a/src/gui/TimeMachineDialog.cxx +++ b/src/gui/TimeMachineDialog.cxx @@ -179,12 +179,12 @@ 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; +// WidgetArray wid; int xpos, ypos; // Set real dimensions @@ -210,7 +210,7 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, 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); - wid.push_back(myTimeline); +// wid.push_back(myTimeline); ypos += rowHeight; // Add time info @@ -224,37 +224,37 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, // Add buttons myRewindAllWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, REWIND_ALL, BUTTON_W, BUTTON_H, kRewindAll); - wid.push_back(myRewindAllWidget); +// 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); +// 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); +// wid.push_back(myRewind1Widget); xpos += buttonWidth + BUTTON_GAP*2; myPlayWidget = new ButtonWidget(this, font, xpos, ypos, buttonWidth, buttonHeight, PLAY, BUTTON_W, BUTTON_H, kPlay); - wid.push_back(myPlayWidget); +// 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); +// 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); +// 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); +// wid.push_back(myUnwindAllWidget); xpos = myUnwindAllWidget->getRight() + BUTTON_GAP * 3; // Add message @@ -262,7 +262,7 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, TextAlign::Left, kBGColor); myMessageWidget->setTextColor(kWidColor); - // FIXME - add wid list to focus list +// addToFocusList(wid); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -277,13 +277,29 @@ void TimeMachineDialog::center() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TimeMachineDialog::loadConfig() { -cerr << "loadConfig()\n"; - // FIXME - set range for timeline - //myTimeline->setMinValue(..); myTimeline->setMaxValue(..); + RewindManager& r = instance().state().rewindManager(); + IntArray cycles = r.cyclesList(); - surface().attributes().blending = true; - surface().attributes().blendalpha = 80; - surface().applyAttributes(); +#if 0 // if we won't be using focused widget, this code can disappear + // Set default focus + bool timelineEnabled = cycles.size() > 0; + myTimeline->setEnabled(timelineEnabled); + if(timelineEnabled) + setFocus(myTimeline); + else + setFocus(myPlayWidget); +#endif + + // Set range for timeline + myTimeline->setMaxValue(std::max(Int32(cycles.size()), 1)); + + // Enable blending (only once is necessary) + if(!surface().attributes().blending) + { + surface().attributes().blending = true; + surface().attributes().blendalpha = 80; + surface().applyAttributes(); + } handleWinds(); myMessageWidget->setLabel(""); @@ -296,8 +312,12 @@ void TimeMachineDialog::handleCommand(CommandSender* sender, int cmd, switch(cmd) { case kTimeline: - cerr << "timeline: " << myTimeline->getValue() << endl; + { + Int32 winds = myTimeline->getValue() - + instance().state().rewindManager().getCurrentIdx() + 1; + handleWinds(winds); break; + } case kPlay: instance().eventHandler().leaveMenuMode(); @@ -338,7 +358,7 @@ 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); @@ -347,7 +367,7 @@ string TimeMachineDialog::getTimeString(uInt64 cycles) cycles -= seconds * freq; uInt32 frames = cycles / (scanlines * 76); - stringstream time; + ostringstream time; time << Common::Base::toString(minutes, Common::Base::F_10_2) << ":"; time << Common::Base::toString(seconds, Common::Base::F_10_2) << "."; time << Common::Base::toString(frames, Common::Base::F_10_2); @@ -363,10 +383,8 @@ void TimeMachineDialog::handleWinds(Int32 numWinds) if(numWinds) { uInt64 startCycles = instance().console().tia().cycles(); - if(numWinds < 0) - r.rewindState(-numWinds); - else - r.unwindState(numWinds); + if(numWinds < 0) r.rewindState(-numWinds); + else if(numWinds > 0) r.unwindState(numWinds); string message = r.getUnitString(instance().console().tia().cycles() - startCycles); myMessageWidget->setLabel((numWinds < 0 ? "(-" : "(+") + message + ")"); @@ -374,6 +392,7 @@ void TimeMachineDialog::handleWinds(Int32 numWinds) // 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()); From d2177ea6102dc0e305f0282891d4b89dad2f616b Mon Sep 17 00:00:00 2001 From: thrust26 Date: Sun, 28 Jan 2018 10:15:39 +0100 Subject: [PATCH 05/13] added extra save state when starting time machine navigation (some TODOs left, see EventHandler and TimeMachineDialog) --- src/common/RewindManager.cxx | 14 +++++++++++-- src/common/RewindManager.hxx | 18 +++++++++++++---- src/common/StateManager.cxx | 22 ++++++++++++++++---- src/common/StateManager.hxx | 18 +++++++++++++---- src/debugger/Debugger.cxx | 2 +- src/emucore/EventHandler.cxx | 38 +++++++++++++++++++---------------- src/emucore/EventHandler.hxx | 1 + src/gui/TimeMachineDialog.cxx | 5 +++-- 8 files changed, 84 insertions(+), 34 deletions(-) diff --git a/src/common/RewindManager.cxx b/src/common/RewindManager.cxx index 1a0194449..216e03943 100644 --- a/src/common/RewindManager.cxx +++ b/src/common/RewindManager.cxx @@ -138,7 +138,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; @@ -177,7 +177,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,6 +210,16 @@ 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() { diff --git a/src/common/RewindManager.hxx b/src/common/RewindManager.hxx index 3c37aaf19..debe0cee8 100644 --- a/src/common/RewindManager.hxx +++ b/src/common/RewindManager.hxx @@ -106,22 +106,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(); } diff --git a/src/common/StateManager.cxx b/src/common/StateManager.cxx index 706f8f019..1b6b2ea7a 100644 --- a/src/common/StateManager.cxx +++ b/src/common/StateManager.cxx @@ -142,17 +142,31 @@ void StateManager::toggleTimeMachine() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool StateManager::rewindState(uInt32 numStates) +bool StateManager::addState(const string& message, bool timeMachine) { RewindManager& r = myOSystem.state().rewindManager(); - return r.rewindState(numStates); + return r.addState(message, timeMachine); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -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); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/StateManager.hxx b/src/common/StateManager.hxx index 0e322f8dd..8206b61b3 100644 --- a/src/common/StateManager.hxx +++ b/src/common/StateManager.hxx @@ -72,14 +72,24 @@ class StateManager void setRewindMode(Mode mode) { myActiveMode = mode; } /** - Rewinds one state; this uses the RewindManager for its functionality. + Adds one state ; this uses the RewindManager for its functionality. */ - bool rewindState(uInt32 numStates = 1); + bool addState(const string& message, bool timeMachine = false); /** - 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. diff --git a/src/debugger/Debugger.cxx b/src/debugger/Debugger.cxx index 95da6a018..ceb88aade 100644 --- a/src/debugger/Debugger.cxx +++ b/src/debugger/Debugger.cxx @@ -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(); diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index 8b0038e1f..c3a018033 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -300,27 +300,19 @@ void EventHandler::handleKeyEvent(StellaKey key, StellaMod mod, bool state) 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: @@ -1223,14 +1215,14 @@ bool EventHandler::eventStateChange(Event::Type type) break; case Event::OptionsMenuMode: - if(myState == EventHandlerState::EMULATION) + if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE) enterMenuMode(EventHandlerState::OPTIONSMENU); else handled = false; break; case Event::CmdMenuMode: - if(myState == EventHandlerState::EMULATION) + if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE) enterMenuMode(EventHandlerState::CMDMENU); else if(myState == EventHandlerState::CMDMENU) leaveMenuMode(); @@ -1239,7 +1231,7 @@ bool EventHandler::eventStateChange(Event::Type type) break; case Event::TimeMachineMode: - if(myState == EventHandlerState::EMULATION) + if(myState == EventHandlerState::EMULATION || myState == EventHandlerState::PAUSE) enterMenuMode(EventHandlerState::TIMEMACHINE); else if(myState == EventHandlerState::TIMEMACHINE) leaveMenuMode(); @@ -1248,8 +1240,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) @@ -2141,6 +2132,19 @@ void EventHandler::leaveDebugMode() #endif } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void EventHandler::enterTimeMachineMenuMode(uInt32 numWinds, bool unwind) +{ + // TODO: maybe remove this state if we leave the menu at the last state + myOSystem.state().addState("enter Time Machine menu", false); // force new state + myOSystem.state().windStates(numWinds, unwind); + + // TODO: probably this check is not necessary + if(myState != EventHandlerState::TIMEMACHINE) + // TODO: display last wind message in time machine dialog + enterMenuMode(EventHandlerState::TIMEMACHINE); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::setEventState(EventHandlerState state) { diff --git a/src/emucore/EventHandler.hxx b/src/emucore/EventHandler.hxx index 0564c5d2e..c1539d38f 100644 --- a/src/emucore/EventHandler.hxx +++ b/src/emucore/EventHandler.hxx @@ -137,6 +137,7 @@ class EventHandler void leaveMenuMode(); bool enterDebugMode(); void leaveDebugMode(); + void enterTimeMachineMenuMode(uInt32 numWinds, bool unwind); void takeSnapshot(uInt32 number = 0); /** diff --git a/src/gui/TimeMachineDialog.cxx b/src/gui/TimeMachineDialog.cxx index d44a59ad5..ef1cb678d 100644 --- a/src/gui/TimeMachineDialog.cxx +++ b/src/gui/TimeMachineDialog.cxx @@ -383,10 +383,11 @@ void TimeMachineDialog::handleWinds(Int32 numWinds) if(numWinds) { uInt64 startCycles = instance().console().tia().cycles(); - if(numWinds < 0) r.rewindState(-numWinds); - else if(numWinds > 0) r.unwindState(numWinds); + if(numWinds < 0) r.rewindStates(-numWinds); + else if(numWinds > 0) r.unwindStates(numWinds); string message = r.getUnitString(instance().console().tia().cycles() - startCycles); + // TODO: add message text from addState() myMessageWidget->setLabel((numWinds < 0 ? "(-" : "(+") + message + ")"); } // Update time From d7cc2036dff3a388068b8e2984abd6ed460d74bf Mon Sep 17 00:00:00 2001 From: thrust26 Date: Sun, 28 Jan 2018 11:21:52 +0100 Subject: [PATCH 06/13] some refinements for additional save states creation --- src/common/StateManager.cxx | 10 +++++++--- src/common/StateManager.hxx | 5 +++-- src/debugger/Debugger.cxx | 3 ++- src/emucore/EventHandler.cxx | 16 ++++++++-------- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/common/StateManager.cxx b/src/common/StateManager.cxx index 1b6b2ea7a..5ab51fb8f 100644 --- a/src/common/StateManager.cxx +++ b/src/common/StateManager.cxx @@ -142,10 +142,14 @@ void StateManager::toggleTimeMachine() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool StateManager::addState(const string& message, bool timeMachine) +bool StateManager::addExtraState(const string& message) { - RewindManager& r = myOSystem.state().rewindManager(); - return r.addState(message, timeMachine); + if(myActiveMode == Mode::TimeMachine) + { + RewindManager& r = myOSystem.state().rewindManager(); + return r.addState(message); + } + return false; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/StateManager.hxx b/src/common/StateManager.hxx index 8206b61b3..851694299 100644 --- a/src/common/StateManager.hxx +++ b/src/common/StateManager.hxx @@ -72,9 +72,10 @@ class StateManager void setRewindMode(Mode mode) { myActiveMode = mode; } /** - Adds 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 addState(const string& message, bool timeMachine = false); + bool addExtraState(const string& message); /** Rewinds states; this uses the RewindManager for its functionality. diff --git a/src/debugger/Debugger.cxx b/src/debugger/Debugger.cxx index ceb88aade..7d5e28404 100644 --- a/src/debugger/Debugger.cxx +++ b/src/debugger/Debugger.cxx @@ -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); diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index c3a018033..45bb3fe58 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -1232,7 +1232,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 @@ -2135,14 +2135,14 @@ void EventHandler::leaveDebugMode() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void EventHandler::enterTimeMachineMenuMode(uInt32 numWinds, bool unwind) { - // TODO: maybe remove this state if we leave the menu at the last state - myOSystem.state().addState("enter Time Machine menu", false); // force new state - myOSystem.state().windStates(numWinds, 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 menu"); // force new state + if (numWinds) + myOSystem.state().windStates(numWinds, unwind); - // TODO: probably this check is not necessary - if(myState != EventHandlerState::TIMEMACHINE) - // TODO: display last wind message in time machine dialog - enterMenuMode(EventHandlerState::TIMEMACHINE); + // TODO: display last wind message (numWinds != 0) in time machine dialog + enterMenuMode(EventHandlerState::TIMEMACHINE); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 05d17496abd4fd15aa851b611c610916802b19b7 Mon Sep 17 00:00:00 2001 From: thrust26 Date: Sun, 28 Jan 2018 11:35:14 +0100 Subject: [PATCH 07/13] minor wording fix --- src/emucore/EventHandler.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index 45bb3fe58..3842b2ca3 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -2137,7 +2137,7 @@ 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 menu"); // force new state + myOSystem.state().addExtraState("enter Time Machine dialog"); // force new state if (numWinds) myOSystem.state().windStates(numWinds, unwind); From de7fd7401be4472485b8cc2a56cb8e338d6acea3 Mon Sep 17 00:00:00 2001 From: Stephen Anthony Date: Sun, 28 Jan 2018 12:21:22 -0330 Subject: [PATCH 08/13] Synchronized shortcuts in TimeMachine to match those from EventHandler. --- src/emucore/EventHandler.cxx | 4 +- src/gui/TimeMachineDialog.cxx | 85 ++++++++++++++++++----------------- src/gui/TimeMachineDialog.hxx | 1 + 3 files changed, 46 insertions(+), 44 deletions(-) diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index 3842b2ca3..ed8721262 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -294,7 +294,7 @@ 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) @@ -2138,8 +2138,6 @@ 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 - if (numWinds) - myOSystem.state().windStates(numWinds, unwind); // TODO: display last wind message (numWinds != 0) in time machine dialog enterMenuMode(EventHandlerState::TIMEMACHINE); diff --git a/src/gui/TimeMachineDialog.cxx b/src/gui/TimeMachineDialog.cxx index ef1cb678d..bd43e282e 100644 --- a/src/gui/TimeMachineDialog.cxx +++ b/src/gui/TimeMachineDialog.cxx @@ -41,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, @@ -184,7 +167,6 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, buttonHeight = BUTTON_H + 10, rowHeight = font.getLineHeight(); -// WidgetArray wid; int xpos, ypos; // Set real dimensions @@ -210,7 +192,6 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, 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); -// wid.push_back(myTimeline); ypos += rowHeight; // Add time info @@ -224,45 +205,36 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, // 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; 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); - -// addToFocusList(wid); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -280,16 +252,6 @@ void TimeMachineDialog::loadConfig() RewindManager& r = instance().state().rewindManager(); IntArray cycles = r.cyclesList(); -#if 0 // if we won't be using focused widget, this code can disappear - // Set default focus - bool timelineEnabled = cycles.size() > 0; - myTimeline->setEnabled(timelineEnabled); - if(timelineEnabled) - setFocus(myTimeline); - else - setFocus(myPlayWidget); -#endif - // Set range for timeline myTimeline->setMaxValue(std::max(Int32(cycles.size()), 1)); @@ -305,6 +267,42 @@ void TimeMachineDialog::loadConfig() 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) @@ -385,10 +383,15 @@ void TimeMachineDialog::handleWinds(Int32 numWinds) uInt64 startCycles = instance().console().tia().cycles(); if(numWinds < 0) r.rewindStates(-numWinds); else if(numWinds > 0) r.unwindStates(numWinds); - string message = r.getUnitString(instance().console().tia().cycles() - startCycles); - // TODO: add message text from addState() - 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())); diff --git a/src/gui/TimeMachineDialog.hxx b/src/gui/TimeMachineDialog.hxx index 75d6da1a1..e842386d5 100644 --- a/src/gui/TimeMachineDialog.hxx +++ b/src/gui/TimeMachineDialog.hxx @@ -33,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() */ From db9cd2834674195dc2af0b822fbf51614d697d1a Mon Sep 17 00:00:00 2001 From: Stephen Anthony Date: Sun, 28 Jan 2018 13:28:17 -0330 Subject: [PATCH 09/13] Added rewind/unwind shortcuts from TimeMachine to debugger. - disabled the old Ctrl-r/y shortcuts, but the code is still there for experimentation --- src/debugger/gui/DebuggerDialog.cxx | 41 ++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/debugger/gui/DebuggerDialog.cxx b/src/debugger/gui/DebuggerDialog.cxx index 9c81fcfdf..117e2ea45 100644 --- a/src/debugger/gui/DebuggerDialog.cxx +++ b/src/debugger/gui/DebuggerDialog.cxx @@ -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; } From 63acb76911ba3136ba6446a23b40351149c7044a Mon Sep 17 00:00:00 2001 From: thrust26 Date: Sun, 28 Jan 2018 20:22:04 +0100 Subject: [PATCH 10/13] changed text color (else invisible in Classic mode) --- src/gui/TimeMachineDialog.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gui/TimeMachineDialog.cxx b/src/gui/TimeMachineDialog.cxx index bd43e282e..eccb1a12a 100644 --- a/src/gui/TimeMachineDialog.cxx +++ b/src/gui/TimeMachineDialog.cxx @@ -181,10 +181,10 @@ 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, @@ -196,10 +196,10 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, // 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 @@ -234,7 +234,7 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, // Add message myMessageWidget = new StaticTextWidget(this, font, xpos, ypos + 3, " ", TextAlign::Left, kBGColor); - myMessageWidget->setTextColor(kWidColor); + myMessageWidget->setTextColor(kColorInfo); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From b11643881a018a0f0a66d83e8f960e7cf8cdaba2 Mon Sep 17 00:00:00 2001 From: Stephen Anthony Date: Sun, 28 Jan 2018 20:11:21 -0330 Subject: [PATCH 11/13] Intervals on timeline are now graphically proportional to time: - intervals are only valid when using key shortcuts - using the mouse to select/scroll does not show proper proportions yet --- src/common/LinkedObjectPool.hxx | 6 +++++ src/common/RewindManager.cxx | 2 +- src/gui/TimeLineWidget.cxx | 41 +++++++++++++++++++++++++-------- src/gui/TimeLineWidget.hxx | 14 +++++++---- src/gui/TimeMachineDialog.cxx | 6 +++-- 5 files changed, 51 insertions(+), 18 deletions(-) diff --git a/src/common/LinkedObjectPool.hxx b/src/common/LinkedObjectPool.hxx index 3bbabfb9f..1b434cea8 100644 --- a/src/common/LinkedObjectPool.hxx +++ b/src/common/LinkedObjectPool.hxx @@ -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. */ diff --git a/src/common/RewindManager.cxx b/src/common/RewindManager.cxx index 216e03943..91273d11d 100644 --- a/src/common/RewindManager.cxx +++ b/src/common/RewindManager.cxx @@ -344,7 +344,7 @@ IntArray RewindManager::cyclesList() const IntArray arr; uInt64 firstCycle = getFirstCycles(); - for(auto it = myStateList.first(); it != myStateList.last(); ++it) + for(auto it = myStateList.cbegin(); it != myStateList.cend(); ++it) arr.push_back(uInt32(it->cycles - firstCycle)); return arr; diff --git a/src/gui/TimeLineWidget.cxx b/src/gui/TimeLineWidget.cxx index 6829ee4e1..d99c7d58a 100644 --- a/src/gui/TimeLineWidget.cxx +++ b/src/gui/TimeLineWidget.cxx @@ -25,13 +25,16 @@ #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), + _stepValue__(1), _valueMin(0), _valueMax(100), _isDragging(false), @@ -45,6 +48,8 @@ TimeLineWidget::TimeLineWidget(GuiObject* boss, const GUI::Font& font, _labelWidth = _font.getStringWidth(_label); _w = w + _labelWidth; + + _stepValue.reserve(100); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -73,13 +78,24 @@ void TimeLineWidget::setMaxValue(int value) _valueMax = value; } -#if 0 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void TimeLineWidget::setStepValue(int value) +void TimeLineWidget::setStepValues(const IntArray& steps) { - _stepValue = value; + // 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); } -#endif // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TimeLineWidget::handleMouseMoved(int x, int y) @@ -132,13 +148,13 @@ bool TimeLineWidget::handleEvent(Event::Type e) case Event::UIDown: case Event::UILeft: case Event::UIPgDown: - setValue(_value - _stepValue); + setValue(_value - _stepValue__); break; case Event::UIUp: case Event::UIRight: case Event::UIPgUp: - setValue(_value + _stepValue); + setValue(_value + _stepValue__); break; case Event::UIHome: @@ -196,9 +212,14 @@ int TimeLineWidget::valueToPos(int value) { if(value < _valueMin) value = _valueMin; else if(value > _valueMax) value = _valueMax; - int range = std::max(_valueMax - _valueMin, 1); // don't divide by zero - return ((_w - _labelWidth - 4) * (value - _valueMin) / range); + 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; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -207,5 +228,5 @@ 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); + return value - (value % _stepValue__); } diff --git a/src/gui/TimeLineWidget.hxx b/src/gui/TimeLineWidget.hxx index c2070500b..397ee241e 100644 --- a/src/gui/TimeLineWidget.hxx +++ b/src/gui/TimeLineWidget.hxx @@ -34,10 +34,12 @@ class TimeLineWidget : public ButtonWidget int getMinValue() const { return _valueMin; } void setMaxValue(int value); int getMaxValue() const { return _valueMax; } -#if 0 - void setStepValue(int value); - int getStepValue() const { return _stepValue; } -#endif + + /** + 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; @@ -52,11 +54,13 @@ class TimeLineWidget : public ButtonWidget int posToValue(int pos); protected: - int _value, _stepValue; + int _value, _stepValue__; int _valueMin, _valueMax; bool _isDragging; int _labelWidth; + IntArray _stepValue; + private: // Following constructors and assignment operators not supported TimeLineWidget() = delete; diff --git a/src/gui/TimeMachineDialog.cxx b/src/gui/TimeMachineDialog.cxx index eccb1a12a..de4b17fd9 100644 --- a/src/gui/TimeMachineDialog.cxx +++ b/src/gui/TimeMachineDialog.cxx @@ -192,6 +192,7 @@ TimeMachineDialog::TimeMachineDialog(OSystem& osystem, DialogContainer& parent, 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 @@ -252,8 +253,9 @@ void TimeMachineDialog::loadConfig() RewindManager& r = instance().state().rewindManager(); IntArray cycles = r.cyclesList(); - // Set range for timeline - myTimeline->setMaxValue(std::max(Int32(cycles.size()), 1)); + // Set range and intervals for timeline + myTimeline->setMaxValue(cycles.size() - 1); + myTimeline->setStepValues(cycles); // Enable blending (only once is necessary) if(!surface().attributes().blending) From aa7dd33dc851df315a23b46cacaf8d4e31518955 Mon Sep 17 00:00:00 2001 From: thrust26 Date: Mon, 29 Jan 2018 16:58:26 +0100 Subject: [PATCH 12/13] removed keeping very 1st state with compression enabled --- docs/index.html | 3 +-- src/common/RewindManager.cxx | 15 ++++----------- src/common/RewindManager.hxx | 3 +++ 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/docs/index.html b/docs/index.html index 49c319547..a89ca6d67 100644 --- a/docs/index.html +++ b/docs/index.html @@ -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.
+ becomes larger the further they are back in time.
(*) Compresion only works if 'Uncompressed size' is smaller than 'Buffer size'. diff --git a/src/common/RewindManager.cxx b/src/common/RewindManager.cxx index 91273d11d..41359a3b4 100644 --- a/src/common/RewindManager.cxx +++ b/src/common/RewindManager.cxx @@ -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; @@ -88,7 +88,6 @@ void RewindManager::setup() else maxFactor = myFactor; } -//cerr << "factor " << myFactor << endl; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -226,38 +225,32 @@ 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::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; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -305,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]; diff --git a/src/common/RewindManager.hxx b/src/common/RewindManager.hxx index debe0cee8..6066beadf 100644 --- a/src/common/RewindManager.hxx +++ b/src/common/RewindManager.hxx @@ -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 From 9a3516b6106c45fde3483e1924765c63f62fe487 Mon Sep 17 00:00:00 2001 From: thrust26 Date: Mon, 29 Jan 2018 17:13:17 +0100 Subject: [PATCH 13/13] fixed compression factor calculation due to previous commit --- src/common/RewindManager.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/RewindManager.cxx b/src/common/RewindManager.cxx index 41359a3b4..165eb1e19 100644 --- a/src/common/RewindManager.cxx +++ b/src/common/RewindManager.cxx @@ -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;