[gtk-vnc] Install hook for Windows key code handling



commit 590c344ad3340dfabb9985c98f418ce2fcae7b44
Author: Daniel P. Berrange <berrange redhat com>
Date:   Fri Feb 6 13:39:48 2015 +0000

    Install hook for Windows key code handling
    
    To fix handling of AltGr keys on Windows displays, install a
    hook for Windows keycode handling. This is copied from code
    in SPICE-GTK
    
    https://bugzilla.gnome.org/show_bug.cgi?id=731825

 src/vncdisplay.c |  103 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 101 insertions(+), 2 deletions(-)
---
diff --git a/src/vncdisplay.c b/src/vncdisplay.c
index b990f97..5ac9492 100644
--- a/src/vncdisplay.c
+++ b/src/vncdisplay.c
@@ -40,6 +40,11 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#ifdef G_OS_WIN32
+#include <windows.h>
+#include <gdk/gdkwin32.h>
+#endif
+
 #define VNC_DISPLAY_GET_PRIVATE(obj)                                    \
     (G_TYPE_INSTANCE_GET_PRIVATE((obj), VNC_TYPE_DISPLAY, VncDisplayPrivate))
 
@@ -83,6 +88,10 @@ struct _VncDisplayPrivate
     gboolean vncgrabpending; /* Key sequence detected, waiting for release */
     VncGrabSequence *vncgrabseq; /* the configured key sequence */
     gboolean *vncactiveseq; /* the currently pressed keys */
+
+#ifdef WIN32
+    HHOOK keyboard_hook;
+#endif
 };
 
 G_DEFINE_TYPE(VncDisplay, vnc_display, GTK_TYPE_DRAWING_AREA)
@@ -285,6 +294,44 @@ static GdkCursor *create_null_cursor(void)
     return cursor;
 }
 
+#ifdef G_OS_WIN32
+static HWND win32_window = NULL;
+
+static LRESULT CALLBACK keyboard_hook_cb(int code, WPARAM wparam, LPARAM lparam)
+{
+    if  (win32_window && code == HC_ACTION && wparam != WM_KEYUP) {
+        KBDLLHOOKSTRUCT *hooked = (KBDLLHOOKSTRUCT*)lparam;
+        DWORD dwmsg = (hooked->flags << 24) | (hooked->scanCode << 16) | 1;
+
+        if (hooked->vkCode == VK_NUMLOCK || hooked->vkCode == VK_RSHIFT) {
+            dwmsg &= ~(1 << 24);
+            SendMessage(win32_window, wparam, hooked->vkCode, dwmsg);
+        }
+        switch (hooked->vkCode) {
+        case VK_CAPITAL:
+        case VK_SCROLL:
+        case VK_NUMLOCK:
+        case VK_LSHIFT:
+        case VK_RSHIFT:
+        case VK_RCONTROL:
+        case VK_LMENU:
+        case VK_RMENU:
+            break;
+        case VK_LCONTROL:
+            /* When pressing AltGr, an extra VK_LCONTROL with a special
+             * scancode with bit 9 set is sent. Let's ignore the extra
+             * VK_LCONTROL, as that will make AltGr misbehave. */
+            if (hooked->scanCode & 0x200)
+                return 1;
+            break;
+        default:
+            SendMessage(win32_window, wparam, hooked->vkCode, dwmsg);
+            return 1;
+        }
+    }
+    return CallNextHookEx(NULL, code, wparam, lparam);
+}
+#endif
 
 static void setup_surface_cache(VncDisplay *dpy, cairo_t *crWin, int w, int h)
 {
@@ -513,7 +560,15 @@ static void do_keyboard_grab(VncDisplay *obj, gboolean quiet)
 {
     VncDisplayPrivate *priv = obj->priv;
 
+#ifdef G_OS_WIN32
+    if (priv->keyboard_hook == NULL)
+        priv->keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_hook_cb,
+                                               GetModuleHandle(NULL), 0);
+    g_warn_if_fail(priv->keyboard_hook != NULL);
+#endif
+
     do_keyboard_grab_all(gtk_widget_get_window(GTK_WIDGET(obj)));
+
     priv->in_keyboard_grab = TRUE;
     if (!quiet)
         g_signal_emit(obj, signals[VNC_KEYBOARD_GRAB], 0);
@@ -525,6 +580,14 @@ static void do_keyboard_ungrab(VncDisplay *obj, gboolean quiet)
     VncDisplayPrivate *priv = obj->priv;
 
     do_keyboard_ungrab_all(gtk_widget_get_window(GTK_WIDGET(obj)));
+
+#ifdef G_OS_WIN32
+    if (priv->keyboard_hook != NULL) {
+        UnhookWindowsHookEx(priv->keyboard_hook);
+        priv->keyboard_hook = NULL;
+    }
+#endif
+
     priv->in_keyboard_grab = FALSE;
     if (!quiet)
         g_signal_emit(obj, signals[VNC_KEYBOARD_UNGRAB], 0);
@@ -884,6 +947,19 @@ static gboolean key_event(GtkWidget *widget, GdkEventKey *key)
               key->type == GDK_KEY_PRESS ? "press" : "release",
               key->hardware_keycode, key->state, key->group, keyval);
 
