xref: /openbmc/linux/tools/perf/ui/browser.c (revision 9d749629)
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 	free(browser->helpline);
277 	browser->helpline = NULL;
278 	pthread_mutex_unlock(&ui__lock);
279 }
280 
281 static void ui_browser__scrollbar_set(struct ui_browser *browser)
282 {
283 	int height = browser->height, h = 0, pct = 0,
284 	    col = browser->width,
285 	    row = browser->y - 1;
286 
287 	if (browser->nr_entries > 1) {
288 		pct = ((browser->index * (browser->height - 1)) /
289 		       (browser->nr_entries - 1));
290 	}
291 
292 	SLsmg_set_char_set(1);
293 
294 	while (h < height) {
295 	        ui_browser__gotorc(browser, row++, col);
296 		SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
297 		++h;
298 	}
299 
300 	SLsmg_set_char_set(0);
301 }
302 
303 static int __ui_browser__refresh(struct ui_browser *browser)
304 {
305 	int row;
306 	int width = browser->width;
307 
308 	row = browser->refresh(browser);
309 	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
310 
311 	if (!browser->use_navkeypressed || browser->navkeypressed)
312 		ui_browser__scrollbar_set(browser);
313 	else
314 		width += 1;
315 
316 	SLsmg_fill_region(browser->y + row, browser->x,
317 			  browser->height - row, width, ' ');
318 
319 	return 0;
320 }
321 
322 int ui_browser__refresh(struct ui_browser *browser)
323 {
324 	pthread_mutex_lock(&ui__lock);
325 	__ui_browser__refresh(browser);
326 	pthread_mutex_unlock(&ui__lock);
327 
328 	return 0;
329 }
330 
331 /*
332  * Here we're updating nr_entries _after_ we started browsing, i.e.  we have to
333  * forget about any reference to any entry in the underlying data structure,
334  * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
335  * after an output_resort and hist decay.
336  */
337 void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
338 {
339 	off_t offset = nr_entries - browser->nr_entries;
340 
341 	browser->nr_entries = nr_entries;
342 
343 	if (offset < 0) {
344 		if (browser->top_idx < (u64)-offset)
345 			offset = -browser->top_idx;
346 
347 		browser->index += offset;
348 		browser->top_idx += offset;
349 	}
350 
351 	browser->top = NULL;
352 	browser->seek(browser, browser->top_idx, SEEK_SET);
353 }
354 
355 int ui_browser__run(struct ui_browser *browser, int delay_secs)
356 {
357 	int err, key;
358 
359 	while (1) {
360 		off_t offset;
361 
362 		pthread_mutex_lock(&ui__lock);
363 		err = __ui_browser__refresh(browser);
364 		SLsmg_refresh();
365 		pthread_mutex_unlock(&ui__lock);
366 		if (err < 0)
367 			break;
368 
369 		key = ui__getch(delay_secs);
370 
371 		if (key == K_RESIZE) {
372 			ui__refresh_dimensions(false);
373 			ui_browser__refresh_dimensions(browser);
374 			__ui_browser__show_title(browser, browser->title);
375 			ui_helpline__puts(browser->helpline);
376 			continue;
377 		}
378 
379 		if (browser->use_navkeypressed && !browser->navkeypressed) {
380 			if (key == K_DOWN || key == K_UP ||
381 			    key == K_PGDN || key == K_PGUP ||
382 			    key == K_HOME || key == K_END ||
383 			    key == ' ') {
384 				browser->navkeypressed = true;
385 				continue;
386 			} else
387 				return key;
388 		}
389 
390 		switch (key) {
391 		case K_DOWN:
392 			if (browser->index == browser->nr_entries - 1)
393 				break;
394 			++browser->index;
395 			if (browser->index == browser->top_idx + browser->height) {
396 				++browser->top_idx;
397 				browser->seek(browser, +1, SEEK_CUR);
398 			}
399 			break;
400 		case K_UP:
401 			if (browser->index == 0)
402 				break;
403 			--browser->index;
404 			if (browser->index < browser->top_idx) {
405 				--browser->top_idx;
406 				browser->seek(browser, -1, SEEK_CUR);
407 			}
408 			break;
409 		case K_PGDN:
410 		case ' ':
411 			if (browser->top_idx + browser->height > browser->nr_entries - 1)
412 				break;
413 
414 			offset = browser->height;
415 			if (browser->index + offset > browser->nr_entries - 1)
416 				offset = browser->nr_entries - 1 - browser->index;
417 			browser->index += offset;
418 			browser->top_idx += offset;
419 			browser->seek(browser, +offset, SEEK_CUR);
420 			break;
421 		case K_PGUP:
422 			if (browser->top_idx == 0)
423 				break;
424 
425 			if (browser->top_idx < browser->height)
426 				offset = browser->top_idx;
427 			else
428 				offset = browser->height;
429 
430 			browser->index -= offset;
431 			browser->top_idx -= offset;
432 			browser->seek(browser, -offset, SEEK_CUR);
433 			break;
434 		case K_HOME:
435 			ui_browser__reset_index(browser);
436 			break;
437 		case K_END:
438 			offset = browser->height - 1;
439 			if (offset >= browser->nr_entries)
440 				offset = browser->nr_entries - 1;
441 
442 			browser->index = browser->nr_entries - 1;
443 			browser->top_idx = browser->index - offset;
444 			browser->seek(browser, -offset, SEEK_END);
445 			break;
446 		default:
447 			return key;
448 		}
449 	}
450 	return -1;
451 }
452 
453 unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
454 {
455 	struct list_head *pos;
456 	struct list_head *head = browser->entries;
457 	int row = 0;
458 
459 	if (browser->top == NULL || browser->top == browser->entries)
460                 browser->top = ui_browser__list_head_filter_entries(browser, head->next);
461 
462 	pos = browser->top;
463 
464 	list_for_each_from(pos, head) {
465 		if (!browser->filter || !browser->filter(browser, pos)) {
466 			ui_browser__gotorc(browser, row, 0);
467 			browser->write(browser, pos, row);
468 			if (++row == browser->height)
469 				break;
470 		}
471 	}
472 
473 	return row;
474 }
475 
476 static struct ui_browser_colorset {
477 	const char *name, *fg, *bg;
478 	int colorset;
479 } ui_browser__colorsets[] = {
480 	{
481 		.colorset = HE_COLORSET_TOP,
482 		.name	  = "top",
483 		.fg	  = "red",
484 		.bg	  = "default",
485 	},
486 	{
487 		.colorset = HE_COLORSET_MEDIUM,
488 		.name	  = "medium",
489 		.fg	  = "green",
490 		.bg	  = "default",
491 	},
492 	{
493 		.colorset = HE_COLORSET_NORMAL,
494 		.name	  = "normal",
495 		.fg	  = "default",
496 		.bg	  = "default",
497 	},
498 	{
499 		.colorset = HE_COLORSET_SELECTED,
500 		.name	  = "selected",
501 		.fg	  = "black",
502 		.bg	  = "lightgray",
503 	},
504 	{
505 		.colorset = HE_COLORSET_CODE,
506 		.name	  = "code",
507 		.fg	  = "blue",
508 		.bg	  = "default",
509 	},
510 	{
511 		.colorset = HE_COLORSET_ADDR,
512 		.name	  = "addr",
513 		.fg	  = "magenta",
514 		.bg	  = "default",
515 	},
516 	{
517 		.name = NULL,
518 	}
519 };
520 
521 
522 static int ui_browser__color_config(const char *var, const char *value,
523 				    void *data __maybe_unused)
524 {
525 	char *fg = NULL, *bg;
526 	int i;
527 
528 	/* same dir for all commands */
529 	if (prefixcmp(var, "colors.") != 0)
530 		return 0;
531 
532 	for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
533 		const char *name = var + 7;
534 
535 		if (strcmp(ui_browser__colorsets[i].name, name) != 0)
536 			continue;
537 
538 		fg = strdup(value);
539 		if (fg == NULL)
540 			break;
541 
542 		bg = strchr(fg, ',');
543 		if (bg == NULL)
544 			break;
545 
546 		*bg = '\0';
547 		while (isspace(*++bg));
548 		ui_browser__colorsets[i].bg = bg;
549 		ui_browser__colorsets[i].fg = fg;
550 		return 0;
551 	}
552 
553 	free(fg);
554 	return -1;
555 }
556 
557 void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
558 {
559 	switch (whence) {
560 	case SEEK_SET:
561 		browser->top = browser->entries;
562 		break;
563 	case SEEK_CUR:
564 		browser->top = browser->top + browser->top_idx + offset;
565 		break;
566 	case SEEK_END:
567 		browser->top = browser->top + browser->nr_entries + offset;
568 		break;
569 	default:
570 		return;
571 	}
572 }
573 
574 unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
575 {
576 	unsigned int row = 0, idx = browser->top_idx;
577 	char **pos;
578 
579 	if (browser->top == NULL)
580 		browser->top = browser->entries;
581 
582 	pos = (char **)browser->top;
583 	while (idx < browser->nr_entries) {
584 		if (!browser->filter || !browser->filter(browser, *pos)) {
585 			ui_browser__gotorc(browser, row, 0);
586 			browser->write(browser, pos, row);
587 			if (++row == browser->height)
588 				break;
589 		}
590 
591 		++idx;
592 		++pos;
593 	}
594 
595 	return row;
596 }
597 
598 void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
599 			 u16 start, u16 end)
600 {
601 	SLsmg_set_char_set(1);
602 	ui_browser__gotorc(browser, start, column);
603 	SLsmg_draw_vline(end - start + 1);
604 	SLsmg_set_char_set(0);
605 }
606 
607 void ui_browser__write_graph(struct ui_browser *browser __maybe_unused,
608 			     int graph)
609 {
610 	SLsmg_set_char_set(1);
611 	SLsmg_write_char(graph);
612 	SLsmg_set_char_set(0);
613 }
614 
615 static void __ui_browser__line_arrow_up(struct ui_browser *browser,
616 					unsigned int column,
617 					u64 start, u64 end)
618 {
619 	unsigned int row, end_row;
620 
621 	SLsmg_set_char_set(1);
622 
623 	if (start < browser->top_idx + browser->height) {
624 		row = start - browser->top_idx;
625 		ui_browser__gotorc(browser, row, column);
626 		SLsmg_write_char(SLSMG_LLCORN_CHAR);
627 		ui_browser__gotorc(browser, row, column + 1);
628 		SLsmg_draw_hline(2);
629 
630 		if (row-- == 0)
631 			goto out;
632 	} else
633 		row = browser->height - 1;
634 
635 	if (end > browser->top_idx)
636 		end_row = end - browser->top_idx;
637 	else
638 		end_row = 0;
639 
640 	ui_browser__gotorc(browser, end_row, column);
641 	SLsmg_draw_vline(row - end_row + 1);
642 
643 	ui_browser__gotorc(browser, end_row, column);
644 	if (end >= browser->top_idx) {
645 		SLsmg_write_char(SLSMG_ULCORN_CHAR);
646 		ui_browser__gotorc(browser, end_row, column + 1);
647 		SLsmg_write_char(SLSMG_HLINE_CHAR);
648 		ui_browser__gotorc(browser, end_row, column + 2);
649 		SLsmg_write_char(SLSMG_RARROW_CHAR);
650 	}
651 out:
652 	SLsmg_set_char_set(0);
653 }
654 
655 static void __ui_browser__line_arrow_down(struct ui_browser *browser,
656 					  unsigned int column,
657 					  u64 start, u64 end)
658 {
659 	unsigned int row, end_row;
660 
661 	SLsmg_set_char_set(1);
662 
663 	if (start >= browser->top_idx) {
664 		row = start - browser->top_idx;
665 		ui_browser__gotorc(browser, row, column);
666 		SLsmg_write_char(SLSMG_ULCORN_CHAR);
667 		ui_browser__gotorc(browser, row, column + 1);
668 		SLsmg_draw_hline(2);
669 
670 		if (row++ == 0)
671 			goto out;
672 	} else
673 		row = 0;
674 
675 	if (end >= browser->top_idx + browser->height)
676 		end_row = browser->height - 1;
677 	else
678 		end_row = end - browser->top_idx;;
679 
680 	ui_browser__gotorc(browser, row, column);
681 	SLsmg_draw_vline(end_row - row + 1);
682 
683 	ui_browser__gotorc(browser, end_row, column);
684 	if (end < browser->top_idx + browser->height) {
685 		SLsmg_write_char(SLSMG_LLCORN_CHAR);
686 		ui_browser__gotorc(browser, end_row, column + 1);
687 		SLsmg_write_char(SLSMG_HLINE_CHAR);
688 		ui_browser__gotorc(browser, end_row, column + 2);
689 		SLsmg_write_char(SLSMG_RARROW_CHAR);
690 	}
691 out:
692 	SLsmg_set_char_set(0);
693 }
694 
695 void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
696 			      u64 start, u64 end)
697 {
698 	if (start > end)
699 		__ui_browser__line_arrow_up(browser, column, start, end);
700 	else
701 		__ui_browser__line_arrow_down(browser, column, start, end);
702 }
703 
704 void ui_browser__init(void)
705 {
706 	int i = 0;
707 
708 	perf_config(ui_browser__color_config, NULL);
709 
710 	while (ui_browser__colorsets[i].name) {
711 		struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
712 		sltt_set_color(c->colorset, c->name, c->fg, c->bg);
713 	}
714 
715 	annotate_browser__init();
716 }
717