#include #include #include #include #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; // ---------------------------------------------------------------------------- // 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) { int ev = getch(); switch (ev) { case CTRL('c'): g_exit = true; break; case KEY_RESIZE: 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"); setlocale(LC_ALL, ""); initscr(); raw(); noecho(); curs_set(0); keypad(stdscr, TRUE); start_color(); init_color(COLOR_BLACK, 0, 0, 0); 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(); } } void quit(void) { tui_line_buff_free(); endwin(); log_info("Shutting down salis data client"); } int main(void) { init(); exec(); quit(); free(g_window_handles); g_window_count = 0; g_window_cap = 0; return 0; }