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