browser.c 16.0 KB
Newer Older
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 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

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

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

55 56 57 58 59 60 61 62 63
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);
}

64 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
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;
}

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

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

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

112 113
	assert(pos != NULL);

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

122
	browser->top = pos;
123 124
}

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

	switch (whence) {
	case SEEK_SET:
		nd = rb_first(root);
		break;
	case SEEK_CUR:
135
		nd = browser->top;
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
		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);
	}

152
	browser->top = nd;
153 154
}

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

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

163
	nd = browser->top;
164 165

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

	return row;
}

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

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

189 190 191 192 193 194 195
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);
}

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

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

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

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
	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';
}

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

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

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);
}

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

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

	browser->refresh_dimensions(browser);
272

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

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

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

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

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

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

307 308
	SLsmg_set_char_set(1);

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

	SLsmg_set_char_set(0);
316 317 318
}

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

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

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

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

	return 0;
}

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

	return 0;
}

346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
/*
 * 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;
	}

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

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

374 375 376
	while (1) {
		off_t offset;

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

384
		key = ui__getch(delay_secs);
385

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

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

405
		switch (key) {
406
		case K_DOWN:
407
			if (browser->index == browser->nr_entries - 1)
408
				break;
409
			++browser->index;
410
			if (browser->index == browser->top_idx + browser->rows) {
411 412
				++browser->top_idx;
				browser->seek(browser, +1, SEEK_CUR);
413 414
			}
			break;
415
		case K_UP:
416
			if (browser->index == 0)
417
				break;
418 419 420 421
			--browser->index;
			if (browser->index < browser->top_idx) {
				--browser->top_idx;
				browser->seek(browser, -1, SEEK_CUR);
422 423
			}
			break;
424
		case K_PGDN:
425
		case ' ':
426
			if (browser->top_idx + browser->rows > browser->nr_entries - 1)
427 428
				break;

429
			offset = browser->rows;
430 431 432 433 434
			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);
435
			break;
436
		case K_PGUP:
437
			if (browser->top_idx == 0)
438 439
				break;

440
			if (browser->top_idx < browser->rows)
441
				offset = browser->top_idx;
442
			else
443
				offset = browser->rows;
444

445 446 447
			browser->index -= offset;
			browser->top_idx -= offset;
			browser->seek(browser, -offset, SEEK_CUR);
448
			break;
449
		case K_HOME:
450
			ui_browser__reset_index(browser);
451
			break;
452
		case K_END:
453
			offset = browser->rows - 1;
454 455
			if (offset >= browser->nr_entries)
				offset = browser->nr_entries - 1;
456

457 458 459
			browser->index = browser->nr_entries - 1;
			browser->top_idx = browser->index - offset;
			browser->seek(browser, -offset, SEEK_END);
460 461
			break;
		default:
462
			return key;
463 464
		}
	}
465
	return -1;
466 467
}

468
unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
469 470
{
	struct list_head *pos;
471
	struct list_head *head = browser->entries;
472 473
	int row = 0;

474 475
	if (browser->top == NULL || browser->top == browser->entries)
                browser->top = ui_browser__list_head_filter_entries(browser, head->next);
476

477
	pos = browser->top;
478 479

	list_for_each_from(pos, head) {
480 481 482
		if (!browser->filter || !browser->filter(browser, pos)) {
			ui_browser__gotorc(browser, row, 0);
			browser->write(browser, pos, row);
483
			if (++row == browser->rows)
484 485
				break;
		}
486 487 488 489 490
	}

	return row;
}

491
static struct ui_browser_colorset {
492 493 494 495 496 497 498
	const char *name, *fg, *bg;
	int colorset;
} ui_browser__colorsets[] = {
	{
		.colorset = HE_COLORSET_TOP,
		.name	  = "top",
		.fg	  = "red",
499
		.bg	  = "default",
500 501 502 503 504
	},
	{
		.colorset = HE_COLORSET_MEDIUM,
		.name	  = "medium",
		.fg	  = "green",
505
		.bg	  = "default",
506 507 508 509
	},
	{
		.colorset = HE_COLORSET_NORMAL,
		.name	  = "normal",
510 511
		.fg	  = "default",
		.bg	  = "default",
512 513 514 515 516 517 518 519 520 521 522
	},
	{
		.colorset = HE_COLORSET_SELECTED,
		.name	  = "selected",
		.fg	  = "black",
		.bg	  = "lightgray",
	},
	{
		.colorset = HE_COLORSET_CODE,
		.name	  = "code",
		.fg	  = "blue",
523
		.bg	  = "default",
524
	},
525 526 527 528 529 530
	{
		.colorset = HE_COLORSET_ADDR,
		.name	  = "addr",
		.fg	  = "magenta",
		.bg	  = "default",
	},
531 532 533 534 535 536
	{
		.colorset = HE_COLORSET_ROOT,
		.name	  = "root",
		.fg	  = "white",
		.bg	  = "blue",
	},
537 538 539
	{
		.name = NULL,
	}
540 541
};

542 543

static int ui_browser__color_config(const char *var, const char *value,
544
				    void *data __maybe_unused)
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
{
	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';
		while (isspace(*++bg));
		ui_browser__colorsets[i].bg = bg;
		ui_browser__colorsets[i].fg = fg;
		return 0;
	}

	free(fg);
	return -1;
}

578 579 580 581 582 583 584 585 586 587
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:
588
		browser->top = browser->top + browser->nr_entries - 1 + offset;
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
		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);
608
			if (++row == browser->rows)
609 610 611 612 613 614 615 616 617 618
				break;
		}

		++idx;
		++pos;
	}

	return row;
}

619 620 621 622 623 624 625 626 627
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);
}

628 629
void ui_browser__write_graph(struct ui_browser *browser __maybe_unused,
			     int graph)
630 631 632 633 634 635
{
	SLsmg_set_char_set(1);
	SLsmg_write_char(graph);
	SLsmg_set_char_set(0);
}

636 637
static void __ui_browser__line_arrow_up(struct ui_browser *browser,
					unsigned int column,
638
					u64 start, u64 end)
639 640 641 642 643
{
	unsigned int row, end_row;

	SLsmg_set_char_set(1);

644
	if (start < browser->top_idx + browser->rows) {
645 646 647 648
		row = start - browser->top_idx;
		ui_browser__gotorc(browser, row, column);
		SLsmg_write_char(SLSMG_LLCORN_CHAR);
		ui_browser__gotorc(browser, row, column + 1);
649
		SLsmg_draw_hline(2);
650 651 652 653

		if (row-- == 0)
			goto out;
	} else
654
		row = browser->rows - 1;
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675

	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);
}

676 677
static void __ui_browser__line_arrow_down(struct ui_browser *browser,
					  unsigned int column,
678
					  u64 start, u64 end)
679 680 681 682 683 684 685 686 687 688
{
	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);
689
		SLsmg_draw_hline(2);
690 691 692 693 694 695

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

696 697
	if (end >= browser->top_idx + browser->rows)
		end_row = browser->rows - 1;
698
	else
699
		end_row = end - browser->top_idx;
700 701 702 703 704

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

	ui_browser__gotorc(browser, end_row, column);
705
	if (end < browser->top_idx + browser->rows) {
706 707 708 709 710 711 712 713 714 715 716
		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,
717
			      u64 start, u64 end)
718 719
{
	if (start > end)
720
		__ui_browser__line_arrow_up(browser, column, start, end);
721
	else
722
		__ui_browser__line_arrow_down(browser, column, start, end);
723 724
}

725 726
void ui_browser__init(void)
{
727
	int i = 0;
728

729 730 731
	perf_config(ui_browser__color_config, NULL);

	while (ui_browser__colorsets[i].name) {
732
		struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
733 734
		sltt_set_color(c->colorset, c->name, c->fg, c->bg);
	}
735 736

	annotate_browser__init();
737
}