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