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