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 20 static 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 32 int 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 40 void 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 47 void ui_browser__gotorc(struct ui_browser *browser, int y, int x) 48 { 49 SLsmg_gotorc(browser->y + y, browser->x + x); 50 } 51 52 void 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 58 void 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 67 static struct list_head * 68 ui_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 80 static struct list_head * 81 ui_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 93 void 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 128 void 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 158 unsigned 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 179 bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row) 180 { 181 return browser->top_idx + row == browser->index; 182 } 183 184 void 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 192 void 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 199 int 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 225 int 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 235 bool 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 245 void 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 251 void __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 258 void 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 265 int 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 291 void 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 299 static 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 321 static 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 340 int 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 */ 355 void 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 373 int 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 485 unsigned 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 508 static 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 560 static 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 595 void 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 612 unsigned 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 636 void __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 645 void 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 653 static 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 } 689 out: 690 SLsmg_set_char_set(0); 691 } 692 693 static 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 } 729 out: 730 SLsmg_set_char_set(0); 731 } 732 733 void __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 742 void 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 771 void 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 } 784