36 using std::shared_ptr;
37 using std::make_shared;
50 static constexpr
bool SANE_CAPSLOCK_BEHAVIOR =
false;
55 static constexpr
bool SANE_CAPSLOCK_BEHAVIOR =
true;
59 static constexpr
int TRY_AGAIN = 0x80;
69 , row(row_), press(press_), release(release_)
72 assert((press != 0) || (release != 0));
76 assert((press & release) == 0);
78 [[nodiscard]]
byte getRow()
const {
return row; }
79 [[nodiscard]]
byte getPress()
const {
return press; }
80 [[nodiscard]]
byte getRelease()
const {
return release; }
82 template<
typename Archive>
void serialize(Archive& ar,
unsigned )
84 ar.template serializeBase<StateChange>(*
this);
85 ar.serialize(
"row", row,
90 byte row, press, release;
101 constexpr std::array<KeyMatrixPosition, UnicodeKeymap::KeyInfo::NUM_MODIFIERS>
145 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,0x75,0x73,
x ,
x ,
x ,0x77,
x ,
x ,
146 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,0x72,
x ,
x ,
x ,
x ,
147 0x80,
x ,
x ,
x ,
x ,
x ,
x ,0x20,
x ,
x ,
x ,
x ,0x22,0x12,0x23,0x24,
148 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x10,0x11,
x ,0x17,
x ,0x13,
x ,
x ,
149 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
150 x ,0x84,0x85,0x87,0x86,
x ,
x ,
x ,
x ,
x ,
x ,0x15,0x14,0x16,
x ,
x ,
151 0x21,0x26,0x27,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x40,0x41,0x42,0x43,0x44,
152 0x45,0x46,0x47,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,
x ,
x ,
x ,
x ,0x83,
153 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
154 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
155 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
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 ,0x81,
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 0x93,0x94,0x95,0x96,0x97,0xA0,0xA1,0xA2,0xA3,0xA4,0xA7,0x92,0x90,0xA5,0x91,0xA6,
162 x ,0x85,0x86,0x87,0x84,0x82,0x81,
x ,
x ,
x ,0x65,0x66,0x67,0x70,0x71,
x ,
163 0x76,0x74,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,0x60,
164 0x60,0x25,0x61,
x ,
x ,0xB3,0xB1,0xB3,0xB1,0xB1,0xB3,
x ,
x ,
x ,
x ,
x ,
165 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
185 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,0x56,0x81,
x ,
x ,
x ,0x66,
x ,
x ,
186 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,0x64,
x ,
x ,
x ,
x ,
187 0x80,
x ,
x ,
x ,
x ,
x ,
x ,0x20,
x ,
x ,
x ,
x ,0x14,0x20,0x16,0x17,
188 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x10,0x11,0x12,
x ,
x ,0x15,
x ,
x ,
189 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
190 x ,0x67,0x57,0x87,0x77,
x ,
x ,
x ,
x ,
x ,
x ,0x53,0x54,0x55,
x ,
x ,
191 x ,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
192 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x50,0x51,0x52,
x ,
x ,
x ,
x ,0x82,
193 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
194 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
195 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
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 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0xA0,0xA1,0xA6,0xA5,0xA4,0xA3,0xA2,0xA7,
202 x ,0x57,0x77,0x87,0x67,0x76,
x ,
x ,
x ,
x ,0x70,0x71,0x72,0x73,0x74,
x ,
203 0x75,0x65,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,0x60,
204 0x60,
x ,0x61,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
205 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
224 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
225 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
226 0x06,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,0x32,
x ,
x ,
227 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x30,0x31,
x ,
x ,
x ,0x33,
x ,
x ,
228 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
229 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
230 x ,0x13,0x42,
x ,0x11,
x ,0x44,0x45,0x46,
x ,0x52,
x ,
x ,0x53,0x43,
x ,
231 x ,
x ,0x47,0x12,0x50,0x40,0x41,0x10,
x ,0x51,
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 ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
234 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
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 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x30,0x31,
x ,0x33,0x32,0x32,0x33,
x ,
241 x ,0x00,0x02,0x01,0x03,
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 ,0x07,
243 0x17,0x06,0x16,0x07,0x07,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
244 x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
x ,
257 , commandController(commandController_)
258 , msxEventDistributor(msxEventDistributor_)
259 , stateChangeDistributor(stateChangeDistributor_)
260 , keyTab(keyTabs[matrix])
262 , keyMatrixUpCmd (commandController, stateChangeDistributor, scheduler_)
263 , keyMatrixDownCmd(commandController, stateChangeDistributor, scheduler_)
264 , keyTypeCmd (commandController, stateChangeDistributor, scheduler_)
265 , capsLockAligner(eventDistributor, scheduler_)
266 , keyboardSettings(commandController)
267 , msxKeyEventQueue(scheduler_, commandController.getInterpreter())
268 , keybDebuggable(motherBoard)
269 , unicodeKeymap(config.getChildData(
271 , hasKeypad(config.getChildDataAsBool(
"has_keypad", true))
272 , blockRow11(matrix == MATRIX_MSX
273 && !config.getChildDataAsBool(
"has_yesno_keys", false))
274 , keyGhosting(config.getChildDataAsBool(
"key_ghosting", true))
275 , keyGhostingSGCprotected(config.getChildDataAsBool(
276 "key_ghosting_sgc_protected", true))
277 , modifierIsLock(
KeyInfo::CAPS_MASK
278 | (config.getChildDataAsBool(
"code_kana_locks", false) ?
KeyInfo::CODE_MASK : 0)
279 | (config.getChildDataAsBool(
"graph_locks", false) ?
KeyInfo::GRAPH_MASK : 0))
305 template<
unsigned NUM_ROWS>
306 static constexpr
void doKeyGhosting(
byte (&matrix)[NUM_ROWS],
bool protectRow6)
324 bool changedSomething =
false;
326 changedSomething =
false;
327 for (
auto i :
xrange(NUM_ROWS - 1)) {
328 auto row1 = matrix[i];
329 for (
auto j :
xrange(i + 1, NUM_ROWS)) {
330 auto row2 = matrix[j];
331 if ((row1 != row2) && ((row1 | row2) != 0xff)) {
332 auto rowIold = matrix[i];
333 auto rowJold = matrix[j];
336 if (protectRow6 && i == 6) {
337 matrix[i] = row1 & row2;
338 matrix[j] = (row1 | 0x15) & row2;
340 }
else if (protectRow6 && j == 6) {
341 matrix[i] = row1 & (row2 | 0x15);
342 matrix[j] = row1 & row2;
343 row1 &= (row2 | 0x15);
347 auto newRow = row1 & row2;
352 if (rowIold != matrix[i] ||
353 rowJold != matrix[j]) {
354 changedSomething =
true;
359 }
while (changedSomething);
366 const auto* matrix = keyTypeCmd.isActive() ? typeKeyMatrix : userKeyMatrix;
368 keyMatrix[row] = cmdKeyMatrix[row] & matrix[row];
371 doKeyGhosting(keyMatrix, keyGhostingSGCprotected);
395 hostKeyMatrix[row] = source.hostKeyMatrix[row];
404 void Keyboard::signalMSXEvent(
const shared_ptr<const Event>& event,
405 EmuTime::param time) noexcept
412 msxKeyEventQueue.process_asap(time, event);
416 void Keyboard::signalStateChange(
const shared_ptr<StateChange>& event)
418 const auto* kms =
dynamic_cast<const KeyMatrixState*
>(
event.get());
421 userKeyMatrix[kms->getRow()] &= ~kms->getPress();
422 userKeyMatrix[kms->getRow()] |= kms->getRelease();
426 void Keyboard::stopReplay(EmuTime::param time) noexcept
428 for (
auto [row, hkm] :
enumerate(hostKeyMatrix)) {
429 changeKeyMatrixEvent(time,
byte(row), hkm);
432 msxKeyEventQueue.clear();
433 memset(dynKeymap, 0,
sizeof(dynKeymap));
438 return modifierIsLock
439 & (locksOn ^ keyInfo.modmask)
443 void Keyboard::pressKeyMatrixEvent(EmuTime::param time, KeyMatrixPosition pos)
445 if (!pos.isValid()) {
449 auto row = pos.getRow();
450 auto press = pos.getMask();
451 if (((hostKeyMatrix[row] & press) == 0) &&
452 ((userKeyMatrix[row] & press) == 0)) {
456 changeKeyMatrixEvent(time, row, hostKeyMatrix[row] & ~press);
459 void Keyboard::releaseKeyMatrixEvent(EmuTime::param time, KeyMatrixPosition pos)
461 if (!pos.isValid()) {
465 auto row = pos.getRow();
466 auto release = pos.getMask();
467 if (((hostKeyMatrix[row] & release) == release) &&
468 ((userKeyMatrix[row] & release) == release)) {
475 changeKeyMatrixEvent(time, row, hostKeyMatrix[row] | release);
478 void Keyboard::changeKeyMatrixEvent(EmuTime::param time,
byte row,
byte newValue)
482 hostKeyMatrix[row] = newValue;
484 byte diff = userKeyMatrix[row] ^ newValue;
485 if (diff == 0)
return;
486 byte press = userKeyMatrix[row] & diff;
487 byte release = newValue & diff;
488 stateChangeDistributor.
distributeNew(make_shared<KeyMatrixState>(
489 time, row, press, release));
495 bool Keyboard::processQueuedEvent(
const Event& event, EmuTime::param time)
499 const auto& keyEvent = checked_cast<const KeyEvent&>(event);
502 ? keyEvent.getScanCode() : keyEvent.getKeyCode();
512 ad_printf(
"Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
513 keyEvent.getUnicode(),
514 keyEvent.getKeyCode(),
516 debug(
"Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
517 keyEvent.getUnicode(),
518 keyEvent.getKeyCode(),
521 ad_printf(
"Key released, keyCode: 0x%05x, keyName: %s\n",
522 keyEvent.getKeyCode(),
524 debug(
"Key released, keyCode: 0x%05x, keyName: %s\n",
525 keyEvent.getKeyCode(),
531 for (
auto n :
xrange(3)) {
534 if (deadkey.isValid()) {
535 updateKeyMatrix(time, down, deadkey.pos);
543 processCapslockEvent(time, down);
546 processCodeKanaChange(time, down);
549 processGraphChange(time, down);
552 processKeypadEnterKey(time, down);
555 return processKeyEvent(time, down, keyEvent);
564 void Keyboard::processCodeKanaChange(EmuTime::param time,
bool down)
577 void Keyboard::processGraphChange(EmuTime::param time,
bool down)
590 void Keyboard::processCapslockEvent(EmuTime::param time,
bool down)
592 if (SANE_CAPSLOCK_BEHAVIOR) {
593 debug(
"Changing CAPS lock state according to SDL request\n");
599 debug(
"Pressing CAPS lock and scheduling a release\n");
606 void Keyboard::executeUntil(EmuTime::param time)
608 debug(
"Releasing CAPS lock\n");
612 void Keyboard::processKeypadEnterKey(EmuTime::param time,
bool down)
619 processSdlKey(time, down,
630 void Keyboard::processSdlKey(EmuTime::param time,
bool down,
Keys::KeyCode key)
633 auto pos = keyTab[key];
635 if (pos.getRow() == 11 && blockRow11) {
639 updateKeyMatrix(time, down, pos);
647 void Keyboard::updateKeyMatrix(EmuTime::param time,
bool down, KeyMatrixPosition pos)
649 if (!pos.isValid()) {
654 pressKeyMatrixEvent(time, pos);
659 for (
auto [i, mp] :
enumerate(modifierPos)) {
661 msxModifiers &= ~(1 << i);
665 releaseKeyMatrixEvent(time, pos);
666 for (
auto [i, mp] :
enumerate(modifierPos)) {
668 msxModifiers |= 1 << i;
683 bool Keyboard::processKeyEvent(EmuTime::param time,
bool down,
const KeyEvent& keyEvent)
687 auto keyCode = keyEvent.getKeyCode();
688 auto scanCode = keyEvent.getScanCode();
697 if (isOnKeypad && !hasKeypad &&
717 #if defined(__APPLE__)
725 unicode = keyEvent.getUnicode();
726 if ((unicode < 0x20) || ((0x7F <= unicode) && (unicode < 0xA0))) {
736 keyInfo = unicodeKeymap.
get(unicode);
737 if (!keyInfo.isValid()) {
751 dynKeymap[key] = unicode;
768 processSdlKey(time, down, key);
773 return pressUnicodeByUser(time, keyInfo, unicode,
true);
777 #if defined(__APPLE__)
791 processSdlKey(time, down, key);
795 pressUnicodeByUser(time, unicodeKeymap.
get(unicode), unicode,
false);
803 unsigned row = tokens[1].getInt(interp);
804 unsigned mask = tokens[2].getInt(interp);
806 throw CommandException(
"Invalid row");
809 throw CommandException(
"Invalid mask");
812 cmdKeyMatrix[row] |=
mask;
814 cmdKeyMatrix[row] &= ~
mask;
857 bool Keyboard::pressUnicodeByUser(
861 bool insertCodeKanaRelease =
false;
870 insertCodeKanaRelease =
true;
879 pressKeyMatrixEvent(time, keyInfo.pos);
881 byte modmask = keyInfo.modmask & ~modifierIsLock;
882 if ((
'A' <= unicode && unicode <=
'Z') || (
'a' <= unicode && unicode <=
'z')) {
887 modmask &= ~
KeyInfo::SHIFT_MASK;
896 for (
auto [i, mp] :
enumerate(modifierPos)) {
897 if ((modmask >> i) & 1) {
898 pressKeyMatrixEvent(time, mp);
903 releaseKeyMatrixEvent(time, keyInfo.pos);
906 for (
auto [i, mp] :
enumerate(modifierPos)) {
907 if (!((modifierIsLock >> i) & 1)) {
910 if ((msxModifiers >> i) & 1) {
911 releaseKeyMatrixEvent(time, mp);
913 pressKeyMatrixEvent(time, mp);
919 return insertCodeKanaRelease;
934 int Keyboard::pressAscii(
unsigned unicode,
bool down)
938 if (!keyInfo.isValid()) {
941 byte modmask = keyInfo.
modmask & ~modifierIsLock;
944 byte toggleLocks = needsLockToggle(keyInfo);
945 for (
auto [i, mp] :
enumerate(modifierPos)) {
946 if ((toggleLocks >> i) & 1) {
947 debug(
"Toggling lock %d\n", i);
949 releaseMask |= 1 << i;
950 typeKeyMatrix[mp.getRow()] &= ~mp.getMask();
953 if (releaseMask == 0) {
954 debug(
"Key pasted, unicode: 0x%04x, row: %02d, col: %d, modmask: %02x\n",
955 unicode, keyInfo.pos.getRow(), keyInfo.pos.getColumn(), modmask);
984 auto isPressed = [&](
auto& key) {
985 return (typeKeyMatrix[key.getRow()] & key.getMask()) == 0;
990 releaseMask = TRY_AGAIN;
994 for (
auto [i, mp] :
enumerate(modifierPos)) {
995 if ((modmask >> i) & 1) {
996 typeKeyMatrix[mp.getRow()] &= ~mp.getMask();
999 if (releaseMask == 0) {
1001 typeKeyMatrix[keyInfo.pos.getRow()] &= ~keyInfo.pos.getMask();
1005 typeKeyMatrix[keyInfo.pos.getRow()] |= keyInfo.pos.getMask();
1006 for (
auto [i, mp] :
enumerate(modifierPos)) {
1007 if ((modmask >> i) & 1) {
1008 typeKeyMatrix[mp.getRow()] |= mp.getMask();
1022 void Keyboard::pressLockKeys(
byte lockKeysMask,
bool down)
1024 for (
auto [i, mp] :
enumerate(modifierPos)) {
1025 if ((lockKeysMask >> i) & 1) {
1028 typeKeyMatrix[mp.getRow()] &= ~mp.getMask();
1031 typeKeyMatrix[mp.getRow()] |= mp.getMask();
1045 bool Keyboard::commonKeys(
unsigned unicode1,
unsigned unicode2)
1048 auto keyPos1 = unicodeKeymap.
get(unicode1).
pos;
1049 auto keyPos2 = unicodeKeymap.
get(unicode2).
pos;
1051 return keyPos1 == keyPos2 && keyPos1.
isValid();
1054 void Keyboard::debug(
const char* format, ...)
1058 va_start(args, format);
1059 vfprintf(stderr, format, args);
1067 Keyboard::KeyMatrixUpCmd::KeyMatrixUpCmd(
1068 CommandController& commandController_,
1069 StateChangeDistributor& stateChangeDistributor_,
1071 : RecordedCommand(commandController_, stateChangeDistributor_,
1072 scheduler_,
"keymatrixup")
1076 void Keyboard::KeyMatrixUpCmd::execute(
1079 checkNumArgs(tokens, 3, Prefix{1},
"row mask");
1081 return keyboard.processCmd(getInterpreter(), tokens,
true);
1084 string Keyboard::KeyMatrixUpCmd::help(
const vector<string>& )
const
1086 return "keymatrixup <row> <bitmask> release a key in the keyboardmatrix\n";
1092 Keyboard::KeyMatrixDownCmd::KeyMatrixDownCmd(CommandController& commandController_,
1093 StateChangeDistributor& stateChangeDistributor_,
1095 : RecordedCommand(commandController_, stateChangeDistributor_,
1096 scheduler_,
"keymatrixdown")
1101 TclObject& , EmuTime::param )
1103 checkNumArgs(tokens, 3, Prefix{1},
"row mask");
1105 return keyboard.processCmd(getInterpreter(), tokens,
false);
1108 string Keyboard::KeyMatrixDownCmd::help(
const vector<string>& )
const
1110 return "keymatrixdown <row> <bitmask> press a key in the keyboardmatrix\n";
1116 Keyboard::MsxKeyEventQueue::MsxKeyEventQueue(
1117 Scheduler& scheduler_, Interpreter& interp_)
1123 void Keyboard::MsxKeyEventQueue::process_asap(
1124 EmuTime::param time,
const shared_ptr<const Event>& event)
1126 bool processImmediately = eventQueue.empty();
1127 eventQueue.push_back(event);
1128 if (processImmediately) {
1133 void Keyboard::MsxKeyEventQueue::clear()
1139 void Keyboard::MsxKeyEventQueue::executeUntil(EmuTime::param time)
1142 shared_ptr<const Event>
event = eventQueue.front();
1144 bool insertCodeKanaRelease = keyboard.processQueuedEvent(*event, time);
1146 if (insertCodeKanaRelease) {
1150 eventQueue.push_front(make_shared<KeyUpEvent>(
1151 keyboard.keyboardSettings.getCodeKanaHostKey()));
1154 if (!eventQueue.empty()) {
1155 eventQueue.pop_front();
1162 if (!eventQueue.empty()) {
1171 Keyboard::KeyInserter::KeyInserter(
1172 CommandController& commandController_,
1173 StateChangeDistributor& stateChangeDistributor_,
1175 : RecordedCommand(commandController_, stateChangeDistributor_,
1176 scheduler_,
"type_via_keyboard")
1179 , releaseLast(false)
1184 releaseBeforePress =
false;
1185 typingFrequency = 15;
1188 void Keyboard::KeyInserter::execute(
1191 checkNumArgs(tokens, AtLeast{2},
"?-release? ?-freq hz? text");
1193 releaseBeforePress =
false;
1194 typingFrequency = 15;
1197 if (tokens.
size() == 2) {
1198 type(tokens[1].getString());
1203 flagArg(
"-release", releaseBeforePress),
1204 valueArg(
"-freq", typingFrequency),
1208 if (typingFrequency <= 0) {
1209 throw CommandException(
"Wrong argument for -freq (should be a positive number)");
1211 if (arguments.size() != 1)
throw SyntaxError();
1213 type(arguments[0].getString());
1216 string Keyboard::KeyInserter::help(
const vector<string>& )
const
1218 return "Type a string in the emulated MSX.\n" \
1219 "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" \
1220 "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";
1223 void Keyboard::KeyInserter::tabCompletion(vector<string>& tokens)
const
1225 using namespace std::literals;
1226 static constexpr std::array options = {
"-release"sv,
"-freq"sv};
1227 completeString(tokens, options);
1230 void Keyboard::KeyInserter::type(std::string_view str)
1236 oldLocksOn = keyboard.locksOn;
1237 if (text_utf8.empty()) {
1238 reschedule(getCurrentTime());
1240 text_utf8.append(str.data(), str.size());
1243 void Keyboard::KeyInserter::executeUntil(EmuTime::param time)
1246 if (lockKeysMask != 0) {
1248 keyboard.pressLockKeys(lockKeysMask,
false);
1251 keyboard.pressAscii(last,
false);
1253 if (text_utf8.empty()) {
1254 releaseLast =
false;
1255 keyboard.debug(
"Restoring locks: %02X -> %02X\n", keyboard.locksOn, oldLocksOn);
1256 auto diff = oldLocksOn ^ keyboard.locksOn;
1257 lockKeysMask = diff;
1260 keyboard.locksOn ^= diff;
1261 keyboard.pressLockKeys(diff,
true);
1268 auto it =
begin(text_utf8);
1270 if (releaseLast && (releaseBeforePress || keyboard.commonKeys(last, current))) {
1274 releaseLast =
false;
1278 lockKeysMask = keyboard.pressAscii(current,
true);
1279 if (lockKeysMask == 0) {
1282 text_utf8.erase(
begin(text_utf8), it);
1283 }
else if (lockKeysMask & TRY_AGAIN) {
1284 lockKeysMask &= ~TRY_AGAIN;
1285 releaseLast =
false;
1286 }
else if (releaseBeforePress) {
1291 }
catch (std::exception&) {
1297 void Keyboard::KeyInserter::reschedule(EmuTime::param time)
1314 Keyboard::CapsLockAligner::CapsLockAligner(
1315 EventDistributor& eventDistributor_,
1318 , eventDistributor(eventDistributor_)
1325 Keyboard::CapsLockAligner::~CapsLockAligner()
1331 int Keyboard::CapsLockAligner::signalEvent(
const shared_ptr<const Event>& event) noexcept
1333 if constexpr (!SANE_CAPSLOCK_BEHAVIOR) {
1338 if (state ==
IDLE) {
1339 EmuTime::param time = getCurrentTime();
1342 alignCapsLock(time);
1344 state = MUST_ALIGN_CAPSLOCK;
1353 void Keyboard::CapsLockAligner::executeUntil(EmuTime::param time)
1356 case MUST_ALIGN_CAPSLOCK:
1357 alignCapsLock(time);
1359 case MUST_DISTRIBUTE_KEY_RELEASE: {
1362 keyboard.msxEventDistributor.distributeEvent(event, time);
1381 void Keyboard::CapsLockAligner::alignCapsLock(EmuTime::param time)
1383 bool hostCapsLockOn = ((SDL_GetModState() & KMOD_CAPS) != 0);
1386 keyboard.debug(
"Resyncing host and MSX CAPS lock\n");
1390 keyboard.msxEventDistributor.distributeEvent(event, time);
1391 keyboard.debug(
"Sending fake CAPS release\n");
1392 state = MUST_DISTRIBUTE_KEY_RELEASE;
1402 Keyboard::KeybDebuggable::KeybDebuggable(MSXMotherBoard& motherBoard_)
1403 : SimpleDebuggable(motherBoard_,
"keymatrix",
"MSX Keyboard Matrix",
1404 KeyMatrixPosition::NUM_ROWS)
1408 byte Keyboard::KeybDebuggable::read(
unsigned address)
1411 return keyboard.getKeys()[address];
1414 void Keyboard::KeybDebuggable::write(
unsigned ,
byte )
1420 template<
typename Archive>
1423 ar.template serializeBase<Schedulable>(*
this);
1424 ar.serialize(
"text", text_utf8,
1426 "lockKeysMask", lockKeysMask,
1427 "releaseLast", releaseLast);
1429 bool oldCodeKanaLockOn, oldGraphLockOn, oldCapsLockOn;
1430 if constexpr (!Archive::IS_LOADER) {
1435 ar.serialize(
"oldCodeKanaLockOn", oldCodeKanaLockOn,
1436 "oldGraphLockOn", oldGraphLockOn,
1437 "oldCapsLockOn", oldCapsLockOn);
1438 if constexpr (Archive::IS_LOADER) {
1458 template<
typename Archive>
1461 ar.serialize(
"keyTypeCmd", keyTypeCmd,
1462 "cmdKeyMatrix", cmdKeyMatrix);
1463 if (ar.versionAtLeast(version, 3)) {
1464 ar.serialize(
"typeKeyMatrix", typeKeyMatrix);
1469 bool msxCapsLockOn, msxCodeKanaLockOn, msxGraphLockOn;
1470 if constexpr (!Archive::IS_LOADER) {
1475 ar.serialize(
"msxCapsLockOn", msxCapsLockOn,
1476 "msxCodeKanaLockOn", msxCodeKanaLockOn,
1477 "msxGraphLockOn", msxGraphLockOn);
1478 if constexpr (Archive::IS_LOADER) {
1484 if (ar.versionAtLeast(version, 2)) {
1485 ar.serialize(
"userKeyMatrix", userKeyMatrix,
1486 "dynKeymap", dynKeymap,
1487 "msxmodifiers", msxModifiers,
1488 "msxKeyEventQueue", msxKeyEventQueue);
1492 if constexpr (Archive::IS_LOADER) {
1499 template<
typename Archive>
1502 ar.template serializeBase<Schedulable>(*
this);
1511 vector<string> eventStrs;
1512 if constexpr (!Archive::IS_LOADER) {
1514 eventQueue, [](
auto& e) {
return e->toString(); }));
1516 ar.serialize(
"eventQueue", eventStrs);
1517 if constexpr (Archive::IS_LOADER) {
1518 assert(eventQueue.empty());
1519 for (
auto& s : eventStrs) {
1520 eventQueue.push_back(
static constexpr EmuDuration sec(unsigned x)
static constexpr EmuDuration hz(unsigned x)
A position (row, column) in a keyboard matrix.
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_, byte row_, byte press_, byte release_)
void serialize(Archive &ar, unsigned)
bool getAlwaysEnableKeypad() const
bool getTraceKeyPresses() const
MappingMode getMappingMode() const
KpEnterMode getKpEnterMode() const
Keys::KeyCode getDeadkeyHostKey(unsigned n) const
bool getAutoToggleCodeKanaLock() const
Keys::KeyCode getCodeKanaHostKey() const
static constexpr int MAX_KEYSYM
void transferHostKeyMatrix(const Keyboard &source)
void serialize(Archive &ar, unsigned version)
const byte * getKeys() const
Returns a pointer to the current KeyBoard matrix.
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(const EventPtr &event)
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.
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.
KeyInfo get(unsigned unicode) const
KeyInfo getDeadkey(unsigned n) const
constexpr subspan_return_t< Offset, Count > subspan() const
constexpr index_type size() const noexcept
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.
string getName(KeyCode keyCode)
Translate key code to key name.
This file implemented 3 utility functions:
constexpr std::array< KeyMatrixPosition, UnicodeKeymap::KeyInfo::NUM_MODIFIERS > modifierPosForMatrix[]
std::vector< TclObject > parseTclArgs(Interpreter &interp, span< const TclObject > inArgs, span< const ArgsInfo > table)
ArgsInfo valueArg(std::string_view name, T &value)
constexpr KeyMatrixPosition x
Keyboard bindings.
void serialize(Archive &ar, T &t, unsigned version)
constexpr const char *const defaultKeymapForMatrix[]
constexpr nibble mask[4][13]
REGISTER_POLYMORPHIC_CLASS(DiskContainer, NowindRomDisk, "NowindRomDisk")
UnicodeKeymap::KeyInfo KeyInfo
ArgsInfo flagArg(std::string_view name, bool &flag)
void fill(ForwardRange &&range, const T &value)
auto copy(InputRange &&range, OutputIter out)
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 byte CAPS_MASK
static constexpr byte GRAPH_MASK
static constexpr byte CODE_MASK
static constexpr byte SHIFT_MASK
constexpr auto xrange(T e)
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)