]> git.karo-electronics.de Git - karo-tx-linux.git/blob - tools/perf/util/ui/browser.c
perf ui browser: Handle K_RESIZE in dialog windows
[karo-tx-linux.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 "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
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__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
179 int ui_browser__warning(struct ui_browser *browser, const char *format, ...)
180 {
181         va_list args;
182         int key;
183
184         va_start(args, format);
185         while ((key = __ui__warning("Warning!", format, args)) == K_RESIZE)
186                 ui_browser__handle_resize(browser);
187         va_end(args);
188
189         return key;
190 }
191
192 int ui_browser__help_window(struct ui_browser *browser, const char *text)
193 {
194         int key;
195
196         while ((key = ui__help_window(text)) == K_RESIZE)
197                 ui_browser__handle_resize(browser);
198
199         return key;
200 }
201
202 bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text)
203 {
204         int key;
205
206         while ((key = ui__dialog_yesno(text)) == K_RESIZE)
207                 ui_browser__handle_resize(browser);
208
209         return key == K_ENTER || toupper(key) == 'Y';
210 }
211
212 void ui_browser__reset_index(struct ui_browser *self)
213 {
214         self->index = self->top_idx = 0;
215         self->seek(self, 0, SEEK_SET);
216 }
217
218 void __ui_browser__show_title(struct ui_browser *browser, const char *title)
219 {
220         SLsmg_gotorc(0, 0);
221         ui_browser__set_color(browser, NEWT_COLORSET_ROOT);
222         slsmg_write_nstring(title, browser->width + 1);
223 }
224
225 void ui_browser__show_title(struct ui_browser *browser, const char *title)
226 {
227         pthread_mutex_lock(&ui__lock);
228         __ui_browser__show_title(browser, title);
229         pthread_mutex_unlock(&ui__lock);
230 }
231
232 int ui_browser__show(struct ui_browser *self, const char *title,
233                      const char *helpline, ...)
234 {
235         int err;
236         va_list ap;
237
238         ui_browser__refresh_dimensions(self);
239
240         pthread_mutex_lock(&ui__lock);
241         __ui_browser__show_title(self, title);
242
243         self->title = title;
244         free(self->helpline);
245         self->helpline = NULL;
246
247         va_start(ap, helpline);
248         err = vasprintf(&self->helpline, helpline, ap);
249         va_end(ap);
250         if (err > 0)
251                 ui_helpline__push(self->helpline);
252         pthread_mutex_unlock(&ui__lock);
253         return err ? 0 : -1;
254 }
255
256 void ui_browser__hide(struct ui_browser *browser __used)
257 {
258         pthread_mutex_lock(&ui__lock);
259         ui_helpline__pop();
260         pthread_mutex_unlock(&ui__lock);
261 }
262
263 static void ui_browser__scrollbar_set(struct ui_browser *browser)
264 {
265         int height = browser->height, h = 0, pct = 0,
266             col = browser->width,
267             row = browser->y - 1;
268
269         if (browser->nr_entries > 1) {
270                 pct = ((browser->index * (browser->height - 1)) /
271                        (browser->nr_entries - 1));
272         }
273
274         SLsmg_set_char_set(1);
275
276         while (h < height) {
277                 ui_browser__gotorc(browser, row++, col);
278                 SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
279                 ++h;
280         }
281
282         SLsmg_set_char_set(0);
283 }
284
285 static int __ui_browser__refresh(struct ui_browser *browser)
286 {
287         int row;
288         int width = browser->width;
289
290         row = browser->refresh(browser);
291         ui_browser__set_color(browser, HE_COLORSET_NORMAL);
292
293         if (!browser->use_navkeypressed || browser->navkeypressed)
294                 ui_browser__scrollbar_set(browser);
295         else
296                 width += 1;
297
298         SLsmg_fill_region(browser->y + row, browser->x,
299                           browser->height - row, width, ' ');
300
301         return 0;
302 }
303
304 int ui_browser__refresh(struct ui_browser *browser)
305 {
306         pthread_mutex_lock(&ui__lock);
307         __ui_browser__refresh(browser);
308         pthread_mutex_unlock(&ui__lock);
309
310         return 0;
311 }
312
313 /*
314  * Here we're updating nr_entries _after_ we started browsing, i.e.  we have to
315  * forget about any reference to any entry in the underlying data structure,
316  * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
317  * after an output_resort and hist decay.
318  */
319 void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
320 {
321         off_t offset = nr_entries - browser->nr_entries;
322
323         browser->nr_entries = nr_entries;
324
325         if (offset < 0) {
326                 if (browser->top_idx < (u64)-offset)
327                         offset = -browser->top_idx;
328
329                 browser->index += offset;
330                 browser->top_idx += offset;
331         }
332
333         browser->top = NULL;
334         browser->seek(browser, browser->top_idx, SEEK_SET);
335 }
336
337 int ui_browser__run(struct ui_browser *self, int delay_secs)
338 {
339         int err, key;
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                         ui__refresh_dimensions(false);
355                         ui_browser__refresh_dimensions(self);
356                         __ui_browser__show_title(self, self->title);
357                         ui_helpline__puts(self->helpline);
358                         continue;
359                 }
360
361                 if (self->use_navkeypressed && !self->navkeypressed) {
362                         if (key == K_DOWN || key == K_UP ||
363                             key == K_PGDN || key == K_PGUP ||
364                             key == K_HOME || key == K_END ||
365                             key == ' ') {
366                                 self->navkeypressed = true;
367                                 continue;
368                         } else
369                                 return key;
370                 }
371
372                 switch (key) {
373                 case K_DOWN:
374                         if (self->index == self->nr_entries - 1)
375                                 break;
376                         ++self->index;
377                         if (self->index == self->top_idx + self->height) {
378                                 ++self->top_idx;
379                                 self->seek(self, +1, SEEK_CUR);
380                         }
381                         break;
382                 case K_UP:
383                         if (self->index == 0)
384                                 break;
385                         --self->index;
386                         if (self->index < self->top_idx) {
387                                 --self->top_idx;
388                                 self->seek(self, -1, SEEK_CUR);
389                         }
390                         break;
391                 case K_PGDN:
392                 case ' ':
393                         if (self->top_idx + self->height > self->nr_entries - 1)
394                                 break;
395
396                         offset = self->height;
397                         if (self->index + offset > self->nr_entries - 1)
398                                 offset = self->nr_entries - 1 - self->index;
399                         self->index += offset;
400                         self->top_idx += offset;
401                         self->seek(self, +offset, SEEK_CUR);
402                         break;
403                 case K_PGUP:
404                         if (self->top_idx == 0)
405                                 break;
406
407                         if (self->top_idx < self->height)
408                                 offset = self->top_idx;
409                         else
410                                 offset = self->height;
411
412                         self->index -= offset;
413                         self->top_idx -= offset;
414                         self->seek(self, -offset, SEEK_CUR);
415                         break;
416                 case K_HOME:
417                         ui_browser__reset_index(self);
418                         break;
419                 case K_END:
420                         offset = self->height - 1;
421                         if (offset >= self->nr_entries)
422                                 offset = self->nr_entries - 1;
423
424                         self->index = self->nr_entries - 1;
425                         self->top_idx = self->index - offset;
426                         self->seek(self, -offset, SEEK_END);
427                         break;
428                 default:
429                         return key;
430                 }
431         }
432         return -1;
433 }
434
435 unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
436 {
437         struct list_head *pos;
438         struct list_head *head = self->entries;
439         int row = 0;
440
441         if (self->top == NULL || self->top == self->entries)
442                 self->top = ui_browser__list_head_filter_entries(self, head->next);
443
444         pos = self->top;
445
446         list_for_each_from(pos, head) {
447                 if (!self->filter || !self->filter(self, pos)) {
448                         ui_browser__gotorc(self, row, 0);
449                         self->write(self, pos, row);
450                         if (++row == self->height)
451                                 break;
452                 }
453         }
454
455         return row;
456 }
457
458 static struct ui_browser__colorset {
459         const char *name, *fg, *bg;
460         int colorset;
461 } ui_browser__colorsets[] = {
462         {
463                 .colorset = HE_COLORSET_TOP,
464                 .name     = "top",
465                 .fg       = "red",
466                 .bg       = "default",
467         },
468         {
469                 .colorset = HE_COLORSET_MEDIUM,
470                 .name     = "medium",
471                 .fg       = "green",
472                 .bg       = "default",
473         },
474         {
475                 .colorset = HE_COLORSET_NORMAL,
476                 .name     = "normal",
477                 .fg       = "default",
478                 .bg       = "default",
479         },
480         {
481                 .colorset = HE_COLORSET_SELECTED,
482                 .name     = "selected",
483                 .fg       = "black",
484                 .bg       = "lightgray",
485         },
486         {
487                 .colorset = HE_COLORSET_CODE,
488                 .name     = "code",
489                 .fg       = "blue",
490                 .bg       = "default",
491         },
492         {
493                 .name = NULL,
494         }
495 };
496
497
498 static int ui_browser__color_config(const char *var, const char *value,
499                                     void *data __used)
500 {
501         char *fg = NULL, *bg;
502         int i;
503
504         /* same dir for all commands */
505         if (prefixcmp(var, "colors.") != 0)
506                 return 0;
507
508         for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
509                 const char *name = var + 7;
510
511                 if (strcmp(ui_browser__colorsets[i].name, name) != 0)
512                         continue;
513
514                 fg = strdup(value);
515                 if (fg == NULL)
516                         break;
517
518                 bg = strchr(fg, ',');
519                 if (bg == NULL)
520                         break;
521
522                 *bg = '\0';
523                 while (isspace(*++bg));
524                 ui_browser__colorsets[i].bg = bg;
525                 ui_browser__colorsets[i].fg = fg;
526                 return 0;
527         }
528
529         free(fg);
530         return -1;
531 }
532
533 void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
534 {
535         switch (whence) {
536         case SEEK_SET:
537                 browser->top = browser->entries;
538                 break;
539         case SEEK_CUR:
540                 browser->top = browser->top + browser->top_idx + offset;
541                 break;
542         case SEEK_END:
543                 browser->top = browser->top + browser->nr_entries + offset;
544                 break;
545         default:
546                 return;
547         }
548 }
549
550 unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
551 {
552         unsigned int row = 0, idx = browser->top_idx;
553         char **pos;
554
555         if (browser->top == NULL)
556                 browser->top = browser->entries;
557
558         pos = (char **)browser->top;
559         while (idx < browser->nr_entries) {
560                 if (!browser->filter || !browser->filter(browser, *pos)) {
561                         ui_browser__gotorc(browser, row, 0);
562                         browser->write(browser, pos, row);
563                         if (++row == browser->height)
564                                 break;
565                 }
566
567                 ++idx;
568                 ++pos;
569         }
570
571         return row;
572 }
573
574 void ui_browser__init(void)
575 {
576         int i = 0;
577
578         perf_config(ui_browser__color_config, NULL);
579
580         while (ui_browser__colorsets[i].name) {
581                 struct ui_browser__colorset *c = &ui_browser__colorsets[i++];
582                 sltt_set_color(c->colorset, c->name, c->fg, c->bg);
583         }
584 }