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