feat: add TUI client with FIFO communication and status display
This commit is contained in:
committed by
Loic Coenen (aider)
parent
5341cb676a
commit
971372eac9
8
client/src/main.c
Normal file
8
client/src/main.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "tui.h"
|
||||
|
||||
int main(void) {
|
||||
tui_init();
|
||||
tui_run();
|
||||
tui_cleanup();
|
||||
return 0;
|
||||
}
|
||||
@@ -36,6 +36,14 @@ static const char *clip_state_string(ClipState s) { (void)s; return "?"; }
|
||||
#define CELL_WIDTH 6
|
||||
#define CELL_HEIGHT 3
|
||||
|
||||
/* status FIFO path */
|
||||
#define STATUS_FIFO "/tmp/looper_status"
|
||||
#define CMD_FIFO "/tmp/looper_cmd"
|
||||
|
||||
/* Per‑cell state array (indexed by row*GRID_COLS+col) */
|
||||
typedef enum { STATE_IDLE, STATE_RECORD, STATE_LOOPING, STATE_PAUSED } ChannelState;
|
||||
static ChannelState cell_state[GRID_ROWS * GRID_COLS];
|
||||
|
||||
/* Color pairs */
|
||||
enum {
|
||||
COLOR_EMPTY=1, COLOR_RECORDING, COLOR_LOOPING, COLOR_STOPPED,
|
||||
@@ -64,8 +72,6 @@ typedef struct {
|
||||
static FuzzySearch fuzzy_search = {0};
|
||||
|
||||
/* ---------- Parse status line from engine status FIFO ---------- */
|
||||
typedef enum { STATE_IDLE, STATE_RECORD, STATE_LOOPING, STATE_PAUSED } ChannelState;
|
||||
|
||||
bool parse_status_line(const char *line, int *ch, int *scene, ChannelState *state) {
|
||||
int sta;
|
||||
if (sscanf(line, "CH=%d SC=%d STATE=%d", ch, scene, &sta) == 3) {
|
||||
@@ -85,14 +91,24 @@ bool parse_status_line(const char *line, int *ch, int *scene, ChannelState *stat
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ---------- State to color (dummy: all white) ---------- */
|
||||
static int state_to_color(ClipState s) { (void)s; return COLOR_EMPTY; }
|
||||
/* ---------- State to color (uses cell_state array) ---------- */
|
||||
static int state_to_color(ChannelState s) {
|
||||
switch (s) {
|
||||
case STATE_IDLE: return COLOR_EMPTY;
|
||||
case STATE_RECORD: return COLOR_RECORDING;
|
||||
case STATE_LOOPING: return COLOR_LOOPING;
|
||||
case STATE_PAUSED: return COLOR_STOPPED;
|
||||
default: return COLOR_EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Draw cell (no AppState) ---------- */
|
||||
static void draw_cell(int grid, int row, int col, bool selected) {
|
||||
int y = row * CELL_HEIGHT + 3;
|
||||
int x = col * CELL_WIDTH + 1;
|
||||
int color = selected ? COLOR_SELECTED : COLOR_EMPTY;
|
||||
int idx = row * GRID_COLS + col;
|
||||
ChannelState s = cell_state[idx];
|
||||
int color = selected ? COLOR_SELECTED : state_to_color(s);
|
||||
attron(COLOR_PAIR(color));
|
||||
for (int dy=0; dy<CELL_HEIGHT; dy++)
|
||||
for (int dx=0; dx<CELL_WIDTH; dx++)
|
||||
@@ -113,7 +129,7 @@ static void draw_grid(void) {
|
||||
selected_grid, selected_row, selected_col);
|
||||
if (show_help) {
|
||||
attron(COLOR_PAIR(COLOR_HELP));
|
||||
mvprintw(GRID_ROWS*CELL_HEIGHT+4, 0, "Help: h/j/k/l navigate, t record, d stop, s scene, ? help, q/Esc quit");
|
||||
mvprintw(GRID_ROWS*CELL_HEIGHT+4, 0, "Help: h/j/k/l navigate, t record, d/D stop, s/S scene, a add, A add_midi, r remove, b bind, u unbind, ? help, Esc/Q quit");
|
||||
attroff(COLOR_PAIR(COLOR_HELP));
|
||||
}
|
||||
refresh();
|
||||
@@ -132,14 +148,42 @@ void tui_init(void) {
|
||||
init_pair(COLOR_SELECTED, COLOR_BLACK, COLOR_CYAN);
|
||||
init_pair(COLOR_HELP, COLOR_CYAN, COLOR_BLACK);
|
||||
for (int i=0;i<26;i++) marks[i] = -1;
|
||||
/* initialise cell states to idle */
|
||||
for (int i = 0; i < GRID_ROWS * GRID_COLS; i++)
|
||||
cell_state[i] = STATE_IDLE;
|
||||
}
|
||||
|
||||
/* ---------- TUI run ---------- */
|
||||
void tui_run(void) {
|
||||
draw_grid();
|
||||
while (1) {
|
||||
int ch = getch();
|
||||
switch (ch) {
|
||||
/* read any available status lines */
|
||||
int fd = open(STATUS_FIFO, O_RDONLY | O_NONBLOCK);
|
||||
if (fd >= 0) {
|
||||
char buf[256];
|
||||
int n = read(fd, buf, sizeof(buf)-1);
|
||||
if (n > 0) {
|
||||
buf[n] = '\0';
|
||||
char *line = buf;
|
||||
while (*line) {
|
||||
char *nl = strchr(line, '\n');
|
||||
if (nl) *nl = '\0';
|
||||
int ch, sc;
|
||||
ChannelState st;
|
||||
if (parse_status_line(line, &ch, &sc, &st)) {
|
||||
if (ch >= 0 && ch < GRID_ROWS * GRID_COLS)
|
||||
cell_state[ch] = st;
|
||||
}
|
||||
if (nl) {
|
||||
*nl = '\n';
|
||||
line = nl + 1;
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
int chc = getch();
|
||||
switch (chc) {
|
||||
case 'h': case KEY_LEFT: selected_col = (selected_col-1+GRID_COLS)%GRID_COLS; break;
|
||||
case 'j': case KEY_DOWN: selected_row = (selected_row+1)%GRID_ROWS; break;
|
||||
case 'k': case KEY_UP: selected_row = (selected_row-1+GRID_ROWS)%GRID_ROWS; break;
|
||||
@@ -150,13 +194,33 @@ void tui_run(void) {
|
||||
send_command(cmd);
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
case 's':
|
||||
send_command("scene_next\n");
|
||||
break;
|
||||
}
|
||||
case 'd': case 'S':
|
||||
case 'S':
|
||||
send_command("scene_prev\n");
|
||||
break;
|
||||
case 'd': case 'D':
|
||||
send_command("stop\n");
|
||||
break;
|
||||
case 'a':
|
||||
send_command("add\n");
|
||||
break;
|
||||
case 'A':
|
||||
send_command("add_midi\n");
|
||||
break;
|
||||
case 'r':
|
||||
send_command("remove\n");
|
||||
break;
|
||||
case 'b': {
|
||||
char cmd[16];
|
||||
snprintf(cmd, sizeof(cmd), "bind %d\n", selected_col);
|
||||
send_command(cmd);
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
send_command("unbind\n");
|
||||
break;
|
||||
case '?': show_help = !show_help; break;
|
||||
case 27: case 'Q': return;
|
||||
default: break;
|
||||
@@ -167,5 +231,8 @@ void tui_run(void) {
|
||||
|
||||
void tui_cleanup(void) {
|
||||
if (yank_buffer.clip_indices) free(yank_buffer.clip_indices);
|
||||
/* delete FIFOs */
|
||||
unlink(STATUS_FIFO);
|
||||
unlink(CMD_FIFO);
|
||||
curs_set(1); endwin();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user