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