]> Pileus Git - ~andy/linux/blob - tools/perf/ui/browser.c
Merge branch 'for-3.9-async' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq
[~andy/linux] / tools / perf / ui / browser.c
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
38 void ui_browser__set_percent_color(struct ui_browser *browser,
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
167 void ui_browser__refresh_dimensions(struct ui_browser *browser)
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
175 void ui_browser__handle_resize(struct ui_browser *browser)
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
254         ui_browser__refresh_dimensions(browser);
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
272 void ui_browser__hide(struct ui_browser *browser __maybe_unused)
273 {
274         pthread_mutex_lock(&ui__lock);
275         ui_helpline__pop();
276         free(browser->helpline);
277         browser->helpline = NULL;
278         pthread_mutex_unlock(&ui__lock);
279 }
280
281 static void ui_browser__scrollbar_set(struct ui_browser *browser)
282 {
283         int height = browser->height, h = 0, pct = 0,
284             col = browser->width,
285             row = browser->y - 1;
286
287         if (browser->nr_entries > 1) {
288                 pct = ((browser->index * (browser->height - 1)) /
289                        (browser->nr_entries - 1));
290         }
291
292         SLsmg_set_char_set(1);
293
294         while (h < height) {
295                 ui_browser__gotorc(browser, row++, col);
296                 SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
297                 ++h;
298         }
299
300         SLsmg_set_char_set(0);
301 }
302
303 static int __ui_browser__refresh(struct ui_browser *browser)
304 {
305         int row;
306         int width = browser->width;
307
308         row = browser->refresh(browser);
309         ui_browser__set_color(browser, HE_COLORSET_NORMAL);
310
311         if (!browser->use_navkeypressed || browser->navkeypressed)
312                 ui_browser__scrollbar_set(browser);
313         else
314                 width += 1;
315
316         SLsmg_fill_region(browser->y + row, browser->x,
317                           browser->height - row, width, ' ');
318
319         return 0;
320 }
321
322 int ui_browser__refresh(struct ui_browser *browser)
323 {
324         pthread_mutex_lock(&ui__lock);
325         __ui_browser__refresh(browser);
326         pthread_mutex_unlock(&ui__lock);
327
328         return 0;
329 }
330
331 /*
332  * Here we're updating nr_entries _after_ we started browsing, i.e.  we have to
333  * forget about any reference to any entry in the underlying data structure,
334  * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
335  * after an output_resort and hist decay.
336  */
337 void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
338 {
339         off_t offset = nr_entries - browser->nr_entries;
340
341         browser->nr_entries = nr_entries;
342
343         if (offset < 0) {
344                 if (browser->top_idx < (u64)-offset)
345                         offset = -browser->top_idx;
346
347                 browser->index += offset;
348                 browser->top_idx += offset;
349         }
350
351         browser->top = NULL;
352         browser->seek(browser, browser->top_idx, SEEK_SET);
353 }
354
355 int ui_browser__run(struct ui_browser *browser, int delay_secs)
356 {
357         int err, key;
358
359         while (1) {
360                 off_t offset;
361
362                 pthread_mutex_lock(&ui__lock);
363                 err = __ui_browser__refresh(browser);
364                 SLsmg_refresh();
365                 pthread_mutex_unlock(&ui__lock);
366                 if (err < 0)
367                         break;
368
369                 key = ui__getch(delay_secs);
370
371                 if (key == K_RESIZE) {
372                         ui__refresh_dimensions(false);
373                         ui_browser__refresh_dimensions(browser);
374                         __ui_browser__show_title(browser, browser->title);
375                         ui_helpline__puts(browser->helpline);
376                         continue;
377                 }
378
379                 if (browser->use_navkeypressed && !browser->navkeypressed) {
380                         if (key == K_DOWN || key == K_UP ||
381                             key == K_PGDN || key == K_PGUP ||
382                             key == K_HOME || key == K_END ||
383                             key == ' ') {
384                                 browser->navkeypressed = true;
385                                 continue;
386                         } else
387                                 return key;
388                 }
389
390                 switch (key) {
391                 case K_DOWN:
392                         if (browser->index == browser->nr_entries - 1)
393                                 break;
394                         ++browser->index;
395                         if (browser->index == browser->top_idx + browser->height) {
396                                 ++browser->top_idx;
397                                 browser->seek(browser, +1, SEEK_CUR);
398                         }
399                         break;
400                 case K_UP:
401                         if (browser->index == 0)
402                                 break;
403                         --browser->index;
404                         if (browser->index < browser->top_idx) {
405                                 --browser->top_idx;
406                                 browser->seek(browser, -1, SEEK_CUR);
407                         }
408                         break;
409                 case K_PGDN:
410                 case ' ':
411                         if (browser->top_idx + browser->height > browser->nr_entries - 1)
412                                 break;
413
414                         offset = browser->height;
415                         if (browser->index + offset > browser->nr_entries - 1)
416                                 offset = browser->nr_entries - 1 - browser->index;
417                         browser->index += offset;
418                         browser->top_idx += offset;
419                         browser->seek(browser, +offset, SEEK_CUR);
420                         break;
421                 case K_PGUP:
422                         if (browser->top_idx == 0)
423                                 break;
424
425                         if (browser->top_idx < browser->height)
426                                 offset = browser->top_idx;
427                         else
428                                 offset = browser->height;
429
430                         browser->index -= offset;
431                         browser->top_idx -= offset;
432                         browser->seek(browser, -offset, SEEK_CUR);
433                         break;
434                 case K_HOME:
435                         ui_browser__reset_index(browser);
436                         break;
437                 case K_END:
438                         offset = browser->height - 1;
439                         if (offset >= browser->nr_entries)
440                                 offset = browser->nr_entries - 1;
441
442                         browser->index = browser->nr_entries - 1;
443                         browser->top_idx = browser->index - offset;
444                         browser->seek(browser, -offset, SEEK_END);
445                         break;
446                 default:
447                         return key;
448                 }
449         }
450         return -1;
451 }
452
453 unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
454 {
455         struct list_head *pos;
456         struct list_head *head = browser->entries;
457         int row = 0;
458
459         if (browser->top == NULL || browser->top == browser->entries)
460                 browser->top = ui_browser__list_head_filter_entries(browser, head->next);
461
462         pos = browser->top;
463
464         list_for_each_from(pos, head) {
465                 if (!browser->filter || !browser->filter(browser, pos)) {
466                         ui_browser__gotorc(browser, row, 0);
467                         browser->write(browser, pos, row);
468                         if (++row == browser->height)
469                                 break;
470                 }
471         }
472
473         return row;
474 }
475
476 static struct ui_browser_colorset {
477         const char *name, *fg, *bg;
478         int colorset;
479 } ui_browser__colorsets[] = {
480         {
481                 .colorset = HE_COLORSET_TOP,
482                 .name     = "top",
483                 .fg       = "red",
484                 .bg       = "default",
485         },
486         {
487                 .colorset = HE_COLORSET_MEDIUM,
488                 .name     = "medium",
489                 .fg       = "green",
490                 .bg       = "default",
491         },
492         {
493                 .colorset = HE_COLORSET_NORMAL,
494                 .name     = "normal",
495                 .fg       = "default",
496                 .bg       = "default",
497         },
498         {
499                 .colorset = HE_COLORSET_SELECTED,
500                 .name     = "selected",
501                 .fg       = "black",
502                 .bg       = "lightgray",
503         },
504         {
505                 .colorset = HE_COLORSET_CODE,
506                 .name     = "code",
507                 .fg       = "blue",
508                 .bg       = "default",
509         },
510         {
511                 .colorset = HE_COLORSET_ADDR,
512                 .name     = "addr",
513                 .fg       = "magenta",
514                 .bg       = "default",
515         },
516         {
517                 .name = NULL,
518         }
519 };
520
521
522 static int ui_browser__color_config(const char *var, const char *value,
523                                     void *data __maybe_unused)
524 {
525         char *fg = NULL, *bg;
526         int i;
527
528         /* same dir for all commands */
529         if (prefixcmp(var, "colors.") != 0)
530                 return 0;
531
532         for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
533                 const char *name = var + 7;
534
535                 if (strcmp(ui_browser__colorsets[i].name, name) != 0)
536                         continue;
537
538                 fg = strdup(value);
539                 if (fg == NULL)
540                         break;
541
542                 bg = strchr(fg, ',');
543                 if (bg == NULL)
544                         break;
545
546                 *bg = '\0';
547                 while (isspace(*++bg));
548                 ui_browser__colorsets[i].bg = bg;
549                 ui_browser__colorsets[i].fg = fg;
550                 return 0;
551         }
552
553         free(fg);
554         return -1;
555 }
556
557 void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
558 {
559         switch (whence) {
560         case SEEK_SET:
561                 browser->top = browser->entries;
562                 break;
563         case SEEK_CUR:
564                 browser->top = browser->top + browser->top_idx + offset;
565                 break;
566         case SEEK_END:
567                 browser->top = browser->top + browser->nr_entries + offset;
568                 break;
569         default:
570                 return;
571         }
572 }
573
574 unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
575 {
576         unsigned int row = 0, idx = browser->top_idx;
577         char **pos;
578
579         if (browser->top == NULL)
580                 browser->top = browser->entries;
581
582         pos = (char **)browser->top;
583         while (idx < browser->nr_entries) {
584                 if (!browser->filter || !browser->filter(browser, *pos)) {
585                         ui_browser__gotorc(browser, row, 0);
586                         browser->write(browser, pos, row);
587                         if (++row == browser->height)
588                                 break;
589                 }
590
591                 ++idx;
592                 ++pos;
593         }
594
595         return row;
596 }
597
598 void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
599                          u16 start, u16 end)
600 {
601         SLsmg_set_char_set(1);
602         ui_browser__gotorc(browser, start, column);
603         SLsmg_draw_vline(end - start + 1);
604         SLsmg_set_char_set(0);
605 }
606
607 void ui_browser__write_graph(struct ui_browser *browser __maybe_unused,
608                              int graph)
609 {
610         SLsmg_set_char_set(1);
611         SLsmg_write_char(graph);
612         SLsmg_set_char_set(0);
613 }
614
615 static void __ui_browser__line_arrow_up(struct ui_browser *browser,
616                                         unsigned int column,
617                                         u64 start, u64 end)
618 {
619         unsigned int row, end_row;
620
621         SLsmg_set_char_set(1);
622
623         if (start < browser->top_idx + browser->height) {
624                 row = start - browser->top_idx;
625                 ui_browser__gotorc(browser, row, column);
626                 SLsmg_write_char(SLSMG_LLCORN_CHAR);
627                 ui_browser__gotorc(browser, row, column + 1);
628                 SLsmg_draw_hline(2);
629
630                 if (row-- == 0)
631                         goto out;
632         } else
633                 row = browser->height - 1;
634
635         if (end > browser->top_idx)
636                 end_row = end - browser->top_idx;
637         else
638                 end_row = 0;
639
640         ui_browser__gotorc(browser, end_row, column);
641         SLsmg_draw_vline(row - end_row + 1);
642
643         ui_browser__gotorc(browser, end_row, column);
644         if (end >= browser->top_idx) {
645                 SLsmg_write_char(SLSMG_ULCORN_CHAR);
646                 ui_browser__gotorc(browser, end_row, column + 1);
647                 SLsmg_write_char(SLSMG_HLINE_CHAR);
648                 ui_browser__gotorc(browser, end_row, column + 2);
649                 SLsmg_write_char(SLSMG_RARROW_CHAR);
650         }
651 out:
652         SLsmg_set_char_set(0);
653 }
654
655 static void __ui_browser__line_arrow_down(struct ui_browser *browser,
656                                           unsigned int column,
657                                           u64 start, u64 end)
658 {
659         unsigned int row, end_row;
660
661         SLsmg_set_char_set(1);
662
663         if (start >= browser->top_idx) {
664                 row = start - browser->top_idx;
665                 ui_browser__gotorc(browser, row, column);
666                 SLsmg_write_char(SLSMG_ULCORN_CHAR);
667                 ui_browser__gotorc(browser, row, column + 1);
668                 SLsmg_draw_hline(2);
669
670                 if (row++ == 0)
671                         goto out;
672         } else
673                 row = 0;
674
675         if (end >= browser->top_idx + browser->height)
676                 end_row = browser->height - 1;
677         else
678                 end_row = end - browser->top_idx;;
679
680         ui_browser__gotorc(browser, row, column);
681         SLsmg_draw_vline(end_row - row + 1);
682
683         ui_browser__gotorc(browser, end_row, column);
684         if (end < browser->top_idx + browser->height) {
685                 SLsmg_write_char(SLSMG_LLCORN_CHAR);
686                 ui_browser__gotorc(browser, end_row, column + 1);
687                 SLsmg_write_char(SLSMG_HLINE_CHAR);
688                 ui_browser__gotorc(browser, end_row, column + 2);
689                 SLsmg_write_char(SLSMG_RARROW_CHAR);
690         }
691 out:
692         SLsmg_set_char_set(0);
693 }
694
695 void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
696                               u64 start, u64 end)
697 {
698         if (start > end)
699                 __ui_browser__line_arrow_up(browser, column, start, end);
700         else
701                 __ui_browser__line_arrow_down(browser, column, start, end);
702 }
703
704 void ui_browser__init(void)
705 {
706         int i = 0;
707
708         perf_config(ui_browser__color_config, NULL);
709
710         while (ui_browser__colorsets[i].name) {
711                 struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
712                 sltt_set_color(c->colorset, c->name, c->fg, c->bg);
713         }
714
715         annotate_browser__init();
716 }