AHK using three or more key presses to trigger hotkey/hotstring - autohotkey

I know this question has been discussed before (several years ago). I am hoping there is a new way to achieve this goal. I would simply like to use three or more keys to trigger an AHK script. For example, CTRL + SHIFT + Q to do a thing
In "fake" ahk, this would be
^+Q::
...
return
Of course, this type of command errors. The previous solution is quite verbose compared to the task and involves checking the state of some key etc. Is there a shorthand way of accomplishing this?

Please describe the error you get. The example you give does not produce an error for me.
But for one thing, a shift modifier on "Q" has no add'l effect (a Shift on "q" gives "Q").
But in the meantime, AHK is quite forgiving and any of these work:
^+Q::
msgbox %A_ThisHotkey%
return
^Q::
msgbox %A_ThisHotkey%
return
^+q::
msgbox %A_ThisHotkey%
return
And in general two or even three modifier keys (Ctrl+Alt+Shift) is quite normal in practice. if you want two or more non-modifier keys, you will have to track key state. I guess you know that from your reference to what was "discussed before (several years ago)."

Related

Conditional Key Remapping AHK

I would like to remap the 'j' key so that it presses n when ergo is true, or y when it is false with AutoHotKey. When I remap normally using "j::n" for example, shift+j outputs a capital N, and so do other modifiers with the 'j' key. However, my code below only works when the letters are pressed without modifiers. Is there a way to get around this, and conditionally remap keys with AutoHotKey?
j::
if (ergo) ;inverted use of the ergo variable to make the code more efficient
Send {n}
else
Send {y}
return
You only want to wrap characters which have a special meaning in a send command in { }. Basically escaping, if you're familiar with that what is.
So you don't want to wrap n or y in { }. It can even result in undesired behavior.
There are quite a few approaches for this. Can't say which is best, since don't know what your full script is like.
I'll present two options that I'd guess to be most likely best approaches for you.
Firstly, the send command way like you tried. Just doing it right:
*j::
if (ergo)
SendInput, {Blind}n
else
SendInput, {Blind}y
return
So, usage of the *(docs) modifier so the hotkey works even if extra modifiers are being held down.
And then usage of the blind send mode so which ever modifier keys you may be holding when triggering the hotkey will not be released.
Also switched over to SendInput due to it being the recommended faster and more reliable send mode.
Second way would be creating a context sensitive hotkey with #If(docs).
#If, ergo
j::n
#If
j::y
This is a convenient and easy approach. But could possibly result in other problems.
Why? Well #If comes with some downsides which you can read more about here, but long story short:
You likely wont have any trouble unless you have a more complicated script.
When I remap normally using "j::n" for example, shift+j outputs a capital N, and so do other modifiers with the 'j' key. However, my code below only works when the letters are pressed without modifiers.
Looks like you are looking for the Wildcard * modifier.
From the docs:
Wildcard: Fire the hotkey even if extra modifiers are being held down. This is often used in conjunction with remapping keys or buttons.
So after this change, your code would look something like:
*j::
if (ergo) ;inverted use of the ergo variable to make the code more efficient
Send {n}
else
Send {y}
return

AutoHotKey Recognizing itself as an input

I was making a real-time translator(not translate btw), that when I type a letter in my language, It automatically types an alphabet of corresponding sound.
r::
Send, t
return
You get the Idea, when I press 'r', It writes 't' instead.
Then the problem happened. As I added more and more letters, I stumbled upon this part:
f::
Send, r
return
When I type f, it types r.
Then it thought that I typed r (in fact, the program typed it), so it types t.
Ultimately, it types t when I press f. (which is not intended)
Is there a parameter or something that prevents it from recognizing its own key input as an input?
(also, I don't want to do this:
^r::
Send, t
return
Sure, This will solve the problem, but I have to hold the Ctrl key the whole time.)
Good that you found the remapping syntax, it's very handy and is indeed what you want to use for this.
And for future reference, to solve the problem of Sending triggering hotkeys, you want to use $.
$2::MsgBox, % "You wont see me if you press 1"
1::SendInput, 2
I solved it, just had to do this
r::t
f::r
...

Please, would you explain me this simple AHK script?

I modified a simple old AHK script and that basically turns a "." (dot) key pressed to a ":" (colon) as output.
The problem was I needed to map both keyboard dots (the numpad one and the one near the right Alt key). Well I somewhat guessed by trial how to modify the code and it works but I would like to understand why and how!
I feel AHK documentation has improved much in recent years, but I can't find a way to get to the bottom of this. Here's the code I wish you to explain (my troubles are primarily on understanding the last line and its connection with the previous ones):
SC034::
NumPadDot:::
:::NumPadDot::SC034
Please, provide links to AHK official documentation whenever possible, I'd very appreciate!
Thank you
SC034:: ; Label for scancode 34 ("."). Since there's no definition
; on this line it's grouped with the next definition
NumPadDot:: : ; NumPadDot and SC34 will map to ":"
:::NumPadDot::SC034 ; defines a hotstring that outputs "SC034" when you type ":NumPadDot"
Autohotkey Documentation for Hotstrings
Autohotkey Documentation for Hotkeys

autohotkey does not exist in current keyboard layout - solution examples

