Autohotkey: Define a hotkey and passthrough it to system dynamically, how to? - autohotkey

I'm facing a somewhat hard question. Look at this script:
; Using Autohotkey 1.1.36.2
F2::
send_f2_withcond()
{
if(WinExist("ahk_class Notepad"))
{
ControlSend Edit1, % A_Now "`r", % "ahk_class Notepad"
}
else
{
; I hope F2 can do its original action. How to?
Send {$F2} ; ??? No effect!
}
}
My purpose:
If there is Notepad running, F2 will send some text to Notepad.
If Notepad is not running, I hope F2 can do it original action, for example, pressing F2 in an Explorer window will start renaming current highlighted file.
Writing Send {F2} is not correct, because it will trigger my own F2:: ... action recursively.
The doc says adding a $ prefix will suppress recursive calling, BUT, Send {$F2} takes no effect(as if I totally omit this Send), the current active application receives only F2's WM_KEYUP, no WM_KEYDOWN.

The $ prefix is used only in hotkey definitions. It forces the keyboard hook to be used, which is designed to filter out keys sent by AutoHotkey scripts.
$F2:: send_f2_withcond()
send_f2_withcond() {
if WinExist("ahk_class Notepad")
ControlSend, Edit1, % A_Now "`r", % "ahk_class Notepad"
else
Send {F2}
}

You can greatly simplify this script by using #IfWinExist.
#IfWinExist, ahk_class Notepad
F2::ControlSend Edit1, % A_Now "`r", % "ahk_class Notepad"
#IfWinExist

Related

AutoHotkey - Detect double press of AltGr

