OXYGENHUB
◆ main openmsx / src / input / StateChangeDistributor.hh
3.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef STATECHANGEDISTRIBUTOR_HH
2
#define STATECHANGEDISTRIBUTOR_HH
3
4
#include "EmuTime.hh"
5
#include "ReverseManager.hh"
6
#include "StateChangeListener.hh"
7
8
#include "ScopedAssign.hh"
9
10
#include <utility>
11
#include <vector>
12
13
namespace openmsx {
14
15
class StateChangeDistributor
16
{
17
public:
18
	StateChangeDistributor() = default;
19
	StateChangeDistributor(const StateChangeDistributor&) = delete;
20
	StateChangeDistributor(StateChangeDistributor&&) = delete;
21
	StateChangeDistributor& operator=(const StateChangeDistributor&) = delete;
22
	StateChangeDistributor& operator=(StateChangeDistributor&&) = delete;
23
	~StateChangeDistributor();
24
25
	/** (Un)registers the given object to receive state change events.
26
	 * @param listener Listener that will be notified when an event arrives.
27
	 */
28
	void registerListener  (StateChangeListener& listener);
29
	void unregisterListener(StateChangeListener& listener);
30
31
	/** (Un)registers the given object to receive state change events.
32
	 * @param recorder Listener that will be notified when an event arrives.
33
	 * These two methods are very similar to the two above. The difference
34
	 * is that there can be at most one registered recorder. This recorder
35
	 * object is always the first object that gets informed about state
36
	 * changing events.
37
	 */
38
	void registerRecorder  (ReverseManager& recorder);
39
	void unregisterRecorder(ReverseManager& recorder);
40
41
	/** Deliver the event to all registered listeners
42
	 * MSX input devices should call the distributeNew() version, only the
43
	 * replayer should call the distributeReplay() version.
44
	 * These two different versions are used to detect the transition from
45
	 * replayed events to live events. Note that a transition from live to
46
	 * replay is not allowed. This transition should be done by creating a
47
	 * new StateChangeDistributor object (object always starts in replay
48
	 * state), but this is automatically taken care of because replay
49
	 * always starts from a freshly restored snapshot.
50
	 * Instead of a 'StateChange' parameter this method takes a
51
	 * template-parameter 'T' and run-time parameters 'time' and '...arg'
52
	 * that together can construct a 'StateChange' subclass 'T'.
53
	 */
54
	template<typename T, typename... Args>
55
	void distributeNew(EmuTime time, Args&& ...args) {
56
		if (recorder) {
57
			if (isReplaying()) {
58
				if (viewOnlyMode || blockNewEventsDuringReplay) return;
59
				stopReplay(time);
60
			}
61
			assert(!isReplaying());
62
			const auto& event = recorder->record<T>(time, std::forward<Args>(args)...);
63
			distribute(event); // might throw, ok
64
		} else {
65
			StateChange event(std::in_place_type_t<T>{},
66
			                  time, std::forward<Args>(args)...);
67
			distribute(event); // might throw, ok
68
		}
69
	}
70
71
	void distributeReplay(const StateChange& event) const {
72
		assert(isReplaying());
73
		distribute(event);
74
	}
75
76
	/** Explicitly stop replay.
77
	 * Should be called when replay->live transition cannot be signaled via
78
	 * a new event, so for example when we reach the end of the replay log.
79
	 * It's OK to call this method when replay was already stopped, in that
80
	 * case this call has no effect.
81
	 */
82
	void stopReplay(EmuTime time);
83
84
	/**
85
	 * Set viewOnlyMode. Call this if you don't want distributeNew events
86
	 * to stop replaying and go to live events (value=true).
87
	 * @param value false if new events stop replay mode
88
	 */
89
	void setViewOnlyMode(bool value) { viewOnlyMode = value; }
90
	[[nodiscard]] bool isViewOnlyMode() const { return viewOnlyMode; }
91
92
	[[nodiscard]] auto tempBlockNewEventsDuringReplay() {
93
		return ScopedAssign{blockNewEventsDuringReplay, true};
94
	}
95
96
	[[nodiscard]] bool isReplaying() const;
97
98
private:
99
	[[nodiscard]] bool isRegistered(StateChangeListener* listener) const;
100
	void distribute(const StateChange& event) const;
101
102
private:
103
	std::vector<StateChangeListener*> listeners; // unordered
104
	ReverseManager* recorder = nullptr;
105
	bool viewOnlyMode = false;
106
	bool blockNewEventsDuringReplay = false; // used when executing callbacks during replay
107
};
108
109
} // namespace openmsx
110
111
#endif