docs: add input-and-placement OpenSpec change and update AGENTS.md
Plan input handling and pattern placement before Game of Life mechanics: keyboard bindings config, mouse placement with preview/rotation, HiDPI scaling, and cell grid. Update AGENTS.md project notes.
This commit is contained in:
@@ -1,49 +1,22 @@
|
|||||||
# wlgol
|
# wlgol
|
||||||
|
|
||||||
Simple Wayland Game of Life in C11. Draws directly into a shared-memory buffer (no OpenGL/Vulkan).
|
Wayland Game of Life in C11; renders into a shared-memory buffer (no GL/Vulkan).
|
||||||
|
|
||||||
- Build definition: `CMakeLists.txt`
|
## Build & test
|
||||||
- Generated code: `xdg-shell.xml` is vendored; `wayland-scanner` produces `xdg-shell.h` and `xdg-shell.c` inside `build/`
|
|
||||||
- Source layout: `wlgol.c` is the entry point; `tkeyboard.c`, `tpointer.c`, `hashtable.c` are modules; headers live in `include/`; tests live in `tests/`
|
|
||||||
|
|
||||||
## Dependencies
|
- Configure: `cmake -G Ninja -B build`
|
||||||
|
- Build: `cmake --build ./build`
|
||||||
|
- Tests: `ctest --test-dir build` (single target `Hashtable`); or run `./build/test_hashtable`
|
||||||
|
- Run app: `./build/wlgol` (requires a running Wayland compositor; not runnable headless)
|
||||||
|
|
||||||
Needs `cmake`, `ninja`, `pkg-config`, `wayland-scanner`, plus the runtime libraries `wayland-client`, `wayland-cursor`, and `xkbcommon`.
|
Dependencies: `cmake`, `ninja`, `pkg-config`, `wayland-scanner`; libraries `wayland-client`, `wayland-cursor`, `xkbcommon`.
|
||||||
|
|
||||||
## Common commands
|
## Generated code
|
||||||
|
|
||||||
Configure (from repo root):
|
- `xdg-shell.h` / `xdg-shell.c` are generated by `wayland-scanner` from the vendored `xdg-shell.xml` into `build/include/` and `build/`. Do not edit them; rebuild to regenerate. `CMakeLists.txt` lists the generated `.c` in `SOURCES` and the `.h` as a source to force generation.
|
||||||
|
- `compile_commands.json` at the repo root is a symlink to `build/compile_commands.json`; configure first.
|
||||||
|
|
||||||
```bash
|
## Code notes
|
||||||
cmake -G Ninja -B build
|
|
||||||
```
|
|
||||||
|
|
||||||
Build:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cmake --build ./build
|
|
||||||
```
|
|
||||||
|
|
||||||
Run tests:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ctest --test-dir build
|
|
||||||
```
|
|
||||||
|
|
||||||
Run a single test directly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./build/test_hashtable
|
|
||||||
```
|
|
||||||
|
|
||||||
Run the program (requires a running Wayland compositor):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./build/wlgol
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- `compile_commands.json` is generated automatically in the build directory because `CMAKE_EXPORT_COMPILE_COMMANDS` is set in `CMakeLists.txt`.
|
|
||||||
- The project currently has one CTest target named `Hashtable`.
|
|
||||||
|
|
||||||
|
- Hashtable: `hashtable_new(size)` requires `size > 0` (used as a modulo). `hashtable_destroy` frees keys/entries but not values (caller owns values). `hashtable_set` returns the previous value or `NULL`, indistinguishable from "key absent".
|
||||||
|
- Entry point is `wlgol.c`; modules `tkeyboard.c`, `tpointer.c`, `hashtable.c`; headers in `include/`.
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-11-08
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
# 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.
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Proposal
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
wlgol currently renders an animated stripe pattern and has no Game of Life state, no input handling beyond Escape, and no HiDPI awareness. Before implementing the automaton, the application needs a usable interaction layer: configurable keyboard bindings, mouse-driven placement of patterns with a live preview, and rendering that stays crisp on scaled displays. This change establishes that layer so later GoL mechanics can build on it.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Add configurable keyboard bindings loaded from `~/.config/wlgol/keys.conf` (TOML), merged over built-in defaults, with modifier-aware matching (Ctrl/Shift/Alt/Super).
|
||||||
|
- Extend keyboard handling to report active modifiers and dispatch bound actions (`quit`, `cancel`, `rotate`, `select:<pattern>`, `clear`, `pause`, `step`, `speed_up`, `speed_down`, `toggle_grid`).
|
||||||
|
- Implement mouse tracking and click handling: selecting a tool shows a rotating preview that follows the cursor; `Space` rotates 90°; left click places the pattern (tool stays armed); `Esc` cancels.
|
||||||
|
- Add a cell grid (8 logical px per cell, wall boundary) with storage, resize-with-preserve, and rendering of alive cells.
|
||||||
|
- Add four built-in patterns (Glider, Blinker, Block, Toad) with rotation support.
|
||||||
|
- Add HiDPI scaling (fractional via `wp_fractional_scale` + `wp_viewporter`, fallback to `wl_output.scale`) with logical/physical coordinate conversion.
|
||||||
|
- Auto-generate a default `keys.conf` on first run if missing.
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
|
||||||
|
- `keyboard-bindings`: configurable, modifier-aware key-to-action mapping with defaults and TOML override.
|
||||||
|
- `pattern-placement`: tool selection, live preview, rotation, click placement, cancellation.
|
||||||
|
- `display-scaling`: fractional and integer HiDPI scaling with logical/physical conversion.
|
||||||
|
- `cell-grid`: grid storage, resize handling, wall boundary, alive-cell rendering.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
- Affected code: `wlgol.c`, `tkeyboard.c/h`, `tpointer.c/h`; new modules `config`, `grid`, `pattern`, `scale`, `render`.
|
||||||
|
- New dependency: vendored `tomlc99` for TOML parsing; generated Wayland protocols `viewporter` and `fractional-scale-v1`.
|
||||||
|
- Config I/O under `~/.config/wlgol/`; no public API changes.
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Stores and renders the Game of Life cell grid with a fixed cell size and wall boundaries, independent of future simulation rules.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Grid stores alive and dead cells
|
||||||
|
The system SHALL maintain a grid of cells, each either alive or dead, addressable by column and row.
|
||||||
|
|
||||||
|
#### Scenario: Cell access
|
||||||
|
- **WHEN** a cell is set to alive
|
||||||
|
- **THEN** reading that cell returns alive
|
||||||
|
- **AND** cells never set remain dead
|
||||||
|
|
||||||
|
### Requirement: Boundary cells are treated as dead
|
||||||
|
The system SHALL treat any cell outside the grid bounds as dead and shall not store state outside the grid.
|
||||||
|
|
||||||
|
#### Scenario: Out of bounds read
|
||||||
|
- **WHEN** a cell position outside the grid is queried
|
||||||
|
- **THEN** the result is dead
|
||||||
|
|
||||||
|
### Requirement: Grid resizes while preserving overlapping cells
|
||||||
|
The system SHALL preserve cells that remain within bounds when the grid dimensions change, clearing only cells that fall outside the new bounds.
|
||||||
|
|
||||||
|
#### Scenario: Enlarge grid
|
||||||
|
- **WHEN** the grid grows in either dimension
|
||||||
|
- **THEN** previously alive cells within the old bounds remain alive
|
||||||
|
|
||||||
|
#### Scenario: Shrink grid
|
||||||
|
- **WHEN** the grid shrinks in either dimension
|
||||||
|
- **THEN** cells outside the new bounds are discarded
|
||||||
|
- **AND** remaining cells keep their state
|
||||||
|
|
||||||
|
### Requirement: Cell size is fixed in logical pixels
|
||||||
|
The system SHALL use a cell size of 8 logical pixels, so grid dimensions derive from the logical surface size.
|
||||||
|
|
||||||
|
#### Scenario: Dimension calculation
|
||||||
|
- **WHEN** the logical surface is 400 pixels wide
|
||||||
|
- **THEN** the grid has 50 columns
|
||||||
|
|
||||||
|
### Requirement: Alive cells are rendered
|
||||||
|
The system SHALL render alive cells distinctly from dead cells.
|
||||||
|
|
||||||
|
#### Scenario: Render alive cell
|
||||||
|
- **WHEN** a cell is alive
|
||||||
|
- **THEN** it is drawn with the alive cell color
|
||||||
|
|
||||||
|
### Requirement: Grid can be cleared
|
||||||
|
The system SHALL set all cells to dead when the clear action is triggered.
|
||||||
|
|
||||||
|
#### Scenario: Clear grid
|
||||||
|
- **WHEN** the user triggers the clear action
|
||||||
|
- **THEN** all cells become dead
|
||||||
|
|
||||||
|
### Requirement: Grid lines can be toggled
|
||||||
|
The system SHALL show or hide grid lines when the toggle_grid action is triggered.
|
||||||
|
|
||||||
|
#### Scenario: Toggle lines
|
||||||
|
- **WHEN** the user triggers the toggle_grid action
|
||||||
|
- **THEN** grid line visibility flips
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Keeps the application rendering crisp on displays with integer or fractional scaling by converting between logical and physical coordinates.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Fractional scale is applied when available
|
||||||
|
The system SHALL use the compositor-provided fractional preferred scale for rendering when the fractional scale protocol is available.
|
||||||
|
|
||||||
|
#### Scenario: Fractional display
|
||||||
|
- **WHEN** the compositor sends a preferred scale of 1.5
|
||||||
|
- **THEN** the render buffer is created at 1.5 times the logical size
|
||||||
|
- **AND** the buffer is displayed at the logical size
|
||||||
|
|
||||||
|
### Requirement: Integer scale is used as fallback
|
||||||
|
The system SHALL use the output's integer scale when fractional scale is unavailable.
|
||||||
|
|
||||||
|
#### Scenario: No fractional protocol
|
||||||
|
- **WHEN** the compositor does not support fractional scaling
|
||||||
|
- **THEN** the render buffer uses the output integer scale
|
||||||
|
- **AND** the buffer is displayed at the logical size
|
||||||
|
|
||||||
|
### Requirement: Input coordinates remain logical
|
||||||
|
The system SHALL report mouse positions and grid cells in logical coordinates regardless of the active scale.
|
||||||
|
|
||||||
|
#### Scenario: Scaled click
|
||||||
|
- **WHEN** the user clicks on a scaled display
|
||||||
|
- **THEN** the selected grid cell matches the visual location of the cursor
|
||||||
|
|
||||||
|
### Requirement: Rendering uses physical resolution
|
||||||
|
The system SHALL render all content at physical resolution so that lines and cells stay sharp at the active scale.
|
||||||
|
|
||||||
|
#### Scenario: Sharp cells
|
||||||
|
- **WHEN** the application runs on a 2x scaled display
|
||||||
|
- **THEN** cells are drawn with 2 physical pixels per logical pixel
|
||||||
|
|
||||||
|
### Requirement: Scale changes trigger re-render
|
||||||
|
The system SHALL recreate buffers and re-render when the active scale changes.
|
||||||
|
|
||||||
|
#### Scenario: Scale change
|
||||||
|
- **WHEN** the compositor changes the preferred scale
|
||||||
|
- **THEN** the application updates its buffers
|
||||||
|
- **AND** subsequent frames use the new scale
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Allows users to configure keyboard shortcuts that map key combinations to application actions, with sensible built-in defaults when no configuration file exists.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Default bindings are available without configuration
|
||||||
|
The system SHALL provide built-in default bindings for all documented actions, including digits 1-4 selecting the Glider, Blinker, Block, and Toad patterns, Space rotating, Escape cancelling, and Ctrl+Q quitting.
|
||||||
|
|
||||||
|
#### Scenario: No config file present
|
||||||
|
- **WHEN** the application starts and `~/.config/wlgol/keys.conf` does not exist
|
||||||
|
- **THEN** the application uses the built-in default bindings
|
||||||
|
- **AND** creates the config file with the default contents
|
||||||
|
|
||||||
|
### Requirement: Configuration file overrides defaults by merge
|
||||||
|
The system SHALL read `~/.config/wlgol/keys.conf` as TOML when it exists and merge its entries over the built-in defaults, so only specified keys change.
|
||||||
|
|
||||||
|
#### Scenario: Partial override
|
||||||
|
- **WHEN** the config file defines a binding for key "1" only
|
||||||
|
- **THEN** key "1" uses the configured action
|
||||||
|
- **AND** all other keys keep their default bindings
|
||||||
|
|
||||||
|
#### Scenario: Full replacement of a key
|
||||||
|
- **WHEN** the config file defines a binding for a key that has a default
|
||||||
|
- **THEN** the configured action replaces the default for that key
|
||||||
|
|
||||||
|
### Requirement: Key combinations are modifier-aware
|
||||||
|
The system SHALL match a binding only when the modifiers required by the binding are active, supporting Ctrl, Shift, Alt, and Super.
|
||||||
|
|
||||||
|
#### Scenario: Modifier required
|
||||||
|
- **WHEN** the user presses Q while Ctrl is held and a `ctrl+q = quit` binding exists
|
||||||
|
- **THEN** the quit action is triggered
|
||||||
|
|
||||||
|
#### Scenario: Modifier not held
|
||||||
|
- **WHEN** the user presses Q without Ctrl while a `ctrl+q = quit` binding exists
|
||||||
|
- **THEN** the quit action is not triggered by that binding
|
||||||
|
|
||||||
|
### Requirement: Readable key aliases are supported
|
||||||
|
The system SHALL accept human-readable key names such as space, escape, enter, and arrow names in binding keys.
|
||||||
|
|
||||||
|
#### Scenario: Alias binding
|
||||||
|
- **WHEN** the config file contains `space = rotate`
|
||||||
|
- **THEN** pressing the space bar triggers the rotate action
|
||||||
|
|
||||||
|
### Requirement: Malformed entries are ignored
|
||||||
|
The system SHALL ignore malformed or unrecognized binding entries and continue using defaults for the affected keys.
|
||||||
|
|
||||||
|
#### Scenario: Invalid entry
|
||||||
|
- **WHEN** the config file contains an entry with an unknown action
|
||||||
|
- **THEN** that entry is ignored
|
||||||
|
- **AND** the application still starts successfully
|
||||||
|
|
||||||
|
### Requirement: Actions trigger on key press
|
||||||
|
The system SHALL dispatch a bound action when the corresponding key is pressed, not on release.
|
||||||
|
|
||||||
|
#### Scenario: Press triggers action
|
||||||
|
- **WHEN** the user presses a key with a valid binding
|
||||||
|
- **THEN** the bound action is invoked once for that press
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# Spec Delta
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Lets users select a Game of Life pattern, preview it under the mouse cursor, rotate it, and place copies on the grid with a click.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Selecting a pattern arms a placement tool
|
||||||
|
The system SHALL activate a placement tool for the selected pattern when a `select:<pattern>` action is triggered, and show a preview following the cursor.
|
||||||
|
|
||||||
|
#### Scenario: Tool activation
|
||||||
|
- **WHEN** the user triggers `select:glider`
|
||||||
|
- **THEN** a glider preview appears at the current cursor position
|
||||||
|
- **AND** the placement tool remains active
|
||||||
|
|
||||||
|
### Requirement: Preview follows cursor motion
|
||||||
|
The system SHALL update the preview position to track the current mouse cursor cell while a tool is active.
|
||||||
|
|
||||||
|
#### Scenario: Mouse moves
|
||||||
|
- **WHEN** the user moves the mouse while a tool is active
|
||||||
|
- **THEN** the preview moves to the cell under the cursor
|
||||||
|
|
||||||
|
### Requirement: Tool can be rotated in 90-degree steps
|
||||||
|
The system SHALL rotate the active preview by 90 degrees each time the rotate action is triggered.
|
||||||
|
|
||||||
|
#### Scenario: Rotate preview
|
||||||
|
- **WHEN** the user triggers the rotate action while a tool is active
|
||||||
|
- **THEN** the preview rotates 90 degrees clockwise
|
||||||
|
- **AND** remains at the same anchor cell
|
||||||
|
|
||||||
|
### Requirement: Left click places the pattern when valid
|
||||||
|
The system SHALL write the active pattern's alive cells into the grid at the preview position when the user left-clicks and the pattern fits entirely within the grid.
|
||||||
|
|
||||||
|
#### Scenario: Valid placement
|
||||||
|
- **WHEN** the user left-clicks while a tool is active and the preview fits in the grid
|
||||||
|
- **THEN** the pattern's cells become alive in the grid
|
||||||
|
- **AND** the tool remains active for further placement
|
||||||
|
|
||||||
|
### Requirement: Invalid placement is rejected
|
||||||
|
The system SHALL not modify the grid when a left click occurs while the preview extends outside the grid.
|
||||||
|
|
||||||
|
#### Scenario: Out of bounds
|
||||||
|
- **WHEN** the user left-clicks while the preview extends outside the grid
|
||||||
|
- **THEN** no cells are changed
|
||||||
|
- **AND** the tool remains active
|
||||||
|
|
||||||
|
### Requirement: Escape cancels the active tool
|
||||||
|
The system SHALL deactivate the placement tool and remove the preview when the cancel action is triggered.
|
||||||
|
|
||||||
|
#### Scenario: Cancel placement
|
||||||
|
- **WHEN** the user triggers the cancel action while a tool is active
|
||||||
|
- **THEN** the preview disappears
|
||||||
|
- **AND** no further clicks place a pattern until a tool is selected again
|
||||||
|
|
||||||
|
### Requirement: Click without active tool does nothing
|
||||||
|
The system SHALL ignore left clicks when no placement tool is active.
|
||||||
|
|
||||||
|
#### Scenario: No tool
|
||||||
|
- **WHEN** the user left-clicks while no tool is active
|
||||||
|
- **THEN** the grid is unchanged
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# Tasks
|
||||||
|
|
||||||
|
## 1. Protocol generation and build setup
|
||||||
|
|
||||||
|
- [ ] 1.1 Vendor `viewporter.xml` and `fractional-scale-v1.xml` into the repo and add wayland-scanner custom commands in `CMakeLists.txt`, verifying headers generate under `build/include`.
|
||||||
|
- [ ] 1.2 Add `tomlc99.c`/`tomlc99.h` to the source tree and CMake sources, verifying the project still configures and builds.
|
||||||
|
|
||||||
|
## 2. Configuration module
|
||||||
|
|
||||||
|
- [ ] 2.1 Implement `config.c/h` to load or auto-create `~/.config/wlgol/keys.conf`, parse TOML bindings, and merge with built-in defaults, verifying a sample file round-trips.
|
||||||
|
- [ ] 2.2 Implement binding string parsing (modifiers + key aliases) and lookup by modifier mask and keysym, verifying unit tests cover merge and modifier matching.
|
||||||
|
|
||||||
|
## 3. Keyboard module
|
||||||
|
|
||||||
|
- [ ] 3.1 Extend `keyboard_key_handler_t` to pass a modifier mask and update `tkeyboard.c` to compute it from xkb state, verifying compilation of all callers.
|
||||||
|
- [ ] 3.2 Wire keyboard events to config-based binding lookup and action dispatch, verifying Escape and Ctrl+Q trigger their actions.
|
||||||
|
|
||||||
|
## 4. Pointer module
|
||||||
|
|
||||||
|
- [ ] 4.1 Track logical cursor position in `tpointer.c/h` from motion events, verifying position updates on move.
|
||||||
|
- [ ] 4.2 Dispatch left-click events to the placement controller, verifying clicks are received.
|
||||||
|
|
||||||
|
## 5. Grid module
|
||||||
|
|
||||||
|
- [ ] 5.1 Implement `grid.c/h` with storage, bounds checks, resize-with-preserve, clear, and toggle state, verifying unit tests for resize and bounds.
|
||||||
|
- [ ] 5.2 Expose grid dimensions derived from logical surface size at 8 px per cell, verifying dimensions update on resize.
|
||||||
|
|
||||||
|
## 6. Pattern module
|
||||||
|
|
||||||
|
- [ ] 6.1 Implement `pattern.c/h` with Glider, Blinker, Block, and Toad offsets and 90-degree rotation transform, verifying rotated offsets and bounds.
|
||||||
|
- [ ] 6.2 Implement placement validity check (pattern fits within grid), verifying in-bounds and out-of-bounds cases.
|
||||||
|
|
||||||
|
## 7. Scaling module
|
||||||
|
|
||||||
|
- [ ] 7.1 Implement `scale.c/h` to bind fractional scale and viewporter, compute physical buffer size, and convert coordinates, verifying scale changes update buffer size.
|
||||||
|
- [ ] 7.2 Implement integer-scale fallback via `wl_output`, verifying fallback path builds without fractional protocol.
|
||||||
|
|
||||||
|
## 8. Rendering and application integration
|
||||||
|
|
||||||
|
- [ ] 8.1 Refactor `wlgol.c` to use logical surface size, create scaled shm buffers, and render grid plus alive cells, verifying build succeeds.
|
||||||
|
- [ ] 8.2 Add placement controller state machine (IDLE/ARMED), preview rendering, rotation, click placement, and cancellation, verifying behavior by manual run.
|
||||||
|
- [ ] 8.3 Integrate config loading, default file creation, and action dispatch into startup, verifying default config is generated.
|
||||||
|
|
||||||
|
## 9. Verification
|
||||||
|
|
||||||
|
- [ ] 9.1 Build with CMake/Ninja and run `ctest --test-dir build`, verifying all tests pass.
|
||||||
|
- [ ] 9.2 Run `./build/wlgol` under a Wayland compositor and manually verify placement, rotation, HiDPI scaling, and Esc cancellation.
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
|
||||||
|
# Project context (optional)
|
||||||
|
# Add only constraints that should guide OpenSpec artifacts and workflows.
|
||||||
|
# Include constraints an agent cannot infer by reading the code.
|
||||||
|
# Keep general project documentation and discoverable codebase facts out.
|
||||||
|
# Example:
|
||||||
|
# context: |
|
||||||
|
# Designs and tasks must cover Windows, macOS, and Linux
|
||||||
|
# Write all artifacts in Spanish
|
||||||
|
|
||||||
|
# Per-artifact rules (optional)
|
||||||
|
# Add custom rules for specific artifacts.
|
||||||
|
# Example:
|
||||||
|
# rules:
|
||||||
|
# proposal:
|
||||||
|
# - Keep proposals under 500 words
|
||||||
|
# - Always state what is out of scope
|
||||||
|
# tasks:
|
||||||
|
# - Break tasks into chunks of max 2 hours
|
||||||
|
|
||||||
|
# Per-operation guidance (optional)
|
||||||
|
# Add advisory guidance for how apply and archive work should be conducted.
|
||||||
|
# This is separate from artifact rules above.
|
||||||
|
# Example:
|
||||||
|
# operations:
|
||||||
|
# apply:
|
||||||
|
# guidance:
|
||||||
|
# - Keep test summaries concise
|
||||||
|
# archive:
|
||||||
|
# guidance:
|
||||||
|
# - Summarize the archive outcome before finishing
|
||||||
Reference in New Issue
Block a user