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))
741 auto& motherBoard = keybDebuggable.getMotherBoard();
742 motherBoard.unregisterKeyboard(*
this);
753static constexpr void doKeyGhosting(std::span<uint8_t, KeyMatrixPosition::NUM_ROWS> matrix,
772 bool changedSomething =
false;
774 changedSomething =
false;
779 auto row1 = matrix[i];
781 auto row2 = matrix[j];
782 if ((row1 != row2) && ((row1 | row2) != 0xff)) {
783 auto rowIold = matrix[i];
784 auto rowJold = matrix[j];
787 if (protectRow6 && i == 6) {
788 matrix[i] = row1 & row2;
789 matrix[j] = (row1 | 0x15) & row2;
791 }
else if (protectRow6 && j == 6) {
792 matrix[i] = row1 & (row2 | 0x15);
793 matrix[j] = row1 & row2;
794 row1 &= (row2 | 0x15);
798 uint8_t newRow = row1 & row2;
803 if (rowIold != matrix[i] ||
804 rowJold != matrix[j]) {
805 changedSomething =
true;
810 }
while (changedSomething);
817 std::span matrix = keyTypeCmd.isActive() ? typeKeyMatrix : userKeyMatrix;
819 keyMatrix[row] = cmdKeyMatrix[row] & matrix[row];
822 doKeyGhosting(keyMatrix, keyGhostingSGCprotected);
846 hostKeyMatrix[row] = source.hostKeyMatrix[row];
852 if (newFocus == focus)
return;
855 syncHostKeyMatrix(time);
863void Keyboard::signalMSXEvent(
const Event& event,
864 EmuTime::param time)
noexcept
867 const auto& keyEvent = get_event<KeyEvent>(event);
868 if (keyEvent.getRepeat())
return;
873 msxKeyEventQueue.process_asap(time, event);
877void Keyboard::signalStateChange(
const StateChange& event)
879 const auto* kms =
dynamic_cast<const KeyMatrixState*
>(&event);
882 userKeyMatrix[kms->getRow()] &= uint8_t(~kms->getPress());
883 userKeyMatrix[kms->getRow()] |= kms->getRelease();
887void Keyboard::stopReplay(EmuTime::param time)
noexcept
889 syncHostKeyMatrix(time);
892void Keyboard::syncHostKeyMatrix(EmuTime::param time)
894 for (
auto [row, hkm] :
enumerate(hostKeyMatrix)) {
895 changeKeyMatrixEvent(time, uint8_t(row), hkm);
898 msxKeyEventQueue.clear();
899 lastUnicodeForKeycode.clear();
902uint8_t Keyboard::needsLockToggle(
const UnicodeKeymap::KeyInfo& keyInfo)
const
904 return modifierIsLock
905 & (locksOn ^ keyInfo.modMask)
909void Keyboard::pressKeyMatrixEvent(EmuTime::param time, KeyMatrixPosition pos)
911 if (!pos.isValid()) {
915 auto row = pos.getRow();
916 auto press = pos.getMask();
917 if (((hostKeyMatrix[row] & press) == 0) &&
918 ((userKeyMatrix[row] & press) == 0)) {
922 changeKeyMatrixEvent(time, row, hostKeyMatrix[row] & ~press);
925void Keyboard::releaseKeyMatrixEvent(EmuTime::param time, KeyMatrixPosition pos)
927 if (!pos.isValid()) {
931 auto row = pos.getRow();
932 auto release = pos.getMask();
933 if (((hostKeyMatrix[row] & release) == release) &&
934 ((userKeyMatrix[row] & release) == release)) {
941 changeKeyMatrixEvent(time, row, hostKeyMatrix[row] | release);
944void Keyboard::changeKeyMatrixEvent(EmuTime::param time, uint8_t row, uint8_t newValue)
946 if (!focus) newValue = 0xff;
950 hostKeyMatrix[row] = newValue;
952 uint8_t diff = userKeyMatrix[row] ^ newValue;
953 if (diff == 0)
return;
954 uint8_t press = userKeyMatrix[row] & diff;
955 uint8_t release = newValue & diff;
957 time, row, press, release);
963bool Keyboard::processQueuedEvent(
const Event& event, EmuTime::param time)
967 const auto& keyEvent = get_event<KeyEvent>(event);
969 auto key = keyEvent.getKey();
978 ad_printf(
"Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
979 keyEvent.getUnicode(),
980 keyEvent.getKeyCode(),
981 key.toString().c_str());
982 debug(
"Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n",
983 keyEvent.getUnicode(),
984 keyEvent.getKeyCode(),
985 key.toString().c_str());
987 ad_printf(
"Key released, keyCode: 0x%05x, keyName: %s\n",
988 keyEvent.getKeyCode(),
989 key.toString().c_str());
990 debug(
"Key released, keyCode: 0x%05x, keyName: %s\n",
991 keyEvent.getKeyCode(),
992 key.toString().c_str());
997 for (
auto n :
xrange(3)) {
999 UnicodeKeymap::KeyInfo deadKey = unicodeKeymap.
getDeadKey(n);
1000 if (deadKey.isValid()) {
1001 updateKeyMatrix(time, down, deadKey.pos);
1008 if (key.sym.sym == SDLK_CAPSLOCK) {
1009 processCapslockEvent(time, down);
1012 processCodeKanaChange(time, down);
1014 }
else if (key.sym.sym == SDLK_LALT) {
1015 processGraphChange(time, down);
1017 }
else if (key.sym.sym == SDLK_KP_ENTER) {
1018 processKeypadEnterKey(time, down);
1021 return processKeyEvent(time, down, keyEvent);
1030void Keyboard::processCodeKanaChange(EmuTime::param time,
bool down)
1043void Keyboard::processGraphChange(EmuTime::param time,
bool down)
1056void Keyboard::processCapslockEvent(EmuTime::param time,
bool down)
1058 if (SANE_CAPSLOCK_BEHAVIOR) {
1059 debug(
"Changing CAPS lock state according to SDL request\n");
1065 debug(
"Pressing CAPS lock and scheduling a release\n");
1072void Keyboard::executeUntil(EmuTime::param time)
1074 debug(
"Releasing CAPS lock\n");
1078void Keyboard::processKeypadEnterKey(EmuTime::param time,
bool down)
1087 ? SDLK_KP_ENTER : SDLK_RETURN,
1097void Keyboard::processSdlKey(EmuTime::param time, SDLKey key)
1099 auto process = [&](KeyMatrixPosition pos) {
1100 assert(pos.isValid());
1101 if (pos.getRow() == 11 && blockRow11) {
1105 updateKeyMatrix(time, key.down, pos);
1110 process(mapping->msx);
1114 process(mapping->msx);
1122void Keyboard::updateKeyMatrix(EmuTime::param time,
bool down, KeyMatrixPosition pos)
1124 if (!pos.isValid()) {
1129 pressKeyMatrixEvent(time, pos);
1134 for (
auto [i, mp] :
enumerate(modifierPos)) {
1136 msxModifiers &= uint8_t(~(1 << i));
1140 releaseKeyMatrixEvent(time, pos);
1141 for (
auto [i, mp] :
enumerate(modifierPos)) {
1143 msxModifiers |= 1 << i;
1158bool Keyboard::processKeyEvent(EmuTime::param time,
bool down,
const KeyEvent& keyEvent)
1162 auto keyCode = keyEvent.getKeyCode();
1163 auto key = keyEvent.getKey();
1166 (SDLK_KP_1 <= keyCode && keyCode <= SDLK_KP_0) ||
1167 (keyCode ==
one_of(SDLK_KP_PERIOD, SDLK_KP_DIVIDE, SDLK_KP_MULTIPLY,
1168 SDLK_KP_MINUS, SDLK_KP_PLUS));
1170 if (isOnKeypad && !hasKeypad &&
1176#if defined(__APPLE__)
1178 if ((key.sym.mod & KMOD_GUI) &&
1179 (( positional && (keyEvent.getScanCode() == SDL_SCANCODE_I)) ||
1180 (!positional && (keyCode == SDLK_i)))) {
1182 keyCode = SDLK_INSERT;
1183 key.sym.sym = SDLK_INSERT;
1184 key.sym.scancode = SDL_SCANCODE_INSERT;
1185 key.sym.mod &= ~KMOD_GUI;
1191 UnicodeKeymap::KeyInfo keyInfo;
1204 unicode = keyEvent.getUnicode();
1205 if ((unicode < 0x20) || ((0x7F <= unicode) && (unicode < 0xA0))) {
1215 keyInfo = unicodeKeymap.
get(unicode);
1216 if (!keyInfo.isValid()) {
1230 auto it =
ranges::lower_bound(lastUnicodeForKeycode, keyCode, {}, &std::pair<SDL_Keycode, uint32_t>::first);
1231 if ((it != lastUnicodeForKeycode.end()) && (it->first == keyCode)) {
1234 it->second = unicode;
1237 lastUnicodeForKeycode.emplace(it, keyCode, unicode);
1249 if (!(key.sym.mod & KMOD_MODE)) {
1250 processSdlKey(time, key);
1255 return pressUnicodeByUser(time, keyInfo, unicode,
true);
1259 auto it =
ranges::lower_bound(lastUnicodeForKeycode, keyCode, {}, &std::pair<SDL_Keycode, uint32_t>::first);
1260 unsigned unicode = ((it != lastUnicodeForKeycode.end()) && (it->first == keyCode))
1267 if (!(key.sym.mod & KMOD_MODE)) {
1268 processSdlKey(time, key);
1272 pressUnicodeByUser(time, unicodeKeymap.
get(unicode), unicode,
false);
1278void Keyboard::processCmd(Interpreter& interp, std::span<const TclObject> tokens,
bool up)
1280 unsigned row = tokens[1].getInt(interp);
1281 unsigned mask = tokens[2].getInt(interp);
1283 throw CommandException(
"Invalid row");
1286 throw CommandException(
"Invalid mask");
1289 cmdKeyMatrix[row] |= narrow_cast<uint8_t>(mask);
1291 cmdKeyMatrix[row] &= narrow_cast<uint8_t>(~mask);
1334bool Keyboard::pressUnicodeByUser(
1335 EmuTime::param time, UnicodeKeymap::KeyInfo keyInfo,
unsigned unicode,
1338 bool insertCodeKanaRelease =
false;
1347 insertCodeKanaRelease =
true;
1356 pressKeyMatrixEvent(time, keyInfo.pos);
1358 uint8_t modMask = keyInfo.modMask & ~modifierIsLock;
1359 if ((
'A' <= unicode && unicode <=
'Z') || (
'a' <= unicode && unicode <=
'z')) {
1364 modMask &= ~KeyInfo::SHIFT_MASK;
1373 for (
auto [i, mp] :
enumerate(modifierPos)) {
1374 if ((modMask >> i) & 1) {
1375 pressKeyMatrixEvent(time, mp);
1380 releaseKeyMatrixEvent(time, keyInfo.pos);
1383 for (
auto [i, mp] :
enumerate(modifierPos)) {
1384 if (!((modifierIsLock >> i) & 1)) {
1387 if ((msxModifiers >> i) & 1) {
1388 releaseKeyMatrixEvent(time, mp);
1390 pressKeyMatrixEvent(time, mp);
1396 return insertCodeKanaRelease;
1411uint8_t Keyboard::pressAscii(
unsigned unicode,
bool down)
1413 uint8_t releaseMask = 0;
1414 UnicodeKeymap::KeyInfo keyInfo = unicodeKeymap.
get(unicode);
1415 if (!keyInfo.isValid()) {
1418 uint8_t modMask = keyInfo.
modMask & ~modifierIsLock;
1421 uint8_t toggleLocks = needsLockToggle(keyInfo);
1422 for (
auto [i, mp] :
enumerate(modifierPos)) {
1423 if ((toggleLocks >> i) & 1) {
1424 debug(
"Toggling lock %d\n", i);
1426 releaseMask |= 1 << i;
1427 typeKeyMatrix[mp.getRow()] &= uint8_t(~mp.getMask());
1430 if (releaseMask == 0) {
1431 debug(
"Key pasted, unicode: 0x%04x, row: %02d, col: %d, modMask: %02x\n",
1432 unicode, keyInfo.pos.getRow(), keyInfo.pos.getColumn(), modMask);
1461 auto isPressed = [&](
auto& key) {
1462 return (typeKeyMatrix[key.getRow()] & key.getMask()) == 0;
1467 releaseMask = TRY_AGAIN;
1471 for (
auto [i, mp] :
enumerate(modifierPos)) {
1472 if ((modMask >> i) & 1) {
1473 typeKeyMatrix[mp.getRow()] &= uint8_t(~mp.getMask());
1476 if (releaseMask == 0) {
1478 typeKeyMatrix[keyInfo.pos.getRow()] &= uint8_t(~keyInfo.pos.getMask());
1482 typeKeyMatrix[keyInfo.pos.getRow()] |= keyInfo.pos.getMask();
1483 for (
auto [i, mp] :
enumerate(modifierPos)) {
1484 if ((modMask >> i) & 1) {
1485 typeKeyMatrix[mp.getRow()] |= mp.getMask();
1499void Keyboard::pressLockKeys(uint8_t lockKeysMask,
bool down)
1501 for (
auto [i, mp] :
enumerate(modifierPos)) {
1502 if ((lockKeysMask >> i) & 1) {
1505 typeKeyMatrix[mp.getRow()] &= uint8_t(~mp.getMask());
1508 typeKeyMatrix[mp.getRow()] |= mp.getMask();
1522bool Keyboard::commonKeys(
unsigned unicode1,
unsigned unicode2)
const
1525 auto keyPos1 = unicodeKeymap.
get(unicode1).
pos;
1526 auto keyPos2 = unicodeKeymap.
get(unicode2).
pos;
1528 return keyPos1 == keyPos2 && keyPos1.
isValid();
1531void Keyboard::debug(
const char* format, ...)
const
1535 va_start(args, format);
1536 vfprintf(stderr, format, args);
1544Keyboard::KeyMatrixUpCmd::KeyMatrixUpCmd(
1545 CommandController& commandController_,
1546 StateChangeDistributor& stateChangeDistributor_,
1547 Scheduler& scheduler_)
1548 : RecordedCommand(commandController_, stateChangeDistributor_,
1549 scheduler_,
"keymatrixup")
1553void Keyboard::KeyMatrixUpCmd::execute(
1554 std::span<const TclObject> tokens, TclObject& , EmuTime::param )
1556 checkNumArgs(tokens, 3, Prefix{1},
"row mask");
1557 auto& keyboard =
OUTER(Keyboard, keyMatrixUpCmd);
1558 return keyboard.processCmd(getInterpreter(), tokens,
true);
1561std::string Keyboard::KeyMatrixUpCmd::help(std::span<const TclObject> )
const
1563 return "keymatrixup <row> <bitmask> release a key in the keyboard matrix\n";
1569Keyboard::KeyMatrixDownCmd::KeyMatrixDownCmd(CommandController& commandController_,
1570 StateChangeDistributor& stateChangeDistributor_,
1571 Scheduler& scheduler_)
1572 : RecordedCommand(commandController_, stateChangeDistributor_,
1573 scheduler_,
"keymatrixdown")
1577void Keyboard::KeyMatrixDownCmd::execute(std::span<const TclObject> tokens,
1578 TclObject& , EmuTime::param )
1580 checkNumArgs(tokens, 3, Prefix{1},
"row mask");
1581 auto& keyboard =
OUTER(Keyboard, keyMatrixDownCmd);
1582 return keyboard.processCmd(getInterpreter(), tokens,
false);
1585std::string Keyboard::KeyMatrixDownCmd::help(std::span<const TclObject> )
const
1587 return "keymatrixdown <row> <bitmask> press a key in the keyboard matrix\n";
1593Keyboard::MsxKeyEventQueue::MsxKeyEventQueue(
1594 Scheduler& scheduler_, Interpreter& interp_)
1600void Keyboard::MsxKeyEventQueue::process_asap(
1601 EmuTime::param time,
const Event& event)
1603 bool processImmediately = eventQueue.empty();
1604 eventQueue.push_back(event);
1605 if (processImmediately) {
1610void Keyboard::MsxKeyEventQueue::clear()
1616void Keyboard::MsxKeyEventQueue::executeUntil(EmuTime::param time)
1619 const Event&
event = eventQueue.front();
1620 auto& keyboard =
OUTER(Keyboard, msxKeyEventQueue);
1621 bool insertCodeKanaRelease = keyboard.processQueuedEvent(event, time);
1623 if (insertCodeKanaRelease) {
1627 eventQueue.emplace_front(
KeyUpEvent::create(keyboard.keyboardSettings.getCodeKanaHostKey()));
1630 if (!eventQueue.empty()) {
1631 eventQueue.pop_front();
1638 if (!eventQueue.empty()) {
1647Keyboard::KeyInserter::KeyInserter(
1648 CommandController& commandController_,
1649 StateChangeDistributor& stateChangeDistributor_,
1650 Scheduler& scheduler_)
1651 : RecordedCommand(commandController_, stateChangeDistributor_,
1652 scheduler_,
"type_via_keyboard")
1657void Keyboard::KeyInserter::execute(
1658 std::span<const TclObject> tokens, TclObject& , EmuTime::param )
1660 checkNumArgs(tokens, AtLeast{2},
"?-release? ?-freq hz? ?-cancel? text");
1662 bool cancel =
false;
1663 releaseBeforePress =
false;
1664 typingFrequency = 15;
1667 flagArg(
"-release", releaseBeforePress),
1668 valueArg(
"-freq", typingFrequency),
1670 auto arguments =
parseTclArgs(getInterpreter(), tokens.subspan(1), info);
1672 if (typingFrequency <= 0) {
1673 throw CommandException(
"Wrong argument for -freq (should be a positive number)");
1680 if (arguments.size() != 1)
throw SyntaxError();
1682 type(arguments[0].getString());
1685std::string Keyboard::KeyInserter::help(std::span<const TclObject> )
const
1687 return "Type a string in the emulated MSX.\n"
1688 "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"
1689 "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"
1690 "Use -cancel to cancel a (long) in-progress type command.";
1693void Keyboard::KeyInserter::tabCompletion(std::vector<std::string>& tokens)
const
1695 using namespace std::literals;
1696 static constexpr std::array options = {
"-release"sv,
"-freq"sv};
1697 completeString(tokens, options);
1700void Keyboard::KeyInserter::type(std::string_view str)
1705 const auto& keyboard =
OUTER(Keyboard, keyTypeCmd);
1706 oldLocksOn = keyboard.locksOn;
1707 if (text_utf8.empty()) {
1708 reschedule(getCurrentTime());
1710 text_utf8.append(str.data(), str.size());
1713void Keyboard::KeyInserter::executeUntil(EmuTime::param time)
1715 auto& keyboard =
OUTER(Keyboard, keyTypeCmd);
1716 if (lockKeysMask != 0) {
1718 keyboard.pressLockKeys(lockKeysMask,
false);
1721 keyboard.pressAscii(last,
false);
1723 if (text_utf8.empty()) {
1724 releaseLast =
false;
1725 keyboard.debug(
"Restoring locks: %02X -> %02X\n", keyboard.locksOn, oldLocksOn);
1726 uint8_t diff = oldLocksOn ^ keyboard.locksOn;
1727 lockKeysMask = diff;
1730 keyboard.locksOn ^= diff;
1731 keyboard.pressLockKeys(diff,
true);
1738 auto it =
begin(text_utf8);
1740 if (releaseLast && (releaseBeforePress || keyboard.commonKeys(last, current))) {
1744 releaseLast =
false;
1748 lockKeysMask = keyboard.pressAscii(current,
true);
1749 if (lockKeysMask == 0) {
1752 text_utf8.erase(
begin(text_utf8), it);
1753 }
else if (lockKeysMask & TRY_AGAIN) {
1754 lockKeysMask &= ~TRY_AGAIN;
1755 releaseLast =
false;
1756 }
else if (releaseBeforePress) {
1761 }
catch (std::exception&) {
1767void Keyboard::KeyInserter::reschedule(EmuTime::param time)
1775Keyboard::Msxcode2UnicodeCmd::Msxcode2UnicodeCmd(CommandController& commandController_)
1776 : Command(commandController_,
"msxcode2unicode")
1780void Keyboard::Msxcode2UnicodeCmd::execute(std::span<const TclObject> tokens, TclObject& result)
1782 checkNumArgs(tokens, Between{2, 3},
"msx-string ?fallback?");
1784 auto& interp = getInterpreter();
1785 const auto& keyboard =
OUTER(Keyboard, msxcode2UnicodeCmd);
1786 const auto& msxChars = keyboard.unicodeKeymap.getMsxChars();
1788 auto msx = tokens[1].getBinary();
1789 auto fallback = [&]() -> std::function<uint32_t(uint8_t)> {
1790 if (tokens.size() < 3) {
1792 return [](uint8_t) {
return uint32_t(
' '); };
1793 }
else if (
auto i = tokens[2].getOptionalInt()) {
1795 return [i = *i](uint8_t) {
return uint32_t(i); };
1800 return [&](uint8_t m) {
1801 TclObject cmd{TclObject::MakeListTag{}, tokens[2], m};
1802 return uint32_t(cmd.executeCommand(interp).getInt(interp));
1807 result = msxChars.msxToUtf8(msx, fallback);
1810std::string Keyboard::Msxcode2UnicodeCmd::help(std::span<const TclObject> )
const
1812 return "msxcode2unicode <msx-string> [<fallback>]\n"
1813 "returns a unicode string converted from an MSX-string, i.e. a string based on\n"
1814 "MSX character codes.\n"
1815 "The optional fallback used for each character that cannot be mapped for the\n"
1816 "current MSX model can be:\n"
1817 "- omitted: then space will be used as fallback character.\n"
1818 "- an integer number: then this number will be used as unicode point to be the\n"
1819 " the fallback character.\n"
1820 "- a Tcl proc, which expects one input character and must return one unicode\n"
1825Keyboard::Unicode2MsxcodeCmd::Unicode2MsxcodeCmd(CommandController& commandController_)
1826 : Command(commandController_,
"unicode2msxcode")
1830void Keyboard::Unicode2MsxcodeCmd::execute(std::span<const TclObject> tokens, TclObject& result)
1832 checkNumArgs(tokens, Between{2, 3},
"unicode-string ?fallback?");
1834 auto& interp = getInterpreter();
1835 const auto& keyboard =
OUTER(Keyboard, unicode2MsxcodeCmd);
1836 const auto& msxChars = keyboard.unicodeKeymap.getMsxChars();
1838 const auto& unicode = tokens[1].getString();
1839 auto fallback = [&]() -> std::function<uint8_t(uint32_t)> {
1840 if (tokens.size() < 3) {
1842 return [](uint32_t) {
return uint8_t(
' '); };
1843 }
else if (
auto i = tokens[2].getOptionalInt()) {
1845 return [i = *i](uint32_t) {
return uint8_t(i); };
1850 return [&](uint32_t u) {
1851 TclObject cmd{TclObject::MakeListTag{}, tokens[2], u};
1852 return uint8_t(cmd.executeCommand(interp).getInt(interp));
1857 result = msxChars.utf8ToMsx(unicode, fallback);
1860std::string Keyboard::Unicode2MsxcodeCmd::help(std::span<const TclObject> )
const
1862 return "unicode2msxcode <unicode-string> [<fallback>]\n"
1863 "returns an MSX string, i.e. a string based on MSX character codes, converted\n"
1864 "from a unicode string.\n"
1865 "The optional fallback used for each character that cannot be mapped for the\n"
1866 "current MSX model can be:\n"
1867 "- omitted: then space will be used as fallback character.\n"
1868 "- an integer number: then this number will be used as MSX character number to\n"
1869 " to be the fallback character.\n"
1870 "- a Tcl proc, which expects one input character and must return one MSX\n"
1871 " character number.";
1887Keyboard::CapsLockAligner::CapsLockAligner(
1888 EventDistributor& eventDistributor_,
1889 Scheduler& scheduler_)
1891 , eventDistributor(eventDistributor_)
1894 eventDistributor.registerEventListener(type, *
this);
1898Keyboard::CapsLockAligner::~CapsLockAligner()
1901 eventDistributor.unregisterEventListener(type, *
this);
1905bool Keyboard::CapsLockAligner::signalEvent(
const Event& event)
1907 if constexpr (!SANE_CAPSLOCK_BEHAVIOR) {
1912 if (state == IDLE) {
1913 EmuTime::param time = getCurrentTime();
1915 [&](
const WindowEvent&
e) {
1916 if (
e.isMainWindow()) {
1917 const auto& evt =
e.getSdlWindowEvent();
1918 if (evt.event ==
one_of(SDL_WINDOWEVENT_FOCUS_GAINED, SDL_WINDOWEVENT_FOCUS_LOST)) {
1919 alignCapsLock(time);
1923 [&](
const BootEvent&) {
1924 state = MUST_ALIGN_CAPSLOCK;
1933void Keyboard::CapsLockAligner::executeUntil(EmuTime::param time)
1936 case MUST_ALIGN_CAPSLOCK:
1937 alignCapsLock(time);
1939 case MUST_DISTRIBUTE_KEY_RELEASE: {
1940 auto& keyboard =
OUTER(Keyboard, capsLockAligner);
1942 keyboard.msxEventDistributor.distributeEvent(event, time);
1961void Keyboard::CapsLockAligner::alignCapsLock(EmuTime::param time)
1963 bool hostCapsLockOn = ((SDL_GetModState() & KMOD_CAPS) != 0);
1964 auto& keyboard =
OUTER(Keyboard, capsLockAligner);
1966 keyboard.debug(
"Resyncing host and MSX CAPS lock\n");
1970 keyboard.msxEventDistributor.distributeEvent(event, time);
1971 keyboard.debug(
"Sending fake CAPS release\n");
1972 state = MUST_DISTRIBUTE_KEY_RELEASE;
1982Keyboard::KeybDebuggable::KeybDebuggable(MSXMotherBoard& motherBoard_)
1983 : SimpleDebuggable(motherBoard_,
"keymatrix",
"MSX Keyboard Matrix",
1984 KeyMatrixPosition::NUM_ROWS)
1988uint8_t Keyboard::KeybDebuggable::read(
unsigned address)
1990 const auto& keyboard =
OUTER(Keyboard, keybDebuggable);
1991 return keyboard.getKeys()[address];
1994void Keyboard::KeybDebuggable::write(
unsigned , uint8_t )
2000template<
typename Archive>
2001void Keyboard::KeyInserter::serialize(Archive& ar,
unsigned )
2003 ar.template serializeBase<Schedulable>(*
this);
2004 ar.serialize(
"text", text_utf8,
2006 "lockKeysMask", lockKeysMask,
2007 "releaseLast", releaseLast);
2009 bool oldCodeKanaLockOn, oldGraphLockOn, oldCapsLockOn;
2010 if constexpr (!Archive::IS_LOADER) {
2015 ar.serialize(
"oldCodeKanaLockOn", oldCodeKanaLockOn,
2016 "oldGraphLockOn", oldGraphLockOn,
2017 "oldCapsLockOn", oldCapsLockOn);
2018 if constexpr (Archive::IS_LOADER) {
2021 | (oldCapsLockOn ?
KeyInfo::CAPS_MASK : 0);
2039template<
typename Archive>
2042 ar.serialize(
"keyTypeCmd", keyTypeCmd,
2043 "cmdKeyMatrix", cmdKeyMatrix);
2044 if (ar.versionAtLeast(version, 3)) {
2045 ar.serialize(
"typeKeyMatrix", typeKeyMatrix);
2047 typeKeyMatrix = cmdKeyMatrix;
2050 bool msxCapsLockOn, msxCodeKanaLockOn, msxGraphLockOn;
2051 if constexpr (!Archive::IS_LOADER) {
2056 ar.serialize(
"msxCapsLockOn", msxCapsLockOn,
2057 "msxCodeKanaLockOn", msxCodeKanaLockOn,
2058 "msxGraphLockOn", msxGraphLockOn);
2059 if constexpr (Archive::IS_LOADER) {
2065 if (ar.versionAtLeast(version, 2)) {
2066 ar.serialize(
"userKeyMatrix", userKeyMatrix,
2067 "msxmodifiers", msxModifiers,
2068 "msxKeyEventQueue", msxKeyEventQueue);
2070 if (ar.versionAtLeast(version, 4)) {
2071 ar.serialize(
"lastUnicodeForKeycode", lastUnicodeForKeycode);
2084 if constexpr (Archive::IS_LOADER) {
2091template<
typename Archive>
2092void Keyboard::MsxKeyEventQueue::serialize(Archive& ar,
unsigned )
2094 ar.template serializeBase<Schedulable>(*
this);
2103 std::vector<std::string> eventStrs;
2104 if constexpr (!Archive::IS_LOADER) {
2106 eventQueue, [](
const auto& e) {
return toString(e); }));
2108 ar.serialize(
"eventQueue", eventStrs);
2109 if constexpr (Archive::IS_LOADER) {
2110 assert(eventQueue.empty());
2111 for (
const auto& s : eventStrs) {
2112 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)
void setFocus(bool newFocus, EmuTime::param time)
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.
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)