7

GitHub - phil294/AHK_X11: AutoHotkey for Linux (X11-based systems)

 2 years ago
source link: https://github.com/phil294/AHK_X11
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

AHK_X11

AutoHotkey for Linux.

MsgBox, AHK_X11 (*)

Direct download (all Linux distributions, x86_64, single executable)

FULL DOCUMENTATION (single HTML page)

Go to installation instructions

AutoHotkey is "Powerful. Easy to learn. The ultimate automation scripting language for Windows.". This project tries to bring large parts of that to Linux.

More specifically: 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 with Crystal/libxdo/crystal-gobject/x11-cr/x_do.cr, with the eventual goal of 80% feature parity, but most likely never full compatibility. Currently about 60% of work is done. This AHK is shipped as a single executable native binary with very low resource overhead and fast execution time.

Note that because of the old version of the spec, many modern AHK features are missing, especially expressions (:=, % v), classes, objects and functions, so you probably can't just port your scripts from Windows.

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:

  • Hotkeys
  • Hotstrings (complete, but does not work in some windows: help needed)
  • Window management (but some commands are still missing)
  • Send keys
  • Control mouse (only clicks so far)
  • File management (but some commands are still missing)
  • GUIs (some controls are still missing)
  • One-click compile script to portable stand-alone executable
  • Scripting: labels, flow control: If/Else, Loop
  • Window Spy
  • Graphical installer (optional)
  • Context menu and compilation just like on Windows

Besides:

  • Interactive console (REPL)

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.

Click to see which commands are implemented and which are missing. Note however that this is not very representative. For example, all `Gui` sub commands are missing. For a better overview on what is already done, skim through the docs.

DONE      34% (74/216):












NEW       2% (5/216): (not part of spec)


REMOVED   10% (22/216):












TO DO     216% (113/216): alphabetically






















Installation

Prerequisites:

  • X11 and GTK are the only dependencies. You most likely have them already. Wayland support would be cool too some day.
  • Old distros like Debian before 10 (Buster) or Ubuntu before 18.04 are not supported (reason). Otherwise, it should not matter what system you use.

Then, you can download the latest binary from the release section. Make the downloaded file executable and you should be good to go.

There is no auto updater yet! (but planned) You will probably want to get the latest version then and again.

Usage

