Commit 590c344a authored by Daniel P. Berrange's avatar Daniel P. Berrange

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
parent 11e654fd
...@@ -40,6 +40,11 @@ ...@@ -40,6 +40,11 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#ifdef G_OS_WIN32
#include <windows.h>
#include <gdk/gdkwin32.h>
#endif
#define VNC_DISPLAY_GET_PRIVATE(obj) \ #define VNC_DISPLAY_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), VNC_TYPE_DISPLAY, VncDisplayPrivate)) (G_TYPE_INSTANCE_GET_PRIVATE((obj), VNC_TYPE_DISPLAY, VncDisplayPrivate))
...@@ -83,6 +88,10 @@ struct _VncDisplayPrivate ...@@ -83,6 +88,10 @@ struct _VncDisplayPrivate
gboolean vncgrabpending; /* Key sequence detected, waiting for release */ gboolean vncgrabpending; /* Key sequence detected, waiting for release */
VncGrabSequence *vncgrabseq; /* the configured key sequence */ VncGrabSequence *vncgrabseq; /* the configured key sequence */
gboolean *vncactiveseq; /* the currently pressed keys */ gboolean *vncactiveseq; /* the currently pressed keys */
#ifdef WIN32
HHOOK keyboard_hook;
#endif
}; };
G_DEFINE_TYPE(VncDisplay, vnc_display, GTK_TYPE_DRAWING_AREA) G_DEFINE_TYPE(VncDisplay, vnc_display, GTK_TYPE_DRAWING_AREA)
...@@ -285,6 +294,44 @@ static GdkCursor *create_null_cursor(void) ...@@ -285,6 +294,44 @@ static GdkCursor *create_null_cursor(void)
return cursor; 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) 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) ...@@ -513,7 +560,15 @@ static void do_keyboard_grab(VncDisplay *obj, gboolean quiet)
{ {
VncDisplayPrivate *priv = obj->priv; 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))); do_keyboard_grab_all(gtk_widget_get_window(GTK_WIDGET(obj)));
priv->in_keyboard_grab = TRUE; priv->in_keyboard_grab = TRUE;
if (!quiet) if (!quiet)
g_signal_emit(obj, signals[VNC_KEYBOARD_GRAB], 0); g_signal_emit(obj, signals[VNC_KEYBOARD_GRAB], 0);
...@@ -525,6 +580,14 @@ static void do_keyboard_ungrab(VncDisplay *obj, gboolean quiet) ...@@ -525,6 +580,14 @@ static void do_keyboard_ungrab(VncDisplay *obj, gboolean quiet)
VncDisplayPrivate *priv = obj->priv; VncDisplayPrivate *priv = obj->priv;
do_keyboard_ungrab_all(gtk_widget_get_window(GTK_WIDGET(obj))); 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; priv->in_keyboard_grab = FALSE;
if (!quiet) if (!quiet)
g_signal_emit(obj, signals[VNC_KEYBOARD_UNGRAB], 0); g_signal_emit(obj, signals[VNC_KEYBOARD_UNGRAB], 0);
...@@ -884,6 +947,19 @@ static gboolean key_event(GtkWidget *widget, GdkEventKey *key) ...@@ -884,6 +947,19 @@ static gboolean key_event(GtkWidget *widget, GdkEventKey *key)
key->type == GDK_KEY_PRESS ? "press" : "release", key->type == GDK_KEY_PRESS ? "press" : "release",
key->hardware_keycode, key->state, key->group, keyval); 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); keyval = vnc_display_keyval_from_keycode(key->hardware_keycode, keyval);
/* /*
...@@ -972,6 +1048,10 @@ static gboolean enter_event(GtkWidget *widget, GdkEventCrossing *crossing G_GNUC ...@@ -972,6 +1048,10 @@ static gboolean enter_event(GtkWidget *widget, GdkEventCrossing *crossing G_GNUC
if (priv->local_pointer) if (priv->local_pointer)
do_pointer_show(VNC_DISPLAY(widget)); do_pointer_show(VNC_DISPLAY(widget));
#ifdef G_OS_WIN32
win32_window = GDK_WINDOW_HWND(gtk_widget_get_window(widget));
#endif
return TRUE; return TRUE;
} }
...@@ -1014,7 +1094,22 @@ static void release_keys(VncDisplay *display) ...@@ -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; VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
...@@ -1022,6 +1117,9 @@ static gboolean focus_event(GtkWidget *widget, GdkEventFocus *focus G_GNUC_UNUSE ...@@ -1022,6 +1117,9 @@ static gboolean focus_event(GtkWidget *widget, GdkEventFocus *focus G_GNUC_UNUSE
return FALSE; return FALSE;
release_keys(VNC_DISPLAY(widget)); release_keys(VNC_DISPLAY(widget));
#ifdef G_OS_WIN32
win32_window = NULL;
#endif
return TRUE; return TRUE;
} }
...@@ -1852,7 +1950,8 @@ static void vnc_display_class_init(VncDisplayClass *klass) ...@@ -1852,7 +1950,8 @@ static void vnc_display_class_init(VncDisplayClass *klass)
gtkwidget_class->key_release_event = key_event; gtkwidget_class->key_release_event = key_event;
gtkwidget_class->enter_notify_event = enter_event; gtkwidget_class->enter_notify_event = enter_event;
gtkwidget_class->leave_notify_event = leave_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->grab_notify = grab_notify;
gtkwidget_class->realize = realize_event; gtkwidget_class->realize = realize_event;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment