Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
browser.c
Go to the documentation of this file.
1 #include "../util.h"
2 #include "../cache.h"
3 #include "../../perf.h"
4 #include "libslang.h"
5 #include <newt.h>
6 #include "ui.h"
7 #include "util.h"
8 #include <linux/compiler.h>
9 #include <linux/list.h>
10 #include <linux/rbtree.h>
11 #include <stdlib.h>
12 #include <sys/ttydefaults.h>
13 #include "browser.h"
14 #include "helpline.h"
15 #include "keysyms.h"
16 #include "../color.h"
17 
18 static int ui_browser__percent_color(struct ui_browser *browser,
19  double percent, bool current)
20 {
21  if (current && (!browser->use_navkeypressed || browser->navkeypressed))
22  return HE_COLORSET_SELECTED;
23  if (percent >= MIN_RED)
24  return HE_COLORSET_TOP;
25  if (percent >= MIN_GREEN)
26  return HE_COLORSET_MEDIUM;
27  return HE_COLORSET_NORMAL;
28 }
29 
30 int ui_browser__set_color(struct ui_browser *browser, int color)
31 {
32  int ret = browser->current_color;
33  browser->current_color = color;
34  SLsmg_set_color(color);
35  return ret;
36 }
37 
39  double percent, bool current)
40 {
41  int color = ui_browser__percent_color(browser, percent, current);
42  ui_browser__set_color(browser, color);
43 }
44 
45 void ui_browser__gotorc(struct ui_browser *browser, int y, int x)
46 {
47  SLsmg_gotorc(browser->y + y, browser->x + x);
48 }
49 
50 static struct list_head *
51 ui_browser__list_head_filter_entries(struct ui_browser *browser,
52  struct list_head *pos)
53 {
54  do {
55  if (!browser->filter || !browser->filter(browser, pos))
56  return pos;
57  pos = pos->next;
58  } while (pos != browser->entries);
59 
60  return NULL;
61 }
62 
63 static struct list_head *
64 ui_browser__list_head_filter_prev_entries(struct ui_browser *browser,
65  struct list_head *pos)
66 {
67  do {
68  if (!browser->filter || !browser->filter(browser, pos))
69  return pos;
70  pos = pos->prev;
71  } while (pos != browser->entries);
72 
73  return NULL;
74 }
75 
76 void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int whence)
77 {
78  struct list_head *head = browser->entries;
79  struct list_head *pos;
80 
81  if (browser->nr_entries == 0)
82  return;
83 
84  switch (whence) {
85  case SEEK_SET:
86  pos = ui_browser__list_head_filter_entries(browser, head->next);
87  break;
88  case SEEK_CUR:
89  pos = browser->top;
90  break;
91  case SEEK_END:
92  pos = ui_browser__list_head_filter_prev_entries(browser, head->prev);
93  break;
94  default:
95  return;
96  }
97 
98  assert(pos != NULL);
99 
100  if (offset > 0) {
101  while (offset-- != 0)
102  pos = ui_browser__list_head_filter_entries(browser, pos->next);
103  } else {
104  while (offset++ != 0)
105  pos = ui_browser__list_head_filter_prev_entries(browser, pos->prev);
106  }
107 
108  browser->top = pos;
109 }
110 
111 void ui_browser__rb_tree_seek(struct ui_browser *browser, off_t offset, int whence)
112 {
113  struct rb_root *root = browser->entries;
114  struct rb_node *nd;
115 
116  switch (whence) {
117  case SEEK_SET:
118  nd = rb_first(root);
119  break;
120  case SEEK_CUR:
121  nd = browser->top;
122  break;
123  case SEEK_END:
124  nd = rb_last(root);
125  break;
126  default:
127  return;
128  }
129 
130  if (offset > 0) {
131  while (offset-- != 0)
132  nd = rb_next(nd);
133  } else {
134  while (offset++ != 0)
135  nd = rb_prev(nd);
136  }
137 
138  browser->top = nd;
139 }
140 
141 unsigned int ui_browser__rb_tree_refresh(struct ui_browser *browser)
142 {
143  struct rb_node *nd;
144  int row = 0;
145 
146  if (browser->top == NULL)
147  browser->top = rb_first(browser->entries);
148 
149  nd = browser->top;
150 
151  while (nd != NULL) {
152  ui_browser__gotorc(browser, row, 0);
153  browser->write(browser, nd, row);
154  if (++row == browser->height)
155  break;
156  nd = rb_next(nd);
157  }
158 
159  return row;
160 }
161 
162 bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row)
163 {
164  return browser->top_idx + row == browser->index;
165 }
166 
168 {
169  browser->width = SLtt_Screen_Cols - 1;
170  browser->height = SLtt_Screen_Rows - 2;
171  browser->y = 1;
172  browser->x = 0;
173 }
174 
176 {
177  ui__refresh_dimensions(false);
178  ui_browser__show(browser, browser->title, ui_helpline__current);
179  ui_browser__refresh(browser);
180 }
181 
182 int ui_browser__warning(struct ui_browser *browser, int timeout,
183  const char *format, ...)
184 {
185  va_list args;
186  char *text;
187  int key = 0, err;
188 
189  va_start(args, format);
190  err = vasprintf(&text, format, args);
191  va_end(args);
192 
193  if (err < 0) {
194  va_start(args, format);
195  ui_helpline__vpush(format, args);
196  va_end(args);
197  } else {
198  while ((key == ui__question_window("Warning!", text,
199  "Press any key...",
200  timeout)) == K_RESIZE)
201  ui_browser__handle_resize(browser);
202  free(text);
203  }
204 
205  return key;
206 }
207 
208 int ui_browser__help_window(struct ui_browser *browser, const char *text)
209 {
210  int key;
211 
212  while ((key = ui__help_window(text)) == K_RESIZE)
213  ui_browser__handle_resize(browser);
214 
215  return key;
216 }
217 
218 bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text)
219 {
220  int key;
221 
222  while ((key = ui__dialog_yesno(text)) == K_RESIZE)
223  ui_browser__handle_resize(browser);
224 
225  return key == K_ENTER || toupper(key) == 'Y';
226 }
227 
228 void ui_browser__reset_index(struct ui_browser *browser)
229 {
230  browser->index = browser->top_idx = 0;
231  browser->seek(browser, 0, SEEK_SET);
232 }
233 
234 void __ui_browser__show_title(struct ui_browser *browser, const char *title)
235 {
236  SLsmg_gotorc(0, 0);
237  ui_browser__set_color(browser, NEWT_COLORSET_ROOT);
238  slsmg_write_nstring(title, browser->width + 1);
239 }
240 
241 void ui_browser__show_title(struct ui_browser *browser, const char *title)
242 {
243  pthread_mutex_lock(&ui__lock);
244  __ui_browser__show_title(browser, title);
245  pthread_mutex_unlock(&ui__lock);
246 }
247 
248 int ui_browser__show(struct ui_browser *browser, const char *title,
249  const char *helpline, ...)
250 {
251  int err;
252  va_list ap;
253 
255 
256  pthread_mutex_lock(&ui__lock);
257  __ui_browser__show_title(browser, title);
258 
259  browser->title = title;
260  free(browser->helpline);
261  browser->helpline = NULL;
262 
263  va_start(ap, helpline);
264  err = vasprintf(&browser->helpline, helpline, ap);
265  va_end(ap);
266  if (err > 0)
267  ui_helpline__push(browser->helpline);
268  pthread_mutex_unlock(&ui__lock);
269  return err ? 0 : -1;
270 }
271 
273 {
274  pthread_mutex_lock(&ui__lock);
276  pthread_mutex_unlock(&ui__lock);
277 }
278 
279 static void ui_browser__scrollbar_set(struct ui_browser *browser)
280 {
281  int height = browser->height, h = 0, pct = 0,
282  col = browser->width,
283  row = browser->y - 1;
284 
285  if (browser->nr_entries > 1) {
286  pct = ((browser->index * (browser->height - 1)) /
287  (browser->nr_entries - 1));
288  }
289 
290  SLsmg_set_char_set(1);
291 
292  while (h < height) {
293  ui_browser__gotorc(browser, row++, col);
294  SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
295  ++h;
296  }
297 
298  SLsmg_set_char_set(0);
299 }
300 
301 static int __ui_browser__refresh(struct ui_browser *browser)
302 {
303  int row;
304  int width = browser->width;
305 
306  row = browser->refresh(browser);
308 
309  if (!browser->use_navkeypressed || browser->navkeypressed)
310  ui_browser__scrollbar_set(browser);
311  else
312  width += 1;
313 
314  SLsmg_fill_region(browser->y + row, browser->x,
315  browser->height - row, width, ' ');
316 
317  return 0;
318 }
319 
320 int ui_browser__refresh(struct ui_browser *browser)
321 {
322  pthread_mutex_lock(&ui__lock);
323  __ui_browser__refresh(browser);
324  pthread_mutex_unlock(&ui__lock);
325 
326  return 0;
327 }
328 
329 /*
330  * Here we're updating nr_entries _after_ we started browsing, i.e. we have to
331  * forget about any reference to any entry in the underlying data structure,
332  * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
333  * after an output_resort and hist decay.
334  */
335 void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
336 {
337  off_t offset = nr_entries - browser->nr_entries;
338 
339  browser->nr_entries = nr_entries;
340 
341  if (offset < 0) {
342  if (browser->top_idx < (u64)-offset)
343  offset = -browser->top_idx;
344 
345  browser->index += offset;
346  browser->top_idx += offset;
347  }
348 
349  browser->top = NULL;
350  browser->seek(browser, browser->top_idx, SEEK_SET);
351 }
352 
353 int ui_browser__run(struct ui_browser *browser, int delay_secs)
354 {
355  int err, key;
356 
357  while (1) {
358  off_t offset;
359 
360  pthread_mutex_lock(&ui__lock);
361  err = __ui_browser__refresh(browser);
362  SLsmg_refresh();
363  pthread_mutex_unlock(&ui__lock);
364  if (err < 0)
365  break;
366 
367  key = ui__getch(delay_secs);
368 
369  if (key == K_RESIZE) {
370  ui__refresh_dimensions(false);
372  __ui_browser__show_title(browser, browser->title);
373  ui_helpline__puts(browser->helpline);
374  continue;
375  }
376 
377  if (browser->use_navkeypressed && !browser->navkeypressed) {
378  if (key == K_DOWN || key == K_UP ||
379  key == K_PGDN || key == K_PGUP ||
380  key == K_HOME || key == K_END ||
381  key == ' ') {
382  browser->navkeypressed = true;
383  continue;
384  } else
385  return key;
386  }
387 
388  switch (key) {
389  case K_DOWN:
390  if (browser->index == browser->nr_entries - 1)
391  break;
392  ++browser->index;
393  if (browser->index == browser->top_idx + browser->height) {
394  ++browser->top_idx;
395  browser->seek(browser, +1, SEEK_CUR);
396  }
397  break;
398  case K_UP:
399  if (browser->index == 0)
400  break;
401  --browser->index;
402  if (browser->index < browser->top_idx) {
403  --browser->top_idx;
404  browser->seek(browser, -1, SEEK_CUR);
405  }
406  break;
407  case K_PGDN:
408  case ' ':
409  if (browser->top_idx + browser->height > browser->nr_entries - 1)
410  break;
411 
412  offset = browser->height;
413  if (browser->index + offset > browser->nr_entries - 1)
414  offset = browser->nr_entries - 1 - browser->index;
415  browser->index += offset;
416  browser->top_idx += offset;
417  browser->seek(browser, +offset, SEEK_CUR);
418  break;
419  case K_PGUP:
420  if (browser->top_idx == 0)
421  break;
422 
423  if (browser->top_idx < browser->height)
424  offset = browser->top_idx;
425  else
426  offset = browser->height;
427 
428  browser->index -= offset;
429  browser->top_idx -= offset;
430  browser->seek(browser, -offset, SEEK_CUR);
431  break;
432  case K_HOME:
433  ui_browser__reset_index(browser);
434  break;
435  case K_END:
436  offset = browser->height - 1;
437  if (offset >= browser->nr_entries)
438  offset = browser->nr_entries - 1;
439 
440  browser->index = browser->nr_entries - 1;
441  browser->top_idx = browser->index - offset;
442  browser->seek(browser, -offset, SEEK_END);
443  break;
444  default:
445  return key;
446  }
447  }
448  return -1;
449 }
450 
451 unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
452 {
453  struct list_head *pos;
454  struct list_head *head = browser->entries;
455  int row = 0;
456 
457  if (browser->top == NULL || browser->top == browser->entries)
458  browser->top = ui_browser__list_head_filter_entries(browser, head->next);
459 
460  pos = browser->top;
461 
462  list_for_each_from(pos, head) {
463  if (!browser->filter || !browser->filter(browser, pos)) {
464  ui_browser__gotorc(browser, row, 0);
465  browser->write(browser, pos, row);
466  if (++row == browser->height)
467  break;
468  }
469  }
470 
471  return row;
472 }
473 
474 static struct ui_browser__colorset {
475  const char *name, *fg, *bg;
476  int colorset;
477 } ui_browser__colorsets[] = {
478  {
479  .colorset = HE_COLORSET_TOP,
480  .name = "top",
481  .fg = "red",
482  .bg = "default",
483  },
484  {
485  .colorset = HE_COLORSET_MEDIUM,
486  .name = "medium",
487  .fg = "green",
488  .bg = "default",
489  },
490  {
491  .colorset = HE_COLORSET_NORMAL,
492  .name = "normal",
493  .fg = "default",
494  .bg = "default",
495  },
496  {
497  .colorset = HE_COLORSET_SELECTED,
498  .name = "selected",
499  .fg = "black",
500  .bg = "lightgray",
501  },
502  {
503  .colorset = HE_COLORSET_CODE,
504  .name = "code",
505  .fg = "blue",
506  .bg = "default",
507  },
508  {
509  .colorset = HE_COLORSET_ADDR,
510  .name = "addr",
511  .fg = "magenta",
512  .bg = "default",
513  },
514  {
515  .name = NULL,
516  }
517 };
518 
519 
520 static int ui_browser__color_config(const char *var, const char *value,
521  void *data __maybe_unused)
522 {
523  char *fg = NULL, *bg;
524  int i;
525 
526  /* same dir for all commands */
527  if (prefixcmp(var, "colors.") != 0)
528  return 0;
529 
530  for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
531  const char *name = var + 7;
532 
533  if (strcmp(ui_browser__colorsets[i].name, name) != 0)
534  continue;
535 
536  fg = strdup(value);
537  if (fg == NULL)
538  break;
539 
540  bg = strchr(fg, ',');
541  if (bg == NULL)
542  break;
543 
544  *bg = '\0';
545  while (isspace(*++bg));
546  ui_browser__colorsets[i].bg = bg;
547  ui_browser__colorsets[i].fg = fg;
548  return 0;
549  }
550 
551  free(fg);
552  return -1;
553 }
554 
555 void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
556 {
557  switch (whence) {
558  case SEEK_SET:
559  browser->top = browser->entries;
560  break;
561  case SEEK_CUR:
562  browser->top = browser->top + browser->top_idx + offset;
563  break;
564  case SEEK_END:
565  browser->top = browser->top + browser->nr_entries + offset;
566  break;
567  default:
568  return;
569  }
570 }
571 
572 unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
573 {
574  unsigned int row = 0, idx = browser->top_idx;
575  char **pos;
576 
577  if (browser->top == NULL)
578  browser->top = browser->entries;
579 
580  pos = (char **)browser->top;
581  while (idx < browser->nr_entries) {
582  if (!browser->filter || !browser->filter(browser, *pos)) {
583  ui_browser__gotorc(browser, row, 0);
584  browser->write(browser, pos, row);
585  if (++row == browser->height)
586  break;
587  }
588 
589  ++idx;
590  ++pos;
591  }
592 
593  return row;
594 }
595 
596 void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
597  u16 start, u16 end)
598 {
599  SLsmg_set_char_set(1);
600  ui_browser__gotorc(browser, start, column);
601  SLsmg_draw_vline(end - start + 1);
602  SLsmg_set_char_set(0);
603 }
604 
605 void ui_browser__write_graph(struct ui_browser *browser __maybe_unused,
606  int graph)
607 {
608  SLsmg_set_char_set(1);
609  SLsmg_write_char(graph);
610  SLsmg_set_char_set(0);
611 }
612 
613 static void __ui_browser__line_arrow_up(struct ui_browser *browser,
614  unsigned int column,
615  u64 start, u64 end)
616 {
617  unsigned int row, end_row;
618 
619  SLsmg_set_char_set(1);
620 
621  if (start < browser->top_idx + browser->height) {
622  row = start - browser->top_idx;
623  ui_browser__gotorc(browser, row, column);
624  SLsmg_write_char(SLSMG_LLCORN_CHAR);
625  ui_browser__gotorc(browser, row, column + 1);
626  SLsmg_draw_hline(2);
627 
628  if (row-- == 0)
629  goto out;
630  } else
631  row = browser->height - 1;
632 
633  if (end > browser->top_idx)
634  end_row = end - browser->top_idx;
635  else
636  end_row = 0;
637 
638  ui_browser__gotorc(browser, end_row, column);
639  SLsmg_draw_vline(row - end_row + 1);
640 
641  ui_browser__gotorc(browser, end_row, column);
642  if (end >= browser->top_idx) {
643  SLsmg_write_char(SLSMG_ULCORN_CHAR);
644  ui_browser__gotorc(browser, end_row, column + 1);
645  SLsmg_write_char(SLSMG_HLINE_CHAR);
646  ui_browser__gotorc(browser, end_row, column + 2);
647  SLsmg_write_char(SLSMG_RARROW_CHAR);
648  }
649 out:
650  SLsmg_set_char_set(0);
651 }
652 
653 static void __ui_browser__line_arrow_down(struct ui_browser *browser,
654  unsigned int column,
655  u64 start, u64 end)
656 {
657  unsigned int row, end_row;
658 
659  SLsmg_set_char_set(1);
660 
661  if (start >= browser->top_idx) {
662  row = start - browser->top_idx;
663  ui_browser__gotorc(browser, row, column);
664  SLsmg_write_char(SLSMG_ULCORN_CHAR);
665  ui_browser__gotorc(browser, row, column + 1);
666  SLsmg_draw_hline(2);
667 
668  if (row++ == 0)
669  goto out;
670  } else
671  row = 0;
672 
673  if (end >= browser->top_idx + browser->height)
674  end_row = browser->height - 1;
675  else
676  end_row = end - browser->top_idx;;
677 
678  ui_browser__gotorc(browser, row, column);
679  SLsmg_draw_vline(end_row - row + 1);
680 
681  ui_browser__gotorc(browser, end_row, column);
682  if (end < browser->top_idx + browser->height) {
683  SLsmg_write_char(SLSMG_LLCORN_CHAR);
684  ui_browser__gotorc(browser, end_row, column + 1);
685  SLsmg_write_char(SLSMG_HLINE_CHAR);
686  ui_browser__gotorc(browser, end_row, column + 2);
687  SLsmg_write_char(SLSMG_RARROW_CHAR);
688  }
689 out:
690  SLsmg_set_char_set(0);
691 }
692 
693 void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
694  u64 start, u64 end)
695 {
696  if (start > end)
697  __ui_browser__line_arrow_up(browser, column, start, end);
698  else
699  __ui_browser__line_arrow_down(browser, column, start, end);
700 }
701 
703 {
704  int i = 0;
705 
706  perf_config(ui_browser__color_config, NULL);
707 
708  while (ui_browser__colorsets[i].name) {
709  struct ui_browser__colorset *c = &ui_browser__colorsets[i++];
710  sltt_set_color(c->colorset, c->name, c->fg, c->bg);
711  }
712 
714 }