Eval / RegExp listen with Autohotkey - autohotkey

I think this must be possible but I can't find it in the documentation anywhere on autohotkey
I want to do something like:
[a-z0-9]::
SoundPlay, %A_WinDir%\Media\[\1].wav
SoundPlay *-1
i.e. listen for all keys a to z and 0-9 find the relevant wav file (a.wav etc..) and play it. If it can't be found Play a default sound.
Is this way out of AHK's league? Should I stick to doing this in python??!
Many thanks in advance

You can use the Hotkey Command to assign all possible hotkeys to the label.
The first loop here uses a trick with converting values to their ASCII codes, doing math, and converting them back to characters. The logic works like, "What is the 5th character after 'a'?" -- to which it replies 'f'.
#Persistent
Loop, 26
Hotkey, % Chr(Asc("a") + A_Index - 1), MyLabel
Loop, 10
Hotkey, % A_Index - 1, MyLabel
return
MyLabel:
MsgBox You pressed %A_ThisHotkey%
return

I'm not aware of a way to use a regex to specify the hotkey, but this is how I've done it in one of my scripts:
#::
a::
b::
c::
; (all other alpha keys...)
; Pass key on, if not in subscription list
If(in_edit_screen())
{
; Handle caps lock
If(GetKeyState("Capslock", "T"))
{
; Send uppercase letter
StringUpper key, A_ThisHotkey
Send %key%
}
Else
{
Send %A_ThisHotkey%
}
Return
}
; etc.
Return
This is part of a routine that's called whenever a certain window is focused, but the window in question has a few different modes; in one of those modes (indicated by the in_edit_screen flag), I want to just pass the keys through, while in another mode, I want the keys to do something special. A_ThisHotkey is the variable that tells the routine which key/key combination the user actually pressed.

Why not launch a script where you use:
Input, CI_KeyVar, I L1
and then read-out which key was pressed (variable = CI_KeyVar), do your thing (check if the ascii code falls between 0 and Z because this input will act on any input) and re-launch the script to wait for the next key press....
The way out would be to trigger on e.g. the esc key, so the script stops.

Related

How to set combination threshold in Autohotkey

There is a description of how to define a custom combination in AutoHotkey:
You can define a custom combination of two keys (except joystick
buttons) by using " & " between them. In the below example, you would
hold down Numpad0 then press the second key to trigger the hotkey:
Numpad0 & Numpad1::MsgBox "You pressed Numpad1 while holding down Numpad0."
Numpad0 & Numpad2::Run "Notepad"
But I couldn't find how to set the threshold. for example, I want Numpad0 & Numpad1 only to happen when user presses Numpad1 in less than 300ms after pressing Numpad0.
You could do something like this for example:
Numpad0::
if (!PressedAt)
PressedAt := A_TickCount
return
Numpad0 Up::PressedAt := 0
#If, A_TickCount - PressedAt < 300
Numpad1::MsgBox
#If
So use A_TickCount(docs) to compare times.
And the if-statement is there because of Windows' key repeat functionality. Without it, the PressedAt time would get set constantly while Numpad0 is held down.
Also, 0 is false, so we can conveniently use the PressedAt variable in the if-statement as well.
Could've also be done without a context sensitive hotkey for Numpad1, it just makes the key retain its original functionality.
If #If were to cause you trouble, you can switch over to a normal if-statement check inside the hotkey label.
And be sure to add the ~ prefix(docs) to the Numpad0 hotkey if you want.

Remap keys in loops

Background: I'm trying to have a f-mode and d-mode which means if I press down the f key and press another key like i then nothing happens excepts a shortcut. let say it will send Up key instead of f and I.
Issue: how I can remap a pressed key (I in my example) to a shortcut (Up as example)?
Code:
d::
f::{
;...
loop{
if !GetKeyState("f","p") && !GetKeyState("d","p"){
break
}
if GetKeyState("i","p") {
OutputDebug "i"
send "{up}"
continue
}
; ...
}
}
Looks like you want to make a custom combination.
From the Docs:
You can define a custom combination of two keys (except joystick
buttons) by using " & " between them. In the below example, you would
hold down Numpad0 then press the second key to trigger the hotkey:
Numpad0 & Numpad1::MsgBox You pressed Numpad1 while holding down Numpad0.
Numpad0 & Numpad2::Run Notepad
But also note:
The prefix key loses its native function: In the above example,
Numpad0 becomes a prefix key; but this also causes Numpad0 to lose its
original/native function when it is pressed by itself. To avoid this,
a script may configure Numpad0 to perform a new action such as one of
the following:
Numpad0::WinMaximize A ; Maximize the active/foreground window.
Numpad0::Send {Numpad0} ; Make the release of Numpad0 produce a Numpad0 keystroke. See comment below.
This is to prevent holding down a key from spamming inputs while you wait to press the second part of a key combination. So essentially, your 'f' and 'd' keys will now perform their normal functions when you release them instead of initially pressing them down.
Anyways, the code would become:
f & i::
d & i::
Send {Up}
return
f::f
d::d

Verifying pressed keys in AutoHotKey

