browser.c 16.3 KB
Newer Older
1
#include "../util.h"
2
#include "../config.h"
3
#include "../../perf.h"
4
#include "libslang.h"
5
#include "ui.h"
6
#include "util.h"
7
#include <linux/compiler.h>
8 9 10 11 12
#include <linux/list.h>
#include <linux/rbtree.h>
#include <stdlib.h>
#include <sys/ttydefaults.h>
#include "browser.h"
13
#include "helpline.h"
14
#include "keysyms.h"
15
#include "../color.h"
16
#include "sane_ctype.h"
17

18 19
static int ui_browser__percent_color(struct ui_browser *browser,
				     double percent, bool current)
20
{
21
	if (current && (!browser->use_navkeypressed || browser->navkeypressed))
22 23 24 25 26 27 28 29
		return HE_COLORSET_SELECTED;
	if (percent >= MIN_RED)
		return HE_COLORSET_TOP;
	if (percent >= MIN_GREEN)
		return HE_COLORSET_MEDIUM;
	return HE_COLORSET_NORMAL;
}

30
int ui_browser__set_color(struct ui_browser *browser, int color)
31
{
32 33
	int ret = browser->current_color;
	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 40
				   double percent, bool current)
{
41 42
	 int color = ui_browser__percent_color(browser, percent, current);
	 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 51 52 53 54 55
void ui_browser__write_nstring(struct ui_browser *browser __maybe_unused, const char *msg,
			       unsigned int width)
{
	slsmg_write_nstring(msg, width);
}

56 57 58 59 60 61 62 63 64
void ui_browser__printf(struct ui_browser *browser __maybe_unused, const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	slsmg_vprintf(fmt, args);
	va_end(args);
}

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
static struct list_head *
ui_browser__list_head_filter_entries(struct ui_browser *browser,
				     struct list_head *pos)
{
	do {
		if (!browser->filter || !browser->filter(browser, pos))
			return pos;
		pos = pos->next;
	} while (pos != browser->entries);

	return NULL;
}

static struct list_head *
ui_browser__list_head_filter_prev_entries(struct ui_browser *browser,
					  struct list_head *pos)
{
	do {
		if (!browser->filter || !browser->filter(browser, pos))
			return pos;
		pos = pos->prev;
	} while (pos != browser->entries);

	return NULL;
}

91
void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int whence)
92
{
93
	struct list_head *head = browser->entries;
94 95
	struct list_head *pos;

96
	if (browser->nr_entries == 0)
97 98
		return;

99 100
	switch (whence) {
	case SEEK_SET:
101
		pos = ui_browser__list_head_filter_entries(browser, head->next);
102 103
		break;
	case SEEK_CUR:
104
		pos = browser->top;
105 106
		break;
	case SEEK_END:
107
		pos = ui_browser__list_head_filter_prev_entries(browser, head->prev);
108 109 110 111 112
		break;
	default:
		return;
	}

113 114
	assert(pos != NULL);

115 116
	if (offset > 0) {
		while (offset-- != 0)
117
			pos = ui_browser__list_head_filter_entries(browser, pos->next);
118 119
	} else {
		while (offset++ != 0)
120
			pos = ui_browser__list_head_filter_prev_entries(browser, pos->prev);
121 122
	}

123
	browser->top = pos;
124 125
}

126
void ui_browser__rb_tree_seek(struct ui_browser *browser, off_t offset, int whence)
127
{
128
	struct rb_root *root = browser->entries;
129 130 131 132 133 134 135
	struct rb_node *nd;

	switch (whence) {
	case SEEK_SET:
		nd = rb_first(root);
		break;
	case SEEK_CUR:
136
		nd = browser->top;
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
		break;
	case SEEK_END:
		nd = rb_last(root);
		break;
	default:
		return;
	}

	if (offset > 0) {
		while (offset-- != 0)
			nd = rb_next(nd);
	} else {
		while (offset++ != 0)
			nd = rb_prev(nd);
	}

153
	browser->top = nd;
154 155
}

156
unsigned int ui_browser__rb_tree_refresh(struct ui_browser *browser)
157 158 159 160
{
	struct rb_node *nd;
	int row = 0;

161 162
	if (browser->top == NULL)
                browser->top = rb_first(browser->entries);
163

164
	nd = browser->top;
165 166

	while (nd != NULL) {
167 168
		ui_browser__gotorc(browser, row, 0);
		browser->write(browser, nd, row);
169
		if (++row == browser->rows)
170 171 172 173 174 175 176
			break;
		nd = rb_next(nd);
	}

	return row;
}

