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