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 key == K_PGDN || key == K_PGUP || 397 key == K_HOME || key == K_END || 398 key == ' ') { 399 browser->navkeypressed = true; 400 continue; 401 } else 402 return key; 403 } 404 405 switch (key) { 406 case K_DOWN: 407 if (browser->index == browser->nr_entries - 1) 408 break; 409 ++browser->index; 410 if (browser->index == browser->top_idx + browser->rows) { 411 ++browser->top_idx; 412 browser->seek(browser, +1, SEEK_CUR); 413 } 414 break; 415 case K_UP: 416 if (browser->index == 0) 417 break; 418 --browser->index; 419 if (browser->index < browser->top_idx) { 420 --browser->top_idx; 421 browser->seek(browser, -1, SEEK_CUR); 422 } 423 break; 424 case K_PGDN: 425 case ' ': 426 if (browser->top_idx + browser->rows > browser->nr_entries - 1) 427 break; 428 429 offset = browser->rows; 430 if (browser->index + offset > browser->nr_entries - 1) 431 offset = browser->nr_entries - 1 - browser->index; 432 browser->index += offset; 433 browser->top_idx += offset; 434 browser->seek(browser, +offset, SEEK_CUR); 435 break; 436 case K_PGUP: 437 if (browser->top_idx == 0) 438 break; 439 440 if (browser->top_idx < browser->rows) 441 offset = browser->top_idx; 442 else 443 offset = browser->rows; 444 445 browser->index -= offset; 446 browser->top_idx -= offset; 447 browser->seek(browser, -offset, SEEK_CUR); 448 break; 449 case K_HOME: 450 ui_browser__reset_index(browser); 451 break; 452 case K_END: 453 offset = browser->rows - 1; 454 if (offset >= browser->nr_entries) 455 offset = browser->nr_entries - 1; 456 457 browser->index = browser->nr_entries - 1; 458 browser->top_idx = browser->index - offset; 459 browser->seek(browser, -offset, SEEK_END); 460 break; 461 default: 462 return key; 463 } 464 } 465 return -1; 466 } 467 468 unsigned int ui_browser__list_head_refresh(struct ui_browser *browser) 469 { 470 struct list_head *pos; 471 struct list_head *head = browser->entries; 472 int row = 0; 473 474 if (browser->top == NULL || browser->top == browser->entries) 475 browser->top = ui_browser__list_head_filter_entries(browser, head->next); 476 477 pos = browser->top; 478 479 list_for_each_from(pos, head) { 480 if (!browser->filter || !browser->filter(browser, pos)) { 481 ui_browser__gotorc(browser, row, 0); 482 browser->write(browser, pos, row); 483 if (++row == browser->rows) 484 break; 485 } 486 } 487 488 return row; 489 } 490 491 static struct ui_browser_colorset { 492 const char *name, *fg, *bg; 493 int colorset; 494 } ui_browser__colorsets[] = { 495 { 496 .colorset = HE_COLORSET_TOP, 497 .name = "top", 498 .fg = "red", 499 .bg = "default", 500 }, 501 { 502 .colorset = HE_COLORSET_MEDIUM, 503 .name = "medium", 504 .fg = "green", 505 .bg = "default", 506 }, 507 { 508 .colorset = HE_COLORSET_NORMAL, 509 .name = "normal", 510 .fg = "default", 511 .bg = "default", 512 }, 513 { 514 .colorset = HE_COLORSET_SELECTED, 515 .name = "selected", 516 .fg = "black", 517 .bg = "lightgray", 518 }, 519 { 520 .colorset = HE_COLORSET_CODE, 521 .name = "code", 522 .fg = "blue", 523 .bg = "default", 524 }, 525 { 526 .colorset = HE_COLORSET_ADDR, 527 .name = "addr", 528 .fg = "magenta", 529 .bg = "default", 530 }, 531 { 532 .colorset = HE_COLORSET_ROOT, 533 .name = "root", 534 .fg = "white", 535 .bg = "blue", 536 }, 537 { 538 .name = NULL, 539 } 540 }; 541 542 543 static int ui_browser__color_config(const char *var, const char *value, 544 void *data __maybe_unused) 545 { 546 char *fg = NULL, *bg; 547 int i; 548 549 /* same dir for all commands */ 550 if (prefixcmp(var, "colors.") != 0) 551 return 0; 552 553 for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) { 554 const char *name = var + 7; 555 556 if (strcmp(ui_browser__colorsets[i].name, name) != 0) 557 continue; 558 559 fg = strdup(value); 560 if (fg == NULL) 561 break; 562 563 bg = strchr(fg, ','); 564 if (bg == NULL) 565 break; 566 567 *bg = '\0'; 568 while (isspace(*++bg)); 569 ui_browser__colorsets[i].bg = bg; 570 ui_browser__colorsets[i].fg = fg; 571 return 0; 572 } 573 574 free(fg); 575 return -1; 576 } 577 578 void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence) 579 { 580 switch (whence) { 581 case SEEK_SET: 582 browser->top = browser->entries; 583 break; 584 case SEEK_CUR: 585 browser->top = browser->top + browser->top_idx + offset; 586 break; 587 case SEEK_END: 588 browser->top = browser->top + browser->nr_entries - 1 + offset; 589 break; 590 default: 591 return; 592 } 593 } 594 595 unsigned int ui_browser__argv_refresh(struct ui_browser *browser) 596 { 597 unsigned int row = 0, idx = browser->top_idx; 598 char **pos; 599 600 if (browser->top == NULL) 601 browser->top = browser->entries; 602 603 pos = (char **)browser->top; 604 while (idx < browser->nr_entries) { 605 if (!browser->filter || !browser->filter(browser, *pos)) { 606 ui_browser__gotorc(browser, row, 0); 607 browser->write(browser, pos, row); 608 if (++row == browser->rows) 609 break; 610 } 611 612 ++idx; 613 ++pos; 614 } 615 616 return row; 617 } 618 619 void __ui_browser__vline(struct ui_browser *browser, unsigned int column, 620 u16 start, u16 end) 621 { 622 SLsmg_set_char_set(1); 623 ui_browser__gotorc(browser, start, column); 624 SLsmg_draw_vline(end - start + 1); 625 SLsmg_set_char_set(0); 626 } 627 628 void ui_browser__write_graph(struct ui_browser *browser __maybe_unused, 629 int graph) 630 { 631 SLsmg_set_char_set(1); 632 SLsmg_write_char(graph); 633 SLsmg_set_char_set(0); 634 } 635 636 static void __ui_browser__line_arrow_up(struct ui_browser *browser, 637 unsigned int column, 638 u64 start, u64 end) 639 { 640 unsigned int row, end_row; 641 642 SLsmg_set_char_set(1); 643 644 if (start < browser->top_idx + browser->rows) { 645 row = start - browser->top_idx; 646 ui_browser__gotorc(browser, row, column); 647 SLsmg_write_char(SLSMG_LLCORN_CHAR); 648 ui_browser__gotorc(browser, row, column + 1); 649 SLsmg_draw_hline(2); 650 651 if (row-- == 0) 652 goto out; 653 } else 654 row = browser->rows - 1; 655 656 if (end > browser->top_idx) 657 end_row = end - browser->top_idx; 658 else 659 end_row = 0; 660 661 ui_browser__gotorc(browser, end_row, column); 662 SLsmg_draw_vline(row - end_row + 1); 663 664 ui_browser__gotorc(browser, end_row, column); 665 if (end >= browser->top_idx) { 666 SLsmg_write_char(SLSMG_ULCORN_CHAR); 667 ui_browser__gotorc(browser, end_row, column + 1); 668 SLsmg_write_char(SLSMG_HLINE_CHAR); 669 ui_browser__gotorc(browser, end_row, column + 2); 670 SLsmg_write_char(SLSMG_RARROW_CHAR); 671 } 672 out: 673 SLsmg_set_char_set(0); 674 } 675 676 static void __ui_browser__line_arrow_down(struct ui_browser *browser, 677 unsigned int column, 678 u64 start, u64 end) 679 { 680 unsigned int row, end_row; 681 682 SLsmg_set_char_set(1); 683 684 if (start >= browser->top_idx) { 685 row = start - browser->top_idx; 686 ui_browser__gotorc(browser, row, column); 687 SLsmg_write_char(SLSMG_ULCORN_CHAR); 688 ui_browser__gotorc(browser, row, column + 1); 689 SLsmg_draw_hline(2); 690 691 if (row++ == 0) 692 goto out; 693 } else 694 row = 0; 695 696 if (end >= browser->top_idx + browser->rows) 697 end_row = browser->rows - 1; 698 else 699 end_row = end - browser->top_idx; 700 701 ui_browser__gotorc(browser, row, column); 702 SLsmg_draw_vline(end_row - row + 1); 703 704 ui_browser__gotorc(browser, end_row, column); 705 if (end < browser->top_idx + browser->rows) { 706 SLsmg_write_char(SLSMG_LLCORN_CHAR); 707 ui_browser__gotorc(browser, end_row, column + 1); 708 SLsmg_write_char(SLSMG_HLINE_CHAR); 709 ui_browser__gotorc(browser, end_row, column + 2); 710 SLsmg_write_char(SLSMG_RARROW_CHAR); 711 } 712 out: 713 SLsmg_set_char_set(0); 714 } 715 716 void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column, 717 u64 start, u64 end) 718 { 719 if (start > end) 720 __ui_browser__line_arrow_up(browser, column, start, end); 721 else 722 __ui_browser__line_arrow_down(browser, column, start, end); 723 } 724 725 void ui_browser__init(void) 726 { 727 int i = 0; 728 729 perf_config(ui_browser__color_config, NULL); 730 731 while (ui_browser__colorsets[i].name) { 732 struct ui_browser_colorset *c = &ui_browser__colorsets[i++]; 733 sltt_set_color(c->colorset, c->name, c->fg, c->bg); 734 } 735 736 annotate_browser__init(); 737 } 738