openMSX
UnicodeKeymap.cc
Go to the documentation of this file.
1#include "UnicodeKeymap.hh"
2#include "MSXException.hh"
3#include "File.hh"
4#include "FileContext.hh"
5#include "FileException.hh"
6#include "one_of.hh"
7#include "ranges.hh"
8#include "stl.hh"
9#include <optional>
10
11using std::string_view;
12
13namespace openmsx {
14
17[[nodiscard]] static constexpr std::optional<unsigned> parseHex(string_view str)
18{
19 if (str.empty()) {
20 return {};
21 }
22 unsigned value = 0;
23 for (const char c : str) {
24 value *= 16;
25 if ('0' <= c && c <= '9') {
26 value += c - '0';
27 } else if ('A' <= c && c <= 'F') {
28 value += c - 'A' + 10;
29 } else if ('a' <= c && c <= 'f') {
30 value += c - 'a' + 10;
31 } else {
32 return {};
33 }
34 }
35 return value;
36}
37
42[[nodiscard]] static constexpr bool isSep(char c)
43{
44 return c == one_of(',', // comma
45 ' ', '\t', '\r', // whitespace
46 '#'); // comment
47}
48
52static constexpr void skipSep(string_view& str)
53{
54 while (!str.empty()) {
55 const char c = str.front();
56 if (!isSep(c)) break;
57 if (c == '#') {
58 // Skip till end of line.
59 while (!str.empty() && str.front() != '\n') str.remove_prefix(1);
60 break;
61 }
62 str.remove_prefix(1);
63 }
64}
65
69[[nodiscard]] static constexpr string_view nextToken(string_view& str)
70{
71 skipSep(str);
72 const auto* tokenBegin = str.data();
73 while (!str.empty() && str.front() != '\n' && !isSep(str.front())) {
74 // Pop non-separator character.
75 str.remove_prefix(1);
76 }
77 return {tokenBegin, size_t(str.data() - tokenBegin)};
78}
79
80
81UnicodeKeymap::UnicodeKeymap(string_view keyboardType)
82{
83 auto filename = systemFileContext().resolve(
84 tmpStrCat("unicodemaps/unicodemap.", keyboardType));
85 try {
86 File file(filename);
87 auto buf = file.mmap();
88 parseUnicodeKeyMapFile(
89 string_view(reinterpret_cast<const char*>(buf.data()), buf.size()));
90 } catch (FileException&) {
91 throw MSXException("Couldn't load unicode keymap file: ", filename);
92 }
93}
94
96{
97 auto m = binary_find(mapData, unicode, {}, &Entry::unicode);
98 return m ? m->keyInfo : KeyInfo();
99}
100
102{
103 assert(n < NUM_DEAD_KEYS);
104 return deadKeys[n];
105}
106
107void UnicodeKeymap::parseUnicodeKeyMapFile(string_view data)
108{
109 ranges::fill(relevantMods, 0);
110
111 while (!data.empty()) {
112 if (data.front() == '\n') {
113 // Next line.
114 data.remove_prefix(1);
115 }
116
117 string_view token = nextToken(data);
118 if (token.empty()) {
119 // Skip empty line.
120 continue;
121 }
122
123 if (token == "MSX-Video-Characterset:") {
124 auto vidFileName = nextToken(data);
125 if (vidFileName.empty()) {
126 throw MSXException("Missing filename for MSX-Video-Characterset");
127 }
128 msxChars.emplace(vidFileName);
129 continue;
130 }
131
132 // Parse first token: a unicode value or the keyword DEADKEY.
133 unsigned unicode = 0;
134 unsigned deadKeyIndex = 0;
135 bool isDeadKey = token.starts_with("DEADKEY");
136 if (isDeadKey) {
137 token.remove_prefix(strlen("DEADKEY"));
138 if (token.empty()) {
139 // The normal keywords are
140 // DEADKEY1 DEADKEY2 DEADKEY3
141 // but for backwards compatibility also still recognize
142 // DEADKEY
143 } else {
144 auto d = parseHex(token);
145 if (!d || *d > NUM_DEAD_KEYS) {
146 throw MSXException(
147 "Wrong deadkey number in keymap file. "
148 "It must be 1..", NUM_DEAD_KEYS);
149 }
150 deadKeyIndex = *d - 1; // Make index 0 based instead of 1 based
151 }
152 } else {
153 auto u = parseHex(token);
154 if (!u || *u > 0x1FBFF) {
155 throw MSXException("Wrong unicode value in keymap file");
156 }
157 unicode = *u;
158 }
159
160 // Parse second token. It must be <ROW><COL>
161 token = nextToken(data);
162 if (token == "--") {
163 // Skip -- for now, it means the character cannot be typed.
164 continue;
165 }
166 auto rowcol = parseHex(token);
167 if (!rowcol || *rowcol >= 0x100) {
168 throw MSXException(
169 (token.empty() ? "Missing" : "Wrong"),
170 " <ROW><COL> value in keymap file");
171 }
172 if ((*rowcol >> 4) >= KeyMatrixPosition::NUM_ROWS) {
173 throw MSXException("Too high row value in keymap file");
174 }
175 if ((*rowcol & 0x0F) >= KeyMatrixPosition::NUM_COLS) {
176 throw MSXException("Too high column value in keymap file");
177 }
178 auto pos = KeyMatrixPosition(*rowcol);
179
180 // Parse remaining tokens. It is an optional list of modifier keywords.
181 byte modMask = 0;
182 while (true) {
183 token = nextToken(data);
184 if (token.empty()) {
185 break;
186 } else if (token == "SHIFT") {
187 modMask |= KeyInfo::SHIFT_MASK;
188 } else if (token == "CTRL") {
189 modMask |= KeyInfo::CTRL_MASK;
190 } else if (token == "GRAPH") {
191 modMask |= KeyInfo::GRAPH_MASK;
192 } else if (token == "CAPSLOCK") {
193 modMask |= KeyInfo::CAPS_MASK;
194 } else if (token == "CODE") {
195 modMask |= KeyInfo::CODE_MASK;
196 } else {
197 throw MSXException(
198 "Invalid modifier \"", token, "\" in keymap file");
199 }
200 }
201
202 if (isDeadKey) {
203 if (modMask != 0) {
204 throw MSXException(
205 "DEADKEY entry in keymap file cannot have modifiers");
206 }
207 deadKeys[deadKeyIndex] = KeyInfo(pos, 0);
208 } else {
209 mapData.emplace_back(Entry{unicode, KeyInfo(pos, modMask)});
210 // Note: getRowCol() uses 3 bits for column, rowcol uses 4.
211 relevantMods[pos.getRowCol()] |= modMask;
212 }
213 }
214
215 ranges::sort(mapData, {}, &Entry::unicode);
216}
217
218} // namespace openmsx
Definition: one_of.hh:7
std::string resolve(std::string_view filename) const
Definition: FileContext.cc:79
std::span< const uint8_t > mmap()
Map file in memory.
Definition: File.cc:102
static constexpr unsigned NUM_COLS
Columns are in the range [0..NUM_COLS).
static constexpr unsigned NUM_ROWS
Rows are in the range [0..NUM_ROWS).
KeyInfo getDeadKey(unsigned n) const
KeyInfo get(unsigned unicode) const
UnicodeKeymap(std::string_view keyboardType)
This file implemented 3 utility functions:
Definition: Autofire.cc:9
const FileContext & systemFileContext()
Definition: FileContext.cc:155
UnicodeKeymap::KeyInfo KeyInfo
Definition: Keyboard.cc:57
constexpr void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:287
constexpr void sort(RandomAccessRange &&range)
Definition: ranges.hh:49
auto * binary_find(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
Definition: ranges.hh:413
TemporaryString tmpStrCat(Ts &&... ts)
Definition: strCat.hh:610
static constexpr byte CAPS_MASK
static constexpr byte CTRL_MASK
static constexpr byte GRAPH_MASK
static constexpr byte CODE_MASK
static constexpr byte SHIFT_MASK