|
version 1.1.1.1, 2012/02/21 22:19:56
|
version 1.1.1.2, 2014/07/30 07:55:27
|
|
Line 1
|
Line 1
|
| /* |
/* |
| * out_curses.c Curses Output |
* out_curses.c Curses Output |
| * |
* |
| * Copyright (c) 2001-2005 Thomas Graf <tgraf@suug.ch> | * Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch> |
| | * Copyright (c) 2013 Red Hat, Inc. |
| * |
* |
| * Permission is hereby granted, free of charge, to any person obtaining a |
* Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
* copy of this software and associated documentation files (the "Software"), |
|
Line 23
|
Line 24
|
| */ |
*/ |
| |
|
| #include <bmon/bmon.h> |
#include <bmon/bmon.h> |
| #include <bmon/graph.h> |
|
| #include <bmon/conf.h> |
#include <bmon/conf.h> |
| #include <bmon/itemtab.h> | #include <bmon/attr.h> |
| | #include <bmon/element.h> |
| | #include <bmon/element_cfg.h> |
| #include <bmon/input.h> |
#include <bmon/input.h> |
| |
#include <bmon/history.h> |
| |
#include <bmon/graph.h> |
| #include <bmon/output.h> |
#include <bmon/output.h> |
| #include <bmon/node.h> |
|
| #include <bmon/bindings.h> |
|
| #include <bmon/utils.h> |
#include <bmon/utils.h> |
| |
|
| |
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 initialized; |
| static int print_help = 0; | static int print_help; |
| static int quit_mode = 0; | static int quit_mode; |
| static int help_page = 0; | static int help_page; |
| | |
| | /* Current row */ |
| static int row; |
static int row; |
| |
|
| |
/* Number of rows */ |
| static int rows; |
static int rows; |
| |
|
| |
/* Number of columns */ |
| static int cols; |
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_use_colors = 1; |
| static int c_combined_node_list = 0; | static int c_show_details = 0; |
| static int c_graphical_in_list = 0; | static int c_show_list = 1; |
| static int c_detailed_in_list = 0; | static int c_show_info = 0; |
| static int c_list_in_list = 1; | static int c_list_min = 6; |
| static int c_nototal = 0; | |
| static int c_nosource = 0; | |
| |
|
| #define NEXT_ROW { \ | static struct graph_cfg c_graph_cfg = { |
| row++; \ | .gc_width = 60, |
| if (row >= rows-1) \ | .gc_height = 6, |
| return; \ | .gc_foreground = '|', |
| move(row,0); \ | .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, ...) | char *float2str(double value, int width, int prec, char *buf, size_t len) |
| { |
{ |
| va_list args; | snprintf(buf, len, "%'*.*f", width, value == 0.0f ? 0 : prec, value); |
| char buf[2048]; | |
| int x, y; | |
| |
|
| memset(buf, 0, sizeof(buf)); | return buf; |
| getyx(stdscr, y, x); | } |
| |
|
| va_start(args, fmt); | static void put_line(const char *fmt, ...) |
| vsnprintf(buf, sizeof(buf), fmt, args); | { |
| va_end(args); | va_list args; |
| | char buf[2048]; |
| | int x, y; |
| |
|
| if (strlen(buf) > cols-x) | memset(buf, 0, sizeof(buf)); |
| buf[cols-x] = '\0'; | getyx(stdscr, y, x); |
| else | |
| memset(&buf[strlen(buf)], ' ', cols-strlen(buf)-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()) | va_list args; |
| exit(1); | char *str; |
| | unsigned int col; |
| |
|
| |
va_start(args, fmt); |
| |
vasprintf(&str, fmt, args); |
| |
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; |
initialized = 1; |
| |
|
| if (!has_colors()) |
if (!has_colors()) |
|
Line 95 static void curses_init(void)
|
Line 206 static void curses_init(void)
|
| #if defined HAVE_USE_DEFAULT_COLORS |
#if defined HAVE_USE_DEFAULT_COLORS |
| use_default_colors(); |
use_default_colors(); |
| #endif |
#endif |
| for (i = 1; i < NR_LAYOUTS+1; i++) | for (i = 1; i < LAYOUT_MAX+1; i++) |
| init_pair(i, layout[i].fg, layout[i].bg); | init_pair(i, cfg_layout[i].l_fg, cfg_layout[i].l_bg); |
| } |
} |
| |
|
| keypad(stdscr, TRUE); |
keypad(stdscr, TRUE); |
|
Line 106 static void curses_init(void)
|
Line 217 static void curses_init(void)
|
| nodelay(stdscr, TRUE); /* getch etc. must be non-blocking */ |
nodelay(stdscr, TRUE); /* getch etc. must be non-blocking */ |
| clear(); |
clear(); |
| curs_set(0); |
curs_set(0); |
| |
|
| |
return 0; |
| } |
} |
| |
|
| static void curses_shutdown(void) |
static void curses_shutdown(void) |
|
Line 114 static void curses_shutdown(void)
|
Line 227 static void curses_shutdown(void)
|
| endwin(); |
endwin(); |
| } |
} |
| |
|
| static void draw_item_list_entry(item_t *intf, node_t *node) | struct detail_arg |
| { |
{ |
| int rxprec, txprec, rxpprec, txpprec; | int nattr; |
| stat_attr_t *bytes, *packets; | }; |
| double rx, tx, rxp, txp; | |
| char *rx_u, *tx_u, *rxp_u, *txp_u; | |
| char pad[23]; | |
| |
|
| bytes = lookup_attr(intf, intf->i_major_attr); | static void draw_attr_detail(struct element *e, struct attr *a, void *arg) |
| packets = lookup_attr(intf, intf->i_minor_attr); | { |
| | char *rx_u, *tx_u, buf1[32], buf2[32]; |
| | int rxprec, txprec, ncol; |
| | struct detail_arg *da = arg; |
| |
|
| if (bytes == NULL || packets == NULL) | double rx = unit_value2str(a->a_rx_rate.r_total, |
| return; | a->a_def->ad_unit, |
| | &rx_u, &rxprec); |
| | double tx = unit_value2str(a->a_tx_rate.r_total, |
| | a->a_def->ad_unit, |
| | &tx_u, &txprec); |
| |
|
| rx = cancel_down(attr_get_rx_rate(bytes), bytes->a_unit, &rx_u, &rxprec); | if (da->nattr >= detail_cols) { |
| tx = cancel_down(attr_get_tx_rate(bytes), bytes->a_unit, &tx_u, &txprec); | NEXT_ROW(); |
| | da->nattr = 0; |
| | } |
| |
|
| rxp = cancel_down(attr_get_rx_rate(packets), packets->a_unit, &rxp_u, &rxpprec); | ncol = (da->nattr * DETAILS_COLS) - 1; |
| txp = cancel_down(attr_get_tx_rate(packets), packets->a_unit, &txp_u, &txpprec); | move(row, ncol); |
| | if (ncol > 0) |
| | addch(ACS_VLINE); |
| |
|
| memset(pad, 0, sizeof(pad)); | put_line(" %-14.14s %8s%-3s %8s%-3s\n", |
| memset(pad, ' ', intf->i_level < 15 ? intf->i_level : 15); | 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) { | static void draw_details(void) |
| strncat(pad, " (", sizeof(pad) - strlen(pad) - 1); | { |
| strncat(pad, intf->i_desc, sizeof(pad) - strlen(pad) - 1); | int i; |
| strncat(pad, ")", sizeof(pad) - strlen(pad) - 1); | 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()) { | NEXT_ROW(); |
| if (c_use_colors) | element_foreach_attr(current_element, draw_attr_detail, &arg); |
| attrset(COLOR_PAIR(LAYOUT_SELECTED) | layout[LAYOUT_SELECTED].attr); | |
| else | /* |
| attron(A_REVERSE); | * 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()) { | for (i = 0; i < len; i++) { |
| if (c_use_colors) | mvaddch(y - 2, x + i, ACS_HLINE); |
| attrset(COLOR_PAIR(LAYOUT_LIST) | layout[LAYOUT_LIST].attr); | mvaddch(y - 1, x + i, ' '); |
| else | mvaddch(y + 1, x + i, ' '); |
| attroff(A_REVERSE); | mvaddch(y + 2, x + i, ACS_HLINE); |
| } |
} |
| |
|
| putl("%-22s %8.*f%s %8.*f%s %2d%% %8.*f%s %8.*f%s %2d%%", pad, | mvaddstr(y, x, text); |
| rxprec, rx, rx_u, rxpprec, rxp, rxp_u, intf->i_rx_usage, | attroff(A_STANDOUT); |
| txprec, tx, tx_u, txpprec, txp, txp_u, intf->i_tx_usage); | |
| |
|
| if (intf->i_rx_usage == -1) { | row = y + 2; |
| move(row, 51); | } |
| addstr(" "); | |
| | 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) { | for (i = 0; i < HW; i++) { |
| move(row, 77); | mvaddch(y - 1, x + i, ACS_HLINE); |
| addstr(" "); | mvaddch(y + HH, x + i, ACS_HLINE); |
| } |
} |
| |
|
| move(row, 28); | attron(A_BOLD); |
| addch(ACS_VLINE); | mvaddnstr(y- 1, x+15, "QUICK REFERENCE", -1); |
| move(row, 54); | attron(A_UNDERLINE); |
| addch(ACS_VLINE); | 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); |
| | |
| | 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); | static void draw_header(void) |
| foreach_child(node, intf, handle_child, node); | { |
| | 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); |
| } |
} |
| |
|
| |
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)) | static const char *help_text = "Press ? for help"; |
| return; | char s[27]; |
| | time_t t = time(0); |
| |
|
| draw_item_list_entry(intf, node); | apply_layout(LAYOUT_STATUSBAR); |
| |
|
| if (!(intf->i_flags & ITEM_FLAG_FOLDED)) | asctime_r(localtime(&t), s); |
| foreach_child(node, intf, handle_child, node); | 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) { | ngraph = 0; |
| 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)++; | |
| } | |
| |
|
| attrset(A_BOLD); | if (c_show_graph && current_element) { |
| NEXT_ROW; | graph_display = GRAPH_DISPLAY_STANDARD; |
| if (c_nosource) | |
| putl("%s", node->n_name); | |
| else | |
| putl("%s (%s)", node->n_name, | |
| node->n_from ? node->n_from : "local"); | |
| |
|
| move(row, 30); | element_foreach_attr(current_element, count_attr_graph, NULL); |
| putl(" Rate # %% Rate # %%"); | |
| attroff(A_BOLD); | |
| |
|
| move(row, 28); | if (ngraph > c_ngraph) |
| addch(ACS_VLINE); | ngraph = c_ngraph; |
| move(row, 54); | |
| addch(ACS_VLINE); | |
| |
|
| if (c_use_colors) | /* check if we have room to draw graphs on the same level */ |
| attrset(COLOR_PAIR(LAYOUT_LIST) | layout[LAYOUT_LIST].attr); | if (cols > (2 * (c_graph_cfg.gc_width + 10))) |
| | graph_display = GRAPH_DISPLAY_SIDE_BY_SIDE; |
| |
|
| for (i = 0; i < node->n_nitems; i++) | /* +2 = header + time axis */ |
| draw_item(node, &node->n_items[i]); | lines = ngraph * (graph_display * (c_graph_cfg.gc_height + 2)); |
| | } |
| |
|
| if (!c_nototal) { | return lines + 1; |
| 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); | |
| |
|
| rx_min = cancel_down(node->n_rx_min_total, U_NUMBER, &rx_min_u, &rx_min_prec); | static int lines_required_for_details(void) |
| tx_min = cancel_down(node->n_tx_min_total, U_NUMBER, &tx_min_u, &tx_min_prec); | { |
| | int lines = 1; |
| |
|
| NEXT_ROW; | if (c_show_details && current_element) { |
| putl(" %-26s %8.*f%s %8.*f%s %8.*f%s %8.*f%s ", "Total", | lines++; /* header */ |
| 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); | |
| |
|
| move(row, 28); | detail_cols = cols / DETAILS_COLS; |
| addch(ACS_VLINE); | |
| move(row, 54); | if (!detail_cols) |
| addch(ACS_VLINE); | detail_cols = 1; |
| | |
| | lines += (current_element->e_nattrs / detail_cols); |
| | if (current_element->e_nattrs % detail_cols) |
| | lines++; |
| } |
} |
| |
|
| if (c_use_colors) | return lines; |
| attrset(COLOR_PAIR(LAYOUT_DEFAULT) | layout[LAYOUT_DEFAULT].attr); | |
| } |
} |
| |
|
| 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())) | if (e == current_element) |
| return get_ngraphs() * ((2 * c_graph_height) + 5); | selection_offset = *lines; |
| else | |
| return INT_MAX; | (*lines)++; |
| } |
} |
| |
|
| static int lines_required_for_detailed(void) | static void count_group_lines(struct element_group *g, void *arg) |
| { |
{ |
| if (get_current_item()) | int *lines = arg; |
| return 2 + ((get_current_item()->i_nattrs + 1) / 2); | |
| else | /* group title */ |
| return INT_MAX; | (*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; | int lines = 0; |
| graph_t *g; | |
| g = create_configued_graph(&a->a_hist, c_graph_height, a->a_unit, xunit); | |
| |
|
| NEXT_ROW; | if (c_show_list) |
| putl("RX %s [%s]", | group_foreach(&count_group_lines, &lines); |
| g->g_rx.t_y_unit, type2desc(a->a_type)); | else |
| | lines = 1; |
| |
|
| if (selected) { | return lines; |
| move(row, 72); | } |
| attron(A_BOLD); | |
| addstr("(sel)"); | |
| attroff(A_BOLD); | |
| move(row, 0); | |
| } | |
| |
|
| for (w = (c_graph_height - 1); w >= 0; w--) { | static inline int line_visible(int line) |
| NEXT_ROW; | { |
| putl(" %8.2f %s\n", g->g_rx.t_y_scale[w], (char *) g->g_rx.t_data + (w * (HISTORY_SIZE + 1))); | return line >= offset && line < (offset + list_length); |
| } | } |
| |
|
| move(row, 71); | static void draw_attr(double rate1, int prec1, char *unit1, |
| putl("[%.2f%%]", rtiming.rt_variance.v_error); | double rate2, int prec2, char *unit2, |
| NEXT_ROW; | float usage, int ncol) |
| putl(" 1 5 10 15 20 25 30 35 40 45 50 55 60 %s", g->g_rx.t_x_unit); | { |
| | char buf[32]; |
| |
|
| NEXT_ROW; | move(row, ncol); |
| putl("TX %s", g->g_tx.t_y_unit); | addch(ACS_VLINE); |
| | printw("%7s%-3s", |
| | float2str(rate1, 7, prec1, buf, sizeof(buf)), unit1); |
| |
|
| for (w = (c_graph_height - 1); w >= 0; w--) { | printw("%7s%-3s", |
| NEXT_ROW; | float2str(rate2, 7, prec2, buf, sizeof(buf)), unit2); |
| putl(" %8.2f %s\n", g->g_tx.t_y_scale[w], (char *) g->g_tx.t_data + (w * (HISTORY_SIZE + 1))); | |
| } | |
| |
|
| move(row, 71); | if (usage != FLT_MAX) |
| putl("[%.2f%%]", rtiming.rt_variance.v_error); | printw("%2.0f%%", usage); |
| NEXT_ROW; | else |
| putl(" 1 5 10 15 20 25 30 35 40 45 50 55 60 %s", g->g_tx.t_x_unit); | printw("%3s", ""); |
| |
| free_graph(g); | |
| } |
} |
| |
|
| 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++) { | apply_layout(LAYOUT_LIST); |
| stat_attr_hist_t *a = (stat_attr_hist_t *) current_attr(i); | |
| |
|
| if (!(a->a_flags & ATTR_FLAG_HISTORY)) | if (line_visible(*line)) { |
| continue; | 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) | NEXT_ROW(); |
| __draw_graphic(a, get_current_item()->i_graph_sel == i, | |
| get_current_item()->i_unit[i]); | |
| |
|
| if (i < (get_ngraphs() - 1)) { | if (e->e_key_attr[GT_MAJOR] && |
| int n; | (a = attr_lookup(e, e->e_key_attr[GT_MAJOR]->ad_id))) |
| NEXT_ROW; | attr_rate2float(a, &rx1, &rxu1, &rx1prec, |
| for (n = 1; n < cols-1; n += 2) | &tx1, &txu1, &tx1prec); |
| mvaddch(row, n, ACS_HLINE); | |
| } | |
| } | |
| | |
| } | |
| |
|
| static void draw_attr_detail(stat_attr_t *attr, void *arg) | if (e->e_key_attr[GT_MINOR] && |
| { | (a = attr_lookup(e, e->e_key_attr[GT_MINOR]->ad_id))) |
| int rxprec, txprec; | attr_rate2float(a, &rx2, &rxu2, &rx2prec, |
| double rx, tx; | &tx2, &txu2, &tx2prec); |
| char *rxu, *txu; | |
| int *t = (int *) arg; | |
| |
|
| rx = cancel_down(attr_get_rx(attr), attr->a_unit, &rxu, &rxprec); | memset(pad, 0, sizeof(pad)); |
| tx = cancel_down(attr_get_tx(attr), attr->a_unit, &txu, &txprec); | memset(pad, ' ', e->e_level < 6 ? e->e_level * 2 : 12); |
| |
|
| if (0 == *t) { | strncat(pad, e->e_name, sizeof(pad) - strlen(pad) - 1); |
| NEXT_ROW; | |
| putl(" %-12s%9.*f%-3s %9.*f%-3s", | |
| type2desc(attr->a_type), | |
| rxprec, rx, rxu, txprec, tx, txu); | |
| |
|
| if (!(attr->a_flags & ATTR_FLAG_RX_ENABLED)) { | if (e->e_description) { |
| move(row, 20); | strncat(pad, " (", sizeof(pad) - strlen(pad) - 1); |
| addstr("N/A"); | 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)) { | if (*line == offset) { |
| move(row, 33); | attron(A_BOLD); |
| addstr("N/A"); | 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); | put_line("%-30.30s", pad); |
| *t = 1; | |
| } else { | draw_attr(rx1, rx1prec, rxu1, rx2, rx2prec, rxu2, |
| putl(" %-12s%9.*f%-3s %9.*f%-3s", | e->e_rx_usage, LIST_COL_1); |
| type2desc(attr->a_type), | |
| rxprec, rx, rxu, txprec, tx, txu); | 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); | (*line)++; |
| *t = 0; | } |
| | |
| | static void draw_group(struct element_group *g, void *arg) |
| | { |
| | 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; | int line = 0; |
| item_t *intf; | |
| int attr_flag = 0; | |
| |
|
| intf = get_current_item(); | group_foreach(draw_group, &line); |
| | } |
| |
|
| if (NULL == intf) | static inline int attr_visible(int nattr) |
| return; | { |
| | return nattr >= graph_offset && nattr < (graph_offset + ngraph); |
| move(row, 39); | } |
| addch(ACS_TTEE); | |
| move(row, 0); | |
| |
|
| NEXT_ROW; | static void draw_graph_centered(struct graph *g, int row, int ncol, |
| start_pos = row; | const char *text) |
| putl(" RX TX " \ | { |
| " RX TX"); | 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; | mvprintw(row, ncol + hcenter, "%.*s", g->g_cfg.gc_width, text); |
| for (i = start_pos; i <= end_pos; i++) { | |
| move(i, 39); | |
| addch(ACS_VLINE); | |
| } | |
| move(end_pos, 0); | |
| } |
} |
| |
|
| 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 i, save_row; |
| * This could be done with ncurses windows but i'm way too lazy to | char buf[32]; |
| * 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); | |
| |
|
| attrset(A_STANDOUT); | if (!tbl->gt_table) { |
| mvaddch(y - 2, x - 1, ACS_ULCORNER); | for (i = g->g_cfg.gc_height; i >= 0; i--) { |
| mvaddch(y + 2, x - 1, ACS_LLCORNER); | move(++row, ncol); |
| mvaddch(y - 2, x + len, ACS_URCORNER); | put_line(""); |
| mvaddch(y + 2, x + len, ACS_LRCORNER); | } |
| | return; |
| | } |
| |
|
| for (i = 0; i < 3; i++) { | move(++row, ncol); |
| mvaddch(y - 1 + i, x + len, ACS_VLINE); | put_line("%8s", tbl->gt_y_unit ? : ""); |
| mvaddch(y - 1 + i, x - 1 ,ACS_VLINE); | |
| | 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); |
| | |
| | for (i = (g->g_cfg.gc_height - 1); i >= 0; i--) { |
| | move(++row, ncol); |
| | put_line("%'8.2f %s", |
| | tbl->gt_scale[i], |
| | tbl->gt_table + (i * graph_row_size(&g->g_cfg))); |
| } |
} |
| |
|
| for (i = 0; i < len; i++) { | move(++row, ncol); |
| mvaddch(y - 2, x + i, ACS_HLINE); | put_line(" 1"); |
| mvaddch(y - 1, x + i, ' '); | |
| mvaddch(y + 1, x + i, ' '); | for (i = 1; i <= g->g_cfg.gc_width; i++) { |
| mvaddch(y + 2, x + i, ACS_HLINE); | if (i % 5 == 0) { |
| | move(row, ncol + i + 7); |
| | printw("%2d", i); |
| | } |
| } |
} |
| |
|
| mvaddstr(y, x, text); | if (!h) { |
| attroff(A_STANDOUT); | 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 | struct graph *g; |
| #define HH 19 | int ncol = 0, save_row; |
| int i, y = (rows/2) - (HH/2); | |
| int x = (cols/2) - (HW/2); | |
| char pad[HW+1]; | |
| |
|
| memset(pad, ' ', sizeof(pad)); | g = graph_alloc(h, &c_graph_cfg); |
| pad[sizeof(pad) - 1] = '\0'; | graph_refill(g, h); |
| |
|
| attron(A_STANDOUT); | save_row = row; |
| | draw_table(g, &g->g_rx, a, h, "RX", ncol); |
| |
|
| for (i = 0; i < HH; i++) | if (graph_display == GRAPH_DISPLAY_SIDE_BY_SIDE) { |
| mvaddnstr(y + i, x, pad, -1); | ncol = cols / 2; |
| | row = save_row; |
| | } |
| |
|
| mvaddch(y - 1, x - 1, ACS_ULCORNER); | draw_table(g, &g->g_tx, a, h, "TX", ncol); |
| 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++) { | graph_free(g); |
| mvaddch(y + i, x - 1, ACS_VLINE); | } |
| mvaddch(y + i, x + HW, ACS_VLINE); | |
| } | |
| |
|
| for (i = 0; i < HW; i++) { | static void draw_attr_graph(struct element *e, struct attr *a, void *arg) |
| mvaddch(y - 1, x + i, ACS_HLINE); | { |
| mvaddch(y + HH, x + i, ACS_HLINE); | int *nattr = arg; |
| } | |
| |
|
| attron(A_BOLD); | if (attr_visible(*nattr)) { |
| mvaddnstr(y- 1, x+15, "QUICK REFERENCE", -1); | struct history_def *sel; |
| attron(A_UNDERLINE); | struct history *h; |
| mvaddnstr(y+ 0, x+3, "Navigation", -1); | |
| attroff(A_BOLD | A_UNDERLINE); | |
| mvaddnstr(y+ 0, x+35, "[Page 1/2]", -1); | |
| |
|
| mvaddnstr(y+ 1, x+5, "Up Previous interface", -1); | sel = history_current(); |
| mvaddnstr(y+ 2, x+5, "Down Next interface", -1); | c_graph_cfg.gc_unit = a->a_def->ad_unit; |
| 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); | |
| |
|
| attron(A_BOLD | A_UNDERLINE); | list_for_each_entry(h, &a->a_history_list, h_list) { |
| mvaddnstr(y+11, x+3, "Display Settings", -1); | if (h->h_definition != sel) |
| attroff(A_BOLD | A_UNDERLINE); | continue; |
| |
|
| mvaddnstr(y+12, x+5, "g Toggle graphical statistics", -1); | draw_history_graph(a, h); |
| mvaddnstr(y+13, x+5, "d Toggle detailed statistics", -1); | goto out; |
| 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); | |
| |
|
| attroff(A_STANDOUT); | draw_history_graph(a, NULL); |
| | } |
| | |
| | out: |
| | (*nattr)++; |
| } |
} |
| |
|
| static void draw_help_2(void) | static void draw_graph(void) |
| { |
{ |
| #define HW 46 | int nattr = 0; |
| #define HH 19 | |
| int i, y = (rows/2) - (HH/2); | |
| int x = (cols/2) - (HW/2); | |
| char pad[HW+1]; | |
| |
|
| memset(pad, ' ', sizeof(pad)); | element_foreach_attr(current_element, &draw_attr_graph, &nattr); |
| pad[sizeof(pad) - 1] = '\0'; | } |
| |
|
| attron(A_STANDOUT); | static int lines_required_for_info(void) |
| | { |
| | int lines = 1; |
| |
|
| for (i = 0; i < HH; i++) | if (c_show_info) { |
| mvaddnstr(y + i, x, pad, -1); | info_cols = cols / DETAILS_COLS; |
| |
|
| mvaddch(y - 1, x - 1, ACS_ULCORNER); | if (!info_cols) |
| mvaddch(y + HH, x - 1, ACS_LLCORNER); | info_cols = 1; |
| | |
| mvaddch(y - 1, x + HW, ACS_URCORNER); | |
| mvaddch(y + HH, x + HW, ACS_LRCORNER); | |
| |
|
| for (i = 0; i < HH; i++) { | lines += (current_element->e_ninfo / info_cols); |
| mvaddch(y + i, x - 1, ACS_VLINE); | if (current_element->e_ninfo % info_cols) |
| mvaddch(y + i, x + HW, ACS_VLINE); | lines++; |
| } |
} |
| |
|
| for (i = 0; i < HW; i++) { | return lines; |
| mvaddch(y - 1, x + i, ACS_HLINE); | } |
| mvaddch(y + HH, x + i, ACS_HLINE); | |
| | 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); | static void draw_info(void) |
| mvaddnstr(y- 1, x+15, "QUICK REFERENCE", -1); | { |
| attron(A_UNDERLINE); | struct info *info; |
| mvaddnstr(y+ 0, x+3, "Graph Management", -1); | int i, ninfo = 0; |
| attroff(A_BOLD | A_UNDERLINE); | |
| mvaddnstr(y+ 0, x+35, "[Page 2/2]", -1); | |
| |
|
| mvaddnstr(y+ 1, x+5, "n New graph", -1); | if (!current_element->e_ninfo) |
| mvaddnstr(y+ 2, x+5, "x Delete graph", -1); | return; |
| mvaddnstr(y+ 3, x+5, "a Next attribute", -1); | |
| |
|
| attron(A_BOLD | A_UNDERLINE); | for (i = 1; i < detail_cols; i++) |
| mvaddnstr(y+ 5, x+3, "Measurement Units", -1); | mvaddch(row, (i * DETAILS_COLS) - 1, |
| attroff(A_BOLD | A_UNDERLINE); | c_show_details ? ACS_PLUS : ACS_TTEE); |
| |
|
| mvaddnstr(y+ 6, x+5, "R Read Interval", -1); | NEXT_ROW(); |
| mvaddnstr(y+ 7, x+5, "S Seconds", -1); | list_for_each_entry(info, ¤t_element->e_info_list, i_list) |
| mvaddnstr(y+ 8, x+5, "M Minutes", -1); | __draw_info(current_element, info, &ninfo); |
| mvaddnstr(y+ 9, x+5, "H Hours", -1); | |
| mvaddnstr(y+10, x+5, "D Days", -1); | /* |
| attroff(A_STANDOUT); | * 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 graph_req, details_req, lines_available, total_req; |
| int required_lines = 0, disable_detailed = 0, disabled_graphical = 0; | int info_req, empty_lines; |
| | int disable_graph = 0, disable_details = 0, disable_info = 0; |
| |
|
| if (NULL == get_current_node()) | if (!current_element) |
| return; |
return; |
| |
|
| if (c_list_in_list) { | /* |
| NEXT_ROW; | * Reset selection offset. Will be set in lines_required_for_list(). |
| attron(A_BOLD); | */ |
| putl(" Name RX TX"); | selection_offset = 0; |
| attron(A_BOLD); | offset = 0; |
| |
|
| if (c_use_colors) | /* Reset graph offset, will be set in lines_required_for_graph() */ |
| attrset(COLOR_PAIR(LAYOUT_HEADER) | layout[LAYOUT_HEADER].attr); | graph_offset = 0; |
| | |
| NEXT_ROW; | |
| hline(ACS_HLINE, cols); | |
| |
|
| move(row, 28); | lines_available = rows - lines_required_for_statusbar() |
| addch(ACS_TTEE); | - lines_required_for_header(); |
| move(row, 54); | |
| addch(ACS_TTEE); | |
| move(row, 0); | |
| |
|
| if (c_combined_node_list) | list_req = lines_required_for_list(); |
| foreach_node(draw_node, &cnt); | graph_req = lines_required_for_graph(); |
| else | details_req = lines_required_for_details(); |
| draw_node(get_current_node(), NULL); | info_req = lines_required_for_info(); |
| } else { | |
| NEXT_ROW; | total_req = list_req + graph_req + details_req + info_req; |
| hline(ACS_HLINE, cols); | |
| move(row, 24); | if (total_req <= lines_available) { |
| addstr(" Press l to enable list view "); | /* |
| move(row, 0); | * 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) | if (c_show_list) { |
| required_lines += lines_required_for_detailed(); | /* |
| else | * ... try shortening the list first. |
| required_lines++; | */ |
| | 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; | offset = selection_offset - (list_length / 2); |
| disable_detailed = 1; | |
| |
|
| /* |
/* |
| * 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(); | * Ensure the full list length is used if one of the |
| else | * last (list_length/2) elements is selected. |
| required_lines++; | */ |
| | if (offset > (list_req - list_length)) |
| | offset = (list_req - list_length); |
| |
|
| if ((rows - row) <= (required_lines + 1)) { | if (offset >= list_req) |
| /* | BUG(); |
| * 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 (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(); |
| NEXT_ROW; putl(""); | hline(ACS_HLINE, cols); |
| move(row, 28); | mvaddch(row, LIST_COL_1, ACS_BTEE); |
| addch(ACS_VLINE); | mvaddch(row, LIST_COL_2, ACS_BTEE); |
| move(row, 54); | |
| addch(ACS_VLINE); | 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); |
hline(ACS_HLINE, cols); |
| |
|
| if (c_graphical_in_list) { | if (!c_show_details) |
| if (disabled_graphical) { | center_text(" Press %c to enable detailed statistics ", |
| move(row, 15); | KEY_TOGGLE_DETAILS); |
| addstr(" Increase screen size to see graphical statistics "); | else { |
| move(row, 0); | if (disable_details) |
| } else { | center_text(" Increase screen height to see detailed statistics "); |
| move(row, 28); | else |
| addch(ACS_BTEE); | draw_details(); |
| 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); | |
| } |
} |
| |
|
| NEXT_ROW; | /* |
| | * Additional information |
| | */ |
| | NEXT_ROW(); |
| hline(ACS_HLINE, cols); |
hline(ACS_HLINE, cols); |
| |
|
| if (c_detailed_in_list) { | if (!c_show_info) |
| if (disable_detailed) { | center_text(" Press %c to enable additional information ", |
| move(row, 15); | KEY_TOGGLE_INFO); |
| addstr(" Increase screen size to see detailed statistics "); | else { |
| move(row, 0); | if (disable_info) |
| } else | center_text(" Increase screen height to see additional information "); |
| draw_detailed(); | else |
| } else { | draw_info(); |
| move(row, 20); | |
| addstr(" Press d to enable detailed statistics "); | |
| move(row, 0); | |
| } |
} |
| } |
} |
| |
|
| |
|
| |
|
| static void curses_draw(void) |
static void curses_draw(void) |
| { |
{ |
| if (NULL == get_current_node()) { |
|
| first_node(); |
|
| first_item(); |
|
| } |
|
| |
|
| row = 0; |
row = 0; |
| move(0,0); |
move(0,0); |
| |
|
| getmaxyx(stdscr, rows, cols); |
getmaxyx(stdscr, rows, cols); |
| | |
| if (cols < 80) { | if (rows < 4) { |
| clear(); |
clear(); |
| putl("Screen must be at least 80 columns wide"); | put_line("Screen must be at least 4 rows in height"); |
| refresh(); | goto out; |
| return; | |
| } |
} |
| |
|
| if (c_use_colors) | if (cols < 48) { |
| attrset(COLOR_PAIR(LAYOUT_STATUSBAR) | layout[LAYOUT_STATUSBAR].attr); | clear(); |
| else | put_line("Screen must be at least 48 columns width"); |
| attrset(A_REVERSE); | goto out; |
| | } |
| |
|
| if (get_current_node() && get_current_item()) { | current_element = element_current(); |
| putl(" %s on %s", | current_attr = attr_current(); |
| get_current_item()->i_name, get_current_node()->n_name); | |
| | 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); | draw_statusbar(); |
| 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(); | |
| |
|
| if (quit_mode) |
if (quit_mode) |
| print_quit(); | print_message(" Really Quit? (y/n) "); |
| else if (print_help) { |
else if (print_help) { |
| if (help_page == 0) |
if (help_page == 0) |
| draw_help(); |
draw_help(); |
| |
#if 0 |
| else |
else |
| draw_help_2(); |
draw_help_2(); |
| |
#endif |
| } |
} |
| |
|
| for (; row < rows-2;) { | out: |
| 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); | |
| } | |
| | |
| attrset(0); |
attrset(0); |
| refresh(); |
refresh(); |
| } |
} |
| |
|
| static void fold(void) |
|
| { |
|
| item_t *intf = get_current_item(); |
|
| node_t *node = get_current_node(); |
|
| int fix = 0; |
|
| |
|
| 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 int handle_input(int ch) |
static int handle_input(int ch) |
| { |
{ |
| switch (ch) |
switch (ch) |
| { |
{ |
| case 'q': |
case 'q': |
| quit_mode = quit_mode ? 0 : 1; | if (print_help) |
| | print_help = 0; |
| | else |
| | quit_mode = quit_mode ? 0 : 1; |
| return 1; |
return 1; |
| |
|
| case 0x1b: |
case 0x1b: |
|
Line 848 static int handle_input(int ch)
|
Line 1097 static int handle_input(int ch)
|
| exit(0); |
exit(0); |
| break; |
break; |
| |
|
| case 'a': |
|
| next_attr(); |
|
| return 1; |
|
| |
|
| case 'n': |
case 'n': |
| if (quit_mode) |
if (quit_mode) |
| quit_mode = 0; |
quit_mode = 0; |
| else |
|
| new_graph(); |
|
| return 1; |
return 1; |
| |
|
| case 'x': |
|
| del_graph(); |
|
| return 1; |
|
| |
|
| case 'f': |
|
| fold(); |
|
| return 1; |
|
| |
|
| case 12: |
case 12: |
| case KEY_CLEAR: |
case KEY_CLEAR: |
| #ifdef HAVE_REDRAWWIN |
#ifdef HAVE_REDRAWWIN |
|
Line 875 static int handle_input(int ch)
|
Line 1110 static int handle_input(int ch)
|
| clear(); |
clear(); |
| return 1; |
return 1; |
| |
|
| case 'c': | case '?': |
| c_combined_node_list = c_combined_node_list ? 0 : 1; | |
| return 1; | |
| |
| case 'S': | |
| clear(); |
clear(); |
| set_graph_unit(X_SEC); | print_help = 1; |
| return 1; |
return 1; |
| |
|
| case 'M': | case KEY_TOGGLE_GRAPH: |
| clear(); | c_show_graph = !c_show_graph; |
| set_graph_unit(X_MIN); | if (c_show_graph && !c_ngraph) |
| | c_ngraph = 1; |
| return 1; |
return 1; |
| |
|
| case 'H': | case KEY_TOGGLE_DETAILS: |
| clear(); | c_show_details = !c_show_details; |
| set_graph_unit(X_HOUR); | |
| return 1; |
return 1; |
| |
|
| case 'D': | case KEY_TOGGLE_LIST: |
| clear(); | c_show_list = !c_show_list; |
| set_graph_unit(X_DAY); | |
| return 1; |
return 1; |
| |
|
| case 'R': | case KEY_TOGGLE_INFO: |
| clear(); | c_show_info = !c_show_info; |
| set_graph_unit(X_READ); | |
| return 1; |
return 1; |
| |
|
| case '?': | case KEY_COLLECT_HISTORY: |
| clear(); | if (current_attr) { |
| print_help = print_help ? 0 : 1; | 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; |
return 1; |
| |
|
| case 'g': | case KEY_NPAGE: |
| c_graphical_in_list = c_graphical_in_list ? 0 : 1; | { |
| | int i; |
| | for (i = 1; i < list_length; i++) |
| | element_select_next(); |
| | } |
| return 1; |
return 1; |
| |
|
| case 'd': | case KEY_DOWN: |
| c_detailed_in_list = c_detailed_in_list ? 0 : 1; | element_select_next(); |
| return 1; |
return 1; |
| |
|
| case 'l': | case KEY_UP: |
| c_list_in_list = c_list_in_list ? 0 : 1; | element_select_prev(); |
| return 1; |
return 1; |
| |
|
| case KEY_PPAGE: | case KEY_LEFT: |
| if (print_help) | attr_select_prev(); |
| help_page = help_page ? 0 : 1; | |
| else | |
| prev_node(); | |
| return 1; |
return 1; |
| |
|
| case KEY_NPAGE: | case KEY_RIGHT: |
| if (print_help) | attr_select_next(); |
| help_page = help_page ? 0 : 1; | |
| else | |
| next_node(); | |
| return 1; |
return 1; |
| |
|
| case KEY_DOWN: | case ']': |
| if (next_item() == END_OF_LIST) { | group_select_next(); |
| if (next_node() != END_OF_LIST) | |
| first_item(); | |
| } | |
| return 1; |
return 1; |
| |
|
| case KEY_UP: | case '[': |
| if (prev_item() == END_OF_LIST) { | group_select_prev(); |
| if (prev_node() != END_OF_LIST) | |
| last_item(); | |
| } | |
| return 1; |
return 1; |
| |
|
| case '0': | case '<': |
| case '1': | c_ngraph--; |
| case '2': | if (c_ngraph <= 1) |
| case '3': | c_ngraph = 1; |
| case '4': | |
| case '5': | |
| case '6': | |
| case '7': | |
| case '8': | |
| case '9': | |
| goto_item(ch - 48); | |
| return 1; |
return 1; |
| |
|
| case '>': |
case '>': |
| next_graph(); | c_ngraph++; |
| | if (c_ngraph > 32) |
| | c_ngraph = 32; |
| return 1; |
return 1; |
| |
|
| case '<': | case '\t': |
| prev_graph(); | history_select_next(); |
| return 1; |
return 1; |
| |
|
| default: |
|
| if (get_current_item()) |
|
| if (handle_bindings(ch, get_current_item()->i_name)) |
|
| return 1; |
|
| break; |
|
| } |
} |
| |
|
| return 0; |
return 0; |
|
Line 982 static int handle_input(int ch)
|
Line 1202 static int handle_input(int ch)
|
| |
|
| static void curses_pre(void) |
static void curses_pre(void) |
| { |
{ |
| |
static int init = 0; |
| |
|
| |
if (!init) { |
| |
curses_init(); |
| |
init = 1; |
| |
} |
| |
|
| for (;;) { |
for (;;) { |
| int ch = getch(); |
int ch = getch(); |
| |
|
|
Line 993 static void curses_pre(void)
|
Line 1220 static void curses_pre(void)
|
| } |
} |
| } |
} |
| |
|
| static int curses_probe(void) |
|
| { |
|
| /* XXX? */ |
|
| return 1; |
|
| } |
|
| |
|
| static void print_module_help(void) |
static void print_module_help(void) |
| { |
{ |
| printf( |
printf( |
| "curses - Curses Output\n" \ |
"curses - Curses Output\n" \ |
| "\n" \ |
"\n" \ |
| " Interactive curses UI. Type '?' to get some help.\n" \ | " Interactive curses UI. Press '?' to see help.\n" \ |
| " Author: Thomas Graf <tgraf@suug.ch>\n" \ |
" Author: Thomas Graf <tgraf@suug.ch>\n" \ |
| "\n" \ |
"\n" \ |
| " Options:\n" \ |
" Options:\n" \ |
|
Line 1012 static void print_module_help(void)
|
Line 1233 static void print_module_help(void)
|
| " bgchar=CHAR Background character (default: '.')\n" \ |
" bgchar=CHAR Background character (default: '.')\n" \ |
| " nchar=CHAR Noise character (default: ':')\n" \ |
" nchar=CHAR Noise character (default: ':')\n" \ |
| " uchar=CHAR Unknown character (default: '?')\n" \ |
" uchar=CHAR Unknown character (default: '?')\n" \ |
| " height=NUM Height of graph (default: 6)\n" \ | " gheight=NUM Height of graph (default: 6)\n" \ |
| " xunit=UNIT X-Axis Unit (default: seconds)\n" \ | " gwidth=NUM Width of graph (default: 60)\n" \ |
| " yunit=UNIT Y-Axis Unit (default: dynamic)\n" \ | " ngraph=NUM Number of graphs (default: 1)\n" \ |
| " nocolors Do not use colors\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" \ |
" graph Show graphical stats by default\n" \ |
| " detail Show detailed stats by default\n"); | " details Show detailed stats 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(type, "fgchar") && value) |
| if (!strcasecmp(attrs->type, "fgchar") && attrs->value) | c_graph_cfg.gc_foreground = value[0]; |
| set_fg_char(attrs->value[0]); | else if (!strcasecmp(type, "bgchar") && value) |
| else if (!strcasecmp(attrs->type, "bgchar") && attrs->value) | c_graph_cfg.gc_background = value[0]; |
| set_bg_char(attrs->value[0]); | else if (!strcasecmp(type, "nchar") && value) |
| else if (!strcasecmp(attrs->type, "nchar") && attrs->value) | c_graph_cfg.gc_noise = value[0]; |
| set_noise_char(attrs->value[0]); | else if (!strcasecmp(type, "uchar") && value) |
| else if (!strcasecmp(attrs->type, "uchar") && attrs->value) | c_graph_cfg.gc_unknown = value[0]; |
| set_unk_char(attrs->value[0]); | else if (!strcasecmp(type, "gheight") && value) |
| else if (!strcasecmp(attrs->type, "xunit") && attrs->value) | c_graph_cfg.gc_height = strtol(value, NULL, 0); |
| set_x_unit(attrs->value, 1); | else if (!strcasecmp(type, "gwidth") && value) |
| else if (!strcasecmp(attrs->type, "yunit") && attrs->value) | c_graph_cfg.gc_width = strtol(value, NULL, 0); |
| set_y_unit(attrs->value); | else if (!strcasecmp(type, "ngraph")) { |
| else if (!strcasecmp(attrs->type, "height") && attrs->value) | c_ngraph = strtol(value, NULL, 0); |
| c_graph_height = strtol(attrs->value, NULL, 0); | c_show_graph = !!c_ngraph; |
| else if (!strcasecmp(attrs->type, "cnl")) | } else if (!strcasecmp(type, "details")) |
| c_combined_node_list = 1; | c_show_details = 1; |
| else if (!strcasecmp(attrs->type, "graph")) | else if (!strcasecmp(type, "nocolors")) |
| c_graphical_in_list = 1; | c_use_colors = 0; |
| else if (!strcasecmp(attrs->type, "detail")) | else if (!strcasecmp(type, "minlist") && value) |
| c_detailed_in_list = 1; | c_list_min = strtol(value, NULL, 0); |
| else if (!strcasecmp(attrs->type, "nocolors")) | else if (!strcasecmp(type, "help")) { |
| c_use_colors = 0; | print_module_help(); |
| else if (!strcasecmp(attrs->type, "nototal")) | exit(0); |
| 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; | |
| } |
} |
| } |
} |
| |
|
| static struct output_module curses_ops = { | static struct bmon_module curses_ops = { |
| .om_name = "curses", | .m_name = "curses", |
| .om_init = curses_init, | .m_flags = BMON_MODULE_DEFAULT, |
| .om_shutdown = curses_shutdown, | .m_shutdown = curses_shutdown, |
| .om_pre = curses_pre, | .m_pre = curses_pre, |
| .om_draw = curses_draw, | .m_do = curses_draw, |
| .om_set_opts = curses_set_opts, | .m_parse_opt = curses_parse_opt, |
| .om_probe = curses_probe, | |
| }; |
}; |
| |
|
| static void __init do_curses_init(void) |
static void __init do_curses_init(void) |
| { |
{ |
| register_output_module(&curses_ops); | output_register(&curses_ops); |
| } |
} |