@mukea/uiohook-napi 1.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/binding.gyp +85 -0
- package/dist/index.d.ts +194 -0
- package/dist/index.js +206 -0
- package/dist/index.js.map +1 -0
- package/dist/prebuild-test-noop.d.ts +0 -0
- package/dist/prebuild-test-noop.js +3 -0
- package/dist/prebuild-test-noop.js.map +1 -0
- package/libuiohook/include/uiohook.h +457 -0
- package/libuiohook/src/darwin/input_helper.c +535 -0
- package/libuiohook/src/darwin/input_helper.h +203 -0
- package/libuiohook/src/darwin/input_hook.c +1436 -0
- package/libuiohook/src/darwin/post_event.c +303 -0
- package/libuiohook/src/darwin/system_properties.c +479 -0
- package/libuiohook/src/logger.c +40 -0
- package/libuiohook/src/logger.h +32 -0
- package/libuiohook/src/windows/input_helper.c +913 -0
- package/libuiohook/src/windows/input_helper.h +146 -0
- package/libuiohook/src/windows/input_hook.c +722 -0
- package/libuiohook/src/windows/post_event.c +248 -0
- package/libuiohook/src/windows/system_properties.c +231 -0
- package/libuiohook/src/x11/input_helper.c +1846 -0
- package/libuiohook/src/x11/input_helper.h +108 -0
- package/libuiohook/src/x11/input_hook.c +1116 -0
- package/libuiohook/src/x11/post_event.c +427 -0
- package/libuiohook/src/x11/system_properties.c +494 -0
- package/package.json +60 -0
- package/prebuilds/darwin/darwin-arm64/@mukea+uiohook-napi.node +0 -0
- package/prebuilds/darwin/darwin-x64/@mukea+uiohook-napi.node +0 -0
- package/prebuilds/linux/linux-arm64/@mukea+uiohook-napi.node +0 -0
- package/prebuilds/linux/linux-x64/@mukea+uiohook-napi.node +0 -0
- package/prebuilds/windows/win32-x64/@mukea+uiohook-napi.node +0 -0
- package/src/lib/addon.c +337 -0
- package/src/lib/napi_helpers.c +51 -0
- package/src/lib/napi_helpers.h +53 -0
- package/src/lib/uiohook_worker.c +200 -0
- package/src/lib/uiohook_worker.h +12 -0
|
@@ -0,0 +1,913 @@
|
|
|
1
|
+
/* libUIOHook: Cross-platform keyboard and mouse hooking from userland.
|
|
2
|
+
* Copyright (C) 2006-2023 Alexander Barker. All Rights Reserved.
|
|
3
|
+
* https://github.com/kwhat/libuiohook/
|
|
4
|
+
*
|
|
5
|
+
* libUIOHook is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Lesser General Public License as published
|
|
7
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* libUIOHook is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Lesser General Public License
|
|
16
|
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
#include <stdbool.h>
|
|
20
|
+
#include <stddef.h>
|
|
21
|
+
#include <stdint.h>
|
|
22
|
+
#include <stdio.h>
|
|
23
|
+
#include <string.h>
|
|
24
|
+
#include <uiohook.h>
|
|
25
|
+
#include <windows.h>
|
|
26
|
+
|
|
27
|
+
#include "logger.h"
|
|
28
|
+
#include "input_helper.h"
|
|
29
|
+
|
|
30
|
+
static const uint16_t keycode_scancode_table[][2] = {
|
|
31
|
+
/* idx { vk_code, scancode }, */
|
|
32
|
+
/* 0 */ { VC_UNDEFINED, 0x0000 }, // 0x00
|
|
33
|
+
/* 1 */ { MOUSE_BUTTON1, VK_ESCAPE }, // 0x01
|
|
34
|
+
/* 2 */ { MOUSE_BUTTON2, 0x0031 }, // 0x02
|
|
35
|
+
/* 3 */ { VC_UNDEFINED, 0x0032 }, // 0x03 VK_CANCEL
|
|
36
|
+
/* 4 */ { MOUSE_BUTTON3, 0x0033 }, // 0x04
|
|
37
|
+
/* 5 */ { MOUSE_BUTTON4, 0x0034 }, // 0x05
|
|
38
|
+
/* 6 */ { MOUSE_BUTTON5, 0x0035 }, // 0x06
|
|
39
|
+
/* 7 */ { VC_UNDEFINED, 0x0036 }, // 0x07 Undefined
|
|
40
|
+
/* 8 */ { VC_BACKSPACE, 0x0037 }, // 0x08 VK_BACK
|
|
41
|
+
/* 9 */ { VC_TAB, 0x0038 }, // 0x09 VK_TAB
|
|
42
|
+
/* 10 */ { VC_UNDEFINED, 0x0039 }, // 0x0A Reserved
|
|
43
|
+
/* 11 */ { VC_UNDEFINED, 0x0030 }, // 0x0B Reserved
|
|
44
|
+
/* 12 */ { VC_CLEAR, VK_OEM_MINUS }, // 0x0C VK_CLEAR
|
|
45
|
+
/* 13 */ { VC_ENTER, VK_OEM_PLUS }, // 0x0D VK_RETURN
|
|
46
|
+
/* 14 */ { VC_UNDEFINED, VK_BACK }, // 0x0E Undefined
|
|
47
|
+
/* 15 */ { VC_UNDEFINED, VK_TAB }, // 0x0F Undefined
|
|
48
|
+
/* 16 */ { VC_SHIFT_L, 0x0051 }, // 0x10 VK_SHIFT
|
|
49
|
+
/* 17 */ { VC_CONTROL_L, 0x0057 }, // 0x11 VK_CONTROL
|
|
50
|
+
/* 18 */ { VC_ALT_L, 0x0045 }, // 0x12 VK_MENU ALT key
|
|
51
|
+
/* 19 */ { VC_PAUSE, 0x0052 }, // 0x13 VK_PAUSE
|
|
52
|
+
/* 20 */ { VC_CAPS_LOCK, 0x0054 }, // 0x14 VK_CAPITAL CAPS LOCK key
|
|
53
|
+
/* 21 */ { VC_KATAKANA, 0x0059 }, // 0x15 VK_KANA IME Kana mode
|
|
54
|
+
/* 22 */ { VC_UNDEFINED, 0x0055 }, // 0x16 Undefined
|
|
55
|
+
/* 23 */ { VC_UNDEFINED, 0x0049 }, // 0x17 VK_JUNJA IME Junja mode
|
|
56
|
+
/* 24 */ { VC_UNDEFINED, 0x004F }, // 0x18 VK_FINAL
|
|
57
|
+
/* 25 */ { VC_KANJI, 0x0050 }, // 0x19 VK_KANJI / VK_HANJA IME Kanji / Hanja mode
|
|
58
|
+
/* 26 */ { VC_UNDEFINED, 0x00DB }, // 0x1A Undefined
|
|
59
|
+
/* 27 */ { VC_ESCAPE, 0x00DD }, // 0x1B VK_ESCAPE ESC key
|
|
60
|
+
/* 28 */ { VC_UNDEFINED, VK_RETURN }, // 0x1C VK_CONVERT IME convert// 0x1C
|
|
61
|
+
/* 29 */ { VC_UNDEFINED, VK_LCONTROL }, // 0x1D VK_NONCONVERT IME nonconvert
|
|
62
|
+
/* 30 */ { VC_UNDEFINED, 0x0041 }, // 0x1E VK_ACCEPT IME accept
|
|
63
|
+
/* 31 */ { VC_UNDEFINED, 0x0053 }, // 0x1F VK_MODECHANGE IME mode change request
|
|
64
|
+
/* 32 */ { VC_SPACE, 0x0044 }, // 0x20 VK_SPACE SPACEBAR
|
|
65
|
+
/* 33 */ { VC_PAGE_UP, 0x0046 }, // 0x21 VK_PRIOR PAGE UP key
|
|
66
|
+
/* 34 */ { VC_PAGE_DOWN, 0x0047 }, // 0x22 VK_NEXT PAGE DOWN key
|
|
67
|
+
/* 35 */ { VC_END, 0x0048 }, // 0x23 VK_END END key
|
|
68
|
+
/* 36 */ { VC_HOME, 0x004A }, // 0x24 VK_HOME HOME key
|
|
69
|
+
/* 37 */ { VC_LEFT, 0x004B }, // 0x25 VK_LEFT LEFT ARROW key
|
|
70
|
+
/* 38 */ { VC_UP, 0x004C }, // 0x26 VK_UP UP ARROW key
|
|
71
|
+
/* 39 */ { VC_RIGHT, VK_OEM_1 }, // 0x27 VK_RIGHT RIGHT ARROW key
|
|
72
|
+
/* 40 */ { VC_DOWN, VK_OEM_7 }, // 0x28 VK_DOWN DOWN ARROW key
|
|
73
|
+
/* 41 */ { VC_UNDEFINED, VK_OEM_3 }, // 0x29 VK_SELECT SELECT key
|
|
74
|
+
/* 42 */ { VC_UNDEFINED, VK_LSHIFT }, // 0x2A VK_PRINT PRINT key
|
|
75
|
+
/* 43 */ { VC_UNDEFINED, VK_OEM_5 }, // 0x2B VK_EXECUTE EXECUTE key
|
|
76
|
+
/* 44 */ { VC_PRINTSCREEN, 0x005A }, // 0x2C VK_SNAPSHOT PRINT SCREEN key
|
|
77
|
+
/* 45 */ { VC_INSERT, 0x0058 }, // 0x2D VK_INSERT INS key
|
|
78
|
+
/* 46 */ { VC_DELETE, 0x0043 }, // 0x2E VK_DELETE DEL key
|
|
79
|
+
/* 47 */ { VC_UNDEFINED, 0x0056 }, // 0x2F VK_HELP HELP key
|
|
80
|
+
/* 48 */ { VC_0, 0x0042 }, // 0x30 0 key
|
|
81
|
+
/* 49 */ { VC_1, 0x004E }, // 0x31 1 key
|
|
82
|
+
/* 50 */ { VC_2, 0x004D }, // 0x32 2 key
|
|
83
|
+
/* 51 */ { VC_3, VK_OEM_COMMA }, // 0x33 3 key
|
|
84
|
+
/* 52 */ { VC_4, VK_OEM_PERIOD }, // 0x34 4 key
|
|
85
|
+
/* 53 */ { VC_5, VK_OEM_2 }, // 0x35 5 key
|
|
86
|
+
/* 54 */ { VC_6, VK_RSHIFT }, // 0x36 6 key
|
|
87
|
+
/* 55 */ { VC_7, VK_MULTIPLY }, // 0x37 7 key
|
|
88
|
+
/* 56 */ { VC_8, VK_LMENU }, // 0x38 8 key
|
|
89
|
+
/* 57 */ { VC_9, VK_SPACE }, // 0x39 9 key
|
|
90
|
+
/* 58 */ { VC_UNDEFINED, VK_CAPITAL }, // 0x3A Undefined
|
|
91
|
+
/* 59 */ { VC_UNDEFINED, VK_F1 }, // 0x3B Undefined
|
|
92
|
+
/* 60 */ { VC_UNDEFINED, VK_F2 }, // 0x3C Undefined
|
|
93
|
+
/* 61 */ { VC_UNDEFINED, VK_F3 }, // 0x3D Undefined
|
|
94
|
+
/* 62 */ { VC_UNDEFINED, VK_F4 }, // 0x3E Undefined
|
|
95
|
+
/* 63 */ { VC_UNDEFINED, VK_F5 }, // 0x3F Undefined
|
|
96
|
+
/* 64 */ { VC_UNDEFINED, VK_F6 }, // 0x40 Undefined
|
|
97
|
+
/* 65 */ { VC_A, VK_F7 }, // 0x41 A key
|
|
98
|
+
/* 66 */ { VC_B, VK_F8 }, // 0x42 B key
|
|
99
|
+
/* 67 */ { VC_C, VK_F9 }, // 0x43 C key
|
|
100
|
+
/* 68 */ { VC_D, VK_F10 }, // 0x44 D key
|
|
101
|
+
/* 69 */ { VC_E, VK_NUMLOCK }, // 0x45 E key
|
|
102
|
+
/* 70 */ { VC_F, VK_SCROLL }, // 0x46 F key
|
|
103
|
+
/* 71 */ { VC_G, VK_NUMPAD7 }, // 0x47 G key
|
|
104
|
+
/* 72 */ { VC_H, VK_NUMPAD8 }, // 0x48 H key
|
|
105
|
+
/* 73 */ { VC_I, VK_NUMPAD9 }, // 0x49 I key
|
|
106
|
+
/* 74 */ { VC_J, VK_SUBTRACT }, // 0x4A J key
|
|
107
|
+
/* 75 */ { VC_K, VK_NUMPAD4 }, // 0x4B K key
|
|
108
|
+
/* 76 */ { VC_L, VK_NUMPAD5 }, // 0x4C L key
|
|
109
|
+
/* 77 */ { VC_M, VK_NUMPAD6 }, // 0x4D M key
|
|
110
|
+
/* 78 */ { VC_N, VK_ADD }, // 0x4E N key
|
|
111
|
+
/* 79 */ { VC_O, VK_NUMPAD1 }, // 0x4F O key
|
|
112
|
+
/* 80 */ { VC_P, VK_NUMPAD2 }, // 0x50 P key
|
|
113
|
+
/* 81 */ { VC_Q, VK_NUMPAD3 }, // 0x51 Q key
|
|
114
|
+
/* 82 */ { VC_R, VK_NUMPAD0 }, // 0x52 R key
|
|
115
|
+
/* 83 */ { VC_S, VK_DECIMAL }, // 0x53 S key
|
|
116
|
+
/* 84 */ { VC_T, 0x0000 }, // 0x54 T key
|
|
117
|
+
/* 85 */ { VC_U, 0x0000 }, // 0x55 U key
|
|
118
|
+
/* 86 */ { VC_V, 0x0000 }, // 0x56 V key
|
|
119
|
+
/* 87 */ { VC_W, VK_F11 }, // 0x57 W key
|
|
120
|
+
/* 88 */ { VC_X, VK_F12 }, // 0x58 X key
|
|
121
|
+
/* 89 */ { VC_Y, 0x0000 }, // 0x59 Y key
|
|
122
|
+
/* 90 */ { VC_Z, 0x0000 }, // 0x5A Z key
|
|
123
|
+
/* 91 */ { VC_META_L, VK_F13 }, // 0x5B VK_LWIN Left Windows key (Natural keyboard)
|
|
124
|
+
/* 92 */ { VC_META_R, VK_F14 }, // 0x5C VK_RWIN Right Windows key (Natural keyboard)
|
|
125
|
+
/* 93 */ { VC_CONTEXT_MENU, VK_F15 }, // 0x5D VK_APPS Applications key (Natural keyboard)
|
|
126
|
+
/* 94 */ { VC_UNDEFINED, 0x0000 }, // 0x5E Reserved
|
|
127
|
+
/* 95 */ { VC_SLEEP, 0x0000 }, // 0x5F VK_SLEEP Computer Sleep key
|
|
128
|
+
/* 96 */ { VC_KP_0, 0x0000 }, // 0x60 VK_NUMPAD0 Numeric keypad 0 key
|
|
129
|
+
/* 97 */ { VC_KP_1, 0x0000 }, // 0x61 VK_NUMPAD1 Numeric keypad 1 key
|
|
130
|
+
/* 98 */ { VC_KP_2, 0x0000 }, // 0x62 VK_NUMPAD2 Numeric keypad 2 key
|
|
131
|
+
/* 99 */ { VC_KP_3, VK_F16 }, // 0x63 VK_NUMPAD3 Numeric keypad 3 key
|
|
132
|
+
/* 100 */ { VC_KP_4, VK_F17 }, // 0x64 VK_NUMPAD4 Numeric keypad 4 key
|
|
133
|
+
/* 101 */ { VC_KP_5, VK_F18 }, // 0x65 VK_NUMPAD5 Numeric keypad 5 key
|
|
134
|
+
/* 102 */ { VC_KP_6, VK_F19 }, // 0x66 VK_NUMPAD6 Numeric keypad 6 key
|
|
135
|
+
/* 103 */ { VC_KP_7, VK_F20 }, // 0x67 VK_NUMPAD7 Numeric keypad 7 key
|
|
136
|
+
/* 104 */ { VC_KP_8, VK_F21 }, // 0x68 VK_NUMPAD8 Numeric keypad 8 key
|
|
137
|
+
/* 105 */ { VC_KP_9, VK_F22 }, // 0x69 VK_NUMPAD9 Numeric keypad 9 key
|
|
138
|
+
/* 106 */ { VC_KP_MULTIPLY, VK_F23 }, // 0x6A VK_MULTIPLY Multiply key
|
|
139
|
+
/* 107 */ { VC_KP_ADD, VK_F24 }, // 0x6B VK_ADD Add key
|
|
140
|
+
/* 108 */ { VC_UNDEFINED, 0x0000 }, // 0x6C VK_SEPARATOR Separator key
|
|
141
|
+
/* 109 */ { VC_KP_SUBTRACT, 0x0000 }, // 0x6D VK_SUBTRACT Subtract key
|
|
142
|
+
/* 110 */ { VC_KP_SEPARATOR, 0x0000 }, // 0x6E VK_DECIMAL Decimal key
|
|
143
|
+
/* 111 */ { VC_KP_DIVIDE, 0x0000 }, // 0x6F VK_DIVIDE Divide key
|
|
144
|
+
/* 112 */ { VC_F1, VK_KANA }, // 0x70 VK_F1 F1 key
|
|
145
|
+
/* 113 */ { VC_F2, 0x0000 }, // 0x71 VK_F2 F2 key
|
|
146
|
+
/* 114 */ { VC_F3, 0x0000 }, // 0x72 VK_F3 F3 key
|
|
147
|
+
/* 115 */ { VC_F4, 0x0000 }, // 0x73 VK_F4 F4 key
|
|
148
|
+
/* 116 */ { VC_F5, 0x0000 }, // 0x74 VK_F5 F5 key
|
|
149
|
+
/* 117 */ { VC_F6, 0x0000 }, // 0x75 VK_F6 F6 key
|
|
150
|
+
/* 118 */ { VC_F7, 0x0000 }, // 0x76 VK_F7 F7 key
|
|
151
|
+
/* 119 */ { VC_F8, 0x0000 }, // 0x77 VK_F8 F8 key
|
|
152
|
+
/* 120 */ { VC_F9, 0x0000 }, // 0x78 VK_F9 F9 key
|
|
153
|
+
/* 121 */ { VC_F10, VK_KANJI }, // 0x79 VK_F10 F10 key
|
|
154
|
+
/* 122 */ { VC_F11, 0x0000 }, // 0x7A VK_F11 F11 key
|
|
155
|
+
/* 123 */ { VC_F12, 0x0000 }, // 0x7B VK_F12 F12 key
|
|
156
|
+
/* 124 */ { VC_F13, 0x0000 }, // 0x7C VK_F13 F13 key
|
|
157
|
+
/* 125 */ { VC_F14, VK_OEM_8 }, // 0x7D VK_F14 F14 key
|
|
158
|
+
/* 126 */ { VC_F15, 0x0000 }, // 0x7E VK_F15 F15 key
|
|
159
|
+
/* 127 */ { VC_F16, 0x0000 }, // 0x7F VK_F16 F16 key
|
|
160
|
+
|
|
161
|
+
// No Offset Offset (i & 0x007F) | 0x80
|
|
162
|
+
|
|
163
|
+
/* 128 */ { VC_F17, 0x0000 }, // 0x80 VK_F17 F17 key
|
|
164
|
+
/* 129 */ { VC_F18, 0x0000 }, // 0x81 VK_F18 F18 key
|
|
165
|
+
/* 130 */ { VC_F19, 0x0000 }, // 0x82 VK_F19 F19 key
|
|
166
|
+
/* 131 */ { VC_F20, 0x0000 }, // 0x83 VK_F20 F20 key
|
|
167
|
+
/* 132 */ { VC_F21, 0x0000 }, // 0x84 VK_F21 F21 key
|
|
168
|
+
/* 133 */ { VC_F22, 0x0000 }, // 0x85 VK_F22 F22 key
|
|
169
|
+
/* 134 */ { VC_F23, 0x0000 }, // 0x86 VK_F23 F23 key
|
|
170
|
+
/* 135 */ { VC_F24, 0x0000 }, // 0x87 VK_F24 F24 key
|
|
171
|
+
/* 136 */ { VC_UNDEFINED, 0x0000 }, // 0x88 Unassigned
|
|
172
|
+
/* 137 */ { VC_UNDEFINED, 0x0000 }, // 0x89 Unassigned
|
|
173
|
+
/* 138 */ { VC_UNDEFINED, 0x0000 }, // 0x8A Unassigned
|
|
174
|
+
/* 139 */ { VC_UNDEFINED, 0x0000 }, // 0x8B Unassigned
|
|
175
|
+
/* 140 */ { VC_UNDEFINED, 0x0000 }, // 0x8C Unassigned
|
|
176
|
+
/* 141 */ { VC_UNDEFINED, 0x0000 }, // 0x8D Unassigned
|
|
177
|
+
/* 142 */ { VC_UNDEFINED, 0x0000 }, // 0x8E Unassigned
|
|
178
|
+
/* 143 */ { VC_UNDEFINED, 0x0000 }, // 0x8F Unassigned
|
|
179
|
+
/* 144 */ { VC_NUM_LOCK, VK_MEDIA_PREV_TRACK }, // 0x90 VK_NUMLOCK NUM LOCK key
|
|
180
|
+
/* 145 */ { VC_SCROLL_LOCK, 0x0000 }, // 0x91 VK_SCROLL SCROLL LOCK key
|
|
181
|
+
/* 146 */ { VC_UNDEFINED, 0x0000 }, // 0x92 OEM specific
|
|
182
|
+
/* 147 */ { VC_UNDEFINED, 0x0000 }, // 0x93 OEM specific
|
|
183
|
+
/* 148 */ { VC_UNDEFINED, 0x0000 }, // 0x94 OEM specific
|
|
184
|
+
/* 149 */ { VC_UNDEFINED, 0x0000 }, // 0x95 OEM specific
|
|
185
|
+
/* 150 */ { VC_UNDEFINED, 0x0000 }, // 0x96 OEM specific
|
|
186
|
+
/* 151 */ { VC_UNDEFINED, 0x0000 }, // 0x97 Unassigned
|
|
187
|
+
/* 152 */ { VC_UNDEFINED, 0x0000 }, // 0x98 Unassigned
|
|
188
|
+
/* 153 */ { VC_UNDEFINED, VK_MEDIA_NEXT_TRACK }, // 0x99 Unassigned
|
|
189
|
+
/* 154 */ { VC_UNDEFINED, 0x0000 }, // 0x9A Unassigned
|
|
190
|
+
/* 155 */ { VC_UNDEFINED, 0x0000 }, // 0x9B Unassigned
|
|
191
|
+
/* 156 */ { VC_UNDEFINED, 0x0000 }, // 0x9C Unassigned
|
|
192
|
+
/* 157 */ { VC_UNDEFINED, VK_RCONTROL }, // 0x9D Unassigned
|
|
193
|
+
/* 158 */ { VC_UNDEFINED, 0x0000 }, // 0x9E Unassigned
|
|
194
|
+
/* 159 */ { VC_UNDEFINED, 0x0000 }, // 0x9F Unassigned
|
|
195
|
+
/* 160 */ { VC_SHIFT_L, VK_VOLUME_MUTE }, // 0xA0 VK_LSHIFT Left SHIFT key
|
|
196
|
+
/* 161 */ { VC_SHIFT_R, VK_LAUNCH_APP2 }, // 0xA1 VK_RSHIFT Right SHIFT key
|
|
197
|
+
/* 162 */ { VC_CONTROL_L, VK_MEDIA_PLAY_PAUSE }, // 0xA2 VK_LCONTROL Left CONTROL key
|
|
198
|
+
/* 163 */ { VC_CONTROL_R, 0x0000 }, // 0xA3 VK_RCONTROL Right CONTROL key
|
|
199
|
+
/* 164 */ { VC_ALT_L, VK_MEDIA_STOP }, // 0xA4 VK_LMENU Left MENU key
|
|
200
|
+
/* 165 */ { VC_ALT_R, 0x0000 }, // 0xA5 VK_RMENU Right MENU key
|
|
201
|
+
/* 166 */ { VC_BROWSER_BACK, 0x0000 }, // 0xA6 VK_BROWSER_BACK Browser Back key
|
|
202
|
+
/* 167 */ { VC_BROWSER_FORWARD, 0x0000 }, // 0xA7 VK_BROWSER_FORWARD Browser Forward key
|
|
203
|
+
/* 168 */ { VC_BROWSER_REFRESH, 0x0000 }, // 0xA8 VK_BROWSER_REFRESH Browser Refresh key
|
|
204
|
+
/* 169 */ { VC_BROWSER_STOP, 0x0000 }, // 0xA9 VK_BROWSER_STOP Browser Stop key
|
|
205
|
+
/* 170 */ { VC_BROWSER_SEARCH, 0x0000 }, // 0xAA VK_BROWSER_SEARCH Browser Search key
|
|
206
|
+
/* 171 */ { VC_BROWSER_FAVORITES, 0x0000 }, // 0xAB VK_BROWSER_FAVORITES Browser Favorites key
|
|
207
|
+
/* 172 */ { VC_BROWSER_HOME, 0x0000 }, // 0xAC VK_BROWSER_HOME Browser Start and Home key
|
|
208
|
+
/* 173 */ { VC_VOLUME_MUTE, 0x0000 }, // 0xAD VK_VOLUME_MUTE Volume Mute key
|
|
209
|
+
/* 174 */ { VC_VOLUME_DOWN, VK_VOLUME_DOWN }, // 0xAE VK_VOLUME_DOWN Volume Down key
|
|
210
|
+
/* 175 */ { VC_VOLUME_UP, 0x0000 }, // 0xAF VK_VOLUME_UP Volume Up key
|
|
211
|
+
/* 176 */ { VC_MEDIA_NEXT, VK_VOLUME_UP }, // 0xB0 VK_MEDIA_NEXT_TRACK Next Track key
|
|
212
|
+
/* 177 */ { VC_MEDIA_PREVIOUS, 0x0000 }, // 0xB1 VK_MEDIA_PREV_TRACK Previous Track key
|
|
213
|
+
/* 178 */ { VC_MEDIA_STOP, VK_BROWSER_HOME }, // 0xB2 VK_MEDIA_STOP Stop Media key
|
|
214
|
+
/* 179 */ { VC_MEDIA_PLAY, 0x0000 }, // 0xB3 VK_MEDIA_PLAY_PAUSE Play/Pause Media key
|
|
215
|
+
/* 180 */ { VC_UNDEFINED, 0x0000 }, // 0xB4 VK_LAUNCH_MAIL Start Mail key
|
|
216
|
+
/* 181 */ { VC_MEDIA_SELECT, VK_DIVIDE }, // 0xB5 VK_LAUNCH_MEDIA_SELECT Select Media key
|
|
217
|
+
/* 182 */ { VC_APP_MAIL, 0x0000 }, // 0xB6 VK_LAUNCH_APP1 Start Application 1 key
|
|
218
|
+
/* 183 */ { VC_APP_CALCULATOR, VK_SNAPSHOT }, // 0xB7 VK_LAUNCH_APP2 Start Application 2 key
|
|
219
|
+
/* 184 */ { VC_UNDEFINED, VK_RMENU }, // 0xB8 Reserved
|
|
220
|
+
/* 185 */ { VC_UNDEFINED, 0x0000 }, // 0xB9 Reserved
|
|
221
|
+
/* 186 */ { VC_SEMICOLON, 0x0000 }, // 0xBA VK_OEM_1 Varies by keyboard. For the US standard keyboard, the ';:' key
|
|
222
|
+
/* 187 */ { VC_EQUALS, 0x0000 }, // 0xBB VK_OEM_PLUS For any country/region, the '+' key
|
|
223
|
+
/* 188 */ { VC_COMMA, 0x00E6 }, // 0xBC VK_OEM_COMMA For any country/region, the ',' key
|
|
224
|
+
/* 189 */ { VC_MINUS, 0x0000 }, // 0xBD VK_OEM_MINUS For any country/region, the '-' key
|
|
225
|
+
/* 190 */ { VC_PERIOD, 0x0000 }, // 0xBE VK_OEM_PERIOD For any country/region, the '.' key
|
|
226
|
+
/* 191 */ { VC_SLASH, 0x0000 }, // 0xBF VK_OEM_2 Varies by keyboard. For the US standard keyboard, the '/?' key
|
|
227
|
+
/* 192 */ { VC_BACKQUOTE, 0x0000 }, // 0xC0 VK_OEM_3 Varies by keyboard. For the US standard keyboard, the '`~' key
|
|
228
|
+
/* 193 */ { VC_UNDEFINED, 0x0000 }, // 0xC1 Reserved
|
|
229
|
+
/* 194 */ { VC_UNDEFINED, 0x0000 }, // 0xC2 Reserved
|
|
230
|
+
/* 195 */ { VC_UNDEFINED, 0x0000 }, // 0xC3 Reserved
|
|
231
|
+
/* 196 */ { VC_UNDEFINED, 0x0000 }, // 0xC4 Reserved
|
|
232
|
+
/* 197 */ { VC_UNDEFINED, VK_PAUSE }, // 0xC5 Reserved
|
|
233
|
+
/* 198 */ { VC_UNDEFINED, 0x0000 }, // 0xC6 Reserved
|
|
234
|
+
/* 199 */ { VC_UNDEFINED, VK_HOME }, // 0xC7 Reserved
|
|
235
|
+
/* 200 */ { VC_UNDEFINED, VK_UP }, // 0xC8 Reserved
|
|
236
|
+
/* 201 */ { VC_UNDEFINED, VK_PRIOR }, // 0xC9 Reserved
|
|
237
|
+
/* 202 */ { VC_UNDEFINED, 0x0000 }, // 0xCA Reserved
|
|
238
|
+
/* 203 */ { VC_UNDEFINED, VK_LEFT }, // 0xCB Reserved
|
|
239
|
+
/* 204 */ { VC_UNDEFINED, VK_CLEAR }, // 0xCC Reserved
|
|
240
|
+
/* 205 */ { VC_UNDEFINED, VK_RIGHT }, // 0xCD Reserved
|
|
241
|
+
/* 206 */ { VC_UNDEFINED, 0x0000 }, // 0xCE Reserved
|
|
242
|
+
/* 207 */ { VC_UNDEFINED, VK_END }, // 0xCF Reserved
|
|
243
|
+
/* 208 */ { VC_UNDEFINED, VK_DOWN }, // 0xD0 Reserved
|
|
244
|
+
/* 209 */ { VC_UNDEFINED, VK_NEXT }, // 0xD1 Reserved
|
|
245
|
+
/* 210 */ { VC_UNDEFINED, VK_INSERT }, // 0xD2 Reserved
|
|
246
|
+
/* 211 */ { VC_UNDEFINED, VK_DELETE }, // 0xD3 Reserved
|
|
247
|
+
/* 212 */ { VC_UNDEFINED, 0x0000 }, // 0xD4 Reserved
|
|
248
|
+
/* 213 */ { VC_UNDEFINED, 0x0000 }, // 0xD5 Reserved
|
|
249
|
+
/* 214 */ { VC_UNDEFINED, 0x0000 }, // 0xD6 Reserved
|
|
250
|
+
/* 215 */ { VC_UNDEFINED, 0x0000 }, // 0xD7 Reserved
|
|
251
|
+
/* 216 */ { VC_UNDEFINED, 0x0000 }, // 0xD8 Unassigned
|
|
252
|
+
/* 217 */ { VC_UNDEFINED, 0x0000 }, // 0xD9 Unassigned
|
|
253
|
+
/* 218 */ { VC_UNDEFINED, 0x0000 }, // 0xDA Unassigned
|
|
254
|
+
/* 219 */ { VC_OPEN_BRACKET, VK_LWIN }, // 0xDB VK_OEM_4 Varies by keyboard. For the US standard keyboard, the '[{' key
|
|
255
|
+
/* 220 */ { VC_BACK_SLASH, VK_RWIN }, // 0xDC VK_OEM_5 Varies by keyboard. For the US standard keyboard, the '\|' key
|
|
256
|
+
/* 221 */ { VC_CLOSE_BRACKET, VK_APPS }, // 0xDD VK_OEM_6 Varies by keyboard. For the US standard keyboard, the ']}' key
|
|
257
|
+
/* 222 */ { VC_QUOTE, 0x0000 }, // 0xDE VK_OEM_7 Varies by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key
|
|
258
|
+
/* 223 */ { VC_YEN, VK_SLEEP }, // 0xDF VK_OEM_8 Varies by keyboard.
|
|
259
|
+
/* 224 */ { VC_UNDEFINED, 0x0000 }, // 0xE0 Reserved
|
|
260
|
+
/* 225 */ { VC_UNDEFINED, 0x0000 }, // 0xE1 OEM specific
|
|
261
|
+
/* 226 */ { VC_LESSER_GREATER, VK_OEM_102 }, // 0xE2 VK_OEM_102 Either the angle bracket key or the backslash key on the RT 102-key keyboard
|
|
262
|
+
/* 227 */ { VC_UNDEFINED, 0x0000 }, // 0xE3 OEM specific
|
|
263
|
+
/* 228 */ { VC_UNDEFINED, 0x00E5 }, // 0xE4 VC_APP_PICTURES OEM specific
|
|
264
|
+
/* 229 */ { VC_APP_PICTURES, VK_BROWSER_SEARCH }, // 0xE5 VK_PROCESSKEY IME PROCESS key
|
|
265
|
+
/* 230 */ { VC_APP_MUSIC, VK_BROWSER_FAVORITES }, // 0xE6 OEM specific
|
|
266
|
+
/* 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.
|
|
267
|
+
/* 232 */ { VC_UNDEFINED, VK_BROWSER_STOP }, // 0xE8 Unassigned
|
|
268
|
+
/* 233 */ { VC_UNDEFINED, VK_BROWSER_FORWARD }, // 0xE9 OEM specific
|
|
269
|
+
/* 234 */ { VC_UNDEFINED, VK_BROWSER_BACK }, // 0xEA OEM specific
|
|
270
|
+
/* 235 */ { VC_UNDEFINED, 0x0000 }, // 0xEB OEM specific
|
|
271
|
+
/* 236 */ { VC_UNDEFINED, VK_LAUNCH_APP1 }, // 0xEC OEM specific
|
|
272
|
+
/* 237 */ { VC_UNDEFINED, VK_LAUNCH_MEDIA_SELECT }, // 0xED OEM specific
|
|
273
|
+
/* 238 */ { VC_UNDEFINED, 0x0000 }, // 0xEE OEM specific
|
|
274
|
+
/* 239 */ { VC_UNDEFINED, 0x0000 }, // 0xEF OEM specific
|
|
275
|
+
/* 240 */ { VC_UNDEFINED, 0x0000 }, // 0xF0 OEM specific
|
|
276
|
+
/* 241 */ { VC_UNDEFINED, 0x0000 }, // 0xF1 OEM specific
|
|
277
|
+
/* 242 */ { VC_UNDEFINED, 0x0000 }, // 0xF2 OEM specific
|
|
278
|
+
/* 243 */ { VC_UNDEFINED, 0x0000 }, // 0xF3 OEM specific
|
|
279
|
+
/* 244 */ { VC_UNDEFINED, 0x0000 }, // 0xF4 OEM specific
|
|
280
|
+
/* 245 */ { VC_UNDEFINED, 0x0000 }, // 0xF5 OEM specific
|
|
281
|
+
/* 246 */ { VC_UNDEFINED, 0x0000 }, // 0xF6 VK_ATTN Attn key
|
|
282
|
+
/* 247 */ { VC_UNDEFINED, 0x0000 }, // 0xF7 VK_CRSEL CrSel key
|
|
283
|
+
/* 248 */ { VC_UNDEFINED, 0x0000 }, // 0xF8 VK_EXSEL ExSel key
|
|
284
|
+
/* 249 */ { VC_UNDEFINED, 0x0000 }, // 0xF9 VK_EREOF Erase EOF key
|
|
285
|
+
/* 250 */ { VC_UNDEFINED, 0x0000 }, // 0xFA VK_PLAY Play key
|
|
286
|
+
/* 251 */ { VC_UNDEFINED, 0x0000 }, // 0xFB VK_ZOOM Zoom key
|
|
287
|
+
/* 252 */ { VC_UNDEFINED, 0x0000 }, // 0xFC VK_NONAME Reserved
|
|
288
|
+
/* 253 */ { VC_UNDEFINED, 0x0000 }, // 0xFD
|
|
289
|
+
/* 254 */ { VC_CLEAR, 0x0000 }, // 0xFE VK_OEM_CLEAR Clear key
|
|
290
|
+
/* 255 */ { VC_UNDEFINED, 0x0000 } // 0xFE Unassigned
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
unsigned short keycode_to_scancode(DWORD vk_code, DWORD flags) {
|
|
294
|
+
unsigned short scancode = VC_UNDEFINED;
|
|
295
|
+
|
|
296
|
+
// Check the vk_code is in range.
|
|
297
|
+
// NOTE vk_code >= 0 is assumed because DWORD is unsigned.
|
|
298
|
+
if (vk_code < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[0])) {
|
|
299
|
+
scancode = keycode_scancode_table[vk_code][0];
|
|
300
|
+
|
|
301
|
+
if (!(flags & LLKHF_EXTENDED)) {
|
|
302
|
+
logger(LOG_LEVEL_DEBUG, "%s [%u]: Using extended lookup for vk_code: %li\n",
|
|
303
|
+
__FUNCTION__, __LINE__, vk_code);
|
|
304
|
+
|
|
305
|
+
switch (vk_code) {
|
|
306
|
+
case VK_PRIOR:
|
|
307
|
+
case VK_NEXT:
|
|
308
|
+
case VK_END:
|
|
309
|
+
case VK_HOME:
|
|
310
|
+
case VK_LEFT:
|
|
311
|
+
case VK_UP:
|
|
312
|
+
case VK_RIGHT:
|
|
313
|
+
case VK_DOWN:
|
|
314
|
+
|
|
315
|
+
case VK_INSERT:
|
|
316
|
+
case VK_DELETE:
|
|
317
|
+
scancode |= 0xEE00;
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
logger(LOG_LEVEL_DEBUG, "%s [%u]: Using normal lookup for vk_code: %li\n",
|
|
322
|
+
__FUNCTION__, __LINE__, vk_code);
|
|
323
|
+
|
|
324
|
+
switch (vk_code) {
|
|
325
|
+
case VK_RETURN:
|
|
326
|
+
scancode |= 0x0E00;
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return scancode;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
DWORD scancode_to_keycode(unsigned short scancode) {
|
|
336
|
+
unsigned short keycode = 0x0000;
|
|
337
|
+
|
|
338
|
+
// Check the vk_code is in range.
|
|
339
|
+
// NOTE vk_code >= 0 is assumed because the scancode is unsigned.
|
|
340
|
+
if (scancode < 128) {
|
|
341
|
+
keycode = keycode_scancode_table[scancode][1];
|
|
342
|
+
} else {
|
|
343
|
+
// Calculate the upper offset based on the lower half of the scancode + 128.
|
|
344
|
+
unsigned short int i = (scancode & 0x007F) | 0x80;
|
|
345
|
+
|
|
346
|
+
if (i < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[1])) {
|
|
347
|
+
keycode = keycode_scancode_table[i][1];
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return keycode;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/* Track the amount of vertical and horizontal rotation between "clicks."
|
|
355
|
+
* This is between mouse wheel delta. */
|
|
356
|
+
static int16_t v_rotation, h_rotation;
|
|
357
|
+
|
|
358
|
+
int16_t get_scroll_wheel_rotation(DWORD data, uint8_t direction) {
|
|
359
|
+
int16_t value;
|
|
360
|
+
|
|
361
|
+
/* Delta GET_WHEEL_DELTA_WPARAM(mshook->mouseData)
|
|
362
|
+
* A positive value indicates that the wheel was rotated
|
|
363
|
+
* forward, away from the user; a negative value indicates that
|
|
364
|
+
* the wheel was rotated backward, toward the user. One wheel
|
|
365
|
+
* click is defined as WHEEL_DELTA, which is 120. */
|
|
366
|
+
if (direction == WHEEL_VERTICAL_DIRECTION) {
|
|
367
|
+
v_rotation += (int16_t) GET_WHEEL_DELTA_WPARAM(data);
|
|
368
|
+
// Vertical direction needs to be inverted on Windows to conform with other platforms.
|
|
369
|
+
value = (int16_t) v_rotation / (-1 * WHEEL_DELTA);
|
|
370
|
+
v_rotation %= WHEEL_DELTA;
|
|
371
|
+
} else {
|
|
372
|
+
h_rotation += (int16_t) GET_WHEEL_DELTA_WPARAM(data);
|
|
373
|
+
value = (int16_t) h_rotation / WHEEL_DELTA;
|
|
374
|
+
h_rotation %= WHEEL_DELTA;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return value;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/***********************************************************************
|
|
381
|
+
* The following code is based on code provided by Marc-André Moreau
|
|
382
|
+
* to work around a failure to support dead keys in the ToUnicode() API.
|
|
383
|
+
* According to the author some parts were taken directly from
|
|
384
|
+
* Microsoft's kbd.h header file that is shipped with the Windows Driver
|
|
385
|
+
* Development Kit.
|
|
386
|
+
*
|
|
387
|
+
* The original code was substantially modified to provide the following:
|
|
388
|
+
* 1) More dynamic code structure.
|
|
389
|
+
* 2) Support for compilers that do not implement _ptr64 (GCC / LLVM).
|
|
390
|
+
* 3) Support for Wow64 at runtime via 32-bit binary.
|
|
391
|
+
* 4) Support for contextual language switching.
|
|
392
|
+
*
|
|
393
|
+
* I have contacted Marc-André Moreau who has granted permission for
|
|
394
|
+
* his original source code to be used under the Public Domain. Although
|
|
395
|
+
* the libUIOHook library as a whole is currently covered under the LGPLv3,
|
|
396
|
+
* please feel free to use and learn from the source code contained in the
|
|
397
|
+
* following functions under the terms of the Public Domain.
|
|
398
|
+
*
|
|
399
|
+
* For further reading and the original code, please visit:
|
|
400
|
+
* http://legacy.docdroppers.org/wiki/index.php?title=Writing_Keyloggers
|
|
401
|
+
* http://www.techmantras.com/content/writing-keyloggers-full-length-tutorial
|
|
402
|
+
*
|
|
403
|
+
***********************************************************************/
|
|
404
|
+
|
|
405
|
+
// Structure and pointers for the keyboard locale cache.
|
|
406
|
+
typedef struct _KeyboardLocale {
|
|
407
|
+
HKL id; // Locale ID
|
|
408
|
+
HINSTANCE library; // Keyboard DLL instance.
|
|
409
|
+
PVK_TO_BIT pVkToBit; // Pointers struct arrays.
|
|
410
|
+
PVK_TO_WCHAR_TABLE pVkToWcharTable;
|
|
411
|
+
PDEADKEY pDeadKey;
|
|
412
|
+
struct _KeyboardLocale* next;
|
|
413
|
+
} KeyboardLocale;
|
|
414
|
+
|
|
415
|
+
static KeyboardLocale* locale_first = NULL;
|
|
416
|
+
static KeyboardLocale* locale_current = NULL;
|
|
417
|
+
static WCHAR deadChar = WCH_NONE;
|
|
418
|
+
|
|
419
|
+
// Amount of pointer padding to apply for Wow64 instances.
|
|
420
|
+
static unsigned short int ptr_padding = 0;
|
|
421
|
+
|
|
422
|
+
#if defined(_WIN32) && !defined(_WIN64)
|
|
423
|
+
// Small function to check and see if we are executing under Wow64.
|
|
424
|
+
static BOOL is_wow64() {
|
|
425
|
+
BOOL status = FALSE;
|
|
426
|
+
|
|
427
|
+
LPFN_ISWOW64PROCESS pIsWow64Process = (LPFN_ISWOW64PROCESS)
|
|
428
|
+
GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process");
|
|
429
|
+
|
|
430
|
+
if (pIsWow64Process != NULL) {
|
|
431
|
+
HANDLE current_proc = GetCurrentProcess();
|
|
432
|
+
|
|
433
|
+
if (!pIsWow64Process(current_proc, &status)) {
|
|
434
|
+
status = FALSE;
|
|
435
|
+
|
|
436
|
+
logger(LOG_LEVEL_DEBUG, "%s [%u]: pIsWow64Process(%#p, %#p) failed!\n",
|
|
437
|
+
__FUNCTION__, __LINE__, current_proc, &status);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return status;
|
|
442
|
+
}
|
|
443
|
+
#endif
|
|
444
|
+
|
|
445
|
+
// Locate the DLL that contains the current keyboard layout.
|
|
446
|
+
static int get_keyboard_layout_file(char *layoutFile, DWORD bufferSize) {
|
|
447
|
+
int status = UIOHOOK_FAILURE;
|
|
448
|
+
HKEY hKey;
|
|
449
|
+
DWORD varType = REG_SZ;
|
|
450
|
+
|
|
451
|
+
char kbdName[KL_NAMELENGTH * 4];
|
|
452
|
+
if (GetKeyboardLayoutName(kbdName)) {
|
|
453
|
+
logger(LOG_LEVEL_DEBUG, "%s [%u]: Found keyboard layout \"%s\".\n",
|
|
454
|
+
__FUNCTION__, __LINE__, kbdName);
|
|
455
|
+
|
|
456
|
+
const char *regPrefix = "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\";
|
|
457
|
+
size_t regPathSize = strlen(regPrefix) + strlen(kbdName) + 1;
|
|
458
|
+
char *regPath = malloc(regPathSize);
|
|
459
|
+
if (regPath != NULL) {
|
|
460
|
+
strcpy_s(regPath, regPathSize, regPrefix);
|
|
461
|
+
strcat_s(regPath, regPathSize, kbdName);
|
|
462
|
+
|
|
463
|
+
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR) regPath, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
|
|
464
|
+
const char *regKey = "Layout File";
|
|
465
|
+
if (RegQueryValueEx(hKey, regKey, NULL, &varType, (LPBYTE) layoutFile, &bufferSize) == ERROR_SUCCESS) {
|
|
466
|
+
RegCloseKey(hKey);
|
|
467
|
+
status = UIOHOOK_SUCCESS;
|
|
468
|
+
} else {
|
|
469
|
+
logger(LOG_LEVEL_WARN, "%s [%u]: RegOpenKeyEx failed to open key: \"%s\"!\n",
|
|
470
|
+
__FUNCTION__, __LINE__, regKey);
|
|
471
|
+
}
|
|
472
|
+
} else {
|
|
473
|
+
logger(LOG_LEVEL_WARN, "%s [%u]: RegOpenKeyEx failed to open key: \"%s\"!\n",
|
|
474
|
+
__FUNCTION__, __LINE__, regPath);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
free(regPath);
|
|
478
|
+
} else {
|
|
479
|
+
logger(LOG_LEVEL_WARN, "%s [%u]: malloc(%u) failed!\n",
|
|
480
|
+
__FUNCTION__, __LINE__, regPathSize);
|
|
481
|
+
}
|
|
482
|
+
} else {
|
|
483
|
+
logger(LOG_LEVEL_WARN, "%s [%u]: GetKeyboardLayoutName() failed!\n",
|
|
484
|
+
__FUNCTION__, __LINE__);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return status;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Returns the number of locales that were loaded.
|
|
491
|
+
static int refresh_locale_list() {
|
|
492
|
+
int count = 0;
|
|
493
|
+
|
|
494
|
+
// Get the number of layouts the user has activated.
|
|
495
|
+
int hkl_size = GetKeyboardLayoutList(0, NULL);
|
|
496
|
+
if (hkl_size > 0) {
|
|
497
|
+
logger(LOG_LEVEL_DEBUG, "%s [%u]: GetKeyboardLayoutList(0, NULL) found %i layouts.\n",
|
|
498
|
+
__FUNCTION__, __LINE__, hkl_size);
|
|
499
|
+
|
|
500
|
+
// Get the thread id that currently has focus for our default.
|
|
501
|
+
DWORD focus_pid = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
|
|
502
|
+
HKL hlk_focus = GetKeyboardLayout(focus_pid);
|
|
503
|
+
HKL hlk_default = GetKeyboardLayout(0);
|
|
504
|
+
HKL *hkl_list = malloc(sizeof(HKL) * hkl_size);
|
|
505
|
+
|
|
506
|
+
int new_size = GetKeyboardLayoutList(hkl_size, hkl_list);
|
|
507
|
+
if (new_size > 0) {
|
|
508
|
+
if (new_size != hkl_size) {
|
|
509
|
+
logger(LOG_LEVEL_WARN, "%s [%u]: Locale size mismatch! "
|
|
510
|
+
"Expected %i, received %i!\n",
|
|
511
|
+
__FUNCTION__, __LINE__, hkl_size, new_size);
|
|
512
|
+
} else {
|
|
513
|
+
logger(LOG_LEVEL_DEBUG, "%s [%u]: Received %i locales.\n",
|
|
514
|
+
__FUNCTION__, __LINE__, new_size);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
KeyboardLocale* locale_previous = NULL;
|
|
518
|
+
KeyboardLocale* locale_item = locale_first;
|
|
519
|
+
|
|
520
|
+
// Go though the linked list and remove KeyboardLocale's that are
|
|
521
|
+
// no longer loaded.
|
|
522
|
+
while (locale_item != NULL) {
|
|
523
|
+
// Check to see if the old HKL is in the new list.
|
|
524
|
+
bool is_loaded = false;
|
|
525
|
+
for (int i = 0; i < new_size && !is_loaded; i++) {
|
|
526
|
+
if (locale_item->id == hkl_list[i]) {
|
|
527
|
+
// Flag and jump out of the loop.
|
|
528
|
+
hkl_list[i] = NULL;
|
|
529
|
+
is_loaded = true;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
if (is_loaded) {
|
|
535
|
+
logger(LOG_LEVEL_DEBUG, "%s [%u]: Found locale ID %#p in the cache.\n",
|
|
536
|
+
__FUNCTION__, __LINE__, locale_item->id);
|
|
537
|
+
|
|
538
|
+
// Set the previous local to the current locale.
|
|
539
|
+
locale_previous = locale_item;
|
|
540
|
+
|
|
541
|
+
// Check and see if the locale is our current active locale.
|
|
542
|
+
if (locale_item->id == hlk_focus) {
|
|
543
|
+
locale_current = locale_item;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
count++;
|
|
547
|
+
} else {
|
|
548
|
+
logger(LOG_LEVEL_DEBUG, "%s [%u]: Removing locale ID %#p from the cache.\n",
|
|
549
|
+
__FUNCTION__, __LINE__, locale_item->id);
|
|
550
|
+
|
|
551
|
+
// If the old id is not in the new list, remove it.
|
|
552
|
+
locale_previous->next = locale_item->next;
|
|
553
|
+
|
|
554
|
+
// Make sure the locale_current points NULL or something valid.
|
|
555
|
+
if (locale_item == locale_current) {
|
|
556
|
+
locale_current = NULL;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Free the memory used by locale_item;
|
|
560
|
+
free(locale_item);
|
|
561
|
+
|
|
562
|
+
// Set the item to the pervious item to guarantee a next.
|
|
563
|
+
locale_item = locale_previous;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Iterate to the next linked list item.
|
|
567
|
+
locale_item = locale_item->next;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
// Insert anything new into the linked list.
|
|
572
|
+
for (int i = 0; i < new_size; i++) {
|
|
573
|
+
// Check to see if the item was already in the list.
|
|
574
|
+
if (hkl_list[i] != NULL) {
|
|
575
|
+
// Set the active keyboard layout for this thread to the HKL.
|
|
576
|
+
ActivateKeyboardLayout(hkl_list[i], 0x00);
|
|
577
|
+
|
|
578
|
+
// Try to pull the current keyboard layout DLL from the registry.
|
|
579
|
+
char layoutFile[MAX_PATH];
|
|
580
|
+
if (get_keyboard_layout_file(layoutFile, MAX_PATH) == UIOHOOK_SUCCESS) {
|
|
581
|
+
// You can't trust the %SYSPATH%, look it up manually.
|
|
582
|
+
char systemDirectory[MAX_PATH];
|
|
583
|
+
if (GetSystemDirectory(systemDirectory, MAX_PATH) != 0) {
|
|
584
|
+
char kbdLayoutFilePath[MAX_PATH];
|
|
585
|
+
snprintf(kbdLayoutFilePath, MAX_PATH, "%s\\%s", systemDirectory, layoutFile);
|
|
586
|
+
|
|
587
|
+
logger(LOG_LEVEL_DEBUG, "%s [%u]: Loading layout for %#p: %s.\n",
|
|
588
|
+
__FUNCTION__, __LINE__, hkl_list[i], layoutFile);
|
|
589
|
+
|
|
590
|
+
// Create the new locale item.
|
|
591
|
+
locale_item = malloc(sizeof(KeyboardLocale));
|
|
592
|
+
locale_item->id = hkl_list[i];
|
|
593
|
+
locale_item->library = LoadLibrary(kbdLayoutFilePath);
|
|
594
|
+
|
|
595
|
+
#if __GNUC__
|
|
596
|
+
#pragma GCC diagnostic push
|
|
597
|
+
#pragma GCC diagnostic ignored "-Wcast-function-type"
|
|
598
|
+
#endif
|
|
599
|
+
// Get the function pointer from the library to get the keyboard layer descriptor.
|
|
600
|
+
KbdLayerDescriptor pKbdLayerDescriptor = (KbdLayerDescriptor) GetProcAddress(locale_item->library, "KbdLayerDescriptor");
|
|
601
|
+
#if __GNUC__
|
|
602
|
+
#pragma GCC diagnostic pop
|
|
603
|
+
#endif
|
|
604
|
+
|
|
605
|
+
if (pKbdLayerDescriptor != NULL) {
|
|
606
|
+
PKBDTABLES pKbd = pKbdLayerDescriptor();
|
|
607
|
+
|
|
608
|
+
// Store the memory address of the following 3 structures.
|
|
609
|
+
BYTE *base = (BYTE *) pKbd;
|
|
610
|
+
|
|
611
|
+
// First element of each structure, no offset adjustment needed.
|
|
612
|
+
locale_item->pVkToBit = pKbd->pCharModifiers->pVkToBit;
|
|
613
|
+
|
|
614
|
+
// Second element of pKbd, +4 byte offset on wow64.
|
|
615
|
+
locale_item->pVkToWcharTable = *((PVK_TO_WCHAR_TABLE *) (base + offsetof(KBDTABLES, pVkToWcharTable) + ptr_padding));
|
|
616
|
+
|
|
617
|
+
// Third element of pKbd, +8 byte offset on wow64.
|
|
618
|
+
locale_item->pDeadKey = *((PDEADKEY *) (base + offsetof(KBDTABLES, pDeadKey) + (ptr_padding * 2)));
|
|
619
|
+
|
|
620
|
+
// This will always be added to the end of the list.
|
|
621
|
+
locale_item->next = NULL;
|
|
622
|
+
|
|
623
|
+
// Insert the item into the linked list.
|
|
624
|
+
if (locale_previous == NULL) {
|
|
625
|
+
// If nothing came before, the list is empty.
|
|
626
|
+
locale_first = locale_item;
|
|
627
|
+
} else {
|
|
628
|
+
// Append the new locale to the end of the list.
|
|
629
|
+
locale_previous->next = locale_item;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Check and see if the locale is our current active locale.
|
|
633
|
+
if (locale_item->id == hlk_focus) {
|
|
634
|
+
locale_current = locale_item;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Set the pervious locale item to the new one.
|
|
638
|
+
locale_previous = locale_item;
|
|
639
|
+
|
|
640
|
+
count++;
|
|
641
|
+
} else {
|
|
642
|
+
logger(LOG_LEVEL_ERROR,
|
|
643
|
+
"%s [%u]: GetProcAddress() failed for KbdLayerDescriptor!\n",
|
|
644
|
+
__FUNCTION__, __LINE__);
|
|
645
|
+
|
|
646
|
+
FreeLibrary(locale_item->library);
|
|
647
|
+
free(locale_item);
|
|
648
|
+
locale_item = NULL;
|
|
649
|
+
}
|
|
650
|
+
} else {
|
|
651
|
+
logger(LOG_LEVEL_ERROR,
|
|
652
|
+
"%s [%u]: GetSystemDirectory() failed!\n",
|
|
653
|
+
__FUNCTION__, __LINE__);
|
|
654
|
+
}
|
|
655
|
+
} else {
|
|
656
|
+
logger(LOG_LEVEL_ERROR,
|
|
657
|
+
"%s [%u]: Could not find keyboard map for locale %#p!\n",
|
|
658
|
+
__FUNCTION__, __LINE__, hkl_list[i]);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
} else {
|
|
663
|
+
logger(LOG_LEVEL_ERROR,
|
|
664
|
+
"%s [%u]: GetKeyboardLayoutList() failed!\n",
|
|
665
|
+
__FUNCTION__, __LINE__);
|
|
666
|
+
|
|
667
|
+
// TODO Try and recover by using the current layout.
|
|
668
|
+
// Hint: Use locale_id instead of hkl_list[i] in the loop above.
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
free(hkl_list);
|
|
672
|
+
ActivateKeyboardLayout(hlk_default, 0x00);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return count;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Returns the number of chars written to the buffer.
|
|
679
|
+
SIZE_T keycode_to_unicode(DWORD keycode, PWCHAR buffer, SIZE_T size) {
|
|
680
|
+
// Get the thread id that currently has focus and ask for its current locale.
|
|
681
|
+
DWORD focus_pid = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
|
|
682
|
+
HKL locale_id = GetKeyboardLayout(focus_pid);
|
|
683
|
+
if (locale_id == NULL) {
|
|
684
|
+
// Default to the current thread's layout if the focused window fails.
|
|
685
|
+
locale_id = GetKeyboardLayout(0);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// If the current Locale is not the new locale, search the linked list.
|
|
689
|
+
if (locale_current == NULL || locale_current->id != locale_id) {
|
|
690
|
+
locale_current = NULL;
|
|
691
|
+
KeyboardLocale* locale_item = locale_first;
|
|
692
|
+
|
|
693
|
+
// Search the linked list...
|
|
694
|
+
while (locale_item != NULL && locale_item->id != locale_id) {
|
|
695
|
+
locale_item = locale_item->next;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// You may already be a winner!
|
|
699
|
+
if (locale_item != NULL && locale_item->id == locale_id) {
|
|
700
|
+
logger(LOG_LEVEL_DEBUG,
|
|
701
|
+
"%s [%u]: Activating keyboard layout %#p.\n",
|
|
702
|
+
__FUNCTION__, __LINE__, locale_item->id);
|
|
703
|
+
|
|
704
|
+
// Switch the current locale.
|
|
705
|
+
locale_current = locale_item;
|
|
706
|
+
locale_item = NULL;
|
|
707
|
+
|
|
708
|
+
// If they layout changes the dead key state needs to be reset.
|
|
709
|
+
// This is consistent with the way Windows handles locale changes.
|
|
710
|
+
deadChar = WCH_NONE;
|
|
711
|
+
} else {
|
|
712
|
+
logger(LOG_LEVEL_DEBUG,
|
|
713
|
+
"%s [%u]: Refreshing locale cache.\n",
|
|
714
|
+
__FUNCTION__, __LINE__);
|
|
715
|
+
|
|
716
|
+
refresh_locale_list();
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// Initialize to empty.
|
|
721
|
+
SIZE_T charCount = 0;
|
|
722
|
+
// buffer[i] = WCH_NONE;
|
|
723
|
+
|
|
724
|
+
// Check and make sure the Unicode helper was loaded.
|
|
725
|
+
if (locale_current != NULL) {
|
|
726
|
+
logger(LOG_LEVEL_DEBUG,
|
|
727
|
+
"%s [%u]: Using keyboard layout %#p.\n",
|
|
728
|
+
__FUNCTION__, __LINE__, locale_current->id);
|
|
729
|
+
|
|
730
|
+
int mod = 0;
|
|
731
|
+
|
|
732
|
+
int capsLock = (GetKeyState(VK_CAPITAL) & 0x01);
|
|
733
|
+
|
|
734
|
+
PVK_TO_BIT pVkToBit = locale_current->pVkToBit;
|
|
735
|
+
PVK_TO_WCHAR_TABLE pVkToWcharTable = locale_current->pVkToWcharTable;
|
|
736
|
+
PDEADKEY pDeadKey = locale_current->pDeadKey;
|
|
737
|
+
|
|
738
|
+
/* Loop over the modifier keys for this locale and determine what is
|
|
739
|
+
* currently depressed. Because this is only a structure of two
|
|
740
|
+
* bytes, we don't need to worry about the structure padding of __ptr64
|
|
741
|
+
* offsets on Wow64.
|
|
742
|
+
*/
|
|
743
|
+
bool is_shift = false, is_ctrl = false, is_alt = false;
|
|
744
|
+
for (int i = 0; pVkToBit[i].Vk != 0; i++) {
|
|
745
|
+
short state = GetAsyncKeyState(pVkToBit[i].Vk);
|
|
746
|
+
|
|
747
|
+
// Check to see if the most significant bit is active.
|
|
748
|
+
if (state & ~SHRT_MAX) {
|
|
749
|
+
if (pVkToBit[i].Vk == VK_SHIFT) {
|
|
750
|
+
is_shift = true;
|
|
751
|
+
} else if (pVkToBit[i].Vk == VK_CONTROL) {
|
|
752
|
+
is_ctrl = true;
|
|
753
|
+
} else if (pVkToBit[i].Vk == VK_MENU) {
|
|
754
|
+
is_alt = true;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// Check the Shift modifier.
|
|
760
|
+
if (is_shift) {
|
|
761
|
+
mod = 1;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// Check for the AltGr modifier.
|
|
765
|
+
if (is_ctrl && is_alt) {
|
|
766
|
+
mod += 3;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Default 32 bit structure size should be 6 bytes (4 for the pointer and 2
|
|
770
|
+
// additional byte fields) that are padded out to 8 bytes by the compiler.
|
|
771
|
+
unsigned short sizeVkToWcharTable = sizeof(VK_TO_WCHAR_TABLE);
|
|
772
|
+
#if defined(_WIN32) && !defined(_WIN64)
|
|
773
|
+
if (is_wow64()) {
|
|
774
|
+
// If we are running under Wow64 the size of the first pointer will be
|
|
775
|
+
// 8 bringing the total size to 10 bytes padded out to 16.
|
|
776
|
+
sizeVkToWcharTable = (sizeVkToWcharTable + ptr_padding + 7) & -8;
|
|
777
|
+
}
|
|
778
|
+
#endif
|
|
779
|
+
|
|
780
|
+
BYTE *ptrCurrentVkToWcharTable = (BYTE *) pVkToWcharTable;
|
|
781
|
+
|
|
782
|
+
int cbSize, n;
|
|
783
|
+
do {
|
|
784
|
+
// cbSize is used to calculate n, and n is used for the size of pVkToWchars[j].wch[n]
|
|
785
|
+
cbSize = *(ptrCurrentVkToWcharTable + offsetof(VK_TO_WCHAR_TABLE, cbSize) + ptr_padding);
|
|
786
|
+
n = (cbSize - 2) / 2;
|
|
787
|
+
|
|
788
|
+
// Same as VK_TO_WCHARS pVkToWchars[] = pVkToWcharTable[i].pVkToWchars
|
|
789
|
+
PVK_TO_WCHARS pVkToWchars = (PVK_TO_WCHARS) ((PVK_TO_WCHAR_TABLE) ptrCurrentVkToWcharTable)->pVkToWchars;
|
|
790
|
+
|
|
791
|
+
if (pVkToWchars != NULL && mod < n) {
|
|
792
|
+
// pVkToWchars[j].VirtualKey
|
|
793
|
+
BYTE *pCurrentVkToWchars = (BYTE *) pVkToWchars;
|
|
794
|
+
|
|
795
|
+
do {
|
|
796
|
+
if (((PVK_TO_WCHARS) pCurrentVkToWchars)->VirtualKey == keycode) {
|
|
797
|
+
if ((((PVK_TO_WCHARS) pCurrentVkToWchars)->Attributes == CAPLOK) && capsLock) {
|
|
798
|
+
if (is_shift && mod > 0) {
|
|
799
|
+
mod -= 1;
|
|
800
|
+
} else {
|
|
801
|
+
mod += 1;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// Set the initial unicode char.
|
|
806
|
+
WCHAR unicode = ((PVK_TO_WCHARS) pCurrentVkToWchars)->wch[mod];
|
|
807
|
+
|
|
808
|
+
// Increment the pCurrentVkToWchars by the size of wch[n].
|
|
809
|
+
pCurrentVkToWchars += sizeof(VK_TO_WCHARS) + (sizeof(WCHAR) * n);
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
if (unicode == WCH_DEAD) {
|
|
813
|
+
// The current unicode char is a dead key...
|
|
814
|
+
if (deadChar == WCH_NONE) {
|
|
815
|
+
// No previous dead key was set so cache the next
|
|
816
|
+
// wchar so we know what to do next time its pressed.
|
|
817
|
+
deadChar = ((PVK_TO_WCHARS) pCurrentVkToWchars)->wch[mod];
|
|
818
|
+
} else {
|
|
819
|
+
if (size >= 2) {
|
|
820
|
+
// Received a second dead key.
|
|
821
|
+
memset(buffer, deadChar, 2);
|
|
822
|
+
//buffer[0] = deadChar;
|
|
823
|
+
//buffer[1] = deadChar;
|
|
824
|
+
|
|
825
|
+
deadChar = WCH_NONE;
|
|
826
|
+
charCount = 2;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
} else if (unicode != WCH_NONE) {
|
|
830
|
+
// We are not WCH_NONE or WCH_DEAD
|
|
831
|
+
if (size >= 1) {
|
|
832
|
+
buffer[0] = unicode;
|
|
833
|
+
charCount = 1;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
break;
|
|
838
|
+
} else {
|
|
839
|
+
// Add sizeof WCHAR because we are really an array of WCHAR[n] not WCHAR[]
|
|
840
|
+
pCurrentVkToWchars += sizeof(VK_TO_WCHARS) + (sizeof(WCHAR) * n);
|
|
841
|
+
}
|
|
842
|
+
} while ( ((PVK_TO_WCHARS) pCurrentVkToWchars)->VirtualKey != 0 );
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// This is effectively the same as: ptrCurrentVkToWcharTable = pVkToWcharTable[++i];
|
|
846
|
+
ptrCurrentVkToWcharTable += sizeVkToWcharTable;
|
|
847
|
+
} while (cbSize != 0);
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
// If the current local has a dead key set.
|
|
851
|
+
if (deadChar != WCH_NONE) {
|
|
852
|
+
// Loop over the pDeadKey lookup table for the locale.
|
|
853
|
+
for (int i = 0; pDeadKey[i].dwBoth != 0; i++) {
|
|
854
|
+
WCHAR baseChar = (WCHAR) pDeadKey[i].dwBoth;
|
|
855
|
+
WCHAR diacritic = (WCHAR) (pDeadKey[i].dwBoth >> 16);
|
|
856
|
+
|
|
857
|
+
// If we locate an extended dead char, set it.
|
|
858
|
+
if (size >= 1 && baseChar == buffer[0] && diacritic == deadChar) {
|
|
859
|
+
deadChar = WCH_NONE;
|
|
860
|
+
|
|
861
|
+
if (charCount <= size) {
|
|
862
|
+
memset(buffer, (WCHAR) pDeadKey[i].wchComposed, charCount);
|
|
863
|
+
//buffer[i] = (WCHAR) pDeadKey[i].wchComposed;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
return charCount;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// Returns the number of locales that were loaded.
|
|
874
|
+
int load_input_helper() {
|
|
875
|
+
#if defined(_WIN32) && !defined(_WIN64)
|
|
876
|
+
if (is_wow64()) {
|
|
877
|
+
ptr_padding = sizeof(void *);
|
|
878
|
+
}
|
|
879
|
+
#endif
|
|
880
|
+
|
|
881
|
+
v_rotation = 0;
|
|
882
|
+
h_rotation = 0;
|
|
883
|
+
|
|
884
|
+
int count = refresh_locale_list();
|
|
885
|
+
|
|
886
|
+
logger(LOG_LEVEL_DEBUG,
|
|
887
|
+
"%s [%u]: refresh_locale_list() found %i locale(s).\n",
|
|
888
|
+
__FUNCTION__, __LINE__, count);
|
|
889
|
+
|
|
890
|
+
return count;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// Returns the number of locales that were removed.
|
|
894
|
+
int unload_input_helper() {
|
|
895
|
+
int count = 0;
|
|
896
|
+
|
|
897
|
+
// Cleanup and free memory from the old list.
|
|
898
|
+
KeyboardLocale* locale_item = locale_first;
|
|
899
|
+
while (locale_item != NULL) {
|
|
900
|
+
// Remove the first item from the linked list.
|
|
901
|
+
FreeLibrary(locale_item->library);
|
|
902
|
+
locale_first = locale_item->next;
|
|
903
|
+
free(locale_item);
|
|
904
|
+
locale_item = locale_first;
|
|
905
|
+
|
|
906
|
+
count++;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// Reset the current local.
|
|
910
|
+
locale_current = NULL;
|
|
911
|
+
|
|
912
|
+
return count;
|
|
913
|
+
}
|