vmech wrote: ↑05 Mar 2021, 20:16
there is no need to use
SetTimer - a regular loop with
Sleep works at least as well.
There is a need for Timers, because Loops cannot handle multithreading. I'm using similar code in scripts with a bunch of looping behaviors that can execute simultaneously, hence requiring Timers.
I just meant that LButton Up procedure is superfluous. Its function is perfectly performed by KeyWait in LButton procedure.
I have now spent some time testing the
KeyWait->
Send, {Key Up} method you suggested and can confirm that it's not usable for my use-case. It doesn't solve the original issue;
loops still get stuck. But most importantly: it changes threading behavior, creating new bugs when combined with other hotkeys of the same kind. I use a variable to toggle the looping on/off, and when off the
KeyWait->
Send, {Key Up} method keeps creating new threads while holding down multiple keys(~50 threads per sec with 2 keys), which is bad in a number of ways.
While testing I also found a minor bug in how KeyWait multithreads, so I might rewrite my code with Bool-checking instead of KeyWait to fix that, despite it making the code longer. That still won't fix the issue with loops getting stuck tho
Or a slightly more flexible variant:
Code: Select all
Hotkey, LButton, MyButton
MyButton() {
Loop
{
Send, {Blind}{%A_ThisHotkey% down}
Sleep, 50
If !(GetKeyState(A_ThisHotkey, "P"))
Break
}
Send, % "{Blind}{" A_ThisHotkey " up}"
}
M8,
while loops exist
Also, I'm already way ahead of you on flexibility:
Code: Select all
global spam := false
spamHotkeys := ["3", "f", "LButton"] ; Hold one of these to spam that key. Just add a key to the array to automatically make it a new spam hotkey
global BoundFuncCache := {} ; A collection of bound functions for use in Timer stopping. Func(f).Bind(k) seems to create an object and return a reference to it, without caching the result, so manual caching is required to reference the same object
for i, key in spamHotkeys { ; Creates hotkeys for each key in the array above
spamBF := Func("Spam").Bind(key) ; Bind(BoundFunc) the Key to the Spam function to use it as input for the Hotkey Command
Hotkey, % "$*" . key, % spamBF ; $ to ensure Hotkeys can't trigger themselves
stopSpamBF := Func("StopSpam").Bind(key)
Hotkey, % "~*" . key . " Up", % stopSpamBF
BoundFuncCache[key] := Func("SendBlind").Bind(key)
}
Spam(key){
Send, % "{Blind}{" . key . " Down}" ; Required because ~ can't be used with KeyWait for blocking Auto-Repeat
if (spam){
tmp := BoundFuncCache[key] ; SetTimer doesn't support function references in expression mode, requiring a temporary variable and regular variable dereferencing
SetTimer, %tmp%, 50 ; Delay between activations in ms. 50ms = 20 times per second. Should be good for most use-cases
KeyWait, % key
}
}
StopSpam(key){
tmp := BoundFuncCache[key]
SetTimer, %tmp%, Off
}
SendBlind(key){ ; Function-wrapper for the Send Command
Send, % "{Blind}{" . key . "}"
}
^L:: ; Ctrl+L toggles Spam On/Off
spam := !spam
return