1 // SPDX-License-Identifier: GPL-2.0
3 #include "../string2.h"
5 #include "../../perf.h"
9 #include <linux/compiler.h>
10 #include <linux/list.h>
11 #include <linux/rbtree.h>
12 #include <linux/string.h>
14 #include <sys/ttydefaults.h>
19 #include "sane_ctype.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, ' ');
352 int ui_browser__refresh(struct ui_browser *browser)
354 pthread_mutex_lock(&ui__lock);
355 __ui_browser__refresh(browser);
356 pthread_mutex_unlock(&ui__lock);
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.
367 void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
369 off_t offset = nr_entries - browser->nr_entries;
371 browser->nr_entries = nr_entries;
374 if (browser->top_idx < (u64)-offset)
375 offset = -browser->top_idx;
377 browser->index += offset;
378 browser->top_idx += offset;
382 browser->seek(browser, browser->top_idx, SEEK_SET);
385 int ui_browser__run(struct ui_browser *browser, int delay_secs)
392 pthread_mutex_lock(&ui__lock);
393 err = __ui_browser__refresh(browser);
395 pthread_mutex_unlock(&ui__lock);
399 key = ui__getch(delay_secs);
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);
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 ||
415 browser->navkeypressed = true;
423 if (browser->index == browser->nr_entries - 1)
426 if (browser->index == browser->top_idx + browser->rows) {
428 browser->seek(browser, +1, SEEK_CUR);
432 if (browser->index == 0)
435 if (browser->index < browser->top_idx) {
437 browser->seek(browser, -1, SEEK_CUR);
441 if (!browser->columns)
443 if (browser->horiz_scroll < browser->columns - 1)
444 ++browser->horiz_scroll;
447 if (!browser->columns)
449 if (browser->horiz_scroll != 0)
450 --browser->horiz_scroll;
454 if (browser->top_idx + browser->rows > browser->nr_entries - 1)
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);
465 if (browser->top_idx == 0)
468 if (browser->top_idx < browser->rows)
469 offset = browser->top_idx;
471 offset = browser->rows;
473 browser->index -= offset;
474 browser->top_idx -= offset;
475 browser->seek(browser, -offset, SEEK_CUR);
478 ui_browser__reset_index(browser);
481 offset = browser->rows - 1;
482 if (offset >= browser->nr_entries)
483 offset = browser->nr_entries - 1;
485 browser->index = browser->nr_entries - 1;
486 browser->top_idx = browser->index - offset;
487 browser->seek(browser, -offset, SEEK_END);
497 unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
499 struct list_head *pos;
500 struct list_head *head = browser->entries;
503 if (browser->top == NULL || browser->top == browser->entries)
504 browser->top = ui_browser__list_head_filter_entries(browser, head->next);
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)
520 static struct ui_browser_colorset {
521 const char *name, *fg, *bg;
523 } ui_browser__colorsets[] = {
525 .colorset = HE_COLORSET_TOP,
531 .colorset = HE_COLORSET_MEDIUM,
537 .colorset = HE_COLORSET_NORMAL,
543 .colorset = HE_COLORSET_SELECTED,
549 .colorset = HE_COLORSET_JUMP_ARROWS,
550 .name = "jump_arrows",
555 .colorset = HE_COLORSET_ADDR,
561 .colorset = HE_COLORSET_ROOT,
572 static int ui_browser__color_config(const char *var, const char *value,
573 void *data __maybe_unused)
575 char *fg = NULL, *bg;
578 /* same dir for all commands */
579 if (!strstarts(var, "colors.") != 0)
582 for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
583 const char *name = var + 7;
585 if (strcmp(ui_browser__colorsets[i].name, name) != 0)
592 bg = strchr(fg, ',');
598 ui_browser__colorsets[i].bg = bg;
599 ui_browser__colorsets[i].fg = fg;
607 void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
611 browser->top = browser->entries;
614 browser->top = browser->top + browser->top_idx + offset;
617 browser->top = browser->top + browser->nr_entries - 1 + offset;
624 unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
626 unsigned int row = 0, idx = browser->top_idx;
629 if (browser->top == NULL)
630 browser->top = browser->entries;
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)
648 void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
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);
657 void ui_browser__write_graph(struct ui_browser *browser __maybe_unused,
660 SLsmg_set_char_set(1);
661 SLsmg_write_char(graph);
662 SLsmg_set_char_set(0);
665 static void __ui_browser__line_arrow_up(struct ui_browser *browser,
669 unsigned int row, end_row;
671 SLsmg_set_char_set(1);
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);
683 row = browser->rows - 1;
685 if (end > browser->top_idx)
686 end_row = end - browser->top_idx;
690 ui_browser__gotorc(browser, end_row, column);
691 SLsmg_draw_vline(row - end_row + 1);
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);
702 SLsmg_set_char_set(0);
705 static void __ui_browser__line_arrow_down(struct ui_browser *browser,
709 unsigned int row, end_row;
711 SLsmg_set_char_set(1);
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);
725 if (end >= browser->top_idx + browser->rows)
726 end_row = browser->rows - 1;
728 end_row = end - browser->top_idx;
730 ui_browser__gotorc(browser, row, column);
731 SLsmg_draw_vline(end_row - row + 1);
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);
742 SLsmg_set_char_set(0);
745 void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
749 __ui_browser__line_arrow_up(browser, column, start, end);
751 __ui_browser__line_arrow_down(browser, column, start, end);
754 void ui_browser__mark_fused(struct ui_browser *browser, unsigned int column,
755 unsigned int row, bool arrow_down)
757 unsigned int end_row;
759 if (row >= browser->top_idx)
760 end_row = row - browser->top_idx;
764 SLsmg_set_char_set(1);
767 ui_browser__gotorc(browser, end_row, column - 1);
768 SLsmg_write_char(SLSMG_ULCORN_CHAR);
769 ui_browser__gotorc(browser, end_row, column);
771 ui_browser__gotorc(browser, end_row + 1, column - 1);
772 SLsmg_write_char(SLSMG_LTEE_CHAR);
774 ui_browser__gotorc(browser, end_row, column - 1);
775 SLsmg_write_char(SLSMG_LTEE_CHAR);
776 ui_browser__gotorc(browser, end_row, column);
780 SLsmg_set_char_set(0);
783 void ui_browser__init(void)
787 perf_config(ui_browser__color_config, NULL);
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);