177
bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row)
178
{
179
	return browser->top_idx + row == browser->index;
180 181
}

182
void ui_browser__refresh_dimensions(struct ui_browser *browser)
183
{
184
	browser->width = SLtt_Screen_Cols - 1;
185
	browser->height = browser->rows = SLtt_Screen_Rows - 2;
186 187
	browser->y = 1;
	browser->x = 0;
188 189
}

190 191 192 193 194 195 196
void ui_browser__handle_resize(struct ui_browser *browser)
{
	ui__refresh_dimensions(false);
	ui_browser__show(browser, browser->title, ui_helpline__current);
	ui_browser__refresh(browser);
}

197 198
int ui_browser__warning(struct ui_browser *browser, int timeout,
			const char *format, ...)
199 200
{
	va_list args;
201 202
	char *text;
	int key = 0, err;
203 204

	va_start(args, format);
205
	err = vasprintf(&text, format, args);
206 207
	va_end(args);

208 209 210 211 212
	if (err < 0) {
		va_start(args, format);
		ui_helpline__vpush(format, args);
		va_end(args);
	} else {
213
		while ((key = ui__question_window("Warning!", text,
214 215 216 217 218 219
						   "Press any key...",
						   timeout)) == K_RESIZE)
			ui_browser__handle_resize(browser);
		free(text);
	}

220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
	return key;
}

int ui_browser__help_window(struct ui_browser *browser, const char *text)
{
	int key;

	while ((key = ui__help_window(text)) == K_RESIZE)
		ui_browser__handle_resize(browser);

	return key;
}

bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text)
{
	int key;

	while ((key = ui__dialog_yesno(text)) == K_RESIZE)
		ui_browser__handle_resize(browser);

	return key == K_ENTER || toupper(key) == 'Y';
}

243
void ui_browser__reset_index(struct ui_browser *browser)
244
{
245 246
	browser->index = browser->top_idx = 0;
	browser->seek(browser, 0, SEEK_SET);
247 248
}

249 250 251
void __ui_browser__show_title(struct ui_browser *browser, const char *title)
{
	SLsmg_gotorc(0, 0);
252
	ui_browser__set_color(browser, HE_COLORSET_ROOT);
253
	ui_browser__write_nstring(browser, title, browser->width + 1);
254 255 256 257 258 259 260 261 262
}

void ui_browser__show_title(struct ui_browser *browser, const char *title)
{
	pthread_mutex_lock(&ui__lock);
	__ui_browser__show_title(browser, title);
	pthread_mutex_unlock(&ui__lock);
}

263
int ui_browser__show(struct ui_browser *browser, const char *title,
264
		     const char *helpline, ...)
265
{
266
	int err;
267 268
	va_list ap;

269 270 271 272
	if (browser->refresh_dimensions == NULL)
		browser->refresh_dimensions = ui_browser__refresh_dimensions;

	browser->refresh_dimensions(browser);
273

274
	pthread_mutex_lock(&ui__lock);
275
	__ui_browser__show_title(browser, title);
276

277
	browser->title = title;
278
	zfree(&browser->helpline);
279 280

	va_start(ap, helpline);
281
	err = vasprintf(&browser->helpline, helpline, ap);
282
	va_end(ap);
283
	if (err > 0)
284
		ui_helpline__push(browser->helpline);
285
	pthread_mutex_unlock(&ui__lock);
286
	return err ? 0 : -1;
287 288
}

289
void ui_browser__hide(struct ui_browser *browser)
290
{
291
	pthread_mutex_lock(&ui__lock);
292
	ui_helpline__pop();
293
	zfree(&browser->helpline);
294
	pthread_mutex_unlock(&ui__lock);
295 296
}

297 298 299 300
static void ui_browser__scrollbar_set(struct ui_browser *browser)
{
	int height = browser->height, h = 0, pct = 0,
	    col = browser->width,
301
	    row = 0;
302 303 304 305 306 307

	if (browser->nr_entries > 1) {
		pct = ((browser->index * (browser->height - 1)) /
		       (browser->nr_entries - 1));
	}

308 309
	SLsmg_set_char_set(1);

310 311
	while (h < height) {
	        ui_browser__gotorc(browser, row++, col);
312
		SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
313 314
		++h;
	}
315 316

	SLsmg_set_char_set(0);
317 318 319
}

