]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 't/sched_improvements'
authorAndre Noll <maan@systemlinux.org>
Wed, 23 Jul 2014 15:29:29 +0000 (17:29 +0200)
committerAndre Noll <maan@systemlinux.org>
Wed, 23 Jul 2014 15:45:26 +0000 (17:45 +0200)
Cooking for two months. This merge required to also patch gui.c due
to semantic conflics against the changes introduced by the gui_sched
branch which was merged to master in commit d15d8509 two weeks ago.
Also a small fix for server.c is needed to squelch a compiler warning
since the global "now" variable has become a const pointer.

* t/sched_improvements: (36 commits)
  audiod: Fix use after free on exit.
  sched: Mark global now pointer as const.
  sched: Directly pass context pointer to pre/post_select().
  sched: kill task->dead.
  sched: Do not shadow task_info in struct task.
  sched: Dont use fixed-size buffer for task names.
  sched: Rename task->error to tast->status.
  sched: Rename task->status to task->name.
  sched: Make struct task private to sched.c.
  sched: Introduce task_status().
  sched: Remove ->owned_by_sched.
  sched: Remove register_task().
  task_register() conversion: grab client task
  task_register() conversion: audiod status task
  task_register() conversion: audiod command task
  task_register() conversion: client task
  task_register() conversion: client supervisor task
  task_register() conversion: client exec task
  task_register() conversion: afs command task
  task_register() conversion: vss task
  ...

1  2 
NEWS
audioc.c
gui.c
server.c
wmadec_filter.c

diff --combined NEWS
index c613d672415945cdb99ec24509b832d833fe7f04,642c1fc422225141a1fa1a555113746a69b8a93e..311aca2b06996005eb1ce2972a5c4df5116f6a58
--- 1/NEWS
--- 2/NEWS
+++ b/NEWS
@@@ -5,16 -5,11 +5,19 @@@ NEW
  0.5.3 (to be released) "symbolic synchronization"
  -------------------------------------------------
  
++Not many new features, but lots of fixes and usability improvements.
++
 +      - para_gui has been converted to use the paraslash scheduler.
        - Various alsa-related fixes, mostly for the raspberry pi.
++      - Many scheduler improvements and cleanups.
        - The test suite has been extended to include sanity checks
          for the generated man pages.
        - ao_writer fixes. This writer was in a quite bad shape. Many
          serious bugs have been fixed.
 +      - new audiod command: version.
 +      - Minor improvements to the bitstream API.
 +      - The cpsi command now prints a meaningful error message if
 +        none of the given patterns matched any audio file.
  
  ----------------------------------------
  0.5.2 (2014-04-11) "orthogonal interior"
diff --combined audioc.c
index fe7165edd966d1afeaf46c15c252fa70c229e2c6,e50267fdfba290bf7ba39050afd5a388ced41ff0..83425a2e485fd49ea3b40a9ab60183865c14a25d
+++ b/audioc.c
@@@ -79,7 -79,7 +79,7 @@@ static struct sched sched
  struct audioc_task {
        int fd;
        struct btr_node *btrn;
-       struct task task;
+       struct task *task;
  };
  
  static struct i9e_completer audiod_completers[];
@@@ -97,15 -97,6 +97,15 @@@ static void help_completer(struct i9e_c
        result->matches = i9e_complete_commands(ci->word, audiod_completers);
  }
  
 +static void version_completer(struct i9e_completion_info *ci,
 +              struct i9e_completion_result *cr)
 +{
 +      char *opts[] = {"-v", NULL};
 +
 +      if (ci->word_num <= 2 && ci->word && ci->word[0] == '-')
 +              i9e_complete_option(opts, ci, cr);
 +}
 +
  static void stat_completer(struct i9e_completion_info *ci,
                struct i9e_completion_result *cr)
  {
@@@ -130,9 -121,9 +130,9 @@@ static struct i9e_completer audiod_comp
        {.name = NULL}
  };
  
- static void audioc_pre_select(struct sched *s, struct task *t)
+ static void audioc_pre_select(struct sched *s, void *context)
  {
-       struct audioc_task *at = container_of(t, struct audioc_task, task);
+       struct audioc_task *at = context;
        int ret = btr_node_status(at->btrn, 0, BTR_NT_ROOT);
  
        if (ret < 0)
        para_fd_set(at->fd, &s->rfds, &s->max_fileno);
  }
  
- static int audioc_post_select(struct sched *s, struct task *t)
+ static int audioc_post_select(struct sched *s, void *context)
  {
        char *buf = NULL;
-       struct audioc_task *at = container_of(t, struct audioc_task, task);
+       struct audioc_task *at = context;
        int ret = btr_node_status(at->btrn, 0, BTR_NT_ROOT);
  
        if (ret < 0)
@@@ -168,13 -159,7 +168,7 @@@ out
        return ret;
  }
  