I've been searching for quite a while and took a look at alot of documentation on AHK, but haven't quite found out how to do this.
So basically, anytime CTRL + another key is pressed, I want to check what the other key is. When the other key is not one of the numpad numbers, I want to only send the other key. When the key is one of the numpad numbers, I want to send CTRL and the numpad key.
Thanks in advance!
I can only think of two ways. Firstly, assigning every hotkey manually.
loop, 255 {
char := chr(a_index - 1)
try {
hotkey, ^%char%, send_key, char
} catch e {
; many keys in the ansi chart cant be assigned to a hotkey, just ignore the error
}
}
return
send_key:
; A_ThisHotkey contains the pressed combination in a format like ^C. Remove the ^
key := subStr(a_thisHotkey, 2)
sendraw %key%
return
The loop iterates over all ANSI characters. See https://msdn.microsoft.com/en-us/library/aa245259(v=vs.60).aspx for a list. It is only a short form for
^a::send a
^b::send b
etc., 255 times. This approach doesn't affect space, numpad numbers (which you want), # and many more, and it also cannot differentiate between lowercase and uppercase letters. But maybe it'll do for you.
Secondly, using input. Read the doc page about it and if you still struggle, edit your question I guess. I don't know if or how this will work for key combinations

Toggle keys in autohotkey

My goal is mapping WASD to the 4 arrow buttons on the keyboard and make 1 'Suspend' the script while z exits it. That was easy enough. Now I'd like a and d only apply conditionally. I look at the docs and I have no idea what's wrong here. I think I'm either using the if statement wrong or Left/Right doesn't work in if statements in which case I have no idea what to do.
#SingleInstance
a::if(UseAD) Left
d::if(UseAD) Right
1::Suspend
2::UseAD:=!UseAD
w::Up
s::Down
z::ExitApp
Try this:
#SingleInstance
$a::Send % UseAD ? "{Left}" : "a"
$d::Send % UseAD ? "{Right}" : "d"
1::Suspend
2::UseAD:=!UseAD
w::Up
s::Down
z::ExitApp
Okay now a break down.
Your If statement wasn't being evaluated correctly. The following line of code after the condition is met is what is run. Like so:
If (true)
do this
Your Hotkey is also wrong for a Multi lined statement. Essentially a single lined Hotkey is a basically a Send command for whatever key or keys specified on that line (unless you specify an assignment/function/command etc...) it will act as a Send Command does. To have an If evaluation requires multiple lines. When you specify a hotkey and you want an evaluation that will require multiple lines you, and must return from a Multi-Lined Hotkey same a Sub Routine:
a::
Code goes here
more code
etc..
Return
b::AnotherHotkey
etc..
Okay so lets plug this Logic in with your code:
#SingleInstance
a::
if(UseAD)
Left
return
d::
if(UseAD)
Right
return
1::Suspend
2::UseAD:=!UseAD
w::Up
s::Down
z::ExitApp
If you run this you'll get an Error about the Text Left... that is because instead of our Hotkey acting as Send command it's acting as a Sub Routine so we have to specify Send command with Left:
a::
if(UseAD)
Send, Left
return
But this isn't correct either, now it's sending the word Left instead of the Key left.. so again we have add Brackets around our named key like so:
a::
if(UseAD)
Send, {Left}
return
Okay, now a and b are not being sent when UseAD is False, so we must Send them by specifying with Else like so:
a::
if(UseAD)
Send, {Left}
else
Send, a
return
Now we run this code and press a or b get an Max Hotkeys reached message because our code is triggering the Hotkey in an Infinate loop. We need to specify our code in such a way that it will not trigger itself, like so:
$a::
if(UseAD)
Send, {Left}
else
Send, a
return
If you notice we have added a $ symbol in front of our hotkey, this adds a keyboard Hook to that Hotkey and will prevent the the script from triggering that hotkey itself. This is now a complete working script but looks entirely different from the first code I posted. That is because I like typing less lines, if I can.
In the first code sample I'm using a Forced Expression % on the Send command and Ternary ? : to evaluate UseAD and if true send Left key if false send the letter, exactly the same as above code, just more concise.

AutoHotKey code to receive ctrl+x while pressing ctrl+c twice

AutoHotKey code to receive CTRL+X while pressing CTRL+V twice
Can anyone help with this?
Many thanks!
Assuming we are talking about Ctrl+C, not V, and assuming you want to keep the original Ctrl+C function but also use it for Ctrl+X when pressing twice in a short time:
#persistent
Transform, cC, Chr, 3 ; store the value of ctrlC into cC.
hotkey, $^c, ctrlC, ON ; this is basically the same as if to say $^c::..., but only deactivable
return
ctrlC:
hotkey, $^c, ctrlC, OFF
input, key, M L1 T0.2
hotkey, $^c, ctrlC, ON
if errorlevel = timeout
send ^c
else if key = %cC%
send ^x
else
send %key%
return
should do..
also see Input for further information. I used this little hotkey-command-trick in order to temporarily disable the Ctrl+C-Hotkey, because otherwise input would not recognize the second c
In this example, I set timeout to 0.2 seconds. Change it to your convenience.
About your capslock idea - sounds like a good idea to me, but anyways, we're not a code-provider network. The command getKeyState should help you started.