Volker RĂ¼melin | 2df9f57 | 2020-05-16 09:20:04 +0200 | [diff] [blame] | 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 | } |