gui: Clear top window only once on resize.
[paraslash.git] / gui.c
diff --git a/gui.c b/gui.c
index e854294ece0a4db3667480c80ed06160dd980f13..96043da05b5f9b602e229f131b5248c7f43d6190 100644 (file)
--- a/gui.c
+++ b/gui.c
 INIT_GUI_ERRLISTS;
 static char *stat_content[NUM_STAT_ITEMS];
 
-#define STANDARD_STATUS_BAR "para_gui " PACKAGE_VERSION " (hit ? for help)"
-
 static int signal_pipe;
 
 static struct gui_window {
        WINDOW *win;
-       size_t begx;
-       size_t begy;
        size_t cols;
        size_t lines;
 } top, bot, sb, in, sep;
@@ -53,7 +49,6 @@ static struct ringbuffer *bot_win_rb;
 
 static unsigned scroll_position;
 
-static bool curses_active;
 static pid_t cmd_pid;
 
 static int command_fds[2] = {-1, -1};
@@ -63,15 +58,22 @@ static struct gui_args_info conf;
 enum {GETCH_MODE, COMMAND_MODE, EXTERNAL_MODE};
 
 
-#define COLOR_STATUSBAR 52
-#define COLOR_COMMAND 53
-#define COLOR_OUTPUT 54
-#define COLOR_MSG 55
-#define COLOR_ERRMSG 56
-#define COLOR_WELCOME 57
-#define COLOR_SEPARATOR 58
-#define COLOR_TOP 59
-#define COLOR_BOT 60
+/**
+ * Codes for various colors.
+ *
+ * Each status item has its own color pair. The ones defined here start at a
+ * higher number so that they do not overlap with these.
+ */
+enum gui_color_pair {
+       COLOR_STATUSBAR = NUM_STAT_ITEMS + 1,
+       COLOR_COMMAND,
+       COLOR_OUTPUT,
+       COLOR_MSG,
+       COLOR_ERRMSG,
+       COLOR_SEPARATOR,
+       COLOR_TOP,
+       COLOR_BOT,
+};
 
 struct gui_command {
        const char *key;
@@ -204,6 +206,12 @@ static int find_cmd_byname(char *name)
        return -1;
 }
 
+/* isendwin() returns false before initscr() was called */
+static bool curses_active(void)
+{
+       return top.win && !isendwin();
+}
+
 /* taken from mutt */
 static char *km_keyname(int c)
 {
@@ -342,7 +350,7 @@ __printf_2_3 static void print_in_bar(int color, const char *fmt,...)
        char *msg;
        va_list ap;
 
-       if (!curses_active)
+       if (!curses_active())
                return;
        wattron(in.win, COLOR_PAIR(color));
        va_start(ap, fmt);
@@ -354,20 +362,14 @@ __printf_2_3 static void print_in_bar(int color, const char *fmt,...)
        wrefresh(in.win);
 }
 
-/*
- * update the status bar
- */
 static void print_status_bar(void)
 {
        char *tmp;
 
-       if (!curses_active)
-               return;
-       tmp = para_strdup(STANDARD_STATUS_BAR);
+       tmp = para_strdup("para_gui " PACKAGE_VERSION " (hit ? for help)");
        wmove(sb.win, 0, 0);
        align_str(sb.win, tmp, sb.cols, CENTER);
        free(tmp);
-       wrefresh(sb.win);
 }
 
 /*
@@ -494,7 +496,7 @@ __printf_2_3 static void outputf(int color, const char* fmt,...)
        char *msg;
        va_list ap;
 
-       if (!curses_active)
+       if (!curses_active())
                return;
        va_start(ap, fmt);
        xvasprintf(&msg, fmt, ap);
@@ -507,7 +509,7 @@ static int add_output_line(char *line, void *data)
 {
        int color = *(int *)data? COLOR_ERRMSG : COLOR_OUTPUT;
 
-       if (!curses_active)
+       if (!curses_active())
                return 1;
        rb_add_entry(color, para_strdup(line));
        return 1;
@@ -520,8 +522,9 @@ static __printf_2_3 void curses_log(int ll, const char *fmt,...)
        int color;
        char *msg;
        va_list ap;
+       unsigned bytes;
 
-       if (ll < loglevel || !curses_active)
+       if (ll < loglevel || !curses_active())
                return;
        switch (ll) {
                case LL_DEBUG:
@@ -533,9 +536,10 @@ static __printf_2_3 void curses_log(int ll, const char *fmt,...)
                        color = COLOR_ERRMSG;
        }
        va_start(ap, fmt);
-       xvasprintf(&msg, fmt, ap);
+       bytes = xvasprintf(&msg, fmt, ap);
        va_end(ap);
-       chop(msg);
+       if (bytes > 0 && msg[bytes - 1] == '\n')
+               msg[bytes - 1] = '\0'; /* cut trailing newline */
        rb_add_entry(color, msg);
        wrefresh(bot.win);
 }