static int __ui_browser__refresh(struct ui_browser *browser)
320 321
{
	int row;
322
	int width = browser->width;
323

324 325
	row = browser->refresh(browser);
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
326 327 328 329 330 331

	if (!browser->use_navkeypressed || browser->navkeypressed)
		ui_browser__scrollbar_set(browser);
	else
		width += 1;

332
	SLsmg_fill_region(browser->y + row, browser->x,
333
			  browser->height - row, width, ' ');
334 335 336 337 338 339

	return 0;
}

int ui_browser__refresh(struct ui_browser *browser)
{
340
	pthread_mutex_lock(&ui__lock);
341
	__ui_browser__refresh(browser);
342
	pthread_mutex_unlock(&ui__lock);
343 344 345 346

	return 0;
}

347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
/*
 * Here we're updating nr_entries _after_ we started browsing, i.e.  we have to
 * forget about any reference to any entry in the underlying data structure,
 * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
 * after an output_resort and hist decay.
 */
void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
{
	off_t offset = nr_entries - browser->nr_entries;

	browser->nr_entries = nr_entries;

	if (offset < 0) {
		if (browser->top_idx < (u64)-offset)
			offset = -browser->top_idx;

		browser->index += offset;
		browser->top_idx += offset;
	}

367
	browser->top = NULL;
368 369 370
	browser->seek(browser, browser->top_idx, SEEK_SET);
}

371
int ui_browser__run(struct ui_browser *browser, int delay_secs)
372
{
373
	int err, key;
374

375 376 377
	while (1) {
		off_t offset;

378
		pthread_mutex_lock(&ui__lock);
379
		err = __ui_browser__refresh(browser);
380 381 382 383 384
		SLsmg_refresh();
		pthread_mutex_unlock(&ui__lock);
		if (err < 0)
			break;

385
		key = ui__getch(delay_secs);
386

387
		if (key == K_RESIZE) {
388
			ui__refresh_dimensions(false);
389
			browser->refresh_dimensions(browser);
390 391
			__ui_browser__show_title(browser, browser->title);
			ui_helpline__puts(browser->helpline);
392 393 394
			continue;
		}

395
		if (browser->use_navkeypressed && !browser->navkeypressed) {
396
			if (key == K_DOWN || key == K_UP ||
397
			    (browser->columns && (key == K_LEFT || key == K_RIGHT)) ||
398 399
			    key == K_PGDN || key == K_PGUP ||
			    key == K_HOME || key == K_END ||
400
			    key == ' ') {
401
				browser->navkeypressed = true;
402 403 404 405 406
				continue;
			} else
				return key;
		}

407
		switch (key) {
408
		case K_DOWN:
409
			if (browser->index == browser->nr_entries - 1)
410
				break;
411
			++browser->index;
412
			if (browser->index == browser->top_idx + browser->rows) {
413 414
				++browser->top_idx;
				browser->seek(browser, +1, SEEK_CUR);
415 416
			}
			break;
417
		case K_UP:
418
			if (browser->index == 0)
419
				break;
420 421 422 423
			--browser->index;
			if (browser->index < browser->top_idx) {
				--browser->top_idx;
				browser->seek(browser, -1, SEEK_CUR);
424 425
			}
			break;
426 427 428 429 430 431 432 433 434 435 436 437
		case K_RIGHT:
			if (!browser->columns)
				goto out;
			if (browser->horiz_scroll < browser->columns - 1)
				++browser->horiz_scroll;
			break;
		case K_LEFT:
			if (!browser->columns)
				goto out;
			if (browser->horiz_scroll != 0)
				--browser->horiz_scroll;
			break;
438
		case K_PGDN:
439
		case ' ':
440
			if (browser->top_idx + browser->rows > browser->nr_entries - 1)
441 442
				break;

443
			offset = browser->rows;
444 445 446 447 448
			if (browser->index + offset > browser->nr_entries - 1)
				offset = browser->nr_entries - 1 - browser->index;
			browser->index += offset;
			browser->top_idx += offset;
			browser->seek(browser, +offset, SEEK_CUR);
449
			break;
450
		case K_PGUP:
451
			if (browser->top_idx == 0)
452 453
				break;

454
			if (browser->top_idx < browser->rows)
455
				offset = browser->top_idx;
456
			else
457
				offset = browser->rows;
458

459 460 461
			browser->index -= offset;
			browser->top_idx -= offset;
			browser->seek(browser, -offset, SEEK_CUR);
462
			break;
463
		case K_HOME:
464
			ui_browser__reset_index(browser);
465
			break;
466
		case K_END:
467
			offset = browser->rows - 1;
468 469
			if (offset >= browser->nr_entries)
				offset = browser->nr_entries - 1;
470

471 472 473
			browser->index = browser->nr_entries - 1;
			browser->top_idx = browser->index - offset;
			browser->seek(browser, -offset, SEEK_END);
474 475
			break;
		default:
476
		out:
477
			return key;
478 479
		}
	}
480
	return -1;
481 482
}

