X-Git-Url: http://git.tuebingen.mpg.de/?a=blobdiff_plain;f=gui.c;h=9b7c2f992520896f2ae33918854604b37bbc7720;hb=ccd288cee4d15e54640d87c1451b46c3ff0e2591;hp=c9ee17f3f7b88ad7cc13a9c12142234978bf841f;hpb=4f5209c5ddb334bf35d293c6ccf6f056267f4407;p=paraslash.git diff --git a/gui.c b/gui.c index c9ee17f3..9b7c2f99 100644 --- a/gui.c +++ b/gui.c @@ -32,9 +32,12 @@ static char *stat_content[NUM_STAT_ITEMS]; static struct gui_window { WINDOW *win; + 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; @@ -44,16 +47,17 @@ static struct ringbuffer *bot_win_rb; static unsigned scroll_position; -static pid_t cmd_pid; +static pid_t exec_pid; -static int command_fds[2] = {-1, -1}; +static int exec_fds[2] = {-1, -1}; static struct gui_args_info conf; static int loglevel; -enum cmd_status { - CMDS_IDLE, /* no command running */ - CMDS_DCMD, /* para or display command running */ - CMDS_XCMD, /* external command running */ +/** 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. */ }; /** @@ -131,8 +135,14 @@ struct status_task { int fd; }; -struct cmd_task { +/** Stdout/stderr of the executing process is read in chunks of this size. */ +#define COMMAND_BUF_SIZE 32768 + +struct exec_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) @@ -291,6 +301,17 @@ static int align_str(WINDOW* win, char *str, unsigned int len, 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; @@ -305,7 +326,7 @@ __printf_2_3 static void print_in_bar(int color, const char *fmt,...) wmove(in.win, 0, 0); align_str(in.win, msg, get_num_cols(&in), LEFT); free(msg); - wrefresh(in.win); + refresh_window(&in); } static void print_status_bar(void) @@ -401,7 +422,7 @@ static void redraw_bot_win(void) waddstr(bot.win, rbe->msg); } out: - wrefresh(bot.win); + refresh_window(&bot); } static void rb_add_entry(int color, char *msg) @@ -448,7 +469,7 @@ __printf_2_3 static void outputf(int color, const char* 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) @@ -475,11 +496,12 @@ static __printf_2_3 void curses_log(int ll, const char *fmt,...) if (bytes > 0 && msg[bytes - 1] == '\n') msg[bytes - 1] = '\0'; /* cut trailing newline */ rb_add_entry(color, msg); - wrefresh(bot.win); - } else if (cmd_pid <= 0) /* no external command running */ + refresh_window(&bot); + } else if (exec_pid <= 0) /* no external command running */ 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) @@ -517,11 +539,10 @@ static void print_stat_item(int i) 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); 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) @@ -769,8 +790,8 @@ reap_next_child: ret = para_reap_child(&pid); if (ret <= 0) return; - if (pid == cmd_pid) { - cmd_pid = 0; + if (pid == exec_pid) { + exec_pid = 0; init_curses(); print_in_bar(COLOR_MSG, " "); } @@ -913,69 +934,65 @@ static int signal_post_select(struct sched *s, __a_unused struct task *t) return 1; } -#define COMMAND_BUF_SIZE 32768 - -static enum cmd_status cmd_status(void) +static enum exec_status exec_status(void) { - if (command_fds[0] >= 0 || command_fds[1] >= 0) - return CMDS_DCMD; - if (cmd_pid > 0) - return CMDS_XCMD; - return CMDS_IDLE; + if (exec_fds[0] >= 0 || exec_fds[1] >= 0) + return EXEC_DCMD; + if (exec_pid > 0) + return EXEC_XCMD; + return EXEC_IDLE; } -static void command_pre_select(struct sched *s, __a_unused struct task *t) +static void exec_pre_select(struct sched *s, __a_unused struct task *t) { - enum cmd_status cmds = cmd_status(); + enum exec_status es = exec_status(); - if (cmds != CMDS_DCMD) + if (es != EXEC_DCMD) return; - if (command_fds[0] >= 0) - para_fd_set(command_fds[0], &s->rfds, &s->max_fileno); - if (command_fds[1] >= 0) - para_fd_set(command_fds[1], &s->rfds, &s->max_fileno); + 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); } -static int command_post_select(struct sched *s, __a_unused struct task *t) +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; - static char command_buf[2][COMMAND_BUF_SIZE]; - static int cbo[2]; /* command buf offsets */ - static unsigned flags[2]; /* for for_each_line() */ - enum cmd_status cmds = cmd_status(); + enum exec_status es = exec_status(); - if (cmds != CMDS_DCMD) + if (es != EXEC_DCMD) return 0; for (i = 0; i < 2; i++) { size_t sz; - if (command_fds[i] < 0) + if (exec_fds[i] < 0) continue; - ret = read_nonblock(command_fds[i], - command_buf[i] + cbo[i], - COMMAND_BUF_SIZE - 1 - cbo[i], &s->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; + 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 || cmd_pid == 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; - cbo[i] = 0; - if (command_fds[!i] < 0) /* both fds closed */ + 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; } - if (cbo[i] == COMMAND_BUF_SIZE - 1) { + if (ct->cbo[i] == COMMAND_BUF_SIZE - 1) { PARA_NOTICE_LOG("discarding overlong line"); - cbo[i] = 0; - flags[i] = FELF_DISCARD_FIRST; + ct->cbo[i] = 0; + ct->flags[i] = FELF_DISCARD_FIRST; } } return 0; @@ -983,19 +1000,19 @@ static int command_post_select(struct sched *s, __a_unused struct task *t) static void input_pre_select(struct sched *s, __a_unused struct task *t) { - enum cmd_status cmds = cmd_status(); - - if (cmds != CMDS_XCMD) + 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_cmd(const char *cmd) +static void exec_and_display(const char *file_and_args) { int ret, fds[3] = {0, 1, 1}; - outputf(COLOR_COMMAND, "%s", cmd); - ret = para_exec_cmdline_pid(&cmd_pid, cmd, fds); + 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]); @@ -1004,42 +1021,36 @@ static void exec_and_display_cmd(const char *cmd) ret = mark_fd_nonblocking(fds[2]); if (ret < 0) goto fail; - command_fds[0] = fds[1]; - command_fds[1] = fds[2]; + 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) { - char *c; + char *file_and_args; - print_in_bar(COLOR_MSG, "executing client command, hit any key to abort\n"); - c = make_message(BINDIR "/para_client -- %s", cmd); - exec_and_display_cmd(c); - free(c); -} - -static void display_cmd(char *cmd) -{ - print_in_bar(COLOR_MSG, "executing display command, hit any key to abort"); - exec_and_display_cmd(cmd); + file_and_args = make_message(BINDIR "/para_client -- %s", args); + exec_and_display(file_and_args); + free(file_and_args); } /* * shutdown curses and stat pipe before executing external commands */ -static void external_cmd(char *cmd) +static void exec_external(char *file_and_args) { int fds[3] = {-1, -1, -1}; - if (cmd_pid) + if (exec_pid) return; shutdown_curses(); - para_exec_cmdline_pid(&cmd_pid, cmd, fds); + para_exec_cmdline_pid(&exec_pid, file_and_args, fds); } static void handle_command(int c) @@ -1060,11 +1071,11 @@ static void handle_command(int c) continue; } if (*handler == 'd') - display_cmd(arg); + exec_and_display(arg); else if (*handler == 'x') - external_cmd(arg); + exec_external(arg); else if (*handler == 'p') - para_cmd(arg); + exec_para(arg); else if (*handler == 'i') { int num = find_cmd_byname(arg); if (num >= 0) @@ -1087,19 +1098,34 @@ static void handle_command(int c) static int input_post_select(__a_unused struct sched *s, __a_unused struct task *t) { int ret; - enum cmd_status cmds = cmd_status(); + enum exec_status exs = exec_status(); - if (cmds == CMDS_XCMD) + 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 || ret == KEY_RESIZE) return 0; - if (cmds == CMDS_IDLE) { + if (exs == EXEC_IDLE) { handle_command(ret); return 0; } - if (cmd_pid != 0) - kill(cmd_pid, SIGTERM); + if (exec_pid != 0) + kill(exec_pid, SIGTERM); return 0; } @@ -1208,7 +1234,7 @@ static void com_scroll_down(void) 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(); } @@ -1245,7 +1271,7 @@ static void com_scroll_up(void) break; i--; } - wrefresh(bot.win); + refresh_window(&bot); print_scroll_msg(); return; err_out: @@ -1385,11 +1411,11 @@ static int setup_tasks_and_schedule(void) .tv_usec = (conf.timeout_arg % 1000) * 1000, }, }; - struct cmd_task cmd_task = { + struct exec_task exec_task = { .task = { - .status = "cmd", - .pre_select = command_pre_select, - .post_select = command_post_select, + .status = "exec", + .pre_select = exec_pre_select, + .post_select = exec_post_select, }, }; struct status_task status_task = { @@ -1421,13 +1447,38 @@ static int setup_tasks_and_schedule(void) para_install_sighandler(SIGWINCH); para_install_sighandler(SIGUSR1); - register_task(&sched, &cmd_task.task); + 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); } +/** + * 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 */