Skip to content

Commit eb37869

Browse files
committed
wayland: Add support for Mod3 and more esoteric Xkb configurations
Adds support for Mod3, which is usually Level 5 shift, but can vary, as well as not altering the functionality of the more esoteric modifier keys, such as meta and hyper.
1 parent dcc645e commit eb37869

File tree

5 files changed

+118
-45
lines changed

5 files changed

+118
-45
lines changed

include/SDL3/SDL_keycode.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ typedef Uint16 SDL_Keymod;
313313
#define SDL_KMOD_NONE 0x0000u /**< no modifier is applicable. */
314314
#define SDL_KMOD_LSHIFT 0x0001u /**< the left Shift key is down. */
315315
#define SDL_KMOD_RSHIFT 0x0002u /**< the right Shift key is down. */
316+
#define SDL_KMOD_LEVEL5 0x0004u /**< the Level 5 Shift key is down. */
316317
#define SDL_KMOD_LCTRL 0x0040u /**< the left Ctrl (Control) key is down. */
317318
#define SDL_KMOD_RCTRL 0x0080u /**< the right Ctrl (Control) key is down. */
318319
#define SDL_KMOD_LALT 0x0100u /**< the left Alt key is down. */

src/events/SDL_keymap.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ SDL_Keymap *SDL_CreateKeymap(void)
5050

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

