root/tools/perf/ui/tui/util.c
// SPDX-License-Identifier: GPL-2.0
#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ttydefaults.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);
        ui_browser__write_nstring(browser, *arg, browser->width);
}

static int popup_menu__run(struct ui_browser *menu, int *keyp)
{
        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:
                        if (keyp) {
                                *keyp = key;
                                key = menu->nr_entries;
                                break;
                        }
                        continue;
                }

                break;
        }

        ui_browser__hide(menu);
        return key;
}

int ui__popup_menu(int argc, char * const argv[], int *keyp)
{
        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, keyp);
}

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;
        }

        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(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(" ", max_len);
        }
        SLsmg_draw_box(y++, x + 1, 3, max_len - 2);

        SLsmg_gotorc(y + 3, x);
        SLsmg_write_nstring(exit_msg, max_len);
        SLsmg_refresh();

        mutex_unlock(&ui__lock);

        x += 2;
        len = 0;
        key = ui__getch(delay_secs);
        while (key != K_TIMER && key != K_ENTER && key != K_ESC) {
                mutex_lock(&ui__lock);

                if (key == K_BKSPC) {
                        if (len == 0) {
                                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();

                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;
}

void __ui__info_window(const char *title, const char *text, const char *exit_msg)
{
        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;
        }

        max_len += 2;
        nr_lines += 2;
        if (exit_msg)
                nr_lines += 2;
        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(title);
        }
        SLsmg_gotorc(++y, x);
        if (exit_msg)
                nr_lines -= 2;
        max_len -= 2;
        SLsmg_write_wrapped_string((unsigned char *)text, y, x,
                                   nr_lines, max_len, 1);
        if (exit_msg) {
                SLsmg_gotorc(y + nr_lines - 2, x);
                SLsmg_write_nstring(" ", max_len);
                SLsmg_gotorc(y + nr_lines - 1, x);
                SLsmg_write_nstring(exit_msg, max_len);
        }
}

void ui__info_window(const char *title, const char *text)
{
        mutex_lock(&ui__lock);
        __ui__info_window(title, text, NULL);
        SLsmg_refresh();
        mutex_unlock(&ui__lock);
}

int ui__question_window(const char *title, const char *text,
                        const char *exit_msg, int delay_secs)
{
        mutex_lock(&ui__lock);
        __ui__info_window(title, text, exit_msg);
        SLsmg_refresh();
        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,
};