- static struct audioc_task audioc_task = {
-       .task = {
-               .pre_select = audioc_pre_select,
-               .post_select = audioc_post_select,
-               .status = "audioc task"
-       },
- }, *at = &audioc_task;
+ static struct audioc_task audioc_task, *at = &audioc_task;
  
  static int audioc_i9e_line_handler(char *line)
  {
        args = NULL;
        at->btrn = btr_new_node(&(struct btr_node_description)
                EMBRACE(.name = "audioc line handler"));
-       at->task.error = 0;
-       register_task(&sched, &at->task);
+       at->task = task_register(&(struct task_info) {
+               .name = "audioc",
+               .pre_select = audioc_pre_select,
+               .post_select = audioc_post_select,
+               .context = at,
+       }, &sched);
        i9e_attach_to_stdout(at->btrn);
        return 1;
  close:
@@@ -248,6 -237,7 +246,7 @@@ __noreturn static void interactive_sess
                goto out;
        para_log = i9e_log;
        ret = schedule(&sched);
+       sched_shutdown(&sched);
        i9e_close();
        para_log = stderr_log;
  out:
diff --combined gui.c
index b077b00aa9dd08a5c01dbdec06003781013d11cb,5a2e659d159e6b6225b5ec94adc2796138d5b358..92b7f7cfa0a7826ab5ca95ded4d4a8ca340f46d9
--- 1/gui.c
--- 2/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 win_data {
 +static struct gui_window {
        WINDOW *win;
 -      size_t begx;
 -      size_t begy;
 -      size_t cols;
 -      size_t lines;
 +      bool needs_update;
  } top, bot, sb, in, sep;
  
 +/** How many lines of output to remember. */
  #define RINGBUFFER_SIZE 512
 +
  struct rb_entry {
        char *msg;
        size_t len;
        int color;
  };
  static struct ringbuffer *bot_win_rb;
 -#define NUM_LINES(len) (1 + (len) / bot.cols)
  
  static unsigned scroll_position;
  
 -static int curses_active;
 -static pid_t cmd_pid;
 +static pid_t exec_pid;
  
 -static int command_fds[2];
 -static int stat_pipe = -1;
 +static int exec_fds[2] = {-1, -1};
  static struct gui_args_info conf;
 +static int loglevel;
  
 -enum {GETCH_MODE, COMMAND_MODE, EXTERNAL_MODE};
 -
 +/** Type of the process currently being executed. */
 +enum exec_status {
 +      EXEC_IDLE, /**< No process running. */
 +      EXEC_DCMD, /**< para or display process running. */
 +      EXEC_XCMD, /**< External process running. */
 +};
  
 -#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;
        void (*handler)(void);
  };
  
 -struct stat_item {
 -      char name[MAXLINE];
 -      char prefix[MAXLINE];
 -      char postfix[MAXLINE];
 -      unsigned y;
 -      unsigned x;
 -      unsigned len;
 -      int fg, bg;
 -      int align;
 -      char content[MAXLINE];
 +static struct gui_theme theme;
 +
 +#define GUI_COMMANDS \
 +      GUI_COMMAND(help, "?", "print help") \
 +      GUI_COMMAND(enlarge_top_win, "+", "enlarge the top window") \
 +      GUI_COMMAND(shrink_top_win, "-", "shrink the top window") \
 +      GUI_COMMAND(reread_conf, "r", "reread configuration file") \
 +      GUI_COMMAND(quit, "q", "exit para_gui") \
 +      GUI_COMMAND(refresh, "^L", "redraw the screen") \
 +      GUI_COMMAND(next_theme, ".", "switch to next theme") \
 +      GUI_COMMAND(prev_theme, ",", "switch to previous theme") \
 +      GUI_COMMAND(ll_incr, ">", "increase loglevel (decreases verbosity)") \
 +      GUI_COMMAND(ll_decr, "<", "decrease loglevel (increases verbosity)") \
 +      GUI_COMMAND(version, "V", "show the para_gui version") \
 +      GUI_COMMAND(scroll_up, "<up>", "scroll up one line") \
 +      GUI_COMMAND(scroll_down, "<down>", "scroll_down") \
 +      GUI_COMMAND(page_up, "<ppage>", "scroll up one page") \
 +      GUI_COMMAND(page_down, "<npage>", "scroll down one page") \
 +      GUI_COMMAND(scroll_top, "<home>", "scroll to top of buffer") \
 +      GUI_COMMAND(cancel_scroll, "<end>", "deactivate scroll mode") \
 +
 +/* declare command handlers */
 +#define GUI_COMMAND(_c, _k, _d) \
 +      static void com_ ## _c(void);
 +GUI_COMMANDS
 +
 +#undef GUI_COMMAND
 +
 +/* define command array */
 +#define GUI_COMMAND(_c, _k, _d) \
 +      { \
 +              .key = _k, \
 +              .name = #_c, \
 +              .description = _d, \
 +              .handler = com_ ## _c \
 +      },
 +
 +static struct gui_command command_list[] = {GUI_COMMANDS {.name = NULL}};
 +
 +struct input_task {
-       struct task task;
++      struct task *task;
  };
  
 -static struct gui_theme theme;
 +struct status_task {
-       struct task task;
++      struct task *task;
 +      pid_t pid;
 +      char *buf;
 +      int bufsize, loaded;
 +      struct timeval next_exec;
 +      int fd;
 +};
  
 -static int _argc;
 -static char **_argv;
 -
 -static void com_help(void);
 -static void com_reread_conf(void);
 -static void com_enlarge_top_win(void);
 -static void com_shrink_top_win(void);
 -static void com_version(void);
 -__noreturn static void com_quit(void);
 -static void com_refresh(void);
 -static void com_ll_incr(void);
 -static void com_ll_decr(void);
 -static void com_prev_theme(void);
 -static void com_next_theme(void);
 -static void com_scroll_up(void);
 -static void com_scroll_down(void);
 -static void com_page_up(void);
 -static void com_page_down(void);
 -static void com_cancel_scrolling(void);
 -static void com_scroll_top(void);
 -
 -static struct gui_command command_list[] = {
 -      {
 -              .key = "?",
 -              .name = "help",
 -              .description = "print help",
 -              .handler = com_help
 -      }, {
 -              .key = "+",
 -              .name = "enlarge_win",
 -              .description = "enlarge the top window",
 -              .handler = com_enlarge_top_win
 -      }, {
 -              .key = "-",
 -              .name = "shrink_win",
 -              .description = "shrink the top window",
 -              .handler = com_shrink_top_win
 -      }, {
 -              .key = "r",
 -              .name = "reread_conf",
 -              .description = "reread configuration file",
 -              .handler = com_reread_conf
 -      }, {
 -              .key = "q",
 -              .name = "quit",
 -              .description = "exit para_gui",
 -              .handler = com_quit
 -      }, {
 -              .key = "^L",
 -              .name = "refresh",
 -              .description = "redraw the screen",
 -              .handler = com_refresh
 -      }, {
 -              .key = ".",
 -              .name = "next_theme",
 -              .description = "switch to next theme",
 -              .handler = com_next_theme
 -      }, {
 -              .key = ",",
 -              .name = "prev_theme",
 -              .description = "switch to previous stream",
 -              .handler = com_prev_theme
 -      }, {
 -              .key = ">",
 -              .name = "ll_incr",
 -              .description = "increase loglevel (decreases verbosity)",
 -              .handler = com_ll_incr
 -      }, {
 -              .key = "<",
 -              .name = "ll_decr",
 -              .description = "decrease loglevel (increases verbosity)",
 -              .handler = com_ll_decr
 -      }, {
 -              .key = "V",
 -              .name = "version",
 -              .description = "show the para_gui version",
 -              .handler = com_version
 -      }, {
 -              .key = "<up>",
 -              .name = "scroll_up",
 -              .description = "scroll up one line",
 -              .handler = com_scroll_up
 -      }, {
 -              .key = "<down>",
 -              .name = "scroll_down",
 -              .description = "scroll down one line",
 -              .handler = com_scroll_down
 -      }, {
 -              .key = "<ppage>",
 -              .name = "page_up",
 -              .description = "scroll up one page",
 -              .handler = com_page_up
 -      }, {
 -              .key = "<npage>",
 -              .name = "page_down",
 -              .description = "scroll down one page",
 -              .handler = com_page_down
 -      }, {
 -              .key = "<home>",
 -              .name = "scroll_top",
 -              .description = "scroll to top of buffer",
 -              .handler = com_scroll_top
 -      }, {
 -              .key = "<end>",
 -              .name = "cancel_scroll",
 -              .description = "deactivate scroll mode",
 -              .handler = com_cancel_scrolling
 -      }, {
 -              .handler = NULL
 -      }
 +/** Stdout/stderr of the executing process is read in chunks of this size. */
 +#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() */
  };
  
  static int find_cmd_byname(char *name)
        return -1;
  }
  
 +/*
 + * Even though ncurses provides getmaxx and getmaxy, these functions/macros are
 + * not described in the XSI Curses standard.
 + */
 +static int get_num_lines(struct gui_window *w)
 +{
 +      int lines;
 +      __a_unused int cols; /* avoid "set but not used" warnings */
 +
 +      getmaxyx(w->win, lines, cols);
 +      return lines;
 +}
 +
 +static int get_num_cols(struct gui_window *w)
 +{
 +      __a_unused int lines; /* avoid "set but not used" warnings */
 +      int cols;
 +
 +      getmaxyx(w->win, lines, cols);
 +      return cols;
 +}
 +
 +/** Number of lines of the window are occupied by an output line. */
 +#define NUM_LINES(len) (1 + (len) / get_num_cols(&bot))
 +
 +/* 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)
  {
        return buf;
  }
  
 -static char *configfile_exists(void)
 -{
 -      static char *config_file;
 -      char *tmp;
 -
 -      if (!conf.config_file_given) {
 -              if (!config_file) {
 -                      char *home = para_homedir();
 -                      config_file = make_message("%s/.paraslash/gui.conf",
 -                              home);
 -                      free(home);
 -              }
 -              tmp = config_file;
 -      } else
 -              tmp = conf.config_file_arg;
 -      return file_exists(tmp)? tmp: NULL;
 -}
 -
  /* Print given number of spaces to curses window. */
  static void add_spaces(WINDOW* win, unsigned int num)
  {
@@@ -302,64 -349,59 +302,64 @@@ static int align_str(WINDOW* win, char 
        return 1;
  }
  
 +static void refresh_window(struct gui_window *gw)
 +{
 +      gw->needs_update = true;
 +}
 +
 +static bool window_update_needed(void)
 +{
 +      return top.needs_update || bot.needs_update || sb.needs_update ||
 +              in.needs_update || sep.needs_update;
 +}
 +
  __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);
        xvasprintf(&msg, fmt, ap);
        va_end(ap);
        wmove(in.win, 0, 0);
 -      align_str(in.win, msg, sb.cols, LEFT);
 +      align_str(in.win, msg, get_num_cols(&in), LEFT);
        free(msg);
 -      wrefresh(in.win);
 +      refresh_window(&in);
  }
  
 -/*
 - * 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);
 +      align_str(sb.win, tmp, get_num_cols(&sb), CENTER);
        free(tmp);
 -      wrefresh(sb.win);
  }
  
  /*
   * get the number of the oldest rbe that is (partially) visible. On return,
 - * lines contains the sum of the number of lines of all visable entries. If the
 + * lines contains the sum of the number of lines of all visible entries. If the
   * first one is only partially visible, lines is greater than bot.lines.
   */
  static int first_visible_rbe(unsigned *lines)
  {
 -      int i;
 +      int i, bot_lines = get_num_lines(&bot);
 +
        *lines = 0;
        for (i = scroll_position; i < RINGBUFFER_SIZE; i++) {
                struct rb_entry *rbe = ringbuffer_get(bot_win_rb, i);
                int rbe_lines;
                if (!rbe)
                        return i - 1;
 -//            fprintf(stderr, "found: %s\n", rbe->msg);
                rbe_lines = NUM_LINES(rbe->len);
 -              if (rbe_lines > bot.lines)
 +              if (rbe_lines > bot_lines)
                        return -1;
                *lines += rbe_lines;
 -              if (*lines >= bot.lines)
 +              if (*lines >= bot_lines)
                        return i;
        }
        return RINGBUFFER_SIZE - 1;
@@@ -370,7 -412,7 +370,7 @@@ returns number of first visible rbe, *l
   */
  static int draw_top_rbe(unsigned *lines)
  {
 -      int ret, fvr = first_visible_rbe(lines);
 +      int bot_cols, bot_lines, ret, fvr = first_visible_rbe(lines);
        struct rb_entry *rbe;
        size_t bytes_to_skip, cells_to_skip, width;
  
        rbe = ringbuffer_get(bot_win_rb, fvr);
        if (!rbe)
                return -1;
 -      if (*lines > bot.lines) {
 +      getmaxyx(bot.win, bot_lines, bot_cols);
 +      if (*lines > bot_lines) {
                /* rbe is partially visible multi-line */
 -              cells_to_skip = (*lines - bot.lines) * bot.cols;
 +              cells_to_skip = (*lines - bot_lines) * bot_cols;
                ret = skip_cells(rbe->msg, cells_to_skip, &bytes_to_skip);
                if (ret < 0)
                        return ret;
  static void redraw_bot_win(void)
  {
        unsigned lines;
 -      int i;
 +      int i, bot_lines = get_num_lines(&bot);
  
        wmove(bot.win, 0, 0);
        wclear(bot.win);
        i = draw_top_rbe(&lines);
        if (i <= 0)
                goto out;
 -      while (i > 0 && lines < bot.lines) {
 +      while (i > 0 && lines < bot_lines) {
                struct rb_entry *rbe = ringbuffer_get(bot_win_rb, --i);
                if (!rbe) {
                        lines++;
                waddstr(bot.win, rbe->msg);
        }
  out:
 -      wrefresh(bot.win);
 +      refresh_window(&bot);
  }
  
  static void rb_add_entry(int color, char *msg)
        new->len = len;
        new->msg = msg;
        old = ringbuffer_add(bot_win_rb, new);
 -//    fprintf(stderr, "added: %s\n", new->msg);
        if (old) {
                free(old->msg);
                free(old);
@@@ -464,66 -506,196 +464,66 @@@ __printf_2_3 static void outputf(int co
        char *msg;
        va_list ap;
  
 -      if (!curses_active)
 +      if (!curses_active())
                return;
        va_start(ap, fmt);
        xvasprintf(&msg, fmt, ap);
        va_end(ap);
        rb_add_entry(color, msg);
 -      wrefresh(bot.win);
 +      refresh_window(&bot);
  }
  
  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;
  }
  
 -static int loglevel;
 -
  static __printf_2_3 void curses_log(int ll, const char *fmt,...)
  {
 -      int color;
 -      char *msg;
        va_list ap;
  
 -      if (ll < loglevel || !curses_active)
 +      if (ll < loglevel)
                return;
 -      switch (ll) {
 -              case LL_DEBUG:
 -              case LL_INFO:
 -              case LL_NOTICE:
 -                      color = COLOR_MSG;
 -                      break;
 -              default:
 -                      color = COLOR_ERRMSG;
 -      }
        va_start(ap, fmt);
 -      xvasprintf(&msg, fmt, ap);
 +      if (curses_active()) {
 +              int color = ll <= LL_NOTICE? COLOR_MSG : COLOR_ERRMSG;
 +              char *msg;
 +              unsigned bytes = xvasprintf(&msg, fmt, ap);
 +              if (bytes > 0 && msg[bytes - 1] == '\n')
 +                      msg[bytes - 1] = '\0'; /* cut trailing newline */
 +              rb_add_entry(color, msg);
 +              refresh_window(&bot);
 +      } else if (exec_pid <= 0) /* no external command running */
 +              vfprintf(stderr, fmt, ap);
        va_end(ap);
 -      chop(msg);
 -      rb_add_entry(color, msg);
 -      wrefresh(bot.win);
  }
 +/** The log function of para_gui, always set to curses_log(). */
  __printf_2_3 void (*para_log)(int, const char*, ...) = curses_log;
  
 -static void setup_signal_handling(void)
 -{
 -      signal_pipe = para_signal_init();
 -      para_install_sighandler(SIGINT);
 -      para_install_sighandler(SIGTERM);
 -      para_install_sighandler(SIGCHLD);
 -      para_install_sighandler(SIGWINCH);
 -      para_install_sighandler(SIGUSR1);
 -}
 -
 -/* kill every process in the process group and exit */
 -__noreturn static void kill_pg_and_die(int ret)
 -{
 -      para_sigaction(SIGTERM, SIG_IGN);
 -      kill(0, SIGTERM);
 -      exit(ret);
 -}
 -
  static void shutdown_curses(void)
  {
 -      if (!curses_active)
 -              return;
        def_prog_mode();
 -      curses_active = 0;
        endwin();
  }
  
 -__noreturn static void finish(int ret)
 -{
 -      shutdown_curses();
 -      kill_pg_and_die(ret);
 -}
 -
 -/*
 - * exit curses and print given message to stdout/stderr
 - */
 -__noreturn __printf_2_3 static void msg_n_exit(int ret, const char* fmt, ...)
 +/* disable curses, print a message, kill running processes and exit */
 +__noreturn __printf_2_3 static void die(int exit_code, const char* fmt, ...)
  {
        va_list argp;
 -      FILE *outfd = ret? stderr: stdout;
  
        shutdown_curses();
        va_start(argp, fmt);
 -      vfprintf(outfd, fmt, argp);
 +      vfprintf(stderr, fmt, argp);
        va_end(argp);
 -      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;
 -
 -      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;
 -
 -      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;
 -
 -      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(sb.win, sb.begy, sb.begx);
 -              wresize(sb.win, sb.lines, sb.cols);
 -
 -              mvwin(sep.win, sep.begy, sep.begx);
 -              wresize(sep.win, sep.lines, sep.cols);
 -
 -              mvwin(bot.win, bot.begy, bot.begx);
 -              wresize(bot.win, bot.lines, bot.cols);
 -
 -              mvwin(in.win, in.begy, in.begx);
 -              wresize(in.win, in.lines, in.cols);
 -      } 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);
 -              if (!top.win || !bot.win || !sb.win || !in.win || !sep.win)
 -                      msg_n_exit(1, "Error: Cannot create curses windows\n");
 -              wclear(bot.win);
 -              wclear(sb.win);
 -              wclear(in.win);
 -              scrollok(bot.win, 1);
 -              wattron(sb.win, COLOR_PAIR(COLOR_STATUSBAR));
 -              wattron(sep.win, COLOR_PAIR(COLOR_SEPARATOR));
 -              wattron(bot.win, COLOR_PAIR(COLOR_BOT));
 -              wattron(top.win, COLOR_PAIR(COLOR_TOP));
 -              nodelay(top.win, 1);
 -              nodelay(bot.win, 1);
 -              nodelay(sb.win, 1);
 -              nodelay(in.win, 0);
 -
 -              keypad(top.win, 1);
 -              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);
 -      wclear(top.win);
 -      //wclear(bot.win);
 -      wnoutrefresh(top.win);
 -      wnoutrefresh(bot.win);
 -      //wnoutrefresh(sb.win);
 -      print_status_bar();
 -      wnoutrefresh(in.win);
 -      wnoutrefresh(sep.win);
 -      doupdate();
 +      /* kill every process in the process group and exit */
 +      para_sigaction(SIGTERM, SIG_IGN);
 +      kill(0, SIGTERM);
 +      exit(exit_code);
  }
  
  /*
@@@ -534,16 -706,16 +534,16 @@@ static void print_stat_item(int i
        char *tmp;
        struct stat_item_data d = theme.data[i];
        char *c = stat_content[i];
 +      int top_lines = get_num_lines(&top);
  
 -      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);
 -      wrefresh(top.win);
 +      wmove(top.win, d.y * top_lines / 100, d.x * COLS / 100);
        wattron(top.win, COLOR_PAIR(i + 1));
        align_str(top.win, tmp, d.len * COLS / 100, d.align);
        free(tmp);
 -      wrefresh(top.win);
 +      refresh_window(&top);
  }
  
  static int update_item(int item_num, char *buf)
@@@ -577,11 -749,44 +577,11 @@@ print
        return 1;
  }
  
 -static int read_stat_pipe(fd_set *rfds)
 -{
 -      static char *buf;
 -      static int bufsize, loaded;
 -      int ret, ret2;
 -      size_t sz;
 -
 -      if (stat_pipe < 0)
 -              return 0;
 -      if (loaded >= bufsize) {
 -              if (bufsize > 1000 * 1000) {
 -                      loaded = 0;
 -                      return 0;
 -              }
 -              bufsize += bufsize + 1000;
 -              buf = para_realloc(buf, bufsize);
 -      }
 -      assert(loaded < bufsize);
 -      ret = read_nonblock(stat_pipe, buf + loaded, bufsize - loaded,
 -              rfds, &sz);
 -      loaded += sz;
 -      ret2 = for_each_stat_item(buf, loaded, update_item);
 -      if (ret < 0 || ret2 < 0) {
 -              loaded = 0;
 -              return ret2 < 0? ret2 : ret;
 -      }
 -      sz = ret2; /* what is left */
 -      if (sz > 0 && sz < loaded)
 -              memmove(buf, buf + loaded - sz, sz);
 -      loaded = sz;
 -      return 1;
 -}
 -
  static void print_all_items(void)
  {
        int i;
  
 -      if (!curses_active)
 +      if (!curses_active())
                return;
        FOR_EACH_STATUS_ITEM(i)
                print_stat_item(i);
@@@ -597,155 -802,10 +597,155 @@@ 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)
 +              para_fd_set(st->fd, &s->rfds, &s->max_fileno);
-       if (task_get_notification(t) < 0)
++      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;
 +      int ret, ret2;
 +
-       ret = task_get_notification(t);
++      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(&st->pid, conf.stat_cmd_arg, fds);
 +              if (ret < 0)
 +                      return 0;
 +              ret = mark_fd_nonblocking(fds[1]);
 +              if (ret < 0) {
 +                      close(fds[1]);
 +                      return 0;
 +              }
 +              st->fd = fds[1];
 +              return 0;
 +      }
 +
 +      if (st->loaded >= st->bufsize) {
 +              if (st->bufsize > 1000 * 1000) {
 +                      st->loaded = 0;
 +                      return 0;
 +              }
 +              st->bufsize += st->bufsize + 1000;
 +              st->buf = para_realloc(st->buf, st->bufsize);
 +      }
 +      assert(st->loaded < st->bufsize);
 +      ret = read_nonblock(st->fd, st->buf + st->loaded,
 +              st->bufsize - st->loaded, &s->rfds, &sz);
 +      st->loaded += sz;
 +      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));
 +              close(st->fd);
 +              st->fd = -1;
 +              clear_all_items();
 +              free(stat_content[SI_BASENAME]);
 +              stat_content[SI_BASENAME] =
 +                      para_strdup("stat command terminated!?");
 +              print_all_items();
 +              return 0;
 +      }
 +      sz = ret2; /* what is left */
 +      if (sz > 0 && sz < st->loaded)
 +              memmove(st->buf, st->buf + st->loaded - sz, sz);
 +      st->loaded = sz;
 +      return 0;
 +}
 +
 +/*
 + * init all windows
 + */
 +static void init_wins(int top_lines)
 +{
 +      int top_y = 0, bot_y = top_lines + 1, sb_y = LINES - 2,
 +              in_y = LINES - 1, sep_y = top_lines;
 +      int bot_lines = LINES - top_lines - 3, sb_lines = 1, in_lines = 1,
 +              sep_lines = 1;
 +
 +      assume_default_colors(theme.dflt.fg, theme.dflt.bg);
 +      if (top.win) {
 +              wresize(top.win, top_lines, COLS);
 +              mvwin(top.win, top_y, 0);
 +
 +              wresize(sb.win, sb_lines, COLS);
 +              mvwin(sb.win, sb_y, 0);
 +
 +              wresize(sep.win, sep_lines, COLS);
 +              mvwin(sep.win, sep_y, 0);
 +
 +              wresize(bot.win, bot_lines, COLS);
 +              mvwin(bot.win, bot_y, 0);
 +
 +              wresize(in.win, in_lines, COLS);
 +              mvwin(in.win, in_y, 0);
 +      } else {
 +              sep.win = newwin(sep_lines, COLS, sep_y, 0);
 +              top.win = newwin(top_lines, COLS, top_y, 0);
 +              bot.win = newwin(bot_lines, COLS, bot_y, 0);
 +              sb.win = newwin(sb_lines, COLS, sb_y, 0);
 +              in.win = newwin(in_lines, COLS, in_y, 0);
 +              if (!top.win || !bot.win || !sb.win || !in.win || !sep.win)
 +                      die(EXIT_FAILURE, "Error: Cannot create curses windows\n");
 +              wclear(bot.win);
 +              wclear(sb.win);
 +              wclear(in.win);
 +              scrollok(bot.win, 1);
 +              wattron(sb.win, COLOR_PAIR(COLOR_STATUSBAR));
 +              wattron(sep.win, COLOR_PAIR(COLOR_SEPARATOR));
 +              wattron(bot.win, COLOR_PAIR(COLOR_BOT));
 +              wattron(top.win, COLOR_PAIR(COLOR_TOP));
 +              nodelay(top.win, 1);
 +              nodelay(bot.win, 1);
 +              nodelay(sb.win, 1);
 +              nodelay(in.win, 0);
 +
 +              keypad(top.win, 1);
 +              keypad(bot.win, 1);
 +              keypad(sb.win, 1);
 +              keypad(in.win, 1);
 +      }
 +      wmove(sep.win, 0, 0);
 +      whline(sep.win, theme.sep_char, COLS);
 +      wclear(top.win);
 +      print_all_items();
 +      //wclear(bot.win);
 +      wnoutrefresh(top.win);
 +      wnoutrefresh(bot.win);
 +      print_status_bar();
 +      wnoutrefresh(sb.win);
 +      wnoutrefresh(in.win);
 +      wnoutrefresh(sep.win);
 +      doupdate();
 +}
 +
  static void init_pair_or_die(short pair, short f, short b)
  {
        if (init_pair(pair, f, b) == ERR)
 -              msg_n_exit(EXIT_FAILURE, "fatal: init_pair() failed\n");
 +              die(EXIT_FAILURE, "fatal: init_pair() failed\n");
  }
  
  static void init_colors_or_die(void)
        int i;
  
        if (!has_colors())
 -              msg_n_exit(EXIT_FAILURE, "fatal: No color term\n");
 +              die(EXIT_FAILURE, "fatal: No color term\n");
        if (start_color() == ERR)
 -              msg_n_exit(EXIT_FAILURE, "fatal: failed to start colors\n");
 +              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_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);
 +                      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. */
  static void init_curses(void)
  {
 -      curses_active = 1;
 -      if (top.win && refresh() == ERR) /* refesh is really needed */
 -              msg_n_exit(EXIT_FAILURE, "refresh() failed\n");
 +      if (curses_active())
 +              return;
 +      if (top.win && refresh() == ERR) /* refresh is really needed */
 +              die(EXIT_FAILURE, "refresh() failed\n");
        if (LINES < theme.lines_min || COLS < theme.cols_min)
 -              msg_n_exit(EXIT_FAILURE, "Error: Terminal (%dx%d) too small"
 +              die(EXIT_FAILURE, "Terminal (%dx%d) too small"
                        " (need at least %dx%d)\n", COLS, LINES,
                        theme.cols_min, theme.lines_min);
        curs_set(0); /* make cursor invisible, ignore errors */
        nonl(); /* do not NL->CR/NL on output, always returns OK */
        /* don't echo input */
        if (noecho() == ERR)
 -              msg_n_exit(EXIT_FAILURE, "fatal: noecho() failed\n");
 +              die(EXIT_FAILURE, "fatal: noecho() failed\n");
        /* take input chars one at a time, no wait for \n */
        if (cbreak() == ERR)
 -              msg_n_exit(EXIT_FAILURE, "fatal: cbreak() failed\n");
 +              die(EXIT_FAILURE, "fatal: cbreak() failed\n");
        init_colors_or_die();
        clear(); /* ignore non-fatal errors */
        init_wins(theme.top_lines_default);
 -      print_all_items();
        // 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 == cmd_pid)
 -              cmd_pid = 0;
 -      goto reap_next_child;
 -}
 -
  /*
   * This sucker modifies its first argument. *handler and *arg are
   * pointers to 0-terminated strings (inside line). Crap.
@@@ -814,346 -888,313 +814,347 @@@ err_out
        return 0;
  }
  
 -static int check_key_map_args(void)
 +static void check_key_map_args_or_die(void)
  {
 -      char *s;
 -      int i, ret = -1;
 -      char *tmp = NULL, *handler, *arg;
 +      int i;
 +      char *tmp = NULL;
  
        for (i = 0; i < conf.key_map_given; ++i) {
 -              s = conf.key_map_arg[i];
 -              if (!(*s))
 -                      goto err_out;
 +              char *handler, *arg;
 +
                free(tmp);
 -              tmp = para_strdup(s);
 +              tmp = para_strdup(conf.key_map_arg[i]);
                if (!split_key_map(tmp, &handler, &arg))
 -                      goto err_out;
 +                      break;
                if (strlen(handler) != 1)
 -                      goto err_out;
 -              if (*handler != 'x'
 -                      && *handler != 'd'
 -                      && *handler != 'i'
 -                      && *handler != 'p')
 -                      goto err_out;
 +                      break;
 +              if (*handler != 'x' && *handler != 'd' && *handler != 'i'
 +                              && *handler != 'p')
 +                      break;
                if (*handler != 'i')
                        continue;
                if (find_cmd_byname(arg) < 0)
 -                      goto err_out;
 +                      break;
        }
 -      ret = 0;
 -err_out:
 +      if (i != conf.key_map_given)
 +              die(EXIT_FAILURE, "invalid key map: %s\n", conf.key_map_arg[i]);
        free(tmp);
 -      return ret;
 +}
 +
 +static void parse_config_file_or_die(bool override)
 +{
 +      bool err;
 +      char *config_file;
 +      struct gui_cmdline_parser_params params = {
 +              .override = override,
 +              .initialize = 0,
 +              .check_required = !override,
 +              .check_ambiguity = 0,
 +              .print_errors = 1,
 +      };
 +
 +      if (conf.config_file_given)
 +              config_file = para_strdup(conf.config_file_arg);
 +      else {
 +              char *home = para_homedir();
 +              config_file = make_message("%s/.paraslash/gui.conf", home);
 +              free(home);
 +      }
 +      if (!file_exists(config_file)) {
 +              if (!conf.config_file_given)
 +                      err = false;
 +              else {
 +                      PARA_EMERG_LOG("config file %s does not exist\n",
 +                              config_file);
 +                      err = true;
 +              }
 +              goto out;
 +      }
 +      gui_cmdline_parser_config_file(config_file, &conf, &params);
 +      loglevel = get_loglevel_by_name(conf.loglevel_arg);
 +      check_key_map_args_or_die();
 +      err = false;
 +out:
 +      free(config_file);
 +      if (err)
 +              exit(EXIT_FAILURE);
 +      theme_init(conf.theme_arg, &theme);
 +}
 +
 +/* reread configuration, terminate on errors */
 +static void reread_conf(void)
 +{
 +      /*
 +       * gengetopt might print to stderr and exit on errors. So we have to
 +       * shutdown curses first.
 +       */
 +      shutdown_curses();
 +      parse_config_file_or_die(true /* override */);
 +      init_curses();
 +      print_in_bar(COLOR_MSG, "config file reloaded\n");
  }
  
  /*
   * React to various signal-related events
   */
- static int signal_post_select(struct sched *s, __a_unused struct task *t)
 -static void handle_signal(int sig)
++static int signal_post_select(struct sched *s, __a_unused void *context)
  {
 -      switch (sig) {
 +      int ret = para_next_signal(&s->rfds);
 +
 +      if (ret <= 0)
 +              return 0;
 +      switch (ret) {
        case SIGTERM:
 -              msg_n_exit(EXIT_FAILURE,
 -                      "only the good die young (caught SIGTERM))\n");
 -              return;
 -      case SIGWINCH:
 -              if (curses_active) {
 -                      shutdown_curses();
 -                      init_curses();
 -                      redraw_bot_win();
 -              }
 -              return;
 +              die(EXIT_FAILURE, "only the good die young (caught SIGTERM)\n");
 +              return 1;
        case SIGINT:
 -              PARA_WARNING_LOG("caught SIGINT, reset");
 +              PARA_WARNING_LOG("caught SIGINT, reset\n");
                /* Nothing to do. SIGINT killed our child which gets noticed
                 * by do_select and resets everything.
                 */
 -              return;
 +              return 1;
        case SIGUSR1:
 -              PARA_NOTICE_LOG("got SIGUSR1, rereading configuration");
 -              com_reread_conf();
 -              return;
 +              PARA_NOTICE_LOG("got SIGUSR1, rereading configuration\n");
 +              reread_conf();
 +              return 1;
        case SIGCHLD:
 -              check_sigchld();
 -              return;
 +              task_notify_all(s, E_GUI_SIGCHLD);
 +              return 1;
        }
 +      return 1;
  }
  
 -static void status_pre_select(fd_set *rfds, int *max_fileno, struct timeval *tv)
 +static enum exec_status exec_status(void)
  {
 -      static struct timeval next_exec, atm, diff;
 -      int ret, fds[3] = {0, 1, 0};
 -      pid_t pid;
 -
 -      if (stat_pipe >= 0)
 -              goto success;
 -      /* Avoid busy loop */
 -      gettimeofday(&atm, NULL);
 -      if (tv_diff(&next_exec, &atm, &diff) > 0) {
 -              if (tv_diff(&diff, tv, NULL) < 0)
 -                      *tv = diff;
 -              return;
 -      }
 -      next_exec.tv_sec = atm.tv_sec + 2;
 -      ret = para_exec_cmdline_pid(&pid, conf.stat_cmd_arg, fds);
 -      if (ret < 0)
 -              return;
 -      ret = mark_fd_nonblocking(fds[1]);
 -      if (ret < 0) {
 -              close(fds[1]);
 -              return;
 -      }
 -      stat_pipe = fds[1];
 -success:
 -      para_fd_set(stat_pipe, rfds, max_fileno);
 +      if (exec_fds[0] >= 0 || exec_fds[1] >= 0)
 +              return EXEC_DCMD;
 +      if (exec_pid > 0)
 +              return EXEC_XCMD;
 +      return EXEC_IDLE;
  }
  
- static void exec_pre_select(struct sched *s, struct task *t)
 -#define COMMAND_BUF_SIZE 32768
++static void exec_pre_select(struct sched *s, void *context)
 +{
++      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(t) < 0)
++      if (task_get_notification(et->task) < 0)
 +              sched_min_delay(s);
 +}
  
- static int exec_post_select(struct sched *s, struct task *t)
 -/*
 - * This is the core select loop. Besides the (internal) signal
 - * pipe, the following other fds are checked according to the mode:
 - *
 - * GETCH_MODE: check stdin, return when key is pressed
 - *
 - * COMMAND_MODE: check command fds and stdin. Return when peer has closed both
 - * stdout and stderr or when any key is pressed.
 - *
 - * EXTERNAL_MODE: Check only signal pipe. Used when an external command
 - * is running. During that time curses is disabled.  Returns when
 - * cmd_pid == 0.
 - */
 -static int do_select(int mode)
 -{
 -      fd_set rfds;
 -      int ret, i, max_fileno;
 -      char command_buf[2][COMMAND_BUF_SIZE] = {"", ""};
 -      int cbo[2] = {0, 0}; /* command buf offsets */
 -      struct timeval tv;
 -      unsigned flags[2] = {0, 0}; /* for for_each_line() */
 -
 -repeat:
 -      tv.tv_sec = conf.timeout_arg  / 1000;
 -      tv.tv_usec = (conf.timeout_arg % 1000) * 1000;
 -//    ret = refresh_status();
 -      FD_ZERO(&rfds);
 -      max_fileno = 0;
 -      status_pre_select(&rfds, &max_fileno, &tv);
 -      /* signal pipe */
 -      para_fd_set(signal_pipe, &rfds, &max_fileno);
 -      /* command pipe only for COMMAND_MODE */
 -      if (mode == COMMAND_MODE) {
 -              if (command_fds[0] >= 0)
 -                      para_fd_set(command_fds[0], &rfds, &max_fileno);
 -              if (command_fds[1] >= 0)
 -                      para_fd_set(command_fds[1], &rfds, &max_fileno);
++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;
 +
-       ret = task_get_notification(t);
++      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, " ");
 +              }
        }
 -      if (mode == GETCH_MODE || mode == COMMAND_MODE)
 -              para_fd_set(STDIN_FILENO, &rfds, &max_fileno);
 -      ret = para_select(max_fileno + 1, &rfds, NULL, &tv);
 -      if (ret <= 0)
 -              goto check_return; /* skip fd checks */
 -      /* signals */
 -      ret = para_next_signal(&rfds);
 -      if (ret > 0)
 -              handle_signal(ret);
 -      /* read command pipe if ready */
 -      if (mode == COMMAND_MODE) {
 -              for (i = 0; i < 2; i++) {
 -                      size_t sz;
 -                      if (command_fds[i] < 0)
 -                              continue;
 -                      ret = read_nonblock(command_fds[i],
 -                              command_buf[i] + cbo[i],
 -                              COMMAND_BUF_SIZE - 1 - cbo[i], &rfds, &sz);
 -                      cbo[i] += sz;
 -                      sz = cbo[i];
 -                      cbo[i] = for_each_line(flags[i], command_buf[i], cbo[i],
 -                              add_output_line, &i);
 -                      if (sz != cbo[i]) { /* at least one line found */
 -                              wrefresh(bot.win);
 -                              flags[i] = 0;
 -                      }
 -                      if (ret < 0) {
 +      for (i = 0; i < 2; i++) {
 +              size_t sz;
 +              if (exec_fds[i] < 0)
 +                      continue;
 +              ret = read_nonblock(exec_fds[i],
 +                      ct->command_buf[i] + ct->cbo[i],
 +                      COMMAND_BUF_SIZE - 1 - ct->cbo[i], &s->rfds, &sz);
 +              ct->cbo[i] += sz;
 +              sz = ct->cbo[i];
 +              ct->cbo[i] = for_each_line(ct->flags[i], ct->command_buf[i],
 +                      ct->cbo[i], add_output_line, &i);
 +              if (sz != ct->cbo[i]) { /* at least one line found */
 +                      refresh_window(&bot);
 +                      ct->flags[i] = 0;
 +              }
 +              if (ret < 0 || exec_pid == 0) {
 +                      if (ret < 0)
                                PARA_NOTICE_LOG("closing command fd %d: %s",
                                        i, para_strerror(-ret));
 -                              close(command_fds[i]);
 -                              command_fds[i] = -1;
 -                              flags[i] = 0;
 -                              if (command_fds[!i] < 0) /* both fds closed */
 -                                      return 0;
 -                      }
 -                      if (cbo[i] == COMMAND_BUF_SIZE - 1) {
 -                              PARA_NOTICE_LOG("discarding overlong line");
 -                              cbo[i] = 0;
 -                              flags[i] = FELF_DISCARD_FIRST;
 -                      }
 +                      close(exec_fds[i]);
 +                      exec_fds[i] = -1;
 +                      ct->flags[i] = 0;
 +                      ct->cbo[i] = 0;
 +                      if (exec_fds[!i] < 0) /* both fds closed */
 +                              return 1;
                }
 -      }
 -      ret = read_stat_pipe(&rfds);
 -      if (ret < 0) {
 -              PARA_NOTICE_LOG("closing stat pipe: %s\n", para_strerror(-ret));
 -              close(stat_pipe);
 -              stat_pipe = -1;
 -              clear_all_items();
 -              free(stat_content[SI_BASENAME]);
 -              stat_content[SI_BASENAME] =
 -                      para_strdup("stat command terminated!?");
 -              print_all_items();
 -      }
 -check_return:
 -      switch (mode) {
 -      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;
 +              if (ct->cbo[i] == COMMAND_BUF_SIZE - 1) {
 +                      PARA_NOTICE_LOG("discarding overlong line");
 +                      ct->cbo[i] = 0;
 +                      ct->flags[i] = FELF_DISCARD_FIRST;
                }
 -              break;
 -      case GETCH_MODE:
 -              ret = wgetch(top.win);
 -              if (ret != ERR && ret != KEY_RESIZE)
 -                      return ret;
 -              break;
 -      case EXTERNAL_MODE:
 -              if (cmd_pid == 0)
 -                      return 0;
        }
 -      goto repeat;
 +      return 0;
  }
  
- static void input_pre_select(struct sched *s, __a_unused struct task *t)
 -/*
 - * read from command pipe and print data to bot window
 - */
 -static void send_output(void)
++static void input_pre_select(struct sched *s, __a_unused void *context)
  {
 -      int ret;
 +      if (exec_status() != EXEC_XCMD)
 +              para_fd_set(STDIN_FILENO, &s->rfds, &s->max_fileno);
 +      if (window_update_needed())
 +              sched_min_delay(s);
 +}
 +
 +/* read from command pipe and print data to bot window */
 +static void exec_and_display(const char *file_and_args)
 +{
 +      int ret, fds[3] = {0, 1, 1};
  
 -      ret = mark_fd_nonblocking(command_fds[0]);
 +      outputf(COLOR_COMMAND, "%s", file_and_args);
 +      ret = para_exec_cmdline_pid(&exec_pid, file_and_args, 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;
 -      if (do_select(COMMAND_MODE) >= 0)
 -              PARA_INFO_LOG("command complete");
 -      else
 -              PARA_NOTICE_LOG("command aborted");
 -      print_in_bar(COLOR_MSG, " ");
 +      exec_fds[0] = fds[1];
 +      exec_fds[1] = fds[2];
 +      print_in_bar(COLOR_MSG, "hit any key to abort\n");
        return;
  fail:
        PARA_ERROR_LOG("%s\n", para_strerror(-ret));
 -      close(command_fds[0]);
 -      close(command_fds[1]);
 +      close(exec_fds[0]);
 +      close(exec_fds[1]);
  }
  
 -static void para_cmd(char *cmd)
 +static void exec_para(const char *args)
  {
 -      int ret, fds[3] = {0, 1, 1};
 -      char *c = make_message(BINDIR "/para_client -- %s", cmd);
 +      char *file_and_args;
  
 -      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);
 -      free(c);
 -      if (ret < 0)
 -              return;
 -      command_fds[0] = fds[1];
 -      command_fds[1] = fds[2];
 -      send_output();
 +      file_and_args = make_message(BINDIR "/para_client -- %s", args);
 +      exec_and_display(file_and_args);
 +      free(file_and_args);
  }
  
  /*
 - * exec command and print output to bot win
 + * shutdown curses and stat pipe before executing external commands
   */
 -static void display_cmd(char *cmd)
 +static void exec_external(char *file_and_args)
  {
 -      int fds[3] = {0, 1, 1};
 +      int fds[3] = {-1, -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)
 +      if (exec_pid)
                return;
 -      command_fds[0] = fds[1];
 -      command_fds[1] = fds[2];
 -      send_output();
 +      shutdown_curses();
 +      para_exec_cmdline_pid(&exec_pid, file_and_args, fds);
  }
  
 -/*
 - * shutdown curses and stat pipe before executing external commands
 - */
 -static void external_cmd(char *cmd)
 +static void handle_command(int c)
  {
 -      int fds[3] = {-1, -1, -1};
 +      int i;
  
 -      if (cmd_pid)
 -              return;
 -      shutdown_curses();
 -      if (para_exec_cmdline_pid(&cmd_pid, cmd, fds) < 0)
 +      /* first check user-defined key bindings */
 +      for (i = 0; i < conf.key_map_given; ++i) {
 +              char *tmp, *handler, *arg;
 +
 +              tmp = para_strdup(conf.key_map_arg[i]);
 +              if (!split_key_map(tmp, &handler, &arg)) {
 +                      free(tmp);
 +                      return;
 +              }
 +              if (strcmp(tmp, km_keyname(c))) {
 +                      free(tmp);
 +                      continue;
 +              }
 +              if (*handler == 'd')
 +                      exec_and_display(arg);
 +              else if (*handler == 'x')
 +                      exec_external(arg);
 +              else if (*handler == 'p')
 +                      exec_para(arg);
 +              else if (*handler == 'i') {
 +                      int num = find_cmd_byname(arg);
 +                      if (num >= 0)
 +                              command_list[num].handler();
 +              }
 +              free(tmp);
                return;
 -      do_select(EXTERNAL_MODE);
 -      init_curses();
 +      }
 +      /* not found, check internal key bindings */
 +      for (i = 0; command_list[i].handler; i++) {
 +              if (!strcmp(km_keyname(c), command_list[i].key)) {
 +                      command_list[i].handler();
 +                      return;
 +              }
 +      }
 +      print_in_bar(COLOR_ERRMSG, "key '%s' is not bound, press ? for help",
 +              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();
 +
 +      if (exs == EXEC_XCMD)
 +              return 0;
 +      if (window_update_needed()) {
 +              if (top.needs_update)
 +                      assert(wnoutrefresh(top.win) == OK);
 +              if (bot.needs_update)
 +                      assert(wnoutrefresh(bot.win) == OK);
 +              if (sep.needs_update)
 +                      assert(wnoutrefresh(sep.win) == OK);
 +              if (sb.needs_update)
 +                      assert(wnoutrefresh(sb.win) == OK);
 +              if (in.needs_update)
 +                      assert(wnoutrefresh(in.win) == OK);
 +              doupdate();
 +              top.needs_update = bot.needs_update = sb.needs_update =
 +                      in.needs_update = sep.needs_update = false;
 +      }
 +      ret = wgetch(top.win);
 +      if (ret == ERR)
 +              return 0;
 +      if (ret == KEY_RESIZE) {
 +              if (curses_active()) {
 +                      shutdown_curses();
 +                      init_curses();
 +                      redraw_bot_win();
 +              }
 +              return 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)
++static void signal_pre_select(struct sched *s, void *context)
 +{
-       struct signal_task *st = container_of(t, struct signal_task, task);
++      struct signal_task *st = context;
 +      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);
        int first_rbe = first_visible_rbe(&lines_total);
 +
        print_in_bar(COLOR_MSG, "scrolled view: %d-%d/%d\n", filled - first_rbe,
                filled - scroll_position, ringbuffer_filled(bot_win_rb));
  }
  
  static void com_scroll_top(void)
  {
 -      int i = RINGBUFFER_SIZE - 1;
 +      int i = RINGBUFFER_SIZE - 1, bot_lines = get_num_lines(&bot);
        unsigned lines = 0;
  
        while (i > 0 && !ringbuffer_get(bot_win_rb, i))
                i--;
        /* i is oldest entry */
 -      for (; lines < bot.lines && i >= 0; i--) {
 +      for (; lines < bot_lines && i >= 0; i--) {
                struct rb_entry *rbe = ringbuffer_get(bot_win_rb, i);
                if (!rbe)
                        break;
        print_in_bar(COLOR_ERRMSG, "top of buffer is shown\n");
  }
  
 -static void com_cancel_scrolling(void)
 +static void com_cancel_scroll(void)
  {
  
        if (scroll_position == 0) {
  static void com_page_down(void)
  {
        unsigned lines = 0;
 -      int i = scroll_position;
 -      while (lines < bot.lines && --i > 0) {
 +      int i = scroll_position, bot_lines = get_num_lines(&bot);
 +
 +      while (lines < bot_lines && --i > 0) {
                struct rb_entry *rbe = ringbuffer_get(bot_win_rb, i);
                if (!rbe)
                        break;
  static void com_page_up(void)
  {
        unsigned lines;
 -      int fvr = first_visible_rbe(&lines);
 +      int fvr = first_visible_rbe(&lines), bot_lines = get_num_lines(&bot);
  
        if (fvr < 0 || fvr + 1 >= ringbuffer_filled(bot_win_rb)) {
                print_in_bar(COLOR_ERRMSG, "top of buffer is shown\n");
        scroll_position = fvr + 1;
        for (; scroll_position > 0; scroll_position--) {
                first_visible_rbe(&lines);
 -              if (lines == bot.lines)
 +              if (lines == bot_lines)
                        break;
        }
        redraw_bot_win();
  static void com_scroll_down(void)
  {
        struct rb_entry *rbe;
 -      int rbe_lines;
 +      int rbe_lines, bot_lines = get_num_lines(&bot);
  
        if (!scroll_position) {
                print_in_bar(COLOR_ERRMSG, "bottom of buffer is shown\n");
        rbe = ringbuffer_get(bot_win_rb, scroll_position);
        rbe_lines = NUM_LINES(rbe->len);
        wscrl(bot.win, rbe_lines);
 -      wmove(bot.win, bot.lines - rbe_lines, 0);
 +      wmove(bot.win, bot_lines - rbe_lines, 0);
        wattron(bot.win, COLOR_PAIR(rbe->color));
        waddstr(bot.win, rbe->msg);
 -      wrefresh(bot.win);
 +      refresh_window(&bot);
        print_scroll_msg();
  }
  
@@@ -1265,6 -1305,7 +1266,6 @@@ static void com_scroll_up(void
                        break;
                rbe_lines = NUM_LINES(rbe->len);
                lines += rbe_lines;
 -//            fprintf(stderr, "msg: %s\n", rbe->msg);
                wattron(bot.win, COLOR_PAIR(rbe->color));
                waddstr(bot.win, "\n");
                waddstr(bot.win, rbe->msg);
                        break;
                i--;
        }
 -      wrefresh(bot.win);
 +      refresh_window(&bot);
        print_scroll_msg();
        return;
  err_out:
@@@ -1301,9 -1342,30 +1302,9 @@@ static void com_ll_incr(void
        print_in_bar(COLOR_MSG, "loglevel set to %d\n", loglevel);
  }
  
 -/*
 - * reread configuration, terminate on errors
 - */
  static void com_reread_conf(void)
  {
 -      char *cf =configfile_exists();
 -      struct gui_cmdline_parser_params params = {
 -              .override = 1,
 -              .initialize = 1,
 -              .check_required = 0,
 -              .check_ambiguity = 0,
 -              .print_errors = 0,
 -      };
 -
 -      if (!cf) {
 -              PARA_WARNING_LOG("there is no configuration to read");
 -              return;
 -      }
 -      PARA_INFO_LOG("rereading command line options and config file");
 -      gui_cmdline_parser_ext(_argc, _argv, &conf, &params);
 -      gui_cmdline_parser_config_file(cf, &conf, &params);
 -      PARA_NOTICE_LOG("config file reloaded");
 -      if (check_key_map_args() < 0)
 -              finish(EXIT_FAILURE);
 +      reread_conf();
  }
  
  static void com_help(void)
  
  static void com_shrink_top_win(void)
  {
 -      if (top.lines <= theme.top_lines_min) {
 -              PARA_WARNING_LOG("can not decrease top window");
 +      int top_lines = get_num_lines(&top);
 +
 +      if (top_lines <= theme.top_lines_min) {
 +              PARA_WARNING_LOG("can not decrease top window\n");
                return;
        }
 -      init_wins(top.lines - 1);
 -      wclear(top.win);
 -      print_all_items();
 +      init_wins(top_lines - 1);
        print_in_bar(COLOR_MSG, "%s", "decreased top window");
  }
  
  static void com_enlarge_top_win(void)
  {
 -      if (bot.lines < 3) {
 -              PARA_WARNING_LOG("can not increase top window");
 +      int top_lines = get_num_lines(&top), bot_lines = get_num_lines(&bot);
 +
 +      if (bot_lines < 3) {
 +              PARA_WARNING_LOG("can not increase top window\n");
                return;
        }
 -      init_wins(top.lines + 1);
 -      wclear(top.win);
 -      print_all_items();
 +      init_wins(top_lines + 1);
        print_in_bar(COLOR_MSG, "increased top window");
  }
  
@@@ -1374,7 -1436,7 +1375,7 @@@ static void com_version(void
  
  __noreturn static void com_quit(void)
  {
 -      finish(0);
 +      die(EXIT_SUCCESS, "%s", "");
  }
  
  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", 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);
 -}
 -
 -
 -static void handle_command(int c)
 -{
 -      int i;
 -
 -      /* first check user's key bindings */
 -      for (i = 0; i < conf.key_map_given; ++i) {
 -              char *tmp, *handler, *arg;
 -
 -              tmp = para_strdup(conf.key_map_arg[i]);
 -              if (!split_key_map(tmp, &handler, &arg)) {
 -                      free(tmp);
 -                      return;
 -              }
 -              if (strcmp(tmp, km_keyname(c))) {
 -                      free(tmp);
 -                      continue;
 -              }
 -              if (*handler == 'd')
 -                      display_cmd(arg);
 -              else if (*handler == 'x')
 -                      external_cmd(arg);
 -              else if (*handler == 'p')
 -                      para_cmd(arg);
 -              else if (*handler == 'i') {
 -                      int num = find_cmd_byname(arg);
 -                      if (num >= 0)
 -                              command_list[num].handler();
 -              }
 -              free(tmp);
 -              return;
 -      }
 -      /* not found, check internal key bindings */
 -      for (i = 0; command_list[i].handler; i++) {
 -              if (!strcmp(km_keyname(c), command_list[i].key)) {
 -                      command_list[i].handler();
 -                      return;
 -              }
 -      }
 -      print_in_bar(COLOR_ERRMSG, "key '%s' is not bound, press ? for help",
 -              km_keyname(c));
 +      theme_prev(&theme);
 +      com_refresh();
  }
  
  __noreturn static void print_help_and_die(void)
        exit(0);
  }
  
 -int main(int argc, char *argv[])
 +static int setup_tasks_and_schedule(void)
  {
 -      int ret;
 -      char *cf;
++      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 = {.task = NULL};
 +      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,
-               },
-       };
 -      _argc = argc;
 -      _argv = argv;
++      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.fd = para_signal_init();
 +      para_install_sighandler(SIGINT);
 +      para_install_sighandler(SIGTERM);
 +      para_install_sighandler(SIGCHLD);
 +      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);
++      signal_task.task = task_register(&(struct task_info) {
++              .name = "signal",
++              .pre_select = signal_pre_select,
++              .post_select = signal_post_select,
++              .context = &signal_task,
++      }, &sched);
 +      return schedule(&sched);
 +}
  
 +/**
 + * The main function of para_gui.
 + *
 + * \param argc Usual argument count.
 + * \param argv Usual argument vector.
 + *
 + * After initialization para_gui registers the following tasks to the paraslash
 + * scheduler: status, exec, signal, input.
 + *
 + * The status task executes the para_audioc stat command to obtain the status
 + * of para_server and para_audiod, and displays this information in the top
 + * window of para_gui.
 + *
 + * The exec task is responsible for printing the output of the currently
 + * running executable to the bottom window.
 + *
 + * The signal task performs suitable actions according to any signals received.
 + * For example it refreshes all windows on terminal size changes and resets the
 + * terminal on \p SIGTERM.
 + *
 + * The input task reads single key strokes from stdin. For each key pressed, it
 + * executes the command handler associated with this key.
 + *
 + * \return \p EXIT_SUCCESS or \p EXIT_FAILURE.
 + */
 +int main(int argc, char *argv[])
 +{
        gui_cmdline_parser(argc, argv, &conf); /* exits on errors */
        loglevel = get_loglevel_by_name(conf.loglevel_arg);
        version_handle_flag("gui", conf.version_given);
        if (conf.help_given || conf.detailed_help_given)
                print_help_and_die();
 -      cf = configfile_exists();
 -      if (!cf && conf.config_file_given) {
 -              fprintf(stderr, "can not read config file %s\n",
 -                      conf.config_file_arg);
 -              exit(EXIT_FAILURE);
 -      }
 -      if (cf) {
 -              struct gui_cmdline_parser_params params = {
 -                      .override = 0,
 -                      .initialize = 0,
 -                      .check_required = 0,
 -                      .check_ambiguity = 0,
 -                      .print_errors = 1,
 -              };
 -              gui_cmdline_parser_config_file(cf, &conf, &params);
 -              loglevel = get_loglevel_by_name(conf.loglevel_arg);
 -      }
 -      if (check_key_map_args() < 0) {
 -              fprintf(stderr, "invalid key map\n");
 -              exit(EXIT_FAILURE);
 -      }
 -      init_theme_or_die(conf.theme_arg, &theme);
 -      top.lines = theme.top_lines_default;
 -      setup_signal_handling();
 +      parse_config_file_or_die(false /* override */);
        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);
 -              if (!ret)
 -                      continue;
 -              print_in_bar(COLOR_MSG, " ");
 -              handle_command(ret);
 -      }
 +      return setup_tasks_and_schedule() < 0? EXIT_FAILURE : EXIT_SUCCESS;
  }
diff --combined server.c
index fa19ce4225111e33ec7f192935f455c6cd5b2d76,bcbf240423bd9c207df9bd33c5f53f3e6b53d275..908e039b1cf66be5909b102edfb20aa83ceb2b2a
+++ b/server.c
@@@ -110,7 -110,7 +110,7 @@@ struct server_command_task 
        /** Argument vector passed to para_server's main function. */
        char **argv;
        /** The command task structure for scheduling. */
-       struct task task;
+       struct task *task;
  };
  
  static int want_colors(void)
@@@ -235,9 -235,9 +235,9 @@@ out
        exit(EXIT_FAILURE);
  }
  
- static void signal_pre_select(struct sched *s, struct task *t)
+ static void signal_pre_select(struct sched *s, void *context)
  {
-       struct signal_task *st = container_of(t, struct signal_task, task);
+       struct signal_task *st = context;
        para_fd_set(st->fd, &s->rfds, &s->max_fileno);
  }
  
@@@ -253,7 -253,7 +253,7 @@@ static void handle_sighup(void
                kill(mmd->afs_pid, SIGHUP);
  }
  
- 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 signum = para_next_signal(&s->rfds);
  
@@@ -312,10 -312,6 +312,6 @@@ static void init_signal_task(void
        static struct signal_task signal_task_struct,
                *st = &signal_task_struct;
  
-       st->task.pre_select = signal_pre_select;
-       st->task.post_select = signal_post_select;
-       sprintf(st->task.status, "signal task");
        PARA_NOTICE_LOG("setting up signal handling\n");
        st->fd = para_signal_init(); /* always successful */
        para_install_sighandler(SIGINT);
        para_install_sighandler(SIGCHLD);
        para_sigaction(SIGPIPE, SIG_IGN);
        add_close_on_fork_list(st->fd);
-       register_task(&sched, &st->task);
+       st->task = task_register(&(struct task_info) {
+               .name = "signal",
+               .pre_select = signal_pre_select,
+               .post_select = signal_post_select,
+               .context = st,
+       }, &sched);
  }
  
- static void command_pre_select(struct sched *s, struct task *t)
+ static void command_pre_select(struct sched *s, void *context)
  {
-       struct server_command_task *sct = container_of(t, struct server_command_task, task);
+       struct server_command_task *sct = context;
        para_fd_set(sct->listen_fd, &s->rfds, &s->max_fileno);
  }
  
- static int command_post_select(struct sched *s, struct task *t)
+ static int command_post_select(struct sched *s, void *context)
  {
-       struct server_command_task *sct = container_of(t, struct server_command_task, task);
+       struct server_command_task *sct = context;
  
        int new_fd, ret, i;
        char *peer_name;
@@@ -399,8 -401,6 +401,6 @@@ static void init_server_command_task(in
                *sct = &server_command_task_struct;
  
        PARA_NOTICE_LOG("initializing tcp command socket\n");
-       sct->task.pre_select = command_pre_select;
-       sct->task.post_select = command_post_select;
        sct->argc = argc;
        sct->argv = argv;
        ret = para_listen_simple(IPPROTO_TCP, conf.port_arg);
        if (ret < 0)
                goto err;
        add_close_on_fork_list(sct->listen_fd); /* child doesn't need the listener */
-       sprintf(sct->task.status, "server command task");
-       register_task(&sched, &sct->task);
+       sct->task = task_register(&(struct task_info) {
+               .name = "server command",
+               .pre_select = command_pre_select,
+               .post_select = command_post_select,
+               .context = sct,
+       }, &sched);
        return;
  err:
        PARA_EMERG_LOG("%s\n", para_strerror(-ret));
@@@ -484,9 -488,9 +488,7 @@@ static void server_init(int argc, char 
        parse_config_or_die(0);
        log_welcome("para_server");
        init_ipc_or_die(); /* init mmd struct and mmd->lock */
--      /* make sure, the global now pointer is uptodate */
--      clock_get_realtime(now);
--      set_server_start_time(now);
++      set_server_start_time(NULL);
        init_user_list(user_list_file);
        /* become daemon */
        if (conf.daemon_given)
@@@ -573,6 -577,7 +575,7 @@@ int main(int argc, char *argv[]
        server_init(argc, argv);
        mutex_lock(mmd_mutex);
        ret = schedule(&sched);
+       sched_shutdown(&sched);
        if (ret < 0) {
                PARA_EMERG_LOG("%s\n", para_strerror(-ret));
                exit(EXIT_FAILURE);
diff --combined wmadec_filter.c
index fdca2814d76a44db720752e2c99d10d9df7bbe5c,562bc2513c94c445805d0639d647e73a39b05506..188b0a416cf9ad587b5de922a2b7912c1101ece0
@@@ -62,11 -62,17 +62,11 @@@ struct private_wmadec_data 
        /** Information contained in the audio file header. */
        struct asf_header_info ahi;
        struct getbit_context gb;
 -      /** Whether to use the bit reservoir. */
 -      int use_bit_reservoir;
 -      /** Whether to use variable block length. */
 -      int use_variable_block_len;
 -      /** Whether to use exponent coding. */
 -      int use_exp_vlc;
        /** Whether perceptual noise is added. */
        int use_noise_coding;
        /** Depends on number of the bits per second and the frame length. */
        int byte_offset_bits;
 -      /** Only used if use_exp_vlc is true. */
 +      /** Only used if ahi->use_exp_vlc is true. */
        struct vlc exp_vlc;
        uint16_t exponent_bands[BLOCK_NB_SIZES][25];
        /** The index of the first coef in high band. */
@@@ -90,7 -96,7 +90,7 @@@
        int frame_len;
        /** log2 of frame_len. */
        int frame_len_bits;
 -      /** Number of block sizes. */
 +      /** Number of block sizes, one if !ahi->use_variable_block_len. */
        int nb_block_sizes;
        /* block info */
        int reset_block_lengths;
@@@ -174,7 -180,7 +174,7 @@@ static void wmadec_cleanup(struct priva
  
        for (i = 0; i < pwd->nb_block_sizes; i++)
                imdct_end(pwd->mdct_ctx[i]);
 -      if (pwd->use_exp_vlc)
 +      if (pwd->ahi.use_exp_vlc)
                free_vlc(&pwd->exp_vlc);
        if (pwd->use_noise_coding)
                free_vlc(&pwd->hgain_vlc);
@@@ -308,7 -314,7 +308,7 @@@ static int wma_init(struct private_wmad
        else
                pwd->frame_len_bits = 11;
        pwd->frame_len = 1 << pwd->frame_len_bits;
 -      if (pwd->use_variable_block_len) {
 +      if (pwd->ahi.use_variable_block_len) {
                int nb_max, nb;
                nb = ((flags2 >> 3) & 3) + 1;
                if ((ahi->bit_rate / ahi->channels) >= 32000)
                pwd->frame_len, bps, bps1,
                high_freq, pwd->byte_offset_bits);
        PARA_INFO_LOG("use_noise_coding=%d use_exp_vlc=%d nb_block_sizes=%d\n",
 -              pwd->use_noise_coding, pwd->use_exp_vlc, pwd->nb_block_sizes);
 +              pwd->use_noise_coding, pwd->ahi.use_exp_vlc, pwd->nb_block_sizes);
  
        compute_scale_factor_band_sizes(pwd, high_freq);
        /* init MDCT windows : simple sinus window */
  
        if (pwd->use_noise_coding) {
                /* init the noise generator */
 -              if (pwd->use_exp_vlc)
 +              if (pwd->ahi.use_exp_vlc)
                        pwd->noise_mult = 0.02;
                else
                        pwd->noise_mult = 0.04;
@@@ -478,6 -484,10 +478,6 @@@ static int wma_decode_init(char *initia
                return ret;
        }
  
 -      pwd->use_exp_vlc = pwd->ahi.flags2 & 0x0001;
 -      pwd->use_bit_reservoir = pwd->ahi.flags2 & 0x0002;
 -      pwd->use_variable_block_len = pwd->ahi.flags2 & 0x0004;
 -
        ret = wma_init(pwd);
        if (ret < 0)
                return ret;
                        wma_hgain_huffcodes, 2);
        }
  
 -      if (pwd->use_exp_vlc) {
 +      if (pwd->ahi.use_exp_vlc) {
                PARA_INFO_LOG("using exp_vlc\n");
                init_vlc(&pwd->exp_vlc, EXPVLCBITS, sizeof(wma_scale_huffbits),
                        wma_scale_huffbits, wma_scale_huffcodes, 4);
@@@ -840,7 -850,7 +840,7 @@@ static int wma_decode_block(struct priv
        int nb_coefs[MAX_CHANNELS];
  
        /* compute current block length */
 -      if (pwd->use_variable_block_len) {
 +      if (pwd->ahi.use_variable_block_len) {
                n = wma_log2(pwd->nb_block_sizes - 1) + 1;
  
                if (pwd->reset_block_lengths) {
        if ((pwd->block_len_bits == pwd->frame_len_bits) || get_bit(&pwd->gb)) {
                for (ch = 0; ch < pwd->ahi.channels; ch++) {
                        if (pwd->channel_coded[ch]) {
 -                              if (pwd->use_exp_vlc) {
 +                              if (pwd->ahi.use_exp_vlc) {
                                        ret = decode_exp_vlc(pwd, ch);
                                        if (ret < 0)
                                                return ret;
@@@ -1086,7 -1096,7 +1086,7 @@@ static int wma_decode_superframe(struc
        buf_size = pwd->ahi.block_align;
        samples = data;
        init_get_bits(&pwd->gb, buf, buf_size);
 -      if (pwd->use_bit_reservoir) {
 +      if (pwd->ahi.use_bit_reservoir) {
                int i, nb_frames, bit_offset, pos, len;
                uint8_t *q;
  
@@@ -1203,9 -1213,9 +1203,9 @@@ static int wmadec_execute(struct btr_no
  
  #define WMA_OUTPUT_BUFFER_SIZE (128 * 1024)
  
- static int wmadec_post_select(__a_unused struct sched *s, struct task *t)
+ static int wmadec_post_select(__a_unused struct sched *s, void *context)
  {
-       struct filter_node *fn = container_of(t, struct filter_node, task);
+       struct filter_node *fn = context;
        int ret, converted, out_size;
        struct private_wmadec_data *pwd = fn->private_data;
        struct btr_node *btrn = fn->btrn;