Loop Error in AutoHotkey [closed] - autohotkey

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I'm trying to make a script push space every 50ms but it only does it every time I press the key. I want it to loop continuously. Here is the code:
Loop
{
^p::
Send, {Space}
Sleep, 50
return
}

Assuming you want to start it by Ctrl+P, you simply have to put the loop inside the hotkey execution body:
^p::
loop {
Send, {Space}
Sleep, 50
}
return
Note: It's good programming style to end your hotkeys with return, but please know that this return will never be reached! (never-ending loop)
For activating and deactivating the space sending, you might want to use setTimer like:
#persistent
active := false
^p::
if(active)
setTimer, sendSpace, off
else
setTimer, sendSpace, 50
active := !active
return
sendSpace:
send {space}
return
I don't know why but it's not working. When I press it again nothing happens and it continues pressing space.
Since it's working for me, I guess your cpu/ram is to blame. I'll quote my answer from this question here: SendInput won't work at high speed :
Looks to me like an AutoHotkey bug most likely, or wrongly sent {space}s because your RAM can't handle the heavy programs well enough.
Things I can think of that you could try:
buy a better computer.
use setBatchLines, 1ms (in the very beginning of your script), making your script sleep 20ms each millisecond and therefore consuming less cpu. This might clear AutoHotkey's mind.
SetKeyDelay, 50 might also help.
Sometimes, a pressed down modifier such as ctrl or alt, slows down windows drastically. This was at least the case under Windows Vista. So you might wanna get rid of the ^ (Ctrl) and change this hotkey to a plain p:: hotkey, for example.
If this is still no option for you, you could try this:
#persistent
active := false
^p::
setTimer, sendSpace, 50
hotKey, p, stopSendSpace, ON
return
sendSpace:
send {space}
return
stopSendSpace:
setTimer, sendSpace, OFF
hotkey, p, stopSendSpace, OFF
return
This will behave like the above (activation by ctrl+p again), but the deactivation happens with P (no Ctrl), without overriding the default behaviour

Related

No 'OnMouseClick'-Trigger?

I wanted to have a window closed by Autohotkey as soon as I click somewhere else and was confident that there would be a fitting trigger / listener like e.g. an OnMouseClick - function. But seems not; MouseClick just actively clicks the mouse. So the best way I could figure was this:
SetTitleMatchMode, 2
Loop{
WinGetActiveTitle, OutputVar
if (OutputVar <> "My_Window_Name_to_close.ahk"){
WinClose , AutoTasks 2.ahk
}
Sleep, 1000
}
return
While the above code does work, it leaves me unsatisfied that that's really the best AHK can do (?), because those few lines of code, while not a real problem, do cost CPU and memory (according to my Task Manager e.g. more than Skype or my Cloud Background service). The 1-second-sleep also introduces a discernible lag, but without it, the loop would even cost 2% CPU!
Can you come up with a more efficient / AHK-native solution (except for further increasing the Sleep-time)?
I've found the answer myself in the meantime:
SetTitleMatchMode, 3 ; exact match
WinWaitNotActive, My_Window_Name_to_close.ahk
WinClose, My_Window_Name_to_close.ahk
Even though you found a solution to your problem, I wanted to provide an answer to the actual question of detecting a click. That can be done using a hotkey for "LButton" in combination with the tilde "~" modifier like so:
~LButton:: ; The "~" sets it so the key's native function will not be blocked
GoSub CheckActiveWindow
Return
CheckActiveWindow:
sleep, 1 ; Wait a small amount of time in case the desired Window is changing from inactive to Active
WinGetActiveTitle, OutputVar
if (OutputVar <> "My_Window_Name_to_close.ahk"){
WinClose , AutoTasks 2.ahk
}
return

AutoHotKey while loop breaks