@@ -561,10 +565,7 @@ __noreturn static void kill_pg_and_die(int ret)
 
 static void shutdown_curses(void)
 {
-       if (!curses_active)
-               return;
        def_prog_mode();
-       curses_active = false;
        endwin();
 }
 
@@ -589,69 +590,42 @@ __noreturn __printf_2_3 static void msg_n_exit(int ret, const char* fmt, ...)
        kill_pg_and_die(ret);
 }
 
-static void print_welcome(void)
-{
-       if (loglevel > LL_NOTICE)
-               return;
-       outputf(COLOR_WELCOME, "Welcome to %s. Theme: %s",
-               version_single_line("gui"), theme.name);
-       wclrtoeol(bot.win);
-}
-
 /*
  * init all windows
  */
 static void init_wins(int top_lines)
 {
-       int i;
+       int top_y = 0, bot_y = top_lines + 1, sb_y = LINES - 2,
+               in_y = LINES - 1, sep_y = top_lines;
 
        top.lines = top_lines;
-       top.cols = COLS;
-       top.begy = 0;
-       top.begx = 0;
-
        bot.lines = LINES - top.lines - 3;
-       bot.cols = COLS;
-       bot.begy = top.lines + 1;
-       bot.begx = 0;
-
-       sb.lines = 1;
-       sb.cols = COLS;
-       sb.begy = LINES - 2;
-       sb.begx = 0;
+       sb.lines = in.lines = sep.lines = 1;
 
-       in.lines = 1;
-       in.cols = COLS;
-       in.begy = LINES - 1;
-       in.begx = 0;
-
-       sep.lines = 1;
-       sep.cols = COLS;
-       sep.begy = top.lines;
-       sep.begx = 0;
+       top.cols = bot.cols = sb.cols = in.cols = sep.cols = COLS;
 
        assume_default_colors(theme.default_fg, theme.default_bg);
        if (top.win) {
-               mvwin(top.win, top.begy, top.begx);
                wresize(top.win, top.lines, top.cols);
+               mvwin(top.win, top_y, 0);
 
-               mvwin(sb.win, sb.begy, sb.begx);
                wresize(sb.win, sb.lines, sb.cols);
+               mvwin(sb.win, sb_y, 0);
 
-               mvwin(sep.win, sep.begy, sep.begx);
                wresize(sep.win, sep.lines, sep.cols);
+               mvwin(sep.win, sep_y, 0);
 
-               mvwin(bot.win, bot.begy, bot.begx);
                wresize(bot.win, bot.lines, bot.cols);
+               mvwin(bot.win, bot_y, 0);
 
-               mvwin(in.win, in.begy, in.begx);
                wresize(in.win, in.lines, in.cols);
+               mvwin(in.win, in_y, 0);
        } else {
-               sep.win = newwin(sep.lines, sep.cols, sep.begy, sep.begx);
-               top.win = newwin(top.lines, top.cols, top.begy, top.begx);
-               bot.win = newwin(bot.lines, bot.cols, bot.begy, bot.begx);
-               sb.win = newwin(sb.lines, sb.cols, sb.begy, sb.begx);
-               in.win = newwin(in.lines, in.cols, in.begy, in.begx);
+               sep.win = newwin(sep.lines, sep.cols, sep_y, 0);
+               top.win = newwin(top.lines, top.cols, top_y, 0);
+               bot.win = newwin(bot.lines, bot.cols, bot_y, 0);
+               sb.win = newwin(sb.lines, sb.cols, sb_y, 0);
+               in.win = newwin(in.lines, in.cols, in_y, 0);
                if (!top.win || !bot.win || !sb.win || !in.win || !sep.win)
                        msg_n_exit(1, "Error: Cannot create curses windows\n");
                wclear(bot.win);
@@ -671,17 +645,15 @@ static void init_wins(int top_lines)
                keypad(bot.win, 1);
                keypad(sb.win, 1);
                keypad(in.win, 1);
-               print_status_bar();
        }
        wmove(sep.win, 0, 0);
-       for (i = 1; i <= COLS; i++)
-               waddstr(sep.win, theme.sep_str);
+       whline(sep.win, theme.sep_char, COLS);
        wclear(top.win);
        //wclear(bot.win);
        wnoutrefresh(top.win);
        wnoutrefresh(bot.win);
-       //wnoutrefresh(sb.win);
        print_status_bar();
+       wnoutrefresh(sb.win);
        wnoutrefresh(in.win);
        wnoutrefresh(sep.win);
        doupdate();
@@ -696,7 +668,7 @@ static void print_stat_item(int i)
        struct stat_item_data d = theme.data[i];
        char *c = stat_content[i];
 
-       if (!curses_active || !d.len || !c)
+       if (!curses_active() || !d.len || !c)
                return;
        tmp = make_message("%s%s%s", d.prefix, c, d.postfix);
        wmove(top.win, d.y * top.lines / 100, d.x * COLS / 100);
@@ -775,7 +747,7 @@ static void print_all_items(void)
 {
        int i;
 
-       if (!curses_active)
+       if (!curses_active())
                return;
        FOR_EACH_STATUS_ITEM(i)
                print_stat_item(i);
@@ -814,7 +786,6 @@ static void init_colors_or_die(void)
        init_pair_or_die(COLOR_OUTPUT, theme.output_fg, theme.output_bg);
        init_pair_or_die(COLOR_MSG, theme.msg_fg, theme.msg_bg);
        init_pair_or_die(COLOR_ERRMSG, theme.err_msg_fg, theme.err_msg_bg);
-       init_pair_or_die(COLOR_WELCOME, theme.welcome_fg, theme.welcome_bg);
        init_pair_or_die(COLOR_SEPARATOR, theme.sep_fg, theme.sep_bg);
        init_pair_or_die(COLOR_TOP, theme.default_fg, theme.default_bg);
        init_pair_or_die(COLOR_BOT, theme.default_fg, theme.default_bg);
@@ -823,7 +794,8 @@ static void init_colors_or_die(void)
 /* (Re-)initialize the curses library. */
 static void init_curses(void)
 {
-       curses_active = true;
+       if (curses_active())
+               return;
        if (top.win && refresh() == ERR) /* refresh is really needed */
                msg_n_exit(EXIT_FAILURE, "refresh() failed\n");
        if (LINES < theme.lines_min || COLS < theme.cols_min)
@@ -921,7 +893,7 @@ static void handle_signal(int sig)
                        "only the good die young (caught SIGTERM))\n");
                return;
        case SIGWINCH:
-               if (curses_active) {
+               if (curses_active()) {
                        shutdown_curses();
                        init_curses();
                        redraw_bot_win();
@@ -1071,14 +1043,6 @@ check_return:
        case COMMAND_MODE:
                ret = wgetch(top.win);
                if (ret != ERR && ret != KEY_RESIZE) {
-                       if (command_fds[0] >= 0) {
-                               close(command_fds[0]);
-                               command_fds[0] = -1;
-                       }
-                       if (command_fds[1] >= 0) {
-                               close(command_fds[1]);
-                               command_fds[1] = -1;
-                       }
                        if (cmd_pid)
                                kill(cmd_pid, SIGTERM);
                        return -1;
@@ -1096,19 +1060,23 @@ check_return:
        goto repeat;
 }
 
-/*
- * read from command pipe and print data to bot window
- */
-static void send_output(void)
+/* read from command pipe and print data to bot window */
+static void exec_and_display_cmd(const char *cmd)
 {
-       int ret;
+       int ret, fds[3] = {0, 1, 1};
 
-       ret = mark_fd_nonblocking(command_fds[0]);
+       outputf(COLOR_COMMAND, "%s", cmd);
+       ret = para_exec_cmdline_pid(&cmd_pid, cmd, fds);
+       if (ret < 0)
+               return;
+       ret = mark_fd_nonblocking(fds[1]);
        if (ret < 0)
                goto fail;
-       ret = mark_fd_nonblocking(command_fds[1]);
+       ret = mark_fd_nonblocking(fds[2]);
        if (ret < 0)
                goto fail;
+       command_fds[0] = fds[1];
+       command_fds[1] = fds[2];
        if (do_select(COMMAND_MODE) >= 0)
                PARA_INFO_LOG("command complete\n");
        else
@@ -1123,34 +1091,18 @@ fail:
 
 static void para_cmd(char *cmd)
 {
-       int ret, fds[3] = {0, 1, 1};
-       char *c = make_message(BINDIR "/para_client -- %s", cmd);
+       char *c;
 
-       outputf(COLOR_COMMAND, "%s", c);
        print_in_bar(COLOR_MSG, "executing client command, hit any key to abort\n");
-       ret = para_exec_cmdline_pid(&cmd_pid, c, fds);
+       c = make_message(BINDIR "/para_client -- %s", cmd);
+       exec_and_display_cmd(c);
        free(c);
-       if (ret < 0)
-               return;
-       command_fds[0] = fds[1];
-       command_fds[1] = fds[2];
-       send_output();
 }
 
-/*
- * exec command and print output to bot win
- */
 static void display_cmd(char *cmd)
 {
-       int fds[3] = {0, 1, 1};
-
        print_in_bar(COLOR_MSG, "executing display command, hit any key to abort");
-       outputf(COLOR_COMMAND, "%s", cmd);
-       if (para_exec_cmdline_pid(&cmd_pid, cmd, fds) < 0)
-               return;
-       command_fds[0] = fds[1];
-       command_fds[1] = fds[2];
-       send_output();
+       exec_and_display_cmd(cmd);
 }
 
 /*
@@ -1411,7 +1363,6 @@ static void com_shrink_top_win(void)
                return;
        }
        init_wins(top.lines - 1);
-       wclear(top.win);
        print_all_items();
        print_in_bar(COLOR_MSG, "%s", "decreased top window");
 }
@@ -1423,7 +1374,6 @@ static void com_enlarge_top_win(void)
                return;
        }
        init_wins(top.lines + 1);
-       wclear(top.win);
        print_all_items();
        print_in_bar(COLOR_MSG, "increased top window");
 }
@@ -1444,29 +1394,18 @@ static void com_refresh(void)
        init_curses();
 }
 
-static void change_theme(int next)
-{
-       if (next)
-               next_theme(&theme);
-       else
-               prev_theme(&theme);
-       /* This seems to be needed twice, why? */
-       com_refresh();
-       com_refresh();
-       PARA_NOTICE_LOG("new theme: %s\n", theme.name);
-}
-
 static void com_next_theme(void)
 {
-       change_theme(1);
+       theme_next(&theme);
+       com_refresh();
 }
 
 static void com_prev_theme(void)
 {
-       change_theme(0);
+       theme_prev(&theme);
+       com_refresh();
 }
 
-
 static void handle_command(int c)
 {
        int i;
@@ -1552,13 +1491,12 @@ int main(int argc, char *argv[])
                fprintf(stderr, "invalid key map\n");
                exit(EXIT_FAILURE);
        }
-       init_theme_or_die(conf.theme_arg, &theme);
+       theme_init(conf.theme_arg, &theme);
        setup_signal_handling();
        bot_win_rb = ringbuffer_new(RINGBUFFER_SIZE);
        setlocale(LC_CTYPE, "");
        initscr(); /* needed only once, always successful */
        init_curses();
-       print_welcome();
        for (;;) {
                print_status_bar();
                ret = do_select(GETCH_MODE);