5656
// If either right or left Shift are set, set both in the output
5757
if (modstate & SDL_KMOD_SHIFT) {

src/test/SDL_test_common.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,9 @@ static void SDLTest_PrintModStateFlag(char *text, size_t maxlen, SDL_Keymod flag
958958
case SDL_KMOD_RSHIFT:
959959
SDL_snprintfcat(text, maxlen, "RSHIFT");
960960
break;
961+
case SDL_KMOD_LEVEL5:
962+
SDL_snprintfcat(text, maxlen, "LEVEL5");
963+
break;
961964
case SDL_KMOD_LCTRL:
962965
SDL_snprintfcat(text, maxlen, "LCTRL");
963966
break;
@@ -999,6 +1002,7 @@ static void SDLTest_PrintModState(char *text, size_t maxlen, SDL_Keymod keymod)
9991002
const SDL_Keymod kmod_flags[] = {
10001003
SDL_KMOD_LSHIFT,
10011004
SDL_KMOD_RSHIFT,
1005+
SDL_KMOD_LEVEL5,
10021006
SDL_KMOD_LCTRL,
10031007
SDL_KMOD_RCTRL,
10041008
SDL_KMOD_LALT,

src/video/wayland/SDL_waylandevents.c

Lines changed: 109 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,14 @@
6868
// Weston uses a ratio of 10 units per scroll tick
6969
#define WAYLAND_WHEEL_AXIS_UNIT 10
7070

71-
// xkbcommon as of 1.4.1 doesn't have a name macro for the mode key
72-
#ifndef XKB_MOD_NAME_MODE
73-
#define XKB_MOD_NAME_MODE "Mod5"
71+
// "Mod5" is typically level 3 shift, which SDL calls SDL_KMOD_MODE (AltGr).
72+
#ifndef XKB_MOD_NAME_MOD5
73+
#define XKB_MOD_NAME_MOD5 "Mod5"
74+
#endif
75+
76+
// "Mod3" is typically level 5 shift, but is often remapped.
77+
#ifndef XKB_MOD_NAME_MOD3
78+
#define XKB_MOD_NAME_MOD3 "Mod3"
7479
#endif
7580

7681
// Keyboard and mouse names to match XWayland
@@ -1267,28 +1272,46 @@ static void Wayland_keymap_iter(struct xkb_keymap *keymap, xkb_keycode_t key, vo
12671272
{
12681273
Wayland_Keymap *sdlKeymap = (Wayland_Keymap *)data;
12691274
const xkb_keysym_t *syms;
1270-
SDL_Scancode scancode;
1271-
1272-
scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, (key - 8));
1275+
const SDL_Scancode scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, (key - 8));
12731276
if (scancode == SDL_SCANCODE_UNKNOWN) {
12741277
return;
12751278
}
12761279

12771280
if (WAYLAND_xkb_state_key_get_syms(sdlKeymap->state, key, &syms) > 0) {
12781281
uint32_t keycode = SDL_KeySymToUcs4(syms[0]);
1282+
bool key_is_unknown = false;
12791283

12801284
if (!keycode) {
1281-
const SDL_Scancode sc = SDL_GetScancodeFromKeySym(syms[0], key);
1285+
switch (syms[0]) {
1286+
// The default SDL scancode table sets this to right alt instead of AltGr/Mode, so handle it separately.
1287+
case XKB_KEY_ISO_Level3_Shift:
1288+
keycode = SDLK_MODE;
1289+
break;
1290+
1291+
/* The default SDL scancode table sets Meta L/R to the GUI keys, and Hyper R to app menu, which is
1292+
* correct as far as physical key placement goes, but these keys are functionally distinct from the
1293+
* default keycodes SDL returns for the scancodes, so they are set to unknown.
1294+
*
1295+
* SDL has no scancode mapping for Hyper L or Level 5 Shift.
1296+
*/
1297+
case XKB_KEY_Meta_L:
1298+
case XKB_KEY_Meta_R:
1299+
case XKB_KEY_Hyper_L:
1300+
case XKB_KEY_Hyper_R:
1301+
case XKB_KEY_ISO_Level5_Shift:
1302+
keycode = SDLK_UNKNOWN;
1303+
key_is_unknown = true;
1304+
break;
12821305

1283-
// Note: The default SDL scancode table sets this to right alt instead of AltGr/Mode, so handle it separately.
1284-
if (syms[0] != XKB_KEY_ISO_Level3_Shift) {
1306+
default:
1307+
{
1308+
const SDL_Scancode sc = SDL_GetScancodeFromKeySym(syms[0], key);
12851309
keycode = SDL_GetKeymapKeycode(NULL, sc, sdlKeymap->modstate);
1286-
} else {
1287-
keycode = SDLK_MODE;
1310+
} break;
12881311
}
12891312
}
12901313

1291-
if (!keycode) {
1314+
if (!keycode && !key_is_unknown) {
12921315
switch (scancode) {
12931316
case SDL_SCANCODE_RETURN:
12941317
keycode = SDLK_RETURN;
@@ -1326,10 +1349,18 @@ static void Wayland_UpdateKeymap(struct SDL_WaylandInput *input)
13261349
{ SDL_KMOD_SHIFT, input->xkb.idx_shift },
13271350
{ SDL_KMOD_CAPS, input->xkb.idx_caps },
13281351
{ SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_shift | input->xkb.idx_caps },
1329-
{ SDL_KMOD_MODE, input->xkb.idx_mode },
1330-
{ SDL_KMOD_MODE | SDL_KMOD_SHIFT, input->xkb.idx_mode | input->xkb.idx_shift },
1331-
{ SDL_KMOD_MODE | SDL_KMOD_CAPS, input->xkb.idx_mode | input->xkb.idx_caps },
1332-
{ SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_mode | input->xkb.idx_shift | input->xkb.idx_caps }
1352+
{ SDL_KMOD_MODE, input->xkb.idx_mod5 },
1353+
{ SDL_KMOD_MODE | SDL_KMOD_SHIFT, input->xkb.idx_mod5 | input->xkb.idx_shift },
1354+
{ SDL_KMOD_MODE | SDL_KMOD_CAPS, input->xkb.idx_mod5 | input->xkb.idx_caps },
1355+
{ SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_mod5 | input->xkb.idx_shift | input->xkb.idx_caps },
1356+
{ SDL_KMOD_LEVEL5, input->xkb.idx_mod3 },
1357+
{ SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT, input->xkb.idx_mod3 | input->xkb.idx_shift },
1358+
{ SDL_KMOD_LEVEL5 | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_caps },
1359+
{ SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_shift | input->xkb.idx_caps },
1360+
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE, input->xkb.idx_mod3 | input->xkb.idx_mod5 },
1361+
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT, input->xkb.idx_mod3 | input->xkb.idx_mod5 | input->xkb.idx_shift },
1362+
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_mod5 | input->xkb.idx_caps },
1363+
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, input->xkb.idx_mod3 | input->xkb.idx_mod5 | input->xkb.idx_shift | input->xkb.idx_caps },
13331364
};
13341365

13351366
if (!input->keyboard_is_virtual) {
@@ -1350,7 +1381,7 @@ static void Wayland_UpdateKeymap(struct SDL_WaylandInput *input)
13501381
for (int i = 0; i < SDL_arraysize(keymod_masks); ++i) {
13511382
keymap.modstate = keymod_masks[i].sdl_mask;
13521383
WAYLAND_xkb_state_update_mask(keymap.state,
1353-
keymod_masks[i].xkb_mask & (input->xkb.idx_shift | input->xkb.idx_mode), 0, keymod_masks[i].xkb_mask & input->xkb.idx_caps,
1384+
keymod_masks[i].xkb_mask & (input->xkb.idx_shift | input->xkb.idx_mod5 | input->xkb.idx_mod3), 0, keymod_masks[i].xkb_mask & input->xkb.idx_caps,
13541385
0, 0, input->xkb.current_group);
13551386
WAYLAND_xkb_keymap_key_for_each(input->xkb.keymap,
13561387
Wayland_keymap_iter,
@@ -1413,7 +1444,8 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
14131444
input->xkb.idx_ctrl = 1 << GET_MOD_INDEX(CTRL);
14141445
input->xkb.idx_alt = 1 << GET_MOD_INDEX(ALT);
14151446
input->xkb.idx_gui = 1 << GET_MOD_INDEX(LOGO);
1416-
input->xkb.idx_mode = 1 << GET_MOD_INDEX(MODE);
1447+
input->xkb.idx_mod3 = 1 << GET_MOD_INDEX(MOD3);
1448+
input->xkb.idx_mod5 = 1 << GET_MOD_INDEX(MOD5);
14171449
input->xkb.idx_num = 1 << GET_MOD_INDEX(NUM);
14181450
input->xkb.idx_caps = 1 << GET_MOD_INDEX(CAPS);
14191451
#undef GET_MOD_INDEX
@@ -1488,59 +1520,77 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
14881520
* Virtual keyboards can have arbitrary layouts, arbitrary scancodes/keycodes, etc...
14891521
* Key presses from these devices must be looked up by their keysym value.
14901522
*/
1491-
static SDL_Scancode Wayland_get_scancode_from_key(struct SDL_WaylandInput *input, uint32_t key)
1523+
static void Wayland_get_scancode_from_key(struct SDL_WaylandInput *input, uint32_t keycode, SDL_Scancode *scancode)
14921524
{
1493-
SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN;
1525+
const xkb_keysym_t *syms;
14941526

14951527
if (!input->keyboard_is_virtual) {
1496-
scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, key - 8);
1528+
*scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_XFREE86_2, keycode);
14971529
} else {
1498-
const xkb_keysym_t *syms;
1499-
if (WAYLAND_xkb_keymap_key_get_syms_by_level(input->xkb.keymap, key, input->xkb.current_group, 0, &syms) > 0) {
1500-
scancode = SDL_GetScancodeFromKeySym(syms[0], key);
1530+
if (WAYLAND_xkb_keymap_key_get_syms_by_level(input->xkb.keymap, keycode + 8, input->xkb.current_group, 0, &syms) > 0) {
1531+
*scancode = SDL_GetScancodeFromKeySym(syms[0], keycode + 8);
15011532
}
15021533
}
1503-
1504-
return scancode;
15051534
}
15061535

1507-
static void Wayland_ReconcileModifiers(struct SDL_WaylandInput *input)
1536+
static void Wayland_ReconcileModifiers(struct SDL_WaylandInput *input, bool key_pressed)
15081537
{
1509-
// Handle pressed modifiers for virtual keyboards that may not send keystrokes.
1510-
if (input->keyboard_is_virtual) {
1538+
/* Handle explicit pressed modifier state. This will correct the modifier state
1539+
* if common modifier keys were remapped and the modifiers presumed to be set
1540+
* during a key press event were incorrect, or if the modifier was set to the
1541+
* pressed state via means other than pressing the physical key.
1542+
*/
1543+
if (!key_pressed) {
15111544
if (input->xkb.wl_pressed_modifiers & input->xkb.idx_shift) {
1512-
input->pressed_modifiers |= SDL_KMOD_SHIFT;
1545+
if (!(input->pressed_modifiers & SDL_KMOD_SHIFT)) {
1546+
input->pressed_modifiers |= SDL_KMOD_SHIFT;
1547+
}
15131548
} else {
15141549
input->pressed_modifiers &= ~SDL_KMOD_SHIFT;
15151550
}
15161551

15171552
if (input->xkb.wl_pressed_modifiers & input->xkb.idx_ctrl) {
1518-
input->pressed_modifiers |= SDL_KMOD_CTRL;
1553+
if (!(input->pressed_modifiers & SDL_KMOD_CTRL)) {
1554+
input->pressed_modifiers |= SDL_KMOD_CTRL;
1555+
}
15191556
} else {
15201557
input->pressed_modifiers &= ~SDL_KMOD_CTRL;
15211558
}
15221559

15231560
if (input->xkb.wl_pressed_modifiers & input->xkb.idx_alt) {
1524-
input->pressed_modifiers |= SDL_KMOD_ALT;
1561+
if (!(input->pressed_modifiers & SDL_KMOD_ALT)) {
1562+
input->pressed_modifiers |= SDL_KMOD_ALT;
1563+
}
15251564
} else {
15261565
input->pressed_modifiers &= ~SDL_KMOD_ALT;
15271566
}
15281567

15291568
if (input->xkb.wl_pressed_modifiers & input->xkb.idx_gui) {
1530-
input->pressed_modifiers |= SDL_KMOD_GUI;
1569+
if (!(input->pressed_modifiers & SDL_KMOD_GUI)) {
1570+
input->pressed_modifiers |= SDL_KMOD_GUI;
1571+
}
15311572
} else {
15321573
input->pressed_modifiers &= ~SDL_KMOD_GUI;
15331574
}
15341575

1535-
if (input->xkb.wl_pressed_modifiers & input->xkb.idx_mode) {
1536-
input->pressed_modifiers |= SDL_KMOD_MODE;
1576+
if (input->xkb.wl_pressed_modifiers & input->xkb.idx_mod3) {
1577+
if (!(input->pressed_modifiers & SDL_KMOD_LEVEL5)) {
1578+
input->pressed_modifiers |= SDL_KMOD_LEVEL5;
1579+
}
1580+
} else {
1581+
input->pressed_modifiers &= ~SDL_KMOD_LEVEL5;
1582+
}
1583+
1584+
if (input->xkb.wl_pressed_modifiers & input->xkb.idx_mod5) {
1585+
if (!(input->pressed_modifiers & SDL_KMOD_MODE)) {
1586+
input->pressed_modifiers |= SDL_KMOD_MODE;
1587+
}
15371588
} else {
15381589
input->pressed_modifiers &= ~SDL_KMOD_MODE;
15391590
}
15401591
}
15411592

1542-
/*
1543-
* If a latch or lock was activated by a keypress, the latch/lock will
1593+
/* If a latch or lock was activated by a keypress, the latch/lock will
15441594
* be tied to the specific left/right key that initiated it. Otherwise,
15451595
* the ambiguous left/right combo is used.
15461596
*
@@ -1591,7 +1641,16 @@ static void Wayland_ReconcileModifiers(struct SDL_WaylandInput *input)
15911641
input->locked_modifiers &= ~SDL_KMOD_GUI;
15921642
}
15931643

1594-
if (input->xkb.wl_locked_modifiers & input->xkb.idx_mode) {
1644+
/* The Mod3 modifier corresponds to no particular SDL keycode, so it is
1645+
* only activated by the backend modifier callback.
1646+
*/
1647+
if (input->xkb.wl_locked_modifiers & input->xkb.idx_mod3) {
1648+
input->locked_modifiers |= SDL_KMOD_LEVEL5;
1649+
} else {
1650+
input->locked_modifiers &= ~SDL_KMOD_LEVEL5;
1651+
}
1652+
1653+
if (input->xkb.wl_locked_modifiers & input->xkb.idx_mod5) {
15951654
input->locked_modifiers |= SDL_KMOD_MODE;
15961655
} else {
15971656
input->locked_modifiers &= ~SDL_KMOD_MODE;
@@ -1618,6 +1677,12 @@ static void Wayland_HandleModifierKeys(struct SDL_WaylandInput *input, SDL_Scanc
16181677
const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false);
16191678
SDL_Keymod mod;
16201679

1680+
/* SDL clients expect modifier state to be activated at the same time as the
1681+
* source keypress, so we set pressed modifier state with the usual modifier
1682+
* keys here, as the explicit modifier event won't arrive until after the
1683+
* keypress event. If this is wrong, it will be corrected when the explicit
1684+
* modifier state is sent at a later time.
1685+
*/
16211686
switch (keycode) {
16221687
case SDLK_LSHIFT:
16231688
mod = SDL_KMOD_LSHIFT;
@@ -1656,7 +1721,7 @@ static void Wayland_HandleModifierKeys(struct SDL_WaylandInput *input, SDL_Scanc
16561721
input->pressed_modifiers &= ~mod;
16571722
}
16581723

1659-
Wayland_ReconcileModifiers(input);
1724+
Wayland_ReconcileModifiers(input, true);
16601725
}
16611726

16621727
static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
@@ -1694,7 +1759,9 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
16941759
window->last_focus_event_time_ns = timestamp;
16951760

16961761
wl_array_for_each (key, keys) {
1697-
const SDL_Scancode scancode = Wayland_get_scancode_from_key(input, *key + 8);
1762+
SDL_Scancode scancode;
1763+
1764+
Wayland_get_scancode_from_key(input, *key, &scancode);
16981765
const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false);
16991766

17001767
switch (keycode) {
@@ -1842,7 +1909,7 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
18421909
keyboard_input_get_text(text, input, key, false, &handled_by_ime);
18431910
}
18441911

1845-
scancode = Wayland_get_scancode_from_key(input, key + 8);
1912+
Wayland_get_scancode_from_key(input, key, &scancode);
18461913
Wayland_HandleModifierKeys(input, scancode, state == WL_KEYBOARD_KEY_STATE_PRESSED);
18471914
Uint64 timestamp = Wayland_GetKeyboardTimestamp(input, time);
18481915
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, input->keyboard_id, key, scancode, (state == WL_KEYBOARD_KEY_STATE_PRESSED));
@@ -1878,7 +1945,7 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
18781945
input->xkb.wl_pressed_modifiers = mods_depressed;
18791946
input->xkb.wl_locked_modifiers = mods_latched | mods_locked;
18801947

1881-
Wayland_ReconcileModifiers(input);
1948+
Wayland_ReconcileModifiers(input, false);
18821949

18831950
// If a key is repeating, update the text to apply the modifier.
18841951
if (keyboard_repeat_is_set(&input->keyboard_repeat)) {

src/video/wayland/SDL_waylandevents_c.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ struct SDL_WaylandInput
114114
uint32_t idx_ctrl;
115115
uint32_t idx_alt;
116116
uint32_t idx_gui;
117-
uint32_t idx_mode;
117+
uint32_t idx_mod3;
118+
uint32_t idx_mod5;
118119
uint32_t idx_num;
119120
uint32_t idx_caps;
120121

0 commit comments

Comments
 (0)