@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.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  3. package/binding.gyp +85 -0
  4. package/dist/index.d.ts +194 -0
  5. package/dist/index.js +206 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/prebuild-test-noop.d.ts +0 -0
  8. package/dist/prebuild-test-noop.js +3 -0
  9. package/dist/prebuild-test-noop.js.map +1 -0
  10. package/libuiohook/include/uiohook.h +457 -0
  11. package/libuiohook/src/darwin/input_helper.c +535 -0
  12. package/libuiohook/src/darwin/input_helper.h +203 -0
  13. package/libuiohook/src/darwin/input_hook.c +1436 -0
  14. package/libuiohook/src/darwin/post_event.c +303 -0
  15. package/libuiohook/src/darwin/system_properties.c +479 -0
  16. package/libuiohook/src/logger.c +40 -0
  17. package/libuiohook/src/logger.h +32 -0
  18. package/libuiohook/src/windows/input_helper.c +913 -0
  19. package/libuiohook/src/windows/input_helper.h +146 -0
  20. package/libuiohook/src/windows/input_hook.c +722 -0
  21. package/libuiohook/src/windows/post_event.c +248 -0
  22. package/libuiohook/src/windows/system_properties.c +231 -0
  23. package/libuiohook/src/x11/input_helper.c +1846 -0
  24. package/libuiohook/src/x11/input_helper.h +108 -0
  25. package/libuiohook/src/x11/input_hook.c +1116 -0
  26. package/libuiohook/src/x11/post_event.c +427 -0
  27. package/libuiohook/src/x11/system_properties.c +494 -0
  28. package/package.json +60 -0
  29. package/prebuilds/darwin/darwin-arm64/@mukea+uiohook-napi.node +0 -0
  30. package/prebuilds/darwin/darwin-x64/@mukea+uiohook-napi.node +0 -0
  31. package/prebuilds/linux/linux-arm64/@mukea+uiohook-napi.node +0 -0
  32. package/prebuilds/linux/linux-x64/@mukea+uiohook-napi.node +0 -0
  33. package/prebuilds/windows/win32-x64/@mukea+uiohook-napi.node +0 -0
  34. package/src/lib/addon.c +337 -0
  35. package/src/lib/napi_helpers.c +51 -0
  36. package/src/lib/napi_helpers.h +53 -0
  37. package/src/lib/uiohook_worker.c +200 -0
  38. package/src/lib/uiohook_worker.h +12 -0
