1 // SPDX-License-Identifier: GPL-2.0
2 #include "../util/util.h"
3 #include "../util/string2.h"
4 #include "../util/config.h"
8 #include <linux/compiler.h>
9 #include <linux/list.h>
10 #include <linux/rbtree.h>
11 #include <linux/string.h>
13 #include <sys/ttydefaults.h>
17 #include "../util/color.h"
18 #include <linux/ctype.h>
19 #include <linux/zalloc.h>
21 static int ui_browser__percent_color(struct ui_browser *browser,
22 double percent, bool current)
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;
33 int ui_browser__set_color(struct ui_browser *browser, int color)
35 int ret = browser->current_color;
36 browser->current_color = color;
37 SLsmg_set_color(color);
41 void ui_browser__set_percent_color(struct ui_browser *browser,
42 double percent, bool current)
44 int color = ui_browser__percent_color(browser, percent, current);
45 ui_browser__set_color(browser, color);
48 void ui_browser__gotorc_title(struct ui_browser *browser, int y, int x)
50 SLsmg_gotorc(browser->y + y, browser->x + x);
53 void ui_browser__gotorc(struct ui_browser *browser, int y, int x)
55 SLsmg_gotorc(browser->y + y + browser->extra_title_lines, browser->x + x);
58 void ui_browser__write_nstring(struct ui_browser *browser __maybe_unused, const char *msg,
61 slsmg_write_nstring(msg, width);
64 void ui_browser__vprintf(struct ui_browser *browser __maybe_unused, const char *fmt, va_list args)
66 slsmg_vprintf(fmt, args);
69 void ui_browser__printf(struct ui_browser *browser __maybe_unused, const char *fmt, ...)
74 ui_browser__vprintf(browser, fmt, args);
78 static struct list_head *
79 ui_browser__list_head_filter_entries(struct ui_browser *browser,
80 struct list_head *pos)
83 if (!browser->filter || !browser->filter(browser, pos))
86 } while (pos != browser->entries);
91 static struct list_head *
92 ui_browser__list_head_filter_prev_entries(struct ui_browser *browser,
93 struct list_head *pos)
96 if (!browser->filter || !browser->filter(browser, pos))
99 } while (pos != browser->entries);
104 void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int whence)
106 struct list_head *head = browser->entries;
107 struct list_head *pos;
109 if (browser->nr_entries == 0)
114 pos = ui_browser__list_head_filter_entries(browser, head->next);
120 pos = ui_browser__list_head_filter_prev_entries(browser, head->prev);
129 while (offset-- != 0)
130 pos = ui_browser__list_head_filter_entries(browser, pos->next);
132 while (offset++ != 0)
133 pos = ui_browser__list_head_filter_prev_entries(browser, pos->prev);
139 void ui_browser__rb_tree_seek(struct ui_browser *browser, off_t offset, int whence)
141 struct rb_root *root = browser->entries;
159 while (offset-- != 0)
162 while (offset++ != 0)
169 unsigned int ui_browser__rb_tree_refresh(struct ui_browser *browser)
174 if (browser->top == NULL)
175 browser->top = rb_first(browser->entries);
180 ui_browser__gotorc(browser, row, 0);
181 browser->write(browser, nd, row);
182 if (++row == browser->rows)
190 bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row)
192 return browser->top_idx + row == browser->index;
195 void ui_browser__refresh_dimensions(struct ui_browser *browser)
197 browser->width = SLtt_Screen_Cols - 1;
198 browser->height = browser->rows = SLtt_Screen_Rows - 2;
199 browser->rows -= browser->extra_title_lines;
204 void ui_browser__handle_resize(struct ui_browser *browser)
206 ui__refresh_dimensions(false);
207 ui_browser__show(browser, browser->title, ui_helpline__current);
208 ui_browser__refresh(browser);
211 int ui_browser__warning(struct ui_browser *browser, int timeout,
212 const char *format, ...)
218 va_start(args, format);
219 err = vasprintf(&text, format, args);
223 va_start(args, format);
224 ui_helpline__vpush(format, args);
227 while ((key = ui__question_window("Warning!", text,
229 timeout)) == K_RESIZE)
230 ui_browser__handle_resize(browser);
237 int ui_browser__help_window(struct ui_browser *browser, const char *text)
241 while ((key = ui__help_window(text)) == K_RESIZE)
242 ui_browser__handle_resize(browser);
247 bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text)
251 while ((key = ui__dialog_yesno(text)) == K_RESIZE)
252 ui_browser__handle_resize(browser);
254 return key == K_ENTER || toupper(key) == 'Y';
257 void ui_browser__reset_index(struct ui_browser *browser)
259 browser->index = browser->top_idx = 0;
260 browser->seek(browser, 0, SEEK_SET);
263 void __ui_browser__show_title(struct ui_browser *browser, const char *title)
266 ui_browser__set_color(browser, HE_COLORSET_ROOT);
267 ui_browser__write_nstring(browser, title, browser->width + 1);
270 void ui_browser__show_title(struct ui_browser *browser, const char *title)
272 pthread_mutex_lock(&ui__lock);
273 __ui_browser__show_title(browser, title);
274 pthread_mutex_unlock(&ui__lock);
277 int ui_browser__show(struct ui_browser *browser, const char *title,
278 const char *helpline, ...)
283 if (browser->refresh_dimensions == NULL)
284 browser->refresh_dimensions = ui_browser__refresh_dimensions;
286 browser->refresh_dimensions(browser);
288 pthread_mutex_lock(&ui__lock);
289 __ui_browser__show_title(browser, title);
291 browser->title = title;
292 zfree(&browser->helpline);
294 va_start(ap, helpline);
295 err = vasprintf(&browser->helpline, helpline, ap);
298 ui_helpline__push(browser->helpline);
299 pthread_mutex_unlock(&ui__lock);
303 void ui_browser__hide(struct ui_browser *browser)
305 pthread_mutex_lock(&ui__lock);
307 zfree(&browser->helpline);
308 pthread_mutex_unlock(&ui__lock);
311 static void ui_browser__scrollbar_set(struct ui_browser *browser)
313 int height = browser->height, h = 0, pct = 0,
314 col = browser->width,
317 if (browser->nr_entries > 1) {
318 pct = ((browser->index * (browser->height - 1)) /
319 (browser->nr_entries - 1));
322 SLsmg_set_char_set(1);
325 ui_browser__gotorc(browser, row++, col);
326 SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
330 SLsmg_set_char_set(0);
333 static int __ui_browser__refresh(struct ui_browser *browser)
336 int width = browser->width;
338 row = browser->refresh(browser);
339 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
341 if (!browser->use_navkeypressed || browser->navkeypressed)
342 ui_browser__scrollbar_set(browser);
346 SLsmg_fill_region(browser->y + row + browser->extra_title_lines, browser->x,
347 browser->rows - row, width, ' ');
349 if (browser->nr_entries == 0 && browser->no_samples_msg)
350 __ui__info_window(NULL, browser->no_samples_msg, NULL);
354 int ui_browser__refresh(struct ui_browser *browser)
356 pthread_mutex_lock(&ui__lock);
357 __ui_browser__refresh(browser);
358 pthread_mutex_unlock(&ui__lock);
364 * Here we're updating nr_entries _after_ we started browsing, i.e. we have to
365 * forget about any reference to any entry in the underlying data structure,
366 * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
367 * after an output_resort and hist decay.
369 void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
371 off_t offset = nr_entries - browser->nr_entries;
373 browser->nr_entries = nr_entries;
376 if (browser->top_idx < (u64)-offset)
377 offset = -browser->top_idx;
379 browser->index += offset;
380 browser->top_idx += offset;
384 browser->seek(browser, browser->top_idx, SEEK_SET);
387 int ui_browser__run(struct ui_browser *browser, int delay_secs)
394 pthread_mutex_lock(&ui__lock);
395 err = __ui_browser__refresh(browser);
397 pthread_mutex_unlock(&ui__lock);
401 key = ui__getch(delay_secs);
403 if (key == K_RESIZE) {
404 ui__refresh_dimensions(false);
405 browser->refresh_dimensions(browser);
406 __ui_browser__show_title(browser, browser->title);
407 ui_helpline__puts(browser->helpline);
411 if (browser->use_navkeypressed && !browser->navkeypressed) {
412 if (key == K_DOWN || key == K_UP ||
413 (browser->columns && (key == K_LEFT || key == K_RIGHT)) ||
414 key == K_PGDN || key == K_PGUP ||
415 key == K_HOME || key == K_END ||
417 browser->navkeypressed = true;
425 if (browser->index == browser->nr_entries - 1)
428 if (browser->index == browser->top_idx + browser->rows) {
430 browser->seek(browser, +1, SEEK_CUR);
434 if (browser->index == 0)
437 if (browser->index < browser->top_idx) {
439 browser->seek(browser, -1, SEEK_CUR);
443 if (!browser->columns)
445 if (browser->horiz_scroll < browser->columns - 1)
446 ++browser->horiz_scroll;
449 if (!browser->columns)
451 if (browser->horiz_scroll != 0)
452 --browser->horiz_scroll;
456 if (browser->top_idx + browser->rows > browser->nr_entries - 1)
459 offset = browser->rows;
460 if (browser->index + offset > browser->nr_entries - 1)
461 offset = browser->nr_entries - 1 - browser->index;
462 browser->index += offset;
463 browser->top_idx += offset;
464 browser->seek(browser, +offset, SEEK_CUR);
467 if (browser->top_idx == 0)
470 if (browser->top_idx < browser->rows)
471 offset = browser->top_idx;
473 offset = browser->rows;
475 browser->index -= offset;
476 browser->top_idx -= offset;
477 browser->seek(browser, -offset, SEEK_CUR);
480 ui_browser__reset_index(browser);
483 offset = browser->rows - 1;
484 if (offset >= browser->nr_entries)
485 offset = browser->nr_entries - 1;
487 browser->index = browser->nr_entries - 1;
488 browser->top_idx = browser->index - offset;
489 browser->seek(browser, -offset, SEEK_END);
499 unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
501 struct list_head *pos;
502 struct list_head *head = browser->entries;
505 if (browser->top == NULL || browser->top == browser->entries)
506 browser->top = ui_browser__list_head_filter_entries(browser, head->next);
510 list_for_each_from(pos, head) {
511 if (!browser->filter || !browser->filter(browser, pos)) {
512 ui_browser__gotorc(browser, row, 0);
513 browser->write(browser, pos, row);
514 if (++row == browser->rows)
522 static struct ui_browser_colorset {
523 const char *name, *fg, *bg;
525 } ui_browser__colorsets[] = {
527 .colorset = HE_COLORSET_TOP,
533 .colorset = HE_COLORSET_MEDIUM,
539 .colorset = HE_COLORSET_NORMAL,
545 .colorset = HE_COLORSET_SELECTED,
551 .colorset = HE_COLORSET_JUMP_ARROWS,
552 .name = "jump_arrows",
557 .colorset = HE_COLORSET_ADDR,
563 .colorset = HE_COLORSET_ROOT,
574 static int ui_browser__color_config(const char *var, const char *value,
575 void *data __maybe_unused)
577 char *fg = NULL, *bg;
580 /* same dir for all commands */
581 if (!strstarts(var, "colors.") != 0)
584 for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
585 const char *name = var + 7;
587 if (strcmp(ui_browser__colorsets[i].name, name) != 0)
594 bg = strchr(fg, ',');
599 bg = skip_spaces(bg + 1);
600 ui_browser__colorsets[i].bg = bg;
601 ui_browser__colorsets[i].fg = fg;
609 void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
613 browser->top = browser->entries;
616 browser->top = (char **)browser->top + offset;
619 browser->top = (char **)browser->entries + browser->nr_entries - 1 + offset;
624 assert((char **)browser->top < (char **)browser->entries + browser->nr_entries);
625 assert((char **)browser->top >= (char **)browser->entries);
628 unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
630 unsigned int row = 0, idx = browser->top_idx;
633 if (browser->top == NULL)
634 browser->top = browser->entries;
636 pos = (char **)browser->top;
637 while (idx < browser->nr_entries &&
638 row < (unsigned)SLtt_Screen_Rows - 1) {
639 assert(pos < (char **)browser->entries + browser->nr_entries);
640 if (!browser->filter || !browser->filter(browser, *pos)) {
641 ui_browser__gotorc(browser, row, 0);
642 browser->write(browser, pos, row);
643 if (++row == browser->rows)
654 void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
657 SLsmg_set_char_set(1);
658 ui_browser__gotorc(browser, start, column);
659 SLsmg_draw_vline(end - start + 1);
660 SLsmg_set_char_set(0);
663 void ui_browser__write_graph(struct ui_browser *browser __maybe_unused,
666 SLsmg_set_char_set(1);
667 SLsmg_write_char(graph);
668 SLsmg_set_char_set(0);
671 static void __ui_browser__line_arrow_up(struct ui_browser *browser,
675 unsigned int row, end_row;
677 SLsmg_set_char_set(1);
679 if (start < browser->top_idx + browser->rows) {
680 row = start - browser->top_idx;
681 ui_browser__gotorc(browser, row, column);
682 SLsmg_write_char(SLSMG_LLCORN_CHAR);
683 ui_browser__gotorc(browser, row, column + 1);
689 row = browser->rows - 1;
691 if (end > browser->top_idx)
692 end_row = end - browser->top_idx;
696 ui_browser__gotorc(browser, end_row, column);
697 SLsmg_draw_vline(row - end_row + 1);
699 ui_browser__gotorc(browser, end_row, column);
700 if (end >= browser->top_idx) {
701 SLsmg_write_char(SLSMG_ULCORN_CHAR);
702 ui_browser__gotorc(browser, end_row, column + 1);
703 SLsmg_write_char(SLSMG_HLINE_CHAR);
704 ui_browser__gotorc(browser, end_row, column + 2);
705 SLsmg_write_char(SLSMG_RARROW_CHAR);
708 SLsmg_set_char_set(0);
711 static void __ui_browser__line_arrow_down(struct ui_browser *browser,
715 unsigned int row, end_row;
717 SLsmg_set_char_set(1);
719 if (start >= browser->top_idx) {
720 row = start - browser->top_idx;
721 ui_browser__gotorc(browser, row, column);
722 SLsmg_write_char(SLSMG_ULCORN_CHAR);
723 ui_browser__gotorc(browser, row, column + 1);
731 if (end >= browser->top_idx + browser->rows)
732 end_row = browser->rows - 1;
734 end_row = end - browser->top_idx;
736 ui_browser__gotorc(browser, row, column);
737 SLsmg_draw_vline(end_row - row + 1);
739 ui_browser__gotorc(browser, end_row, column);
740 if (end < browser->top_idx + browser->rows) {
741 SLsmg_write_char(SLSMG_LLCORN_CHAR);
742 ui_browser__gotorc(browser, end_row, column + 1);
743 SLsmg_write_char(SLSMG_HLINE_CHAR);
744 ui_browser__gotorc(browser, end_row, column + 2);
745 SLsmg_write_char(SLSMG_RARROW_CHAR);
748 SLsmg_set_char_set(0);
751 void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
755 __ui_browser__line_arrow_up(browser, column, start, end);
757 __ui_browser__line_arrow_down(browser, column, start, end);
760 void ui_browser__mark_fused(struct ui_browser *browser, unsigned int column,
761 unsigned int row, bool arrow_down)
763 unsigned int end_row;
765 if (row >= browser->top_idx)
766 end_row = row - browser->top_idx;
770 SLsmg_set_char_set(1);
773 ui_browser__gotorc(browser, end_row, column - 1);
774 SLsmg_write_char(SLSMG_ULCORN_CHAR);
775 ui_browser__gotorc(browser, end_row, column);
777 ui_browser__gotorc(browser, end_row + 1, column - 1);
778 SLsmg_write_char(SLSMG_LTEE_CHAR);
780 ui_browser__gotorc(browser, end_row, column - 1);
781 SLsmg_write_char(SLSMG_LTEE_CHAR);
782 ui_browser__gotorc(browser, end_row, column);
786 SLsmg_set_char_set(0);
789 void ui_browser__init(void)
793 perf_config(ui_browser__color_config, NULL);
795 while (ui_browser__colorsets[i].name) {
796 struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
797 sltt_set_color(c->colorset, c->name, c->fg, c->bg);