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