53static constexpr bool SANE_CAPSLOCK_BEHAVIOR =
false;
58static constexpr bool SANE_CAPSLOCK_BEHAVIOR =
true;
62static constexpr uint8_t TRY_AGAIN = 0x80;
70 KeyMatrixState(EmuTime::param time_, uint8_t row_, uint8_t press_, uint8_t release_)
72 , row(row_), press(press_), release(release_)
75 assert((press != 0) || (release != 0));
79 assert((press & release) == 0);
81 [[nodiscard]] uint8_t
getRow()
const {
return row; }
82 [[nodiscard]] uint8_t
getPress()
const {
return press; }
83 [[nodiscard]] uint8_t
getRelease()
const {
return release; }
85 template<
typename Archive>
void serialize(Archive& ar,
unsigned )
87 ar.template serializeBase<StateChange>(*
this);
88 ar.serialize(
"row", row,
93 uint8_t row, press, release;
140template<
typename Proj>
141static constexpr size_t count(std::span<const MsxKeyScanMapping> mapping, Proj proj)
143 using Array = std::remove_cvref_t<
decltype(std::invoke(proj, mapping[0]))>;
144 using Code =
typename Array::value_type;
147 for (
const auto& m : mapping) {
148 for (
const auto& c : std::invoke(proj, m)) {
149 if (c != Code{}) ++result;
155template<
typename GetMapping>
156static constexpr auto extractKeyCodeMapping(GetMapping getMapping)
158 constexpr auto mapping = getMapping();
160 std::array<KeyCodeMsxMapping, N> result;
162 for (
const auto& m : mapping) {
163 for (
const auto& k : m.hostKeyCodes) {
164 if (k != SDLK_UNKNOWN) {
165 result[i++] = {k, m.msx};
173template<
typename GetMapping>
174static constexpr auto extractScanCodeMapping(GetMapping getMapping)
176 constexpr auto mapping = getMapping();
178 std::array<ScanCodeMsxMapping, N> result;
180 for (
const auto& m : mapping) {
181 for (
const auto& k : m.hostScanCodes) {
182 if (k != SDL_SCANCODE_UNKNOWN) {
183 result[i++] = {k, m.msx};
192static constexpr auto getMSXMapping()
211 using M = MsxKeyScanMapping;
212 using P = KeyMatrixPosition;
213 using K = std::array<SDL_Keycode, 3>;
214 using S = std::array<SDL_Scancode, 3>;
215 std::array mapping = {
216 M{P{0x00}, K{SDLK_0},
S{SDL_SCANCODE_0}},
217 M{P{0x01}, K{SDLK_1},
S{SDL_SCANCODE_1}},
218 M{P{0x02}, K{SDLK_2},
S{SDL_SCANCODE_2}},
219 M{P{0x03}, K{SDLK_3},
S{SDL_SCANCODE_3}},
220 M{P{0x04}, K{SDLK_4},
S{SDL_SCANCODE_4}},
221 M{P{0x05}, K{SDLK_5},
S{SDL_SCANCODE_5}},
222 M{P{0x06}, K{SDLK_6},
S{SDL_SCANCODE_6}},
223 M{P{0x07}, K{SDLK_7},
S{SDL_SCANCODE_7}},
225 M{P{0x10}, K{SDLK_8},
S{SDL_SCANCODE_8}},
226 M{P{0x11}, K{SDLK_9},
S{SDL_SCANCODE_9}},
227 M{P{0x12}, K{SDLK_MINUS},
S{SDL_SCANCODE_MINUS}},
228 M{P{0x13}, K{SDLK_EQUALS},
S{SDL_SCANCODE_EQUALS}},
229 M{P{0x14}, K{SDLK_BACKSLASH},
S{SDL_SCANCODE_BACKSLASH}},
230 M{P{0x15}, K{SDLK_LEFTBRACKET},
S{SDL_SCANCODE_LEFTBRACKET}},
231 M{P{0x16}, K{SDLK_RIGHTBRACKET},
S{SDL_SCANCODE_RIGHTBRACKET}},
232 M{P{0x17}, K{SDLK_SEMICOLON},
S{SDL_SCANCODE_SEMICOLON}},
234 M{P{0x20}, K{SDLK_QUOTE},
S{SDL_SCANCODE_APOSTROPHE}},
235 M{P{0x21}, K{SDLK_BACKQUOTE},
S{SDL_SCANCODE_GRAVE}},
236 M{P{0x22}, K{SDLK_COMMA},
S{SDL_SCANCODE_COMMA}},
237 M{P{0x23}, K{SDLK_PERIOD},
S{SDL_SCANCODE_PERIOD}},
238 M{P{0x24}, K{SDLK_SLASH},
S{SDL_SCANCODE_SLASH}},
239 M{P{0x25}, K{SDLK_RCTRL},
S{SDL_SCANCODE_RCTRL}},
240 M{P{0x26}, K{SDLK_a},
S{SDL_SCANCODE_A}},
241 M{P{0x27}, K{SDLK_b},
S{SDL_SCANCODE_B}},
243 M{P{0x30}, K{SDLK_c},
S{SDL_SCANCODE_C}},
244 M{P{0x31}, K{SDLK_d},
S{SDL_SCANCODE_D}},
245 M{P{0x32}, K{SDLK_e},
S{SDL_SCANCODE_E}},
246 M{P{0x33}, K{SDLK_f},
S{SDL_SCANCODE_F}},
247 M{P{0x34}, K{SDLK_g},
S{SDL_SCANCODE_G}},
248 M{P{0x35}, K{SDLK_h},
S{SDL_SCANCODE_H}},
249 M{P{0x36}, K{SDLK_i},
S{SDL_SCANCODE_I}},
250 M{P{0x37}, K{SDLK_j},
S{SDL_SCANCODE_J}},
252 M{P{0x40}, K{SDLK_k},
S{SDL_SCANCODE_K}},
253 M{P{0x41}, K{SDLK_l},
S{SDL_SCANCODE_L}},
254 M{P{0x42}, K{SDLK_m},
S{SDL_SCANCODE_M}},
255 M{P{0x43}, K{SDLK_n},
S{SDL_SCANCODE_N}},
256 M{P{0x44}, K{SDLK_o},
S{SDL_SCANCODE_O}},
257 M{P{0x45}, K{SDLK_p},
S{SDL_SCANCODE_P}},
258 M{P{0x46}, K{SDLK_q},
S{SDL_SCANCODE_Q}},
259 M{P{0x47}, K{SDLK_r},
S{SDL_SCANCODE_R}},
261 M{P{0x50}, K{SDLK_s},
S{SDL_SCANCODE_S}},
262 M{P{0x51}, K{SDLK_t},
S{SDL_SCANCODE_T}},
263 M{P{0x52}, K{SDLK_u},
S{SDL_SCANCODE_U}},
264 M{P{0x53}, K{SDLK_v},
S{SDL_SCANCODE_V}},
265 M{P{0x54}, K{SDLK_w},
S{SDL_SCANCODE_W}},
266 M{P{0x55}, K{SDLK_x},
S{SDL_SCANCODE_X}},
267 M{P{0x56}, K{SDLK_y},
S{SDL_SCANCODE_Y}},
268 M{P{0x57}, K{SDLK_z},
S{SDL_SCANCODE_Z}},
270 M{P{0x60}, K{SDLK_LSHIFT, SDLK_RSHIFT},
S{SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT}},
271 M{P{0x61}, K{SDLK_LCTRL},
S{SDL_SCANCODE_LCTRL}},
272 M{P{0x62}, K{SDLK_LALT},
S{SDL_SCANCODE_LALT}},
273 M{P{0x63}, K{SDLK_CAPSLOCK},
S{SDL_SCANCODE_CAPSLOCK}},
274 M{P{0x64}, K{SDLK_RALT},
S{SDL_SCANCODE_RALT}},
275 M{P{0x65}, K{SDLK_F1},
S{SDL_SCANCODE_F1}},
276 M{P{0x66}, K{SDLK_F2},
S{SDL_SCANCODE_F2}},
277 M{P{0x67}, K{SDLK_F3},
S{SDL_SCANCODE_F3}},
279 M{P{0x70}, K{SDLK_F4},
S{SDL_SCANCODE_F4}},
280 M{P{0x71}, K{SDLK_F5},
S{SDL_SCANCODE_F5}},
281 M{P{0x72}, K{SDLK_ESCAPE},
S{SDL_SCANCODE_ESCAPE}},
282 M{P{0x73}, K{SDLK_TAB},
S{SDL_SCANCODE_TAB}},
283 M{P{0x74}, K{SDLK_F8},
S{SDL_SCANCODE_F8}},
284 M{P{0x75}, K{SDLK_BACKSPACE},
S{SDL_SCANCODE_BACKSPACE}},
285 M{P{0x76}, K{SDLK_F7},
S{SDL_SCANCODE_F7}},
286 M{P{0x77}, K{SDLK_RETURN},
S{SDL_SCANCODE_RETURN}},
288 M{P{0x80}, K{SDLK_SPACE},
S{SDL_SCANCODE_SPACE}},
289 M{P{0x81}, K{SDLK_HOME},
S{SDL_SCANCODE_HOME}},
290 M{P{0x82}, K{SDLK_INSERT},
S{SDL_SCANCODE_INSERT}},
291 M{P{0x83}, K{SDLK_DELETE},
S{SDL_SCANCODE_DELETE}},
292 M{P{0x84}, K{SDLK_LEFT},
S{SDL_SCANCODE_LEFT}},
293 M{P{0x85}, K{SDLK_UP},
S{SDL_SCANCODE_UP}},
294 M{P{0x86}, K{SDLK_DOWN},
S{SDL_SCANCODE_DOWN}},
295 M{P{0x87}, K{SDLK_RIGHT},
S{SDL_SCANCODE_RIGHT}},
297 M{P{0x90}, K{SDLK_KP_MULTIPLY},
S{SDL_SCANCODE_KP_MULTIPLY}},
298 M{P{0x91}, K{SDLK_KP_PLUS},
S{SDL_SCANCODE_KP_PLUS}},
299 M{P{0x92}, K{SDLK_KP_DIVIDE},
S{SDL_SCANCODE_KP_DIVIDE}},
300 M{P{0x93}, K{SDLK_KP_0},
S{SDL_SCANCODE_KP_0}},
301 M{P{0x94}, K{SDLK_KP_1},
S{SDL_SCANCODE_KP_1}},
302 M{P{0x95}, K{SDLK_KP_2},
S{SDL_SCANCODE_KP_2}},
303 M{P{0x96}, K{SDLK_KP_3},
S{SDL_SCANCODE_KP_3}},
304 M{P{0x97}, K{SDLK_KP_4},
S{SDL_SCANCODE_KP_4}},
306 M{P{0xA0}, K{SDLK_KP_5},
S{SDL_SCANCODE_KP_5}},
307 M{P{0xA1}, K{SDLK_KP_6},
S{SDL_SCANCODE_KP_6}},
308 M{P{0xA2}, K{SDLK_KP_7},
S{SDL_SCANCODE_KP_7}},
309 M{P{0xA3}, K{SDLK_KP_8},
S{SDL_SCANCODE_KP_8}},
310 M{P{0xA4}, K{SDLK_KP_9},
S{SDL_SCANCODE_KP_9}},
311 M{P{0xA5}, K{SDLK_KP_MINUS},
S{SDL_SCANCODE_KP_MINUS}},
312 M{P{0xA6}, K{SDLK_KP_COMMA},
S{SDL_SCANCODE_KP_COMMA}},
313 M{P{0xA7}, K{SDLK_KP_PERIOD},
S{SDL_SCANCODE_KP_PERIOD}},
315 M{P{0xB1}, K{SDLK_RGUI},
S{SDL_SCANCODE_RGUI}},
316 M{P{0xB3}, K{SDLK_LGUI},
S{SDL_SCANCODE_LGUI}},
321static constexpr auto getSVIMapping()
339 using M = MsxKeyScanMapping;
340 using P = KeyMatrixPosition;
341 using K = std::array<SDL_Keycode, 3>;
342 using S = std::array<SDL_Scancode, 3>;
343 std::array mapping = {
344 M{P{0x00}, K{SDLK_0},
S{SDL_SCANCODE_0}},
345 M{P{0x01}, K{SDLK_1},
S{SDL_SCANCODE_1}},
346 M{P{0x02}, K{SDLK_2},
S{SDL_SCANCODE_2}},
347 M{P{0x03}, K{SDLK_3},
S{SDL_SCANCODE_3}},
348 M{P{0x04}, K{SDLK_4},
S{SDL_SCANCODE_4}},
349 M{P{0x05}, K{SDLK_5},
S{SDL_SCANCODE_5}},
350 M{P{0x06}, K{SDLK_6},
S{SDL_SCANCODE_6}},
351 M{P{0x07}, K{SDLK_7},
S{SDL_SCANCODE_7}},
353 M{P{0x10}, K{SDLK_8},
S{SDL_SCANCODE_8}},
354 M{P{0x11}, K{SDLK_9},
S{SDL_SCANCODE_9}},
355 M{P{0x12}, K{SDLK_SEMICOLON},
S{SDL_SCANCODE_SEMICOLON}},
356 M{P{0x13}, K{SDLK_QUOTE},
S{SDL_SCANCODE_APOSTROPHE}},
357 M{P{0x14}, K{SDLK_COMMA},
S{SDL_SCANCODE_COMMA}},
358 M{P{0x15}, K{SDLK_EQUALS},
S{SDL_SCANCODE_EQUALS}},
359 M{P{0x16}, K{SDLK_PERIOD},
S{SDL_SCANCODE_PERIOD}},
360 M{P{0x17}, K{SDLK_SLASH},
S{SDL_SCANCODE_SLASH}},
362 M{P{0x20}, K{SDLK_MINUS},
S{SDL_SCANCODE_MINUS}},
363 M{P{0x21}, K{SDLK_a},
S{SDL_SCANCODE_A}},
364 M{P{0x22}, K{SDLK_b},
S{SDL_SCANCODE_B}},
365 M{P{0x23}, K{SDLK_c},
S{SDL_SCANCODE_C}},
366 M{P{0x24}, K{SDLK_d},
S{SDL_SCANCODE_D}},
367 M{P{0x25}, K{SDLK_e},
S{SDL_SCANCODE_E}},
368 M{P{0x26}, K{SDLK_f},
S{SDL_SCANCODE_F}},
369 M{P{0x27}, K{SDLK_g},
S{SDL_SCANCODE_G}},
371 M{P{0x30}, K{SDLK_h},
S{SDL_SCANCODE_H}},
372 M{P{0x31}, K{SDLK_i},
S{SDL_SCANCODE_I}},
373 M{P{0x32}, K{SDLK_j},
S{SDL_SCANCODE_J}},
374 M{P{0x33}, K{SDLK_k},
S{SDL_SCANCODE_K}},
375 M{P{0x34}, K{SDLK_l},
S{SDL_SCANCODE_L}},
376 M{P{0x35}, K{SDLK_m},
S{SDL_SCANCODE_M}},
377 M{P{0x36}, K{SDLK_n},
S{SDL_SCANCODE_N}},
378 M{P{0x37}, K{SDLK_o},
S{SDL_SCANCODE_O}},
380 M{P{0x40}, K{SDLK_p},
S{SDL_SCANCODE_P}},
381 M{P{0x41}, K{SDLK_q},
S{SDL_SCANCODE_Q}},
382 M{P{0x42}, K{SDLK_r},
S{SDL_SCANCODE_R}},
383 M{P{0x43}, K{SDLK_s},
S{SDL_SCANCODE_S}},
384 M{P{0x44}, K{SDLK_t},
S{SDL_SCANCODE_T}},
385 M{P{0x45}, K{SDLK_u},
S{SDL_SCANCODE_U}},
386 M{P{0x46}, K{SDLK_v},
S{SDL_SCANCODE_V}},
387 M{P{0x47}, K{SDLK_w},
S{SDL_SCANCODE_W}},
389 M{P{0x50}, K{SDLK_x},
S{SDL_SCANCODE_X}},
390 M{P{0x51}, K{SDLK_y},
S{SDL_SCANCODE_Y}},
391 M{P{0x52}, K{SDLK_z},
S{SDL_SCANCODE_Z}},
392 M{P{0x53}, K{SDLK_LEFTBRACKET},
S{SDL_SCANCODE_LEFTBRACKET}},
393 M{P{0x54}, K{SDLK_BACKSLASH},
S{SDL_SCANCODE_BACKSLASH}},
394 M{P{0x55}, K{SDLK_RIGHTBRACKET},
S{SDL_SCANCODE_RIGHTBRACKET}},
395 M{P{0x56}, K{SDLK_BACKSPACE},
S{SDL_SCANCODE_BACKSPACE}},
396 M{P{0x57}, K{SDLK_UP},
S{SDL_SCANCODE_UP}},
398 M{P{0x60}, K{SDLK_LSHIFT, SDLK_RSHIFT},
S{SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT}},
399 M{P{0x61}, K{SDLK_LCTRL},
S{SDL_SCANCODE_LCTRL}},
400 M{P{0x62}, K{SDLK_LALT},
S{SDL_SCANCODE_LALT}},
401 M{P{0x63}, K{SDLK_RALT},
S{SDL_SCANCODE_RALT}},
402 M{P{0x64}, K{SDLK_ESCAPE},
S{SDL_SCANCODE_ESCAPE}},
403 M{P{0x65}, K{SDLK_F8},
S{SDL_SCANCODE_F8}},
404 M{P{0x66}, K{SDLK_RETURN},
S{SDL_SCANCODE_RETURN}},
405 M{P{0x67}, K{SDLK_LEFT},
S{SDL_SCANCODE_LEFT}},
407 M{P{0x70}, K{SDLK_F1},
S{SDL_SCANCODE_F1}},
408 M{P{0x71}, K{SDLK_F2},
S{SDL_SCANCODE_F2}},
409 M{P{0x72}, K{SDLK_F3},
S{SDL_SCANCODE_F3}},
410 M{P{0x73}, K{SDLK_F4},
S{SDL_SCANCODE_F4}},
411 M{P{0x74}, K{SDLK_F5},
S{SDL_SCANCODE_F5}},
412 M{P{0x75}, K{SDLK_F7},
S{SDL_SCANCODE_F7}},
413 M{P{0x76}, K{SDLK_INSERT},
S{SDL_SCANCODE_INSERT}},
414 M{P{0x77}, K{SDLK_DOWN},
S{SDL_SCANCODE_DOWN}},
416 M{P{0x80}, K{SDLK_SPACE},
S{SDL_SCANCODE_F1}},
417 M{P{0x81}, K{SDLK_TAB},
S{SDL_SCANCODE_F2}},
418 M{P{0x82}, K{SDLK_DELETE},
S{SDL_SCANCODE_F3}},
419 M{P{0x83}, K{SDLK_CAPSLOCK},
S{SDL_SCANCODE_F4}},
420 M{P{0x84}, K{SDLK_F6},
S{SDL_SCANCODE_F6}},
421 M{P{0x85}, K{SDLK_PRINTSCREEN},
S{SDL_SCANCODE_PRINTSCREEN}},
423 M{P{0x87}, K{SDLK_RIGHT},
S{SDL_SCANCODE_RIGHT}},
425 M{P{0x90}, K{SDLK_KP_0},
S{SDL_SCANCODE_KP_0}},
426 M{P{0x91}, K{SDLK_KP_1},
S{SDL_SCANCODE_KP_1}},
427 M{P{0x92}, K{SDLK_KP_2},
S{SDL_SCANCODE_KP_2}},
428 M{P{0x93}, K{SDLK_KP_3},
S{SDL_SCANCODE_KP_3}},
429 M{P{0x94}, K{SDLK_KP_4},
S{SDL_SCANCODE_KP_4}},
430 M{P{0x95}, K{SDLK_KP_5},
S{SDL_SCANCODE_KP_5}},
431 M{P{0x96}, K{SDLK_KP_6},
S{SDL_SCANCODE_KP_6}},
432 M{P{0x97}, K{SDLK_KP_7},
S{SDL_SCANCODE_KP_7}},
434 M{P{0xA0}, K{SDLK_KP_8},
S{SDL_SCANCODE_KP_8}},
435 M{P{0xA1}, K{SDLK_KP_9},
S{SDL_SCANCODE_KP_9}},
436 M{P{0xA2}, K{SDLK_KP_PLUS},
S{SDL_SCANCODE_KP_PLUS}},
437 M{P{0xA3}, K{SDLK_KP_MINUS},
S{SDL_SCANCODE_KP_MINUS}},
438 M{P{0xA4}, K{SDLK_KP_MULTIPLY},
S{SDL_SCANCODE_KP_MULTIPLY}},
439 M{P{0xA5}, K{SDLK_KP_DIVIDE},
S{SDL_SCANCODE_KP_DIVIDE}},
440 M{P{0xA6}, K{SDLK_KP_PERIOD},
S{SDL_SCANCODE_KP_PERIOD}},
441 M{P{0xA7}, K{SDLK_KP_COMMA},
S{SDL_SCANCODE_KP_COMMA}},
446static constexpr auto getCvJoyMapping()
463 using M = MsxKeyScanMapping;
464 using P = KeyMatrixPosition;
465 using K = std::array<SDL_Keycode, 3>;
466 using S = std::array<SDL_Scancode, 3>;
467 std::array mapping = {
468 M{P{0x00}, K{SDLK_UP},
S{SDL_SCANCODE_UP}},
469 M{P{0x01}, K{SDLK_RIGHT},
S{SDL_SCANCODE_RIGHT}},
470 M{P{0x02}, K{SDLK_DOWN},
S{SDL_SCANCODE_DOWN}},
471 M{P{0x03}, K{SDLK_LEFT},
S{SDL_SCANCODE_LEFT}},
472 M{P{0x06}, K{SDLK_SPACE, SDLK_RCTRL},
473 S{SDL_SCANCODE_SPACE, SDL_SCANCODE_RCTRL}},
474 M{P{0x07}, K{SDLK_RSHIFT, SDLK_RALT, SDLK_LALT},
475 S{SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_LALT}},
477 M{P{0x10}, K{SDLK_w},
S{SDL_SCANCODE_W}},
478 M{P{0x11}, K{SDLK_d},
S{SDL_SCANCODE_D}},
479 M{P{0x12}, K{SDLK_s},
S{SDL_SCANCODE_S}},
480 M{P{0x13}, K{SDLK_a},
S{SDL_SCANCODE_A}},
481 M{P{0x16}, K{SDLK_LCTRL},
S{SDL_SCANCODE_LCTRL}},
482 M{P{0x17}, K{SDLK_LSHIFT},
S{SDL_SCANCODE_LSHIFT}},
484 M{P{0x20}, K{SDLK_0, SDLK_KP_0},
S{SDL_SCANCODE_0}},
485 M{P{0x21}, K{SDLK_1, SDLK_KP_1},
S{SDL_SCANCODE_1}},
486 M{P{0x22}, K{SDLK_2, SDLK_KP_2},
S{SDL_SCANCODE_2}},
487 M{P{0x23}, K{SDLK_3, SDLK_KP_3},
S{SDL_SCANCODE_3}},
488 M{P{0x24}, K{SDLK_4, SDLK_KP_4},
S{SDL_SCANCODE_4}},
489 M{P{0x25}, K{SDLK_5, SDLK_KP_5},
S{SDL_SCANCODE_5}},
490 M{P{0x26}, K{SDLK_6, SDLK_KP_6},
S{SDL_SCANCODE_6}},
491 M{P{0x27}, K{SDLK_7, SDLK_KP_7},
S{SDL_SCANCODE_7}},
493 M{P{0x30}, K{SDLK_8, SDLK_KP_8},
S{SDL_SCANCODE_8}},
494 M{P{0x31}, K{SDLK_9, SDLK_KP_9},
S{SDL_SCANCODE_9}},
495 M{P{0x32}, K{SDLK_MINUS, SDLK_KP_MULTIPLY, SDLK_KP_MINUS},
496 S{SDL_SCANCODE_MINUS, SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_KP_MINUS}},
497 M{P{0x33}, K{SDLK_EQUALS, SDLK_KP_DIVIDE, SDLK_KP_PLUS},
498 S{SDL_SCANCODE_EQUALS, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_KP_PLUS}},
500 M{P{0x40}, K{SDLK_u},
S{SDL_SCANCODE_U}},
501 M{P{0x41}, K{SDLK_v},
S{SDL_SCANCODE_V}},
502 M{P{0x42}, K{SDLK_b},
S{SDL_SCANCODE_B}},
503 M{P{0x43}, K{SDLK_n},
S{SDL_SCANCODE_N}},
504 M{P{0x44}, K{SDLK_f},
S{SDL_SCANCODE_F}},
505 M{P{0x45}, K{SDLK_g},
S{SDL_SCANCODE_G}},
506 M{P{0x46}, K{SDLK_h},
S{SDL_SCANCODE_H}},
507 M{P{0x47}, K{SDLK_r},
S{SDL_SCANCODE_R}},
509 M{P{0x50}, K{SDLK_t},
S{SDL_SCANCODE_T}},
510 M{P{0x51}, K{SDLK_y},
S{SDL_SCANCODE_Y}},
511 M{P{0x52}, K{SDLK_j},
S{SDL_SCANCODE_J}},
512 M{P{0x53}, K{SDLK_m},
S{SDL_SCANCODE_M}},
517static constexpr auto getSegaMapping()
542 using M = MsxKeyScanMapping;
543 using P = KeyMatrixPosition;
544 using K = std::array<SDL_Keycode, 3>;
545 using S = std::array<SDL_Scancode, 3>;
546 std::array mapping = {
547 M{P{0x00}, K{SDLK_1, SDLK_KP_1},
548 S{SDL_SCANCODE_1, SDL_SCANCODE_KP_1}},
549 M{P{0x01}, K{SDLK_q},
S{SDL_SCANCODE_Q}},
550 M{P{0x02}, K{SDLK_a},
S{SDL_SCANCODE_A}},
551 M{P{0x03}, K{SDLK_z},
S{SDL_SCANCODE_Z}},
552 M{P{0x04}, K{SDLK_RALT},
S{SDL_SCANCODE_RALT}},
553 M{P{0x05}, K{SDLK_COMMA},
S{SDL_SCANCODE_COMMA}},
554 M{P{0x06}, K{SDLK_k},
S{SDL_SCANCODE_K}},
555 M{P{0x07}, K{SDLK_i},
S{SDL_SCANCODE_I}},
557 M{P{0x10}, K{SDLK_2, SDLK_KP_2},
558 S{SDL_SCANCODE_2, SDL_SCANCODE_KP_2}},
559 M{P{0x11}, K{SDLK_w},
S{SDL_SCANCODE_W}},
560 M{P{0x12}, K{SDLK_s},
S{SDL_SCANCODE_S}},
561 M{P{0x13}, K{SDLK_x},
S{SDL_SCANCODE_X}},
562 M{P{0x14}, K{SDLK_SPACE},
S{SDL_SCANCODE_SPACE}},
563 M{P{0x15}, K{SDLK_PERIOD, SDLK_KP_PERIOD},
564 S{SDL_SCANCODE_PERIOD, SDL_SCANCODE_KP_PERIOD}},
565 M{P{0x16}, K{SDLK_l},
S{SDL_SCANCODE_L}},
566 M{P{0x17}, K{SDLK_o},
S{SDL_SCANCODE_O}},
568 M{P{0x20}, K{SDLK_3, SDLK_KP_3},
569 S{SDL_SCANCODE_3, SDL_SCANCODE_KP_3}},
570 M{P{0x21}, K{SDLK_e},
S{SDL_SCANCODE_E}},
571 M{P{0x22}, K{SDLK_d},
S{SDL_SCANCODE_D}},
572 M{P{0x23}, K{SDLK_c},
S{SDL_SCANCODE_C}},
573 M{P{0x24}, K{SDLK_HOME},
S{SDL_SCANCODE_HOME}},
574 M{P{0x25}, K{SDLK_SLASH, SDLK_KP_DIVIDE},
575 S{SDL_SCANCODE_SLASH, SDL_SCANCODE_KP_DIVIDE}},
576 M{P{0x26}, K{SDLK_SEMICOLON, SDLK_KP_PLUS},
577 S{SDL_SCANCODE_SEMICOLON, SDL_SCANCODE_KP_PLUS}},
578 M{P{0x27}, K{SDLK_p},
S{SDL_SCANCODE_P}},
580 M{P{0x30}, K{SDLK_4, SDLK_KP_4},
581 S{SDL_SCANCODE_4, SDL_SCANCODE_KP_4}},
582 M{P{0x31}, K{SDLK_r},
S{SDL_SCANCODE_R}},
583 M{P{0x32}, K{SDLK_f},
S{SDL_SCANCODE_F}},
584 M{P{0x33}, K{SDLK_v},
S{SDL_SCANCODE_V}},
585 M{P{0x34}, K{SDLK_INSERT},
S{SDL_SCANCODE_INSERT}},
586 M{P{0x35}, K{SDLK_F7},
S{SDL_SCANCODE_F7}},
587 M{P{0x36}, K{SDLK_QUOTE, SDLK_KP_MULTIPLY},
588 S{SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_KP_MULTIPLY}},
589 M{P{0x37}, K{SDLK_BACKQUOTE},
S{SDL_SCANCODE_GRAVE}},
591 M{P{0x40}, K{SDLK_5, SDLK_KP_5},
592 S{SDL_SCANCODE_5, SDL_SCANCODE_KP_5}},
593 M{P{0x41}, K{SDLK_t},
S{SDL_SCANCODE_T}},
594 M{P{0x42}, K{SDLK_g},
S{SDL_SCANCODE_G}},
595 M{P{0x43}, K{SDLK_b},
S{SDL_SCANCODE_B}},
597 M{P{0x45}, K{SDLK_DOWN},
S{SDL_SCANCODE_DOWN}},
598 M{P{0x46}, K{SDLK_RIGHTBRACKET},
S{SDL_SCANCODE_RIGHTBRACKET}},
599 M{P{0x47}, K{SDLK_LEFTBRACKET},
S{SDL_SCANCODE_LEFTBRACKET}},
601 M{P{0x50}, K{SDLK_6, SDLK_KP_6},
602 S{SDL_SCANCODE_6, SDL_SCANCODE_KP_6}},
603 M{P{0x51}, K{SDLK_y},
S{SDL_SCANCODE_Y}},
604 M{P{0x52}, K{SDLK_h},
S{SDL_SCANCODE_H}},
605 M{P{0x53}, K{SDLK_n},
S{SDL_SCANCODE_N}},
607 M{P{0x55}, K{SDLK_LEFT},
S{SDL_SCANCODE_LEFT}},
608 M{P{0x56}, K{SDLK_RETURN, SDLK_KP_ENTER},
609 S{SDL_SCANCODE_RETURN, SDL_SCANCODE_KP_ENTER}},
612 M{P{0x60}, K{SDLK_7, SDLK_KP_7},
613 S{SDL_SCANCODE_7, SDL_SCANCODE_KP_7}},
614 M{P{0x61}, K{SDLK_u},
S{SDL_SCANCODE_U}},
615 M{P{0x62}, K{SDLK_j},
S{SDL_SCANCODE_J}},
616 M{P{0x63}, K{SDLK_m},
S{SDL_SCANCODE_M}},
618 M{P{0x65}, K{SDLK_RIGHT},
S{SDL_SCANCODE_RIGHT}},
619 M{P{0x66}, K{SDLK_UP},
S{SDL_SCANCODE_UP}},
622 M{P{0x70}, K{SDLK_8, SDLK_KP_8},
623 S{SDL_SCANCODE_8, SDL_SCANCODE_KP_8}},
624 M{P{0x80}, K{SDLK_9, SDLK_KP_9},
625 S{SDL_SCANCODE_9, SDL_SCANCODE_KP_9}},
626 M{P{0x90}, K{SDLK_0, SDLK_KP_0},
627 S{SDL_SCANCODE_0, SDL_SCANCODE_KP_0}},
628 M{P{0xA0}, K{SDLK_MINUS, SDLK_KP_MINUS},
629 S{SDL_SCANCODE_MINUS, SDL_SCANCODE_KP_MINUS}},
630 M{P{0xB0}, K{SDLK_EQUALS},
S{SDL_SCANCODE_EQUALS}},
632 M{P{0xC0}, K{SDLK_BACKSLASH},
S{SDL_SCANCODE_BACKSLASH}},
633 M{P{0xC3}, K{SDLK_TAB},
S{SDL_SCANCODE_TAB}},
635 M{P{0xD0}, K{SDLK_ESCAPE, SDLK_F8},
636 S{SDL_SCANCODE_ESCAPE, SDL_SCANCODE_F8}},
637 M{P{0xD1}, K{SDLK_LALT},
S{SDL_SCANCODE_LALT}},
638 M{P{0xD2}, K{SDLK_LCTRL},
S{SDL_SCANCODE_LCTRL}},
639 M{P{0xD3}, K{SDLK_LSHIFT, SDLK_RSHIFT},
640 S{SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT}},
667static constexpr auto msxKeyCodeMapping = extractKeyCodeMapping ([] {
return getMSXMapping(); });
668static constexpr auto msxScanCodeMapping = extractScanCodeMapping([] {
return getMSXMapping(); });
669static constexpr auto sviKeyCodeMapping = extractKeyCodeMapping ([] {
return getSVIMapping(); });
670static constexpr auto sviScanCodeMapping = extractScanCodeMapping([] {
return getSVIMapping(); });
671static constexpr auto cvJoyKeyCodeMapping = extractKeyCodeMapping ([] {
return getCvJoyMapping(); });
672static constexpr auto cvJoyScanCodeMapping = extractScanCodeMapping([] {
return getCvJoyMapping(); });
673static constexpr auto segaKeyCodeMapping = extractKeyCodeMapping ([] {
return getSegaMapping(); });
674static constexpr auto segaScanCodeMapping = extractScanCodeMapping([] {
return getSegaMapping(); });
685 cvJoyScanCodeMapping,
698 , commandController(commandController_)
699 , msxEventDistributor(msxEventDistributor_)
700 , stateChangeDistributor(stateChangeDistributor_)
701 , keyCodeTab (
to_vector(defaultKeyCodeMappings [matrix]))
702 , scanCodeTab(
to_vector(defaultScanCodeMappings[matrix]))
703 , modifierPos(modifierPosForMatrix[matrix])
704 , keyMatrixUpCmd (commandController, stateChangeDistributor, scheduler_)
705 , keyMatrixDownCmd(commandController, stateChangeDistributor, scheduler_)
706 , keyTypeCmd (commandController, stateChangeDistributor, scheduler_)
707 , msxcode2UnicodeCmd(commandController)
708 , unicode2MsxcodeCmd(commandController)
709 , capsLockAligner(eventDistributor, scheduler_)
710 , keyboardSettings(commandController)
711 , msxKeyEventQueue(scheduler_, commandController.getInterpreter())
712 , keybDebuggable(motherBoard)
713 , unicodeKeymap(config.getChildData(
714 "keyboard_type", defaultKeymapForMatrix[matrix]))
715 , hasKeypad(config.getChildDataAsBool(
"has_keypad", true))
716 , blockRow11(matrix ==
Matrix::MSX
717 && !config.getChildDataAsBool(
"has_yesno_keys", false))
718 , keyGhosting(config.getChildDataAsBool(
"key_ghosting", true))
719 , keyGhostingSGCprotected(config.getChildDataAsBool(
720 "key_ghosting_sgc_protected", true))
721 , modifierIsLock(
KeyInfo::CAPS_MASK
722 | (config.getChildDataAsBool(
"code_kana_locks", false) ?
KeyInfo::CODE_MASK : 0)
723 | (config.getChildDataAsBool(
"graph_locks", false) ?
KeyInfo::GRAPH_MASK : 0))
750static constexpr void doKeyGhosting(std::span<uint8_t, KeyMatrixPosition::NUM_ROWS> matrix,
769 bool changedSomething =
false;
771 changedSomething =
false;
776 auto row1 = matrix[i];
778 auto row2 = matrix[j];
779 if ((row1 != row2) && ((row1 | row2) != 0xff)) {
780 auto rowIold = matrix[i];
781 auto rowJold = matrix[j];
784 if (protectRow6 && i == 6) {
785 matrix[i] = row1 & row2;
786 matrix[j] = (row1 | 0x15) & row2;
788 }
else if (protectRow6 && j == 6) {
789 matrix[i] = row1 & (row2 | 0x15);
790 matrix[j] = row1 & row2;
791 row1 &= (row2 | 0x15);
795 uint8_t newRow = row1 & row2;
800 if (rowIold != matrix[i] ||
801 rowJold != matrix[j]) {
802 changedSomething =
true;
807 }
while (changedSomething);
814 std::span matrix = keyTypeCmd.isActive() ? typeKeyMatrix : userKeyMatrix;
816 keyMatrix[row] = cmdKeyMatrix[row] & matrix[row];
819 doKeyGhosting(keyMatrix, keyGhostingSGCprotected);
843 hostKeyMatrix[row] = source.hostKeyMatrix[row];
852void Keyboard::signalMSXEvent(
const Event& event,
853 EmuTime::param time)
noexcept
856 const auto& keyEvent = get_event<KeyEvent>(event);
857 if (keyEvent.getRepeat())
return;
862 msxKeyEventQueue.process_asap(time, event);
866void Keyboard::signalStateChange(
const StateChange& event)
868 const auto* kms =
dynamic_cast<const KeyMatrixState*
>(&event);
871 userKeyMatrix[kms->getRow()] &= uint8_t(~kms->getPress());
872 userKeyMatrix[kms->getRow()] |= kms->getRelease();
876void Keyboard::stopReplay(EmuTime::param time)
noexcept
878 for (
auto [row, hkm] :
enumerate(hostKeyMatrix)) {
879 changeKeyMatrixEvent(time, uint8_t(row), hkm);
882 msxKeyEventQueue.clear();
883 lastUnicodeForKeycode.clear();
886uint8_t Keyboard::needsLockToggle(
const UnicodeKeymap::KeyInfo& keyInfo)
const
888 return modifierIsLock
889 & (locksOn ^ keyInfo.modMask)
893void Keyboard::pressKeyMatrixEvent(EmuTime::param time, KeyMatrixPosition pos)
895 if (!pos.isValid()) {
899 auto row = pos.getRow();
900 auto press = pos.getMask();
901 if (((hostKeyMatrix[row] & press) == 0) &&
902 ((userKeyMatrix[row] & press) == 0)) {
906 changeKeyMatrixEvent(time, row, hostKeyMatrix[row] & ~press);
909void Keyboard::releaseKeyMatrixEvent(EmuTime::param time, KeyMatrixPosition pos)
911 if (!pos.isValid()) {
915 auto row = pos.getRow();
916 auto release = pos.getMask();
917 if (((hostKeyMatrix[row] & release) == release) &&
918 ((userKeyMatrix[row] & release) == release)) {
925 changeKeyMatrixEvent(time, row, hostKeyMatrix[row] | release);
928void Keyboard::changeKeyMatrixEvent(EmuTime::param time, uint8_t row, uint8_t newValue)
932 hostKeyMatrix[row] = newValue;
934 uint8_t diff = userKeyMatrix[row] ^ newValue;
935 if (diff == 0)
return;
936 uint8_t press = userKeyMatrix[row] & diff;
937 uint8_t release = newValue & diff;
939 time, row, press, release);
945bool Keyboard::processQueuedEvent(
const Event& event, EmuTime::param time)
949 const auto& keyEvent = get_event<KeyEvent>(event);
951 auto key = keyEvent.getKey();
960 ad_printf(
"Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
961 keyEvent.getUnicode(),
962 keyEvent.getKeyCode(),
963 key.toString().c_str());
964 debug(
"Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
965 keyEvent.getUnicode(),
966 keyEvent.getKeyCode(),
967 key.toString().c_str());
969 ad_printf(
"Key released, keyCode: 0x%05x, keyName: %s\n",
970 keyEvent.getKeyCode(),
971 key.toString().c_str());
972 debug(
"Key released, keyCode: 0x%05x, keyName: %s\n",
973 keyEvent.getKeyCode(),
974 key.toString().c_str());
979 for (
auto n :
xrange(3)) {
981 UnicodeKeymap::KeyInfo deadKey = unicodeKeymap.
getDeadKey(n);
982 if (deadKey.isValid()) {
983 updateKeyMatrix(time, down, deadKey.pos);
990 if (key.sym.sym == SDLK_CAPSLOCK) {
991 processCapslockEvent(time, down);
994 processCodeKanaChange(time, down);
996 }
else if (key.sym.sym == SDLK_LALT) {
997 processGraphChange(time, down);
999 }
else if (key.sym.sym == SDLK_KP_ENTER) {
1000 processKeypadEnterKey(time, down);
1003 return processKeyEvent(time, down, keyEvent);
1012void Keyboard::processCodeKanaChange(EmuTime::param time,
bool down)
1025void Keyboard::processGraphChange(EmuTime::param time,
bool down)
1038void Keyboard::processCapslockEvent(EmuTime::param time,
bool down)
1040 if (SANE_CAPSLOCK_BEHAVIOR) {
1041 debug(
"Changing CAPS lock state according to SDL request\n");
1047 debug(
"Pressing CAPS lock and scheduling a release\n");
1054void Keyboard::executeUntil(EmuTime::param time)
1056 debug(
"Releasing CAPS lock\n");
1060void Keyboard::processKeypadEnterKey(EmuTime::param time,
bool down)
1069 ? SDLK_KP_ENTER : SDLK_RETURN,
1079void Keyboard::processSdlKey(EmuTime::param time, SDLKey key)
1081 auto process = [&](KeyMatrixPosition pos) {
1082 assert(pos.isValid());
1083 if (pos.getRow() == 11 && blockRow11) {
1087 updateKeyMatrix(time, key.down, pos);
1092 process(mapping->msx);
1096 process(mapping->msx);
1104void Keyboard::updateKeyMatrix(EmuTime::param time,
bool down, KeyMatrixPosition pos)
1106 if (!pos.isValid()) {
1111 pressKeyMatrixEvent(time, pos);
1116 for (
auto [i, mp] :
enumerate(modifierPos)) {
1118 msxModifiers &= uint8_t(~(1 << i));
1122 releaseKeyMatrixEvent(time, pos);
1123 for (
auto [i, mp] :
enumerate(modifierPos)) {
1125 msxModifiers |= 1 << i;
1140bool Keyboard::processKeyEvent(EmuTime::param time,
bool down,
const KeyEvent& keyEvent)
1144 auto keyCode = keyEvent.getKeyCode();
1145 auto key = keyEvent.getKey();
1148 (SDLK_KP_1 <= keyCode && keyCode <= SDLK_KP_0) ||
1149 (keyCode ==
one_of(SDLK_KP_PERIOD, SDLK_KP_DIVIDE, SDLK_KP_MULTIPLY,
1150 SDLK_KP_MINUS, SDLK_KP_PLUS));
1152 if (isOnKeypad && !hasKeypad &&
1158#if defined(__APPLE__)
1160 if ((key.sym.mod & KMOD_GUI) &&
1161 (( positional && (keyEvent.getScanCode() == SDL_SCANCODE_I)) ||
1162 (!positional && (keyCode == SDLK_i)))) {
1164 keyCode = SDLK_INSERT;
1165 key.sym.sym = SDLK_INSERT;
1166 key.sym.scancode = SDL_SCANCODE_INSERT;
1167 key.sym.mod &= ~KMOD_GUI;
1173 UnicodeKeymap::KeyInfo keyInfo;
1186 unicode = keyEvent.getUnicode();
1187 if ((unicode < 0x20) || ((0x7F <= unicode) && (unicode < 0xA0))) {
1197 keyInfo = unicodeKeymap.
get(unicode);
1198 if (!keyInfo.isValid()) {
1212 auto it =
ranges::lower_bound(lastUnicodeForKeycode, keyCode, {}, &std::pair<SDL_Keycode, uint32_t>::first);
1213 if ((it != lastUnicodeForKeycode.end()) && (it->first == keyCode)) {
1216 it->second = unicode;
1219 lastUnicodeForKeycode.emplace(it, keyCode, unicode);
1231 if (!(key.sym.mod & KMOD_MODE)) {
1232 processSdlKey(time, key);
1237 return pressUnicodeByUser(time, keyInfo, unicode,
true);
1241 auto it =
ranges::lower_bound(lastUnicodeForKeycode, keyCode, {}, &std::pair<SDL_Keycode, uint32_t>::first);
1242 unsigned unicode = ((it != lastUnicodeForKeycode.end()) && (it->first == keyCode))
1249 if (!(key.sym.mod & KMOD_MODE)) {
1250 processSdlKey(time, key);
1254 pressUnicodeByUser(time, unicodeKeymap.
get(unicode), unicode,
false);
1260void Keyboard::processCmd(Interpreter& interp, std::span<const TclObject> tokens,
bool up)
1262 unsigned row = tokens[1].getInt(interp);
1263 unsigned mask = tokens[2].getInt(interp);
1265 throw CommandException(
"Invalid row");
1268 throw CommandException(
"Invalid mask");
1271 cmdKeyMatrix[row] |= narrow_cast<uint8_t>(mask);
1273 cmdKeyMatrix[row] &= narrow_cast<uint8_t>(~mask);
1316bool Keyboard::pressUnicodeByUser(
1317 EmuTime::param time, UnicodeKeymap::KeyInfo keyInfo,
unsigned unicode,
1320 bool insertCodeKanaRelease =
false;
1329 insertCodeKanaRelease =
true;
1338 pressKeyMatrixEvent(time, keyInfo.pos);
1340 uint8_t modMask = keyInfo.modMask & ~modifierIsLock;
1341 if ((
'A' <= unicode && unicode <=
'Z') || (
'a' <= unicode && unicode <=
'z')) {
1346 modMask &= ~KeyInfo::SHIFT_MASK;
1355 for (
auto [i, mp] :
enumerate(modifierPos)) {
1356 if ((modMask >> i) & 1) {
1357 pressKeyMatrixEvent(time, mp);
1362 releaseKeyMatrixEvent(time, keyInfo.pos);
1365 for (
auto [i, mp] :
enumerate(modifierPos)) {
1366 if (!((modifierIsLock >> i) & 1)) {
1369 if ((msxModifiers >> i) & 1) {
1370 releaseKeyMatrixEvent(time, mp);
1372 pressKeyMatrixEvent(time, mp);
1378 return insertCodeKanaRelease;
1393uint8_t Keyboard::pressAscii(
unsigned unicode,
bool down)
1395 uint8_t releaseMask = 0;
1396 UnicodeKeymap::KeyInfo keyInfo = unicodeKeymap.
get(unicode);
1397 if (!keyInfo.isValid()) {
1400 uint8_t modMask = keyInfo.
modMask & ~modifierIsLock;
1403 uint8_t toggleLocks = needsLockToggle(keyInfo);
1404 for (
auto [i, mp] :
enumerate(modifierPos)) {
1405 if ((toggleLocks >> i) & 1) {
1406 debug(
"Toggling lock %d\n", i);
1408 releaseMask |= 1 << i;
1409 typeKeyMatrix[mp.getRow()] &= uint8_t(~mp.getMask());
1412 if (releaseMask == 0) {
1413 debug(
"Key pasted, unicode: 0x%04x, row: %02d, col: %d, modMask: %02x\n",
1414 unicode, keyInfo.pos.getRow(), keyInfo.pos.getColumn(), modMask);
1443 auto isPressed = [&](
auto& key) {
1444 return (typeKeyMatrix[key.getRow()] & key.getMask()) == 0;
1449 releaseMask = TRY_AGAIN;
1453 for (
auto [i, mp] :
enumerate(modifierPos)) {
1454 if ((modMask >> i) & 1) {
1455 typeKeyMatrix[mp.getRow()] &= uint8_t(~mp.getMask());
1458 if (releaseMask == 0) {
1460 typeKeyMatrix[keyInfo.pos.getRow()] &= uint8_t(~keyInfo.pos.getMask());
1464 typeKeyMatrix[keyInfo.pos.getRow()] |= keyInfo.pos.getMask();
1465 for (
auto [i, mp] :
enumerate(modifierPos)) {
1466 if ((modMask >> i) & 1) {
1467 typeKeyMatrix[mp.getRow()] |= mp.getMask();
1481void Keyboard::pressLockKeys(uint8_t lockKeysMask,
bool down)
1483 for (
auto [i, mp] :
enumerate(modifierPos)) {
1484 if ((lockKeysMask >> i) & 1) {
1487 typeKeyMatrix[mp.getRow()] &= uint8_t(~mp.getMask());
1490 typeKeyMatrix[mp.getRow()] |= mp.getMask();
1504bool Keyboard::commonKeys(
unsigned unicode1,
unsigned unicode2)
const
1507 auto keyPos1 = unicodeKeymap.
get(unicode1).
pos;
1508 auto keyPos2 = unicodeKeymap.
get(unicode2).
pos;
1510 return keyPos1 == keyPos2 && keyPos1.
isValid();
1513void Keyboard::debug(
const char* format, ...)
const
1517 va_start(args, format);
1518 vfprintf(stderr, format, args);
1526Keyboard::KeyMatrixUpCmd::KeyMatrixUpCmd(
1527 CommandController& commandController_,
1528 StateChangeDistributor& stateChangeDistributor_,
1529 Scheduler& scheduler_)
1530 : RecordedCommand(commandController_, stateChangeDistributor_,
1531 scheduler_,
"keymatrixup")
1535void Keyboard::KeyMatrixUpCmd::execute(
1536 std::span<const TclObject> tokens, TclObject& , EmuTime::param )
1538 checkNumArgs(tokens, 3, Prefix{1},
"row mask");
1539 auto& keyboard =
OUTER(Keyboard, keyMatrixUpCmd);
1540 return keyboard.processCmd(getInterpreter(), tokens,
true);
1543std::string Keyboard::KeyMatrixUpCmd::help(std::span<const TclObject> )
const
1545 return "keymatrixup <row> <bitmask> release a key in the keyboard matrix\n";
1551Keyboard::KeyMatrixDownCmd::KeyMatrixDownCmd(CommandController& commandController_,
1552 StateChangeDistributor& stateChangeDistributor_,
1553 Scheduler& scheduler_)
1554 : RecordedCommand(commandController_, stateChangeDistributor_,
1555 scheduler_,
"keymatrixdown")
1559void Keyboard::KeyMatrixDownCmd::execute(std::span<const TclObject> tokens,
1560 TclObject& , EmuTime::param )
1562 checkNumArgs(tokens, 3, Prefix{1},
"row mask");
1563 auto& keyboard =
OUTER(Keyboard, keyMatrixDownCmd);
1564 return keyboard.processCmd(getInterpreter(), tokens,
false);
1567std::string Keyboard::KeyMatrixDownCmd::help(std::span<const TclObject> )
const
1569 return "keymatrixdown <row> <bitmask> press a key in the keyboard matrix\n";
1575Keyboard::MsxKeyEventQueue::MsxKeyEventQueue(
1576 Scheduler& scheduler_, Interpreter& interp_)
1582void Keyboard::MsxKeyEventQueue::process_asap(
1583 EmuTime::param time,
const Event& event)
1585 bool processImmediately = eventQueue.empty();
1586 eventQueue.push_back(event);
1587 if (processImmediately) {
1592void Keyboard::MsxKeyEventQueue::clear()
1598void Keyboard::MsxKeyEventQueue::executeUntil(EmuTime::param time)
1601 const Event&
event = eventQueue.front();
1602 auto& keyboard =
OUTER(Keyboard, msxKeyEventQueue);
1603 bool insertCodeKanaRelease = keyboard.processQueuedEvent(event, time);
1605 if (insertCodeKanaRelease) {
1609 eventQueue.emplace_front(
KeyUpEvent::create(keyboard.keyboardSettings.getCodeKanaHostKey()));
1612 if (!eventQueue.empty()) {
1613 eventQueue.pop_front();
1620 if (!eventQueue.empty()) {
1629Keyboard::KeyInserter::KeyInserter(
1630 CommandController& commandController_,
1631 StateChangeDistributor& stateChangeDistributor_,
1632 Scheduler& scheduler_)
1633 : RecordedCommand(commandController_, stateChangeDistributor_,
1634 scheduler_,
"type_via_keyboard")
1639void Keyboard::KeyInserter::execute(
1640 std::span<const TclObject> tokens, TclObject& , EmuTime::param )
1642 checkNumArgs(tokens, AtLeast{2},
"?-release? ?-freq hz? ?-cancel? text");
1644 bool cancel =
false;
1645 releaseBeforePress =
false;
1646 typingFrequency = 15;
1649 flagArg(
"-release", releaseBeforePress),
1650 valueArg(
"-freq", typingFrequency),
1652 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan(1), info);
1654 if (typingFrequency <= 0) {
1655 throw CommandException(
"Wrong argument for -freq (should be a positive number)");
1662 if (arguments.size() != 1)
throw SyntaxError();
1664 type(arguments[0].getString());
1667std::string Keyboard::KeyInserter::help(std::span<const TclObject> )
const
1669 return "Type a string in the emulated MSX.\n"
1670 "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"
1671 "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"
1672 "Use -cancel to cancel a (long) in-progress type command.";
1675void Keyboard::KeyInserter::tabCompletion(std::vector<std::string>& tokens)
const
1677 using namespace std::literals;
1678 static constexpr std::array options = {
"-release"sv,
"-freq"sv};
1679 completeString(tokens, options);
1682void Keyboard::KeyInserter::type(std::string_view str)
1687 const auto& keyboard =
OUTER(Keyboard, keyTypeCmd);
1688 oldLocksOn = keyboard.locksOn;
1689 if (text_utf8.empty()) {
1690 reschedule(getCurrentTime());
1692 text_utf8.append(str.data(), str.size());
1695void Keyboard::KeyInserter::executeUntil(EmuTime::param time)
1697 auto& keyboard =
OUTER(Keyboard, keyTypeCmd);
1698 if (lockKeysMask != 0) {
1700 keyboard.pressLockKeys(lockKeysMask,
false);
1703 keyboard.pressAscii(last,
false);
1705 if (text_utf8.empty()) {
1706 releaseLast =
false;
1707 keyboard.debug(
"Restoring locks: %02X -> %02X\n", keyboard.locksOn, oldLocksOn);
1708 uint8_t diff = oldLocksOn ^ keyboard.locksOn;
1709 lockKeysMask = diff;
1712 keyboard.locksOn ^= diff;
1713 keyboard.pressLockKeys(diff,
true);
1720 auto it =
begin(text_utf8);
1722 if (releaseLast && (releaseBeforePress || keyboard.commonKeys(last, current))) {
1726 releaseLast =
false;
1730 lockKeysMask = keyboard.pressAscii(current,
true);
1731 if (lockKeysMask == 0) {
1734 text_utf8.erase(
begin(text_utf8), it);
1735 }
else if (lockKeysMask & TRY_AGAIN) {
1736 lockKeysMask &= ~TRY_AGAIN;
1737 releaseLast =
false;
1738 }
else if (releaseBeforePress) {
1743 }
catch (std::exception&) {
1749void Keyboard::KeyInserter::reschedule(EmuTime::param time)
1757Keyboard::Msxcode2UnicodeCmd::Msxcode2UnicodeCmd(CommandController& commandController_)
1758 : Command(commandController_,
"msxcode2unicode")
1762void Keyboard::Msxcode2UnicodeCmd::execute(std::span<const TclObject> tokens, TclObject& result)
1764 checkNumArgs(tokens, Between{2, 3},
"msx-string ?fallback?");
1766 auto& interp = getInterpreter();
1767 const auto& keyboard =
OUTER(Keyboard, msxcode2UnicodeCmd);
1768 const auto& msxChars = keyboard.unicodeKeymap.getMsxChars();
1770 auto msx = tokens[1].getBinary();
1771 auto fallback = [&]() -> std::function<uint32_t(uint8_t)> {
1772 if (tokens.size() < 3) {
1774 return [](uint8_t) {
return uint32_t(
' '); };
1775 }
else if (
auto i = tokens[2].getOptionalInt()) {
1777 return [i = *i](uint8_t) {
return uint32_t(i); };
1782 return [&](uint8_t m) {
1783 TclObject cmd{TclObject::MakeListTag{}, tokens[2], m};
1784 return uint32_t(cmd.executeCommand(interp).getInt(interp));
1789 result = msxChars.msxToUtf8(msx, fallback);
1792std::string Keyboard::Msxcode2UnicodeCmd::help(std::span<const TclObject> )
const
1794 return "msxcode2unicode <msx-string> [<fallback>]\n"
1795 "returns a unicode string converted from an MSX-string, i.e. a string based on\n"
1796 "MSX character codes.\n"
1797 "The optional fallback used for each character that cannot be mapped for the\n"
1798 "current MSX model can be:\n"
1799 "- omitted: then space will be used as fallback character.\n"
1800 "- an integer number: then this number will be used as unicode point to be the\n"
1801 " the fallback character.\n"
1802 "- a Tcl proc, which expects one input character and must return one unicode\n"
1807Keyboard::Unicode2MsxcodeCmd::Unicode2MsxcodeCmd(CommandController& commandController_)
1808 : Command(commandController_,
"unicode2msxcode")
1812void Keyboard::Unicode2MsxcodeCmd::execute(std::span<const TclObject> tokens, TclObject& result)
1814 checkNumArgs(tokens, Between{2, 3},
"unicode-string ?fallback?");
1816 auto& interp = getInterpreter();
1817 const auto& keyboard =
OUTER(Keyboard, unicode2MsxcodeCmd);
1818 const auto& msxChars = keyboard.unicodeKeymap.getMsxChars();
1820 const auto& unicode = tokens[1].getString();
1821 auto fallback = [&]() -> std::function<uint8_t(uint32_t)> {
1822 if (tokens.size() < 3) {
1824 return [](uint32_t) {
return uint8_t(
' '); };
1825 }
else if (
auto i = tokens[2].getOptionalInt()) {
1827 return [i = *i](uint32_t) {
return uint8_t(i); };
1832 return [&](uint32_t u) {
1833 TclObject cmd{TclObject::MakeListTag{}, tokens[2], u};
1834 return uint8_t(cmd.executeCommand(interp).getInt(interp));
1839 result = msxChars.utf8ToMsx(unicode, fallback);
1842std::string Keyboard::Unicode2MsxcodeCmd::help(std::span<const TclObject> )
const
1844 return "unicode2msxcode <unicode-string> [<fallback>]\n"
1845 "returns an MSX string, i.e. a string based on MSX character codes, converted\n"
1846 "from a unicode string.\n"
1847 "The optional fallback used for each character that cannot be mapped for the\n"
1848 "current MSX model can be:\n"
1849 "- omitted: then space will be used as fallback character.\n"
1850 "- an integer number: then this number will be used as MSX character number to\n"
1851 " to be the fallback character.\n"
1852 "- a Tcl proc, which expects one input character and must return one MSX\n"
1853 " character number.";
1869Keyboard::CapsLockAligner::CapsLockAligner(
1870 EventDistributor& eventDistributor_,
1871 Scheduler& scheduler_)
1873 , eventDistributor(eventDistributor_)
1876 eventDistributor.registerEventListener(type, *
this);
1880Keyboard::CapsLockAligner::~CapsLockAligner()
1883 eventDistributor.unregisterEventListener(type, *
this);
1887bool Keyboard::CapsLockAligner::signalEvent(
const Event& event)
1889 if constexpr (!SANE_CAPSLOCK_BEHAVIOR) {
1894 if (state == IDLE) {
1895 EmuTime::param time = getCurrentTime();
1897 [&](
const WindowEvent&
e) {
1898 if (
e.isMainWindow()) {
1899 const auto& evt =
e.getSdlWindowEvent();
1900 if (evt.event ==
one_of(SDL_WINDOWEVENT_FOCUS_GAINED, SDL_WINDOWEVENT_FOCUS_LOST)) {
1901 alignCapsLock(time);
1905 [&](
const BootEvent&) {
1906 state = MUST_ALIGN_CAPSLOCK;
1915void Keyboard::CapsLockAligner::executeUntil(EmuTime::param time)
1918 case MUST_ALIGN_CAPSLOCK:
1919 alignCapsLock(time);
1921 case MUST_DISTRIBUTE_KEY_RELEASE: {
1922 auto& keyboard =
OUTER(Keyboard, capsLockAligner);
1924 keyboard.msxEventDistributor.distributeEvent(event, time);
1943void Keyboard::CapsLockAligner::alignCapsLock(EmuTime::param time)
1945 bool hostCapsLockOn = ((SDL_GetModState() & KMOD_CAPS) != 0);
1946 auto& keyboard =
OUTER(Keyboard, capsLockAligner);
1948 keyboard.debug(
"Resyncing host and MSX CAPS lock\n");
1952 keyboard.msxEventDistributor.distributeEvent(event, time);
1953 keyboard.debug(
"Sending fake CAPS release\n");
1954 state = MUST_DISTRIBUTE_KEY_RELEASE;
1964Keyboard::KeybDebuggable::KeybDebuggable(MSXMotherBoard& motherBoard_)
1965 : SimpleDebuggable(motherBoard_,
"keymatrix",
"MSX Keyboard Matrix",
1966 KeyMatrixPosition::NUM_ROWS)
1970uint8_t Keyboard::KeybDebuggable::read(
unsigned address)
1972 const auto& keyboard =
OUTER(Keyboard, keybDebuggable);
1973 return keyboard.getKeys()[address];
1976void Keyboard::KeybDebuggable::write(
unsigned , uint8_t )
1982template<
typename Archive>
1983void Keyboard::KeyInserter::serialize(Archive& ar,
unsigned )
1985 ar.template serializeBase<Schedulable>(*
this);
1986 ar.serialize(
"text", text_utf8,
1988 "lockKeysMask", lockKeysMask,
1989 "releaseLast", releaseLast);
1991 bool oldCodeKanaLockOn, oldGraphLockOn, oldCapsLockOn;
1992 if constexpr (!Archive::IS_LOADER) {
1997 ar.serialize(
"oldCodeKanaLockOn", oldCodeKanaLockOn,
1998 "oldGraphLockOn", oldGraphLockOn,
1999 "oldCapsLockOn", oldCapsLockOn);
2000 if constexpr (Archive::IS_LOADER) {
2003 | (oldCapsLockOn ?
KeyInfo::CAPS_MASK : 0);
2021template<
typename Archive>
2024 ar.serialize(
"keyTypeCmd", keyTypeCmd,
2025 "cmdKeyMatrix", cmdKeyMatrix);
2026 if (ar.versionAtLeast(version, 3)) {
2027 ar.serialize(
"typeKeyMatrix", typeKeyMatrix);
2029 typeKeyMatrix = cmdKeyMatrix;
2032 bool msxCapsLockOn, msxCodeKanaLockOn, msxGraphLockOn;
2033 if constexpr (!Archive::IS_LOADER) {
2038 ar.serialize(
"msxCapsLockOn", msxCapsLockOn,
2039 "msxCodeKanaLockOn", msxCodeKanaLockOn,
2040 "msxGraphLockOn", msxGraphLockOn);
2041 if constexpr (Archive::IS_LOADER) {
2047 if (ar.versionAtLeast(version, 2)) {
2048 ar.serialize(
"userKeyMatrix", userKeyMatrix,
2049 "msxmodifiers", msxModifiers,
2050 "msxKeyEventQueue", msxKeyEventQueue);
2052 if (ar.versionAtLeast(version, 4)) {
2053 ar.serialize(
"lastUnicodeForKeycode", lastUnicodeForKeycode);
2066 if constexpr (Archive::IS_LOADER) {
2073template<
typename Archive>
2074void Keyboard::MsxKeyEventQueue::serialize(Archive& ar,
unsigned )
2076 ar.template serializeBase<Schedulable>(*
this);
2085 std::vector<std::string> eventStrs;
2086 if constexpr (!Archive::IS_LOADER) {
2088 eventQueue, [](
const auto& e) {
return toString(e); }));
2090 ar.serialize(
"eventQueue", eventStrs);
2091 if constexpr (Archive::IS_LOADER) {
2092 assert(eventQueue.empty());
2093 for (
const auto& s : eventStrs) {
2094 eventQueue.push_back(
static constexpr EmuDuration sec(unsigned x)
static constexpr EmuDuration hz(unsigned x)
static KeyDownEvent create(SDL_Keycode code, SDL_Keymod mod=KMOD_NONE)
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_, uint8_t row_, uint8_t press_, uint8_t release_)
void serialize(Archive &ar, unsigned)
uint8_t getRelease() const
static KeyUpEvent create(SDL_Keycode code, SDL_Keymod mod=KMOD_NONE)
bool getAlwaysEnableKeypad() const
bool getTraceKeyPresses() const
MappingMode getMappingMode() const
KpEnterMode getKpEnterMode() const
bool getAutoToggleCodeKanaLock() const
SDL_Keycode getDeadKeyHostKey(unsigned n) const
SDL_Keycode getCodeKanaHostKey() const
Keyboard(MSXMotherBoard &motherBoard, Scheduler &scheduler, CommandController &commandController, EventDistributor &eventDistributor, MSXEventDistributor &msxEventDistributor, StateChangeDistributor &stateChangeDistributor, Matrix matrix, const DeviceConfig &config)
Constructs a new Keyboard object.
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
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....
This file implemented 3 utility functions:
ArgsInfo valueArg(std::string_view name, T &value)
std::vector< TclObject > parseTclArgs(Interpreter &interp, std::span< const TclObject > inArgs, std::span< const ArgsInfo > table)
EventType getType(const Event &event)
std::string toString(const BooleanInput &input)
std::variant< KeyUpEvent, KeyDownEvent, MouseMotionEvent, MouseButtonUpEvent, MouseButtonDownEvent, MouseWheelEvent, JoystickAxisMotionEvent, JoystickHatEvent, JoystickButtonUpEvent, JoystickButtonDownEvent, OsdControlReleaseEvent, OsdControlPressEvent, WindowEvent, TextEvent, FileDropEvent, QuitEvent, FinishFrameEvent, CliCommandEvent, GroupEvent, BootEvent, FrameDrawnEvent, BreakEvent, SwitchRendererEvent, TakeReverseSnapshotEvent, AfterTimedEvent, MachineLoadedEvent, MachineActivatedEvent, MachineDeactivatedEvent, MidiInReaderEvent, MidiInWindowsEvent, MidiInCoreMidiEvent, MidiInCoreMidiVirtualEvent, MidiInALSAEvent, Rs232TesterEvent, Rs232NetEvent, ImGuiDelayedActionEvent, ImGuiActiveEvent > Event
UnicodeKeymap::KeyInfo KeyInfo
ArgsInfo flagArg(std::string_view name, bool &flag)
constexpr void fill(ForwardRange &&range, const T &value)
constexpr void sort(RandomAccessRange &&range)
auto lower_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
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)
auto * binary_find(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
auto to_vector(Range &&range) -> std::vector< detail::ToVectorType< T, decltype(std::begin(range))> >
std::array< SDL_Scancode, 3 > hostScanCodes
std::array< SDL_Keycode, 3 > hostKeyCodes
static SDLKey create(SDL_Keycode code, bool down, uint16_t mod=0)
SDL_Scancode hostScanCode
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)