Atlas - SDL_keyboard.c

Home / ext / SDL / src / events Lines: 2 | Size: 28223 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 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_JoystickID *)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 // See if the current window has lost focus 341 if (keyboard->focus && keyboard->focus != window) { 342 SDL_SendWindowEvent(keyboard->focus, SDL_EVENT_WINDOW_FOCUS_LOST, 0, 0); 343 344#if !defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_ANDROID) 345 // Ensures IME compositions are committed 346 if (SDL_TextInputActive(keyboard->focus)) { 347 if (video && video->StopTextInput) { 348 video->StopTextInput(video, keyboard->focus); 349 } 350 } 351#endif // !SDL_PLATFORM_IOS && !SDL_PLATFORM_ANDROID 352 } 353 354 if (keyboard->focus && !window) { 355 // We won't get anymore keyboard messages, so reset keyboard state 356 SDL_ResetKeyboard(); 357 358 // Also leave mouse relative mode 359 if (mouse->relative_mode) { 360 SDL_SetRelativeMouseMode(false); 361 362 SDL_Window *focus = keyboard->focus; 363 if ((focus->flags & SDL_WINDOW_MINIMIZED) != 0) { 364 // We can't warp the mouse within minimized windows, so manually restore the position 365 float x = focus->x + mouse->x; 366 float y = focus->y + mouse->y; 367 SDL_WarpMouseGlobal(x, y); 368 } 369 } 370 } 371 372 keyboard->focus = window; 373 374 if (keyboard->focus) { 375 SDL_SendWindowEvent(keyboard->focus, SDL_EVENT_WINDOW_FOCUS_GAINED, 0, 0); 376 377#if !defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_ANDROID) 378 if (SDL_TextInputActive(keyboard->focus)) { 379 if (video && video->StartTextInput) { 380 video->StartTextInput(video, keyboard->focus, keyboard->focus->text_input_props); 381 } 382 } 383#endif // !SDL_PLATFORM_IOS && !SDL_PLATFORM_ANDROID 384 } 385 386 SDL_UpdateRelativeMouseMode(); 387 388 return true; 389} 390 391static SDL_Keycode SDL_ConvertNumpadKeycode(SDL_Keycode keycode, bool numlock) 392{ 393 switch (keycode) { 394 case SDLK_KP_DIVIDE: 395 return SDLK_SLASH; 396 case SDLK_KP_MULTIPLY: 397 return SDLK_ASTERISK; 398 case SDLK_KP_MINUS: 399 return SDLK_MINUS; 400 case SDLK_KP_PLUS: 401 return SDLK_PLUS; 402 case SDLK_KP_ENTER: 403 return SDLK_RETURN; 404 case SDLK_KP_1: 405 return numlock ? SDLK_1 : SDLK_END; 406 case SDLK_KP_2: 407 return numlock ? SDLK_2 : SDLK_DOWN; 408 case SDLK_KP_3: 409 return numlock ? SDLK_3 : SDLK_PAGEDOWN; 410 case SDLK_KP_4: 411 return numlock ? SDLK_4 : SDLK_LEFT; 412 case SDLK_KP_5: 413 return numlock ? SDLK_5 : SDLK_CLEAR; 414 case SDLK_KP_6: 415 return numlock ? SDLK_6 : SDLK_RIGHT; 416 case SDLK_KP_7: 417 return numlock ? SDLK_7 : SDLK_HOME; 418 case SDLK_KP_8: 419 return numlock ? SDLK_8 : SDLK_UP; 420 case SDLK_KP_9: 421 return numlock ? SDLK_9 : SDLK_PAGEUP; 422 case SDLK_KP_0: 423 return numlock ? SDLK_0 : SDLK_INSERT; 424 case SDLK_KP_PERIOD: 425 return numlock ? SDLK_PERIOD : SDLK_DELETE; 426 case SDLK_KP_EQUALS: 427 return SDLK_EQUALS; 428 case SDLK_KP_COMMA: 429 return SDLK_COMMA; 430 case SDLK_KP_EQUALSAS400: 431 return SDLK_EQUALS; 432 case SDLK_KP_LEFTPAREN: 433 return SDLK_LEFTPAREN; 434 case SDLK_KP_RIGHTPAREN: 435 return SDLK_RIGHTPAREN; 436 case SDLK_KP_LEFTBRACE: 437 return SDLK_LEFTBRACE; 438 case SDLK_KP_RIGHTBRACE: 439 return SDLK_RIGHTBRACE; 440 case SDLK_KP_TAB: 441 return SDLK_TAB; 442 case SDLK_KP_BACKSPACE: 443 return SDLK_BACKSPACE; 444 case SDLK_KP_A: 445 return SDLK_A; 446 case SDLK_KP_B: 447 return SDLK_B; 448 case SDLK_KP_C: 449 return SDLK_C; 450 case SDLK_KP_D: 451 return SDLK_D; 452 case SDLK_KP_E: 453 return SDLK_E; 454 case SDLK_KP_F: 455 return SDLK_F; 456 case SDLK_KP_PERCENT: 457 return SDLK_PERCENT; 458 case SDLK_KP_LESS: 459 return SDLK_LESS; 460 case SDLK_KP_GREATER: 461 return SDLK_GREATER; 462 case SDLK_KP_AMPERSAND: 463 return SDLK_AMPERSAND; 464 case SDLK_KP_COLON: 465 return SDLK_COLON; 466 case SDLK_KP_HASH: 467 return SDLK_HASH; 468 case SDLK_KP_SPACE: 469 return SDLK_SPACE; 470 case SDLK_KP_AT: 471 return SDLK_AT; 472 case SDLK_KP_EXCLAM: 473 return SDLK_EXCLAIM; 474 case SDLK_KP_PLUSMINUS: 475 return SDLK_PLUSMINUS; 476 default: 477 return keycode; 478 } 479} 480 481SDL_Keycode SDL_GetKeyFromScancode(SDL_Scancode scancode, SDL_Keymod modstate, bool key_event) 482{ 483 SDL_Keyboard *keyboard = &SDL_keyboard; 484 485 if (key_event) { 486 SDL_Keymap *keymap = SDL_GetCurrentKeymap(false); 487 bool numlock = (modstate & SDL_KMOD_NUM) != 0; 488 SDL_Keycode keycode; 489 490 // We won't be applying any modifiers by default 491 modstate = SDL_KMOD_NONE; 492 493 if ((keyboard->keycode_options & KEYCODE_OPTION_FRENCH_NUMBERS) && 494 keymap && keymap->french_numbers && 495 (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0)) { 496 // Add the shift state to generate a numeric keycode 497 modstate |= SDL_KMOD_SHIFT; 498 } 499 500 keycode = SDL_GetKeymapKeycode(keymap, scancode, modstate); 501 502 if (keyboard->keycode_options & KEYCODE_OPTION_HIDE_NUMPAD) { 503 keycode = SDL_ConvertNumpadKeycode(keycode, numlock); 504 } 505 return keycode; 506 } 507 508 return SDL_GetKeymapKeycode(keyboard->keymap, scancode, modstate); 509} 510 511SDL_Scancode SDL_GetScancodeFromKey(SDL_Keycode key, SDL_Keymod *modstate) 512{ 513 SDL_Keyboard *keyboard = &SDL_keyboard; 514 515 return SDL_GetKeymapScancode(keyboard->keymap, key, modstate); 516} 517 518static bool SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, bool down) 519{ 520 SDL_Keyboard *keyboard = &SDL_keyboard; 521 bool posted = false; 522 SDL_Keycode keycode = SDLK_UNKNOWN; 523 Uint32 type; 524 bool repeat = false; 525 const Uint8 source = flags & KEYBOARD_SOURCE_MASK; 526 527#ifdef DEBUG_KEYBOARD 528 SDL_Log("The '%s' key has been %s", SDL_GetScancodeName(scancode), down ? "pressed" : "released"); 529#endif 530 531 // Figure out what type of event this is 532 if (down) { 533 type = SDL_EVENT_KEY_DOWN; 534 } else { 535 type = SDL_EVENT_KEY_UP; 536 } 537 538 if (scancode > SDL_SCANCODE_UNKNOWN && scancode < SDL_SCANCODE_COUNT) { 539 // Drop events that don't change state 540 if (down) { 541 if (keyboard->keystate[scancode]) { 542 if (!(keyboard->keysource[scancode] & source)) { 543 keyboard->keysource[scancode] |= source; 544 return false; 545 } 546 repeat = true; 547 } 548 keyboard->keysource[scancode] |= source; 549 } else { 550 if (!keyboard->keystate[scancode]) { 551 return false; 552 } 553 keyboard->keysource[scancode] = 0; 554 } 555 556 // Update internal keyboard state 557 keyboard->keystate[scancode] = down; 558 559 keycode = SDL_GetKeyFromScancode(scancode, keyboard->modstate, true); 560 561 } else if (rawcode == 0) { 562 // Nothing to do! 563 return false; 564 } 565 566 if (source == KEYBOARD_HARDWARE) { 567 keyboard->hardware_timestamp = SDL_GetTicks(); 568 } else if (source == KEYBOARD_AUTORELEASE) { 569 keyboard->autorelease_pending = true; 570 } 571 572 // Update modifiers state if applicable 573 if (!(flags & KEYBOARD_IGNOREMODIFIERS) && !repeat) { 574 SDL_Keymod modifier; 575 576 switch (keycode) { 577 case SDLK_LCTRL: 578 modifier = SDL_KMOD_LCTRL; 579 break; 580 case SDLK_RCTRL: 581 modifier = SDL_KMOD_RCTRL; 582 break; 583 case SDLK_LSHIFT: 584 modifier = SDL_KMOD_LSHIFT; 585 break; 586 case SDLK_RSHIFT: 587 modifier = SDL_KMOD_RSHIFT; 588 break; 589 case SDLK_LALT: 590 modifier = SDL_KMOD_LALT; 591 break; 592 case SDLK_RALT: 593 modifier = SDL_KMOD_RALT; 594 break; 595 case SDLK_LGUI: 596 modifier = SDL_KMOD_LGUI; 597 break; 598 case SDLK_RGUI: 599 modifier = SDL_KMOD_RGUI; 600 break; 601 case SDLK_MODE: 602 modifier = SDL_KMOD_MODE; 603 break; 604 default: 605 modifier = SDL_KMOD_NONE; 606 break; 607 } 608 if (SDL_EVENT_KEY_DOWN == type) { 609 switch (keycode) { 610 case SDLK_NUMLOCKCLEAR: 611 keyboard->modstate ^= SDL_KMOD_NUM; 612 break; 613 case SDLK_CAPSLOCK: 614 keyboard->modstate ^= SDL_KMOD_CAPS; 615 break; 616 case SDLK_SCROLLLOCK: 617 keyboard->modstate ^= SDL_KMOD_SCROLL; 618 break; 619 default: 620 keyboard->modstate |= modifier; 621 break; 622 } 623 } else { 624 keyboard->modstate &= ~modifier; 625 } 626 } 627 628 // Post the event, if desired 629 if (SDL_EventEnabled(type)) { 630 SDL_Event event; 631 event.type = type; 632 event.common.timestamp = timestamp; 633 event.key.scancode = scancode; 634 event.key.key = keycode; 635 event.key.mod = keyboard->modstate; 636 event.key.raw = (Uint16)rawcode; 637 event.key.down = down; 638 event.key.repeat = repeat; 639 event.key.windowID = keyboard->focus ? keyboard->focus->id : 0; 640 event.key.which = keyboardID; 641 posted = SDL_PushEvent(&event); 642 } 643 644 /* If the keyboard is grabbed and the grabbed window is in full-screen, 645 minimize the window when we receive Alt+Tab, unless the application 646 has explicitly opted out of this behavior. */ 647 if (keycode == SDLK_TAB && down && 648 (keyboard->modstate & SDL_KMOD_ALT) && 649 keyboard->focus && 650 (keyboard->focus->flags & SDL_WINDOW_KEYBOARD_GRABBED) && 651 (keyboard->focus->flags & SDL_WINDOW_FULLSCREEN) && 652 SDL_GetHintBoolean(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, true)) { 653 /* We will temporarily forfeit our grab by minimizing our window, 654 allowing the user to escape the application */ 655 SDL_MinimizeWindow(keyboard->focus); 656 } 657 658 return posted; 659} 660 661void SDL_SendKeyboardUnicodeKey(Uint64 timestamp, Uint32 ch) 662{ 663 SDL_Keyboard *keyboard = &SDL_keyboard; 664 SDL_Keymod modstate = SDL_KMOD_NONE; 665 SDL_Scancode scancode; 666 667 if (ch == '\n') { 668 ch = SDLK_RETURN; 669 } 670 scancode = SDL_GetKeymapScancode(keyboard->keymap, ch, &modstate); 671 672 // Make sure we have this keycode in our keymap 673 if (scancode == SDL_SCANCODE_UNKNOWN && ch < SDLK_SCANCODE_MASK) { 674 scancode = GetNextReservedScancode(); 675 SetKeymapEntry(scancode, modstate, ch); 676 } 677 678 if (modstate & SDL_KMOD_SHIFT) { 679 // If the character uses shift, press shift down 680 SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_LSHIFT, true); 681 } 682 683 // Send a keydown and keyup for the character 684 SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, true); 685 SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, false); 686 687 if (modstate & SDL_KMOD_SHIFT) { 688 // If the character uses shift, release shift 689 SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_LSHIFT, false); 690 } 691} 692 693bool SDL_SendKeyboardKey(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, bool down) 694{ 695 return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, keyboardID, rawcode, scancode, down); 696} 697 698bool SDL_SendKeyboardKeyAndKeycode(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, SDL_Keycode keycode, bool down) 699{ 700 if (down) { 701 // Make sure we have this keycode in our keymap 702 SetKeymapEntry(scancode, SDL_GetModState(), keycode); 703 } 704 705 return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, keyboardID, rawcode, scancode, down); 706} 707 708bool SDL_SendKeyboardKeyIgnoreModifiers(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, bool down) 709{ 710 return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE | KEYBOARD_IGNOREMODIFIERS, keyboardID, rawcode, scancode, down); 711} 712 713bool SDL_SendKeyboardKeyAutoRelease(Uint64 timestamp, SDL_Scancode scancode) 714{ 715 return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_AUTORELEASE, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, true); 716} 717 718void SDL_ReleaseAutoReleaseKeys(void) 719{ 720 SDL_Keyboard *keyboard = &SDL_keyboard; 721 int scancode; 722 723 if (keyboard->autorelease_pending) { 724 for (scancode = SDL_SCANCODE_UNKNOWN; scancode < SDL_SCANCODE_COUNT; ++scancode) { 725 if (keyboard->keysource[scancode] == KEYBOARD_AUTORELEASE) { 726 SDL_SendKeyboardKeyInternal(0, KEYBOARD_AUTORELEASE, SDL_GLOBAL_KEYBOARD_ID, 0, (SDL_Scancode)scancode, false); 727 } 728 } 729 keyboard->autorelease_pending = false; 730 } 731 732 if (keyboard->hardware_timestamp) { 733 // Keep hardware keyboard "active" for 250 ms 734 if (SDL_GetTicks() >= keyboard->hardware_timestamp + 250) { 735 keyboard->hardware_timestamp = 0; 736 } 737 } 738} 739 740bool SDL_HardwareKeyboardKeyPressed(void) 741{ 742 SDL_Keyboard *keyboard = &SDL_keyboard; 743 int scancode; 744 745 for (scancode = SDL_SCANCODE_UNKNOWN; scancode < SDL_SCANCODE_COUNT; ++scancode) { 746 if (keyboard->keysource[scancode] & KEYBOARD_HARDWARE) { 747 return true; 748 } 749 } 750 751 return keyboard->hardware_timestamp ? true : false; 752} 753 754void SDL_SendKeyboardText(const char *text) 755{ 756 SDL_Keyboard *keyboard = &SDL_keyboard; 757 758 if (!keyboard->focus || !SDL_TextInputActive(keyboard->focus)) { 759 return; 760 } 761 762 if (!text || !*text) { 763 return; 764 } 765 766 // Don't post text events for unprintable characters 767 if (SDL_iscntrl((unsigned char)*text)) { 768 return; 769 } 770 771 // Post the event, if desired 772 if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) { 773 SDL_Event event; 774 event.type = SDL_EVENT_TEXT_INPUT; 775 event.common.timestamp = 0; 776 event.text.windowID = keyboard->focus ? keyboard->focus->id : 0; 777 event.text.text = SDL_CreateTemporaryString(text); 778 if (!event.text.text) { 779 return; 780 } 781 SDL_PushEvent(&event); 782 } 783} 784 785void SDL_SendEditingText(const char *text, int start, int length) 786{ 787 SDL_Keyboard *keyboard = &SDL_keyboard; 788 789 if (!keyboard->focus || !SDL_TextInputActive(keyboard->focus)) { 790 return; 791 } 792 793 if (!text) { 794 return; 795 } 796 797 // Post the event, if desired 798 if (SDL_EventEnabled(SDL_EVENT_TEXT_EDITING)) { 799 SDL_Event event; 800 801 event.type = SDL_EVENT_TEXT_EDITING; 802 event.common.timestamp = 0; 803 event.edit.windowID = keyboard->focus ? keyboard->focus->id : 0; 804 event.edit.start = start; 805 event.edit.length = length; 806 event.edit.text = SDL_CreateTemporaryString(text); 807 if (!event.edit.text) { 808 return; 809 } 810 SDL_PushEvent(&event); 811 } 812} 813 814static const char * const *CreateCandidatesForEvent(char **candidates, int num_candidates) 815{ 816 const char **event_candidates; 817 int i; 818 char *ptr; 819 size_t total_length = (num_candidates + 1) * sizeof(*event_candidates); 820 821 for (i = 0; i < num_candidates; ++i) { 822 size_t length = SDL_strlen(candidates[i]) + 1; 823 824 total_length += length; 825 } 826 827 event_candidates = (const char **)SDL_AllocateTemporaryMemory(total_length); 828 if (!event_candidates) { 829 return NULL; 830 } 831 ptr = (char *)(event_candidates + (num_candidates + 1)); 832 833 for (i = 0; i < num_candidates; ++i) { 834 size_t length = SDL_strlen(candidates[i]) + 1; 835 836 event_candidates[i] = ptr; 837 SDL_memcpy(ptr, candidates[i], length); 838 ptr += length; 839 } 840 event_candidates[i] = NULL; 841 842 return event_candidates; 843} 844 845void SDL_SendEditingTextCandidates(char **candidates, int num_candidates, int selected_candidate, bool horizontal) 846{ 847 SDL_Keyboard *keyboard = &SDL_keyboard; 848 849 if (!keyboard->focus || !SDL_TextInputActive(keyboard->focus)) { 850 return; 851 } 852 853 // Post the event, if desired 854 if (SDL_EventEnabled(SDL_EVENT_TEXT_EDITING_CANDIDATES)) { 855 SDL_Event event; 856 857 event.type = SDL_EVENT_TEXT_EDITING_CANDIDATES; 858 event.common.timestamp = 0; 859 event.edit.windowID = keyboard->focus ? keyboard->focus->id : 0; 860 if (num_candidates > 0) { 861 const char * const *event_candidates = CreateCandidatesForEvent(candidates, num_candidates); 862 if (!event_candidates) { 863 return; 864 } 865 event.edit_candidates.candidates = event_candidates; 866 event.edit_candidates.num_candidates = num_candidates; 867 event.edit_candidates.selected_candidate = selected_candidate; 868 event.edit_candidates.horizontal = horizontal; 869 } else { 870 event.edit_candidates.candidates = NULL; 871 event.edit_candidates.num_candidates = 0; 872 event.edit_candidates.selected_candidate = -1; 873 event.edit_candidates.horizontal = false; 874 } 875 SDL_PushEvent(&event); 876 } 877} 878 879void SDL_QuitKeyboard(void) 880{ 881 SDL_keyboard_quitting = true; 882 883 for (int i = SDL_keyboard_count; i--;) { 884 SDL_RemoveKeyboard(SDL_keyboards[i]); 885 } 886 SDL_free(SDL_keyboards); 887 SDL_keyboards = NULL; 888 889 SDL_DestroyHashTable(SDL_keyboard_names); 890 SDL_keyboard_names = NULL; 891 892 if (SDL_keyboard.keymap && SDL_keyboard.keymap->auto_release) { 893 SDL_DestroyKeymap(SDL_keyboard.keymap); 894 SDL_keyboard.keymap = NULL; 895 } 896 897 SDL_RemoveHintCallback(SDL_HINT_KEYCODE_OPTIONS, 898 SDL_KeycodeOptionsChanged, &SDL_keyboard); 899 900 SDL_keyboard_quitting = false; 901} 902 903const bool *SDL_GetKeyboardState(int *numkeys) 904{ 905 SDL_Keyboard *keyboard = &SDL_keyboard; 906 907 if (numkeys) { 908 *numkeys = SDL_SCANCODE_COUNT; 909 } 910 return keyboard->keystate; 911} 912 913SDL_Keymod SDL_GetModState(void) 914{ 915 SDL_Keyboard *keyboard = &SDL_keyboard; 916 917 return keyboard->modstate; 918} 919 920void SDL_SetModState(SDL_Keymod modstate) 921{ 922 SDL_Keyboard *keyboard = &SDL_keyboard; 923 924 keyboard->modstate = modstate; 925} 926 927// Note that SDL_ToggleModState() is not a public API. SDL_SetModState() is. 928void SDL_ToggleModState(SDL_Keymod modstate, bool toggle) 929{ 930 SDL_Keyboard *keyboard = &SDL_keyboard; 931 if (toggle) { 932 keyboard->modstate |= modstate; 933 } else { 934 keyboard->modstate &= ~modstate; 935 } 936} 937 938
[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.