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