49static constexpr bool SANE_CAPSLOCK_BEHAVIOR =
false;
54static constexpr bool SANE_CAPSLOCK_BEHAVIOR =
true;
58static constexpr uint8_t TRY_AGAIN = 0x80;
66 KeyMatrixState(EmuTime::param time_, uint8_t row_, uint8_t press_, uint8_t release_)
68 , row(row_), press(press_), release(release_)
71 assert((press != 0) || (release != 0));
75 assert((press & release) == 0);
77 [[nodiscard]] uint8_t
getRow()
const {
return row; }
78 [[nodiscard]] uint8_t
getPress()
const {
return press; }
79 [[nodiscard]] uint8_t
getRelease()
const {
return release; }
81 template<
typename Archive>
void serialize(Archive& ar,
unsigned )
83 ar.template serializeBase<StateChange>(*
this);
84 ar.serialize(
"row", row,
89 uint8_t row, press, release;
94static constexpr std::array<std::string_view, 4> defaultKeymapForMatrix = {
101static constexpr std::array modifierPosForMatrix = {
103 KeyMatrixPosition(6, 0),
104 KeyMatrixPosition(6, 1),
105 KeyMatrixPosition(6, 2),
106 KeyMatrixPosition(6, 3),
107 KeyMatrixPosition(6, 4),
110 KeyMatrixPosition(6, 0),
111 KeyMatrixPosition(6, 1),
112 KeyMatrixPosition(6, 2),
113 KeyMatrixPosition(8, 3),
114 KeyMatrixPosition(6, 3),
116 std::array<KeyMatrixPosition, UnicodeKeymap::KeyInfo::NUM_MODIFIERS>{
119 KeyMatrixPosition(13, 3),
120 KeyMatrixPosition(13, 2),
121 KeyMatrixPosition(13, 1),
123 KeyMatrixPosition( 0, 4),
135template<
typename Proj>
136static constexpr size_t count(std::span<const MsxKeyScanMapping> mapping, Proj proj)
138 using Array = std::remove_cvref_t<
decltype(std::invoke(proj, mapping[0]))>;
139 using Code =
typename Array::value_type;
142 for (
const auto& m : mapping) {
143 for (
const auto& c : std::invoke(proj, m)) {
144 if (c != Code{}) ++result;
150template<
typename GetMapping>
151static constexpr auto extractKeyCodeMapping(GetMapping getMapping)
153 constexpr auto mapping = getMapping();
155 std::array<KeyCodeMsxMapping, N> result;
157 for (
const auto& m : mapping) {
158 for (
const auto& k : m.hostKeyCodes) {
159 if (k != SDLK_UNKNOWN) {
160 result[i++] = {k, m.msx};
168template<
typename GetMapping>
169static constexpr auto extractScanCodeMapping(GetMapping getMapping)
171 constexpr auto mapping = getMapping();
173 std::array<ScanCodeMsxMapping, N> result;
175 for (
const auto& m : mapping) {
176 for (
const auto& k : m.hostScanCodes) {
177 if (k != SDL_SCANCODE_UNKNOWN) {
178 result[i++] = {k, m.msx};
187static constexpr auto getMSXMapping()
206 using M = MsxKeyScanMapping;
207 using K = std::array<SDL_Keycode, 3>;
208 using S = std::array<SDL_Scancode, 3>;
209 std::array mapping = {
210 M{0x00, K{SDLK_0},
S{SDL_SCANCODE_0}},
211 M{0x01, K{SDLK_1},
S{SDL_SCANCODE_1}},
212 M{0x02, K{SDLK_2},
S{SDL_SCANCODE_2}},
213 M{0x03, K{SDLK_3},
S{SDL_SCANCODE_3}},
214 M{0x04, K{SDLK_4},
S{SDL_SCANCODE_4}},
215 M{0x05, K{SDLK_5},
S{SDL_SCANCODE_5}},
216 M{0x06, K{SDLK_6},
S{SDL_SCANCODE_6}},
217 M{0x07, K{SDLK_7},
S{SDL_SCANCODE_7}},
219 M{0x10, K{SDLK_8},
S{SDL_SCANCODE_8}},
220 M{0x11, K{SDLK_9},
S{SDL_SCANCODE_9}},
221 M{0x12, K{SDLK_MINUS},
S{SDL_SCANCODE_MINUS}},
222 M{0x13, K{SDLK_EQUALS},
S{SDL_SCANCODE_EQUALS}},
223 M{0x14, K{SDLK_BACKSLASH},
S{SDL_SCANCODE_BACKSLASH}},
224 M{0x15, K{SDLK_LEFTBRACKET},
S{SDL_SCANCODE_LEFTBRACKET}},
225 M{0x16, K{SDLK_RIGHTBRACKET},
S{SDL_SCANCODE_RIGHTBRACKET}},
226 M{0x17, K{SDLK_SEMICOLON},
S{SDL_SCANCODE_SEMICOLON}},
228 M{0x20, K{SDLK_QUOTE},
S{SDL_SCANCODE_APOSTROPHE}},
229 M{0x21, K{SDLK_BACKQUOTE},
S{SDL_SCANCODE_GRAVE}},
230 M{0x22, K{SDLK_COMMA},
S{SDL_SCANCODE_COMMA}},
231 M{0x23, K{SDLK_PERIOD},
S{SDL_SCANCODE_PERIOD}},
232 M{0x24, K{SDLK_SLASH},
S{SDL_SCANCODE_SLASH}},
233 M{0x25, K{SDLK_RCTRL},
S{SDL_SCANCODE_RCTRL}},
234 M{0x26, K{SDLK_a},
S{SDL_SCANCODE_A}},
235 M{0x27, K{SDLK_b},
S{SDL_SCANCODE_B}},
237 M{0x30, K{SDLK_c},
S{SDL_SCANCODE_C}},
238 M{0x31, K{SDLK_d},
S{SDL_SCANCODE_D}},
239 M{0x32, K{SDLK_e},
S{SDL_SCANCODE_E}},
240 M{0x33, K{SDLK_f},
S{SDL_SCANCODE_F}},
241 M{0x34, K{SDLK_g},
S{SDL_SCANCODE_G}},
242 M{0x35, K{SDLK_h},
S{SDL_SCANCODE_H}},
243 M{0x36, K{SDLK_i},
S{SDL_SCANCODE_I}},
244 M{0x37, K{SDLK_j},
S{SDL_SCANCODE_J}},
246 M{0x40, K{SDLK_k},
S{SDL_SCANCODE_K}},
247 M{0x41, K{SDLK_l},
S{SDL_SCANCODE_L}},
248 M{0x42, K{SDLK_m},
S{SDL_SCANCODE_M}},
249 M{0x43, K{SDLK_n},
S{SDL_SCANCODE_N}},
250 M{0x44, K{SDLK_o},
S{SDL_SCANCODE_O}},
251 M{0x45, K{SDLK_p},
S{SDL_SCANCODE_P}},
252 M{0x46, K{SDLK_q},
S{SDL_SCANCODE_Q}},
253 M{0x47, K{SDLK_r},
S{SDL_SCANCODE_R}},
255 M{0x50, K{SDLK_s},
S{SDL_SCANCODE_S}},
256 M{0x51, K{SDLK_t},
S{SDL_SCANCODE_T}},
257 M{0x52, K{SDLK_u},
S{SDL_SCANCODE_U}},
258 M{0x53, K{SDLK_v},
S{SDL_SCANCODE_V}},
259 M{0x54, K{SDLK_w},
S{SDL_SCANCODE_W}},
260 M{0x55, K{SDLK_x},
S{SDL_SCANCODE_X}},
261 M{0x56, K{SDLK_y},
S{SDL_SCANCODE_Y}},
262 M{0x57, K{SDLK_z},
S{SDL_SCANCODE_Z}},
264 M{0x60, K{SDLK_LSHIFT, SDLK_RSHIFT},
S{SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT}},
265 M{0x61, K{SDLK_LCTRL},
S{SDL_SCANCODE_LCTRL}},
266 M{0x62, K{SDLK_LALT},
S{SDL_SCANCODE_LALT}},
267 M{0x63, K{SDLK_CAPSLOCK},
S{SDL_SCANCODE_CAPSLOCK}},
268 M{0x64, K{SDLK_RALT},
S{SDL_SCANCODE_RALT}},
269 M{0x65, K{SDLK_F1},
S{SDL_SCANCODE_F1}},
270 M{0x66, K{SDLK_F2},
S{SDL_SCANCODE_F2}},
271 M{0x67, K{SDLK_F3},
S{SDL_SCANCODE_F3}},
273 M{0x70, K{SDLK_F4},
S{SDL_SCANCODE_F4}},
274 M{0x71, K{SDLK_F5},
S{SDL_SCANCODE_F5}},
275 M{0x72, K{SDLK_ESCAPE},
S{SDL_SCANCODE_ESCAPE}},
276 M{0x73, K{SDLK_TAB},
S{SDL_SCANCODE_TAB}},
277 M{0x74, K{SDLK_F8},
S{SDL_SCANCODE_F8}},
278 M{0x75, K{SDLK_BACKSPACE},
S{SDL_SCANCODE_BACKSPACE}},
279 M{0x76, K{SDLK_F7},
S{SDL_SCANCODE_F7}},
280 M{0x77, K{SDLK_RETURN},
S{SDL_SCANCODE_RETURN}},
282 M{0x80, K{SDLK_SPACE},
S{SDL_SCANCODE_SPACE}},
283 M{0x81, K{SDLK_HOME},
S{SDL_SCANCODE_HOME}},
284 M{0x82, K{SDLK_INSERT},
S{SDL_SCANCODE_INSERT}},
285 M{0x83, K{SDLK_DELETE},
S{SDL_SCANCODE_DELETE}},
286 M{0x84, K{SDLK_LEFT},
S{SDL_SCANCODE_LEFT}},
287 M{0x85, K{SDLK_UP},
S{SDL_SCANCODE_UP}},
288 M{0x86, K{SDLK_DOWN},
S{SDL_SCANCODE_DOWN}},
289 M{0x87, K{SDLK_RIGHT},
S{SDL_SCANCODE_RIGHT}},
291 M{0x90, K{SDLK_KP_MULTIPLY},
S{SDL_SCANCODE_KP_MULTIPLY}},
292 M{0x91, K{SDLK_KP_PLUS},
S{SDL_SCANCODE_KP_PLUS}},
293 M{0x92, K{SDLK_KP_DIVIDE},
S{SDL_SCANCODE_KP_DIVIDE}},
294 M{0x93, K{SDLK_KP_0},
S{SDL_SCANCODE_KP_0}},
295 M{0x94, K{SDLK_KP_1},
S{SDL_SCANCODE_KP_1}},
296 M{0x95, K{SDLK_KP_2},
S{SDL_SCANCODE_KP_2}},
297 M{0x96, K{SDLK_KP_3},
S{SDL_SCANCODE_KP_3}},
298 M{0x97, K{SDLK_KP_4},
S{SDL_SCANCODE_KP_4}},
300 M{0xA0, K{SDLK_KP_5},
S{SDL_SCANCODE_KP_5}},
301 M{0xA1, K{SDLK_KP_6},
S{SDL_SCANCODE_KP_6}},
302 M{0xA2, K{SDLK_KP_7},
S{SDL_SCANCODE_KP_7}},
303 M{0xA3, K{SDLK_KP_8},
S{SDL_SCANCODE_KP_8}},
304 M{0xA4, K{SDLK_KP_9},
S{SDL_SCANCODE_KP_9}},
305 M{0xA5, K{SDLK_KP_MINUS},
S{SDL_SCANCODE_KP_MINUS}},
306 M{0xA6, K{SDLK_KP_COMMA},
S{SDL_SCANCODE_KP_COMMA}},
307 M{0xA7, K{SDLK_KP_PERIOD},
S{SDL_SCANCODE_KP_PERIOD}},
309 M{0xB1, K{SDLK_RGUI},
S{SDL_SCANCODE_RGUI}},
310 M{0xB3, K{SDLK_LGUI},
S{SDL_SCANCODE_LGUI}},
315static constexpr auto getSVIMapping()
333 using M = MsxKeyScanMapping;
334 using K = std::array<SDL_Keycode, 3>;
335 using S = std::array<SDL_Scancode, 3>;
336 std::array mapping = {
337 M{0x00, K{SDLK_0},
S{SDL_SCANCODE_0}},
338 M{0x01, K{SDLK_1},
S{SDL_SCANCODE_1}},
339 M{0x02, K{SDLK_2},
S{SDL_SCANCODE_2}},
340 M{0x03, K{SDLK_3},
S{SDL_SCANCODE_3}},
341 M{0x04, K{SDLK_4},
S{SDL_SCANCODE_4}},
342 M{0x05, K{SDLK_5},
S{SDL_SCANCODE_5}},
343 M{0x06, K{SDLK_6},
S{SDL_SCANCODE_6}},
344 M{0x07, K{SDLK_7},
S{SDL_SCANCODE_7}},
346 M{0x10, K{SDLK_8},
S{SDL_SCANCODE_8}},
347 M{0x11, K{SDLK_9},
S{SDL_SCANCODE_9}},
348 M{0x12, K{SDLK_SEMICOLON},
S{SDL_SCANCODE_SEMICOLON}},
349 M{0x13, K{SDLK_QUOTE},
S{SDL_SCANCODE_APOSTROPHE}},
350 M{0x14, K{SDLK_COMMA},
S{SDL_SCANCODE_COMMA}},
351 M{0x15, K{SDLK_EQUALS},
S{SDL_SCANCODE_EQUALS}},
352 M{0x16, K{SDLK_PERIOD},
S{SDL_SCANCODE_PERIOD}},
353 M{0x17, K{SDLK_SLASH},
S{SDL_SCANCODE_SLASH}},
355 M{0x20, K{SDLK_MINUS},
S{SDL_SCANCODE_MINUS}},
356 M{0x21, K{SDLK_a},
S{SDL_SCANCODE_A}},
357 M{0x22, K{SDLK_b},
S{SDL_SCANCODE_B}},
358 M{0x23, K{SDLK_c},
S{SDL_SCANCODE_C}},
359 M{0x24, K{SDLK_d},
S{SDL_SCANCODE_D}},
360 M{0x25, K{SDLK_e},
S{SDL_SCANCODE_E}},
361 M{0x26, K{SDLK_f},
S{SDL_SCANCODE_F}},
362 M{0x27, K{SDLK_g},
S{SDL_SCANCODE_G}},
364 M{0x30, K{SDLK_h},
S{SDL_SCANCODE_H}},
365 M{0x31, K{SDLK_i},
S{SDL_SCANCODE_I}},
366 M{0x32, K{SDLK_j},
S{SDL_SCANCODE_J}},
367 M{0x33, K{SDLK_k},
S{SDL_SCANCODE_K}},
368 M{0x34, K{SDLK_l},
S{SDL_SCANCODE_L}},
369 M{0x35, K{SDLK_m},
S{SDL_SCANCODE_M}},
370 M{0x36, K{SDLK_n},
S{SDL_SCANCODE_N}},
371 M{0x37, K{SDLK_o},
S{SDL_SCANCODE_O}},
373 M{0x40, K{SDLK_p},
S{SDL_SCANCODE_P}},
374 M{0x41, K{SDLK_q},
S{SDL_SCANCODE_Q}},
375 M{0x42, K{SDLK_r},
S{SDL_SCANCODE_R}},
376 M{0x43, K{SDLK_s},
S{SDL_SCANCODE_S}},
377 M{0x44, K{SDLK_t},
S{SDL_SCANCODE_T}},
378 M{0x45, K{SDLK_u},
S{SDL_SCANCODE_U}},
379 M{0x46, K{SDLK_v},
S{SDL_SCANCODE_V}},
380 M{0x47, K{SDLK_w},
S{SDL_SCANCODE_W}},
382 M{0x50, K{SDLK_x},
S{SDL_SCANCODE_X}},
383 M{0x51, K{SDLK_y},
S{SDL_SCANCODE_Y}},
384 M{0x52, K{SDLK_z},
S{SDL_SCANCODE_Z}},
385 M{0x53, K{SDLK_LEFTBRACKET},
S{SDL_SCANCODE_LEFTBRACKET}},
386 M{0x54, K{SDLK_BACKSLASH},
S{SDL_SCANCODE_BACKSLASH}},
387 M{0x55, K{SDLK_RIGHTBRACKET},
S{SDL_SCANCODE_RIGHTBRACKET}},
388 M{0x56, K{SDLK_BACKSPACE},
S{SDL_SCANCODE_BACKSPACE}},
389 M{0x57, K{SDLK_UP},
S{SDL_SCANCODE_UP}},
391 M{0x60, K{SDLK_LSHIFT, SDLK_RSHIFT},
S{SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT}},
392 M{0x61, K{SDLK_LCTRL},
S{SDL_SCANCODE_LCTRL}},
393 M{0x62, K{SDLK_LALT},
S{SDL_SCANCODE_LALT}},
394 M{0x63, K{SDLK_RALT},
S{SDL_SCANCODE_RALT}},
395 M{0x64, K{SDLK_ESCAPE},
S{SDL_SCANCODE_ESCAPE}},
396 M{0x65, K{SDLK_F8},
S{SDL_SCANCODE_F8}},
397 M{0x66, K{SDLK_RETURN},
S{SDL_SCANCODE_RETURN}},
398 M{0x67, K{SDLK_LEFT},
S{SDL_SCANCODE_LEFT}},
400 M{0x70, K{SDLK_F1},
S{SDL_SCANCODE_F1}},
401 M{0x71, K{SDLK_F2},
S{SDL_SCANCODE_F2}},
402 M{0x72, K{SDLK_F3},
S{SDL_SCANCODE_F3}},
403 M{0x73, K{SDLK_F4},
S{SDL_SCANCODE_F4}},
404 M{0x74, K{SDLK_F5},
S{SDL_SCANCODE_F5}},
405 M{0x75, K{SDLK_F7},
S{SDL_SCANCODE_F7}},
406 M{0x76, K{SDLK_INSERT},
S{SDL_SCANCODE_INSERT}},
407 M{0x77, K{SDLK_DOWN},
S{SDL_SCANCODE_DOWN}},
409 M{0x80, K{SDLK_SPACE},
S{SDL_SCANCODE_F1}},
410 M{0x81, K{SDLK_TAB},
S{SDL_SCANCODE_F2}},
411 M{0x82, K{SDLK_DELETE},
S{SDL_SCANCODE_F3}},
412 M{0x83, K{SDLK_CAPSLOCK},
S{SDL_SCANCODE_F4}},
413 M{0x84, K{SDLK_F6},
S{SDL_SCANCODE_F6}},
414 M{0x85, K{SDLK_PRINTSCREEN},
S{SDL_SCANCODE_PRINTSCREEN}},
416 M{0x87, K{SDLK_RIGHT},
S{SDL_SCANCODE_RIGHT}},
418 M{0x90, K{SDLK_KP_0},
S{SDL_SCANCODE_KP_0}},
419 M{0x91, K{SDLK_KP_1},
S{SDL_SCANCODE_KP_1}},
420 M{0x92, K{SDLK_KP_2},
S{SDL_SCANCODE_KP_2}},
421 M{0x93, K{SDLK_KP_3},
S{SDL_SCANCODE_KP_3}},
422 M{0x94, K{SDLK_KP_4},
S{SDL_SCANCODE_KP_4}},
423 M{0x95, K{SDLK_KP_5},
S{SDL_SCANCODE_KP_5}},
424 M{0x96, K{SDLK_KP_6},
S{SDL_SCANCODE_KP_6}},
425 M{0x97, K{SDLK_KP_7},
S{SDL_SCANCODE_KP_7}},
427 M{0xA0, K{SDLK_KP_8},
S{SDL_SCANCODE_KP_8}},
428 M{0xA1, K{SDLK_KP_9},
S{SDL_SCANCODE_KP_9}},
429 M{0xA2, K{SDLK_KP_PLUS},
S{SDL_SCANCODE_KP_PLUS}},
430 M{0xA3, K{SDLK_KP_MINUS},
S{SDL_SCANCODE_KP_MINUS}},
431 M{0xA4, K{SDLK_KP_MULTIPLY},
S{SDL_SCANCODE_KP_MULTIPLY}},
432 M{0xA5, K{SDLK_KP_DIVIDE},
S{SDL_SCANCODE_KP_DIVIDE}},
433 M{0xA6, K{SDLK_KP_PERIOD},
S{SDL_SCANCODE_KP_PERIOD}},
434 M{0xA7, K{SDLK_KP_COMMA},
S{SDL_SCANCODE_KP_COMMA}},
439static constexpr auto getCvJoyMapping()
456 using M = MsxKeyScanMapping;
457 using K = std::array<SDL_Keycode, 3>;
458 using S = std::array<SDL_Scancode, 3>;
459 std::array mapping = {
460 M{0x00, K{SDLK_UP},
S{SDL_SCANCODE_UP}},
461 M{0x01, K{SDLK_RIGHT},
S{SDL_SCANCODE_RIGHT}},
462 M{0x02, K{SDLK_DOWN},
S{SDL_SCANCODE_DOWN}},
463 M{0x03, K{SDLK_LEFT},
S{SDL_SCANCODE_LEFT}},
464 M{0x06, K{SDLK_SPACE, SDLK_RCTRL},
465 S{SDL_SCANCODE_SPACE, SDL_SCANCODE_RCTRL}},
466 M{0x07, K{SDLK_RSHIFT, SDLK_RALT, SDLK_LALT},
467 S{SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_LALT}},
469 M{0x10, K{SDLK_w},
S{SDL_SCANCODE_W}},
470 M{0x11, K{SDLK_d},
S{SDL_SCANCODE_D}},
471 M{0x12, K{SDLK_s},
S{SDL_SCANCODE_S}},
472 M{0x13, K{SDLK_a},
S{SDL_SCANCODE_A}},
473 M{0x16, K{SDLK_LCTRL},
S{SDL_SCANCODE_LCTRL}},
474 M{0x17, K{SDLK_LSHIFT},
S{SDL_SCANCODE_LSHIFT}},
476 M{0x20, K{SDLK_0, SDLK_KP_0},
S{SDL_SCANCODE_0}},
477 M{0x21, K{SDLK_1, SDLK_KP_1},
S{SDL_SCANCODE_1}},
478 M{0x22, K{SDLK_2, SDLK_KP_2},
S{SDL_SCANCODE_2}},
479 M{0x23, K{SDLK_3, SDLK_KP_3},
S{SDL_SCANCODE_3}},
480 M{0x24, K{SDLK_4, SDLK_KP_4},
S{SDL_SCANCODE_4}},
481 M{0x25, K{SDLK_5, SDLK_KP_5},
S{SDL_SCANCODE_5}},
482 M{0x26, K{SDLK_6, SDLK_KP_6},
S{SDL_SCANCODE_6}},
483 M{0x27, K{SDLK_7, SDLK_KP_7},
S{SDL_SCANCODE_7}},
485 M{0x30, K{SDLK_8, SDLK_KP_8},
S{SDL_SCANCODE_8}},
486 M{0x31, K{SDLK_9, SDLK_KP_9},
S{SDL_SCANCODE_9}},
487 M{0x32, K{SDLK_MINUS, SDLK_KP_MULTIPLY, SDLK_KP_MINUS},
488 S{SDL_SCANCODE_MINUS, SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_KP_MINUS}},
489 M{0x33, K{SDLK_EQUALS, SDLK_KP_DIVIDE, SDLK_KP_PLUS},
490 S{SDL_SCANCODE_EQUALS, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_KP_PLUS}},
492 M{0x40, K{SDLK_u},
S{SDL_SCANCODE_U}},
493 M{0x41, K{SDLK_v},
S{SDL_SCANCODE_V}},
494 M{0x42, K{SDLK_b},
S{SDL_SCANCODE_B}},
495 M{0x43, K{SDLK_n},
S{SDL_SCANCODE_N}},
496 M{0x44, K{SDLK_f},
S{SDL_SCANCODE_F}},
497 M{0x45, K{SDLK_g},
S{SDL_SCANCODE_G}},
498 M{0x46, K{SDLK_h},
S{SDL_SCANCODE_H}},
499 M{0x47, K{SDLK_r},
S{SDL_SCANCODE_R}},
501 M{0x50, K{SDLK_t},
S{SDL_SCANCODE_T}},
502 M{0x51, K{SDLK_y},
S{SDL_SCANCODE_Y}},
503 M{0x52, K{SDLK_j},
S{SDL_SCANCODE_J}},
504 M{0x53, K{SDLK_m},
S{SDL_SCANCODE_M}},
509static constexpr auto getSegaMapping()
534 using M = MsxKeyScanMapping;
535 using K = std::array<SDL_Keycode, 3>;
536 using S = std::array<SDL_Scancode, 3>;
537 std::array mapping = {
538 M{0x00, K{SDLK_1, SDLK_KP_1},
539 S{SDL_SCANCODE_1, SDL_SCANCODE_KP_1}},
540 M{0x01, K{SDLK_q},
S{SDL_SCANCODE_Q}},
541 M{0x02, K{SDLK_a},
S{SDL_SCANCODE_A}},
542 M{0x03, K{SDLK_z},
S{SDL_SCANCODE_Z}},
543 M{0x04, K{SDLK_RALT},
S{SDL_SCANCODE_RALT}},
544 M{0x05, K{SDLK_COMMA},
S{SDL_SCANCODE_COMMA}},
545 M{0x06, K{SDLK_k},
S{SDL_SCANCODE_K}},
546 M{0x07, K{SDLK_i},
S{SDL_SCANCODE_I}},
548 M{0x10, K{SDLK_2, SDLK_KP_2},
549 S{SDL_SCANCODE_2, SDL_SCANCODE_KP_2}},
550 M{0x11, K{SDLK_w},
S{SDL_SCANCODE_W}},
551 M{0x12, K{SDLK_s},
S{SDL_SCANCODE_S}},
552 M{0x13, K{SDLK_x},
S{SDL_SCANCODE_X}},
553 M{0x14, K{SDLK_SPACE},
S{SDL_SCANCODE_SPACE}},
554 M{0x15, K{SDLK_PERIOD, SDLK_KP_PERIOD},
555 S{SDL_SCANCODE_PERIOD, SDL_SCANCODE_KP_PERIOD}},
556 M{0x16, K{SDLK_l},
S{SDL_SCANCODE_L}},
557 M{0x17, K{SDLK_o},
S{SDL_SCANCODE_O}},
559 M{0x20, K{SDLK_3, SDLK_KP_3},
560 S{SDL_SCANCODE_3, SDL_SCANCODE_KP_3}},
561 M{0x21, K{SDLK_e},
S{SDL_SCANCODE_E}},
562 M{0x22, K{SDLK_d},
S{SDL_SCANCODE_D}},
563 M{0x23, K{SDLK_c},
S{SDL_SCANCODE_C}},
564 M{0x24, K{SDLK_HOME},
S{SDL_SCANCODE_HOME}},
565 M{0x25, K{SDLK_SLASH, SDLK_KP_DIVIDE},
566 S{SDL_SCANCODE_SLASH, SDL_SCANCODE_KP_DIVIDE}},
567 M{0x26, K{SDLK_SEMICOLON, SDLK_KP_PLUS},
568 S{SDL_SCANCODE_SEMICOLON, SDL_SCANCODE_KP_PLUS}},
569 M{0x27, K{SDLK_p},
S{SDL_SCANCODE_P}},
571 M{0x30, K{SDLK_4, SDLK_KP_4},
572 S{SDL_SCANCODE_4, SDL_SCANCODE_KP_4}},
573 M{0x31, K{SDLK_r},
S{SDL_SCANCODE_R}},
574 M{0x32, K{SDLK_f},
S{SDL_SCANCODE_F}},
575 M{0x33, K{SDLK_v},
S{SDL_SCANCODE_V}},
576 M{0x34, K{SDLK_INSERT},
S{SDL_SCANCODE_INSERT}},
577 M{0x35, K{SDLK_F7},
S{SDL_SCANCODE_F7}},
578 M{0x36, K{SDLK_QUOTE, SDLK_KP_MULTIPLY},
579 S{SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_KP_MULTIPLY}},
580 M{0x37, K{SDLK_BACKQUOTE},
S{SDL_SCANCODE_GRAVE}},
582 M{0x40, K{SDLK_5, SDLK_KP_5},
583 S{SDL_SCANCODE_5, SDL_SCANCODE_KP_5}},
584 M{0x41, K{SDLK_t},
S{SDL_SCANCODE_T}},
585 M{0x42, K{SDLK_g},
S{SDL_SCANCODE_G}},
586 M{0x43, K{SDLK_b},
S{SDL_SCANCODE_B}},
588 M{0x45, K{SDLK_DOWN},
S{SDL_SCANCODE_DOWN}},
589 M{0x46, K{SDLK_RIGHTBRACKET},
S{SDL_SCANCODE_RIGHTBRACKET}},
590 M{0x47, K{SDLK_LEFTBRACKET},
S{SDL_SCANCODE_LEFTBRACKET}},
592 M{0x50, K{SDLK_6, SDLK_KP_6},
593 S{SDL_SCANCODE_6, SDL_SCANCODE_KP_6}},
594 M{0x51, K{SDLK_y},
S{SDL_SCANCODE_Y}},
595 M{0x52, K{SDLK_h},
S{SDL_SCANCODE_H}},
596 M{0x53, K{SDLK_n},
S{SDL_SCANCODE_N}},
598 M{0x55, K{SDLK_LEFT},
S{SDL_SCANCODE_LEFT}},
599 M{0x56, K{SDLK_RETURN, SDLK_KP_ENTER},
600 S{SDL_SCANCODE_RETURN, SDL_SCANCODE_KP_ENTER}},
603 M{0x60, K{SDLK_7, SDLK_KP_7},
604 S{SDL_SCANCODE_7, SDL_SCANCODE_KP_7}},
605 M{0x61, K{SDLK_u},
S{SDL_SCANCODE_U}},
606 M{0x62, K{SDLK_j},
S{SDL_SCANCODE_J}},
607 M{0x63, K{SDLK_m},
S{SDL_SCANCODE_M}},
609 M{0x65, K{SDLK_RIGHT},
S{SDL_SCANCODE_RIGHT}},
610 M{0x66, K{SDLK_UP},
S{SDL_SCANCODE_UP}},
613 M{0x70, K{SDLK_8, SDLK_KP_8},
614 S{SDL_SCANCODE_8, SDL_SCANCODE_KP_8}},
615 M{0x80, K{SDLK_9, SDLK_KP_9},
616 S{SDL_SCANCODE_9, SDL_SCANCODE_KP_9}},
617 M{0x90, K{SDLK_0, SDLK_KP_0},
618 S{SDL_SCANCODE_0, SDL_SCANCODE_KP_0}},
619 M{0xA0, K{SDLK_MINUS, SDLK_KP_MINUS},
620 S{SDL_SCANCODE_MINUS, SDL_SCANCODE_KP_MINUS}},
621 M{0xB0, K{SDLK_EQUALS},
S{SDL_SCANCODE_EQUALS}},
623 M{0xC0, K{SDLK_BACKSLASH},
S{SDL_SCANCODE_BACKSLASH}},
624 M{0xC3, K{SDLK_TAB},
S{SDL_SCANCODE_TAB}},
626 M{0xD0, K{SDLK_ESCAPE, SDLK_F8},
627 S{SDL_SCANCODE_ESCAPE, SDL_SCANCODE_F8}},
628 M{0xD1, K{SDLK_LALT},
S{SDL_SCANCODE_LALT}},
629 M{0xD2, K{SDLK_LCTRL},
S{SDL_SCANCODE_LCTRL}},
630 M{0xD3, K{SDLK_LSHIFT, SDLK_RSHIFT},
631 S{SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT}},
658static constexpr auto msxKeyCodeMapping = extractKeyCodeMapping ([] {
return getMSXMapping(); });
659static constexpr auto msxScanCodeMapping = extractScanCodeMapping([] {
return getMSXMapping(); });
660static constexpr auto sviKeyCodeMapping = extractKeyCodeMapping ([] {
return getSVIMapping(); });
661static constexpr auto sviScanCodeMapping = extractScanCodeMapping([] {
return getSVIMapping(); });
662static constexpr auto cvJoyKeyCodeMapping = extractKeyCodeMapping ([] {
return getCvJoyMapping(); });
663static constexpr auto cvJoyScanCodeMapping = extractScanCodeMapping([] {
return getCvJoyMapping(); });
664static constexpr auto segaKeyCodeMapping = extractKeyCodeMapping ([] {
return getSegaMapping(); });
665static constexpr auto segaScanCodeMapping = extractScanCodeMapping([] {
return getSegaMapping(); });
667static constexpr std::array<std::span<const KeyCodeMsxMapping>, 4> defaultKeyCodeMappings = {
673static constexpr std::array<std::span<const ScanCodeMsxMapping>, 4> defaultScanCodeMappings = {
676 cvJoyScanCodeMapping,
689 , commandController(commandController_)
690 , msxEventDistributor(msxEventDistributor_)
691 , stateChangeDistributor(stateChangeDistributor_)
692 , keyCodeTab (
to_vector(defaultKeyCodeMappings [matrix]))
693 , scanCodeTab(
to_vector(defaultScanCodeMappings[matrix]))
694 , modifierPos(modifierPosForMatrix[matrix])
695 , keyMatrixUpCmd (commandController, stateChangeDistributor, scheduler_)
696 , keyMatrixDownCmd(commandController, stateChangeDistributor, scheduler_)
697 , keyTypeCmd (commandController, stateChangeDistributor, scheduler_)
698 , msxcode2UnicodeCmd(commandController)
699 , unicode2MsxcodeCmd(commandController)
700 , capsLockAligner(eventDistributor, scheduler_)
701 , keyboardSettings(commandController)
702 , msxKeyEventQueue(scheduler_, commandController.getInterpreter())
703 , keybDebuggable(motherBoard)
704 , unicodeKeymap(config.getChildData(
705 "keyboard_type", defaultKeymapForMatrix[matrix]))
706 , hasKeypad(config.getChildDataAsBool(
"has_keypad", true))
707 , blockRow11(matrix == MATRIX_MSX
708 && !config.getChildDataAsBool(
"has_yesno_keys", false))
709 , keyGhosting(config.getChildDataAsBool(
"key_ghosting", true))
710 , keyGhostingSGCprotected(config.getChildDataAsBool(
711 "key_ghosting_sgc_protected", true))
712 , modifierIsLock(
KeyInfo::CAPS_MASK
713 | (config.getChildDataAsBool(
"code_kana_locks", false) ?
KeyInfo::CODE_MASK : 0)
714 | (config.getChildDataAsBool(
"graph_locks", false) ?
KeyInfo::GRAPH_MASK : 0))
741static constexpr void doKeyGhosting(std::span<uint8_t, KeyMatrixPosition::NUM_ROWS> matrix,
760 bool changedSomething =
false;
762 changedSomething =
false;
767 auto row1 = matrix[i];
769 auto row2 = matrix[j];
770 if ((row1 != row2) && ((row1 | row2) != 0xff)) {
771 auto rowIold = matrix[i];
772 auto rowJold = matrix[j];
775 if (protectRow6 && i == 6) {
776 matrix[i] = row1 & row2;
777 matrix[j] = (row1 | 0x15) & row2;
779 }
else if (protectRow6 && j == 6) {
780 matrix[i] = row1 & (row2 | 0x15);
781 matrix[j] = row1 & row2;
782 row1 &= (row2 | 0x15);
786 uint8_t newRow = row1 & row2;
791 if (rowIold != matrix[i] ||
792 rowJold != matrix[j]) {
793 changedSomething =
true;
798 }
while (changedSomething);
805 std::span matrix = keyTypeCmd.isActive() ? typeKeyMatrix : userKeyMatrix;
807 keyMatrix[row] = cmdKeyMatrix[row] & matrix[row];
810 doKeyGhosting(keyMatrix, keyGhostingSGCprotected);
834 hostKeyMatrix[row] = source.hostKeyMatrix[row];
843void Keyboard::signalMSXEvent(
const Event& event,
844 EmuTime::param time)
noexcept
847 const auto& keyEvent = get_event<KeyEvent>(event);
848 if (keyEvent.getRepeat())
return;
853 msxKeyEventQueue.process_asap(time, event);
857void Keyboard::signalStateChange(
const StateChange& event)
859 const auto* kms =
dynamic_cast<const KeyMatrixState*
>(&event);
862 userKeyMatrix[kms->getRow()] &= uint8_t(~kms->getPress());
863 userKeyMatrix[kms->getRow()] |= kms->getRelease();
867void Keyboard::stopReplay(EmuTime::param time)
noexcept
869 for (
auto [row, hkm] :
enumerate(hostKeyMatrix)) {
870 changeKeyMatrixEvent(time, uint8_t(row), hkm);
873 msxKeyEventQueue.clear();
874 lastUnicodeForKeycode.clear();
879 return modifierIsLock
880 & (locksOn ^ keyInfo.modMask)
884void Keyboard::pressKeyMatrixEvent(EmuTime::param time, KeyMatrixPosition pos)
886 if (!pos.isValid()) {
890 auto row = pos.getRow();
891 auto press = pos.getMask();
892 if (((hostKeyMatrix[row] & press) == 0) &&
893 ((userKeyMatrix[row] & press) == 0)) {
897 changeKeyMatrixEvent(time, row, hostKeyMatrix[row] & ~press);
900void Keyboard::releaseKeyMatrixEvent(EmuTime::param time, KeyMatrixPosition pos)
902 if (!pos.isValid()) {
906 auto row = pos.getRow();
907 auto release = pos.getMask();
908 if (((hostKeyMatrix[row] & release) == release) &&
909 ((userKeyMatrix[row] & release) == release)) {
916 changeKeyMatrixEvent(time, row, hostKeyMatrix[row] | release);
919void Keyboard::changeKeyMatrixEvent(EmuTime::param time, uint8_t row, uint8_t newValue)
923 hostKeyMatrix[row] = newValue;
925 uint8_t diff = userKeyMatrix[row] ^ newValue;
926 if (diff == 0)
return;
927 uint8_t press = userKeyMatrix[row] & diff;
928 uint8_t release = newValue & diff;
930 time, row, press, release);
936bool Keyboard::processQueuedEvent(
const Event& event, EmuTime::param time)
940 const auto& keyEvent = get_event<KeyEvent>(event);
942 auto key = keyEvent.getKey();
951 ad_printf(
"Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
952 keyEvent.getUnicode(),
953 keyEvent.getKeyCode(),
954 key.toString().c_str());
955 debug(
"Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
956 keyEvent.getUnicode(),
957 keyEvent.getKeyCode(),
958 key.toString().c_str());
960 ad_printf(
"Key released, keyCode: 0x%05x, keyName: %s\n",
961 keyEvent.getKeyCode(),
962 key.toString().c_str());
963 debug(
"Key released, keyCode: 0x%05x, keyName: %s\n",
964 keyEvent.getKeyCode(),
965 key.toString().c_str());
970 for (
auto n :
xrange(3)) {
973 if (deadKey.isValid()) {
974 updateKeyMatrix(time, down, deadKey.pos);
981 if (key.sym.sym == SDLK_CAPSLOCK) {
982 processCapslockEvent(time, down);
985 processCodeKanaChange(time, down);
987 }
else if (key.sym.sym == SDLK_LALT) {
988 processGraphChange(time, down);
990 }
else if (key.sym.sym == SDLK_KP_ENTER) {
991 processKeypadEnterKey(time, down);
994 return processKeyEvent(time, down, keyEvent);
1003void Keyboard::processCodeKanaChange(EmuTime::param time,
bool down)
1016void Keyboard::processGraphChange(EmuTime::param time,
bool down)
1029void Keyboard::processCapslockEvent(EmuTime::param time,
bool down)
1031 if (SANE_CAPSLOCK_BEHAVIOR) {
1032 debug(
"Changing CAPS lock state according to SDL request\n");
1038 debug(
"Pressing CAPS lock and scheduling a release\n");
1045void Keyboard::executeUntil(EmuTime::param time)
1047 debug(
"Releasing CAPS lock\n");
1051void Keyboard::processKeypadEnterKey(EmuTime::param time,
bool down)
1060 ? SDLK_KP_ENTER : SDLK_RETURN,
1070void Keyboard::processSdlKey(EmuTime::param time, SDLKey key)
1072 auto process = [&](KeyMatrixPosition pos) {
1073 assert(pos.isValid());
1074 if (pos.getRow() == 11 && blockRow11) {
1078 updateKeyMatrix(time, key.down, pos);
1083 process(mapping->msx);
1087 process(mapping->msx);
1095void Keyboard::updateKeyMatrix(EmuTime::param time,
bool down, KeyMatrixPosition pos)
1097 if (!pos.isValid()) {
1102 pressKeyMatrixEvent(time, pos);
1107 for (
auto [i, mp] :
enumerate(modifierPos)) {
1109 msxModifiers &= uint8_t(~(1 << i));
1113 releaseKeyMatrixEvent(time, pos);
1114 for (
auto [i, mp] :
enumerate(modifierPos)) {
1116 msxModifiers |= 1 << i;
1131bool Keyboard::processKeyEvent(EmuTime::param time,
bool down,
const KeyEvent& keyEvent)
1135 auto keyCode = keyEvent.getKeyCode();
1136 auto key = keyEvent.getKey();
1139 (keyCode >= SDLK_KP_0 && keyCode <= SDLK_KP_9) ||
1140 (keyCode ==
one_of(SDLK_KP_PERIOD, SDLK_KP_DIVIDE, SDLK_KP_MULTIPLY,
1141 SDLK_KP_MINUS, SDLK_KP_PLUS));
1143 if (isOnKeypad && !hasKeypad &&
1149#if defined(__APPLE__)
1151 if ((key.sym.mod & KMOD_GUI) &&
1152 (( positional && (keyEvent.getScanCode() == SDL_SCANCODE_I)) ||
1153 (!positional && (keyCode == SDLK_i)))) {
1155 keyCode = SDLK_INSERT;
1156 key.sym.sym = SDLK_INSERT;
1157 key.sym.scancode = SDL_SCANCODE_INSERT;
1158 key.sym.mod &= ~KMOD_GUI;
1177 unicode = keyEvent.getUnicode();
1178 if ((unicode < 0x20) || ((0x7F <= unicode) && (unicode < 0xA0))) {
1188 keyInfo = unicodeKeymap.
get(unicode);
1189 if (!keyInfo.isValid()) {
1203 auto it =
ranges::lower_bound(lastUnicodeForKeycode, keyCode, {}, &std::pair<SDL_Keycode, uint32_t>::first);
1204 if ((it != lastUnicodeForKeycode.end()) && (it->first == keyCode)) {
1207 it->second = unicode;
1210 lastUnicodeForKeycode.emplace(it, keyCode, unicode);
1222 if (!(key.sym.mod & KMOD_MODE)) {
1223 processSdlKey(time, key);
1228 return pressUnicodeByUser(time, keyInfo, unicode,
true);
1232 auto it =
ranges::lower_bound(lastUnicodeForKeycode, keyCode, {}, &std::pair<SDL_Keycode, uint32_t>::first);
1233 unsigned unicode = ((it != lastUnicodeForKeycode.end()) && (it->first == keyCode))
1240 if (!(key.sym.mod & KMOD_MODE)) {
1241 processSdlKey(time, key);
1245 pressUnicodeByUser(time, unicodeKeymap.
get(unicode), unicode,
false);
1251void Keyboard::processCmd(Interpreter& interp, std::span<const TclObject> tokens,
bool up)
1253 unsigned row = tokens[1].getInt(interp);
1254 unsigned mask = tokens[2].getInt(interp);
1256 throw CommandException(
"Invalid row");
1259 throw CommandException(
"Invalid mask");
1262 cmdKeyMatrix[row] |= narrow_cast<uint8_t>(mask);
1264 cmdKeyMatrix[row] &= narrow_cast<uint8_t>(~mask);
1307bool Keyboard::pressUnicodeByUser(
1311 bool insertCodeKanaRelease =
false;
1320 insertCodeKanaRelease =
true;
1329 pressKeyMatrixEvent(time, keyInfo.pos);
1331 uint8_t modMask = keyInfo.modMask & ~modifierIsLock;
1332 if ((
'A' <= unicode && unicode <=
'Z') || (
'a' <= unicode && unicode <=
'z')) {
1337 modMask &= ~KeyInfo::SHIFT_MASK;
1346 for (
auto [i, mp] :
enumerate(modifierPos)) {
1347 if ((modMask >> i) & 1) {
1348 pressKeyMatrixEvent(time, mp);
1353 releaseKeyMatrixEvent(time, keyInfo.pos);
1356 for (
auto [i, mp] :
enumerate(modifierPos)) {
1357 if (!((modifierIsLock >> i) & 1)) {
1360 if ((msxModifiers >> i) & 1) {
1361 releaseKeyMatrixEvent(time, mp);
1363 pressKeyMatrixEvent(time, mp);
1369 return insertCodeKanaRelease;
1384uint8_t Keyboard::pressAscii(
unsigned unicode,
bool down)
1386 uint8_t releaseMask = 0;
1388 if (!keyInfo.isValid()) {
1391 uint8_t modMask = keyInfo.
modMask & ~modifierIsLock;
1394 uint8_t toggleLocks = needsLockToggle(keyInfo);
1395 for (
auto [i, mp] :
enumerate(modifierPos)) {
1396 if ((toggleLocks >> i) & 1) {
1397 debug(
"Toggling lock %d\n", i);
1399 releaseMask |= 1 << i;
1400 typeKeyMatrix[mp.getRow()] &= uint8_t(~mp.getMask());
1403 if (releaseMask == 0) {
1404 debug(
"Key pasted, unicode: 0x%04x, row: %02d, col: %d, modMask: %02x\n",
1405 unicode, keyInfo.pos.getRow(), keyInfo.pos.getColumn(), modMask);
1434 auto isPressed = [&](
auto& key) {
1435 return (typeKeyMatrix[key.getRow()] & key.getMask()) == 0;
1440 releaseMask = TRY_AGAIN;
1444 for (
auto [i, mp] :
enumerate(modifierPos)) {
1445 if ((modMask >> i) & 1) {
1446 typeKeyMatrix[mp.getRow()] &= uint8_t(~mp.getMask());
1449 if (releaseMask == 0) {
1451 typeKeyMatrix[keyInfo.pos.getRow()] &= uint8_t(~keyInfo.pos.getMask());
1455 typeKeyMatrix[keyInfo.pos.getRow()] |= keyInfo.pos.getMask();
1456 for (
auto [i, mp] :
enumerate(modifierPos)) {
1457 if ((modMask >> i) & 1) {
1458 typeKeyMatrix[mp.getRow()] |= mp.getMask();
1472void Keyboard::pressLockKeys(uint8_t lockKeysMask,
bool down)
1474 for (
auto [i, mp] :
enumerate(modifierPos)) {
1475 if ((lockKeysMask >> i) & 1) {
1478 typeKeyMatrix[mp.getRow()] &= uint8_t(~mp.getMask());
1481 typeKeyMatrix[mp.getRow()] |= mp.getMask();
1495bool Keyboard::commonKeys(
unsigned unicode1,
unsigned unicode2)
1498 auto keyPos1 = unicodeKeymap.
get(unicode1).
pos;
1499 auto keyPos2 = unicodeKeymap.
get(unicode2).
pos;
1501 return keyPos1 == keyPos2 && keyPos1.
isValid();
1504void Keyboard::debug(
const char* format, ...)
1508 va_start(args, format);
1509 vfprintf(stderr, format, args);
1517Keyboard::KeyMatrixUpCmd::KeyMatrixUpCmd(
1518 CommandController& commandController_,
1519 StateChangeDistributor& stateChangeDistributor_,
1521 : RecordedCommand(commandController_, stateChangeDistributor_,
1522 scheduler_,
"keymatrixup")
1526void Keyboard::KeyMatrixUpCmd::execute(
1527 std::span<const TclObject> tokens, TclObject& , EmuTime::param )
1529 checkNumArgs(tokens, 3, Prefix{1},
"row mask");
1531 return keyboard.processCmd(getInterpreter(), tokens,
true);
1534std::string Keyboard::KeyMatrixUpCmd::help(std::span<const TclObject> )
const
1536 return "keymatrixup <row> <bitmask> release a key in the keyboard matrix\n";
1542Keyboard::KeyMatrixDownCmd::KeyMatrixDownCmd(CommandController& commandController_,
1543 StateChangeDistributor& stateChangeDistributor_,
1545 : RecordedCommand(commandController_, stateChangeDistributor_,
1546 scheduler_,
"keymatrixdown")
1550void Keyboard::KeyMatrixDownCmd::execute(std::span<const TclObject> tokens,
1551 TclObject& , EmuTime::param )
1553 checkNumArgs(tokens, 3, Prefix{1},
"row mask");
1555 return keyboard.processCmd(getInterpreter(), tokens,
false);
1558std::string Keyboard::KeyMatrixDownCmd::help(std::span<const TclObject> )
const
1560 return "keymatrixdown <row> <bitmask> press a key in the keyboard matrix\n";
1566Keyboard::MsxKeyEventQueue::MsxKeyEventQueue(
1567 Scheduler& scheduler_, Interpreter& interp_)
1573void Keyboard::MsxKeyEventQueue::process_asap(
1574 EmuTime::param time,
const Event& event)
1576 bool processImmediately = eventQueue.empty();
1577 eventQueue.push_back(event);
1578 if (processImmediately) {
1583void Keyboard::MsxKeyEventQueue::clear()
1589void Keyboard::MsxKeyEventQueue::executeUntil(EmuTime::param time)
1592 const Event&
event = eventQueue.front();
1594 bool insertCodeKanaRelease = keyboard.processQueuedEvent(event, time);
1596 if (insertCodeKanaRelease) {
1600 eventQueue.push_front(
KeyUpEvent::create(keyboard.keyboardSettings.getCodeKanaHostKey()));
1603 if (!eventQueue.empty()) {
1604 eventQueue.pop_front();
1611 if (!eventQueue.empty()) {
1620Keyboard::KeyInserter::KeyInserter(
1621 CommandController& commandController_,
1622 StateChangeDistributor& stateChangeDistributor_,
1624 : RecordedCommand(commandController_, stateChangeDistributor_,
1625 scheduler_,
"type_via_keyboard")
1630void Keyboard::KeyInserter::execute(
1631 std::span<const TclObject> tokens, TclObject& , EmuTime::param )
1633 checkNumArgs(tokens, AtLeast{2},
"?-release? ?-freq hz? ?-cancel? text");
1635 bool cancel =
false;
1636 releaseBeforePress =
false;
1637 typingFrequency = 15;
1640 flagArg(
"-release", releaseBeforePress),
1641 valueArg(
"-freq", typingFrequency),
1643 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan(1), info);
1645 if (typingFrequency <= 0) {
1646 throw CommandException(
"Wrong argument for -freq (should be a positive number)");
1653 if (arguments.size() != 1)
throw SyntaxError();
1655 type(arguments[0].getString());
1658std::string Keyboard::KeyInserter::help(std::span<const TclObject> )
const
1660 return "Type a string in the emulated MSX.\n"
1661 "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"
1662 "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"
1663 "Use -cancel to cancel a (long) in-progress type command.";
1666void Keyboard::KeyInserter::tabCompletion(std::vector<std::string>& tokens)
const
1668 using namespace std::literals;
1669 static constexpr std::array options = {
"-release"sv,
"-freq"sv};
1670 completeString(tokens, options);
1673void Keyboard::KeyInserter::type(std::string_view str)
1679 oldLocksOn = keyboard.locksOn;
1680 if (text_utf8.empty()) {
1681 reschedule(getCurrentTime());
1683 text_utf8.append(str.data(), str.size());
1686void Keyboard::KeyInserter::executeUntil(EmuTime::param time)
1689 if (lockKeysMask != 0) {
1691 keyboard.pressLockKeys(lockKeysMask,
false);
1694 keyboard.pressAscii(last,
false);
1696 if (text_utf8.empty()) {
1697 releaseLast =
false;
1698 keyboard.debug(
"Restoring locks: %02X -> %02X\n", keyboard.locksOn, oldLocksOn);
1699 uint8_t diff = oldLocksOn ^ keyboard.locksOn;
1700 lockKeysMask = diff;
1703 keyboard.locksOn ^= diff;
1704 keyboard.pressLockKeys(diff,
true);
1711 auto it =
begin(text_utf8);
1713 if (releaseLast && (releaseBeforePress || keyboard.commonKeys(last, current))) {
1717 releaseLast =
false;
1721 lockKeysMask = keyboard.pressAscii(current,
true);
1722 if (lockKeysMask == 0) {
1725 text_utf8.erase(
begin(text_utf8), it);
1726 }
else if (lockKeysMask & TRY_AGAIN) {
1727 lockKeysMask &= ~TRY_AGAIN;
1728 releaseLast =
false;
1729 }
else if (releaseBeforePress) {
1734 }
catch (std::exception&) {
1740void Keyboard::KeyInserter::reschedule(EmuTime::param time)
1748Keyboard::Msxcode2UnicodeCmd::Msxcode2UnicodeCmd(CommandController& commandController_)
1749 : Command(commandController_,
"msxcode2unicode")
1753void Keyboard::Msxcode2UnicodeCmd::execute(std::span<const TclObject> tokens, TclObject& result)
1755 checkNumArgs(tokens, Between{2, 3},
"msx-string ?fallback?");
1757 auto& interp = getInterpreter();
1759 const auto& msxChars = keyboard.unicodeKeymap.getMsxChars();
1761 auto msx = tokens[1].getBinary();
1762 auto fallback = [&]() -> std::function<uint32_t(uint8_t)> {
1763 if (tokens.size() < 3) {
1765 return [](uint8_t) {
return uint32_t(
' '); };
1766 }
else if (
auto i = tokens[2].getOptionalInt()) {
1768 return [i = *i](uint8_t) {
return uint32_t(i); };
1773 return [&](uint8_t m) {
1774 TclObject cmd{TclObject::MakeListTag{}, tokens[2], m};
1775 return uint32_t(cmd.executeCommand(interp).getInt(interp));
1780 result = msxChars.msxToUtf8(msx, fallback);
1783std::string Keyboard::Msxcode2UnicodeCmd::help(std::span<const TclObject> )
const
1785 return "msxcode2unicode <msx-string> [<fallback>]\n"
1786 "returns a unicode string converted from an MSX-string, i.e. a string based on\n"
1787 "MSX character codes.\n"
1788 "The optional fallback used for each character that cannot be mapped for the\n"
1789 "current MSX model can be:\n"
1790 "- omitted: then space will be used as fallback character.\n"
1791 "- an integer number: then this number will be used as unicode point to be the\n"
1792 " the fallback character.\n"
1793 "- a Tcl proc, which expects one input character and must return one unicode\n"
1798Keyboard::Unicode2MsxcodeCmd::Unicode2MsxcodeCmd(CommandController& commandController_)
1799 : Command(commandController_,
"unicode2msxcode")
1803void Keyboard::Unicode2MsxcodeCmd::execute(std::span<const TclObject> tokens, TclObject& result)
1805 checkNumArgs(tokens, Between{2, 3},
"unicode-string ?fallback?");
1807 auto& interp = getInterpreter();
1809 const auto& msxChars = keyboard.unicodeKeymap.getMsxChars();
1811 const auto& unicode = tokens[1].getString();
1812 auto fallback = [&]() -> std::function<uint8_t(uint32_t)> {
1813 if (tokens.size() < 3) {
1815 return [](uint32_t) {
return uint8_t(
' '); };
1816 }
else if (
auto i = tokens[2].getOptionalInt()) {
1818 return [i = *i](uint32_t) {
return uint8_t(i); };
1823 return [&](uint32_t u) {
1824 TclObject cmd{TclObject::MakeListTag{}, tokens[2], u};
1825 return uint8_t(cmd.executeCommand(interp).getInt(interp));
1830 result = msxChars.utf8ToMsx(unicode, fallback);
1833std::string Keyboard::Unicode2MsxcodeCmd::help(std::span<const TclObject> )
const
1835 return "unicode2msxcode <unicode-string> [<fallback>]\n"
1836 "returns an MSX string, i.e. a string based on MSX character codes, converted\n"
1837 "from a unicode string.\n"
1838 "The optional fallback used for each character that cannot be mapped for the\n"
1839 "current MSX model can be:\n"
1840 "- omitted: then space will be used as fallback character.\n"
1841 "- an integer number: then this number will be used as MSX character number to\n"
1842 " to be the fallback character.\n"
1843 "- a Tcl proc, which expects one input character and must return one MSX\n"
1844 " character number.";
1860Keyboard::CapsLockAligner::CapsLockAligner(
1861 EventDistributor& eventDistributor_,
1864 , eventDistributor(eventDistributor_)
1870Keyboard::CapsLockAligner::~CapsLockAligner()
1876int Keyboard::CapsLockAligner::signalEvent(
const Event& event)
1878 if constexpr (!SANE_CAPSLOCK_BEHAVIOR) {
1883 if (state == IDLE) {
1884 EmuTime::param time = getCurrentTime();
1886 [&](
const WindowEvent&
e) {
1887 if (
e.isMainWindow()) {
1888 const auto& evt =
e.getSdlWindowEvent();
1889 if (evt.event ==
one_of(SDL_WINDOWEVENT_FOCUS_GAINED, SDL_WINDOWEVENT_FOCUS_LOST)) {
1890 alignCapsLock(time);
1894 [&](
const BootEvent&) {
1895 state = MUST_ALIGN_CAPSLOCK;
1904void Keyboard::CapsLockAligner::executeUntil(EmuTime::param time)
1907 case MUST_ALIGN_CAPSLOCK:
1908 alignCapsLock(time);
1910 case MUST_DISTRIBUTE_KEY_RELEASE: {
1913 keyboard.msxEventDistributor.distributeEvent(event, time);
1932void Keyboard::CapsLockAligner::alignCapsLock(EmuTime::param time)
1934 bool hostCapsLockOn = ((SDL_GetModState() & KMOD_CAPS) != 0);
1937 keyboard.debug(
"Resyncing host and MSX CAPS lock\n");
1941 keyboard.msxEventDistributor.distributeEvent(event, time);
1942 keyboard.debug(
"Sending fake CAPS release\n");
1943 state = MUST_DISTRIBUTE_KEY_RELEASE;
1953Keyboard::KeybDebuggable::KeybDebuggable(MSXMotherBoard& motherBoard_)
1954 : SimpleDebuggable(motherBoard_,
"keymatrix",
"MSX Keyboard Matrix",
1955 KeyMatrixPosition::NUM_ROWS)
1959uint8_t Keyboard::KeybDebuggable::read(
unsigned address)
1962 return keyboard.getKeys()[address];
1965void Keyboard::KeybDebuggable::write(
unsigned , uint8_t )
1971template<
typename Archive>
1974 ar.template serializeBase<Schedulable>(*
this);
1975 ar.serialize(
"text", text_utf8,
1977 "lockKeysMask", lockKeysMask,
1978 "releaseLast", releaseLast);
1980 bool oldCodeKanaLockOn, oldGraphLockOn, oldCapsLockOn;
1981 if constexpr (!Archive::IS_LOADER) {
1986 ar.serialize(
"oldCodeKanaLockOn", oldCodeKanaLockOn,
1987 "oldGraphLockOn", oldGraphLockOn,
1988 "oldCapsLockOn", oldCapsLockOn);
1989 if constexpr (Archive::IS_LOADER) {
2010template<
typename Archive>
2013 ar.serialize(
"keyTypeCmd", keyTypeCmd,
2014 "cmdKeyMatrix", cmdKeyMatrix);
2015 if (ar.versionAtLeast(version, 3)) {
2016 ar.serialize(
"typeKeyMatrix", typeKeyMatrix);
2018 typeKeyMatrix = cmdKeyMatrix;
2021 bool msxCapsLockOn, msxCodeKanaLockOn, msxGraphLockOn;
2022 if constexpr (!Archive::IS_LOADER) {
2027 ar.serialize(
"msxCapsLockOn", msxCapsLockOn,
2028 "msxCodeKanaLockOn", msxCodeKanaLockOn,
2029 "msxGraphLockOn", msxGraphLockOn);
2030 if constexpr (Archive::IS_LOADER) {
2036 if (ar.versionAtLeast(version, 2)) {
2037 ar.serialize(
"userKeyMatrix", userKeyMatrix,
2038 "msxmodifiers", msxModifiers,
2039 "msxKeyEventQueue", msxKeyEventQueue);
2041 if (ar.versionAtLeast(version, 4)) {
2042 ar.serialize(
"lastUnicodeForKeycode", lastUnicodeForKeycode);
2055 if constexpr (Archive::IS_LOADER) {
2062template<
typename Archive>
2065 ar.template serializeBase<Schedulable>(*
this);
2074 std::vector<std::string> eventStrs;
2075 if constexpr (!Archive::IS_LOADER) {
2077 eventQueue, [](
const auto&
e) {
return toString(
e); }));
2079 ar.serialize(
"eventQueue", eventStrs);
2080 if constexpr (Archive::IS_LOADER) {
2081 assert(eventQueue.empty());
2082 for (
auto& s : eventStrs) {
2083 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
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....
This file implemented 3 utility functions:
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, ImGuiDelayedActionEvent, ImGuiActiveEvent > 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)
std::string toString(const BooleanInput &input)
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)