Diff for /embedaddon/bmon/src/out_curses.c between versions 1.1 and 1.1.1.3

version 1.1, 2012/02/21 22:19:56 version 1.1.1.3, 2019/10/21 14:58:35
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, ...)static 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 __unused__;
   
    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);
           if (vasprintf(&str, fmt, args) < 0) {
                   fprintf(stderr, "vasprintf: Out of memory\n");
                   exit(ENOMEM);
           }
           va_end(args);
   
           col = (cols / 2) - (strlen(str) / 2);
           if (col > cols - 1)
                   col = cols - 1;
   
           move(row, col);
           addstr(str);
           move(row, 0);
   
           free(str);
   }
   
   static int curses_init(void)
   {
           if (!initscr()) {
                   fprintf(stderr, "Unable to initialize curses screen\n");
                   return -EOPNOTSUPP;
           }
   
         initialized = 1;          initialized = 1;
                   
         if (!has_colors())          if (!has_colors())
Line 95  static void curses_init(void) Line 209  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 220  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 230  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(rate_get_total(&a->a_rx_rate),
                return;                                   a->a_def->ad_unit,
                                    &rx_u, &rxprec);
         double tx = unit_value2str(rate_get_total(&a->a_tx_rate),
                                    a->a_def->ad_unit,
                                    &tx_u, &txprec);
   
        rx = cancel_down(attr_get_rx_rate(bytes), bytes->a_unit, &rx_u, &rxprec);        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",
        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);
         mvaddnstr(y+18, x+3, "r             Reset counter of element", -1);
 
         attroff(A_STANDOUT);
 
         row = y + HH;
 }  }
   
static void handle_child(item_t *intf, void *arg)static int lines_required_for_header(void)
 {  {
        node_t *node = arg;        return 1;
 }
   
        draw_item_list_entry(intf, node);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);
         apply_layout(LAYOUT_LIST);
 }  }
   
   static int lines_required_for_statusbar(void)
   {
           return 1;
   }
   
static void draw_item(node_t *node, item_t *intf)static void draw_statusbar(void)
 {  {
        if (!intf->i_name[0] || row >= (rows - 1) || (intf->i_flags & ITEM_FLAG_IS_CHILD))        static const char *help_text = "Press ? for help";
                return;        char s[27];
         time_t t = time(NULL);
   
        draw_item_list_entry(intf, node);        apply_layout(LAYOUT_STATUSBAR);
   
        if (!(intf->i_flags & ITEM_FLAG_FOLDED))        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)
 {
         apply_layout(LAYOUT_HEADER);
         int *line = arg;
 
         if (line_visible(*line)) {
                 NEXT_ROW();
                 attron(A_BOLD);
                 put_line("%s", g->g_hdr->gh_title);
 
                 attroff(A_BOLD);
                 mvaddch(row, LIST_COL_1, ACS_VLINE);
                 attron(A_BOLD);
                 put_line("%7s   %7s     %%",
                         g->g_hdr->gh_column[0],
                         g->g_hdr->gh_column[1]);
 
                 attroff(A_BOLD);
                 mvaddch(row, LIST_COL_2, ACS_VLINE);
                 attron(A_BOLD);
                 put_line("%7s   %7s     %%",
                         g->g_hdr->gh_column[2],
                         g->g_hdr->gh_column[3]);
         }          }
   
           (*line)++;
   
           group_foreach_element(g, draw_element, arg);
 }  }
   
static void draw_detailed(void)static void draw_element_list(void)
 {  {
        int start_pos, end_pos, i;        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 layout)
 {  {
        /*        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);
 
     memset(buf, 0, strlen(buf));
         for (i = (g->g_cfg.gc_height - 1); i >= 0; i--) {
                 move(++row, ncol);
         sprintf(buf, "%'8.2f ", tbl->gt_scale[i]);
         addstr(buf);
         apply_layout(layout);
         put_line("%s", tbl->gt_table + (i * graph_row_size(&g->g_cfg)));
         apply_layout(LAYOUT_LIST);
         }          }
   
        for (i = 0; i < len; i++) {        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, LAYOUT_RX_GRAPH);
   
        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, LAYOUT_TX_GRAPH);
        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, &current_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)static void __reset_attr_counter(struct element *e, struct attr *a, void *arg)
 {  {
        item_t *intf = get_current_item();        attr_reset_counter(a);
        node_t *node = get_current_node();}
        int fix = 0; 
   
        if (NULL == intf || NULL == node)static void reset_counters(void)
                return;{
        element_foreach_attr(current_element, __reset_attr_counter, NULL);
        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)
Line 835  static int handle_input(int ch) Line 1100  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 1116  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 1129  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 = print_help ? 0 : 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:                case 'r':
                        if (get_current_item())                        reset_counters();
                                if (handle_bindings(ch, get_current_item()->i_name))                        return 1;
                                        return 1; 
                        break; 
         }          }
   
         return 0;          return 0;
Line 982  static int handle_input(int ch) Line 1225  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 1243  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 1256  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" \
         "    info           Show additional info screen by default\n" \
         "    minlist=INT    Minimum item list length\n");
 }  }
   
static void curses_set_opts(tv_t *attrs)static void curses_parse_opt(const char *type, const char *value)
 {  {
        while (attrs) {        if (!strcasecmp(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") && value) {
                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, "info"))
                        c_graphical_in_list = 1;                c_show_info = 1;
                else if (!strcasecmp(attrs->type, "detail"))        else if (!strcasecmp(type, "nocolors"))
                        c_detailed_in_list = 1;                c_use_colors = 0;
                else if (!strcasecmp(attrs->type, "nocolors"))        else if (!strcasecmp(type, "minlist") && value)
                        c_use_colors = 0;                c_list_min = strtol(value, NULL, 0);
                else if (!strcasecmp(attrs->type, "nototal"))        else if (!strcasecmp(type, "help")) {
                        c_nototal = 1;                print_module_help();
                else if (!strcasecmp(attrs->type, "nosource"))                exit(0);
                        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);
 }  }

Removed from v.1.1  
changed lines
  Added in v.1.1.1.3


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>