add support for ppm images

This commit is contained in:
2026-08-02 22:00:22 +02:00
parent 9dba86f3ca
commit 3e5e6c6fa8
5 changed files with 328 additions and 21 deletions
+24 -7
View File
@@ -5,7 +5,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project
`att_lock` — an Android-style 4×4 dot-pattern screen locker for wlroots-based
Wayland compositors. Single C11 binary, ~1500 LOC across four `src/*.c` files. No test suite exists.
Wayland compositors. Single C11 binary, ~2000 LOC across five `src/*.c` files. No test suite exists.
## Build
@@ -21,10 +21,11 @@ nix develop --command ninja -C build # incremental rebu
Add new dependencies to `nativeDeps`/`libDeps` in `flake.nix` *and* to `meson.build`, never to a
global environment.
**Gotcha:** this repo is not git-tracked, so a plain `src = ./.` would vacuum the local `build/` dir
into the Nix sandbox and break `nix build` (meson chokes on a stale pre-configured build dir).
`flake.nix` filters `build` and `result*` out via `lib.cleanSourceWith` — keep that filter (or init
a git repo) whenever touching `flake.nix`.
**Gotcha:** the repo is a git repo now, so `nix build` only sees **git-tracked** files — a new
`src/*.c` has to be `git add`ed before it will build, or meson fails with `File src/... does not
exist` while `ninja -C build` keeps working fine. `flake.nix` additionally filters `build` and
`result*` out via `lib.cleanSourceWith`; that filter predates the git repo (it kept a plain
`src = ./.` from vacuuming a stale pre-configured `build/` into the sandbox) and is harmless to keep.
## Running it
@@ -129,8 +130,24 @@ Three things about the stored hash are deliberate and load-bearing:
corrupt file and `main()` routes that into the setup flow.
**Rendering** (`src/render.c`) — `wl_shm` + Cairo only, no EGL/GPU. Two buffers per output with a
`busy` flag driven by `wl_buffer.release`; `buffer_get()` reallocates on size change. Background
images are PNG-only (Cairo's built-in loader).
`busy` flag driven by `wl_buffer.release`; `buffer_get()` reallocates on size change. The background
is drawn cover-fit (`fmax` of the two axis ratios), so it follows a rotation with no extra work.
**Background images** (`src/image.c`) — `image_load()` takes PNG or PPM (both `P6` binary and `P3`
ASCII, 8- or 16-bit samples), by path or `-` for stdin: `image_gen | att_lock -i -`. Cairo only
decodes PNG and only from a file or stream, so the input is always buffered whole and then sniffed by
magic — which is also what lets a pipe work, since it can be neither reopened nor seeked. PPM decodes
straight into `CAIRO_FORMAT_RGB24`, a native-endian `0x00RRGGBB` word per pixel (write through a
`uint32_t*`, don't assemble bytes by hand, or the channels swap on the way out).
Two things here are deliberate:
- **Every failure returns NULL and is non-fatal.** A bad image must degrade to the plain dark
background, never to a locked session with nothing drawn on it. Decoding happens in `main()` before
`wl_display_connect()`, which also means `-i -` drains the pipe *before* taking the lock rather
than holding a blank screen for as long as the producer runs.
- **`-i -` refuses an interactive stdin** (`isatty`), because `att_lock -i -` typed at a shell would
otherwise block on the terminal forever with the session not yet locked.
## Conventions