Hotkey to remap keys does not trigger hotstring - autohotkey

I have a hotkey and hotstring that don't seem to work together:
9::(
:?ob0:(::){left 1}
To give some context, in one part of the code I remapped all the symbols to the number below them and vice versa so 9 prints the parenthesis (. Later on I put a hotstring that would type a closed parenthesis after an open one and then places the cursor in between.
Seems simple enough because they both work individually but together when I press the key for 9 and press the Spacebar I only get the open parenthesis ( as if the hotstring was ignored.
Am I missing something obvious?

Try using a combination of Send and InputLevel.
#InputLevel 1
9::SendEvent (
#InputLevel 0
;; Add closing parenthesis
:?ob0:(::){left 1}
Explanation
#InputLevel
By default, hook hotkeys and hotstrings ignore keyboard and mouse events generated by any AutoHotkey script. This behavior can be overridden using SendLevel or #InputLevel
By setting the 9 hotkey to a higher InputLevel, it is able to the activate other hotstrings.
SendEvent
Bizarrely, remapping a numkey to its Shift+# equivalent produced no input when #InputLevel 1 was active.
i.e. Couldn't use 1::!, 2::#, 3::#, ..., 8::*, 9::(, etc.
A Send command was used to workaround this remapping limitation
By default, Send and SendEvent are synonymous with each other.
Notes
SendPlay is not affected by InputLevel.
Remarks for Remapping Keys may explain why 9::( wouldn't trigger other hotkeys.
"Although a remapped key can trigger normal hotkeys, by default it cannot trigger mouse hotkeys or hook hotkeys."
Related
#InputLevel, Send, Remapping (Remarks)

Related

Sending keys from ahk so ahk registers them

Is it possible to Send key strokes (e.g., ctrl+alt+b xyz) using ahk and have activated corresponding hotkey ?
E.g., I have ^!b:: that shows MsgBox. I have ^!c:: from which I'd like to Send, ^!b and I want ^!b:: code to be activated.
My goal: I have various hotkeys in combination with Input. I want to have a hotkey to repeat last command.
Thank you

AHK script designed to toggle based on CapsLock Status always toggles on 'off' and I cannot change it to 'on'

(Note: Very new to scripting, borrowed some phrases from other scripts I've online.) I have Carpal Tunnel and play a video game that does not have any key-bind options not set to the F1-F0 keys so I want to rebind the F1-F4 keys to Z-V but only when capslock is enabled, to prevent being unable to type in chat windows and in other programs without closing the script. However, the script re-binds the basic keys to the f-keys ONLY when capslock is OFF, rather than allowing me to change it to ON. Not sure why.
I've tried 'hotfixing' it by rebinding it to Numlock, but when I moved to CapsLock changing 'OFF' to 'ON' did not keep the script from only rebinding the keys while CapsLock was OFF. Not sure why.
$Z::
GetKeyState, state, NumLock, T
if state = D ; NumLock is toggled ON
send, {z}
else
send, {F1}
Return
$X::
GetKeyState, state, NumLock, T
if state = D ; NumLock is toggled ON
send, {x}
else
send, {F2}
Return
etc...
etc...
I expected changing the value 'OFF' to 'ON' would result in the key rebinds only happening during the CapsLock status being toggled on.
Have you checked if you restarted the script after making changes? It's a very common mistake, not only among beginners. According to your example, your keys Z..V should behave as F1..F4 only when the NumLock is toggled off.
Given the nature of your script, you can consider to add the directive #SingleInstance Force which will automatically replace any older instance of your script by a new one each time you run the script again, making testing easier.
You can do conditional binding much easily with an #if directive, which makes the subsequent hotkeys and hotstring only effective when a condition is met.
To check the state of the CapsLock or NumLock keys you can also use the built-in function GetKeyState, which for toggle keys, such as CapsLock or NumLock, with the "T" mode returns either True or False based on the toggle state of the key.
Also, if you want to remap keys, you can simply write the target key's name at the right of the hotkey, which will completely bind the keys, on both Down and Up events. However, for this to work, you must specify your triggering keys as lowercase, since specifying uppercase would only trigger the remap when pressed the keys with the Shift key as well (the CapsLock would have no effect), and that is not your desired behaviour. [More on remapping keys]
Here is an example of what you could do:
#If GetKeyState("CapsLock", "T")
z::F1
x::F2
c::F3
v::F4
Note that, since key remapping always uses the keyboard hook (because it needs to register the Up events as well), there is no need to use the $ prefix in your hotkeys at all.
Nonetheless, you can automate your script even more if you use as condition for your hotkeys the currently active window and bind them to your game using the #IfWinActive directive.
However if there are also chats in the game, you might want to combine both conditions in a single #If, using the built-in function WinActive like this:
#If GetKeyState("CapsLock", "T") and WinActive("My Game Title")
z::F1
; ...
You can check how to narrow your search for the window by its title on the documentation for the WinTitle parameter.
If you want to improve your script even further, you could explore if there is any detectable change on the game window when the chat is active, such as if a certain control exists (you can check that as if it was another window using the WinExist function to check for a certain Window class.
To seek for such changes, you can use a script as the following (from the MouseGetPos documentation):
#Persistent
SetTimer, WatchCursor, 100
return
WatchCursor:
MouseGetPos, , , id, control
WinGetTitle, title, ahk_id %id%
WinGetClass, class, ahk_id %id%
ToolTip, ahk_id %id%`nahk_class %class%`n%title%`nControl: %control%
return
Which would allow you to see the window information of the windows below your mouse. You can use it to check for the name or class of the chat control by placing your mouse over it.
However, keep in mind that many games do not use Windows controls at all in their interfaces and rather just draw them on screen by themselves, so if you're trying this and can't progress much after a while you shouldn't waste too much time on it and rather enjoy playing with your CapsLock toggled binding.
Another tricky way to check if the chat is active is searching for an image on the screen or a pixel color using ImageSearch or much simpler PixelGetColor, but you can only do that if your game's interface is not very complex/animated.

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

Send command, isn't something wrong with AutoHotkey?

So I have this game, called AirMech. It doesn't recognize mouse buttons as controls (yet) so I tried to use AutoHotkey to circumvent it until it's implemented.
#IfWinActive, AirMech
XButton1::Send c
Didn't work. So I tried SendGame, SendPlay and everything else, didn't work either. I googled it, and found out that some games don't recognize any Send commands at all.
Before giving up, I just tried a simple mapping:
#IfWinActive, AirMech
XButton1::c
It actually worked.
Is it expected than no Send command works, but the latter does? What if I wanted to trigger other actions ('c' plus a MsgBox, for instance)?
AutoHotkey has the ability to send keystrokes in a variety of different ways (SendRaw / SendInput / SendPlay / SendEvent). I'm not quite sure what approach the simple key::key mapping uses, but it must be one of them. My guess is that one of SendRaw, SendInput, SendPlay, or SendEvent will work the same as key::key.
Also #IfWinActive sometimes doesn't work exactly the way you expect, especially with fullscreen games. So I usually test my AHK scripts without the #IfWinActive to make sure they're working correctly. Once it's working, I introduce the conditional.
UPDATE
From http://www.autohotkey.com/docs/misc/Remap.htm:
When a script is launched, each remapping is translated into a pair of
hotkeys. For example, a script containing a::b actually contains the
following two hotkeys instead:
*a::
SetKeyDelay -1 ; If the destination key is a mouse button, SetMouseDelay is used instead.
Send {Blind}{b DownTemp} ; DownTemp is like Down except that other Send commands in the script won't assume "b" should stay down during their Send.
return
*a up::
SetKeyDelay -1 ; See note below for why press-duration is not specified with either of these SetKeyDelays. If the destination key is a mouse button, SetMouseDelay is used instead.
Send {Blind}{b Up}
return
My notes:
I suspect the reason a::b is working but a::Send b is not is because of how a::b breaks button down and button up handlers into two separate mappings. The game's gameloop probably polls the gameplay keys for "keydown" state, which would not be maintained consistently if AHK is synthesizing repeats. Remapping a_down->b_down and a_up->b_up probably makes AHK emulate more accurately the act of holding the key down, which may matter for programs which test for key state in particular ways (GetAsyncKeyState?).
The asterisk in the mapping means "Fire the hotkey even if extra modifiers are being held down."

Autohotkey: Commands with Alt Key Working Properly only if not Restricted to a Specific Application

I'm trying to add custom keyboard commands to an application using Autohotkey.
In many of these hotkeys I would like to use the alt key in combination with some other key of my choice (any of the standard letters).
All works fine as long as I don't restrict their usage in such a manner that they work in the target application only (via the #IfWinActive directive ). If I do so, the hotkeys themselves still work, however their behavior is very strange.
I found that they get activated either if
a) I hold down the alt key and then press the second key (in my case the 'b' key) twice
or
b) I use this hotkey two times consecutively with a very short delay between the two triggerings
- The above two cases might actually be 1 case. I'm not sure...
I have no problems when doing the same with Shift or CTRL.
The 'b' key is not what's causing the problem - no alt + 'letter' combination works.
I have tried all SendModes, but so far with no effect.
Code sample:
#IfWinActive, MyAppTitle ahk_class MyAppClass
!b::
click 367, 86
return
Alt+letter commands in AutoHotkey such as !b work without issue. It's possible the version at the time of this post contained certain bugs or was out of date from the current version.
For your code, it could be done like so:
!b::
WinGetTitle, Title, A
if (RegExMatch(Title, "MyAppTitle"))
{
MouseClick, left, 367, 86
}
return