Files
looper/docs/8-tui.md

149 lines
6.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TUI Client Architecture and Usage
## Overview
The TUI client (`looper-client`) is a standalone ncurses application that communicates with the looper engine **only** via two named pipes:
- `/tmp/looper_cmd` the client writes text commands to the engine.
- `/tmp/looper_status` the engine writes one line per active channel after each mainloop iteration, reporting the current scene state.
The client never links against engine source code. It is built from files in `client/src/` and linked only with `-lncurses`.
## Architecture
```
User keypress
tui_run() ──► getch() ──► switch(ch)
│ │
│ ▼
│ send_command(cmd)
│ │
│ ▼
│ write("/tmp/looper_cmd")
│ ┌──────────────────┐
│ │ Engine main loop │
│ │ (looper.c) │
│ │ │
│ │ looper_process_ │
│ │ commands() │
│ │ │ │
│ │ ▼ │
│ │ looper_write_ │
│ │ status() │
│ │ │ │
│ └─────────┼────────┘
│ │
│ ▼
│ write("/tmp/looper_status")
│ read("/tmp/looper_status") ◄──────────── (nonblocking open)
│ │
│ ▼
parse_status_line(...)
cell_state[ch] = state
draw_grid() ──► state_to_color(state) returns colour pair
apply colour to cell
```
## Key Bindings
| Key | Action | FIFO command sent |
|------------------|---------------------------------------------|------------------------------|
| `h` / `←` | Move selection left | (none) |
| `j` / `↓` | Move selection down | (none) |
| `k` / `↑` | Move selection up | (none) |
| `l` / `→` | Move selection right | (none) |
| `t` | Record / toggle on selected column | `record <col>\n` |
| `s` | Next scene | `scene_next\n` |
| `S` | Previous scene | `scene_prev\n` |
| `d` / `D` | Stop all channels | `stop\n` |
| `a` | Add audio channel | `add\n` |
| `A` | Add MIDI channel | `add_midi\n` |
| `r` | Remove last dynamic channel | `remove\n` |
| `b` | Bind to selected column | `bind <col>\n` |
| `u` | Unbind (reset to channel 0) | `unbind\n` |
| `?` | Toggle help text | (none) |
| `Esc` / `Q` | Quit | (none) |
## Status Line Format
Each line written by the engine to `/tmp/looper_status` follows this pattern:
```
CH=<channel_number> SC=<scene_index> STATE=<state_string>
```
`<state_string>` is one of `IDLE`, `RECORD`, `LOOPING`, `PAUSED`.
Example:
```
CH=0 SC=0 STATE=RECORD
CH=1 SC=0 STATE=LOOPING
```
The client parses these lines and updates the colour of the corresponding cell:
- `IDLE` → white (`COLOR_EMPTY`)
- `RECORD` → red (`COLOR_RECORDING`)
- `LOOPING` → green (`COLOR_LOOPING`)
- `PAUSED` → blue (`COLOR_STOPPED`)
## Building and Running
### Engine
```sh
cd engine
make # produces `looper`
```
### Client
```sh
cd client
make # produces `looper-client`
```
### Running Together
1. Start the JACK server (e.g., `jackd -d alsa` or `pipewire`).
2. In a terminal, start the engine:
```sh
cd engine && ./looper
```
3. In another terminal, start the client:
```sh
cd client && ./looper-client
```
4. Use the TUI keys described above.
## Cleanup
When the client exits, it deletes both FIFOs (`/tmp/looper_cmd` and `/tmp/looper_status`).
If the engine is still running, it will continue to try to write to the status FIFO; that write will fail silently (the engine uses `O_NONBLOCK` and ignores errors).
The engine creates the status FIFO on startup and does not delete it.
## Testing
- **Unit test for status line parser**: `make test` in `client/` runs `test_status_parse`.
- **Integration test for status FIFO** (engine side): `make test` in `engine/tests/` runs `test_status_fifo`.
These are **not** executed automatically from the toplevel `make test` they must be invoked manually or added to the toplevel Makefile.
The engine status FIFO test (`test_status_fifo`) uses `select()` with a timeout and retry loop to wait for a status line showing `STATE=RECORD`. It is reliable and does not hang.
## Future Work
- Replace dead stubs (`FuzzySearch`, `marks`, `yank_buffer`, visual mode) with real implementations or remove them.
- Support transport play/pause via a dedicated FIFO command.
- Allow the client to display multiple scenes per channel (e.g., via a tab or side panel).
- Graceful error recovery when the engine or FIFO is not available.