Conditional Key Remapping AHK - autohotkey

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

Related

How can I swap colon key and slash key using AutoHotkey?

I searched for hours but can't find a way to swap : with it's shifted key /. I am new to AutotHotkey. Can someone help me ?
colon key with slash (shifted)
Answer for the updated question from comments:
The layout is French AZERTY. The layout has a : key, and if it's shifted, it sends /. These should be swapped around so not shifting would send / and shifting would send :.
So the trick is to just send the other key, when other key is detected. Like so:
#UseHook
:::SendInput, /
/::SendInput, :
Using the keyboard hook #UseHook(docs) is important to make the hotkeys not trigger each other.
Normally this could be done with the $(docs) prefix, but due to bug in the syntax, $::: comes up as a syntax error.
Also, why couldn't the simple remapping syntax be used?
:::/
/:::
It's because the remapping syntax uses the blind sendmode(docs), which would cause the shift modifier to pass through and you'd always end up with the shifted variant of the key.
Technically you can use the remapping syntax for the first hotkey like this:
:::/
/::SendInput, :
This also wouldn't require you to use the keyboard hook, due to DownR(docs) being used in the remapping syntax.
Since we cannot use the typical remap sequence here (i.e. ::), we can instead use the Hotkey command to detect when the Colon is pressed, and then to remap it to a label
Hotkey, :, ColonDetected
return
ColonDetected:
Send, /
Answer based on this post in the AHK forums: https://autohotkey.com/board/topic/99092-remap-colon-key-help/

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
...

How to manually trigger Autohotkey hotstrings?

In my main Autohotkey script I have several hundred hotstrings like these:
::fe::for example
::f::and
::fi::for instance
::fo::fortunate
::foy::fortunately
::glo::global
::gloy::globally
::ha::have
::hv::however
Fairly often it would be convenient to trigger a hotstring manually (e.g. by pressing ALT-9) rather than pressing and end character. Is there a way to do this? I haven't found anything in my Googling, so maybe there isn't. But it would be useful.
I've read the hotstrings options e.g. :*: but this isn't the same - I want normal hotstring operation, but also the option to manually force them to trigger as needed.
Updated:
So what you are in fact looking for is using ALT+9 as an end character. I'm not sure but you can probably not use key-combinations for that (see hotstring doc). I cannot think of a really clever way of doing that right now. You might try something like
::fe::
Transform, CtrlC, Chr, 3 ; comes from ahk input documentation, I never really understood how this is supposed to work but I guess there is a code for alt 9 as well somehow
input, key, L1 I M ; wait for the next 1 key
if(key==CtrlC)
sendraw forExample
return
Old answer:
You have to outsource the hotstring body:
::fe::gosub forExample
forExample:
send for example
return
, then you can define a hotkey somewhere:
!9::gosub forExample
If you want to be cool, use functions instead of subroutines.
Note:
::fe::something
is just a short form for
::fe::
send something
return
::fe::for example
::f::and
::fi::for instance
::fo::fortunate
::foy::fortunately
::glo::global
::gloy::globally
::ha::have
::hv::however
!8:: trigger_hotstring("fi")
!9:: trigger_hotstring("ha")
trigger_hotstring(hotstring){
Loop, Read, %A_ScriptFullPath%
{
If InStr(A_LoopReadLine, "::"hotstring "::")
{
SendInput, % StrSplit(A_LoopReadLine,"::"hotstring "::").2
break
}
}
}
If you use AutoHotkey v1.1.06+ you can use #InputLevel
::fe::for example
::f::and
; and so on
#InputLevel, 1 ; sending space will trigger hotstrings above this line
!F9::Send {space}
Edit:
I now see you want to omit the end char which needs a bit of extra work
One way would be to duplicate the hotstrings with additional options:
::fe::for example
:*O:fe_::for example ; duplicate the hotstrings like so
::f::and
::fi::for instance
::fo::fortunate
; and so on
#InputLevel, 1
!9::Send _
Another way would be to remove the endchar
::fe::for example
::f::and
::fi::for instance
::fo::fortunate
; and so on
#InputLevel, 1
!9::
Send {space}
Sleep 100 ; give it time to expand the hotstring, experiment with timing
Send {bs} ; now remove trailing space
Return
I generally use TAB as a trigger for all my hotstrings. like
:*:#pm ::mail1#protonmail.com
:*:#G ::mail2#gmail.com
:*:Btw ::By the way,{Left}
Note that the space that you see here is a tab and not a space. you can do this instead of doing Alt+9 to trigger your macro hotstring.
you can even use more than one Tab so you can be sure that you only trigger it when you really intend to.

AutoHotKey - Key spamming does not work like I want it to

Key Spam does not work so good.
Here is the code:
Loop
{
If GetKeyState("z", "P")
send w
If GetKeyState("g", "P")
send a
If GetKeyState("j", "P")
send d
If GetKeyState("h", "P")
send s
}
but when I hold z or zg together, then the original keys z and zg come all time once or twice between the w or wa output.
What can I write that the keys z+g+h+j never will be seen in the text file only w+a+s+d if I run this script?
Karoline, you are going about it the wrong way. Autohotkey was designed to do just what you really want to do.
As MCL says in his comment, you need only to map your keys to a send function
Put this in your script:
z::send w
g::send a
j::send d
h::send s
Now, when you push z it will type w instead.
Keep in mind that this is global over your entire computer unless you limit it to notepad using IfWinActive. The script will not exit on its own.
but this doenst work like i want
i want that i can press g+z same time or z+j same time or g+h same time or h+j same time
and that i get then awawawawaw or asasasasasasas or wdwdwdwdwdwdwd
that when i press 2 buttons same time it switch output ever 1 char
and when i use your answer and holz z it spams wwwwwwwwwwwwwwwwwwwwwwwwwwwww
and if if press then z+g it only continou spaming wwwwwwwwwwwwww not wawawawawawawawaw
thans
Karoline
According to your "answer", you are targeting a keyboard behaviour that's not standard. That is, using remapped keys with AHK or not, when pressing two letter keys simultaneously, solely the key pressed at last will continue firing.
Here's an example on how you can achieve an alternating behaviour instead:
SendMode, Input
g & z::
z & g::
Send, aw
return
g::Send, a
+g::Send, A
z::Send, w
+z::Send, W
You will have to explicitely define every custom combination with an & modifier, which makes them prefix keys. This comes with an upside and with a small downside: The upside is, you can now keep pressing your combination and it will continue firing two keys in an alternating fashion; no loop or the like required. The downside: You need to reconstruct prefix hotkeys' original behaviour. In our example, that's sending a lowercase or uppercase letter.

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