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