From 96a5cd0779cb5a62966ea7bf282d51d7c1556827 Mon Sep 17 00:00:00 2001 From: David Zhao Akeley Date: Sun, 17 Jul 2022 12:55:28 -0700 Subject: [PATCH] X11: Fix inconsistent modifier state Use XkbGetState to poll modifier key state mask for up-to-date modifier state after modifier KeyPress/KeyRelease events. Fixes https://github.com/glfw/glfw/issues/1630 --- README.md | 1 + src/x11_window.c | 48 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 452577de..6e569348 100644 --- a/README.md +++ b/README.md @@ -387,6 +387,7 @@ information on what to include when reporting a bug. (#1380) - [EGL] Bugfix: The `GLFW_DOUBLEBUFFER` context attribute was ignored (#1843) - [GLX] Bugfix: Context creation failed if GLX 1.4 was not exported by GLX library + - [X11] Bugfix: Inconsistency for Key Event Modifiers between Windows and Linux/X11 ## Contact diff --git a/src/x11_window.c b/src/x11_window.c index 98f990b2..4d09d874 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -31,6 +31,9 @@ #include #include +#include +#include +#include #include @@ -210,26 +213,43 @@ static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer po event->xproperty.atom == notification->xselection.property; } -// Translates an X event modifier state mask +// Polls the X11 display for up-to-date modifier key state and +// translate to GLFW modifier state mask. // -static int translateState(int state) +// NB: we use this instead of the X event modifier state mask due to a +// platform quirk: modifier key press/release events don't reflect their +// own state change (e.g. pressing left control generates a KeyPress event +// WITHOUT the ctrl state bit set). https://github.com/glfw/glfw/issues/1630 +// +// In the event that XKB is not available, we just translate the +// passed state argument instead. +static int getMods(int state_fallback) { - int mods = 0; + int glfw_mods = 0, state; + + if (_glfw.x11.xkb.available) { + XkbStateRec record; + XkbGetState(_glfw.x11.display, XkbUseCoreKbd, &record); + state = record.mods; + } + else { + state = state_fallback; + } if (state & ShiftMask) - mods |= GLFW_MOD_SHIFT; + glfw_mods |= GLFW_MOD_SHIFT; if (state & ControlMask) - mods |= GLFW_MOD_CONTROL; + glfw_mods |= GLFW_MOD_CONTROL; if (state & Mod1Mask) - mods |= GLFW_MOD_ALT; + glfw_mods |= GLFW_MOD_ALT; if (state & Mod4Mask) - mods |= GLFW_MOD_SUPER; + glfw_mods |= GLFW_MOD_SUPER; if (state & LockMask) - mods |= GLFW_MOD_CAPS_LOCK; + glfw_mods |= GLFW_MOD_CAPS_LOCK; if (state & Mod2Mask) - mods |= GLFW_MOD_NUM_LOCK; + glfw_mods |= GLFW_MOD_NUM_LOCK; - return mods; + return glfw_mods; } // Translates an X11 key code to a GLFW key token @@ -1187,7 +1207,7 @@ static void processEvent(XEvent *event) case KeyPress: { const int key = translateKey(keycode); - const int mods = translateState(event->xkey.state); + const int mods = getMods(event->xkey.state); const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); if (window->x11.ic) @@ -1259,7 +1279,7 @@ static void processEvent(XEvent *event) case KeyRelease: { const int key = translateKey(keycode); - const int mods = translateState(event->xkey.state); + const int mods = getMods(event->xkey.state); if (!_glfw.x11.xkb.detectable) { @@ -1299,7 +1319,7 @@ static void processEvent(XEvent *event) case ButtonPress: { - const int mods = translateState(event->xbutton.state); + const int mods = getMods(event->xbutton.state); if (event->xbutton.button == Button1) _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods); @@ -1333,7 +1353,7 @@ static void processEvent(XEvent *event) case ButtonRelease: { - const int mods = translateState(event->xbutton.state); + const int mods = getMods(event->xbutton.state); if (event->xbutton.button == Button1) {