-
Notifications
You must be signed in to change notification settings - Fork 291
/
input_hook.c
774 lines (617 loc) · 23.3 KB
/
input_hook.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
/* libUIOHook: Cross-platfrom userland keyboard and mouse hooking.
* Copyright (C) 2006-2017 Alexander Barker. All Rights Received.
* https://github.com/kwhat/libuiohook/
*
* libUIOHook 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.
*
* libUIOHook is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <inttypes.h>
#include <uiohook.h>
#include <windows.h>
#include "input_helper.h"
#include "logger.h"
// Thread and hook handles.
static DWORD hook_thread_id = 0;
static HHOOK keyboard_event_hhook = NULL, mouse_event_hhook = NULL;
static HWINEVENTHOOK win_event_hhook = NULL;
// The handle to the DLL module pulled in DllMain on DLL_PROCESS_ATTACH.
extern HINSTANCE hInst;
// Modifiers for tracking key masks.
static unsigned short int current_modifiers = 0x0000;
// Click count globals.
static unsigned short click_count = 0;
static DWORD click_time = 0;
static unsigned short int click_button = MOUSE_NOBUTTON;
static POINT last_click;
// Static event memory.
static uiohook_event event;
// Event dispatch callback.
static dispatcher_t dispatcher = NULL;
static unsigned short int grab_mouse_click_event = 0x00;
UIOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc) {
logger(LOG_LEVEL_DEBUG, "%s [%u]: Setting new dispatch callback to %#p.\n",
__FUNCTION__, __LINE__, dispatch_proc);
dispatcher = dispatch_proc;
}
// Send out an event if a dispatcher was set.
static inline void dispatch_event(uiohook_event *const event) {
if (dispatcher != NULL) {
logger(LOG_LEVEL_DEBUG, "%s [%u]: Dispatching event type %u.\n",
__FUNCTION__, __LINE__, event->type);
dispatcher(event);
}
else {
logger(LOG_LEVEL_WARN, "%s [%u]: No dispatch callback set!\n",
__FUNCTION__, __LINE__);
}
}
// Set the native modifier mask for future events.
static inline void set_modifier_mask(unsigned short int mask) {
current_modifiers |= mask;
}
// Unset the native modifier mask for future events.
static inline void unset_modifier_mask(unsigned short int mask) {
current_modifiers ^= mask;
}
// Get the current native modifier mask state.
static inline unsigned short int get_modifiers() {
return current_modifiers;
}
// Initialize the modifier mask to the current modifiers.
static void initialize_modifiers() {
current_modifiers = 0x0000;
// NOTE We are checking the high order bit, so it will be < 0 for a singed short.
if (GetKeyState(VK_LSHIFT) < 0) { set_modifier_mask(MASK_SHIFT_L); }
if (GetKeyState(VK_RSHIFT) < 0) { set_modifier_mask(MASK_SHIFT_R); }
if (GetKeyState(VK_LCONTROL) < 0) { set_modifier_mask(MASK_CTRL_L); }
if (GetKeyState(VK_RCONTROL) < 0) { set_modifier_mask(MASK_CTRL_R); }
if (GetKeyState(VK_LMENU) < 0) { set_modifier_mask(MASK_ALT_L); }
if (GetKeyState(VK_RMENU) < 0) { set_modifier_mask(MASK_ALT_R); }
if (GetKeyState(VK_LWIN) < 0) { set_modifier_mask(MASK_META_L); }
if (GetKeyState(VK_RWIN) < 0) { set_modifier_mask(MASK_META_R); }
if (GetKeyState(VK_LBUTTON) < 0) { set_modifier_mask(MASK_BUTTON1); }
if (GetKeyState(VK_RBUTTON) < 0) { set_modifier_mask(MASK_BUTTON2); }
if (GetKeyState(VK_MBUTTON) < 0) { set_modifier_mask(MASK_BUTTON3); }
if (GetKeyState(VK_XBUTTON1) < 0) { set_modifier_mask(MASK_BUTTON4); }
if (GetKeyState(VK_XBUTTON2) < 0) { set_modifier_mask(MASK_BUTTON5); }
if (GetKeyState(VK_NUMLOCK) < 0) { set_modifier_mask(MASK_NUM_LOCK); }
if (GetKeyState(VK_CAPITAL) < 0) { set_modifier_mask(MASK_CAPS_LOCK); }
if (GetKeyState(VK_SCROLL) < 0) { set_modifier_mask(MASK_SCROLL_LOCK); }
}
/* Retrieves the mouse wheel scroll type. This function cannot be included as
* part of the input_helper.h due to platform specific calling restrictions.
*/
static unsigned short int get_scroll_wheel_type() {
unsigned short int value;
UINT wheel_type;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheel_type, 0);
if (wheel_type == WHEEL_PAGESCROLL) {
value = WHEEL_BLOCK_SCROLL;
}
else {
value = WHEEL_UNIT_SCROLL;
}
return value;
}
/* Retrieves the mouse wheel scroll amount. This function cannot be included as
* part of the input_helper.h due to platform specific calling restrictions.
*/
static unsigned short int get_scroll_wheel_amount() {
unsigned short int value;
UINT wheel_amount;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheel_amount, 0);
if (wheel_amount == WHEEL_PAGESCROLL) {
value = 1;
}
else {
value = (unsigned short int) wheel_amount;
}
return value;
}
void unregister_running_hooks() {
// Stop the event hook and any timer still running.
if (win_event_hhook != NULL) {
UnhookWinEvent(win_event_hhook);
win_event_hhook = NULL;
}
// Destroy the native hooks.
if (keyboard_event_hhook != NULL) {
UnhookWindowsHookEx(keyboard_event_hhook);
keyboard_event_hhook = NULL;
}
if (mouse_event_hhook != NULL) {
UnhookWindowsHookEx(mouse_event_hhook);
mouse_event_hhook = NULL;
}
}
void hook_start_proc() {
// Get the local system time in UNIX epoch form.
uint64_t timestamp = GetMessageTime();
// Populate the hook start event.
event.time = timestamp;
event.reserved = 0x00;
event.type = EVENT_HOOK_ENABLED;
event.mask = 0x00;
// Fire the hook start event.
dispatch_event(&event);
}
void hook_stop_proc() {
// Get the local system time in UNIX epoch form.
uint64_t timestamp = GetMessageTime();
// Populate the hook stop event.
event.time = timestamp;
event.reserved = 0x00;
event.type = EVENT_HOOK_DISABLED;
event.mask = 0x00;
// Fire the hook stop event.
dispatch_event(&event);
}
static void process_key_pressed(KBDLLHOOKSTRUCT *kbhook) {
// Check and setup modifiers.
if (kbhook->vkCode == VK_LSHIFT) { set_modifier_mask(MASK_SHIFT_L); }
else if (kbhook->vkCode == VK_RSHIFT) { set_modifier_mask(MASK_SHIFT_R); }
else if (kbhook->vkCode == VK_LCONTROL) { set_modifier_mask(MASK_CTRL_L); }
else if (kbhook->vkCode == VK_RCONTROL) { set_modifier_mask(MASK_CTRL_R); }
else if (kbhook->vkCode == VK_LMENU) { set_modifier_mask(MASK_ALT_L); }
else if (kbhook->vkCode == VK_RMENU) { set_modifier_mask(MASK_ALT_R); }
else if (kbhook->vkCode == VK_LWIN) { set_modifier_mask(MASK_META_L); }
else if (kbhook->vkCode == VK_RWIN) { set_modifier_mask(MASK_META_R); }
else if (kbhook->vkCode == VK_NUMLOCK) { set_modifier_mask(MASK_NUM_LOCK); }
else if (kbhook->vkCode == VK_CAPITAL) { set_modifier_mask(MASK_CAPS_LOCK); }
else if (kbhook->vkCode == VK_SCROLL) { set_modifier_mask(MASK_SCROLL_LOCK); }
// Populate key pressed event.
event.time = kbhook->time;
event.reserved = 0x00;
event.type = EVENT_KEY_PRESSED;
event.mask = get_modifiers();
event.data.keyboard.keycode = keycode_to_scancode(kbhook->vkCode, kbhook->flags);
event.data.keyboard.rawcode = kbhook->vkCode;
event.data.keyboard.keychar = CHAR_UNDEFINED;
logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X pressed. (%#X)\n",
__FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode);
// Populate key pressed event.
dispatch_event(&event);
// If the pressed event was not consumed...
if (event.reserved ^ 0x01) {
// Buffer for unicode typed chars. No more than 2 needed.
WCHAR buffer[2]; // = { WCH_NONE };
// If the pressed event was not consumed and a unicode char exists...
SIZE_T count = keycode_to_unicode(kbhook->vkCode, buffer, sizeof(buffer));
for (unsigned int i = 0; i < count; i++) {
// Populate key typed event.
event.time = kbhook->time;
event.reserved = 0x00;
event.type = EVENT_KEY_TYPED;
event.mask = get_modifiers();
event.data.keyboard.keycode = VC_UNDEFINED;
event.data.keyboard.rawcode = kbhook->vkCode;
event.data.keyboard.keychar = buffer[i];
logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X typed. (%lc)\n",
__FUNCTION__, __LINE__, event.data.keyboard.keycode, (wint_t) event.data.keyboard.keychar);
// Fire key typed event.
dispatch_event(&event);
}
}
}
static void process_key_released(KBDLLHOOKSTRUCT *kbhook) {
// Check and setup modifiers.
if (kbhook->vkCode == VK_LSHIFT) { unset_modifier_mask(MASK_SHIFT_L); }
else if (kbhook->vkCode == VK_RSHIFT) { unset_modifier_mask(MASK_SHIFT_R); }
else if (kbhook->vkCode == VK_LCONTROL) { unset_modifier_mask(MASK_CTRL_L); }
else if (kbhook->vkCode == VK_RCONTROL) { unset_modifier_mask(MASK_CTRL_R); }
else if (kbhook->vkCode == VK_LMENU) { unset_modifier_mask(MASK_ALT_L); }
else if (kbhook->vkCode == VK_RMENU) { unset_modifier_mask(MASK_ALT_R); }
else if (kbhook->vkCode == VK_LWIN) { unset_modifier_mask(MASK_META_L); }
else if (kbhook->vkCode == VK_RWIN) { unset_modifier_mask(MASK_META_R); }
else if (kbhook->vkCode == VK_NUMLOCK) { unset_modifier_mask(MASK_NUM_LOCK); }
else if (kbhook->vkCode == VK_CAPITAL) { unset_modifier_mask(MASK_CAPS_LOCK); }
else if (kbhook->vkCode == VK_SCROLL) { unset_modifier_mask(MASK_SCROLL_LOCK); }
// Populate key pressed event.
event.time = kbhook->time;
event.reserved = 0x00;
event.type = EVENT_KEY_RELEASED;
event.mask = get_modifiers();
event.data.keyboard.keycode = keycode_to_scancode(kbhook->vkCode, kbhook->flags);
event.data.keyboard.rawcode = kbhook->vkCode;
event.data.keyboard.keychar = CHAR_UNDEFINED;
logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X released. (%#X)\n",
__FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode);
// Fire key released event.
dispatch_event(&event);
}
LRESULT CALLBACK keyboard_hook_event_proc(int nCode, WPARAM wParam, LPARAM lParam) {
KBDLLHOOKSTRUCT *kbhook = (KBDLLHOOKSTRUCT *) lParam;
switch (wParam) {
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
process_key_pressed(kbhook);
break;
case WM_KEYUP:
case WM_SYSKEYUP:
process_key_released(kbhook);
break;
default:
// In theory this *should* never execute.
logger(LOG_LEVEL_DEBUG, "%s [%u]: Unhandled Windows keyboard event: %#X.\n",
__FUNCTION__, __LINE__, (unsigned int) wParam);
break;
}
LRESULT hook_result = -1;
if (nCode < 0 || event.reserved ^ 0x01) {
hook_result = CallNextHookEx(keyboard_event_hhook, nCode, wParam, lParam);
}
else {
logger(LOG_LEVEL_DEBUG, "%s [%u]: Consuming the current event. (%li)\n",
__FUNCTION__, __LINE__, (long) hook_result);
}
return hook_result;
}
static void process_button_pressed(MSLLHOOKSTRUCT *mshook, uint16_t button) {
uint64_t timestamp = GetMessageTime();
// Track the number of clicks, the button must match the previous button.
if (button == click_button && (long int) (timestamp - click_time) <= hook_get_multi_click_time()) {
if (click_count < USHRT_MAX) {
click_count++;
}
else {
logger(LOG_LEVEL_WARN, "%s [%u]: Click count overflow detected!\n",
__FUNCTION__, __LINE__);
}
}
else {
// Reset the click count.
click_count = 1;
// Set the previous button.
click_button = button;
}
// Save this events time to calculate the click_count.
click_time = timestamp;
// Store the last click point.
last_click.x = mshook->pt.x;
last_click.y = mshook->pt.y;
// Populate mouse pressed event.
event.time = timestamp;
event.reserved = grab_mouse_click_event;
event.type = EVENT_MOUSE_PRESSED;
event.mask = get_modifiers();
event.data.mouse.button = button;
event.data.mouse.clicks = click_count;
event.data.mouse.x = mshook->pt.x;
event.data.mouse.y = mshook->pt.y;
logger(LOG_LEVEL_INFO, "%s [%u]: Button %u pressed %u time(s). (%u, %u)\n",
__FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks,
event.data.mouse.x, event.data.mouse.y);
// Fire mouse pressed event.
dispatch_event(&event);
}
static void process_button_released(MSLLHOOKSTRUCT *mshook, uint16_t button) {
// Populate mouse released event.
event.time = GetMessageTime();
event.reserved = grab_mouse_click_event;
event.type = EVENT_MOUSE_RELEASED;
event.mask = get_modifiers();
event.data.mouse.button = button;
event.data.mouse.clicks = click_count;
event.data.mouse.x = mshook->pt.x;
event.data.mouse.y = mshook->pt.y;
logger(LOG_LEVEL_INFO, "%s [%u]: Button %u released %u time(s). (%u, %u)\n",
__FUNCTION__, __LINE__, event.data.mouse.button,
event.data.mouse.clicks,
event.data.mouse.x, event.data.mouse.y);
// Fire mouse released event.
dispatch_event(&event);
// If the pressed event was not consumed...
if ((event.reserved ^ 0x01 || grab_mouse_click_event ^ 0x00) && last_click.x == mshook->pt.x && last_click.y == mshook->pt.y) {
// Populate mouse clicked event.
event.time = GetMessageTime();
event.reserved = grab_mouse_click_event;
event.type = EVENT_MOUSE_CLICKED;
event.mask = get_modifiers();
event.data.mouse.button = button;
event.data.mouse.clicks = click_count;
event.data.mouse.x = mshook->pt.x;
event.data.mouse.y = mshook->pt.y;
logger(LOG_LEVEL_INFO, "%s [%u]: Button %u clicked %u time(s). (%u, %u)\n",
__FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks,
event.data.mouse.x, event.data.mouse.y);
// Fire mouse clicked event.
dispatch_event(&event);
}
// Reset the number of clicks.
if (button == click_button && (long int) (event.time - click_time) > hook_get_multi_click_time()) {
// Reset the click count.
click_count = 0;
}
}
static void process_mouse_moved(MSLLHOOKSTRUCT *mshook) {
uint64_t timestamp = GetMessageTime();
// We received a mouse move event with the mouse actually moving.
// This verifies that the mouse was moved after being depressed.
if (last_click.x != mshook->pt.x || last_click.y != mshook->pt.y) {
// Reset the click count.
if (click_count != 0 && (long) (timestamp - click_time) > hook_get_multi_click_time()) {
click_count = 0;
}
// Populate mouse move event.
event.time = timestamp;
event.reserved = 0x00;
event.mask = get_modifiers();
// Check the modifier mask range for MASK_BUTTON1 - 5.
bool mouse_dragged = event.mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5);
if (mouse_dragged) {
// Create Mouse Dragged event.
event.type = EVENT_MOUSE_DRAGGED;
}
else {
// Create a Mouse Moved event.
event.type = EVENT_MOUSE_MOVED;
}
event.data.mouse.button = MOUSE_NOBUTTON;
event.data.mouse.clicks = click_count;
event.data.mouse.x = mshook->pt.x;
event.data.mouse.y = mshook->pt.y;
logger(LOG_LEVEL_INFO, "%s [%u]: Mouse %s to %u, %u.\n",
__FUNCTION__, __LINE__, mouse_dragged ? "dragged" : "moved",
event.data.mouse.x, event.data.mouse.y);
// Fire mouse move event.
dispatch_event(&event);
}
}
static void process_mouse_wheel(MSLLHOOKSTRUCT *mshook, uint8_t direction) {
// Track the number of clicks.
// Reset the click count and previous button.
click_count = 1;
click_button = MOUSE_NOBUTTON;
// Populate mouse wheel event.
event.time = GetMessageTime();
event.reserved = 0x00;
event.type = EVENT_MOUSE_WHEEL;
event.mask = get_modifiers();
event.data.wheel.clicks = click_count;
event.data.wheel.x = mshook->pt.x;
event.data.wheel.y = mshook->pt.y;
event.data.wheel.type = get_scroll_wheel_type();
event.data.wheel.amount = get_scroll_wheel_amount();
/* Delta HIWORD(mshook->mouseData)
* A positive value indicates that the wheel was rotated
* forward, away from the user; a negative value indicates that
* the wheel was rotated backward, toward the user. One wheel
* click is defined as WHEEL_DELTA, which is 120. */
event.data.wheel.rotation = ((int16_t) HIWORD(mshook->mouseData) / WHEEL_DELTA) * -1;
// Set the direction based on what event was received.
event.data.wheel.direction = direction;
logger(LOG_LEVEL_INFO, "%s [%u]: Mouse wheel type %u, rotated %i units in the %u direction at %u, %u.\n",
__FUNCTION__, __LINE__, event.data.wheel.type,
event.data.wheel.amount * event.data.wheel.rotation,
event.data.wheel.direction,
event.data.wheel.x, event.data.wheel.y);
// Fire mouse wheel event.
dispatch_event(&event);
}
LRESULT CALLBACK mouse_hook_event_proc(int nCode, WPARAM wParam, LPARAM lParam) {
MSLLHOOKSTRUCT *mshook = (MSLLHOOKSTRUCT *) lParam;
switch (wParam) {
case WM_LBUTTONDOWN:
set_modifier_mask(MASK_BUTTON1);
process_button_pressed(mshook, MOUSE_BUTTON1);
break;
case WM_RBUTTONDOWN:
set_modifier_mask(MASK_BUTTON2);
process_button_pressed(mshook, MOUSE_BUTTON2);
break;
case WM_MBUTTONDOWN:
set_modifier_mask(MASK_BUTTON3);
process_button_pressed(mshook, MOUSE_BUTTON3);
break;
case WM_XBUTTONDOWN:
case WM_NCXBUTTONDOWN:
if (HIWORD(mshook->mouseData) == XBUTTON1) {
set_modifier_mask(MASK_BUTTON4);
process_button_pressed(mshook, MOUSE_BUTTON4);
}
else if (HIWORD(mshook->mouseData) == XBUTTON2) {
set_modifier_mask(MASK_BUTTON5);
process_button_pressed(mshook, MOUSE_BUTTON5);
}
else {
// Extra mouse buttons.
uint16_t button = HIWORD(mshook->mouseData);
// Add support for mouse 4 & 5.
if (button == 4) {
set_modifier_mask(MOUSE_BUTTON4);
}
else if (button == 5) {
set_modifier_mask(MOUSE_BUTTON5);
}
process_button_pressed(mshook, button);
}
break;
case WM_LBUTTONUP:
unset_modifier_mask(MASK_BUTTON1);
process_button_released(mshook, MOUSE_BUTTON1);
break;
case WM_RBUTTONUP:
unset_modifier_mask(MASK_BUTTON2);
process_button_released(mshook, MOUSE_BUTTON2);
break;
case WM_MBUTTONUP:
unset_modifier_mask(MASK_BUTTON3);
process_button_released(mshook, MOUSE_BUTTON3);
break;
case WM_XBUTTONUP:
case WM_NCXBUTTONUP:
if (HIWORD(mshook->mouseData) == XBUTTON1) {
unset_modifier_mask(MASK_BUTTON4);
process_button_released(mshook, MOUSE_BUTTON4);
}
else if (HIWORD(mshook->mouseData) == XBUTTON2) {
unset_modifier_mask(MASK_BUTTON5);
process_button_released(mshook, MOUSE_BUTTON5);
}
else {
// Extra mouse buttons.
uint16_t button = HIWORD(mshook->mouseData);
// Add support for mouse 4 & 5.
if (button == 4) {
unset_modifier_mask(MOUSE_BUTTON4);
}
else if (button == 5) {
unset_modifier_mask(MOUSE_BUTTON5);
}
process_button_released(mshook, MOUSE_BUTTON5);
}
break;
case WM_MOUSEMOVE:
process_mouse_moved(mshook);
break;
case WM_MOUSEWHEEL:
process_mouse_wheel(mshook, WHEEL_VERTICAL_DIRECTION);
break;
/* For horizontal scroll wheel support.
* NOTE Windows >= Vista
* case 0x020E:
*/
case WM_MOUSEHWHEEL:
process_mouse_wheel(mshook, WHEEL_HORIZONTAL_DIRECTION);
break;
default:
// In theory this *should* never execute.
logger(LOG_LEVEL_DEBUG, "%s [%u]: Unhandled Windows mouse event: %#X.\n",
__FUNCTION__, __LINE__, (unsigned int) wParam);
break;
}
LRESULT hook_result = -1;
if (nCode < 0 || event.reserved ^ 0x01) {
hook_result = CallNextHookEx(mouse_event_hhook, nCode, wParam, lParam);
}
else {
logger(LOG_LEVEL_DEBUG, "%s [%u]: Consuming the current event. (%li)\n",
__FUNCTION__, __LINE__, (long) hook_result);
}
return hook_result;
}
// Callback function that handles events.
void CALLBACK win_hook_event_proc(HWINEVENTHOOK hook, DWORD event, HWND hWnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime) {
switch (event) {
case EVENT_OBJECT_NAMECHANGE:
logger(LOG_LEVEL_INFO, "%s [%u]: Restarting Windows input hook on window event: %#X.\n",
__FUNCTION__, __LINE__, event);
// Remove any keyboard or mouse hooks that are still running.
if (keyboard_event_hhook != NULL) {
UnhookWindowsHookEx(keyboard_event_hhook);
}
if (mouse_event_hhook != NULL) {
UnhookWindowsHookEx(mouse_event_hhook);
}
// Restart the event hooks.
keyboard_event_hhook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_hook_event_proc, hInst, 0);
mouse_event_hhook = SetWindowsHookEx(WH_MOUSE_LL, mouse_hook_event_proc, hInst, 0);
// Re-initialize modifier masks.
initialize_modifiers();
// FIXME We should compare the modifier mask before and after the restart
// to determine if we should synthesize missing events.
// Check for event hook error.
if (keyboard_event_hhook == NULL || mouse_event_hhook == NULL) {
logger(LOG_LEVEL_ERROR, "%s [%u]: SetWindowsHookEx() failed! (%#lX)\n",
__FUNCTION__, __LINE__, (unsigned long) GetLastError());
}
break;
default:
logger(LOG_LEVEL_INFO, "%s [%u]: Unhandled Windows window event: %#X.\n",
__FUNCTION__, __LINE__, event);
}
}
UIOHOOK_API void grab_mouse_click(bool enabled) {
if (enabled) {
grab_mouse_click_event = 0x01;
} else {
grab_mouse_click_event = 0x00;
}
}
UIOHOOK_API int hook_run() {
int status = UIOHOOK_FAILURE;
// Set the thread id we want to signal later.
hook_thread_id = GetCurrentThreadId();
// Spot check the hInst incase the library was statically linked and DllMain
// did not receive a pointer on load.
if (hInst == NULL) {
logger(LOG_LEVEL_INFO, "%s [%u]: hInst was not set by DllMain().\n",
__FUNCTION__, __LINE__);
hInst = GetModuleHandle(NULL);
if (hInst != NULL) {
// Initialize native input helper functions.
load_input_helper();
}
else {
logger(LOG_LEVEL_ERROR, "%s [%u]: Could not determine hInst for SetWindowsHookEx()! (%#lX)\n",
__FUNCTION__, __LINE__, (unsigned long) GetLastError());
status = UIOHOOK_ERROR_GET_MODULE_HANDLE;
}
}
// Create the native hooks.
keyboard_event_hhook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_hook_event_proc, hInst, 0);
mouse_event_hhook = SetWindowsHookEx(WH_MOUSE_LL, mouse_hook_event_proc, hInst, 0);
// Create a window event hook to listen for capture change.
win_event_hhook = SetWinEventHook(
EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_NAMECHANGE,
NULL,
win_hook_event_proc,
0, 0,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
// If we did not encounter a problem, start processing events.
if (keyboard_event_hhook != NULL && mouse_event_hhook != NULL) {
if (win_event_hhook == NULL) {
logger(LOG_LEVEL_WARN, "%s [%u]: SetWinEventHook() failed!\n",
__FUNCTION__, __LINE__);
}
logger(LOG_LEVEL_DEBUG, "%s [%u]: SetWindowsHookEx() successful.\n",
__FUNCTION__, __LINE__);
// Check and setup modifiers.
initialize_modifiers();
// Set the exit status.
status = UIOHOOK_SUCCESS;
// Windows does not have a hook start event or callback so we need to
// manually fake it.
hook_start_proc();
// Block until the thread receives an WM_QUIT request.
MSG message;
while (GetMessage(&message, (HWND) NULL, 0, 0) > 0) {
TranslateMessage(&message);
DispatchMessage(&message);
}
}
else {
logger(LOG_LEVEL_ERROR, "%s [%u]: SetWindowsHookEx() failed! (%#lX)\n",
__FUNCTION__, __LINE__, (unsigned long) GetLastError());
status = UIOHOOK_ERROR_SET_WINDOWS_HOOK_EX;
}
// Unregister any hooks that may still be installed.
unregister_running_hooks();
// We must explicitly call the cleanup handler because Windows does not
// provide a thread cleanup method like POSIX pthread_cleanup_push/pop.
hook_stop_proc();
return status;
}
UIOHOOK_API int hook_stop() {
int status = UIOHOOK_FAILURE;
// Try to exit the thread naturally.
if (PostThreadMessage(hook_thread_id, WM_QUIT, (WPARAM) NULL, (LPARAM) NULL)) {
status = UIOHOOK_SUCCESS;
}
logger(LOG_LEVEL_DEBUG, "%s [%u]: Status: %#X.\n",
__FUNCTION__, __LINE__, status);
return status;
}