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 
12 using std::string_view;
13 
14 namespace openmsx {
15 
20 static unsigned parseHex(string_view str, bool& ok)
21 {
22  if (str.empty()) {
23  ok = false;
24  return 0;
25  }
26  unsigned value = 0;
27  for (const char c : str) {
28  value *= 16;
29  if ('0' <= c && c <= '9') {
30  value += c - '0';
31  } else if ('A' <= c && c <= 'F') {
32  value += c - 'A' + 10;
33  } else if ('a' <= c && c <= 'f') {
34  value += c - 'a' + 10;
35  } else {
36  ok = false;
37  return 0;
38  }
39  }
40  ok = true;
41  return value;
42 }
43 
48 static inline bool isSep(char c)
49 {
50  return c == one_of(',', // comma
51  ' ', '\t', '\r', // whitespace
52  '#'); // comment
53 }
54 
58 static void skipSep(string_view& str)
59 {
60  while (!str.empty()) {
61  const char c = str.front();
62  if (!isSep(c)) break;
63  if (c == '#') {
64  // Skip till end of line.
65  while (!str.empty() && str.front() != '\n') str.remove_prefix(1);
66  break;
67  }
68  str.remove_prefix(1);
69  }
70 }
71 
75 static string_view nextToken(string_view& str)
76 {
77  skipSep(str);
78  auto tokenBegin = str.data();
79  while (!str.empty() && str.front() != '\n' && !isSep(str.front())) {
80  // Pop non-separator character.
81  str.remove_prefix(1);
82  }
83  return string_view(tokenBegin, str.data() - tokenBegin);
84 }
85 
86 
87 UnicodeKeymap::UnicodeKeymap(string_view keyboardType)
88 {
90  strCat("unicodemaps/unicodemap.", keyboardType));
91  try {
92  File file(filename);
93  auto buf = file.mmap();
94  parseUnicodeKeymapfile(
95  string_view(reinterpret_cast<const char*>(buf.data()), buf.size()));
96  } catch (FileException&) {
97  throw MSXException("Couldn't load unicode keymap file: ", filename);
98  }
99 }
100 
102 {
103  auto it = ranges::lower_bound(mapdata, unicode, LessTupleElement<0>());
104  return ((it != end(mapdata)) && (it->first == unicode))
105  ? it->second : KeyInfo();
106 }
107 
109 {
110  assert(n < NUM_DEAD_KEYS);
111  return deadKeys[n];
112 }
113 
114 void UnicodeKeymap::parseUnicodeKeymapfile(string_view data)
115 {
116  memset(relevantMods, 0, sizeof(relevantMods));
117 
118  while (!data.empty()) {
119  if (data.front() == '\n') {
120  // Next line.
121  data.remove_prefix(1);
122  }
123 
124  string_view token = nextToken(data);
125  if (token.empty()) {
126  // Skip empty line.
127  continue;
128  }
129 
130  // Parse first token: a unicode value or the keyword DEADKEY.
131  unsigned unicode = 0;
132  unsigned deadKeyIndex = 0;
133  bool isDeadKey = StringOp::startsWith(token, "DEADKEY");
134  if (isDeadKey) {
135  token.remove_prefix(strlen("DEADKEY"));
136  if (token.empty()) {
137  // The normal keywords are
138  // DEADKEY1 DEADKEY2 DEADKEY3
139  // but for backwards compatibility also still recognize
140  // DEADKEY
141  } else {
142  bool ok;
143  deadKeyIndex = parseHex(token, ok);
144  deadKeyIndex--; // Make index 0 based instead of 1 based
145  if (!ok || deadKeyIndex >= NUM_DEAD_KEYS) {
146  throw MSXException(
147  "Wrong deadkey number in keymap file. "
148  "It must be 1..", NUM_DEAD_KEYS);
149  }
150  }
151  } else {
152  bool ok;
153  unicode = parseHex(token, ok);
154  if (!ok || unicode > 0x1FBAF) {
155  throw MSXException("Wrong unicode value in keymap file");
156  }
157  }
158 
159  // Parse second token. It must be <ROW><COL>
160  token = nextToken(data);
161  if (token == "--") {
162  // Skip -- for now, it means the character cannot be typed.
163  continue;
164  }
165  bool ok;
166  unsigned rowcol = parseHex(token, ok);
167  if (!ok || 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(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, LessTupleElement<0>());
216 }
217 
218 } // namespace openmsx
one_of.hh
FileException.hh
StringOp::startsWith
bool startsWith(string_view total, string_view part)
Definition: StringOp.cc:71
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:87
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:101
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:108
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