06fc9ebd8d3f6a9b0485ee4164485e3daaee3487
[pandora-kernel.git] / tools / perf / util / ui / browser.c
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 <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 "../color.h"
15
16 int newtGetKey(void);
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 void ui_browser__set_color(struct ui_browser *self __used, int color)
31 {
32         SLsmg_set_color(color);
33 }
34
35 void 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
42 void ui_browser__gotorc(struct ui_browser *self, int y, int x)
43 {
44         SLsmg_gotorc(self->y + y, self->x + x);
45 }
46
47 static struct list_head *
48 ui_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
60 static struct list_head *
61 ui_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
73 void 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
108 void 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
138 unsigned 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
159 bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
160 {
161         return self->top_idx + row == self->index;
162 }
163
164 void 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
172 void ui_browser__reset_index(struct ui_browser *self)
173 {
174         self->index = self->top_idx = 0;
175         self->seek(self, 0, SEEK_SET);
176 }
177
178 void __ui_browser__show_title(struct ui_browser *browser, const char *title)
179 {
180         SLsmg_gotorc(0, 0);
181         ui_browser__set_color(browser, NEWT_COLORSET_ROOT);
182         slsmg_write_nstring(title, browser->width + 1);
183 }
184
185 void ui_browser__show_title(struct ui_browser *browser, const char *title)
186 {
187         pthread_mutex_lock(&ui__lock);
188         __ui_browser__show_title(browser, title);
189         pthread_mutex_unlock(&ui__lock);
190 }
191
192 int ui_browser__show(struct ui_browser *self, const char *title,
193                      const char *helpline, ...)
194 {
195         int err;
196         va_list ap;
197
198         ui_browser__refresh_dimensions(self);
199
200         pthread_mutex_lock(&ui__lock);
201         __ui_browser__show_title(self, title);
202
203         self->title = title;
204         free(self->helpline);
205         self->helpline = NULL;
206
207         va_start(ap, helpline);
208         err = vasprintf(&self->helpline, helpline, ap);
209         va_end(ap);
210         if (err > 0)
211                 ui_helpline__push(self->helpline);
212         pthread_mutex_unlock(&ui__lock);
213         return err ? 0 : -1;
214 }
215
216 void ui_browser__hide(struct ui_browser *browser __used)
217 {
218         pthread_mutex_lock(&ui__lock);
219         ui_helpline__pop();
220         pthread_mutex_unlock(&ui__lock);
221 }
222
223 static void ui_browser__scrollbar_set(struct ui_browser *browser)
224 {
225         int height = browser->height, h = 0, pct = 0,
226             col = browser->width,
227             row = browser->y - 1;
228
229         if (browser->nr_entries > 1) {
230                 pct = ((browser->index * (browser->height - 1)) /
231                        (browser->nr_entries - 1));
232         }
233
234         while (h < height) {
235                 ui_browser__gotorc(browser, row++, col);
236                 SLsmg_set_char_set(1);
237                 SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_BOARD_CHAR);
238                 SLsmg_set_char_set(0);
239                 ++h;
240         }
241 }
242
243 static int __ui_browser__refresh(struct ui_browser *browser)
244 {
245         int row;
246         int width = browser->width;
247
248         row = browser->refresh(browser);
249         ui_browser__set_color(browser, HE_COLORSET_NORMAL);
250
251         if (!browser->use_navkeypressed || browser->navkeypressed)
252                 ui_browser__scrollbar_set(browser);
253         else
254                 width += 1;
255
256         SLsmg_fill_region(browser->y + row, browser->x,
257                           browser->height - row, width, ' ');
258
259         return 0;
260 }
261
262 int ui_browser__refresh(struct ui_browser *browser)
263 {
264         pthread_mutex_lock(&ui__lock);
265         __ui_browser__refresh(browser);
266         pthread_mutex_unlock(&ui__lock);
267
268         return 0;
269 }
270
271 /*
272  * Here we're updating nr_entries _after_ we started browsing, i.e.  we have to
273  * forget about any reference to any entry in the underlying data structure,
274  * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
275  * after an output_resort and hist decay.
276  */
277 void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
278 {
279         off_t offset = nr_entries - browser->nr_entries;
280
281         browser->nr_entries = nr_entries;
282
283         if (offset < 0) {
284                 if (browser->top_idx < (u64)-offset)
285                         offset = -browser->top_idx;
286
287                 browser->index += offset;
288                 browser->top_idx += offset;
289         }
290
291         browser->top = NULL;
292         browser->seek(browser, browser->top_idx, SEEK_SET);
293 }
294
295 int ui_browser__run(struct ui_browser *self, int delay_secs)
296 {
297         int err, key;
298         struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL;
299
300         pthread__unblock_sigwinch();
301
302         while (1) {
303                 off_t offset;
304                 fd_set read_set;
305
306                 pthread_mutex_lock(&ui__lock);
307                 err = __ui_browser__refresh(self);
308                 SLsmg_refresh();
309                 pthread_mutex_unlock(&ui__lock);
310                 if (err < 0)
311                         break;
312
313                 FD_ZERO(&read_set);
314                 FD_SET(0, &read_set);
315
316                 if (delay_secs) {
317                         timeout.tv_sec = delay_secs;
318                         timeout.tv_usec = 0;
319                 }
320
321                 err = select(1, &read_set, NULL, NULL, ptimeout);
322                 if (err > 0 && FD_ISSET(0, &read_set))
323                         key = newtGetKey();
324                 else if (err == 0)
325                         break;
326                 else {
327                         pthread_mutex_lock(&ui__lock);
328                         SLtt_get_screen_size();
329                         SLsmg_reinit_smg();
330                         pthread_mutex_unlock(&ui__lock);
331                         ui_browser__refresh_dimensions(self);
332                         __ui_browser__show_title(self, self->title);
333                         ui_helpline__puts(self->helpline);
334                         continue;
335                 }
336
337                 if (self->use_navkeypressed && !self->navkeypressed) {
338                         if (key == NEWT_KEY_DOWN || key == NEWT_KEY_UP ||
339                             key == NEWT_KEY_PGDN || key == NEWT_KEY_PGUP ||
340                             key == NEWT_KEY_HOME || key == NEWT_KEY_END ||
341                             key == ' ') {
342                                 self->navkeypressed = true;
343                                 continue;
344                         } else
345                                 return key;
346                 }
347
348                 switch (key) {
349                 case NEWT_KEY_DOWN:
350                         if (self->index == self->nr_entries - 1)
351                                 break;
352                         ++self->index;
353                         if (self->index == self->top_idx + self->height) {
354                                 ++self->top_idx;
355                                 self->seek(self, +1, SEEK_CUR);
356                         }
357                         break;
358                 case NEWT_KEY_UP:
359                         if (self->index == 0)
360                                 break;
361                         --self->index;
362                         if (self->index < self->top_idx) {
363                                 --self->top_idx;
364                                 self->seek(self, -1, SEEK_CUR);
365                         }
366                         break;
367                 case NEWT_KEY_PGDN:
368                 case ' ':
369                         if (self->top_idx + self->height > self->nr_entries - 1)
370                                 break;
371
372                         offset = self->height;
373                         if (self->index + offset > self->nr_entries - 1)
374                                 offset = self->nr_entries - 1 - self->index;
375                         self->index += offset;
376                         self->top_idx += offset;
377                         self->seek(self, +offset, SEEK_CUR);
378                         break;
379                 case NEWT_KEY_PGUP:
380                         if (self->top_idx == 0)
381                                 break;
382
383                         if (self->top_idx < self->height)
384                                 offset = self->top_idx;
385                         else
386                                 offset = self->height;
387
388                         self->index -= offset;
389                         self->top_idx -= offset;
390                         self->seek(self, -offset, SEEK_CUR);
391                         break;
392                 case NEWT_KEY_HOME:
393                         ui_browser__reset_index(self);
394                         break;
395                 case NEWT_KEY_END:
396                         offset = self->height - 1;
397                         if (offset >= self->nr_entries)
398                                 offset = self->nr_entries - 1;
399
400                         self->index = self->nr_entries - 1;
401                         self->top_idx = self->index - offset;
402                         self->seek(self, -offset, SEEK_END);
403                         break;
404                 default:
405                         return key;
406                 }
407         }
408         return -1;
409 }
410
411 unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
412 {
413         struct list_head *pos;
414         struct list_head *head = self->entries;
415         int row = 0;
416
417         if (self->top == NULL || self->top == self->entries)
418                 self->top = ui_browser__list_head_filter_entries(self, head->next);
419
420         pos = self->top;
421
422         list_for_each_from(pos, head) {
423                 if (!self->filter || !self->filter(self, pos)) {
424                         ui_browser__gotorc(self, row, 0);
425                         self->write(self, pos, row);
426                         if (++row == self->height)
427                                 break;
428                 }
429         }
430
431         return row;
432 }
433
434 static struct ui_browser__colorset {
435         const char *name, *fg, *bg;
436         int colorset;
437 } ui_browser__colorsets[] = {
438         {
439                 .colorset = HE_COLORSET_TOP,
440                 .name     = "top",
441                 .fg       = "red",
442                 .bg       = "default",
443         },
444         {
445                 .colorset = HE_COLORSET_MEDIUM,
446                 .name     = "medium",
447                 .fg       = "green",
448                 .bg       = "default",
449         },
450         {
451                 .colorset = HE_COLORSET_NORMAL,
452                 .name     = "normal",
453                 .fg       = "default",
454                 .bg       = "default",
455         },
456         {
457                 .colorset = HE_COLORSET_SELECTED,
458                 .name     = "selected",
459                 .fg       = "black",
460                 .bg       = "lightgray",
461         },
462         {
463                 .colorset = HE_COLORSET_CODE,
464                 .name     = "code",
465                 .fg       = "blue",
466                 .bg       = "default",
467         },
468         {
469                 .name = NULL,
470         }
471 };
472
473
474 static int ui_browser__color_config(const char *var, const char *value,
475                                     void *data __used)
476 {
477         char *fg = NULL, *bg;
478         int i;
479
480         /* same dir for all commands */
481         if (prefixcmp(var, "colors.") != 0)
482                 return 0;
483
484         for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
485                 const char *name = var + 7;
486
487                 if (strcmp(ui_browser__colorsets[i].name, name) != 0)
488                         continue;
489
490                 fg = strdup(value);
491                 if (fg == NULL)
492                         break;
493
494                 bg = strchr(fg, ',');
495                 if (bg == NULL)
496                         break;
497
498                 *bg = '\0';
499                 while (isspace(*++bg));
500                 ui_browser__colorsets[i].bg = bg;
501                 ui_browser__colorsets[i].fg = fg;
502                 return 0;
503         }
504
505         free(fg);
506         return -1;
507 }
508
509 void ui_browser__init(void)
510 {
511         int i = 0;
512
513         perf_config(ui_browser__color_config, NULL);
514
515         while (ui_browser__colorsets[i].name) {
516                 struct ui_browser__colorset *c = &ui_browser__colorsets[i++];
517                 sltt_set_color(c->colorset, c->name, c->fg, c->bg);
518         }
519 }