Atlas - SDL_keyboard.c
Home / ext / SDL / src / events Lines: 2 | Size: 28267 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 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// General keyboard handling code for SDL 24 25#include "SDL_events_c.h" 26#include "SDL_keymap_c.h" 27#include "../video/SDL_sysvideo.h" 28 29#if 0 30#define DEBUG_KEYBOARD 31#endif 32 33// Global keyboard information 34 35#define KEYBOARD_HARDWARE 0x01 36#define KEYBOARD_VIRTUAL 0x02 37#define KEYBOARD_AUTORELEASE 0x04 38#define KEYBOARD_IGNOREMODIFIERS 0x08 39 40#define KEYBOARD_SOURCE_MASK (KEYBOARD_HARDWARE | KEYBOARD_AUTORELEASE) 41 42#define KEYCODE_OPTION_HIDE_NUMPAD 0x01 43#define KEYCODE_OPTION_FRENCH_NUMBERS 0x02 44#define KEYCODE_OPTION_LATIN_LETTERS 0x04 45#define DEFAULT_KEYCODE_OPTIONS (KEYCODE_OPTION_FRENCH_NUMBERS | KEYCODE_OPTION_LATIN_LETTERS) 46 47typedef struct SDL_Keyboard 48{ 49 // Data common to all keyboards 50 SDL_Window *focus; 51 SDL_Keymod modstate; 52 Uint8 keysource[SDL_SCANCODE_COUNT]; 53 bool keystate[SDL_SCANCODE_COUNT]; 54 SDL_Keymap *keymap; 55 Uint32 keycode_options; 56 bool autorelease_pending; 57 Uint64 hardware_timestamp; 58} SDL_Keyboard; 59 60static SDL_Keyboard SDL_keyboard; 61static int SDL_keyboard_count; 62static SDL_KeyboardID *SDL_keyboards; 63static SDL_HashTable *SDL_keyboard_names; 64static bool SDL_keyboard_quitting; 65 66static void SDLCALL SDL_KeycodeOptionsChanged(void *userdata, const char *name, const char *oldValue, const char *hint) 67{ 68 SDL_Keyboard *keyboard = (SDL_Keyboard *)userdata; 69 70 if (hint && *hint) { 71 keyboard->keycode_options = 0; 72 if (!SDL_strstr(hint, "none")) { 73 if (SDL_strstr(hint, "hide_numpad")) { 74 keyboard->keycode_options |= KEYCODE_OPTION_HIDE_NUMPAD; 75 } 76 if (SDL_strstr(hint, "french_numbers")) { 77 keyboard->keycode_options |= KEYCODE_OPTION_FRENCH_NUMBERS; 78 } 79 if (SDL_strstr(hint, "latin_letters")) { 80 keyboard->keycode_options |= KEYCODE_OPTION_LATIN_LETTERS; 81 } 82 } 83 } else { 84 keyboard->keycode_options = DEFAULT_KEYCODE_OPTIONS; 85 } 86} 87 88// Public functions 89bool SDL_InitKeyboard(void) 90{ 91 SDL_AddHintCallback(SDL_HINT_KEYCODE_OPTIONS, 92 SDL_KeycodeOptionsChanged, &SDL_keyboard); 93 94 SDL_keyboard_names = SDL_CreateHashTable(0, true, SDL_HashID, SDL_KeyMatchID, SDL_DestroyHashValue, NULL); 95 96 return true; 97} 98 99bool SDL_IsKeyboard(Uint16 vendor, Uint16 product, int num_keys) 100{ 101 const int REAL_KEYBOARD_KEY_COUNT = 50; 102 if (num_keys > 0 && num_keys < REAL_KEYBOARD_KEY_COUNT) { 103 return false; 104 } 105 106 // Eventually we'll have a blacklist of devices that enumerate as keyboards but aren't really 107 return true; 108} 109 110static int SDL_GetKeyboardIndex(SDL_KeyboardID keyboardID) 111{ 112 for (int i = 0; i < SDL_keyboard_count; ++i) { 113 if (keyboardID == SDL_keyboards[i]) { 114 return i; 115 } 116 } 117 return -1; 118} 119 120void SDL_AddKeyboard(SDL_KeyboardID keyboardID, const char *name) 121{ 122 int keyboard_index = SDL_GetKeyboardIndex(keyboardID); 123 if (keyboard_index >= 0) { 124 // We already know about this keyboard 125 return; 126 } 127 128 SDL_assert(keyboardID != 0); 129 130 SDL_KeyboardID *keyboards = (SDL_KeyboardID *)SDL_realloc(SDL_keyboards, (SDL_keyboard_count + 1) * sizeof(*keyboards)); 131 if (!keyboards) { 132 return; 133 } 134 keyboards[SDL_keyboard_count] = keyboardID; 135 SDL_keyboards = keyboards; 136 ++SDL_keyboard_count; 137 138 if (!name) { 139 name = "Keyboard"; 140 } 141 SDL_InsertIntoHashTable(SDL_keyboard_names, (const void *)(uintptr_t)keyboardID, SDL_strdup(name), true); 142 143 SDL_Event event; 144 SDL_zero(event); 145 event.type = SDL_EVENT_KEYBOARD_ADDED; 146 event.kdevice.which = keyboardID; 147 SDL_PushEvent(&event); 148} 149 150void SDL_RemoveKeyboard(SDL_KeyboardID keyboardID) 151{ 152 int keyboard_index = SDL_GetKeyboardIndex(keyboardID); 153 if (keyboard_index < 0) { 154 // We don't know about this keyboard 155 return; 156 } 157 158 if (keyboard_index != SDL_keyboard_count - 1) { 159 SDL_memmove(&SDL_keyboards[keyboard_index], &SDL_keyboards[keyboard_index + 1], (SDL_keyboard_count - keyboard_index - 1) * sizeof(SDL_keyboards[keyboard_index])); 160 } 161 --SDL_keyboard_count; 162 163 if (!SDL_keyboard_quitting) { 164 SDL_Event event; 165 SDL_zero(event); 166 event.type = SDL_EVENT_KEYBOARD_REMOVED; 167 event.kdevice.which = keyboardID; 168 SDL_PushEvent(&event); 169 } 170} 171 172bool SDL_HasKeyboard(void) 173{ 174 return (SDL_keyboard_count > 0); 175} 176 177SDL_KeyboardID *SDL_GetKeyboards(int *count) 178{ 179 int i; 180 SDL_KeyboardID *keyboards; 181 182 keyboards = (SDL_KeyboardID *)SDL_malloc((SDL_keyboard_count + 1) * sizeof(*keyboards)); 183 if (keyboards) { 184 if (count) { 185 *count = SDL_keyboard_count; 186 } 187 188 for (i = 0; i < SDL_keyboard_count; ++i) { 189 keyboards[i] = SDL_keyboards[i]; 190 } 191 keyboards[i] = 0; 192 } else { 193 if (count) { 194 *count = 0; 195 } 196 } 197 198 return keyboards; 199} 200 201const char *SDL_GetKeyboardNameForID(SDL_KeyboardID instance_id) 202{ 203 const char *name = NULL; 204 if (!SDL_FindInHashTable(SDL_keyboard_names, (const void *)(uintptr_t)instance_id, (const void **)&name)) { 205 SDL_SetError("Keyboard %" SDL_PRIu32 " not found", instance_id); 206 return NULL; 207 } 208 if (!name) { 209 // SDL_strdup() failed during insert 210 SDL_OutOfMemory(); 211 return NULL; 212 } 213 return name; 214} 215 216void SDL_ResetKeyboard(void) 217{ 218 SDL_Keyboard *keyboard = &SDL_keyboard; 219 int scancode; 220 221#ifdef DEBUG_KEYBOARD 222 SDL_Log("Resetting keyboard"); 223#endif 224 for (scancode = SDL_SCANCODE_UNKNOWN; scancode < SDL_SCANCODE_COUNT; ++scancode) { 225 if (keyboard->keystate[scancode]) { 226 SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, (SDL_Scancode)scancode, false); 227 } 228 } 229} 230 231SDL_Keymap *SDL_GetCurrentKeymap(bool ignore_options) 232{ 233 SDL_Keyboard *keyboard = &SDL_keyboard; 234 SDL_Keymap *keymap = SDL_keyboard.keymap; 235 236 if (!ignore_options) { 237 if (keymap && keymap->thai_keyboard) { 238 // Thai keyboards are QWERTY plus Thai characters, use the default QWERTY keymap 239 return NULL; 240 } 241 242 if ((keyboard->keycode_options & KEYCODE_OPTION_LATIN_LETTERS) && 243 keymap && !keymap->latin_letters) { 244 // We'll use the default QWERTY keymap 245 return NULL; 246 } 247 } 248 249 return keyboard->keymap; 250} 251 252void SDL_SetKeymap(SDL_Keymap *keymap, bool send_event) 253{ 254 SDL_Keyboard *keyboard = &SDL_keyboard; 255 256 if (keyboard->keymap && keyboard->keymap->auto_release) { 257 SDL_DestroyKeymap(keyboard->keymap); 258 } 259 260 keyboard->keymap = keymap; 261 262 if (keymap && !keymap->layout_determined) { 263 keymap->layout_determined = true; 264 265 // Detect French number row (all symbols) 266 keymap->french_numbers = true; 267 for (int i = SDL_SCANCODE_1; i <= SDL_SCANCODE_0; ++i) { 268 if (SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE)) || 269 !SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_SHIFT))) { 270 keymap->french_numbers = false; 271 break; 272 } 273 } 274 275 // Detect non-Latin keymap 276 keymap->thai_keyboard = false; 277 keymap->latin_letters = false; 278 for (int i = SDL_SCANCODE_A; i <= SDL_SCANCODE_D; ++i) { 279 SDL_Keycode key = SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE); 280 if (key <= 0xFF) { 281 keymap->latin_letters = true; 282 break; 283 } 284 285 if (key >= 0x0E00 && key <= 0x0E7F) { 286 keymap->thai_keyboard = true; 287 break; 288 } 289 } 290 } 291 292 if (send_event) { 293 SDL_SendKeymapChangedEvent(); 294 } 295} 296 297static SDL_Scancode GetNextReservedScancode(void) 298{ 299 SDL_Keyboard *keyboard = &SDL_keyboard; 300 301 if (!keyboard->keymap) { 302 keyboard->keymap = SDL_CreateKeymap(true); 303 } 304 305 return SDL_GetKeymapNextReservedScancode(keyboard->keymap); 306} 307 308static void SetKeymapEntry(SDL_Scancode scancode, SDL_Keymod modstate, SDL_Keycode keycode) 309{ 310 SDL_Keyboard *keyboard = &SDL_keyboard; 311 312 if (!keyboard->keymap) { 313 keyboard->keymap = SDL_CreateKeymap(true); 314 } 315 316 SDL_SetKeymapEntry(keyboard->keymap, scancode, modstate, keycode); 317} 318 319SDL_Window *SDL_GetKeyboardFocus(void) 320{ 321 SDL_Keyboard *keyboard = &SDL_keyboard; 322 323 return keyboard->focus; 324} 325 326bool SDL_SetKeyboardFocus(SDL_Window *window) 327{ 328#if !defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_ANDROID) 329 SDL_VideoDevice *video = SDL_GetVideoDevice(); 330#endif 331 SDL_Keyboard *keyboard = &SDL_keyboard; 332 SDL_Mouse *mouse = SDL_GetMouse(); 333 334 if (window) { 335 if (!SDL_ObjectValid(window, SDL_OBJECT_TYPE_WINDOW) || window->is_destroying) { 336 return SDL_SetError("Invalid window"); 337 } 338 } 339 340 if (keyboard->focus && !window) { 341 // We won't get anymore keyboard messages, so reset keyboard state 342 SDL_ResetKeyboard(); 343 } 344 345 // See if the current window has lost focus 346 if (keyboard->focus && keyboard->focus != window) { 347 SDL_SendWindowEvent(keyboard->focus, SDL_EVENT_WINDOW_FOCUS_LOST, 0, 0); 348 349#if !defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_ANDROID) 350 // Ensures IME compositions are committed 351 if (SDL_TextInputActive(keyboard->focus)) { 352 if (video && video->StopTextInput) { 353 video->StopTextInput(video, keyboard->focus); 354 } 355 } 356#endif // !SDL_PLATFORM_IOS && !SDL_PLATFORM_ANDROID 357 } 358 359 if (keyboard->focus && !window) { 360 // Also leave mouse relative mode 361 if (mouse->relative_mode) { 362 SDL_SetRelativeMouseMode(false); 363 364 SDL_Window *focus = keyboard->focus; 365 if ((focus->flags & SDL_WINDOW_MINIMIZED) != 0) { 366 // We can't warp the mouse within minimized windows, so manually restore the position 367 float x = focus->x + mouse->x; 368 float y = focus->y + mouse->y; 369 SDL_WarpMouseGlobal(x, y); 370 } 371 } 372 } 373 374 keyboard->focus = window; 375 376 if (keyboard->focus) { 377 SDL_SendWindowEvent(keyboard->focus, SDL_EVENT_WINDOW_FOCUS_GAINED, 0, 0); 378 379#if !defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_ANDROID) 380 if (SDL_TextInputActive(keyboard->focus)) { 381 if (video && video->StartTextInput) { 382 video->StartTextInput(video, keyboard->focus, keyboard->focus->text_input_props); 383 } 384 } 385#endif // !SDL_PLATFORM_IOS && !SDL_PLATFORM_ANDROID 386 } 387 388 SDL_UpdateRelativeMouseMode(); 389 390 return true; 391} 392 393static SDL_Keycode SDL_ConvertNumpadKeycode(SDL_Keycode keycode, bool numlock) 394{ 395 switch (keycode) { 396 case SDLK_KP_DIVIDE: 397 return SDLK_SLASH; 398 case SDLK_KP_MULTIPLY: 399 return SDLK_ASTERISK; 400 case SDLK_KP_MINUS: 401 return SDLK_MINUS; 402 case SDLK_KP_PLUS: 403 return SDLK_PLUS; 404 case SDLK_KP_ENTER: 405 return SDLK_RETURN; 406 case SDLK_KP_1: 407 return numlock ? SDLK_1 : SDLK_END; 408 case SDLK_KP_2: 409 return numlock ? SDLK_2 : SDLK_DOWN; 410 case SDLK_KP_3: 411 return numlock ? SDLK_3 : SDLK_PAGEDOWN; 412 case SDLK_KP_4: 413 return numlock ? SDLK_4 : SDLK_LEFT; 414 case SDLK_KP_5: 415 return numlock ? SDLK_5 : SDLK_CLEAR; 416 case SDLK_KP_6: 417 return numlock ? SDLK_6 : SDLK_RIGHT; 418 case SDLK_KP_7: 419 return numlock ? SDLK_7 : SDLK_HOME; 420 case SDLK_KP_8: 421 return numlock ? SDLK_8 : SDLK_UP; 422 case SDLK_KP_9: 423 return numlock ? SDLK_9 : SDLK_PAGEUP; 424 case SDLK_KP_0: 425 return numlock ? SDLK_0 : SDLK_INSERT; 426 case SDLK_KP_PERIOD: 427 return numlock ? SDLK_PERIOD : SDLK_DELETE; 428 case SDLK_KP_EQUALS: 429 return SDLK_EQUALS; 430 case SDLK_KP_COMMA: 431 return SDLK_COMMA; 432 case SDLK_KP_EQUALSAS400: 433 return SDLK_EQUALS; 434 case SDLK_KP_LEFTPAREN: 435 return SDLK_LEFTPAREN; 436 case SDLK_KP_RIGHTPAREN: 437 return SDLK_RIGHTPAREN; 438 case SDLK_KP_LEFTBRACE: 439 return SDLK_LEFTBRACE; 440 case SDLK_KP_RIGHTBRACE: 441 return SDLK_RIGHTBRACE; 442 case SDLK_KP_TAB: 443 return SDLK_TAB; 444 case SDLK_KP_BACKSPACE: 445 return SDLK_BACKSPACE; 446 case SDLK_KP_A: 447 return SDLK_A; 448 case SDLK_KP_B: 449 return SDLK_B; 450 case SDLK_KP_C: 451 return SDLK_C; 452 case SDLK_KP_D: 453 return SDLK_D; 454 case SDLK_KP_E: 455 return SDLK_E; 456 case SDLK_KP_F: 457 return SDLK_F; 458 case SDLK_KP_PERCENT: 459 return SDLK_PERCENT; 460 case SDLK_KP_LESS: 461 return SDLK_LESS; 462 case SDLK_KP_GREATER: 463 return SDLK_GREATER; 464 case SDLK_KP_AMPERSAND: 465 return SDLK_AMPERSAND; 466 case SDLK_KP_COLON: 467 return SDLK_COLON; 468 case SDLK_KP_HASH: 469 return SDLK_HASH; 470 case SDLK_KP_SPACE: 471 return SDLK_SPACE; 472 case SDLK_KP_AT: 473 return SDLK_AT; 474 case SDLK_KP_EXCLAM: 475 return SDLK_EXCLAIM; 476 case SDLK_KP_PLUSMINUS: 477 return SDLK_PLUSMINUS; 478 default: 479 return keycode; 480 } 481} 482 483SDL_Keycode SDL_GetKeyFromScancode(SDL_Scancode scancode, SDL_Keymod modstate, bool key_event) 484{ 485 SDL_Keyboard *keyboard = &SDL_keyboard; 486 487 if (key_event) { 488 SDL_Keymap *keymap = SDL_GetCurrentKeymap(false); 489 bool numlock = (modstate & SDL_KMOD_NUM) != 0; 490 SDL_Keycode keycode; 491 492 // We won't be applying any modifiers by default 493 modstate = SDL_KMOD_NONE; 494 495 if ((keyboard->keycode_options & KEYCODE_OPTION_FRENCH_NUMBERS) && 496 keymap && keymap->french_numbers && 497 (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0)) { 498 // Add the shift state to generate a numeric keycode 499 modstate |= SDL_KMOD_SHIFT; 500 } 501 502 keycode = SDL_GetKeymapKeycode(keymap, scancode, modstate); 503 504 if (keyboard->keycode_options & KEYCODE_OPTION_HIDE_NUMPAD) { 505 keycode = SDL_ConvertNumpadKeycode(keycode, numlock); 506 } 507 return keycode; 508 } 509 510 return SDL_GetKeymapKeycode(keyboard->keymap, scancode, modstate); 511} 512 513SDL_Scancode SDL_GetScancodeFromKey(SDL_Keycode key, SDL_Keymod *modstate) 514{ 515 SDL_Keyboard *keyboard = &SDL_keyboard; 516 517 return SDL_GetKeymapScancode(keyboard->keymap, key, modstate); 518} 519 520static bool SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, bool down) 521{ 522 SDL_Keyboard *keyboard = &SDL_keyboard; 523 bool posted = false; 524 SDL_Keycode keycode = SDLK_UNKNOWN; 525 Uint32 type; 526 bool repeat = false; 527 const Uint8 source = flags & KEYBOARD_SOURCE_MASK; 528 529#ifdef DEBUG_KEYBOARD 530 SDL_Log("The '%s' key has been %s", SDL_GetScancodeName(scancode), down ? "pressed" : "released"); 531#endif 532 533 // Figure out what type of event this is 534 if (down) { 535 type = SDL_EVENT_KEY_DOWN; 536 } else { 537 type = SDL_EVENT_KEY_UP; 538 } 539 540 if (scancode > SDL_SCANCODE_UNKNOWN && scancode < SDL_SCANCODE_COUNT) { 541 // Drop events that don't change state 542 if (down) { 543 if (keyboard->keystate[scancode]) { 544 if (!(keyboard->keysource[scancode] & source)) { 545 keyboard->keysource[scancode] |= source; 546 return false; 547 } 548 repeat = true; 549 } 550 keyboard->keysource[scancode] |= source; 551 } else { 552 if (!keyboard->keystate[scancode]) { 553 return false; 554 } 555 keyboard->keysource[scancode] = 0; 556 } 557 558 // Update internal keyboard state 559 keyboard->keystate[scancode] = down; 560 561 keycode = SDL_GetKeyFromScancode(scancode, keyboard->modstate, true); 562 563 } else if (rawcode == 0) { 564 // Nothing to do! 565 return false; 566 } 567 568 if (source == KEYBOARD_HARDWARE) { 569 keyboard->hardware_timestamp = SDL_GetTicks(); 570 } else if (source == KEYBOARD_AUTORELEASE) { 571 keyboard->autorelease_pending = true; 572 } 573 574 // Update modifiers state if applicable 575 if (!(flags & KEYBOARD_IGNOREMODIFIERS) && !repeat) { 576 SDL_Keymod modifier; 577 578 switch (keycode) { 579 case SDLK_LCTRL: 580 modifier = SDL_KMOD_LCTRL; 581 break; 582 case SDLK_RCTRL: 583 modifier = SDL_KMOD_RCTRL; 584 break; 585 case SDLK_LSHIFT: 586 modifier = SDL_KMOD_LSHIFT; 587 break; 588 case SDLK_RSHIFT: 589 modifier = SDL_KMOD_RSHIFT; 590 break; 591 case SDLK_LALT: 592 modifier = SDL_KMOD_LALT; 593 break; 594 case SDLK_RALT: 595 modifier = SDL_KMOD_RALT; 596 break; 597 case SDLK_LGUI: 598 modifier = SDL_KMOD_LGUI; 599 break; 600 case SDLK_RGUI: 601 modifier = SDL_KMOD_RGUI; 602 break; 603 case SDLK_MODE: 604 modifier = SDL_KMOD_MODE; 605 break; 606 default: 607 modifier = SDL_KMOD_NONE; 608 break; 609 } 610 if (SDL_EVENT_KEY_DOWN == type) { 611 switch (keycode) { 612 case SDLK_NUMLOCKCLEAR: 613 keyboard->modstate ^= SDL_KMOD_NUM; 614 break; 615 case SDLK_CAPSLOCK: 616 keyboard->modstate ^= SDL_KMOD_CAPS; 617 break; 618 case SDLK_SCROLLLOCK: 619 keyboard->modstate ^= SDL_KMOD_SCROLL; 620 break; 621 default: 622 keyboard->modstate |= modifier; 623 break; 624 } 625 } else { 626 keyboard->modstate &= ~modifier; 627 } 628 } 629 630 // Post the event, if desired 631 if (SDL_EventEnabled(type)) { 632 SDL_Event event; 633 event.type = type; 634 event.common.timestamp = timestamp; 635 event.key.scancode = scancode; 636 event.key.key = keycode; 637 event.key.mod = keyboard->modstate; 638 event.key.raw = (Uint16)rawcode; 639 event.key.down = down; 640 event.key.repeat = repeat; 641 event.key.windowID = keyboard->focus ? keyboard->focus->id : 0; 642 event.key.which = keyboardID; 643 posted = SDL_PushEvent(&event); 644 } 645 646 /* If the keyboard is grabbed and the grabbed window is in full-screen, 647 minimize the window when we receive Alt+Tab, unless the application 648 has explicitly opted out of this behavior. */ 649 if (keycode == SDLK_TAB && down && 650 (keyboard->modstate & SDL_KMOD_ALT) && 651 keyboard->focus && 652 (keyboard->focus->flags & SDL_WINDOW_KEYBOARD_GRABBED) && 653 (keyboard->focus->flags & SDL_WINDOW_FULLSCREEN) && 654 SDL_GetHintBoolean(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, true)) { 655 /* We will temporarily forfeit our grab by minimizing our window, 656 allowing the user to escape the application */ 657 SDL_MinimizeWindow(keyboard->focus); 658 } 659 660 return posted; 661} 662 663void SDL_SendKeyboardUnicodeKey(Uint64 timestamp, Uint32 ch) 664{ 665 SDL_Keyboard *keyboard = &SDL_keyboard; 666 SDL_Keymod modstate = SDL_KMOD_NONE; 667 SDL_Scancode scancode; 668 669 if (ch == '\n') { 670 ch = SDLK_RETURN; 671 } 672 scancode = SDL_GetKeymapScancode(keyboard->keymap, ch, &modstate); 673 674 // Make sure we have this keycode in our keymap 675 if (scancode == SDL_SCANCODE_UNKNOWN && ch < SDLK_SCANCODE_MASK) { 676 scancode = GetNextReservedScancode(); 677 SetKeymapEntry(scancode, modstate, ch); 678 } 679 680 if (modstate & SDL_KMOD_SHIFT) { 681 // If the character uses shift, press shift down 682 SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_LSHIFT, true); 683 } 684 685 // Send a keydown and keyup for the character 686 SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, true); 687 SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, false); 688 689 if (modstate & SDL_KMOD_SHIFT) { 690 // If the character uses shift, release shift 691 SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_LSHIFT, false); 692 } 693} 694 695bool SDL_SendKeyboardKey(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, bool down) 696{ 697 return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, keyboardID, rawcode, scancode, down); 698} 699 700bool SDL_SendKeyboardKeyAndKeycode(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, SDL_Keycode keycode, bool down) 701{ 702 if (down) { 703 // Make sure we have this keycode in our keymap 704 SetKeymapEntry(scancode, SDL_GetModState(), keycode); 705 } 706 707 return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, keyboardID, rawcode, scancode, down); 708} 709 710bool SDL_SendKeyboardKeyIgnoreModifiers(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, bool down) 711{ 712 return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE | KEYBOARD_IGNOREMODIFIERS, keyboardID, rawcode, scancode, down); 713} 714 715bool SDL_SendKeyboardKeyAutoRelease(Uint64 timestamp, SDL_Scancode scancode) 716{ 717 return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_AUTORELEASE, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, true); 718} 719 720void SDL_ReleaseAutoReleaseKeys(void) 721{ 722 SDL_Keyboard *keyboard = &SDL_keyboard; 723 int scancode; 724 725 if (keyboard->autorelease_pending) { 726 for (scancode = SDL_SCANCODE_UNKNOWN; scancode < SDL_SCANCODE_COUNT; ++scancode) { 727 if (keyboard->keysource[scancode] == KEYBOARD_AUTORELEASE) { 728 SDL_SendKeyboardKeyInternal(0, KEYBOARD_AUTORELEASE, SDL_GLOBAL_KEYBOARD_ID, 0, (SDL_Scancode)scancode, false); 729 } 730 } 731 keyboard->autorelease_pending = false; 732 } 733 734 if (keyboard->hardware_timestamp) { 735 // Keep hardware keyboard "active" for 250 ms 736 if (SDL_GetTicks() >= keyboard->hardware_timestamp + 250) { 737 keyboard->hardware_timestamp = 0; 738 } 739 } 740} 741 742bool SDL_HardwareKeyboardKeyPressed(void) 743{ 744 SDL_Keyboard *keyboard = &SDL_keyboard; 745 int scancode; 746 747 for (scancode = SDL_SCANCODE_UNKNOWN; scancode < SDL_SCANCODE_COUNT; ++scancode) { 748 if (keyboard->keysource[scancode] & KEYBOARD_HARDWARE) { 749 return true; 750 } 751 } 752 753 return keyboard->hardware_timestamp ? true : false; 754} 755 756void SDL_SendKeyboardText(const char *text) 757{ 758 SDL_Keyboard *keyboard = &SDL_keyboard; 759 760 if (!keyboard->focus || !SDL_TextInputActive(keyboard->focus)) { 761 return; 762 } 763 764 if (!text || !*text) { 765 return; 766 } 767 768 // Don't post text events for unprintable characters 769 if (SDL_iscntrl((unsigned char)*text)) { 770 return; 771 } 772 773 // Post the event, if desired 774 if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) { 775 SDL_Event event; 776 event.type = SDL_EVENT_TEXT_INPUT; 777 event.common.timestamp = 0; 778 event.text.windowID = keyboard->focus ? keyboard->focus->id : 0; 779 event.text.text = SDL_CreateTemporaryString(text); 780 if (!event.text.text) { 781 return; 782 } 783 SDL_PushEvent(&event); 784 } 785} 786 787void SDL_SendEditingText(const char *text, int start, int length) 788{ 789 SDL_Keyboard *keyboard = &SDL_keyboard; 790 791 if (!keyboard->focus || !SDL_TextInputActive(keyboard->focus)) { 792 return; 793 } 794 795 if (!text) { 796 return; 797 } 798 799 // Post the event, if desired 800 if (SDL_EventEnabled(SDL_EVENT_TEXT_EDITING)) { 801 SDL_Event event; 802 803 event.type = SDL_EVENT_TEXT_EDITING; 804 event.common.timestamp = 0; 805 event.edit.windowID = keyboard->focus ? keyboard->focus->id : 0; 806 event.edit.start = start; 807 event.edit.length = length; 808 event.edit.text = SDL_CreateTemporaryString(text); 809 if (!event.edit.text) { 810 return; 811 } 812 SDL_PushEvent(&event); 813 } 814} 815 816static const char * const *CreateCandidatesForEvent(char **candidates, int num_candidates) 817{ 818 const char **event_candidates; 819 int i; 820 char *ptr; 821 size_t total_length = (num_candidates + 1) * sizeof(*event_candidates); 822 823 for (i = 0; i < num_candidates; ++i) { 824 size_t length = SDL_strlen(candidates[i]) + 1; 825 826 total_length += length; 827 } 828 829 event_candidates = (const char **)SDL_AllocateTemporaryMemory(total_length); 830 if (!event_candidates) { 831 return NULL; 832 } 833 ptr = (char *)(event_candidates + (num_candidates + 1)); 834 835 for (i = 0; i < num_candidates; ++i) { 836 size_t length = SDL_strlen(candidates[i]) + 1; 837 838 event_candidates[i] = ptr; 839 SDL_memcpy(ptr, candidates[i], length); 840 ptr += length; 841 } 842 event_candidates[i] = NULL; 843 844 return event_candidates; 845} 846 847void SDL_SendEditingTextCandidates(char **candidates, int num_candidates, int selected_candidate, bool horizontal) 848{ 849 SDL_Keyboard *keyboard = &SDL_keyboard; 850 851 if (!keyboard->focus || !SDL_TextInputActive(keyboard->focus)) { 852 return; 853 } 854 855 // Post the event, if desired 856 if (SDL_EventEnabled(SDL_EVENT_TEXT_EDITING_CANDIDATES)) { 857 SDL_Event event; 858 859 event.type = SDL_EVENT_TEXT_EDITING_CANDIDATES; 860 event.common.timestamp = 0; 861 event.edit.windowID = keyboard->focus ? keyboard->focus->id : 0; 862 if (num_candidates > 0) { 863 const char * const *event_candidates = CreateCandidatesForEvent(candidates, num_candidates); 864 if (!event_candidates) { 865 return; 866 } 867 event.edit_candidates.candidates = event_candidates; 868 event.edit_candidates.num_candidates = num_candidates; 869 event.edit_candidates.selected_candidate = selected_candidate; 870 event.edit_candidates.horizontal = horizontal; 871 } else { 872 event.edit_candidates.candidates = NULL; 873 event.edit_candidates.num_candidates = 0; 874 event.edit_candidates.selected_candidate = -1; 875 event.edit_candidates.horizontal = false; 876 } 877 SDL_PushEvent(&event); 878 } 879} 880 881void SDL_QuitKeyboard(void) 882{ 883 SDL_keyboard_quitting = true; 884 885 for (int i = SDL_keyboard_count; i--;) { 886 SDL_RemoveKeyboard(SDL_keyboards[i]); 887 } 888 SDL_free(SDL_keyboards); 889 SDL_keyboards = NULL; 890 891 SDL_DestroyHashTable(SDL_keyboard_names); 892 SDL_keyboard_names = NULL; 893 894 if (SDL_keyboard.keymap && SDL_keyboard.keymap->auto_release) { 895 SDL_DestroyKeymap(SDL_keyboard.keymap); 896 SDL_keyboard.keymap = NULL; 897 } 898 899 SDL_RemoveHintCallback(SDL_HINT_KEYCODE_OPTIONS, 900 SDL_KeycodeOptionsChanged, &SDL_keyboard); 901 902 SDL_keyboard_quitting = false; 903} 904 905const bool *SDL_GetKeyboardState(int *numkeys) 906{ 907 SDL_Keyboard *keyboard = &SDL_keyboard; 908 909 if (numkeys) { 910 *numkeys = SDL_SCANCODE_COUNT; 911 } 912 return keyboard->keystate; 913} 914 915SDL_Keymod SDL_GetModState(void) 916{ 917 SDL_Keyboard *keyboard = &SDL_keyboard; 918 919 return keyboard->modstate; 920} 921 922void SDL_SetModState(SDL_Keymod modstate) 923{ 924 SDL_Keyboard *keyboard = &SDL_keyboard; 925 926 keyboard->modstate = modstate; 927} 928 929// Note that SDL_ToggleModState() is not a public API. SDL_SetModState() is. 930void SDL_ToggleModState(SDL_Keymod modstate, bool toggle) 931{ 932 SDL_Keyboard *keyboard = &SDL_keyboard; 933 if (toggle) { 934 keyboard->modstate |= modstate; 935 } else { 936 keyboard->modstate &= ~modstate; 937 } 938} 939 940[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.