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;
static struct gui_args_info conf;
static int loglevel;
+/** Type of the process currently being executed. */
enum exec_status {
- EXEC_IDLE, /* no command running */
- EXEC_DCMD, /* para or display command running */
- EXEC_XCMD, /* external command running */
+ EXEC_IDLE, /**< No process running. */
+ EXEC_DCMD, /**< para or display process running. */
+ EXEC_XCMD, /**< External process running. */
};
/**
struct status_task {
struct task task;
+ pid_t pid;
char *buf;
int bufsize, loaded;
struct timeval next_exec;
int fd;
};
+/** Stdout/stderr of the executing process is read in chunks of this size. */
#define COMMAND_BUF_SIZE 32768
struct exec_task {
vfprintf(stderr, fmt, ap);
va_end(ap);
}
+/** The log function of para_gui, always set to curses_log(). */
__printf_2_3 void (*para_log)(int, const char*, ...) = curses_log;
static void shutdown_curses(void)
struct status_task *st = container_of(t, struct status_task, task);
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(t) < 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)
{
struct status_task *st = container_of(t, struct status_task, task);
size_t sz;
- pid_t pid;
int ret, ret2;
+ ret = task_get_notification(t);
+ 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]);
// 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.
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
reread_conf();
return 1;
case SIGCHLD:
- check_sigchld();
+ task_notify_all(s, E_GUI_SIGCHLD);
return 1;
}
return 1;
return EXEC_IDLE;
}
-static void exec_pre_select(struct sched *s, __a_unused struct task *t)
+static void exec_pre_select(struct sched *s, struct task *t)
{
- enum exec_status es = exec_status();
-
- if (es != EXEC_DCMD)
- return;
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)
+ sched_min_delay(s);
}
static int exec_post_select(struct sched *s, struct task *t)
{
struct exec_task *ct = container_of(t, struct exec_task, task);
int i, ret;
- enum exec_status es = exec_status();
- if (es != EXEC_DCMD)
- return 0;
+ ret = task_get_notification(t);
+ 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)
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;
}
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);
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 */