openMSX
ReverseManager.hh
Go to the documentation of this file.
1#ifndef REVERSEMANGER_HH
2#define REVERSEMANGER_HH
3
4#include "Schedulable.hh"
5#include "EventListener.hh"
6#include "Command.hh"
7#include "EmuTime.hh"
8#include "MemBuffer.hh"
9#include "DeltaBlock.hh"
10#include "outer.hh"
11#include <cstdint>
12#include <deque>
13#include <span>
14#include <map>
15#include <memory>
16#include <string_view>
17#include <vector>
18
19namespace openmsx {
20
21class EventDelay;
22class EventDistributor;
23class Interpreter;
24class MSXMotherBoard;
25class Keyboard;
26class StateChange;
27class TclObject;
28
29class ReverseManager final : private EventListener
30{
31public:
32 static constexpr std::string_view REPLAY_DIR = "replays";
33 static constexpr std::string_view REPLAY_EXTENSION = ".omr";
34
35public:
36 explicit ReverseManager(MSXMotherBoard& motherBoard);
38
39 // Keyboard is special because we need to transfer the host keyboard
40 // state on 'reverse goto' to be able to resynchronize when replay
41 // stops. See Keyboard::transferHostKeyMatrix() for more info.
42 void registerKeyboard(Keyboard& keyboard_) {
43 keyboard = &keyboard_;
44 }
45
46 // To not loose any events we need to flush delayed events before
47 // switching machine. See comments in goTo() for more info.
48 void registerEventDelay(EventDelay& eventDelay_) {
49 eventDelay = &eventDelay_;
50 }
51
52 // Should only be used by MSXMotherBoard to be able to transfer
53 // reRecordCount to ReverseManager for version 2 of MSXMotherBoard
54 // serializers.
55 void setReRecordCount(unsigned count) {
56 reRecordCount = count;
57 }
58
59 [[nodiscard]] bool isReplaying() const;
60 void stopReplay(EmuTime::param time) noexcept;
61
62 template<typename T, typename... Args>
63 StateChange& record(EmuTime::param time, Args&& ...args) {
64 assert(!isReplaying());
65 ++replayIndex;
66 history.events.push_back(std::make_unique<T>(time, std::forward<Args>(args)...));
67 return *history.events.back();
68 }
69
70 [[nodiscard]] bool isCollecting() const { return collecting; }
71 [[nodiscard]] bool isViewOnlyMode() const;
72 [[nodiscard]] double getBegin() const;
73 [[nodiscard]] double getEnd() const;
74 [[nodiscard]] double getCurrent() const;
75 [[nodiscard]] std::vector<double> getSnapshotTimes() const;
76
77private:
78 struct ReverseChunk {
79 ReverseChunk() : time(EmuTime::zero()) {}
80
81 EmuTime time;
82 std::vector<std::shared_ptr<DeltaBlock>> deltaBlocks;
83 MemBuffer<uint8_t> savestate;
84 size_t size;
85
86 // Number of recorded events (or replay index) when this
87 // snapshot was created. So when going back replay should
88 // start at this index.
89 unsigned eventCount;
90 };
91 using Chunks = std::map<unsigned, ReverseChunk>;
92 using Events = std::deque<std::unique_ptr<StateChange>>;
93
94 struct ReverseHistory {
95 void swap(ReverseHistory& other) noexcept;
96 void clear();
97 [[nodiscard]] unsigned getNextSeqNum(EmuTime::param time) const;
98
99 Chunks chunks;
100 Events events;
101 LastDeltaBlocks lastDeltaBlocks;
102 };
103
104 void start();
105 void stop();
106 void status(TclObject& result) const;
107 void debugInfo(TclObject& result) const;
108 void goBack(std::span<const TclObject> tokens);
109 void goTo(std::span<const TclObject> tokens);
110 void saveReplay(Interpreter& interp,
111 std::span<const TclObject> tokens, TclObject& result);
112 void loadReplay(Interpreter& interp,
113 std::span<const TclObject> tokens, TclObject& result);
114
115 void signalStopReplay(EmuTime::param time);
116 [[nodiscard]] EmuTime::param getEndTime(const ReverseHistory& history) const;
117 void goTo(EmuTime::param targetTime, bool noVideo);
118 void goTo(EmuTime::param targetTime, bool noVideo,
119 ReverseHistory& history, bool sameTimeLine);
120 void transferHistory(ReverseHistory& oldHistory,
121 unsigned oldEventCount);
122 void transferState(MSXMotherBoard& newBoard);
123 void takeSnapshot(EmuTime::param time);
124 void schedule(EmuTime::param time);
125 void replayNextEvent();
126 template<unsigned N> void dropOldSnapshots(unsigned count);
127
128 // Schedulable
129 struct SyncNewSnapshot final : Schedulable {
130 friend class ReverseManager;
131 explicit SyncNewSnapshot(Scheduler& s) : Schedulable(s) {}
132 void executeUntil(EmuTime::param /*time*/) override {
133 auto& rm = OUTER(ReverseManager, syncNewSnapshot);
134 rm.execNewSnapshot();
135 }
136 } syncNewSnapshot;
137 struct SyncInputEvent final : Schedulable {
138 friend class ReverseManager;
139 explicit SyncInputEvent(Scheduler& s) : Schedulable(s) {}
140 void executeUntil(EmuTime::param /*time*/) override {
141 auto& rm = OUTER(ReverseManager, syncInputEvent);
142 rm.execInputEvent();
143 }
144 } syncInputEvent;
145
146 void execNewSnapshot();
147 void execInputEvent();
148 [[nodiscard]] EmuTime::param getCurrentTime() const { return syncNewSnapshot.getCurrentTime(); }
149
150 // EventListener
151 int signalEvent(const Event& event) override;
152
153private:
154 MSXMotherBoard& motherBoard;
155 EventDistributor& eventDistributor;
156
157 struct ReverseCmd final : Command {
158 explicit ReverseCmd(CommandController& controller);
159 void execute(std::span<const TclObject> tokens, TclObject& result) override;
160 [[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
161 void tabCompletion(std::vector<std::string>& tokens) const override;
162 } reverseCmd;
163
164 Keyboard* keyboard = nullptr;
165 EventDelay* eventDelay = nullptr;
166 ReverseHistory history;
167 unsigned replayIndex = 0;
168 bool collecting = false;
169 bool pendingTakeSnapshot = false;
170
171 unsigned reRecordCount = 0;
172
173 friend struct Replay;
174};
175
176} // namespace openmsx
177
178#endif
This class is responsible for translating host events into MSX events.
Definition EventDelay.hh:27
void setReRecordCount(unsigned count)
void registerKeyboard(Keyboard &keyboard_)
void stopReplay(EmuTime::param time) noexcept
std::vector< double > getSnapshotTimes() const
static constexpr std::string_view REPLAY_DIR
void registerEventDelay(EventDelay &eventDelay_)
static constexpr std::string_view REPLAY_EXTENSION
StateChange & record(EmuTime::param time, Args &&...args)
Schedulable(const Schedulable &)=delete
Base class for all external MSX state changing events.
This file implemented 3 utility functions:
Definition Autofire.cc:11
std::variant< KeyUpEvent, KeyDownEvent, MouseMotionEvent, MouseButtonUpEvent, MouseButtonDownEvent, MouseWheelEvent, JoystickAxisMotionEvent, JoystickHatEvent, JoystickButtonUpEvent, JoystickButtonDownEvent, OsdControlReleaseEvent, OsdControlPressEvent, WindowEvent, TextEvent, FileDropEvent, QuitEvent, FinishFrameEvent, CliCommandEvent, GroupEvent, BootEvent, FrameDrawnEvent, BreakEvent, SwitchRendererEvent, TakeReverseSnapshotEvent, AfterTimedEvent, MachineLoadedEvent, MachineActivatedEvent, MachineDeactivatedEvent, MidiInReaderEvent, MidiInWindowsEvent, MidiInCoreMidiEvent, MidiInCoreMidiVirtualEvent, MidiInALSAEvent, Rs232TesterEvent, Rs232NetEvent, ImGuiDelayedActionEvent, ImGuiActiveEvent > Event
Definition Event.hh:446
#define OUTER(type, member)
Definition outer.hh:42