483
unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
484 485
{
	struct list_head *pos;
486
	struct list_head *head = browser->entries;
487 488
	int row = 0;

489 490
	if (browser->top == NULL || browser->top == browser->entries)
                browser->top = ui_browser__list_head_filter_entries(browser, head->next);
491

492
	pos = browser->top;
493 494

	list_for_each_from(pos, head) {
495 496 497
		if (!browser->filter || !browser->filter(browser, pos)) {
			ui_browser__gotorc(browser, row, 0);
			browser->write(browser, pos, row);
498
			if (++row == browser->rows)
499 500
				break;
		}
501 502 503 504 505
	}

	return row;
}

506
static struct ui_browser_colorset {
507 508 509 510 511 512 513
	const char *name, *fg, *bg;
	int colorset;
} ui_browser__colorsets[] = {
	{
		.colorset = HE_COLORSET_TOP,
		.name	  = "top",
		.fg	  = "red",
514
		.bg	  = "default",
515 516 517 518 519
	},
	{
		.colorset = HE_COLORSET_MEDIUM,
		.name	  = "medium",
		.fg	  = "green",
520
		.bg	  = "default",
521 522 523 524
	},
	{
		.colorset = HE_COLORSET_NORMAL,
		.name	  = "normal",
525 526
		.fg	  = "default",
		.bg	  = "default",
527 528 529 530 531
	},
	{
		.colorset = HE_COLORSET_SELECTED,
		.name	  = "selected",
		.fg	  = "black",
532
		.bg	  = "yellow",
533 534
	},
	{
535 536
		.colorset = HE_COLORSET_JUMP_ARROWS,
		.name	  = "jump_arrows",
537
		.fg	  = "blue",
538
		.bg	  = "default",
539
	},
540 541 542 543 544 545
	{
		.colorset = HE_COLORSET_ADDR,
		.name	  = "addr",
		.fg	  = "magenta",
		.bg	  = "default",
	},
546 547 548 549 550 551
	{
		.colorset = HE_COLORSET_ROOT,
		.name	  = "root",
		.fg	  = "white",
		.bg	  = "blue",
	},
552 553 554
	{
		.name = NULL,
	}
555 556
};

557 558

static int ui_browser__color_config(const char *var, const char *value,
559
				    void *data __maybe_unused)
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
{
	char *fg = NULL, *bg;
	int i;

	/* same dir for all commands */
	if (prefixcmp(var, "colors.") != 0)
		return 0;

	for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
		const char *name = var + 7;

		if (strcmp(ui_browser__colorsets[i].name, name) != 0)
			continue;

		fg = strdup(value);
		if (fg == NULL)
			break;

		bg = strchr(fg, ',');
		if (bg == NULL)
			break;

		*bg = '\0';
583
		bg = ltrim(++bg);
584 585 586 587 588 589 590 591 592
		ui_browser__colorsets[i].bg = bg;
		ui_browser__colorsets[i].fg = fg;
		return 0;
	}

	free(fg);
	return -1;
}

593 594 595 596 597 598 599 600 601 602
void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
{
	switch (whence) {
	case SEEK_SET:
		browser->top = browser->entries;
		break;
	case SEEK_CUR:
		browser->top = browser->top + browser->top_idx + offset;
		break;
	case SEEK_END:
603
		browser->top = browser->top + browser->nr_entries - 1 + offset;
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
		break;
	default:
		return;
	}
}

unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
{
	unsigned int row = 0, idx = browser->top_idx;
	char **pos;

	if (browser->top == NULL)
		browser->top = browser->entries;

	pos = (char **)browser->top;
	while (idx < browser->nr_entries) {
		if (!browser->filter || !browser->filter(browser, *pos)) {
			ui_browser__gotorc(browser, row, 0);
			browser->write(browser, pos, row);
623
			if (++row == browser->rows)
624 625 626 627 628 629 630 631 632 633
				break;
		}

		++idx;
		++pos;
	}

	return row;
}

634 635 636 637 638 639 640 641 642
void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
			 u16 start, u16 end)
{
	SLsmg_set_char_set(1);
	ui_browser__gotorc(browser, start, column);
	SLsmg_draw_vline(end - start + 1);
	SLsmg_set_char_set(0);
}

643 644
void ui_browser__write_graph(struct ui_browser *browser __maybe_unused,
			     int graph)
645 646 647 648 649 650
{
	SLsmg_set_char_set(1);
	SLsmg_write_char(graph);
	SLsmg_set_char_set(0);
}

651 652
static void __ui_browser__line_arrow_up(struct ui_browser *browser,
					unsigned int column,
653
					u64 start, u64 end)
654 655 656 657 658
{
	unsigned int row, end_row;

	SLsmg_set_char_set(1);

659
	if (start < browser->top_idx + browser->rows) {
660 661 662 663
		row = start - browser->top_idx;
		ui_browser__gotorc(browser, row, column);
		SLsmg_write_char(SLSMG_LLCORN_CHAR);
		ui_browser__gotorc(browser, row, column + 1);
664
		SLsmg_draw_hline(2);
665 666 667 668

		if (row-- == 0)
			goto out;
	} else
669
		row = browser->rows - 1;
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690

	if (end > browser->top_idx)
		end_row = end - browser->top_idx;
	else
		end_row = 0;

	ui_browser__gotorc(browser, end_row, column);
	SLsmg_draw_vline(row - end_row + 1);

	ui_browser__gotorc(browser, end_row, column);
	if (end >= browser->top_idx) {
		SLsmg_write_char(SLSMG_ULCORN_CHAR);
		ui_browser__gotorc(browser, end_row, column + 1);
		SLsmg_write_char(SLSMG_HLINE_CHAR);
		ui_browser__gotorc(browser, end_row, column + 2);
		SLsmg_write_char(SLSMG_RARROW_CHAR);
	}
out:
	SLsmg_set_char_set(0);
}

691 692
static void __ui_browser__line_arrow_down(struct ui_browser *browser,
					  unsigned int column,
693
					  u64 start, u64 end)
694 695 696 697 698 699 700 701 702 703
{
	unsigned int row, end_row;

	SLsmg_set_char_set(1);

	if (start >= browser->top_idx) {
		row = start - browser->top_idx;
		ui_browser__gotorc(browser, row, column);
		SLsmg_write_char(SLSMG_ULCORN_CHAR);
		ui_browser__gotorc(browser, row, column + 1);
704
		SLsmg_draw_hline(2);
705 706 707 708 709 710

		if (row++ == 0)
			goto out;
	} else
		row = 0;

711 712
	if (end >= browser->top_idx + browser->rows)
		end_row = browser->rows - 1;
713
	else
714
		end_row = end - browser->top_idx;
715 716 717 718 719

	ui_browser__gotorc(browser, row, column);
	SLsmg_draw_vline(end_row - row + 1);

	ui_browser__gotorc(browser, end_row, column);
720
	if (end < browser->top_idx + browser->rows) {
721 722 723 724 725 726 727 728 729 730 731
		SLsmg_write_char(SLSMG_LLCORN_CHAR);
		ui_browser__gotorc(browser, end_row, column + 1);
		SLsmg_write_char(SLSMG_HLINE_CHAR);
		ui_browser__gotorc(browser, end_row, column + 2);
		SLsmg_write_char(SLSMG_RARROW_CHAR);
	}
out:
	SLsmg_set_char_set(0);
}

void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
732
			      u64 start, u64 end)
733 734
{
	if (start > end)
735
		__ui_browser__line_arrow_up(browser, column, start, end);
736
	else
737
		__ui_browser__line_arrow_down(browser, column, start, end);
738 739
}

740 741
void ui_browser__init(void)
{
742
	int i = 0;
743

744 745 746
	perf_config(ui_browser__color_config, NULL);

	while (ui_browser__colorsets[i].name) {
747
		struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
748 749
		sltt_set_color(c->colorset, c->name, c->fg, c->bg);
	}
750 751

	annotate_browser__init();
752
}