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); |
} |
} |