Annotation of embedaddon/bmon/src/out_curses.c, revision 1.1.1.3
1.1 misho 1: /*
2: * out_curses.c Curses Output
3: *
1.1.1.2 misho 4: * Copyright (c) 2001-2013 Thomas Graf <tgraf@suug.ch>
5: * Copyright (c) 2013 Red Hat, Inc.
1.1 misho 6: *
7: * Permission is hereby granted, free of charge, to any person obtaining a
8: * copy of this software and associated documentation files (the "Software"),
9: * to deal in the Software without restriction, including without limitation
10: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11: * and/or sell copies of the Software, and to permit persons to whom the
12: * Software is furnished to do so, subject to the following conditions:
13: *
14: * The above copyright notice and this permission notice shall be included
15: * in all copies or substantial portions of the Software.
16: *
17: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18: * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23: * DEALINGS IN THE SOFTWARE.
24: */
25:
26: #include <bmon/bmon.h>
27: #include <bmon/conf.h>
1.1.1.2 misho 28: #include <bmon/attr.h>
29: #include <bmon/element.h>
30: #include <bmon/element_cfg.h>
1.1 misho 31: #include <bmon/input.h>
1.1.1.2 misho 32: #include <bmon/history.h>
33: #include <bmon/graph.h>
1.1 misho 34: #include <bmon/output.h>
35: #include <bmon/utils.h>
36:
1.1.1.2 misho 37: enum {
38: GRAPH_DISPLAY_SIDE_BY_SIDE = 1,
39: GRAPH_DISPLAY_STANDARD = 2,
40: };
41:
42: enum {
43: KEY_TOGGLE_LIST = 'l',
44: KEY_TOGGLE_GRAPH = 'g',
45: KEY_TOGGLE_DETAILS = 'd',
46: KEY_TOGGLE_INFO = 'i',
47: KEY_COLLECT_HISTORY = 'h',
48: };
49:
50: #define DETAILS_COLS 40
51:
52: #define LIST_COL_1 31
53: #define LIST_COL_2 55
54:
55: /* Set to element_current() before drawing */
56: static struct element *current_element;
57:
58: static struct attr *current_attr;
59:
60: /* Length of list to draw, updated in draw_content() */
61: static int list_length;
62:
63: static int list_req;
64:
65: /* Number of graphs to draw (may be < c_ngraph) */
66: static int ngraph;
67:
68: /*
69: * Offset in number of lines within the the element list of the currently
70: * selected element. Updated while summing up required lines.
71: */
72: static unsigned int selection_offset;
73:
74: /*
75: * Offset in number of lines of the first element to be drawn. Updated
76: * in draw_content()
77: */
78: static int offset;
79:
80: /*
81: * Offset to the first graph to draw in number of attributes with graphs.
82: */
83: static unsigned int graph_offset;
84:
85: static int graph_display = GRAPH_DISPLAY_STANDARD;
86:
87: /*
88: * Number of detail columns
89: */
90: static int detail_cols;
91: static int info_cols;
92:
1.1 misho 93: static int initialized;
1.1.1.2 misho 94: static int print_help;
95: static int quit_mode;
96: static int help_page;
97:
98: /* Current row */
1.1 misho 99: static int row;
1.1.1.2 misho 100:
101: /* Number of rows */
1.1 misho 102: static int rows;
1.1.1.2 misho 103:
104: /* Number of columns */
1.1 misho 105: static int cols;
106:
1.1.1.2 misho 107: static int c_show_graph = 1;
108: static int c_ngraph = 1;
1.1 misho 109: static int c_use_colors = 1;
1.1.1.2 misho 110: static int c_show_details = 0;
111: static int c_show_list = 1;
112: static int c_show_info = 0;
113: static int c_list_min = 6;
114:
115: static struct graph_cfg c_graph_cfg = {
116: .gc_width = 60,
117: .gc_height = 6,
118: .gc_foreground = '|',
119: .gc_background = '.',
120: .gc_noise = ':',
121: .gc_unknown = '?',
122: };
123:
124: #define NEXT_ROW() \
125: do { \
126: row++; \
127: if (row >= rows - 1) \
128: return; \
129: move(row, 0); \
130: } while(0)
131:
132: static void apply_layout(int layout)
133: {
134: if (c_use_colors)
135: attrset(COLOR_PAIR(layout) | cfg_layout[layout].l_attr);
136: else
137: attrset(cfg_layout[layout].l_attr);
138: }
139:
1.1.1.3 ! misho 140: static char *float2str(double value, int width, int prec, char *buf, size_t len)
1.1.1.2 misho 141: {
142: snprintf(buf, len, "%'*.*f", width, value == 0.0f ? 0 : prec, value);
1.1 misho 143:
1.1.1.2 misho 144: return buf;
1.1 misho 145: }
146:
1.1.1.2 misho 147: static void put_line(const char *fmt, ...)
1.1 misho 148: {
1.1.1.2 misho 149: va_list args;
150: char buf[2048];
1.1.1.3 ! misho 151: int x, y __unused__;
1.1 misho 152:
1.1.1.2 misho 153: memset(buf, 0, sizeof(buf));
154: getyx(stdscr, y, x);
1.1 misho 155:
1.1.1.2 misho 156: va_start(args, fmt);
157: vsnprintf(buf, sizeof(buf), fmt, args);
158: va_end(args);
1.1 misho 159:
1.1.1.2 misho 160: if (strlen(buf) > cols-x)
161: buf[cols - x] = '\0';
162: else
163: memset(&buf[strlen(buf)], ' ', cols - strlen(buf)-x);
1.1 misho 164:
1.1.1.2 misho 165: addstr(buf);
1.1 misho 166: }
167:
1.1.1.2 misho 168: static void center_text(const char *fmt, ...)
1.1 misho 169: {
1.1.1.2 misho 170: va_list args;
171: char *str;
172: unsigned int col;
173:
174: va_start(args, fmt);
1.1.1.3 ! misho 175: if (vasprintf(&str, fmt, args) < 0) {
! 176: fprintf(stderr, "vasprintf: Out of memory\n");
! 177: exit(ENOMEM);
! 178: }
1.1.1.2 misho 179: va_end(args);
180:
181: col = (cols / 2) - (strlen(str) / 2);
182: if (col > cols - 1)
183: col = cols - 1;
184:
185: move(row, col);
186: addstr(str);
187: move(row, 0);
188:
189: free(str);
190: }
191:
192: static int curses_init(void)
193: {
194: if (!initscr()) {
195: fprintf(stderr, "Unable to initialize curses screen\n");
196: return -EOPNOTSUPP;
197: }
1.1 misho 198:
199: initialized = 1;
200:
201: if (!has_colors())
202: c_use_colors = 0;
203:
204: if (c_use_colors) {
205: int i;
206:
207: start_color();
208:
209: #if defined HAVE_USE_DEFAULT_COLORS
210: use_default_colors();
211: #endif
1.1.1.2 misho 212: for (i = 1; i < LAYOUT_MAX+1; i++)
213: init_pair(i, cfg_layout[i].l_fg, cfg_layout[i].l_bg);
1.1 misho 214: }
215:
216: keypad(stdscr, TRUE);
217: nonl();
218: cbreak();
219: noecho();
220: nodelay(stdscr, TRUE); /* getch etc. must be non-blocking */
221: clear();
222: curs_set(0);
1.1.1.2 misho 223:
224: return 0;
1.1 misho 225: }
226:
227: static void curses_shutdown(void)
228: {
229: if (initialized)
230: endwin();
231: }
232:
1.1.1.2 misho 233: struct detail_arg
1.1 misho 234: {
1.1.1.2 misho 235: int nattr;
236: };
1.1 misho 237:
1.1.1.2 misho 238: static void draw_attr_detail(struct element *e, struct attr *a, void *arg)
239: {
240: char *rx_u, *tx_u, buf1[32], buf2[32];
241: int rxprec, txprec, ncol;
242: struct detail_arg *da = arg;
243:
1.1.1.3 ! misho 244: double rx = unit_value2str(rate_get_total(&a->a_rx_rate),
1.1.1.2 misho 245: a->a_def->ad_unit,
246: &rx_u, &rxprec);
1.1.1.3 ! misho 247: double tx = unit_value2str(rate_get_total(&a->a_tx_rate),
1.1.1.2 misho 248: a->a_def->ad_unit,
249: &tx_u, &txprec);
250:
251: if (da->nattr >= detail_cols) {
252: NEXT_ROW();
253: da->nattr = 0;
254: }
255:
256: ncol = (da->nattr * DETAILS_COLS) - 1;
257: move(row, ncol);
258: if (ncol > 0)
259: addch(ACS_VLINE);
1.1 misho 260:
1.1.1.3 ! misho 261: put_line(" %-14.14s %8s%-3s %8s%-3s",
1.1.1.2 misho 262: a->a_def->ad_description,
263: (a->a_flags & ATTR_RX_ENABLED) ?
264: float2str(rx, 8, rxprec, buf1, sizeof(buf1)) : "-", rx_u,
265: (a->a_flags & ATTR_TX_ENABLED) ?
266: float2str(tx, 8, txprec, buf2, sizeof(buf2)) : "-", tx_u);
1.1 misho 267:
1.1.1.2 misho 268: da->nattr++;
269: }
1.1 misho 270:
1.1.1.2 misho 271: static void draw_details(void)
272: {
273: int i;
274: struct detail_arg arg = {
275: .nattr = 0,
276: };
1.1 misho 277:
1.1.1.2 misho 278: if (!current_element->e_nattrs)
279: return;
1.1 misho 280:
1.1.1.2 misho 281: for (i = 1; i < detail_cols; i++)
282: mvaddch(row, (i * DETAILS_COLS) - 1, ACS_TTEE);
1.1 misho 283:
1.1.1.2 misho 284: NEXT_ROW();
285: put_line("");
286: for (i = 0; i < detail_cols; i++) {
287: if (i > 0)
288: mvaddch(row, (i * DETAILS_COLS) - 1, ACS_VLINE);
289: move(row, (i * DETAILS_COLS) + 22);
290: put_line("RX TX");
1.1 misho 291: }
292:
1.1.1.2 misho 293: NEXT_ROW();
294: element_foreach_attr(current_element, draw_attr_detail, &arg);
295:
296: /*
297: * If the last row was incomplete, not all vlines have been drawn.
298: * draw them here
299: */
300: for (i = 1; i < detail_cols; i++)
301: mvaddch(row, (i * DETAILS_COLS - 1), ACS_VLINE);
302: }
303:
304: static void print_message(const char *text)
305: {
306: int i, y = (rows/2) - 2;
307: int len = strlen(text);
308: int x = (cols/2) - (len / 2);
309:
310: attrset(A_STANDOUT);
311: mvaddch(y - 2, x - 1, ACS_ULCORNER);
312: mvaddch(y + 2, x - 1, ACS_LLCORNER);
313: mvaddch(y - 2, x + len, ACS_URCORNER);
314: mvaddch(y + 2, x + len, ACS_LRCORNER);
315:
316: for (i = 0; i < 3; i++) {
317: mvaddch(y - 1 + i, x + len, ACS_VLINE);
318: mvaddch(y - 1 + i, x - 1 ,ACS_VLINE);
1.1 misho 319: }
320:
1.1.1.2 misho 321: for (i = 0; i < len; i++) {
322: mvaddch(y - 2, x + i, ACS_HLINE);
323: mvaddch(y - 1, x + i, ' ');
324: mvaddch(y + 1, x + i, ' ');
325: mvaddch(y + 2, x + i, ACS_HLINE);
1.1 misho 326: }
327:
1.1.1.2 misho 328: mvaddstr(y, x, text);
329: attroff(A_STANDOUT);
330:
331: row = y + 2;
332: }
333:
334: static void draw_help(void)
335: {
336: #define HW 46
337: #define HH 19
338: int i, y = (rows/2) - (HH/2);
339: int x = (cols/2) - (HW/2);
340: char pad[HW+1];
341:
342: memset(pad, ' ', sizeof(pad));
343: pad[sizeof(pad) - 1] = '\0';
344:
345: attron(A_STANDOUT);
346:
347: for (i = 0; i < HH; i++)
348: mvaddnstr(y + i, x, pad, -1);
349:
350: mvaddch(y - 1, x - 1, ACS_ULCORNER);
351: mvaddch(y + HH, x - 1, ACS_LLCORNER);
352:
353: mvaddch(y - 1, x + HW, ACS_URCORNER);
354: mvaddch(y + HH, x + HW, ACS_LRCORNER);
1.1 misho 355:
1.1.1.2 misho 356: for (i = 0; i < HH; i++) {
357: mvaddch(y + i, x - 1, ACS_VLINE);
358: mvaddch(y + i, x + HW, ACS_VLINE);
1.1 misho 359: }
360:
1.1.1.2 misho 361: for (i = 0; i < HW; i++) {
362: mvaddch(y - 1, x + i, ACS_HLINE);
363: mvaddch(y + HH, x + i, ACS_HLINE);
1.1 misho 364: }
365:
1.1.1.2 misho 366: attron(A_BOLD);
367: mvaddnstr(y- 1, x+15, "QUICK REFERENCE", -1);
368: attron(A_UNDERLINE);
369: mvaddnstr(y+ 0, x+1, "Navigation", -1);
370: attroff(A_BOLD | A_UNDERLINE);
371:
372: mvaddnstr(y+ 1, x+3, "Up, Down Previous/Next element", -1);
373: mvaddnstr(y+ 2, x+3, "PgUp, PgDown Scroll up/down entire page", -1);
374: mvaddnstr(y+ 3, x+3, "Left, Right Previous/Next attribute", -1);
375: mvaddnstr(y+ 4, x+3, "[, ] Previous/Next group", -1);
376: mvaddnstr(y+ 5, x+3, "? Toggle quick reference", -1);
377: mvaddnstr(y+ 6, x+3, "q Quit bmon", -1);
378:
379: attron(A_BOLD | A_UNDERLINE);
380: mvaddnstr(y+ 8, x+1, "Display Settings", -1);
381: attroff(A_BOLD | A_UNDERLINE);
382:
383: mvaddnstr(y+ 9, x+3, "d Toggle detailed statistics", -1);
384: mvaddnstr(y+10, x+3, "l Toggle element list", -1);
385: mvaddnstr(y+11, x+3, "i Toggle additional info", -1);
386:
387: attron(A_BOLD | A_UNDERLINE);
388: mvaddnstr(y+13, x+1, "Graph Settings", -1);
389: attroff(A_BOLD | A_UNDERLINE);
390:
391: mvaddnstr(y+14, x+3, "g Toggle graphical statistics", -1);
392: mvaddnstr(y+15, x+3, "H Start recording history data", -1);
393: mvaddnstr(y+16, x+3, "TAB Switch time unit of graph", -1);
394: mvaddnstr(y+17, x+3, "<, > Change number of graphs", -1);
1.1.1.3 ! misho 395: mvaddnstr(y+18, x+3, "r Reset counter of element", -1);
1.1.1.2 misho 396:
397: attroff(A_STANDOUT);
398:
399: row = y + HH;
400: }
401:
402: static int lines_required_for_header(void)
403: {
404: return 1;
1.1 misho 405: }
406:
1.1.1.2 misho 407: static void draw_header(void)
1.1 misho 408: {
1.1.1.2 misho 409: apply_layout(LAYOUT_STATUSBAR);
1.1 misho 410:
1.1.1.2 misho 411: if (current_element)
412: put_line(" %s %c%s%c",
413: current_element->e_name,
414: current_element->e_description ? '(' : ' ',
415: current_element->e_description ? : "",
416: current_element->e_description ? ')' : ' ');
417: else
418: put_line("");
419:
420: move(row, COLS - strlen(PACKAGE_STRING) - 1);
421: put_line("%s", PACKAGE_STRING);
422: move(row, 0);
1.1.1.3 ! misho 423: apply_layout(LAYOUT_LIST);
1.1 misho 424: }
425:
1.1.1.2 misho 426: static int lines_required_for_statusbar(void)
427: {
428: return 1;
429: }
1.1 misho 430:
1.1.1.2 misho 431: static void draw_statusbar(void)
1.1 misho 432: {
1.1.1.2 misho 433: static const char *help_text = "Press ? for help";
434: char s[27];
1.1.1.3 ! misho 435: time_t t = time(NULL);
1.1 misho 436:
1.1.1.2 misho 437: apply_layout(LAYOUT_STATUSBAR);
1.1 misho 438:
1.1.1.2 misho 439: asctime_r(localtime(&t), s);
440: s[strlen(s) - 1] = '\0';
441:
442: row = rows-1;
443: move(row, 0);
444: put_line(" %s", s);
445:
446: move(row, COLS - strlen(help_text) - 1);
447: put_line("%s", help_text);
448:
449: move(row, 0);
1.1 misho 450: }
451:
1.1.1.2 misho 452: static void count_attr_graph(struct element *g, struct attr *a, void *arg)
453: {
454: if (a == current_attr)
455: graph_offset = ngraph;
456:
457: ngraph++;
458: }
1.1 misho 459:
1.1.1.2 misho 460: static int lines_required_for_graph(void)
1.1 misho 461: {
1.1.1.2 misho 462: int lines = 0;
1.1 misho 463:
1.1.1.2 misho 464: ngraph = 0;
1.1 misho 465:
1.1.1.2 misho 466: if (c_show_graph && current_element) {
467: graph_display = GRAPH_DISPLAY_STANDARD;
1.1 misho 468:
1.1.1.2 misho 469: element_foreach_attr(current_element, count_attr_graph, NULL);
1.1 misho 470:
1.1.1.2 misho 471: if (ngraph > c_ngraph)
472: ngraph = c_ngraph;
1.1 misho 473:
1.1.1.2 misho 474: /* check if we have room to draw graphs on the same level */
475: if (cols > (2 * (c_graph_cfg.gc_width + 10)))
476: graph_display = GRAPH_DISPLAY_SIDE_BY_SIDE;
1.1 misho 477:
1.1.1.2 misho 478: /* +2 = header + time axis */
479: lines = ngraph * (graph_display * (c_graph_cfg.gc_height + 2));
480: }
1.1 misho 481:
1.1.1.2 misho 482: return lines + 1;
483: }
1.1 misho 484:
1.1.1.2 misho 485: static int lines_required_for_details(void)
486: {
487: int lines = 1;
1.1 misho 488:
1.1.1.2 misho 489: if (c_show_details && current_element) {
490: lines++; /* header */
1.1 misho 491:
1.1.1.2 misho 492: detail_cols = cols / DETAILS_COLS;
493:
494: if (!detail_cols)
495: detail_cols = 1;
496:
497: lines += (current_element->e_nattrs / detail_cols);
498: if (current_element->e_nattrs % detail_cols)
499: lines++;
1.1 misho 500: }
501:
1.1.1.2 misho 502: return lines;
1.1 misho 503: }
504:
1.1.1.2 misho 505: static void count_element_lines(struct element_group *g, struct element *e,
506: void *arg)
1.1 misho 507: {
1.1.1.2 misho 508: int *lines = arg;
1.1 misho 509:
1.1.1.2 misho 510: if (e == current_element)
511: selection_offset = *lines;
512:
513: (*lines)++;
1.1 misho 514: }
515:
1.1.1.2 misho 516: static void count_group_lines(struct element_group *g, void *arg)
1.1 misho 517: {
1.1.1.2 misho 518: int *lines = arg;
519:
520: /* group title */
521: (*lines)++;
522:
523: group_foreach_element(g, &count_element_lines, arg);
1.1 misho 524: }
525:
1.1.1.2 misho 526: static int lines_required_for_list(void)
1.1 misho 527: {
1.1.1.2 misho 528: int lines = 0;
1.1 misho 529:
1.1.1.2 misho 530: if (c_show_list)
531: group_foreach(&count_group_lines, &lines);
532: else
533: lines = 1;
1.1 misho 534:
1.1.1.2 misho 535: return lines;
536: }
1.1 misho 537:
1.1.1.2 misho 538: static inline int line_visible(int line)
539: {
540: return line >= offset && line < (offset + list_length);
541: }
1.1 misho 542:
1.1.1.2 misho 543: static void draw_attr(double rate1, int prec1, char *unit1,
544: double rate2, int prec2, char *unit2,
545: float usage, int ncol)
546: {
547: char buf[32];
1.1 misho 548:
1.1.1.2 misho 549: move(row, ncol);
550: addch(ACS_VLINE);
551: printw("%7s%-3s",
552: float2str(rate1, 7, prec1, buf, sizeof(buf)), unit1);
1.1 misho 553:
1.1.1.2 misho 554: printw("%7s%-3s",
555: float2str(rate2, 7, prec2, buf, sizeof(buf)), unit2);
1.1 misho 556:
1.1.1.2 misho 557: if (usage != FLT_MAX)
558: printw("%2.0f%%", usage);
559: else
560: printw("%3s", "");
1.1 misho 561: }
562:
1.1.1.2 misho 563: static void draw_element(struct element_group *g, struct element *e,
564: void *arg)
1.1 misho 565: {
1.1.1.2 misho 566: int *line = arg;
567:
568: apply_layout(LAYOUT_LIST);
569:
570: if (line_visible(*line)) {
571: char *rxu1 = "", *txu1 = "", *rxu2 = "", *txu2 = "";
572: double rx1 = 0.0f, tx1 = 0.0f, rx2 = 0.0f, tx2 = 0.0f;
573: char pad[IFNAMSIZ + 32];
574: int rx1prec = 0, tx1prec = 0, rx2prec = 0, tx2prec = 0;
575: struct attr *a;
1.1 misho 576:
1.1.1.2 misho 577: NEXT_ROW();
1.1 misho 578:
1.1.1.2 misho 579: if (e->e_key_attr[GT_MAJOR] &&
580: (a = attr_lookup(e, e->e_key_attr[GT_MAJOR]->ad_id)))
581: attr_rate2float(a, &rx1, &rxu1, &rx1prec,
582: &tx1, &txu1, &tx1prec);
1.1 misho 583:
1.1.1.2 misho 584: if (e->e_key_attr[GT_MINOR] &&
585: (a = attr_lookup(e, e->e_key_attr[GT_MINOR]->ad_id)))
586: attr_rate2float(a, &rx2, &rxu2, &rx2prec,
587: &tx2, &txu2, &tx2prec);
588:
589: memset(pad, 0, sizeof(pad));
590: memset(pad, ' ', e->e_level < 6 ? e->e_level * 2 : 12);
591:
592: strncat(pad, e->e_name, sizeof(pad) - strlen(pad) - 1);
593:
594: if (e->e_description) {
595: strncat(pad, " (", sizeof(pad) - strlen(pad) - 1);
596: strncat(pad, e->e_description, sizeof(pad) - strlen(pad) - 1);
597: strncat(pad, ")", sizeof(pad) - strlen(pad) - 1);
1.1 misho 598: }
1.1.1.2 misho 599:
600: if (*line == offset) {
601: attron(A_BOLD);
602: addch(ACS_UARROW);
603: attroff(A_BOLD);
604: addch(' ');
605: } else if (e == current_element) {
606: apply_layout(LAYOUT_SELECTED);
607: addch(' ');
608: attron(A_BOLD);
609: addch(ACS_RARROW);
610: attroff(A_BOLD);
611: apply_layout(LAYOUT_LIST);
612: } else if (*line == offset + list_length - 1 &&
613: *line < (list_req - 1)) {
614: attron(A_BOLD);
615: addch(ACS_DARROW);
616: attroff(A_BOLD);
617: addch(' ');
618: } else
619: printw(" ");
620:
621: put_line("%-30.30s", pad);
622:
623: draw_attr(rx1, rx1prec, rxu1, rx2, rx2prec, rxu2,
624: e->e_rx_usage, LIST_COL_1);
1.1 misho 625:
1.1.1.2 misho 626: draw_attr(tx1, tx1prec, txu1, tx2, tx2prec, txu2,
627: e->e_tx_usage, LIST_COL_2);
628:
629: }
630:
631: (*line)++;
1.1 misho 632: }
633:
1.1.1.2 misho 634: static void draw_group(struct element_group *g, void *arg)
1.1 misho 635: {
1.1.1.3 ! misho 636: apply_layout(LAYOUT_HEADER);
1.1.1.2 misho 637: int *line = arg;
1.1 misho 638:
1.1.1.2 misho 639: if (line_visible(*line)) {
640: NEXT_ROW();
641: attron(A_BOLD);
642: put_line("%s", g->g_hdr->gh_title);
1.1 misho 643:
1.1.1.2 misho 644: attroff(A_BOLD);
645: mvaddch(row, LIST_COL_1, ACS_VLINE);
646: attron(A_BOLD);
647: put_line("%7s %7s %%",
648: g->g_hdr->gh_column[0],
649: g->g_hdr->gh_column[1]);
1.1 misho 650:
1.1.1.2 misho 651: attroff(A_BOLD);
652: mvaddch(row, LIST_COL_2, ACS_VLINE);
653: attron(A_BOLD);
654: put_line("%7s %7s %%",
655: g->g_hdr->gh_column[2],
656: g->g_hdr->gh_column[3]);
1.1 misho 657: }
1.1.1.2 misho 658:
659: (*line)++;
660:
661: group_foreach_element(g, draw_element, arg);
1.1 misho 662: }
663:
1.1.1.2 misho 664: static void draw_element_list(void)
1.1 misho 665: {
1.1.1.2 misho 666: int line = 0;
1.1 misho 667:
1.1.1.2 misho 668: group_foreach(draw_group, &line);
669: }
1.1 misho 670:
1.1.1.2 misho 671: static inline int attr_visible(int nattr)
672: {
673: return nattr >= graph_offset && nattr < (graph_offset + ngraph);
674: }
1.1 misho 675:
1.1.1.2 misho 676: static void draw_graph_centered(struct graph *g, int row, int ncol,
677: const char *text)
678: {
679: int hcenter = (g->g_cfg.gc_width / 2) - (strlen(text) / 2) + 8;
680:
681: if (hcenter < 9)
682: hcenter = 9;
683:
684: mvprintw(row, ncol + hcenter, "%.*s", g->g_cfg.gc_width, text);
1.1 misho 685: }
686:
1.1.1.2 misho 687: static void draw_table(struct graph *g, struct graph_table *tbl,
688: struct attr *a, struct history *h,
1.1.1.3 ! misho 689: const char *hdr, int ncol, int layout)
1.1 misho 690: {
1.1.1.2 misho 691: int i, save_row;
692: char buf[32];
1.1 misho 693:
1.1.1.2 misho 694: if (!tbl->gt_table) {
695: for (i = g->g_cfg.gc_height; i >= 0; i--) {
696: move(++row, ncol);
697: put_line("");
698: }
699: return;
700: }
1.1 misho 701:
1.1.1.2 misho 702: move(++row, ncol);
703: put_line("%8s", tbl->gt_y_unit ? : "");
704:
705: snprintf(buf, sizeof(buf), "(%s %s/%s)",
706: hdr, a->a_def->ad_description,
707: h ? h->h_definition->hd_name : "?");
708:
709: draw_graph_centered(g, row, ncol, buf);
710:
711: //move(row, ncol + g->g_cfg.gc_width - 3);
712: //put_line("[err %.2f%%]", rtiming.rt_variance.v_error);
713:
1.1.1.3 ! misho 714: memset(buf, 0, strlen(buf));
1.1.1.2 misho 715: for (i = (g->g_cfg.gc_height - 1); i >= 0; i--) {
716: move(++row, ncol);
1.1.1.3 ! misho 717: sprintf(buf, "%'8.2f ", tbl->gt_scale[i]);
! 718: addstr(buf);
! 719: apply_layout(layout);
! 720: put_line("%s", tbl->gt_table + (i * graph_row_size(&g->g_cfg)));
! 721: apply_layout(LAYOUT_LIST);
1.1 misho 722: }
723:
1.1.1.2 misho 724: move(++row, ncol);
725: put_line(" 1");
726:
727: for (i = 1; i <= g->g_cfg.gc_width; i++) {
728: if (i % 5 == 0) {
729: move(row, ncol + i + 7);
730: printw("%2d", i);
731: }
1.1 misho 732: }
733:
1.1.1.2 misho 734: if (!h) {
735: const char *t1 = " No history data available. ";
736: const char *t2 = " Press h to start collecting history. ";
737: int vcenter = g->g_cfg.gc_height / 2;
738:
739: save_row = row;
740: draw_graph_centered(g, save_row - vcenter - 1, ncol, t1);
741: draw_graph_centered(g, save_row - vcenter, ncol, t2);
742: row = save_row;
743: }
1.1 misho 744: }
745:
1.1.1.2 misho 746: static void draw_history_graph(struct attr *a, struct history *h)
1.1 misho 747: {
1.1.1.2 misho 748: struct graph *g;
749: int ncol = 0, save_row;
1.1 misho 750:
1.1.1.2 misho 751: g = graph_alloc(h, &c_graph_cfg);
752: graph_refill(g, h);
1.1 misho 753:
1.1.1.2 misho 754: save_row = row;
1.1.1.3 ! misho 755: draw_table(g, &g->g_rx, a, h, "RX", ncol, LAYOUT_RX_GRAPH);
1.1 misho 756:
1.1.1.2 misho 757: if (graph_display == GRAPH_DISPLAY_SIDE_BY_SIDE) {
758: ncol = cols / 2;
759: row = save_row;
760: }
1.1 misho 761:
1.1.1.3 ! misho 762: draw_table(g, &g->g_tx, a, h, "TX", ncol, LAYOUT_TX_GRAPH);
1.1 misho 763:
1.1.1.2 misho 764: graph_free(g);
765: }
1.1 misho 766:
1.1.1.2 misho 767: static void draw_attr_graph(struct element *e, struct attr *a, void *arg)
768: {
769: int *nattr = arg;
1.1 misho 770:
1.1.1.2 misho 771: if (attr_visible(*nattr)) {
772: struct history_def *sel;
773: struct history *h;
1.1 misho 774:
1.1.1.2 misho 775: sel = history_current();
776: c_graph_cfg.gc_unit = a->a_def->ad_unit;
1.1 misho 777:
1.1.1.2 misho 778: list_for_each_entry(h, &a->a_history_list, h_list) {
779: if (h->h_definition != sel)
780: continue;
1.1 misho 781:
1.1.1.2 misho 782: draw_history_graph(a, h);
783: goto out;
784: }
1.1 misho 785:
1.1.1.2 misho 786: draw_history_graph(a, NULL);
787: }
788:
789: out:
790: (*nattr)++;
1.1 misho 791: }
792:
1.1.1.2 misho 793: static void draw_graph(void)
1.1 misho 794: {
1.1.1.2 misho 795: int nattr = 0;
1.1 misho 796:
1.1.1.2 misho 797: element_foreach_attr(current_element, &draw_attr_graph, &nattr);
798: }
1.1 misho 799:
1.1.1.2 misho 800: static int lines_required_for_info(void)
801: {
802: int lines = 1;
1.1 misho 803:
1.1.1.2 misho 804: if (c_show_info) {
805: info_cols = cols / DETAILS_COLS;
1.1 misho 806:
1.1.1.2 misho 807: if (!info_cols)
808: info_cols = 1;
1.1 misho 809:
1.1.1.2 misho 810: lines += (current_element->e_ninfo / info_cols);
811: if (current_element->e_ninfo % info_cols)
812: lines++;
1.1 misho 813: }
814:
1.1.1.2 misho 815: return lines;
816: }
817:
818: static void __draw_info(struct element *e, struct info *info, int *ninfo)
819: {
820: int ncol;
821:
822: ncol = ((*ninfo) * DETAILS_COLS) - 1;
823: move(row, ncol);
824: if (ncol > 0)
825: addch(ACS_VLINE);
826:
827: put_line(" %-14.14s %22.22s", info->i_name, info->i_value);
828:
829: if (++(*ninfo) >= info_cols) {
830: NEXT_ROW();
831: *ninfo = 0;
1.1 misho 832: }
1.1.1.2 misho 833: }
1.1 misho 834:
1.1.1.2 misho 835: static void draw_info(void)
836: {
837: struct info *info;
838: int i, ninfo = 0;
1.1 misho 839:
1.1.1.2 misho 840: if (!current_element->e_ninfo)
841: return;
1.1 misho 842:
1.1.1.2 misho 843: for (i = 1; i < detail_cols; i++)
844: mvaddch(row, (i * DETAILS_COLS) - 1,
845: c_show_details ? ACS_PLUS : ACS_TTEE);
846:
847: NEXT_ROW();
848: list_for_each_entry(info, ¤t_element->e_info_list, i_list)
849: __draw_info(current_element, info, &ninfo);
1.1 misho 850:
1.1.1.2 misho 851: /*
852: * If the last row was incomplete, not all vlines have been drawn.
853: * draw them here
854: */
855: for (i = 1; i < info_cols; i++)
856: mvaddch(row, (i * DETAILS_COLS - 1), ACS_VLINE);
1.1 misho 857: }
858:
1.1.1.2 misho 859: static void draw_content(void)
1.1 misho 860: {
1.1.1.2 misho 861: int graph_req, details_req, lines_available, total_req;
862: int info_req, empty_lines;
863: int disable_graph = 0, disable_details = 0, disable_info = 0;
1.1 misho 864:
1.1.1.2 misho 865: if (!current_element)
1.1 misho 866: return;
867:
1.1.1.2 misho 868: /*
869: * Reset selection offset. Will be set in lines_required_for_list().
870: */
871: selection_offset = 0;
872: offset = 0;
1.1 misho 873:
1.1.1.2 misho 874: /* Reset graph offset, will be set in lines_required_for_graph() */
875: graph_offset = 0;
1.1 misho 876:
1.1.1.2 misho 877: lines_available = rows - lines_required_for_statusbar()
878: - lines_required_for_header();
1.1 misho 879:
1.1.1.2 misho 880: list_req = lines_required_for_list();
881: graph_req = lines_required_for_graph();
882: details_req = lines_required_for_details();
883: info_req = lines_required_for_info();
884:
885: total_req = list_req + graph_req + details_req + info_req;
886:
887: if (total_req <= lines_available) {
888: /*
889: * Enough lines available for all data to displayed, all
890: * is good. Display the full list.
891: */
892: list_length = list_req;
893: goto draw;
1.1 misho 894: }
895:
896: /*
1.1.1.2 misho 897: * Not enough lines available for full list and all details
898: * requested...
1.1 misho 899: */
900:
1.1.1.2 misho 901: if (c_show_list) {
902: /*
903: * ... try shortening the list first.
904: */
905: list_length = lines_available - (total_req - list_req);
906: if (list_length >= c_list_min)
907: goto draw;
908: }
909:
910: if (c_show_info) {
911: /* try disabling info */
912: list_length = lines_available - (total_req - info_req + 1);
913: if (list_length >= c_list_min) {
914: disable_info = 1;
915: goto draw;
916: }
917: }
918:
919: if (c_show_details) {
920: /* ... try disabling details */
921: list_length = lines_available - (total_req - details_req + 1);
922: if (list_length >= c_list_min) {
923: disable_details = 1;
924: goto draw;
925: }
926: }
1.1 misho 927:
1.1.1.2 misho 928: /* ... try disabling graph, details, and info */
929: list_length = lines_available - 1 - 1 - 1;
930: if (list_length >= c_list_min) {
931: disable_graph = 1;
932: disable_details = 1;
933: disable_info = 1;
934: goto draw;
935: }
936:
937: NEXT_ROW();
938: put_line("A minimum of %d lines is required to display content.\n",
939: (rows - lines_available) + c_list_min + 2);
940: return;
941:
942: draw:
943: if (selection_offset && list_length > 0) {
1.1 misho 944: /*
1.1.1.2 misho 945: * Vertically align the selected element in the middle
946: * of the list.
1.1 misho 947: */
1.1.1.2 misho 948: offset = selection_offset - (list_length / 2);
1.1 misho 949:
950: /*
1.1.1.2 misho 951: * If element 0..(list_length/2) is selected, offset is
952: * negative here. Start drawing from first element.
1.1 misho 953: */
1.1.1.2 misho 954: if (offset < 0)
955: offset = 0;
1.1 misho 956:
1.1.1.2 misho 957: /*
958: * Ensure the full list length is used if one of the
959: * last (list_length/2) elements is selected.
960: */
961: if (offset > (list_req - list_length))
962: offset = (list_req - list_length);
1.1 misho 963:
1.1.1.2 misho 964: if (offset >= list_req)
965: BUG();
966: }
967:
968: if (c_show_list) {
969: draw_element_list();
970: } else {
971: NEXT_ROW();
972: hline(ACS_HLINE, cols);
973: center_text(" Press %c to enable list view ",
974: KEY_TOGGLE_LIST);
1.1 misho 975: }
976:
977: /*
1.1.1.2 misho 978: * Graphical statistics
1.1 misho 979: */
1.1.1.2 misho 980: NEXT_ROW();
981: hline(ACS_HLINE, cols);
982: mvaddch(row, LIST_COL_1, ACS_BTEE);
983: mvaddch(row, LIST_COL_2, ACS_BTEE);
984:
985: if (!c_show_graph)
986: center_text(" Press %c to enable graphical statistics ",
987: KEY_TOGGLE_GRAPH);
988: else {
989: if (disable_graph)
990: center_text(" Increase screen height to see graphical statistics ");
991: else
992: draw_graph();
993: }
994:
995: empty_lines = rows - row - details_req - info_req
996: - lines_required_for_statusbar() - 1;
997:
998: while (empty_lines-- > 0) {
999: NEXT_ROW();
1000: put_line("");
1.1 misho 1001: }
1002:
1.1.1.2 misho 1003: /*
1004: * Detailed statistics
1005: */
1006: NEXT_ROW();
1.1 misho 1007: hline(ACS_HLINE, cols);
1008:
1.1.1.2 misho 1009: if (!c_show_details)
1010: center_text(" Press %c to enable detailed statistics ",
1011: KEY_TOGGLE_DETAILS);
1012: else {
1013: if (disable_details)
1014: center_text(" Increase screen height to see detailed statistics ");
1015: else
1016: draw_details();
1.1 misho 1017: }
1018:
1.1.1.2 misho 1019: /*
1020: * Additional information
1021: */
1022: NEXT_ROW();
1.1 misho 1023: hline(ACS_HLINE, cols);
1024:
1.1.1.2 misho 1025: if (!c_show_info)
1026: center_text(" Press %c to enable additional information ",
1027: KEY_TOGGLE_INFO);
1028: else {
1029: if (disable_info)
1030: center_text(" Increase screen height to see additional information ");
1031: else
1032: draw_info();
1.1 misho 1033: }
1034: }
1035:
1036:
1037: static void curses_draw(void)
1038: {
1039: row = 0;
1040: move(0,0);
1041:
1042: getmaxyx(stdscr, rows, cols);
1.1.1.2 misho 1043:
1044: if (rows < 4) {
1.1 misho 1045: clear();
1.1.1.2 misho 1046: put_line("Screen must be at least 4 rows in height");
1047: goto out;
1.1 misho 1048: }
1049:
1.1.1.2 misho 1050: if (cols < 48) {
1051: clear();
1052: put_line("Screen must be at least 48 columns width");
1053: goto out;
1054: }
1055:
1056: current_element = element_current();
1057: current_attr = attr_current();
1.1 misho 1058:
1.1.1.2 misho 1059: draw_header();
1060:
1061: apply_layout(LAYOUT_DEFAULT);
1062: draw_content();
1063:
1064: /* fill empty lines with blanks */
1065: while (row < (rows - 1 - lines_required_for_statusbar())) {
1066: move(++row, 0);
1067: put_line("");
1.1 misho 1068: }
1069:
1.1.1.2 misho 1070: draw_statusbar();
1.1 misho 1071:
1072: if (quit_mode)
1.1.1.2 misho 1073: print_message(" Really Quit? (y/n) ");
1.1 misho 1074: else if (print_help) {
1075: if (help_page == 0)
1076: draw_help();
1.1.1.2 misho 1077: #if 0
1.1 misho 1078: else
1079: draw_help_2();
1.1.1.2 misho 1080: #endif
1.1 misho 1081: }
1082:
1.1.1.2 misho 1083: out:
1.1 misho 1084: attrset(0);
1085: refresh();
1086: }
1087:
1.1.1.3 ! misho 1088: static void __reset_attr_counter(struct element *e, struct attr *a, void *arg)
! 1089: {
! 1090: attr_reset_counter(a);
! 1091: }
! 1092:
! 1093: static void reset_counters(void)
! 1094: {
! 1095: element_foreach_attr(current_element, __reset_attr_counter, NULL);
! 1096: }
! 1097:
1.1 misho 1098: static int handle_input(int ch)
1099: {
1100: switch (ch)
1101: {
1102: case 'q':
1.1.1.2 misho 1103: if (print_help)
1104: print_help = 0;
1105: else
1106: quit_mode = quit_mode ? 0 : 1;
1.1 misho 1107: return 1;
1108:
1109: case 0x1b:
1110: quit_mode = 0;
1111: print_help = 0;
1112: return 1;
1113:
1114: case 'y':
1115: if (quit_mode)
1116: exit(0);
1117: break;
1118:
1119: case 'n':
1120: if (quit_mode)
1121: quit_mode = 0;
1122: return 1;
1123:
1124: case 12:
1125: case KEY_CLEAR:
1126: #ifdef HAVE_REDRAWWIN
1127: redrawwin(stdscr);
1128: #endif
1129: clear();
1130: return 1;
1131:
1.1.1.2 misho 1132: case '?':
1.1 misho 1133: clear();
1.1.1.3 ! misho 1134: print_help = print_help ? 0 : 1;
1.1 misho 1135: return 1;
1136:
1.1.1.2 misho 1137: case KEY_TOGGLE_GRAPH:
1138: c_show_graph = !c_show_graph;
1139: if (c_show_graph && !c_ngraph)
1140: c_ngraph = 1;
1.1 misho 1141: return 1;
1142:
1.1.1.2 misho 1143: case KEY_TOGGLE_DETAILS:
1144: c_show_details = !c_show_details;
1.1 misho 1145: return 1;
1146:
1.1.1.2 misho 1147: case KEY_TOGGLE_LIST:
1148: c_show_list = !c_show_list;
1.1 misho 1149: return 1;
1150:
1.1.1.2 misho 1151: case KEY_TOGGLE_INFO:
1152: c_show_info = !c_show_info;
1.1 misho 1153: return 1;
1154:
1.1.1.2 misho 1155: case KEY_COLLECT_HISTORY:
1156: if (current_attr) {
1157: attr_start_collecting_history(current_attr);
1158: return 1;
1159: }
1160: break;
1161:
1162: case KEY_PPAGE:
1163: {
1164: int i;
1165: for (i = 1; i < list_length; i++)
1166: element_select_prev();
1167: }
1.1 misho 1168: return 1;
1169:
1.1.1.2 misho 1170: case KEY_NPAGE:
1171: {
1172: int i;
1173: for (i = 1; i < list_length; i++)
1174: element_select_next();
1175: }
1.1 misho 1176: return 1;
1177:
1.1.1.2 misho 1178: case KEY_DOWN:
1179: element_select_next();
1.1 misho 1180: return 1;
1181:
1.1.1.2 misho 1182: case KEY_UP:
1183: element_select_prev();
1.1 misho 1184: return 1;
1185:
1.1.1.2 misho 1186: case KEY_LEFT:
1187: attr_select_prev();
1.1 misho 1188: return 1;
1189:
1.1.1.2 misho 1190: case KEY_RIGHT:
1191: attr_select_next();
1.1 misho 1192: return 1;
1193:
1.1.1.2 misho 1194: case ']':
1195: group_select_next();
1.1 misho 1196: return 1;
1197:
1.1.1.2 misho 1198: case '[':
1199: group_select_prev();
1.1 misho 1200: return 1;
1201:
1.1.1.2 misho 1202: case '<':
1203: c_ngraph--;
1204: if (c_ngraph <= 1)
1205: c_ngraph = 1;
1.1 misho 1206: return 1;
1207:
1208: case '>':
1.1.1.2 misho 1209: c_ngraph++;
1210: if (c_ngraph > 32)
1211: c_ngraph = 32;
1.1 misho 1212: return 1;
1213:
1.1.1.2 misho 1214: case '\t':
1215: history_select_next();
1.1 misho 1216: return 1;
1.1.1.3 ! misho 1217:
! 1218: case 'r':
! 1219: reset_counters();
! 1220: return 1;
1.1 misho 1221: }
1222:
1223: return 0;
1224: }
1225:
1226: static void curses_pre(void)
1227: {
1.1.1.2 misho 1228: static int init = 0;
1229:
1230: if (!init) {
1231: curses_init();
1232: init = 1;
1233: }
1234:
1.1 misho 1235: for (;;) {
1236: int ch = getch();
1237:
1238: if (ch == -1)
1239: break;
1240:
1241: if (handle_input(ch))
1242: curses_draw();
1243: }
1244: }
1245:
1246: static void print_module_help(void)
1247: {
1248: printf(
1249: "curses - Curses Output\n" \
1250: "\n" \
1.1.1.2 misho 1251: " Interactive curses UI. Press '?' to see help.\n" \
1.1 misho 1252: " Author: Thomas Graf <tgraf@suug.ch>\n" \
1253: "\n" \
1254: " Options:\n" \
1255: " fgchar=CHAR Foreground character (default: '*')\n" \
1256: " bgchar=CHAR Background character (default: '.')\n" \
1257: " nchar=CHAR Noise character (default: ':')\n" \
1258: " uchar=CHAR Unknown character (default: '?')\n" \
1.1.1.2 misho 1259: " gheight=NUM Height of graph (default: 6)\n" \
1260: " gwidth=NUM Width of graph (default: 60)\n" \
1261: " ngraph=NUM Number of graphs (default: 1)\n" \
1.1 misho 1262: " nocolors Do not use colors\n" \
1263: " graph Show graphical stats by default\n" \
1.1.1.2 misho 1264: " details Show detailed stats by default\n" \
1.1.1.3 ! misho 1265: " info Show additional info screen by default\n" \
1.1.1.2 misho 1266: " minlist=INT Minimum item list length\n");
1.1 misho 1267: }
1268:
1.1.1.2 misho 1269: static void curses_parse_opt(const char *type, const char *value)
1.1 misho 1270: {
1.1.1.2 misho 1271: if (!strcasecmp(type, "fgchar") && value)
1272: c_graph_cfg.gc_foreground = value[0];
1273: else if (!strcasecmp(type, "bgchar") && value)
1274: c_graph_cfg.gc_background = value[0];
1275: else if (!strcasecmp(type, "nchar") && value)
1276: c_graph_cfg.gc_noise = value[0];
1277: else if (!strcasecmp(type, "uchar") && value)
1278: c_graph_cfg.gc_unknown = value[0];
1279: else if (!strcasecmp(type, "gheight") && value)
1280: c_graph_cfg.gc_height = strtol(value, NULL, 0);
1281: else if (!strcasecmp(type, "gwidth") && value)
1282: c_graph_cfg.gc_width = strtol(value, NULL, 0);
1.1.1.3 ! misho 1283: else if (!strcasecmp(type, "ngraph") && value) {
1.1.1.2 misho 1284: c_ngraph = strtol(value, NULL, 0);
1285: c_show_graph = !!c_ngraph;
1286: } else if (!strcasecmp(type, "details"))
1287: c_show_details = 1;
1.1.1.3 ! misho 1288: else if (!strcasecmp(type, "info"))
! 1289: c_show_info = 1;
1.1.1.2 misho 1290: else if (!strcasecmp(type, "nocolors"))
1291: c_use_colors = 0;
1292: else if (!strcasecmp(type, "minlist") && value)
1293: c_list_min = strtol(value, NULL, 0);
1294: else if (!strcasecmp(type, "help")) {
1295: print_module_help();
1296: exit(0);
1.1 misho 1297: }
1298: }
1299:
1.1.1.2 misho 1300: static struct bmon_module curses_ops = {
1301: .m_name = "curses",
1302: .m_flags = BMON_MODULE_DEFAULT,
1303: .m_shutdown = curses_shutdown,
1304: .m_pre = curses_pre,
1305: .m_do = curses_draw,
1306: .m_parse_opt = curses_parse_opt,
1.1 misho 1307: };
1308:
1309: static void __init do_curses_init(void)
1310: {
1.1.1.2 misho 1311: output_register(&curses_ops);
1.1 misho 1312: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>