+#ifdef G_OS_WIN32
+    /* on windows, we ought to ignore the reserved key event? */
+    if (key->hardware_keycode == 0xff)
+        return FALSE;
+
+    if (!priv->in_keyboard_grab) {
+        if (key->hardware_keycode == VK_LWIN ||
+            key->hardware_keycode == VK_RWIN ||
+            key->hardware_keycode == VK_APPS)
+            return FALSE;
+    }
+#endif
+
     keyval = vnc_display_keyval_from_keycode(key->hardware_keycode, keyval);
 
     /*
@@ -972,6 +1048,10 @@ static gboolean enter_event(GtkWidget *widget, GdkEventCrossing *crossing G_GNUC
     if (priv->local_pointer)
         do_pointer_show(VNC_DISPLAY(widget));
 
+#ifdef G_OS_WIN32
+    win32_window = GDK_WINDOW_HWND(gtk_widget_get_window(widget));
+#endif
+
     return TRUE;
 }
 
@@ -1014,7 +1094,22 @@ static void release_keys(VncDisplay *display)
     }
 }
 
-static gboolean focus_event(GtkWidget *widget, GdkEventFocus *focus G_GNUC_UNUSED)
+static gboolean focus_in_event(GtkWidget *widget, GdkEventFocus *focus G_GNUC_UNUSED)
+{
+    VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
+
+    if (priv->conn == NULL || !vnc_connection_is_initialized(priv->conn))
+        return FALSE;
+
+#ifdef G_OS_WIN32
+    win32_window = GDK_WINDOW_HWND(gtk_widget_get_window(widget));
+#endif
+
+    return TRUE;
+}
+
+
+static gboolean focus_out_event(GtkWidget *widget, GdkEventFocus *focus G_GNUC_UNUSED)
 {
     VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
 
@@ -1022,6 +1117,9 @@ static gboolean focus_event(GtkWidget *widget, GdkEventFocus *focus G_GNUC_UNUSE
         return FALSE;
 
     release_keys(VNC_DISPLAY(widget));
+#ifdef G_OS_WIN32
+    win32_window = NULL;
+#endif
 
     return TRUE;
 }
@@ -1852,7 +1950,8 @@ static void vnc_display_class_init(VncDisplayClass *klass)
     gtkwidget_class->key_release_event = key_event;
     gtkwidget_class->enter_notify_event = enter_event;
     gtkwidget_class->leave_notify_event = leave_event;
-    gtkwidget_class->focus_out_event = focus_event;
+    gtkwidget_class->focus_in_event = focus_in_event;
+    gtkwidget_class->focus_out_event = focus_out_event;
     gtkwidget_class->grab_notify = grab_notify;
     gtkwidget_class->realize = realize_event;
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]