Merge git://git.kernel.org/pub/scm/virt/kvm/kvm
[cascardo/linux.git] / 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 __used)
273 {
274         pthread_mutex_lock(&ui__lock);
275         ui_helpline__pop();
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);
307         ui_browser__set_color(browser, HE_COLORSET_NORMAL);
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);
371                         ui_browser__refresh_dimensions(browser);
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 __used)
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 __used, int graph)
606 {
607         SLsmg_set_char_set(1);
608         SLsmg_write_char(graph);
609         SLsmg_set_char_set(0);
610 }
611
612 static void __ui_browser__line_arrow_up(struct ui_browser *browser,
613                                         unsigned int column,
614                                         u64 start, u64 end)
615 {
616         unsigned int row, end_row;
617
618         SLsmg_set_char_set(1);
619
620         if (start < browser->top_idx + browser->height) {
621                 row = start - browser->top_idx;
622                 ui_browser__gotorc(browser, row, column);
623                 SLsmg_write_char(SLSMG_LLCORN_CHAR);
624                 ui_browser__gotorc(browser, row, column + 1);
625                 SLsmg_draw_hline(2);
626
627                 if (row-- == 0)
628                         goto out;
629         } else
630                 row = browser->height - 1;
631
632         if (end > browser->top_idx)
633                 end_row = end - browser->top_idx;
634         else
635                 end_row = 0;
636
637         ui_browser__gotorc(browser, end_row, column);
638         SLsmg_draw_vline(row - end_row + 1);
639
640         ui_browser__gotorc(browser, end_row, column);
641         if (end >= browser->top_idx) {
642                 SLsmg_write_char(SLSMG_ULCORN_CHAR);
643                 ui_browser__gotorc(browser, end_row, column + 1);
644                 SLsmg_write_char(SLSMG_HLINE_CHAR);
645                 ui_browser__gotorc(browser, end_row, column + 2);
646                 SLsmg_write_char(SLSMG_RARROW_CHAR);
647         }
648 out:
649         SLsmg_set_char_set(0);
650 }
651
652 static void __ui_browser__line_arrow_down(struct ui_browser *browser,
653                                           unsigned int column,
654                                           u64 start, u64 end)
655 {
656         unsigned int row, end_row;
657
658         SLsmg_set_char_set(1);
659
660         if (start >= browser->top_idx) {
661                 row = start - browser->top_idx;
662                 ui_browser__gotorc(browser, row, column);
663                 SLsmg_write_char(SLSMG_ULCORN_CHAR);
664                 ui_browser__gotorc(browser, row, column + 1);
665                 SLsmg_draw_hline(2);
666
667                 if (row++ == 0)
668                         goto out;
669         } else
670                 row = 0;
671
672         if (end >= browser->top_idx + browser->height)
673                 end_row = browser->height - 1;
674         else
675                 end_row = end - browser->top_idx;;
676
677         ui_browser__gotorc(browser, row, column);
678         SLsmg_draw_vline(end_row - row + 1);
679
680         ui_browser__gotorc(browser, end_row, column);
681         if (end < browser->top_idx + browser->height) {
682                 SLsmg_write_char(SLSMG_LLCORN_CHAR);
683                 ui_browser__gotorc(browser, end_row, column + 1);
684                 SLsmg_write_char(SLSMG_HLINE_CHAR);
685                 ui_browser__gotorc(browser, end_row, column + 2);
686                 SLsmg_write_char(SLSMG_RARROW_CHAR);
687         }
688 out:
689         SLsmg_set_char_set(0);
690 }
691
692 void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
693                               u64 start, u64 end)
694 {
695         if (start > end)
696                 __ui_browser__line_arrow_up(browser, column, start, end);
697         else
698                 __ui_browser__line_arrow_down(browser, column, start, end);
699 }
700
701 void ui_browser__init(void)
702 {
703         int i = 0;
704
705         perf_config(ui_browser__color_config, NULL);
706
707         while (ui_browser__colorsets[i].name) {
708                 struct ui_browser__colorset *c = &ui_browser__colorsets[i++];
709                 sltt_set_color(c->colorset, c->name, c->fg, c->bg);
710         }
711
712         annotate_browser__init();
713 }