#include "SDL_internal.h"
#include "SDL_events_c.h"
#include "SDL_keymap_c.h"
#include "../video/SDL_sysvideo.h"
#if 0#endif
#define KEYBOARD_HARDWARE 0x01
#define KEYBOARD_VIRTUAL 0x02
#define KEYBOARD_AUTORELEASE 0x04
#define KEYBOARD_IGNOREMODIFIERS 0x08
#define KEYBOARD_SOURCE_MASK (KEYBOARD_HARDWARE | KEYBOARD_AUTORELEASE)
#define KEYCODE_OPTION_HIDE_NUMPAD 0x01
#define KEYCODE_OPTION_FRENCH_NUMBERS 0x02
#define KEYCODE_OPTION_LATIN_LETTERS 0x04
#define DEFAULT_KEYCODE_OPTIONS (KEYCODE_OPTION_FRENCH_NUMBERS | KEYCODE_OPTION_LATIN_LETTERS)
typedef struct SDL_Keyboard
{
SDL_Window *focus;
SDL_Keymod modstate;
Uint8 keysource[SDL_SCANCODE_COUNT];
bool keystate[SDL_SCANCODE_COUNT];
SDL_Keymap *keymap;
Uint32 keycode_options;
bool autorelease_pending;
Uint64 hardware_timestamp;
} SDL_Keyboard;
static SDL_Keyboard SDL_keyboard;
static int SDL_keyboard_count;
static SDL_KeyboardID *SDL_keyboards;
static SDL_HashTable *SDL_keyboard_names;
static bool SDL_keyboard_quitting;
static void SDLCALL SDL_KeycodeOptionsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_Keyboard *keyboard = (SDL_Keyboard *)userdata;
if (hint && *hint) {
keyboard->keycode_options = 0;
if (!SDL_strstr(hint, "none")) {
if (SDL_strstr(hint, "hide_numpad")) {
keyboard->keycode_options |= KEYCODE_OPTION_HIDE_NUMPAD;
}
if (SDL_strstr(hint, "french_numbers")) {
keyboard->keycode_options |= KEYCODE_OPTION_FRENCH_NUMBERS;
}
if (SDL_strstr(hint, "latin_letters")) {
keyboard->keycode_options |= KEYCODE_OPTION_LATIN_LETTERS;
}
}
} else {
keyboard->keycode_options = DEFAULT_KEYCODE_OPTIONS;
}
}
bool SDL_InitKeyboard(void)
{
SDL_AddHintCallback(SDL_HINT_KEYCODE_OPTIONS,
SDL_KeycodeOptionsChanged, &SDL_keyboard);
SDL_keyboard_names = SDL_CreateHashTable(0, true, SDL_HashID, SDL_KeyMatchID, SDL_DestroyHashValue, NULL);
return true;
}
bool SDL_IsKeyboard(Uint16 vendor, Uint16 product, int num_keys)
{
const int REAL_KEYBOARD_KEY_COUNT = 50;
if (num_keys > 0 && num_keys < REAL_KEYBOARD_KEY_COUNT) {
return false;
}
return true;
}
static int SDL_GetKeyboardIndex(SDL_KeyboardID keyboardID)
{
for (int i = 0; i < SDL_keyboard_count; ++i) {
if (keyboardID == SDL_keyboards[i]) {
return i;
}
}
return -1;
}
void SDL_AddKeyboard(SDL_KeyboardID keyboardID, const char *name)
{
int keyboard_index = SDL_GetKeyboardIndex(keyboardID);
if (keyboard_index >= 0) {
return;
}
SDL_assert(keyboardID != 0);
SDL_KeyboardID *keyboards = (SDL_KeyboardID *)SDL_realloc(SDL_keyboards, (SDL_keyboard_count + 1) * sizeof(*keyboards));
if (!keyboards) {
return;
}
keyboards[SDL_keyboard_count] = keyboardID;
SDL_keyboards = keyboards;
++SDL_keyboard_count;
if (!name) {
name = "Keyboard";
}
SDL_InsertIntoHashTable(SDL_keyboard_names, (const void *)(uintptr_t)keyboardID, SDL_strdup(name), true);
SDL_Event event;
SDL_zero(event);
event.type = SDL_EVENT_KEYBOARD_ADDED;
event.kdevice.which = keyboardID;
SDL_PushEvent(&event);
}
void SDL_RemoveKeyboard(SDL_KeyboardID keyboardID)
{
int keyboard_index = SDL_GetKeyboardIndex(keyboardID);
if (keyboard_index < 0) {
return;
}
if (keyboard_index != SDL_keyboard_count - 1) {
SDL_memmove(&SDL_keyboards[keyboard_index], &SDL_keyboards[keyboard_index + 1], (SDL_keyboard_count - keyboard_index - 1) * sizeof(SDL_keyboards[keyboard_index]));
}
--SDL_keyboard_count;
if (!SDL_keyboard_quitting) {
SDL_Event event;
SDL_zero(event);
event.type = SDL_EVENT_KEYBOARD_REMOVED;
event.kdevice.which = keyboardID;
SDL_PushEvent(&event);
}
}
bool SDL_HasKeyboard(void)
{
return (SDL_keyboard_count > 0);
}
SDL_KeyboardID *SDL_GetKeyboards(int *count)
{
int i;
SDL_KeyboardID *keyboards;
keyboards = (SDL_KeyboardID *)SDL_malloc((SDL_keyboard_count + 1) * sizeof(*keyboards));
if (keyboards) {
if (count) {
*count = SDL_keyboard_count;
}
for (i = 0; i < SDL_keyboard_count; ++i) {
keyboards[i] = SDL_keyboards[i];
}
keyboards[i] = 0;
} else {
if (count) {
*count = 0;
}
}
return keyboards;
}
const char *SDL_GetKeyboardNameForID(SDL_KeyboardID instance_id)
{
const char *name = NULL;
if (!SDL_FindInHashTable(SDL_keyboard_names, (const void *)(uintptr_t)instance_id, (const void **)&name)) {
SDL_SetError("Keyboard %" SDL_PRIu32 " not found", instance_id);
return NULL;
}
if (!name) {
SDL_OutOfMemory();
return NULL;
}
return name;
}
void SDL_ResetKeyboard(void)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
int scancode;
#ifdef DEBUG_KEYBOARD
SDL_Log("Resetting keyboard");
#endif
for (scancode = SDL_SCANCODE_UNKNOWN; scancode < SDL_SCANCODE_COUNT; ++scancode) {
if (keyboard->keystate[scancode]) {
SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, 0, (SDL_Scancode)scancode, false);
}
}
}
SDL_Keymap *SDL_GetCurrentKeymap(bool ignore_options)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
SDL_Keymap *keymap = SDL_keyboard.keymap;
if (!ignore_options) {
if (keymap && keymap->thai_keyboard) {
return NULL;
}
if ((keyboard->keycode_options & KEYCODE_OPTION_LATIN_LETTERS) &&
keymap && !keymap->latin_letters) {
return NULL;
}
}
return keyboard->keymap;
}
void SDL_SetKeymap(SDL_Keymap *keymap, bool send_event)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
if (keyboard->keymap && keyboard->keymap->auto_release) {
SDL_DestroyKeymap(keyboard->keymap);
}
keyboard->keymap = keymap;
if (keymap && !keymap->layout_determined) {
keymap->layout_determined = true;
keymap->french_numbers = true;
for (int i = SDL_SCANCODE_1; i <= SDL_SCANCODE_0; ++i) {
if (SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE)) ||
!SDL_isdigit(SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_SHIFT))) {
keymap->french_numbers = false;
break;
}
}
keymap->thai_keyboard = false;
keymap->latin_letters = false;
for (int i = SDL_SCANCODE_A; i <= SDL_SCANCODE_D; ++i) {
SDL_Keycode key = SDL_GetKeymapKeycode(keymap, (SDL_Scancode)i, SDL_KMOD_NONE);
if (key <= 0xFF) {
keymap->latin_letters = true;
break;
}
if (key >= 0x0E00 && key <= 0x0E7F) {
keymap->thai_keyboard = true;
break;
}
}
}
if (send_event) {
SDL_SendKeymapChangedEvent();
}
}
static SDL_Scancode GetNextReservedScancode(void)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
if (!keyboard->keymap) {
keyboard->keymap = SDL_CreateKeymap(true);
}
return SDL_GetKeymapNextReservedScancode(keyboard->keymap);
}
static void SetKeymapEntry(SDL_Scancode scancode, SDL_Keymod modstate, SDL_Keycode keycode)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
if (!keyboard->keymap) {
keyboard->keymap = SDL_CreateKeymap(true);
}
SDL_SetKeymapEntry(keyboard->keymap, scancode, modstate, keycode);
}
SDL_Window *SDL_GetKeyboardFocus(void)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
return keyboard->focus;
}
bool SDL_SetKeyboardFocus(SDL_Window *window)
{
#if !defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_ANDROID)
SDL_VideoDevice *video = SDL_GetVideoDevice();
#endif
SDL_Keyboard *keyboard = &SDL_keyboard;
SDL_Mouse *mouse = SDL_GetMouse();
if (window) {
if (!SDL_ObjectValid(window, SDL_OBJECT_TYPE_WINDOW) || window->is_destroying) {
return SDL_SetError("Invalid window");
}
}
if (keyboard->focus && !window) {
SDL_ResetKeyboard();
}
if (keyboard->focus && keyboard->focus != window) {
SDL_SendWindowEvent(keyboard->focus, SDL_EVENT_WINDOW_FOCUS_LOST, 0, 0);
#if !defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_ANDROID)
if (SDL_TextInputActive(keyboard->focus)) {
if (video && video->StopTextInput) {
video->StopTextInput(video, keyboard->focus);
}
}
#endif }
if (keyboard->focus && !window) {
if (mouse->relative_mode) {
SDL_SetRelativeMouseMode(false);
SDL_Window *focus = keyboard->focus;
if ((focus->flags & SDL_WINDOW_MINIMIZED) != 0) {
float x = focus->x + mouse->x;
float y = focus->y + mouse->y;
SDL_WarpMouseGlobal(x, y);
}
}
}
keyboard->focus = window;
if (keyboard->focus) {
SDL_SendWindowEvent(keyboard->focus, SDL_EVENT_WINDOW_FOCUS_GAINED, 0, 0);
#if !defined(SDL_PLATFORM_IOS) && !defined(SDL_PLATFORM_ANDROID)
if (SDL_TextInputActive(keyboard->focus)) {
if (video && video->StartTextInput) {
video->StartTextInput(video, keyboard->focus, keyboard->focus->text_input_props);
}
}
#endif }
SDL_UpdateRelativeMouseMode();
return true;
}
static SDL_Keycode SDL_ConvertNumpadKeycode(SDL_Keycode keycode, bool numlock)
{
switch (keycode) {
case SDLK_KP_DIVIDE:
return SDLK_SLASH;
case SDLK_KP_MULTIPLY:
return SDLK_ASTERISK;
case SDLK_KP_MINUS:
return SDLK_MINUS;
case SDLK_KP_PLUS:
return SDLK_PLUS;
case SDLK_KP_ENTER:
return SDLK_RETURN;
case SDLK_KP_1:
return numlock ? SDLK_1 : SDLK_END;
case SDLK_KP_2:
return numlock ? SDLK_2 : SDLK_DOWN;
case SDLK_KP_3:
return numlock ? SDLK_3 : SDLK_PAGEDOWN;
case SDLK_KP_4:
return numlock ? SDLK_4 : SDLK_LEFT;
case SDLK_KP_5:
return numlock ? SDLK_5 : SDLK_CLEAR;
case SDLK_KP_6:
return numlock ? SDLK_6 : SDLK_RIGHT;
case SDLK_KP_7:
return numlock ? SDLK_7 : SDLK_HOME;
case SDLK_KP_8:
return numlock ? SDLK_8 : SDLK_UP;
case SDLK_KP_9:
return numlock ? SDLK_9 : SDLK_PAGEUP;
case SDLK_KP_0:
return numlock ? SDLK_0 : SDLK_INSERT;
case SDLK_KP_PERIOD:
return numlock ? SDLK_PERIOD : SDLK_DELETE;
case SDLK_KP_EQUALS:
return SDLK_EQUALS;
case SDLK_KP_COMMA:
return SDLK_COMMA;
case SDLK_KP_EQUALSAS400:
return SDLK_EQUALS;
case SDLK_KP_LEFTPAREN:
return SDLK_LEFTPAREN;
case SDLK_KP_RIGHTPAREN:
return SDLK_RIGHTPAREN;
case SDLK_KP_LEFTBRACE:
return SDLK_LEFTBRACE;
case SDLK_KP_RIGHTBRACE:
return SDLK_RIGHTBRACE;
case SDLK_KP_TAB:
return SDLK_TAB;
case SDLK_KP_BACKSPACE:
return SDLK_BACKSPACE;
case SDLK_KP_A:
return SDLK_A;
case SDLK_KP_B:
return SDLK_B;
case SDLK_KP_C:
return SDLK_C;
case SDLK_KP_D:
return SDLK_D;
case SDLK_KP_E:
return SDLK_E;
case SDLK_KP_F:
return SDLK_F;
case SDLK_KP_PERCENT:
return SDLK_PERCENT;
case SDLK_KP_LESS:
return SDLK_LESS;
case SDLK_KP_GREATER:
return SDLK_GREATER;
case SDLK_KP_AMPERSAND:
return SDLK_AMPERSAND;
case SDLK_KP_COLON:
return SDLK_COLON;
case SDLK_KP_HASH:
return SDLK_HASH;
case SDLK_KP_SPACE:
return SDLK_SPACE;
case SDLK_KP_AT:
return SDLK_AT;
case SDLK_KP_EXCLAM:
return SDLK_EXCLAIM;
case SDLK_KP_PLUSMINUS:
return SDLK_PLUSMINUS;
default:
return keycode;
}
}
SDL_Keycode SDL_GetKeyFromScancode(SDL_Scancode scancode, SDL_Keymod modstate, bool key_event)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
if (key_event) {
SDL_Keymap *keymap = SDL_GetCurrentKeymap(false);
bool numlock = (modstate & SDL_KMOD_NUM) != 0;
SDL_Keycode keycode;
modstate = SDL_KMOD_NONE;
if ((keyboard->keycode_options & KEYCODE_OPTION_FRENCH_NUMBERS) &&
keymap && keymap->french_numbers &&
(scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_0)) {
modstate |= SDL_KMOD_SHIFT;
}
keycode = SDL_GetKeymapKeycode(keymap, scancode, modstate);
if (keyboard->keycode_options & KEYCODE_OPTION_HIDE_NUMPAD) {
keycode = SDL_ConvertNumpadKeycode(keycode, numlock);
}
return keycode;
}
return SDL_GetKeymapKeycode(keyboard->keymap, scancode, modstate);
}
SDL_Scancode SDL_GetScancodeFromKey(SDL_Keycode key, SDL_Keymod *modstate)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
return SDL_GetKeymapScancode(keyboard->keymap, key, modstate);
}
static bool SDL_SendKeyboardKeyInternal(Uint64 timestamp, Uint32 flags, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, bool down)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
bool posted = false;
SDL_Keycode keycode = SDLK_UNKNOWN;
Uint32 type;
bool repeat = false;
const Uint8 source = flags & KEYBOARD_SOURCE_MASK;
#ifdef DEBUG_KEYBOARD
SDL_Log("The '%s' key has been %s", SDL_GetScancodeName(scancode), down ? "pressed" : "released");
#endif
if (down) {
type = SDL_EVENT_KEY_DOWN;
} else {
type = SDL_EVENT_KEY_UP;
}
if (scancode > SDL_SCANCODE_UNKNOWN && scancode < SDL_SCANCODE_COUNT) {
if (down) {
if (keyboard->keystate[scancode]) {
if (!(keyboard->keysource[scancode] & source)) {
keyboard->keysource[scancode] |= source;
return false;
}
repeat = true;
}
keyboard->keysource[scancode] |= source;
} else {
if (!keyboard->keystate[scancode]) {
return false;
}
keyboard->keysource[scancode] = 0;
}
keyboard->keystate[scancode] = down;
keycode = SDL_GetKeyFromScancode(scancode, keyboard->modstate, true);
} else if (rawcode == 0) {
return false;
}
if (source == KEYBOARD_HARDWARE) {
keyboard->hardware_timestamp = SDL_GetTicks();
} else if (source == KEYBOARD_AUTORELEASE) {
keyboard->autorelease_pending = true;
}
if (!(flags & KEYBOARD_IGNOREMODIFIERS) && !repeat) {
SDL_Keymod modifier;
switch (keycode) {
case SDLK_LCTRL:
modifier = SDL_KMOD_LCTRL;
break;
case SDLK_RCTRL:
modifier = SDL_KMOD_RCTRL;
break;
case SDLK_LSHIFT:
modifier = SDL_KMOD_LSHIFT;
break;
case SDLK_RSHIFT:
modifier = SDL_KMOD_RSHIFT;
break;
case SDLK_LALT:
modifier = SDL_KMOD_LALT;
break;
case SDLK_RALT:
modifier = SDL_KMOD_RALT;
break;
case SDLK_LGUI:
modifier = SDL_KMOD_LGUI;
break;
case SDLK_RGUI:
modifier = SDL_KMOD_RGUI;
break;
case SDLK_MODE:
modifier = SDL_KMOD_MODE;
break;
default:
modifier = SDL_KMOD_NONE;
break;
}
if (SDL_EVENT_KEY_DOWN == type) {
switch (keycode) {
case SDLK_NUMLOCKCLEAR:
keyboard->modstate ^= SDL_KMOD_NUM;
break;
case SDLK_CAPSLOCK:
keyboard->modstate ^= SDL_KMOD_CAPS;
break;
case SDLK_SCROLLLOCK:
keyboard->modstate ^= SDL_KMOD_SCROLL;
break;
default:
keyboard->modstate |= modifier;
break;
}
} else {
keyboard->modstate &= ~modifier;
}
}
if (SDL_EventEnabled(type)) {
SDL_Event event;
event.type = type;
event.common.timestamp = timestamp;
event.key.scancode = scancode;
event.key.key = keycode;
event.key.mod = keyboard->modstate;
event.key.raw = (Uint16)rawcode;
event.key.down = down;
event.key.repeat = repeat;
event.key.windowID = keyboard->focus ? keyboard->focus->id : 0;
event.key.which = keyboardID;
posted = SDL_PushEvent(&event);
}
if (keycode == SDLK_TAB && down &&
(keyboard->modstate & SDL_KMOD_ALT) &&
keyboard->focus &&
(keyboard->focus->flags & SDL_WINDOW_KEYBOARD_GRABBED) &&
(keyboard->focus->flags & SDL_WINDOW_FULLSCREEN) &&
SDL_GetHintBoolean(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, true)) {
SDL_MinimizeWindow(keyboard->focus);
}
return posted;
}
void SDL_SendKeyboardUnicodeKey(Uint64 timestamp, Uint32 ch)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
SDL_Keymod modstate = SDL_KMOD_NONE;
SDL_Scancode scancode;
if (ch == '\n') {
ch = SDLK_RETURN;
}
scancode = SDL_GetKeymapScancode(keyboard->keymap, ch, &modstate);
if (scancode == SDL_SCANCODE_UNKNOWN && ch < SDLK_SCANCODE_MASK) {
scancode = GetNextReservedScancode();
SetKeymapEntry(scancode, modstate, ch);
}
if (modstate & SDL_KMOD_SHIFT) {
SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_LSHIFT, true);
}
SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, true);
SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, false);
if (modstate & SDL_KMOD_SHIFT) {
SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_VIRTUAL, SDL_GLOBAL_KEYBOARD_ID, 0, SDL_SCANCODE_LSHIFT, false);
}
}
bool SDL_SendKeyboardKey(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, bool down)
{
return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, keyboardID, rawcode, scancode, down);
}
bool SDL_SendKeyboardKeyAndKeycode(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, SDL_Keycode keycode, bool down)
{
if (down) {
SetKeymapEntry(scancode, SDL_GetModState(), keycode);
}
return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE, keyboardID, rawcode, scancode, down);
}
bool SDL_SendKeyboardKeyIgnoreModifiers(Uint64 timestamp, SDL_KeyboardID keyboardID, int rawcode, SDL_Scancode scancode, bool down)
{
return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_HARDWARE | KEYBOARD_IGNOREMODIFIERS, keyboardID, rawcode, scancode, down);
}
bool SDL_SendKeyboardKeyAutoRelease(Uint64 timestamp, SDL_Scancode scancode)
{
return SDL_SendKeyboardKeyInternal(timestamp, KEYBOARD_AUTORELEASE, SDL_GLOBAL_KEYBOARD_ID, 0, scancode, true);
}
void SDL_ReleaseAutoReleaseKeys(void)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
int scancode;
if (keyboard->autorelease_pending) {
for (scancode = SDL_SCANCODE_UNKNOWN; scancode < SDL_SCANCODE_COUNT; ++scancode) {
if (keyboard->keysource[scancode] == KEYBOARD_AUTORELEASE) {
SDL_SendKeyboardKeyInternal(0, KEYBOARD_AUTORELEASE, SDL_GLOBAL_KEYBOARD_ID, 0, (SDL_Scancode)scancode, false);
}
}
keyboard->autorelease_pending = false;
}
if (keyboard->hardware_timestamp) {
if (SDL_GetTicks() >= keyboard->hardware_timestamp + 250) {
keyboard->hardware_timestamp = 0;
}
}
}
bool SDL_HardwareKeyboardKeyPressed(void)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
int scancode;
for (scancode = SDL_SCANCODE_UNKNOWN; scancode < SDL_SCANCODE_COUNT; ++scancode) {
if (keyboard->keysource[scancode] & KEYBOARD_HARDWARE) {
return true;
}
}
return keyboard->hardware_timestamp ? true : false;
}
void SDL_SendKeyboardText(const char *text)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
if (!keyboard->focus || !SDL_TextInputActive(keyboard->focus)) {
return;
}
if (!text || !*text) {
return;
}
if (SDL_iscntrl((unsigned char)*text)) {
return;
}
if (SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
SDL_Event event;
event.type = SDL_EVENT_TEXT_INPUT;
event.common.timestamp = 0;
event.text.windowID = keyboard->focus ? keyboard->focus->id : 0;
event.text.text = SDL_CreateTemporaryString(text);
if (!event.text.text) {
return;
}
SDL_PushEvent(&event);
}
}
void SDL_SendEditingText(const char *text, int start, int length)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
if (!keyboard->focus || !SDL_TextInputActive(keyboard->focus)) {
return;
}
if (!text) {
return;
}
if (SDL_EventEnabled(SDL_EVENT_TEXT_EDITING)) {
SDL_Event event;
event.type = SDL_EVENT_TEXT_EDITING;
event.common.timestamp = 0;
event.edit.windowID = keyboard->focus ? keyboard->focus->id : 0;
event.edit.start = start;
event.edit.length = length;
event.edit.text = SDL_CreateTemporaryString(text);
if (!event.edit.text) {
return;
}
SDL_PushEvent(&event);
}
}
static const char * const *CreateCandidatesForEvent(char **candidates, int num_candidates)
{
const char **event_candidates;
int i;
char *ptr;
size_t total_length = (num_candidates + 1) * sizeof(*event_candidates);
for (i = 0; i < num_candidates; ++i) {
size_t length = SDL_strlen(candidates[i]) + 1;
total_length += length;
}
event_candidates = (const char **)SDL_AllocateTemporaryMemory(total_length);
if (!event_candidates) {
return NULL;
}
ptr = (char *)(event_candidates + (num_candidates + 1));
for (i = 0; i < num_candidates; ++i) {
size_t length = SDL_strlen(candidates[i]) + 1;
event_candidates[i] = ptr;
SDL_memcpy(ptr, candidates[i], length);
ptr += length;
}
event_candidates[i] = NULL;
return event_candidates;
}
void SDL_SendEditingTextCandidates(char **candidates, int num_candidates, int selected_candidate, bool horizontal)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
if (!keyboard->focus || !SDL_TextInputActive(keyboard->focus)) {
return;
}
if (SDL_EventEnabled(SDL_EVENT_TEXT_EDITING_CANDIDATES)) {
SDL_Event event;
event.type = SDL_EVENT_TEXT_EDITING_CANDIDATES;
event.common.timestamp = 0;
event.edit.windowID = keyboard->focus ? keyboard->focus->id : 0;
if (num_candidates > 0) {
const char * const *event_candidates = CreateCandidatesForEvent(candidates, num_candidates);
if (!event_candidates) {
return;
}
event.edit_candidates.candidates = event_candidates;
event.edit_candidates.num_candidates = num_candidates;
event.edit_candidates.selected_candidate = selected_candidate;
event.edit_candidates.horizontal = horizontal;
} else {
event.edit_candidates.candidates = NULL;
event.edit_candidates.num_candidates = 0;
event.edit_candidates.selected_candidate = -1;
event.edit_candidates.horizontal = false;
}
SDL_PushEvent(&event);
}
}
void SDL_QuitKeyboard(void)
{
SDL_keyboard_quitting = true;
for (int i = SDL_keyboard_count; i--;) {
SDL_RemoveKeyboard(SDL_keyboards[i]);
}
SDL_free(SDL_keyboards);
SDL_keyboards = NULL;
SDL_DestroyHashTable(SDL_keyboard_names);
SDL_keyboard_names = NULL;
if (SDL_keyboard.keymap && SDL_keyboard.keymap->auto_release) {
SDL_DestroyKeymap(SDL_keyboard.keymap);
SDL_keyboard.keymap = NULL;
}
SDL_RemoveHintCallback(SDL_HINT_KEYCODE_OPTIONS,
SDL_KeycodeOptionsChanged, &SDL_keyboard);
SDL_keyboard_quitting = false;
}
const bool *SDL_GetKeyboardState(int *numkeys)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
if (numkeys) {
*numkeys = SDL_SCANCODE_COUNT;
}
return keyboard->keystate;
}
SDL_Keymod SDL_GetModState(void)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
return keyboard->modstate;
}
void SDL_SetModState(SDL_Keymod modstate)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
keyboard->modstate = modstate;
}
void SDL_ToggleModState(SDL_Keymod modstate, bool toggle)
{
SDL_Keyboard *keyboard = &SDL_keyboard;
if (toggle) {
keyboard->modstate |= modstate;
} else {
keyboard->modstate &= ~modstate;
}
}