38 using std::shared_ptr;
39 using std::make_shared;
43 static const byte SHIFT_MASK = 0x01;
44 static const byte CTRL_MASK = 0x02;
45 static const byte GRAPH_MASK = 0x04;
46 static const byte CAPS_MASK = 0x08;
47 static const byte CODE_MASK = 0x10;
56 virtual string help(
const vector<string>& tokens)
const;
68 virtual string help(
const vector<string>& tokens)
const;
79 template<
typename Archive>
80 void serialize(Archive& ar,
unsigned version);
85 std::deque<shared_ptr<const Event>> eventQueue;
95 template<
typename Archive>
96 void serialize(Archive& ar,
unsigned version);
99 void type(
const string& str);
103 virtual string execute(
const vector<string>& tokens,
EmuTime::param time);
104 virtual string help(
const vector<string>& tokens)
const;
105 virtual void tabCompletion(vector<string>& tokens)
const;
115 bool oldCodeKanaLockOn;
119 bool releaseBeforePress;
120 unsigned typingFrequency;
133 virtual int signalEvent(
const shared_ptr<const Event>& event);
144 enum CapsLockAlignerStateType {
145 MUST_ALIGN_CAPSLOCK, MUST_DISTRIBUTE_KEY_RELEASE, IDLE
153 virtual byte read(
unsigned address);
154 virtual void write(
unsigned address,
byte value);
166 , row(row_), press(press_), release(release_)
169 assert((press != 0) || (release != 0));
173 assert((press & release) == 0);
179 template<
typename Archive>
void serialize(Archive& ar,
unsigned )
181 ar.template serializeBase<StateChange>(*this);
182 ar.serialize(
"row", row);
183 ar.serialize(
"press", press);
184 ar.serialize(
"release", release);
187 byte row, press, release;
192 static bool checkSDLReleasesCapslock()
194 const SDL_version* v = SDL_Linked_Version();
195 if (SDL_VERSIONNUM(v->major, v->minor, v->patch) < SDL_VERSIONNUM(1, 2, 14)) {
200 char *val = SDL_getenv(
"SDL_DISABLE_LOCK_KEYS");
201 return val && (strcmp(val,
"1") == 0 || strcmp(val,
"2") == 0);
211 string_ref keyboardType,
bool hasKP,
bool hasYNKeys,
212 bool keyGhosting_,
bool keyGhostSGCprotected,
213 bool codeKanaLocks_,
bool graphLocks_)
215 , commandController(commandController_)
216 , msxEventDistributor(msxEventDistributor_)
217 , stateChangeDistributor(stateChangeDistributor_)
219 commandController, stateChangeDistributor, scheduler, *this))
221 commandController, stateChangeDistributor, scheduler, *this))
223 commandController, stateChangeDistributor, scheduler, *this))
225 eventDistributor, msxEventDistributor, scheduler, *this))
231 , hasYesNoKeys(hasYNKeys)
232 , keyGhosting(keyGhosting_)
233 , keyGhostingSGCprotected(keyGhostSGCprotected)
234 , codeKanaLocks(codeKanaLocks_)
235 , graphLocks(graphLocks_)
236 , sdlReleasesCapslock(checkSDLReleasesCapslock())
243 msxCapsLockOn =
false;
244 msxCodeKanaLockOn =
false;
245 msxGraphLockOn =
false;
247 memset(keyMatrix, 255,
sizeof(keyMatrix));
248 memset(cmdKeyMatrix, 255,
sizeof(cmdKeyMatrix));
249 memset(userKeyMatrix, 255,
sizeof(userKeyMatrix));
250 memset(hostKeyMatrix, 255,
sizeof(hostKeyMatrix));
251 memset(dynKeymap, 0,
sizeof(dynKeymap));
273 keyMatrix[i] = cmdKeyMatrix[i] & userKeyMatrix[i];
299 for (
unsigned row = 0; row <
NR_KEYROWS; ++row) {
300 hostKeyMatrix[row] = source.hostKeyMatrix[row];
309 void Keyboard::signalEvent(
const shared_ptr<const Event>& event,
319 msxKeyEventQueue->process_asap(time, event);
323 void Keyboard::signalStateChange(
const shared_ptr<StateChange>& event)
325 auto kms =
dynamic_cast<KeyMatrixState*
>(
event.get());
328 userKeyMatrix[kms->getRow()] &= ~kms->getPress();
329 userKeyMatrix[kms->getRow()] |= kms->getRelease();
335 for (
unsigned row = 0; row <
NR_KEYROWS; ++row) {
336 changeKeyMatrixEvent(time, row, hostKeyMatrix[row]);
339 msxKeyEventQueue->clear();
340 memset(dynKeymap, 0,
sizeof(dynKeymap));
346 if (((hostKeyMatrix[row] & press) == 0) &&
347 ((userKeyMatrix[row] & press) == 0)) {
351 changeKeyMatrixEvent(time, row, hostKeyMatrix[row] & ~press);
356 if (((hostKeyMatrix[row] & release) == release) &&
357 ((userKeyMatrix[row] & release) == release)) {
364 changeKeyMatrixEvent(time, row, hostKeyMatrix[row] | release);
371 hostKeyMatrix[row] = newValue;
373 byte diff = userKeyMatrix[row] ^ newValue;
374 if (diff == 0)
return;
375 byte press = userKeyMatrix[row] & diff;
376 byte release = newValue & diff;
377 stateChangeDistributor.
distributeNew(make_shared<KeyMatrixState>(
378 time, row, press, release));
381 bool Keyboard::processQueuedEvent(
const Event& event,
EmuTime::param time)
383 bool insertCodeKanaRelease =
false;
384 auto& keyEvent = checked_cast<
const KeyEvent&>(event);
395 ad_printf(
"Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
396 keyEvent.getUnicode(),
397 keyEvent.getKeyCode(),
399 debug(
"Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
400 keyEvent.getUnicode(),
401 keyEvent.getKeyCode(),
404 ad_printf(
"Key released, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
405 keyEvent.getUnicode(),
406 keyEvent.getKeyCode(),
408 debug(
"Key released, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
409 keyEvent.getUnicode(),
410 keyEvent.getKeyCode(),
413 if (key == keyboardSettings->getDeadkeyHostKey(0) &&
414 keyboardSettings->getMappingMode().getValue() ==
416 processDeadKeyEvent(0, time, down);
417 }
else if (key == keyboardSettings->getDeadkeyHostKey(1) &&
418 keyboardSettings->getMappingMode().getValue() ==
420 processDeadKeyEvent(1, time, down);
421 }
else if (key == keyboardSettings->getDeadkeyHostKey(2) &&
422 keyboardSettings->getMappingMode().getValue() ==
424 processDeadKeyEvent(2, time, down);
426 processCapslockEvent(time, down);
427 }
else if (key == keyboardSettings->getCodeKanaHostKey().getValue()) {
428 processCodeKanaChange(time, down);
430 processGraphChange(time, down);
432 processKeypadEnterKey(time, down);
434 insertCodeKanaRelease = processKeyEvent(time, down, keyEvent);
436 return insertCodeKanaRelease;
444 void Keyboard::processCodeKanaChange(
EmuTime::param time,
bool down)
447 msxCodeKanaLockOn = !msxCodeKanaLockOn;
449 updateKeyMatrix(time, down, 6, CODE_MASK);
460 msxGraphLockOn = !msxGraphLockOn;
462 updateKeyMatrix(time, down, 6, GRAPH_MASK);
469 void Keyboard::processDeadKeyEvent(
unsigned n,
EmuTime::param time,
bool down)
471 UnicodeKeymap::KeyInfo deadkey = unicodeKeymap->getDeadkey(n);
472 if (deadkey.keymask) {
473 updateKeyMatrix(time, down, deadkey.row, deadkey.keymask);
497 void Keyboard::processCapslockEvent(
EmuTime::param time,
bool down)
499 if (sdlReleasesCapslock) {
500 debug(
"Changing CAPS lock state according to SDL request\n");
502 msxCapsLockOn = !msxCapsLockOn;
504 updateKeyMatrix(time, down, 6, CAPS_MASK);
506 debug(
"Pressing CAPS lock and scheduling a release\n");
507 msxCapsLockOn = !msxCapsLockOn;
508 updateKeyMatrix(time,
true, 6, CAPS_MASK);
515 debug(
"Releasing CAPS lock\n");
516 updateKeyMatrix(time,
false, 6, CAPS_MASK);
519 void Keyboard::processKeypadEnterKey(
EmuTime::param time,
bool down)
521 if (!hasKeypad && !keyboardSettings->getAlwaysEnableKeypad().getValue()) {
528 if (keyboardSettings->getKpEnterMode().getValue() ==
536 updateKeyMatrix(time, down, row, mask);
545 void Keyboard::processSdlKey(
EmuTime::param time,
bool down,
int key)
547 if (key < MAX_KEYSYM) {
548 int row = keyTab[key] >> 4;
549 byte mask = 1 << (keyTab[key] & 0xf);
550 if (keyTab[key] == 0xff) assert(mask == 0);
552 if ((row == 11) && !hasYesNoKeys) {
557 updateKeyMatrix(time, down, row, mask);
569 pressKeyMatrixEvent(time, row, mask);
576 msxmodifiers &= ~(mask & 0x17);
579 releaseKeyMatrixEvent(time, row, mask);
581 msxmodifiers |= (mask & 0x17);
594 bool Keyboard::processKeyEvent(
EmuTime::param time,
bool down,
const KeyEvent& keyEvent)
596 bool insertCodeKanaRelease =
false;
610 if (isOnKeypad && !hasKeypad &&
611 !keyboardSettings->getAlwaysEnableKeypad().getValue()) {
631 unicode = keyEvent.getUnicode();
632 if ((unicode < 0x20) || ((0x7F <= unicode) && (unicode < 0xA0))) {
636 }
else if ((0xE000 <= unicode) && (unicode < 0xF900)) {
643 if (key < MAX_KEYSYM) {
650 dynKeymap[key] = unicode;
667 processSdlKey(time, down, key);
671 insertCodeKanaRelease = pressUnicodeByUser(time, unicode,
true);
675 if (key < MAX_KEYSYM) {
676 unicode = dynKeymap[key];
685 processSdlKey(time, down, key);
689 pressUnicodeByUser(time, unicode,
false);
692 return insertCodeKanaRelease;
695 void Keyboard::doKeyGhosting()
713 bool changedSomething;
715 changedSomething =
false;
716 for (
unsigned i = 0; i < NR_KEYROWS - 1; i++) {
717 byte row1 = keyMatrix[i];
718 for (
unsigned j = i + 1; j <
NR_KEYROWS; j++) {
719 byte row2 = keyMatrix[j];
720 if ((row1 != row2) && ((row1 | row2) != 0xff)) {
721 byte rowIold = keyMatrix[i];
722 byte rowJold = keyMatrix[j];
723 if (keyGhostingSGCprotected && i == 6) {
724 keyMatrix[i] = row1 & row2;
725 keyMatrix[j] = (row1 | 0x15) & row2;
728 else if (keyGhostingSGCprotected && j == 6) {
729 keyMatrix[i] = row1 & (row2 | 0x15);
730 keyMatrix[j] = row1 & row2;
731 row1 &= (row2 | 0x15);
736 byte newRow = row1 & row2;
737 keyMatrix[i] = newRow;
738 keyMatrix[j] = newRow;
741 if (rowIold != keyMatrix[i] ||
742 rowJold != keyMatrix[j]) {
743 changedSomething =
true;
748 }
while (changedSomething);
751 string Keyboard::processCmd(
const vector<string>& tokens,
bool up)
753 if (tokens.size() != 3) {
758 throw CommandException(
"Invalid row");
761 throw CommandException(
"Invalid mask");
764 cmdKeyMatrix[row] |= mask;
766 cmdKeyMatrix[row] &= ~mask;
810 bool Keyboard::pressUnicodeByUser(
EmuTime::param time,
unsigned unicode,
bool down)
812 bool insertCodeKanaRelease =
false;
813 UnicodeKeymap::KeyInfo keyInfo = unicodeKeymap->get(unicode);
814 if (keyInfo.keymask == 0) {
815 return insertCodeKanaRelease;
819 keyboardSettings->getAutoToggleCodeKanaLock().getValue() &&
820 msxCodeKanaLockOn != ((keyInfo.modmask & CODE_MASK) == CODE_MASK) &&
825 msxCodeKanaLockOn = !msxCodeKanaLockOn;
826 pressKeyMatrixEvent(time, 6, CODE_MASK);
827 insertCodeKanaRelease =
true;
836 assert(keyInfo.keymask);
837 pressKeyMatrixEvent(time, keyInfo.row, keyInfo.keymask);
839 byte modmask = keyInfo.modmask & ~CAPS_MASK;
840 if (codeKanaLocks) modmask &= ~CODE_MASK;
841 if (graphLocks) modmask &= ~GRAPH_MASK;
842 if ((
'A' <= unicode && unicode <=
'Z') || (
'a' <= unicode && unicode <=
'z')) {
848 byte press = modmask & ~SHIFT_MASK;
850 pressKeyMatrixEvent(time, 6, press);
856 byte newRow = (userKeyMatrix[6] | SHIFT_MASK) & ~modmask;
857 changeKeyMatrixEvent(time, 6, newRow);
861 assert(keyInfo.keymask);
862 releaseKeyMatrixEvent(time, keyInfo.row, keyInfo.keymask);
866 byte mask = SHIFT_MASK | CTRL_MASK;
867 if (!codeKanaLocks) mask |= CODE_MASK;
868 if (!graphLocks) mask |= GRAPH_MASK;
869 byte newRow = userKeyMatrix[6];
870 newRow &= msxmodifiers | ~mask;
871 newRow |= msxmodifiers & mask;
872 changeKeyMatrixEvent(time, 6, newRow);
875 return insertCodeKanaRelease;
884 int Keyboard::pressAscii(
unsigned unicode,
bool down)
887 UnicodeKeymap::KeyInfo keyInfo = unicodeKeymap->get(unicode);
888 byte modmask = keyInfo.modmask & (~CAPS_MASK);
890 modmask &= (~CODE_MASK);
893 modmask &= (~GRAPH_MASK);
897 msxCodeKanaLockOn != ((keyInfo.modmask & CODE_MASK) == CODE_MASK) &&
899 debug(
"Toggling CODE/KANA lock\n");
900 msxCodeKanaLockOn = !msxCodeKanaLockOn;
901 cmdKeyMatrix[6] &= (~CODE_MASK);
902 releaseMask = CODE_MASK;
905 msxGraphLockOn != ((keyInfo.modmask & GRAPH_MASK) == GRAPH_MASK) &&
907 debug(
"Toggling GRAPH lock\n");
908 msxGraphLockOn = !msxGraphLockOn;
909 cmdKeyMatrix[6] &= (~GRAPH_MASK);
910 releaseMask |= GRAPH_MASK;
912 if (msxCapsLockOn != ((keyInfo.modmask & CAPS_MASK) == CAPS_MASK) &&
914 debug(
"Toggling CAPS lock\n");
915 msxCapsLockOn = !msxCapsLockOn;
916 cmdKeyMatrix[6] &= (~CAPS_MASK);
917 releaseMask |= CAPS_MASK;
919 if (releaseMask == 0) {
920 debug(
"Key pasted, unicode: 0x%04x, row: %02d, mask: %02x, modmask: %02x\n",
921 unicode, keyInfo.row, keyInfo.keymask, modmask);
922 cmdKeyMatrix[keyInfo.row] &= ~keyInfo.keymask;
923 cmdKeyMatrix[6] &= ~modmask;
926 cmdKeyMatrix[keyInfo.row] |= keyInfo.keymask;
927 cmdKeyMatrix[6] |= modmask;
939 void Keyboard::pressLockKeys(
int lockKeysMask,
bool down)
943 cmdKeyMatrix[6] &= (~lockKeysMask);
946 cmdKeyMatrix[6] |= lockKeysMask;
958 bool Keyboard::commonKeys(
unsigned unicode1,
unsigned unicode2)
961 UnicodeKeymap::KeyInfo keyInfo1 = unicodeKeymap->get(unicode1);
962 UnicodeKeymap::KeyInfo keyInfo2 = unicodeKeymap->get(unicode2);
964 return ((keyInfo1.row == keyInfo2.row) &&
965 (keyInfo1.keymask & keyInfo2.keymask));
968 void Keyboard::debug(
const char* format, ...)
970 if (keyboardSettings->getTraceKeyPresses().getValue()) {
972 va_start(args, format);
973 vfprintf(stderr, format, args);
985 scheduler,
"keymatrixup")
986 , keyboard(keyboard_)
992 return keyboard.processCmd(tokens,
true);
997 static const string helpText =
998 "keymatrixup <row> <bitmask> release a key in the keyboardmatrix\n";
1009 scheduler,
"keymatrixdown")
1010 , keyboard(keyboard_)
1016 return keyboard.processCmd(tokens,
false);
1021 static const string helpText=
1022 "keymatrixdown <row> <bitmask> press a key in the keyboardmatrix\n";
1031 , keyboard(keyboard_)
1036 const shared_ptr<const Event>& event)
1038 bool processImmediately = eventQueue.empty();
1039 eventQueue.push_back(event);
1040 if (processImmediately) {
1041 executeUntil(time, 0);
1054 shared_ptr<const Event>
event = eventQueue.front();
1055 bool insertCodeKanaRelease = keyboard.processQueuedEvent(*event, time);
1057 if (insertCodeKanaRelease) {
1061 eventQueue.push_front(make_shared<KeyUpEvent>(
1062 keyboard.keyboardSettings->getCodeKanaHostKey().getValue()));
1065 if (!eventQueue.empty()) {
1066 eventQueue.pop_front();
1073 if (!eventQueue.empty()) {
1088 , keyboard(keyboard_)
1090 , releaseLast(false)
1094 oldCodeKanaLockOn =
false;
1095 oldGraphLockOn =
false;
1096 oldCapsLockOn =
false;
1097 releaseBeforePress =
false;
1098 typingFrequency = 15;
1101 string KeyInserter::execute(
const vector<string>& tokens,
EmuTime::param )
1103 if (tokens.size() < 2) {
1107 releaseBeforePress =
false;
1108 typingFrequency = 15;
1111 if (tokens.size() == 2) {
1116 vector<string> arguments;
1117 for (
unsigned i = 1; i < tokens.size(); ++i) {
1118 const string token = tokens[i];
1119 if (token ==
"-release") {
1120 releaseBeforePress =
true;
1121 }
else if (token ==
"-freq") {
1122 if (++i == tokens.size()) {
1123 throw CommandException(
"Missing argument");
1126 throw CommandException(
"Wrong argument for -freq (should be a positive number)");
1129 arguments.push_back(token);
1133 if (arguments.size() != 1)
throw SyntaxError();
1140 string KeyInserter::help(
const vector<string>& )
const
1142 static const string helpText =
"Type a string in the emulated MSX.\n" \
1143 "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" \
1144 "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";
1148 void KeyInserter::tabCompletion(vector<string>& tokens)
const
1150 vector<const char*> options;
1151 if (find(tokens.begin(), tokens.end(),
"-release") == tokens.end()) {
1152 options.push_back(
"-release");
1154 if (find(tokens.begin(), tokens.end(),
"-freq") == tokens.end()) {
1155 options.push_back(
"-freq");
1160 void KeyInserter::type(
const string& str)
1165 oldCodeKanaLockOn = keyboard.msxCodeKanaLockOn;
1166 oldGraphLockOn = keyboard.msxGraphLockOn;
1167 oldCapsLockOn = keyboard.msxCapsLockOn;
1168 if (text_utf8.empty()) {
1176 if (lockKeysMask != 0) {
1178 keyboard.pressLockKeys(lockKeysMask,
false);
1181 keyboard.pressAscii(last,
false);
1183 if (text_utf8.empty()) {
1184 releaseLast =
false;
1186 if (oldCodeKanaLockOn != keyboard.msxCodeKanaLockOn) {
1187 keyboard.debug(
"Restoring CODE/KANA lock\n");
1188 lockKeysMask = CODE_MASK;
1189 keyboard.msxCodeKanaLockOn = !keyboard.msxCodeKanaLockOn;
1191 if (oldGraphLockOn != keyboard.msxGraphLockOn) {
1192 keyboard.debug(
"Restoring GRAPH lock\n");
1193 lockKeysMask |= GRAPH_MASK;
1194 keyboard.msxGraphLockOn = !keyboard.msxGraphLockOn;
1196 if (oldCapsLockOn != keyboard.msxCapsLockOn) {
1197 keyboard.debug(
"Restoring CAPS lock\n");
1198 lockKeysMask |= CAPS_MASK;
1199 keyboard.msxCapsLockOn = !keyboard.msxCapsLockOn;
1201 if (lockKeysMask != 0) {
1203 keyboard.pressLockKeys(lockKeysMask,
true);
1210 auto it = text_utf8.begin();
1211 unsigned current =
utf8::next(it, text_utf8.end());
1212 if (releaseLast ==
true && (releaseBeforePress || keyboard.commonKeys(last, current))) {
1216 releaseLast =
false;
1220 lockKeysMask = keyboard.pressAscii(current,
true);
1221 if (lockKeysMask == 0) {
1224 text_utf8.erase(text_utf8.begin(), it);
1226 if (releaseBeforePress) releaseLast =
true;
1229 }
catch (std::exception&) {
1256 , keyboard(keyboard_)
1257 , eventDistributor(eventDistributor_)
1258 , msxEventDistributor(msxEventDistributor_)
1271 int CapsLockAligner::signalEvent(
const shared_ptr<const Event>& event)
1273 if (state == IDLE) {
1277 alignCapsLock(time);
1279 state = MUST_ALIGN_CAPSLOCK;
1291 case MUST_ALIGN_CAPSLOCK:
1292 alignCapsLock(time);
1294 case MUST_DISTRIBUTE_KEY_RELEASE: {
1295 assert(keyboard.sdlReleasesCapslock);
1318 bool hostCapsLockOn = ((SDL_GetModState() & KMOD_CAPS) != 0);
1319 if (keyboard.msxCapsLockOn != hostCapsLockOn) {
1320 keyboard.debug(
"Resyncing host and MSX CAPS lock\n");
1325 if (keyboard.sdlReleasesCapslock) {
1326 keyboard.debug(
"Sending fake CAPS release\n");
1327 state = MUST_DISTRIBUTE_KEY_RELEASE;
1343 , keyboard(keyboard_)
1349 return keyboard.
getKeys()[address];
1358 template<
typename Archive>
1361 ar.template serializeBase<Schedulable>(*this);
1362 ar.serialize(
"text", text_utf8);
1363 ar.serialize(
"last", last);
1364 ar.serialize(
"lockKeysMask", lockKeysMask);
1365 ar.serialize(
"releaseLast", releaseLast);
1366 ar.serialize(
"oldCodeKanaLockOn", oldCodeKanaLockOn);
1367 ar.serialize(
"oldGraphLockOn", oldGraphLockOn);
1368 ar.serialize(
"oldCapsLockOn", oldCapsLockOn);
1383 template<
typename Archive>
1386 ar.serialize(
"keyTypeCmd", *keyTypeCmd);
1387 ar.serialize(
"cmdKeyMatrix", cmdKeyMatrix);
1388 ar.serialize(
"msxCapsLockOn", msxCapsLockOn);
1389 ar.serialize(
"msxCodeKanaLockOn", msxCodeKanaLockOn);
1390 ar.serialize(
"msxGraphLockOn", msxGraphLockOn);
1392 if (ar.versionAtLeast(version, 2)) {
1393 ar.serialize(
"userKeyMatrix", userKeyMatrix);
1394 ar.serialize(
"dynKeymap", dynKeymap);
1395 ar.serialize(
"msxmodifiers", msxmodifiers);
1396 ar.serialize(
"msxKeyEventQueue", *msxKeyEventQueue);
1400 if (ar.isLoader()) {
1408 template<
typename Archive>
1411 ar.template serializeBase<Schedulable>(*this);
1420 vector<string> eventStrs;
1421 if (!ar.isLoader()) {
1422 for (
auto& e : eventQueue) {
1423 eventStrs.push_back(e->toString());
1426 ar.serialize(
"eventQueue", eventStrs);
1427 if (ar.isLoader()) {
1428 assert(eventQueue.empty());
1429 for (
auto& s : eventStrs) {
1458 static const byte x = 0xff;
1459 const byte Keyboard::keyTab[MAX_KEYSYM] = {
1461 x , x , x , x , x , x , x , x ,0x75,0x73, x , x , x ,0x77, x , x ,
1462 x , x , x , x , x , x , x , x , x , x , x ,0x72, x , x , x , x ,
1463 0x80, x , x , x , x , x , x ,0x20, x , x , x , x ,0x22,0x12,0x23,0x24,
1464 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x10,0x11, x ,0x17, x ,0x13, x , x ,
1465 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
1466 x ,0x84,0x85,0x87,0x86, x , x , x , x , x , x ,0x15,0x14,0x16, x , x ,
1467 0x21,0x26,0x27,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x40,0x41,0x42,0x43,0x44,
1468 0x45,0x46,0x47,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, x , x , x ,0x62,0x83,
1469 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
1470 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
1471 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
1472 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
1473 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
1474 x , x , x , x , x , x , x , x ,0x81, x , x , x , x , x , x , x ,
1475 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
1476 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
1477 0x93,0x94,0x95,0x96,0x97,0xA0,0xA1,0xA2,0xA3,0xA4,0xA7,0x92,0x90,0xA5,0x91,0xA6,
1478 x ,0x85,0x86,0x87,0x84,0x82,0x81, x , x , x ,0x65,0x66,0x67,0x70,0x71, x ,
1479 0x76,0x74, x , x , x , x , x , x , x , x , x , x , x ,0x63, x ,0x60,
1480 0x60,0x25,0x61,0x64,0x62,0xB3,0xB1,0xB3,0xB1,0xB1,0xB3, x , x , x , x , x ,
1481 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,