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