# Ah ah ah, you didn't say the magic word On a failed lock-screen unlock attempt, this spawns a looping animation of **the author's face photoshopped onto Dennis Nedry's body** and plays the "ah ah ah, you didn't say the magic word" clip from Jurassic Park. Each wrong guess adds another sprite at a random screen position. Volume is forced to 100% on the first failure and restored when the screen unlocks. ## Platform support The integration method differs by display server: | Platform | Display server | Module loaded | How it works | |---|---|---|---| | Linux | **Wayland** | `ahfail-module.so` | Loaded directly by `gtklock` via its module API | | Linux | **X11** | `libahfail_pam.so` + `ahfail-display` | PAM cleanup hook spawns the display binary after each failed auth | | macOS | — | `libahfail_pam.so` + `ahfail-display` | Same PAM approach, hooks into the screensaver PAM stack | The **gtklock module** is Wayland-only — it uses gtklock's internal window API to overlay sprites directly on the lock screen. The **PAM module** works on X11 and macOS by registering a cleanup callback that fires on each failed authentication attempt; it double-forks a standalone display binary (`ahfail-display`) that runs independently of the PAM stack. --- ## Linux (x86\_64) — binary install Pre-built binaries for both Wayland and X11 are available on the [releases page](https://gitea.weircon.dk/agw/gtk-ahfail/releases). The tarball contains all three files: | File | Used by | |---|---| | `ahfail-module.so` | Wayland — loaded by `gtklock` | | `libahfail_pam.so` | X11 — loaded by PAM | | `ahfail-display` | X11 — spawned by the PAM module | ```bash # Download and extract (replace v0.1.0 with the latest release) curl -fsSL https://gitea.weircon.dk/agw/gtk-ahfail/releases/download/v0.1.0/ahfail-linux-x86_64.tar.gz \ | sudo tar -xz -C /tmp/ahfail-install # Wayland (gtklock) sudo install -Dm755 /tmp/ahfail-install/ahfail-module.so /usr/lib/gtklock/ahfail-module.so # X11 (PAM module + display binary) sudo install -Dm755 /tmp/ahfail-install/libahfail_pam.so /usr/lib/ahfail/libahfail_pam.so sudo install -Dm755 /tmp/ahfail-install/ahfail-display /usr/lib/ahfail/ahfail-display ``` Then follow the [Wayland](#usage-linux---wayland-only-gtklock) or [X11](#usage-linux---x11-i3lock-xscreensaver-etc) configuration below. --- ## Linux compile ### Install dependencies and build from source ```bash sudo pacman -S meson ninja rust gtk3 gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gtklock meson setup builddir --prefix=/usr meson compile -C builddir sudo meson install -C builddir ``` `--prefix=/usr` matches Arch conventions (same as pacman) and ensures the compiled-in default path for `ahfail-display` matches where it is installed. Installs: - `/usr/lib/gtklock/ahfail-module.so` — gtklock module - `/usr/lib/ahfail/libahfail_pam.so` — PAM module (for X11) - `/usr/lib/ahfail/ahfail-display` — standalone display binary --- ## Usage Linux - wayland (only gtklock) ```bash gtklock -m /usr/lib/gtklock/ahfail-module.so ``` ### Arguments Pass module arguments after `--`: ```bash gtklock -m /usr/lib/gtklock/ahfail-module.so -- --deadzone=860,440,200,200 ``` - `--deadzone=X,Y,W,H` — rectangle where sprites will not spawn (keep your password field clear) - `--audio-uri=URI` — override the default audio clip, e.g. `file:///home/user/custom.mp3` --- ## Usage Linux - X11 (i3lock, xscreensaver, etc.) Add a line to your screen locker's PAM service file (e.g. `/etc/pam.d/i3lock`). This is a config file entry — use `tee -a` to append it, not `sudo` directly: **Arch / standard (`--prefix=/usr`):** ```bash echo 'auth optional /usr/lib/ahfail/libahfail_pam.so' | sudo tee -a /etc/pam.d/i3lock ``` **Fedora / RHEL (multilib):** ```bash echo 'auth optional /usr/lib64/ahfail/libahfail_pam.so' | sudo tee -a /etc/pam.d/i3lock ``` **Debian / Ubuntu (multiarch):** ```bash echo 'auth optional /usr/lib/x86_64-linux-gnu/ahfail/libahfail_pam.so' | sudo tee -a /etc/pam.d/i3lock ``` Replace `i3lock` with your locker's service name (`xscreensaver`, `lightdm`, etc.). The full path is required — `$(libdir)/ahfail` is not in PAM's default search path. If the display binary is not at the default location, add `display_path=`: ```bash echo 'auth optional /usr/lib/ahfail/libahfail_pam.so display_path=/usr/lib/ahfail/ahfail-display' | sudo tee -a /etc/pam.d/i3lock ``` --- ## macOS ### Homebrew (recommended) ```bash brew tap agw/ahfail https://gitea.weircon.dk/agw/homebrew-ahfail brew install ahfail ``` After install, Homebrew prints the PAM line to add to `/etc/pam.d/screensaverui` (macOS 13+) or `/etc/pam.d/screensaver` (macOS 12 and earlier). That one-line edit is the only manual step. To upgrade: `brew upgrade ahfail`. ### Script install ```bash git clone https://gitea.weircon.dk/agw/gtk-ahfail.git cd gtk-ahfail bash scripts/install-macos.sh ``` Installs Homebrew dependencies, builds from source, copies binaries to `/usr/local/lib/ahfail/`, and patches the screensaver PAM configuration automatically. ### Manual install ```bash brew install gtk+3 gstreamer gst-plugins-base gst-plugins-good meson ninja # install Rust via https://rustup.rs if not present meson setup builddir && meson compile -C builddir sudo mkdir -p /usr/local/lib/ahfail sudo cp builddir/libahfail_pam.so builddir/ahfail-display /usr/local/lib/ahfail/ ``` Add to `/etc/pam.d/screensaverui` (macOS 13+) or `/etc/pam.d/screensaver` after the existing `auth` entries: ``` auth optional /usr/local/lib/ahfail/libahfail_pam.so ``` --- ## Customization The sprite is the author's face on Nedry's body. To use your own: 1. Replace the sprite frames in `assets/sprites/` with your own PNG sequence. 2. Update `assets/ahfail.gresource.xml` if you add or remove files. 3. Rebuild with Meson. To replace the audio clip, swap `assets/audio/magic-word.mp3` and rebuild. --- ## Development ```bash cargo test # unit + integration tests (needs display) cargo test --test benchmarks -- --nocapture # sprite placement benchmarks cargo clippy # lint meson setup builddir && meson compile -C builddir # full build including .so xvfb-run meson test -C builddir --verbose # Meson tests headless ```