Skip to content

Commit 6b01cdd

Browse files
committed
x11: Add support for Mod3 and more esoteric Xkb configurations
Adds support for Mod3, which is usually Level 5 shift, as well as not altering the functionality of the more esoteric modifier keys, such as meta and hyper. Also use the system modifier state instead of setting them based on key presses, which may be incorrect due to remapping, or toggled in some other manner.
1 parent eb37869 commit 6b01cdd

File tree

3 files changed

+245
-79
lines changed

3 files changed

+245
-79
lines changed

src/video/x11/SDL_x11events.c

Lines changed: 133 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -246,54 +246,133 @@ static void X11_HandleGenericEvent(SDL_VideoDevice *_this, XEvent *xev)
246246
}
247247
#endif // SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
248248

249-
static unsigned X11_GetNumLockModifierMask(SDL_VideoDevice *_this)
249+
static void X11_ReconcileModifiers(SDL_VideoData *viddata)
250250
{
251-
SDL_VideoData *videodata = _this->internal;
252-
Display *display = videodata->display;
253-
unsigned num_mask = 0;
254-
int i, j;
255-
XModifierKeymap *xmods;
256-
unsigned n;
257-
258-
xmods = X11_XGetModifierMapping(display);
259-
n = xmods->max_keypermod;
260-
for (i = 3; i < 8; i++) {
261-
for (j = 0; j < n; j++) {
262-
KeyCode kc = xmods->modifiermap[i * n + j];
263-
if (videodata->key_layout[kc] == SDL_SCANCODE_NUMLOCKCLEAR) {
264-
num_mask = 1 << i;
265-
break;
266-
}
251+
Window junk_window;
252+
int x, y;
253+
Uint32 xk_modifiers = 0;
254+
255+
X11_XQueryPointer(viddata->display, DefaultRootWindow(viddata->display), &junk_window, &junk_window, &x, &y, &x, &y, &xk_modifiers);
256+
257+
/* If a modifier was activated by a keypress, it will be tied to the
258+
* specific left/right key that initiated it. Otherwise, the ambiguous
259+
* left/right combo is used.
260+
*/
261+
if (xk_modifiers & ShiftMask) {
262+
if (!(viddata->xkb.active_modifiers & SDL_KMOD_SHIFT)) {
263+
viddata->xkb.active_modifiers |= SDL_KMOD_SHIFT;
264+
}
265+
} else {
266+
viddata->xkb.active_modifiers &= ~SDL_KMOD_SHIFT;
267+
}
268+
269+
if (xk_modifiers & ControlMask) {
270+
if (!(viddata->xkb.active_modifiers & SDL_KMOD_CTRL)) {
271+
viddata->xkb.active_modifiers |= SDL_KMOD_CTRL;
272+
}
273+
} else {
274+
viddata->xkb.active_modifiers &= ~SDL_KMOD_CTRL;
275+
}
276+
277+
// Mod1 is used for the Alt keys
278+
if (xk_modifiers & Mod1Mask) {
279+
if (!(viddata->xkb.active_modifiers & SDL_KMOD_ALT)) {
280+
viddata->xkb.active_modifiers |= SDL_KMOD_ALT;
281+
}
282+
} else {
283+
viddata->xkb.active_modifiers &= ~SDL_KMOD_ALT;
284+
}
285+
286+
// Mod4 is used for the Super (aka GUI/Logo) keys.
287+
if (xk_modifiers & Mod4Mask) {
288+
if (!(viddata->xkb.active_modifiers & SDL_KMOD_GUI)) {
289+
viddata->xkb.active_modifiers |= SDL_KMOD_GUI;
267290
}
291+
} else {
292+
viddata->xkb.active_modifiers &= ~SDL_KMOD_GUI;
293+
}
294+
295+
// Mod3 is typically Level 5 shift.
296+
if (xk_modifiers & Mod3Mask) {
297+
viddata->xkb.active_modifiers |= SDL_KMOD_LEVEL5;
298+
} else {
299+
viddata->xkb.active_modifiers &= ~SDL_KMOD_LEVEL5;
300+
}
301+
302+
// Mod5 is typically Level 3 shift (aka AltGr).
303+
if (xk_modifiers & Mod5Mask) {
304+
viddata->xkb.active_modifiers |= SDL_KMOD_MODE;
305+
} else {
306+
viddata->xkb.active_modifiers &= ~SDL_KMOD_MODE;
307+
}
308+
309+
if (xk_modifiers & viddata->xkb.numlock_mask) {
310+
viddata->xkb.active_modifiers |= SDL_KMOD_NUM;
311+
} else {
312+
viddata->xkb.active_modifiers &= ~SDL_KMOD_NUM;
313+
}
314+
315+
if (xk_modifiers & viddata->xkb.scrolllock_mask) {
316+
viddata->xkb.active_modifiers |= SDL_KMOD_SCROLL;
317+
} else {
318+
viddata->xkb.active_modifiers &= ~SDL_KMOD_SCROLL;
268319
}
269-
X11_XFreeModifiermap(xmods);
270320

271-
return num_mask;
321+
SDL_SetModState(viddata->xkb.active_modifiers);
272322
}
273323

274-
static unsigned X11_GetScrollLockModifierMask(SDL_VideoDevice *_this)
324+
static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed, bool reconcile)
275325
{
276-
SDL_VideoData *videodata = _this->internal;
277-
Display *display = videodata->display;
278-
unsigned num_mask = 0;
279-
int i, j;
280-
XModifierKeymap *xmods;
281-
unsigned n;
282-
283-
xmods = X11_XGetModifierMapping(display);
284-
n = xmods->max_keypermod;
285-
for (i = 3; i < 8; i++) {
286-
for (j = 0; j < n; j++) {
287-
KeyCode kc = xmods->modifiermap[i * n + j];
288-
if (videodata->key_layout[kc] == SDL_SCANCODE_SCROLLLOCK) {
289-
num_mask = 1 << i;
290-
break;
291-
}
292-
}
326+
const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false);
327+
SDL_Keymod mod = SDL_KMOD_NONE;
328+
329+
/* SDL clients expect modifier state to be activated at the same time as the
330+
* source keypress, so we set pressed modifier state with the usual modifier
331+
* keys here, as the explicit modifier event won't arrive until after the
332+
* keypress event. If this is wrong, it will be corrected when the explicit
333+
* modifier state is checked.
334+
*/
335+
switch (keycode) {
336+
case SDLK_LSHIFT:
337+
mod = SDL_KMOD_LSHIFT;
338+
break;
339+
case SDLK_RSHIFT:
340+
mod = SDL_KMOD_RSHIFT;
341+
break;
342+
case SDLK_LCTRL:
343+
mod = SDL_KMOD_LCTRL;
344+
break;
345+
case SDLK_RCTRL:
346+
mod = SDL_KMOD_RCTRL;
347+
break;
348+
case SDLK_LALT:
349+
mod = SDL_KMOD_LALT;
350+
break;
351+
case SDLK_RALT:
352+
mod = SDL_KMOD_RALT;
353+
break;
354+
case SDLK_LGUI:
355+
mod = SDL_KMOD_LGUI;
356+
break;
357+
case SDLK_RGUI:
358+
mod = SDL_KMOD_RGUI;
359+
break;
360+
case SDLK_MODE:
361+
mod = SDL_KMOD_MODE;
362+
break;
363+
default:
364+
return;
293365
}
294-
X11_XFreeModifiermap(xmods);
295366

296-
return num_mask;
367+
if (pressed) {
368+
viddata->xkb.active_modifiers |= mod;
369+
} else {
370+
viddata->xkb.active_modifiers &= ~mod;
371+
}
372+
373+
if (reconcile) {
374+
X11_ReconcileModifiers(viddata);
375+
}
297376
}
298377

299378
void X11_ReconcileKeyboardState(SDL_VideoDevice *_this)
@@ -302,20 +381,10 @@ void X11_ReconcileKeyboardState(SDL_VideoDevice *_this)
302381
Display *display = videodata->display;
303382
char keys[32];
304383
int keycode;
305-
Window junk_window;
306-
int x, y;
307-
unsigned int mask;
308384
const bool *keyboardState;
309385

310386
X11_XQueryKeymap(display, keys);
311387

312-
// Sync up the keyboard modifier state
313-
if (X11_XQueryPointer(display, DefaultRootWindow(display), &junk_window, &junk_window, &x, &y, &x, &y, &mask)) {
314-
SDL_ToggleModState(SDL_KMOD_CAPS, (mask & LockMask) ? true : false);
315-
SDL_ToggleModState(SDL_KMOD_NUM, (mask & X11_GetNumLockModifierMask(_this)) ? true : false);
316-
SDL_ToggleModState(SDL_KMOD_SCROLL, (mask & X11_GetScrollLockModifierMask(_this)) ? true : false);
317-
}
318-
319388
keyboardState = SDL_GetKeyboardState(0);
320389
for (keycode = 0; keycode < SDL_arraysize(videodata->key_layout); ++keycode) {
321390
SDL_Scancode scancode = videodata->key_layout[keycode];
@@ -334,15 +403,19 @@ void X11_ReconcileKeyboardState(SDL_VideoDevice *_this)
334403
case SDLK_LGUI:
335404
case SDLK_RGUI:
336405
case SDLK_MODE:
337-
SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, true);
406+
X11_HandleModifierKeys(videodata, scancode, true, false);
407+
SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, true);
338408
break;
339409
default:
340410
break;
341411
}
342412
} else if (!x11KeyPressed && sdlKeyPressed) {
343-
SDL_SendKeyboardKey(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, false);
413+
X11_HandleModifierKeys(videodata, scancode, false, false);
414+
SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, false);
344415
}
345416
}
417+
418+
X11_ReconcileModifiers(videodata);
346419
}
347420

