diff options
Diffstat (limited to 'tools/perf/ui/tui')
| -rw-r--r-- | tools/perf/ui/tui/helpline.c | 58 | ||||
| -rw-r--r-- | tools/perf/ui/tui/progress.c | 44 | ||||
| -rw-r--r-- | tools/perf/ui/tui/setup.c | 151 | ||||
| -rw-r--r-- | tools/perf/ui/tui/tui.h | 6 | ||||
| -rw-r--r-- | tools/perf/ui/tui/util.c | 256 | 
5 files changed, 515 insertions, 0 deletions
diff --git a/tools/perf/ui/tui/helpline.c b/tools/perf/ui/tui/helpline.c new file mode 100644 index 00000000000..1c8b9afd5d6 --- /dev/null +++ b/tools/perf/ui/tui/helpline.c @@ -0,0 +1,58 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> + +#include "../../util/debug.h" +#include "../helpline.h" +#include "../ui.h" +#include "../libslang.h" + +char ui_helpline__last_msg[1024]; + +static void tui_helpline__pop(void) +{ +} + +static void tui_helpline__push(const char *msg) +{ +	const size_t sz = sizeof(ui_helpline__current); + +	SLsmg_gotorc(SLtt_Screen_Rows - 1, 0); +	SLsmg_set_color(0); +	SLsmg_write_nstring((char *)msg, SLtt_Screen_Cols); +	SLsmg_refresh(); +	strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0'; +} + +static int tui_helpline__show(const char *format, va_list ap) +{ +	int ret; +	static int backlog; + +	pthread_mutex_lock(&ui__lock); +	ret = vscnprintf(ui_helpline__last_msg + backlog, +			sizeof(ui_helpline__last_msg) - backlog, format, ap); +	backlog += ret; + +	if (ui_helpline__last_msg[backlog - 1] == '\n') { +		ui_helpline__puts(ui_helpline__last_msg); +		SLsmg_refresh(); +		backlog = 0; +	} +	pthread_mutex_unlock(&ui__lock); + +	return ret; +} + +struct ui_helpline tui_helpline_fns = { +	.pop	= tui_helpline__pop, +	.push	= tui_helpline__push, +	.show	= tui_helpline__show, +}; + +void ui_helpline__init(void) +{ +	helpline_fns = &tui_helpline_fns; +	ui_helpline__puts(" "); +} diff --git a/tools/perf/ui/tui/progress.c b/tools/perf/ui/tui/progress.c new file mode 100644 index 00000000000..c61d14b101e --- /dev/null +++ b/tools/perf/ui/tui/progress.c @@ -0,0 +1,44 @@ +#include "../cache.h" +#include "../progress.h" +#include "../libslang.h" +#include "../ui.h" +#include "tui.h" +#include "../browser.h" + +static void tui_progress__update(struct ui_progress *p) +{ +	int bar, y; +	/* +	 * FIXME: We should have a per UI backend way of showing progress, +	 * stdio will just show a percentage as NN%, etc. +	 */ +	if (use_browser <= 0) +		return; + +	if (p->total == 0) +		return; + +	ui__refresh_dimensions(false); +	pthread_mutex_lock(&ui__lock); +	y = SLtt_Screen_Rows / 2 - 2; +	SLsmg_set_color(0); +	SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols); +	SLsmg_gotorc(y++, 1); +	SLsmg_write_string((char *)p->title); +	SLsmg_fill_region(y, 1, 1, SLtt_Screen_Cols - 2, ' '); +	SLsmg_set_color(HE_COLORSET_SELECTED); +	bar = ((SLtt_Screen_Cols - 2) * p->curr) / p->total; +	SLsmg_fill_region(y, 1, 1, bar, ' '); +	SLsmg_refresh(); +	pthread_mutex_unlock(&ui__lock); +} + +static struct ui_progress_ops tui_progress__ops = +{ +	.update		= tui_progress__update, +}; + +void tui_progress__init(void) +{ +	ui_progress__ops = &tui_progress__ops; +} diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c new file mode 100644 index 00000000000..2f612562978 --- /dev/null +++ b/tools/perf/ui/tui/setup.c @@ -0,0 +1,151 @@ +#include <signal.h> +#include <stdbool.h> + +#include "../../util/cache.h" +#include "../../util/debug.h" +#include "../browser.h" +#include "../helpline.h" +#include "../ui.h" +#include "../util.h" +#include "../libslang.h" +#include "../keysyms.h" +#include "tui.h" + +static volatile int ui__need_resize; + +extern struct perf_error_ops perf_tui_eops; + +extern void hist_browser__init_hpp(void); + +void ui__refresh_dimensions(bool force) +{ +	if (force || ui__need_resize) { +		ui__need_resize = 0; +		pthread_mutex_lock(&ui__lock); +		SLtt_get_screen_size(); +		SLsmg_reinit_smg(); +		pthread_mutex_unlock(&ui__lock); +	} +} + +static void ui__sigwinch(int sig __maybe_unused) +{ +	ui__need_resize = 1; +} + +static void ui__setup_sigwinch(void) +{ +	static bool done; + +	if (done) +		return; + +	done = true; +	pthread__unblock_sigwinch(); +	signal(SIGWINCH, ui__sigwinch); +} + +int ui__getch(int delay_secs) +{ +	struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL; +	fd_set read_set; +	int err, key; + +	ui__setup_sigwinch(); + +	FD_ZERO(&read_set); +	FD_SET(0, &read_set); + +	if (delay_secs) { +		timeout.tv_sec = delay_secs; +		timeout.tv_usec = 0; +	} + +        err = select(1, &read_set, NULL, NULL, ptimeout); + +	if (err == 0) +		return K_TIMER; + +	if (err == -1) { +		if (errno == EINTR) +			return K_RESIZE; +		return K_ERROR; +	} + +	key = SLang_getkey(); +	if (key != K_ESC) +		return key; + +	FD_ZERO(&read_set); +	FD_SET(0, &read_set); +	timeout.tv_sec = 0; +	timeout.tv_usec = 20; +        err = select(1, &read_set, NULL, NULL, &timeout); +	if (err == 0) +		return K_ESC; + +	SLang_ungetkey(key); +	return SLkp_getkey(); +} + +static void ui__signal(int sig) +{ +	ui__exit(false); +	psignal(sig, "perf"); +	exit(0); +} + +int ui__init(void) +{ +	int err; + +	SLutf8_enable(-1); +	SLtt_get_terminfo(); +	SLtt_get_screen_size(); + +	err = SLsmg_init_smg(); +	if (err < 0) +		goto out; +	err = SLang_init_tty(0, 0, 0); +	if (err < 0) +		goto out; + +	err = SLkp_init(); +	if (err < 0) { +		pr_err("TUI initialization failed.\n"); +		goto out; +	} + +	SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); + +	ui_helpline__init(); +	ui_browser__init(); +	tui_progress__init(); + +	signal(SIGSEGV, ui__signal); +	signal(SIGFPE, ui__signal); +	signal(SIGINT, ui__signal); +	signal(SIGQUIT, ui__signal); +	signal(SIGTERM, ui__signal); + +	perf_error__register(&perf_tui_eops); + +	hist_browser__init_hpp(); +out: +	return err; +} + +void ui__exit(bool wait_for_ok) +{ +	if (wait_for_ok) +		ui__question_window("Fatal Error", +				    ui_helpline__last_msg, +				    "Press any key...", 0); + +	SLtt_set_cursor_visibility(1); +	SLsmg_refresh(); +	SLsmg_reset_smg(); +	SLang_reset_tty(); + +	perf_error__unregister(&perf_tui_eops); +} diff --git a/tools/perf/ui/tui/tui.h b/tools/perf/ui/tui/tui.h new file mode 100644 index 00000000000..18961c7b6ec --- /dev/null +++ b/tools/perf/ui/tui/tui.h @@ -0,0 +1,6 @@ +#ifndef _PERF_TUI_H_ +#define _PERF_TUI_H_ 1 + +void tui_progress__init(void); + +#endif /* _PERF_TUI_H_ */ diff --git a/tools/perf/ui/tui/util.c b/tools/perf/ui/tui/util.c new file mode 100644 index 00000000000..bf890f72fe8 --- /dev/null +++ b/tools/perf/ui/tui/util.c @@ -0,0 +1,256 @@ +#include "../../util/util.h" +#include <signal.h> +#include <stdbool.h> +#include <string.h> +#include <sys/ttydefaults.h> + +#include "../../util/cache.h" +#include "../../util/debug.h" +#include "../browser.h" +#include "../keysyms.h" +#include "../helpline.h" +#include "../ui.h" +#include "../util.h" +#include "../libslang.h" + +static void ui_browser__argv_write(struct ui_browser *browser, +				   void *entry, int row) +{ +	char **arg = entry; +	bool current_entry = ui_browser__is_current_entry(browser, row); + +	ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : +						       HE_COLORSET_NORMAL); +	slsmg_write_nstring(*arg, browser->width); +} + +static int popup_menu__run(struct ui_browser *menu) +{ +	int key; + +	if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0) +		return -1; + +	while (1) { +		key = ui_browser__run(menu, 0); + +		switch (key) { +		case K_RIGHT: +		case K_ENTER: +			key = menu->index; +			break; +		case K_LEFT: +		case K_ESC: +		case 'q': +		case CTRL('c'): +			key = -1; +			break; +		default: +			continue; +		} + +		break; +	} + +	ui_browser__hide(menu); +	return key; +} + +int ui__popup_menu(int argc, char * const argv[]) +{ +	struct ui_browser menu = { +		.entries    = (void *)argv, +		.refresh    = ui_browser__argv_refresh, +		.seek	    = ui_browser__argv_seek, +		.write	    = ui_browser__argv_write, +		.nr_entries = argc, +	}; + +	return popup_menu__run(&menu); +} + +int ui_browser__input_window(const char *title, const char *text, char *input, +			     const char *exit_msg, int delay_secs) +{ +	int x, y, len, key; +	int max_len = 60, nr_lines = 0; +	static char buf[50]; +	const char *t; + +	t = text; +	while (1) { +		const char *sep = strchr(t, '\n'); + +		if (sep == NULL) +			sep = strchr(t, '\0'); +		len = sep - t; +		if (max_len < len) +			max_len = len; +		++nr_lines; +		if (*sep == '\0') +			break; +		t = sep + 1; +	} + +	pthread_mutex_lock(&ui__lock); + +	max_len += 2; +	nr_lines += 8; +	y = SLtt_Screen_Rows / 2 - nr_lines / 2; +	x = SLtt_Screen_Cols / 2 - max_len / 2; + +	SLsmg_set_color(0); +	SLsmg_draw_box(y, x++, nr_lines, max_len); +	if (title) { +		SLsmg_gotorc(y, x + 1); +		SLsmg_write_string((char *)title); +	} +	SLsmg_gotorc(++y, x); +	nr_lines -= 7; +	max_len -= 2; +	SLsmg_write_wrapped_string((unsigned char *)text, y, x, +				   nr_lines, max_len, 1); +	y += nr_lines; +	len = 5; +	while (len--) { +		SLsmg_gotorc(y + len - 1, x); +		SLsmg_write_nstring((char *)" ", max_len); +	} +	SLsmg_draw_box(y++, x + 1, 3, max_len - 2); + +	SLsmg_gotorc(y + 3, x); +	SLsmg_write_nstring((char *)exit_msg, max_len); +	SLsmg_refresh(); + +	pthread_mutex_unlock(&ui__lock); + +	x += 2; +	len = 0; +	key = ui__getch(delay_secs); +	while (key != K_TIMER && key != K_ENTER && key != K_ESC) { +		pthread_mutex_lock(&ui__lock); + +		if (key == K_BKSPC) { +			if (len == 0) { +				pthread_mutex_unlock(&ui__lock); +				goto next_key; +			} +			SLsmg_gotorc(y, x + --len); +			SLsmg_write_char(' '); +		} else { +			buf[len] = key; +			SLsmg_gotorc(y, x + len++); +			SLsmg_write_char(key); +		} +		SLsmg_refresh(); + +		pthread_mutex_unlock(&ui__lock); + +		/* XXX more graceful overflow handling needed */ +		if (len == sizeof(buf) - 1) { +			ui_helpline__push("maximum size of symbol name reached!"); +			key = K_ENTER; +			break; +		} +next_key: +		key = ui__getch(delay_secs); +	} + +	buf[len] = '\0'; +	strncpy(input, buf, len+1); +	return key; +} + +int ui__question_window(const char *title, const char *text, +			const char *exit_msg, int delay_secs) +{ +	int x, y; +	int max_len = 0, nr_lines = 0; +	const char *t; + +	t = text; +	while (1) { +		const char *sep = strchr(t, '\n'); +		int len; + +		if (sep == NULL) +			sep = strchr(t, '\0'); +		len = sep - t; +		if (max_len < len) +			max_len = len; +		++nr_lines; +		if (*sep == '\0') +			break; +		t = sep + 1; +	} + +	pthread_mutex_lock(&ui__lock); + +	max_len += 2; +	nr_lines += 4; +	y = SLtt_Screen_Rows / 2 - nr_lines / 2, +	x = SLtt_Screen_Cols / 2 - max_len / 2; + +	SLsmg_set_color(0); +	SLsmg_draw_box(y, x++, nr_lines, max_len); +	if (title) { +		SLsmg_gotorc(y, x + 1); +		SLsmg_write_string((char *)title); +	} +	SLsmg_gotorc(++y, x); +	nr_lines -= 2; +	max_len -= 2; +	SLsmg_write_wrapped_string((unsigned char *)text, y, x, +				   nr_lines, max_len, 1); +	SLsmg_gotorc(y + nr_lines - 2, x); +	SLsmg_write_nstring((char *)" ", max_len); +	SLsmg_gotorc(y + nr_lines - 1, x); +	SLsmg_write_nstring((char *)exit_msg, max_len); +	SLsmg_refresh(); + +	pthread_mutex_unlock(&ui__lock); + +	return ui__getch(delay_secs); +} + +int ui__help_window(const char *text) +{ +	return ui__question_window("Help", text, "Press any key...", 0); +} + +int ui__dialog_yesno(const char *msg) +{ +	return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0); +} + +static int __ui__warning(const char *title, const char *format, va_list args) +{ +	char *s; + +	if (vasprintf(&s, format, args) > 0) { +		int key; + +		key = ui__question_window(title, s, "Press any key...", 0); +		free(s); +		return key; +	} + +	fprintf(stderr, "%s\n", title); +	vfprintf(stderr, format, args); +	return K_ESC; +} + +static int perf_tui__error(const char *format, va_list args) +{ +	return __ui__warning("Error:", format, args); +} + +static int perf_tui__warning(const char *format, va_list args) +{ +	return __ui__warning("Warning:", format, args); +} + +struct perf_error_ops perf_tui_eops = { +	.error		= perf_tui__error, +	.warning	= perf_tui__warning, +};  | 
