OXYGENHUB
◆ main openmsx / src / input / UnicodeKeymap.hh
4.7 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#ifndef UNICODEKEYMAP_HH
2
#define UNICODEKEYMAP_HH
3
4
#include "CommandException.hh"
5
#include "MsxChar2Unicode.hh"
6
7
#include "narrow.hh"
8
9
#include <array>
10
#include <cassert>
11
#include <cstdint>
12
#include <optional>
13
#include <string_view>
14
#include <utility>
15
#include <vector>
16
17
namespace openmsx {
18
19
/** A position (row, column) in a keyboard matrix.
20
  */
21
class KeyMatrixPosition final {
22
	static constexpr unsigned INVALID = 0xFF;
23
24
public:
25
	/** Rows are in the range [0..NUM_ROWS). */
26
	static constexpr unsigned NUM_ROWS = 16;
27
	/** Columns are in the range [0..NUM_COLS). */
28
	static constexpr unsigned NUM_COLS = 8;
29
	/** Combined row and column values are in the range [0..NUM_ROWCOL). */
30
	static constexpr unsigned NUM_ROWCOL = 1 << 7;
31
32
	/** Creates an invalid key matrix position, which can be used when
33
	  * a key does not exist on a particular keyboard.
34
	  */
35
	constexpr KeyMatrixPosition() = default;
36
37
	/** Creates a key matrix position from a uint8_t: the row is stored in
38
	  * the high nibble, the column is stored in the low nibble.
39
	  */
40
	explicit constexpr KeyMatrixPosition(uint8_t rowCol_)
41
		: KeyMatrixPosition(rowCol_ >> 4, rowCol_ & 0x0F)
42
	{
43
	}
44
45
	/** Creates a key matrix position with a given row and column.
46
	  */
47
	constexpr KeyMatrixPosition(unsigned row, unsigned col)
48
		: rowCol(narrow<uint8_t>((row << 3) | col))
49
	{
50
		assert(row < NUM_ROWS);
51
		assert(col < NUM_COLS);
52
		assert(isValid());
53
	}
54
55
	/** Returns true iff this position is valid.
56
	  */
57
	[[nodiscard]] constexpr bool isValid() const {
58
		return rowCol != INVALID;
59
	}
60
61
	/** Returns the matrix row.
62
	  * Must only be called on valid positions.
63
	  */
64
	[[nodiscard]] constexpr uint8_t getRow() const {
65
		assert(isValid());
66
		return rowCol >> 3;
67
	}
68
69
	/** Returns the matrix column.
70
	  * Must only be called on valid positions.
71
	  */
72
	[[nodiscard]] constexpr uint8_t getColumn() const {
73
		assert(isValid());
74
		return rowCol & 0x07;
75
	}
76
77
	/** Returns the matrix row and column combined in a single uint8_t: the column
78
	  * is stored in the lower 3 bits, the row is stored in the higher bits.
79
	  * Must only be called on valid positions.
80
	  */
81
	[[nodiscard]] constexpr uint8_t getRowCol() const {
82
		assert(isValid());
83
		return rowCol;
84
	}
85
86
	/** Returns a mask with the bit corresponding to this position's
87
	  * column set, all other bits clear.
88
	  * Must only be called on valid positions.
89
	  */
90
	[[nodiscard]] constexpr uint8_t getMask() const {
91
		assert(isValid());
92
		return uint8_t(1 << getColumn());
93
	}
94
95
	[[nodiscard]] constexpr bool operator==(const KeyMatrixPosition&) const = default;
96
97
private:
98
	uint8_t rowCol = INVALID;
99
};
100
101
class UnicodeKeymap
102
{
103
public:
104
	struct KeyInfo {
105
		enum class Modifier : uint8_t { SHIFT, CTRL, GRAPH, CAPS, CODE, NUM };
106
		// Modifier masks:
107
		static constexpr uint8_t SHIFT_MASK = 1 << std::to_underlying(Modifier::SHIFT);
108
		static constexpr uint8_t CTRL_MASK  = 1 << std::to_underlying(Modifier::CTRL);
109
		static constexpr uint8_t GRAPH_MASK = 1 << std::to_underlying(Modifier::GRAPH);
110
		static constexpr uint8_t CAPS_MASK  = 1 << std::to_underlying(Modifier::CAPS);
111
		static constexpr uint8_t CODE_MASK  = 1 << std::to_underlying(Modifier::CODE);
112
113
		constexpr KeyInfo() = default;
114
		constexpr KeyInfo(KeyMatrixPosition pos_, uint8_t modMask_)
115
			: pos(pos_), modMask(modMask_)
116
		{
117
			assert(pos.isValid());
118
		}
119
		[[nodiscard]] constexpr bool isValid() const {
120
			return pos.isValid();
121
		}
122
		KeyMatrixPosition pos;
123
		uint8_t modMask = 0;
124
	};
125
126
	explicit UnicodeKeymap(std::string_view keyboardType);
127
128
	[[nodiscard]] KeyInfo get(unsigned unicode) const;
129
	[[nodiscard]] KeyInfo getDeadKey(unsigned n) const;
130
131
	/** Returns a mask in which a bit is set iff the corresponding modifier
132
	  * is relevant for the given key. A modifier is considered relevant if
133
	  * there is at least one mapping entry for the key that requires the
134
	  * modifier to be active.
135
	  * Must only be called on valid KeyInfos.
136
	  */
137
	[[nodiscard]] uint8_t getRelevantMods(const KeyInfo& keyInfo) const {
138
		return relevantMods[keyInfo.pos.getRowCol()];
139
	}
140
141
	[[nodiscard]] const MsxChar2Unicode& getMsxChars() const {
142
		if (!msxChars) throw CommandException("Missing MSX-Video-characterset file"); // TODO make this required for MSX/SVI machines
143
		return *msxChars;
144
	}
145
146
private:
147
	static constexpr unsigned NUM_DEAD_KEYS = 3;
148
149
	void parseUnicodeKeyMapFile(std::string_view data);
150
151
private:
152
	struct Entry {
153
		unsigned unicode;
154
		KeyInfo keyInfo;
155
	};
156
	std::vector<Entry> mapData; // sorted on unicode
157
158
	/** Contains a mask for each key matrix position, which for each modifier
159
	  * has the corresponding bit set if that modifier that affects the key.
160
	  */
161
	std::array<uint8_t, KeyMatrixPosition::NUM_ROWCOL> relevantMods;
162
	std::array<KeyInfo, NUM_DEAD_KEYS> deadKeys;
163
164
	std::optional<MsxChar2Unicode> msxChars; // TODO should this be required for MSX/SVI machines?
165
};
166
167
} // namespace openmsx
168
169
#endif