xref: /openbmc/qemu/ui/win32-kbd-hook.c (revision c6446a1b)
1 /*
2  * This work is licensed under the terms of the GNU GPL, version 2 or
3  * (at your option) any later version.  See the COPYING file in the
4  * top-level directory.
5  *
6  * The win32 keyboard hooking code was imported from project spice-gtk.
7  */
8 
9 #include "qemu/osdep.h"
10 #include "sysemu/sysemu.h"
11 #include "ui/win32-kbd-hook.h"
12 
13 static Notifier win32_unhook_notifier;
14 static HHOOK win32_keyboard_hook;
15 static HWND win32_window;
16 static DWORD win32_grab;
17 
18 static LRESULT CALLBACK keyboard_hook_cb(int code, WPARAM wparam, LPARAM lparam)
19 {
20     if  (win32_window && code == HC_ACTION && win32_window == GetFocus()) {
21         KBDLLHOOKSTRUCT *hooked = (KBDLLHOOKSTRUCT *)lparam;
22 
23         if (wparam != WM_KEYUP) {
24             DWORD dwmsg = (hooked->flags << 24) |
25                           ((hooked->scanCode & 0xff) << 16) | 1;
26 
27             switch (hooked->vkCode) {
28             case VK_CAPITAL:
29                 /* fall through */
30             case VK_SCROLL:
31                 /* fall through */
32             case VK_NUMLOCK:
33                 /* fall through */
34             case VK_LSHIFT:
35                 /* fall through */
36             case VK_RSHIFT:
37                 /* fall through */
38             case VK_RCONTROL:
39                 /* fall through */
40             case VK_LMENU:
41                 /* fall through */
42             case VK_RMENU:
43                 break;
44 
45             case VK_LCONTROL:
46                 /*
47                  * When pressing AltGr, an extra VK_LCONTROL with a special
48                  * scancode with bit 9 set is sent. Let's ignore the extra
49                  * VK_LCONTROL, as that will make AltGr misbehave.
50                  */
51                 if (hooked->scanCode & 0x200) {
52                     return 1;
53                 }
54                 break;
55 
56             default:
57                 if (win32_grab) {
58                     SendMessage(win32_window, wparam, hooked->vkCode, dwmsg);
59                     return 1;
60                 }
61                 break;
62             }
63 
64         } else {
65             switch (hooked->vkCode) {
66             case VK_LCONTROL:
67                 if (hooked->scanCode & 0x200) {
68                     return 1;
69                 }
70                 break;
71             }
72         }
73     }
74 
75     return CallNextHookEx(NULL, code, wparam, lparam);
76 }
77 
78 static void keyboard_hook_unhook(Notifier *n, void *data)
79 {
80     UnhookWindowsHookEx(win32_keyboard_hook);
81     win32_keyboard_hook = NULL;
82 }
83 
84 void win32_kbd_set_window(void *hwnd)
85 {
86     if (hwnd && !win32_keyboard_hook) {
87         /* note: the installing thread must have a message loop */
88         win32_keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_hook_cb,
89                                                GetModuleHandle(NULL), 0);
90         if (win32_keyboard_hook) {
91             win32_unhook_notifier.notify = keyboard_hook_unhook;
92             qemu_add_exit_notifier(&win32_unhook_notifier);
93         }
94     }
95 
96     win32_window = hwnd;
97 }
98 
99 void win32_kbd_set_grab(bool grab)
100 {
101     win32_grab = grab;
102 }
103