diff options
-rwxr-xr-x | configure | 2 | ||||
-rw-r--r-- | include/ui/win32-kbd-hook.h | 14 | ||||
-rw-r--r-- | stubs/Makefile.objs | 1 | ||||
-rw-r--r-- | stubs/win32-kbd-hook.c | 18 | ||||
-rw-r--r-- | ui/Makefile.objs | 3 | ||||
-rw-r--r-- | ui/gtk.c | 194 | ||||
-rw-r--r-- | ui/sdl2-input.c | 3 | ||||
-rw-r--r-- | ui/sdl2.c | 33 | ||||
-rw-r--r-- | ui/trace-events | 3 | ||||
-rw-r--r-- | ui/win32-kbd-hook.c | 102 |
10 files changed, 264 insertions, 109 deletions
@@ -2897,7 +2897,7 @@ fi if test "$gtk" != "no"; then gtkpackage="gtk+-3.0" gtkx11package="gtk+-x11-3.0" - gtkversion="3.14.0" + gtkversion="3.22.0" if $pkg_config --exists "$gtkpackage >= $gtkversion"; then gtk_cflags=$($pkg_config --cflags $gtkpackage) gtk_libs=$($pkg_config --libs $gtkpackage) diff --git a/include/ui/win32-kbd-hook.h b/include/ui/win32-kbd-hook.h new file mode 100644 index 0000000000..4bd9f00f97 --- /dev/null +++ b/include/ui/win32-kbd-hook.h @@ -0,0 +1,14 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef UI_WIN32_KBD_HOOK_H +#define UI_WIN32_KBD_HOOK_H + +void win32_kbd_set_window(void *hwnd); +void win32_kbd_set_grab(bool grab); + +#endif diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 45be5dc0ed..6a9e3135e8 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -32,6 +32,7 @@ stub-obj-y += trace-control.o stub-obj-y += uuid.o stub-obj-y += vm-stop.o stub-obj-y += vmstate.o +stub-obj-y += win32-kbd-hook.o stub-obj-y += fd-register.o stub-obj-y += qmp_memory_device.o stub-obj-y += target-monitor-defs.o diff --git a/stubs/win32-kbd-hook.c b/stubs/win32-kbd-hook.c new file mode 100644 index 0000000000..1a084b081a --- /dev/null +++ b/stubs/win32-kbd-hook.c @@ -0,0 +1,18 @@ +/* + * Win32 keyboard hook stubs + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "ui/win32-kbd-hook.h" + +void win32_kbd_set_window(void *hwnd) +{ +} + +void win32_kbd_set_grab(bool grab) +{ +} diff --git a/ui/Makefile.objs b/ui/Makefile.objs index e6da6ff047..504b196479 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -15,6 +15,9 @@ common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o common-obj-$(CONFIG_COCOA) += cocoa.o common-obj-$(CONFIG_VNC) += $(vnc-obj-y) common-obj-$(call lnot,$(CONFIG_VNC)) += vnc-stubs.o +ifneq (,$(findstring m,$(CONFIG_SDL)$(CONFIG_GTK))) +common-obj-$(CONFIG_WIN32) += win32-kbd-hook.o +endif # ui-sdl module common-obj-$(CONFIG_SDL) += sdl.mo @@ -38,6 +38,10 @@ #include "ui/console.h" #include "ui/gtk.h" +#ifdef G_OS_WIN32 +#include <gdk/gdkwin32.h> +#endif +#include "ui/win32-kbd-hook.h" #include <glib/gi18n.h> #include <locale.h> @@ -108,15 +112,6 @@ # define VTE_CHECK_VERSION(a, b, c) 0 #endif -/* Some older mingw versions lack this constant or have - * it conditionally defined */ -#ifdef _WIN32 -# ifndef MAPVK_VK_TO_VSC -# define MAPVK_VK_TO_VSC 0 -# endif -#endif - - #define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK) static const guint16 *keycode_map; @@ -173,8 +168,6 @@ struct GtkDisplayState { bool external_pause_update; - bool ignore_keys; - DisplayOptions *opts; }; @@ -428,6 +421,16 @@ static void gd_widget_reparent(GtkWidget *from, GtkWidget *to, g_object_unref(G_OBJECT(widget)); } +static void *gd_win32_get_hwnd(VirtualConsole *vc) +{ +#ifdef G_OS_WIN32 + return gdk_win32_window_get_impl_hwnd( + gtk_widget_get_window(vc->window ? vc->window : vc->s->window)); +#else + return NULL; +#endif +} + /** DisplayState Callbacks **/ static void gd_update(DisplayChangeListener *dcl, @@ -487,12 +490,7 @@ static void gd_refresh(DisplayChangeListener *dcl) static GdkDevice *gd_get_pointer(GdkDisplay *dpy) { -#if GTK_CHECK_VERSION(3, 20, 0) return gdk_seat_get_pointer(gdk_display_get_default_seat(dpy)); -#else - return gdk_device_manager_get_client_pointer( - gdk_display_get_device_manager(dpy)); -#endif } static void gd_mouse_set(DisplayChangeListener *dcl, @@ -874,27 +872,18 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, if (!qemu_input_is_absolute() && s->ptr_owner == vc) { GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area); + GdkDisplay *dpy = gtk_widget_get_display(widget); + GdkWindow *win = gtk_widget_get_window(widget); + GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win); + GdkRectangle geometry; int screen_width, screen_height; int x = (int)motion->x_root; int y = (int)motion->y_root; -#if GTK_CHECK_VERSION(3, 22, 0) - { - GdkDisplay *dpy = gtk_widget_get_display(widget); - GdkWindow *win = gtk_widget_get_window(widget); - GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win); - GdkRectangle geometry; - gdk_monitor_get_geometry(monitor, &geometry); - screen_width = geometry.width; - screen_height = geometry.height; - } -#else - { - screen_width = gdk_screen_get_width(screen); - screen_height = gdk_screen_get_height(screen); - } -#endif + gdk_monitor_get_geometry(monitor, &geometry); + screen_width = geometry.width; + screen_height = geometry.height; /* In relative mode check to see if client pointer hit * one of the screen edges, and if so move it back by @@ -1023,8 +1012,8 @@ static const guint16 *gd_get_keymap(size_t *maplen) #ifdef GDK_WINDOWING_WIN32 if (GDK_IS_WIN32_DISPLAY(dpy)) { trace_gd_keymap_windowing("win32"); - *maplen = qemu_input_map_win32_to_qcode_len; - return qemu_input_map_win32_to_qcode; + *maplen = qemu_input_map_atset1_to_qcode_len; + return qemu_input_map_atset1_to_qcode; } #endif @@ -1070,6 +1059,25 @@ static int gd_map_keycode(int scancode) return keycode_map[scancode]; } +static int gd_get_keycode(GdkEventKey *key) +{ +#ifdef G_OS_WIN32 + int scancode = gdk_event_get_scancode((GdkEvent *)key); + + /* translate Windows native scancodes to atset1 keycodes */ + switch (scancode & (KF_EXTENDED | 0xff)) { + case 0x145: /* NUMLOCK */ + return scancode & 0xff; + } + + return scancode & KF_EXTENDED ? + 0xe000 | (scancode & 0xff) : scancode & 0xff; + +#else + return key->hardware_keycode; +#endif +} + static gboolean gd_text_key_down(GtkWidget *widget, GdkEventKey *key, void *opaque) { @@ -1081,7 +1089,7 @@ static gboolean gd_text_key_down(GtkWidget *widget, } else if (key->length) { kbd_put_string_console(con, key->string, key->length); } else { - int qcode = gd_map_keycode(key->hardware_keycode); + int qcode = gd_map_keycode(gd_get_keycode(key)); kbd_put_qcode_console(con, qcode, false); } return TRUE; @@ -1090,18 +1098,19 @@ static gboolean gd_text_key_down(GtkWidget *widget, static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) { VirtualConsole *vc = opaque; - GtkDisplayState *s = vc->s; - int qcode; - - if (s->ignore_keys) { - s->ignore_keys = (key->type == GDK_KEY_PRESS); - return TRUE; - } + int keycode, qcode; -#ifdef WIN32 +#ifdef G_OS_WIN32 /* on windows, we ought to ignore the reserved key event? */ if (key->hardware_keycode == 0xff) return false; + + if (!vc->s->kbd_owner) { + if (key->hardware_keycode == VK_LWIN || + key->hardware_keycode == VK_RWIN) { + return FALSE; + } + } #endif if (key->keyval == GDK_KEY_Pause @@ -1117,9 +1126,10 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) return TRUE; } - qcode = gd_map_keycode(key->hardware_keycode); + keycode = gd_get_keycode(key); + qcode = gd_map_keycode(keycode); - trace_gd_key_event(vc->label, key->hardware_keycode, qcode, + trace_gd_key_event(vc->label, keycode, qcode, (key->type == GDK_KEY_PRESS) ? "down" : "up"); qkbd_state_key_event(vc->gfx.kbd, qcode, @@ -1128,6 +1138,25 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) return TRUE; } +static gboolean gd_grab_broken_event(GtkWidget *widget, + GdkEventGrabBroken *event, void *opaque) +{ +#ifdef CONFIG_WIN32 + /* + * On Windows the Ctrl-Alt-Del key combination can't be grabbed. This + * key combination leaves all three keys in a stuck condition. We use + * the grab-broken-event to release all keys. + */ + if (event->keyboard) { + VirtualConsole *vc = opaque; + GtkDisplayState *s = vc->s; + + gtk_release_modifiers(s); + } +#endif + return TRUE; +} + static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque) { if (event->type == GDK_MOTION_NOTIFY) { @@ -1180,7 +1209,6 @@ static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque) gtk_notebook_set_current_page(nb, page); gtk_widget_grab_focus(vc->focus); } - s->ignore_keys = false; } static void gd_accel_switch_vc(void *opaque) @@ -1390,7 +1418,6 @@ static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque) gd_update_full_redraw(vc); } -#if GTK_CHECK_VERSION(3, 20, 0) static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr) { GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); @@ -1414,32 +1441,6 @@ static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr) gdk_seat_ungrab(seat); } } -#else -static void gd_grab_devices(VirtualConsole *vc, bool grab, - GdkInputSource source, GdkEventMask mask, - GdkCursor *cursor) -{ - GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area); - GdkDeviceManager *mgr = gdk_display_get_device_manager(display); - GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER); - GList *tmp = devs; - - for (tmp = devs; tmp; tmp = tmp->next) { - GdkDevice *dev = tmp->data; - if (gdk_device_get_source(dev) != source) { - continue; - } - if (grab) { - GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area); - gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE, - mask, cursor, GDK_CURRENT_TIME); - } else { - gdk_device_ungrab(dev, GDK_CURRENT_TIME); - } - } - g_list_free(devs); -} -#endif static void gd_grab_keyboard(VirtualConsole *vc, const char *reason) { @@ -1451,13 +1452,8 @@ static void gd_grab_keyboard(VirtualConsole *vc, const char *reason) } } -#if GTK_CHECK_VERSION(3, 20, 0) + win32_kbd_set_grab(true); gd_grab_update(vc, true, vc->s->ptr_owner == vc); -#else - gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD, - GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, - NULL); -#endif vc->s->kbd_owner = vc; gd_update_caption(vc->s); trace_gd_grab(vc->label, "kbd", reason); @@ -1472,11 +1468,8 @@ static void gd_ungrab_keyboard(GtkDisplayState *s) } s->kbd_owner = NULL; -#if GTK_CHECK_VERSION(3, 20, 0) + win32_kbd_set_grab(false); gd_grab_update(vc, false, vc->s->ptr_owner == vc); -#else - gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL); -#endif gd_update_caption(s); trace_gd_ungrab(vc->label, "kbd"); } @@ -1493,21 +1486,9 @@ static void gd_grab_pointer(VirtualConsole *vc, const char *reason) } } -#if GTK_CHECK_VERSION(3, 20, 0) gd_grab_update(vc, vc->s->kbd_owner == vc, true); gdk_device_get_position(gd_get_pointer(display), NULL, &vc->s->grab_x_root, &vc->s->grab_y_root); -#else - gd_grab_devices(vc, true, GDK_SOURCE_MOUSE, - GDK_POINTER_MOTION_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_BUTTON_MOTION_MASK | - GDK_SCROLL_MASK, - vc->s->null_cursor); - gdk_device_get_position(gd_get_pointer(display), - NULL, &vc->s->grab_x_root, &vc->s->grab_y_root); -#endif vc->s->ptr_owner = vc; gd_update_caption(vc->s); trace_gd_grab(vc->label, "ptr", reason); @@ -1524,17 +1505,10 @@ static void gd_ungrab_pointer(GtkDisplayState *s) s->ptr_owner = NULL; display = gtk_widget_get_display(vc->gfx.drawing_area); -#if GTK_CHECK_VERSION(3, 20, 0) gd_grab_update(vc, vc->s->kbd_owner == vc, false); gdk_device_warp(gd_get_pointer(display), gtk_widget_get_screen(vc->gfx.drawing_area), vc->s->grab_x_root, vc->s->grab_y_root); -#else - gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL); - gdk_device_warp(gd_get_pointer(display), - gtk_widget_get_screen(vc->gfx.drawing_area), - vc->s->grab_x_root, vc->s->grab_y_root); -#endif gd_update_caption(s); trace_gd_ungrab(vc->label, "ptr"); } @@ -1614,12 +1588,22 @@ static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing, return TRUE; } +static gboolean gd_focus_in_event(GtkWidget *widget, + GdkEventFocus *event, gpointer opaque) +{ + VirtualConsole *vc = opaque; + + win32_kbd_set_window(gd_win32_get_hwnd(vc)); + return TRUE; +} + static gboolean gd_focus_out_event(GtkWidget *widget, - GdkEventCrossing *crossing, gpointer opaque) + GdkEventFocus *event, gpointer opaque) { VirtualConsole *vc = opaque; GtkDisplayState *s = vc->s; + win32_kbd_set_window(NULL); gtk_release_modifiers(s); return TRUE; } @@ -1878,10 +1862,14 @@ static void gd_connect_vc_gfx_signals(VirtualConsole *vc) G_CALLBACK(gd_enter_event), vc); g_signal_connect(vc->gfx.drawing_area, "leave-notify-event", G_CALLBACK(gd_leave_event), vc); + g_signal_connect(vc->gfx.drawing_area, "focus-in-event", + G_CALLBACK(gd_focus_in_event), vc); g_signal_connect(vc->gfx.drawing_area, "focus-out-event", G_CALLBACK(gd_focus_out_event), vc); g_signal_connect(vc->gfx.drawing_area, "configure-event", G_CALLBACK(gd_configure), vc); + g_signal_connect(vc->gfx.drawing_area, "grab-broken-event", + G_CALLBACK(gd_grab_broken_event), vc); } else { g_signal_connect(vc->gfx.drawing_area, "key-press-event", G_CALLBACK(gd_text_key_down), vc); diff --git a/ui/sdl2-input.c b/ui/sdl2-input.c index 1f9fe831b3..f068382209 100644 --- a/ui/sdl2-input.c +++ b/ui/sdl2-input.c @@ -27,6 +27,7 @@ #include "ui/console.h" #include "ui/input.h" #include "ui/sdl2.h" +#include "trace.h" void sdl2_process_key(struct sdl2_console *scon, SDL_KeyboardEvent *ev) @@ -38,6 +39,8 @@ void sdl2_process_key(struct sdl2_console *scon, return; } qcode = qemu_input_map_usb_to_qcode[ev->keysym.scancode]; + trace_sdl2_process_key(ev->keysym.scancode, qcode, + ev->type == SDL_KEYDOWN ? "down" : "up"); qkbd_state_key_event(scon->kbd, qcode, ev->type == SDL_KEYDOWN); if (!qemu_console_is_graphic(con)) { @@ -30,6 +30,7 @@ #include "ui/sdl2.h" #include "sysemu/runstate.h" #include "sysemu/sysemu.h" +#include "ui/win32-kbd-hook.h" static int sdl2_num_outputs; static struct sdl2_console *sdl2_console; @@ -220,6 +221,7 @@ static void sdl_grab_start(struct sdl2_console *scon) } SDL_SetWindowGrab(scon->real_window, SDL_TRUE); gui_grab = 1; + win32_kbd_set_grab(true); sdl_update_caption(scon); } @@ -227,6 +229,7 @@ static void sdl_grab_end(struct sdl2_console *scon) { SDL_SetWindowGrab(scon->real_window, SDL_FALSE); gui_grab = 0; + win32_kbd_set_grab(false); sdl_show_cursor(scon); sdl_update_caption(scon); } @@ -325,6 +328,19 @@ static int get_mod_state(void) } } +static void *sdl2_win32_get_hwnd(struct sdl2_console *scon) +{ +#ifdef CONFIG_WIN32 + SDL_SysWMinfo info; + + SDL_VERSION(&info.version); + if (SDL_GetWindowWMInfo(scon->real_window, &info)) { + return info.info.win.window; + } +#endif + return NULL; +} + static void handle_keydown(SDL_Event *ev) { int win; @@ -544,6 +560,11 @@ static void handle_windowevent(SDL_Event *ev) sdl2_redraw(scon); break; case SDL_WINDOWEVENT_FOCUS_GAINED: + win32_kbd_set_grab(gui_grab); + if (qemu_console_is_graphic(scon->dcl.con)) { + win32_kbd_set_window(sdl2_win32_get_hwnd(scon)); + } + /* fall through */ case SDL_WINDOWEVENT_ENTER: if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) { absolute_mouse_grab(scon); @@ -558,6 +579,9 @@ static void handle_windowevent(SDL_Event *ev) scon->ignore_hotkeys = get_mod_state(); break; case SDL_WINDOWEVENT_FOCUS_LOST: + if (qemu_console_is_graphic(scon->dcl.con)) { + win32_kbd_set_window(NULL); + } if (gui_grab && !gui_fullscreen) { sdl_grab_end(scon); } @@ -857,17 +881,16 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) SDL_SetWindowIcon(sdl2_console[0].real_window, icon); } - gui_grab = 0; - if (gui_fullscreen) { - sdl_grab_start(0); - } - mouse_mode_notifier.notify = sdl_mouse_mode_change; qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier); sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); sdl_cursor_normal = SDL_GetCursor(); + if (gui_fullscreen) { + sdl_grab_start(&sdl2_console[0]); + } + atexit(sdl_cleanup); } diff --git a/ui/trace-events b/ui/trace-events index 0dcda393c1..5367fd3f16 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -75,6 +75,9 @@ input_event_abs(int conidx, const char *axis, int value) "con %d, axis %s, value input_event_sync(void) "" input_mouse_mode(int absolute) "absolute %d" +# sdl2-input.c +sdl2_process_key(int sdl_scancode, int qcode, const char *action) "translated SDL scancode %d to QKeyCode %d (%s)" + # spice-display.c qemu_spice_add_memslot(int qid, uint32_t slot_id, unsigned long virt_start, unsigned long virt_end, int async) "%d %u: host virt 0x%lx - 0x%lx async=%d" qemu_spice_del_memslot(int qid, uint32_t gid, uint32_t slot_id) "%d gid=%u sid=%u" diff --git a/ui/win32-kbd-hook.c b/ui/win32-kbd-hook.c new file mode 100644 index 0000000000..1ac237db9e --- /dev/null +++ b/ui/win32-kbd-hook.c @@ -0,0 +1,102 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + * + * The win32 keyboard hooking code was imported from project spice-gtk. + */ + +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "ui/win32-kbd-hook.h" + +static Notifier win32_unhook_notifier; +static HHOOK win32_keyboard_hook; +static HWND win32_window; +static DWORD win32_grab; + +static LRESULT CALLBACK keyboard_hook_cb(int code, WPARAM wparam, LPARAM lparam) +{ + if (win32_window && code == HC_ACTION && win32_window == GetFocus()) { + KBDLLHOOKSTRUCT *hooked = (KBDLLHOOKSTRUCT *)lparam; + + if (wparam != WM_KEYUP) { + DWORD dwmsg = (hooked->flags << 24) | + ((hooked->scanCode & 0xff) << 16) | 1; + + switch (hooked->vkCode) { + case VK_CAPITAL: + /* fall through */ + case VK_SCROLL: + /* fall through */ + case VK_NUMLOCK: + /* fall through */ + case VK_LSHIFT: + /* fall through */ + case VK_RSHIFT: + /* fall through */ + case VK_RCONTROL: + /* fall through */ + case VK_LMENU: + /* fall through */ + 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: + if (win32_grab) { + SendMessage(win32_window, wparam, hooked->vkCode, dwmsg); + return 1; + } + break; + } + + } else { + switch (hooked->vkCode) { + case VK_LCONTROL: + if (hooked->scanCode & 0x200) { + return 1; + } + break; + } + } + } + + return CallNextHookEx(NULL, code, wparam, lparam); +} + +static void keyboard_hook_unhook(Notifier *n, void *data) +{ + UnhookWindowsHookEx(win32_keyboard_hook); + win32_keyboard_hook = NULL; +} + +void win32_kbd_set_window(void *hwnd) +{ + if (hwnd && !win32_keyboard_hook) { + /* note: the installing thread must have a message loop */ + win32_keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_hook_cb, + GetModuleHandle(NULL), 0); + if (win32_keyboard_hook) { + win32_unhook_notifier.notify = keyboard_hook_unhook; + qemu_add_exit_notifier(&win32_unhook_notifier); + } + } + + win32_window = hwnd; +} + +void win32_kbd_set_grab(bool grab) +{ + win32_grab = grab; +} |