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
This commit is contained in:
David Zhao Akeley 2022-07-17 12:55:28 -07:00
parent c50d53160f
commit 96a5cd0779
2 changed files with 35 additions and 14 deletions

View File

@ -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

View File

@ -31,6 +31,9 @@
#include <X11/cursorfont.h>
#include <X11/Xmd.h>
#include <X11/extensions/XKB.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XKBgeom.h>
#include <poll.h>
@ -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)
{