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->height) 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 = 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 ui_browser__refresh_dimensions(browser); 254 255 pthread_mutex_lock(&ui__lock); 256 __ui_browser__show_title(browser, title); 257 258 browser->title = title; 259 zfree(&browser->helpline); 260 261 va_start(ap, helpline); 262 err = vasprintf(&browser->helpline, helpline, ap); 263 va_end(ap); 264 if (err > 0) 265 ui_helpline__push(browser->helpline); 266 pthread_mutex_unlock(&ui__lock); 267 return err ? 0 : -1; 268 } 269 270 void ui_browser__hide(struct ui_browser *browser) 271 { 272 pthread_mutex_lock(&ui__lock); 273 ui_helpline__pop(); 274 zfree(&browser->helpline); 275 pthread_mutex_unlock(&ui__lock); 276 } 277 278 static void ui_browser__scrollbar_set(struct ui_browser *browser) 279 { 280 int height = browser->height, h = 0, pct = 0, 281 col = browser->width, 282 row = browser->y - 1; 283 284 if (browser->nr_entries > 1) { 285 pct = ((browser->index * (browser->height - 1)) / 286 (browser->nr_entries - 1)); 287 } 288 289 SLsmg_set_char_set(1); 290 291 while (h < height) { 292 ui_browser__gotorc(browser, row++, col); 293 SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR); 294 ++h; 295 } 296 297 SLsmg_set_char_set(0); 298 } 299 300 static int __ui_browser__refresh(struct ui_browser *browser) 301 { 302 int row; 303 int width = browser->width; 304 305 row = browser->refresh(browser); 306 ui_browser__set_color(browser, HE_COLORSET_NORMAL); 307 308 if (!browser->use_navkeypressed || browser->navkeypressed) 309 ui_browser__scrollbar_set(browser); 310 else 311 width += 1; 312 313 SLsmg_fill_region(browser->y + row, browser->x, 314 browser->height - row, width, ' '); 315 316 return 0; 317 } 318 319 int ui_browser__refresh(struct ui_browser *browser) 320 { 321 pthread_mutex_lock(&ui__lock); 322 __ui_browser__refresh(browser); 323 pthread_mutex_unlock(&ui__lock); 324 325 return 0; 326 } 327 328 /* 329 * Here we're updating nr_entries _after_ we started browsing, i.e. we have to 330 * forget about any reference to any entry in the underlying data structure, 331 * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser 332 * after an output_resort and hist decay. 333 */ 334 void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries) 335 { 336 off_t offset = nr_entries - browser->nr_entries; 337 338 browser->nr_entries = nr_entries; 339 340 if (offset < 0) { 341 if (browser->top_idx < (u64)-offset) 342 offset = -browser->top_idx; 343 344 browser->index += offset; 345 browser->top_idx += offset; 346 } 347 348 browser->top = NULL; 349 browser->seek(browser, browser->top_idx, SEEK_SET); 350 } 351 352 int ui_browser__run(struct ui_browser *browser, int delay_secs) 353 { 354 int err, key; 355 356 while (1) { 357 off_t offset; 358 359 pthread_mutex_lock(&ui__lock); 360 err = __ui_browser__refresh(browser); 361 SLsmg_refresh(); 362 pthread_mutex_unlock(&ui__lock); 363 if (err < 0) 364 break; 365 366 key = ui__getch(delay_secs); 367 368 if (key == K_RESIZE) { 369 ui__refresh_dimensions(false); 370 ui_browser__refresh_dimensions(browser); 371 __ui_browser__show_title(browser, browser->title); 372 ui_helpline__puts(browser->helpline); 373 continue; 374 } 375 376 if (browser->use_navkeypressed && !browser->navkeypressed) { 377 if (key == K_DOWN || key == K_UP || 378 key == K_PGDN || key == K_PGUP || 379 key == K_HOME || key == K_END || 380 key == ' ') { 381 browser->navkeypressed = true; 382 continue; 383 } else 384 return key; 385 } 386 387 switch (key) { 388 case K_DOWN: 389 if (browser->index == browser->nr_entries - 1) 390 break; 391 ++browser->index; 392 if (browser->index == browser->top_idx + browser->height) { 393 ++browser->top_idx; 394 browser->seek(browser, +1, SEEK_CUR); 395 } 396 break; 397 case K_UP: 398 if (browser->index == 0) 399 break; 400 --browser->index; 401 if (browser->index < browser->top_idx) { 402 --browser->top_idx; 403 browser->seek(browser, -1, SEEK_CUR); 404 } 405 break; 406 case K_PGDN: 407 case ' ': 408 if (browser->top_idx + browser->height > browser->nr_entries - 1) 409 break; 410 411 offset = browser->height; 412 if (browser->index + offset > browser->nr_entries - 1) 413 offset = browser->nr_entries - 1 - browser->index; 414 browser->index += offset; 415 browser->top_idx += offset; 416 browser->seek(browser, +offset, SEEK_CUR); 417 break; 418 case K_PGUP: 419 if (browser->top_idx == 0) 420 break; 421 422 if (browser->top_idx < browser->height) 423 offset = browser->top_idx; 424 else 425 offset = browser->height; 426 427 browser->index -= offset; 428 browser->top_idx -= offset; 429 browser->seek(browser, -offset, SEEK_CUR); 430 break; 431 case K_HOME: 432 ui_browser__reset_index(browser); 433 break; 434 case K_END: 435 offset = browser->height - 1; 436 if (offset >= browser->nr_entries) 437 offset = browser->nr_entries - 1; 438 439 browser->index = browser->nr_entries - 1; 440 browser->top_idx = browser->index - offset; 441 browser->seek(browser, -offset, SEEK_END); 442 break; 443 default: 444 return key; 445 } 446 } 447 return -1; 448 } 449 450 unsigned int ui_browser__list_head_refresh(struct ui_browser *browser) 451 { 452 struct list_head *pos; 453 struct list_head *head = browser->entries; 454 int row = 0; 455 456 if (browser->top == NULL || browser->top == browser->entries) 457 browser->top = ui_browser__list_head_filter_entries(browser, head->next); 458 459 pos = browser->top; 460 461 list_for_each_from(pos, head) { 462 if (!browser->filter || !browser->filter(browser, pos)) { 463 ui_browser__gotorc(browser, row, 0); 464 browser->write(browser, pos, row); 465 if (++row == browser->height) 466 break; 467 } 468 } 469 470 return row; 471 } 472 473 static struct ui_browser_colorset { 474 const char *name, *fg, *bg; 475 int colorset; 476 } ui_browser__colorsets[] = { 477 { 478 .colorset = HE_COLORSET_TOP, 479 .name = "top", 480 .fg = "red", 481 .bg = "default", 482 }, 483 { 484 .colorset = HE_COLORSET_MEDIUM, 485 .name = "medium", 486 .fg = "green", 487 .bg = "default", 488 }, 489 { 490 .colorset = HE_COLORSET_NORMAL, 491 .name = "normal", 492 .fg = "default", 493 .bg = "default", 494 }, 495 { 496 .colorset = HE_COLORSET_SELECTED, 497 .name = "selected", 498 .fg = "black", 499 .bg = "lightgray", 500 }, 501 { 502 .colorset = HE_COLORSET_CODE, 503 .name = "code", 504 .fg = "blue", 505 .bg = "default", 506 }, 507 { 508 .colorset = HE_COLORSET_ADDR, 509 .name = "addr", 510 .fg = "magenta", 511 .bg = "default", 512 }, 513 { 514 .colorset = HE_COLORSET_ROOT, 515 .name = "root", 516 .fg = "white", 517 .bg = "blue", 518 }, 519 { 520 .name = NULL, 521 } 522 }; 523 524 525 static int ui_browser__color_config(const char *var, const char *value, 526 void *data __maybe_unused) 527 { 528 char *fg = NULL, *bg; 529 int i; 530 531 /* same dir for all commands */ 532 if (prefixcmp(var, "colors.") != 0) 533 return 0; 534 535 for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) { 536 const char *name = var + 7; 537 538 if (strcmp(ui_browser__colorsets[i].name, name) != 0) 539 continue; 540 541 fg = strdup(value); 542 if (fg == NULL) 543 break; 544 545 bg = strchr(fg, ','); 546 if (bg == NULL) 547 break; 548 549 *bg = '\0'; 550 while (isspace(*++bg)); 551 ui_browser__colorsets[i].bg = bg; 552 ui_browser__colorsets[i].fg = fg; 553 return 0; 554 } 555 556 free(fg); 557 return -1; 558 } 559 560 void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence) 561 { 562 switch (whence) { 563 case SEEK_SET: 564 browser->top = browser->entries; 565 break; 566 case SEEK_CUR: 567 browser->top = browser->top + browser->top_idx + offset; 568 break; 569 case SEEK_END: 570 browser->top = browser->top + browser->nr_entries - 1 + offset; 571 break; 572 default: 573 return; 574 } 575 } 576 577 unsigned int ui_browser__argv_refresh(struct ui_browser *browser) 578 { 579 unsigned int row = 0, idx = browser->top_idx; 580 char **pos; 581 582 if (browser->top == NULL) 583 browser->top = browser->entries; 584 585 pos = (char **)browser->top; 586 while (idx < browser->nr_entries) { 587 if (!browser->filter || !browser->filter(browser, *pos)) { 588 ui_browser__gotorc(browser, row, 0); 589 browser->write(browser, pos, row); 590 if (++row == browser->height) 591 break; 592 } 593 594 ++idx; 595 ++pos; 596 } 597 598 return row; 599 } 600 601 void __ui_browser__vline(struct ui_browser *browser, unsigned int column, 602 u16 start, u16 end) 603 { 604 SLsmg_set_char_set(1); 605 ui_browser__gotorc(browser, start, column); 606 SLsmg_draw_vline(end - start + 1); 607 SLsmg_set_char_set(0); 608 } 609 610 void ui_browser__write_graph(struct ui_browser *browser __maybe_unused, 611 int graph) 612 { 613 SLsmg_set_char_set(1); 614 SLsmg_write_char(graph); 615 SLsmg_set_char_set(0); 616 } 617 618 static void __ui_browser__line_arrow_up(struct ui_browser *browser, 619 unsigned int column, 620 u64 start, u64 end) 621 { 622 unsigned int row, end_row; 623 624 SLsmg_set_char_set(1); 625 626 if (start < browser->top_idx + browser->height) { 627 row = start - browser->top_idx; 628 ui_browser__gotorc(browser, row, column); 629 SLsmg_write_char(SLSMG_LLCORN_CHAR); 630 ui_browser__gotorc(browser, row, column + 1); 631 SLsmg_draw_hline(2); 632 633 if (row-- == 0) 634 goto out; 635 } else 636 row = browser->height - 1; 637 638 if (end > browser->top_idx) 639 end_row = end - browser->top_idx; 640 else 641 end_row = 0; 642 643 ui_browser__gotorc(browser, end_row, column); 644 SLsmg_draw_vline(row - end_row + 1); 645 646 ui_browser__gotorc(browser, end_row, column); 647 if (end >= browser->top_idx) { 648 SLsmg_write_char(SLSMG_ULCORN_CHAR); 649 ui_browser__gotorc(browser, end_row, column + 1); 650 SLsmg_write_char(SLSMG_HLINE_CHAR); 651 ui_browser__gotorc(browser, end_row, column + 2); 652 SLsmg_write_char(SLSMG_RARROW_CHAR); 653 } 654 out: 655 SLsmg_set_char_set(0); 656 } 657 658 static void __ui_browser__line_arrow_down(struct ui_browser *browser, 659 unsigned int column, 660 u64 start, u64 end) 661 { 662 unsigned int row, end_row; 663 664 SLsmg_set_char_set(1); 665 666 if (start >= browser->top_idx) { 667 row = start - browser->top_idx; 668 ui_browser__gotorc(browser, row, column); 669 SLsmg_write_char(SLSMG_ULCORN_CHAR); 670 ui_browser__gotorc(browser, row, column + 1); 671 SLsmg_draw_hline(2); 672 673 if (row++ == 0) 674 goto out; 675 } else 676 row = 0; 677 678 if (end >= browser->top_idx + browser->height) 679 end_row = browser->height - 1; 680 else 681 end_row = end - browser->top_idx; 682 683 ui_browser__gotorc(browser, row, column); 684 SLsmg_draw_vline(end_row - row + 1); 685 686 ui_browser__gotorc(browser, end_row, column); 687 if (end < browser->top_idx + browser->height) { 688 SLsmg_write_char(SLSMG_LLCORN_CHAR); 689 ui_browser__gotorc(browser, end_row, column + 1); 690 SLsmg_write_char(SLSMG_HLINE_CHAR); 691 ui_browser__gotorc(browser, end_row, column + 2); 692 SLsmg_write_char(SLSMG_RARROW_CHAR); 693 } 694 out: 695 SLsmg_set_char_set(0); 696 } 697 698 void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column, 699 u64 start, u64 end) 700 { 701 if (start > end) 702 __ui_browser__line_arrow_up(browser, column, start, end); 703 else 704 __ui_browser__line_arrow_down(browser, column, start, end); 705 } 706 707 void ui_browser__init(void) 708 { 709 int i = 0; 710 711 perf_config(ui_browser__color_config, NULL); 712 713 while (ui_browser__colorsets[i].name) { 714 struct ui_browser_colorset *c = &ui_browser__colorsets[i++]; 715 sltt_set_color(c->colorset, c->name, c->fg, c->bg); 716 } 717 718 annotate_browser__init(); 719 } 720