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