$*e::
While GetKeyState("e","P")
{
Send, {Blind}e
Sleep, 10 ; every 10 miliseconds
}
Return
Every few uses the while loop breaks and keeps spamming forever until i press the bind again.
Although I am still unable to replicate this issue, based on what #Charlie Armstrong mentioned, the script may work properly if SendEvent is used instead of a SendInput. The following modified script would still keep the SendMode as Input, but would override only the Send statement inside the while loop.
$*e::
While GetKeyState("e","P")
{
SendEvent, {Blind}e
Sleep, 10 ; every 10 miliseconds
}
Return
More information about Send and SendMode can be found here and here.

AutoHotKey WinActive returns wrong value when changing window focus

In my AutoHotKey script I'm using #IfWinActive to detect if the Roblox window is in focus, and then press the number 1 button whenever left clicking the mouse, like this:
#IfWinActive, Roblox
LButton::
MouseClick, Left
SendInput, {1}
return
#IfWinActive
It works great, except for when I'm clicking out of the Roblox window back to another window. It still fires this code on the first click, resulting in it typing the number 1 into Notepad (or whatever window I switch focus to).
I figured that when I'm clicking on Notepad the focus is still on the Roblox window, which is why the code still fires. So I tried changing the code to this:
#IfWinActive, Roblox
LButton::
Sleep, 100
if WinActive("Roblox")
{
MouseClick, Left
SendInput, {1}
}
return
#IfWinActive
Assuming that by the time the Sleep finished the focus would have shifted to the Notepad window and If WinActive("Roblox") would return false, but it still returns true and types 1 into Notepad.
I also tried using StartTimer and a label, thinking that maybe the Sleep wasn't asynchronous, but that has the same problem as well.
Anybody know how to get around this issue? Thanks in advance!
The main problem in this case is that the hotkey is fired immediately after LButton is pressed down and the Roblox window is still active.
The only solution I see is to fire the hotkey upon release of the LButton using the tilde prefix (~) to prevent AHK from blocking the key-down/up events:
#IfWinActive, Roblox
~LButton Up:: SendInput, 1
#IfWinActive
There are a couple of ways we can achieve this. TL;DR for solution, check the yellow part of this post.
Firstly I'll address the problems in your code:
Usage of MouseClick over Click. Technically nothing wrong, but Click is said to be more reliable in some situations and easier to use. Looks cleaner as well.
Wrapping 1 in {} is not needed and does nothing for you here. In some cases you may even produce unwanted behavior by doing this. In a send command, {} is used to escape keys that have special meaning, or to define keys that you can't just type in. More about this from the documentation.
Having a somewhat of a bad WinTitle that you're matching against. Again, nothing technically wrong, but right now you match any window that starts with the word Roblox. Shouldn't be too hard accidentally match the wrong window.
A quick and a very effective solution would be matching against the process name of your Roblox window.
So #IfWinActive, ahk_exe Roblox.exe or in an if-statement if (WinActive("ahk_exe Roblox.exe")) (assuming that's the process' name, I have no idea)
For an absolutely fool proof way could match against the hwnd of the Roblox window. However, that's maybe a bit overkill and you couldn't really use it with #IfWinActive either. An example I'll write below will use this that though.
However, problems 1 and 2 can be entirely avoided by doing this neat way of remapping a key (remapping is pretty much what you're doing here).
~LButton::1
Ok, so why does that work?
key::key is just the syntax to easily do a basic remap, and with ~ we specify that the hotkey isn't consumed when it fires.
Cool, but now onto the actual problem you're having.
So what went wrong with the sleeping thing? Well since you're consuming the hotkey, all you're actually doing is firing the hotkey, waiting 100ms, then checking if Roblox is active. Well yes, it will still be active since nothing was ever done to switch focus away from it.
If you were to not consume the left clicking action, it would work, but it's definitely not a good idea. You do not want to sleep inside a hotkey statement. AHK does not have true multithreading and unless you would've specified a higher #MaxThreadsPerHotkey for your hotkey, all subsequent presses of the hotkey would be totally ignored for that 100ms.
So yes, with specifying a higher amount of threads that can run for that hotkey, it would kind of make this solution work, but it's still bad practice. We can come up with something better.
With timers you can avoid sleeping in the hotkey statement. Sounds like you tried the timers already, but I can't be sure it went right since code wasn't provided so I'll go over it:
#IfWinActive, ahk_exe Roblox.exe
~LButton::SetTimer, OurTimersCallbackLabel, -100 ;-100 specifies that it runs ONCE after 100ms has passed
#IfWinActive
OurTimersCallbackLabel:
if (WinActive("ahk_exe Roblox.exe"))
SendInput, 1
return
And now onto the real solution, to which #user3419297 seems to have beat me to, just as I'm writing this line of text.
Using the up event of your LButton press as the hotkey.
#IfWinActive, ahk_exe Roblox.exe
~LButton Up::SendInput, 1
#IfWinActive
This way the down event has already switched focus of the window and our hotkey wont even fire.
Note that here we unfortunately can't use the key::key way of remapping I described above.
Bonus:
Here's something that could be used if the up event of our keypress wouldn't be desirable, or somehow the window switching of the active window was delayed.
RobloxHwnd := WinExist("ahk_exe Roblox.exe")
#If, RobloxUnderMouse()
~LButton::1
#If
RobloxUnderMouse()
{
global RobloxHwnd ;specify that we're using the variable defined outside of this function scope
;could've also ran the code to get Roblox's hwnd here every time, but that would be wasteful
MouseGetPos, , , HwndUnderMouse ;we don't need the first two parameters
return RobloxHwnd == HwndUnderMouse ;are they the same hwnd? (return true or false)
}
Here we're first storing the hwnd of our Roblox to the variable RobloxHwnd.
Note that Roblox would need to be running before we run this script, and if you restart robox, script would need to be restarted as well.
So adding some way of updating the value of this variable on the fly would be good, maybe under some hotkey.
Then by using #If we're evaluating an expression (in our case, running a function and evaluating its return value) every time we're about to attempt to fire the hotkey. If the expression evaluates to true, we fire the hotkey.
Usage of #If is actually not recommended, and it is good practice to avoid using if at all possible. However, you wont encounter any problems in a script this small, so using #If is going to be very convenient here.
If you were to have a bigger script in which there's a lot of code running often, you'd be likely to run into problems.

Using AutoHotKey to temporarily disable stuck modifier keys

I want to temporarily disable all modifier keys if it seems like one or more of them has become 'stuck' - basically, I need the opposite of Windows' StickyKeys.
I'm working with a Windows tablet (ie. no physical keyboard) with a faulty input device, and it sometimes just jams a bunch of modifier keys, with no predictable trigger. Until I have time to actually troubleshoot the (potentially hardware-level) bug, this script will be a stopgap.
I just need a few seconds to sleep and unsleep the system, since that usually smacks input back into order - unfortunately, the stuck modifier keys interfere with the machine's normal sleep button behavior.
I'm trying to work with the ctrl key first, just to get the concept working, then test for the other modifiers later.
TimerVar := 0
CtrlIsStuck := False
Loop {
CtrlKeyPhysicallyDown := GetKeyState("Ctrl", "P")
If CtrlKeyPhysicallyDown
TimerVar++
Else
TimerVar := 0
If TimerVar > 1 ; TODO: make sane before deploy
{
CtrlIsStuck := True
Break
}
Sleep, 500 ; TODO: make sane before deploy
}
#If CtrlIsStuck
ToolTip, Stuck keys detected; jamming for 15 seconds. Use Sleep button now.
SetTimer, DoReload, 15100
Hotkey, Ctrl, DoNothing
Send, {Ctrl Up}
Sleep 15000
DoReload:
Reload
DoNothing:
Return
I expect this to check in a loop to see if the ctrl key has been held for a span of time, and if it has, bind ctrl to something that does nothing, claim ctrl has been physically released, then wait for a bit.
The 'check if it's held' logic is working, but once it gets past that #If line, things start behaving in a way that, after reading the manual for a while, I still don't understand. While it definitely runs, the Hotkey statement doesn't seem to do anything useful, and the SetTimer line effectively behaves redundantly; I suspect I'm missing something obvious about AutoHotKey's script flow, but I'm unsure what.

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.