6.2 KiB
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 main‑loop 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") ◄──────────── (non‑blocking 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
cd engine
make # produces `looper`
Client
cd client
make # produces `looper-client`
Running Together
- Start the JACK server (e.g.,
jackd -d alsaorpipewire). - In a terminal, start the engine:
cd engine && ./looper - In another terminal, start the client:
cd client && ./looper-client - 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 testinclient/runstest_status_parse. - Integration test for status FIFO (engine side):
make testinengine/tests/runstest_status_fifo.
These are not executed automatically from the top‑level make test – they must be invoked manually or added to the top‑level 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.