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