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