diff options
| author | Paul Oliver <contact@pauloliver.dev> | 2026-05-04 23:33:08 +0200 |
|---|---|---|
| committer | Paul Oliver <contact@pauloliver.dev> | 2026-05-24 23:46:55 +0200 |
| commit | 522e11c8086b7d8ab76b9be07c1861f35ed2327f (patch) | |
| tree | 48c61dd69cf817fce674c073d051de8a01a7f0ad /data/client.c | |
| parent | 6cecf64dbb488949a67eb080bf27d06f51533f40 (diff) | |
Adds prototype for the data client UI
Diffstat (limited to 'data/client.c')
| -rw-r--r-- | data/client.c | 449 |
1 files changed, 447 insertions, 2 deletions
diff --git a/data/client.c b/data/client.c index 31e18b8..d5e8e56 100644 --- a/data/client.c +++ b/data/client.c @@ -1,23 +1,410 @@ #include <curses.h> #include <locale.h> #include <stdlib.h> +#include <string.h> #include "logger.c" +#include "plots.c" #include "tui.c" +#define UI_AVAIL_PLOTS_COL PANE_AND_MARGIN_WIDTH +#define UI_WINDOWS_COL (PANE_AND_MARGIN_WIDTH * 2) +#define UI_PLOTS_COL (PANE_AND_MARGIN_WIDTH * 3) + +#define MAX_WINDOW_ROWS 4 +#define MAX_WINDOW_COLS 4 +#define MAX_WINDOW_PLOTS (MAX_WINDOW_ROWS * MAX_WINDOW_COLS) + #define CTRL(x) (x & 0x1f) enum { PAIR_HEADER = 1, + PAIR_SELECTED = 2, + PAIR_TO_BE_CREATED = 3, + PAIR_TO_BE_UPDATED = 4, + PAIR_TO_BE_REMOVED = 5, +}; + +enum UIColumn { + UICOL_AVAIL_PLOTS, + UICOL_WINDOWS, + UICOL_PLOTS, + UICOL_COUNT, +}; + +enum WindowState { + WINDOW_TO_BE_CREATED, + WINDOW_TO_BE_UPDATED, + WINDOW_IS_LIVE, + WINDOW_TO_BE_REMOVED, +}; + +enum WindowPlotState { + PLOT_TO_BE_CREATED, + PLOT_IS_LIVE, + PLOT_TO_BE_REMOVED, +}; + +struct WindowHandle { + size_t wid; + size_t rows; + size_t cols; + size_t rows_update; + size_t cols_update; + enum WindowState state; + size_t plot_count; + size_t plot_sel; + enum WindowPlotState plot_states[MAX_WINDOW_PLOTS]; + struct PlotDef *plot_defs[MAX_WINDOW_PLOTS]; }; // Globals bool g_exit; +enum UIColumn g_col_sel; +size_t g_apsel; +size_t g_wsel; +size_t g_apscroll; +size_t g_wscroll; +size_t g_pscroll; +size_t g_wid_count; +struct WindowHandle *g_window_handles; +size_t g_window_count; +size_t g_window_cap; -void ui_print(void) { +// ---------------------------------------------------------------------------- +// UI functions +// ---------------------------------------------------------------------------- +void ui_print_sim_description(void) { + // Simulation desciption int l = 1; tui_line(false, l++, PAIR_HEADER, A_BOLD, "SALIS DATA CLIENT"); + tui_str_field(l++, "name", NAME); + tui_ulx_field(l++, "seed", SEED); + tui_str_field(l++, "conn", IP ":" PORT_STR); + tui_str_field(l++, "anc", ANC); + tui_str_field(l++, "arch", ARCH); + tui_ulx_field(l++, "asav", AUTOSAVE_INTERVAL); + tui_ulx_field(l++, "cres", CORES); +#if defined(MUTA_FLIP) + tui_str_field(l++, "mflp", "true"); +#else + tui_str_field(l++, "mflp", "false"); +#endif + tui_ulx_field(l++, "mrng", MUTA_RANGE); + tui_ulx_field(l++, "size", MVEC_SIZE); +#if defined(MVEC_LOOP) + tui_str_field(l++, "loop", "true"); +#else + tui_str_field(l++, "loop", "false"); +#endif +#if defined(COMPRESS) + tui_str_field(l++, "xsav", "enabled"); +#else + tui_str_field(l++, "xsav", "disabled"); +#endif + tui_ulx_field(l++, "dpsi", DATA_PUSH_INTERVAL); + + // Window summary + l++; + + tui_line(false, l++, PAIR_HEADER, A_BOLD, "SUMMARY"); + tui_uld_field(l++, "wcnt", g_window_count); + tui_uld_field(l++, "wcap", g_window_cap); + tui_uld_field(l++, "widc", g_wid_count); +} + +void ui_print_avail_plots(void) { + int l = 1; + int pair = g_col_sel == UICOL_AVAIL_PLOTS ? PAIR_SELECTED : PAIR_HEADER; + + tui_field(l++, UI_AVAIL_PLOTS_COL, pair, A_BOLD, "AVAIL PLOTS [%ld:%ld]", g_apsel, g_apscroll); + + for (size_t i = g_apscroll; i < g_general_plots_count; i++) { + pair = i == g_apsel ? PAIR_SELECTED : PAIR_NORMAL; + tui_field(l++, UI_AVAIL_PLOTS_COL, pair, A_NORMAL, g_general_plots_def[i].name); + } +} + +void ui_print_windows(void) { + int l = 1; + int pair = g_col_sel == UICOL_WINDOWS ? PAIR_SELECTED : PAIR_HEADER; + + struct WindowHandle *whdl = &g_window_handles[g_wsel]; + tui_field(l++, UI_WINDOWS_COL, pair, A_BOLD, "WINDOWS [%ld:%ld]", whdl->wid, g_wscroll); + + for (size_t i = g_wscroll; i < g_window_count; i++) { + whdl = &g_window_handles[i]; + char mark = ' '; + + switch (whdl->state) { + case WINDOW_TO_BE_CREATED: + pair = PAIR_TO_BE_CREATED; + mark = '+'; + break; + case WINDOW_TO_BE_UPDATED: + pair = PAIR_TO_BE_UPDATED; + mark = 'u'; + break; + case WINDOW_IS_LIVE: + pair = PAIR_NORMAL; + break; + case WINDOW_TO_BE_REMOVED: + pair = PAIR_TO_BE_REMOVED; + mark = 'd'; + break; + } + + pair = i == g_wsel ? PAIR_SELECTED : pair; + tui_field(l++, UI_WINDOWS_COL, pair, A_NORMAL, "wid:%ld [%dx%d] %c", whdl->wid, whdl->rows_update, whdl->cols_update, mark); + } +} + +void ui_print_plots(void) { + int l = 1; + int pair = g_col_sel == UICOL_PLOTS ? PAIR_SELECTED : PAIR_HEADER; + + struct WindowHandle *whdl = &g_window_handles[g_wsel]; + tui_field(l++, UI_PLOTS_COL, pair, A_BOLD, "PLOTS [%ld:%ld]", whdl->plot_sel, g_pscroll); + + if (!g_window_count) return; + + for (size_t i = g_pscroll; i < whdl->plot_count; i++) { + struct PlotDef *plot_def = whdl->plot_defs[i]; + enum WindowPlotState plot_state = whdl->plot_states[i]; + char mark = ' '; + + switch (plot_state) { + case PLOT_TO_BE_CREATED: + pair = PAIR_TO_BE_CREATED; + mark = '+'; + break; + case PLOT_IS_LIVE: + pair = PAIR_NORMAL; + break; + case PLOT_TO_BE_REMOVED: + pair = PAIR_TO_BE_REMOVED; + mark = 'd'; + break; + } + + pair = i == whdl->plot_sel ? PAIR_SELECTED : pair; + tui_field(l++, UI_PLOTS_COL, pair, A_NORMAL, "%s %c", plot_def->name, mark); + plot_def++; + } +} + +void ui_print_footer(void) { + tui_field(LINES - 1, 1, PAIR_NORMAL, A_NORMAL, "[+] new window | [enter] add plot | [x] delete elem | [ctrl+c] quit"); +} + +void ui_print(void) { + ui_print_sim_description(); + ui_print_avail_plots(); + ui_print_windows(); + ui_print_plots(); + ui_print_footer(); +} + +// ---------------------------------------------------------------------------- +// Control function +// ---------------------------------------------------------------------------- +void ev_scroll(int ev) { + size_t *scroll_var = NULL; + + switch (g_col_sel) { + case UICOL_AVAIL_PLOTS: + scroll_var = &g_apscroll; + break; + case UICOL_WINDOWS: + scroll_var = &g_wscroll; + break; + case UICOL_PLOTS: + scroll_var = &g_pscroll; + break; + default:; + } + + assert(scroll_var); + + switch (ev) { + case 'w': + *scroll_var += 1; + break; + case 's': + *scroll_var -= (*scroll_var ? 1 : 0); + break; + case 'q': + *scroll_var = 0; + break; + } +} + +void ev_new_window(void) { + if (g_window_count == g_window_cap) { + // Reallocate dynamic array + size_t new_window_cap = g_window_cap * 2; + struct WindowHandle *new_window_handles = calloc(new_window_cap, sizeof(struct WindowHandle)); + memcpy(new_window_handles, g_window_handles, sizeof(struct WindowHandle) * g_window_count); + free(g_window_handles); + g_window_cap = new_window_cap; + g_window_handles = new_window_handles; + } + + g_window_count++; + g_window_handles[g_window_count - 1] = (struct WindowHandle){ + .wid = g_wid_count++, + .rows = 2, + .cols = 2, + .rows_update = 2, + .cols_update = 2, + .state = WINDOW_TO_BE_CREATED, + }; +} + +void ev_delete_window_handle(size_t widx) { + assert(g_window_count); + assert(widx < g_window_count); + assert(g_window_handles[widx].state == WINDOW_TO_BE_CREATED); + + for (size_t i = widx; i < g_window_count - 1; i++) { + memcpy(&g_window_handles[i], &g_window_handles[i + 1], sizeof(struct WindowHandle)); + } + + g_window_handles[g_window_count - 1] = (struct WindowHandle){ 0 }; + g_window_count--; + g_wsel -= (g_window_count && g_wsel == g_window_count) ? 1 : 0; +} + +void ev_add_plot(void) { + assert(g_col_sel == UICOL_AVAIL_PLOTS); + assert(g_window_count); + + struct WindowHandle *whdl = &g_window_handles[g_wsel]; + size_t max_plots = whdl->rows_update * whdl->cols_update; + size_t pidx = 0; + + do { + if (!whdl->plot_defs[pidx]) break; + pidx++; + } while (pidx < max_plots); + + if (pidx == max_plots) return; + + whdl->plot_count++; + whdl->plot_states[pidx] = PLOT_TO_BE_CREATED; + whdl->plot_defs[pidx] = &g_general_plots_def[g_apsel]; +} + +void ev_delete_plot(size_t widx) { + assert(g_window_count); + assert(widx < g_window_count); + + struct WindowHandle *whdl = &g_window_handles[g_wsel]; + assert(whdl->plot_count); + assert(whdl->plot_sel < whdl->plot_count); + assert(whdl->plot_states[whdl->plot_sel] == PLOT_TO_BE_CREATED); + + for (size_t i = whdl->plot_sel; i < whdl->plot_count - 1; i++) { + whdl->plot_states[i] = whdl->plot_states[i + 1]; + whdl->plot_defs[i] = whdl->plot_defs[i + 1]; + } + + whdl->plot_states[whdl->plot_count - 1] = 0; + whdl->plot_defs[whdl->plot_count - 1] = NULL; + whdl->plot_count--; + + if (!whdl->plot_count) { + whdl->plot_sel = 0; + } else if (whdl->plot_sel >= whdl->plot_count) { + whdl->plot_sel = whdl->plot_count - 1; + } +} + +void ev_delete_elem(void) { + if (g_col_sel == UICOL_WINDOWS && g_window_count) { + struct WindowHandle *whdl = &g_window_handles[g_wsel]; + + switch (whdl->state) { + case WINDOW_TO_BE_CREATED: + ev_delete_window_handle(g_wsel); + break; + case WINDOW_TO_BE_UPDATED: + break; + case WINDOW_IS_LIVE: + break; + case WINDOW_TO_BE_REMOVED: + break; + } + } + + if (g_col_sel == UICOL_PLOTS && g_window_count && g_window_handles[g_wsel].plot_count) { + struct WindowHandle *whdl = &g_window_handles[g_wsel]; + + switch (whdl->plot_states[whdl->plot_sel]) { + case PLOT_TO_BE_CREATED: + ev_delete_plot(g_wsel); + break; + case PLOT_IS_LIVE: + break; + case PLOT_TO_BE_REMOVED: + break; + } + } +} + +void ev_resize_window(int ev) { + assert(g_col_sel == UICOL_WINDOWS); + assert(g_window_count); + + struct WindowHandle *whdl = &g_window_handles[g_wsel]; + + switch (ev) { + case 'A': + if (whdl->cols_update > 1 && (whdl->rows_update * (whdl->cols_update - 1)) >= whdl->plot_count) whdl->cols_update--; + break; + case 'D': + whdl->cols_update += whdl->cols_update < MAX_WINDOW_COLS ? 1 : 0; + break; + case 'W': + if (whdl->rows_update > 1 && (whdl->cols_update * (whdl->rows_update - 1)) >= whdl->plot_count) whdl->rows_update--; + break; + case 'S': + whdl->rows_update += whdl->rows_update < MAX_WINDOW_ROWS ? 1 : 0; + break; + } +} + +void ev_swap_plots(int ev) { + assert(g_col_sel == UICOL_PLOTS); + assert(g_window_count); + + struct WindowHandle *whdl = &g_window_handles[g_wsel]; + size_t pidx1; + size_t pidx2; + + switch (ev) { + case 'W': + if (!whdl->plot_count || !whdl->plot_sel) return; + pidx1 = whdl->plot_sel; + pidx2 = whdl->plot_sel - 1; + whdl->plot_sel--; + break; + case 'S': + if (!whdl->plot_count || whdl->plot_sel >= whdl->plot_count - 1) return; + pidx1 = whdl->plot_sel; + pidx2 = whdl->plot_sel + 1; + whdl->plot_sel++; + break; + } + + enum WindowPlotState tmp_state = whdl->plot_states[pidx1]; + whdl->plot_states[pidx1] = whdl->plot_states[pidx2]; + whdl->plot_states[pidx2] = tmp_state; + + struct PlotDef *tmp_def = whdl->plot_defs[pidx1]; + whdl->plot_defs[pidx1] = whdl->plot_defs[pidx2]; + whdl->plot_defs[pidx2] = tmp_def; } void ev_handle(void) { @@ -28,13 +415,58 @@ void ev_handle(void) { g_exit = true; break; case KEY_RESIZE: - clear(); tui_line_buff_resize(); + break; + case 'w': + case 's': + case 'q': + ev_scroll(ev); + break; + case 'Q': + g_apscroll = 0; + g_wscroll = 0; + g_pscroll = 0; + break; + case KEY_LEFT: + g_col_sel -= g_col_sel ? 1 : 0; + break; + case KEY_RIGHT: + g_col_sel += g_col_sel < UICOL_COUNT - 1 ? 1 : 0; + break; + case KEY_UP: + if (g_col_sel == UICOL_AVAIL_PLOTS && g_apsel) g_apsel--; + if (g_col_sel == UICOL_WINDOWS && g_wsel) g_wsel--; + if (g_col_sel == UICOL_PLOTS && g_window_count && g_window_handles[g_wsel].plot_sel) g_window_handles[g_wsel].plot_sel--; + break; + case KEY_DOWN: + if (g_col_sel == UICOL_AVAIL_PLOTS && g_apsel < g_general_plots_count - 1) g_apsel++; + if (g_col_sel == UICOL_WINDOWS && g_window_count && g_wsel < g_window_count - 1) g_wsel++; + if (g_col_sel == UICOL_PLOTS && g_window_count && g_window_handles[g_wsel].plot_count && g_window_handles[g_wsel].plot_sel < g_window_handles[g_wsel].plot_count - 1) g_window_handles[g_wsel].plot_sel++; + break; + case '+': + ev_new_window(); + break; + case '\n': + if (g_col_sel == UICOL_AVAIL_PLOTS && g_window_count) ev_add_plot(); + break; + case 'x': + ev_delete_elem(); + break; + case 'A': + case 'D': + case 'W': + case 'S': + if (g_col_sel == UICOL_WINDOWS && g_window_count) ev_resize_window(ev); + if (g_col_sel == UICOL_PLOTS && g_window_count) ev_swap_plots(ev); + break; default: break; } } +// ---------------------------------------------------------------------------- +// Main functions +// ---------------------------------------------------------------------------- void init(void) { log_info("Initializing salis data client"); @@ -51,14 +483,23 @@ void init(void) { init_pair(PAIR_NORMAL, COLOR_WHITE, COLOR_BLACK); init_pair(PAIR_HEADER, COLOR_BLUE, COLOR_BLACK); + init_pair(PAIR_SELECTED, COLOR_YELLOW, COLOR_BLACK); + init_pair(PAIR_TO_BE_CREATED, COLOR_GREEN, COLOR_BLACK); + init_pair(PAIR_TO_BE_UPDATED, COLOR_GREEN, COLOR_BLACK); + init_pair(PAIR_TO_BE_REMOVED, COLOR_RED, COLOR_BLACK); tui_line_buff_resize(); + + g_window_count = 0; + g_window_cap = 1; + g_window_handles = calloc(g_window_cap, sizeof(struct WindowHandle)); } void exec(void) { while (!g_exit) { ui_print(); ev_handle(); + clear(); } } @@ -73,5 +514,9 @@ int main(void) { exec(); quit(); + free(g_window_handles); + g_window_count = 0; + g_window_cap = 0; + return 0; } |
