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 @@
#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;
......
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