e8555c89 Andy McCall 1 month ago
1
#include " Keyboard.hh"
2
3
#include " CommandController.hh"
4
#include " CommandException.hh"
5
#include " DeviceConfig.hh"
6
#include " Event.hh"
7
#include " EventDistributor.hh"
8
#include " InputEventFactory.hh"
9
#include " MSXEventDistributor.hh"
10
#include " MSXMotherBoard.hh"
11
#include " ReverseManager.hh"
12
#include " SDLKey.hh"
13
#include " StateChange.hh"
14
#include " StateChangeDistributor.hh"
15
#include " TclArgParser.hh"
16
#include " TclObject.hh"
17
#include " UnicodeKeymap.hh"
18
#include " serialize.hh"
19
#include " serialize_meta.hh"
20
#include " serialize_stl.hh"
21
22
#include " enumerate.hh"
23
#include " one_of.hh"
24
#include " outer.hh"
25
#include " ranges.hh"
26
#include " stl.hh"
27
#include " unreachable.hh"
28
#include " utf8_checked.hh"
29
#include " utf8_unchecked.hh"
30
#include " xrange.hh"
31
32
#include < SDL.h>
33
34
#include < algorithm>
35
#include < array>
36
#include < cassert>
37
#include < cstdarg>
38
#include < cstdint>
39
#include < cstdio>
40
#include < functional>
41
#include < ranges>
42
#include < type_traits>
43
44
#if defined (_WIN32)
45
46
#ifndef WIN32_LEAN_AND_MEAN
47
#define WIN32_LEAN_AND_MEAN
48
#endif
49
50
#ifdef _MSC_VER
51
#pragma comment(lib, "imm32")
52
#endif
53
#include " SDL_syswm.h"
54
55
#endif
56
57
58
namespace openmsx {
59
60
61
#ifdef __APPLE__
62
63
64
65
66
67
68
69
static constexpr bool SANE_CAPSLOCK_BEHAVIOR = false ;
70
#else
71
72
73
74
static constexpr bool SANE_CAPSLOCK_BEHAVIOR = true ;
75
#endif
76
77
78
static constexpr uint8_t TRY_AGAIN = 0x80 ;
79
80
using KeyInfo = UnicodeKeymap:: KeyInfo;
81
82
static constexpr array_with_enum_index< Keyboard:: Matrix, std:: string_view> defaultKeymapForMatrix = {
83
" int" ,
84
" svi" ,
85
" cvjoy" ,
86
" sega_int" ,
87
} ;
88
89
using ModifierArray = array_with_enum_index< UnicodeKeymap:: KeyInfo:: Modifier, KeyMatrixPosition> ;
90
static constexpr array_with_enum_index< Keyboard:: Matrix, ModifierArray> modifierPosForMatrix = {
91
ModifierArray{
92
KeyMatrixPosition ( 6 , 0 ) ,
93
KeyMatrixPosition ( 6 , 1 ) ,
94
KeyMatrixPosition ( 6 , 2 ) ,
95
KeyMatrixPosition ( 6 , 3 ) ,
96
KeyMatrixPosition ( 6 , 4 ) ,
97
} ,
98
ModifierArray{
99
KeyMatrixPosition ( 6 , 0 ) ,
100
KeyMatrixPosition ( 6 , 1 ) ,
101
KeyMatrixPosition ( 6 , 2 ) ,
102
KeyMatrixPosition ( 8 , 3 ) ,
103
KeyMatrixPosition ( 6 , 3 ) ,
104
} ,
105
ModifierArray{
106
} ,
107
ModifierArray{
108
KeyMatrixPosition ( 13 , 3 ) ,
109
KeyMatrixPosition ( 13 , 2 ) ,
110
KeyMatrixPosition ( 13 , 1 ) ,
111
KeyMatrixPosition ( ) ,
112
KeyMatrixPosition ( 0 , 4 ) ,
113
} ,
114
} ;
115
116
117
118
struct MsxKeyScanMapping {
119
KeyMatrixPosition msx;
120
std:: array< SDL_Keycode, 3 > hostKeyCodes;
121
std:: array< SDL_Scancode, 3 > hostScanCodes;
122
} ;
123
124
template < typename Proj>
125
static constexpr size_t count ( std:: span< const MsxKeyScanMapping> mapping , Proj proj )
126
{
127
using Array = std:: remove_cvref_t< decltype ( std:: invoke ( proj, mapping[ 0 ] ) ) > ;
128
using Code = typename Array:: value_type;
129
130
size_t result = 0 ;
131
for ( const auto & m : mapping) {
132
for ( const auto & c : std:: invoke ( proj, m ) ) {
133
if ( c != Code{ } ) + + result;
134
}
135
}
136
return result;
137
}
138
139
template < typename GetMapping>
140
static constexpr auto extractKeyCodeMapping ( GetMapping getMapping )
141
{
142
constexpr auto mapping = getMapping ( ) ;
143
constexpr size_t N = count ( mapping, & MsxKeyScanMapping:: hostKeyCodes ) ;
144
std:: array< KeyCodeMsxMapping, N> result;
145
size_t i = 0 ;
146
for ( const auto & m : mapping) {
147
for ( const auto & k : m. hostKeyCodes ) {
148
if ( k != SDLK_UNKNOWN) {
149
result[ i+ + ] = { k, m. msx } ;
150
}
151
}
152
}
153
assert ( i == N ) ;
154
std:: ranges:: sort ( result, { } , & KeyCodeMsxMapping:: hostKeyCode ) ;
155
return result;
156
}
157
template < typename GetMapping>
158
static constexpr auto extractScanCodeMapping ( GetMapping getMapping )
159
{
160
constexpr auto mapping = getMapping ( ) ;
161
constexpr size_t N = count ( mapping, & MsxKeyScanMapping:: hostScanCodes ) ;
162
std:: array< ScanCodeMsxMapping, N> result;
163
size_t i = 0 ;
164
for ( const auto & m : mapping) {
165
for ( const auto & k : m. hostScanCodes ) {
166
if ( k != SDL_SCANCODE_UNKNOWN) {
167
result[ i+ + ] = { k, m. msx } ;
168
}
169
}
170
}
171
assert ( i == N ) ;
172
std:: ranges:: sort ( result, { } , & ScanCodeMsxMapping:: hostScanCode ) ;
173
return result;
174
}
175
176
static constexpr auto getMSXMapping ( )
177
{
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
using M = MsxKeyScanMapping;
196
using P = KeyMatrixPosition;
197
using K = std:: array< SDL_Keycode, 3 > ;
198
using S = std:: array< SDL_Scancode, 3 > ;
199
std:: array mapping = {
200
M{ P{ 0x00 } , K{ SDLK_0} , S{ SDL_SCANCODE_0} } ,
201
M{ P{ 0x01 } , K{ SDLK_1} , S{ SDL_SCANCODE_1} } ,
202
M{ P{ 0x02 } , K{ SDLK_2} , S{ SDL_SCANCODE_2} } ,
203
M{ P{ 0x03 } , K{ SDLK_3} , S{ SDL_SCANCODE_3} } ,
204
M{ P{ 0x04 } , K{ SDLK_4} , S{ SDL_SCANCODE_4} } ,
205
M{ P{ 0x05 } , K{ SDLK_5} , S{ SDL_SCANCODE_5} } ,
206
M{ P{ 0x06 } , K{ SDLK_6} , S{ SDL_SCANCODE_6} } ,
207
M{ P{ 0x07 } , K{ SDLK_7} , S{ SDL_SCANCODE_7} } ,
208
209
M{ P{ 0x10 } , K{ SDLK_8} , S{ SDL_SCANCODE_8} } ,
210
M{ P{ 0x11 } , K{ SDLK_9} , S{ SDL_SCANCODE_9} } ,
211
M{ P{ 0x12 } , K{ SDLK_MINUS} , S{ SDL_SCANCODE_MINUS} } ,
212
M{ P{ 0x13 } , K{ SDLK_EQUALS} , S{ SDL_SCANCODE_EQUALS} } ,
213
M{ P{ 0x14 } , K{ SDLK_BACKSLASH} , S{ SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_INTERNATIONAL3} } ,
214
M{ P{ 0x15 } , K{ SDLK_LEFTBRACKET} , S{ SDL_SCANCODE_LEFTBRACKET} } ,
215
M{ P{ 0x16 } , K{ SDLK_RIGHTBRACKET} , S{ SDL_SCANCODE_RIGHTBRACKET} } ,
216
M{ P{ 0x17 } , K{ SDLK_SEMICOLON} , S{ SDL_SCANCODE_SEMICOLON} } ,
217
218
M{ P{ 0x20 } , K{ SDLK_QUOTE} , S{ SDL_SCANCODE_APOSTROPHE} } ,
219
M{ P{ 0x21 } , K{ SDLK_BACKQUOTE} , S{ SDL_SCANCODE_GRAVE} } ,
220
M{ P{ 0x22 } , K{ SDLK_COMMA} , S{ SDL_SCANCODE_COMMA} } ,
221
M{ P{ 0x23 } , K{ SDLK_PERIOD} , S{ SDL_SCANCODE_PERIOD} } ,
222
M{ P{ 0x24 } , K{ SDLK_SLASH} , S{ SDL_SCANCODE_SLASH} } ,
223
M{ P{ 0x25 } , K{ SDLK_RCTRL} , S{ SDL_SCANCODE_RCTRL, SDL_SCANCODE_NONUSBACKSLASH} } ,
224
M{ P{ 0x26 } , K{ SDLK_a} , S{ SDL_SCANCODE_A} } ,
225
M{ P{ 0x27 } , K{ SDLK_b} , S{ SDL_SCANCODE_B} } ,
226
227
M{ P{ 0x30 } , K{ SDLK_c} , S{ SDL_SCANCODE_C} } ,
228
M{ P{ 0x31 } , K{ SDLK_d} , S{ SDL_SCANCODE_D} } ,
229
M{ P{ 0x32 } , K{ SDLK_e} , S{ SDL_SCANCODE_E} } ,
230
M{ P{ 0x33 } , K{ SDLK_f} , S{ SDL_SCANCODE_F} } ,
231
M{ P{ 0x34 } , K{ SDLK_g} , S{ SDL_SCANCODE_G} } ,
232
M{ P{ 0x35 } , K{ SDLK_h} , S{ SDL_SCANCODE_H} } ,
233
M{ P{ 0x36 } , K{ SDLK_i} , S{ SDL_SCANCODE_I} } ,
234
M{ P{ 0x37 } , K{ SDLK_j} , S{ SDL_SCANCODE_J} } ,
235
236
M{ P{ 0x40 } , K{ SDLK_k} , S{ SDL_SCANCODE_K} } ,
237
M{ P{ 0x41 } , K{ SDLK_l} , S{ SDL_SCANCODE_L} } ,
238
M{ P{ 0x42 } , K{ SDLK_m} , S{ SDL_SCANCODE_M} } ,
239
M{ P{ 0x43 } , K{ SDLK_n} , S{ SDL_SCANCODE_N} } ,
240
M{ P{ 0x44 } , K{ SDLK_o} , S{ SDL_SCANCODE_O} } ,
241
M{ P{ 0x45 } , K{ SDLK_p} , S{ SDL_SCANCODE_P} } ,
242
M{ P{ 0x46 } , K{ SDLK_q} , S{ SDL_SCANCODE_Q} } ,
243
M{ P{ 0x47 } , K{ SDLK_r} , S{ SDL_SCANCODE_R} } ,
244
245
M{ P{ 0x50 } , K{ SDLK_s} , S{ SDL_SCANCODE_S} } ,
246
M{ P{ 0x51 } , K{ SDLK_t} , S{ SDL_SCANCODE_T} } ,
247
M{ P{ 0x52 } , K{ SDLK_u} , S{ SDL_SCANCODE_U} } ,
248
M{ P{ 0x53 } , K{ SDLK_v} , S{ SDL_SCANCODE_V} } ,
249
M{ P{ 0x54 } , K{ SDLK_w} , S{ SDL_SCANCODE_W} } ,
250
M{ P{ 0x55 } , K{ SDLK_x} , S{ SDL_SCANCODE_X} } ,
251
M{ P{ 0x56 } , K{ SDLK_y} , S{ SDL_SCANCODE_Y} } ,
252
M{ P{ 0x57 } , K{ SDLK_z} , S{ SDL_SCANCODE_Z} } ,
253
254
M{ P{ 0x60 } , K{ SDLK_LSHIFT, SDLK_RSHIFT} , S{ SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT} } ,
255
M{ P{ 0x61 } , K{ SDLK_LCTRL} , S{ SDL_SCANCODE_LCTRL} } ,
256
M{ P{ 0x62 } , K{ SDLK_LALT} , S{ SDL_SCANCODE_LALT} } ,
257
M{ P{ 0x63 } , K{ SDLK_CAPSLOCK} , S{ SDL_SCANCODE_CAPSLOCK} } ,
258
M{ P{ 0x64 } , K{ SDLK_RALT} , S{ SDL_SCANCODE_RALT} } ,
259
M{ P{ 0x65 } , K{ SDLK_F1} , S{ SDL_SCANCODE_F1} } ,
260
M{ P{ 0x66 } , K{ SDLK_F2} , S{ SDL_SCANCODE_F2} } ,
261
M{ P{ 0x67 } , K{ SDLK_F3} , S{ SDL_SCANCODE_F3} } ,
262
263
M{ P{ 0x70 } , K{ SDLK_F4} , S{ SDL_SCANCODE_F4} } ,
264
M{ P{ 0x71 } , K{ SDLK_F5} , S{ SDL_SCANCODE_F5} } ,
265
M{ P{ 0x72 } , K{ SDLK_ESCAPE} , S{ SDL_SCANCODE_ESCAPE} } ,
266
M{ P{ 0x73 } , K{ SDLK_TAB} , S{ SDL_SCANCODE_TAB} } ,
267
M{ P{ 0x74 } , K{ SDLK_F8} , S{ SDL_SCANCODE_F8} } ,
268
M{ P{ 0x75 } , K{ SDLK_BACKSPACE} , S{ SDL_SCANCODE_BACKSPACE} } ,
269
M{ P{ 0x76 } , K{ SDLK_F7} , S{ SDL_SCANCODE_F7} } ,
270
M{ P{ 0x77 } , K{ SDLK_RETURN, SDLK_KP_ENTER} ,
271
S{ SDL_SCANCODE_RETURN, SDL_SCANCODE_KP_ENTER} } ,
272
273
M{ P{ 0x80 } , K{ SDLK_SPACE} , S{ SDL_SCANCODE_SPACE} } ,
274
M{ P{ 0x81 } , K{ SDLK_HOME} , S{ SDL_SCANCODE_HOME} } ,
275
M{ P{ 0x82 } , K{ SDLK_INSERT} , S{ SDL_SCANCODE_INSERT} } ,
276
M{ P{ 0x83 } , K{ SDLK_DELETE} , S{ SDL_SCANCODE_DELETE} } ,
277
M{ P{ 0x84 } , K{ SDLK_LEFT} , S{ SDL_SCANCODE_LEFT} } ,
278
M{ P{ 0x85 } , K{ SDLK_UP} , S{ SDL_SCANCODE_UP} } ,
279
M{ P{ 0x86 } , K{ SDLK_DOWN} , S{ SDL_SCANCODE_DOWN} } ,
280
M{ P{ 0x87 } , K{ SDLK_RIGHT} , S{ SDL_SCANCODE_RIGHT} } ,
281
282
M{ P{ 0x90 } , K{ SDLK_KP_MULTIPLY} , S{ SDL_SCANCODE_KP_MULTIPLY} } ,
283
M{ P{ 0x91 } , K{ SDLK_KP_PLUS} , S{ SDL_SCANCODE_KP_PLUS} } ,
284
M{ P{ 0x92 } , K{ SDLK_KP_DIVIDE} , S{ SDL_SCANCODE_KP_DIVIDE} } ,
285
M{ P{ 0x93 } , K{ SDLK_KP_0} , S{ SDL_SCANCODE_KP_0} } ,
286
M{ P{ 0x94 } , K{ SDLK_KP_1} , S{ SDL_SCANCODE_KP_1} } ,
287
M{ P{ 0x95 } , K{ SDLK_KP_2} , S{ SDL_SCANCODE_KP_2} } ,
288
M{ P{ 0x96 } , K{ SDLK_KP_3} , S{ SDL_SCANCODE_KP_3} } ,
289
M{ P{ 0x97 } , K{ SDLK_KP_4} , S{ SDL_SCANCODE_KP_4} } ,
290
291
M{ P{ 0xA0 } , K{ SDLK_KP_5} , S{ SDL_SCANCODE_KP_5} } ,
292
M{ P{ 0xA1 } , K{ SDLK_KP_6} , S{ SDL_SCANCODE_KP_6} } ,
293
M{ P{ 0xA2 } , K{ SDLK_KP_7} , S{ SDL_SCANCODE_KP_7} } ,
294
M{ P{ 0xA3 } , K{ SDLK_KP_8} , S{ SDL_SCANCODE_KP_8} } ,
295
M{ P{ 0xA4 } , K{ SDLK_KP_9} , S{ SDL_SCANCODE_KP_9} } ,
296
M{ P{ 0xA5 } , K{ SDLK_KP_MINUS} , S{ SDL_SCANCODE_KP_MINUS} } ,
297
M{ P{ 0xA6 } , K{ SDLK_KP_COMMA} , S{ SDL_SCANCODE_KP_COMMA} } ,
298
M{ P{ 0xA7 } , K{ SDLK_KP_PERIOD} , S{ SDL_SCANCODE_KP_PERIOD} } ,
299
300
M{ P{ 0xB1 } , K{ SDLK_RGUI} , S{ SDL_SCANCODE_RGUI} } ,
301
M{ P{ 0xB3 } , K{ SDLK_LGUI} , S{ SDL_SCANCODE_LGUI} } ,
302
} ;
303
return mapping;
304
}
305
306
static constexpr auto getSVIMapping ( )
307
{
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
using M = MsxKeyScanMapping;
325
using P = KeyMatrixPosition;
326
using K = std:: array< SDL_Keycode, 3 > ;
327
using S = std:: array< SDL_Scancode, 3 > ;
328
std:: array mapping = {
329
M{ P{ 0x00 } , K{ SDLK_0} , S{ SDL_SCANCODE_0} } ,
330
M{ P{ 0x01 } , K{ SDLK_1} , S{ SDL_SCANCODE_1} } ,
331
M{ P{ 0x02 } , K{ SDLK_2} , S{ SDL_SCANCODE_2} } ,
332
M{ P{ 0x03 } , K{ SDLK_3} , S{ SDL_SCANCODE_3} } ,
333
M{ P{ 0x04 } , K{ SDLK_4} , S{ SDL_SCANCODE_4} } ,
334
M{ P{ 0x05 } , K{ SDLK_5} , S{ SDL_SCANCODE_5} } ,
335
M{ P{ 0x06 } , K{ SDLK_6} , S{ SDL_SCANCODE_6} } ,
336
M{ P{ 0x07 } , K{ SDLK_7} , S{ SDL_SCANCODE_7} } ,
337
338
M{ P{ 0x10 } , K{ SDLK_8} , S{ SDL_SCANCODE_8} } ,
339
M{ P{ 0x11 } , K{ SDLK_9} , S{ SDL_SCANCODE_9} } ,
340
M{ P{ 0x12 } , K{ SDLK_SEMICOLON} , S{ SDL_SCANCODE_SEMICOLON} } ,
341
M{ P{ 0x13 } , K{ SDLK_QUOTE} , S{ SDL_SCANCODE_APOSTROPHE} } ,
342
M{ P{ 0x14 } , K{ SDLK_COMMA} , S{ SDL_SCANCODE_COMMA} } ,
343
M{ P{ 0x15 } , K{ SDLK_EQUALS} , S{ SDL_SCANCODE_EQUALS} } ,
344
M{ P{ 0x16 } , K{ SDLK_PERIOD} , S{ SDL_SCANCODE_PERIOD} } ,
345
M{ P{ 0x17 } , K{ SDLK_SLASH} , S{ SDL_SCANCODE_SLASH} } ,
346
347
M{ P{ 0x20 } , K{ SDLK_MINUS} , S{ SDL_SCANCODE_MINUS} } ,
348
M{ P{ 0x21 } , K{ SDLK_a} , S{ SDL_SCANCODE_A} } ,
349
M{ P{ 0x22 } , K{ SDLK_b} , S{ SDL_SCANCODE_B} } ,
350
M{ P{ 0x23 } , K{ SDLK_c} , S{ SDL_SCANCODE_C} } ,
351
M{ P{ 0x24 } , K{ SDLK_d} , S{ SDL_SCANCODE_D} } ,
352
M{ P{ 0x25 } , K{ SDLK_e} , S{ SDL_SCANCODE_E} } ,
353
M{ P{ 0x26 } , K{ SDLK_f} , S{ SDL_SCANCODE_F} } ,
354
M{ P{ 0x27 } , K{ SDLK_g} , S{ SDL_SCANCODE_G} } ,
355
356
M{ P{ 0x30 } , K{ SDLK_h} , S{ SDL_SCANCODE_H} } ,
357
M{ P{ 0x31 } , K{ SDLK_i} , S{ SDL_SCANCODE_I} } ,
358
M{ P{ 0x32 } , K{ SDLK_j} , S{ SDL_SCANCODE_J} } ,
359
M{ P{ 0x33 } , K{ SDLK_k} , S{ SDL_SCANCODE_K} } ,
360
M{ P{ 0x34 } , K{ SDLK_l} , S{ SDL_SCANCODE_L} } ,
361
M{ P{ 0x35 } , K{ SDLK_m} , S{ SDL_SCANCODE_M} } ,
362
M{ P{ 0x36 } , K{ SDLK_n} , S{ SDL_SCANCODE_N} } ,
363
M{ P{ 0x37 } , K{ SDLK_o} , S{ SDL_SCANCODE_O} } ,
364
365
M{ P{ 0x40 } , K{ SDLK_p} , S{ SDL_SCANCODE_P} } ,
366
M{ P{ 0x41 } , K{ SDLK_q} , S{ SDL_SCANCODE_Q} } ,
367
M{ P{ 0x42 } , K{ SDLK_r} , S{ SDL_SCANCODE_R} } ,
368
M{ P{ 0x43 } , K{ SDLK_s} , S{ SDL_SCANCODE_S} } ,
369
M{ P{ 0x44 } , K{ SDLK_t} , S{ SDL_SCANCODE_T} } ,
370
M{ P{ 0x45 } , K{ SDLK_u} , S{ SDL_SCANCODE_U} } ,
371
M{ P{ 0x46 } , K{ SDLK_v} , S{ SDL_SCANCODE_V} } ,
372
M{ P{ 0x47 } , K{ SDLK_w} , S{ SDL_SCANCODE_W} } ,
373
374
M{ P{ 0x50 } , K{ SDLK_x} , S{ SDL_SCANCODE_X} } ,
375
M{ P{ 0x51 } , K{ SDLK_y} , S{ SDL_SCANCODE_Y} } ,
376
M{ P{ 0x52 } , K{ SDLK_z} , S{ SDL_SCANCODE_Z} } ,
377
M{ P{ 0x53 } , K{ SDLK_LEFTBRACKET} , S{ SDL_SCANCODE_LEFTBRACKET} } ,
378
M{ P{ 0x54 } , K{ SDLK_BACKSLASH} , S{ SDL_SCANCODE_BACKSLASH} } ,
379
M{ P{ 0x55 } , K{ SDLK_RIGHTBRACKET} , S{ SDL_SCANCODE_RIGHTBRACKET} } ,
380
M{ P{ 0x56 } , K{ SDLK_BACKSPACE} , S{ SDL_SCANCODE_BACKSPACE} } ,
381
M{ P{ 0x57 } , K{ SDLK_UP} , S{ SDL_SCANCODE_UP} } ,
382
383
M{ P{ 0x60 } , K{ SDLK_LSHIFT, SDLK_RSHIFT} , S{ SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT} } ,
384
M{ P{ 0x61 } , K{ SDLK_LCTRL} , S{ SDL_SCANCODE_LCTRL} } ,
385
M{ P{ 0x62 } , K{ SDLK_LALT} , S{ SDL_SCANCODE_LALT} } ,
386
M{ P{ 0x63 } , K{ SDLK_RALT} , S{ SDL_SCANCODE_RALT} } ,
387
M{ P{ 0x64 } , K{ SDLK_ESCAPE} , S{ SDL_SCANCODE_ESCAPE} } ,
388
M{ P{ 0x65 } , K{ SDLK_F8} , S{ SDL_SCANCODE_F8} } ,
389
M{ P{ 0x66 } , K{ SDLK_RETURN, SDLK_KP_ENTER} ,
390
S{ SDL_SCANCODE_RETURN, SDL_SCANCODE_KP_ENTER} } ,
391
M{ P{ 0x67 } , K{ SDLK_LEFT} , S{ SDL_SCANCODE_LEFT} } ,
392
393
M{ P{ 0x70 } , K{ SDLK_F1} , S{ SDL_SCANCODE_F1} } ,
394
M{ P{ 0x71 } , K{ SDLK_F2} , S{ SDL_SCANCODE_F2} } ,
395
M{ P{ 0x72 } , K{ SDLK_F3} , S{ SDL_SCANCODE_F3} } ,
396
M{ P{ 0x73 } , K{ SDLK_F4} , S{ SDL_SCANCODE_F4} } ,
397
M{ P{ 0x74 } , K{ SDLK_F5} , S{ SDL_SCANCODE_F5} } ,
398
M{ P{ 0x75 } , K{ SDLK_F6} , S{ SDL_SCANCODE_F6} } ,
399
M{ P{ 0x76 } , K{ SDLK_INSERT} , S{ SDL_SCANCODE_INSERT} } ,
400
M{ P{ 0x77 } , K{ SDLK_DOWN} , S{ SDL_SCANCODE_DOWN} } ,
401
402
M{ P{ 0x80 } , K{ SDLK_SPACE} , S{ SDL_SCANCODE_SPACE} } ,
403
M{ P{ 0x81 } , K{ SDLK_TAB} , S{ SDL_SCANCODE_TAB} } ,
404
M{ P{ 0x82 } , K{ SDLK_DELETE} , S{ SDL_SCANCODE_DELETE} } ,
405
M{ P{ 0x83 } , K{ SDLK_CAPSLOCK} , S{ SDL_SCANCODE_CAPSLOCK} } ,
406
M{ P{ 0x84 } , K{ SDLK_F7} , S{ SDL_SCANCODE_F7} } ,
407
M{ P{ 0x85 } , K{ SDLK_PRINTSCREEN} , S{ SDL_SCANCODE_PRINTSCREEN} } ,
408
409
M{ P{ 0x87 } , K{ SDLK_RIGHT} , S{ SDL_SCANCODE_RIGHT} } ,
410
411
M{ P{ 0x90 } , K{ SDLK_KP_0} , S{ SDL_SCANCODE_KP_0} } ,
412
M{ P{ 0x91 } , K{ SDLK_KP_1} , S{ SDL_SCANCODE_KP_1} } ,
413
M{ P{ 0x92 } , K{ SDLK_KP_2} , S{ SDL_SCANCODE_KP_2} } ,
414
M{ P{ 0x93 } , K{ SDLK_KP_3} , S{ SDL_SCANCODE_KP_3} } ,
415
M{ P{ 0x94 } , K{ SDLK_KP_4} , S{ SDL_SCANCODE_KP_4} } ,
416
M{ P{ 0x95 } , K{ SDLK_KP_5} , S{ SDL_SCANCODE_KP_5} } ,
417
M{ P{ 0x96 } , K{ SDLK_KP_6} , S{ SDL_SCANCODE_KP_6} } ,
418
M{ P{ 0x97 } , K{ SDLK_KP_7} , S{ SDL_SCANCODE_KP_7} } ,
419
420
M{ P{ 0xA0 } , K{ SDLK_KP_8} , S{ SDL_SCANCODE_KP_8} } ,
421
M{ P{ 0xA1 } , K{ SDLK_KP_9} , S{ SDL_SCANCODE_KP_9} } ,
422
M{ P{ 0xA2 } , K{ SDLK_KP_PLUS} , S{ SDL_SCANCODE_KP_PLUS} } ,
423
M{ P{ 0xA3 } , K{ SDLK_KP_MINUS} , S{ SDL_SCANCODE_KP_MINUS} } ,
424
M{ P{ 0xA4 } , K{ SDLK_KP_MULTIPLY} , S{ SDL_SCANCODE_KP_MULTIPLY} } ,
425
M{ P{ 0xA5 } , K{ SDLK_KP_DIVIDE} , S{ SDL_SCANCODE_KP_DIVIDE} } ,
426
M{ P{ 0xA6 } , K{ SDLK_KP_PERIOD} , S{ SDL_SCANCODE_KP_PERIOD} } ,
427
M{ P{ 0xA7 } , K{ SDLK_KP_COMMA} , S{ SDL_SCANCODE_KP_COMMA} } ,
428
} ;
429
return mapping;
430
}
431
432
static constexpr auto getCvJoyMapping ( )
433
{
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
using M = MsxKeyScanMapping;
450
using P = KeyMatrixPosition;
451
using K = std:: array< SDL_Keycode, 3 > ;
452
using S = std:: array< SDL_Scancode, 3 > ;
453
std:: array mapping = {
454
M{ P{ 0x00 } , K{ SDLK_UP} , S{ SDL_SCANCODE_UP} } ,
455
M{ P{ 0x01 } , K{ SDLK_RIGHT} , S{ SDL_SCANCODE_RIGHT} } ,
456
M{ P{ 0x02 } , K{ SDLK_DOWN} , S{ SDL_SCANCODE_DOWN} } ,
457
M{ P{ 0x03 } , K{ SDLK_LEFT} , S{ SDL_SCANCODE_LEFT} } ,
458
M{ P{ 0x06 } , K{ SDLK_SPACE, SDLK_RCTRL} ,
459
S{ SDL_SCANCODE_SPACE, SDL_SCANCODE_RCTRL} } ,
460
M{ P{ 0x07 } , K{ SDLK_RSHIFT, SDLK_RALT, SDLK_LALT} ,
461
S{ SDL_SCANCODE_RSHIFT, SDL_SCANCODE_RALT, SDL_SCANCODE_LALT} } ,
462
463
M{ P{ 0x10 } , K{ SDLK_w} , S{ SDL_SCANCODE_W} } ,
464
M{ P{ 0x11 } , K{ SDLK_d} , S{ SDL_SCANCODE_D} } ,
465
M{ P{ 0x12 } , K{ SDLK_s} , S{ SDL_SCANCODE_S} } ,
466
M{ P{ 0x13 } , K{ SDLK_a} , S{ SDL_SCANCODE_A} } ,
467
M{ P{ 0x16 } , K{ SDLK_LCTRL} , S{ SDL_SCANCODE_LCTRL} } ,
468
M{ P{ 0x17 } , K{ SDLK_LSHIFT} , S{ SDL_SCANCODE_LSHIFT} } ,
469
470
M{ P{ 0x20 } , K{ SDLK_0, SDLK_KP_0} , S{ SDL_SCANCODE_0} } ,
471
M{ P{ 0x21 } , K{ SDLK_1, SDLK_KP_1} , S{ SDL_SCANCODE_1} } ,
472
M{ P{ 0x22 } , K{ SDLK_2, SDLK_KP_2} , S{ SDL_SCANCODE_2} } ,
473
M{ P{ 0x23 } , K{ SDLK_3, SDLK_KP_3} , S{ SDL_SCANCODE_3} } ,
474
M{ P{ 0x24 } , K{ SDLK_4, SDLK_KP_4} , S{ SDL_SCANCODE_4} } ,
475
M{ P{ 0x25 } , K{ SDLK_5, SDLK_KP_5} , S{ SDL_SCANCODE_5} } ,
476
M{ P{ 0x26 } , K{ SDLK_6, SDLK_KP_6} , S{ SDL_SCANCODE_6} } ,
477
M{ P{ 0x27 } , K{ SDLK_7, SDLK_KP_7} , S{ SDL_SCANCODE_7} } ,
478
479
M{ P{ 0x30 } , K{ SDLK_8, SDLK_KP_8} , S{ SDL_SCANCODE_8} } ,
480
M{ P{ 0x31 } , K{ SDLK_9, SDLK_KP_9} , S{ SDL_SCANCODE_9} } ,
481
M{ P{ 0x32 } , K{ SDLK_MINUS, SDLK_KP_MULTIPLY, SDLK_KP_MINUS} ,
482
S{ SDL_SCANCODE_MINUS, SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_KP_MINUS} } ,
483
M{ P{ 0x33 } , K{ SDLK_EQUALS, SDLK_KP_DIVIDE, SDLK_KP_PLUS} ,
484
S{ SDL_SCANCODE_EQUALS, SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_KP_PLUS} } ,
485
486
M{ P{ 0x40 } , K{ SDLK_u} , S{ SDL_SCANCODE_U} } ,
487
M{ P{ 0x41 } , K{ SDLK_v} , S{ SDL_SCANCODE_V} } ,
488
M{ P{ 0x42 } , K{ SDLK_b} , S{ SDL_SCANCODE_B} } ,
489
M{ P{ 0x43 } , K{ SDLK_n} , S{ SDL_SCANCODE_N} } ,
490
M{ P{ 0x44 } , K{ SDLK_f} , S{ SDL_SCANCODE_F} } ,
491
M{ P{ 0x45 } , K{ SDLK_g} , S{ SDL_SCANCODE_G} } ,
492
M{ P{ 0x46 } , K{ SDLK_h} , S{ SDL_SCANCODE_H} } ,
493
M{ P{ 0x47 } , K{ SDLK_r} , S{ SDL_SCANCODE_R} } ,
494
495
M{ P{ 0x50 } , K{ SDLK_t} , S{ SDL_SCANCODE_T} } ,
496
M{ P{ 0x51 } , K{ SDLK_y} , S{ SDL_SCANCODE_Y} } ,
497
M{ P{ 0x52 } , K{ SDLK_j} , S{ SDL_SCANCODE_J} } ,
498
M{ P{ 0x53 } , K{ SDLK_m} , S{ SDL_SCANCODE_M} } ,
499
} ;
500
return mapping;
501
}
502
503
static constexpr auto getSegaMapping ( )
504
{
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
using M = MsxKeyScanMapping;
529
using P = KeyMatrixPosition;
530
using K = std:: array< SDL_Keycode, 3 > ;
531
using S = std:: array< SDL_Scancode, 3 > ;
532
std:: array mapping = {
533
M{ P{ 0x00 } , K{ SDLK_1, SDLK_KP_1} ,
534
S{ SDL_SCANCODE_1, SDL_SCANCODE_KP_1} } ,
535
M{ P{ 0x01 } , K{ SDLK_q} , S{ SDL_SCANCODE_Q} } ,
536
M{ P{ 0x02 } , K{ SDLK_a} , S{ SDL_SCANCODE_A} } ,
537
M{ P{ 0x03 } , K{ SDLK_z} , S{ SDL_SCANCODE_Z} } ,
538
M{ P{ 0x04 } , K{ SDLK_RALT} , S{ SDL_SCANCODE_RALT} } ,
539
M{ P{ 0x05 } , K{ SDLK_COMMA} , S{ SDL_SCANCODE_COMMA} } ,
540
M{ P{ 0x06 } , K{ SDLK_k} , S{ SDL_SCANCODE_K} } ,
541
M{ P{ 0x07 } , K{ SDLK_i} , S{ SDL_SCANCODE_I} } ,
542
543
M{ P{ 0x10 } , K{ SDLK_2, SDLK_KP_2} ,
544
S{ SDL_SCANCODE_2, SDL_SCANCODE_KP_2} } ,
545
M{ P{ 0x11 } , K{ SDLK_w} , S{ SDL_SCANCODE_W} } ,
546
M{ P{ 0x12 } , K{ SDLK_s} , S{ SDL_SCANCODE_S} } ,
547
M{ P{ 0x13 } , K{ SDLK_x} , S{ SDL_SCANCODE_X} } ,
548
M{ P{ 0x14 } , K{ SDLK_SPACE} , S{ SDL_SCANCODE_SPACE} } ,
549
M{ P{ 0x15 } , K{ SDLK_PERIOD, SDLK_KP_PERIOD} ,
550
S{ SDL_SCANCODE_PERIOD, SDL_SCANCODE_KP_PERIOD} } ,
551
M{ P{ 0x16 } , K{ SDLK_l} , S{ SDL_SCANCODE_L} } ,
552
M{ P{ 0x17 } , K{ SDLK_o} , S{ SDL_SCANCODE_O} } ,
553
554
M{ P{ 0x20 } , K{ SDLK_3, SDLK_KP_3} ,
555
S{ SDL_SCANCODE_3, SDL_SCANCODE_KP_3} } ,
556
M{ P{ 0x21 } , K{ SDLK_e} , S{ SDL_SCANCODE_E} } ,
557
M{ P{ 0x22 } , K{ SDLK_d} , S{ SDL_SCANCODE_D} } ,
558
M{ P{ 0x23 } , K{ SDLK_c} , S{ SDL_SCANCODE_C} } ,
559
M{ P{ 0x24 } , K{ SDLK_HOME} , S{ SDL_SCANCODE_HOME} } ,
560
M{ P{ 0x25 } , K{ SDLK_SLASH, SDLK_KP_DIVIDE} ,
561
S{ SDL_SCANCODE_SLASH, SDL_SCANCODE_KP_DIVIDE} } ,
562
M{ P{ 0x26 } , K{ SDLK_SEMICOLON, SDLK_KP_PLUS} ,
563
S{ SDL_SCANCODE_SEMICOLON, SDL_SCANCODE_KP_PLUS} } ,
564
M{ P{ 0x27 } , K{ SDLK_p} , S{ SDL_SCANCODE_P} } ,
565
566
M{ P{ 0x30 } , K{ SDLK_4, SDLK_KP_4} ,
567
S{ SDL_SCANCODE_4, SDL_SCANCODE_KP_4} } ,
568
M{ P{ 0x31 } , K{ SDLK_r} , S{ SDL_SCANCODE_R} } ,
569
M{ P{ 0x32 } , K{ SDLK_f} , S{ SDL_SCANCODE_F} } ,
570
M{ P{ 0x33 } , K{ SDLK_v} , S{ SDL_SCANCODE_V} } ,
571
M{ P{ 0x34 } , K{ SDLK_INSERT} , S{ SDL_SCANCODE_INSERT} } ,
572
M{ P{ 0x35 } , K{ SDLK_F7} , S{ SDL_SCANCODE_F7} } ,
573
M{ P{ 0x36 } , K{ SDLK_QUOTE, SDLK_KP_MULTIPLY} ,
574
S{ SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_KP_MULTIPLY} } ,
575
M{ P{ 0x37 } , K{ SDLK_BACKQUOTE} , S{ SDL_SCANCODE_GRAVE} } ,
576
577
M{ P{ 0x40 } , K{ SDLK_5, SDLK_KP_5} ,
578
S{ SDL_SCANCODE_5, SDL_SCANCODE_KP_5} } ,
579
M{ P{ 0x41 } , K{ SDLK_t} , S{ SDL_SCANCODE_T} } ,
580
M{ P{ 0x42 } , K{ SDLK_g} , S{ SDL_SCANCODE_G} } ,
581
M{ P{ 0x43 } , K{ SDLK_b} , S{ SDL_SCANCODE_B} } ,
582
583
M{ P{ 0x45 } , K{ SDLK_DOWN} , S{ SDL_SCANCODE_DOWN} } ,
584
M{ P{ 0x46 } , K{ SDLK_RIGHTBRACKET} , S{ SDL_SCANCODE_RIGHTBRACKET} } ,
585
M{ P{ 0x47 } , K{ SDLK_LEFTBRACKET} , S{ SDL_SCANCODE_LEFTBRACKET} } ,
586
587
M{ P{ 0x50 } , K{ SDLK_6, SDLK_KP_6} ,
588
S{ SDL_SCANCODE_6, SDL_SCANCODE_KP_6} } ,
589
M{ P{ 0x51 } , K{ SDLK_y} , S{ SDL_SCANCODE_Y} } ,
590
M{ P{ 0x52 } , K{ SDLK_h} , S{ SDL_SCANCODE_H} } ,
591
M{ P{ 0x53 } , K{ SDLK_n} , S{ SDL_SCANCODE_N} } ,
592
593
M{ P{ 0x55 } , K{ SDLK_LEFT} , S{ SDL_SCANCODE_LEFT} } ,
594
M{ P{ 0x56 } , K{ SDLK_RETURN, SDLK_KP_ENTER} ,
595
S{ SDL_SCANCODE_RETURN, SDL_SCANCODE_KP_ENTER} } ,
596
597
598
M{ P{ 0x60 } , K{ SDLK_7, SDLK_KP_7} ,
599
S{ SDL_SCANCODE_7, SDL_SCANCODE_KP_7} } ,
600
M{ P{ 0x61 } , K{ SDLK_u} , S{ SDL_SCANCODE_U} } ,
601
M{ P{ 0x62 } , K{ SDLK_j} , S{ SDL_SCANCODE_J} } ,
602
M{ P{ 0x63 } , K{ SDLK_m} , S{ SDL_SCANCODE_M} } ,
603
604
M{ P{ 0x65 } , K{ SDLK_RIGHT} , S{ SDL_SCANCODE_RIGHT} } ,
605
M{ P{ 0x66 } , K{ SDLK_UP} , S{ SDL_SCANCODE_UP} } ,
606
607
608
M{ P{ 0x70 } , K{ SDLK_8, SDLK_KP_8} ,
609
S{ SDL_SCANCODE_8, SDL_SCANCODE_KP_8} } ,
610
M{ P{ 0x80 } , K{ SDLK_9, SDLK_KP_9} ,
611
S{ SDL_SCANCODE_9, SDL_SCANCODE_KP_9} } ,
612
M{ P{ 0x90 } , K{ SDLK_0, SDLK_KP_0} ,
613
S{ SDL_SCANCODE_0, SDL_SCANCODE_KP_0} } ,
614
M{ P{ 0xA0 } , K{ SDLK_MINUS, SDLK_KP_MINUS} ,
615
S{ SDL_SCANCODE_MINUS, SDL_SCANCODE_KP_MINUS} } ,
616
M{ P{ 0xB0 } , K{ SDLK_EQUALS} , S{ SDL_SCANCODE_EQUALS} } ,
617
618
M{ P{ 0xC0 } , K{ SDLK_BACKSLASH} , S{ SDL_SCANCODE_BACKSLASH} } ,
619
M{ P{ 0xC3 } , K{ SDLK_TAB} , S{ SDL_SCANCODE_TAB} } ,
620
621
M{ P{ 0xD0 } , K{ SDLK_ESCAPE, SDLK_F8} ,
622
S{ SDL_SCANCODE_ESCAPE, SDL_SCANCODE_F8} } ,
623
M{ P{ 0xD1 } , K{ SDLK_LALT} , S{ SDL_SCANCODE_LALT} } ,
624
M{ P{ 0xD2 } , K{ SDLK_LCTRL} , S{ SDL_SCANCODE_LCTRL} } ,
625
M{ P{ 0xD3 } , K{ SDLK_LSHIFT, SDLK_RSHIFT} ,
626
S{ SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT} } ,
627
} ;
628
return mapping;
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
}
652
653
static constexpr auto msxKeyCodeMapping = extractKeyCodeMapping ( [ ] { return getMSXMapping ( ) ; } ) ;
654
static constexpr auto msxScanCodeMapping = extractScanCodeMapping ( [ ] { return getMSXMapping ( ) ; } ) ;
655
static constexpr auto sviKeyCodeMapping = extractKeyCodeMapping ( [ ] { return getSVIMapping ( ) ; } ) ;
656
static constexpr auto sviScanCodeMapping = extractScanCodeMapping ( [ ] { return getSVIMapping ( ) ; } ) ;
657
static constexpr auto cvJoyKeyCodeMapping = extractKeyCodeMapping ( [ ] { return getCvJoyMapping ( ) ; } ) ;
658
static constexpr auto cvJoyScanCodeMapping = extractScanCodeMapping ( [ ] { return getCvJoyMapping ( ) ; } ) ;
659
static constexpr auto segaKeyCodeMapping = extractKeyCodeMapping ( [ ] { return getSegaMapping ( ) ; } ) ;
660
static constexpr auto segaScanCodeMapping = extractScanCodeMapping ( [ ] { return getSegaMapping ( ) ; } ) ;
661
662
static constexpr array_with_enum_index< Keyboard:: Matrix, std:: span< const KeyCodeMsxMapping> > defaultKeyCodeMappings = {
663
msxKeyCodeMapping,
664
sviKeyCodeMapping,
665
cvJoyKeyCodeMapping,
666
segaKeyCodeMapping,
667
} ;
668
static constexpr array_with_enum_index< Keyboard:: Matrix, std:: span< const ScanCodeMsxMapping> > defaultScanCodeMappings = {
669
msxScanCodeMapping,
670
sviScanCodeMapping,
671
cvJoyScanCodeMapping,
672
segaScanCodeMapping,
673
} ;
674
675
Keyboard:: Keyboard ( MSXMotherBoard& motherBoard ,
676
Scheduler& scheduler_ ,
677
CommandController& commandController_ ,
678
EventDistributor& eventDistributor_ ,
679
MSXEventDistributor& msxEventDistributor_ ,
680
StateChangeDistributor& stateChangeDistributor_ ,
681
Matrix matrix ,
682
const DeviceConfig& config )
683
: Schedulable ( scheduler_ )
684
, commandController ( commandController_ )
685
, msxEventDistributor ( msxEventDistributor_ )
686
, stateChangeDistributor ( stateChangeDistributor_ )
687
, eventDistributor ( eventDistributor_ )
688
, keyCodeTab ( defaultKeyCodeMappings [ matrix] )
689
, scanCodeTab ( defaultScanCodeMappings[ matrix] )
690
, modifierPos ( modifierPosForMatrix[ matrix] )
691
, keyMatrixUpCmd ( commandController, stateChangeDistributor, scheduler_ )
692
, keyMatrixDownCmd ( commandController, stateChangeDistributor, scheduler_ )
693
, keyTypeCmd ( commandController, stateChangeDistributor, scheduler_ )
694
, msxcode2UnicodeCmd ( commandController )
695
, unicode2MsxcodeCmd ( commandController )
696
, capsLockAligner ( eventDistributor, scheduler_ )
697
#if defined (_WIN32)
698
, imeManager ( eventDistributor )
699
#endif
700
, keyboardSettings ( commandController )
701
, msxKeyEventQueue ( scheduler_, commandController. getInterpreter ( ) )
702
, keybDebuggable ( motherBoard )
703
, unicodeKeymap ( config. getChildData (
704
" keyboard_type" , defaultKeymapForMatrix[ matrix] ) )
705
, hasKeypad ( config. getChildDataAsBool ( " has_keypad" , true ) )
706
, blockRow11 ( matrix == Matrix:: MSX
707
&& ! config. getChildDataAsBool ( " has_yesno_keys" , false ) )
708
, keyGhosting ( config. getChildDataAsBool ( " key_ghosting" , true ) )
709
, keyGhostingSGCprotected ( config. getChildDataAsBool (
710
" key_ghosting_sgc_protected" , true ) )
711
, modifierIsLock ( KeyInfo:: CAPS_MASK
712
| ( config. getChildDataAsBool ( " code_kana_locks" , false ) ? KeyInfo:: CODE_MASK : 0 )
713
| ( config. getChildDataAsBool ( " graph_locks" , false ) ? KeyInfo:: GRAPH_MASK : 0 ) )
714
{
715
std:: ranges:: fill ( keyMatrix, 255 ) ;
716
std:: ranges:: fill ( cmdKeyMatrix, 255 ) ;
717
std:: ranges:: fill ( typeKeyMatrix, 255 ) ;
718
std:: ranges:: fill ( userKeyMatrix, 255 ) ;
719
std:: ranges:: fill ( hostKeyMatrix, 255 ) ;
720
721
msxEventDistributor. registerEventListener ( * this ) ;
722
stateChangeDistributor. registerListener ( * this ) ;
723
724
725
726
eventDistributor. registerEventListener ( EventType:: KEYMAP_CHANGED, * this ) ;
727
728
motherBoard. registerKeyboard ( * this ) ;
729
}
730
731
Keyboard:: ~Keyboard ( )
732
{
733
auto & motherBoard = keybDebuggable. getMotherBoard ( ) ;
734
motherBoard. unregisterKeyboard ( * this ) ;
735
736
eventDistributor. unregisterEventListener ( EventType:: KEYMAP_CHANGED, * this ) ;
737
738
stateChangeDistributor. unregisterListener ( * this ) ;
739
msxEventDistributor. unregisterEventListener ( * this ) ;
740
}
741
742
const MsxChar2Unicode& Keyboard:: getMsxChar2Unicode ( ) const
743
{
744
return unicodeKeymap. getMsxChars ( ) ;
745
}
746
747
static constexpr void doKeyGhosting ( std:: span< uint8_t , KeyMatrixPosition:: NUM_ROWS> matrix ,
748
bool protectRow6 )
749
{
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
bool changedSomething = false ;
767
do {
768
changedSomething = false ;
769
770
771
772
for ( auto i : xrange ( KeyMatrixPosition:: NUM_ROWS - 1 ) ) {
773
auto row1 = matrix[ i] ;
774
for ( auto j : xrange ( i + 1 , KeyMatrixPosition:: NUM_ROWS ) ) {
775
auto row2 = matrix[ j] ;
776
if ( ( row1 != row2) && ( ( row1 | row2) != 0xff ) ) {
777
auto rowIold = matrix[ i] ;
778
auto rowJold = matrix[ j] ;
779
780
781
if ( protectRow6 && i == 6 ) {
782
matrix[ i] = row1 & row2;
783
matrix[ j] = ( row1 | 0x15 ) & row2;
784
row1 &= row2;
785
} else if ( protectRow6 && j == 6 ) {
786
matrix[ i] = row1 & ( row2 | 0x15 ) ;
787
matrix[ j] = row1 & row2;
788
row1 &= ( row2 | 0x15 ) ;
789
} else {
790
791
792
uint8_t newRow = row1 & row2;
793
matrix[ i] = newRow;
794
matrix[ j] = newRow;
795
row1 = newRow;
796
}
797
if ( rowIold != matrix[ i] ||
798
rowJold != matrix[ j] ) {
799
changedSomething = true ;
800
}
801
}
802
}
803
}
804
} while ( changedSomething) ;
805
}
806
807
std:: span< const uint8_t , KeyMatrixPosition:: NUM_ROWS> Keyboard:: getKeys ( ) const
808
{
809
if ( keysChanged) {
810
keysChanged = false ;
811
std:: span matrix = keyTypeCmd. isActive ( ) ? typeKeyMatrix : userKeyMatrix;
812
for ( auto row : xrange ( KeyMatrixPosition:: NUM_ROWS ) ) {
813
keyMatrix[ row] = cmdKeyMatrix[ row] & matrix[ row] ;
814
}
815
if ( keyGhosting) {
816
doKeyGhosting ( keyMatrix, keyGhostingSGCprotected ) ;
817
}
818
}
819
return keyMatrix;
820
}
821
822
void Keyboard:: transferHostKeyMatrix ( const Keyboard& source )
823
{
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
for ( auto row : xrange ( KeyMatrixPosition:: NUM_ROWS ) ) {
840
hostKeyMatrix[ row] = source. hostKeyMatrix [ row] ;
841
}
842
}
843
844
void Keyboard:: setFocus ( bool newFocus , EmuTime time )
845
{
846
if ( newFocus == focus) return ;
847
focus = newFocus;
848
849
if ( ! stateChangeDistributor. isReplaying ( ) ) {
850
syncHostKeyMatrix ( time ) ;
851
}
852
}
853
854
855
856
857
858
859
void Keyboard:: signalMSXEvent ( const Event& event ,
860
EmuTime time ) noexcept
861
{
862
if ( getType ( event ) == one_of ( EventType:: KEY_DOWN, EventType:: KEY_UP ) ) {
863
const auto & keyEvent = get_event < KeyEvent> ( event ) ;
864
if ( keyEvent. getRepeat ( ) ) return ;
865
866
867
868
869
msxKeyEventQueue. process_asap ( time, event ) ;
870
}
871
}
872
873
void Keyboard:: signalStateChange ( const StateChange& event )
874
{
875
const auto * kms = std:: get_if < KeyMatrixState> ( & event) ;
876
if ( ! kms) return ;
877
878
userKeyMatrix[ kms-> getRow ( ) ] &= uint8_t ( ~ kms-> getPress ( ) ) ;
879
userKeyMatrix[ kms-> getRow ( ) ] |= kms-> getRelease ( ) ;
880
keysChanged = true ;
881
}
882
883
void Keyboard:: stopReplay ( EmuTime time ) noexcept
884
{
885
syncHostKeyMatrix ( time ) ;
886
}
887
888
void Keyboard:: syncHostKeyMatrix ( EmuTime time )
889
{
890
for ( auto [ row, hkm] : enumerate ( hostKeyMatrix ) ) {
891
changeKeyMatrixEvent ( time, uint8_t ( row) , hkm ) ;
892
}
893
msxModifiers = 0xff ;
894
msxKeyEventQueue. clear ( ) ;
895
lastUnicodeForScancode. clear ( ) ;
896
}
897
898
uint8_t Keyboard:: needsLockToggle ( const UnicodeKeymap:: KeyInfo& keyInfo ) const
899
{
900
return modifierIsLock
901
& ( locksOn ^ keyInfo. modMask )
902
& unicodeKeymap. getRelevantMods ( keyInfo ) ;
903
}
904
905
bool Keyboard:: isKeyMatrixPressed ( KeyMatrixPosition pos ) const
906
{
907
if ( ! pos. isValid ( ) ) return false ;
908
auto row = pos. getRow ( ) ;
909
auto mask = pos. getMask ( ) ;
910
return ( ( hostKeyMatrix[ row] & mask) == 0 ) &&
911
( ( userKeyMatrix[ row] & mask) == 0 ) ;
912
}
913
914
void Keyboard:: pressKeyMatrixEvent ( EmuTime time , KeyMatrixPosition pos )
915
{
916
if ( ! pos. isValid ( ) ) {
917
918
return ;
919
}
920
if ( isKeyMatrixPressed ( pos ) ) {
921
922
return ;
923
}
924
auto row = pos. getRow ( ) ;
925
changeKeyMatrixEvent ( time, row, hostKeyMatrix[ row] & ~ pos. getMask ( ) ) ;
926
}
927
928
void Keyboard:: releaseKeyMatrixEvent ( EmuTime time , KeyMatrixPosition pos )
929
{
930
if ( ! pos. isValid ( ) ) {
931
932
return ;
933
}
934
auto row = pos. getRow ( ) ;
935
auto release = pos. getMask ( ) ;
936
if ( ( ( hostKeyMatrix[ row] & release) == release) &&
937
( ( userKeyMatrix[ row] & release) == release) ) {
938
939
940
941
942
return ;
943
}
944
changeKeyMatrixEvent ( time, row, hostKeyMatrix[ row] | release ) ;
945
}
946
947
void Keyboard:: changeKeyMatrixEvent ( EmuTime time , uint8_t row , uint8_t newValue )
948
{
949
if ( ! focus) newValue = 0xff ;
950
951
952
953
hostKeyMatrix[ row] = newValue;
954
955
uint8_t diff = userKeyMatrix[ row] ^ newValue;
956
if ( diff == 0 ) return ;
957
uint8_t press = userKeyMatrix[ row] & diff;
958
uint8_t release = newValue & diff;
959
stateChangeDistributor. distributeNew < KeyMatrixState> (
960
time, row, press, release ) ;
961
}
962
963
964
965
966
bool Keyboard:: processQueuedEvent ( const Event& event , EmuTime time )
967
{
968
auto mode = keyboardSettings. getMappingMode ( ) ;
969
970
const auto & keyEvent = get_event < KeyEvent> ( event ) ;
971
bool down = getType ( event ) == EventType:: KEY_DOWN;
972
auto key = keyEvent. getKey ( ) ;
973
974
975
976
977
978
979
980
981
982
if ( hostKeyMap == HostKeyMap:: UNKNOWN) {
983
if ( SDL_GetKeyFromScancode ( SDL_SCANCODE_LEFTBRACKET ) == SDLK_AT) {
984
hostKeyMap = HostKeyMap:: JP;
985
} else {
986
hostKeyMap = HostKeyMap:: EN;
987
}
988
}
989
990
if ( down) {
991
auto codepointToUtf8 = [ ] ( uint32_t cp) {
992
std:: array< char , 4 > buffer;
993
auto * start = buffer. data ( ) ;
994
auto * end = utf8:: unchecked:: append ( cp, start ) ;
995
return std:: string ( start, end ) ;
996
} ;
997
998
999
1000
1001
1002
1003
1004
debug ( " Key pressed, unicode: 0x%04x (%s ), keyCode: 0x%05x (%s ), scanCode: 0x%03x (%s ), keyName: %s \n " ,
1005
keyEvent. getUnicode ( ) ,
1006
codepointToUtf8 ( keyEvent. getUnicode ( ) ) . c_str ( ) ,
1007
keyEvent. getKeyCode ( ) ,
1008
SDL_GetKeyName ( keyEvent. getKeyCode ( ) ) ,
1009
keyEvent. getScanCode ( ) ,
1010
SDL_GetScancodeName ( keyEvent. getScanCode ( ) ) ,
1011
key. toString ( ) . c_str ( ) ) ;
1012
} else {
1013
debug ( " Key released, keyCode: 0x%05x (%s ), scanCode: 0x%03x (%s ), keyName: %s \n " ,
1014
keyEvent. getKeyCode ( ) ,
1015
SDL_GetKeyName ( keyEvent. getKeyCode ( ) ) ,
1016
keyEvent. getScanCode ( ) ,
1017
SDL_GetScancodeName ( keyEvent. getScanCode ( ) ) ,
1018
key. toString ( ) . c_str ( ) ) ;
1019
}
1020
1021
1022
if ( mode == KeyboardSettings:: MappingMode:: CHARACTER) {
1023
for ( auto n : xrange ( 3 ) ) {
1024
if ( key. sym . sym == keyboardSettings. getDeadKeyHostKey ( n ) ) {
1025
UnicodeKeymap:: KeyInfo deadKey = unicodeKeymap. getDeadKey ( n ) ;
1026
if ( deadKey. isValid ( ) ) {
1027
updateKeyMatrix ( time, down, deadKey. pos ) ;
1028
return false ;
1029
}
1030
}
1031
}
1032
}
1033
1034
if ( key. sym . sym == SDLK_CAPSLOCK) {
1035
processCapslockEvent ( time, down ) ;
1036
return false ;
1037
} else if ( key. sym . sym == keyboardSettings. getCodeKanaHostKey ( ) ) {
1038
processCodeKanaChange ( time, down ) ;
1039
return false ;
1040
} else if ( key. sym . sym == SDLK_LALT) {
1041
processGraphChange ( time, down ) ;
1042
return false ;
1043
1044
} else if ( ( key. sym . sym == SDLK_KP_ENTER) || ( key. sym . sym == SDLK_KP_COMMA) ) {
1045
processKeypadEnterKey ( time, down ) ;
1046
return false ;
1047
} else {
1048
1049
1050
if ( keyEvent. getScanCode ( ) == SDL_SCANCODE_UNKNOWN) {
1051
return false ;
1052
}
1053
if ( ( keyEvent. getScanCode ( ) == SDL_SCANCODE_GRAVE) && ( hostKeyMap == HostKeyMap:: JP) ) {
1054
1055
1056
return false ;
1057
}
1058
return processKeyEvent ( time, down, keyEvent ) ;
1059
}
1060
}
1061
1062
1063
1064
1065
1066
1067
void Keyboard:: processCodeKanaChange ( EmuTime time , bool down )
1068
{
1069
if ( down) {
1070
locksOn ^= KeyInfo:: CODE_MASK;
1071
}
1072
updateKeyMatrix ( time, down, modifierPos[ KeyInfo:: Modifier:: CODE] ) ;
1073
}
1074
1075
1076
1077
1078
1079
1080
void Keyboard:: processGraphChange ( EmuTime time , bool down )
1081
{
1082
if ( down) {
1083
locksOn ^= KeyInfo:: GRAPH_MASK;
1084
}
1085
updateKeyMatrix ( time, down, modifierPos[ KeyInfo:: Modifier:: GRAPH] ) ;
1086
}
1087
1088
1089
1090
1091
1092
1093
void Keyboard:: processCapslockEvent ( EmuTime time , bool down )
1094
{
1095
if ( SANE_CAPSLOCK_BEHAVIOR) {
1096
debug ( " Changing CAPS lock state according to SDL request\n " ) ;
1097
if ( down) {
1098
locksOn ^= KeyInfo:: CAPS_MASK;
1099
}
1100
updateKeyMatrix ( time, down, modifierPos[ KeyInfo:: Modifier:: CAPS] ) ;
1101
} else {
1102
debug ( " Pressing CAPS lock and scheduling a release\n " ) ;
1103
locksOn ^= KeyInfo:: CAPS_MASK;
1104
updateKeyMatrix ( time, true , modifierPos[ KeyInfo:: Modifier:: CAPS] ) ;
1105
setSyncPoint ( time + EmuDuration:: hz ( 10 ) ) ;
1106
}
1107
}
1108
1109
void Keyboard:: executeUntil ( EmuTime time )
1110
{
1111
debug ( " Releasing CAPS lock\n " ) ;
1112
updateKeyMatrix ( time, false , modifierPos[ KeyInfo:: Modifier:: CAPS] ) ;
1113
}
1114
1115
bool Keyboard:: signalEvent ( const Event& event )
1116
{
1117
if ( getType ( event ) == EventType:: KEYMAP_CHANGED) {
1118
1119
hostKeyMap = HostKeyMap:: UNKNOWN;
1120
}
1121
return false ;
1122
}
1123
1124
void Keyboard:: processKeypadEnterKey ( EmuTime time , bool down )
1125
{
1126
if ( ! hasKeypad && ! keyboardSettings. getAlwaysEnableKeypad ( ) ) {
1127
1128
1129
return ;
1130
}
1131
processSdlKey ( time,
1132
keyboardSettings. getKpEnterMode ( ) == KeyboardSettings:: KpEnterMode:: MSX_KP_COMMA ?
1133
SDLKey:: create ( SDLK_KP_COMMA, SDL_SCANCODE_KP_COMMA, down ) :
1134
SDLKey:: create ( SDLK_KP_ENTER, SDL_SCANCODE_KP_ENTER, down ) ) ;
1135
}
1136
1137
1138
1139
1140
1141
1142
1143
void Keyboard:: processSdlKey ( EmuTime time , SDLKey key )
1144
{
1145
auto process = [ & ] ( KeyMatrixPosition pos) {
1146
assert ( pos. isValid ( ) ) ;
1147
if ( pos. getRow ( ) == 11 && blockRow11) {
1148
1149
return ;
1150
}
1151
updateKeyMatrix ( time, key. down , pos ) ;
1152
} ;
1153
1154
if ( keyboardSettings. getMappingMode ( ) == KeyboardSettings:: MappingMode:: POSITIONAL) {
1155
if ( ( key. sym . sym == SDLK_RIGHTBRACKET) && ( key. sym . scancode == SDL_SCANCODE_BACKSLASH) )
1156
{
1157
1158
if ( const auto * mapping = binary_find ( scanCodeTab, SDL_SCANCODE_GRAVE, { } , & ScanCodeMsxMapping:: hostScanCode ) ) {
1159
process ( mapping-> msx ) ;
1160
}
1161
} else if ( const auto * mapping = binary_find ( scanCodeTab, key. sym . scancode , { } , & ScanCodeMsxMapping:: hostScanCode ) ) {
1162
process ( mapping-> msx ) ;
1163
}
1164
} else {
1165
if ( const auto * mapping = binary_find ( keyCodeTab, key. sym . sym , { } , & KeyCodeMsxMapping:: hostKeyCode ) ) {
1166
process ( mapping-> msx ) ;
1167
}
1168
}
1169
}
1170
1171
1172
1173
1174
void Keyboard:: updateKeyMatrix ( EmuTime time , bool down , KeyMatrixPosition pos )
1175
{
1176
if ( ! pos. isValid ( ) ) {
1177
1178
return ;
1179
}
1180
if ( down) {
1181
pressKeyMatrixEvent ( time, pos ) ;
1182
1183
1184
1185
1186
for ( auto [ i, mp] : enumerate ( modifierPos ) ) {
1187
if ( pos == mp) {
1188
msxModifiers &= uint8_t ( ~ ( 1 << i) ) ;
1189
}
1190
}
1191
} else {
1192
releaseKeyMatrixEvent ( time, pos ) ;
1193
for ( auto [ i, mp] : enumerate ( modifierPos ) ) {
1194
if ( pos == mp) {
1195
msxModifiers |= 1 << i;
1196
}
1197
}
1198
}
1199
}
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
bool Keyboard:: processKeyEvent ( EmuTime time , bool down , const KeyEvent& keyEvent )
1211
{
1212
auto mode = keyboardSettings. getMappingMode ( ) ;
1213
1214
auto keyCode = keyEvent. getKeyCode ( ) ;
1215
auto scanCode = keyEvent. getScanCode ( ) ;
1216
auto key = keyEvent. getKey ( ) ;
1217
1218
bool isOnKeypad =
1219
( SDLK_KP_1 <= keyCode && keyCode <= SDLK_KP_0) ||
1220
( keyCode == one_of ( SDLK_KP_PERIOD, SDLK_KP_DIVIDE, SDLK_KP_MULTIPLY,
1221
SDLK_KP_MINUS, SDLK_KP_PLUS ) ) ;
1222
1223
if ( isOnKeypad && ! hasKeypad &&
1224
! keyboardSettings. getAlwaysEnableKeypad ( ) ) {
1225
1226
1227
return false ;
1228
}
1229
#ifdef __APPLE__
1230
bool positional = mode == KeyboardSettings:: MappingMode:: POSITIONAL;
1231
if ( ( key. sym . mod & KMOD_GUI) &&
1232
( ( positional && ( keyEvent. getScanCode ( ) == SDL_SCANCODE_I) ) ||
1233
( ! positional && ( keyCode == SDLK_i) ) ) ) {
1234
1235
keyCode = SDLK_INSERT;
1236
key. sym . sym = SDLK_INSERT;
1237
key. sym . scancode = SDL_SCANCODE_INSERT;
1238
key. sym . mod &= ~ KMOD_GUI;
1239
key. sym . unused = 0 ;
1240
}
1241
#endif
1242
1243
if ( down) {
1244
UnicodeKeymap:: KeyInfo keyInfo;
1245
unsigned unicode;
1246
if ( isOnKeypad ||
1247
mode == one_of ( KeyboardSettings:: MappingMode:: KEY,
1248
KeyboardSettings:: MappingMode:: POSITIONAL ) ) {
1249
1250
1251
1252
1253
1254
1255
unicode = 0 ;
1256
} else {
1257
unicode = keyEvent. getUnicode ( ) ;
1258
if ( ( unicode < 0x20 ) || ( ( 0x7F <= unicode) && ( unicode < 0xA0 ) ) ) {
1259
1260
1261
unicode = 0 ;
1262
} else if ( utf8:: is_pua ( unicode ) ) {
1263
1264
1265
1266
unicode = 0 ;
1267
} else {
1268
keyInfo = unicodeKeymap. get ( unicode ) ;
1269
if ( ! keyInfo. isValid ( ) ) {
1270
1271
1272
unicode = 0 ;
1273
}
1274
}
1275
}
1276
1277
1278
1279
1280
1281
1282
1283
if ( auto it = std:: ranges:: lower_bound ( lastUnicodeForScancode, scanCode, { } , & std:: pair< int32_t , uint32_t > :: first ) ;
1284
( it != lastUnicodeForScancode. end ( ) ) && ( it-> first == scanCode) ) {
1285
1286
1287
it-> second = unicode;
1288
} else {
1289
1290
lastUnicodeForScancode. emplace ( it, scanCode, unicode ) ;
1291
}
1292
1293
if ( unicode == 0 ) {
1294
1295
1296
1297
1298
1299
1300
1301
1302
if ( ! ( key. sym . mod & KMOD_MODE) ) {
1303
processSdlKey ( time, key ) ;
1304
}
1305
return false ;
1306
} else {
1307
1308
return pressUnicodeByUser ( time, keyInfo, unicode, true ) ;
1309
}
1310
} else {
1311
1312
auto it = std:: ranges:: lower_bound ( lastUnicodeForScancode, scanCode, { } , & std:: pair< int32_t , uint32_t > :: first ) ;
1313
unsigned unicode = ( ( it != lastUnicodeForScancode. end ( ) ) && ( it-> first == scanCode) )
1314
? it-> second
1315
: 0 ;
1316
if ( unicode == 0 ) {
1317
1318
1319
1320
if ( ! ( key. sym . mod & KMOD_MODE) ) {
1321
processSdlKey ( time, key ) ;
1322
}
1323
} else {
1324
1325
pressUnicodeByUser ( time, unicodeKeymap. get ( unicode ) , unicode, false ) ;
1326
}
1327
return false ;
1328
}
1329
}
1330
1331
void Keyboard:: processCmd ( Interpreter& interp , std:: span< const TclObject> tokens , bool up )
1332
{
1333
unsigned row = tokens[ 1 ] . getInt ( interp ) ;
1334
unsigned mask = tokens[ 2 ] . getInt ( interp ) ;
1335
if ( row >= KeyMatrixPosition:: NUM_ROWS) {
1336
throw CommandException ( " Invalid row" ) ;
1337
}
1338
if ( mask >= 256 ) {
1339
throw CommandException ( " Invalid mask" ) ;
1340
}
1341
if ( up) {
1342
cmdKeyMatrix[ row] |= narrow_cast < uint8_t > ( mask ) ;
1343
} else {
1344
cmdKeyMatrix[ row] &= narrow_cast < uint8_t > ( ~ mask) ;
1345
}
1346
keysChanged = true ;
1347
}
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
bool Keyboard:: pressUnicodeByUser (
1388
EmuTime time , UnicodeKeymap:: KeyInfo keyInfo , unsigned unicode ,
1389
bool down )
1390
{
1391
bool insertCodeKanaRelease = false ;
1392
if ( down) {
1393
if ( ( needsLockToggle ( keyInfo ) & KeyInfo:: CODE_MASK) &&
1394
keyboardSettings. getAutoToggleCodeKanaLock ( ) &&
1395
! isKeyMatrixPressed ( modifierPos[ KeyInfo:: Modifier:: CODE] ) ) {
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
locksOn ^= KeyInfo:: CODE_MASK;
1408
pressKeyMatrixEvent ( time, modifierPos[ KeyInfo:: Modifier:: CODE] ) ;
1409
insertCodeKanaRelease = true ;
1410
} else {
1411
1412
1413
1414
1415
1416
1417
1418
pressKeyMatrixEvent ( time, keyInfo. pos ) ;
1419
1420
uint8_t modMask = keyInfo. modMask & ~ modifierIsLock;
1421
if ( ( ' A' <= unicode && unicode <= ' Z' ) || ( ' a' <= unicode && unicode <= ' z' ) ) {
1422
1423
1424
1425
1426
modMask &= ~ KeyInfo:: SHIFT_MASK;
1427
} else {
1428
1429
if ( ~ modMask & KeyInfo:: SHIFT_MASK) {
1430
releaseKeyMatrixEvent ( time, modifierPos[ KeyInfo:: Modifier:: SHIFT] ) ;
1431
}
1432
}
1433
1434
1435
for ( auto [ i, mp] : enumerate ( modifierPos ) ) {
1436
if ( ( modMask >> i) & 1 ) {
1437
pressKeyMatrixEvent ( time, mp ) ;
1438
}
1439
}
1440
}
1441
} else {
1442
releaseKeyMatrixEvent ( time, keyInfo. pos ) ;
1443
1444
1445
for ( auto [ i, mp] : enumerate ( modifierPos ) ) {
1446
if ( ! ( ( modifierIsLock >> i) & 1 ) ) {
1447
1448
1449
if ( ( msxModifiers >> i) & 1 ) {
1450
releaseKeyMatrixEvent ( time, mp ) ;
1451
} else {
1452
pressKeyMatrixEvent ( time, mp ) ;
1453
}
1454
}
1455
}
1456
}
1457
keysChanged = true ;
1458
return insertCodeKanaRelease;
1459
}
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
uint8_t Keyboard:: pressAscii ( unsigned unicode , bool down )
1474
{
1475
uint8_t releaseMask = 0 ;
1476
UnicodeKeymap:: KeyInfo keyInfo = unicodeKeymap. get ( unicode ) ;
1477
if ( ! keyInfo. isValid ( ) ) {
1478
return releaseMask;
1479
}
1480
uint8_t modMask = keyInfo. modMask & ~ modifierIsLock;
1481
if ( down) {
1482
1483
uint8_t toggleLocks = needsLockToggle ( keyInfo ) ;
1484
for ( auto [ i, mp] : enumerate ( modifierPos ) ) {
1485
if ( ( toggleLocks >> i) & 1 ) {
1486
debug ( " Toggling lock %d \n " , i) ;
1487
locksOn ^= 1 << i;
1488
releaseMask |= 1 << i;
1489
typeKeyMatrix[ mp. getRow ( ) ] &= uint8_t ( ~ mp. getMask ( ) ) ;
1490
}
1491
}
1492
if ( releaseMask == 0 ) {
1493
debug ( " Key pasted, unicode: 0x%04x , row: %02d , col: %d , modMask: %02x \n " ,
1494
unicode, keyInfo. pos . getRow ( ) , keyInfo. pos . getColumn ( ) , modMask ) ;
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
if ( modMask & KeyInfo:: GRAPH_MASK) {
1523
auto isPressed = [ & ] ( auto & key) {
1524
return ( typeKeyMatrix[ key. getRow ( ) ] & key. getMask ( ) ) == 0 ;
1525
} ;
1526
if ( ! isPressed ( modifierPos[ KeyInfo:: Modifier:: GRAPH] ) ) {
1527
1528
1529
releaseMask = TRY_AGAIN;
1530
}
1531
}
1532
1533
for ( auto [ i, mp] : enumerate ( modifierPos ) ) {
1534
if ( ( modMask >> i) & 1 ) {
1535
typeKeyMatrix[ mp. getRow ( ) ] &= uint8_t ( ~ mp. getMask ( ) ) ;
1536
}
1537
}
1538
if ( releaseMask == 0 ) {
1539
1540
typeKeyMatrix[ keyInfo. pos . getRow ( ) ] &= uint8_t ( ~ keyInfo. pos . getMask ( ) ) ;
1541
}
1542
}
1543
} else {
1544
typeKeyMatrix[ keyInfo. pos . getRow ( ) ] |= keyInfo. pos . getMask ( ) ;
1545
for ( auto [ i, mp] : enumerate ( modifierPos ) ) {
1546
if ( ( modMask >> i) & 1 ) {
1547
typeKeyMatrix[ mp. getRow ( ) ] |= mp. getMask ( ) ;
1548
}
1549
}
1550
}
1551
keysChanged = true ;
1552
return releaseMask;
1553
}
1554
1555
1556
1557
1558
1559
1560
1561
void Keyboard:: pressLockKeys ( uint8_t lockKeysMask , bool down )
1562
{
1563
for ( auto [ i, mp] : enumerate ( modifierPos ) ) {
1564
if ( ( lockKeysMask >> i) & 1 ) {
1565
if ( down) {
1566
1567
typeKeyMatrix[ mp. getRow ( ) ] &= uint8_t ( ~ mp. getMask ( ) ) ;
1568
} else {
1569
1570
typeKeyMatrix[ mp. getRow ( ) ] |= mp. getMask ( ) ;
1571
}
1572
}
1573
}
1574
keysChanged = true ;
1575
}
1576
1577
1578
1579
1580
1581
1582
1583
1584
bool Keyboard:: commonKeys ( unsigned unicode1 , unsigned unicode2 ) const
1585
{
1586
1587
auto keyPos1 = unicodeKeymap. get ( unicode1 ) . pos ;
1588
auto keyPos2 = unicodeKeymap. get ( unicode2 ) . pos ;
1589
1590
return keyPos1 == keyPos2 && keyPos1. isValid ( ) ;
1591
}
1592
1593
void Keyboard:: debug ( const char * format , ... ) const
1594
{
1595
if ( keyboardSettings. getTraceKeyPresses ( ) ) {
1596
va_list args;
1597
va_start ( args, format ) ;
1598
vfprintf ( stderr, format, args ) ;
1599
va_end ( args ) ;
1600
}
1601
}
1602
1603
1604
1605
1606
Keyboard:: KeyMatrixUpCmd:: KeyMatrixUpCmd (
1607
CommandController& commandController_ ,
1608
StateChangeDistributor& stateChangeDistributor_ ,
1609
Scheduler& scheduler_ )
1610
: RecordedCommand ( commandController_, stateChangeDistributor_,
1611
scheduler_, " keymatrixup" )
1612
{
1613
}
1614
1615
void Keyboard:: KeyMatrixUpCmd:: execute (
1616
std:: span< const TclObject> tokens , TclObject& , EmuTime )
1617
{
1618
checkNumArgs ( tokens, 3 , Prefix{ 1 } , " row mask" ) ;
1619
auto & keyboard = OUTER ( Keyboard, keyMatrixUpCmd ) ;
1620
keyboard. processCmd ( getInterpreter ( ) , tokens, true ) ;
1621
}
1622
1623
std:: string Keyboard:: KeyMatrixUpCmd:: help ( std:: span< const TclObject> ) const
1624
{
1625
return " keymatrixup <row> <bitmask> release a key in the keyboard matrix\n " ;
1626
}
1627
1628
1629
1630
1631
Keyboard:: KeyMatrixDownCmd:: KeyMatrixDownCmd ( CommandController& commandController_ ,
1632
StateChangeDistributor& stateChangeDistributor_ ,
1633
Scheduler& scheduler_ )
1634
: RecordedCommand ( commandController_, stateChangeDistributor_,
1635
scheduler_, " keymatrixdown" )
1636
{
1637
}
1638
1639
void Keyboard:: KeyMatrixDownCmd:: execute ( std:: span< const TclObject> tokens ,
1640
TclObject& , EmuTime )
1641
{
1642
checkNumArgs ( tokens, 3 , Prefix{ 1 } , " row mask" ) ;
1643
auto & keyboard = OUTER ( Keyboard, keyMatrixDownCmd ) ;
1644
keyboard. processCmd ( getInterpreter ( ) , tokens, false ) ;
1645
}
1646
1647
std:: string Keyboard:: KeyMatrixDownCmd:: help ( std:: span< const TclObject> ) const
1648
{
1649
return " keymatrixdown <row> <bitmask> press a key in the keyboard matrix\n " ;
1650
}
1651
1652
1653
1654
1655
Keyboard:: MsxKeyEventQueue:: MsxKeyEventQueue (
1656
Scheduler& scheduler_ , Interpreter& interp_ )
1657
: Schedulable ( scheduler_ )
1658
, interp ( interp_ )
1659
{
1660
}
1661
1662
void Keyboard:: MsxKeyEventQueue:: process_asap (
1663
EmuTime time , const Event& event )
1664
{
1665
bool processImmediately = eventQueue. empty ( ) ;
1666
eventQueue. push_back ( event ) ;
1667
if ( processImmediately) {
1668
executeUntil ( time ) ;
1669
}
1670
}
1671
1672
void Keyboard:: MsxKeyEventQueue:: clear ( )
1673
{
1674
eventQueue. clear ( ) ;
1675
removeSyncPoint ( ) ;
1676
}
1677
1678
void Keyboard:: MsxKeyEventQueue:: executeUntil ( EmuTime time )
1679
{
1680
1681
const Event& event = eventQueue. front ( ) ;
1682
auto & keyboard = OUTER ( Keyboard, msxKeyEventQueue ) ;
1683
bool insertCodeKanaRelease = keyboard. processQueuedEvent ( event, time ) ;
1684
1685
if ( insertCodeKanaRelease) {
1686
1687
1688
1689
eventQueue. emplace_front ( KeyUpEvent:: create ( keyboard. keyboardSettings . getCodeKanaHostKey ( ) ) ) ;
1690
} else {
1691
1692
if ( ! eventQueue. empty ( ) ) {
1693
eventQueue. pop_front ( ) ;
1694
} else {
1695
1696
1697
}
1698
}
1699
1700
if ( ! eventQueue. empty ( ) ) {
1701
1702
setSyncPoint ( time + EmuDuration:: hz ( 15 ) ) ;
1703
}
1704
}
1705
1706
1707
1708
1709
Keyboard:: KeyInserter:: KeyInserter (
1710
CommandController& commandController_ ,
1711
StateChangeDistributor& stateChangeDistributor_ ,
1712
Scheduler& scheduler_ )
1713
: RecordedCommand ( commandController_, stateChangeDistributor_,
1714
scheduler_, " type_via_keyboard" )
1715
, Schedulable ( scheduler_ )
1716
{
1717
}
1718
1719
void Keyboard:: KeyInserter:: execute (
1720
std:: span< const TclObject> tokens , TclObject& , EmuTime )
1721
{
1722
checkNumArgs ( tokens, AtLeast{ 2 } , " ?-release? ?-freq hz? ?-cancel? text" ) ;
1723
1724
bool cancel = false ;
1725
releaseBeforePress = false ;
1726
typingFrequency = 15 ;
1727
std:: array info = {
1728
flagArg ( " -cancel" , cancel) ,
1729
flagArg ( " -release" , releaseBeforePress) ,
1730
valueArg ( " -freq" , typingFrequency) ,
1731
} ;
1732
auto arguments = parseTclArgs ( getInterpreter ( ) , tokens. subspan ( 1 ) , info) ;
1733
1734
if ( typingFrequency <= 0 ) {
1735
throw CommandException ( " Wrong argument for -freq (should be a positive number)" ) ;
1736
}
1737
if ( cancel) {
1738
text_utf8. clear ( ) ;
1739
return ;
1740
}
1741
1742
if ( arguments. size ( ) != 1 ) throw SyntaxError ( ) ;
1743
1744
type ( arguments[ 0 ] . getString ( ) ) ;
1745
}
1746
1747
std:: string Keyboard:: KeyInserter:: help ( std:: span< const TclObject> ) const
1748
{
1749
return " Type a string in the emulated MSX.\n "
1750
" 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 "
1751
" 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 "
1752
" Use -cancel to cancel a (long) in-progress type command." ;
1753
}
1754
1755
void Keyboard:: KeyInserter:: tabCompletion ( std:: vector< std:: string> & tokens ) const
1756
{
1757
using namespace std:: literals;
1758
static constexpr std:: array options = { " -release" sv, " -freq" sv} ;
1759
completeString ( tokens, options ) ;
1760
}
1761
1762
void Keyboard:: KeyInserter:: type ( std:: string_view str )
1763
{
1764
if ( str. empty ( ) ) {
1765
return ;
1766
}
1767
const auto & keyboard = OUTER ( Keyboard, keyTypeCmd ) ;
1768
oldLocksOn = keyboard. locksOn ;
1769
if ( text_utf8. empty ( ) ) {
1770
reschedule ( getCurrentTime ( ) ) ;
1771
}
1772
text_utf8. append ( str. data ( ) , str. size ( ) ) ;
1773
}
1774
1775
void Keyboard:: KeyInserter:: executeUntil ( EmuTime time )
1776
{
1777
auto & keyboard = OUTER ( Keyboard, keyTypeCmd ) ;
1778
if ( lockKeysMask != 0 ) {
1779
1780
keyboard. pressLockKeys ( lockKeysMask, false ) ;
1781
}
1782
if ( releaseLast) {
1783
keyboard. pressAscii ( last, false ) ;
1784
}
1785
if ( text_utf8. empty ( ) ) {
1786
releaseLast = false ;
1787
keyboard. debug ( " Restoring locks: %02X -> %02X \n " , keyboard. locksOn , oldLocksOn) ;
1788
uint8_t diff = oldLocksOn ^ keyboard. locksOn ;
1789
lockKeysMask = diff;
1790
if ( diff != 0 ) {
1791
1792
keyboard. locksOn ^= diff;
1793
keyboard. pressLockKeys ( diff, true ) ;
1794
reschedule ( time ) ;
1795
}
1796
return ;
1797
}
1798
1799
try {
1800
auto it = begin ( text_utf8 ) ;
1801
unsigned current = utf8:: next ( it, end ( text_utf8 ) ) ;
1802
if ( releaseLast && ( releaseBeforePress || keyboard. commonKeys ( last, current ) ) ) {
1803
1804
1805
1806
releaseLast = false ;
1807
} else {
1808
1809
1810
lockKeysMask = keyboard. pressAscii ( current, true ) ;
1811
if ( lockKeysMask == 0 ) {
1812
last = current;
1813
releaseLast = true ;
1814
text_utf8. erase ( begin ( text_utf8 ) , it) ;
1815
} else if ( lockKeysMask & TRY_AGAIN) {
1816
lockKeysMask &= ~ TRY_AGAIN;
1817
releaseLast = false ;
1818
} else if ( releaseBeforePress) {
1819
releaseLast = true ;
1820
}
1821
}
1822
reschedule ( time ) ;
1823
} catch ( std:: exception& ) {
1824
1825
text_utf8. clear ( ) ;
1826
}
1827
}
1828
1829
void Keyboard:: KeyInserter:: reschedule ( EmuTime time )
1830
{
1831
setSyncPoint ( time + EmuDuration:: hz ( typingFrequency ) ) ;
1832
}
1833
1834
1835
1836
1837
Keyboard:: Msxcode2UnicodeCmd:: Msxcode2UnicodeCmd ( CommandController& commandController_ )
1838
: Command ( commandController_, " msxcode2unicode" )
1839
{
1840
}
1841
1842
void Keyboard:: Msxcode2UnicodeCmd:: execute ( std:: span< const TclObject> tokens , TclObject& result )
1843
{
1844
checkNumArgs ( tokens, Between{ 2 , 3 } , " msx-string ?fallback?" ) ;
1845
1846
auto & interp = getInterpreter ( ) ;
1847
const auto & keyboard = OUTER ( Keyboard, msxcode2UnicodeCmd ) ;
1848
const auto & msxChars = keyboard. unicodeKeymap . getMsxChars ( ) ;
1849
1850
auto msx = tokens[ 1 ] . getBinary ( getInterpreter ( ) ) ;
1851
auto fallback = [ & ] -> std :: function < uint32_t ( uint8_t ) > {
1852
if ( tokens. size ( ) < 3 ) {
1853
1854
return [ ] ( uint8_t ) { return uint32_t ( ' ' ) ; } ;
1855
} else if ( auto i = tokens[ 2 ] . getOptionalInt ( ) ) {
1856
1857
return [ i = * i] ( uint8_t ) { return uint32_t ( i) ; } ;
1858
} else {
1859
1860
1861
1862
return [ & ] ( uint8_t m) {
1863
TclObject cmd{ TclObject:: MakeListTag{ } , tokens[ 2 ] , m} ;
1864
return uint32_t ( cmd. executeCommand ( interp ) . getInt ( interp ) ) ;
1865
} ;
1866
}
1867
} ( ) ;
1868
1869
result = msxChars. msxToUtf8 ( msx, fallback ) ;
1870
}
1871
1872
std:: string Keyboard:: Msxcode2UnicodeCmd:: help ( std:: span< const TclObject> ) const
1873
{
1874
return " msxcode2unicode <msx-string> [<fallback>]\n "
1875
" returns a unicode string converted from an MSX-string, i.e. a string based on\n "
1876
" MSX character codes.\n "
1877
" The optional fallback used for each character that cannot be mapped for the\n "
1878
" current MSX model can be:\n "
1879
" - omitted: then space will be used as fallback character.\n "
1880
" - an integer number: then this number will be used as unicode point to be the\n "
1881
" the fallback character.\n "
1882
" - a Tcl proc, which expects one input character and must return one unicode\n "
1883
" point." ;
1884
}
1885
1886
1887
Keyboard:: Unicode2MsxcodeCmd:: Unicode2MsxcodeCmd ( CommandController& commandController_ )
1888
: Command ( commandController_, " unicode2msxcode" )
1889
{
1890
}
1891
1892
void Keyboard:: Unicode2MsxcodeCmd:: execute ( std:: span< const TclObject> tokens , TclObject& result )
1893
{
1894
checkNumArgs ( tokens, Between{ 2 , 3 } , " unicode-string ?fallback?" ) ;
1895
1896
auto & interp = getInterpreter ( ) ;
1897
const auto & keyboard = OUTER ( Keyboard, unicode2MsxcodeCmd ) ;
1898
const auto & msxChars = keyboard. unicodeKeymap . getMsxChars ( ) ;
1899
1900
const auto & unicode = tokens[ 1 ] . getString ( ) ;
1901
auto fallback = [ & ] -> std :: function < uint8_t ( uint32_t ) > {
1902
if ( tokens. size ( ) < 3 ) {
1903
1904
return [ ] ( uint32_t ) { return uint8_t ( ' ' ) ; } ;
1905
} else if ( auto i = tokens[ 2 ] . getOptionalInt ( ) ) {
1906
1907
return [ i = * i] ( uint32_t ) { return uint8_t ( i) ; } ;
1908
} else {
1909
1910
1911
1912
return [ & ] ( uint32_t u) {
1913
TclObject cmd{ TclObject:: MakeListTag{ } , tokens[ 2 ] , u} ;
1914
return uint8_t ( cmd. executeCommand ( interp ) . getInt ( interp ) ) ;
1915
} ;
1916
}
1917
} ( ) ;
1918
1919
result = msxChars. utf8ToMsx ( unicode, fallback ) ;
1920
}
1921
1922
std:: string Keyboard:: Unicode2MsxcodeCmd:: help ( std:: span< const TclObject> ) const
1923
{
1924
return " unicode2msxcode <unicode-string> [<fallback>]\n "
1925
" returns an MSX string, i.e. a string based on MSX character codes, converted\n "
1926
" from a unicode string.\n "
1927
" The optional fallback used for each character that cannot be mapped for the\n "
1928
" current MSX model can be:\n "
1929
" - omitted: then space will be used as fallback character.\n "
1930
" - an integer number: then this number will be used as MSX character number to\n "
1931
" to be the fallback character.\n "
1932
" - a Tcl proc, which expects one input character and must return one MSX\n "
1933
" character number." ;
1934
}
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
Keyboard:: CapsLockAligner:: CapsLockAligner (
1950
EventDistributor& eventDistributor_ ,
1951
Scheduler& scheduler_ )
1952
: Schedulable ( scheduler_ )
1953
, eventDistributor ( eventDistributor_ )
1954
{
1955
for ( auto type : { EventType:: BOOT, EventType:: WINDOW} ) {
1956
eventDistributor. registerEventListener ( type, * this ) ;
1957
}
1958
}
1959
1960
Keyboard:: CapsLockAligner:: ~CapsLockAligner ( )
1961
{
1962
for ( auto type : { EventType:: WINDOW, EventType:: BOOT} ) {
1963
eventDistributor. unregisterEventListener ( type, * this ) ;
1964
}
1965
}
1966
1967
bool Keyboard:: CapsLockAligner:: signalEvent ( const Event& event )
1968
{
1969
if constexpr ( ! SANE_CAPSLOCK_BEHAVIOR) {
1970
1971
return false ;
1972
}
1973
1974
if ( state == IDLE) {
1975
EmuTime time = getCurrentTime ( ) ;
1976
std:: visit ( overloaded{
1977
[ & ] ( const WindowEvent& e) {
1978
if ( e. isMainWindow ( ) ) {
1979
const auto & evt = e. getSdlWindowEvent ( ) ;
1980
if ( evt. event == one_of ( SDL_WINDOWEVENT_FOCUS_GAINED, SDL_WINDOWEVENT_FOCUS_LOST ) ) {
1981
alignCapsLock ( time ) ;
1982
}
1983
}
1984
} ,
1985
[ & ] ( const BootEvent& ) {
1986
state = MUST_ALIGN_CAPSLOCK;
1987
setSyncPoint ( time + EmuDuration:: sec ( 2 ) ) ;
1988
} ,
1989
[ ] ( const EventBase& ) {
1990
1991
1992
}
1993
} , event) ;
1994
}
1995
return false ;
1996
}
1997
1998
void Keyboard:: CapsLockAligner:: executeUntil ( EmuTime time )
1999
{
2000
switch ( state) {
2001
case MUST_ALIGN_CAPSLOCK:
2002
alignCapsLock ( time ) ;
2003
break ;
2004
case MUST_DISTRIBUTE_KEY_RELEASE: {
2005
auto & keyboard = OUTER ( Keyboard, capsLockAligner ) ;
2006
auto event = KeyUpEvent:: create ( SDLK_CAPSLOCK ) ;
2007
keyboard. msxEventDistributor . distributeEvent ( event, time ) ;
2008
state = IDLE;
2009
break ;
2010
}
2011
default :
2012
UNREACHABLE;
2013
}
2014
}
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
void Keyboard:: CapsLockAligner:: alignCapsLock ( EmuTime time )
2027
{
2028
bool hostCapsLockOn = ( ( SDL_GetModState ( ) & KMOD_CAPS) != 0 ) ;
2029
auto & keyboard = OUTER ( Keyboard, capsLockAligner ) ;
2030
if ( bool ( keyboard. locksOn & KeyInfo:: CAPS_MASK) != hostCapsLockOn) {
2031
keyboard. debug ( " Resyncing host and MSX CAPS lock\n " ) ;
2032
2033
2034
auto event = KeyDownEvent:: create ( SDLK_CAPSLOCK ) ;
2035
keyboard. msxEventDistributor . distributeEvent ( event, time ) ;
2036
keyboard. debug ( " Sending fake CAPS release\n " ) ;
2037
state = MUST_DISTRIBUTE_KEY_RELEASE;
2038
setSyncPoint ( time + EmuDuration:: hz ( 10 ) ) ;
2039
} else {
2040
state = IDLE;
2041
}
2042
}
2043
2044
#if defined (_WIN32)
2045
2046
2047
2048
2049
2050
2051
Keyboard:: ImeManager:: ImeManager (
2052
EventDistributor& eventDistributor_ )
2053
: eventDistributor ( eventDistributor_ )
2054
{
2055
eventDistributor. registerEventListener ( EventType:: WINDOW, * this ) ;
2056
hImc = 0 ;
2057
}
2058
2059
Keyboard:: ImeManager:: ~ImeManager ( )
2060
{
2061
eventDistributor. unregisterEventListener ( EventType:: WINDOW, * this ) ;
2062
}
2063
2064
2065
2066
2067
2068
bool Keyboard:: ImeManager:: signalEvent ( const Event& event )
2069
{
2070
auto getWindow = [ ] ( const WindowEvent& e) -> HWND {
2071
SDL_SysWMinfo info;
2072
SDL_VERSION ( & info. version ) ;
2073
auto window = SDL_GetWindowFromID ( e. getMainWindowId ( ) ) ;
2074
if ( SDL_GetWindowWMInfo ( window, & info ) ) {
2075
return info. info . win . window ;
2076
}
2077
return nullptr ;
2078
} ;
2079
2080
std:: visit ( overloaded{
2081
[ & ] ( const WindowEvent& e) {
2082
if ( e. isMainWindow ( ) ) {
2083
const auto & evt = e. getSdlWindowEvent ( ) ;
2084
if ( evt. event == SDL_WINDOWEVENT_FOCUS_GAINED) {
2085
auto & keyboard = OUTER ( Keyboard, imeManager ) ;
2086
if ( keyboard. keyboardSettings . getDisableIME ( ) ) {
2087
2088
auto hwnd = getWindow ( e ) ;
2089
if ( hwnd) {
2090
hImc = :: ImmAssociateContext( hwnd, 0 ) ;
2091
}
2092
}
2093
}
2094
else if ( evt. event == SDL_WINDOWEVENT_FOCUS_LOST) {
2095
auto hwnd = getWindow ( e ) ;
2096
if ( hwnd && hImc) {
2097
hImc = :: ImmAssociateContext( hwnd, hImc ) ;
2098
}
2099
}
2100
}
2101
} ,
2102
[ ] ( const EventBase& ) { UNREACHABLE; }
2103
} , event) ;
2104
2105
return false ;
2106
}
2107
#endif
2108
2109
2110
2111
Keyboard:: KeybDebuggable:: KeybDebuggable ( MSXMotherBoard& motherBoard_ )
2112
: SimpleDebuggable ( motherBoard_, " keymatrix" , " MSX Keyboard Matrix" ,
2113
KeyMatrixPosition:: NUM_ROWS )
2114
{
2115
}
2116
2117
uint8_t Keyboard:: KeybDebuggable:: read ( unsigned address )
2118
{
2119
const auto & keyboard = OUTER ( Keyboard, keybDebuggable ) ;
2120
return keyboard. getKeys ( ) [ address] ;
2121
}
2122
2123
void Keyboard:: KeybDebuggable:: readBlock ( unsigned start , std:: span< uint8_t > output )
2124
{
2125
const auto & keyboard = OUTER ( Keyboard, keybDebuggable ) ;
2126
copy_to_range ( keyboard. getKeys ( ) . subspan ( start, output. size ( ) ) , output ) ;
2127
}
2128
2129
void Keyboard:: KeybDebuggable:: write ( unsigned , uint8_t )
2130
{
2131
2132
}
2133
2134
2135
template < typename Archive>
2136
void Keyboard:: KeyInserter:: serialize ( Archive& ar , unsigned )
2137
{
2138
ar. template serializeBase < Schedulable> ( * this ) ;
2139
ar. serialize ( " text" , text_utf8,
2140
" last" , last,
2141
" lockKeysMask" , lockKeysMask,
2142
" releaseLast" , releaseLast ) ;
2143
2144
bool oldCodeKanaLockOn, oldGraphLockOn, oldCapsLockOn;
2145
if constexpr ( ! Archive:: IS_LOADER) {
2146
oldCodeKanaLockOn = oldLocksOn & KeyInfo:: CODE_MASK;
2147
oldGraphLockOn = oldLocksOn & KeyInfo:: GRAPH_MASK;
2148
oldCapsLockOn = oldLocksOn & KeyInfo:: CAPS_MASK;
2149
}
2150
ar. serialize ( " oldCodeKanaLockOn" , oldCodeKanaLockOn,
2151
" oldGraphLockOn" , oldGraphLockOn,
2152
" oldCapsLockOn" , oldCapsLockOn ) ;
2153
if constexpr ( Archive:: IS_LOADER) {
2154
oldLocksOn = ( oldCodeKanaLockOn ? KeyInfo:: CODE_MASK : 0 )
2155
| ( oldGraphLockOn ? KeyInfo:: GRAPH_MASK : 0 )
2156
| ( oldCapsLockOn ? KeyInfo:: CAPS_MASK : 0 ) ;
2157
}
2158
}
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
template < typename Archive>
2176
void Keyboard:: serialize ( Archive& ar , unsigned version )
2177
{
2178
ar. serialize ( " keyTypeCmd" , keyTypeCmd,
2179
" cmdKeyMatrix" , cmdKeyMatrix ) ;
2180
if ( ar. versionAtLeast ( version, 3 ) ) {
2181
ar. serialize ( " typeKeyMatrix" , typeKeyMatrix) ;
2182
} else {
2183
typeKeyMatrix = cmdKeyMatrix;
2184
}
2185
2186
bool msxCapsLockOn, msxCodeKanaLockOn, msxGraphLockOn;
2187
if constexpr ( ! Archive:: IS_LOADER) {
2188
msxCapsLockOn = locksOn & KeyInfo:: CAPS_MASK;
2189
msxCodeKanaLockOn = locksOn & KeyInfo:: CODE_MASK;
2190
msxGraphLockOn = locksOn & KeyInfo:: GRAPH_MASK;
2191
}
2192
ar. serialize ( " msxCapsLockOn" , msxCapsLockOn,
2193
" msxCodeKanaLockOn" , msxCodeKanaLockOn,
2194
" msxGraphLockOn" , msxGraphLockOn ) ;
2195
if constexpr ( Archive:: IS_LOADER) {
2196
locksOn = ( msxCapsLockOn ? KeyInfo:: CAPS_MASK : 0 )
2197
| ( msxCodeKanaLockOn ? KeyInfo:: CODE_MASK : 0 )
2198
| ( msxGraphLockOn ? KeyInfo:: GRAPH_MASK : 0 ) ;
2199
}
2200
2201
if ( ar. versionAtLeast ( version, 2 ) ) {
2202
ar. serialize ( " userKeyMatrix" , userKeyMatrix,
2203
" msxmodifiers" , msxModifiers,
2204
" msxKeyEventQueue" , msxKeyEventQueue ) ;
2205
}
2206
if ( ar. versionAtLeast ( version, 5 ) ) {
2207
ar. serialize ( " lastUnicodeForScancode" , lastUnicodeForScancode) ;
2208
}
2209
else if ( ar. versionAtLeast ( version, 4 ) ) {
2210
2211
std:: vector< std:: pair< SDL_Keycode, uint32_t > > lastUnicodeForKeycode;
2212
ar. serialize ( " lastUnicodeForKeycode" , lastUnicodeForKeycode) ;
2213
lastUnicodeForScancode. clear ( ) ;
2214
for ( const auto & [ keyCode, unicode] : lastUnicodeForKeycode) {
2215
auto scanCode = SDL_GetScancodeFromKey ( keyCode ) ;
2216
lastUnicodeForScancode. emplace_back ( scanCode, unicode ) ;
2217
}
2218
} else {
2219
2220
2221
2222
2223
2224
2225
2226
2227
}
2228
2229
2230
if constexpr ( Archive:: IS_LOADER) {
2231
2232
keysChanged = true ;
2233
}
2234
}
2235
INSTANTIATE_SERIALIZE_METHODS ( Keyboard ) ;
2236
2237
template < typename Archive>
2238
void Keyboard:: MsxKeyEventQueue:: serialize ( Archive& ar , unsigned )
2239
{
2240
ar. template serializeBase < Schedulable> ( * this ) ;
2241
2242
2243
2244
2245
2246
2247
2248
2249
std:: vector< std:: string> eventStrs;
2250
if constexpr ( ! Archive:: IS_LOADER) {
2251
eventStrs = to_vector ( std:: views:: transform (
2252
eventQueue, [ ] ( const auto & e) { return toString ( e ) ; } ) ) ;
2253
}
2254
ar. serialize ( " eventQueue" , eventStrs) ;
2255
if constexpr ( Archive:: IS_LOADER) {
2256
assert ( eventQueue. empty ( ) ) ;
2257
for ( const auto & s : eventStrs) {
2258
eventQueue. push_back (
2259
InputEventFactory:: createInputEvent ( s, interp ) ) ;
2260
}
2261
}
2262
}
2263
INSTANTIATE_SERIALIZE_METHODS ( Keyboard:: MsxKeyEventQueue ) ;
2264
2265
}