From 78f816d74e114dd493c4524b9aa91b4f803e131b Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Tue, 11 Feb 2025 11:46:34 -0500 Subject: [PATCH] x11: Apply the modifier state from key events Use the modifier state supplied with key events to track the system modifier state instead of relying on the state returned by XQueryPointer(), which can be racy when used with automated text entry. --- src/video/x11/SDL_x11events.c | 48 ++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 7bca58b373..5f264a3483 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -310,6 +310,12 @@ static void X11_ReconcileModifiers(SDL_VideoData *viddata) viddata->xkb.sdl_modifiers &= ~SDL_KMOD_MODE; } + if (xk_modifiers & LockMask) { + viddata->xkb.sdl_modifiers |= SDL_KMOD_CAPS; + } else { + viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CAPS; + } + if (xk_modifiers & viddata->xkb.numlock_mask) { viddata->xkb.sdl_modifiers |= SDL_KMOD_NUM; } else { @@ -325,10 +331,11 @@ static void X11_ReconcileModifiers(SDL_VideoData *viddata) SDL_SetModState(viddata->xkb.sdl_modifiers); } -static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed, bool reconcile) +static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed, bool allow_reconciliation) { const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false); SDL_Keymod mod = SDL_KMOD_NONE; + bool reconcile = false; /* SDL clients expect modifier state to be activated at the same time as the * source keypress, so we set pressed modifier state with the usual modifier @@ -367,7 +374,36 @@ static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode case SDLK_LEVEL5_SHIFT: mod = SDL_KMOD_LEVEL5; break; + case SDLK_CAPSLOCK: + case SDLK_NUMLOCKCLEAR: + case SDLK_SCROLLLOCK: + { + /* For locking modifier keys, query the lock state directly, or we may have to wait until the next + * key press event to know if a lock was actually activated from the key event. + */ + unsigned int cur_mask = viddata->xkb.xkb_modifiers; + X11_UpdateSystemKeyModifiers(viddata); + + if (viddata->xkb.xkb_modifiers & LockMask) { + cur_mask |= LockMask; + } else { + cur_mask &= ~LockMask; + } + if (viddata->xkb.xkb_modifiers & viddata->xkb.numlock_mask) { + cur_mask |= viddata->xkb.numlock_mask; + } else { + cur_mask &= ~viddata->xkb.numlock_mask; + } + if (viddata->xkb.xkb_modifiers & viddata->xkb.scrolllock_mask) { + cur_mask |= viddata->xkb.scrolllock_mask; + } else { + cur_mask &= ~viddata->xkb.scrolllock_mask; + } + + viddata->xkb.xkb_modifiers = cur_mask; + } SDL_FALLTHROUGH; default: + reconcile = true; break; } @@ -377,8 +413,12 @@ static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode viddata->xkb.sdl_modifiers &= ~mod; } - if (reconcile) { - X11_ReconcileModifiers(viddata); + if (allow_reconciliation) { + if (reconcile) { + X11_ReconcileModifiers(viddata); + } else { + SDL_SetModState(viddata->xkb.sdl_modifiers); + } } } @@ -906,7 +946,7 @@ void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_ #endif // DEBUG SCANCODES text[0] = '\0'; - X11_UpdateSystemKeyModifiers(videodata); + videodata->xkb.xkb_modifiers = xevent->xkey.state; if (SDL_TextInputActive(windowdata->window)) { // filter events catches XIM events and sends them to the correct handler