#include "tui.h" #include #include #include #include #include #include #include #include #include #include /* ---------- FIFO command helper ---------- */ int send_command(const char *cmd) { const char *fifo_path = getenv("LOOPER_CMD_FIFO"); if (!fifo_path) fifo_path = "/tmp/looper_cmd"; int fd = open(fifo_path, O_WRONLY | O_NONBLOCK); if (fd < 0) return -1; size_t len = strlen(cmd); int n = write(fd, cmd, len); if (n == (int)len && cmd[len-1] != '\n') write(fd, "\n", 1); close(fd); return (n >= 0) ? 0 : -1; } /* ---------- Stub functions (no engine) ---------- */ // Clip states – dummy values used as placeholders typedef enum { CLIP_EMPTY, CLIP_RECORDING, CLIP_LOOPING, CLIP_STOPPED } ClipState; static const char *clip_state_string(ClipState s) { (void)s; return "?"; } /* Grid dimensions */ #define GRID_ROWS 8 #define GRID_COLS 8 #define NUM_GRIDS 8 #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, COLOR_SELECTED, COLOR_HELP }; static int selected_row = 0, selected_col = 0; static int selected_grid = 0; static bool show_help = false; /* Visual mode, marks, yank buffer – keep but only local state */ static int marks[26]; typedef struct { int *clip_indices; int count; } YankBuffer; static YankBuffer yank_buffer = {NULL, 0}; typedef enum { MODE_NORMAL, MODE_VISUAL, MODE_MOVE } UIMode; static UIMode current_mode = MODE_NORMAL; static int visual_start_row, visual_start_col, visual_end_row, visual_end_col; /* Fuzzy search – keep struct but stub carla calls */ typedef struct { char query[256]; int query_len, selected_index, num_results; int result_indices[256]; bool active; char prompt[64]; void (*callback)(const char *); const char **items; int num_items; bool free_items; } FuzzySearch; static FuzzySearch fuzzy_search = {0}; /* ---------- Parse status line from engine status FIFO ---------- */ 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) { if (sta >= 0 && sta <= 3) { *state = (ChannelState)sta; return true; } } /* try text-based format */ char state_str[32]; if (sscanf(line, "CH=%d SC=%d STATE=%31s", ch, scene, state_str) != 3) return false; if (strcmp(state_str, "IDLE") == 0) { *state = STATE_IDLE; return true; } if (strcmp(state_str, "RECORD") == 0) { *state = STATE_RECORD; return true; } if (strcmp(state_str, "LOOPING") == 0) { *state = STATE_LOOPING; return true; } if (strcmp(state_str, "PAUSED") == 0) { *state = STATE_PAUSED; return true; } return false; } /* ---------- 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 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= 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; case 'l': case KEY_RIGHT: selected_col = (selected_col+1)%GRID_COLS; break; case 't': { char cmd[32]; snprintf(cmd, sizeof(cmd), "record %d\n", selected_col); send_command(cmd); break; } case 's': send_command("scene_next\n"); break; 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; } draw_grid(); } } 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(); }