Files
gtk-ahfail/CLAUDE.md
Asger Geel Weirsøe 2b89653be6 refactor: remove dead utils/bench.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 09:25:39 +02:00

3.6 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

What This Is

ahfail is a gtklock module. It compiles to ahfail-module.so, which gtklock loads at runtime. On each failed unlock attempt (PW_FAILURE), it spawns an animated "Nedry" sprite at a random screen location and plays an audio clip ("ah ah ah, you didn't say the magic word").

Build System

The build is a two-stage hybrid:

  1. Meson compiles GResources from assets/ahfail.gresource.xml into C, invokes Cargo to produce libahfail_module.a, then links everything into ahfail-module.so.
  2. Cargo handles only the Rust crate — it produces a staticlib that Meson links.

Always use Meson for the final shared object; cargo build alone does not produce the loadable module.

# First-time setup
meson setup builddir

# Build
meson compile -C builddir

# Run Rust tests only (no GTK display required)
cargo test

# Run Meson tests (loads the .so, requires GTK)
meson test -C builddir

# Manual integration test
gtklock -d -m builddir/ahfail-module.so -- --deadzone=X,Y,W,H

Architecture

All FFI entry points live in src/lib.rs — these are the extern "C" functions that gtklock calls (on_activation, on_window_create, on_window_destroy, on_idle_hide, etc.).

Module lifecycle:

  • on_activation — called once at startup; initialises GTK/GStreamer, loads all sprite frames into a PixbufSimpleAnim, stores them in MODULE_STATE.
  • on_window_create — called per monitor; calls WindowHandler::create, which creates a gtk::Fixed overlay, a pool of pre-warmed GStreamer players, and wires up a signal on error_label that fires on each failed attempt.
  • on_window_destroy / on_idle_hide — clean up sprites and stop players.

Key types:

Type File Purpose
MODULE_STATE src/state.rs Thread-local RefCell<ModuleState> holding the shared animation, audio URI, and deadzone config
WindowData src/state.rs Heap-allocated per-window state (sprites, player pool, GTK signal handle)
WindowContext src/context.rs Safe wrapper around the raw *mut Window pointer passed from C
Window / GtkLock src/context.rs #[repr(C)] mirrors of gtklock's internal structs — must match include/gtklock-module.h
WindowHandler src/handler.rs Sprite placement logic (random position, deadzone avoidance with retries) and GStreamer player management
ModuleConfig src/config.rs Parses the --deadzone=x,y,w,h CLI argument via glib's GOptionEntry

Assets are embedded as GResources at build time. At runtime they are accessed via resource:///ahfail/sprites/... and resource:///ahfail/audio/magic-word.mp3.

Safety Conventions

  • All unsafe pointer work on *mut Window goes through WindowContext — never dereference the raw pointer outside that wrapper.
  • WindowData is heap-allocated via Box::into_raw and reclaimed via Box::from_raw in WindowContext::take_data. Do not free it any other way.
  • The module_data flexible array field on Window is a C ABI contract with gtklock — index 0 is this module's slot.

Tests

tests/ahfail_tests.rs — integration tests that construct mock Window / GtkLock structs directly to exercise handler logic without a running gtklock process. Run with cargo test.

tests/benchmarks.rs — criterion benchmarks for sprite placement.

tests/module_test.c — C smoke test built by Meson that dlopen-style verifies the exported symbols exist.