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