mirror of
https://github.com/robotn/gohook.git
synced 2024-11-23 12:06:47 +08:00
824 lines
35 KiB
C
824 lines
35 KiB
C
|
/* Copyright (C) 2006-2017 Alexander Barker.
|
||
|
*
|
||
|
* This program is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU Lesser General Public License as published
|
||
|
* by the Free Software Foundation, either version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include <config.h>
|
||
|
#endif
|
||
|
|
||
|
#include <stdbool.h>
|
||
|
#include <stddef.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <windows.h>
|
||
|
|
||
|
#include "../iohook.h"
|
||
|
#include "../logger_c.h"
|
||
|
#include "input.h"
|
||
|
|
||
|
static const uint16_t keycode_scancode_table[][2] = {
|
||
|
/* idx { vk_code, scancode }, */
|
||
|
/* 0 */ { VC_UNDEFINED, 0x0000 }, // 0x00
|
||
|
/* 1 */ { MOUSE_BUTTON1, VK_ESCAPE }, // 0x01
|
||
|
/* 2 */ { MOUSE_BUTTON2, 0x0031 }, // 0x02
|
||
|
/* 3 */ { VC_UNDEFINED, 0x0032 }, // 0x03 VK_CANCEL
|
||
|
/* 4 */ { MOUSE_BUTTON3, 0x0033 }, // 0x04
|
||
|
/* 5 */ { MOUSE_BUTTON4, 0x0034 }, // 0x05
|
||
|
/* 6 */ { MOUSE_BUTTON5, 0x0035 }, // 0x06
|
||
|
/* 7 */ { VC_UNDEFINED, 0x0036 }, // 0x07 Undefined
|
||
|
/* 8 */ { VC_BACKSPACE, 0x0037 }, // 0x08 VK_BACK
|
||
|
/* 9 */ { VC_TAB, 0x0038 }, // 0x09 VK_TAB
|
||
|
/* 10 */ { VC_UNDEFINED, 0x0039 }, // 0x0A Reserved
|
||
|
/* 11 */ { VC_UNDEFINED, 0x0030 }, // 0x0B Reserved
|
||
|
/* 12 */ { VC_CLEAR, VK_OEM_MINUS }, // 0x0C VK_CLEAR
|
||
|
/* 13 */ { VC_ENTER, VK_OEM_PLUS }, // 0x0D VK_RETURN
|
||
|
/* 14 */ { VC_UNDEFINED, VK_BACK }, // 0x0E Undefined
|
||
|
/* 15 */ { VC_UNDEFINED, VK_TAB }, // 0x0F Undefined
|
||
|
/* 16 */ { VC_SHIFT_L, 0x0051 }, // 0x10 VK_SHIFT
|
||
|
/* 17 */ { VC_CONTROL_L, 0x0057 }, // 0x11 VK_CONTROL
|
||
|
/* 18 */ { VC_ALT_L, 0x0045 }, // 0x12 VK_MENU ALT key
|
||
|
/* 19 */ { VC_PAUSE, 0x0052 }, // 0x13 VK_PAUSE
|
||
|
/* 20 */ { VC_CAPS_LOCK, 0x0054 }, // 0x14 VK_CAPITAL CAPS LOCK key
|
||
|
/* 21 */ { VC_KATAKANA, 0x0059 }, // 0x15 VK_KANA IME Kana mode
|
||
|
/* 22 */ { VC_UNDEFINED, 0x0055 }, // 0x16 Undefined
|
||
|
/* 23 */ { VC_UNDEFINED, 0x0049 }, // 0x17 VK_JUNJA IME Junja mode
|
||
|
/* 24 */ { VC_UNDEFINED, 0x004F }, // 0x18 VK_FINAL
|
||
|
/* 25 */ { VC_KANJI, 0x0050 }, // 0x19 VK_KANJI / VK_HANJA IME Kanji / Hanja mode
|
||
|
/* 26 */ { VC_UNDEFINED, 0x00DB }, // 0x1A Undefined
|
||
|
/* 27 */ { VC_ESCAPE, 0x00DD }, // 0x1B VK_ESCAPE ESC key
|
||
|
/* 28 */ { VC_UNDEFINED, VK_RETURN }, // 0x1C VK_CONVERT IME convert// 0x1C
|
||
|
/* 29 */ { VC_UNDEFINED, VK_LCONTROL }, // 0x1D VK_NONCONVERT IME nonconvert
|
||
|
/* 30 */ { VC_UNDEFINED, 0x0041 }, // 0x1E VK_ACCEPT IME accept
|
||
|
/* 31 */ { VC_UNDEFINED, 0x0053 }, // 0x1F VK_MODECHANGE IME mode change request
|
||
|
/* 32 */ { VC_SPACE, 0x0044 }, // 0x20 VK_SPACE SPACEBAR
|
||
|
/* 33 */ { VC_PAGE_UP, 0x0046 }, // 0x21 VK_PRIOR PAGE UP key
|
||
|
/* 34 */ { VC_PAGE_DOWN, 0x0047 }, // 0x22 VK_NEXT PAGE DOWN key
|
||
|
/* 35 */ { VC_END, 0x0048 }, // 0x23 VK_END END key
|
||
|
/* 36 */ { VC_HOME, 0x004A }, // 0x24 VK_HOME HOME key
|
||
|
/* 37 */ { VC_LEFT, 0x004B }, // 0x25 VK_LEFT LEFT ARROW key
|
||
|
/* 38 */ { VC_UP, 0x004C }, // 0x26 VK_UP UP ARROW key
|
||
|
/* 39 */ { VC_RIGHT, VK_OEM_1 }, // 0x27 VK_RIGHT RIGHT ARROW key
|
||
|
/* 40 */ { VC_DOWN, VK_OEM_7 }, // 0x28 VK_DOWN DOWN ARROW key
|
||
|
/* 41 */ { VC_UNDEFINED, VK_OEM_3 }, // 0x29 VK_SELECT SELECT key
|
||
|
/* 42 */ { VC_UNDEFINED, VK_LSHIFT }, // 0x2A VK_PRINT PRINT key
|
||
|
/* 43 */ { VC_UNDEFINED, VK_OEM_5 }, // 0x2B VK_EXECUTE EXECUTE key
|
||
|
/* 44 */ { VC_PRINTSCREEN, 0x005A }, // 0x2C VK_SNAPSHOT PRINT SCREEN key
|
||
|
/* 45 */ { VC_INSERT, 0x0058 }, // 0x2D VK_INSERT INS key
|
||
|
/* 46 */ { VC_DELETE, 0x0043 }, // 0x2E VK_DELETE DEL key
|
||
|
/* 47 */ { VC_UNDEFINED, 0x0056 }, // 0x2F VK_HELP HELP key
|
||
|
/* 48 */ { VC_0, 0x0042 }, // 0x30 0 key
|
||
|
/* 49 */ { VC_1, 0x004E }, // 0x31 1 key
|
||
|
/* 50 */ { VC_2, 0x004D }, // 0x32 2 key
|
||
|
/* 51 */ { VC_3, VK_OEM_COMMA }, // 0x33 3 key
|
||
|
/* 52 */ { VC_4, VK_OEM_PERIOD }, // 0x34 4 key
|
||
|
/* 53 */ { VC_5, VK_OEM_2 }, // 0x35 5 key
|
||
|
/* 54 */ { VC_6, VK_RSHIFT }, // 0x36 6 key
|
||
|
/* 55 */ { VC_7, VK_MULTIPLY }, // 0x37 7 key
|
||
|
/* 56 */ { VC_8, VK_LMENU }, // 0x38 8 key
|
||
|
/* 57 */ { VC_9, VK_SPACE }, // 0x39 9 key
|
||
|
/* 58 */ { VC_UNDEFINED, VK_CAPITAL }, // 0x3A Undefined
|
||
|
/* 59 */ { VC_UNDEFINED, VK_F1 }, // 0x3B Undefined
|
||
|
/* 60 */ { VC_UNDEFINED, VK_F2 }, // 0x3C Undefined
|
||
|
/* 61 */ { VC_UNDEFINED, VK_F3 }, // 0x3D Undefined
|
||
|
/* 62 */ { VC_UNDEFINED, VK_F4 }, // 0x3E Undefined
|
||
|
/* 63 */ { VC_UNDEFINED, VK_F5 }, // 0x3F Undefined
|
||
|
/* 64 */ { VC_UNDEFINED, VK_F6 }, // 0x40 Undefined
|
||
|
/* 65 */ { VC_A, VK_F7 }, // 0x41 A key
|
||
|
/* 66 */ { VC_B, VK_F8 }, // 0x42 B key
|
||
|
/* 67 */ { VC_C, VK_F9 }, // 0x43 C key
|
||
|
/* 68 */ { VC_D, VK_F10 }, // 0x44 D key
|
||
|
/* 69 */ { VC_E, VK_NUMLOCK }, // 0x45 E key
|
||
|
/* 70 */ { VC_F, VK_SCROLL }, // 0x46 F key
|
||
|
/* 71 */ { VC_G, VK_NUMPAD7 }, // 0x47 G key
|
||
|
/* 72 */ { VC_H, VK_NUMPAD8 }, // 0x48 H key
|
||
|
/* 73 */ { VC_I, VK_NUMPAD9 }, // 0x49 I key
|
||
|
/* 74 */ { VC_J, VK_SUBTRACT }, // 0x4A J key
|
||
|
/* 75 */ { VC_K, VK_NUMPAD4 }, // 0x4B K key
|
||
|
/* 76 */ { VC_L, VK_NUMPAD5 }, // 0x4C L key
|
||
|
/* 77 */ { VC_M, VK_NUMPAD6 }, // 0x4D M key
|
||
|
/* 78 */ { VC_N, VK_ADD }, // 0x4E N key
|
||
|
/* 79 */ { VC_O, VK_NUMPAD1 }, // 0x4F O key
|
||
|
/* 80 */ { VC_P, VK_NUMPAD2 }, // 0x50 P key
|
||
|
/* 81 */ { VC_Q, VK_NUMPAD3 }, // 0x51 Q key
|
||
|
/* 82 */ { VC_R, VK_NUMPAD0 }, // 0x52 R key
|
||
|
/* 83 */ { VC_S, VK_DECIMAL }, // 0x53 S key
|
||
|
/* 84 */ { VC_T, 0x0000 }, // 0x54 T key
|
||
|
/* 85 */ { VC_U, 0x0000 }, // 0x55 U key
|
||
|
/* 86 */ { VC_V, 0x0000 }, // 0x56 V key
|
||
|
/* 87 */ { VC_W, VK_F11 }, // 0x57 W key
|
||
|
/* 88 */ { VC_X, VK_F12 }, // 0x58 X key
|
||
|
/* 89 */ { VC_Y, 0x0000 }, // 0x59 Y key
|
||
|
/* 90 */ { VC_Z, 0x0000 }, // 0x5A Z key
|
||
|
/* 91 */ { VC_META_L, VK_F13 }, // 0x5B VK_LWIN Left Windows key (Natural keyboard)
|
||
|
/* 92 */ { VC_META_R, VK_F14 }, // 0x5C VK_RWIN Right Windows key (Natural keyboard)
|
||
|
/* 93 */ { VC_CONTEXT_MENU, VK_F15 }, // 0x5D VK_APPS Applications key (Natural keyboard)
|
||
|
/* 94 */ { VC_UNDEFINED, 0x0000 }, // 0x5E Reserved
|
||
|
/* 95 */ { VC_SLEEP, 0x0000 }, // 0x5F VK_SLEEP Computer Sleep key
|
||
|
/* 96 */ { VC_KP_0, 0x0000 }, // 0x60 VK_NUMPAD0 Numeric keypad 0 key
|
||
|
/* 97 */ { VC_KP_1, 0x0000 }, // 0x61 VK_NUMPAD1 Numeric keypad 1 key
|
||
|
/* 98 */ { VC_KP_2, 0x0000 }, // 0x62 VK_NUMPAD2 Numeric keypad 2 key
|
||
|
/* 99 */ { VC_KP_3, VK_F16 }, // 0x63 VK_NUMPAD3 Numeric keypad 3 key
|
||
|
/* 100 */ { VC_KP_4, VK_F17 }, // 0x64 VK_NUMPAD4 Numeric keypad 4 key
|
||
|
/* 101 */ { VC_KP_5, VK_F18 }, // 0x65 VK_NUMPAD5 Numeric keypad 5 key
|
||
|
/* 102 */ { VC_KP_6, VK_F19 }, // 0x66 VK_NUMPAD6 Numeric keypad 6 key
|
||
|
/* 103 */ { VC_KP_7, VK_F20 }, // 0x67 VK_NUMPAD7 Numeric keypad 7 key
|
||
|
/* 104 */ { VC_KP_8, VK_F21 }, // 0x68 VK_NUMPAD8 Numeric keypad 8 key
|
||
|
/* 105 */ { VC_KP_9, VK_F22 }, // 0x69 VK_NUMPAD9 Numeric keypad 9 key
|
||
|
/* 106 */ { VC_KP_MULTIPLY, VK_F23 }, // 0x6A VK_MULTIPLY Multiply key
|
||
|
/* 107 */ { VC_KP_ADD, VK_F24 }, // 0x6B VK_ADD Add key
|
||
|
/* 108 */ { VC_UNDEFINED, 0x0000 }, // 0x6C VK_SEPARATOR Separator key
|
||
|
/* 109 */ { VC_KP_SUBTRACT, 0x0000 }, // 0x6D VK_SUBTRACT Subtract key
|
||
|
/* 110 */ { VC_KP_SEPARATOR, 0x0000 }, // 0x6E VK_DECIMAL Decimal key
|
||
|
/* 111 */ { VC_KP_DIVIDE, 0x0000 }, // 0x6F VK_DIVIDE Divide key
|
||
|
/* 112 */ { VC_F1, VK_KANA }, // 0x70 VK_F1 F1 key
|
||
|
/* 113 */ { VC_F2, 0x0000 }, // 0x71 VK_F2 F2 key
|
||
|
/* 114 */ { VC_F3, 0x0000 }, // 0x72 VK_F3 F3 key
|
||
|
/* 115 */ { VC_F4, 0x0000 }, // 0x73 VK_F4 F4 key
|
||
|
/* 116 */ { VC_F5, 0x0000 }, // 0x74 VK_F5 F5 key
|
||
|
/* 117 */ { VC_F6, 0x0000 }, // 0x75 VK_F6 F6 key
|
||
|
/* 118 */ { VC_F7, 0x0000 }, // 0x76 VK_F7 F7 key
|
||
|
/* 119 */ { VC_F8, 0x0000 }, // 0x77 VK_F8 F8 key
|
||
|
/* 120 */ { VC_F9, 0x0000 }, // 0x78 VK_F9 F9 key
|
||
|
/* 121 */ { VC_F10, VK_KANJI }, // 0x79 VK_F10 F10 key
|
||
|
/* 122 */ { VC_F11, 0x0000 }, // 0x7A VK_F11 F11 key
|
||
|
/* 123 */ { VC_F12, 0x0000 }, // 0x7B VK_F12 F12 key
|
||
|
/* 124 */ { VC_F13, 0x0000 }, // 0x7C VK_F13 F13 key
|
||
|
/* 125 */ { VC_F14, VK_OEM_8 }, // 0x7D VK_F14 F14 key
|
||
|
/* 126 */ { VC_F15, 0x0000 }, // 0x7E VK_F15 F15 key
|
||
|
/* 127 */ { VC_F16, 0x0000 }, // 0x7F VK_F16 F16 key
|
||
|
|
||
|
// No Offset Offset (i & 0x007F) | 0x80
|
||
|
|
||
|
/* 128 */ { VC_F17, 0x0000 }, // 0x80 VK_F17 F17 key
|
||
|
/* 129 */ { VC_F18, 0x0000 }, // 0x81 VK_F18 F18 key
|
||
|
/* 130 */ { VC_F19, 0x0000 }, // 0x82 VK_F19 F19 key
|
||
|
/* 131 */ { VC_F20, 0x0000 }, // 0x83 VK_F20 F20 key
|
||
|
/* 132 */ { VC_F21, 0x0000 }, // 0x84 VK_F21 F21 key
|
||
|
/* 133 */ { VC_F22, 0x0000 }, // 0x85 VK_F22 F22 key
|
||
|
/* 134 */ { VC_F23, 0x0000 }, // 0x86 VK_F23 F23 key
|
||
|
/* 135 */ { VC_F24, 0x0000 }, // 0x87 VK_F24 F24 key
|
||
|
/* 136 */ { VC_UNDEFINED, 0x0000 }, // 0x88 Unassigned
|
||
|
/* 137 */ { VC_UNDEFINED, 0x0000 }, // 0x89 Unassigned
|
||
|
/* 138 */ { VC_UNDEFINED, 0x0000 }, // 0x8A Unassigned
|
||
|
/* 139 */ { VC_UNDEFINED, 0x0000 }, // 0x8B Unassigned
|
||
|
/* 140 */ { VC_UNDEFINED, 0x0000 }, // 0x8C Unassigned
|
||
|
/* 141 */ { VC_UNDEFINED, 0x0000 }, // 0x8D Unassigned
|
||
|
/* 142 */ { VC_UNDEFINED, 0x0000 }, // 0x8E Unassigned
|
||
|
/* 143 */ { VC_UNDEFINED, 0x0000 }, // 0x8F Unassigned
|
||
|
/* 144 */ { VC_NUM_LOCK, VK_MEDIA_PREV_TRACK }, // 0x90 VK_NUMLOCK NUM LOCK key
|
||
|
/* 145 */ { VC_SCROLL_LOCK, 0x0000 }, // 0x91 VK_SCROLL SCROLL LOCK key
|
||
|
/* 146 */ { VC_UNDEFINED, 0x0000 }, // 0x92 OEM specific
|
||
|
/* 147 */ { VC_UNDEFINED, 0x0000 }, // 0x93 OEM specific
|
||
|
/* 148 */ { VC_UNDEFINED, 0x0000 }, // 0x94 OEM specific
|
||
|
/* 149 */ { VC_UNDEFINED, 0x0000 }, // 0x95 OEM specific
|
||
|
/* 150 */ { VC_UNDEFINED, 0x0000 }, // 0x96 OEM specific
|
||
|
/* 151 */ { VC_UNDEFINED, 0x0000 }, // 0x97 Unassigned
|
||
|
/* 152 */ { VC_UNDEFINED, 0x0000 }, // 0x98 Unassigned
|
||
|
/* 153 */ { VC_UNDEFINED, VK_MEDIA_NEXT_TRACK }, // 0x99 Unassigned
|
||
|
/* 154 */ { VC_UNDEFINED, 0x0000 }, // 0x9A Unassigned
|
||
|
/* 155 */ { VC_UNDEFINED, 0x0000 }, // 0x9B Unassigned
|
||
|
/* 156 */ { VC_UNDEFINED, 0x0000 }, // 0x9C Unassigned
|
||
|
/* 157 */ { VC_UNDEFINED, VK_RCONTROL }, // 0x9D Unassigned
|
||
|
/* 158 */ { VC_UNDEFINED, 0x0000 }, // 0x9E Unassigned
|
||
|
/* 159 */ { VC_UNDEFINED, 0x0000 }, // 0x9F Unassigned
|
||
|
/* 160 */ { VC_SHIFT_L, VK_VOLUME_MUTE }, // 0xA0 VK_LSHIFT Left SHIFT key
|
||
|
/* 161 */ { VC_SHIFT_R, VK_LAUNCH_APP2 }, // 0xA1 VK_RSHIFT Right SHIFT key
|
||
|
/* 162 */ { VC_CONTROL_L, VK_MEDIA_PLAY_PAUSE }, // 0xA2 VK_LCONTROL Left CONTROL key
|
||
|
/* 163 */ { VC_CONTROL_R, 0x0000 }, // 0xA3 VK_RCONTROL Right CONTROL key
|
||
|
/* 164 */ { VC_ALT_L, VK_MEDIA_STOP }, // 0xA4 VK_LMENU Left MENU key
|
||
|
/* 165 */ { VC_ALT_R, 0x0000 }, // 0xA5 VK_RMENU Right MENU key
|
||
|
/* 166 */ { VC_BROWSER_BACK, 0x0000 }, // 0xA6 VK_BROWSER_BACK Browser Back key
|
||
|
/* 167 */ { VC_BROWSER_FORWARD, 0x0000 }, // 0xA7 VK_BROWSER_FORWARD Browser Forward key
|
||
|
/* 168 */ { VC_BROWSER_REFRESH, 0x0000 }, // 0xA8 VK_BROWSER_REFRESH Browser Refresh key
|
||
|
/* 169 */ { VC_BROWSER_STOP, 0x0000 }, // 0xA9 VK_BROWSER_STOP Browser Stop key
|
||
|
/* 170 */ { VC_BROWSER_SEARCH, 0x0000 }, // 0xAA VK_BROWSER_SEARCH Browser Search key
|
||
|
/* 171 */ { VC_BROWSER_FAVORITES, 0x0000 }, // 0xAB VK_BROWSER_FAVORITES Browser Favorites key
|
||
|
/* 172 */ { VC_BROWSER_HOME, 0x0000 }, // 0xAC VK_BROWSER_HOME Browser Start and Home key
|
||
|
/* 173 */ { VC_VOLUME_MUTE, 0x0000 }, // 0xAD VK_VOLUME_MUTE Volume Mute key
|
||
|
/* 174 */ { VC_VOLUME_DOWN, VK_VOLUME_DOWN }, // 0xAE VK_VOLUME_DOWN Volume Down key
|
||
|
/* 175 */ { VC_VOLUME_UP, 0x0000 }, // 0xAF VK_VOLUME_UP Volume Up key
|
||
|
/* 176 */ { VC_MEDIA_NEXT, VK_VOLUME_UP }, // 0xB0 VK_MEDIA_NEXT_TRACK Next Track key
|
||
|
/* 177 */ { VC_MEDIA_PREVIOUS, 0x0000 }, // 0xB1 VK_MEDIA_PREV_TRACK Previous Track key
|
||
|
/* 178 */ { VC_MEDIA_STOP, VK_BROWSER_HOME }, // 0xB2 VK_MEDIA_STOP Stop Media key
|
||
|
/* 179 */ { VC_MEDIA_PLAY, 0x0000 }, // 0xB3 VK_MEDIA_PLAY_PAUSE Play/Pause Media key
|
||
|
/* 180 */ { VC_UNDEFINED, 0x0000 }, // 0xB4 VK_LAUNCH_MAIL Start Mail key
|
||
|
/* 181 */ { VC_MEDIA_SELECT, VK_DIVIDE }, // 0xB5 VK_LAUNCH_MEDIA_SELECT Select Media key
|
||
|
/* 182 */ { VC_APP_MAIL, 0x0000 }, // 0xB6 VK_LAUNCH_APP1 Start Application 1 key
|
||
|
/* 183 */ { VC_APP_CALCULATOR, VK_SNAPSHOT }, // 0xB7 VK_LAUNCH_APP2 Start Application 2 key
|
||
|
/* 184 */ { VC_UNDEFINED, VK_RMENU }, // 0xB8 Reserved
|
||
|
/* 185 */ { VC_UNDEFINED, 0x0000 }, // 0xB9 Reserved
|
||
|
/* 186 */ { VC_SEMICOLON, 0x0000 }, // 0xBA VK_OEM_1 Varies by keyboard. For the US standard keyboard, the ';:' key
|
||
|
/* 187 */ { VC_EQUALS, 0x0000 }, // 0xBB VK_OEM_PLUS For any country/region, the '+' key
|
||
|
/* 188 */ { VC_COMMA, 0x00E6 }, // 0xBC VK_OEM_COMMA For any country/region, the ',' key
|
||
|
/* 189 */ { VC_MINUS, 0x0000 }, // 0xBD VK_OEM_MINUS For any country/region, the '-' key
|
||
|
/* 190 */ { VC_PERIOD, 0x0000 }, // 0xBE VK_OEM_PERIOD For any country/region, the '.' key
|
||
|
/* 191 */ { VC_SLASH, 0x0000 }, // 0xBF VK_OEM_2 Varies by keyboard. For the US standard keyboard, the '/?' key
|
||
|
/* 192 */ { VC_BACKQUOTE, 0x0000 }, // 0xC0 VK_OEM_3 Varies by keyboard. For the US standard keyboard, the '`~' key
|
||
|
/* 193 */ { VC_UNDEFINED, 0x0000 }, // 0xC1 Reserved
|
||
|
/* 194 */ { VC_UNDEFINED, 0x0000 }, // 0xC2 Reserved
|
||
|
/* 195 */ { VC_UNDEFINED, 0x0000 }, // 0xC3 Reserved
|
||
|
/* 196 */ { VC_UNDEFINED, 0x0000 }, // 0xC4 Reserved
|
||
|
/* 197 */ { VC_UNDEFINED, VK_PAUSE }, // 0xC5 Reserved
|
||
|
/* 198 */ { VC_UNDEFINED, 0x0000 }, // 0xC6 Reserved
|
||
|
/* 199 */ { VC_UNDEFINED, VK_HOME }, // 0xC7 Reserved
|
||
|
/* 200 */ { VC_UNDEFINED, VK_UP }, // 0xC8 Reserved
|
||
|
/* 201 */ { VC_UNDEFINED, VK_PRIOR }, // 0xC9 Reserved
|
||
|
/* 202 */ { VC_UNDEFINED, 0x0000 }, // 0xCA Reserved
|
||
|
/* 203 */ { VC_UNDEFINED, VK_LEFT }, // 0xCB Reserved
|
||
|
/* 204 */ { VC_UNDEFINED, VK_CLEAR }, // 0xCC Reserved
|
||
|
/* 205 */ { VC_UNDEFINED, VK_RIGHT }, // 0xCD Reserved
|
||
|
/* 206 */ { VC_UNDEFINED, 0x0000 }, // 0xCE Reserved
|
||
|
/* 207 */ { VC_UNDEFINED, VK_END }, // 0xCF Reserved
|
||
|
/* 208 */ { VC_UNDEFINED, VK_DOWN }, // 0xD0 Reserved
|
||
|
/* 209 */ { VC_UNDEFINED, VK_NEXT }, // 0xD1 Reserved
|
||
|
/* 210 */ { VC_UNDEFINED, VK_INSERT }, // 0xD2 Reserved
|
||
|
/* 211 */ { VC_UNDEFINED, VK_DELETE }, // 0xD3 Reserved
|
||
|
/* 212 */ { VC_UNDEFINED, 0x0000 }, // 0xD4 Reserved
|
||
|
/* 213 */ { VC_UNDEFINED, 0x0000 }, // 0xD5 Reserved
|
||
|
/* 214 */ { VC_UNDEFINED, 0x0000 }, // 0xD6 Reserved
|
||
|
/* 215 */ { VC_UNDEFINED, 0x0000 }, // 0xD7 Reserved
|
||
|
/* 216 */ { VC_UNDEFINED, 0x0000 }, // 0xD8 Unassigned
|
||
|
/* 217 */ { VC_UNDEFINED, 0x0000 }, // 0xD9 Unassigned
|
||
|
/* 218 */ { VC_UNDEFINED, 0x0000 }, // 0xDA Unassigned
|
||
|
/* 219 */ { VC_OPEN_BRACKET, VK_LWIN }, // 0xDB VK_OEM_4 Varies by keyboard. For the US standard keyboard, the '[{' key
|
||
|
/* 220 */ { VC_BACK_SLASH, VK_RWIN }, // 0xDC VK_OEM_5 Varies by keyboard. For the US standard keyboard, the '\|' key
|
||
|
/* 221 */ { VC_CLOSE_BRACKET, VK_APPS }, // 0xDD VK_OEM_6 Varies by keyboard. For the US standard keyboard, the ']}' key
|
||
|
/* 222 */ { VC_QUOTE, 0x0000 }, // 0xDE VK_OEM_7 Varies by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key
|
||
|
/* 223 */ { VC_YEN, VK_SLEEP }, // 0xDF VK_OEM_8 Varies by keyboard.
|
||
|
/* 224 */ { VC_UNDEFINED, 0x0000 }, // 0xE0 Reserved
|
||
|
/* 225 */ { VC_UNDEFINED, 0x0000 }, // 0xE1 OEM specific
|
||
|
/* 226 */ { VC_UNDEFINED, 0x0000 }, // 0xE2 VK_OEM_102 Either the angle bracket key or the backslash key on the RT 102-key keyboard
|
||
|
/* 227 */ { VC_UNDEFINED, 0x0000 }, // 0xE3 OEM specific
|
||
|
/* 228 */ { VC_UNDEFINED, 0x00E5 }, // 0xE4 VC_APP_PICTURES OEM specific
|
||
|
/* 229 */ { VC_APP_PICTURES, VK_BROWSER_SEARCH }, // 0xE5 VK_PROCESSKEY IME PROCESS key
|
||
|
/* 230 */ { VC_APP_MUSIC, VK_BROWSER_FAVORITES }, // 0xE6 OEM specific
|
||
|
/* 231 */ { VC_UNDEFINED, VK_BROWSER_REFRESH }, // 0xE7 VK_PACKET Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods.
|
||
|
/* 232 */ { VC_UNDEFINED, VK_BROWSER_STOP }, // 0xE8 Unassigned
|
||
|
/* 233 */ { VC_UNDEFINED, VK_BROWSER_FORWARD }, // 0xE9 OEM specific
|
||
|
/* 234 */ { VC_UNDEFINED, VK_BROWSER_BACK }, // 0xEA OEM specific
|
||
|
/* 235 */ { VC_UNDEFINED, 0x0000 }, // 0xEB OEM specific
|
||
|
/* 236 */ { VC_UNDEFINED, VK_LAUNCH_APP1 }, // 0xEC OEM specific
|
||
|
/* 237 */ { VC_UNDEFINED, VK_LAUNCH_MEDIA_SELECT }, // 0xED OEM specific
|
||
|
/* 238 */ { VC_UNDEFINED, 0x0000 }, // 0xEE OEM specific
|
||
|
/* 239 */ { VC_UNDEFINED, 0x0000 }, // 0xEF OEM specific
|
||
|
/* 240 */ { VC_UNDEFINED, 0x0000 }, // 0xF0 OEM specific
|
||
|
/* 241 */ { VC_UNDEFINED, 0x0000 }, // 0xF1 OEM specific
|
||
|
/* 242 */ { VC_UNDEFINED, 0x0000 }, // 0xF2 OEM specific
|
||
|
/* 243 */ { VC_UNDEFINED, 0x0000 }, // 0xF3 OEM specific
|
||
|
/* 244 */ { VC_UNDEFINED, 0x0000 }, // 0xF4 OEM specific
|
||
|
/* 245 */ { VC_UNDEFINED, 0x0000 }, // 0xF5 OEM specific
|
||
|
/* 246 */ { VC_UNDEFINED, 0x0000 }, // 0xF6 VK_ATTN Attn key
|
||
|
/* 247 */ { VC_UNDEFINED, 0x0000 }, // 0xF7 VK_CRSEL CrSel key
|
||
|
/* 248 */ { VC_UNDEFINED, 0x0000 }, // 0xF8 VK_EXSEL ExSel key
|
||
|
/* 249 */ { VC_UNDEFINED, 0x0000 }, // 0xF9 VK_EREOF Erase EOF key
|
||
|
/* 250 */ { VC_UNDEFINED, 0x0000 }, // 0xFA VK_PLAY Play key
|
||
|
/* 251 */ { VC_UNDEFINED, 0x0000 }, // 0xFB VK_ZOOM Zoom key
|
||
|
/* 252 */ { VC_UNDEFINED, 0x0000 }, // 0xFC VK_NONAME Reserved
|
||
|
/* 253 */ { VC_UNDEFINED, 0x0000 }, // 0xFD
|
||
|
/* 254 */ { VC_CLEAR, 0x0000 }, // 0xFE VK_OEM_CLEAR Clear key
|
||
|
/* 255 */ { VC_UNDEFINED, 0x0000 } // 0xFE Unassigned
|
||
|
};
|
||
|
|
||
|
unsigned short keycode_to_scancode(DWORD vk_code, DWORD flags) {
|
||
|
unsigned short scancode = VC_UNDEFINED;
|
||
|
|
||
|
// Check the vk_code is in range.
|
||
|
// NOTE vk_code >= 0 is assumed because DWORD is unsigned.
|
||
|
if (vk_code < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[0])) {
|
||
|
scancode = keycode_scancode_table[vk_code][0];
|
||
|
|
||
|
if (flags & LLKHF_EXTENDED) {
|
||
|
// logger(LOG_LEVEL_WARN, "%s [%u]: EXTD2, vk_code %li\n",
|
||
|
// __FUNCTION__, __LINE__, vk_code);
|
||
|
|
||
|
switch (vk_code) {
|
||
|
case VK_PRIOR:
|
||
|
case VK_NEXT:
|
||
|
case VK_END:
|
||
|
case VK_HOME:
|
||
|
case VK_LEFT:
|
||
|
case VK_UP:
|
||
|
case VK_RIGHT:
|
||
|
case VK_DOWN:
|
||
|
|
||
|
case VK_INSERT:
|
||
|
case VK_DELETE:
|
||
|
scancode |= 0xEE00;
|
||
|
break;
|
||
|
|
||
|
case VK_RETURN:
|
||
|
scancode |= 0x0E00;
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
// logger(LOG_LEVEL_WARN, "%s [%u]: Test2, vk_code %li\n",
|
||
|
// __FUNCTION__, __LINE__, vk_code);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return scancode;
|
||
|
}
|
||
|
|
||
|
DWORD scancode_to_keycode(unsigned short scancode) {
|
||
|
unsigned short keycode = 0x0000;
|
||
|
|
||
|
// Check the vk_code is in range.
|
||
|
// NOTE vk_code >= 0 is assumed because the scancode is unsigned.
|
||
|
if (scancode < 128) {
|
||
|
keycode = keycode_scancode_table[scancode][1];
|
||
|
} else {
|
||
|
// Calculate the upper offset based on the lower half of the scancode + 128.
|
||
|
unsigned short int i = (scancode & 0x007F) | 0x80;
|
||
|
|
||
|
if (i < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[1])) {
|
||
|
keycode = keycode_scancode_table[i][1];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return keycode;
|
||
|
}
|
||
|
|
||
|
|
||
|
/************************************************************************/
|
||
|
|
||
|
// Structure and pointers for the keyboard locale cache.
|
||
|
typedef struct _KeyboardLocale {
|
||
|
HKL id; // Locale ID
|
||
|
HINSTANCE library; // Keyboard DLL instance.
|
||
|
PVK_TO_BIT pVkToBit; // Pointers struct arrays.
|
||
|
PVK_TO_WCHAR_TABLE pVkToWcharTable;
|
||
|
PDEADKEY pDeadKey;
|
||
|
struct _KeyboardLocale* next;
|
||
|
} KeyboardLocale;
|
||
|
|
||
|
static KeyboardLocale* locale_first = NULL;
|
||
|
static KeyboardLocale* locale_current = NULL;
|
||
|
static WCHAR deadChar = WCH_NONE;
|
||
|
|
||
|
// Amount of pointer padding to apply for Wow64 instances.
|
||
|
static unsigned short int ptr_padding = 0;
|
||
|
|
||
|
#if defined(_WIN32) && !defined(_WIN64)
|
||
|
// Small function to check and see if we are executing under Wow64.
|
||
|
static BOOL is_wow64() {
|
||
|
BOOL status = FALSE;
|
||
|
|
||
|
LPFN_ISWOW64PROCESS pIsWow64Process = (LPFN_ISWOW64PROCESS)
|
||
|
GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process");
|
||
|
|
||
|
if (pIsWow64Process != NULL) {
|
||
|
HANDLE current_proc = GetCurrentProcess();
|
||
|
|
||
|
if (!pIsWow64Process(current_proc, &status)) {
|
||
|
status = FALSE;
|
||
|
|
||
|
logger(LOG_LEVEL_DEBUG, "%s [%u]: pIsWow64Process(%#p, %#p) failed!\n",
|
||
|
__FUNCTION__, __LINE__, current_proc, &status);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Locate the DLL that contains the current keyboard layout.
|
||
|
static int get_keyboard_layout_file(char *layoutFile, DWORD bufferSize) {
|
||
|
int status = IOHOOK_FAILURE;
|
||
|
HKEY hKey;
|
||
|
DWORD varType = REG_SZ;
|
||
|
|
||
|
char kbdName[KL_NAMELENGTH];
|
||
|
if (GetKeyboardLayoutName(kbdName)) {
|
||
|
char kbdKeyPath[51 + KL_NAMELENGTH];
|
||
|
snprintf(kbdKeyPath, 51 + KL_NAMELENGTH, "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s", kbdName);
|
||
|
|
||
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR) kbdKeyPath, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
|
||
|
if (RegQueryValueEx(hKey, "Layout File", NULL, &varType, (LPBYTE) layoutFile, &bufferSize) == ERROR_SUCCESS) {
|
||
|
RegCloseKey(hKey);
|
||
|
status = IOHOOK_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
static int refresh_locale_list() {
|
||
|
int count = 0;
|
||
|
|
||
|
// Get the number of layouts the user has activated.
|
||
|
int hkl_size = GetKeyboardLayoutList(0, NULL);
|
||
|
if (hkl_size > 0) {
|
||
|
logger(LOG_LEVEL_INFO, "%s [%u]: GetKeyboardLayoutList(0, NULL) found %i layouts.\n",
|
||
|
__FUNCTION__, __LINE__, hkl_size);
|
||
|
|
||
|
// Get the thread id that currently has focus for our default.
|
||
|
DWORD focus_pid = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
|
||
|
HKL hlk_focus = GetKeyboardLayout(focus_pid);
|
||
|
HKL hlk_default = GetKeyboardLayout(0);
|
||
|
HKL *hkl_list = malloc(sizeof(HKL) * hkl_size);
|
||
|
|
||
|
int new_size = GetKeyboardLayoutList(hkl_size, hkl_list);
|
||
|
if (new_size > 0) {
|
||
|
if (new_size != hkl_size) {
|
||
|
logger(LOG_LEVEL_WARN, "%s [%u]: Locale size mismatch! "
|
||
|
"Expected %i, received %i!\n",
|
||
|
__FUNCTION__, __LINE__, hkl_size, new_size);
|
||
|
} else {
|
||
|
logger(LOG_LEVEL_INFO, "%s [%u]: Received %i locales.\n",
|
||
|
__FUNCTION__, __LINE__, new_size);
|
||
|
}
|
||
|
|
||
|
KeyboardLocale* locale_previous = NULL;
|
||
|
KeyboardLocale* locale_item = locale_first;
|
||
|
|
||
|
// Go though the linked list and remove KeyboardLocale's that are
|
||
|
// no longer loaded.
|
||
|
while (locale_item != NULL) {
|
||
|
// Check to see if the old HKL is in the new list.
|
||
|
bool is_loaded = false;
|
||
|
int i;
|
||
|
for (i = 0; i < new_size && !is_loaded; i++) {
|
||
|
if (locale_item->id == hkl_list[i]) {
|
||
|
// Flag and jump out of the loop.
|
||
|
hkl_list[i] = NULL;
|
||
|
is_loaded = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (is_loaded) {
|
||
|
logger(LOG_LEVEL_DEBUG, "%s [%u]: Found locale ID %#p in the cache.\n",
|
||
|
__FUNCTION__, __LINE__, locale_item->id);
|
||
|
|
||
|
// Set the previous local to the current locale.
|
||
|
locale_previous = locale_item;
|
||
|
|
||
|
// Check and see if the locale is our current active locale.
|
||
|
if (locale_item->id == hlk_focus) {
|
||
|
locale_current = locale_item;
|
||
|
}
|
||
|
|
||
|
count++;
|
||
|
} else {
|
||
|
logger(LOG_LEVEL_DEBUG, "%s [%u]: Removing locale ID %#p from the cache.\n",
|
||
|
__FUNCTION__, __LINE__, locale_item->id);
|
||
|
|
||
|
// If the old id is not in the new list, remove it.
|
||
|
locale_previous->next = locale_item->next;
|
||
|
|
||
|
// Make sure the locale_current points NULL or something valid.
|
||
|
if (locale_item == locale_current) {
|
||
|
locale_current = NULL;
|
||
|
}
|
||
|
|
||
|
// Free the memory used by locale_item;
|
||
|
free(locale_item);
|
||
|
|
||
|
// Set the item to the pervious item to guarantee a next.
|
||
|
locale_item = locale_previous;
|
||
|
}
|
||
|
|
||
|
// Iterate to the next linked list item.
|
||
|
locale_item = locale_item->next;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Insert anything new into the linked list.
|
||
|
int i;
|
||
|
for (i = 0; i < new_size; i++) {
|
||
|
// Check to see if the item was already in the list.
|
||
|
if (hkl_list[i] != NULL) {
|
||
|
// Set the active keyboard layout for this thread to the HKL.
|
||
|
ActivateKeyboardLayout(hkl_list[i], 0x00);
|
||
|
|
||
|
// Try to pull the current keyboard layout DLL from the registry.
|
||
|
char layoutFile[MAX_PATH];
|
||
|
if (get_keyboard_layout_file(layoutFile, sizeof(layoutFile)) == IOHOOK_SUCCESS) {
|
||
|
// You can't trust the %SYSPATH%, look it up manually.
|
||
|
char systemDirectory[MAX_PATH];
|
||
|
if (GetSystemDirectory(systemDirectory, MAX_PATH) != 0) {
|
||
|
char kbdLayoutFilePath[MAX_PATH];
|
||
|
snprintf(kbdLayoutFilePath, MAX_PATH, "%s\\%s", systemDirectory, layoutFile);
|
||
|
|
||
|
logger(LOG_LEVEL_DEBUG, "%s [%u]: Loading layout for %#p: %s.\n",
|
||
|
__FUNCTION__, __LINE__, hkl_list[i], layoutFile);
|
||
|
|
||
|
// Create the new locale item.
|
||
|
locale_item = malloc(sizeof(KeyboardLocale));
|
||
|
locale_item->id = hkl_list[i];
|
||
|
locale_item->library = LoadLibrary(kbdLayoutFilePath);
|
||
|
|
||
|
// Get the function pointer from the library to get the keyboard layer descriptor.
|
||
|
KbdLayerDescriptor pKbdLayerDescriptor = (KbdLayerDescriptor) GetProcAddress(locale_item->library, "KbdLayerDescriptor");
|
||
|
if (pKbdLayerDescriptor != NULL) {
|
||
|
PKBDTABLES pKbd = pKbdLayerDescriptor();
|
||
|
|
||
|
// Store the memory address of the following 3 structures.
|
||
|
BYTE *base = (BYTE *) pKbd;
|
||
|
|
||
|
// First element of each structure, no offset adjustment needed.
|
||
|
locale_item->pVkToBit = pKbd->pCharModifiers->pVkToBit;
|
||
|
|
||
|
// Second element of pKbd, +4 byte offset on wow64.
|
||
|
locale_item->pVkToWcharTable = *((PVK_TO_WCHAR_TABLE *) (base + offsetof(KBDTABLES, pVkToWcharTable) + ptr_padding));
|
||
|
|
||
|
// Third element of pKbd, +8 byte offset on wow64.
|
||
|
locale_item->pDeadKey = *((PDEADKEY *) (base + offsetof(KBDTABLES, pDeadKey) + (ptr_padding * 2)));
|
||
|
|
||
|
// This will always be added to the end of the list.
|
||
|
locale_item->next = NULL;
|
||
|
|
||
|
// Insert the item into the linked list.
|
||
|
if (locale_previous == NULL) {
|
||
|
// If nothing came before, the list is empty.
|
||
|
locale_first = locale_item;
|
||
|
} else {
|
||
|
// Append the new locale to the end of the list.
|
||
|
locale_previous->next = locale_item;
|
||
|
}
|
||
|
|
||
|
// Check and see if the locale is our current active locale.
|
||
|
if (locale_item->id == hlk_focus) {
|
||
|
locale_current = locale_item;
|
||
|
}
|
||
|
|
||
|
// Set the pervious locale item to the new one.
|
||
|
locale_previous = locale_item;
|
||
|
|
||
|
count++;
|
||
|
} else {
|
||
|
logger(LOG_LEVEL_ERROR,
|
||
|
"%s [%u]: GetProcAddress() failed for KbdLayerDescriptor!\n",
|
||
|
__FUNCTION__, __LINE__);
|
||
|
|
||
|
FreeLibrary(locale_item->library);
|
||
|
free(locale_item);
|
||
|
locale_item = NULL;
|
||
|
}
|
||
|
} else {
|
||
|
logger(LOG_LEVEL_ERROR,
|
||
|
"%s [%u]: GetSystemDirectory() failed!\n",
|
||
|
__FUNCTION__, __LINE__);
|
||
|
}
|
||
|
} else {
|
||
|
logger(LOG_LEVEL_ERROR,
|
||
|
"%s [%u]: Could not find keyboard map for locale %#p!\n",
|
||
|
__FUNCTION__, __LINE__, hkl_list[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
logger(LOG_LEVEL_ERROR,
|
||
|
"%s [%u]: GetKeyboardLayoutList() failed!\n",
|
||
|
__FUNCTION__, __LINE__);
|
||
|
|
||
|
// TODO Try and recover by using the current layout.
|
||
|
// Hint: Use locale_id instead of hkl_list[i] in the loop above.
|
||
|
}
|
||
|
|
||
|
free(hkl_list);
|
||
|
ActivateKeyboardLayout(hlk_default, 0x00);
|
||
|
}
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
SIZE_T keycode_to_unicode(DWORD keycode, PWCHAR buffer, SIZE_T size) {
|
||
|
// Get the thread id that currently has focus and ask for its current
|
||
|
// locale.
|
||
|
DWORD focus_pid = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
|
||
|
HKL locale_id = GetKeyboardLayout(focus_pid);
|
||
|
|
||
|
// If the current Locale is not the new locale, search the linked list.
|
||
|
if (locale_current == NULL || locale_current->id != locale_id) {
|
||
|
locale_current = NULL;
|
||
|
KeyboardLocale* locale_item = locale_first;
|
||
|
|
||
|
// Search the linked list...
|
||
|
while (locale_item != NULL && locale_item->id != locale_id) {
|
||
|
locale_item = locale_item->next;
|
||
|
}
|
||
|
|
||
|
// You may already be a winner!
|
||
|
if (locale_item != NULL && locale_item->id != locale_id) {
|
||
|
logger(LOG_LEVEL_INFO,
|
||
|
"%s [%u]: Activating keyboard layout %#p.\n",
|
||
|
__FUNCTION__, __LINE__, locale_item->id);
|
||
|
|
||
|
// Switch the current locale.
|
||
|
locale_current = locale_item;
|
||
|
locale_item = NULL;
|
||
|
|
||
|
// If they layout changes the dead key state needs to be reset.
|
||
|
// This is consistent with the way Windows handles locale changes.
|
||
|
deadChar = WCH_NONE;
|
||
|
} else {
|
||
|
logger(LOG_LEVEL_DEBUG,
|
||
|
"%s [%u]: Refreshing locale cache.\n",
|
||
|
__FUNCTION__, __LINE__);
|
||
|
|
||
|
refresh_locale_list();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Initialize to empty.
|
||
|
SIZE_T charCount = 0;
|
||
|
// buffer[i] = WCH_NONE;
|
||
|
|
||
|
// Check and make sure the Unicode helper was loaded.
|
||
|
if (locale_current != NULL) {
|
||
|
logger(LOG_LEVEL_INFO,
|
||
|
"%s [%u]: Using keyboard layout %#p.\n",
|
||
|
__FUNCTION__, __LINE__, locale_current->id);
|
||
|
|
||
|
int mod = 0;
|
||
|
|
||
|
int capsLock = (GetKeyState(VK_CAPITAL) & 0x01);
|
||
|
|
||
|
PVK_TO_BIT pVkToBit = locale_current->pVkToBit;
|
||
|
PVK_TO_WCHAR_TABLE pVkToWcharTable = locale_current->pVkToWcharTable;
|
||
|
PDEADKEY pDeadKey = locale_current->pDeadKey;
|
||
|
|
||
|
/* Loop over the modifier keys for this locale and determine what is
|
||
|
* currently depressed. Because this is only a structure of two
|
||
|
* bytes, we don't need to worry about the structure padding of __ptr64
|
||
|
* offsets on Wow64.
|
||
|
*/
|
||
|
bool is_shift = false, is_ctrl = false, is_alt = false;
|
||
|
int i;
|
||
|
for (i = 0; pVkToBit[i].Vk != 0; i++) {
|
||
|
short state = GetAsyncKeyState(pVkToBit[i].Vk);
|
||
|
|
||
|
// Check to see if the most significant bit is active.
|
||
|
if (state & ~SHRT_MAX) {
|
||
|
if (pVkToBit[i].Vk == VK_SHIFT) {
|
||
|
is_shift = true;
|
||
|
} else if (pVkToBit[i].Vk == VK_CONTROL) {
|
||
|
is_ctrl = true;
|
||
|
} else if (pVkToBit[i].Vk == VK_MENU) {
|
||
|
is_alt = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check the Shift modifier.
|
||
|
if (is_shift) {
|
||
|
mod = 1;
|
||
|
}
|
||
|
|
||
|
// Check for the AltGr modifier.
|
||
|
if (is_ctrl && is_alt) {
|
||
|
mod += 3;
|
||
|
}
|
||
|
|
||
|
// Default 32 bit structure size should be 6 bytes (4 for the pointer and 2
|
||
|
// additional byte fields) that are padded out to 8 bytes by the compiler.
|
||
|
unsigned short sizeVkToWcharTable = sizeof(VK_TO_WCHAR_TABLE);
|
||
|
#if defined(_WIN32) && !defined(_WIN64)
|
||
|
if (is_wow64()) {
|
||
|
// If we are running under Wow64 the size of the first pointer will be
|
||
|
// 8 bringing the total size to 10 bytes padded out to 16.
|
||
|
sizeVkToWcharTable = (sizeVkToWcharTable + ptr_padding + 7) & -8;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
BYTE *ptrCurrentVkToWcharTable = (BYTE *) pVkToWcharTable;
|
||
|
|
||
|
int cbSize, n;
|
||
|
do {
|
||
|
// cbSize is used to calculate n, and n is used for the size of pVkToWchars[j].wch[n]
|
||
|
cbSize = *(ptrCurrentVkToWcharTable + offsetof(VK_TO_WCHAR_TABLE, cbSize) + ptr_padding);
|
||
|
n = (cbSize - 2) / 2;
|
||
|
|
||
|
// Same as VK_TO_WCHARS pVkToWchars[] = pVkToWcharTable[i].pVkToWchars
|
||
|
PVK_TO_WCHARS pVkToWchars = (PVK_TO_WCHARS) ((PVK_TO_WCHAR_TABLE) ptrCurrentVkToWcharTable)->pVkToWchars;
|
||
|
|
||
|
if (pVkToWchars != NULL && mod < n) {
|
||
|
// pVkToWchars[j].VirtualKey
|
||
|
BYTE *pCurrentVkToWchars = (BYTE *) pVkToWchars;
|
||
|
|
||
|
do {
|
||
|
if (((PVK_TO_WCHARS) pCurrentVkToWchars)->VirtualKey == keycode) {
|
||
|
if ((((PVK_TO_WCHARS) pCurrentVkToWchars)->Attributes == CAPLOK) && capsLock) {
|
||
|
if (is_shift && mod > 0) {
|
||
|
mod -= 1;
|
||
|
} else {
|
||
|
mod += 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Set the initial unicode char.
|
||
|
WCHAR unicode = ((PVK_TO_WCHARS) pCurrentVkToWchars)->wch[mod];
|
||
|
|
||
|
// Increment the pCurrentVkToWchars by the size of wch[n].
|
||
|
pCurrentVkToWchars += sizeof(VK_TO_WCHARS) + (sizeof(WCHAR) * n);
|
||
|
|
||
|
|
||
|
if (unicode == WCH_DEAD) {
|
||
|
// The current unicode char is a dead key...
|
||
|
if (deadChar == WCH_NONE) {
|
||
|
// No previous dead key was set so cache the next
|
||
|
// wchar so we know what to do next time its pressed.
|
||
|
deadChar = ((PVK_TO_WCHARS) pCurrentVkToWchars)->wch[mod];
|
||
|
} else {
|
||
|
if (size >= 2) {
|
||
|
// Received a second dead key.
|
||
|
memset(buffer, deadChar, 2);
|
||
|
//buffer[0] = deadChar;
|
||
|
//buffer[1] = deadChar;
|
||
|
|
||
|
deadChar = WCH_NONE;
|
||
|
charCount = 2;
|
||
|
}
|
||
|
}
|
||
|
} else if (unicode != WCH_NONE) {
|
||
|
// We are not WCH_NONE or WCH_DEAD
|
||
|
if (size >= 1) {
|
||
|
buffer[0] = unicode;
|
||
|
charCount = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
} else {
|
||
|
// Add sizeof WCHAR because we are really an array of WCHAR[n] not WCHAR[]
|
||
|
pCurrentVkToWchars += sizeof(VK_TO_WCHARS) + (sizeof(WCHAR) * n);
|
||
|
}
|
||
|
} while ( ((PVK_TO_WCHARS) pCurrentVkToWchars)->VirtualKey != 0 );
|
||
|
}
|
||
|
|
||
|
// This is effectively the same as: ptrCurrentVkToWcharTable = pVkToWcharTable[++i];
|
||
|
ptrCurrentVkToWcharTable += sizeVkToWcharTable;
|
||
|
} while (cbSize != 0);
|
||
|
|
||
|
|
||
|
// If the current local has a dead key set.
|
||
|
if (deadChar != WCH_NONE) {
|
||
|
// Loop over the pDeadKey lookup table for the locale.
|
||
|
int i;
|
||
|
for (i = 0; pDeadKey[i].dwBoth != 0; i++) {
|
||
|
WCHAR baseChar = (WCHAR) pDeadKey[i].dwBoth;
|
||
|
WCHAR diacritic = (WCHAR) (pDeadKey[i].dwBoth >> 16);
|
||
|
|
||
|
// If we locate an extended dead char, set it.
|
||
|
if (size >= 1 && baseChar == buffer[0] && diacritic == deadChar) {
|
||
|
deadChar = WCH_NONE;
|
||
|
|
||
|
if (charCount <= size) {
|
||
|
memset(buffer, (WCHAR) pDeadKey[i].wchComposed, charCount);
|
||
|
//buffer[i] = (WCHAR) pDeadKey[i].wchComposed;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return charCount;
|
||
|
}
|
||
|
|
||
|
int load_input_helper() {
|
||
|
int count = 0;
|
||
|
|
||
|
#if defined(_WIN32) && !defined(_WIN64)
|
||
|
if (is_wow64()) {
|
||
|
ptr_padding = sizeof(void *);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
count = refresh_locale_list();
|
||
|
|
||
|
logger(LOG_LEVEL_INFO,
|
||
|
"%s [%u]: refresh_locale_list() found %i locale(s).\n",
|
||
|
__FUNCTION__, __LINE__, count);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
// This returns the number of locales that were removed.
|
||
|
int unload_input_helper() {
|
||
|
int count = 0;
|
||
|
|
||
|
// Cleanup and free memory from the old list.
|
||
|
KeyboardLocale* locale_item = locale_first;
|
||
|
while (locale_item != NULL) {
|
||
|
// Remove the first item from the linked list.
|
||
|
FreeLibrary(locale_item->library);
|
||
|
locale_first = locale_item->next;
|
||
|
free(locale_item);
|
||
|
locale_item = locale_first;
|
||
|
|
||
|
count++;
|
||
|
}
|
||
|
|
||
|
// Reset the current local.
|
||
|
locale_current = NULL;
|
||
|
|
||
|
return count;
|
||
|
}
|