OXYGENHUB
◆ main openmsx / src / input / Keyboard.hh
11.1 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef KEYBOARD_HH
2
#define KEYBOARD_HH
3
4
#include "KeyboardSettings.hh"
5
#include "MSXEventListener.hh"
6
#include "StateChangeListener.hh"
7
#include "UnicodeKeymap.hh"
8
9
#include "Event.hh"
10
#include "EventListener.hh"
11
#include "RecordedCommand.hh"
12
#include "Schedulable.hh"
13
#include "SimpleDebuggable.hh"
14
#include "serialize_meta.hh"
15
16
#include <array>
17
#include <cstdint>
18
#include <deque>
19
#include <span>
20
#include <string_view>
21
#include <vector>
22
23
#if defined(_WIN32)
24
#ifndef WIN32_LEAN_AND_MEAN
25
#define WIN32_LEAN_AND_MEAN
26
#endif
27
28
#include <windows.h>
29
#include <imm.h>
30
#endif // _WIN32
31
32
namespace openmsx {
33
34
class MSXMotherBoard;
35
class Scheduler;
36
class CommandController;
37
class DeviceConfig;
38
class EventDistributor;
39
class MSXEventDistributor;
40
class StateChangeDistributor;
41
class TclObject;
42
class Interpreter;
43
44
struct KeyCodeMsxMapping {
45
	SDL_Keycode hostKeyCode;
46
	KeyMatrixPosition msx;
47
};
48
struct ScanCodeMsxMapping {
49
	SDL_Scancode hostScanCode;
50
	KeyMatrixPosition msx;
51
};
52
53
class Keyboard final : private MSXEventListener, private StateChangeListener
54
                     , private Schedulable, private EventListener
55
{
56
public:
57
	static constexpr int MAX_KEYSYM = 0x150;
58
	enum class Matrix : uint8_t { MSX, SVI, CVJOY, SEGA, NUM };
59
60
	/** Constructs a new Keyboard object.
61
	 * @param motherBoard ref to the motherBoard
62
	 * @param scheduler ref to the scheduler
63
	 * @param commandController ref to the command controller
64
	 * @param eventDistributor ref to the emu event distributor
65
	 * @param msxEventDistributor ref to the user input event distributor
66
	 * @param stateChangeDistributor ref to the state change distributor
67
	 * @param matrix which system's keyboard matrix to use
68
	 * @param config ref to the device configuration
69
	 */
70
	Keyboard(MSXMotherBoard& motherBoard, Scheduler& scheduler,
71
	         CommandController& commandController,
72
	         EventDistributor& eventDistributor,
73
	         MSXEventDistributor& msxEventDistributor,
74
	         StateChangeDistributor& stateChangeDistributor,
75
	         Matrix matrix, const DeviceConfig& config);
76
77
	~Keyboard();
78
79
	[[nodiscard]] const MsxChar2Unicode& getMsxChar2Unicode() const;
80
81
	/** Returns a pointer to the current KeyBoard matrix
82
	 */
83
	[[nodiscard]] std::span<const uint8_t, KeyMatrixPosition::NUM_ROWS> getKeys() const;
84
85
	void transferHostKeyMatrix(const Keyboard& source);
86
	void setFocus(bool newFocus, EmuTime time);
87
88
	template<typename Archive>
89
	void serialize(Archive& ar, unsigned version);
90
91
private:
92
	// MSXEventListener
93
	void signalMSXEvent(const Event& event,
94
	                    EmuTime time) noexcept override;
95
	// StateChangeListener
96
	void signalStateChange(const StateChange& event) override;
97
	void stopReplay(EmuTime time) noexcept override;
98
99
	// Schedulable
100
	void executeUntil(EmuTime time) override;
101
102
	// EventListener
103
	bool signalEvent(const Event& event) override;
104
105
	void syncHostKeyMatrix(EmuTime time);
106
	/** Returns true iff the given key is currently held down in the MSX
107
	  * keyboard matrix. Pressing such a key again does not produce a new
108
	  * key-press edge, so the MSX will not see it as a new key press.
109
	  */
110
	[[nodiscard]] bool isKeyMatrixPressed(KeyMatrixPosition pos) const;
111
	void pressKeyMatrixEvent(EmuTime time, KeyMatrixPosition pos);
112
	void releaseKeyMatrixEvent(EmuTime time, KeyMatrixPosition pos);
113
	void changeKeyMatrixEvent (EmuTime time, uint8_t row, uint8_t newValue);
114
115
	void processCapslockEvent(EmuTime time, bool down);
116
	void processCodeKanaChange(EmuTime time, bool down);
117
	void processGraphChange(EmuTime time, bool down);
118
	void processKeypadEnterKey(EmuTime time, bool down);
119
	void processSdlKey(EmuTime time, SDLKey key);
120
	bool processQueuedEvent(const Event& event, EmuTime time);
121
	bool processKeyEvent(EmuTime time, bool down, const KeyEvent& keyEvent);
122
	void updateKeyMatrix(EmuTime time, bool down, KeyMatrixPosition pos);
123
	void processCmd(Interpreter& interp, std::span<const TclObject> tokens, bool up);
124
	bool pressUnicodeByUser(
125
			EmuTime time, UnicodeKeymap::KeyInfo keyInfo, unsigned unicode,
126
			bool down);
127
	uint8_t pressAscii(unsigned unicode, bool down);
128
	void pressLockKeys(uint8_t lockKeysMask, bool down);
129
	[[nodiscard]] bool commonKeys(unsigned unicode1, unsigned unicode2) const;
130
	void debug(const char* format, ...) const;
131
132
	/** Returns a bit vector in which the bit for a modifier is set iff that
133
	  * modifier is a lock key and must be toggled before the given key input
134
	  * can be produced.
135
	  */
136
	uint8_t needsLockToggle(const UnicodeKeymap::KeyInfo& keyInfo) const;
137
138
private:
139
	CommandController& commandController;
140
	MSXEventDistributor& msxEventDistributor;
141
	StateChangeDistributor& stateChangeDistributor;
142
	EventDistributor& eventDistributor;
143
144
	std::span<const KeyCodeMsxMapping> keyCodeTab;
145
	std::span<const ScanCodeMsxMapping> scanCodeTab;
146
147
	const array_with_enum_index<UnicodeKeymap::KeyInfo::Modifier, KeyMatrixPosition>& modifierPos;
148
149
	struct KeyMatrixUpCmd final : RecordedCommand {
150
		KeyMatrixUpCmd(CommandController& commandController,
151
			       StateChangeDistributor& stateChangeDistributor,
152
			       Scheduler& scheduler);
153
		void execute(std::span<const TclObject> tokens, TclObject& result,
154
			     EmuTime time) override;
155
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
156
	} keyMatrixUpCmd;
157
158
	struct KeyMatrixDownCmd final : RecordedCommand {
159
		KeyMatrixDownCmd(CommandController& commandController,
160
				 StateChangeDistributor& stateChangeDistributor,
161
				 Scheduler& scheduler);
162
		void execute(std::span<const TclObject> tokens, TclObject& result,
163
			     EmuTime time) override;
164
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
165
	} keyMatrixDownCmd;
166
167
	class KeyInserter final : public RecordedCommand, public Schedulable {
168
	public:
169
		KeyInserter(CommandController& commandController,
170
			    StateChangeDistributor& stateChangeDistributor,
171
			    Scheduler& scheduler);
172
		[[nodiscard]] bool isActive() const { return isPending().has_value(); }
173
		template<typename Archive>
174
		void serialize(Archive& ar, unsigned version);
175
176
	private:
177
		void type(std::string_view str);
178
		void reschedule(EmuTime time);
179
180
		// Command
181
		void execute(std::span<const TclObject> tokens, TclObject& result,
182
			     EmuTime time) override;
183
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
184
		void tabCompletion(std::vector<std::string>& tokens) const override;
185
186
		// Schedulable
187
		void executeUntil(EmuTime time) override;
188
189
	private:
190
		std::string text_utf8;
191
		unsigned last = 0;
192
		uint8_t lockKeysMask = 0;
193
		bool releaseLast = false;
194
		uint8_t oldLocksOn = 0;
195
196
		bool releaseBeforePress = false;
197
		int typingFrequency = 15;
198
	} keyTypeCmd;
199
200
	struct Msxcode2UnicodeCmd final : public Command {
201
		explicit Msxcode2UnicodeCmd(CommandController& commandController);
202
		void execute(std::span<const TclObject> tokens, TclObject& result) override;
203
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
204
	} msxcode2UnicodeCmd;
205
206
	struct Unicode2MsxcodeCmd final : public Command {
207
		explicit Unicode2MsxcodeCmd(CommandController& commandController);
208
		void execute(std::span<const TclObject> tokens, TclObject& result) override;
209
		[[nodiscard]] std::string help(std::span<const TclObject> tokens) const override;
210
	} unicode2MsxcodeCmd;
211
212
	class CapsLockAligner final : private EventListener, private Schedulable {
213
	public:
214
		CapsLockAligner(EventDistributor& eventDistributor,
215
				Scheduler& scheduler);
216
		~CapsLockAligner();
217
218
	private:
219
		// EventListener
220
		bool signalEvent(const Event& event) override;
221
222
		// Schedulable
223
		void executeUntil(EmuTime time) override;
224
225
		void alignCapsLock(EmuTime time);
226
227
	private:
228
		EventDistributor& eventDistributor;
229
230
		enum CapsLockAlignerStateType : uint8_t {
231
			MUST_ALIGN_CAPSLOCK, MUST_DISTRIBUTE_KEY_RELEASE, IDLE
232
		} state = IDLE;
233
	} capsLockAligner;
234
235
#if defined(_WIN32)
236
	class ImeManager final : private EventListener {
237
	public:
238
		ImeManager(EventDistributor& eventDistributor);
239
		~ImeManager();
240
241
	private:
242
		// EventListener
243
		bool signalEvent(const Event& event) override;
244
245
	private:
246
		EventDistributor& eventDistributor;
247
248
		HIMC hImc;
249
250
	} imeManager;
251
#endif
252
253
	KeyboardSettings keyboardSettings;
254
255
	class MsxKeyEventQueue final : public Schedulable {
256
	public:
257
		MsxKeyEventQueue(Scheduler& scheduler, Interpreter& interp);
258
		void process_asap(EmuTime time,
259
		                  const Event& event);
260
		void clear();
261
		template<typename Archive>
262
		void serialize(Archive& ar, unsigned version);
263
	private:
264
		// Schedulable
265
		void executeUntil(EmuTime time) override;
266
	private:
267
		std::deque<Event> eventQueue;
268
		Interpreter& interp;
269
	} msxKeyEventQueue;
270
271
	struct KeybDebuggable final : SimpleDebuggable {
272
		explicit KeybDebuggable(MSXMotherBoard& motherBoard);
273
		[[nodiscard]] uint8_t read(unsigned address) override;
274
		void write(unsigned address, uint8_t value) override;
275
		void readBlock(unsigned start, std::span<uint8_t> output) override;
276
	} keybDebuggable;
277
278
	UnicodeKeymap unicodeKeymap;
279
280
	// Remembers the last unicode for a key-press-scancode. To be used later
281
	// on the corresponding key-release, because those don't have unicode info.
282
	// Since there are keys for which keycodes are not reported, it is not
283
	// desirable to save them as keycodes.
284
	// To be able to serialize this, we use int32_t instead of
285
	// SDL_Scancode, because enums are not serializable
286
	std::vector<std::pair<int32_t, uint32_t>> lastUnicodeForScancode; // sorted on SDL_Scancode
287
	static_assert(sizeof(int32_t) >= sizeof(SDL_Scancode), "lastUnicodeForScancode: 1st elm. size error.");
288
289
	/** Keyboard matrix state for 'keymatrix' command. */
290
	std::array<uint8_t, KeyMatrixPosition::NUM_ROWS> cmdKeyMatrix;
291
	/** Keyboard matrix state for 'type' command. */
292
	std::array<uint8_t, KeyMatrixPosition::NUM_ROWS> typeKeyMatrix;
293
	/** Keyboard matrix state for pressed user keys (live or replay). */
294
	std::array<uint8_t, KeyMatrixPosition::NUM_ROWS> userKeyMatrix;
295
	/** Keyboard matrix state that is always in sync with host keyb, also during replay. */
296
	std::array<uint8_t, KeyMatrixPosition::NUM_ROWS> hostKeyMatrix;
297
	/** Combination of 'cmdKeyMatrix', 'typeKeyMatrix' and 'userKeyMatrix'. */
298
	mutable std::array<uint8_t, KeyMatrixPosition::NUM_ROWS> keyMatrix;
299
300
	uint8_t msxModifiers = 0xff;
301
302
	/** True iff keyboard includes a numeric keypad. */
303
	const bool hasKeypad;
304
	/** True iff changes to keyboard row 11 must be rejected.
305
	  * On MSX, row 11 contains the (Japanese) Yes/No keys.
306
	  */
307
	const bool blockRow11;
308
	/** True iff pressing multiple keys at once can add ghost key presses. */
309
	const bool keyGhosting;
310
	/** True iff Shift, Graph and Code are protected against key ghosting. */
311
	const bool keyGhostingSGCprotected;
312
	/** Bit vector where each modifier's bit (using KeyInfo::Modifier's
313
	  * numbering) is set iff it is a lock key.
314
	  */
315
	const uint8_t modifierIsLock;
316
	mutable bool keysChanged = false;
317
	/** Bit vector where each modifier's bit (using KeyInfo::Modifier's
318
	  * numbering) is set iff it is a lock key that is currently on in
319
	  * the emulated machine.
320
	  */
321
	uint8_t locksOn = 0;
322
323
	/*  Countermeasure for missing Release events on Japanese keyboard s*/
324
	enum class HostKeyMap : uint8_t { UNKNOWN, EN, JP };
325
	HostKeyMap hostKeyMap = HostKeyMap::UNKNOWN;
326
327
	bool focus = true;
328
};
329
SERIALIZE_CLASS_VERSION(Keyboard, 5);
330
331
} // namespace openmsx
332
333
#endif