OXYGENHUB
◆ main openmsx / src / events / HotKey.hh
4.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#ifndef HOTKEY_HH
2
#define HOTKEY_HH
3
4
#include "Event.hh"
5
#include "EventDistributor.hh"
6
#include "EventListener.hh"
7
8
#include "Command.hh"
9
#include "RTSchedulable.hh"
10
#include "TclObject.hh"
11
12
#include <map>
13
#include <optional>
14
#include <string>
15
#include <string_view>
16
#include <vector>
17
18
namespace openmsx {
19
20
class GlobalCommandController;
21
class RTScheduler;
22
23
class HotKey final : public RTSchedulable
24
{
25
public:
26
	struct Data {
27
		std::string_view key;
28
		std::string_view cmd;
29
		bool repeat = false;
30
		bool event = false;
31
		bool msx = false;
32
	};
33
34
	struct HotKeyInfo {
35
		HotKeyInfo(Event event_, std::string command_,
36
		           bool repeat_ = false, bool passEvent_ = false, bool msx_ = false)
37
			: event(std::move(event_)), command(std::move(command_))
38
			, repeat(repeat_), passEvent(passEvent_), msx(msx_) {}
39
		Event event;
40
		std::string command;
41
		bool repeat;
42
		bool passEvent; // whether to pass event with args back to command
43
		bool msx; // false->global binding,  true->only active when msx window has focus
44
	};
45
	using BindMap = std::vector<HotKeyInfo>; // unsorted
46
	using KeySet  = std::vector<Event>;   // unsorted
47
48
	HotKey(RTScheduler& rtScheduler,
49
	       GlobalCommandController& commandController,
50
	       EventDistributor& eventDistributor);
51
52
	void loadInit();
53
	void loadBind(const Data& data);
54
	void loadUnbind(std::string_view key);
55
56
	template<typename XmlStream>
57
	void saveBindings(XmlStream& xml) const
58
	{
59
		xml.begin("bindings");
60
		// add explicit binds
61
		for (const auto& k : boundKeys) {
62
			xml.begin("bind");
63
			xml.attribute("key", toString(k));
64
			const auto& info = *find_unguarded(cmdMap, k, &HotKeyInfo::event);
65
			if (info.repeat) {
66
				xml.attribute("repeat", "true");
67
			}
68
			if (info.passEvent) {
69
				xml.attribute("event", "true");
70
			}
71
			if (info.msx) {
72
				xml.attribute("msx", "true");
73
			}
74
			xml.data(info.command);
75
			xml.end("bind");
76
		}
77
		// add explicit unbinds
78
		for (const auto& k : unboundKeys) {
79
			xml.begin("unbind");
80
			xml.attribute("key", toString(k));
81
			xml.end("unbind");
82
		}
83
		xml.end("bindings");
84
	}
85
86
	[[nodiscard]] const auto& getGlobalBindings() const { return cmdMap; }
87
88
private:
89
	struct LayerInfo {
90
		std::string layer;
91
		bool blocking;
92
	};
93
94
	void initDefaultBindings();
95
	void bind         (HotKeyInfo&& info);
96
	void unbind       (const Event& event);
97
	void bindDefault  (HotKeyInfo&& info);
98
	void unbindDefault(const Event& event);
99
	void bindLayer    (HotKeyInfo&& info, const std::string& layer);
100
	void unbindLayer  (const Event& event, const std::string& layer);
101
	void unbindFullLayer(const std::string& layer);
102
	void activateLayer  (std::string layer, bool blocking);
103
	void deactivateLayer(std::string_view layer);
104
105
	bool executeEvent(const Event& event, EventDistributor::Priority priority);
106
	void executeBinding(const Event& event, const HotKeyInfo& info);
107
	void startRepeat  (const Event& event);
108
	void stopRepeat();
109
110
	bool signalEvent(const Event& event, EventDistributor::Priority priority);
111
112
	// RTSchedulable
113
	void executeRT() override;
114
115
private:
116
	class BindCmd final : public Command {
117
	public:
118
		BindCmd(CommandController& commandController, HotKey& hotKey,
119
			bool defaultCmd);
120
		void execute(std::span<const TclObject> tokens, TclObject& result) override;
121
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
122
	private:
123
		HotKey& hotKey;
124
		const bool defaultCmd;
125
	};
126
	BindCmd bindCmd;
127
	BindCmd bindDefaultCmd;
128
129
	class UnbindCmd final : public Command {
130
	public:
131
		UnbindCmd(CommandController& commandController, HotKey& hotKey,
132
			  bool defaultCmd);
133
		void execute(std::span<const TclObject> tokens, TclObject& result) override;
134
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
135
	private:
136
		HotKey& hotKey;
137
		const bool defaultCmd;
138
	};
139
	UnbindCmd unbindCmd;
140
	UnbindCmd unbindDefaultCmd;
141
142
	struct ActivateCmd final : Command {
143
		explicit ActivateCmd(CommandController& commandController);
144
		void execute(std::span<const TclObject> tokens, TclObject& result) override;
145
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
146
	} activateCmd;
147
148
	struct DeactivateCmd final : Command {
149
		explicit DeactivateCmd(CommandController& commandController);
150
		void execute(std::span<const TclObject> tokens, TclObject& result) override;
151
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
152
	} deactivateCmd;
153
154
	BindMap cmdMap;
155
	BindMap defaultMap;
156
	std::map<std::string, BindMap, std::less<>> layerMap;
157
	std::vector<LayerInfo> activeLayers;
158
	KeySet boundKeys;
159
	KeySet unboundKeys;
160
	GlobalCommandController& commandController;
161
	EventDistributor& eventDistributor;
162
	std::optional<Event> lastEvent;
163
164
	class Listener : public EventListener {
165
	public:
166
		Listener(HotKey& hotKey, EventDistributor::Priority priority);
167
		~Listener();
168
		bool signalEvent(const Event& event) override;
169
	private:
170
		HotKey& hotKey;
171
		EventDistributor::Priority priority;
172
	};
173
	Listener listenerHigh;
174
	Listener listenerLow;
175
};
176
177
} // namespace openmsx
178
179
#endif