move to AppImage

This commit is contained in:
phil294 committed 2023-02-05 21:27:44 +01:00
1 parent acc7187f45
commit a3baab7cf2
13 files changed
+151 -70

No files matched your search

+1
View File
@@ -0,0 +1 @@
See [./build/README.md](./build/README.md)
+3 -59
View File
@@ -125,11 +125,12 @@ Also planned, even though it's not part of 1.0.24 spec:
## Installation
**[Download the latest binary from the release section](https://github.com/phil294/AHK_X11/releases)**. Make the downloaded file executable and you should be good to go.
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](https://github.com/jhass/crystal-gobject/issues/73#issuecomment-661235729)). Otherwise, it should not matter what system you use.
Then, you can download the latest binary from the [release section](https://github.com/phil294/AHK_X11/releases). 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.
@@ -221,66 +222,9 @@ More tips:
- The first time an AtSpi-related command (`Control`-*, `WinGetText`, ... see "Accessibility" section above) runs, the interface needs to be initialized which can take some time (0-5s)
- Searching for windows is slow. Querying the active window is not. Also, windows are internally cached by their ID during the lifetime of the thread, so e.g. `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`.
## 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 DO <EM>NOT</EM> NEED TO FOLLOW THESE STEPS AS AN AUTOHOTKEY DEVELOPER**. To do that, download AHK_X11 - see **INSTALLATION** ABOVE. Then you can start scripting. The below steps are for DEVELOPING IN **CRYSTAL LANGUAGE**.
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 libatspi2.0-dev libssl-dev
```
1. [Install](https://crystal-lang.org/install/) Crystal and Shards (Shards is typically included in Crystal installation)
1. Arch Linux:
```
sudo pacman -S crystal shards gcc libxkbcommon libxinerama libxtst gtk3 gc
```
1. `git clone https://github.com/phil294/AHK_X11`
1. `cd AHK_X11`
1. `shards install`
1. Run various library tweaks with `./setup_dependencies.sh`. This is mostly WIP and hacked together, so if anything doesn't work, please open an issue.
1. Now everything is ready for local use with `shards build -Dpreview_mt`, *if* you have `libxdo` (xdotool) version 2021* upwards installed. For version 2016*, you'll need to upgrade this dependency somehow. One way to achieve this is explained below.<br>Read on for a cross-distro compatible build.
1. 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. (Update: this entire section is still correct, but not future proof and WIP, see [#24](https://github.com/phil294/AHK_X11/issues/24))
- 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](https://github.com/jordansissel/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`, `libXi`, `libXinerama` and `libXext`. The static libraries should be available from your package manager dependencies installed above so normally there's nothing you need to do.
- 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
- `libssl` and `libcrypto` because Ubuntu 22.04 introduced an abi version bump
- 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](https://github.com/AppImage/pkg2appimage/blob/master/excludelist): `libX11`, `libm`, `libpthread`, `librt`, `libdl`.
1. All in all, once you have `libxdo.a` inside the folder `static`, the following builds the final binary which should be very portable: `shards build -Dpreview_mt --link-flags="-no-pie -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 4.7 MiB in size.
1. 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.
## Contributing
If you feel like it, you are welcome to contribute! The language in use, Crystal, is resembling Ruby syntax also great for beginners.
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`](https://github.com/phil294/AHK_X11/blob/master/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](https://docs.gtk.org/gtk3) 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), `crystal-gobject` for Gtk (`Gui`, `MsgBox`, `gui.cr`) and Atspi (control handling, `at-spi.cr`), and `x11-cr` for low-level X interaction (hotkeys, hotstrings, `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`](https://github.com/phil294/AHK_X11/blob/master/src/run/thread.cr) docs).
Current commits are collected in the `development` branch and then merged into `master` for each release.
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](./build/README.md).
## Issues
+9
View File
@@ -0,0 +1,9 @@
[Desktop Entry]
Name=Compile with ahk_x11
Exec=ahk_x11 --compile %u
MimeType=application/x-ahk_x11
Icon=ahk_x11
Terminal=false
Type=Application
Categories=GTK;Utility;Development
Comment=AHK_X11: AutoHotkey for Linux - this is the compiler for ahk_x11 to create stand-alone binaries.
+8
View File
@@ -0,0 +1,8 @@
[Desktop Entry]
Name=Window Spy
Exec=ahk_x11 --windowspy
Icon=ahk_x11
Terminal=false
Type=Application
Categories=GTK;Utility;Development
Comment=AHK_X11: AutoHotkey for Linux - Window Spy is a tool to help with ahk_x11 scripting.
+9
View File
@@ -0,0 +1,9 @@
[Desktop Entry]
Name=ahk_x11
Exec=ahk_x11 %u
MimeType=application/x-ahk_x11
Icon=ahk_x11
Terminal=false
Type=Application
Categories=GTK;Utility;Development
Comment=AHK_X11: AutoHotkey for Linux
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

+4
View File
@@ -0,0 +1,4 @@
libxdo.so*
linuxdeploy-x86_64.AppImage
linuxdeploy-plugin-gtk.sh
*.AppImage
+66
View File
@@ -0,0 +1,66 @@
# Building from source
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 DO <EM>NOT</EM> NEED TO FOLLOW THESE STEPS FOR AUTOHOTKEY SCRIPTING**. To do that, download AHK_X11 from the release section instead. Then you can use it like AHK on Windows. The below steps are for DEVELOPING IN **CRYSTAL LANGUAGE**. If you don't know what this is about, **[GO BACK TO THE MAIN README](../README.md) and read INSTALLATION**. ⚠️
## Development
### For local usage
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 libatspi2.0-dev libssl-dev
```
1. [Install](https://crystal-lang.org/install/) Crystal and Shards (Shards is typically included in Crystal installation)
1. Arch Linux:
```
sudo pacman -S crystal shards gcc libxkbcommon libxinerama libxtst gtk3 gc
```
1. `git clone https://github.com/phil294/AHK_X11`
1. `cd AHK_X11`
1. `shards install`
1. Run various library tweaks with `./setup_dependencies.sh`. This is mostly WIP and hacked together, so if anything doesn't work, please open an issue.
1. Now everything is ready for local use with `shards build -Dpreview_mt`, *if* you have `libxdo` (xdotool) version 2021* upwards installed. For version 2016*, you'll need to upgrade this dependency somehow. One way to achieve this is explained below.
1. Find your final binary in the `./bin` folder, it's about 4 MiB in size.
### For making release-like binaries
The released binaries are special because they need to be portable. We achieve this by using AppImage. Portability 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. Below are the instructions on how to do this / how the released binaries are produced. It's all optional but recommended. Might automate this some day, but for now it's all manual.
1. Get on an Ubuntu 18.04 system, e.g. using Docker
1. `libxdo` isn't backwards compatible (e.g. Ubuntu 18.04 and 20.04 versions are incompatible). Also, we fix a rarely occurring fatal error here (probably Crystal-specific?). So,
- clone [xdotool](https://github.com/jordansissel/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` and then copy the files `libxdo.so` and `libxdo.so.3` into this very `build` folder.
1. Do the same steps as listed in "For local testing": Install dependencies etc.
1. Get `linuxdeploy-x86_64.AppImage` from https://github.com/linuxdeploy/linuxdeploy/, into this `build` folder
1. Get `linuxdeploy-plugin-gtk.sh` from https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh
1. Instead of `shards build`, run `./build.sh`. This also does shards build, but it adds the `--release` flag (slower compilation, faster output binary) and does the AppImage magic and attaches the installer.
1. Find your final binary `ahk_x11.AppImage` in the `build` folder. It's about 30 MiB in size.
## Contributing
If you feel like it, you are welcome to contribute! The language in use, Crystal, is resembling Ruby syntax and consequently also great for beginners.
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`](https://github.com/phil294/AHK_X11/blob/master/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](https://docs.gtk.org/gtk3) 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), `crystal-gobject` for Gtk (`Gui`, `MsgBox`, `gui.cr`) and Atspi (control handling, `at-spi.cr`), and `x11-cr` for low-level X interaction (hotkeys, hotstrings, `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`](https://github.com/phil294/AHK_X11/blob/master/src/run/thread.cr) docs).
Current commits are collected in the `development` branch and then merged into `master` for each release.
Executable
+37
View File
@@ -0,0 +1,37 @@
#!/bin/bash
set -e
cd "$(dirname "$0")"
cd ..
# Cannot overwrite CRYSTAL_LIBRARY_PATH because crystal#12380, need link-flag instead.
# -no-pie prevents
shards build -Dpreview_mt --link-flags="-L$PWD/build" \
-Dgc_none \
--release \
# --debug
# -Dgc_none
cd build
rm -rf AppDir
# export DEPLOY_GTK_VERSION=3 # is autodetected
export LD_LIBRARY_PATH="$PWD"
./linuxdeploy-x86_64.AppImage --appdir AppDir --output appimage --desktop-file ../assets/ahk_x11.desktop --icon-file ../assets/ahk_x11.png --executable ../bin/ahk_x11 --library ./libxdo.so.3 --plugin gtk
rm -rf AppDir
mv ahk_x11-x86_64.AppImage ahk_x11.AppImage
# Attaching the installer:
# 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.
# 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.
cp ahk_x11.AppImage ahk_x11_pre_installer.AppImage
./ahk_x11.AppImage --compile ../src/installer.ahk tmp && mv tmp ahk_x11.AppImage
echo 'bump shard version'
+1 -1
View File
@@ -50,7 +50,7 @@ if ARGV[0]?
puts "AHK_X11 version: #{version}\nTargets to partially implement Classic Windows AutoHotkey specification: v1.0.24 (2004). AutoHotkey is a scripting language."
::exit
elsif ARGV[0] == "-h" || ARGV[0] == "--help"
puts "AHK_X11 is a Linux implementation for AutoHotkey classic version 1.0.24 (2004). Internal version: #{version}. Full up to date documentation can be found at https://phil294.github.io/AHK_X11/.\n\nPossible methods of invocation:\n\nahk_x11 \"path to script.ahk\"\nahk_x11 /dev/stdin <<< $'MsgBox, 1\\nMsgBox, 2'\nahk_x11 --repl\nahk_x11 --windowspy\nahk_x11 --compile \"path to script.ahk\" \"optional: output executable file path\"\n\nAlternatively, just run the program without arguments to open the graphical installer. Once installed, you should be able to run and/or compile any .ahk file in your file manager by selecting it from the right click context menu."
puts "AHK_X11 is a Linux implementation for AutoHotkey classic version 1.0.24 (2004). Internal version: #{version}. Full up to date documentation can be found at https://phil294.github.io/AHK_X11/.\n\nPossible methods of invocation:\n\nahk_x11.AppImage \"path to script.ahk\"\nahk_x11.AppImage /dev/stdin <<< $'MsgBox, 1\\nMsgBox, 2'\nahk_x11.AppImage --repl\nahk_x11.AppImage --windowspy\nahk_x11.AppImage --compile \"path to script.ahk\" \"optional: output executable file path\"\n\nAlternatively, just run the program without arguments to open the graphical installer. Once installed, you should be able to run and/or compile any .ahk file in your file manager by selecting it from the right click context menu."
::exit
elsif ARGV[0] == "--repl"
lines = ["#Persistent"]
+6 -2
View File
@@ -2,8 +2,12 @@
class Compiler
TRAILER = "AHK_SOUR"
def initialize
bin_path = Process.executable_path
raise "Cannot determine binary path" if ! bin_path
bin_path = ENV["APPIMAGE"]?
if ! bin_path || bin_path.empty?
bin_path = Process.executable_path
raise "Cannot determine binary path" if ! bin_path
puts "WARNING: AHK_X11 Binary does not appear to be inside an AppImage. Compilation will succeed but the resulting standalone program will not be very portable as dependencies are not bundled. This means that it may not run on other systems, or may fail to run on your system in the future. It is highly recommended you download the official ahk_x11.AppImage file instead, or add the packaging step to your build process (check ahk_x11's latest build instructions)."
end
@bin_file = File.new(bin_path)
end
def finalize
+1 -1
View File
@@ -6,7 +6,7 @@ app_name = ahk_x11
app_ext = ahk
app_comment = AHK_X11: AutoHotkey for Linux
binary_dir = %A_Home%/.local/bin
binary_path = %binary_dir%/ahk_x11
binary_path = %binary_dir%/ahk_x11.AppImage
app_logo = /tmp/tmp_ahk_x11_logo.png
Gui, Add, Text, x180, %app_comment%
+6 -7
View File
@@ -69,11 +69,14 @@ module Run
def initialize(*, @builder, @script_file, @headless)
@labels = @builder.labels
@settings = @builder.runner_settings
script = @script_file ? @script_file.not_nil! : Path[Process.executable_path || ""].expand
script = @script_file ? @script_file.not_nil! : Path[binary_path].expand
set_global_built_in_static_var "A_ScriptDir", script.dirname
set_global_built_in_static_var "A_ScriptName", script.basename
set_global_built_in_static_var "A_ScriptFullPath", script.to_s
end
private def binary_path()
ENV["APPIMAGE"]? || Process.executable_path || raise RuntimeException.new "Cannot determine binary path"
end
def run
@settings.persistent ||= (! @builder.hotkeys.empty? || ! @builder.hotstrings.empty?)
if ! @headless
@@ -152,16 +155,12 @@ module Run
def reload
STDERR.puts "Reloading..."
bin_path = Process.executable_path
raise RuntimeException.new "Cannot determine binary path" if ! bin_path
p = Process.new bin_path, ARGV, chdir: @initial_working_dir
p = Process.new binary_path, ARGV, chdir: @initial_working_dir
exit_app 0
end
def launch_window_spy
bin_path = Process.executable_path
raise RuntimeException.new "Cannot determine binary path" if ! bin_path
p = Process.new bin_path, ["--windowspy"], chdir: @initial_working_dir
p = Process.new binary_path, ["--windowspy"], chdir: @initial_working_dir
end
private def auto_execute_section_ended