diff --git a/docs/README-migration.md b/docs/README-migration.md index e3924f29ae..cc890fb071 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -1031,6 +1031,9 @@ The following structures have been removed: SDL_Keycode is now Uint32 and the SDLK_* constants are now defines instead of an enum, to more clearly reflect that they are a subset of the possible values of an SDL_Keycode. +In addition to the `SDLK_SCANCODE_MASK` bit found on key codes that directly map to scancodes, there is now the +`SDLK_EXTENDED_MASK` bit used to denote key codes that don't have a corresponding scancode, and aren't a unicode value. + The following symbols have been removed: * KMOD_RESERVED - No replacement. A bit named "RESERVED" probably shouldn't be used in an app, but if you need it, this was equivalent to KMOD_SCROLL (0x8000) in SDL2. diff --git a/include/SDL3/SDL_keycode.h b/include/SDL3/SDL_keycode.h index cb155a5a1c..3c40e96259 100644 --- a/include/SDL3/SDL_keycode.h +++ b/include/SDL3/SDL_keycode.h @@ -47,11 +47,15 @@ * A special exception is the number keys at the top of the keyboard which map * to SDLK_0...SDLK_9 on AZERTY layouts. * + * Keys with the `SDLK_EXTENDED_MASK` bit set do not map to a scancode or + * unicode code point. + * * \since This datatype is available since SDL 3.1.3. */ typedef Uint32 SDL_Keycode; -#define SDLK_SCANCODE_MASK (1u<<30) +#define SDLK_EXTENDED_MASK (1u << 29) +#define SDLK_SCANCODE_MASK (1u << 30) #define SDL_SCANCODE_TO_KEYCODE(X) (X | SDLK_SCANCODE_MASK) #define SDLK_UNKNOWN 0x00000000u /**< 0 */ #define SDLK_RETURN 0x0000000du /**< '\r' */ @@ -302,6 +306,13 @@ typedef Uint32 SDL_Keycode; #define SDLK_SOFTRIGHT 0x40000120u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_SOFTRIGHT) */ #define SDLK_CALL 0x40000121u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_CALL) */ #define SDLK_ENDCALL 0x40000122u /**< SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_ENDCALL) */ +#define SDLK_LEFT_TAB 0x20000001u /**< Extended key Left Tab */ +#define SDLK_LEVEL5_SHIFT 0x20000002u /**< Extended key Level 5 Shift */ +#define SDLK_MULTI_KEY_COMPOSE 0x20000003u /**< Extended key Multi-key Compose */ +#define SDLK_LMETA 0x20000004u /**< Extended key Left Meta */ +#define SDLK_RMETA 0x20000005u /**< Extended key Right Meta */ +#define SDLK_LHYPER 0x20000006u /**< Extended key Left Hyper */ +#define SDLK_RHYPER 0x20000007u /**< Extended key Right Hyper */ /** * Valid key modifiers (possibly OR'd together). diff --git a/src/events/SDL_keymap.c b/src/events/SDL_keymap.c index 1aeb57c6c5..5c3f784c6c 100644 --- a/src/events/SDL_keymap.c +++ b/src/events/SDL_keymap.c @@ -189,6 +189,18 @@ static const SDL_Keycode shifted_default_symbols[] = { SDLK_QUESTION }; +static const struct +{ + SDL_Keycode keycode; + SDL_Scancode scancode; +} extended_default_symbols[] = { + { SDLK_LEFT_TAB, SDL_SCANCODE_TAB }, + { SDLK_MULTI_KEY_COMPOSE, SDL_SCANCODE_APPLICATION }, // Sun keyboards + { SDLK_LMETA, SDL_SCANCODE_LGUI }, + { SDLK_RMETA, SDL_SCANCODE_RGUI }, + { SDLK_RHYPER, SDL_SCANCODE_APPLICATION } +}; + static SDL_Keycode SDL_GetDefaultKeyFromScancode(SDL_Scancode scancode, SDL_Keymod modstate) { if (((int)scancode) < SDL_SCANCODE_UNKNOWN || scancode >= SDL_SCANCODE_COUNT) { @@ -600,6 +612,16 @@ static SDL_Scancode SDL_GetDefaultScancodeFromKey(SDL_Keycode key, SDL_Keymod *m return SDL_SCANCODE_UNKNOWN; } + if (key & SDLK_EXTENDED_MASK) { + for (int i = 0; i < SDL_arraysize(extended_default_symbols); ++i) { + if (extended_default_symbols[i].keycode == key) { + return extended_default_symbols[i].scancode; + } + } + + return SDL_SCANCODE_UNKNOWN; + } + if (key & SDLK_SCANCODE_MASK) { return (SDL_Scancode)(key & ~SDLK_SCANCODE_MASK); } @@ -932,6 +954,16 @@ static const char *SDL_scancode_names[SDL_SCANCODE_COUNT] = /* 290 */ "EndCall", }; +static const char *SDL_extended_key_names[] = { + "LeftTab", /* 0x01 SDLK_LEFT_TAB */ + "Level5Shift", /* 0x02 SDLK_LEVEL5_SHIFT */ + "MultiKeyCompose", /* 0x03 SDLK_MULTI_KEY_COMPOSE */ + "Left Meta", /* 0x04 SDLK_LMETA */ + "Right Meta", /* 0x05 SDLK_RMETA */ + "Left Hyper", /* 0x06 SDLK_LHYPER */ + "Right Hyper" /* 0x07 SDLK_RHYPER */ +}; + bool SDL_SetScancodeName(SDL_Scancode scancode, const char *name) { if (((int)scancode) < SDL_SCANCODE_UNKNOWN || scancode >= SDL_SCANCODE_COUNT) { @@ -990,6 +1022,17 @@ const char *SDL_GetKeyName(SDL_Keycode key) return SDL_GetScancodeName((SDL_Scancode)(key & ~SDLK_SCANCODE_MASK)); } + if (key & SDLK_EXTENDED_MASK) { + const SDL_Keycode idx = (key & ~SDLK_EXTENDED_MASK); + if (idx > 0 && (idx - 1) < SDL_arraysize(SDL_extended_key_names)) { + return SDL_extended_key_names[idx - 1]; + } + + // Key out of name index bounds. + SDL_InvalidParamError("key"); + return ""; + } + switch (key) { case SDLK_RETURN: return SDL_GetScancodeName(SDL_SCANCODE_RETURN); @@ -1087,5 +1130,12 @@ SDL_Keycode SDL_GetKeyFromName(const char *name) return key; } + // Check the extended key names + for (SDL_Keycode i = 0; i < SDL_arraysize(SDL_extended_key_names); ++i) { + if (SDL_strcasecmp(name, SDL_extended_key_names[i]) == 0) { + return (i + 1) | SDLK_EXTENDED_MASK; + } + } + return SDL_GetKeyFromScancode(SDL_GetScancodeFromName(name), SDL_KMOD_NONE, false); } diff --git a/src/events/SDL_keysym_to_keycode.c b/src/events/SDL_keysym_to_keycode.c new file mode 100644 index 0000000000..7cbfcb62fe --- /dev/null +++ b/src/events/SDL_keysym_to_keycode.c @@ -0,0 +1,68 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#if defined(SDL_VIDEO_DRIVER_WAYLAND) || defined(SDL_VIDEO_DRIVER_X11) + +#include "SDL_keyboard_c.h" +#include "SDL_keysym_to_scancode_c.h" +#include "imKStoUCS.h" + + +// Extended key code mappings +static const struct +{ + Uint32 keysym; + SDL_Keycode keycode; +} keysym_to_keycode_table[] = { + { 0xfe03, SDLK_MODE }, // XK_ISO_Level3_Shift + { 0xfe11, SDLK_LEVEL5_SHIFT }, // XK_ISO_Level5_Shift + { 0xfe20, SDLK_LEFT_TAB }, // XK_ISO_Left_Tab + { 0xff20, SDLK_MULTI_KEY_COMPOSE }, // XK_Multi_key + { 0xffe7, SDLK_LMETA }, // XK_Meta_L + { 0xffe8, SDLK_RMETA }, // XK_Meta_R + { 0xffed, SDLK_LHYPER }, // XK_Hyper_L + { 0xffee, SDLK_RHYPER }, // XK_Hyper_R +}; + +SDL_Keycode SDL_GetKeyCodeFromKeySym(Uint32 keysym, Uint32 keycode, SDL_Keymod modifiers) +{ + SDL_Keycode sdl_keycode = SDL_KeySymToUcs4(keysym); + + if (!sdl_keycode) { + for (int i = 0; i < SDL_arraysize(keysym_to_keycode_table); ++i) { + if (keysym == keysym_to_keycode_table[i].keysym) { + return keysym_to_keycode_table[i].keycode; + } + } + } + + if (!sdl_keycode) { + const SDL_Scancode scancode = SDL_GetScancodeFromKeySym(keysym, keycode); + if (scancode != SDL_SCANCODE_UNKNOWN) { + sdl_keycode = SDL_GetKeymapKeycode(NULL, scancode, modifiers); + } + } + + return sdl_keycode; +} + +#endif // SDL_VIDEO_DRIVER_WAYLAND || SDL_VIDEO_DRIVER_X11 diff --git a/src/events/SDL_keysym_to_keycode_c.h b/src/events/SDL_keysym_to_keycode_c.h new file mode 100644 index 0000000000..2321d2022b --- /dev/null +++ b/src/events/SDL_keysym_to_keycode_c.h @@ -0,0 +1,28 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL_keysym_to_keycode_c_h_ +#define SDL_keysym_to_keycode_c_h_ + +// Convert a keysym to an SDL key code +extern SDL_Keycode SDL_GetKeyCodeFromKeySym(Uint32 keysym, Uint32 keycode, SDL_Keymod modifiers); + +#endif // SDL_keysym_to_scancode_c_h_ diff --git a/src/events/SDL_keysym_to_scancode.c b/src/events/SDL_keysym_to_scancode.c index 8067e05c59..8d43ca3509 100644 --- a/src/events/SDL_keysym_to_scancode.c +++ b/src/events/SDL_keysym_to_scancode.c @@ -45,6 +45,7 @@ static const struct { { 0xFF62, SDL_SCANCODE_EXECUTE }, // XK_Execute { 0xFFEE, SDL_SCANCODE_APPLICATION }, // XK_Hyper_R { 0xFE03, SDL_SCANCODE_RALT }, // XK_ISO_Level3_Shift + { 0xFE20, SDL_SCANCODE_TAB }, // XK_ISO_Left_Tab { 0xFFEB, SDL_SCANCODE_LGUI }, // XK_Super_L { 0xFFEC, SDL_SCANCODE_RGUI }, // XK_Super_R { 0xFF7E, SDL_SCANCODE_MODE }, // XK_Mode_switch diff --git a/src/events/SDL_keysym_to_scancode_c.h b/src/events/SDL_keysym_to_scancode_c.h index 8d0e2143af..2e890fda81 100644 --- a/src/events/SDL_keysym_to_scancode_c.h +++ b/src/events/SDL_keysym_to_scancode_c.h @@ -25,4 +25,7 @@ // This function only correctly maps letters and numbers for keyboards in US QWERTY layout extern SDL_Scancode SDL_GetScancodeFromKeySym(Uint32 keysym, Uint32 keycode); +// Convert a keysym to an extended SDL key code +extern SDL_Keycode SDL_GetExtendedKeyCodeFromKeySym(Uint32 keysym); + #endif // SDL_keysym_to_scancode_c_h_ diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index b92c84a12e..36e2933970 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -26,6 +26,7 @@ #include "../../core/unix/SDL_poll.h" #include "../../events/SDL_events_c.h" #include "../../events/SDL_scancode_tables_c.h" +#include "../../events/SDL_keysym_to_keycode_c.h" #include "../../core/linux/SDL_system_theme.h" #include "../SDL_sysvideo.h" @@ -68,16 +69,14 @@ // Weston uses a ratio of 10 units per scroll tick #define WAYLAND_WHEEL_AXIS_UNIT 10 -// "Mod5" is typically level 3 shift, which SDL calls SDL_KMOD_MODE (AltGr). -#ifndef XKB_MOD_NAME_MOD5 -#define XKB_MOD_NAME_MOD5 "Mod5" -#endif - -// "Mod3" is typically level 5 shift, but is often remapped. #ifndef XKB_MOD_NAME_MOD3 #define XKB_MOD_NAME_MOD3 "Mod3" #endif +#ifndef XKB_MOD_NAME_MOD5 +#define XKB_MOD_NAME_MOD5 "Mod5" +#endif + // Keyboard and mouse names to match XWayland #define WAYLAND_DEFAULT_KEYBOARD_NAME "Virtual core keyboard" #define WAYLAND_DEFAULT_POINTER_NAME "Virtual core pointer" @@ -1278,40 +1277,9 @@ static void Wayland_keymap_iter(struct xkb_keymap *keymap, xkb_keycode_t key, vo } if (WAYLAND_xkb_state_key_get_syms(sdlKeymap->state, key, &syms) > 0) { - uint32_t keycode = SDL_KeySymToUcs4(syms[0]); - bool key_is_unknown = false; + SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(syms[0], key, sdlKeymap->modstate); if (!keycode) { - switch (syms[0]) { - // The default SDL scancode table sets this to right alt instead of AltGr/Mode, so handle it separately. - case XKB_KEY_ISO_Level3_Shift: - keycode = SDLK_MODE; - break; - - /* The default SDL scancode table sets Meta L/R to the GUI keys, and Hyper R to app menu, which is - * correct as far as physical key placement goes, but these keys are functionally distinct from the - * default keycodes SDL returns for the scancodes, so they are set to unknown. - * - * SDL has no scancode mapping for Hyper L or Level 5 Shift. - */ - case XKB_KEY_Meta_L: - case XKB_KEY_Meta_R: - case XKB_KEY_Hyper_L: - case XKB_KEY_Hyper_R: - case XKB_KEY_ISO_Level5_Shift: - keycode = SDLK_UNKNOWN; - key_is_unknown = true; - break; - - default: - { - const SDL_Scancode sc = SDL_GetScancodeFromKeySym(syms[0], key); - keycode = SDL_GetKeymapKeycode(NULL, sc, sdlKeymap->modstate); - } break; - } - } - - if (!keycode && !key_is_unknown) { switch (scancode) { case SDL_SCANCODE_RETURN: keycode = SDLK_RETURN; @@ -1322,9 +1290,6 @@ static void Wayland_keymap_iter(struct xkb_keymap *keymap, xkb_keycode_t key, vo case SDL_SCANCODE_BACKSPACE: keycode = SDLK_BACKSPACE; break; - case SDL_SCANCODE_TAB: - keycode = SDLK_TAB; - break; case SDL_SCANCODE_DELETE: keycode = SDLK_DELETE; break; @@ -1520,17 +1485,20 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, * Virtual keyboards can have arbitrary layouts, arbitrary scancodes/keycodes, etc... * Key presses from these devices must be looked up by their keysym value. */ -static void Wayland_get_scancode_from_key(struct SDL_WaylandInput *input, uint32_t keycode, SDL_Scancode *scancode) +static SDL_Scancode Wayland_GetScancodeForKey(struct SDL_WaylandInput *input, uint32_t key) { - const xkb_keysym_t *syms; + SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; if (!input->keyboard_is_virtual) { - *scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, keycode); + scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, key); } else { - if (WAYLAND_xkb_keymap_key_get_syms_by_level(input->xkb.keymap, keycode + 8, input->xkb.current_group, 0, &syms) > 0) { - *scancode = SDL_GetScancodeFromKeySym(syms[0], keycode + 8); + const xkb_keysym_t *syms; + if (WAYLAND_xkb_keymap_key_get_syms_by_level(input->xkb.keymap, key + 8, input->xkb.current_group, 0, &syms) > 0) { + scancode = SDL_GetScancodeFromKeySym(syms[0], key); } } + + return scancode; } static void Wayland_ReconcileModifiers(struct SDL_WaylandInput *input, bool key_pressed) @@ -1573,6 +1541,9 @@ static void Wayland_ReconcileModifiers(struct SDL_WaylandInput *input, bool key_ input->pressed_modifiers &= ~SDL_KMOD_GUI; } + /* Note: This is not backwards: in the default keymap, Mod5 is typically + * level 3 shift, and Mod3 is typically level 5 shift. + */ if (input->xkb.wl_pressed_modifiers & input->xkb.idx_mod3) { if (!(input->pressed_modifiers & SDL_KMOD_LEVEL5)) { input->pressed_modifiers |= SDL_KMOD_LEVEL5; @@ -1641,9 +1612,7 @@ static void Wayland_ReconcileModifiers(struct SDL_WaylandInput *input, bool key_ input->locked_modifiers &= ~SDL_KMOD_GUI; } - /* The Mod3 modifier corresponds to no particular SDL keycode, so it is - * only activated by the backend modifier callback. - */ + // As above, this is correct: Mod3 is typically level 5 shift, and Mod5 is typically level 3 shift. if (input->xkb.wl_locked_modifiers & input->xkb.idx_mod3) { input->locked_modifiers |= SDL_KMOD_LEVEL5; } else { @@ -1711,6 +1680,9 @@ static void Wayland_HandleModifierKeys(struct SDL_WaylandInput *input, SDL_Scanc case SDLK_MODE: mod = SDL_KMOD_MODE; break; + case SDLK_LEVEL5_SHIFT: + mod = SDL_KMOD_LEVEL5; + break; default: return; } @@ -1759,9 +1731,7 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, window->last_focus_event_time_ns = timestamp; wl_array_for_each (key, keys) { - SDL_Scancode scancode; - - Wayland_get_scancode_from_key(input, *key, &scancode); + const SDL_Scancode scancode = Wayland_GetScancodeForKey(input, *key); const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false); switch (keycode) { @@ -1774,6 +1744,7 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, case SDLK_LGUI: case SDLK_RGUI: case SDLK_MODE: + case SDLK_LEVEL5_SHIFT: Wayland_HandleModifierKeys(input, scancode, true); SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, *key, scancode, true); break; @@ -1883,7 +1854,6 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, { struct SDL_WaylandInput *input = data; enum wl_keyboard_key_state state = state_w; - SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; char text[8]; bool has_text = false; bool handled_by_ime = false; @@ -1909,10 +1879,11 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard, keyboard_input_get_text(text, input, key, false, &handled_by_ime); } - Wayland_get_scancode_from_key(input, key, &scancode); + const SDL_Scancode scancode = Wayland_GetScancodeForKey(input, key); Wayland_HandleModifierKeys(input, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED); Uint64 timestamp = Wayland_GetKeyboardTimestamp(input, time); - SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, key, scancode, (state == WL_KEYBOARD_KEY_STATE_PRESSED)); + + SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, key, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED); if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { if (has_text && !(SDL_GetModState() & SDL_KMOD_CTRL)) { diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 8cdce7184a..3641cf9c35 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -246,79 +246,83 @@ static void X11_HandleGenericEvent(SDL_VideoDevice *_this, XEvent *xev) } #endif // SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS -static void X11_ReconcileModifiers(SDL_VideoData *viddata) +static void X11_UpdateSystemKeyModifiers(SDL_VideoData *viddata) { Window junk_window; int x, y; - Uint32 xk_modifiers = 0; - X11_XQueryPointer(viddata->display, DefaultRootWindow(viddata->display), &junk_window, &junk_window, &x, &y, &x, &y, &xk_modifiers); + X11_XQueryPointer(viddata->display, DefaultRootWindow(viddata->display), &junk_window, &junk_window, &x, &y, &x, &y, &viddata->xkb.xkb_modifiers); +} + +static void X11_ReconcileModifiers(SDL_VideoData *viddata) +{ + const Uint32 xk_modifiers = viddata->xkb.xkb_modifiers; /* If a modifier was activated by a keypress, it will be tied to the * specific left/right key that initiated it. Otherwise, the ambiguous * left/right combo is used. */ if (xk_modifiers & ShiftMask) { - if (!(viddata->xkb.active_modifiers & SDL_KMOD_SHIFT)) { - viddata->xkb.active_modifiers |= SDL_KMOD_SHIFT; + if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_SHIFT)) { + viddata->xkb.sdl_modifiers |= SDL_KMOD_SHIFT; } } else { - viddata->xkb.active_modifiers &= ~SDL_KMOD_SHIFT; + viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SHIFT; } if (xk_modifiers & ControlMask) { - if (!(viddata->xkb.active_modifiers & SDL_KMOD_CTRL)) { - viddata->xkb.active_modifiers |= SDL_KMOD_CTRL; + if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_CTRL)) { + viddata->xkb.sdl_modifiers |= SDL_KMOD_CTRL; } } else { - viddata->xkb.active_modifiers &= ~SDL_KMOD_CTRL; + viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CTRL; } // Mod1 is used for the Alt keys if (xk_modifiers & Mod1Mask) { - if (!(viddata->xkb.active_modifiers & SDL_KMOD_ALT)) { - viddata->xkb.active_modifiers |= SDL_KMOD_ALT; + if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_ALT)) { + viddata->xkb.sdl_modifiers |= SDL_KMOD_ALT; } } else { - viddata->xkb.active_modifiers &= ~SDL_KMOD_ALT; + viddata->xkb.sdl_modifiers &= ~SDL_KMOD_ALT; } // Mod4 is used for the Super (aka GUI/Logo) keys. if (xk_modifiers & Mod4Mask) { - if (!(viddata->xkb.active_modifiers & SDL_KMOD_GUI)) { - viddata->xkb.active_modifiers |= SDL_KMOD_GUI; + if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_GUI)) { + viddata->xkb.sdl_modifiers |= SDL_KMOD_GUI; } } else { - viddata->xkb.active_modifiers &= ~SDL_KMOD_GUI; + viddata->xkb.sdl_modifiers &= ~SDL_KMOD_GUI; } // Mod3 is typically Level 5 shift. if (xk_modifiers & Mod3Mask) { - viddata->xkb.active_modifiers |= SDL_KMOD_LEVEL5; + viddata->xkb.sdl_modifiers |= SDL_KMOD_LEVEL5; } else { - viddata->xkb.active_modifiers &= ~SDL_KMOD_LEVEL5; + viddata->xkb.sdl_modifiers &= ~SDL_KMOD_LEVEL5; } // Mod5 is typically Level 3 shift (aka AltGr). if (xk_modifiers & Mod5Mask) { - viddata->xkb.active_modifiers |= SDL_KMOD_MODE; + viddata->xkb.sdl_modifiers |= SDL_KMOD_MODE; } else { - viddata->xkb.active_modifiers &= ~SDL_KMOD_MODE; + viddata->xkb.sdl_modifiers &= ~SDL_KMOD_MODE; } if (xk_modifiers & viddata->xkb.numlock_mask) { - viddata->xkb.active_modifiers |= SDL_KMOD_NUM; + viddata->xkb.sdl_modifiers |= SDL_KMOD_NUM; } else { - viddata->xkb.active_modifiers &= ~SDL_KMOD_NUM; + viddata->xkb.sdl_modifiers &= ~SDL_KMOD_NUM; } if (xk_modifiers & viddata->xkb.scrolllock_mask) { - viddata->xkb.active_modifiers |= SDL_KMOD_SCROLL; + viddata->xkb.sdl_modifiers |= SDL_KMOD_SCROLL; } else { - viddata->xkb.active_modifiers &= ~SDL_KMOD_SCROLL; + viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SCROLL; } - SDL_SetModState(viddata->xkb.active_modifiers); + SDL_SetModState(viddata->xkb.sdl_modifiers); } static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed, bool reconcile) @@ -360,14 +364,17 @@ static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode case SDLK_MODE: mod = SDL_KMOD_MODE; break; + case SDLK_LEVEL5_SHIFT: + mod = SDL_KMOD_LEVEL5; + break; default: - return; + break; } if (pressed) { - viddata->xkb.active_modifiers |= mod; + viddata->xkb.sdl_modifiers |= mod; } else { - viddata->xkb.active_modifiers &= ~mod; + viddata->xkb.sdl_modifiers &= ~mod; } if (reconcile) { @@ -403,6 +410,7 @@ void X11_ReconcileKeyboardState(SDL_VideoDevice *_this) case SDLK_LGUI: case SDLK_RGUI: case SDLK_MODE: + case SDLK_LEVEL5_SHIFT: X11_HandleModifierKeys(videodata, scancode, true, false); SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, true); break; @@ -415,6 +423,7 @@ void X11_ReconcileKeyboardState(SDL_VideoDevice *_this) } } + X11_UpdateSystemKeyModifiers(videodata); X11_ReconcileModifiers(videodata); } @@ -901,6 +910,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ #endif // DEBUG SCANCODES text[0] = '\0'; + X11_UpdateSystemKeyModifiers(videodata); if (SDL_TextInputActive(windowdata->window)) { #if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX) @@ -924,13 +934,12 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ SDL_Scancode scancode = videodata->key_layout[orig_keycode]; videodata->filter_code = orig_keycode; videodata->filter_time = xevent->xkey.time; - if (orig_event_type == KeyPress) { X11_HandleModifierKeys(videodata, scancode, true, true); SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, orig_keycode, scancode, true); } else { X11_HandleModifierKeys(videodata, scancode, false, true); - SDL_SendKeyboardKey(timestamp, keyboardID, orig_keycode, scancode, false); + SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, orig_keycode, scancode, false); } #endif return; @@ -1183,6 +1192,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) if (X11_XkbGetState(videodata->display, XkbUseCoreKbd, &state) == Success) { if (state.group != videodata->xkb.current_group) { // Only rebuild the keymap if the layout has changed. + videodata->xkb.current_group = state.group; X11_UpdateKeymap(_this, true); } } diff --git a/src/video/x11/SDL_x11keyboard.c b/src/video/x11/SDL_x11keyboard.c index f192d77b8b..d1d60f30c5 100644 --- a/src/video/x11/SDL_x11keyboard.c +++ b/src/video/x11/SDL_x11keyboard.c @@ -32,6 +32,7 @@ #include "../../events/imKStoUCS.h" #include "../../events/SDL_keysym_to_scancode_c.h" +#include "../../events/SDL_keysym_to_keycode_c.h" #ifdef X_HAVE_UTF8_STRING #include @@ -280,7 +281,7 @@ bool X11_InitKeyboard(SDL_VideoDevice *_this) if (scancode == data->key_layout[i]) { continue; } - if ((SDL_GetKeymapKeycode(NULL, scancode, SDL_KMOD_NONE) & SDLK_SCANCODE_MASK) && X11_ScancodeIsRemappable(scancode)) { + if ((SDL_GetKeymapKeycode(NULL, scancode, SDL_KMOD_NONE) & (SDLK_SCANCODE_MASK | SDLK_EXTENDED_MASK)) && X11_ScancodeIsRemappable(scancode)) { // Not a character key and the scancode is safe to remap #ifdef DEBUG_KEYBOARD SDL_Log("Changing scancode, was %d (%s), now %d (%s)\n", data->key_layout[i], SDL_GetScancodeName(data->key_layout[i]), scancode, SDL_GetScancodeName(scancode)); @@ -402,11 +403,8 @@ void X11_UpdateKeymap(SDL_VideoDevice *_this, bool send_event) }; SDL_VideoData *data = _this->internal; - int i; SDL_Scancode scancode; - SDL_Keymap *keymap; - - keymap = SDL_CreateKeymap(); + SDL_Keymap *keymap = SDL_CreateKeymap(); #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM if (data->xkb.desc_ptr) { @@ -420,9 +418,7 @@ void X11_UpdateKeymap(SDL_VideoDevice *_this, bool send_event) #endif for (int m = 0; m < SDL_arraysize(keymod_masks); ++m) { - for (i = 0; i < SDL_arraysize(data->key_layout); i++) { - SDL_Keycode keycode; - + for (int i = 0; i < SDL_arraysize(data->key_layout); ++i) { // Make sure this is a valid scancode scancode = data->key_layout[i]; if (scancode == SDL_SCANCODE_UNKNOWN) { @@ -430,40 +426,32 @@ void X11_UpdateKeymap(SDL_VideoDevice *_this, bool send_event) } const KeySym keysym = X11_KeyCodeToSym(_this, i, data->xkb.current_group, keymod_masks[m].xkb_mask); - bool key_is_unknown = false; - switch (keysym) { - // The default SDL scancode table sets this to right alt instead of AltGr/Mode, so handle it separately. - case XK_ISO_Level3_Shift: - keycode = SDLK_MODE; - break; + if (keysym != NoSymbol) { + SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(keysym, i, keymod_masks[m].sdl_mask); - /* The default SDL scancode table sets Meta L/R to the GUI keys, and Hyper R to app menu, which is - * correct as far as physical key placement goes, but these keys are functionally distinct from the - * default keycodes SDL returns for the scancodes, so they are set to unknown. - * - * SDL has no scancode mapping for Hyper L or Level 5 Shift, and they are usually mapped to something - * else, like Caps Lock, so just pass through the unknown keycode. - */ - case XK_Meta_L: - case XK_Meta_R: - case XK_Hyper_L: - case XK_Hyper_R: - case XK_ISO_Level5_Shift: - keycode = SDLK_UNKNOWN; - key_is_unknown = true; - break; + if (!keycode) { + switch (scancode) { + case SDL_SCANCODE_RETURN: + keycode = SDLK_RETURN; + break; + case SDL_SCANCODE_ESCAPE: + keycode = SDLK_ESCAPE; + break; + case SDL_SCANCODE_BACKSPACE: + keycode = SDLK_BACKSPACE; + break; + case SDL_SCANCODE_DELETE: + keycode = SDLK_DELETE; + break; + default: + keycode = SDL_SCANCODE_TO_KEYCODE(scancode); + break; + } + } - default: - keycode = SDL_KeySymToUcs4(keysym); - break; + SDL_SetKeymapEntry(keymap, scancode, keymod_masks[m].sdl_mask, keycode); } - - if (!keycode && !key_is_unknown) { - const SDL_Scancode keyScancode = SDL_GetScancodeFromKeySym(keysym, (KeyCode)i); - keycode = SDL_GetKeymapKeycode(NULL, keyScancode, keymod_masks[m].sdl_mask); - } - SDL_SetKeymapEntry(keymap, scancode, keymod_masks[m].sdl_mask, keycode); } } diff --git a/src/video/x11/SDL_x11video.h b/src/video/x11/SDL_x11video.h index eb5b649259..a336a800f5 100644 --- a/src/video/x11/SDL_x11video.h +++ b/src/video/x11/SDL_x11video.h @@ -148,8 +148,9 @@ struct SDL_VideoData #endif int event; unsigned int current_group; + unsigned int xkb_modifiers; - SDL_Keymod active_modifiers; + SDL_Keymod sdl_modifiers; Uint32 numlock_mask; Uint32 scrolllock_mask; diff --git a/test/checkkeys.c b/test/checkkeys.c index effd67eb8b..67954923fc 100644 --- a/test/checkkeys.c +++ b/test/checkkeys.c @@ -167,6 +167,9 @@ static void print_modifiers(char **text, size_t *maxlen, SDL_Keymod mod) if (mod & SDL_KMOD_MODE) { print_string(text, maxlen, " MODE"); } + if (mod & SDL_KMOD_LEVEL5) { + print_string(text, maxlen, " LEVEL5"); + } if (mod & SDL_KMOD_SCROLL) { print_string(text, maxlen, " SCROLL"); }