Simple keypress script with loop in Autohotkey - autohotkey

Whenever you press w you get into a loop that press e around every 10 seconds. Another button has to be pressed to get out of it again at any moment (and making it possible to start over again). This is what I have so far:
w::
Loop
{
Send, e
Random, SleepAmount, 9000, 10000
Sleep, %SleepAmount%
x::Break
}
Return
I don't understand why it is not working yet. It presses e once and doesn't do anything anymore after that.

x::Break
is the short form for
x::
break
return
and therefore terminates the current subroutine. Never define a hotkey within any other execution bodies. Instead, define the x-hotkey outside the w hotkey and make it stop the loop.
Example using goTo (note that goSub is different for the latter will not terminate the subroutine):
w::
Loop {
send e
Random, SleepAmount, 9000, 10000
Sleep, %SleepAmount%
}
after_loop:
return
x::
goTo after_loop
return
; or, more compact:
; x::goto after_loop
Since gotos are pretty bad programming style, you might consider using a timer (instead of the loop). But to be honest, it's not worth it because your sleep amount is not fix.

Related

AutoHotKey, Repeat key press

All I want is a simple keypress of lowercase "v" 4 times with a 1 second delay between. I have tried so many iterations of this simple-sounding action to no avail and the most infuriating thing is that I can get it to work with a capital "V" – but that also emulates a shift+v key press. I just don't understand.
v::
Loop, 4
{
sleep 10
SendInput {V} ; capital V works perfectly fine but includes Shift+v, I don't want a shift
sleep 1000
}
return
Whereas ...
v::
Loop, 4
{
sleep 10
SendInput {v} ; Lowercase v, Does absolutely nothing...
sleep 1000
}
return
What am I missing?
Try
$v::
Loop, 4
{
sleep 10
SendInput v
sleep 1000
}
return
The $ prefix works by forcing the keyboard hook to be used and prevents the Send command from triggering the hotkey itself.
Alternative #UseHook can be specified somewhere above, so the keyboard hook is implemented for every hotkey after it
Another solution:
#IfWinActive ahk_class your_app_ahk_class
$v::
SetKeyDelay, 1000
Send {v 4}
Return
I don't know what's the purpose of this, but since the shortcut does not include any control keys, it might be better to limit this to a specific app. The first line in my snippet tries to achieve this.

AHK Click event while Toggled

I've found AHK codes that separately work "ok" but I need one inside another. So, I have:
1. The first rapidly fires click when you hold down the left mouse button:
~$LButton::
While GetKeyState("LButton", "P"){
Click
Sleep .1 ; milliseconds
}
return
2. The second is a toggle script that sends the same left mouse button firing events but can be toggled on and off with a button press (F8 in this case)
toggle = 0
#MaxThreadsPerHotkey 2
F8::
Toggle := !Toggle
While Toggle{
Click
sleep 1
}
return
What I need is: when I push F8 once, I want my left mouse button to fire click events rapidly while holding it. When I push F8 again it should do nothing. If it's important, I need those clicks while holding Ctrl in-game.
I've read a bit about AHK and tried this code but it doesn't work any close to what I want:
toggle = 0
#MaxThreadsPerHotkey 2
F8::
Toggle := !Toggle
If Toggle{
~$LButton::
While GetKeyState("LButton", "P"){
Click
Sleep .5 ; milliseconds
}
}
return
This one gives me errors about missing "return" but I tried a lot of merging variations.
Also, I've read a lot about MaxThreads and still don't know why there should be 2 and what is it for.
Firstly, not sure what amount of time you're trying to give the Sleep commands, but decimal numbers wont work. Just whole numbers, and they're in milliseconds. .1 and .5 are likely interpreted as 0, not sure though. Also, the sleep command isn't as accurate as you may think it is. Read the remarks section in the documentation for more.
Secondly, you shouldn't loop inside hotkey labels. It's bad practice due to AHK not offering true multithreading.
Though, at the end of the day, it wont make any difference if this is all your script it.
For future reference if you want to start writing nicer and bigger scripts, I'll show you the usage of timers though. They should be used for this.
LButton::SetTimer, MyCoolFunction, 0 ;when LButton is clicked down start a timer with the shortest possible period
LButton Up::SetTimer, MyCoolFunction, Off ;when LButton is released, stop the timer
MyCoolFunction()
{
Click
}
And same for your toggle version, you don't want to loop inside a hotkey label:
F8::
toggle := !toggle
if(toggle) ;if true
SetTimer, MyCoolFunction, 0
else
SetTimer, MyCoolFunction, Off
return
MyCoolFunction()
{
Click
}
And if you don't know what toggle := !toggle actually is, and want to know, you can read a previous answer of mine here. It also shows how you can compact that code down to just one line of code. And also explains why there's no need to define the variable toggle on top of your script (as you were doing).
And about #MaxThreadsPerHotkey2:
It's because AHK doesn't offer true multithreading. When you're looping side a hotkey definition, that hotkey is completely locked up. You can't run the hotkey again to stop the loop. Unless, you set the hotkey to use more threads (better to call them instances) than one.
That way you're able to launch the hotkey again and you're able to change the toggle variable's value so you can stop the loop.
But again, you shouldn't loop inside hotkeys. If you use a timer, like I showed above, you don't need to worry about this dirty workaround.
And then to the new code you want to create.
Well first about what went wrong in your attempt. I guess it was a good thought, but it's not even close. Hard to say what exactly is wrong in it, since it's not even close to working. I guess what I can say is that hotkey labels (hotkey::) are evaluated once when the script starts, and then never again. So you can't put them inside some runtime logic. The Hotkey command would be used for that.
Luckily your problem is actually much simple than that. You don't need to mess around with the Hotkey command.
All you're looking to do is toggle the hotkeys on/off. Suspend is used for that like so:
F8::Suspend
And now the script's hotkeys (and hotstrings) toggle on/off every time you press F8.
So here's your final script:
LButton::SetTimer, MyCoolFunction, 0 ;when LButton is clicked down start a timer with the shortest possible period
LButton Up::SetTimer, MyCoolFunction, Off ;when LButton is released, stop the timer
MyCoolFunction()
{
Click
}
F8::
Suspend
SetTimer, MyCoolFunction, Off ;set the timer off just incase we hadn't released LButton before we hit F8
return