I have written a python script for my co-workers, and then created an autohotkey script to run it every time someone presses Ctrl+LShift+Y. Looks something like this:
^+y::Run helper.py
The python script is fine, but the ahk script doesn't work on all the computers. Sometimes it works fine, and sometimes you get this error:
^+y does not exist in current keyboard layout
Now, searching the web this seems to be a problem with multi-language keyboards (we're using both Hebrew and English), because different languages means a different layouts (I guess?). I also found someone explaining that to solve this you need to use scan codes instead of the usual ^ and + and so on (I'd link to it but I cannot seem to find it now).
This all vaguely makes sense to me on a theoretical level, but when I want to realize it with actual code, I don't really know what to do. To me it seems as if this topic is hardly discussed (with the few exceptions being lacking in examples or hard to understand), so I'd love an answer that would include the following:
some simple way of determining the scan code for a key. This should preferably be a pythonic solution (and just out of curiosity, I'd love to know how to do this with linux as well). This is probably the easier part (but I think is an inherent part of a complete answer).
This is the important part: examples of how you implement that scan code in an autohotkey script, including edge-cases (if there are any).
Question 1
As you want to use the key with autohotkey, it makes sense to use autohotkey detect the key in the first place. Obviously this method works only on windows where autohotkey is running.
Write a Autohotkey script with this line and run it.
#InstallKeybdHook
Press the key you want to examine.
Open the script menu by right clicking the icon of the script in the right lower corner of your screen.
Select OPEN, then from the Menu "View / Key history and script info"
There is a line for each keypress.
First column is the VK (Virtual key) code, next is the scancode.
For example for CAPSLOCK the VK is 14 and the Scancode 03a
Question 2:
#InstallKeybdHook
VK14::
msgbox, you pressed capslock!
return
OR
#InstallKeybdHook
SC03a::
msgbox, you pressed capslock!
return
both work.
Note that you can combine two keys into a hotkey by combining them with & (but not 3)
#InstallKeybdHook
RShift & SC03a::
msgbox, you pressed Rshift capslock!
return
You can modify a Scancode with + and ^
#InstallKeybdHook
^+SC02C::
msgbox, you pressed Ctrl Shift and Y(maybe)!
return
Further info about this is on the page "List of Keys, Mouse Buttons, and Joystick Controls" of the autohotkey help file that comes with the default installation.

Autohotkey send key, let it trigger other hotkeys (#InputLevel confusion)

I want to create a hotkey that sends some key, and then another hotkey for that very just sent key, that in turn sends a third key.
That seems to be possible, using #InputLevel:
#InputLevel 1
a::b
#InputLevel 0
b::c
The above works as intended: By pressing a I get c.
However, I want not only to remap the first key: I want to do more before sending the key. So I thought I could just rewrite the above a little bit:
#InputLevel 1
Hotkey *a, foo
#InputLevel 0
b::c
foo:
; Do something more here …
SendInput {Blind}b
return
The above however does not work as intended: By pressing a I get b (not c).
Update: #Robert Ilbrink reminded me that you can execute more than one command, without using the Hotkey command:
#InputLevel 1
*a::
; Do something here …
SendEvent {Blind}b
return
#InputLevel 0
b::c
The above does give the intended effect: Pressing a results in c. However, I have to rephrase my problem. I guess the problem is: I need to set the hotkeys dynamically, which means I have to use the Hotkey command with a label (as far as I know). (Also notice that I use SendEvent above. Using SendInput produces a b. Odd.)
(End of update.)
I know there is a companion command to #InputLevel—SendLevel—which might be relevant. I've tried putting it many places but it has never made any difference.
So, that was the reduced, theoretical example. Remapping a to b to c is of course useless in reality (and the net result could of course be achieved by a::c). On to my use case. Just keep in mind that if it turns out that the "real" solution means doing what I'm trying to do some other way, I'm still interested in knowing more about #InputLevel and SendLevel, and why my example does not work as intended.
I'm working on implementing dual-role modifier keys. For example, send ) when pressing RShift alone, but RShift+key when pressed together with some other key. Basically, RShift on keydown, and RShift up and ) on keyup. However, that has one flaw: Even when combining RShift with some other key, ) is still sent. So the script needs to know when there has been a combination. My solution is to add hotkeys to all letter keys, the arrow keys and some other keys, like this:
for comboKey in filteredComboKeys {
Hotkey % "*" comboKey, Dual_comboKey
}
; Later in script:
Dual_comboKey:
; The following function lets the dual-role modifier keys know that they have
; been combined with another key (and a few other things, which I don't think
; are important for the issue.)
Dual.combo() ;
key := Dual.cleanKey(A_ThisHotkey)
SendInput {Blind}%key%
return
The above solution works very well for my purpose—except that the break all remappings and other hotkeys the user might have made: These simply never occur.
Why not:
a::
; Do something
Send, b
Return
As far as I can gather, #InputLevel doesn't bite on the Hotkey command. However, I stumbled on a solution for one of the snippets I originally posted:
Hotkey *a, foo
b::c
foo:
; Do something more here …
SendLevel 1
SetKeyDelay 0 ; Optional; Only affects this hotkey.
SendEvent {Blind}b
return
Note that SendEvent must be used. SendInput produces b. SendPlay produces nothing at all. I don't know why.
However, this technique won't work if you want to send the hotkey itself. Then you end up in an infinite loop. Using the keyboard hook does not help, since SendLevel overrides it.
So, again I have an answer the solves one of the initial examples, but does not help me in reality. I need to send the hotkey itself. I guess I have to let the user remap their keys using my script. Sigh.
Update:
I've published my dual-role modifiers script now, in case anyone is interested in more details, and how I deal with the problems.
Update:
I've updated my dual-role modifiers script. I now stay away from the Hotkey command. It's easier when dealing with this kind of thing, I think.
By now (starting Autohotkey 1.1.01), this can be achieved quite easily like so:
~Shift up::
IfInString, A_PriorKey, Shift
{
Send )
}
return