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