How to repeat an ongoing script if the same input is given in autohotkey

i use this code to send more mouse wheel scrolls using one notch, the problem is when an input is sent again before the loop is finished, it is not taken in account and the loop needs to end before relaunching
I tried looking for a way to break up the code if there is an input and relaunch it with no success
WheelUp::
loop 100
{
sendInput {WheelUp}
sleep 2
}
return
Expected behavior: on the 50th loop, if input is given again, reset the loop count (stop and relaunch the script) so the total repetitions would be 150
Try this approach.
You have to change the #MaxThreadsPerHotkey setting.
This setting is used to control how many "instances" of a given hotkey
or hotstring subroutine are allowed to exist simultaneously. For
example, if a hotkey has a max of 1 and it is pressed again while its
subroutine is already running, the press will be ignored.
That will allow the hotkey to "interupt" itself.
Each time you scroll up while the hotkey is already active it will add another 100 scrolls to the loop.
#MaxThreadsPerHotkey 2
WheelUp::
if (counter > 0) ; this means the hotkey is already active
{
counter := counter + 100 ; in that case we just add another 100 scrolls to the loop
return
}
else ; this means the hotkey is not active, we start fresh with 100 scrolls
{
counter := 100
}
while, counter > 0
{
sendinput, {WheelUp}
sleep 40 ; change this to your requirement
counter--
; tooltip, % counter ; un-comment this line for testing
}
return
~WheelDown::counter := 0 ; extra hotkey to stop the loop immediately if needed
I cannot respond to the other comment but WheelUp::Send, {WheelUp 100} won't break the current 100 Wheelups and will add another 100 WheelUps. So if the input was given again on the 50th WheelUp, it would still send 200 WheelUps.
In the loop you will need a conditional statement to detect if a physical scroll up was detected recently, and then restart the loop if it does. For most keys you would use GetKeyState as the check in the if statement, however the mouse wheel has no state to detect.
In my example code, the Enter button will restart the loop. I'm not sure how to restart the loop if trying to use WheelUp again since there is no state to detect, and A_TimeSincePriorHotkey is not reliable because the loop is sending WheelUps.
WheelUp::
loop 100
{
sendInput {WheelUp}
sleep 2
if (GetKeyState("Enter", "P"))
Goto WheelUp
}
return

hotkeys does not work when send is in loop

Assume this code:
Loop
{
if enabled
Send, /
}
m::
enabled := !enabled
Return
I want to toggle sending / to a Notepad for example. But if I run this code by pressing M on keyboard, then pressing the M key again does not disable sending.
Looks like the send command in the Loop cause this issue since Ive tried using msgbox which does not disable the m key.
How can I make this code to work? (SendInput and Play does not work too)
It's because your loop is blocking any other execution. Unless that loop is the only thing in your script, you generally want to avoid using loops and use timers instead.
Timers don't block further execution but act more like their own thread. Here's an example using a timer:
slashTimerActive := 0
m::
if (!slashTimerActive)
SetTimer, SendSlash, 100 ; Call the sub every 100ms
else
SetTimer, SendSlash, Off
slashTimerActive := !slashTimerActive ; Flip the variable
return
; Subroutine
SendSlash:
SendInput, /
return

AHK Prevent Multiple Hotkey Triggers

It's a quite simple code.
I just want my Mousewheeldown to Send P only once.
Even if I scroll it like 3 times, I only want it to send P only once every 100ms or sth.
Here is my really small bit of code so far:
SetKeyDelay , -1, 50
#NoTrayIcon
#NoEnv
#persistent
#MaxMem 2
WheelDown::
Send {p}
return
Sleeping a bit after sending the keystroke would solve this problem.
WheelDown:
Send, p
Sleep, 100
return
Also, you do not need to put the p between {}-s (curly braces), as it is not a special key.
The sleep command takes it's parameter as milliseconds, so if for example you would want to allow it only one 'p' in a second, you would write Sleep, 1000.