openMSX
Keyboard.cc
Go to the documentation of this file.
1 #include "Keyboard.hh"
2 #include "Keys.hh"
3 #include "DeviceConfig.hh"
4 #include "EventDistributor.hh"
5 #include "InputEventFactory.hh"
6 #include "MSXEventDistributor.hh"
8 #include "MSXMotherBoard.hh"
9 #include "ReverseManager.hh"
10 #include "CommandController.hh"
11 #include "CommandException.hh"
12 #include "InputEvents.hh"
13 #include "StateChange.hh"
14 #include "TclArgParser.hh"
15 #include "utf8_checked.hh"
16 #include "checked_cast.hh"
17 #include "unreachable.hh"
18 #include "serialize.hh"
19 #include "serialize_stl.hh"
20 #include "serialize_meta.hh"
21 #include "openmsx.hh"
22 #include "one_of.hh"
23 #include "outer.hh"
24 #include "stl.hh"
25 #include "view.hh"
26 #include <SDL.h>
27 #include <cstdio>
28 #include <cstring>
29 #include <cassert>
30 #include <cstdarg>
31 
32 using std::string;
33 using std::vector;
34 using std::shared_ptr;
35 using std::make_shared;
36 
37 namespace openmsx {
38 
39 // How does the CAPSLOCK key behave?
40 #ifdef __APPLE__
41 // See the comments in this issue:
42 // https://github.com/openMSX/openMSX/issues/1261
43 // Basically it means on apple:
44 // when the host capslock key is pressed, SDL sends capslock-pressed
45 // when the host capslock key is released, SDL sends nothing
46 // when the host capslock key is pressed again, SDL sends capslock-released
47 // when the host capslock key is released, SDL sends nothing
48 static constexpr bool SANE_CAPSLOCK_BEHAVIOR = false;
49 #else
50 // We get sane capslock events from SDL:
51 // when the host capslock key is pressed, SDL sends capslock-pressed
52 // when the host capslock key is released, SDL sends capslock-released
53 static constexpr bool SANE_CAPSLOCK_BEHAVIOR = true;
54 #endif
55 
56 
57 static const int TRY_AGAIN = 0x80; // see pressAscii()
58 
60 
61 class KeyMatrixState final : public StateChange
62 {
63 public:
64  KeyMatrixState() = default; // for serialize
65  KeyMatrixState(EmuTime::param time_, byte row_, byte press_, byte release_)
66  : StateChange(time_)
67  , row(row_), press(press_), release(release_)
68  {
69  // disallow useless events
70  assert((press != 0) || (release != 0));
71  // avoid confusion about what happens when some bits are both
72  // set and reset (in other words: don't rely on order of and-
73  // and or-operations)
74  assert((press & release) == 0);
75  }
76  byte getRow() const { return row; }
77  byte getPress() const { return press; }
78  byte getRelease() const { return release; }
79 
80  template<typename Archive> void serialize(Archive& ar, unsigned /*version*/)
81  {
82  ar.template serializeBase<StateChange>(*this);
83  ar.serialize("row", row,
84  "press", press,
85  "release", release);
86  }
87 private:
88  byte row, press, release;
89 };
90 REGISTER_POLYMORPHIC_CLASS(StateChange, KeyMatrixState, "KeyMatrixState");
91 
92 
93 constexpr const char* const defaultKeymapForMatrix[] = {
94  "int", // MATRIX_MSX
95  "svi", // MATRIX_SVI
96  "cvjoy", // MATRIX_CVJOY
97 };
98 
99 constexpr std::array<KeyMatrixPosition, UnicodeKeymap::KeyInfo::NUM_MODIFIERS>
101  { // MATRIX_MSX
102  KeyMatrixPosition(6, 0), // SHIFT
103  KeyMatrixPosition(6, 1), // CTRL
104  KeyMatrixPosition(6, 2), // GRAPH
105  KeyMatrixPosition(6, 3), // CAPS
106  KeyMatrixPosition(6, 4), // CODE
107  },
108  { // MATRIX_SVI
109  KeyMatrixPosition(6, 0), // SHIFT
110  KeyMatrixPosition(6, 1), // CTRL
111  KeyMatrixPosition(6, 2), // LGRAPH
112  KeyMatrixPosition(8, 3), // CAPS
113  KeyMatrixPosition(6, 3), // RGRAPH
114  },
115  { // MATRIX_CVJOY
116  },
117 };
118 
120  Scheduler& scheduler_,
121  CommandController& commandController_,
122  EventDistributor& eventDistributor,
123  MSXEventDistributor& msxEventDistributor_,
124  StateChangeDistributor& stateChangeDistributor_,
125  MatrixType matrix,
126  const DeviceConfig& config)
127  : Schedulable(scheduler_)
128  , commandController(commandController_)
129  , msxEventDistributor(msxEventDistributor_)
130  , stateChangeDistributor(stateChangeDistributor_)
131  , keyTab(keyTabs[matrix])
132  , modifierPos(modifierPosForMatrix[matrix])
133  , keyMatrixUpCmd (commandController, stateChangeDistributor, scheduler_)
134  , keyMatrixDownCmd(commandController, stateChangeDistributor, scheduler_)
135  , keyTypeCmd (commandController, stateChangeDistributor, scheduler_)
136  , capsLockAligner(eventDistributor, scheduler_)
137  , keyboardSettings(commandController)
138  , msxKeyEventQueue(scheduler_, commandController.getInterpreter())
139  , keybDebuggable(motherBoard)
140  , unicodeKeymap(config.getChildData(
141  "keyboard_type", defaultKeymapForMatrix[matrix]))
142  , hasKeypad(config.getChildDataAsBool("has_keypad", true))
143  , blockRow11(matrix == MATRIX_MSX
144  && !config.getChildDataAsBool("has_yesno_keys", false))
145  , keyGhosting(config.getChildDataAsBool("key_ghosting", true))
146  , keyGhostingSGCprotected(config.getChildDataAsBool(
147  "key_ghosting_sgc_protected", true))
148  , modifierIsLock(KeyInfo::CAPS_MASK
149  | (config.getChildDataAsBool("code_kana_locks", false) ? KeyInfo::CODE_MASK : 0)
150  | (config.getChildDataAsBool("graph_locks", false) ? KeyInfo::GRAPH_MASK : 0))
151  , locksOn(0)
152 {
153  keysChanged = false;
154  msxmodifiers = 0xff;
155  ranges::fill(keyMatrix, 255);
156  ranges::fill(cmdKeyMatrix, 255);
157  ranges::fill(typeKeyMatrix, 255);
158  ranges::fill(userKeyMatrix, 255);
159  ranges::fill(hostKeyMatrix, 255);
160  ranges::fill(dynKeymap, 0);
161 
162  msxEventDistributor.registerEventListener(*this);
163  stateChangeDistributor.registerListener(*this);
164  // We do not listen for CONSOLE_OFF_EVENTS because rescanning the
165  // keyboard can have unwanted side effects
166 
167  motherBoard.getReverseManager().registerKeyboard(*this);
168 }
169 
171 {
172  stateChangeDistributor.unregisterListener(*this);
173  msxEventDistributor.unregisterEventListener(*this);
174 }
175 
176 template<unsigned NUM_ROWS>
177 static void doKeyGhosting(byte (&matrix)[NUM_ROWS], bool protectRow6)
178 {
179  // This routine enables keyghosting as seen on a real MSX
180  //
181  // If on a real MSX in the keyboardmatrix the
182  // real buttons are pressed as in the left matrix
183  // then the matrix to the
184  // 10111111 right will be read by 10110101
185  // 11110101 because of the simple 10110101
186  // 10111101 electrical connections 10110101
187  // that are established by
188  // the closed switches
189  // However, some MSX models have protection against
190  // key-ghosting for SHIFT, GRAPH and CODE keys
191  // On those models, SHIFT, GRAPH and CODE are
192  // connected to row 6 via a diode. It prevents that
193  // SHIFT, GRAPH and CODE get ghosted to another
194  // row.
195  bool changedSomething;
196  do {
197  changedSomething = false;
198  for (unsigned i = 0; i < NUM_ROWS - 1; i++) {
199  auto row1 = matrix[i];
200  for (unsigned j = i + 1; j < NUM_ROWS; j++) {
201  auto row2 = matrix[j];
202  if ((row1 != row2) && ((row1 | row2) != 0xff)) {
203  auto rowIold = matrix[i];
204  auto rowJold = matrix[j];
205  // TODO: The shift/graph/code key ghosting protection
206  // implementation is only correct for MSX.
207  if (protectRow6 && i == 6) {
208  matrix[i] = row1 & row2;
209  matrix[j] = (row1 | 0x15) & row2;
210  row1 &= row2;
211  } else if (protectRow6 && j == 6) {
212  matrix[i] = row1 & (row2 | 0x15);
213  matrix[j] = row1 & row2;
214  row1 &= (row2 | 0x15);
215  } else {
216  // not same and some common zero's
217  // --> inherit other zero's
218  auto newRow = row1 & row2;
219  matrix[i] = newRow;
220  matrix[j] = newRow;
221  row1 = newRow;
222  }
223  if (rowIold != matrix[i] ||
224  rowJold != matrix[j]) {
225  changedSomething = true;
226  }
227  }
228  }
229  }
230  } while (changedSomething);
231 }
232 
233 const byte* Keyboard::getKeys() const
234 {
235  if (keysChanged) {
236  keysChanged = false;
237  const auto* matrix = keyTypeCmd.isActive() ? typeKeyMatrix : userKeyMatrix;
238  for (unsigned row = 0; row < KeyMatrixPosition::NUM_ROWS; ++row) {
239  keyMatrix[row] = cmdKeyMatrix[row] & matrix[row];
240  }
241  if (keyGhosting) {
242  doKeyGhosting(keyMatrix, keyGhostingSGCprotected);
243  }
244  }
245  return keyMatrix;
246 }
247 
249 {
250  // This mechanism exists to solve the following problem:
251  // - play a game where the spacebar is constantly pressed (e.g.
252  // Road Fighter)
253  // - go back in time (press the reverse hotkey) while keeping the
254  // spacebar pressed
255  // - interrupt replay by pressing the cursor keys, still while
256  // keeping spacebar pressed
257  // At the moment replay is interrupted, we need to resynchronize the
258  // msx keyboard with the host keyboard. In the past we assumed the host
259  // keyboard had no keys pressed. But this is wrong in the above
260  // scenario. Now we remember the state of the host keyboard and
261  // transfer that to the new keyboard(s) that get created for reverese.
262  // When replay is stopped we restore this host keyboard state, see
263  // stopReplay().
264 
265  for (unsigned row = 0; row < KeyMatrixPosition::NUM_ROWS; ++row) {
266  hostKeyMatrix[row] = source.hostKeyMatrix[row];
267  }
268 }
269 
270 /* Received an MSX event
271  * Following events get processed:
272  * OPENMSX_KEY_DOWN_EVENT
273  * OPENMSX_KEY_UP_EVENT
274  */
275 void Keyboard::signalMSXEvent(const shared_ptr<const Event>& event,
276  EmuTime::param time)
277 {
278  if (event->getType() == one_of(OPENMSX_KEY_DOWN_EVENT, OPENMSX_KEY_UP_EVENT)) {
279  // Ignore possible console on/off events:
280  // we do not rescan the keyboard since this may lead to
281  // an unwanted pressing of <return> in MSX after typing
282  // "set console off" in the console.
283  msxKeyEventQueue.process_asap(time, event);
284  }
285 }
286 
287 void Keyboard::signalStateChange(const shared_ptr<StateChange>& event)
288 {
289  auto kms = dynamic_cast<KeyMatrixState*>(event.get());
290  if (!kms) return;
291 
292  userKeyMatrix[kms->getRow()] &= ~kms->getPress();
293  userKeyMatrix[kms->getRow()] |= kms->getRelease();
294  keysChanged = true; // do ghosting at next getKeys()
295 }
296 
297 void Keyboard::stopReplay(EmuTime::param time)
298 {
299  for (unsigned row = 0; row < KeyMatrixPosition::NUM_ROWS; ++row) {
300  changeKeyMatrixEvent(time, row, hostKeyMatrix[row]);
301  }
302  msxmodifiers = 0xff;
303  msxKeyEventQueue.clear();
304  memset(dynKeymap, 0, sizeof(dynKeymap));
305 }
306 
307 byte Keyboard::needsLockToggle(const UnicodeKeymap::KeyInfo& keyInfo) const
308 {
309  return modifierIsLock
310  & (locksOn ^ keyInfo.modmask)
311  & unicodeKeymap.getRelevantMods(keyInfo);
312 }
313 
314 void Keyboard::pressKeyMatrixEvent(EmuTime::param time, KeyMatrixPosition pos)
315 {
316  if (!pos.isValid()) {
317  // No such key.
318  return;
319  }
320  auto row = pos.getRow();
321  auto press = pos.getMask();
322  if (((hostKeyMatrix[row] & press) == 0) &&
323  ((userKeyMatrix[row] & press) == 0)) {
324  // Won't have any effect, ignore.
325  return;
326  }
327  changeKeyMatrixEvent(time, row, hostKeyMatrix[row] & ~press);
328 }
329 
330 void Keyboard::releaseKeyMatrixEvent(EmuTime::param time, KeyMatrixPosition pos)
331 {
332  if (!pos.isValid()) {
333  // No such key.
334  return;
335  }
336  auto row = pos.getRow();
337  auto release = pos.getMask();
338  if (((hostKeyMatrix[row] & release) == release) &&
339  ((userKeyMatrix[row] & release) == release)) {
340  // Won't have any effect, ignore.
341  // Test scenario: during replay, exit the openmsx console with
342  // the 'toggle console' command. The 'enter,release' event will
343  // end up here. But it shouldn't stop replay.
344  return;
345  }
346  changeKeyMatrixEvent(time, row, hostKeyMatrix[row] | release);
347 }
348 
349 void Keyboard::changeKeyMatrixEvent(EmuTime::param time, byte row, byte newValue)
350 {
351  // This method already updates hostKeyMatrix[],
352  // userKeyMatrix[] will soon be updated via KeyMatrixState events.
353  hostKeyMatrix[row] = newValue;
354 
355  byte diff = userKeyMatrix[row] ^ newValue;
356  if (diff == 0) return;
357  byte press = userKeyMatrix[row] & diff;
358  byte release = newValue & diff;
359  stateChangeDistributor.distributeNew(make_shared<KeyMatrixState>(
360  time, row, press, release));
361 }
362 
363 /*
364  * @return True iff a release event for the CODE/KANA key must be scheduled.
365  */
366 bool Keyboard::processQueuedEvent(const Event& event, EmuTime::param time)
367 {
368  auto mode = keyboardSettings.getMappingMode();
369 
370  auto& keyEvent = checked_cast<const KeyEvent&>(event);
371  bool down = event.getType() == OPENMSX_KEY_DOWN_EVENT;
372  auto code = (mode == KeyboardSettings::POSITIONAL_MAPPING)
373  ? keyEvent.getScanCode() : keyEvent.getKeyCode();
374  auto key = static_cast<Keys::KeyCode>(int(code) & int(Keys::K_MASK));
375 
376  if (down) {
377  // TODO: refactor debug(...) method to expect a std::string and then adapt
378  // all invocations of it to provide a properly formatted string, using the C++
379  // features for it.
380  // Once that is done, debug(...) can pass the c_str() version of that string
381  // to ad_printf(...) so that I don't have to make an explicit ad_printf(...)
382  // invocation for each debug(...) invocation
383  ad_printf("Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
384  keyEvent.getUnicode(),
385  keyEvent.getKeyCode(),
386  Keys::getName(keyEvent.getKeyCode()).c_str());
387  debug("Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
388  keyEvent.getUnicode(),
389  keyEvent.getKeyCode(),
390  Keys::getName(keyEvent.getKeyCode()).c_str());
391  } else {
392  ad_printf("Key released, keyCode: 0x%05x, keyName: %s\n",
393  keyEvent.getKeyCode(),
394  Keys::getName(keyEvent.getKeyCode()).c_str());
395  debug("Key released, keyCode: 0x%05x, keyName: %s\n",
396  keyEvent.getKeyCode(),
397  Keys::getName(keyEvent.getKeyCode()).c_str());
398  }
399 
400  // Process deadkeys.
402  for (unsigned n = 0; n < 3; n++) {
403  if (key == keyboardSettings.getDeadkeyHostKey(n)) {
404  UnicodeKeymap::KeyInfo deadkey = unicodeKeymap.getDeadkey(n);
405  if (deadkey.isValid()) {
406  updateKeyMatrix(time, down, deadkey.pos);
407  return false;
408  }
409  }
410  }
411  }
412 
413  if (key == Keys::K_CAPSLOCK) {
414  processCapslockEvent(time, down);
415  return false;
416  } else if (key == keyboardSettings.getCodeKanaHostKey()) {
417  processCodeKanaChange(time, down);
418  return false;
419  } else if (key == Keys::K_LALT) {
420  processGraphChange(time, down);
421  return false;
422  } else if (key == Keys::K_KP_ENTER) {
423  processKeypadEnterKey(time, down);
424  return false;
425  } else {
426  return processKeyEvent(time, down, keyEvent);
427  }
428 }
429 
430 /*
431  * Process a change (up or down event) of the CODE/KANA key
432  * It presses or releases the key in the MSX keyboard matrix
433  * and changes the kanalock state in case of a press
434  */
435 void Keyboard::processCodeKanaChange(EmuTime::param time, bool down)
436 {
437  if (down) {
438  locksOn ^= KeyInfo::CODE_MASK;
439  }
440  updateKeyMatrix(time, down, modifierPos[KeyInfo::CODE]);
441 }
442 
443 /*
444  * Process a change (up or down event) of the GRAPH key
445  * It presses or releases the key in the MSX keyboard matrix
446  * and changes the graphlock state in case of a press
447  */
448 void Keyboard::processGraphChange(EmuTime::param time, bool down)
449 {
450  if (down) {
451  locksOn ^= KeyInfo::GRAPH_MASK;
452  }
453  updateKeyMatrix(time, down, modifierPos[KeyInfo::GRAPH]);
454 }
455 
456 /*
457  * Process a change (up or down event) of the CAPSLOCK key
458  * It presses or releases the key in the MSX keyboard matrix
459  * and changes the capslock state in case of a press
460  */
461 void Keyboard::processCapslockEvent(EmuTime::param time, bool down)
462 {
463  if (SANE_CAPSLOCK_BEHAVIOR) {
464  debug("Changing CAPS lock state according to SDL request\n");
465  if (down) {
466  locksOn ^= KeyInfo::CAPS_MASK;
467  }
468  updateKeyMatrix(time, down, modifierPos[KeyInfo::CAPS]);
469  } else {
470  debug("Pressing CAPS lock and scheduling a release\n");
471  locksOn ^= KeyInfo::CAPS_MASK;
472  updateKeyMatrix(time, true, modifierPos[KeyInfo::CAPS]);
473  setSyncPoint(time + EmuDuration::hz(10)); // 0.1s (in MSX time)
474  }
475 }
476 
477 void Keyboard::executeUntil(EmuTime::param time)
478 {
479  debug("Releasing CAPS lock\n");
480  updateKeyMatrix(time, false, modifierPos[KeyInfo::CAPS]);
481 }
482 
483 void Keyboard::processKeypadEnterKey(EmuTime::param time, bool down)
484 {
485  if (!hasKeypad && !keyboardSettings.getAlwaysEnableKeypad()) {
486  // User entered on host keypad but this MSX model does not have one
487  // Ignore the keypress/release
488  return;
489  }
490  processSdlKey(time, down,
491  keyboardSettings.getKpEnterMode() == KeyboardSettings::MSX_KP_COMMA
493 }
494 
495 /*
496  * Process an SDL key press/release event. It concerns a
497  * special key (e.g. SHIFT, UP, DOWN, F1, F2, ...) that can not
498  * be unambiguously derived from a unicode character;
499  * Map the SDL key to an equivalent MSX key press/release event
500  */
501 void Keyboard::processSdlKey(EmuTime::param time, bool down, Keys::KeyCode key)
502 {
503  if (key < MAX_KEYSYM) {
504  auto pos = keyTab[key];
505  if (pos.isValid()) {
506  if (pos.getRow() == 11 && blockRow11) {
507  // do not process row 11 if we have no Yes/No keys
508  return;
509  }
510  updateKeyMatrix(time, down, pos);
511  }
512  }
513 }
514 
515 /*
516  * Update the MSX keyboard matrix
517  */
518 void Keyboard::updateKeyMatrix(EmuTime::param time, bool down, KeyMatrixPosition pos)
519 {
520  if (!pos.isValid()) {
521  // No such key.
522  return;
523  }
524  if (down) {
525  pressKeyMatrixEvent(time, pos);
526  // Keep track of the MSX modifiers.
527  // The MSX modifiers sometimes get overruled by the unicode character
528  // processing, in which case the unicode processing must be able to
529  // restore them to the real key-combinations pressed by the user.
530  for (unsigned i = 0; i < KeyInfo::NUM_MODIFIERS; i++) {
531  if (pos == modifierPos[i]) {
532  msxmodifiers &= ~(1 << i);
533  }
534  }
535  } else {
536  releaseKeyMatrixEvent(time, pos);
537  for (unsigned i = 0; i < KeyInfo::NUM_MODIFIERS; i++) {
538  if (pos == modifierPos[i]) {
539  msxmodifiers |= 1 << i;
540  }
541  }
542  }
543 }
544 
545 /*
546  * Process an SDL key event;
547  * Check if it is a special key, in which case it can be directly
548  * mapped to the MSX matrix.
549  * Otherwise, retrieve the unicode character value for the event
550  * and map the unicode character to the key-combination that must
551  * be pressed to generate the equivalent character on the MSX
552  * @return True iff a release event for the CODE/KANA key must be scheduled.
553  */
554 bool Keyboard::processKeyEvent(EmuTime::param time, bool down, const KeyEvent& keyEvent)
555 {
556  auto mode = keyboardSettings.getMappingMode();
557 
558  auto keyCode = keyEvent.getKeyCode();
559  auto scanCode = keyEvent.getScanCode();
560  auto code = (mode == KeyboardSettings::POSITIONAL_MAPPING) ? scanCode : keyCode;
561  auto key = static_cast<Keys::KeyCode>(int(code) & int(Keys::K_MASK));
562 
563  bool isOnKeypad =
564  (key >= Keys::K_KP0 && key <= Keys::K_KP9) ||
567 
568  if (isOnKeypad && !hasKeypad &&
569  !keyboardSettings.getAlwaysEnableKeypad()) {
570  // User entered on host keypad but this MSX model does not have one
571  // Ignore the keypress/release
572  return false;
573  }
574 
575  if (down) {
576  UnicodeKeymap::KeyInfo keyInfo;
577  unsigned unicode;
578  if (isOnKeypad ||
581  // User entered a key on numeric keypad or the driver is in
582  // KEY/POSITIONAL mapping mode.
583  // First option (keypad) maps to same unicode as some other key
584  // combinations (e.g. digit on main keyboard or TAB/DEL).
585  // Use unicode to handle the more common combination and use direct
586  // matrix to matrix mapping for the exceptional cases (keypad).
587  unicode = 0;
588 #if defined(__APPLE__)
589  } else if ((keyCode & (Keys::K_MASK | Keys::KM_META))
590  == (Keys::K_I | Keys::KM_META)) {
591  // Apple keyboards don't have an Insert key, use Cmd+I as an alternative.
592  keyCode = key = Keys::K_INSERT;
593  unicode = 0;
594 #endif
595  } else {
596  unicode = keyEvent.getUnicode();
597  if ((unicode < 0x20) || ((0x7F <= unicode) && (unicode < 0xA0))) {
598  // Control character in C0 or C1 range.
599  // Use SDL's interpretation instead.
600  unicode = 0;
601  } else if (utf8::is_pua(unicode)) {
602  // Code point in Private Use Area: undefined by Unicode,
603  // so we rely on SDL's interpretation instead.
604  // For example the Mac's cursor keys are in this range.
605  unicode = 0;
606  } else {
607  keyInfo = unicodeKeymap.get(unicode);
608  if (!keyInfo.isValid()) {
609  // Unicode does not exist in our mapping; try to process
610  // the key using its keycode instead.
611  unicode = 0;
612  }
613  }
614  }
615  if (key < MAX_KEYSYM) {
616  // Remember which unicode character is currently derived
617  // from this SDL key. It must be stored here (during key-press)
618  // because during key-release SDL never returns the unicode
619  // value (it always returns the value 0). But we must know
620  // the unicode value in order to be able to perform the correct
621  // key-combination-release in the MSX
622  dynKeymap[key] = unicode;
623  } else {
624  // Unexpectedly high key-code. Can't store the unicode
625  // character for this key. Instead directly treat the key
626  // via matrix to matrix mapping
627  unicode = 0;
628  }
629  if (unicode == 0) {
630  // It was an ambiguous key (numeric key-pad, CTRL+character)
631  // or a special key according to SDL (like HOME, INSERT, etc)
632  // or a first keystroke of a composed key
633  // (e.g. altr-gr + = on azerty keyboard) or driver is in
634  // direct SDL mapping mode:
635  // Perform direct SDL matrix to MSX matrix mapping
636  // But only when it is not a first keystroke of a
637  // composed key
638  if ((keyCode & Keys::KM_MODE) == 0) {
639  processSdlKey(time, down, key);
640  }
641  return false;
642  } else {
643  // It is a unicode character; map it to the right key-combination
644  return pressUnicodeByUser(time, keyInfo, unicode, true);
645  }
646  } else {
647  // key was released
648 #if defined(__APPLE__)
649  if ((keyCode & (Keys::K_MASK | Keys::KM_META))
650  == (Keys::K_I | Keys::KM_META)) {
651  keyCode = key = Keys::K_INSERT;
652  }
653 #endif
654  unsigned unicode;
655  if (key < MAX_KEYSYM) {
656  unicode = dynKeymap[key]; // Get the unicode that was derived from this key
657  } else {
658  unicode = 0;
659  }
660  if (unicode == 0) {
661  // It was a special key, perform matrix to matrix mapping
662  // But only when it is not a first keystroke of a
663  // composed key
664  if ((keyCode & Keys::KM_MODE) == 0) {
665  processSdlKey(time, down, key);
666  }
667  } else {
668  // It was a unicode character; map it to the right key-combination
669  pressUnicodeByUser(time, unicodeKeymap.get(unicode), unicode, false);
670  }
671  return false;
672  }
673 }
674 
675 void Keyboard::processCmd(Interpreter& interp, span<const TclObject> tokens, bool up)
676 {
677  unsigned row = tokens[1].getInt(interp);
678  unsigned mask = tokens[2].getInt(interp);
679  if (row >= KeyMatrixPosition::NUM_ROWS) {
680  throw CommandException("Invalid row");
681  }
682  if (mask >= 256) {
683  throw CommandException("Invalid mask");
684  }
685  if (up) {
686  cmdKeyMatrix[row] |= mask;
687  } else {
688  cmdKeyMatrix[row] &= ~mask;
689  }
690  keysChanged = true;
691 }
692 
693 /*
694  * This routine processes unicode characters. It maps a unicode character
695  * to the correct key-combination on the MSX.
696  *
697  * There are a few caveats with respect to the MSX and Host modifier keys
698  * that you must be aware about if you want to understand why the routine
699  * works as it works.
700  *
701  * Row 6 of the MSX keyboard matrix contains the MSX modifier keys:
702  * CTRL, CODE, GRAPH and SHIFT
703  *
704  * The SHIFT key is also a modifier key on the host machine. However, the
705  * SHIFT key behaviour can differ between HOST and MSX for all 'special'
706  * characters (anything but A-Z).
707  * For example, on AZERTY host keyboard, user presses SHIFT+& to make the '1'
708  * On MSX QWERTY keyboard, the same key-combination leads to '!'.
709  * So this routine must not only PRESS the SHIFT key when required according
710  * to the unicode mapping table but it must also RELEASE the SHIFT key for all
711  * these special keys when the user PRESSES the key/character.
712  *
713  * On the other hand, for A-Z, this routine must not touch the SHIFT key at all.
714  * Otherwise it might give strange behaviour when CAPS lock is on (which also
715  * acts as a key-modifier for A-Z). The routine can rely on the fact that
716  * SHIFT+A-Z behaviour is the same on all host and MSX keyboards. It is
717  * approximately the only part of keyboards that is de-facto standardized :-)
718  *
719  * For the other modifiers (CTRL, CODE and GRAPH), the routine must be able to
720  * PRESS them when required but there is no need to RELEASE them during
721  * character press. On the contrary; the host keys that map to CODE and GRAPH
722  * do not work as modifiers on the host itself, so if the routine would release
723  * them, it would give wrong result.
724  * For example, 'ALT-A' on Host will lead to unicode character 'a', just like
725  * only pressing the 'A' key. The MSX however must know about the difference.
726  *
727  * As a reminder: here is the build-up of row 6 of the MSX key matrix
728  * 7 6 5 4 3 2 1 0
729  * row 6 | F3 | F2 | F1 | code| caps|graph| ctrl|shift|
730  */
731 bool Keyboard::pressUnicodeByUser(
732  EmuTime::param time, UnicodeKeymap::KeyInfo keyInfo, unsigned unicode,
733  bool down)
734 {
735  bool insertCodeKanaRelease = false;
736  if (down) {
737  if ((needsLockToggle(keyInfo) & KeyInfo::CODE_MASK) &&
738  keyboardSettings.getAutoToggleCodeKanaLock()) {
739  // Code Kana locks, is in wrong state and must be auto-toggled:
740  // Toggle it by pressing the lock key and scheduling a
741  // release event
742  locksOn ^= KeyInfo::CODE_MASK;
743  pressKeyMatrixEvent(time, modifierPos[KeyInfo::CODE]);
744  insertCodeKanaRelease = true;
745  } else {
746  // Press the character key and related modifiers
747  // Ignore the CODE key in case that Code Kana locks
748  // (e.g. do not press it).
749  // Ignore the GRAPH key in case that Graph locks
750  // Always ignore CAPSLOCK mask (assume that user will
751  // use real CAPS lock to switch/ between hiragana and
752  // katanana on japanese model)
753  pressKeyMatrixEvent(time, keyInfo.pos);
754 
755  byte modmask = keyInfo.modmask & ~modifierIsLock;
756  if (('A' <= unicode && unicode <= 'Z') || ('a' <= unicode && unicode <= 'z')) {
757  // For a-z and A-Z, leave SHIFT unchanged, this to cater
758  // for difference in behaviour between host and emulated
759  // machine with respect to how the combination of CAPSLOCK
760  // and SHIFT is interpreted for these characters.
761  modmask &= ~KeyInfo::SHIFT_MASK;
762  } else {
763  // Release SHIFT if our character does not require it.
764  if (~modmask & KeyInfo::SHIFT_MASK) {
765  releaseKeyMatrixEvent(time, modifierPos[KeyInfo::SHIFT]);
766  }
767  }
768  // Press required modifiers for our character.
769  // Note that these modifiers are only pressed, never released.
770  for (unsigned i = 0; i < KeyInfo::NUM_MODIFIERS; i++) {
771  if ((modmask >> i) & 1) {
772  pressKeyMatrixEvent(time, modifierPos[i]);
773  }
774  }
775  }
776  } else {
777  releaseKeyMatrixEvent(time, keyInfo.pos);
778 
779  // Restore non-lock modifier keys.
780  for (unsigned i = 0; i < KeyInfo::NUM_MODIFIERS; i++) {
781  if (!((modifierIsLock >> i) & 1)) {
782  // Do not simply unpress graph, ctrl, code and shift but
783  // restore them to the values currently pressed by the user.
784  if ((msxmodifiers >> i) & 1) {
785  releaseKeyMatrixEvent(time, modifierPos[i]);
786  } else {
787  pressKeyMatrixEvent(time, modifierPos[i]);
788  }
789  }
790  }
791  }
792  keysChanged = true;
793  return insertCodeKanaRelease;
794 }
795 
796 /*
797  * Press an ASCII character. It is used by the 'Insert characters'
798  * function that is exposed to the console.
799  * The characters are inserted in a separate keyboard matrix, to prevent
800  * interference with the keypresses of the user on the MSX itself.
801  *
802  * @returns:
803  * zero : handling this character is done
804  * non-zero: typing this character needs additional actions
805  * bits 0-4: release these modifiers and call again
806  * bit 7 : simply call again (used by SHIFT+GRAPH heuristic)
807  */
808 int Keyboard::pressAscii(unsigned unicode, bool down)
809 {
810  int releaseMask = 0;
811  UnicodeKeymap::KeyInfo keyInfo = unicodeKeymap.get(unicode);
812  if (!keyInfo.isValid()) {
813  return releaseMask;
814  }
815  byte modmask = keyInfo.modmask & ~modifierIsLock;
816  if (down) {
817  // check for modifier toggles
818  byte toggleLocks = needsLockToggle(keyInfo);
819  for (unsigned i = 0; i < KeyInfo::NUM_MODIFIERS; i++) {
820  if ((toggleLocks >> i) & 1) {
821  debug("Toggling lock %d\n", i);
822  locksOn ^= 1 << i;
823  releaseMask |= 1 << i;
824  auto lockPos = modifierPos[i];
825  typeKeyMatrix[lockPos.getRow()] &= ~lockPos.getMask();
826  }
827  }
828  if (releaseMask == 0) {
829  debug("Key pasted, unicode: 0x%04x, row: %02d, col: %d, modmask: %02x\n",
830  unicode, keyInfo.pos.getRow(), keyInfo.pos.getColumn(), modmask);
831  // Workaround MSX-BIOS(?) bug/limitation:
832  //
833  // Under these conditions:
834  // - Typing a graphical MSX character 00-1F (such a char 'x' gets
835  // printed as chr$(1) followed by chr$(x+64)).
836  // - Typing this character requires pressing SHIFT+GRAPH and one
837  // 'regular' key.
838  // - And all 3 keys are immediately pressed simultaneously.
839  // Then, from time to time, instead of the intended character 'x'
840  // (00-1F), the wrong character 'x+64' gets printed.
841  // When first SHIFT+GRAPH is pressed, and only one frame later the
842  // other keys is pressed (additionally), this problem seems to
843  // disappear.
844  //
845  // After implementing the above we found that a similar problem
846  // occurs when:
847  // - a GRAPH + <x> (without SHIFT) key combo is typed
848  // - immediately after a key combo with GRAPH + SHIFT + <x>.
849  // For example:
850  // type "\u2666\u266a"
851  // from time to time 2nd character got wrongly typed as a
852  // 'M' (instead of a musical note symbol). But typing a sequence
853  // of \u266a chars without a preceding \u2666 just works.
854  //
855  // To fix both these problems (and possibly still undiscovered
856  // variations), I'm now extending the workaround to all characters
857  // that are typed via a key combination that includes GRAPH.
858  if (modmask & KeyInfo::GRAPH_MASK) {
859  auto isPressed = [&](auto& key) {
860  return (typeKeyMatrix[key.getRow()] & key.getMask()) == 0;
861  };
862  if (!isPressed(modifierPos[KeyInfo::GRAPH])) {
863  // GRAPH not yet pressed ->
864  // first press it before adding the non-modifier key
865  releaseMask = TRY_AGAIN;
866  }
867  }
868  // press modifiers
869  for (unsigned i = 0; i < KeyInfo::NUM_MODIFIERS; i++) {
870  if ((modmask >> i) & 1) {
871  auto modPos = modifierPos[i];
872  typeKeyMatrix[modPos.getRow()] &= ~modPos.getMask();
873  }
874  }
875  if (releaseMask == 0) {
876  // press key
877  typeKeyMatrix[keyInfo.pos.getRow()] &= ~keyInfo.pos.getMask();
878  }
879  }
880  } else {
881  typeKeyMatrix[keyInfo.pos.getRow()] |= keyInfo.pos.getMask();
882  for (unsigned i = 0; i < KeyInfo::NUM_MODIFIERS; i++) {
883  if ((modmask >> i) & 1) {
884  auto modPos = modifierPos[i];
885  typeKeyMatrix[modPos.getRow()] |= modPos.getMask();
886  }
887  }
888  }
889  keysChanged = true;
890  return releaseMask;
891 }
892 
893 /*
894  * Press a lock key. It is used by the 'Insert characters'
895  * function that is exposed to the console.
896  * The characters are inserted in a separate keyboard matrix, to prevent
897  * interference with the keypresses of the user on the MSX itself
898  */
899 void Keyboard::pressLockKeys(byte lockKeysMask, bool down)
900 {
901  for (unsigned i = 0; i < KeyInfo::NUM_MODIFIERS; i++) {
902  if ((lockKeysMask >> i) & 1) {
903  auto lockPos = modifierPos[i];
904  if (down) {
905  // press lock key
906  typeKeyMatrix[lockPos.getRow()] &= ~lockPos.getMask();
907  } else {
908  // release lock key
909  typeKeyMatrix[lockPos.getRow()] |= lockPos.getMask();
910  }
911  }
912  }
913  keysChanged = true;
914 }
915 
916 /*
917  * Check if there are common keys in the MSX matrix for
918  * two different unicodes.
919  * It is used by the 'insert keys' function to determine if it has to wait for
920  * a short while after releasing a key (to enter a certain character) before
921  * pressing the next key (to enter the next character)
922  */
923 bool Keyboard::commonKeys(unsigned unicode1, unsigned unicode2)
924 {
925  // get row / mask of key (note: ignore modifier mask)
926  auto keyPos1 = unicodeKeymap.get(unicode1).pos;
927  auto keyPos2 = unicodeKeymap.get(unicode2).pos;
928 
929  return keyPos1 == keyPos2 && keyPos1.isValid();
930 }
931 
932 void Keyboard::debug(const char* format, ...)
933 {
934  if (keyboardSettings.getTraceKeyPresses()) {
935  va_list args;
936  va_start(args, format);
937  vfprintf(stderr, format, args);
938  va_end(args);
939  }
940 }
941 
942 
943 // class KeyMatrixUpCmd
944 
945 Keyboard::KeyMatrixUpCmd::KeyMatrixUpCmd(
946  CommandController& commandController_,
947  StateChangeDistributor& stateChangeDistributor_,
948  Scheduler& scheduler_)
949  : RecordedCommand(commandController_, stateChangeDistributor_,
950  scheduler_, "keymatrixup")
951 {
952 }
953 
954 void Keyboard::KeyMatrixUpCmd::execute(
955  span<const TclObject> tokens, TclObject& /*result*/, EmuTime::param /*time*/)
956 {
957  checkNumArgs(tokens, 3, Prefix{1}, "row mask");
958  auto& keyboard = OUTER(Keyboard, keyMatrixUpCmd);
959  return keyboard.processCmd(getInterpreter(), tokens, true);
960 }
961 
962 string Keyboard::KeyMatrixUpCmd::help(const vector<string>& /*tokens*/) const
963 {
964  static const string helpText =
965  "keymatrixup <row> <bitmask> release a key in the keyboardmatrix\n";
966  return helpText;
967 }
968 
969 
970 // class KeyMatrixDownCmd
971 
972 Keyboard::KeyMatrixDownCmd::KeyMatrixDownCmd(CommandController& commandController_,
973  StateChangeDistributor& stateChangeDistributor_,
974  Scheduler& scheduler_)
975  : RecordedCommand(commandController_, stateChangeDistributor_,
976  scheduler_, "keymatrixdown")
977 {
978 }
979 
980 void Keyboard::KeyMatrixDownCmd::execute(span<const TclObject> tokens,
981  TclObject& /*result*/, EmuTime::param /*time*/)
982 {
983  checkNumArgs(tokens, 3, Prefix{1}, "row mask");
984  auto& keyboard = OUTER(Keyboard, keyMatrixDownCmd);
985  return keyboard.processCmd(getInterpreter(), tokens, false);
986 }
987 
988 string Keyboard::KeyMatrixDownCmd::help(const vector<string>& /*tokens*/) const
989 {
990  static const string helpText =
991  "keymatrixdown <row> <bitmask> press a key in the keyboardmatrix\n";
992  return helpText;
993 }
994 
995 
996 // class MsxKeyEventQueue
997 
998 Keyboard::MsxKeyEventQueue::MsxKeyEventQueue(
999  Scheduler& scheduler_, Interpreter& interp_)
1000  : Schedulable(scheduler_)
1001  , interp(interp_)
1002 {
1003 }
1004 
1005 void Keyboard::MsxKeyEventQueue::process_asap(
1006  EmuTime::param time, const shared_ptr<const Event>& event)
1007 {
1008  bool processImmediately = eventQueue.empty();
1009  eventQueue.push_back(event);
1010  if (processImmediately) {
1011  executeUntil(time);
1012  }
1013 }
1014 
1015 void Keyboard::MsxKeyEventQueue::clear()
1016 {
1017  eventQueue.clear();
1018  removeSyncPoint();
1019 }
1020 
1021 void Keyboard::MsxKeyEventQueue::executeUntil(EmuTime::param time)
1022 {
1023  // Get oldest event from the queue and process it
1024  shared_ptr<const Event> event = eventQueue.front();
1025  auto& keyboard = OUTER(Keyboard, msxKeyEventQueue);
1026  bool insertCodeKanaRelease = keyboard.processQueuedEvent(*event, time);
1027 
1028  if (insertCodeKanaRelease) {
1029  // The processor pressed the CODE/KANA key
1030  // Schedule a CODE/KANA release event, to be processed
1031  // before any of the other events in the queue
1032  eventQueue.push_front(make_shared<KeyUpEvent>(
1033  keyboard.keyboardSettings.getCodeKanaHostKey()));
1034  } else {
1035  // The event has been completely processed. Delete it from the queue
1036  if (!eventQueue.empty()) {
1037  eventQueue.pop_front();
1038  } else {
1039  // it's possible clear() has been called
1040  // (indirectly from keyboard.processQueuedEvent())
1041  }
1042  }
1043 
1044  if (!eventQueue.empty()) {
1045  // There are still events. Process them in 1/15s from now
1046  setSyncPoint(time + EmuDuration::hz(15));
1047  }
1048 }
1049 
1050 
1051 // class KeyInserter
1052 
1053 Keyboard::KeyInserter::KeyInserter(
1054  CommandController& commandController_,
1055  StateChangeDistributor& stateChangeDistributor_,
1056  Scheduler& scheduler_)
1057  : RecordedCommand(commandController_, stateChangeDistributor_,
1058  scheduler_, "type_via_keyboard")
1059  , Schedulable(scheduler_)
1060  , lockKeysMask(0)
1061  , releaseLast(false)
1062 {
1063  // avoid UMR
1064  last = 0;
1065  oldLocksOn = 0;
1066  releaseBeforePress = false;
1067  typingFrequency = 15;
1068 }
1069 
1070 void Keyboard::KeyInserter::execute(
1071  span<const TclObject> tokens, TclObject& /*result*/, EmuTime::param /*time*/)
1072 {
1073  checkNumArgs(tokens, AtLeast{2}, "?-release? ?-freq hz? text");
1074 
1075  releaseBeforePress = false;
1076  typingFrequency = 15;
1077 
1078  // for full backwards compatibility: one option means type it...
1079  if (tokens.size() == 2) {
1080  type(tokens[1].getString());
1081  return;
1082  }
1083 
1084  ArgsInfo info[] = {
1085  flagArg("-release", releaseBeforePress),
1086  valueArg("-freq", typingFrequency),
1087  };
1088  auto arguments = parseTclArgs(getInterpreter(), tokens.subspan(1), info);
1089 
1090  if (typingFrequency <= 0) {
1091  throw CommandException("Wrong argument for -freq (should be a positive number)");
1092  }
1093  if (arguments.size() != 1) throw SyntaxError();
1094 
1095  type(arguments[0].getString());
1096 }
1097 
1098 string Keyboard::KeyInserter::help(const vector<string>& /*tokens*/) const
1099 {
1100  static const string helpText = "Type a string in the emulated MSX.\n" \
1101  "Use -release to make sure the keys are always released before typing new ones (necessary for some game input routines, but in general, this means typing is twice as slow).\n" \
1102  "Use -freq to tweak how fast typing goes and how long the keys will be pressed (and released in case -release was used). Keys will be typed at the given frequency and will remain pressed/released for 1/freq seconds";
1103  return helpText;
1104 }
1105 
1106 void Keyboard::KeyInserter::tabCompletion(vector<string>& tokens) const
1107 {
1108  vector<const char*> options;
1109  if (!contains(tokens, "-release")) {
1110  options.push_back("-release");
1111  }
1112  if (!contains(tokens, "-freq")) {
1113  options.push_back("-freq");
1114  }
1115  completeString(tokens, options);
1116 }
1117 
1118 void Keyboard::KeyInserter::type(std::string_view str)
1119 {
1120  if (str.empty()) {
1121  return;
1122  }
1123  auto& keyboard = OUTER(Keyboard, keyTypeCmd);
1124  oldLocksOn = keyboard.locksOn;
1125  if (text_utf8.empty()) {
1126  reschedule(getCurrentTime());
1127  }
1128  text_utf8.append(str.data(), str.size());
1129 }
1130 
1131 void Keyboard::KeyInserter::executeUntil(EmuTime::param time)
1132 {
1133  auto& keyboard = OUTER(Keyboard, keyTypeCmd);
1134  if (lockKeysMask != 0) {
1135  // release CAPS and/or Code/Kana Lock keys
1136  keyboard.pressLockKeys(lockKeysMask, false);
1137  }
1138  if (releaseLast) {
1139  keyboard.pressAscii(last, false); // release previous character
1140  }
1141  if (text_utf8.empty()) {
1142  releaseLast = false;
1143  keyboard.debug("Restoring locks: %02X -> %02X\n", keyboard.locksOn, oldLocksOn);
1144  auto diff = oldLocksOn ^ keyboard.locksOn;
1145  lockKeysMask = diff;
1146  if (diff != 0) {
1147  // press CAPS, GRAPH and/or Code/Kana Lock keys
1148  keyboard.locksOn ^= diff;
1149  keyboard.pressLockKeys(diff, true);
1150  reschedule(time);
1151  }
1152  return;
1153  }
1154 
1155  try {
1156  auto it = begin(text_utf8);
1157  unsigned current = utf8::next(it, end(text_utf8));
1158  if (releaseLast && (releaseBeforePress || keyboard.commonKeys(last, current))) {
1159  // There are common keys between previous and current character
1160  // Do not immediately press again but give MSX the time to notice
1161  // that the keys have been released
1162  releaseLast = false;
1163  } else {
1164  // All keys in current char differ from previous char. The new keys
1165  // can immediately be pressed
1166  lockKeysMask = keyboard.pressAscii(current, true);
1167  if (lockKeysMask == 0) {
1168  last = current;
1169  releaseLast = true;
1170  text_utf8.erase(begin(text_utf8), it);
1171  } else if (lockKeysMask & TRY_AGAIN) {
1172  lockKeysMask &= ~TRY_AGAIN;
1173  releaseLast = false;
1174  } else if (releaseBeforePress) {
1175  releaseLast = true;
1176  }
1177  }
1178  reschedule(time);
1179  } catch (std::exception&) {
1180  // utf8 encoding error
1181  text_utf8.clear();
1182  }
1183 }
1184 
1185 void Keyboard::KeyInserter::reschedule(EmuTime::param time)
1186 {
1187  setSyncPoint(time + EmuDuration::hz(typingFrequency));
1188 }
1189 
1190 /*
1191  * class CapsLockAligner
1192  *
1193  * It is used to align MSX CAPS lock status with the host CAPS lock status
1194  * during the reset of the MSX or after the openMSX window regains focus.
1195  *
1196  * It listens to the 'BOOT' event and schedules the real alignment
1197  * 2 seconds later. Reason is that it takes a while before the MSX
1198  * reset routine starts monitoring the MSX keyboard.
1199  *
1200  * For focus regain, the alignment is done immediately.
1201  */
1202 Keyboard::CapsLockAligner::CapsLockAligner(
1203  EventDistributor& eventDistributor_,
1204  Scheduler& scheduler_)
1205  : Schedulable(scheduler_)
1206  , eventDistributor(eventDistributor_)
1207 {
1208  state = IDLE;
1209  eventDistributor.registerEventListener(OPENMSX_BOOT_EVENT, *this);
1210  eventDistributor.registerEventListener(OPENMSX_FOCUS_EVENT, *this);
1211 }
1212 
1213 Keyboard::CapsLockAligner::~CapsLockAligner()
1214 {
1215  eventDistributor.unregisterEventListener(OPENMSX_FOCUS_EVENT, *this);
1216  eventDistributor.unregisterEventListener(OPENMSX_BOOT_EVENT, *this);
1217 }
1218 
1219 int Keyboard::CapsLockAligner::signalEvent(const shared_ptr<const Event>& event)
1220 {
1221  if (!SANE_CAPSLOCK_BEHAVIOR) {
1222  // don't even try
1223  return 0;
1224  }
1225 
1226  if (state == IDLE) {
1227  EmuTime::param time = getCurrentTime();
1228  EventType type = event->getType();
1229  if (type == OPENMSX_FOCUS_EVENT) {
1230  alignCapsLock(time);
1231  } else if (type == OPENMSX_BOOT_EVENT) {
1232  state = MUST_ALIGN_CAPSLOCK;
1233  setSyncPoint(time + EmuDuration::sec(2)); // 2s (MSX time)
1234  } else {
1235  UNREACHABLE;
1236  }
1237  }
1238  return 0;
1239 }
1240 
1241 void Keyboard::CapsLockAligner::executeUntil(EmuTime::param time)
1242 {
1243  switch (state) {
1244  case MUST_ALIGN_CAPSLOCK:
1245  alignCapsLock(time);
1246  break;
1247  case MUST_DISTRIBUTE_KEY_RELEASE: {
1248  auto& keyboard = OUTER(Keyboard, capsLockAligner);
1249  auto event = make_shared<KeyUpEvent>(Keys::K_CAPSLOCK);
1250  keyboard.msxEventDistributor.distributeEvent(event, time);
1251  state = IDLE;
1252  break;
1253  }
1254  default:
1255  UNREACHABLE;
1256  }
1257 }
1258 
1259 /*
1260  * Align MSX caps lock state with host caps lock state
1261  * WARNING: This function assumes that the MSX will see and
1262  * process the caps lock key press.
1263  * If MSX misses the key press for whatever reason (e.g.
1264  * interrupts are disabled), the caps lock state in this
1265  * module will mismatch with the real MSX caps lock state
1266  * TODO: Find a solution for the above problem. For example by monitoring
1267  * the MSX caps-lock LED state.
1268  */
1269 void Keyboard::CapsLockAligner::alignCapsLock(EmuTime::param time)
1270 {
1271  bool hostCapsLockOn = ((SDL_GetModState() & KMOD_CAPS) != 0);
1272  auto& keyboard = OUTER(Keyboard, capsLockAligner);
1273  if (bool(keyboard.locksOn & KeyInfo::CAPS_MASK) != hostCapsLockOn) {
1274  keyboard.debug("Resyncing host and MSX CAPS lock\n");
1275  // note: send out another event iso directly calling
1276  // processCapslockEvent() because we want this to be recorded
1277  auto event = make_shared<KeyDownEvent>(Keys::K_CAPSLOCK);
1278  keyboard.msxEventDistributor.distributeEvent(event, time);
1279  keyboard.debug("Sending fake CAPS release\n");
1280  state = MUST_DISTRIBUTE_KEY_RELEASE;
1281  setSyncPoint(time + EmuDuration::hz(10)); // 0.1s (MSX time)
1282  } else {
1283  state = IDLE;
1284  }
1285 }
1286 
1287 
1288 // class KeybDebuggable
1289 
1290 Keyboard::KeybDebuggable::KeybDebuggable(MSXMotherBoard& motherBoard_)
1291  : SimpleDebuggable(motherBoard_, "keymatrix", "MSX Keyboard Matrix",
1292  KeyMatrixPosition::NUM_ROWS)
1293 {
1294 }
1295 
1296 byte Keyboard::KeybDebuggable::read(unsigned address)
1297 {
1298  auto& keyboard = OUTER(Keyboard, keybDebuggable);
1299  return keyboard.getKeys()[address];
1300 }
1301 
1302 void Keyboard::KeybDebuggable::write(unsigned /*address*/, byte /*value*/)
1303 {
1304  // ignore
1305 }
1306 
1307 
1308 template<typename Archive>
1309 void Keyboard::KeyInserter::serialize(Archive& ar, unsigned /*version*/)
1310 {
1311  ar.template serializeBase<Schedulable>(*this);
1312  ar.serialize("text", text_utf8,
1313  "last", last,
1314  "lockKeysMask", lockKeysMask,
1315  "releaseLast", releaseLast);
1316 
1317  bool oldCodeKanaLockOn, oldGraphLockOn, oldCapsLockOn;
1318  if (!ar.isLoader()) {
1319  oldCodeKanaLockOn = oldLocksOn & KeyInfo::CODE_MASK;
1320  oldGraphLockOn = oldLocksOn & KeyInfo::GRAPH_MASK;
1321  oldCapsLockOn = oldLocksOn & KeyInfo::CAPS_MASK;
1322  }
1323  ar.serialize("oldCodeKanaLockOn", oldCodeKanaLockOn,
1324  "oldGraphLockOn", oldGraphLockOn,
1325  "oldCapsLockOn", oldCapsLockOn);
1326  if (ar.isLoader()) {
1327  oldLocksOn = (oldCodeKanaLockOn ? KeyInfo::CODE_MASK : 0)
1328  | (oldGraphLockOn ? KeyInfo::GRAPH_MASK : 0)
1329  | (oldCapsLockOn ? KeyInfo::CAPS_MASK : 0);
1330  }
1331 }
1332 
1333 // version 1: Initial version: {userKeyMatrix, dynKeymap, msxmodifiers,
1334 // msxKeyEventQueue} was intentionally not serialized. The reason
1335 // was that after a loadstate, you want the MSX keyboard to reflect
1336 // the state of the host keyboard. So any pressed MSX keys from the
1337 // time the savestate was created are cleared.
1338 // version 2: For reverse-replay it is important that snapshots contain the
1339 // full state of the MSX keyboard, so now we do serialize it.
1340 // version 3: split cmdKeyMatrix into cmdKeyMatrix + typeKeyMatrix
1341 // TODO Is the assumption in version 1 correct (clear keyb state on load)?
1342 // If it is still useful for 'regular' loadstate, then we could implement
1343 // it by explicitly clearing the keyb state from the actual loadstate
1344 // command. (But let's only do this when experience shows it's really
1345 // better).
1346 template<typename Archive>
1347 void Keyboard::serialize(Archive& ar, unsigned version)
1348 {
1349  ar.serialize("keyTypeCmd", keyTypeCmd,
1350  "cmdKeyMatrix", cmdKeyMatrix);
1351  if (ar.versionAtLeast(version, 3)) {
1352  ar.serialize("typeKeyMatrix", typeKeyMatrix);
1353  } else {
1354  ranges::copy(cmdKeyMatrix, typeKeyMatrix);
1355  }
1356 
1357  bool msxCapsLockOn, msxCodeKanaLockOn, msxGraphLockOn;
1358  if (!ar.isLoader()) {
1359  msxCapsLockOn = locksOn & KeyInfo::CAPS_MASK;
1360  msxCodeKanaLockOn = locksOn & KeyInfo::CODE_MASK;
1361  msxGraphLockOn = locksOn & KeyInfo::GRAPH_MASK;
1362  }
1363  ar.serialize("msxCapsLockOn", msxCapsLockOn,
1364  "msxCodeKanaLockOn", msxCodeKanaLockOn,
1365  "msxGraphLockOn", msxGraphLockOn);
1366  if (ar.isLoader()) {
1367  locksOn = (msxCapsLockOn ? KeyInfo::CAPS_MASK : 0)
1368  | (msxCodeKanaLockOn ? KeyInfo::CODE_MASK : 0)
1369  | (msxGraphLockOn ? KeyInfo::GRAPH_MASK : 0);
1370  }
1371 
1372  if (ar.versionAtLeast(version, 2)) {
1373  ar.serialize("userKeyMatrix", userKeyMatrix,
1374  "dynKeymap", dynKeymap,
1375  "msxmodifiers", msxmodifiers,
1376  "msxKeyEventQueue", msxKeyEventQueue);
1377  }
1378  // don't serialize hostKeyMatrix
1379 
1380  if (ar.isLoader()) {
1381  // force recalculation of keyMatrix
1382  keysChanged = true;
1383  }
1384 }
1386 
1387 template<typename Archive>
1388 void Keyboard::MsxKeyEventQueue::serialize(Archive& ar, unsigned /*version*/)
1389 {
1390  ar.template serializeBase<Schedulable>(*this);
1391 
1392  // serialization of deque<shared_ptr<const Event>> is not directly
1393  // supported by the serialization framework (main problem is the
1394  // constness, collections of shared_ptr to polymorhpic objects are
1395  // not a problem). Worked around this by serializing the events in
1396  // ascii format. (In all practical cases this queue will anyway be
1397  // empty or contain very few elements).
1398  //ar.serialize("eventQueue", eventQueue);
1399  vector<string> eventStrs;
1400  if (!ar.isLoader()) {
1401  eventStrs = to_vector(view::transform(
1402  eventQueue, [](auto& e) { return e->toString(); }));
1403  }
1404  ar.serialize("eventQueue", eventStrs);
1405  if (ar.isLoader()) {
1406  assert(eventQueue.empty());
1407  for (auto& s : eventStrs) {
1408  eventQueue.push_back(
1410  }
1411  }
1412 }
1413 INSTANTIATE_SERIALIZE_METHODS(Keyboard::MsxKeyEventQueue);
1414 
1415 
1418 // Mapping from SDL keys to emulated keys, ordered by MatrixType
1420 const KeyMatrixPosition Keyboard::keyTabs[][MAX_KEYSYM] = {
1421  {
1422 // MSX Key-Matrix table
1423 //
1424 // row/bit 7 6 5 4 3 2 1 0
1425 // +-----+-----+-----+-----+-----+-----+-----+-----+
1426 // 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
1427 // 1 | ; | ] | [ | \ | = | - | 9 | 8 |
1428 // 2 | B | A | Acc | / | . | , | ` | ' |
1429 // 3 | J | I | H | G | F | E | D | C |
1430 // 4 | R | Q | P | O | N | M | L | K |
1431 // 5 | Z | Y | X | W | V | U | T | S |
1432 // 6 | F3 | F2 | F1 | code| caps|graph| ctrl|shift|
1433 // 7 | ret |selec| bs | stop| tab | esc | F5 | F4 |
1434 // 8 |right| down| up | left| del | ins | hom |space|
1435 // 9 | 4 | 3 | 2 | 1 | 0 | / | + | * |
1436 // 10 | . | , | - | 9 | 8 | 7 | 6 | 5 |
1437 // 11 | | | | | 'NO'| |'YES'| |
1438 // +-----+-----+-----+-----+-----+-----+-----+-----+
1439 // 0 1 2 3 4 5 6 7 8 9 a b c d e f
1440  x , x , x , x , x , x , x , x ,0x75,0x73, x , x , x ,0x77, x , x , //000
1441  x , x , x , x , x , x , x , x , x , x , x ,0x72, x , x , x , x , //010
1442  0x80, x , x , x , x , x , x ,0x20, x , x , x , x ,0x22,0x12,0x23,0x24, //020
1443  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x10,0x11, x ,0x17, x ,0x13, x , x , //030
1444  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //040
1445  x ,0x84,0x85,0x87,0x86, x , x , x , x , x , x ,0x15,0x14,0x16, x , x , //050
1446  0x21,0x26,0x27,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x40,0x41,0x42,0x43,0x44, //060
1447  0x45,0x46,0x47,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, x , x , x , x ,0x83, //070
1448  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //080
1449  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //090
1450  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0A0
1451  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0B0
1452  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0C0
1453  x , x , x , x , x , x , x , x ,0x81, x , x , x , x , x , x , x , //0D0
1454  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0E0
1455  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0F0
1456  0x93,0x94,0x95,0x96,0x97,0xA0,0xA1,0xA2,0xA3,0xA4,0xA7,0x92,0x90,0xA5,0x91,0xA6, //100
1457  x ,0x85,0x86,0x87,0x84,0x82,0x81, x , x , x ,0x65,0x66,0x67,0x70,0x71, x , //110
1458  0x76,0x74, x , x , x , x , x , x , x , x , x , x , x , x , x ,0x60, //120
1459  0x60,0x25,0x61, x , x ,0xB3,0xB1,0xB3,0xB1,0xB1,0xB3, x , x , x , x , x , //130
1460  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //140
1461  },
1462  {
1463 // SVI Keyboard Matrix
1464 //
1465 // row/bit 7 6 5 4 3 2 1 0
1466 // +-----+-----+-----+-----+-----+-----+-----+-----+
1467 // 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
1468 // 1 | / | . | = | , | ' | : | 9 | 8 |
1469 // 2 | G | F | E | D | C | B | A | - |
1470 // 3 | O | N | M | L | K | J | I | H |
1471 // 4 | W | V | U | T | S | R | Q | P |
1472 // 5 | UP | BS | ] | \ | [ | Z | Y | X |
1473 // 6 |LEFT |ENTER|STOP | ESC |RGRAP|LGRAP|CTRL |SHIFT|
1474 // 7 |DOWN | INS | CLS | F5 | F4 | F3 | F2 | F1 |
1475 // 8 |RIGHT| |PRINT| SEL |CAPS | DEL | TAB |SPACE|
1476 // 9 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Numerical keypad
1477 // 10 | , | . | / | * | - | + | 9 | 8 | SVI-328 only
1478 // +-----+-----+-----+-----+-----+-----+-----+-----+
1479 // 0 1 2 3 4 5 6 7 8 9 a b c d e f
1480  x , x , x , x , x , x , x , x ,0x56,0x81, x , x , x ,0x66, x , x , //000
1481  x , x , x , x , x , x , x , x , x , x , x ,0x64, x , x , x , x , //010
1482  0x80, x , x , x , x , x , x ,0x20, x , x , x , x ,0x14,0x20,0x16,0x17, //020
1483  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x10,0x11,0x12, x , x ,0x15, x , x , //030
1484  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //040
1485  x ,0x67,0x57,0x87,0x77, x , x , x , x , x , x ,0x53,0x54,0x55, x , x , //050
1486  x ,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, //060
1487  0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x50,0x51,0x52, x , x , x , x ,0x82, //070
1488  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //080
1489  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //090
1490  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0A0
1491  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0B0
1492  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0C0
1493  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0D0
1494  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0E0
1495  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0F0
1496  0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0xA0,0xA1,0xA6,0xA5,0xA4,0xA3,0xA2,0xA7, //100
1497  x ,0x57,0x77,0x87,0x67,0x76, x , x , x , x ,0x70,0x71,0x72,0x73,0x74, x , //110
1498  0x75,0x65, x , x , x , x , x , x , x , x , x , x , x , x , x ,0x60, //120
1499  0x60, x ,0x61, x , x , x , x , x , x , x , x , x , x , x , x , x , //130
1500  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //140
1501  },
1502  {
1503 // ColecoVision Joystick "Matrix"
1504 //
1505 // The hardware consists of 2 controllers that each have 2 triggers
1506 // and a 12-key keypad. They're not actually connected in a matrix,
1507 // but a ghosting-free matrix is the easiest way to model it in openMSX.
1508 //
1509 // row/bit 7 6 5 4 3 2 1 0
1510 // +-----+-----+-----+-----+-----+-----+-----+-----+
1511 // 0 |TRIGB|TRIGA| | |LEFT |DOWN |RIGHT| UP | controller 1
1512 // 1 |TRIGB|TRIGA| | |LEFT |DOWN |RIGHT| UP | controller 2
1513 // 2 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | controller 1
1514 // 3 | | | | | # | * | 9 | 8 | controller 1
1515 // 4 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | controller 2
1516 // 5 | | | | | # | * | 9 | 8 | controller 2
1517 // +-----+-----+-----+-----+-----+-----+-----+-----+
1518 // 0 1 2 3 4 5 6 7 8 9 a b c d e f
1519  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //000
1520  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //010
1521  0x06, x , x , x , x , x , x , x , x , x , x , x , x ,0x32, x , x , //020
1522  0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x30,0x31, x , x , x ,0x33, x , x , //030
1523  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //040
1524  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //050
1525  x ,0x13,0x42, x ,0x11, x ,0x44,0x45,0x46, x ,0x52, x , x ,0x53,0x43, x , //060
1526  x , x ,0x47,0x12,0x50,0x40,0x41,0x10, x ,0x51, x , x , x , x , x , x , //070
1527  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //080
1528  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //090
1529  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0A0
1530  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0B0
1531  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0C0
1532  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0D0
1533  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0E0
1534  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0F0
1535  0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x30,0x31, x ,0x33,0x32,0x32,0x33, x , //100
1536  x ,0x00,0x02,0x01,0x03, x , x , x , x , x , x , x , x , x , x , x , //110
1537  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,0x07, //120
1538  0x17,0x06,0x16,0x07,0x07, x , x , x , x , x , x , x , x , x , x , x , //130
1539  x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //140
1540  }
1541 };
1542 
1543 } // namespace openmsx
openmsx::UnicodeKeymap::KeyInfo::GRAPH
@ GRAPH
Definition: UnicodeKeymap.hh:100
one_of.hh
openmsx::UnicodeKeymap::KeyInfo::pos
KeyMatrixPosition pos
Definition: UnicodeKeymap.hh:117
openmsx.hh
utf8::is_pua
bool is_pua(uint32_t cp)
Definition: utf8_core.hh:256
openmsx::Keys::K_INSERT
@ K_INSERT
Definition: Keys.hh:128
openmsx::Keys::KM_META
@ KM_META
Definition: Keys.hh:207
openmsx::KeyboardSettings::getAlwaysEnableKeypad
bool getAlwaysEnableKeypad() const
Definition: KeyboardSettings.hh:35
openmsx::IDLE
constexpr auto IDLE
Definition: WD2793.cc:40
openmsx::Scheduler
Definition: Scheduler.hh:33
openmsx::CommandController
Definition: CommandController.hh:17
serialize.hh
openmsx::OPENMSX_BOOT_EVENT
@ OPENMSX_BOOT_EVENT
Definition: Event.hh:36
openmsx::Keys::K_KP_MINUS
@ K_KP_MINUS
Definition: Keys.hh:118
openmsx::flagArg
ArgsInfo flagArg(std::string_view name, bool &flag)
Definition: TclArgParser.hh:72
MSXEventDistributor.hh
openmsx::Keyboard::Keyboard
Keyboard(MSXMotherBoard &motherBoard, Scheduler &scheduler, CommandController &commandController, EventDistributor &eventDistributor, MSXEventDistributor &msxEventDistributor, StateChangeDistributor &stateChangeDistributor, MatrixType matrix, const DeviceConfig &config)
Constructs a new Keyboard object.
Definition: Keyboard.cc:119
openmsx::MSXEventDistributor
Definition: MSXEventDistributor.hh:13
openmsx::StateChange
Base class for all external MSX state changing events.
Definition: StateChange.hh:13
openmsx::StateChangeDistributor::registerListener
void registerListener(StateChangeListener &listener)
(Un)registers the given object to receive state change events.
Definition: StateChangeDistributor.cc:19
openmsx::DeviceConfig
Definition: DeviceConfig.hh:19
serialize_meta.hh
openmsx::StateChangeDistributor::distributeNew
void distributeNew(const EventPtr &event)
Deliver the event to all registered listeners MSX input devices should call the distributeNew() versi...
Definition: StateChangeDistributor.cc:43
openmsx::Keyboard::serialize
void serialize(Archive &ar, unsigned version)
Definition: Keyboard.cc:1347
contains
bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition: stl.hh:92
openmsx::StateChangeDistributor
Definition: StateChangeDistributor.hh:14
openmsx::KeyboardSettings::getMappingMode
MappingMode getMappingMode() const
Definition: KeyboardSettings.hh:32
openmsx::KeyMatrixState::serialize
void serialize(Archive &ar, unsigned)
Definition: Keyboard.cc:80
openmsx::Schedulable
Every class that wants to get scheduled at some point must inherit from this class.
Definition: Schedulable.hh:33
openmsx::Keyboard::~Keyboard
~Keyboard()
Definition: Keyboard.cc:170
openmsx::Keys::K_KP_PERIOD
@ K_KP_PERIOD
Definition: Keys.hh:115
openmsx::KeyMatrixPosition
A position (row, column) in a keyboard matrix.
Definition: UnicodeKeymap.hh:14
openmsx::Keys::K_KP0
@ K_KP0
Definition: Keys.hh:105
openmsx::KeyInfo
UnicodeKeymap::KeyInfo KeyInfo
Definition: Keyboard.cc:59
utf8::next
uint32_t next(octet_iterator &it, octet_iterator end)
Definition: utf8_checked.hh:146
openmsx::OPENMSX_KEY_DOWN_EVENT
@ OPENMSX_KEY_DOWN_EVENT
Definition: Event.hh:13
openmsx::InputEventFactory::createInputEvent
EventPtr createInputEvent(const TclObject &str, Interpreter &interp)
Definition: InputEventFactory.cc:255
openmsx::EventDistributor
Definition: EventDistributor.hh:16
openmsx::KeyMatrixPosition::NUM_ROWS
static constexpr unsigned NUM_ROWS
Rows are in the range [0..NUM_ROWS).
Definition: UnicodeKeymap.hh:19
StateChangeDistributor.hh
openmsx::UnicodeKeymap::KeyInfo::modmask
byte modmask
Definition: UnicodeKeymap.hh:118
span::subspan
constexpr subspan_return_t< Offset, Count > subspan() const
Definition: span.hh:266
span
Definition: span.hh:34
openmsx::KeyMatrixState
Definition: Keyboard.cc:61
openmsx::KeyboardSettings::getDeadkeyHostKey
Keys::KeyCode getDeadkeyHostKey(unsigned n) const
Definition: KeyboardSettings.hh:22
openmsx::Keyboard::transferHostKeyMatrix
void transferHostKeyMatrix(const Keyboard &source)
Definition: Keyboard.cc:248
openmsx::Schedulable::setSyncPoint
void setSyncPoint(EmuTime::param timestamp)
Definition: Schedulable.cc:23
openmsx::Keys::K_KP_MULTIPLY
@ K_KP_MULTIPLY
Definition: Keys.hh:117
CommandController.hh
TclArgParser.hh
OUTER
#define OUTER(type, member)
Definition: outer.hh:41
view::transform
auto transform(Range &&range, UnaryOp op)
Definition: view.hh:306
UNREACHABLE
#define UNREACHABLE
Definition: unreachable.hh:38
openmsx::Keyboard::MatrixType
MatrixType
Definition: Keyboard.hh:39
ReverseManager.hh
openmsx::UnicodeKeymap::KeyInfo::CAPS
@ CAPS
Definition: UnicodeKeymap.hh:100
openmsx::KeyMatrixState::getRelease
byte getRelease() const
Definition: Keyboard.cc:78
openmsx::Keys::getName
string getName(KeyCode keyCode)
Translate key code to key name.
Definition: Keys.cc:740
openmsx::MSXMotherBoard::getReverseManager
ReverseManager & getReverseManager()
Definition: MSXMotherBoard.hh:134
openmsx::UnicodeKeymap::KeyInfo::NUM_MODIFIERS
@ NUM_MODIFIERS
Definition: UnicodeKeymap.hh:100
openmsx::MSXMotherBoard
Definition: MSXMotherBoard.hh:59
one_of
Definition: one_of.hh:7
EventDistributor.hh
InputEvents.hh
openmsx::modifierPosForMatrix
constexpr std::array< KeyMatrixPosition, UnicodeKeymap::KeyInfo::NUM_MODIFIERS > modifierPosForMatrix[]
Definition: Keyboard.cc:100
INSTANTIATE_SERIALIZE_METHODS
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:981
openmsx::KeyMatrixState::getRow
byte getRow() const
Definition: Keyboard.cc:76
openmsx::Keys::K_I
@ K_I
Definition: Keys.hh:84
StateChange.hh
openmsx::KeyboardSettings::getKpEnterMode
KpEnterMode getKpEnterMode() const
Definition: KeyboardSettings.hh:29
openmsx::KeyboardSettings::CHARACTER_MAPPING
@ CHARACTER_MAPPING
Definition: KeyboardSettings.hh:18
serialize_stl.hh
openmsx::Keyboard::getKeys
const byte * getKeys() const
Returns a pointer to the current KeyBoard matrix.
Definition: Keyboard.cc:233
view.hh
ranges::copy
auto copy(InputRange &&range, OutputIter out)
Definition: ranges.hh:149
openmsx::Keyboard
Keyboard
Definition: Keyboard.cc:1385
Keys.hh
openmsx::Keyboard
Definition: Keyboard.hh:35
InputEventFactory.hh
openmsx::UnicodeKeymap::KeyInfo
Definition: UnicodeKeymap.hh:99
utf8_checked.hh
openmsx::Keys::K_CAPSLOCK
@ K_CAPSLOCK
Definition: Keys.hh:162
openmsx::EmuDuration::hz
static constexpr EmuDuration hz(unsigned x)
Definition: EmuDuration.hh:45
outer.hh
openmsx::x
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:1419
checked_cast.hh
openmsx::OPENMSX_FOCUS_EVENT
@ OPENMSX_FOCUS_EVENT
Definition: Event.hh:29
openmsx::Keys::K_KP_ENTER
@ K_KP_ENTER
Definition: Keys.hh:120
openmsx::Schedulable
Schedulable
Definition: Schedulable.cc:69
openmsx::KeyboardSettings::POSITIONAL_MAPPING
@ POSITIONAL_MAPPING
Definition: KeyboardSettings.hh:18
span::size
constexpr index_type size() const noexcept
Definition: span.hh:296
openmsx::MSXEventDistributor::unregisterEventListener
void unregisterEventListener(MSXEventListener &listener)
Unregisters a previously registered event listener.
Definition: MSXEventDistributor.cc:24
openmsx::EmuDuration::sec
static constexpr EmuDuration sec(unsigned x)
Definition: EmuDuration.hh:39
openmsx::UnicodeKeymap::KeyInfo::GRAPH_MASK
static constexpr byte GRAPH_MASK
Definition: UnicodeKeymap.hh:104
openmsx::KeyboardSettings::getAutoToggleCodeKanaLock
bool getAutoToggleCodeKanaLock() const
Definition: KeyboardSettings.hh:41
Keyboard.hh
openmsx::KeyboardSettings::getTraceKeyPresses
bool getTraceKeyPresses() const
Definition: KeyboardSettings.hh:38
openmsx::UnicodeKeymap::KeyInfo::CODE_MASK
static constexpr byte CODE_MASK
Definition: UnicodeKeymap.hh:106
openmsx::KeyMatrixState::KeyMatrixState
KeyMatrixState(EmuTime::param time_, byte row_, byte press_, byte release_)
Definition: Keyboard.cc:65
openmsx::EventType
EventType
Definition: Event.hh:10
openmsx::Keys::K_RETURN
@ K_RETURN
Definition: Keys.hh:34
openmsx::KeyboardSettings::KEY_MAPPING
@ KEY_MAPPING
Definition: KeyboardSettings.hh:18
openmsx::parseTclArgs
std::vector< TclObject > parseTclArgs(Interpreter &interp, span< const TclObject > inArgs, span< const ArgsInfo > table)
Definition: commands/TclArgParser.cc:11
openmsx::KeyboardSettings::getCodeKanaHostKey
Keys::KeyCode getCodeKanaHostKey() const
Definition: KeyboardSettings.hh:26
openmsx::UnicodeKeymap::get
KeyInfo get(unsigned unicode) const
Definition: UnicodeKeymap.cc:101
openmsx::mask
constexpr nibble mask[4][13]
Definition: RP5C01.cc:33
ad_printf
#define ad_printf(...)
Definition: openmsx.hh:11
openmsx::StateChangeDistributor::unregisterListener
void unregisterListener(StateChangeListener &listener)
Definition: StateChangeDistributor.cc:25
openmsx::Scheduler
Scheduler
Definition: Scheduler.cc:133
stl.hh
openmsx::UnicodeKeymap::KeyInfo::SHIFT
@ SHIFT
Definition: UnicodeKeymap.hh:100
openmsx::Keys::K_KP_PLUS
@ K_KP_PLUS
Definition: Keys.hh:119
openmsx::REGISTER_POLYMORPHIC_CLASS
REGISTER_POLYMORPHIC_CLASS(DiskContainer, NowindRomDisk, "NowindRomDisk")
openmsx::defaultKeymapForMatrix
constexpr const char *const defaultKeymapForMatrix[]
Definition: Keyboard.cc:93
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::Keys::KeyCode
KeyCode
Constants that identify keys and key modifiers.
Definition: Keys.hh:26
openmsx::OPENMSX_KEY_UP_EVENT
@ OPENMSX_KEY_UP_EVENT
Definition: Event.hh:12
openmsx::valueArg
ArgsInfo valueArg(std::string_view name, T &value)
Definition: TclArgParser.hh:85
openmsx::ReverseManager::registerKeyboard
void registerKeyboard(Keyboard &keyboard_)
Definition: ReverseManager.hh:36
unreachable.hh
DeviceConfig.hh
openmsx::UnicodeKeymap::KeyInfo::CAPS_MASK
static constexpr byte CAPS_MASK
Definition: UnicodeKeymap.hh:105
openmsx::KeyboardSettings::MSX_KP_COMMA
@ MSX_KP_COMMA
Definition: KeyboardSettings.hh:17
openmsx::Keys::KM_MODE
@ KM_MODE
Definition: Keys.hh:208
openmsx::serialize
void serialize(Archive &ar, T &t, unsigned version)
Definition: serialize_core.hh:41
ranges::fill
void fill(ForwardRange &&range, const T &value)
Definition: ranges.hh:191
CommandException.hh
openmsx::Keys::K_LALT
@ K_LALT
Definition: Keys.hh:169
openmsx
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
to_vector
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))>>
Definition: stl.hh:311
openmsx::Keys::K_MASK
@ K_MASK
Definition: Keys.hh:27
openmsx::Keys::K_KP_DIVIDE
@ K_KP_DIVIDE
Definition: Keys.hh:116
MSXMotherBoard.hh
openmsx::KeyMatrixPosition::isValid
constexpr bool isValid() const
Returns true iff this position is valid.
Definition: UnicodeKeymap.hh:50
openmsx::KeyMatrixState::getPress
byte getPress() const
Definition: Keyboard.cc:77
openmsx::MSXEventDistributor::registerEventListener
void registerEventListener(MSXEventListener &listener)
Registers a given object to receive certain events.
Definition: MSXEventDistributor.cc:18
openmsx::Keys::K_KP9
@ K_KP9
Definition: Keys.hh:114
openmsx::UnicodeKeymap::KeyInfo::CODE
@ CODE
Definition: UnicodeKeymap.hh:100
openmsx::UnicodeKeymap::getRelevantMods
byte getRelevantMods(const KeyInfo &keyInfo) const
Returns a mask in which a bit is set iff the corresponding modifier is relevant for the given key.
Definition: UnicodeKeymap.hh:132
openmsx::KeyMatrixState::KeyMatrixState
KeyMatrixState()=default