348421
static void X11_DispatchFocusIn(SDL_VideoDevice *_this, SDL_WindowData *data)
@@ -853,8 +926,10 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_
853926
videodata->filter_time = xevent->xkey.time;
854927

855928
if (orig_event_type == KeyPress) {
856-
SDL_SendKeyboardKey(timestamp, keyboardID, orig_keycode, scancode, true);
929+
X11_HandleModifierKeys(videodata, scancode, true, true);
930+
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, orig_keycode, scancode, true);
857931
} else {
932+
X11_HandleModifierKeys(videodata, scancode, false, true);
858933
SDL_SendKeyboardKey(timestamp, keyboardID, orig_keycode, scancode, false);
859934
}
860935
#endif
@@ -881,7 +956,8 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_
881956
if (xevent->type == KeyPress) {
882957
// Don't send the key if it looks like a duplicate of a filtered key sent by an IME
883958
if (xevent->xkey.keycode != videodata->filter_code || xevent->xkey.time != videodata->filter_time) {
884-
SDL_SendKeyboardKey(timestamp, keyboardID, keycode, videodata->key_layout[keycode], true);
959+
X11_HandleModifierKeys(videodata, videodata->key_layout[keycode], true, true);
960+
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, videodata->key_layout[keycode], true);
885961
}
886962
if (*text) {
887963
text[text_length] = '\0';
@@ -892,7 +968,8 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_
892968
// We're about to get a repeated key down, ignore the key up
893969
return;
894970
}
895-
SDL_SendKeyboardKey(timestamp, keyboardID, keycode, videodata->key_layout[keycode], false);
971+
X11_HandleModifierKeys(videodata, videodata->key_layout[keycode], false, true);
972+
SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, videodata->key_layout[keycode], false);
896973
}
897974
}
898975

@@ -1101,10 +1178,10 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
11011178
#endif
11021179
if (SDL_GetKeyboardFocus() != NULL) {
11031180
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
1104-
if (videodata->xkb) {
1181+
if (videodata->xkb.desc_ptr) {
11051182
XkbStateRec state;
11061183
if (X11_XkbGetState(videodata->display, XkbUseCoreKbd, &state) == Success) {
1107-
if (state.group != videodata->xkb_group) {
1184+
if (state.group != videodata->xkb.current_group) {
11081185
// Only rebuild the keymap if the layout has changed.
11091186
X11_UpdateKeymap(_this, true);
11101187
}

0 commit comments

Comments
 (0)