gui: Fix config file reloading.
[paraslash.git] / gui.c
diff --git a/gui.c b/gui.c
index 9b7c2f992520896f2ae33918854604b37bbc7720..1a89681d196bce31b863e390bd60fc62f441a886 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1998-2014 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1998 Andre Noll <maan@tuebingen.mpg.de>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -124,11 +124,12 @@ GUI_COMMANDS
 static struct gui_command command_list[] = {GUI_COMMANDS {.name = NULL}};
 
 struct input_task {
-       struct task task;
+       struct task *task;
 };
 
 struct status_task {
-       struct task task;
+       struct task *task;
+       pid_t pid;
        char *buf;
        int bufsize, loaded;
        struct timeval next_exec;
@@ -139,7 +140,7 @@ struct status_task {
 #define COMMAND_BUF_SIZE 32768
 
 struct exec_task {
-       struct task task;
+       struct task *task;
        char command_buf[2][COMMAND_BUF_SIZE]; /* stdout/stderr of command */
        int cbo[2]; /* command buf offsets */
        unsigned flags[2]; /* passed to for_each_line() */
@@ -596,29 +597,42 @@ static void clear_all_items(void)
        }
 }
 
-static void status_pre_select(struct sched *s, struct task *t)
+static void status_pre_select(struct sched *s, void *context)
 {
-       struct status_task *st = container_of(t, struct status_task, task);
+       struct status_task *st = context;
 
        if (st->fd >= 0)
-               return para_fd_set(st->fd, &s->rfds, &s->max_fileno);
-       sched_request_barrier_or_min_delay(&st->next_exec, s);
+               para_fd_set(st->fd, &s->rfds, &s->max_fileno);
+       if (task_get_notification(st->task) < 0)
+               return sched_min_delay(s);
+       if (st->fd < 0)
+               sched_request_barrier_or_min_delay(&st->next_exec, s);
 }
 
-static int status_post_select(struct sched *s, struct task *t)
+static int status_post_select(struct sched *s, void *context)
 {
-       struct status_task *st = container_of(t, struct status_task, task);
+       struct status_task *st = context;
        size_t sz;
-       pid_t pid;
        int ret, ret2;
 
+       ret = task_get_notification(st->task);
+       if (ret == -E_GUI_SIGCHLD && st->pid > 0) {
+               int exit_status;
+               if (waitpid(st->pid, &exit_status, WNOHANG) == st->pid) {
+                       st->pid = 0;
+                       PARA_ERROR_LOG("stat command exit status: %d",
+                               exit_status);
+               }
+       }
        if (st->fd < 0) {
                int fds[3] = {0, 1, 0};
+               if (st->pid > 0)
+                       return 0;
                /* Avoid busy loop */
                if (tv_diff(&st->next_exec, now, NULL) > 0)
                        return 0;
                st->next_exec.tv_sec = now->tv_sec + 2;
-               ret = para_exec_cmdline_pid(&pid, conf.stat_cmd_arg, fds);
+               ret = para_exec_cmdline_pid(&st->pid, conf.stat_cmd_arg, fds);
                if (ret < 0)
                        return 0;
                ret = mark_fd_nonblocking(fds[1]);
@@ -645,7 +659,8 @@ static int status_post_select(struct sched *s, struct task *t)
        ret2 = for_each_stat_item(st->buf, st->loaded, update_item);
        if (ret < 0 || ret2 < 0) {
                st->loaded = 0;
-               PARA_NOTICE_LOG("closing stat pipe: %s\n", para_strerror(-ret));
+               PARA_NOTICE_LOG("closing stat pipe: %s\n",
+                       para_strerror(ret < 0? -ret : -ret2));
                close(st->fd);
                st->fd = -1;
                clear_all_items();
@@ -672,7 +687,7 @@ static void init_wins(int top_lines)
        int bot_lines = LINES - top_lines - 3, sb_lines = 1, in_lines = 1,
                sep_lines = 1;
 
-       assume_default_colors(theme.default_fg, theme.default_bg);
+       assume_default_colors(theme.dflt.fg, theme.dflt.bg);
        if (top.win) {
                wresize(top.win, top_lines, COLS);
                mvwin(top.win, top_y, 0);
@@ -744,16 +759,16 @@ static void init_colors_or_die(void)
                die(EXIT_FAILURE, "fatal: failed to start colors\n");
        FOR_EACH_STATUS_ITEM(i)
                if (theme.data[i].len)
-                       init_pair_or_die(i + 1, theme.data[i].fg,
-                               theme.data[i].bg);
-       init_pair_or_die(COLOR_STATUSBAR, theme.sb_fg, theme.sb_bg);
-       init_pair_or_die(COLOR_COMMAND, theme.cmd_fg, theme.cmd_bg);
-       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_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);
+                       init_pair_or_die(i + 1, theme.data[i].color.fg,
+                               theme.data[i].color.bg);
+       init_pair_or_die(COLOR_STATUSBAR, theme.sb.fg, theme.sb.bg);
+       init_pair_or_die(COLOR_COMMAND, theme.cmd.fg, theme.cmd.bg);
+       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_SEPARATOR, theme.sep.fg, theme.sep.bg);
+       init_pair_or_die(COLOR_TOP, theme.dflt.fg, theme.dflt.bg);
+       init_pair_or_die(COLOR_BOT, theme.dflt.fg, theme.dflt.bg);
 }
 
 /* (Re-)initialize the curses library. */
@@ -781,23 +796,6 @@ static void init_curses(void)
        // noecho(); /* don't echo input */
 }
 
-static void check_sigchld(void)
-{
-       int ret;
-       pid_t pid;
-
-reap_next_child:
-       ret = para_reap_child(&pid);
-       if (ret <= 0)
-               return;
-       if (pid == exec_pid) {
-               exec_pid = 0;
-               init_curses();
-               print_in_bar(COLOR_MSG, " ");
-       }
-       goto reap_next_child;
-}
-
 /*
  * This sucker modifies its first argument. *handler and *arg are
  * pointers to 0-terminated strings (inside line). Crap.
@@ -873,15 +871,32 @@ static void parse_config_file_or_die(bool override)
                }
                goto out;
        }
+       /*
+        * When the gengetopt config file parser is called more than once, any
+        * key map arguments found in the config file are _appended_ to the old
+        * values, even though we turn on ->override. We want the new arguments
+        * to replace the old ones, so we must empty the key_map_arg array
+        * first. Unfortunately, this also clears any key map arguments given
+        * at the command line.
+        */
+       if (override) {
+               int i;
+               for (i = 0; i < conf.key_map_given; i++) {
+                       free(conf.key_map_arg[i]);
+                       conf.key_map_arg[i] = NULL;
+               }
+               conf.key_map_given = 0;
+       }
+
        gui_cmdline_parser_config_file(config_file, &conf, &params);
        loglevel = get_loglevel_by_name(conf.loglevel_arg);
        check_key_map_args_or_die();
-       theme_init(conf.theme_arg, &theme);
        err = false;
 out:
        free(config_file);
        if (err)
                exit(EXIT_FAILURE);
+       theme_init(conf.theme_arg, &theme);
 }
 
 /* reread configuration, terminate on errors */