There are different ways to use it.

  1. The graphical way, like on Windows: Running the program directly opens up the interactive installer.
    • Once installed, all .ahk files are associated with AHK_X11, so you can simply double click them.
    • Also adds the Compiler into Open as... Menus.
  2. Command line: Pass the script to execute as first parameter, e.g. ./ahk_x11 "path to your script.ahk"
    • Once your script's auto-execute section has finished, you can also execute arbitrary single line commands in the console. Code blocks aren't supported yet in that situation. Those single lines each run in their separate threads, which is why variables like %ErrorLevel% will always be 0.
    • When you don't want to pass a script, you can specify --repl instead (implicit #Persistent).
    • If you want to pass your command from stdin instead of file, do it like this: ./ahk_x11 /dev/stdin <<< 'MsgBox'.
    • Compile scripts with `./ahk_x11 --compile "path/script.ahk"
    • Hashbang supported if first line starts with #!

Caveats

Focus stealing prevention

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:

  • disable said setting

  • use the always on top setting of MsgBox

  • hack around it with code
    , MsgBoxToFront, 
    , Hello
    
    
    :
    , MsgBoxToFront, 
    
      ahk_x11
    

Appearance

(*) 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.

Development

These are the steps required to build this project locally, such as if you want to contribute to the project. Please open an issue if anything doesn't work.

You don't need to follow this steps to use AHK_X11, for that, please see Installation above.

  1. Install development versions of prerequisites.
    1. Ubuntu 20.04 and up:
      1. Dependencies
        sudo apt-get install libxinerama-dev libxkbcommon-dev libxtst-dev libgtk-3-dev libxi-dev libx11-dev libgirepository1.0-dev
        
      2. Install Crystal and Shards (Shards is typically included in Crystal installation)
    2. Arch Linux:
      sudo pacman -S crystal shards gcc libxkbcommon libxinerama libxtst gtk3 gc
      
  2. git clone https://github.com/phil294/AHK_X11
  3. cd AHK_X11
  4. Run these commands one by one (I haven't double checked them, so it's best to go through them manually). Most of it is all WIP and temporary and only necessary so the different dependencies get along fine (x11 and gobject bindings). As a bonus, the build_namespace invocations cache the GIR (require_gobject calls) and thus reduce the overall compile time from ~6 to ~3 seconds.
    shards install
    # populate cache
    crystal run lib/gobject/src/generator/build_namespace.cr -- Gtk 3.0 > lib/gobject/src/gtk/gobject-cache-gtk.cr
    crystal run lib/gobject/src/generator/build_namespace.cr -- xlib 2.0 > lib/gobject/src/gtk/gobject-cache-xlib--modified.cr
    for lib in "GObject 2.0" "GLib 2.0" "Gio 2.0" "GModule 2.0" "Atk 1.0" "freetype2" "HarfBuzz 0.0" "GdkPixbuf 2.0" "cairo 1.0" "Pango 1.0" "Gdk 3.0"; do
        echo "### $lib" >> lib/gobject/src/gtk/gobject-cache-gtk-other-deps.cr
        crystal run lib/gobject/src/generator/build_namespace.cr -- $lib >> lib/gobject/src/gtk/gobject-cache-gtk-other-deps.cr
    done
    # update lib to use cache
    sed -i -E 's/^(require_gobject)/# \1/g' lib/gobject/src/gtk/gobject-cache-gtk.cr lib/gobject/src/gtk/gobject-cache-gtk-other-deps.cr
    sed -i -E 's/^require_gobject "Gtk", "3.0"$/require ".\/gobject-cache-gtk"/' lib/gobject/src/gtk/gtk.cr
    echo 'require "./gobject-cache-xlib--modified"' > tmp.txt; echo 'require "./gobject-cache-gtk-other-deps"' >> tmp.txt; cat lib/gobject/src/gtk/gobject-cache-gtk.cr >> tmp.txt; mv tmp.txt lib/gobject/src/gtk/gobject-cache-gtk.cr
    echo 'macro require_gobject(namespace, version = nil) end' >> lib/gobject/src/gobject.cr
    # delete conflicting c function binding by modifying the cache
    sed -i -E 's/  fun open_display = XOpenDisplay : Void$//' lib/gobject/src/gtk/gobject-cache-xlib--modified.cr
    # https://github.com/jhass/crystal-gobject/issues/103
    sed -i -E 's/(def self.new_from_stream.+: self)$/\1?/g' lib/gobject/src/gtk/gobject-cache-gtk-other-deps.cr
  5. Now everything is ready for local use with shards build -Dpreview_mt, if you have libxdo (xdotool) version 2016x installed. Read on for a cross-distro compatible build.
  6. In lib/x_do/src/x_do/libxdo.cr, add line role : LibC::Char* after winname : LibC::Char*
  7. add xdo_quit_window TODO: Waiting for a PR on x_do.cr, so for now, probably best just comment out the failing lines later on in win-close and win-maximize and win-restore
  8. add xdo_set_state TODO: same as above
  9. To make AHK_X11 maximally portable, various dependencies should be statically linked. This is especially important because of the script compilation feature: You can use the binary to transform a script into a new stand-alone binary, and that resulting binary should be portable across various Linux distributions without ever requiring the user to install any dependencies. Here is an overview of all dependencies. All of this was tested on Ubuntu 18.04.
    • Should be statically linked:
      • libxdo. Additionally to the above reasons, it isn't backwards compatible (e.g. Ubuntu 18.04 and 20.04 versions are incompatible) and may introduce even more breaking changes in the future. Also, we fix a rarely occurring fatal error here (probably Crystal-specific?). So,
        • clone xdotool somewhere, in there,
        • in xdo.c, after data = xdo_get_window_property_by_atom(xdo, wid, request, &nitems, &type, &size);, add another if(data == NULL) return XDO_ERROR;
        • run make clean && make libxdo.a and then copy the file libxdo.a into our static folder (create if it doesn't exist yet).
      • Dependencies of libxdo: libxkbcommon, libXtst and libXi. The static libraries should be available from your package manager dependencies installed above so normally there's nothing you need to do.
      • More dependencies of libxdo which are also available from the packages, but their linking fails with obscure PIE errors: libXinerama and libXext. I solved this by getting the source for these two and building the .a files locally (but apparently no makefile changes were required). Not very sure if these aren't actually part of every standard libx11 install anyway, so maybe they should be dynamic...
      • Other (crystal dependencies?), also via package manager: libevent_pthreads, libevent, and libpcre
      • libgc is currently shipped and linked automatically by Crystal itself so there is no need for it
    • Stays dynamically linked:
      • libgtk-3 and its dependencies, because afaik Gtk is installed everywhere, even on Qt-based distros. If you know of any common distribution that does not include Gtk libs by default please let me know. Gtk does also not officially support static linking. libgtk-3, libgd_pixbuf-2.0, libgio-2.0, libgobject-2.0, libglib-2.0, libgobject-2.0
      • glibc / unproblematic libraries according to this list: libX11, libm, libpthread, librt, libdl.
  10. All in all, once you have libxdo.a, libXext.a and libXinerama.a inside the folder static, the following builds the final binary which should be very portable: shards build -Dpreview_mt --link-flags="-L$PWD/static -Wl,-Bstatic -lxdo -lxkbcommon -lXinerama -lXext -lXtst -lXi -levent_pthreads -levent -lpcre -Wl,-Bdynamic". When not in development, increase optimizations and runtime speed by adding --release. The resulting binary is about 3.6 MiB in size.
  11. Attach the installer with bin/ahk_x11 --compile src/installer.ahk tmp && mv tmp bin/ahk_x11. Explanation: The installer is not shipped separately and instead bundled with the binary by doing this. Bundling is the same thing as compiling a script as a user. As you can see, it is possible to repeatedly compile a binary, with each script being appended at the end each time. Only the last one actually executed - and only if no params are passed to the program. There's no point in compiling multiple times, but it allows us to ship a default script (the installer) for when no arguments are passed. In other words, this is possible for a user: ahk_x11 --compile script1.ahk && ./script1 --compile script2.ahk && ./script2 but no one will ever do that.

Performance

Not yet explicitly tuned for performance, but by design and choice of technology, it should run reasonably fast. Most recent tests yielded 0.03 ms for parsing one instruction line (this happens once at startup). Execution speed even is at least x100 faster than that.

TODO: speed measurements for Send and window operations

Contributing

If you feel like it, you are welcome to contribute. This program has a very modular structure due to its nature which should make it easier to add features. Most work pending is just implementing commands, as almost everything more complicated is now bootstrapped. Simply adhere to the 2004 spec chm linked above. There's documentation blocks all across the source.

Commands behave mostly autonomous. See for example src/cmd/file/file-copy.cr: All that is needed for most commands is min_args, max_args, the run implementation and the correct class name: The last part of the class name (here FileCopy) is automatically inferred to be the actual command name in scripts. Regarding run: Anything can happen here, but several commands will access the thread or thread.runner, mostly for thread.runner.get_user_var, thread.get_var and thread.runner.set_user_var.

GUI: Several controls and their options still need to be translated into GTK. For that, both the GTK Docs for C and lib/gobject/src/gtk/gobject-cache-gtk.cr are helpful.

A more general overview:

  • src/build does the parsing etc. and is mostly complete
  • src/run/runner and src/run/thread are worth looking into, this is the heart of the application and where global and thread state is stored
  • src/cmd contains all commands exposed to the user.
  • There's three libraries included which somehow interact with the X server: x_do.cr for automatization (window, keyboard, mouse) as runner.x_do, crystal-gobject for Gtk (Gui, MsgBox) as runner.gui (gui.cr), and x11-cr for low-level X interaction (hotkeys, hotstrings) as runner.x11 (x11.cr).

There's also several TODO:s scattered around all source files mostly around technical problems that need some revisiting.

While Crystal brings its own hidden ::Thread class, any reference to Thread in the source refers to Run::Thread which actually are no real threads (see Run::Thread docs).

Issues

For bugs and feature requests, please open up an issue. I am also available on the AHK Discord server or the forum.

License

GPL-2.0


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK