--- embedaddon/bmon/src/out_curses.c 2012/02/21 22:19:56 1.1 +++ embedaddon/bmon/src/out_curses.c 2019/10/21 14:58:35 1.1.1.3 @@ -1,7 +1,8 @@ /* * out_curses.c Curses Output * - * Copyright (c) 2001-2005 Thomas Graf + * Copyright (c) 2001-2013 Thomas Graf + * Copyright (c) 2013 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -23,65 +24,178 @@ */ #include -#include #include -#include +#include +#include +#include #include +#include +#include #include -#include -#include #include +enum { + GRAPH_DISPLAY_SIDE_BY_SIDE = 1, + GRAPH_DISPLAY_STANDARD = 2, +}; + +enum { + KEY_TOGGLE_LIST = 'l', + KEY_TOGGLE_GRAPH = 'g', + KEY_TOGGLE_DETAILS = 'd', + KEY_TOGGLE_INFO = 'i', + KEY_COLLECT_HISTORY = 'h', +}; + +#define DETAILS_COLS 40 + +#define LIST_COL_1 31 +#define LIST_COL_2 55 + +/* Set to element_current() before drawing */ +static struct element *current_element; + +static struct attr *current_attr; + +/* Length of list to draw, updated in draw_content() */ +static int list_length; + +static int list_req; + +/* Number of graphs to draw (may be < c_ngraph) */ +static int ngraph; + +/* + * Offset in number of lines within the the element list of the currently + * selected element. Updated while summing up required lines. + */ +static unsigned int selection_offset; + +/* + * Offset in number of lines of the first element to be drawn. Updated + * in draw_content() + */ +static int offset; + +/* + * Offset to the first graph to draw in number of attributes with graphs. + */ +static unsigned int graph_offset; + +static int graph_display = GRAPH_DISPLAY_STANDARD; + +/* + * Number of detail columns + */ +static int detail_cols; +static int info_cols; + static int initialized; -static int print_help = 0; -static int quit_mode = 0; -static int help_page = 0; +static int print_help; +static int quit_mode; +static int help_page; + +/* Current row */ static int row; + +/* Number of rows */ static int rows; + +/* Number of columns */ static int cols; -static int c_graph_height = 6; +static int c_show_graph = 1; +static int c_ngraph = 1; static int c_use_colors = 1; -static int c_combined_node_list = 0; -static int c_graphical_in_list = 0; -static int c_detailed_in_list = 0; -static int c_list_in_list = 1; -static int c_nototal = 0; -static int c_nosource = 0; +static int c_show_details = 0; +static int c_show_list = 1; +static int c_show_info = 0; +static int c_list_min = 6; -#define NEXT_ROW { \ - row++; \ - if (row >= rows-1) \ - return; \ - move(row,0); \ +static struct graph_cfg c_graph_cfg = { + .gc_width = 60, + .gc_height = 6, + .gc_foreground = '|', + .gc_background = '.', + .gc_noise = ':', + .gc_unknown = '?', +}; + +#define NEXT_ROW() \ + do { \ + row++; \ + if (row >= rows - 1) \ + return; \ + move(row, 0); \ + } while(0) + +static void apply_layout(int layout) +{ + if (c_use_colors) + attrset(COLOR_PAIR(layout) | cfg_layout[layout].l_attr); + else + attrset(cfg_layout[layout].l_attr); } -static void putl(const char *fmt, ...) +static char *float2str(double value, int width, int prec, char *buf, size_t len) { - va_list args; - char buf[2048]; - int x, y; + snprintf(buf, len, "%'*.*f", width, value == 0.0f ? 0 : prec, value); - memset(buf, 0, sizeof(buf)); - getyx(stdscr, y, x); + return buf; +} - va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); +static void put_line(const char *fmt, ...) +{ + va_list args; + char buf[2048]; + int x, y __unused__; - if (strlen(buf) > cols-x) - buf[cols-x] = '\0'; - else - memset(&buf[strlen(buf)], ' ', cols-strlen(buf)-x); + memset(buf, 0, sizeof(buf)); + getyx(stdscr, y, x); - addstr(buf); + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + if (strlen(buf) > cols-x) + buf[cols - x] = '\0'; + else + memset(&buf[strlen(buf)], ' ', cols - strlen(buf)-x); + + addstr(buf); } -static void curses_init(void) +static void center_text(const char *fmt, ...) { - if (!initscr()) - exit(1); + va_list args; + char *str; + unsigned int col; + va_start(args, fmt); + if (vasprintf(&str, fmt, args) < 0) { + fprintf(stderr, "vasprintf: Out of memory\n"); + exit(ENOMEM); + } + va_end(args); + + col = (cols / 2) - (strlen(str) / 2); + if (col > cols - 1) + col = cols - 1; + + move(row, col); + addstr(str); + move(row, 0); + + free(str); +} + +static int curses_init(void) +{ + if (!initscr()) { + fprintf(stderr, "Unable to initialize curses screen\n"); + return -EOPNOTSUPP; + } + initialized = 1; if (!has_colors()) @@ -95,8 +209,8 @@ static void curses_init(void) #if defined HAVE_USE_DEFAULT_COLORS use_default_colors(); #endif - for (i = 1; i < NR_LAYOUTS+1; i++) - init_pair(i, layout[i].fg, layout[i].bg); + for (i = 1; i < LAYOUT_MAX+1; i++) + init_pair(i, cfg_layout[i].l_fg, cfg_layout[i].l_bg); } keypad(stdscr, TRUE); @@ -106,6 +220,8 @@ static void curses_init(void) nodelay(stdscr, TRUE); /* getch etc. must be non-blocking */ clear(); curs_set(0); + + return 0; } static void curses_shutdown(void) @@ -114,720 +230,869 @@ static void curses_shutdown(void) endwin(); } -static void draw_item_list_entry(item_t *intf, node_t *node) +struct detail_arg { - int rxprec, txprec, rxpprec, txpprec; - stat_attr_t *bytes, *packets; - double rx, tx, rxp, txp; - char *rx_u, *tx_u, *rxp_u, *txp_u; - char pad[23]; + int nattr; +}; - bytes = lookup_attr(intf, intf->i_major_attr); - packets = lookup_attr(intf, intf->i_minor_attr); +static void draw_attr_detail(struct element *e, struct attr *a, void *arg) +{ + char *rx_u, *tx_u, buf1[32], buf2[32]; + int rxprec, txprec, ncol; + struct detail_arg *da = arg; - if (bytes == NULL || packets == NULL) - return; + double rx = unit_value2str(rate_get_total(&a->a_rx_rate), + a->a_def->ad_unit, + &rx_u, &rxprec); + double tx = unit_value2str(rate_get_total(&a->a_tx_rate), + a->a_def->ad_unit, + &tx_u, &txprec); - rx = cancel_down(attr_get_rx_rate(bytes), bytes->a_unit, &rx_u, &rxprec); - tx = cancel_down(attr_get_tx_rate(bytes), bytes->a_unit, &tx_u, &txprec); + if (da->nattr >= detail_cols) { + NEXT_ROW(); + da->nattr = 0; + } - rxp = cancel_down(attr_get_rx_rate(packets), packets->a_unit, &rxp_u, &rxpprec); - txp = cancel_down(attr_get_tx_rate(packets), packets->a_unit, &txp_u, &txpprec); + ncol = (da->nattr * DETAILS_COLS) - 1; + move(row, ncol); + if (ncol > 0) + addch(ACS_VLINE); - memset(pad, 0, sizeof(pad)); - memset(pad, ' ', intf->i_level < 15 ? intf->i_level : 15); + put_line(" %-14.14s %8s%-3s %8s%-3s", + a->a_def->ad_description, + (a->a_flags & ATTR_RX_ENABLED) ? + float2str(rx, 8, rxprec, buf1, sizeof(buf1)) : "-", rx_u, + (a->a_flags & ATTR_TX_ENABLED) ? + float2str(tx, 8, txprec, buf2, sizeof(buf2)) : "-", tx_u); - strncat(pad, intf->i_name, sizeof(pad) - strlen(pad) - 1); + da->nattr++; +} - if (intf->i_desc) { - strncat(pad, " (", sizeof(pad) - strlen(pad) - 1); - strncat(pad, intf->i_desc, sizeof(pad) - strlen(pad) - 1); - strncat(pad, ")", sizeof(pad) - strlen(pad) - 1); +static void draw_details(void) +{ + int i; + struct detail_arg arg = { + .nattr = 0, + }; + + if (!current_element->e_nattrs) + return; + + for (i = 1; i < detail_cols; i++) + mvaddch(row, (i * DETAILS_COLS) - 1, ACS_TTEE); + + NEXT_ROW(); + put_line(""); + for (i = 0; i < detail_cols; i++) { + if (i > 0) + mvaddch(row, (i * DETAILS_COLS) - 1, ACS_VLINE); + move(row, (i * DETAILS_COLS) + 22); + put_line("RX TX"); } - NEXT_ROW; - if (intf->i_index == node->n_selected && node == get_current_node()) { - if (c_use_colors) - attrset(COLOR_PAIR(LAYOUT_SELECTED) | layout[LAYOUT_SELECTED].attr); - else - attron(A_REVERSE); + NEXT_ROW(); + element_foreach_attr(current_element, draw_attr_detail, &arg); + + /* + * If the last row was incomplete, not all vlines have been drawn. + * draw them here + */ + for (i = 1; i < detail_cols; i++) + mvaddch(row, (i * DETAILS_COLS - 1), ACS_VLINE); +} + +static void print_message(const char *text) +{ + int i, y = (rows/2) - 2; + int len = strlen(text); + int x = (cols/2) - (len / 2); + + attrset(A_STANDOUT); + mvaddch(y - 2, x - 1, ACS_ULCORNER); + mvaddch(y + 2, x - 1, ACS_LLCORNER); + mvaddch(y - 2, x + len, ACS_URCORNER); + mvaddch(y + 2, x + len, ACS_LRCORNER); + + for (i = 0; i < 3; i++) { + mvaddch(y - 1 + i, x + len, ACS_VLINE); + mvaddch(y - 1 + i, x - 1 ,ACS_VLINE); } - - printw(" %-3d%s", intf->i_index, (intf->i_flags & ITEM_FLAG_FOLDED) ? "+" : " "); - if (intf->i_index == node->n_selected && node == get_current_node()) { - if (c_use_colors) - attrset(COLOR_PAIR(LAYOUT_LIST) | layout[LAYOUT_LIST].attr); - else - attroff(A_REVERSE); + for (i = 0; i < len; i++) { + mvaddch(y - 2, x + i, ACS_HLINE); + mvaddch(y - 1, x + i, ' '); + mvaddch(y + 1, x + i, ' '); + mvaddch(y + 2, x + i, ACS_HLINE); } - putl("%-22s %8.*f%s %8.*f%s %2d%% %8.*f%s %8.*f%s %2d%%", pad, - rxprec, rx, rx_u, rxpprec, rxp, rxp_u, intf->i_rx_usage, - txprec, tx, tx_u, txpprec, txp, txp_u, intf->i_tx_usage); + mvaddstr(y, x, text); + attroff(A_STANDOUT); - if (intf->i_rx_usage == -1) { - move(row, 51); - addstr(" "); + row = y + 2; +} + +static void draw_help(void) +{ +#define HW 46 +#define HH 19 + int i, y = (rows/2) - (HH/2); + int x = (cols/2) - (HW/2); + char pad[HW+1]; + + memset(pad, ' ', sizeof(pad)); + pad[sizeof(pad) - 1] = '\0'; + + attron(A_STANDOUT); + + for (i = 0; i < HH; i++) + mvaddnstr(y + i, x, pad, -1); + + mvaddch(y - 1, x - 1, ACS_ULCORNER); + mvaddch(y + HH, x - 1, ACS_LLCORNER); + + mvaddch(y - 1, x + HW, ACS_URCORNER); + mvaddch(y + HH, x + HW, ACS_LRCORNER); + + for (i = 0; i < HH; i++) { + mvaddch(y + i, x - 1, ACS_VLINE); + mvaddch(y + i, x + HW, ACS_VLINE); } - if (intf->i_tx_usage == -1) { - move(row, 77); - addstr(" "); + for (i = 0; i < HW; i++) { + mvaddch(y - 1, x + i, ACS_HLINE); + mvaddch(y + HH, x + i, ACS_HLINE); } - move(row, 28); - addch(ACS_VLINE); - move(row, 54); - addch(ACS_VLINE); + attron(A_BOLD); + mvaddnstr(y- 1, x+15, "QUICK REFERENCE", -1); + attron(A_UNDERLINE); + mvaddnstr(y+ 0, x+1, "Navigation", -1); + attroff(A_BOLD | A_UNDERLINE); + + mvaddnstr(y+ 1, x+3, "Up, Down Previous/Next element", -1); + mvaddnstr(y+ 2, x+3, "PgUp, PgDown Scroll up/down entire page", -1); + mvaddnstr(y+ 3, x+3, "Left, Right Previous/Next attribute", -1); + mvaddnstr(y+ 4, x+3, "[, ] Previous/Next group", -1); + mvaddnstr(y+ 5, x+3, "? Toggle quick reference", -1); + mvaddnstr(y+ 6, x+3, "q Quit bmon", -1); + + attron(A_BOLD | A_UNDERLINE); + mvaddnstr(y+ 8, x+1, "Display Settings", -1); + attroff(A_BOLD | A_UNDERLINE); + + mvaddnstr(y+ 9, x+3, "d Toggle detailed statistics", -1); + mvaddnstr(y+10, x+3, "l Toggle element list", -1); + mvaddnstr(y+11, x+3, "i Toggle additional info", -1); + + attron(A_BOLD | A_UNDERLINE); + mvaddnstr(y+13, x+1, "Graph Settings", -1); + attroff(A_BOLD | A_UNDERLINE); + + mvaddnstr(y+14, x+3, "g Toggle graphical statistics", -1); + mvaddnstr(y+15, x+3, "H Start recording history data", -1); + mvaddnstr(y+16, x+3, "TAB Switch time unit of graph", -1); + mvaddnstr(y+17, x+3, "<, > Change number of graphs", -1); + mvaddnstr(y+18, x+3, "r Reset counter of element", -1); + + attroff(A_STANDOUT); + + row = y + HH; } -static void handle_child(item_t *intf, void *arg) +static int lines_required_for_header(void) { - node_t *node = arg; + return 1; +} - draw_item_list_entry(intf, node); - foreach_child(node, intf, handle_child, node); +static void draw_header(void) +{ + apply_layout(LAYOUT_STATUSBAR); + + if (current_element) + put_line(" %s %c%s%c", + current_element->e_name, + current_element->e_description ? '(' : ' ', + current_element->e_description ? : "", + current_element->e_description ? ')' : ' '); + else + put_line(""); + + move(row, COLS - strlen(PACKAGE_STRING) - 1); + put_line("%s", PACKAGE_STRING); + move(row, 0); + apply_layout(LAYOUT_LIST); } +static int lines_required_for_statusbar(void) +{ + return 1; +} -static void draw_item(node_t *node, item_t *intf) +static void draw_statusbar(void) { - if (!intf->i_name[0] || row >= (rows - 1) || (intf->i_flags & ITEM_FLAG_IS_CHILD)) - return; + static const char *help_text = "Press ? for help"; + char s[27]; + time_t t = time(NULL); - draw_item_list_entry(intf, node); + apply_layout(LAYOUT_STATUSBAR); - if (!(intf->i_flags & ITEM_FLAG_FOLDED)) - foreach_child(node, intf, handle_child, node); + asctime_r(localtime(&t), s); + s[strlen(s) - 1] = '\0'; + + row = rows-1; + move(row, 0); + put_line(" %s", s); + + move(row, COLS - strlen(help_text) - 1); + put_line("%s", help_text); + + move(row, 0); } +static void count_attr_graph(struct element *g, struct attr *a, void *arg) +{ + if (a == current_attr) + graph_offset = ngraph; -static void draw_node(node_t *node, void *arg) + ngraph++; +} + +static int lines_required_for_graph(void) { - int i, *cnt = (int *) arg; + int lines = 0; - if (cnt) { - if (*cnt) { - int n; - NEXT_ROW; - for (n = 1; n < cols; n += 2) - mvaddch(row, n, ACS_HLINE); - move(row, 28); - addch(ACS_VLINE); - move(row, 54); - addch(ACS_VLINE); - } - (*cnt)++; - } + ngraph = 0; - attrset(A_BOLD); - NEXT_ROW; - if (c_nosource) - putl("%s", node->n_name); - else - putl("%s (%s)", node->n_name, - node->n_from ? node->n_from : "local"); + if (c_show_graph && current_element) { + graph_display = GRAPH_DISPLAY_STANDARD; - move(row, 30); - putl(" Rate # %% Rate # %%"); - attroff(A_BOLD); + element_foreach_attr(current_element, count_attr_graph, NULL); - move(row, 28); - addch(ACS_VLINE); - move(row, 54); - addch(ACS_VLINE); + if (ngraph > c_ngraph) + ngraph = c_ngraph; - if (c_use_colors) - attrset(COLOR_PAIR(LAYOUT_LIST) | layout[LAYOUT_LIST].attr); + /* check if we have room to draw graphs on the same level */ + if (cols > (2 * (c_graph_cfg.gc_width + 10))) + graph_display = GRAPH_DISPLAY_SIDE_BY_SIDE; - for (i = 0; i < node->n_nitems; i++) - draw_item(node, &node->n_items[i]); + /* +2 = header + time axis */ + lines = ngraph * (graph_display * (c_graph_cfg.gc_height + 2)); + } - if (!c_nototal) { - int rx_maj_prec, tx_maj_prec, rx_min_prec, tx_min_prec; - double rx_maj, tx_maj, rx_min, tx_min; - char *rx_maj_u, *tx_maj_u, *rx_min_u, *tx_min_u; - - rx_maj = cancel_down(node->n_rx_maj_total, U_BYTES, &rx_maj_u, &rx_maj_prec); - tx_maj = cancel_down(node->n_tx_maj_total, U_BYTES, &tx_maj_u, &tx_maj_prec); + return lines + 1; +} - rx_min = cancel_down(node->n_rx_min_total, U_NUMBER, &rx_min_u, &rx_min_prec); - tx_min = cancel_down(node->n_tx_min_total, U_NUMBER, &tx_min_u, &tx_min_prec); +static int lines_required_for_details(void) +{ + int lines = 1; - NEXT_ROW; - putl(" %-26s %8.*f%s %8.*f%s %8.*f%s %8.*f%s ", "Total", - rx_maj_prec, rx_maj, rx_maj_u, rx_min_prec, rx_min, rx_min_u, - tx_maj_prec, tx_maj, tx_maj_u, tx_min_prec, tx_min, tx_min_u); + if (c_show_details && current_element) { + lines++; /* header */ - move(row, 28); - addch(ACS_VLINE); - move(row, 54); - addch(ACS_VLINE); + detail_cols = cols / DETAILS_COLS; + + if (!detail_cols) + detail_cols = 1; + + lines += (current_element->e_nattrs / detail_cols); + if (current_element->e_nattrs % detail_cols) + lines++; } - if (c_use_colors) - attrset(COLOR_PAIR(LAYOUT_DEFAULT) | layout[LAYOUT_DEFAULT].attr); + return lines; } -static int lines_required_for_graphical(void) +static void count_element_lines(struct element_group *g, struct element *e, + void *arg) { - item_t *it; + int *lines = arg; - if ((it = get_current_item())) - return get_ngraphs() * ((2 * c_graph_height) + 5); - else - return INT_MAX; + if (e == current_element) + selection_offset = *lines; + + (*lines)++; } -static int lines_required_for_detailed(void) +static void count_group_lines(struct element_group *g, void *arg) { - if (get_current_item()) - return 2 + ((get_current_item()->i_nattrs + 1) / 2); - else - return INT_MAX; + int *lines = arg; + + /* group title */ + (*lines)++; + + group_foreach_element(g, &count_element_lines, arg); } -static void __draw_graphic(stat_attr_hist_t *a, int selected, int xunit) +static int lines_required_for_list(void) { - int w; - graph_t *g; - g = create_configued_graph(&a->a_hist, c_graph_height, a->a_unit, xunit); + int lines = 0; - NEXT_ROW; - putl("RX %s [%s]", - g->g_rx.t_y_unit, type2desc(a->a_type)); + if (c_show_list) + group_foreach(&count_group_lines, &lines); + else + lines = 1; - if (selected) { - move(row, 72); - attron(A_BOLD); - addstr("(sel)"); - attroff(A_BOLD); - move(row, 0); - } + return lines; +} - for (w = (c_graph_height - 1); w >= 0; w--) { - NEXT_ROW; - putl(" %8.2f %s\n", g->g_rx.t_y_scale[w], (char *) g->g_rx.t_data + (w * (HISTORY_SIZE + 1))); - } +static inline int line_visible(int line) +{ + return line >= offset && line < (offset + list_length); +} - move(row, 71); - putl("[%.2f%%]", rtiming.rt_variance.v_error); - NEXT_ROW; - putl(" 1 5 10 15 20 25 30 35 40 45 50 55 60 %s", g->g_rx.t_x_unit); +static void draw_attr(double rate1, int prec1, char *unit1, + double rate2, int prec2, char *unit2, + float usage, int ncol) +{ + char buf[32]; - NEXT_ROW; - putl("TX %s", g->g_tx.t_y_unit); + move(row, ncol); + addch(ACS_VLINE); + printw("%7s%-3s", + float2str(rate1, 7, prec1, buf, sizeof(buf)), unit1); - for (w = (c_graph_height - 1); w >= 0; w--) { - NEXT_ROW; - putl(" %8.2f %s\n", g->g_tx.t_y_scale[w], (char *) g->g_tx.t_data + (w * (HISTORY_SIZE + 1))); - } + printw("%7s%-3s", + float2str(rate2, 7, prec2, buf, sizeof(buf)), unit2); - move(row, 71); - putl("[%.2f%%]", rtiming.rt_variance.v_error); - NEXT_ROW; - putl(" 1 5 10 15 20 25 30 35 40 45 50 55 60 %s", g->g_tx.t_x_unit); - - free_graph(g); + if (usage != FLT_MAX) + printw("%2.0f%%", usage); + else + printw("%3s", ""); } -static void draw_graphic(void) +static void draw_element(struct element_group *g, struct element *e, + void *arg) { - int i; + int *line = arg; - for (i = 0; i < get_ngraphs(); i++) { - stat_attr_hist_t *a = (stat_attr_hist_t *) current_attr(i); + apply_layout(LAYOUT_LIST); - if (!(a->a_flags & ATTR_FLAG_HISTORY)) - continue; + if (line_visible(*line)) { + char *rxu1 = "", *txu1 = "", *rxu2 = "", *txu2 = ""; + double rx1 = 0.0f, tx1 = 0.0f, rx2 = 0.0f, tx2 = 0.0f; + char pad[IFNAMSIZ + 32]; + int rx1prec = 0, tx1prec = 0, rx2prec = 0, tx2prec = 0; + struct attr *a; - if (a) - __draw_graphic(a, get_current_item()->i_graph_sel == i, - get_current_item()->i_unit[i]); + NEXT_ROW(); - if (i < (get_ngraphs() - 1)) { - int n; - NEXT_ROW; - for (n = 1; n < cols-1; n += 2) - mvaddch(row, n, ACS_HLINE); - } - } - -} + if (e->e_key_attr[GT_MAJOR] && + (a = attr_lookup(e, e->e_key_attr[GT_MAJOR]->ad_id))) + attr_rate2float(a, &rx1, &rxu1, &rx1prec, + &tx1, &txu1, &tx1prec); -static void draw_attr_detail(stat_attr_t *attr, void *arg) -{ - int rxprec, txprec; - double rx, tx; - char *rxu, *txu; - int *t = (int *) arg; + if (e->e_key_attr[GT_MINOR] && + (a = attr_lookup(e, e->e_key_attr[GT_MINOR]->ad_id))) + attr_rate2float(a, &rx2, &rxu2, &rx2prec, + &tx2, &txu2, &tx2prec); - rx = cancel_down(attr_get_rx(attr), attr->a_unit, &rxu, &rxprec); - tx = cancel_down(attr_get_tx(attr), attr->a_unit, &txu, &txprec); + memset(pad, 0, sizeof(pad)); + memset(pad, ' ', e->e_level < 6 ? e->e_level * 2 : 12); - if (0 == *t) { - NEXT_ROW; - putl(" %-12s%9.*f%-3s %9.*f%-3s", - type2desc(attr->a_type), - rxprec, rx, rxu, txprec, tx, txu); + strncat(pad, e->e_name, sizeof(pad) - strlen(pad) - 1); - if (!(attr->a_flags & ATTR_FLAG_RX_ENABLED)) { - move(row, 20); - addstr("N/A"); + if (e->e_description) { + strncat(pad, " (", sizeof(pad) - strlen(pad) - 1); + strncat(pad, e->e_description, sizeof(pad) - strlen(pad) - 1); + strncat(pad, ")", sizeof(pad) - strlen(pad) - 1); } - if (!(attr->a_flags & ATTR_FLAG_TX_ENABLED)) { - move(row, 33); - addstr("N/A"); - } + if (*line == offset) { + attron(A_BOLD); + addch(ACS_UARROW); + attroff(A_BOLD); + addch(' '); + } else if (e == current_element) { + apply_layout(LAYOUT_SELECTED); + addch(' '); + attron(A_BOLD); + addch(ACS_RARROW); + attroff(A_BOLD); + apply_layout(LAYOUT_LIST); + } else if (*line == offset + list_length - 1 && + *line < (list_req - 1)) { + attron(A_BOLD); + addch(ACS_DARROW); + attroff(A_BOLD); + addch(' '); + } else + printw(" "); - move(row, 40); - *t = 1; - } else { - putl(" %-12s%9.*f%-3s %9.*f%-3s", - type2desc(attr->a_type), - rxprec, rx, rxu, txprec, tx, txu); + put_line("%-30.30s", pad); + + draw_attr(rx1, rx1prec, rxu1, rx2, rx2prec, rxu2, + e->e_rx_usage, LIST_COL_1); + + draw_attr(tx1, tx1prec, txu1, tx2, tx2prec, txu2, + e->e_tx_usage, LIST_COL_2); - if (!(attr->a_flags & ATTR_FLAG_RX_ENABLED)) { - move(row, 60); - addstr("N/A"); - } - - if (!(attr->a_flags & ATTR_FLAG_TX_ENABLED)) { - move(row, 73); - addstr("N/A"); - } + } - move(row, 0); - *t = 0; + (*line)++; +} + +static void draw_group(struct element_group *g, void *arg) +{ + apply_layout(LAYOUT_HEADER); + int *line = arg; + + if (line_visible(*line)) { + NEXT_ROW(); + attron(A_BOLD); + put_line("%s", g->g_hdr->gh_title); + + attroff(A_BOLD); + mvaddch(row, LIST_COL_1, ACS_VLINE); + attron(A_BOLD); + put_line("%7s %7s %%", + g->g_hdr->gh_column[0], + g->g_hdr->gh_column[1]); + + attroff(A_BOLD); + mvaddch(row, LIST_COL_2, ACS_VLINE); + attron(A_BOLD); + put_line("%7s %7s %%", + g->g_hdr->gh_column[2], + g->g_hdr->gh_column[3]); } + + (*line)++; + + group_foreach_element(g, draw_element, arg); } -static void draw_detailed(void) +static void draw_element_list(void) { - int start_pos, end_pos, i; - item_t *intf; - int attr_flag = 0; + int line = 0; - intf = get_current_item(); + group_foreach(draw_group, &line); +} - if (NULL == intf) - return; - - move(row, 39); - addch(ACS_TTEE); - move(row, 0); +static inline int attr_visible(int nattr) +{ + return nattr >= graph_offset && nattr < (graph_offset + ngraph); +} - NEXT_ROW; - start_pos = row; - putl(" RX TX " \ - " RX TX"); +static void draw_graph_centered(struct graph *g, int row, int ncol, + const char *text) +{ + int hcenter = (g->g_cfg.gc_width / 2) - (strlen(text) / 2) + 8; - foreach_attr(intf, draw_attr_detail, &attr_flag); + if (hcenter < 9) + hcenter = 9; - end_pos = row; - for (i = start_pos; i <= end_pos; i++) { - move(i, 39); - addch(ACS_VLINE); - } - move(end_pos, 0); + mvprintw(row, ncol + hcenter, "%.*s", g->g_cfg.gc_width, text); } -static void print_quit(void) +static void draw_table(struct graph *g, struct graph_table *tbl, + struct attr *a, struct history *h, + const char *hdr, int ncol, int layout) { - /* - * This could be done with ncurses windows but i'm way too lazy to - * look into it - */ - int i, y = (rows/2) - 2; - char *text = " Really Quit? (y/n) "; - int len = strlen(text); - int x = (cols/2) - (len / 2); + int i, save_row; + char buf[32]; - attrset(A_STANDOUT); - mvaddch(y - 2, x - 1, ACS_ULCORNER); - mvaddch(y + 2, x - 1, ACS_LLCORNER); - mvaddch(y - 2, x + len, ACS_URCORNER); - mvaddch(y + 2, x + len, ACS_LRCORNER); + if (!tbl->gt_table) { + for (i = g->g_cfg.gc_height; i >= 0; i--) { + move(++row, ncol); + put_line(""); + } + return; + } - for (i = 0; i < 3; i++) { - mvaddch(y - 1 + i, x + len, ACS_VLINE); - mvaddch(y - 1 + i, x - 1 ,ACS_VLINE); + move(++row, ncol); + put_line("%8s", tbl->gt_y_unit ? : ""); + + snprintf(buf, sizeof(buf), "(%s %s/%s)", + hdr, a->a_def->ad_description, + h ? h->h_definition->hd_name : "?"); + + draw_graph_centered(g, row, ncol, buf); + + //move(row, ncol + g->g_cfg.gc_width - 3); + //put_line("[err %.2f%%]", rtiming.rt_variance.v_error); + + memset(buf, 0, strlen(buf)); + for (i = (g->g_cfg.gc_height - 1); i >= 0; i--) { + move(++row, ncol); + sprintf(buf, "%'8.2f ", tbl->gt_scale[i]); + addstr(buf); + apply_layout(layout); + put_line("%s", tbl->gt_table + (i * graph_row_size(&g->g_cfg))); + apply_layout(LAYOUT_LIST); } - for (i = 0; i < len; i++) { - mvaddch(y - 2, x + i, ACS_HLINE); - mvaddch(y - 1, x + i, ' '); - mvaddch(y + 1, x + i, ' '); - mvaddch(y + 2, x + i, ACS_HLINE); + move(++row, ncol); + put_line(" 1"); + + for (i = 1; i <= g->g_cfg.gc_width; i++) { + if (i % 5 == 0) { + move(row, ncol + i + 7); + printw("%2d", i); + } } - mvaddstr(y, x, text); - attroff(A_STANDOUT); + if (!h) { + const char *t1 = " No history data available. "; + const char *t2 = " Press h to start collecting history. "; + int vcenter = g->g_cfg.gc_height / 2; + + save_row = row; + draw_graph_centered(g, save_row - vcenter - 1, ncol, t1); + draw_graph_centered(g, save_row - vcenter, ncol, t2); + row = save_row; + } } -static void draw_help(void) +static void draw_history_graph(struct attr *a, struct history *h) { -#define HW 46 -#define HH 19 - int i, y = (rows/2) - (HH/2); - int x = (cols/2) - (HW/2); - char pad[HW+1]; + struct graph *g; + int ncol = 0, save_row; - memset(pad, ' ', sizeof(pad)); - pad[sizeof(pad) - 1] = '\0'; + g = graph_alloc(h, &c_graph_cfg); + graph_refill(g, h); - attron(A_STANDOUT); + save_row = row; + draw_table(g, &g->g_rx, a, h, "RX", ncol, LAYOUT_RX_GRAPH); - for (i = 0; i < HH; i++) - mvaddnstr(y + i, x, pad, -1); + if (graph_display == GRAPH_DISPLAY_SIDE_BY_SIDE) { + ncol = cols / 2; + row = save_row; + } - mvaddch(y - 1, x - 1, ACS_ULCORNER); - mvaddch(y + HH, x - 1, ACS_LLCORNER); - - mvaddch(y - 1, x + HW, ACS_URCORNER); - mvaddch(y + HH, x + HW, ACS_LRCORNER); + draw_table(g, &g->g_tx, a, h, "TX", ncol, LAYOUT_TX_GRAPH); - for (i = 0; i < HH; i++) { - mvaddch(y + i, x - 1, ACS_VLINE); - mvaddch(y + i, x + HW, ACS_VLINE); - } + graph_free(g); +} - for (i = 0; i < HW; i++) { - mvaddch(y - 1, x + i, ACS_HLINE); - mvaddch(y + HH, x + i, ACS_HLINE); - } +static void draw_attr_graph(struct element *e, struct attr *a, void *arg) +{ + int *nattr = arg; - attron(A_BOLD); - mvaddnstr(y- 1, x+15, "QUICK REFERENCE", -1); - attron(A_UNDERLINE); - mvaddnstr(y+ 0, x+3, "Navigation", -1); - attroff(A_BOLD | A_UNDERLINE); - mvaddnstr(y+ 0, x+35, "[Page 1/2]", -1); + if (attr_visible(*nattr)) { + struct history_def *sel; + struct history *h; - mvaddnstr(y+ 1, x+5, "Up Previous interface", -1); - mvaddnstr(y+ 2, x+5, "Down Next interface", -1); - mvaddnstr(y+ 3, x+5, "Pg-Up Previous node", -1); - mvaddnstr(y+ 4, x+5, "Pg-Down Next node", -1); - mvaddnstr(y+ 5, x+5, "< Previous graph", -1); - mvaddnstr(y+ 6, x+5, "> Next graph", -1); - mvaddnstr(y+ 7, x+5, "0-9 Goto n-th interface", -1); - mvaddnstr(y+ 8, x+5, "? Toggle quick reference", -1); - mvaddnstr(y+ 9, x+5, "q Quit bmon", -1); + sel = history_current(); + c_graph_cfg.gc_unit = a->a_def->ad_unit; - attron(A_BOLD | A_UNDERLINE); - mvaddnstr(y+11, x+3, "Display Settings", -1); - attroff(A_BOLD | A_UNDERLINE); + list_for_each_entry(h, &a->a_history_list, h_list) { + if (h->h_definition != sel) + continue; - mvaddnstr(y+12, x+5, "g Toggle graphical statistics", -1); - mvaddnstr(y+13, x+5, "d Toggle detailed statistics", -1); - mvaddnstr(y+14, x+5, "c Toggle combined node list", -1); - mvaddnstr(y+15, x+5, "l Toggle interface list", -1); - mvaddnstr(y+16, x+5, "f (Un)fold sub interfaces", -1); + draw_history_graph(a, h); + goto out; + } - attroff(A_STANDOUT); + draw_history_graph(a, NULL); + } + +out: + (*nattr)++; } -static void draw_help_2(void) +static void draw_graph(void) { -#define HW 46 -#define HH 19 - int i, y = (rows/2) - (HH/2); - int x = (cols/2) - (HW/2); - char pad[HW+1]; + int nattr = 0; - memset(pad, ' ', sizeof(pad)); - pad[sizeof(pad) - 1] = '\0'; + element_foreach_attr(current_element, &draw_attr_graph, &nattr); +} - attron(A_STANDOUT); +static int lines_required_for_info(void) +{ + int lines = 1; - for (i = 0; i < HH; i++) - mvaddnstr(y + i, x, pad, -1); + if (c_show_info) { + info_cols = cols / DETAILS_COLS; - mvaddch(y - 1, x - 1, ACS_ULCORNER); - mvaddch(y + HH, x - 1, ACS_LLCORNER); - - mvaddch(y - 1, x + HW, ACS_URCORNER); - mvaddch(y + HH, x + HW, ACS_LRCORNER); + if (!info_cols) + info_cols = 1; - for (i = 0; i < HH; i++) { - mvaddch(y + i, x - 1, ACS_VLINE); - mvaddch(y + i, x + HW, ACS_VLINE); + lines += (current_element->e_ninfo / info_cols); + if (current_element->e_ninfo % info_cols) + lines++; } - for (i = 0; i < HW; i++) { - mvaddch(y - 1, x + i, ACS_HLINE); - mvaddch(y + HH, x + i, ACS_HLINE); + return lines; +} + +static void __draw_info(struct element *e, struct info *info, int *ninfo) +{ + int ncol; + + ncol = ((*ninfo) * DETAILS_COLS) - 1; + move(row, ncol); + if (ncol > 0) + addch(ACS_VLINE); + + put_line(" %-14.14s %22.22s", info->i_name, info->i_value); + + if (++(*ninfo) >= info_cols) { + NEXT_ROW(); + *ninfo = 0; } +} - attron(A_BOLD); - mvaddnstr(y- 1, x+15, "QUICK REFERENCE", -1); - attron(A_UNDERLINE); - mvaddnstr(y+ 0, x+3, "Graph Management", -1); - attroff(A_BOLD | A_UNDERLINE); - mvaddnstr(y+ 0, x+35, "[Page 2/2]", -1); +static void draw_info(void) +{ + struct info *info; + int i, ninfo = 0; - mvaddnstr(y+ 1, x+5, "n New graph", -1); - mvaddnstr(y+ 2, x+5, "x Delete graph", -1); - mvaddnstr(y+ 3, x+5, "a Next attribute", -1); + if (!current_element->e_ninfo) + return; - attron(A_BOLD | A_UNDERLINE); - mvaddnstr(y+ 5, x+3, "Measurement Units", -1); - attroff(A_BOLD | A_UNDERLINE); + for (i = 1; i < detail_cols; i++) + mvaddch(row, (i * DETAILS_COLS) - 1, + c_show_details ? ACS_PLUS : ACS_TTEE); - mvaddnstr(y+ 6, x+5, "R Read Interval", -1); - mvaddnstr(y+ 7, x+5, "S Seconds", -1); - mvaddnstr(y+ 8, x+5, "M Minutes", -1); - mvaddnstr(y+ 9, x+5, "H Hours", -1); - mvaddnstr(y+10, x+5, "D Days", -1); - attroff(A_STANDOUT); + NEXT_ROW(); + list_for_each_entry(info, ¤t_element->e_info_list, i_list) + __draw_info(current_element, info, &ninfo); + + /* + * If the last row was incomplete, not all vlines have been drawn. + * draw them here + */ + for (i = 1; i < info_cols; i++) + mvaddch(row, (i * DETAILS_COLS - 1), ACS_VLINE); } -static void print_content(void) +static void draw_content(void) { - int cnt = 0; - int required_lines = 0, disable_detailed = 0, disabled_graphical = 0; + int graph_req, details_req, lines_available, total_req; + int info_req, empty_lines; + int disable_graph = 0, disable_details = 0, disable_info = 0; - if (NULL == get_current_node()) + if (!current_element) return; - if (c_list_in_list) { - NEXT_ROW; - attron(A_BOLD); - putl(" Name RX TX"); - attron(A_BOLD); + /* + * Reset selection offset. Will be set in lines_required_for_list(). + */ + selection_offset = 0; + offset = 0; - if (c_use_colors) - attrset(COLOR_PAIR(LAYOUT_HEADER) | layout[LAYOUT_HEADER].attr); - - NEXT_ROW; - hline(ACS_HLINE, cols); + /* Reset graph offset, will be set in lines_required_for_graph() */ + graph_offset = 0; - move(row, 28); - addch(ACS_TTEE); - move(row, 54); - addch(ACS_TTEE); - move(row, 0); + lines_available = rows - lines_required_for_statusbar() + - lines_required_for_header(); - if (c_combined_node_list) - foreach_node(draw_node, &cnt); - else - draw_node(get_current_node(), NULL); - } else { - NEXT_ROW; - hline(ACS_HLINE, cols); - move(row, 24); - addstr(" Press l to enable list view "); - move(row, 0); + list_req = lines_required_for_list(); + graph_req = lines_required_for_graph(); + details_req = lines_required_for_details(); + info_req = lines_required_for_info(); + + total_req = list_req + graph_req + details_req + info_req; + + if (total_req <= lines_available) { + /* + * Enough lines available for all data to displayed, all + * is good. Display the full list. + */ + list_length = list_req; + goto draw; } /* - * calculate lines required for graphical and detailed stats unfolded + * Not enough lines available for full list and all details + * requested... */ - if (c_graphical_in_list) - required_lines += lines_required_for_graphical(); - else - required_lines++; - if (c_detailed_in_list) - required_lines += lines_required_for_detailed(); - else - required_lines++; + if (c_show_list) { + /* + * ... try shortening the list first. + */ + list_length = lines_available - (total_req - list_req); + if (list_length >= c_list_min) + goto draw; + } - if ((rows - row) <= (required_lines + 1)) { + if (c_show_info) { + /* try disabling info */ + list_length = lines_available - (total_req - info_req + 1); + if (list_length >= c_list_min) { + disable_info = 1; + goto draw; + } + } + + if (c_show_details) { + /* ... try disabling details */ + list_length = lines_available - (total_req - details_req + 1); + if (list_length >= c_list_min) { + disable_details = 1; + goto draw; + } + } + + /* ... try disabling graph, details, and info */ + list_length = lines_available - 1 - 1 - 1; + if (list_length >= c_list_min) { + disable_graph = 1; + disable_details = 1; + disable_info = 1; + goto draw; + } + + NEXT_ROW(); + put_line("A minimum of %d lines is required to display content.\n", + (rows - lines_available) + c_list_min + 2); + return; + +draw: + if (selection_offset && list_length > 0) { /* - * not enough lines, start over with detailed stats disabled + * Vertically align the selected element in the middle + * of the list. */ - required_lines = 0; - disable_detailed = 1; + offset = selection_offset - (list_length / 2); /* - * 1 line for folded detailed stats display + * If element 0..(list_length/2) is selected, offset is + * negative here. Start drawing from first element. */ - required_lines++; + if (offset < 0) + offset = 0; - if (c_graphical_in_list) - required_lines += lines_required_for_graphical(); - else - required_lines++; + /* + * Ensure the full list length is used if one of the + * last (list_length/2) elements is selected. + */ + if (offset > (list_req - list_length)) + offset = (list_req - list_length); - if ((rows - row) <= (required_lines + 1)) { - /* - * bad luck, not even enough space for graphical stats - * reserve 2 lines for displaying folded detailed and - * graphical stats - */ - required_lines = 2; - disabled_graphical = 1; - } + if (offset >= list_req) + BUG(); } + if (c_show_list) { + draw_element_list(); + } else { + NEXT_ROW(); + hline(ACS_HLINE, cols); + center_text(" Press %c to enable list view ", + KEY_TOGGLE_LIST); + } + /* - * Clear out spare space + * Graphical statistics */ - while (row < (rows - (required_lines + 2))) { - NEXT_ROW; putl(""); - move(row, 28); - addch(ACS_VLINE); - move(row, 54); - addch(ACS_VLINE); + NEXT_ROW(); + hline(ACS_HLINE, cols); + mvaddch(row, LIST_COL_1, ACS_BTEE); + mvaddch(row, LIST_COL_2, ACS_BTEE); + + if (!c_show_graph) + center_text(" Press %c to enable graphical statistics ", + KEY_TOGGLE_GRAPH); + else { + if (disable_graph) + center_text(" Increase screen height to see graphical statistics "); + else + draw_graph(); } - NEXT_ROW; + empty_lines = rows - row - details_req - info_req + - lines_required_for_statusbar() - 1; + + while (empty_lines-- > 0) { + NEXT_ROW(); + put_line(""); + } + + /* + * Detailed statistics + */ + NEXT_ROW(); hline(ACS_HLINE, cols); - if (c_graphical_in_list) { - if (disabled_graphical) { - move(row, 15); - addstr(" Increase screen size to see graphical statistics "); - move(row, 0); - } else { - move(row, 28); - addch(ACS_BTEE); - move(row, 54); - addch(ACS_BTEE); - move(row, 0); - draw_graphic(); - } - } else { - move(row, 20); - addstr(" Press g to enable graphical statistics "); - move(row, 0); + if (!c_show_details) + center_text(" Press %c to enable detailed statistics ", + KEY_TOGGLE_DETAILS); + else { + if (disable_details) + center_text(" Increase screen height to see detailed statistics "); + else + draw_details(); } - NEXT_ROW; + /* + * Additional information + */ + NEXT_ROW(); hline(ACS_HLINE, cols); - if (c_detailed_in_list) { - if (disable_detailed) { - move(row, 15); - addstr(" Increase screen size to see detailed statistics "); - move(row, 0); - } else - draw_detailed(); - } else { - move(row, 20); - addstr(" Press d to enable detailed statistics "); - move(row, 0); + if (!c_show_info) + center_text(" Press %c to enable additional information ", + KEY_TOGGLE_INFO); + else { + if (disable_info) + center_text(" Increase screen height to see additional information "); + else + draw_info(); } } - static void curses_draw(void) { - if (NULL == get_current_node()) { - first_node(); - first_item(); - } - row = 0; move(0,0); getmaxyx(stdscr, rows, cols); - - if (cols < 80) { + + if (rows < 4) { clear(); - putl("Screen must be at least 80 columns wide"); - refresh(); - return; + put_line("Screen must be at least 4 rows in height"); + goto out; } - if (c_use_colors) - attrset(COLOR_PAIR(LAYOUT_STATUSBAR) | layout[LAYOUT_STATUSBAR].attr); - else - attrset(A_REVERSE); + if (cols < 48) { + clear(); + put_line("Screen must be at least 48 columns width"); + goto out; + } - if (get_current_node() && get_current_item()) { - putl(" %s on %s", - get_current_item()->i_name, get_current_node()->n_name); + current_element = element_current(); + current_attr = attr_current(); + + draw_header(); + + apply_layout(LAYOUT_DEFAULT); + draw_content(); + + /* fill empty lines with blanks */ + while (row < (rows - 1 - lines_required_for_statusbar())) { + move(++row, 0); + put_line(""); } - move(row, COLS - strlen(PACKAGE_STRING) - 1); - putl("%s", PACKAGE_STRING); - move(row, 0); - - if (c_use_colors) - attrset(COLOR_PAIR(LAYOUT_DEFAULT) | layout[LAYOUT_DEFAULT].attr); - else - attroff(A_REVERSE); - - print_content(); + draw_statusbar(); if (quit_mode) - print_quit(); + print_message(" Really Quit? (y/n) "); else if (print_help) { if (help_page == 0) draw_help(); +#if 0 else draw_help_2(); +#endif } - for (; row < rows-2;) { - move(++row, 0); - putl(""); - } - - row = rows-1; - move(row, 0); - - if (c_use_colors) - attrset(COLOR_PAIR(LAYOUT_STATUSBAR) | layout[LAYOUT_STATUSBAR].attr); - else - attrset(A_REVERSE); - - if (1) { - char s[27]; - time_t t = time(0); - double d; - int h, m; - - asctime_r(localtime(&t), s); - s[strlen(s) - 1] = '\0'; - d = difftime(time(0), start_time); - - if (d / 3600) { - h = (int) d / 3600; - m = (int) d % 3600; - m /= 60; - } else { - h = 0; - m = (int) d / 60; - } - - putl(" %s (%dh/%dm)", s, h, m); - move(row, COLS - strlen("Press ? for help") - 1); - putl("%s", "Press ? for help"); - move(row, 0); - } - +out: attrset(0); refresh(); } -static void fold(void) +static void __reset_attr_counter(struct element *e, struct attr *a, void *arg) { - item_t *intf = get_current_item(); - node_t *node = get_current_node(); - int fix = 0; + attr_reset_counter(a); +} - if (NULL == intf || NULL == node) - return; - - if (intf->i_flags & ITEM_FLAG_IS_CHILD) - fix = 1; - - while (intf->i_flags & ITEM_FLAG_IS_CHILD) - intf = get_item(node, intf->i_parent); - - if (intf->i_flags & ITEM_FLAG_FOLDED) - intf->i_flags &= ~ITEM_FLAG_FOLDED; - else - intf->i_flags |= ITEM_FLAG_FOLDED; - - if (fix) - prev_item(); +static void reset_counters(void) +{ + element_foreach_attr(current_element, __reset_attr_counter, NULL); } static int handle_input(int ch) @@ -835,7 +1100,10 @@ static int handle_input(int ch) switch (ch) { case 'q': - quit_mode = quit_mode ? 0 : 1; + if (print_help) + print_help = 0; + else + quit_mode = quit_mode ? 0 : 1; return 1; case 0x1b: @@ -848,25 +1116,11 @@ static int handle_input(int ch) exit(0); break; - case 'a': - next_attr(); - return 1; - case 'n': if (quit_mode) quit_mode = 0; - else - new_graph(); return 1; - case 'x': - del_graph(); - return 1; - - case 'f': - fold(); - return 1; - case 12: case KEY_CLEAR: #ifdef HAVE_REDRAWWIN @@ -875,106 +1129,95 @@ static int handle_input(int ch) clear(); return 1; - case 'c': - c_combined_node_list = c_combined_node_list ? 0 : 1; - return 1; - - case 'S': + case '?': clear(); - set_graph_unit(X_SEC); + print_help = print_help ? 0 : 1; return 1; - case 'M': - clear(); - set_graph_unit(X_MIN); + case KEY_TOGGLE_GRAPH: + c_show_graph = !c_show_graph; + if (c_show_graph && !c_ngraph) + c_ngraph = 1; return 1; - case 'H': - clear(); - set_graph_unit(X_HOUR); + case KEY_TOGGLE_DETAILS: + c_show_details = !c_show_details; return 1; - case 'D': - clear(); - set_graph_unit(X_DAY); + case KEY_TOGGLE_LIST: + c_show_list = !c_show_list; return 1; - case 'R': - clear(); - set_graph_unit(X_READ); + case KEY_TOGGLE_INFO: + c_show_info = !c_show_info; return 1; - case '?': - clear(); - print_help = print_help ? 0 : 1; + case KEY_COLLECT_HISTORY: + if (current_attr) { + attr_start_collecting_history(current_attr); + return 1; + } + break; + + case KEY_PPAGE: + { + int i; + for (i = 1; i < list_length; i++) + element_select_prev(); + } return 1; - case 'g': - c_graphical_in_list = c_graphical_in_list ? 0 : 1; + case KEY_NPAGE: + { + int i; + for (i = 1; i < list_length; i++) + element_select_next(); + } return 1; - case 'd': - c_detailed_in_list = c_detailed_in_list ? 0 : 1; + case KEY_DOWN: + element_select_next(); return 1; - case 'l': - c_list_in_list = c_list_in_list ? 0 : 1; + case KEY_UP: + element_select_prev(); return 1; - case KEY_PPAGE: - if (print_help) - help_page = help_page ? 0 : 1; - else - prev_node(); + case KEY_LEFT: + attr_select_prev(); return 1; - case KEY_NPAGE: - if (print_help) - help_page = help_page ? 0 : 1; - else - next_node(); + case KEY_RIGHT: + attr_select_next(); return 1; - case KEY_DOWN: - if (next_item() == END_OF_LIST) { - if (next_node() != END_OF_LIST) - first_item(); - } + case ']': + group_select_next(); return 1; - case KEY_UP: - if (prev_item() == END_OF_LIST) { - if (prev_node() != END_OF_LIST) - last_item(); - } + case '[': + group_select_prev(); return 1; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - goto_item(ch - 48); + case '<': + c_ngraph--; + if (c_ngraph <= 1) + c_ngraph = 1; return 1; case '>': - next_graph(); + c_ngraph++; + if (c_ngraph > 32) + c_ngraph = 32; return 1; - case '<': - prev_graph(); + case '\t': + history_select_next(); return 1; - - default: - if (get_current_item()) - if (handle_bindings(ch, get_current_item()->i_name)) - return 1; - break; + + case 'r': + reset_counters(); + return 1; } return 0; @@ -982,6 +1225,13 @@ static int handle_input(int ch) static void curses_pre(void) { + static int init = 0; + + if (!init) { + curses_init(); + init = 1; + } + for (;;) { int ch = getch(); @@ -993,18 +1243,12 @@ static void curses_pre(void) } } -static int curses_probe(void) -{ - /* XXX? */ - return 1; -} - static void print_module_help(void) { printf( "curses - Curses Output\n" \ "\n" \ - " Interactive curses UI. Type '?' to get some help.\n" \ + " Interactive curses UI. Press '?' to see help.\n" \ " Author: Thomas Graf \n" \ "\n" \ " Options:\n" \ @@ -1012,66 +1256,57 @@ static void print_module_help(void) " bgchar=CHAR Background character (default: '.')\n" \ " nchar=CHAR Noise character (default: ':')\n" \ " uchar=CHAR Unknown character (default: '?')\n" \ - " height=NUM Height of graph (default: 6)\n" \ - " xunit=UNIT X-Axis Unit (default: seconds)\n" \ - " yunit=UNIT Y-Axis Unit (default: dynamic)\n" \ + " gheight=NUM Height of graph (default: 6)\n" \ + " gwidth=NUM Width of graph (default: 60)\n" \ + " ngraph=NUM Number of graphs (default: 1)\n" \ " nocolors Do not use colors\n" \ - " nototal Do not print per node total\n" \ - " nosource Do not print the source of a item\n" \ - " cnl Combined node list\n" \ " graph Show graphical stats by default\n" \ - " detail Show detailed stats by default\n"); + " details Show detailed stats by default\n" \ + " info Show additional info screen by default\n" \ + " minlist=INT Minimum item list length\n"); } -static void curses_set_opts(tv_t *attrs) +static void curses_parse_opt(const char *type, const char *value) { - while (attrs) { - if (!strcasecmp(attrs->type, "fgchar") && attrs->value) - set_fg_char(attrs->value[0]); - else if (!strcasecmp(attrs->type, "bgchar") && attrs->value) - set_bg_char(attrs->value[0]); - else if (!strcasecmp(attrs->type, "nchar") && attrs->value) - set_noise_char(attrs->value[0]); - else if (!strcasecmp(attrs->type, "uchar") && attrs->value) - set_unk_char(attrs->value[0]); - else if (!strcasecmp(attrs->type, "xunit") && attrs->value) - set_x_unit(attrs->value, 1); - else if (!strcasecmp(attrs->type, "yunit") && attrs->value) - set_y_unit(attrs->value); - else if (!strcasecmp(attrs->type, "height") && attrs->value) - c_graph_height = strtol(attrs->value, NULL, 0); - else if (!strcasecmp(attrs->type, "cnl")) - c_combined_node_list = 1; - else if (!strcasecmp(attrs->type, "graph")) - c_graphical_in_list = 1; - else if (!strcasecmp(attrs->type, "detail")) - c_detailed_in_list = 1; - else if (!strcasecmp(attrs->type, "nocolors")) - c_use_colors = 0; - else if (!strcasecmp(attrs->type, "nototal")) - c_nototal = 1; - else if (!strcasecmp(attrs->type, "nosource")) - c_nosource = 1; - else if (!strcasecmp(attrs->type, "help")) { - print_module_help(); - exit(0); - } - - attrs = attrs->next; + if (!strcasecmp(type, "fgchar") && value) + c_graph_cfg.gc_foreground = value[0]; + else if (!strcasecmp(type, "bgchar") && value) + c_graph_cfg.gc_background = value[0]; + else if (!strcasecmp(type, "nchar") && value) + c_graph_cfg.gc_noise = value[0]; + else if (!strcasecmp(type, "uchar") && value) + c_graph_cfg.gc_unknown = value[0]; + else if (!strcasecmp(type, "gheight") && value) + c_graph_cfg.gc_height = strtol(value, NULL, 0); + else if (!strcasecmp(type, "gwidth") && value) + c_graph_cfg.gc_width = strtol(value, NULL, 0); + else if (!strcasecmp(type, "ngraph") && value) { + c_ngraph = strtol(value, NULL, 0); + c_show_graph = !!c_ngraph; + } else if (!strcasecmp(type, "details")) + c_show_details = 1; + else if (!strcasecmp(type, "info")) + c_show_info = 1; + else if (!strcasecmp(type, "nocolors")) + c_use_colors = 0; + else if (!strcasecmp(type, "minlist") && value) + c_list_min = strtol(value, NULL, 0); + else if (!strcasecmp(type, "help")) { + print_module_help(); + exit(0); } } -static struct output_module curses_ops = { - .om_name = "curses", - .om_init = curses_init, - .om_shutdown = curses_shutdown, - .om_pre = curses_pre, - .om_draw = curses_draw, - .om_set_opts = curses_set_opts, - .om_probe = curses_probe, +static struct bmon_module curses_ops = { + .m_name = "curses", + .m_flags = BMON_MODULE_DEFAULT, + .m_shutdown = curses_shutdown, + .m_pre = curses_pre, + .m_do = curses_draw, + .m_parse_opt = curses_parse_opt, }; static void __init do_curses_init(void) { - register_output_module(&curses_ops); + output_register(&curses_ops); }