Blob Blame History Raw
From 98065821bbf0178981b50515094f565b703fcaa8 Mon Sep 17 00:00:00 2001
From: Scott Talbert <swt@techie.net>
Date: Tue, 13 Sep 2016 13:24:12 +0200
Subject: [PATCH] Fix wxGetKeyState() on non-X11 wxGTK backends (e.g., Wayland)

wxGetKeyState() does not currently work on non-X11 GTK backends, and in some
cases it has been reported to crash.  It seems that the most likely use case
for wxGetKeyState() is to query the modifier keys, so on non-X11 backends, use
GTK+ calls to retrieve the modifier key state.

Non-modifier keys are not currently implemented, update the documentation to
mention this.

Closes https://github.com/wxWidgets/wxWidgets/pull/322

(this is a combined backport of 1033fb048dec849906f76ece25f154e6a61fde4e,
9f9c09e24a7f9d86ea51997bd4c55c1ddb7c3159 and
a18fe083cc91bee442863c8ab7f97d6549f2b75c from master)
---
 interface/wx/utils.h  |  3 +++
 src/unix/utilsx11.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/interface/wx/utils.h b/interface/wx/utils.h
index 0bac1c0..f127a74 100644
--- a/interface/wx/utils.h
+++ b/interface/wx/utils.h
@@ -372,6 +372,9 @@ wxString wxGetDisplayName();
     Even though there are virtual key codes defined for mouse buttons, they
     cannot be used with this function currently.
 
+    In wxGTK, this function can be only used with modifier keys (@c WXK_ALT, @c
+    WXK_CONTROL and @c WXK_SHIFT) when not using X11 backend currently.
+
     @header{wx/utils.h}
 */
 bool wxGetKeyState(wxKeyCode key);
diff --git a/src/unix/utilsx11.cpp b/src/unix/utilsx11.cpp
index 6b35551..efc0837 100644
--- a/src/unix/utilsx11.cpp
+++ b/src/unix/utilsx11.cpp
@@ -809,7 +809,7 @@ WXKeySym wxCharCodeWXToX(int id)
 // check current state of a key
 // ----------------------------------------------------------------------------
 
-bool wxGetKeyState(wxKeyCode key)
+static bool wxGetKeyStateX11(wxKeyCode key)
 {
     wxASSERT_MSG(key != WXK_LBUTTON && key != WXK_RBUTTON && key !=
         WXK_MBUTTON, wxT("can't use wxGetKeyState() for mouse buttons"));
@@ -851,11 +851,69 @@ bool wxGetKeyState(wxKeyCode key)
     // with the least-significant bit in the byte representing key 8N.
     char key_vector[32];
     XQueryKeymap(pDisplay, key_vector);
-    return key_vector[keyCode >> 3] & (1 << (keyCode & 7));
+    return (key_vector[keyCode >> 3] & (1 << (keyCode & 7))) != 0;
 }
 
 #endif // !defined(__WXGTK__) || defined(GDK_WINDOWING_X11)
 
+// We need to use GDK functions when using wxGTK with a non-X11 backend, the
+// X11 code above can't work in this case.
+#ifdef __WXGTK__
+
+// gdk_keymap_get_modifier_state() is only available since 3.4
+#if GTK_CHECK_VERSION(3,4,0)
+
+#define wxHAS_GETKEYSTATE_GTK
+
+extern GtkWidget *wxGetRootWindow();
+
+static bool wxGetKeyStateGTK(wxKeyCode key)
+{
+    if (gtk_check_version(3,4,0) != NULL)
+        return false;
+
+    GdkDisplay* display = gtk_widget_get_display(wxGetRootWindow());
+    GdkKeymap* keymap = gdk_keymap_get_for_display(display);
+    guint state = gdk_keymap_get_modifier_state(keymap);
+    guint mask = 0;
+    switch (key)
+    {
+        case WXK_ALT:
+            mask = GDK_MOD1_MASK;
+            break;
+
+        case WXK_CONTROL:
+            mask = GDK_CONTROL_MASK;
+            break;
+
+        case WXK_SHIFT:
+            mask = GDK_SHIFT_MASK;
+            break;
+
+        default:
+            wxFAIL_MSG(wxS("Unsupported key, only modifiers can be used"));
+            return false;
+    }
+    return (state & mask) != 0;
+}
+
+#endif // GTK+ 3.4
+#endif // __WXGTK__
+
+bool wxGetKeyState(wxKeyCode key)
+{
+#ifdef wxHAS_GETKEYSTATE_GTK
+    GdkDisplay* display = gtk_widget_get_display(wxGetRootWindow());
+    const char* name = g_type_name(G_TYPE_FROM_INSTANCE(display));
+    if (strcmp(name, "GdkX11Display") != 0)
+    {
+        return wxGetKeyStateGTK(key);
+    }
+#endif // GTK+ 3.4+
+
+    return wxGetKeyStateX11(key);
+}
+
 // ----------------------------------------------------------------------------
 // Launch document with default app
 // ----------------------------------------------------------------------------