@@ -0,0 +1,248 @@
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 <stdio.h>
20
+ #include <uiohook.h>
21
+ #include <windows.h>
22
+
23
+ #include "input_helper.h"
24
+ #include "logger.h"
25
+
26
+ // Some buggy versions of MinGW and MSys do not include these constants in winuser.h.
27
+ #ifndef MAPVK_VK_TO_VSC
28
+ #define MAPVK_VK_TO_VSC 0
29
+ #define MAPVK_VSC_TO_VK 1
30
+ #define MAPVK_VK_TO_CHAR 2
31
+ #define MAPVK_VSC_TO_VK_EX 3
32
+ #endif
33
+ // Some buggy versions of MinGW and MSys only define this value for Windows
34
+ // versions >= 0x0600 (Windows Vista) when it should be 0x0500 (Windows 2000).
35
+ #ifndef MAPVK_VK_TO_VSC_EX
36
+ #define MAPVK_VK_TO_VSC_EX 4
37
+ #endif
38
+
39
+ #ifndef KEYEVENTF_SCANCODE
40
+ #define KEYEVENTF_EXTENDEDKEY 0x0001
41
+ #define KEYEVENTF_KEYUP 0x0002
42
+ #define KEYEVENTF_UNICODE 0x0004
43
+ #define KEYEVENTF_SCANCODE 0x0008
44
+ #endif
45
+
46
+ #ifndef KEYEVENTF_KEYDOWN
47
+ #define KEYEVENTF_KEYDOWN 0x0000
48
+ #endif
49
+
50
+ #define MAX_WINDOWS_COORD_VALUE (1 << 16)
51
+
52
+ // TODO I doubt this table is complete.
53
+ // http://letcoderock.blogspot.fr/2011/10/sendinput-with-shift-key-not-work.html
54
+ static const uint16_t extend_key_table[10] = {
55
+ VK_UP,
56
+ VK_DOWN,
57
+ VK_LEFT,
58
+ VK_RIGHT,
59
+ VK_HOME,
60
+ VK_END,
61
+ VK_PRIOR, // PgUp
62
+ VK_NEXT, // PgDn
63
+ VK_INSERT,
64
+ VK_DELETE
65
+ };
66
+
67
+
68
+ static LONG convert_to_relative_position(int coordinate, int screen_size) {
69
+ // See https://stackoverflow.com/a/4555214 and its comments
70
+ int offset = (coordinate > 0 ? 1 : -1); // Negative coordinates appear when using multiple monitors
71
+ return ((coordinate * MAX_WINDOWS_COORD_VALUE) / screen_size) + offset;
72
+ }
73
+
74
+ static int map_keyboard_event(uiohook_event * const event, INPUT * const input) {
75
+ input->type = INPUT_KEYBOARD; // | KEYEVENTF_SCANCODE
76
+ //input->ki.wScan = event->data.keyboard.rawcode;
77
+ //input->ki.time = GetSystemTime();
78
+
79
+ switch (event->type) {
80
+ case EVENT_KEY_PRESSED:
81
+ input->ki.dwFlags = KEYEVENTF_KEYDOWN;
82
+ break;
83
+
84
+ case EVENT_KEY_RELEASED:
85
+ input->ki.dwFlags = KEYEVENTF_KEYUP;
86
+ break;
87
+
88
+ default:
89
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Invalid event for keyboard event mapping: %#X.\n",
90
+ __FUNCTION__, __LINE__, event->type);
91
+ return UIOHOOK_FAILURE;
92
+ }
93
+
94
+ input->ki.wVk = (WORD) scancode_to_keycode(event->data.keyboard.keycode);
95
+ if (input->ki.wVk == 0x0000) {
96
+ logger(LOG_LEVEL_WARN, "%s [%u]: Unable to lookup scancode: %li\n",
97
+ __FUNCTION__, __LINE__, event->data.keyboard.keycode);
98
+ return UIOHOOK_FAILURE;
99
+ }
100
+
101
+ // FIXME Why is this checking MASK_SHIFT
102
+ if (event->mask & MASK_SHIFT) {
103
+ for (int i = 0; i < sizeof(extend_key_table) / sizeof(uint16_t)
104
+ && input->ki.wVk == extend_key_table[i]; i++) {
105
+ input->ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
106
+ }
107
+ }
108
+
109
+ return UIOHOOK_SUCCESS;
110
+ }
111
+
112
+ static int map_mouse_event(uiohook_event * const event, INPUT * const input) {
113
+ // FIXME implement multiple monitor support
114
+ uint16_t screen_width = GetSystemMetrics(SM_CXSCREEN);
115
+ uint16_t screen_height = GetSystemMetrics(SM_CYSCREEN);
116
+
117
+ input->type = INPUT_MOUSE;
118
+ input->mi.mouseData = 0;
119
+ input->mi.dwExtraInfo = 0;
120
+ input->mi.time = 0; // GetSystemTime();
121
+
122
+ input->mi.dx = convert_to_relative_position(event->data.mouse.x, screen_width);
123
+ input->mi.dy = convert_to_relative_position(event->data.mouse.y, screen_height);
124
+
125
+ switch (event->type) {
126
+ case EVENT_MOUSE_PRESSED:
127
+ if (event->data.mouse.button == MOUSE_NOBUTTON) {
128
+ logger(LOG_LEVEL_WARN, "%s [%u]: No button specified for mouse pressed event!\n",
129
+ __FUNCTION__, __LINE__);
130
+ return UIOHOOK_FAILURE;
131
+ } else if (event->data.mouse.button == MOUSE_BUTTON1) {
132
+ input->mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
133
+ } else if (event->data.mouse.button == MOUSE_BUTTON2) {
134
+ input->mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
135
+ } else if (event->data.mouse.button == MOUSE_BUTTON3) {
136
+ input->mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN;
137
+ } else {
138
+ input->mi.dwFlags = MOUSEEVENTF_XDOWN;
139
+ if (event->data.mouse.button == MOUSE_BUTTON4) {
140
+ input->mi.mouseData = XBUTTON1;
141
+ } else if (event->data.mouse.button == MOUSE_BUTTON5) {
142
+ input->mi.mouseData = XBUTTON2;
143
+ } else {
144
+ input->mi.mouseData = event->data.mouse.button - 3;
145
+ }
146
+ }
147
+
148
+ // We need to move the mouse to the correct location prior to clicking.
149
+ event->type = EVENT_MOUSE_MOVED;
150
+ // TODO Remember to check the status here.
151
+ hook_post_event(event);
152
+ event->type = EVENT_MOUSE_PRESSED;
153
+ break;
154
+
155
+ case EVENT_MOUSE_RELEASED:
156
+ if (event->data.mouse.button == MOUSE_NOBUTTON) {
157
+ logger(LOG_LEVEL_WARN, "%s [%u]: No button specified for mouse released event!\n",
158
+ __FUNCTION__, __LINE__);
159
+ return UIOHOOK_FAILURE;
160
+ } else if (event->data.mouse.button == MOUSE_BUTTON1) {
161
+ input->mi.dwFlags = MOUSEEVENTF_LEFTUP;
162
+ } else if (event->data.mouse.button == MOUSE_BUTTON2) {
163
+ input->mi.dwFlags = MOUSEEVENTF_RIGHTUP;
164
+ } else if (event->data.mouse.button == MOUSE_BUTTON3) {
165
+ input->mi.dwFlags = MOUSEEVENTF_MIDDLEUP;
166
+ } else {
167
+ input->mi.dwFlags = MOUSEEVENTF_XUP;
168
+ if (event->data.mouse.button == MOUSE_BUTTON4) {
169
+ input->mi.mouseData = XBUTTON1;
170
+ } else if (event->data.mouse.button == MOUSE_BUTTON5) {
171
+ input->mi.mouseData = XBUTTON2;
172
+ } else {
173
+ input->mi.mouseData = event->data.mouse.button - 3;
174
+ }
175
+ }
176
+
177
+ // We need to move the mouse to the correct location prior to clicking.
178
+ event->type = EVENT_MOUSE_MOVED;
179
+ // TODO Remember to check the status here.
180
+ hook_post_event(event);
181
+ event->type = EVENT_MOUSE_PRESSED;
182
+ break;
183
+
184
+ case EVENT_MOUSE_WHEEL:
185
+ input->mi.dwFlags = MOUSEEVENTF_WHEEL;
186
+
187
+ // type, amount and rotation?
188
+ input->mi.mouseData = event->data.wheel.amount * event->data.wheel.rotation * WHEEL_DELTA;
189
+ break;
190
+
191
+ case EVENT_MOUSE_DRAGGED:
192
+ case EVENT_MOUSE_MOVED:
193
+ input->mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
194
+ break;
195
+
196
+ default:
197
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Invalid event for mouse event mapping: %#X.\n",
198
+ __FUNCTION__, __LINE__, event->type);
199
+ return UIOHOOK_FAILURE;
200
+ }
201
+
202
+ return UIOHOOK_SUCCESS;
203
+ }
204
+
205
+ // TODO This should return a status code, UIOHOOK_SUCCESS or otherwise.
206
+ UIOHOOK_API void hook_post_event(uiohook_event * const event) {
207
+ int status = UIOHOOK_FAILURE;
208
+
209
+ INPUT *input = (INPUT *) calloc(1, sizeof(INPUT)) ;
210
+ if (input == NULL) {
211
+ logger(LOG_LEVEL_ERROR, "%s [%u]: failed to allocate memory: calloc!\n",
212
+ __FUNCTION__, __LINE__);
213
+ return; // UIOHOOK_ERROR_OUT_OF_MEMORY
214
+ }
215
+
216
+ switch (event->type) {
217
+ case EVENT_KEY_PRESSED:
218
+ case EVENT_KEY_RELEASED:
219
+ status = map_keyboard_event(event, input);
220
+ break;
221
+
222
+ case EVENT_MOUSE_PRESSED:
223
+ case EVENT_MOUSE_RELEASED:
224
+ case EVENT_MOUSE_WHEEL:
225
+ case EVENT_MOUSE_MOVED:
226
+ case EVENT_MOUSE_DRAGGED:
227
+ status = map_mouse_event(event, input);
228
+ break;
229
+
230
+ case EVENT_KEY_TYPED:
231
+ case EVENT_MOUSE_CLICKED:
232
+
233
+ case EVENT_HOOK_ENABLED:
234
+ case EVENT_HOOK_DISABLED:
235
+
236
+ default:
237
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Ignoring post event: %#X.\n",
238
+ __FUNCTION__, __LINE__, event->type);
239
+
240
+ }
241
+
242
+ if (status != UIOHOOK_FAILURE && !SendInput(1, input, sizeof(INPUT))) {
243
+ logger(LOG_LEVEL_ERROR, "%s [%u]: SendInput() failed! (%#lX)\n",
244
+ __FUNCTION__, __LINE__, (unsigned long) GetLastError());
245
+ }
246
+
247
+ free(input);
248
+ }
@@ -0,0 +1,231 @@
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 <uiohook.h>
20
+ #include <windows.h>
21
+
22
+ #include "logger.h"
23
+ #include "input_helper.h"
24
+
25
+ // The handle to the DLL module pulled in DllMain on DLL_PROCESS_ATTACH.
26
+ HINSTANCE hInst;
27
+
28
+ // input_hook.c
29
+ extern void unregister_running_hooks();
30
+
31
+
32
+ // Structure for the monitor_enum_proc() callback so we can track the count.
33
+ typedef struct _screen_info {
34
+ uint8_t count;
35
+ screen_data *data;
36
+ } screen_info;
37
+
38
+ /* The following function was contributed by Anthony Liguori Jan 14, 2015.
39
+ * https://github.com/kwhat/libuiohook/pull/17
40
+ *
41
+ * callback function called by EnumDisplayMonitors for each enabled monitor
42
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/dd162610(v=vs.85).aspx
43
+ * http://msdn.microsoft.com/en-us/library/dd162610%28VS.85%29.aspx
44
+ * http://msdn.microsoft.com/en-us/library/dd145061%28VS.85%29.aspx
45
+ * http://msdn.microsoft.com/en-us/library/dd144901(v=vs.85).aspx
46
+ */
47
+ static BOOL CALLBACK monitor_enum_proc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
48
+ int width = lprcMonitor->right - lprcMonitor->left;
49
+ int height = lprcMonitor->bottom - lprcMonitor->top;
50
+ int origin_x = lprcMonitor->left;
51
+ int origin_y = lprcMonitor->top;
52
+
53
+ if (width > 0 && height > 0) {
54
+ screen_info *screens = (screen_info *) dwData;
55
+
56
+ int current_screen = screens->count++;
57
+
58
+ if (screens->data == NULL) {
59
+ screens->data = (screen_data *) malloc(sizeof(screen_data));
60
+ } else {
61
+ screens->data = (screen_data *) realloc(screens->data, sizeof(screen_data) * screens->count);
62
+ }
63
+
64
+ screens->data[current_screen] = (screen_data) {
65
+ // Should monitor count start @ zero? Currently it starts at 1.
66
+ .number = screens->count,
67
+ .x = origin_x,
68
+ .y = origin_y,
69
+ .width = width,
70
+ .height = height
71
+ };
72
+
73
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: Monitor %d: %ldx%ld (%ld, %ld)\n",
74
+ __FUNCTION__, __LINE__, screens->count, width, height, origin_x, origin_y);
75
+ }
76
+
77
+ return TRUE;
78
+ }
79
+
80
+ /* The following function was contributed by Anthony Liguori Jan 14, 2015.
81
+ * https://github.com/kwhat/libuiohook/pull/17
82
+ */
83
+ UIOHOOK_API screen_data* hook_create_screen_info(unsigned char *count) {
84
+ // Initialize count to zero.
85
+ *count = 0;
86
+
87
+ // Create a simple structure to make working with monitor_enum_proc easier.
88
+ screen_info screens = {
89
+ .count = 0,
90
+ .data = NULL
91
+ };
92
+
93
+ BOOL status = EnumDisplayMonitors(NULL, NULL, monitor_enum_proc, (LPARAM) &screens);
94
+
95
+ if (!status || screens.count == 0) {
96
+ // Fallback in case EnumDisplayMonitors fails.
97
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: EnumDisplayMonitors failed. Fallback.\n",
98
+ __FUNCTION__, __LINE__);
99
+
100
+ int width = GetSystemMetrics(SM_CXSCREEN);
101
+ int height = GetSystemMetrics(SM_CYSCREEN);
102
+
103
+ if (width > 0 && height > 0) {
104
+ screens.data = (screen_data *) malloc(sizeof(screen_data));
105
+
106
+ if (screens.data != NULL) {
107
+ *count = 1;
108
+ screens.data[0] = (screen_data) {
109
+ .number = 1,
110
+ .x = 0,
111
+ .y = 0,
112
+ .width = width,
113
+ .height = height
114
+ };
115
+ }
116
+ }
117
+ } else {
118
+ // Populate the count.
119
+ *count = screens.count;
120
+ }
121
+
122
+ return screens.data;
123
+ }
124
+
125
+ UIOHOOK_API long int hook_get_auto_repeat_rate() {
126
+ long int value = -1;
127
+ long int rate;
128
+
129
+ if (SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &rate, 0)) {
130
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: SPI_GETKEYBOARDSPEED: %li.\n",
131
+ __FUNCTION__, __LINE__, rate);
132
+
133
+ value = rate;
134
+ }
135
+
136
+ return value;
137
+ }
138
+
139
+ UIOHOOK_API long int hook_get_auto_repeat_delay() {
140
+ long int value = -1;
141
+ long int delay;
142
+
143
+ if (SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &delay, 0)) {
144
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: SPI_GETKEYBOARDDELAY: %li.\n",
145
+ __FUNCTION__, __LINE__, delay);
146
+
147
+ value = delay;
148
+ }
149
+
150
+ return value;
151
+ }
152
+
153
+ UIOHOOK_API long int hook_get_pointer_acceleration_multiplier() {
154
+ long int value = -1;
155
+ int mouse[3]; // 0-Threshold X, 1-Threshold Y and 2-Speed.
156
+
157
+ if (SystemParametersInfo(SPI_GETMOUSE, 0, &mouse, 0)) {
158
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: SPI_GETMOUSE[2]: %i.\n",
159
+ __FUNCTION__, __LINE__, mouse[2]);
160
+
161
+ value = mouse[2];
162
+ }
163
+
164
+ return value;
165
+ }
166
+
167
+ UIOHOOK_API long int hook_get_pointer_acceleration_threshold() {
168
+ long int value = -1;
169
+ int mouse[3]; // 0-Threshold X, 1-Threshold Y and 2-Speed.
170
+
171
+ if (SystemParametersInfo(SPI_GETMOUSE, 0, &mouse, 0)) {
172
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: SPI_GETMOUSE[0]: %i.\n",
173
+ __FUNCTION__, __LINE__, mouse[0]);
174
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: SPI_GETMOUSE[1]: %i.\n",
175
+ __FUNCTION__, __LINE__, mouse[1]);
176
+
177
+ // Average the x and y thresholds.
178
+ value = (mouse[0] + mouse[1]) / 2;
179
+ }
180
+
181
+ return value;
182
+ }
183
+
184
+ UIOHOOK_API long int hook_get_pointer_sensitivity() {
185
+ long int value = -1;
186
+ int sensitivity;
187
+
188
+ if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &sensitivity, 0)) {
189
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: SPI_GETMOUSESPEED: %i.\n",
190
+ __FUNCTION__, __LINE__, sensitivity);
191
+
192
+ value = sensitivity;
193
+ }
194
+
195
+ return value;
196
+ }
197
+
198
+ UIOHOOK_API long int hook_get_multi_click_time() {
199
+ long int value = -1;
200
+ UINT clicktime;
201
+
202
+ clicktime = GetDoubleClickTime();
203
+ logger(LOG_LEVEL_DEBUG, "%s [%u]: GetDoubleClickTime: %u.\n",
204
+ __FUNCTION__, __LINE__, (unsigned int) clicktime);
205
+
206
+ value = (long int) clicktime;
207
+
208
+ return value;
209
+ }
210
+
211
+ // DLL Entry point.
212
+ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved) {
213
+ switch (fdwReason) {
214
+ case DLL_PROCESS_ATTACH:
215
+ // Save the DLL address.
216
+ hInst = hInstDLL;
217
+ break;
218
+
219
+ case DLL_PROCESS_DETACH:
220
+ // Unregister any hooks that may still be installed.
221
+ unregister_running_hooks();
222
+ break;
223
+
224
+ case DLL_THREAD_ATTACH:
225
+ case DLL_THREAD_DETACH:
226
+ // Do Nothing.
227
+ break;
228
+ }
229
+
230
+ return TRUE;
231
+ }