Files
wlgol/openspec/changes/archive/2026-11-08-input-and-placement/design.md
T
arek 7874a5f7c6 feat: implement keyboard and mouse input, pattern placement, and HiDPI scaling
Add configurable key bindings (TOML), modifier-aware dispatch, mouse-driven
pattern placement with preview and rotation, cell grid storage/rendering,
and fractional/integer HiDPI scaling. Archive the input-and-placement change
and sync its specs to main specs.

Tests: Hashtable, Config, Grid, Pattern (4/4 pass).
2026-11-08 14:46:04 +01:00

4.5 KiB

Design

Context

Current application (wlgol.c) renders an animated stripe pattern into a fixed 400x400 shm buffer, handles only Escape, and ignores configure size and output scale. Input modules (tkeyboard, tpointer) exist but pointer motion/buttons are unimplemented and keyboard callbacks do not carry modifiers. HiDPI is unsupported. See proposal.md for motivation.

Goals / Non-Goals

Goals:

  • Configurable, modifier-aware key bindings with TOML merge over defaults.
  • Interactive pattern placement with preview, rotation, click placement, cancellation.
  • HiDPI-correct rendering (fractional and integer).
  • Cell grid storage/rendering with resize preservation and wall boundaries.

Non-Goals:

  • Game of Life simulation rules (future change).
  • Text rendering/HUD (status via stdout).
  • Persistent grid save/load.
  • Fractional scaling on compositors lacking the protocol beyond integer fallback.

Decisions

D1: TOML config via vendored tomlc99

  • Choice: Vendor tomlc99 (single .c/.h) for parsing ~/.config/wlgol/keys.conf.
  • Rationale: User requested TOML; no system TOML library available; vendoring avoids new system dependency.
  • Alternative: Hand-rolled subset parser (less code, but TOML edge cases and maintenance burden).

D2: Binding representation

  • Choice: Store bindings as array of {mods_mask, keysym, action}. Lookup by exact modifier mask (ignoring lock mods) and keysym on key press.
  • Rationale: Simple, fast enough for single-threaded event loop.
  • Alternative: Hash table keyed by (mods, keysym) — premature; linear scan over ~20 bindings is trivial.

D3: Keyboard callback carries modifiers

  • Choice: Extend keyboard_key_handler_t to (..., uint32_t mods, void *user_data), computed in tkeyboard.c from xkb state.
  • Rationale: Binding matching needs modifier state; centralizes xkb modifier queries.

D4: HiDPI via fractional scale + viewporter

  • Choice: Prefer wp_fractional_scale_v1; render physical buffer = ceil(logical * scale); set buffer_scale=1 and wp_viewport destination = logical size. Fallback to wl_output.scale integer (treat as scale value).
  • Rationale: Exact fractional scaling; unifies rendering path through viewporter.
  • Alternative: Integer-only scaling (simpler but blurry/wrong at 125%/150%).

D5: Logical canvas and grid

  • Choice: Logical surface size from xdg configure (default 400x400). Grid dimensions = logical / 8. Cell size in logical pixels fixed at 8.
  • Rationale: Consistent interaction across scales; HiDPI keeps physical cell size proportional.

D6: Grid storage and resize

  • Choice: struct grid { int cols, rows; unsigned char *cells; } (0/1). On resize, allocate new buffer, copy overlapping region, free old.
  • Rationale: Preserves user work on window resize; simple.

D7: Pattern definitions and rotation

  • Choice: Each pattern stores base cell offsets and a rotation (0..3). Transform offsets at use time; bounding box computed from transformed offsets. Anchor = top-left of bounding box at cursor cell.
  • Rationale: Avoids duplicating 4 variants per pattern; rotation stays consistent.

D8: Placement state machine

  • Choice: States IDLE and ARMED. Key select → ARMED; motion updates preview cell; rotate increments rotation; left click places if in-bounds (stays ARMED); Escape → IDLE.
  • Rationale: Matches requested UX; tool remains active for repeated placement.

D9: Protocols generation

  • Choice: Vendor/copy viewporter.xml and fractional-scale-v1.xml into repo (from system wayland-protocols) and generate headers/sources via wayland-scanner in CMake, mirroring xdg-shell.
  • Rationale: Reproducible builds without depending on system protocol paths.

Risks / Trade-offs

  • [Fractional scaling complexity] → Mitigation: isolate in scale.c; integer fallback ensures basic functionality.
  • [Resize resets grid partially] → Mitigation: preserve overlapping region; document behavior.
  • [tomlc99 vendoring licensing] → Mitigation: verify license (MIT) before commit.
  • [No font rendering] → Status via stdout; acceptable for this change.
  • [Placement near edges] → Mitigation: reject invalid placement; preview indicates invalid state (e.g., red tint).

Migration Plan

  1. Add generated protocol files and CMake changes.
  2. Vendor tomlc99.
  3. Implement modules and integrate into wlgol.c.
  4. Update tests (hashtable unchanged; add config/grid unit tests if feasible).
  5. Run ctest and build.

Open Questions

None — all blocking decisions resolved during exploration.