1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#include "SDL_keymap_c.h"
24#include "SDL_keyboard_c.h"
25
26struct SDL_Keymap
27{
28 SDL_HashTable *scancode_to_keycode;
29 SDL_HashTable *keycode_to_scancode;
30};
31
32static SDL_Keycode SDL_GetDefaultKeyFromScancode(SDL_Scancode scancode, SDL_Keymod modstate);
33static SDL_Scancode SDL_GetDefaultScancodeFromKey(SDL_Keycode key, SDL_Keymod *modstate);
34
35SDL_Keymap *SDL_CreateKeymap(void)
36{
37 SDL_Keymap *keymap = (SDL_Keymap *)SDL_malloc(sizeof(*keymap));
38 if (!keymap) {
39 return NULL;
40 }
41
42 keymap->scancode_to_keycode = SDL_CreateHashTable(256, false, SDL_HashID, SDL_KeyMatchID, NULL, NULL);
43 keymap->keycode_to_scancode = SDL_CreateHashTable(256, false, SDL_HashID, SDL_KeyMatchID, NULL, NULL);
44 if (!keymap->scancode_to_keycode || !keymap->keycode_to_scancode) {
45 SDL_DestroyKeymap(keymap);
46 return NULL;
47 }
48 return keymap;
49}
50
51static SDL_Keymod NormalizeModifierStateForKeymap(SDL_Keymod modstate)
52{
53 // The modifiers that affect the keymap are: SHIFT, CAPS, ALT, MODE, and LEVEL5
54 modstate &= (SDL_KMOD_SHIFT | SDL_KMOD_CAPS | SDL_KMOD_ALT | SDL_KMOD_MODE | SDL_KMOD_LEVEL5);
55
56 // If either right or left Shift are set, set both in the output
57 if (modstate & SDL_KMOD_SHIFT) {
58 modstate |= SDL_KMOD_SHIFT;
59 }
60
61 // If either right or left Alt are set, set both in the output
62 if (modstate & SDL_KMOD_ALT) {
63 modstate |= SDL_KMOD_ALT;
64 }
65
66 return modstate;
67}
68
69void SDL_SetKeymapEntry(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod modstate, SDL_Keycode keycode)
70{
71 if (!keymap) {
72 return;
73 }
74
75 modstate = NormalizeModifierStateForKeymap(modstate);
76 Uint32 key = ((Uint32)modstate << 16) | scancode;
77 const void *value;
78 if (SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) {
79 const SDL_Keycode existing_keycode = (SDL_Keycode)(uintptr_t)value;
80 if (existing_keycode == keycode) {
81 // We already have this mapping
82 return;
83 }
84 // InsertIntoHashTable will replace the existing entry in the keymap atomically.
85 }
86 SDL_InsertIntoHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, (void *)(uintptr_t)keycode, true);
87
88 bool update_keycode = true;
89 if (SDL_FindInHashTable(keymap->keycode_to_scancode, (void *)(uintptr_t)keycode, &value)) {
90 const Uint32 existing_value = (Uint32)(uintptr_t)value;
91 const SDL_Keymod existing_modstate = (SDL_Keymod)(existing_value >> 16);
92
93 // Keep the simplest combination of scancode and modifiers to generate this keycode
94 if (existing_modstate <= modstate) {
95 update_keycode = false;
96 }
97 }
98 if (update_keycode) {
99 SDL_InsertIntoHashTable(keymap->keycode_to_scancode, (void *)(uintptr_t)keycode, (void *)(uintptr_t)key, true);
100 }
101}
102
103SDL_Keycode SDL_GetKeymapKeycode(SDL_Keymap *keymap, SDL_Scancode scancode, SDL_Keymod modstate)
104{
105 SDL_Keycode keycode;
106
107 const Uint32 key = ((Uint32)NormalizeModifierStateForKeymap(modstate) << 16) | scancode;
108 const void *value;
109 if (keymap && SDL_FindInHashTable(keymap->scancode_to_keycode, (void *)(uintptr_t)key, &value)) {
110 keycode = (SDL_Keycode)(uintptr_t)value;
111 } else {
112 keycode = SDL_GetDefaultKeyFromScancode(scancode, modstate);
113 }
114 return keycode;
115}
116
117SDL_Scancode SDL_GetKeymapScancode(SDL_Keymap *keymap, SDL_Keycode keycode, SDL_Keymod *modstate)
118{
119 SDL_Scancode scancode;
120
121 const void *value;
122 if (keymap && SDL_FindInHashTable(keymap->keycode_to_scancode, (void *)(uintptr_t)keycode, &value)) {
123 scancode = (SDL_Scancode)((uintptr_t)value & 0xFFFF);
124 if (modstate) {
125 *modstate = (SDL_Keymod)((uintptr_t)value >> 16);
126 }
127 } else {
128 scancode = SDL_GetDefaultScancodeFromKey(keycode, modstate);
129 }
130 return scancode;
131}
132
133void SDL_DestroyKeymap(SDL_Keymap *keymap)
134{
135 if (!keymap) {
136 return;
137 }
138
139 SDL_DestroyHashTable(keymap->scancode_to_keycode);
140 SDL_DestroyHashTable(keymap->keycode_to_scancode);
141 SDL_free(keymap);
142}
143
144static const SDL_Keycode normal_default_symbols[] = {
145 SDLK_1,
146 SDLK_2,
147 SDLK_3,
148 SDLK_4,
149 SDLK_5,
150 SDLK_6,
151 SDLK_7,
152 SDLK_8,
153 SDLK_9,
154 SDLK_0,
155 SDLK_RETURN,
156 SDLK_ESCAPE,
157 SDLK_BACKSPACE,
158 SDLK_TAB,
159 SDLK_SPACE,
160 SDLK_MINUS,
161 SDLK_EQUALS,
162 SDLK_LEFTBRACKET,
163 SDLK_RIGHTBRACKET,
164 SDLK_BACKSLASH,
165 SDLK_HASH,
166 SDLK_SEMICOLON,
167 SDLK_APOSTROPHE,
168 SDLK_GRAVE,
169 SDLK_COMMA,
170 SDLK_PERIOD,
171 SDLK_SLASH,
172};
173
174static const SDL_Keycode shifted_default_symbols[] = {
175 SDLK_EXCLAIM,
176 SDLK_AT,
177 SDLK_HASH,
178 SDLK_DOLLAR,
179 SDLK_PERCENT,
180 SDLK_CARET,
181 SDLK_AMPERSAND,
182 SDLK_ASTERISK,
183 SDLK_LEFTPAREN,
184 SDLK_RIGHTPAREN,
185 SDLK_RETURN,
186 SDLK_ESCAPE,
187 SDLK_BACKSPACE,
188 SDLK_TAB,
189 SDLK_SPACE,
190 SDLK_UNDERSCORE,
191 SDLK_PLUS,
192 SDLK_LEFTBRACE,
193 SDLK_RIGHTBRACE,
194 SDLK_PIPE,
195 SDLK_HASH,
196 SDLK_COLON,
197 SDLK_DBLAPOSTROPHE,
198 SDLK_TILDE,
199 SDLK_LESS,
200 SDLK_GREATER,
201 SDLK_QUESTION
202};
203
204static const struct
205{
206 SDL_Keycode keycode;
207 SDL_Scancode scancode;
208} extended_default_symbols[] = {
209 { SDLK_LEFT_TAB, SDL_SCANCODE_TAB },
210 { SDLK_MULTI_KEY_COMPOSE, SDL_SCANCODE_APPLICATION }, // Sun keyboards
211 { SDLK_LMETA, SDL_SCANCODE_LGUI },
212 { SDLK_RMETA, SDL_SCANCODE_RGUI },
213 { SDLK_RHYPER, SDL_SCANCODE_APPLICATION }
214};
215
216static SDL_Keycode SDL_GetDefaultKeyFromScancode(SDL_Scancode scancode, SDL_Keymod modstate)
217{
218 if (((int)scancode) < SDL_SCANCODE_UNKNOWN || scancode >= SDL_SCANCODE_COUNT) {
219 SDL_InvalidParamError("scancode");
220 return SDLK_UNKNOWN;
221 }
222
223 if (scancode < SDL_SCANCODE_A) {
224 return SDLK_UNKNOWN;
225 }
226
227 if (scancode < SDL_SCANCODE_1) {
228 bool shifted = (modstate & SDL_KMOD_SHIFT) ? true : false;
229#ifdef SDL_PLATFORM_APPLE
230 // Apple maps to upper case for either shift or capslock inclusive
231 if (modstate & SDL_KMOD_CAPS) {
232 shifted = true;
233 }
234#else
235 if (modstate & SDL_KMOD_CAPS) {
236 shifted = !shifted;
237 }
238#endif
239 if (modstate & SDL_KMOD_MODE) {
240 return SDLK_UNKNOWN;
241 }
242 if (!shifted) {
243 return (SDL_Keycode)('a' + scancode - SDL_SCANCODE_A);
244 } else {
245 return (SDL_Keycode)('A' + scancode - SDL_SCANCODE_A);
246 }
247 }
248
249 if (scancode < SDL_SCANCODE_CAPSLOCK) {
250 bool shifted = (modstate & SDL_KMOD_SHIFT) ? true : false;
251
252 if (modstate & SDL_KMOD_MODE) {
253 return SDLK_UNKNOWN;
254 }
255 if (!shifted) {
256 return normal_default_symbols[scancode - SDL_SCANCODE_1];
257 } else {
258 return shifted_default_symbols[scancode - SDL_SCANCODE_1];
259 }
260 }
261
262 // These scancodes are not mapped to printable keycodes
263 switch (scancode) {
264 case SDL_SCANCODE_DELETE:
265 return SDLK_DELETE;
266 case SDL_SCANCODE_CAPSLOCK:
267 return SDLK_CAPSLOCK;
268 case SDL_SCANCODE_F1:
269 return SDLK_F1;
270 case SDL_SCANCODE_F2:
271 return SDLK_F2;
272 case SDL_SCANCODE_F3:
273 return SDLK_F3;
274 case SDL_SCANCODE_F4:
275 return SDLK_F4;
276 case SDL_SCANCODE_F5:
277 return SDLK_F5;
278 case SDL_SCANCODE_F6:
279 return SDLK_F6;
280 case SDL_SCANCODE_F7:
281 return SDLK_F7;
282 case SDL_SCANCODE_F8:
283 return SDLK_F8;
284 case SDL_SCANCODE_F9:
285 return SDLK_F9;
286 case SDL_SCANCODE_F10:
287 return SDLK_F10;
288 case SDL_SCANCODE_F11:
289 return SDLK_F11;
290 case SDL_SCANCODE_F12:
291 return SDLK_F12;
292 case SDL_SCANCODE_PRINTSCREEN:
293 return SDLK_PRINTSCREEN;
294 case SDL_SCANCODE_SCROLLLOCK:
295 return SDLK_SCROLLLOCK;
296 case SDL_SCANCODE_PAUSE:
297 return SDLK_PAUSE;
298 case SDL_SCANCODE_INSERT:
299 return SDLK_INSERT;
300 case SDL_SCANCODE_HOME:
301 return SDLK_HOME;
302 case SDL_SCANCODE_PAGEUP:
303 return SDLK_PAGEUP;
304 case SDL_SCANCODE_END:
305 return SDLK_END;
306 case SDL_SCANCODE_PAGEDOWN:
307 return SDLK_PAGEDOWN;
308 case SDL_SCANCODE_RIGHT:
309 return SDLK_RIGHT;
310 case SDL_SCANCODE_LEFT:
311 return SDLK_LEFT;
312 case SDL_SCANCODE_DOWN:
313 return SDLK_DOWN;
314 case SDL_SCANCODE_UP:
315 return SDLK_UP;
316 case SDL_SCANCODE_NUMLOCKCLEAR:
317 return SDLK_NUMLOCKCLEAR;
318 case SDL_SCANCODE_KP_DIVIDE:
319 return SDLK_KP_DIVIDE;
320 case SDL_SCANCODE_KP_MULTIPLY:
321 return SDLK_KP_MULTIPLY;
322 case SDL_SCANCODE_KP_MINUS:
323 return SDLK_KP_MINUS;
324 case SDL_SCANCODE_KP_PLUS:
325 return SDLK_KP_PLUS;
326 case SDL_SCANCODE_KP_ENTER:
327 return SDLK_KP_ENTER;
328 case SDL_SCANCODE_KP_1:
329 return SDLK_KP_1;
330 case SDL_SCANCODE_KP_2:
331 return SDLK_KP_2;
332 case SDL_SCANCODE_KP_3:
333 return SDLK_KP_3;
334 case SDL_SCANCODE_KP_4:
335 return SDLK_KP_4;
336 case SDL_SCANCODE_KP_5:
337 return SDLK_KP_5;
338 case SDL_SCANCODE_KP_6:
339 return SDLK_KP_6;
340 case SDL_SCANCODE_KP_7:
341 return SDLK_KP_7;
342 case SDL_SCANCODE_KP_8:
343 return SDLK_KP_8;
344 case SDL_SCANCODE_KP_9:
345 return SDLK_KP_9;
346 case SDL_SCANCODE_KP_0:
347 return SDLK_KP_0;
348 case SDL_SCANCODE_KP_PERIOD:
349 return SDLK_KP_PERIOD;
350 case SDL_SCANCODE_APPLICATION:
351 return SDLK_APPLICATION;
352 case SDL_SCANCODE_POWER:
353 return SDLK_POWER;
354 case SDL_SCANCODE_KP_EQUALS:
355 return SDLK_KP_EQUALS;
356 case SDL_SCANCODE_F13:
357 return SDLK_F13;
358 case SDL_SCANCODE_F14:
359 return SDLK_F14;
360 case SDL_SCANCODE_F15:
361 return SDLK_F15;
362 case SDL_SCANCODE_F16:
363 return SDLK_F16;
364 case SDL_SCANCODE_F17:
365 return SDLK_F17;
366 case SDL_SCANCODE_F18:
367 return SDLK_F18;
368 case SDL_SCANCODE_F19:
369 return SDLK_F19;
370 case SDL_SCANCODE_F20:
371 return SDLK_F20;
372 case SDL_SCANCODE_F21:
373 return SDLK_F21;
374 case SDL_SCANCODE_F22:
375 return SDLK_F22;
376 case SDL_SCANCODE_F23:
377 return SDLK_F23;
378 case SDL_SCANCODE_F24:
379 return SDLK_F24;
380 case SDL_SCANCODE_EXECUTE:
381 return SDLK_EXECUTE;
382 case SDL_SCANCODE_HELP:
383 return SDLK_HELP;
384 case SDL_SCANCODE_MENU:
385 return SDLK_MENU;
386 case SDL_SCANCODE_SELECT:
387 return SDLK_SELECT;
388 case SDL_SCANCODE_STOP:
389 return SDLK_STOP;
390 case SDL_SCANCODE_AGAIN:
391 return SDLK_AGAIN;
392 case SDL_SCANCODE_UNDO:
393 return SDLK_UNDO;
394 case SDL_SCANCODE_CUT:
395 return SDLK_CUT;
396 case SDL_SCANCODE_COPY:
397 return SDLK_COPY;
398 case SDL_SCANCODE_PASTE:
399 return SDLK_PASTE;
400 case SDL_SCANCODE_FIND:
401 return SDLK_FIND;
402 case SDL_SCANCODE_MUTE:
403 return SDLK_MUTE;
404 case SDL_SCANCODE_VOLUMEUP:
405 return SDLK_VOLUMEUP;
406 case SDL_SCANCODE_VOLUMEDOWN:
407 return SDLK_VOLUMEDOWN;
408 case SDL_SCANCODE_KP_COMMA:
409 return SDLK_KP_COMMA;
410 case SDL_SCANCODE_KP_EQUALSAS400:
411 return SDLK_KP_EQUALSAS400;
412 case SDL_SCANCODE_ALTERASE:
413 return SDLK_ALTERASE;
414 case SDL_SCANCODE_SYSREQ:
415 return SDLK_SYSREQ;
416 case SDL_SCANCODE_CANCEL:
417 return SDLK_CANCEL;
418 case SDL_SCANCODE_CLEAR:
419 return SDLK_CLEAR;
420 case SDL_SCANCODE_PRIOR:
421 return SDLK_PRIOR;
422 case SDL_SCANCODE_RETURN2:
423 return SDLK_RETURN2;
424 case SDL_SCANCODE_SEPARATOR:
425 return SDLK_SEPARATOR;
426 case SDL_SCANCODE_OUT:
427 return SDLK_OUT;
428 case SDL_SCANCODE_OPER:
429 return SDLK_OPER;
430 case SDL_SCANCODE_CLEARAGAIN:
431 return SDLK_CLEARAGAIN;
432 case SDL_SCANCODE_CRSEL:
433 return SDLK_CRSEL;
434 case SDL_SCANCODE_EXSEL:
435 return SDLK_EXSEL;
436 case SDL_SCANCODE_KP_00:
437 return SDLK_KP_00;
438 case SDL_SCANCODE_KP_000:
439 return SDLK_KP_000;
440 case SDL_SCANCODE_THOUSANDSSEPARATOR:
441 return SDLK_THOUSANDSSEPARATOR;
442 case SDL_SCANCODE_DECIMALSEPARATOR:
443 return SDLK_DECIMALSEPARATOR;
444 case SDL_SCANCODE_CURRENCYUNIT:
445 return SDLK_CURRENCYUNIT;
446 case SDL_SCANCODE_CURRENCYSUBUNIT:
447 return SDLK_CURRENCYSUBUNIT;
448 case SDL_SCANCODE_KP_LEFTPAREN:
449 return SDLK_KP_LEFTPAREN;
450 case SDL_SCANCODE_KP_RIGHTPAREN:
451 return SDLK_KP_RIGHTPAREN;
452 case SDL_SCANCODE_KP_LEFTBRACE:
453 return SDLK_KP_LEFTBRACE;
454 case SDL_SCANCODE_KP_RIGHTBRACE:
455 return SDLK_KP_RIGHTBRACE;
456 case SDL_SCANCODE_KP_TAB:
457 return SDLK_KP_TAB;
458 case SDL_SCANCODE_KP_BACKSPACE:
459 return SDLK_KP_BACKSPACE;
460 case SDL_SCANCODE_KP_A:
461 return SDLK_KP_A;
462 case SDL_SCANCODE_KP_B:
463 return SDLK_KP_B;
464 case SDL_SCANCODE_KP_C:
465 return SDLK_KP_C;
466 case SDL_SCANCODE_KP_D:
467 return SDLK_KP_D;
468 case SDL_SCANCODE_KP_E:
469 return SDLK_KP_E;
470 case SDL_SCANCODE_KP_F:
471 return SDLK_KP_F;
472 case SDL_SCANCODE_KP_XOR:
473 return SDLK_KP_XOR;
474 case SDL_SCANCODE_KP_POWER:
475 return SDLK_KP_POWER;
476 case SDL_SCANCODE_KP_PERCENT:
477 return SDLK_KP_PERCENT;
478 case SDL_SCANCODE_KP_LESS:
479 return SDLK_KP_LESS;
480 case SDL_SCANCODE_KP_GREATER:
481 return SDLK_KP_GREATER;
482 case SDL_SCANCODE_KP_AMPERSAND:
483 return SDLK_KP_AMPERSAND;
484 case SDL_SCANCODE_KP_DBLAMPERSAND:
485 return SDLK_KP_DBLAMPERSAND;
486 case SDL_SCANCODE_KP_VERTICALBAR:
487 return SDLK_KP_VERTICALBAR;
488 case SDL_SCANCODE_KP_DBLVERTICALBAR:
489 return SDLK_KP_DBLVERTICALBAR;
490 case SDL_SCANCODE_KP_COLON:
491 return SDLK_KP_COLON;
492 case SDL_SCANCODE_KP_HASH:
493 return SDLK_KP_HASH;
494 case SDL_SCANCODE_KP_SPACE:
495 return SDLK_KP_SPACE;
496 case SDL_SCANCODE_KP_AT:
497 return SDLK_KP_AT;
498 case SDL_SCANCODE_KP_EXCLAM:
499 return SDLK_KP_EXCLAM;
500 case SDL_SCANCODE_KP_MEMSTORE:
501 return SDLK_KP_MEMSTORE;
502 case SDL_SCANCODE_KP_MEMRECALL:
503 return SDLK_KP_MEMRECALL;
504 case SDL_SCANCODE_KP_MEMCLEAR:
505 return SDLK_KP_MEMCLEAR;
506 case SDL_SCANCODE_KP_MEMADD:
507 return SDLK_KP_MEMADD;
508 case SDL_SCANCODE_KP_MEMSUBTRACT:
509 return SDLK_KP_MEMSUBTRACT;
510 case SDL_SCANCODE_KP_MEMMULTIPLY:
511 return SDLK_KP_MEMMULTIPLY;
512 case SDL_SCANCODE_KP_MEMDIVIDE:
513 return SDLK_KP_MEMDIVIDE;
514 case SDL_SCANCODE_KP_PLUSMINUS:
515 return SDLK_KP_PLUSMINUS;
516 case SDL_SCANCODE_KP_CLEAR:
517 return SDLK_KP_CLEAR;
518 case SDL_SCANCODE_KP_CLEARENTRY:
519 return SDLK_KP_CLEARENTRY;
520 case SDL_SCANCODE_KP_BINARY:
521 return SDLK_KP_BINARY;
522 case SDL_SCANCODE_KP_OCTAL:
523 return SDLK_KP_OCTAL;
524 case SDL_SCANCODE_KP_DECIMAL:
525 return SDLK_KP_DECIMAL;
526 case SDL_SCANCODE_KP_HEXADECIMAL:
527 return SDLK_KP_HEXADECIMAL;
528 case SDL_SCANCODE_LCTRL:
529 return SDLK_LCTRL;
530 case SDL_SCANCODE_LSHIFT:
531 return SDLK_LSHIFT;
532 case SDL_SCANCODE_LALT:
533 return SDLK_LALT;
534 case SDL_SCANCODE_LGUI:
535 return SDLK_LGUI;
536 case SDL_SCANCODE_RCTRL:
537 return SDLK_RCTRL;
538 case SDL_SCANCODE_RSHIFT:
539 return SDLK_RSHIFT;
540 case SDL_SCANCODE_RALT:
541 return SDLK_RALT;
542 case SDL_SCANCODE_RGUI:
543 return SDLK_RGUI;
544 case SDL_SCANCODE_MODE:
545 return SDLK_MODE;
546 case SDL_SCANCODE_SLEEP:
547 return SDLK_SLEEP;
548 case SDL_SCANCODE_WAKE:
549 return SDLK_WAKE;
550 case SDL_SCANCODE_CHANNEL_INCREMENT:
551 return SDLK_CHANNEL_INCREMENT;
552 case SDL_SCANCODE_CHANNEL_DECREMENT:
553 return SDLK_CHANNEL_DECREMENT;
554 case SDL_SCANCODE_MEDIA_PLAY:
555 return SDLK_MEDIA_PLAY;
556 case SDL_SCANCODE_MEDIA_PAUSE:
557 return SDLK_MEDIA_PAUSE;
558 case SDL_SCANCODE_MEDIA_RECORD:
559 return SDLK_MEDIA_RECORD;
560 case SDL_SCANCODE_MEDIA_FAST_FORWARD:
561 return SDLK_MEDIA_FAST_FORWARD;
562 case SDL_SCANCODE_MEDIA_REWIND:
563 return SDLK_MEDIA_REWIND;
564 case SDL_SCANCODE_MEDIA_NEXT_TRACK:
565 return SDLK_MEDIA_NEXT_TRACK;
566 case SDL_SCANCODE_MEDIA_PREVIOUS_TRACK:
567 return SDLK_MEDIA_PREVIOUS_TRACK;
568 case SDL_SCANCODE_MEDIA_STOP:
569 return SDLK_MEDIA_STOP;
570 case SDL_SCANCODE_MEDIA_EJECT:
571 return SDLK_MEDIA_EJECT;
572 case SDL_SCANCODE_MEDIA_PLAY_PAUSE:
573 return SDLK_MEDIA_PLAY_PAUSE;
574 case SDL_SCANCODE_MEDIA_SELECT:
575 return SDLK_MEDIA_SELECT;
576 case SDL_SCANCODE_AC_NEW:
577 return SDLK_AC_NEW;
578 case SDL_SCANCODE_AC_OPEN:
579 return SDLK_AC_OPEN;
580 case SDL_SCANCODE_AC_CLOSE:
581 return SDLK_AC_CLOSE;
582 case SDL_SCANCODE_AC_EXIT:
583 return SDLK_AC_EXIT;
584 case SDL_SCANCODE_AC_SAVE:
585 return SDLK_AC_SAVE;
586 case SDL_SCANCODE_AC_PRINT:
587 return SDLK_AC_PRINT;
588 case SDL_SCANCODE_AC_PROPERTIES:
589 return SDLK_AC_PROPERTIES;
590 case SDL_SCANCODE_AC_SEARCH:
591 return SDLK_AC_SEARCH;
592 case SDL_SCANCODE_AC_HOME:
593 return SDLK_AC_HOME;
594 case SDL_SCANCODE_AC_BACK:
595 return SDLK_AC_BACK;
596 case SDL_SCANCODE_AC_FORWARD:
597 return SDLK_AC_FORWARD;
598 case SDL_SCANCODE_AC_STOP:
599 return SDLK_AC_STOP;
600 case SDL_SCANCODE_AC_REFRESH:
601 return SDLK_AC_REFRESH;
602 case SDL_SCANCODE_AC_BOOKMARKS:
603 return SDLK_AC_BOOKMARKS;
604 case SDL_SCANCODE_SOFTLEFT:
605 return SDLK_SOFTLEFT;
606 case SDL_SCANCODE_SOFTRIGHT:
607 return SDLK_SOFTRIGHT;
608 case SDL_SCANCODE_CALL:
609 return SDLK_CALL;
610 case SDL_SCANCODE_ENDCALL:
611 return SDLK_ENDCALL;
612 default:
613 return SDLK_UNKNOWN;
614 }
615}
616
617static SDL_Scancode SDL_GetDefaultScancodeFromKey(SDL_Keycode key, SDL_Keymod *modstate)
618{
619 if (modstate) {
620 *modstate = SDL_KMOD_NONE;
621 }
622
623 if (key == SDLK_UNKNOWN) {
624 return SDL_SCANCODE_UNKNOWN;
625 }
626
627 if (key & SDLK_EXTENDED_MASK) {
628 for (int i = 0; i < SDL_arraysize(extended_default_symbols); ++i) {
629 if (extended_default_symbols[i].keycode == key) {
630 return extended_default_symbols[i].scancode;
631 }
632 }
633
634 return SDL_SCANCODE_UNKNOWN;
635 }
636
637 if (key & SDLK_SCANCODE_MASK) {
638 return (SDL_Scancode)(key & ~SDLK_SCANCODE_MASK);
639 }
640
641 if (key >= SDLK_A && key <= SDLK_Z) {
642 return (SDL_Scancode)(SDL_SCANCODE_A + key - SDLK_A);
643 }
644
645 if (key >= 'A' && key <= 'Z') {
646 if (modstate) {
647 *modstate = SDL_KMOD_SHIFT;
648 }
649 return (SDL_Scancode)(SDL_SCANCODE_A + key - 'A');
650 }
651
652 for (int i = 0; i < SDL_arraysize(normal_default_symbols); ++i) {
653 if (key == normal_default_symbols[i]) {
654 return(SDL_Scancode)(SDL_SCANCODE_1 + i);
655 }
656 }
657
658 for (int i = 0; i < SDL_arraysize(shifted_default_symbols); ++i) {
659 if (key == shifted_default_symbols[i]) {
660 if (modstate) {
661 *modstate = SDL_KMOD_SHIFT;
662 }
663 return(SDL_Scancode)(SDL_SCANCODE_1 + i);
664 }
665 }
666
667 if (key == SDLK_DELETE) {
668 return SDL_SCANCODE_DELETE;
669 }
670
671 return SDL_SCANCODE_UNKNOWN;
672}
673
674static const char *SDL_scancode_names[SDL_SCANCODE_COUNT] =
675{
676 /* 0 */ NULL,
677 /* 1 */ NULL,
678 /* 2 */ NULL,
679 /* 3 */ NULL,
680 /* 4 */ "A",
681 /* 5 */ "B",
682 /* 6 */ "C",
683 /* 7 */ "D",
684 /* 8 */ "E",
685 /* 9 */ "F",
686 /* 10 */ "G",
687 /* 11 */ "H",
688 /* 12 */ "I",
689 /* 13 */ "J",
690 /* 14 */ "K",
691 /* 15 */ "L",
692 /* 16 */ "M",
693 /* 17 */ "N",
694 /* 18 */ "O",
695 /* 19 */ "P",
696 /* 20 */ "Q",
697 /* 21 */ "R",
698 /* 22 */ "S",
699 /* 23 */ "T",
700 /* 24 */ "U",
701 /* 25 */ "V",
702 /* 26 */ "W",
703 /* 27 */ "X",
704 /* 28 */ "Y",
705 /* 29 */ "Z",
706 /* 30 */ "1",
707 /* 31 */ "2",
708 /* 32 */ "3",
709 /* 33 */ "4",
710 /* 34 */ "5",
711 /* 35 */ "6",
712 /* 36 */ "7",
713 /* 37 */ "8",
714 /* 38 */ "9",
715 /* 39 */ "0",
716 /* 40 */ "Return",
717 /* 41 */ "Escape",
718 /* 42 */ "Backspace",
719 /* 43 */ "Tab",
720 /* 44 */ "Space",
721 /* 45 */ "-",
722 /* 46 */ "=",
723 /* 47 */ "[",
724 /* 48 */ "]",
725 /* 49 */ "\\",
726 /* 50 */ "#",
727 /* 51 */ ";",
728 /* 52 */ "'",
729 /* 53 */ "`",
730 /* 54 */ ",",
731 /* 55 */ ".",
732 /* 56 */ "/",
733 /* 57 */ "CapsLock",
734 /* 58 */ "F1",
735 /* 59 */ "F2",
736 /* 60 */ "F3",
737 /* 61 */ "F4",
738 /* 62 */ "F5",
739 /* 63 */ "F6",
740 /* 64 */ "F7",
741 /* 65 */ "F8",
742 /* 66 */ "F9",
743 /* 67 */ "F10",
744 /* 68 */ "F11",
745 /* 69 */ "F12",
746 /* 70 */ "PrintScreen",
747 /* 71 */ "ScrollLock",
748 /* 72 */ "Pause",
749 /* 73 */ "Insert",
750 /* 74 */ "Home",
751 /* 75 */ "PageUp",
752 /* 76 */ "Delete",
753 /* 77 */ "End",
754 /* 78 */ "PageDown",
755 /* 79 */ "Right",
756 /* 80 */ "Left",
757 /* 81 */ "Down",
758 /* 82 */ "Up",
759 /* 83 */ "Numlock",
760 /* 84 */ "Keypad /",
761 /* 85 */ "Keypad *",
762 /* 86 */ "Keypad -",
763 /* 87 */ "Keypad +",
764 /* 88 */ "Keypad Enter",
765 /* 89 */ "Keypad 1",
766 /* 90 */ "Keypad 2",
767 /* 91 */ "Keypad 3",
768 /* 92 */ "Keypad 4",
769 /* 93 */ "Keypad 5",
770 /* 94 */ "Keypad 6",
771 /* 95 */ "Keypad 7",
772 /* 96 */ "Keypad 8",
773 /* 97 */ "Keypad 9",
774 /* 98 */ "Keypad 0",
775 /* 99 */ "Keypad .",
776 /* 100 */ "NonUSBackslash",
777 /* 101 */ "Application",
778 /* 102 */ "Power",
779 /* 103 */ "Keypad =",
780 /* 104 */ "F13",
781 /* 105 */ "F14",
782 /* 106 */ "F15",
783 /* 107 */ "F16",
784 /* 108 */ "F17",
785 /* 109 */ "F18",
786 /* 110 */ "F19",
787 /* 111 */ "F20",
788 /* 112 */ "F21",
789 /* 113 */ "F22",
790 /* 114 */ "F23",
791 /* 115 */ "F24",
792 /* 116 */ "Execute",
793 /* 117 */ "Help",
794 /* 118 */ "Menu",
795 /* 119 */ "Select",
796 /* 120 */ "Stop",
797 /* 121 */ "Again",
798 /* 122 */ "Undo",
799 /* 123 */ "Cut",
800 /* 124 */ "Copy",
801 /* 125 */ "Paste",
802 /* 126 */ "Find",
803 /* 127 */ "Mute",
804 /* 128 */ "VolumeUp",
805 /* 129 */ "VolumeDown",
806 /* 130 */ NULL,
807 /* 131 */ NULL,
808 /* 132 */ NULL,
809 /* 133 */ "Keypad ,",
810 /* 134 */ "Keypad = (AS400)",
811 /* 135 */ "International 1",
812 /* 136 */ "International 2",
813 /* 137 */ "International 3",
814 /* 138 */ "International 4",
815 /* 139 */ "International 5",
816 /* 140 */ "International 6",
817 /* 141 */ "International 7",
818 /* 142 */ "International 8",
819 /* 143 */ "International 9",
820 /* 144 */ "Language 1",
821 /* 145 */ "Language 2",
822 /* 146 */ "Language 3",
823 /* 147 */ "Language 4",
824 /* 148 */ "Language 5",
825 /* 149 */ "Language 6",
826 /* 150 */ "Language 7",
827 /* 151 */ "Language 8",
828 /* 152 */ "Language 9",
829 /* 153 */ "AltErase",
830 /* 154 */ "SysReq",
831 /* 155 */ "Cancel",
832 /* 156 */ "Clear",
833 /* 157 */ "Prior",
834 /* 158 */ "Return",
835 /* 159 */ "Separator",
836 /* 160 */ "Out",
837 /* 161 */ "Oper",
838 /* 162 */ "Clear / Again",
839 /* 163 */ "CrSel",
840 /* 164 */ "ExSel",
841 /* 165 */ NULL,
842 /* 166 */ NULL,
843 /* 167 */ NULL,
844 /* 168 */ NULL,
845 /* 169 */ NULL,
846 /* 170 */ NULL,
847 /* 171 */ NULL,
848 /* 172 */ NULL,
849 /* 173 */ NULL,
850 /* 174 */ NULL,
851 /* 175 */ NULL,
852 /* 176 */ "Keypad 00",
853 /* 177 */ "Keypad 000",
854 /* 178 */ "ThousandsSeparator",
855 /* 179 */ "DecimalSeparator",
856 /* 180 */ "CurrencyUnit",
857 /* 181 */ "CurrencySubUnit",
858 /* 182 */ "Keypad (",
859 /* 183 */ "Keypad )",
860 /* 184 */ "Keypad {",
861 /* 185 */ "Keypad }",
862 /* 186 */ "Keypad Tab",
863 /* 187 */ "Keypad Backspace",
864 /* 188 */ "Keypad A",
865 /* 189 */ "Keypad B",
866 /* 190 */ "Keypad C",
867 /* 191 */ "Keypad D",
868 /* 192 */ "Keypad E",
869 /* 193 */ "Keypad F",
870 /* 194 */ "Keypad XOR",
871 /* 195 */ "Keypad ^",
872 /* 196 */ "Keypad %",
873 /* 197 */ "Keypad <",
874 /* 198 */ "Keypad >",
875 /* 199 */ "Keypad &",
876 /* 200 */ "Keypad &&",
877 /* 201 */ "Keypad |",
878 /* 202 */ "Keypad ||",
879 /* 203 */ "Keypad :",
880 /* 204 */ "Keypad #",
881 /* 205 */ "Keypad Space",
882 /* 206 */ "Keypad @",
883 /* 207 */ "Keypad !",
884 /* 208 */ "Keypad MemStore",
885 /* 209 */ "Keypad MemRecall",
886 /* 210 */ "Keypad MemClear",
887 /* 211 */ "Keypad MemAdd",
888 /* 212 */ "Keypad MemSubtract",
889 /* 213 */ "Keypad MemMultiply",
890 /* 214 */ "Keypad MemDivide",
891 /* 215 */ "Keypad +/-",
892 /* 216 */ "Keypad Clear",
893 /* 217 */ "Keypad ClearEntry",
894 /* 218 */ "Keypad Binary",
895 /* 219 */ "Keypad Octal",
896 /* 220 */ "Keypad Decimal",
897 /* 221 */ "Keypad Hexadecimal",
898 /* 222 */ NULL,
899 /* 223 */ NULL,
900 /* 224 */ "Left Ctrl",
901 /* 225 */ "Left Shift",
902 /* 226 */ "Left Alt",
903 /* 227 */ "Left GUI",
904 /* 228 */ "Right Ctrl",
905 /* 229 */ "Right Shift",
906 /* 230 */ "Right Alt",
907 /* 231 */ "Right GUI",
908 /* 232 */ NULL,
909 /* 233 */ NULL,
910 /* 234 */ NULL,
911 /* 235 */ NULL,
912 /* 236 */ NULL,
913 /* 237 */ NULL,
914 /* 238 */ NULL,
915 /* 239 */ NULL,
916 /* 240 */ NULL,
917 /* 241 */ NULL,
918 /* 242 */ NULL,
919 /* 243 */ NULL,
920 /* 244 */ NULL,
921 /* 245 */ NULL,
922 /* 246 */ NULL,
923 /* 247 */ NULL,
924 /* 248 */ NULL,
925 /* 249 */ NULL,
926 /* 250 */ NULL,
927 /* 251 */ NULL,
928 /* 252 */ NULL,
929 /* 253 */ NULL,
930 /* 254 */ NULL,
931 /* 255 */ NULL,
932 /* 256 */ NULL,
933 /* 257 */ "ModeSwitch",
934 /* 258 */ "Sleep",
935 /* 259 */ "Wake",
936 /* 260 */ "ChannelUp",
937 /* 261 */ "ChannelDown",
938 /* 262 */ "MediaPlay",
939 /* 263 */ "MediaPause",
940 /* 264 */ "MediaRecord",
941 /* 265 */ "MediaFastForward",
942 /* 266 */ "MediaRewind",
943 /* 267 */ "MediaTrackNext",
944 /* 268 */ "MediaTrackPrevious",
945 /* 269 */ "MediaStop",
946 /* 270 */ "Eject",
947 /* 271 */ "MediaPlayPause",
948 /* 272 */ "MediaSelect",
949 /* 273 */ "AC New",
950 /* 274 */ "AC Open",
951 /* 275 */ "AC Close",
952 /* 276 */ "AC Exit",
953 /* 277 */ "AC Save",
954 /* 278 */ "AC Print",
955 /* 279 */ "AC Properties",
956 /* 280 */ "AC Search",
957 /* 281 */ "AC Home",
958 /* 282 */ "AC Back",
959 /* 283 */ "AC Forward",
960 /* 284 */ "AC Stop",
961 /* 285 */ "AC Refresh",
962 /* 286 */ "AC Bookmarks",
963 /* 287 */ "SoftLeft",
964 /* 288 */ "SoftRight",
965 /* 289 */ "Call",
966 /* 290 */ "EndCall",
967};
968
969static const char *SDL_extended_key_names[] = {
970 "LeftTab", /* 0x01 SDLK_LEFT_TAB */
971 "Level5Shift", /* 0x02 SDLK_LEVEL5_SHIFT */
972 "MultiKeyCompose", /* 0x03 SDLK_MULTI_KEY_COMPOSE */
973 "Left Meta", /* 0x04 SDLK_LMETA */
974 "Right Meta", /* 0x05 SDLK_RMETA */
975 "Left Hyper", /* 0x06 SDLK_LHYPER */
976 "Right Hyper" /* 0x07 SDLK_RHYPER */
977};
978
979bool SDL_SetScancodeName(SDL_Scancode scancode, const char *name)
980{
981 if (((int)scancode) < SDL_SCANCODE_UNKNOWN || scancode >= SDL_SCANCODE_COUNT) {
982 return SDL_InvalidParamError("scancode");
983 }
984
985 SDL_scancode_names[scancode] = name;
986 return true;
987}
988
989const char *SDL_GetScancodeName(SDL_Scancode scancode)
990{
991 const char *name;
992 if (((int)scancode) < SDL_SCANCODE_UNKNOWN || scancode >= SDL_SCANCODE_COUNT) {
993 SDL_InvalidParamError("scancode");
994 return "";
995 }
996
997 name = SDL_scancode_names[scancode];
998 if (!name) {
999 name = "";
1000 }
1001 // This is pointing to static memory or application managed memory
1002 return name;
1003}
1004
1005SDL_Scancode SDL_GetScancodeFromName(const char *name)
1006{
1007 int i;
1008
1009 if (!name || !*name) {
1010 SDL_InvalidParamError("name");
1011 return SDL_SCANCODE_UNKNOWN;
1012 }
1013
1014 for (i = 0; i < SDL_arraysize(SDL_scancode_names); ++i) {
1015 if (!SDL_scancode_names[i]) {
1016 continue;
1017 }
1018 if (SDL_strcasecmp(name, SDL_scancode_names[i]) == 0) {
1019 return (SDL_Scancode)i;
1020 }
1021 }
1022
1023 SDL_InvalidParamError("name");
1024 return SDL_SCANCODE_UNKNOWN;
1025}
1026
1027const char *SDL_GetKeyName(SDL_Keycode key)
1028{
1029 const bool uppercase = true;
1030 char name[8];
1031 char *end;
1032
1033 if (key & SDLK_SCANCODE_MASK) {
1034 return SDL_GetScancodeName((SDL_Scancode)(key & ~SDLK_SCANCODE_MASK));
1035 }
1036
1037 if (key & SDLK_EXTENDED_MASK) {
1038 const SDL_Keycode idx = (key & ~SDLK_EXTENDED_MASK);
1039 if (idx > 0 && (idx - 1) < SDL_arraysize(SDL_extended_key_names)) {
1040 return SDL_extended_key_names[idx - 1];
1041 }
1042
1043 // Key out of name index bounds.
1044 SDL_InvalidParamError("key");
1045 return "";
1046 }
1047
1048 switch (key) {
1049 case SDLK_RETURN:
1050 return SDL_GetScancodeName(SDL_SCANCODE_RETURN);
1051 case SDLK_ESCAPE:
1052 return SDL_GetScancodeName(SDL_SCANCODE_ESCAPE);
1053 case SDLK_BACKSPACE:
1054 return SDL_GetScancodeName(SDL_SCANCODE_BACKSPACE);
1055 case SDLK_TAB:
1056 return SDL_GetScancodeName(SDL_SCANCODE_TAB);
1057 case SDLK_SPACE:
1058 return SDL_GetScancodeName(SDL_SCANCODE_SPACE);
1059 case SDLK_DELETE:
1060 return SDL_GetScancodeName(SDL_SCANCODE_DELETE);
1061 default:
1062 if (uppercase) {
1063 // SDL_Keycode is defined as the unshifted key on the keyboard,
1064 // but the key name is defined as the letter printed on that key,
1065 // which is usually the shifted capital letter.
1066 if (key > 0x7F || (key >= 'a' && key <= 'z')) {
1067 SDL_Keymap *keymap = SDL_GetCurrentKeymap();
1068 SDL_Keymod modstate;
1069 SDL_Scancode scancode = SDL_GetKeymapScancode(keymap, key, &modstate);
1070 if (scancode != SDL_SCANCODE_UNKNOWN && !(modstate & SDL_KMOD_SHIFT)) {
1071 SDL_Keycode capital = SDL_GetKeymapKeycode(keymap, scancode, SDL_KMOD_SHIFT);
1072 if (capital > 0x7F || (capital >= 'A' && capital <= 'Z')) {
1073 key = capital;
1074 }
1075 }
1076 }
1077 }
1078
1079 end = SDL_UCS4ToUTF8(key, name);
1080 *end = '\0';
1081 return SDL_GetPersistentString(name);
1082 }
1083}
1084
1085SDL_Keycode SDL_GetKeyFromName(const char *name)
1086{
1087 const bool uppercase = true;
1088 SDL_Keycode key;
1089
1090 // Check input
1091 if (!name) {
1092 return SDLK_UNKNOWN;
1093 }
1094
1095 // If it's a single UTF-8 character, then that's the keycode itself
1096 key = *(const unsigned char *)name;
1097 if (key >= 0xF0) {
1098 if (SDL_strlen(name) == 4) {
1099 int i = 0;
1100 key = (Uint16)(name[i] & 0x07) << 18;
1101 key |= (Uint16)(name[++i] & 0x3F) << 12;
1102 key |= (Uint16)(name[++i] & 0x3F) << 6;
1103 key |= (Uint16)(name[++i] & 0x3F);
1104 } else {
1105 key = SDLK_UNKNOWN;
1106 }
1107 } else if (key >= 0xE0) {
1108 if (SDL_strlen(name) == 3) {
1109 int i = 0;
1110 key = (Uint16)(name[i] & 0x0F) << 12;
1111 key |= (Uint16)(name[++i] & 0x3F) << 6;
1112 key |= (Uint16)(name[++i] & 0x3F);
1113 } else {
1114 key = SDLK_UNKNOWN;
1115 }
1116 } else if (key >= 0xC0) {
1117 if (SDL_strlen(name) == 2) {
1118 int i = 0;
1119 key = (Uint16)(name[i] & 0x1F) << 6;
1120 key |= (Uint16)(name[++i] & 0x3F);
1121 } else {
1122 key = SDLK_UNKNOWN;
1123 }
1124 } else {
1125 if (SDL_strlen(name) != 1) {
1126 key = SDLK_UNKNOWN;
1127 }
1128 }
1129
1130 if (key != SDLK_UNKNOWN) {
1131 if (uppercase) {
1132 // SDL_Keycode is defined as the unshifted key on the keyboard,
1133 // but the key name is defined as the letter printed on that key,
1134 // which is usually the shifted capital letter.
1135 SDL_Keymap *keymap = SDL_GetCurrentKeymap();
1136 SDL_Keymod modstate;
1137 SDL_Scancode scancode = SDL_GetKeymapScancode(keymap, key, &modstate);
1138 if (scancode != SDL_SCANCODE_UNKNOWN && (modstate & (SDL_KMOD_SHIFT | SDL_KMOD_CAPS))) {
1139 key = SDL_GetKeymapKeycode(keymap, scancode, SDL_KMOD_NONE);
1140 }
1141 }
1142 return key;
1143 }
1144
1145 // Check the extended key names
1146 for (SDL_Keycode i = 0; i < SDL_arraysize(SDL_extended_key_names); ++i) {
1147 if (SDL_strcasecmp(name, SDL_extended_key_names[i]) == 0) {
1148 return (i + 1) | SDLK_EXTENDED_MASK;
1149 }
1150 }
1151
1152 return SDL_GetKeyFromScancode(SDL_GetScancodeFromName(name), SDL_KMOD_NONE, false);
1153}
1154