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 
ui_browser__percent_color(struct ui_browser * browser,double percent,bool current)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 
ui_browser__set_color(struct ui_browser * self __used,int color)30 void ui_browser__set_color(struct ui_browser *self __used, int color)
31 {
32 	SLsmg_set_color(color);
33 }
34 
ui_browser__set_percent_color(struct ui_browser * self,double percent,bool current)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 
ui_browser__gotorc(struct ui_browser * self,int y,int x)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 *
ui_browser__list_head_filter_entries(struct ui_browser * browser,struct list_head * pos)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 *
ui_browser__list_head_filter_prev_entries(struct ui_browser * browser,struct list_head * pos)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 
ui_browser__list_head_seek(struct ui_browser * self,off_t offset,int whence)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 
ui_browser__rb_tree_seek(struct ui_browser * self,off_t offset,int whence)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 
ui_browser__rb_tree_refresh(struct ui_browser * self)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 
ui_browser__is_current_entry(struct ui_browser * self,unsigned row)159 bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
160 {
161 	return self->top_idx + row == self->index;
162 }
163 
ui_browser__refresh_dimensions(struct ui_browser * self)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 
ui_browser__handle_resize(struct ui_browser * browser)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 
ui_browser__warning(struct ui_browser * browser,int timeout,const char * format,...)179 int 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 
ui_browser__help_window(struct ui_browser * browser,const char * text)205 int 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 
ui_browser__dialog_yesno(struct ui_browser * browser,const char * text)215 bool 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 
ui_browser__reset_index(struct ui_browser * self)225 void 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 
__ui_browser__show_title(struct ui_browser * browser,const char * title)231 void __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 
ui_browser__show_title(struct ui_browser * browser,const char * title)238 void 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 
ui_browser__show(struct ui_browser * self,const char * title,const char * helpline,...)245 int 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 
ui_browser__hide(struct ui_browser * browser __used)269 void 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 
ui_browser__scrollbar_set(struct ui_browser * browser)276 static 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 
__ui_browser__refresh(struct ui_browser * browser)298 static 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 
ui_browser__refresh(struct ui_browser * browser)317 int 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  */
ui_browser__update_nr_entries(struct ui_browser * browser,u32 nr_entries)332 void 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 
ui_browser__run(struct ui_browser * self,int delay_secs)350 int 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 
ui_browser__list_head_refresh(struct ui_browser * self)448 unsigned 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 
471 static 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 
ui_browser__color_config(const char * var,const char * value,void * data __used)511 static 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 
ui_browser__argv_seek(struct ui_browser * browser,off_t offset,int whence)546 void 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 
ui_browser__argv_refresh(struct ui_browser * browser)563 unsigned 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 
ui_browser__init(void)587 void 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