@@ -900,7 +915,7 @@ static void reread_conf(void)
 /*
  * React to various signal-related events
  */
-static int signal_post_select(struct sched *s, __a_unused struct task *t)
+static int signal_post_select(struct sched *s, __a_unused void *context)
 {
        int ret = para_next_signal(&s->rfds);
 
@@ -910,13 +925,6 @@ static int signal_post_select(struct sched *s, __a_unused struct task *t)
        case SIGTERM:
                die(EXIT_FAILURE, "only the good die young (caught SIGTERM)\n");
                return 1;
-       case SIGWINCH:
-               if (curses_active()) {
-                       shutdown_curses();
-                       init_curses();
-                       redraw_bot_win();
-               }
-               return 1;
        case SIGINT:
                PARA_WARNING_LOG("caught SIGINT, reset\n");
                /* Nothing to do. SIGINT killed our child which gets noticed
@@ -928,7 +936,7 @@ static int signal_post_select(struct sched *s, __a_unused struct task *t)
                reread_conf();
                return 1;
        case SIGCHLD:
-               check_sigchld();
+               task_notify_all(s, E_GUI_SIGCHLD);
                return 1;
        }
        return 1;
@@ -943,26 +951,32 @@ static enum exec_status exec_status(void)
        return EXEC_IDLE;
 }
 
-static void exec_pre_select(struct sched *s, __a_unused struct task *t)
+static void exec_pre_select(struct sched *s, void *context)
 {
-       enum exec_status es = exec_status();
-
-       if (es != EXEC_DCMD)
-               return;
+       struct exec_task *et = context;
        if (exec_fds[0] >= 0)
                para_fd_set(exec_fds[0], &s->rfds, &s->max_fileno);
        if (exec_fds[1] >= 0)
                para_fd_set(exec_fds[1], &s->rfds, &s->max_fileno);
+       if (task_get_notification(et->task) < 0)
+               sched_min_delay(s);
 }
 
-static int exec_post_select(struct sched *s, struct task *t)
+static int exec_post_select(struct sched *s, void *context)
 {
-       struct exec_task *ct = container_of(t, struct exec_task, task);
+       struct exec_task *ct = context;
        int i, ret;
-       enum exec_status es = exec_status();
 
-       if (es != EXEC_DCMD)
-               return 0;
+       ret = task_get_notification(ct->task);
+       if (ret == -E_GUI_SIGCHLD && exec_pid > 0) {
+               int exit_status;
+               if (waitpid(exec_pid, &exit_status, WNOHANG) == exec_pid) {
+                       exec_pid = 0;
+                       init_curses();
+                       PARA_INFO_LOG("command exit status: %d", exit_status);
+                       print_in_bar(COLOR_MSG, " ");
+               }
+       }
        for (i = 0; i < 2; i++) {
                size_t sz;
                if (exec_fds[i] < 0)
@@ -998,7 +1012,7 @@ static int exec_post_select(struct sched *s, struct task *t)
        return 0;
 }
 
-static void input_pre_select(struct sched *s, __a_unused struct task *t)
+static void input_pre_select(struct sched *s, __a_unused void *context)
 {
        if (exec_status() != EXEC_XCMD)
                para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
@@ -1095,7 +1109,7 @@ static void handle_command(int c)
                km_keyname(c));
 }
 
-static int input_post_select(__a_unused struct sched *s, __a_unused struct task *t)
+static int input_post_select(__a_unused struct sched *s, __a_unused void *context)
 {
        int ret;
        enum exec_status exs = exec_status();
@@ -1118,23 +1132,23 @@ static int input_post_select(__a_unused struct sched *s, __a_unused struct task
                        in.needs_update = sep.needs_update = false;
        }
        ret = wgetch(top.win);
-       if (ret == ERR || ret == KEY_RESIZE)
+       if (ret == ERR)
                return 0;
-       if (exs == EXEC_IDLE) {
-               handle_command(ret);
+       if (ret == KEY_RESIZE) {
+               if (curses_active()) {
+                       shutdown_curses();
+                       init_curses();
+                       redraw_bot_win();
+               }
                return 0;
        }
-       if (exec_pid != 0)
+       if (exs == EXEC_IDLE)
+               handle_command(ret);
+       else if (exec_pid > 0)
                kill(exec_pid, SIGTERM);
        return 0;
 }
 
-static void signal_pre_select(struct sched *s, struct task *t)
-{
-       struct signal_task *st = container_of(t, struct signal_task, task);
-       para_fd_set(st->fd, &s->rfds, &s->max_fileno);
-}
-
 static void print_scroll_msg(void)
 {
        unsigned lines_total, filled = ringbuffer_filled(bot_win_rb);
@@ -1405,53 +1419,54 @@ __noreturn static void print_help_and_die(void)
 
 static int setup_tasks_and_schedule(void)
 {
+       int ret;
+       struct exec_task exec_task = {.task = NULL};
+       struct status_task status_task = {.fd = -1};
+       struct input_task input_task = {.task = NULL};
+       struct signal_task *signal_task;
        struct sched sched = {
                .default_timeout = {
                        .tv_sec = conf.timeout_arg  / 1000,
                        .tv_usec = (conf.timeout_arg % 1000) * 1000,
                },
        };
-       struct exec_task exec_task = {
-               .task = {
-                       .status = "exec",
-                       .pre_select = exec_pre_select,
-                       .post_select = exec_post_select,
-               },
-       };
-       struct status_task status_task = {
-               .task = {
-                       .status = "status",
-                       .pre_select = status_pre_select,
-                       .post_select = status_post_select,
-               },
-               .fd = -1
-       };
-       struct input_task input_task = {
-               .task = {
-                       .status = "input",
-                       .pre_select = input_pre_select,
-                       .post_select = input_post_select,
-               },
-       };
-       struct signal_task signal_task = {
-               .task = {
-                       .status = "signal",
-                       .pre_select = signal_pre_select,
-                       .post_select = signal_post_select,
-               },
-       };
-       signal_task.fd = para_signal_init();
+
+       exec_task.task = task_register(&(struct task_info) {
+               .name = "exec",
+               .pre_select = exec_pre_select,
+               .post_select = exec_post_select,
+               .context = &exec_task,
+       }, &sched);
+
+       status_task.task = task_register(&(struct task_info) {
+               .name = "status",
+               .pre_select = status_pre_select,
+               .post_select = status_post_select,
+               .context = &status_task,
+       }, &sched);
+
+       input_task.task = task_register(&(struct task_info) {
+               .name = "input",
+               .pre_select = input_pre_select,
+               .post_select = input_post_select,
+               .context = &input_task,
+       }, &sched);
+
+       signal_task = signal_init_or_die();
        para_install_sighandler(SIGINT);
        para_install_sighandler(SIGTERM);
        para_install_sighandler(SIGCHLD);
-       para_install_sighandler(SIGWINCH);
        para_install_sighandler(SIGUSR1);
-
-       register_task(&sched, &exec_task.task);
-       register_task(&sched, &status_task.task);
-       register_task(&sched, &input_task.task);
-       register_task(&sched, &signal_task.task);
-       return schedule(&sched);
+       signal_task->task = task_register(&(struct task_info) {
+               .name = "signal",
+               .pre_select = signal_pre_select,
+               .post_select = signal_post_select,
+               .context = signal_task,
+       }, &sched);
+       ret = schedule(&sched);
+       sched_shutdown(&sched);
+       signal_shutdown(signal_task);
+       return ret;
 }
 
 /**