AutoHotkey for Linux.
ahk_x11.AppImage
under assets
)AutoHotkey is "Powerful. Easy to learn. The ultimate automation scripting language for Windows.". This project tries to bring large parts of that to Linux. This README mostly tells you about the differences between Win AHK and AHK_X11. If you have no prior experience with AutoHotkey, read the docs tailored to this project.
Still, here is a brief peek at the syntax. (click the arrows)
AHK_X11 is a very basic but functional reimplementation AutoHotkey v1.0.24 (2004) for Unix-like systems with an X window system (X11), written from ground up in Crystal, with the eventual goal of 80% feature parity, but most likely never full compatibility. Currently about 80% of work of getting there is done, but even at 100%, because of the old version of the spec (at least for now), many modern AHK features are missing, especially advanced expressions and functions (x := y(z)
, % v
), classes, and objects, so you probably can't just port your scripts from Windows. More to read: Project goals
This AHK is shipped as a single executable native binary with very low resource overhead and fast execution time. You can use AHK_X11 to create stand-alone binaries with no dependencies, including full functionality like Hotkeys and GUIs. (just like on Windows)
Please also check out Keysharp, a WIP fork of IronAHK, another complete rewrite of AutoHotkey in C# that tries to be compatible with multiple OSes and support modern, v2-like AHK syntax with much more features than this one. In comparison, AHK_X11 is a lot less ambitious and more compact, and Linux only.
Features:
Besides:
AHK_X11 can be used completely without a terminal. You can however if you want use it console-only too. Graphical commands are optional, it also runs headless.
Download the latest binary from the release section (the file called ahk_x11.AppImage
). Make the downloaded file executable (how?) and you should be good to go: Just double click it or run it in the console without arguments (without sudo). If this doesn't work, you might have to also install FUSE for now: sudo apt-get install libfuse2
Prerequisites:
There is no auto updater yet! (but planned) You will probably want to get the latest version then and again.
There are different ways to use it.
.ahk
files are associated with AHK_X11, so you can simply double click them.Open as...
Menus../ahk_x11 "path to your script.ahk"
echo $'var = 123\nMsgBox %var%' | ./ahk_x11
~/.local/bin/ahk_x11.AppImage
%ErrorLevel%
will always be 0
.--repl
instead (implicit #Persistent
)../ahk_x11 --compile "path/script.ahk"
./ahk_x11 --windowspy
#!
DISPLAY= ./ahk_x11 <<< 'Echo abc'
just prints abc
to the console (Echo
command is a special ahk_x11-only command). The only advantage is faster startup time.If you run a script that contains e.g. a Hotkey, an icon will appear in your task bar tray notification area. You can right click that icon to quit. If you're not familiar with these basics of AutoHotkey, please read the AutoHotkey Tutorial and Overview from the docs.
You can't send / remap mouse buttons when triggered by another mouse button's {Down}
event. For example:
F1::LButton ; Works!
RButton::Tab ; Works!
RButton up::Send {LButton} ; Works!
RButton::Send {LButton} ; DOES NOT WORK
RButton::LButton ; DOES NOT WORK
Some commands or options related to Controls (e.g. ControlClick
or ControlGetPos
or WinGetText
which is just an accumulation of ControlGetText
or WinActivate, , Match by Text...
) relies on assistive technologies. While almost all windows support this, this typically needs adjustments on the running system. Read the documentation section on accessibility for instructions.
Some Linux distros offer a configurable setting for focus stealing prevention. Usually, it's default off. But if you have activated it, window focus changing actions like MsgBox
or WinActivate
will not work as expected: A MsgBox
will appear hidden behind the active window. This can be useful to prevent accidental popup dismissal but when you don't like that, you have three options:
always on top
setting of MsgBox(*) The MsgBox
picture at the top was taken on a XFCE system with Chicago95 installed, a theme that resembles Win95 look&feel. On your system, it will look like whatever GTK popups always look like.
Like covered above, AHK_X11 is vastly different to modern Windows-AutoHotkey because 1. its spec is missing its more recent features and 2. there are still several features missing. Apart from that, there are a few minor incompatibilities between AHK_X11 and the then-Windows-AutoHotkey 1.0.24:
#NoEnv
is the default, this means, to access environment variables, you'll have to use EnvGet
.#DefineCommand
, Echo
and Eval
.Besides, it should be noted that undocumented == undefined.
Besides the Legacy Syntax used in AHK_X11, more modern Windows AHK v1.1+ also supports (and favors) expression syntax. So chances are you might have to adjust a few things. Here's a general guideline on how to convert modern to legacy code. If you're annoyed by this, feel free to contribute a more capable parser so we can also parse expressions. Please also let us know if you know of an automated conversion tool. Note that in AHK v2.x, legacy was entirely dropped.
MODERN
-> LEGACY (AHK_X11)
x := 1
-> x = 1
x := y
-> x = %y%
b := a + 3
->
Simple math instructions with :=
like this one are actually wonkily supported, so chances are you can leave it as is. In this case, you'd omit the percent signs %
unless you want to double-deref, just like on Windows. But for more complicated stuff such as string concatenation, you'll have to resort back to classic syntax:
b = %a%
b += 3
MsgBox, % "c plus 3 is " . c + 3 . "."
->
new_c = %c%
new_c += 3
MsgBox, c plus 3 is %new_c%.
If(my_var = "my string")
-> If my_var = my string
If(d == e && f == "x") {
->
If d = %e%
{
If f = x
{
IfWinExist("Wikipedia, the free encyclopedia")
-> IfWinExist, Wikipedia`, the free encyclopedia
; Functions
Send, % Add(7,8)
Add(x, y) {
return x + y
}
->
add_first = 7
add_second = 8
GoSub, Add
Send, %result%
Return
Add:
result = %add_first%
result += %add_second%
Return
or you can use the special built-in command (AHK_X11-only) #DefineCommand
to reduce code repetition:
#DefineCommand Add, LblAdd
Add, result, 7, 8
Send, %result%
Return
LblAdd:
%A_Param1% = %A_Param2%
%A_Param1% += %A_Param3%
Return
my_array := ["one", "two", "three"]
->my_array := ["one", "two", "three"]
index = 1
MsgBox, % my_array[index]
->
my_array1 = one
my_array2 = two
my_array3 = three
index = 1
; Use any command that has an "InputVar" argument. StringTrimLeft is a nice choice because it doesn't do anything to the received value if the "Count" argument is 0 as below. Yes, it's weird, AHK_X11 has no actual arrays. In fact, in AHK v1.0, it used to be the same and the official docs also used StringTrimLeft a lot
StringTrimLeft, my_value, my_array%index%, 0
MsgBox, %my_value%
colors := "red,green,blue"
for index, color in StrSplit(colors, ",")
MsgBox % "Color number " index " is " color
->
colors = red,green,blue
StringSplit, color_array, colors, `,
Loop, %color_array0%
{
StringTrimLeft, this_color, color_array%A_Index%, 0
MsgBox, Color number %A_Index% is %this_color%
}
my_obj
:= { key_a: 1, key_b: "value b" }` -> Doesn't exist. You'll have to use normal variables instead.class MyClass {}
-> Doesn't exist. You'll have to use normal variables instead.x := y%z%
(double de-ref) -> StringTrimLeft, x, y%z%, 0
. You can also use Eval, x = `%y%z%`%
but that's ten times slower as Eval
is generally very slow.AHK_X11 is an interpreted language, not a compiled one. This means that no compile time optimizations take place on your script code, apart from some validation and reference placements. Also, all variables are of type String. So you probably wouldn't want to use it for performance-critical applications. However, the tool itself is written in Crystal and thus compiled and optimized for speed, so everything should still be reasonably fast. The speed of some of the slower commands depends on either libxdo or X11 and it's not yet clear whether there is much room for improvement. Some tests run on a 3.5 GHz machine:
Parsing a single line takes about 30 µs (this happens once at startup), and execution time depends on what a command does:
x = 1
: 70 ns (0.00000007 s)FileRead, x, y.txt
: 10 µs (0.00001 s)WinGetTitle, A
: 87 µs (0.000087 s)Send, a
: 530 µs (0.00053 s)Clipboard = a
: 6 ms (0.006 s)SendRaw, a
: 9 ms (0.009 s) (??)WinActivate, title
: 60 ms (0.06 s)WinGetText
: 0-3 s (!)You can run fine-grained benchmarks with the following special hidden instruction:
AHK_X11_track_performance_start
Loop, 1000
Send, a
AHK_X11_track_performance_stop
prints something like:
[{"send", count: 1000, total: 00:00:00.530032328>},
{"loop", count: 1001, total: 00:00:00.000206347>}]
Note that the internal code around executing commands takes about 10 µs between two every commands and you can't do anything about it and this not measured / included in the benchmark command's output. This can actually be the bottleneck in some scripts and should probably be improved
More tips:
Control
-*, WinGetText
, ... see "Accessibility" section above) runs, the interface needs to be initialized which can take some time (0-5s)WinActivate, ahk_id %win_id%
will be much much faster than WinActivate, window name
. So for many window operations you might want to do a single WinGet, win_id, ID
beforehand and then reuse that %win_id
.If you want to help with AHK_X11 development or prefer to build from source instead of using the prebuilt binaries, detailed build instructions are to be found in ./build/README.md. Most users will not want to do that, please go to Installation if you're not sure.
For bugs and feature requests, please open up an issue, or check the Discord or Forum.