46static constexpr bool SANE_CAPSLOCK_BEHAVIOR =
false;
51static constexpr bool SANE_CAPSLOCK_BEHAVIOR =
true;
55static constexpr uint8_t TRY_AGAIN = 0x80;
63 KeyMatrixState(EmuTime::param time_, uint8_t row_, uint8_t press_, uint8_t release_)
65 , row(row_), press(press_), release(release_)
68 assert((press != 0) || (release != 0));
72 assert((press & release) == 0);
74 [[nodiscard]] uint8_t
getRow()
const {
return row; }
75 [[nodiscard]] uint8_t
getPress()
const {
return press; }
76 [[nodiscard]] uint8_t
getRelease()
const {
return release; }
78 template<
typename Archive>
void serialize(Archive& ar,
unsigned )
80 ar.template serializeBase<StateChange>(*
this);
81 ar.serialize(
"row", row,
86 uint8_t row, press, release;
91static constexpr std::array<std::string_view, 4> defaultKeymapForMatrix = {
98static constexpr std::array modifierPosForMatrix = {
100 KeyMatrixPosition(6, 0),
101 KeyMatrixPosition(6, 1),
102 KeyMatrixPosition(6, 2),
103 KeyMatrixPosition(6, 3),
104 KeyMatrixPosition(6, 4),
107 KeyMatrixPosition(6, 0),
108 KeyMatrixPosition(6, 1),
109 KeyMatrixPosition(6, 2),
110 KeyMatrixPosition(8, 3),
111 KeyMatrixPosition(6, 3),
113 std::array<KeyMatrixPosition, UnicodeKeymap::KeyInfo::NUM_MODIFIERS>{
116 KeyMatrixPosition(13, 3),
117 KeyMatrixPosition(13, 2),
118 KeyMatrixPosition(13, 1),
120 KeyMatrixPosition( 0, 4),
127static constexpr KeyMatrixPosition x = KeyMatrixPosition();
128static constexpr std::array keyTabs = {
129 std::array<KeyMatrixPosition, Keyboard::MAX_KEYSYM>{
148 x , x , x , x , x , x , x , x ,0x75,0x73, x , x , x ,0x77, x , x ,
149 x , x , x , x , x , x , x , x , x , x , x ,0x72, x , x , x , x ,
150 0x80, x , x , x , x , x , x ,0x20, x , x , x , x ,0x22,0x12,0x23,0x24,
151 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x10,0x11, x ,0x17, x ,0x13, x , x ,
152 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
153 x ,0x84,0x85,0x87,0x86, x , x , x , x , x , x ,0x15,0x14,0x16, x , x ,
154 0x21,0x26,0x27,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x40,0x41,0x42,0x43,0x44,
155 0x45,0x46,0x47,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, x , x , x , x ,0x83,
156 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
157 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
158 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
159 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
160 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
161 x , x , x , x , x , x , x , x ,0x81, x , x , x , x , x , x , x ,
162 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
163 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
164 0x93,0x94,0x95,0x96,0x97,0xA0,0xA1,0xA2,0xA3,0xA4,0xA7,0x92,0x90,0xA5,0x91,0xA6,
165 x ,0x85,0x86,0x87,0x84,0x82,0x81, x , x , x ,0x65,0x66,0x67,0x70,0x71, x ,
166 0x76,0x74, x , x , x , x , x , x , x , x , x , x , x , x , x ,0x60,
167 0x60,0x25,0x61, x , x ,0xB3,0xB1,0xB3,0xB1,0xB1,0xB3, x , x , x , x , x ,
168 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
170 std::array<KeyMatrixPosition, Keyboard::MAX_KEYSYM>{
188 x , x , x , x , x , x , x , x ,0x56,0x81, x , x , x ,0x66, x , x ,
189 x , x , x , x , x , x , x , x , x , x , x ,0x64, x , x , x , x ,
190 0x80, x , x , x , x , x , x ,0x20, x , x , x , x ,0x14,0x20,0x16,0x17,
191 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x10,0x11,0x12, x , x ,0x15, x , x ,
192 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
193 x ,0x67,0x57,0x87,0x77, x , x , x , x , x , x ,0x53,0x54,0x55, x , x ,
194 x ,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
195 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x50,0x51,0x52, x , x , x , x ,0x82,
196 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
197 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
198 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
199 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
200 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
201 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
202 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
203 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
204 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0xA0,0xA1,0xA6,0xA5,0xA4,0xA3,0xA2,0xA7,
205 x ,0x57,0x77,0x87,0x67,0x76, x , x , x , x ,0x70,0x71,0x72,0x73,0x74, x ,
206 0x75,0x65, x , x , x , x , x , x , x , x , x , x , x , x , x ,0x60,
207 0x60, x ,0x61, x , x , x , x , x , x , x , x , x , x , x , x , x ,
208 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
210 std::array<KeyMatrixPosition, Keyboard::MAX_KEYSYM>{
227 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
228 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
229 0x06, x , x , x , x , x , x , x , x , x , x , x , x ,0x32, x , x ,
230 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x30,0x31, x , x , x ,0x33, x , x ,
231 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
232 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
233 x ,0x13,0x42, x ,0x11, x ,0x44,0x45,0x46, x ,0x52, x , x ,0x53,0x43, x ,
234 x , x ,0x47,0x12,0x50,0x40,0x41,0x10, x ,0x51, x , x , x , x , x , x ,
235 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
236 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
237 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
238 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
239 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
240 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
241 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
242 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
243 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x30,0x31, x ,0x33,0x32,0x32,0x33, x ,
244 x ,0x00,0x02,0x01,0x03, x , x , x , x , x , x , x , x , x , x , x ,
245 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,0x07,
246 0x17,0x06,0x16,0x07,0x07, x , x , x , x , x , x , x , x , x , x , x ,
247 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
249 std::array<KeyMatrixPosition, Keyboard::MAX_KEYSYM>{
275 x , x , x , x , x , x , x , x ,0x34,0xC3, x , x , x ,0x56, x , x ,
276 x , x , x , x , x , x , x , x , x , x , x ,0xD0, x , x , x , x ,
277 0x14, x , x , x , x , x , x ,0x36, x , x , x , x ,0x05,0xA0,0x15,0x25,
278 0x90,0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x36,0x26, x ,0xB0, x , x ,
279 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
280 x ,0x55,0x66,0x65,0x45, x , x , x , x , x , x ,0x47,0xC0,0x46, x , x ,
281 0x37,0x02,0x43,0x23,0x22,0x21,0x32,0x42,0x52,0x07,0x62,0x06,0x16,0x63,0x53,0x17,
282 0x27,0x01,0x31,0x12,0x41,0x61,0x33,0x11,0x13,0x51,0x03, x , x , x , x ,0x34,
283 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
284 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
285 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
286 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
287 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
288 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
289 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
290 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
291 0x90,0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x15,0x25,0x36,0xA0,0x26,0x56,
292 x ,0x66,0x45,0x65,0x55,0x34,0x24, x , x , x , x , x , x , x , x , x ,
293 0x35,0xD0, x , x , x , x , x , x , x , x , x , x , x , x , x ,0xD3,
294 0xD3, x ,0xD2,0x04,0xD1, x , x , x , x , x , x , x , x , x , x , x ,
295 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x ,
308 , commandController(commandController_)
309 , msxEventDistributor(msxEventDistributor_)
310 , stateChangeDistributor(stateChangeDistributor_)
311 , keyTab(keyTabs[matrix])
312 , modifierPos(modifierPosForMatrix[matrix])
313 , keyMatrixUpCmd (commandController, stateChangeDistributor, scheduler_)
314 , keyMatrixDownCmd(commandController, stateChangeDistributor, scheduler_)
315 , keyTypeCmd (commandController, stateChangeDistributor, scheduler_)
316 , msxcode2UnicodeCmd(commandController)
317 , unicode2MsxcodeCmd(commandController)
318 , capsLockAligner(eventDistributor, scheduler_)
319 , keyboardSettings(commandController)
320 , msxKeyEventQueue(scheduler_, commandController.getInterpreter())
321 , keybDebuggable(motherBoard)
322 , unicodeKeymap(config.getChildData(
323 "keyboard_type", defaultKeymapForMatrix[matrix]))
324 , hasKeypad(config.getChildDataAsBool(
"has_keypad", true))
325 , blockRow11(matrix == MATRIX_MSX
326 && !config.getChildDataAsBool(
"has_yesno_keys", false))
327 , keyGhosting(config.getChildDataAsBool(
"key_ghosting", true))
328 , keyGhostingSGCprotected(config.getChildDataAsBool(
329 "key_ghosting_sgc_protected", true))
330 , modifierIsLock(
KeyInfo::CAPS_MASK
331 | (config.getChildDataAsBool(
"code_kana_locks", false) ?
KeyInfo::CODE_MASK : 0)
332 | (config.getChildDataAsBool(
"graph_locks", false) ?
KeyInfo::GRAPH_MASK : 0))
360static constexpr void doKeyGhosting(std::span<uint8_t, KeyMatrixPosition::NUM_ROWS> matrix,
379 bool changedSomething =
false;
381 changedSomething =
false;
386 auto row1 = matrix[i];
388 auto row2 = matrix[j];
389 if ((row1 != row2) && ((row1 | row2) != 0xff)) {
390 auto rowIold = matrix[i];
391 auto rowJold = matrix[j];
394 if (protectRow6 && i == 6) {
395 matrix[i] = row1 & row2;
396 matrix[j] = (row1 | 0x15) & row2;
398 }
else if (protectRow6 && j == 6) {
399 matrix[i] = row1 & (row2 | 0x15);
400 matrix[j] = row1 & row2;
401 row1 &= (row2 | 0x15);
405 uint8_t newRow = row1 & row2;
410 if (rowIold != matrix[i] ||
411 rowJold != matrix[j]) {
412 changedSomething =
true;
417 }
while (changedSomething);
424 std::span matrix = keyTypeCmd.isActive() ? typeKeyMatrix : userKeyMatrix;
426 keyMatrix[row] = cmdKeyMatrix[row] & matrix[row];
429 doKeyGhosting(keyMatrix, keyGhostingSGCprotected);
453 hostKeyMatrix[row] = source.hostKeyMatrix[row];
462void Keyboard::signalMSXEvent(
const Event& event,
463 EmuTime::param time)
noexcept
470 msxKeyEventQueue.process_asap(time, event);
474void Keyboard::signalStateChange(
const StateChange& event)
476 const auto* kms =
dynamic_cast<const KeyMatrixState*
>(&event);
479 userKeyMatrix[kms->getRow()] &= uint8_t(~kms->getPress());
480 userKeyMatrix[kms->getRow()] |= kms->getRelease();
484void Keyboard::stopReplay(EmuTime::param time)
noexcept
486 for (
auto [row, hkm] :
enumerate(hostKeyMatrix)) {
487 changeKeyMatrixEvent(time, uint8_t(row), hkm);
490 msxKeyEventQueue.clear();
496 return modifierIsLock
497 & (locksOn ^ keyInfo.modMask)
501void Keyboard::pressKeyMatrixEvent(EmuTime::param time, KeyMatrixPosition pos)
503 if (!pos.isValid()) {
507 auto row = pos.getRow();
508 auto press = pos.getMask();
509 if (((hostKeyMatrix[row] & press) == 0) &&
510 ((userKeyMatrix[row] & press) == 0)) {
514 changeKeyMatrixEvent(time, row, hostKeyMatrix[row] & ~press);
517void Keyboard::releaseKeyMatrixEvent(EmuTime::param time, KeyMatrixPosition pos)
519 if (!pos.isValid()) {
523 auto row = pos.getRow();
524 auto release = pos.getMask();
525 if (((hostKeyMatrix[row] & release) == release) &&
526 ((userKeyMatrix[row] & release) == release)) {
533 changeKeyMatrixEvent(time, row, hostKeyMatrix[row] | release);
536void Keyboard::changeKeyMatrixEvent(EmuTime::param time, uint8_t row, uint8_t newValue)
540 hostKeyMatrix[row] = newValue;
542 uint8_t diff = userKeyMatrix[row] ^ newValue;
543 if (diff == 0)
return;
544 uint8_t press = userKeyMatrix[row] & diff;
545 uint8_t release = newValue & diff;
547 time, row, press, release);
553bool Keyboard::processQueuedEvent(
const Event& event, EmuTime::param time)
557 const auto& keyEvent = get<KeyEvent>(event);
560 ? keyEvent.getScanCode() : keyEvent.getKeyCode();
570 ad_printf(
"Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
571 keyEvent.getUnicode(),
572 keyEvent.getKeyCode(),
574 debug(
"Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
575 keyEvent.getUnicode(),
576 keyEvent.getKeyCode(),
579 ad_printf(
"Key released, keyCode: 0x%05x, keyName: %s\n",
580 keyEvent.getKeyCode(),
582 debug(
"Key released, keyCode: 0x%05x, keyName: %s\n",
583 keyEvent.getKeyCode(),
589 for (
auto n :
xrange(3)) {
592 if (deadKey.isValid()) {
593 updateKeyMatrix(time, down, deadKey.pos);
601 processCapslockEvent(time, down);
604 processCodeKanaChange(time, down);
607 processGraphChange(time, down);
610 processKeypadEnterKey(time, down);
613 return processKeyEvent(time, down, keyEvent);
622void Keyboard::processCodeKanaChange(EmuTime::param time,
bool down)
635void Keyboard::processGraphChange(EmuTime::param time,
bool down)
648void Keyboard::processCapslockEvent(EmuTime::param time,
bool down)
650 if (SANE_CAPSLOCK_BEHAVIOR) {
651 debug(
"Changing CAPS lock state according to SDL request\n");
657 debug(
"Pressing CAPS lock and scheduling a release\n");
664void Keyboard::executeUntil(EmuTime::param time)
666 debug(
"Releasing CAPS lock\n");
670void Keyboard::processKeypadEnterKey(EmuTime::param time,
bool down)
677 processSdlKey(time, down,
688void Keyboard::processSdlKey(EmuTime::param time,
bool down,
Keys::KeyCode key)
691 auto pos = keyTab[key];
693 if (pos.getRow() == 11 && blockRow11) {
697 updateKeyMatrix(time, down, pos);
705void Keyboard::updateKeyMatrix(EmuTime::param time,
bool down, KeyMatrixPosition pos)
707 if (!pos.isValid()) {
712 pressKeyMatrixEvent(time, pos);
717 for (
auto [i, mp] :
enumerate(modifierPos)) {
719 msxModifiers &= uint8_t(~(1 << i));
723 releaseKeyMatrixEvent(time, pos);
724 for (
auto [i, mp] :
enumerate(modifierPos)) {
726 msxModifiers |= 1 << i;
741bool Keyboard::processKeyEvent(EmuTime::param time,
bool down,
const KeyEvent& keyEvent)
745 auto keyCode = keyEvent.getKeyCode();
746 auto scanCode = keyEvent.getScanCode();
755 if (isOnKeypad && !hasKeypad &&
775#if defined(__APPLE__)
783 unicode = keyEvent.getUnicode();
784 if ((unicode < 0x20) || ((0x7F <= unicode) && (unicode < 0xA0))) {
794 keyInfo = unicodeKeymap.
get(unicode);
795 if (!keyInfo.isValid()) {
809 dynKeymap[key] = unicode;
826 processSdlKey(time, down, key);
831 return pressUnicodeByUser(time, keyInfo, unicode,
true);
835#if defined(__APPLE__)
849 processSdlKey(time, down, key);
853 pressUnicodeByUser(time, unicodeKeymap.
get(unicode), unicode,
false);
859void Keyboard::processCmd(Interpreter& interp, std::span<const TclObject> tokens,
bool up)
861 unsigned row = tokens[1].getInt(interp);
862 unsigned mask = tokens[2].getInt(interp);
864 throw CommandException(
"Invalid row");
867 throw CommandException(
"Invalid mask");
870 cmdKeyMatrix[row] |= narrow_cast<uint8_t>(mask);
872 cmdKeyMatrix[row] &= narrow_cast<uint8_t>(~mask);
915bool Keyboard::pressUnicodeByUser(
919 bool insertCodeKanaRelease =
false;
928 insertCodeKanaRelease =
true;
937 pressKeyMatrixEvent(time, keyInfo.pos);
939 uint8_t modMask = keyInfo.modMask & ~modifierIsLock;
940 if ((
'A' <= unicode && unicode <=
'Z') || (
'a' <= unicode && unicode <=
'z')) {
945 modMask &= ~KeyInfo::SHIFT_MASK;
954 for (
auto [i, mp] :
enumerate(modifierPos)) {
955 if ((modMask >> i) & 1) {
956 pressKeyMatrixEvent(time, mp);
961 releaseKeyMatrixEvent(time, keyInfo.pos);
964 for (
auto [i, mp] :
enumerate(modifierPos)) {
965 if (!((modifierIsLock >> i) & 1)) {
968 if ((msxModifiers >> i) & 1) {
969 releaseKeyMatrixEvent(time, mp);
971 pressKeyMatrixEvent(time, mp);
977 return insertCodeKanaRelease;
992uint8_t Keyboard::pressAscii(
unsigned unicode,
bool down)
994 uint8_t releaseMask = 0;
996 if (!keyInfo.isValid()) {
999 uint8_t modMask = keyInfo.
modMask & ~modifierIsLock;
1002 uint8_t toggleLocks = needsLockToggle(keyInfo);
1003 for (
auto [i, mp] :
enumerate(modifierPos)) {
1004 if ((toggleLocks >> i) & 1) {
1005 debug(
"Toggling lock %d\n", i);
1007 releaseMask |= 1 << i;
1008 typeKeyMatrix[mp.getRow()] &= uint8_t(~mp.getMask());
1011 if (releaseMask == 0) {
1012 debug(
"Key pasted, unicode: 0x%04x, row: %02d, col: %d, modMask: %02x\n",
1013 unicode, keyInfo.pos.getRow(), keyInfo.pos.getColumn(), modMask);
1042 auto isPressed = [&](
auto& key) {
1043 return (typeKeyMatrix[key.getRow()] & key.getMask()) == 0;
1048 releaseMask = TRY_AGAIN;
1052 for (
auto [i, mp] :
enumerate(modifierPos)) {
1053 if ((modMask >> i) & 1) {
1054 typeKeyMatrix[mp.getRow()] &= uint8_t(~mp.getMask());
1057 if (releaseMask == 0) {
1059 typeKeyMatrix[keyInfo.pos.getRow()] &= uint8_t(~keyInfo.pos.getMask());
1063 typeKeyMatrix[keyInfo.pos.getRow()] |= keyInfo.pos.getMask();
1064 for (
auto [i, mp] :
enumerate(modifierPos)) {
1065 if ((modMask >> i) & 1) {
1066 typeKeyMatrix[mp.getRow()] |= mp.getMask();
1080void Keyboard::pressLockKeys(uint8_t lockKeysMask,
bool down)
1082 for (
auto [i, mp] :
enumerate(modifierPos)) {
1083 if ((lockKeysMask >> i) & 1) {
1086 typeKeyMatrix[mp.getRow()] &= uint8_t(~mp.getMask());
1089 typeKeyMatrix[mp.getRow()] |= mp.getMask();
1103bool Keyboard::commonKeys(
unsigned unicode1,
unsigned unicode2)
1106 auto keyPos1 = unicodeKeymap.
get(unicode1).
pos;
1107 auto keyPos2 = unicodeKeymap.
get(unicode2).
pos;
1109 return keyPos1 == keyPos2 && keyPos1.
isValid();
1112void Keyboard::debug(
const char* format, ...)
1116 va_start(args, format);
1117 vfprintf(stderr, format, args);
1125Keyboard::KeyMatrixUpCmd::KeyMatrixUpCmd(
1126 CommandController& commandController_,
1127 StateChangeDistributor& stateChangeDistributor_,
1129 : RecordedCommand(commandController_, stateChangeDistributor_,
1130 scheduler_,
"keymatrixup")
1134void Keyboard::KeyMatrixUpCmd::execute(
1135 std::span<const TclObject> tokens, TclObject& , EmuTime::param )
1137 checkNumArgs(tokens, 3, Prefix{1},
"row mask");
1139 return keyboard.processCmd(getInterpreter(), tokens,
true);
1142std::string Keyboard::KeyMatrixUpCmd::help(std::span<const TclObject> )
const
1144 return "keymatrixup <row> <bitmask> release a key in the keyboard matrix\n";
1150Keyboard::KeyMatrixDownCmd::KeyMatrixDownCmd(CommandController& commandController_,
1151 StateChangeDistributor& stateChangeDistributor_,
1153 : RecordedCommand(commandController_, stateChangeDistributor_,
1154 scheduler_,
"keymatrixdown")
1158void Keyboard::KeyMatrixDownCmd::execute(std::span<const TclObject> tokens,
1159 TclObject& , EmuTime::param )
1161 checkNumArgs(tokens, 3, Prefix{1},
"row mask");
1163 return keyboard.processCmd(getInterpreter(), tokens,
false);
1166std::string Keyboard::KeyMatrixDownCmd::help(std::span<const TclObject> )
const
1168 return "keymatrixdown <row> <bitmask> press a key in the keyboard matrix\n";
1174Keyboard::MsxKeyEventQueue::MsxKeyEventQueue(
1175 Scheduler& scheduler_, Interpreter& interp_)
1181void Keyboard::MsxKeyEventQueue::process_asap(
1182 EmuTime::param time,
const Event& event)
1184 bool processImmediately = eventQueue.empty();
1185 eventQueue.push_back(event);
1186 if (processImmediately) {
1191void Keyboard::MsxKeyEventQueue::clear()
1197void Keyboard::MsxKeyEventQueue::executeUntil(EmuTime::param time)
1200 Event
event = eventQueue.front();
1202 bool insertCodeKanaRelease = keyboard.processQueuedEvent(event, time);
1204 if (insertCodeKanaRelease) {
1208 eventQueue.push_front(Event::create<KeyUpEvent>(
1209 keyboard.keyboardSettings.getCodeKanaHostKey()));
1212 if (!eventQueue.empty()) {
1213 eventQueue.pop_front();
1220 if (!eventQueue.empty()) {
1229Keyboard::KeyInserter::KeyInserter(
1230 CommandController& commandController_,
1231 StateChangeDistributor& stateChangeDistributor_,
1233 : RecordedCommand(commandController_, stateChangeDistributor_,
1234 scheduler_,
"type_via_keyboard")
1239void Keyboard::KeyInserter::execute(
1240 std::span<const TclObject> tokens, TclObject& , EmuTime::param )
1242 checkNumArgs(tokens, AtLeast{2},
"?-release? ?-freq hz? ?-cancel? text");
1244 bool cancel =
false;
1245 releaseBeforePress =
false;
1246 typingFrequency = 15;
1249 flagArg(
"-release", releaseBeforePress),
1250 valueArg(
"-freq", typingFrequency),
1252 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan(1), info);
1254 if (typingFrequency <= 0) {
1255 throw CommandException(
"Wrong argument for -freq (should be a positive number)");
1262 if (arguments.size() != 1)
throw SyntaxError();
1264 type(arguments[0].getString());
1267std::string Keyboard::KeyInserter::help(std::span<const TclObject> )
const
1269 return "Type a string in the emulated MSX.\n"
1270 "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"
1271 "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\n"
1272 "Use -cancel to cancel a (long) in-progress type command.";
1275void Keyboard::KeyInserter::tabCompletion(std::vector<std::string>& tokens)
const
1277 using namespace std::literals;
1278 static constexpr std::array options = {
"-release"sv,
"-freq"sv};
1279 completeString(tokens, options);
1282void Keyboard::KeyInserter::type(std::string_view str)
1288 oldLocksOn = keyboard.locksOn;
1289 if (text_utf8.empty()) {
1290 reschedule(getCurrentTime());
1292 text_utf8.append(str.data(), str.size());
1295void Keyboard::KeyInserter::executeUntil(EmuTime::param time)
1298 if (lockKeysMask != 0) {
1300 keyboard.pressLockKeys(lockKeysMask,
false);
1303 keyboard.pressAscii(last,
false);
1305 if (text_utf8.empty()) {
1306 releaseLast =
false;
1307 keyboard.debug(
"Restoring locks: %02X -> %02X\n", keyboard.locksOn, oldLocksOn);
1308 uint8_t diff = oldLocksOn ^ keyboard.locksOn;
1309 lockKeysMask = diff;
1312 keyboard.locksOn ^= diff;
1313 keyboard.pressLockKeys(diff,
true);
1320 auto it =
begin(text_utf8);
1322 if (releaseLast && (releaseBeforePress || keyboard.commonKeys(last, current))) {
1326 releaseLast =
false;
1330 lockKeysMask = keyboard.pressAscii(current,
true);
1331 if (lockKeysMask == 0) {
1334 text_utf8.erase(
begin(text_utf8), it);
1335 }
else if (lockKeysMask & TRY_AGAIN) {
1336 lockKeysMask &= ~TRY_AGAIN;
1337 releaseLast =
false;
1338 }
else if (releaseBeforePress) {
1343 }
catch (std::exception&) {
1349void Keyboard::KeyInserter::reschedule(EmuTime::param time)
1357Keyboard::Msxcode2UnicodeCmd::Msxcode2UnicodeCmd(CommandController& commandController_)
1358 : Command(commandController_,
"msxcode2unicode")
1362void Keyboard::Msxcode2UnicodeCmd::execute(std::span<const TclObject> tokens, TclObject& result)
1364 checkNumArgs(tokens, Between{2, 3},
"msx-string ?fallback?");
1366 auto& interp = getInterpreter();
1368 const auto& msxChars = keyboard.unicodeKeymap.getMsxChars();
1370 auto msx = tokens[1].getBinary();
1371 auto fallback = [&]() -> std::function<uint32_t(uint8_t)> {
1372 if (tokens.size() < 3) {
1374 return [](uint8_t) {
return uint32_t(
' '); };
1375 }
else if (
auto i = tokens[2].getOptionalInt()) {
1377 return [i = *i](uint8_t) {
return uint32_t(i); };
1382 return [&](uint8_t m) {
1383 TclObject cmd{TclObject::MakeListTag{}, tokens[2], m};
1384 return uint32_t(cmd.executeCommand(interp).getInt(interp));
1389 result = msxChars.msxToUtf8(msx, fallback);
1392std::string Keyboard::Msxcode2UnicodeCmd::help(std::span<const TclObject> )
const
1394 return "msxcode2unicode <msx-string> [<fallback>]\n"
1395 "returns a unicode string converted from an MSX-string, i.e. a string based on\n"
1396 "MSX character codes.\n"
1397 "The optional fallback used for each character that cannot be mapped for the\n"
1398 "current MSX model can be:\n"
1399 "- omitted: then space will be used as fallback character.\n"
1400 "- an integer number: then this number will be used as unicode point to be the\n"
1401 " the fallback character.\n"
1402 "- a Tcl proc, which expects one input character and must return one unicode\n"
1407Keyboard::Unicode2MsxcodeCmd::Unicode2MsxcodeCmd(CommandController& commandController_)
1408 : Command(commandController_,
"unicode2msxcode")
1412void Keyboard::Unicode2MsxcodeCmd::execute(std::span<const TclObject> tokens, TclObject& result)
1414 checkNumArgs(tokens, Between{2, 3},
"unicode-string ?fallback?");
1416 auto& interp = getInterpreter();
1418 const auto& msxChars = keyboard.unicodeKeymap.getMsxChars();
1420 const auto& unicode = tokens[1].getString();
1421 auto fallback = [&]() -> std::function<uint8_t(uint32_t)> {
1422 if (tokens.size() < 3) {
1424 return [](uint32_t) {
return uint8_t(
' '); };
1425 }
else if (
auto i = tokens[2].getOptionalInt()) {
1427 return [i = *i](uint32_t) {
return uint8_t(i); };
1432 return [&](uint32_t u) {
1433 TclObject cmd{TclObject::MakeListTag{}, tokens[2], u};
1434 return uint8_t(cmd.executeCommand(interp).getInt(interp));
1439 result = msxChars.utf8ToMsx(unicode, fallback);
1442std::string Keyboard::Unicode2MsxcodeCmd::help(std::span<const TclObject> )
const
1444 return "unicode2msxcode <unicode-string> [<fallback>]\n"
1445 "returns an MSX string, i.e. a string based on MSX character codes, converted\n"
1446 "from a unicode string.\n"
1447 "The optional fallback used for each character that cannot be mapped for the\n"
1448 "current MSX model can be:\n"
1449 "- omitted: then space will be used as fallback character.\n"
1450 "- an integer number: then this number will be used as MSX character number to\n"
1451 " to be the fallback character.\n"
1452 "- a Tcl proc, which expects one input character and must return one MSX\n"
1453 " character number.";
1469Keyboard::CapsLockAligner::CapsLockAligner(
1470 EventDistributor& eventDistributor_,
1473 , eventDistributor(eventDistributor_)
1479Keyboard::CapsLockAligner::~CapsLockAligner()
1485int Keyboard::CapsLockAligner::signalEvent(
const Event& event)
1487 if constexpr (!SANE_CAPSLOCK_BEHAVIOR) {
1492 if (state == IDLE) {
1493 EmuTime::param time = getCurrentTime();
1495 [&](
const FocusEvent&) {
1496 alignCapsLock(time);
1498 [&](
const BootEvent&) {
1499 state = MUST_ALIGN_CAPSLOCK;
1508void Keyboard::CapsLockAligner::executeUntil(EmuTime::param time)
1511 case MUST_ALIGN_CAPSLOCK:
1512 alignCapsLock(time);
1514 case MUST_DISTRIBUTE_KEY_RELEASE: {
1517 keyboard.msxEventDistributor.distributeEvent(event, time);
1536void Keyboard::CapsLockAligner::alignCapsLock(EmuTime::param time)
1538 bool hostCapsLockOn = ((SDL_GetModState() & KMOD_CAPS) != 0);
1541 keyboard.debug(
"Resyncing host and MSX CAPS lock\n");
1545 keyboard.msxEventDistributor.distributeEvent(event, time);
1546 keyboard.debug(
"Sending fake CAPS release\n");
1547 state = MUST_DISTRIBUTE_KEY_RELEASE;
1557Keyboard::KeybDebuggable::KeybDebuggable(MSXMotherBoard& motherBoard_)
1558 : SimpleDebuggable(motherBoard_,
"keymatrix",
"MSX Keyboard Matrix",
1559 KeyMatrixPosition::NUM_ROWS)
1563uint8_t Keyboard::KeybDebuggable::read(
unsigned address)
1566 return keyboard.getKeys()[address];
1569void Keyboard::KeybDebuggable::write(
unsigned , uint8_t )
1575template<
typename Archive>
1578 ar.template serializeBase<Schedulable>(*
this);
1579 ar.serialize(
"text", text_utf8,
1581 "lockKeysMask", lockKeysMask,
1582 "releaseLast", releaseLast);
1584 bool oldCodeKanaLockOn, oldGraphLockOn, oldCapsLockOn;
1585 if constexpr (!Archive::IS_LOADER) {
1590 ar.serialize(
"oldCodeKanaLockOn", oldCodeKanaLockOn,
1591 "oldGraphLockOn", oldGraphLockOn,
1592 "oldCapsLockOn", oldCapsLockOn);
1593 if constexpr (Archive::IS_LOADER) {
1613template<
typename Archive>
1616 ar.serialize(
"keyTypeCmd", keyTypeCmd,
1617 "cmdKeyMatrix", cmdKeyMatrix);
1618 if (ar.versionAtLeast(version, 3)) {
1619 ar.serialize(
"typeKeyMatrix", typeKeyMatrix);
1621 typeKeyMatrix = cmdKeyMatrix;
1624 bool msxCapsLockOn, msxCodeKanaLockOn, msxGraphLockOn;
1625 if constexpr (!Archive::IS_LOADER) {
1630 ar.serialize(
"msxCapsLockOn", msxCapsLockOn,
1631 "msxCodeKanaLockOn", msxCodeKanaLockOn,
1632 "msxGraphLockOn", msxGraphLockOn);
1633 if constexpr (Archive::IS_LOADER) {
1639 if (ar.versionAtLeast(version, 2)) {
1640 ar.serialize(
"userKeyMatrix", userKeyMatrix,
1641 "dynKeymap", dynKeymap,
1642 "msxmodifiers", msxModifiers,
1643 "msxKeyEventQueue", msxKeyEventQueue);
1647 if constexpr (Archive::IS_LOADER) {
1654template<
typename Archive>
1657 ar.template serializeBase<Schedulable>(*
this);
1666 std::vector<std::string> eventStrs;
1667 if constexpr (!Archive::IS_LOADER) {
1669 eventQueue, [](
const auto&
e) {
return toString(
e); }));
1671 ar.serialize(
"eventQueue", eventStrs);
1672 if constexpr (Archive::IS_LOADER) {
1673 assert(eventQueue.empty());
1674 for (
auto& s : eventStrs) {
1675 eventQueue.push_back(
static constexpr EmuDuration sec(unsigned x)
static constexpr EmuDuration hz(unsigned x)
constexpr bool isValid() const
Returns true iff this position is valid.
static constexpr unsigned NUM_ROWS
Rows are in the range [0..NUM_ROWS).
KeyMatrixState(EmuTime::param time_, uint8_t row_, uint8_t press_, uint8_t release_)
void serialize(Archive &ar, unsigned)
uint8_t getRelease() const
bool getAlwaysEnableKeypad() const
bool getTraceKeyPresses() const
MappingMode getMappingMode() const
Keys::KeyCode getDeadKeyHostKey(unsigned n) const
KpEnterMode getKpEnterMode() const
bool getAutoToggleCodeKanaLock() const
Keys::KeyCode getCodeKanaHostKey() const
static constexpr int MAX_KEYSYM
void transferHostKeyMatrix(const Keyboard &source)
void serialize(Archive &ar, unsigned version)
std::span< const uint8_t, KeyMatrixPosition::NUM_ROWS > getKeys() const
Returns a pointer to the current KeyBoard matrix.
const MsxChar2Unicode & getMsxChar2Unicode() const
Keyboard(MSXMotherBoard &motherBoard, Scheduler &scheduler, CommandController &commandController, EventDistributor &eventDistributor, MSXEventDistributor &msxEventDistributor, StateChangeDistributor &stateChangeDistributor, MatrixType matrix, const DeviceConfig &config)
Constructs a new Keyboard object.
void registerEventListener(MSXEventListener &listener)
Registers a given object to receive certain events.
void unregisterEventListener(MSXEventListener &listener)
Unregisters a previously registered event listener.
ReverseManager & getReverseManager()
void registerKeyboard(Keyboard &keyboard_)
Every class that wants to get scheduled at some point must inherit from this class.
void setSyncPoint(EmuTime::param timestamp)
void registerListener(StateChangeListener &listener)
(Un)registers the given object to receive state change events.
void distributeNew(EmuTime::param time, Args &&...args)
Deliver the event to all registered listeners MSX input devices should call the distributeNew() versi...
void unregisterListener(StateChangeListener &listener)
Base class for all external MSX state changing events.
KeyInfo getDeadKey(unsigned n) const
KeyInfo get(unsigned unicode) const
uint8_t getRelevantMods(const KeyInfo &keyInfo) const
Returns a mask in which a bit is set iff the corresponding modifier is relevant for the given key.
const MsxChar2Unicode & getMsxChars() const
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
KeyCode
Constants that identify keys and key modifiers.
std::string getName(KeyCode keyCode)
Translate key code to key name.
This file implemented 3 utility functions:
auto visit(Visitor &&visitor, const Event &event)
ArgsInfo valueArg(std::string_view name, T &value)
REGISTER_POLYMORPHIC_CLASS(StateChange, AutofireStateChange, "AutofireStateChange")
std::vector< TclObject > parseTclArgs(Interpreter &interp, std::span< const TclObject > inArgs, std::span< const ArgsInfo > table)
void serialize(Archive &ar, T &t, unsigned version)
EventType getType(const Event &event)
UnicodeKeymap::KeyInfo KeyInfo
ArgsInfo flagArg(std::string_view name, bool &flag)
std::string toString(const Event &event)
Get a string representation of this event.
constexpr void fill(ForwardRange &&range, const T &value)
constexpr bool is_pua(uint32_t cp)
uint32_t next(octet_iterator &it, octet_iterator end)
constexpr auto transform(Range &&range, UnaryOp op)
#define OUTER(type, member)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))> >
static constexpr uint8_t CAPS_MASK
static constexpr uint8_t SHIFT_MASK
static constexpr uint8_t GRAPH_MASK
static constexpr uint8_t CODE_MASK
constexpr auto xrange(T e)
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)