Skip to content

X11/Wayland: Support the "Mod3" modifier and add extra keycodes for common Xkb keys #11871

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/README-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
14 changes: 13 additions & 1 deletion include/SDL3/SDL_keycode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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' */
Expand Down Expand Up @@ -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).
Expand All @@ -313,6 +324,7 @@ typedef Uint16 SDL_Keymod;
#define SDL_KMOD_NONE 0x0000u /**< no modifier is applicable. */
#define SDL_KMOD_LSHIFT 0x0001u /**< the left Shift key is down. */
#define SDL_KMOD_RSHIFT 0x0002u /**< the right Shift key is down. */
#define SDL_KMOD_LEVEL5 0x0004u /**< the Level 5 Shift key is down. */
#define SDL_KMOD_LCTRL 0x0040u /**< the left Ctrl (Control) key is down. */
#define SDL_KMOD_RCTRL 0x0080u /**< the right Ctrl (Control) key is down. */
#define SDL_KMOD_LALT 0x0100u /**< the left Alt key is down. */
Expand Down
54 changes: 52 additions & 2 deletions src/events/SDL_keymap.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ SDL_Keymap *SDL_CreateKeymap(void)

static SDL_Keymod NormalizeModifierStateForKeymap(SDL_Keymod modstate)
{
// The modifiers that affect the keymap are: SHIFT, CAPS, ALT, and MODE
modstate &= (SDL_KMOD_SHIFT | SDL_KMOD_CAPS | SDL_KMOD_ALT | SDL_KMOD_MODE);
// The modifiers that affect the keymap are: SHIFT, CAPS, ALT, MODE, and LEVEL5
modstate &= (SDL_KMOD_SHIFT | SDL_KMOD_CAPS | SDL_KMOD_ALT | SDL_KMOD_MODE | SDL_KMOD_LEVEL5);

// If either right or left Shift are set, set both in the output
if (modstate & SDL_KMOD_SHIFT) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
68 changes: 68 additions & 0 deletions src/events/SDL_keysym_to_keycode.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>

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
28 changes: 28 additions & 0 deletions src/events/SDL_keysym_to_keycode_c.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>

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_
1 change: 1 addition & 0 deletions src/events/SDL_keysym_to_scancode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/events/SDL_keysym_to_scancode_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_
4 changes: 4 additions & 0 deletions src/test/SDL_test_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,9 @@ static void SDLTest_PrintModStateFlag(char *text, size_t maxlen, SDL_Keymod flag
case SDL_KMOD_RSHIFT:
SDL_snprintfcat(text, maxlen, "RSHIFT");
break;
case SDL_KMOD_LEVEL5:
SDL_snprintfcat(text, maxlen, "LEVEL5");
break;
case SDL_KMOD_LCTRL:
SDL_snprintfcat(text, maxlen, "LCTRL");
break;
Expand Down Expand Up @@ -999,6 +1002,7 @@ static void SDLTest_PrintModState(char *text, size_t maxlen, SDL_Keymod keymod)
const SDL_Keymod kmod_flags[] = {
SDL_KMOD_LSHIFT,
SDL_KMOD_RSHIFT,
SDL_KMOD_LEVEL5,
SDL_KMOD_LCTRL,
SDL_KMOD_RCTRL,
SDL_KMOD_LALT,
Expand Down
Loading
Loading