]> Pileus Git - ~andy/linux/blob - tools/perf/util/ui/browser.c
dce16ee4364567db48b85583feed6d8813d97350
[~andy/linux] / tools / perf / util / ui / browser.c
1 #include "../util.h"
2 #include "../../perf.h"
3 #include "libslang.h"
4 #include <newt.h>
5 #include "ui.h"
6 #include <linux/compiler.h>
7 #include <linux/list.h>
8 #include <linux/rbtree.h>
9 #include <stdlib.h>
10 #include <sys/ttydefaults.h>
11 #include "browser.h"
12 #include "helpline.h"
13 #include "../color.h"
14
15 int newtGetKey(void);
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 int ui_browser__run(struct ui_browser *self, int delay_secs)
295 {
296         int err, key;
297         struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL;
298
299         pthread__unblock_sigwinch();
300
301         while (1) {
302                 off_t offset;
303                 fd_set read_set;
304
305                 pthread_mutex_lock(&ui__lock);
306                 err = __ui_browser__refresh(self);
307                 SLsmg_refresh();
308                 pthread_mutex_unlock(&ui__lock);
309                 if (err < 0)
310                         break;
311
312                 FD_ZERO(&read_set);
313                 FD_SET(0, &read_set);
314
315                 if (delay_secs) {
316                         timeout.tv_sec = delay_secs;
317                         timeout.tv_usec = 0;
318                 }
319
320                 err = select(1, &read_set, NULL, NULL, ptimeout);
321                 if (err > 0 && FD_ISSET(0, &read_set))
322                         key = newtGetKey();
323                 else if (err == 0)
324                         break;
325                 else {
326                         pthread_mutex_lock(&ui__lock);
327                         SLtt_get_screen_size();
328                         SLsmg_reinit_smg();
329                         pthread_mutex_unlock(&ui__lock);
330                         ui_browser__refresh_dimensions(self);
331                         __ui_browser__show_title(self, self->title);
332                         ui_helpline__puts(self->helpline);
333                         continue;
334                 }
335
336                 if (self->use_navkeypressed && !self->navkeypressed) {
337                         if (key == NEWT_KEY_DOWN || key == NEWT_KEY_UP ||
338                             key == NEWT_KEY_PGDN || key == NEWT_KEY_PGUP ||
339                             key == NEWT_KEY_HOME || key == NEWT_KEY_END ||
340                             key == ' ') {
341                                 self->navkeypressed = true;
342                                 continue;
343                         } else
344                                 return key;
345                 }
346
347                 switch (key) {
348                 case NEWT_KEY_DOWN:
349                         if (self->index == self->nr_entries - 1)
350                                 break;
351                         ++self->index;
352                         if (self->index == self->top_idx + self->height) {
353                                 ++self->top_idx;
354                                 self->seek(self, +1, SEEK_CUR);
355                         }
356                         break;
357                 case NEWT_KEY_UP:
358                         if (self->index == 0)
359                                 break;
360                         --self->index;
361                         if (self->index < self->top_idx) {
362                                 --self->top_idx;
363                                 self->seek(self, -1, SEEK_CUR);
364                         }
365                         break;
366                 case NEWT_KEY_PGDN:
367                 case ' ':
368                         if (self->top_idx + self->height > self->nr_entries - 1)
369                                 break;
370
371                         offset = self->height;
372                         if (self->index + offset > self->nr_entries - 1)
373                                 offset = self->nr_entries - 1 - self->index;
374                         self->index += offset;
375                         self->top_idx += offset;
376                         self->seek(self, +offset, SEEK_CUR);
377                         break;
378                 case NEWT_KEY_PGUP:
379                         if (self->top_idx == 0)
380                                 break;
381
382                         if (self->top_idx < self->height)
383                                 offset = self->top_idx;
384                         else
385                                 offset = self->height;
386
387                         self->index -= offset;
388                         self->top_idx -= offset;
389                         self->seek(self, -offset, SEEK_CUR);
390                         break;
391                 case NEWT_KEY_HOME:
392                         ui_browser__reset_index(self);
393                         break;
394                 case NEWT_KEY_END:
395                         offset = self->height - 1;
396                         if (offset >= self->nr_entries)
397                                 offset = self->nr_entries - 1;
398
399                         self->index = self->nr_entries - 1;
400                         self->top_idx = self->index - offset;
401                         self->seek(self, -offset, SEEK_END);
402                         break;
403                 default:
404                         return key;
405                 }
406         }
407         return -1;
408 }
409
410 unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
411 {
412         struct list_head *pos;
413         struct list_head *head = self->entries;
414         int row = 0;
415
416         if (self->top == NULL || self->top == self->entries)
417                 self->top = ui_browser__list_head_filter_entries(self, head->next);
418
419         pos = self->top;
420
421         list_for_each_from(pos, head) {
422                 if (!self->filter || !self->filter(self, pos)) {
423                         ui_browser__gotorc(self, row, 0);
424                         self->write(self, pos, row);
425                         if (++row == self->height)
426                                 break;
427                 }
428         }
429
430         return row;
431 }
432
433 static struct ui_browser__colors {
434         const char *topColorFg, *topColorBg;
435         const char *mediumColorFg, *mediumColorBg;
436         const char *normalColorFg, *normalColorBg;
437         const char *selColorFg, *selColorBg;
438         const char *codeColorFg, *codeColorBg;
439 } ui_browser__default_colors = {
440         "red",       "lightgray",
441         "green",     "lightgray",
442         "black",     "lightgray",
443         "lightgray", "magenta",
444         "blue",      "lightgray",
445 };
446
447 void ui_browser__init(void)
448 {
449         struct ui_browser__colors *c = &ui_browser__default_colors;
450
451         sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
452         sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
453         sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
454         sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
455         sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
456 }