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