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