I want to detect double press on AltGr.
According to documentation:
; Example #4: Detects when a key has been double-pressed (similar to double-click).
; KeyWait is used to stop the keyboard's auto-repeat feature from creating an unwanted
; double-press when you hold down the RControl key to modify another key. It does this by
; keeping the hotkey's thread running, which blocks the auto-repeats by relying upon
; #MaxThreadsPerHotkey being at its default setting of 1.
; Note: There is a more elaborate script to distinguish between single, double, and
; triple-presses at the bottom of the SetTimer page.
~RControl::
if (A_PriorHotkey <> "~RControl" or A_TimeSincePriorHotkey > 400)
{
; Too much time between presses, so this isn't a double-press.
KeyWait, RControl
return
}
MsgBox You double-pressed the right control key.
return
AltGr is actually a combination of LControl & RAlt. So, for AltGr, script should be something like this:
~LControl & RAlt::
if (A_PriorHotkey <> "~LControl & RAlt" or A_TimeSincePriorHotkey > 400)
{
click
KeyWait, LControl & RAlt
return
}
click 2
return
But when I try to load this script, AutoHotkey gives an error:
Maybe there is a way to make an alias for key combinations.
As mentioned in the comments, KeyWait can only wait on one key (not hotkey) at a time. You only need to wait for RAlt to be released, not the combination of LCtrl and RAlt.
This works:
~LControl & RAlt::
if (A_PriorHotkey <> "~LControl & RAlt" or A_TimeSincePriorHotkey > 400)
{
KeyWait, RAlt
return
}
MsgBox Double-click
return
However, in this case KeyWait is only being used (in combination with the default #MaxThreadsPerHotkey setting of 1) to prevent key-repeat from activating the hotkey. You can remove KeyWait and it will still detect double-presses; but it will also activate if you hold AltGr down until it auto-repeats.
Note that in your case, double-pressing the hotkey would click three times: once on the first press and an additional two times on the second press.
If you just want to use AltGr as a mouse button and allow double-click, all you need is <^RAlt::Click.
If you want to perform two different actions depending on whether it is a single or double click, you must delay the response to the first click until you know whether there's a second click. For example:
<^RAlt::
KeyWait RAlt
KeyWait RAlt, D T0.4
if ErrorLevel
MsgBox Single
else
MsgBox Double
return

AHK: Assign hotkey only for one specific active window and not for others

I have just done a piece of code that does the following thing. When I make a selection by mouse in Firefox or EndNote, the script sents a Ctrl+c and checks the clipboard for a regex match. If there is a match, it changes the clipboard contents and shows a tooltip. It works fine for these two programs. Adobe Acrobat sometimes shows an error when a Ctrl+c is sent (even if a user presses a ctrl-c Acrobat sometimes shows famous "There was an error while copying to the Clipboard. An internal error occurred). So it decided to assign an F9 hotkey, but it works for all programs and not just for Acrobat. How do I assign an hotkey for only one window – Acrobat? Here's my code. I know it's lame – I am a newbie to programming in general, and in AHK in particular.
#If WinActive("ahk_exe firefox.exe") || WinActive("ahk_exe EndNote.exe") || WinActive("ahk_exe Acrobat.exe")
if WinActive("ahk_exe Acrobat.exe")
F9::
{
Clipboard:=""
send,^c
ClipWait, 1
ToolTip % Clipboard := RegExReplace(Clipboard, "\r\n", " ")
SetTimer, ToolTipOff, -1000
}
return
~LButton::
now := A_TickCount
while GetKeyState("LButton", "P")
continue
if (A_TickCount-now > 500 )
{
Send ^c
if WinActive("ahk_exe firefox.exe")
{
If RegExMatch(Clipboard, "[0-9]\.\s[A-Za-z,]*\s[A-Za-z]*")
{
regex := "[0-9]\.\s*|\s?\([^)]*\)|\."
replace := ""
}
else If RegExMatch(Clipboard,"[0-9]{2}[-\/][0-9]{2}[-\/][0-9]{4}")
{
Clipboard := RegExReplace(Clipboard, "^0", "")
regex := "\/"
replace := "."
}
else return
}
else if WinActive("ahk_exe EndNote.exe")
{
If RegExMatch(Clipboard, "[a-z]+\,\s[A-Z0-9‘“]")
{
regex := "\??!?\:|\?|!"
replace := "."
}
else return
}
ToolTip % Clipboard := RegExReplace(Clipboard, regex, replace)
SetTimer, ToolTipOff, -1000
}
return
#If
ToolTipOff:
ToolTip
return
I see some very fundamental problems in the first few lines. Let me explain...
There are two types of if-statements in AutoHotkey If and #If.
You usually always use the normal If-statements unless you are doing something with hotkeys and you want specific hotkeys to be context-sensitive.
Here are some important rules:
Normal If-statements have to use curly braces {} to mark the area of code that should be executed if the expression is true. If you don't use curly braces, the If-statement will work as if you had put curly braces around the first command directly under the If-statement.
Example:
If WinActive("Firefox") {
Send, Test
MsgBox, The script just typed "Test.
}
Another example:
If WinActive("Firefox")
MsgBox, Firefox is the active window.
Normal If-statements cannot be used around a hotkey definition, but only within it.
This is allowed:
F1::
If (A_OSVersion = "WIN_7") {
MsgBox, Your operating system is Windows 7 and you just pressed F1.
}
Return
This is NOT:
If (A_OSVersion = "WIN_7") {
F1::
MsgBox, Your operating system is Windows 7 and you just pressed F1.
Return
}
But there is a way around that and that is #If-statements.
#If-statements don't use curly braces ever.
They can only be used on hotkey definitions.
And they can only be closed by another #If-statement.
(It's very common to simply use an empty #If to close it.)
Examples:
#If (A_OSVersion = "WIN_7")
F1::
MsgBox, Your operating system is Windows 7 and you just pressed F1.
Return
#If
A more complex example:
#If (A_ScreenWidth >= 1920)
F1::
MsgBox, Your your screen is at least 1920 pixels wide.
Return
F2::
MsgBox, Your operating system is %A_OSVersion%.
Return
#If (A_ScreenWidth < 1920)
F1::
MsgBox, Your your screen width is smaller than 1920 pixels.
Return
#If
As you might have guessed by now, hotkey definitions are always started by a pattern like this hotkey:: and closed by a Return. Although you can define hotkeys on a single line.
Examples:
F1::MsgBox, Hello!
F2::a ;This will remap the F2 key to an a-key.
Hotkeys by themselves do never use curly braces! Though an If-statement within a hotkey still has to use them according to the before mentioned rules.

Autohotkey: Toggle caps lock on/off on activating certain windows

I want to write a script that will turn on caps lock when I activate a window containing particular keyword in its title(like SQL). I also want the caps lock to be turned off when I switch to a window whose title does not contain any of the keywords that I have specified.
How can I do it? I have considered #Persistent with a timer to periodically check active window. But, I think there should be a better way.
check answers at: http://www.reddit.com/r/AutoHotkey/comments/1qjf83/force_specific_program_to_use_caps/. Especially G33kDude's answer. It's a clever and efficient solution: check of current window is binded only to windows activation.
=======================
Edit: Code inserted below.
Please note that it's not a complete solution, you'll need to make some edits for your needs. Not a big ones, though.
#Persistent ; Don't close when the auto-execute ends
SetTitleMatchMode, 2 ; Partial title matching
WinGet, myHwnd, ID, Notepad ; Get the handle to the your window
; Listen for activation messages to all windows
DllCall("CoInitialize", "uint", 0)
if (!hWinEventHook := DllCall("SetWinEventHook", "uint", 0x3, "uint", 0x3, "uint", 0, "uint", RegisterCallback("HookProc"), "uint", 0, "uint", 0, "uint", 0))
{
MsgBox, Error creating shell hook
Exitapp
}
;MsgBox, Hook made
;DllCall("UnhookWinEvent", "uint", hWinEventHook) ; Remove the message listening hook
return
; Handle the messages we hooked on to
HookProc(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime)
{
global myHwnd
static lastHwnd
WinGetTitle, title, ahk_id %hwnd%
if (hwnd == myHwnd) ; If our window was just activated
{
tooltip, Gained focus
}
else if (lastHwnd == myHwnd) ; If our window was just deactivated
{
tooltip, Lost focus
}
lastHwnd := hwnd
}
If you want to do this without using SetTimer, the best way would be to use context-sensitive hotkeys. For Example:
SetTitleMatchMode, 2
#If WinActive("SQL") or WinActive("Notepad")
a::A
b::B
c::C
d::D
e::E
;; etc.
You could also use the WinActive function with Window Groups instead of the title if you wanted to avoid a very long #If line.
EDIT: Case-Insensitive Example
SetTitleMatchMode, Regex
GroupAdd, Editor, (?i).*sql ; Regular expression for window title
GroupAdd, Editor, (?i).*ahk
#IfWinActive ahk_group Editor
a::A
b::B
c::C
d::D
e::E
;; etc.
Since you have a Autoit in your tag, here is how it is done easy in autoit.
Opt("SendCapslockMode", 0)
While 1
Sleep(200)
$title = WinGetTitle("", ""); will get the title of the active window
If StringInStr($title, "sql") Then
Send("{CAPSLOCK ON}")
Else
Send("{CAPSLOCK OFF}")
EndIf
WEnd
Milos' answer is quite straight forward, but it misses a vital point. You need to set SendCapslockMode to 0. Else the effect of the Send command will be useless, because after the command the original state will be restored.
The next thing is, you don't need to use an infinite loop with a Sleep which will execute the complete loop body every few milliseconds, but you can wait for the active window to not be active any more, which is less CPU intensive. So a fully working solution in AutoIt is:
Opt("SendCapslockMode", 0)
While True
$win = WinGetHandle("[ACTIVE]")
If StringInStr(WinGetTitle($win), "sql") Then
Send("{CAPSLOCK ON}")
Else
Send("{CAPSLOCK OFF}")
EndIf
WinWaitNotActive($win)
WEnd
#Persistent
SetTitleMatchMode, 2 ; use RegEx for finer control
Loop
{
WinWaitActive, Notepad
{
WinGet, opVar, ID
SetCapsLockState, On
}
WinWaitNotActive, ahk_id %opVar%
SetCapsLockState, Off
}
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
; This script solves the problem of forgetting to turn on or off the capslock when using specific programs
; I for one ALWAYS use capital letters when typing things in CAD. My problem was when switching to another
; program, usually e-mail, I'd start typing and then have to erase it. Problem solved!
; The beauty of this script is that you don't lose the ability to manually turn the capslock on or off
; My first few interations of this script would turn it on when in the CAD window but then
; I'd never be able to turn it on wihtout it being forced off again.
Loop {
state := GetKeyState("Capslock", "T") ; check toggle state, save in variable 'state'
if state = 0
status = Off ; this block converts the 1 or 0 of variable 'state'
if state = 1 ; into the word ON or OFF and stores it in variable 'status'
status = On
Beginsub:
WinGetTitle, Title, A ; retreive current window title, store in Title
if title contains AutoCAD 2012 ; if title contains "AutoCAD" turn on capslock
; then go back to the BeginSub subroutine and see if the title
{ SetCapsLockState, on ; still matches "AutoCAD 2012" if it does not...\/\/\/
goto, BeginSub
}
SetCapsLockState, %status% ; ...Put caps lock back to the state it was when we started.
; 'SetCapsLockState' doesn't recognize a 1/0 variable
; therefore the use of 'status' and ON/OFF words
Sleep, 1000 ; only check every second
}

How to pass window title to user function in AutoHotKey

I want to pass the window title into a function I wrote in AutoHotKey, is window title WinTitle a string? I have 4 window titles, and I need to pass them to the same function.
Extract(my_window_title) {
; Wake and select the correct window to be in focus
WinWait, my_window_title,
IfWinNotActive, my_window_title, , WinActivate, my_window_title,
WinWaitActive, my_window_title,
; ... do a bunch of things
}
I call the function like this
title1 = "Some title"
Extract(title1)
and I also tried putting % in all the variables
Yes WinTitle is basically a string.
Check out your Autohotkey-folder, there should be a file called "AU3_Spy.exe". Use it to find the window titles.
And as Elliot DeNolf already mentioned, you made some mistakes with variables. You should also take another look at the syntax of IfWInNotActive.
This should work:
Extract(my_window_title) {
; Wake and select the correct window to be in focus
WinWait, %my_window_title%
IfWinNotActive, %my_window_title%
{
WinActivate, %my_window_title%
WinWaitActive, %my_window_title%
}
msgbox, %my_window_title%
; ... do a bunch of things
}
title1 = MyWindowTitle
Extract(title1) ;functions always expect variables, no percent-signs here
There are a few things that look like they are causing an issue in your script.
When assigning a string value and using =, quotes are not needed. If you assign the value using :=, then you need the quotes. These 2 lines are equivalent:
title1 := "Some Title"
title1 = Some Title
Once these values are called via a function ie. Extract(title1), % symbols must be used (as you mentioned at the end of your question). This can be called in 2 ways:
WinActivate, %my_window_title%
WinActivate, % my_window_title
If the title is invalid, your script will wait indefinitely on WinWait and WinWaitActive. I would recommend using a timeout value and then checking ErrorLevel to see if it was successful or not.

How to toggle a set of keybinds on and off?

I am trying to set up a group of keybinds that I can toggle on and off with a single button press but haven't been able to find any examples anywhere.
I want ^NumpadSub to toggle these different keybinds to turn them on and off when I press ^NumpadSub.
q::w
z::s
w::up
s::down
Can anyone help on how I would set up the code to do so?
When these are the ONLY ones, you could add one more hotkey:
^NumpadSub::Suspend
This will suspend ALL hotkeys (except the one that is used for toggling suspend on/off)
Otherwise you would have to use the actual hotkey function (http://www.autohotkey.com/docs/commands/Hotkey.htm) which allows you to turn hotkeys on/off, but the hotkey function refers to labels: (go to addresses).
If you want to ONLY have these keys act a certain way when you use ONE particular application (Game), you can use the #IfWinActive command.
e.g.
SetTitleMatchMode, 2
#IfWinActive, Notepad ; Start of Notepad specific keys.
a::Send, Haha
b::SoundBeep, 500, 500
#IfWinActive ; End of Notepad specific keys.
In that situation, Check out if this works for you! I added $ signs in front of w and s because hitting q and z would trigger the execution of w and s
Hotkey, q , MyQ, On
Hotkey, z , MyZ, On
Hotkey, $w , MyW, On
Hotkey, $s , MyS, On
Return
^NumpadSub::
KeyToggle:=!KeyToggle
Hotkey, q , % (KeyToggle ? "Off": "On")
Hotkey, z , % (KeyToggle ? "Off": "On")
Hotkey, $w , % (KeyToggle ? "Off": "On")
Hotkey, $s , % (KeyToggle ? "Off": "On")
Return
MyQ:
SendInput, w
Return
MyZ:
SendInput, s
Return
MyW:
SendInput, {Up}
Return
MyS:
SendInput, {Down}
Return