-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathkeyboard_mouse.cpp
4236 lines (3887 loc) · 234 KB
/
keyboard_mouse.cpp
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
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
AutoHotkey
Copyright 2003-2008 Chris Mallett ([email protected])
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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.
*/
#include "stdafx.h" // pre-compiled headers
#include "keyboard_mouse.h"
#include "globaldata.h" // for g.KeyDelay
#include "application.h" // for MsgSleep()
#include "util.h" // for strlicmp()
#include "window.h" // for IsWindowHung()
// Added for v1.0.25. Search on sPrevEventType for more comments:
static KeyEventTypes sPrevEventType;
static vk_type sPrevVK = 0;
// For v1.0.25, the below is static to track it in between sends, so that the below will continue
// to work:
// Send {LWinDown}
// Send {LWinUp} ; Should still open the Start Menu even though it's a separate Send.
static vk_type sPrevEventModifierDown = 0;
static modLR_type sModifiersLR_persistent = 0; // Tracks this script's own lifetime/persistent modifiers (the ones it caused to be persistent and thus is responsible for tracking).
// v1.0.44.03: Below supports multiple keyboard layouts better by having script adapt to active window's layout.
#define MAX_CACHED_LAYOUTS 10 // Hard to imagine anyone using more languages/layouts than this, but even if they do it will still work; performance would just be a little worse due to being uncached.
static CachedLayoutType sCachedLayout[MAX_CACHED_LAYOUTS] = {{0}};
static HKL sTargetKeybdLayout; // Set by SendKeys() for use by the functions it calls directly and indirectly.
static ResultType sTargetLayoutHasAltGr; //
// v1.0.43: Support for SendInput() and journal-playback hook:
#define MAX_INITIAL_EVENTS_SI 500UL // sizeof(INPUT) == 28 as of 2006. Since Send is called so often, and since most Sends are short, reducing the load on the stack is also a deciding factor for these.
#define MAX_INITIAL_EVENTS_PB 1500UL // sizeof(PlaybackEvent) == 8, so more events are justified before resorting to malloc().
static LPINPUT sEventSI; // No init necessary. An array that's allocated/deallocated by SendKeys().
static PlaybackEvent *&sEventPB = (PlaybackEvent *&)sEventSI;
static UINT sEventCount, sMaxEvents; // Number of items in the above arrays and the current array capacity.
static UINT sCurrentEvent;
static modLR_type sEventModifiersLR; // Tracks the modifier state to following the progress/building of the SendInput array.
static POINT sSendInputCursorPos; // Tracks/predicts cursor position as SendInput array is built.
static HookType sHooksToRemoveDuringSendInput;
SendModes sSendMode = SM_EVENT; // Whether a SendInput or Hook array is currently being constructed.
static bool sAbortArraySend; // No init needed.
static bool sFirstCallForThisEvent; //
static DWORD sThisEventTime; //
// Dyamically resolve SendInput() because otherwise the app won't launch at all on Windows 95/NT-pre-SP3:
typedef UINT (WINAPI *MySendInputType)(UINT, LPINPUT, int);
static MySendInputType sMySendInput = (MySendInputType)GetProcAddress(GetModuleHandle("user32"), "SendInput");
// Above will be NULL for Win95/NT-pre-SP3.
void DisguiseWinAltIfNeeded(vk_type aVK, bool aInBlindMode)
// For v1.0.25, the following situation is fixed by the code below: If LWin or LAlt
// becomes a persistent modifier (e.g. via Send {LWin down}) and the user physically
// releases LWin immediately before: 1) the {LWin up} is scheduled; and 2) SendKey()
// returns. Then SendKey() will push the modifier back down so that it is in effect
// for other things done by its caller (SendKeys) and also so that if the Send
// operation ends, the key will still be down as the user intended (to modify future
// keystrokes, physical or simulated). However, since that down-event is followed
// immediately by an up-event, the Start Menu appears for WIN-key or the active
// window's menu bar is activated for ALT-key. SOLUTION: Disguise Win-up and Alt-up
// events in these cases. This workaround has been successfully tested. It's also
// limited is scope so that a script can still explicitly invoke the Start Menu with
// "Send {LWin}", or activate the menu bar with "Send {Alt}".
// The check of sPrevEventModifierDown allows "Send {LWinDown}{LWinUp}" etc., to
// continue to work.
// v1.0.40: For maximum flexibility and minimum interference while in blind mode,
// don't disguise Win and Alt keystrokes then.
{
// Caller has ensured that aVK is about to have a key-up event, so if the event immediately
// prior to this one is a key-down of the same type of modifier key, it's our job here
// to send the disguising keystrokes now (if appropriate).
if (sPrevEventType == KEYDOWN && sPrevEventModifierDown != aVK && !aInBlindMode
// SendPlay mode can't display Start Menu, so no need for disguise keystrokes (such keystrokes might cause
// unwanted effects in certain games):
&& ((aVK == VK_LWIN || aVK == VK_RWIN) && (sPrevVK == VK_LWIN || sPrevVK == VK_RWIN) && sSendMode != SM_PLAY
|| (aVK == VK_LMENU || (aVK == VK_RMENU && sTargetLayoutHasAltGr != CONDITION_TRUE)) && (sPrevVK == VK_LMENU || sPrevVK == VK_RMENU)))
KeyEvent(KEYDOWNANDUP, VK_CONTROL); // Disguise it to suppress Start Menu or prevent activation of active window's menu bar.
}
void SendKeys(char *aKeys, bool aSendRaw, SendModes aSendModeOrig, HWND aTargetWindow, unsigned int sendahk) // N11
//void SendKeys(char *aKeys, bool aSendRaw, SendModes aSendModeOrig, HWND aTargetWindow)
// The aKeys string must be modifiable (not constant), since for performance reasons,
// it's allowed to be temporarily altered by this function. mThisHotkeyModifiersLR, if non-zero,
// shoul be the set of modifiers used to trigger the hotkey that called the subroutine
// containing the Send that got us here. If any of those modifiers are still down,
// they will be released prior to sending the batch of keys specified in <aKeys>.
// v1.0.43: aSendModeOrig was added.
{
/*
if (!*aKeys)
return;
// Might be better to do this prior to changing capslock state. UPDATE: In v1.0.44.03, the following section
// has been moved to the top of the function because:
// 1) For ControlSend, GetModifierLRState() might be more accurate if the threads are attached beforehand.
// 2) Determines sTargetKeybdLayout and sTargetLayoutHasAltGr early (for maintainability).
bool threads_are_attached = false; // Set default.
DWORD keybd_layout_thread = 0; //
DWORD target_thread; // Doesn't need init.
if (aTargetWindow) // Caller has ensured this is NULL for SendInput and SendPlay modes.
{
if ((target_thread = GetWindowThreadProcessId(aTargetWindow, NULL)) // Assign.
&& target_thread != g_MainThreadID && !IsWindowHung(aTargetWindow))
{
threads_are_attached = AttachThreadInput(g_MainThreadID, target_thread, TRUE) != 0;
keybd_layout_thread = target_thread; // Testing shows that ControlSend benefits from the adapt-to-layout technique too.
}
//else no target thread, or it's our thread, or it's hung; so keep keybd_layout_thread at its default.
}
else
{
// v1.0.44.03: The following change is meaningful only to people who use more than one keyboard layout.
// It seems that the vast majority of them would want the Send command (as well as other features like
// Hotstrings and the Input command) to adapt to the keyboard layout of the active window (or target window
// in the case of ControlSend) rather than sticking with the script's own keyboard layout. In addition,
// testing shows that this adapt-to-layout method costs almost nothing in performance, especially since
// the active window, its thread, and its layout are retrieved only once for each Send rather than once
// for each keystroke.
HWND active_window;
if (active_window = GetForegroundWindow())
keybd_layout_thread = GetWindowThreadProcessId(active_window, NULL);
//else no foreground window, so keep keybd_layout_thread at default.
}
sTargetKeybdLayout = g_HKL; // N11 GetKeyboardLayout(keybd_layout_thread); // If keybd_layout_thread==0, this will get our thread's own layout, which seems like the best/safest default.
sTargetLayoutHasAltGr = LayoutHasAltGr(sTargetKeybdLayout); // Note that WM_INPUTLANGCHANGEREQUEST is not monitored by MsgSleep for the purpose of caching our thread's keyboard layout. This is because it would be unreliable if another msg pump such as MsgBox is running. Plus it hardly helps perf. at all, and hurts maintainability.
// Below is now called with "true" so that the hook's modifier state will be corrected (if necessary)
// prior to every send.
modLR_type mods_current = GetModifierLRState(true); // Current "logical" modifier state.
// Make a best guess of what the physical state of the keys is prior to starting (there's no way
// to be certain without the keyboard hook). Note: We only want those physical
// keys that are also logically down (it's possible for a key to be down physically
// but not logically such as when R-control, for example, is a suffix hotkey and the
// user is physically holding it down):
modLR_type mods_down_physically_orig, mods_down_physically_and_logically
, mods_down_physically_but_not_logically_orig;
if (g_KeybdHook)
{
// Since hook is installed, use its more reliable tracking to determine which
// modifiers are down.
mods_down_physically_orig = g_modifiersLR_physical;
mods_down_physically_and_logically = g_modifiersLR_physical & g_modifiersLR_logical; // intersect
mods_down_physically_but_not_logically_orig = g_modifiersLR_physical & ~g_modifiersLR_logical;
}
else // Use best-guess instead.
{
// Even if TickCount has wrapped due to system being up more than about 49 days,
// DWORD math still gives the right answer as long as g_script.mThisHotkeyStartTime
// itself isn't more than about 49 days ago:
if ((GetTickCount() - g_script.mThisHotkeyStartTime) < (DWORD)g_HotkeyModifierTimeout) // Elapsed time < timeout-value
mods_down_physically_orig = mods_current & g_script.mThisHotkeyModifiersLR; // Bitwise AND is set intersection.
else
// Since too much time as passed since the user pressed the hotkey, it seems best,
// based on the action that will occur below, to assume that no hotkey modifiers
// are physically down:
mods_down_physically_orig = 0;
mods_down_physically_and_logically = mods_down_physically_orig;
mods_down_physically_but_not_logically_orig = 0; // There's no way of knowing, so assume none.
}
// Any of the external modifiers that are down but NOT due to the hotkey are probably
// logically down rather than physically (perhaps from a prior command such as
// "Send, {CtrlDown}". Since there's no way to be sure without the keyboard hook or some
// driver-level monitoring, it seems best to assume that
// they are logically vs. physically down. This value contains the modifiers that
// we will not attempt to change (e.g. "Send, A" will not release the LWin
// before sending "A" if this value indicates that LWin is down). The below sets
// the value to be all the down-keys in mods_current except any that are physically
// down due to the hotkey itself. UPDATE: To improve the above, we now exclude from
// the set of persistent modifiers any that weren't made persistent by this script.
// Such a policy seems likely to do more good than harm as there have been cases where
// a modifier was detected as persistent just because #HotkeyModifier had timed out
// while the user was still holding down the key, but then when the user released it,
// this logic here would think it's still persistent and push it back down again
// to enforce it as "always-down" during the send operation. Thus, the key would
// basically get stuck down even after the send was over:
sModifiersLR_persistent &= mods_current & ~mods_down_physically_and_logically;
modLR_type persistent_modifiers_for_this_SendKeys, extra_persistent_modifiers_for_blind_mode;
bool in_blind_mode; // For performance and also to reserve future flexibility, recognize {Blind} only when it's the first item in the string.
if (in_blind_mode = !aSendRaw && !strnicmp(aKeys, "{Blind}", 7)) // Don't allow {Blind} while in raw mode due to slight chance {Blind} is intended to be sent as a literal string.
{
// Blind Mode (since this seems too obscure to document, it's mentioned here): Blind Mode relies
// on modifiers already down for something like ^c because ^c is saying "manifest a ^c", which will
// happen if ctrl is already down. By contrast, Blind does not release shift to produce lowercase
// letters because avoiding that adds flexibility that couldn't be achieved otherwise.
// Thus, ^c::Send {Blind}c produces the same result when ^c is substituted for the final c.
// But Send {Blind}{LControl down} will generate the extra events even if ctrl already down.
aKeys += 7; // Remove "{Blind}" from further consideration (essential for "SendRaw {Blind}").
// The following value is usually zero unless the user is currently holding down
// some modifiers as part of a hotkey. These extra modifiers are the ones that
// this send operation (along with all its calls to SendKey and similar) should
// consider to be down for the duration of the Send (unless they go up via an
// explicit {LWin up}, etc.)
extra_persistent_modifiers_for_blind_mode = mods_current & ~sModifiersLR_persistent;
persistent_modifiers_for_this_SendKeys = mods_current;
}
else
{
extra_persistent_modifiers_for_blind_mode = 0;
persistent_modifiers_for_this_SendKeys = sModifiersLR_persistent;
}
// Above:
// Keep sModifiersLR_persistent and persistent_modifiers_for_this_SendKeys in sync with each other from now on.
// By contrast to persistent_modifiers_for_this_SendKeys, sModifiersLR_persistent is the lifetime modifiers for
// this script that stay in effect between sends. For example, "Send {LAlt down}" leaves the alt key down
// even after the Send ends, by design.
//
// It seems best not to change persistent_modifiers_for_this_SendKeys in response to the user making physical
// modifier changes during the course of the Send. This is because it seems more often desirable that a constant
// state of modifiers be kept in effect for the entire Send rather than having the user's release of a hotkey
// modifier key, which typically occurs at some unpredictable time during the Send, to suddenly alter the nature
// of the Send in mid-stride. Another reason is to make the behavior of Send consistent with that of SendInput.
// The default behavior is to turn the capslock key off prior to sending any keys
// because otherwise lowercase letters would come through as uppercase and vice versa.
ToggleValueType prior_capslock_state;
if (threads_are_attached || !g_os.IsWin9x())
{
// Only under either of the above conditions can the state of Capslock be reliably
// retrieved and changed. Remember that apps like MS Word have an auto-correct feature that
// might make it wrongly seem that the turning off of Capslock below needs a Sleep(0) to take effect.
prior_capslock_state = g.StoreCapslockMode && !in_blind_mode
? ToggleKeyState(VK_CAPITAL, TOGGLED_OFF)
: TOGGLE_INVALID; // In blind mode, don't do store capslock (helps remapping and also adds flexibility).
}
else // OS is Win9x and threads are not attached.
{
// Attempt to turn off capslock, but never attempt to turn it back on because we can't
// reliably detect whether it was on beforehand. Update: This didn't do any good, so
// it's disabled for now:
//CapslockOffWin9x();
prior_capslock_state = TOGGLE_INVALID;
}
int orig_key_delay = g.KeyDelay;
int orig_press_duration = g.PressDuration;
if (aSendModeOrig == SM_INPUT || aSendModeOrig == SM_INPUT_FALLBACK_TO_PLAY)
{
// Both of these modes fall back to a different mode depending on whether some other script
// is running with a keyboard/mouse hook active. Of course, the detection of this isn't foolproof
// because older versions of AHK may be running and/or other apps with LL keyboard hooks. It's
// just designed to add a lot of value for typical usage because SendInput is prefered due to it
// being considerably faster than SendPlay, especially for long replacements when the CPU is under
// heavy load.
if ( !sMySendInput // Win95/NT-pre-SP3 don't support SendInput, so fall back to the specified mode.
|| SystemHasAnotherKeybdHook() // This function has been benchmarked to ensure it doesn't yield our timeslice, etc. 200 calls take 0ms according to tick-count, even when CPU is maxed.
|| !aSendRaw && SystemHasAnotherMouseHook() && strcasestr(aKeys, "{Click") ) // Ordered for short-circuit boolean performance. v1.0.43.09: Fixed to be strcasestr vs. !strcasestr
{
// Need to detect in advance what type of array to build (for performance and code size). That's why
// it done this way, and here are the comments about it:
// strcasestr() above has an unwanted amount of overhead if aKeys is huge, but it seems acceptable
// because it's called only when system has another mouse hook but *not* another keybd hook (very rare).
// Also, for performance reasons, {LButton and such are not checked for, which is documented and seems
// justified because the new {Click} method is expected to become prevalent, especially since this
// whole section only applies when the new SendInput mode is in effect.
// Finally, checking aSendRaw isn't foolproof because the string might contain {Raw} prior to {Click,
// but the complexity and performance of checking for that seems unjustified given the rarity,
// especially since there are almost never any consequences to reverting to hook mode vs. SendInput.
if (aSendModeOrig == SM_INPUT_FALLBACK_TO_PLAY)
aSendModeOrig = SM_PLAY;
else // aSendModeOrig == SM_INPUT, so fall back to EVENT.
{
aSendModeOrig = SM_EVENT;
// v1.0.43.08: When SendInput reverts to SendEvent mode, the majority of users would want
// a fast sending rate that is more comparable to SendInput's speed that the default KeyDelay
// of 10ms. PressDuration may be generally superior to KeyDelay because it does a delay after
// each changing of modifier state (which tends to improve reliability for certain apps).
// The following rules seem likely to be the best benefit in terms of speed and reliability:
// KeyDelay 0+,-1+ --> -1, 0
// KeyDelay -1, 0+ --> -1, 0
// KeyDelay -1,-1 --> -1, -1
g.PressDuration = (g.KeyDelay < 0 && g.PressDuration < 0) ? -1 : 0;
g.KeyDelay = -1; // Above line must be done before this one.
}
}
else // SendInput is available and no other impacting hooks are obviously present on the system, so use SendInput unconditionally.
aSendModeOrig = SM_INPUT; // Resolve early so that other sections don't have to consider SM_INPUT_FALLBACK_TO_PLAY a valid value.
}
// sSendMode must be set only after setting Capslock state above, because the hook method
// is incapable of changing the on/off state of toggleable keys like Capslock.
// However, it can change Capslock state as seen in the window to which playback events are being
// sent; but the behavior seems inconsistent and might vary depending on OS type, so it seems best
// not to rely on it.
sSendMode = aSendModeOrig;
if (sSendMode) // Build an array. We're also responsible for setting sSendMode to SM_EVENT prior to returning.
{
size_t mem_size;
if (sSendMode == SM_INPUT)
{
mem_size = MAX_INITIAL_EVENTS_SI * sizeof(INPUT);
sMaxEvents = MAX_INITIAL_EVENTS_SI;
}
else // Playback type.
{
mem_size = MAX_INITIAL_EVENTS_PB * sizeof(PlaybackEvent);
sMaxEvents = MAX_INITIAL_EVENTS_PB;
}
// _alloca() is used to avoid the overhead of malloc/free (99% of Sends will thus fit in stack memory).
// _alloca() never returns a failure code, it just raises an exception (e.g. stack overflow).
InitEventArray(_alloca(mem_size), sMaxEvents, mods_current);
}
bool blockinput_prev = g_BlockInput;
bool do_selective_blockinput = (g_BlockInputMode == TOGGLE_SEND || g_BlockInputMode == TOGGLE_SENDANDMOUSE)
&& !sSendMode && !aTargetWindow && g_os.IsWinNT4orLater();
if (do_selective_blockinput)
Line::ScriptBlockInput(true); // Turn it on unconditionally even if it was on, since Ctrl-Alt-Del might have disabled it.
vk_type vk;
sc_type sc;
modLR_type key_as_modifiersLR = 0;
modLR_type mods_for_next_key = 0;
// Above: For v1.0.35, it was changed to modLR vs. mod so that AltGr keys such as backslash and '{'
// are supported on layouts such as German when sending to apps such as Putty that are fussy about
// which ALT key is held down to produce the character.
vk_type this_event_modifier_down;
size_t key_text_length, key_name_length;
char *end_pos, *space_pos, *next_word, old_char, single_char_string[2];
KeyEventTypes event_type;
int repeat_count, click_x, click_y;
bool move_offset, key_down_is_persistent;
DWORD placeholder;
single_char_string[1] = '\0'; // Terminate in advance.
LONG_OPERATION_INIT // Needed even for SendInput/Play.
for (; *aKeys; ++aKeys, sPrevEventModifierDown = this_event_modifier_down)
{
this_event_modifier_down = 0; // Set default for this iteration, overridden selectively below.
if (!sSendMode)
LONG_OPERATION_UPDATE_FOR_SENDKEYS // This does not measurably affect the performance of SendPlay/Event.
if (!aSendRaw && strchr("^+!#{}", *aKeys))
{
switch (*aKeys)
{
case '^':
if (!(persistent_modifiers_for_this_SendKeys & (MOD_LCONTROL|MOD_RCONTROL)))
mods_for_next_key |= MOD_LCONTROL;
// else don't add it, because the value of mods_for_next_key may also used to determine
// which keys to release after the key to which this modifier applies is sent.
// We don't want persistent modifiers to ever be released because that's how
// AutoIt2 behaves and it seems like a reasonable standard.
continue;
case '+':
if (!(persistent_modifiers_for_this_SendKeys & (MOD_LSHIFT|MOD_RSHIFT)))
mods_for_next_key |= MOD_LSHIFT;
continue;
case '!':
if (!(persistent_modifiers_for_this_SendKeys & (MOD_LALT|MOD_RALT)))
mods_for_next_key |= MOD_LALT;
continue;
case '#':
if (g_script.mIsAutoIt2) // Since AutoIt2 ignores these, ignore them if script is in AutoIt2 mode.
continue;
if (!(persistent_modifiers_for_this_SendKeys & (MOD_LWIN|MOD_RWIN)))
mods_for_next_key |= MOD_LWIN;
continue;
case '}': continue; // Important that these be ignored. Be very careful about changing this, see below.
case '{':
{
if ( !(end_pos = strchr(aKeys + 1, '}')) ) // Ignore it and due to rarity, don't reset mods_for_next_key.
continue; // This check is relied upon by some things below that assume a '}' is preset prior to the terminator.
aKeys = omit_leading_whitespace(aKeys + 1); // v1.0.43: Skip leading whitespace inside the braces to be more flexible.
if ( !(key_text_length = end_pos - aKeys) )
{
if (end_pos[1] == '}')
{
// The literal string "{}}" has been encountered, which is interpreted as a single "}".
++end_pos;
key_text_length = 1;
}
else // Empty braces {} were encountered (or all whitespace, but literal whitespace isn't sent).
goto brace_case_end; // The loop's ++aKeys will now skip over the '}', ignoring it.
}
if (!strnicmp(aKeys, "Click", 5))
{
*end_pos = '\0'; // Temporarily terminate the string here to omit the closing brace from consideration below.
ParseClickOptions(omit_leading_whitespace(aKeys + 5), click_x, click_y, vk
, event_type, repeat_count, move_offset);
*end_pos = '}'; // Undo temp termination.
if (repeat_count < 1) // Allow {Click 100, 100, 0} to do a mouse-move vs. click (but modifiers like ^{Click..} aren't supported in this case.
MouseMove(click_x, click_y, placeholder, g.DefaultMouseSpeed, move_offset);
else // Use SendKey because it supports modifiers (e.g. ^{Click}) SendKey requires repeat_count>=1.
SendKey(vk, 0, mods_for_next_key, persistent_modifiers_for_this_SendKeys
, repeat_count, event_type, 0, aTargetWindow, click_x, click_y, move_offset);
goto brace_case_end; // This {} item completely handled, so move on to next.
}
else if (!strnicmp(aKeys, "Raw", 3)) // This is used by auto-replace hotstrings too.
{
// As documented, there's no way to switch back to non-raw mode afterward since there's no
// correct way to support special (non-literal) strings such as {Raw Off} while in raw mode.
aSendRaw = true;
goto brace_case_end; // This {} item completely handled, so move on to next.
}
// Since above didn't "goto", this item isn't {Click}.
event_type = KEYDOWNANDUP; // Set defaults.
repeat_count = 1; //
key_name_length = key_text_length; //
*end_pos = '\0'; // Temporarily terminate the string here to omit the closing brace from consideration below.
if (space_pos = StrChrAny(aKeys, " \t")) // Assign. Also, it relies on the fact that {} key names contain no spaces.
{
old_char = *space_pos;
*space_pos = '\0'; // Temporarily terminate here so that TextToVK() can properly resolve a single char.
key_name_length = space_pos - aKeys; // Override the default value set above.
next_word = omit_leading_whitespace(space_pos + 1);
UINT next_word_length = (UINT)(end_pos - next_word);
if (next_word_length > 0)
{
if (!strnicmp(next_word, "Down", 4))
{
event_type = KEYDOWN;
// v1.0.44.05: Added key_down_is_persistent (which is not initialized except here because
// it's only applicable when event_type==KEYDOWN). It avoids the following problem:
// When a key is remapped to become a modifier (such as F1::Control), launching one of
// the script's own hotkeys via F1 would lead to bad side-effects if that hotkey uses
// the Send command. This is because the Send command assumes that any modifiers pressed
// down by the script itself (such as Control) are intended to stay down during all
// keystrokes generated by that script. To work around this, something like KeyWait F1
// would otherwise be needed. within any hotkey triggered by the F1 key.
key_down_is_persistent = strnicmp(next_word + 4, "Temp", 4); // "DownTemp" means non-persistent.
}
else if (!stricmp(next_word, "Up"))
event_type = KEYUP;
else
repeat_count = ATOI(next_word);
// Above: If negative or zero, that is handled further below.
// There is no complaint for values <1 to support scripts that want to conditionally send
// zero keystrokes, e.g. Send {a %Count%}
}
}
vk = TextToVK(aKeys, &mods_for_next_key, true, false, sTargetKeybdLayout); // false must be passed due to below.
sc = vk ? 0 : TextToSC(aKeys); // If sc is 0, it will be resolved by KeyEvent() later.
if (!vk && !sc && toupper(aKeys[0]) == 'V' && toupper(aKeys[1]) == 'K')
{
char *sc_string = StrChrAny(aKeys + 2, "Ss"); // Look for the "SC" that demarks the scan code.
if (sc_string && toupper(sc_string[1]) == 'C')
sc = (sc_type)strtol(sc_string + 2, NULL, 16); // Convert from hex.
// else leave sc set to zero and just get the specified VK. This supports Send {VKnn}.
vk = (vk_type)strtol(aKeys + 2, NULL, 16); // Convert from hex.
}
if (space_pos) // undo the temporary termination
*space_pos = old_char;
*end_pos = '}'; // undo the temporary termination
if (repeat_count < 1)
goto brace_case_end; // Gets rid of one level of indentation. Well worth it.
if (vk || sc)
{
if (key_as_modifiersLR = KeyToModifiersLR(vk, sc)) // Assign
{
if (!aTargetWindow)
{
if (event_type == KEYDOWN) // i.e. make {Shift down} have the same effect {ShiftDown}
{
this_event_modifier_down = vk;
if (key_down_is_persistent) // v1.0.44.05.
sModifiersLR_persistent |= key_as_modifiersLR;
persistent_modifiers_for_this_SendKeys |= key_as_modifiersLR; // v1.0.44.06: Added this line to fix the fact that "DownTemp" should keep the key pressed down after the send.
}
else if (event_type == KEYUP) // *not* KEYDOWNANDUP, since that would be an intentional activation of the Start Menu or menu bar.
{
DisguiseWinAltIfNeeded(vk, in_blind_mode);
sModifiersLR_persistent &= ~key_as_modifiersLR;
// By contrast with KEYDOWN, KEYUP should also remove this modifier
// from extra_persistent_modifiers_for_blind_mode if it happens to be
// in there. For example, if "#i::Send {LWin Up}" is a hotkey,
// LWin should become persistently up in every respect.
extra_persistent_modifiers_for_blind_mode &= ~key_as_modifiersLR;
// Fix for v1.0.43: Also remove LControl if this key happens to be AltGr.
if (vk == VK_RMENU && sTargetLayoutHasAltGr == CONDITION_TRUE) // It is AltGr.
extra_persistent_modifiers_for_blind_mode &= ~MOD_LCONTROL;
// Since key_as_modifiersLR isn't 0, update to reflect any changes made above:
persistent_modifiers_for_this_SendKeys = sModifiersLR_persistent | extra_persistent_modifiers_for_blind_mode;
}
// else must never change sModifiersLR_persistent in response to KEYDOWNANDUP
// because that would break existing scripts. This is because that same
// modifier key may have been pushed down via {ShiftDown} rather than "{Shift Down}".
// In other words, {Shift} should never undo the effects of a prior {ShiftDown}
// or {Shift down}.
}
//else don't add this event to sModifiersLR_persistent because it will not be
// manifest via keybd_event. Instead, it will done via less intrusively
// (less interference with foreground window) via SetKeyboardState() and
// PostMessage(). This change is for ControlSend in v1.0.21 and has been
// documented.
}
// Below: sModifiersLR_persistent stays in effect (pressed down) even if the key
// being sent includes that same modifier. Surprisingly, this is how AutoIt2
// behaves also, which is good. Example: Send, {AltDown}!f ; this will cause
// Alt to still be down after the command is over, even though F is modified
// by Alt.
SendKey(vk, sc, mods_for_next_key, persistent_modifiers_for_this_SendKeys
, repeat_count, event_type, key_as_modifiersLR, aTargetWindow);
}
else if (key_name_length == 1) // No vk/sc means a char of length one is sent via special method.
{
// v1.0.40: SendKeySpecial sends only keybd_event keystrokes, not ControlSend style
// keystrokes.
// v1.0.43.07: Added check of event_type!=KEYUP, which causes something like Send {ð up} to
// do nothing if the curr. keyboard layout lacks such a key. This is relied upon by remappings
// such as F1::ð (i.e. a destination key that doesn't have a VK, at least in English).
if (!aTargetWindow && event_type != KEYUP) // In this mode, mods_for_next_key and event_type are ignored due to being unsupported.
SendKeySpecial(aKeys[0], repeat_count);
//else do nothing since it's there's no known way to send the keystokes.
}
// See comment "else must never change sModifiersLR_persistent" above about why
// !aTargetWindow is used below:
else if (vk = TextToSpecial(aKeys, (UINT)key_text_length, event_type
, persistent_modifiers_for_this_SendKeys, !aTargetWindow)) // Assign.
{
if (!aTargetWindow)
{
if (event_type == KEYDOWN)
this_event_modifier_down = vk;
else // It must be KEYUP because TextToSpecial() never returns KEYDOWNANDUP.
DisguiseWinAltIfNeeded(vk, in_blind_mode);
}
// Since we're here, repeat_count > 0.
// v1.0.42.04: A previous call to SendKey() or SendKeySpecial() might have left modifiers
// in the wrong state (e.g. Send +{F1}{ControlDown}). Since modifiers can sometimes affect
// each other, make sure they're in the state intended by the user before beginning:
SetModifierLRState(persistent_modifiers_for_this_SendKeys
, sSendMode ? sEventModifiersLR : GetModifierLRState()
, aTargetWindow, false, false); // It also does DoKeyDelay(g.PressDuration).
for (int i = 0; i < repeat_count; ++i)
{
// Don't tell it to save & restore modifiers because special keys like this one
// should have maximum flexibility (i.e. nothing extra should be done so that the
// user can have more control):
if (sendahk) // N11 inject keys not ignored by ahk
KeyEvent(event_type, vk, 0, aTargetWindow, true, KEY_NOIGNORE);
else
KeyEvent(event_type, vk, 0, aTargetWindow, true);
// N11
if (!sSendMode)
LONG_OPERATION_UPDATE_FOR_SENDKEYS
}
}
else if (key_text_length > 4 && !strnicmp(aKeys, "ASC ", 4) && !aTargetWindow) // {ASC nnnnn}
{
// Include the trailing space in "ASC " to increase uniqueness (selectivity).
// Also, sending the ASC sequence to window doesn't work, so don't even try:
SendASC(omit_leading_whitespace(aKeys + 3));
// Do this only once at the end of the sequence:
DoKeyDelay(); // It knows not to do the delay for SM_INPUT.
}
//else do nothing since it isn't recognized as any of the above "else if" cases (see below).
// If what's between {} is unrecognized, such as {Bogus}, it's safest not to send
// the contents literally since that's almost certainly not what the user intended.
// In addition, reset the modifiers, since they were intended to apply only to
// the key inside {}. Also, the below is done even if repeat-count is zero.
brace_case_end: // This label is used to simplify the code without sacrificing performance.
aKeys = end_pos; // In prep for aKeys++ done by the loop.
mods_for_next_key = 0;
continue;
} // case '{'
} // switch()
} // if (!aSendRaw && strchr("^+!#{}", *aKeys))
else // Encountered a character other than ^+!#{} ... or we're in raw mode.
{
// Best to call this separately, rather than as first arg in SendKey, since it changes the
// value of modifiers and the updated value is *not* guaranteed to be passed.
// In other words, SendKey(TextToVK(...), modifiers, ...) would often send the old
// value for modifiers.
single_char_string[0] = *aKeys; // String was pre-terminated earlier.
if (vk = TextToVK(single_char_string, &mods_for_next_key, true, true, sTargetKeybdLayout))
// TextToVK() takes no measurable time compared to the amount of time SendKey takes.
// ahkx N11 sendahk
{
if (!sendahk)
SendKey(vk, 0, mods_for_next_key, persistent_modifiers_for_this_SendKeys, 1, KEYDOWNANDUP
, 0, aTargetWindow);
else
SendKey(vk, 0, mods_for_next_key, persistent_modifiers_for_this_SendKeys, 1, KEYDOWNANDUP
, 0, aTargetWindow, COORD_UNSPECIFIED, COORD_UNSPECIFIED, false, KEY_NOIGNORE);
} // N11
else // Try to send it by alternate means.
{
// v1.0.40: SendKeySpecial sends only keybd_event keystrokes, not ControlSend style keystrokes:
if (!aTargetWindow) // In this mode, mods_for_next_key is ignored due to being unsupported.
SendKeySpecial(*aKeys, 1);
//else do nothing since it's there's no known way to send the keystokes.
}
mods_for_next_key = 0; // Safest to reset this regardless of whether a key was sent.
}
} // for()
modLR_type mods_to_set;
if (sSendMode)
{
int final_key_delay = -1; // Set default.
if (!sAbortArraySend && sEventCount > 0) // Check for zero events for performance, but more importantly because playback hook will not operate correctly with zero.
{
// Add more events to the array (prior to sending) to support the following:
// Restore the modifiers to match those the user is physically holding down, but do it as *part*
// of the single SendInput/Play call. The reasons it's done here as part of the array are:
// 1) It avoids the need for #HotkeyModifierTimeout (and it's superior to it) for both SendInput
// and SendPlay.
// 2) The hook will not be present during the SendInput, nor can it be reinstalled in time to
// catch any physical events generated by the user during the Send. Consequently, there is no
// known way to reliably detect physical keystate changes.
// 3) Changes made to modifier state by SendPlay are seen only by the active window's thread.
// Thus, it would be inconsistent and poasibly incorrect to adjust global modifier state
// after (or during) a SendPlay.
// So rather than resorting to #HotkeyModifierTimeout, we can restore the modifiers within the
// protection of SendInput/Play's uninterruptibility, allowing the user's buffered keystrokes
// (if any) to hit against the correct modifier state when the SendInput/Play completes.
// For example, if #c:: is a hotkey and the user releases Win during the SendInput/Play, that
// release would hit after SendInput/Play restores Win to the down position, and thus Win would
// not be stuck down. Furthermore, if the user didn't release Win, Win would be in the
// correct/intended position.
// This approach has a few weaknesses (but the strengths appear to outweigh them):
// 1) Hitting SendInput's 5000 char limit would omit the tail-end keystrokes, which would mess up
// all the assumptions here. But hitting that limit should be very rare, especially since it's
// documented and thus scripts will avoid it.
// 2) SendInput's assumed uninterruptibility is false if any other app or script has an LL hook
// installed. This too is documented, so scripts should generally avoid using SendInput when
// they know there are other LL hooks in the system. In any case, there's no known solution
// for it, so nothing can be done.
mods_to_set = persistent_modifiers_for_this_SendKeys
| (in_blind_mode ? 0 : (mods_down_physically_orig & ~mods_down_physically_but_not_logically_orig)); // The last item is usually 0.
// Above: When in blind mode, don't restore physical modifiers. This is done to allow a hotkey
// such as the following to release Shift:
// +space::SendInput/Play {Blind}{Shift up}
// Note that SendPlay can make such a change only from the POV of the target window; i.e. it can
// release shift as seen by the target window, but not by any other thread; so the shift key would
// still be considered to be down for the purpose of firing hotkeys (it can't change global key state
// as seen by GetAsyncKeyState).
// For more explanation of above, see a similar section for the non-array/old Send below.
SetModifierLRState(mods_to_set, sEventModifiersLR, NULL, true, true); // Disguise in case user released or pressed Win/Alt during the Send (seems best to do it even for SendPlay, though it probably needs only Alt, not Win).
// mods_to_set is used further below as the set of modifiers that were explicitly put into effect at the tail end of SendInput.
SendEventArray(final_key_delay, mods_to_set);
}
CleanupEventArray(final_key_delay);
}
else // A non-array send is in effect, so a more elaborate adjustment to logical modifiers is called for.
{
// Determine (or use best-guess, if necessary) which modifiers are down physically now as opposed
// to right before the Send began.
modLR_type mods_down_physically; // As compared to mods_down_physically_orig.
if (g_KeybdHook)
mods_down_physically = g_modifiersLR_physical;
else // No hook, so consult g_HotkeyModifierTimeout to make the determination.
// Assume that the same modifiers that were phys+logically down before the Send are still
// physically down (though not necessarily logically, since the Send may have released them),
// but do this only if the timeout period didn't expire (or the user specified that it never
// times out; i.e. elapsed time < timeout-value; DWORD math gives the right answer even if
// tick-count has wrapped around).
mods_down_physically = (g_HotkeyModifierTimeout < 0 // It never times out or...
|| (GetTickCount() - g_script.mThisHotkeyStartTime) < (DWORD)g_HotkeyModifierTimeout) // It didn't time out.
? mods_down_physically_orig : 0;
// Restore the state of the modifiers to be those the user is physically holding down right now.
// Any modifiers that are logically "persistent", as detected upon entrance to this function
// (e.g. due to something such as a prior "Send, {LWinDown}"), are also pushed down if they're not already.
// Don't press back down the modifiers that were used to trigger this hotkey if there's
// any doubt that they're still down, since doing so when they're not physically down
// would cause them to be stuck down, which might cause unwanted behavior when the unsuspecting
// user resumes typing.
// v1.0.42.04: Now that SendKey() is lazy about releasing Ctrl and/or Shift (but not Win/Alt),
// the section below also releases Ctrl/Shift if appropriate. See SendKey() for more details.
mods_to_set = persistent_modifiers_for_this_SendKeys; // Set default.
if (in_blind_mode) // This section is not needed for the array-sending modes because they exploit uninterruptibility to perform a more reliable restoration.
{
// At the end of a blind-mode send, modifiers are restored differently than normal. One
// reason for this is to support the explicit ability for a Send to turn off a hotkey's
// modifiers even if the user is still physically holding them down. For example:
// #space::Send {LWin up} ; Fails to release it, by design and for backward compatibility.
// #space::Send {Blind}{LWin up} ; Succeeds, allowing LWin to be logically up even though it's physically down.
modLR_type mods_changed_physically_during_send = mods_down_physically_orig ^ mods_down_physically;
// Fix for v1.0.42.04: To prevent keys from getting stuck down, compensate for any modifiers
// the user physically pressed or released during the Send (especially those released).
// Remove any modifiers physically released during the send so that they don't get pushed back down:
mods_to_set &= ~(mods_changed_physically_during_send & mods_down_physically_orig); // Remove those that changed from down to up.
// Conversely, add any modifiers newly, physically pressed down during the Send, because in
// most cases the user would want such modifiers to be logically down after the Send.
// Obsolete comment from v1.0.40: For maximum flexibility and minimum interference while
// in blind mode, never restore modifiers to the down position then.
mods_to_set |= mods_changed_physically_during_send & mods_down_physically; // Add those that changed from up to down.
}
else // Regardless of whether the keyboard hook is present, the following formula applies.
mods_to_set |= mods_down_physically & ~mods_down_physically_but_not_logically_orig; // The second item is usually 0.
// Above takes into account the fact that the user may have pressed and/or released some modifiers
// during the Send.
// So it includes all keys that are physically down except those that were down physically but not
// logically at the *start* of the send operation (since the send operation may have changed the
// logical state). In other words, we want to restore the keys to their former logical-down
// position to match the fact that the user is still holding them down physically. The
// previously-down keys we don't do this for are those that were physically but not logically down,
// such as a naked Control key that's used as a suffix without being a prefix. More details:
// mods_down_physically_but_not_logically_orig is used to distinguish between the following two cases,
// allowing modifiers to be properly restored to the down position when the hook is installed:
// 1) A naked modifier key used only as suffix: when the user phys. presses it, it isn't
// logically down because the hook suppressed it.
// 2) A modifier that is a prefix, that triggers a hotkey via a suffix, and that hotkey sends
// that modifier. The modifier will go back up after the SEND, so the key will be physically
// down but not logically.
// Use KEY_IGNORE_ALL_EXCEPT_MODIFIER to tell the hook to adjust g_modifiersLR_logical_non_ignored
// because these keys being put back down match the physical pressing of those same keys by the
// user, and we want such modifiers to be taken into account for the purpose of deciding whether
// other hotkeys should fire (or the same one again if auto-repeating):
// v1.0.42.04: A previous call to SendKey() might have left Shift/Ctrl in the down position
// because by procrastinatating, extraneous keystrokes in examples such as "Send ABCD" are
// eliminated (previously, such that example released the shift key after sending each key,
// only to have to press it down again for the next one. For this reason, some modifiers
// might get released here in addition to any that need to get pressed down. That's why
// SetModifierLRState() is called rather than the old method of pushing keys down only,
// never releasing them.
// Put the modifiers in mods_to_set into effect. Although "true" is passed to disguise up-events,
// there generally shouldn't be any up-events for Alt or Win because SendKey() would have already
// released them. One possible exception to this is when the user physically released Alt or Win
// during the send (perhaps only during specific sensitive/vulnerable moments).
SetModifierLRState(mods_to_set, GetModifierLRState(), aTargetWindow, true, true); // It also does DoKeyDelay(g.PressDuration).
} // End of non-array Send.
// For peace of mind and because that's how it was tested originally, the following is done
// only after adjusting the modifier state above (since that adjustment might be able to
// affect the global variables used below in a meaningful way).
if (g_KeybdHook)
{
// Ensure that g_modifiersLR_logical_non_ignored does not contain any down-modifiers
// that aren't down in g_modifiersLR_logical. This is done mostly for peace-of-mind,
// since there might be ways, via combinations of physical user input and the Send
// commands own input (overlap and interference) for one to get out of sync with the
// other. The below uses ^ to find the differences between the two, then uses & to
// find which are down in non_ignored that aren't in logical, then inverts those bits
// in g_modifiersLR_logical_non_ignored, which sets those keys to be in the up position:
g_modifiersLR_logical_non_ignored &= ~((g_modifiersLR_logical ^ g_modifiersLR_logical_non_ignored)
& g_modifiersLR_logical_non_ignored);
}
if (prior_capslock_state == TOGGLED_ON) // The current user setting requires us to turn it back on.
ToggleKeyState(VK_CAPITAL, TOGGLED_ON);
// Might be better to do this after changing capslock state, since having the threads attached
// tends to help with updating the global state of keys (perhaps only under Win9x in this case):
if (threads_are_attached)
AttachThreadInput(g_MainThreadID, target_thread, FALSE);
if (do_selective_blockinput && !blockinput_prev) // Turn it back off only if it was off before we started.
Line::ScriptBlockInput(false);
// v1.0.43.03: Someone reported that when a non-autoreplace hotstring calls us to do its backspacing, the
// hotstring's subroutine can execute a command that activates another window owned by the script before
// the original window finished receiving its backspaces. Although I can't reproduce it, this behavior
// fits with expectations since our thread won't necessarily have a chance to process the incoming
// keystrokes before executing the command that comes after SendInput. If those command(s) activate
// another of this thread's windows, that window will most likely intercept the keystrokes (assuming
// that the message pump dispatches buffered keystrokes to whichever window is active at the time the
// message is processed).
// This fix does not apply to the SendPlay or SendEvent modes, the former due to the fact that it sleeps
// a lot while the playback is running, and the latter due to key-delay and because testing has never shown
// a need for it.
if (aSendModeOrig == SM_INPUT && GetWindowThreadProcessId(GetForegroundWindow(), NULL) == g_MainThreadID) // GetWindowThreadProcessId() tolerates a NULL hwnd.
SLEEP_WITHOUT_INTERRUPTION(-1);
// v1.0.43.08: Restore the original thread key-delay values in case above temporarily overrode them.
g.KeyDelay = orig_key_delay;
g.PressDuration = orig_press_duration;
*/
}
void SendKey(vk_type aVK, sc_type aSC, modLR_type aModifiersLR, modLR_type aModifiersLRPersistent
, int aRepeatCount, KeyEventTypes aEventType, modLR_type aKeyAsModifiersLR, HWND aTargetWindow
, int aX, int aY, bool aMoveOffset, unsigned int sendahk) // ahkx N11 added sendahk
// Caller has ensured that: 1) vk or sc may be zero, but not both; 2) aRepeatCount > 0.
// This function is reponsible for first setting the correct state of the modifier keys
// (as specified by the caller) before sending the key. After sending, it should put the
// modifier keys back to the way they were originally (UPDATE: It does this only for Win/Alt
// for the reasons described near the end of this function).
{
/*
// Caller is now responsible for verifying this:
// Avoid changing modifier states and other things if there is nothing to be sent.
// Otherwise, menu bar might activated due to ALT keystrokes that don't modify any key,
// the Start Menu might appear due to WIN keystrokes that don't modify anything, etc:
//if ((!aVK && !aSC) || aRepeatCount < 1)
// return;
// I thought maybe it might be best not to release unwanted modifier keys that are already down
// (perhaps via something like "Send, {altdown}{esc}{altup}"), but that harms the case where
// modifier keys are down somehow, unintentionally: The send command wouldn't behave as expected.
// e.g. "Send, abc" while the control key is held down by other means, would send ^a^b^c,
// possibly dangerous. So it seems best to default to making sure all modifiers are in the
// proper down/up position prior to sending any Keybd events. UPDATE: This has been changed
// so that only modifiers that were actually used to trigger that hotkey are released during
// the send. Other modifiers that are down may be down intentially, e.g. due to a previous
// call to Send such as: Send {ShiftDown}.
// UPDATE: It seems best to save the initial state only once, prior to sending the key-group,
// because only at the beginning can the original state be determined without having to
// save and restore it in each loop iteration.
// UPDATE: Not saving and restoring at all anymore, due to interference (side-effects)
// caused by the extra keybd events.
// The combination of aModifiersLR and aModifiersLRPersistent are the modifier keys that
// should be down prior to sending the specified aVK/aSC. aModifiersLR are the modifiers
// for this particular aVK keystroke, but aModifiersLRPersistent are the ones that will stay
// in pressed down even after it's sent.
modLR_type modifiersLR_specified = aModifiersLR | aModifiersLRPersistent;
bool vk_is_mouse = IsMouseVK(aVK); // Caller has ensured that VK is non-zero when it wants a mouse click.
LONG_OPERATION_INIT
for (int i = 0; i < aRepeatCount; ++i)
{
if (!sSendMode)
LONG_OPERATION_UPDATE_FOR_SENDKEYS // This does not measurably affect the performance of SendPlay/Event.
// These modifiers above stay in effect for each of these keypresses.
// Always on the first iteration, and thereafter only if the send won't be essentially
// instantaneous. The modifiers are checked before every key is sent because
// if a high repeat-count was specified, the user may have time to release one or more
// of the modifier keys that were used to trigger a hotkey. That physical release
// will cause a key-up event which will cause the state of the modifiers, as seen
// by the system, to change. For example, if user releases control-key during the operation,
// some of the D's won't be control-D's:
// ^c::Send,^{d 15}
// Also: Seems best to do SetModifierLRState() even if Keydelay < 0:
// Update: If this key is itself a modifier, don't change the state of the other
// modifier keys just for it, since most of the time that is unnecessary and in
// some cases, the extra generated keystrokes would cause complications/side-effects.
if (!aKeyAsModifiersLR)
{
// DISGUISE UP: Pass "true" to disguise UP-events on WIN and ALT due to hotkeys such as:
// !a::Send test
// !a::Send {LButton}
// v1.0.40: It seems okay to tell SetModifierLRState to disguise Win/Alt regardless of
// whether our caller is in blind mode. This is because our caller already put any extra
// blind-mode modifiers into modifiersLR_specified, which prevents any actual need to
// disguise anything (only the release of Win/Alt is ever disguised).
// DISGUISE DOWN: Pass "false" to avoid disguising DOWN-events on Win and Alt because Win/Alt
// will be immediately followed by some key for them to "modify". The exceptions to this are
// when aVK is a mouse button (e.g. sending !{LButton} or #{LButton}). But both of those are
// so rare that the flexibility of doing exactly what the script specifies seems better than
// a possibly unwanted disguising. Also note that hotkeys such as #LButton automatically use
// both hooks so that the Start Menu doesn't appear when the Win key is released, so we're
// not responsible for that type of disguising here.
SetModifierLRState(modifiersLR_specified, sSendMode ? sEventModifiersLR : GetModifierLRState()
, aTargetWindow, false, true, KEY_IGNORE); // See keyboard_mouse.h for explantion of KEY_IGNORE.
// SetModifierLRState() also does DoKeyDelay(g.PressDuration).
}
// v1.0.42.04: Mouse clicks are now handled here in the same loop as keystrokes so that the modifiers
// will be readjusted (above) if the user presses/releases modifier keys during the mouse clicks.
if (vk_is_mouse && !aTargetWindow)
MouseClick(aVK, aX, aY, 1, g.DefaultMouseSpeed, aEventType, aMoveOffset);
// Above: Since it's rare to send more than one click, it seems best to simplify and reduce code size
// by not doing more than one click at a time event when mode is SendInput/Play.
else
// Sending mouse clicks via ControlSend is not supported, so in that case fall back to the
// old method of sending the VK directly (which probably has no effect 99% of the time):
{ // ahkx N11 send events not ignored by ahk
if(!sendahk)
KeyEvent(aEventType, aVK, aSC, aTargetWindow, true);
else
KeyEvent(aEventType, aVK, aSC, aTargetWindow, true, sendahk);
} // N11
} // for() [aRepeatCount]
// The final iteration by the above loop does a key or mouse delay (KeyEvent and MouseClick do it internally)
// prior to us changing the modifiers below. This is a good thing because otherwise the modifiers would
// sometimes be released so soon after the keys they modify that the modifiers are not in effect.
// This can be seen sometimes when/ ctrl-shift-tabbing back through a multi-tabbed dialog:
// The last ^+{tab} might otherwise not take effect because the CTRL key would be released too quickly.
// Release any modifiers that were pressed down just for the sake of the above
// event (i.e. leave any persistent modifiers pressed down). The caller should
// already have verified that aModifiersLR does not contain any of the modifiers
// in aModifiersLRPersistent. Also, call GetModifierLRState() again explicitly
// rather than trying to use a saved value from above, in case the above itself
// changed the value of the modifiers (i.e. aVk/aSC is a modifier). Admittedly,
// that would be pretty strange but it seems the most correct thing to do (another
// reason is that the user may have pressed or released modifier keys during the
// final mouse/key delay that was done above).
if (!aKeyAsModifiersLR) // See prior use of this var for explanation.
{
// It seems best not to use KEY_IGNORE_ALL_EXCEPT_MODIFIER in this case, though there's
// a slight chance that a script or two might be broken by not doing so. The chance
// is very slight because the only thing KEY_IGNORE_ALL_EXCEPT_MODIFIER would allow is
// something like the following example. Note that the hotkey below must be a hook
// hotkey (even more rare) because registered hotkeys will still see the logical modifier
// state and thus fire regardless of whether g_modifiersLR_logical_non_ignored says that
// they shouldn't:
// #b::Send, {CtrlDown}{AltDown}
// $^!a::MsgBox You pressed the A key after pressing the B key.
// In the above, making ^!a a hook hotkey prevents it from working in conjunction with #b.
// UPDATE: It seems slightly better to have it be KEY_IGNORE_ALL_EXCEPT_MODIFIER for these reasons:
// 1) Persistent modifiers are fairly rare. When they're in effect, it's usually for a reason
// and probably a pretty good one and from a user who knows what they're doing.
// 2) The condition that g_modifiersLR_logical_non_ignored was added to fix occurs only when
// the user physically presses a suffix key (or auto-repeats one by holding it down)
// during the course of a SendKeys() operation. Since the persistent modifiers were
// (by definition) already in effect prior to the Send, putting them back down for the
// purpose of firing hook hotkeys does not seem unreasonable, and may in fact add value.
// DISGUISE DOWN: When SetModifierLRState() is called below, it should only release keys, not press
// any down (except if the user's physical keystrokes interferred). Therefore, passing true or false
// for the disguise-down-events paramater doesn't matter much (but pass "true" in case the user's
// keystrokes did interfere in a way that requires a Alt or Win to be pressed back down, because
// disguising it seems best).
// DISGUISE UP: When SetModifierLRState() is called below, it is passed "false" for disguise-up
// to avoid generating unnecessary disguise-keystrokes. They are not needed because if our keystrokes
// were modified by either WIN or ALT, the release of the WIN or ALT key will already be disguised due to
// its having modified something while it was down. The exceptions to this are when aVK is a mouse button
// (e.g. sending !{LButton} or #{LButton}). But both of those are so rare that the flexibility of doing
// exactly what the script specifies seems better than a possibly unwanted disguising.
// UPDATE for v1.0.42.04: Only release Win and Alt (if appropriate), not Ctrl and Shift, since we know
// Win/Alt don't have to be disguised but our caller would have trouble tracking that info or making that
// determination. This avoids extra keystrokes, while still procrastinating the release of Ctrl/Shift so
// that those can be left down if the caller's next keystroke happens to need them.
modLR_type state_now = sSendMode ? sEventModifiersLR : GetModifierLRState();
modLR_type win_alt_to_be_released = ((state_now ^ aModifiersLRPersistent) & state_now) // The modifiers to be released...
& (MOD_LWIN|MOD_RWIN|MOD_LALT|MOD_RALT); // ... but restrict them to only Win/Alt.
if (win_alt_to_be_released)
SetModifierLRState(state_now & ~win_alt_to_be_released
, state_now, aTargetWindow, true, false); // It also does DoKeyDelay(g.PressDuration).
}
*/
}
void SendKeySpecial(char aChar, int aRepeatCount)
// Caller must be aware that keystrokes are sent directly (i.e. never to a target window via ControlSend mode).
// It must also be aware that the event type KEYDOWNANDUP is always what's used since there's no way
// to support anything else. Furthermore, there's no way to support "modifiersLR_for_next_key" such as ^
// (assuming is a character for which SendKeySpecial() is required in the current layout).
// This function uses some of the same code as SendKey() above, so maintain them together.
{
/*
// Caller must verify that aRepeatCount > 1.
// Avoid changing modifier states and other things if there is nothing to be sent.
// Otherwise, menu bar might activated due to ALT keystrokes that don't modify any key,
// the Start Menu might appear due to WIN keystrokes that don't modify anything, etc:
//if (aRepeatCount < 1)
// return;
// v1.0.40: This function was heavily simplified because the old method of simulating
// characters via dead keys apparently never executed under any keyboard layout. It never
// got past the following on the layouts I tested (Russian, German, Danish, Spanish):
// if (!send1 && !send2) // Can't simulate aChar.
// return;
// This might be partially explained by the fact that the following old code always exceeded
// the bounds of the array (because aChar was always between 0 and 127), so it was never valid
// in the first place:
// asc_int = cAnsiToAscii[(int)((aChar - 128) & 0xff)] & 0xff;
// Producing ANSI characters via Alt+Numpad and a leading zero appears standard on most languages
// and layouts (at least those whose active code page is 1252/Latin 1 US/Western Europe). However,
// Russian (code page 1251 Cyrillic) is apparently one exception as shown by the fact that sending
// all of the characters above Chr(127) while under Russian layout produces Cyrillic characters
// if the active window's focused control is an Edit control (even if its an ANSI app).
// I don't know the difference between how such characters are actually displayed as opposed to how
// they're stored stored in memory (in notepad at least, there appears to be some kind of on-the-fly
// translation to Unicode as shown when you try to save such a file). But for now it doesn't matter
// because for backward compatibility, it seems best not to change it until some alternative is
// discovered that's high enough in value to justify breaking existing scripts that run under Russian
// and other non-code-page-1252 layouts.
//
// Production of ANSI characters above 127 has been tested on both Windows XP and 98se (but not the
// Win98 command prompt).
char asc_string[16], *cp = asc_string;
// The following range isn't checked because this function appears never to be called for such
// characters (tested in English and Russian so far), probably because VkKeyScan() finds a way to
// manifest them via Control+VK combinations:
//if (aChar > -1 && aChar < 32)
// return;
if (aChar < 0) // Try using ANSI.
*cp++ = '0'; // ANSI mode is achieved via leading zero in the Alt+Numpad keystrokes.