diff --git a/client.c b/client.c index 841e581e..d1a31de7 100644 --- a/client.c +++ b/client.c @@ -402,6 +402,8 @@ client_main(struct event_base *base, int argc, char **argv, int flags, int feat) } else if (client_exitreason != CLIENT_EXIT_NONE) fprintf(stderr, "%s\n", client_exit_message()); setblocking(STDIN_FILENO, 1); + setblocking(STDOUT_FILENO, 1); + setblocking(STDERR_FILENO, 1); return (client_exitval); } @@ -429,6 +431,9 @@ client_send_identify(const char *ttynam, const char *cwd, int feat) if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); + if ((fd = dup(STDOUT_FILENO)) == -1) + fatal("dup failed"); + proc_send(client_peer, MSG_IDENTIFY_STDOUT, fd, NULL, 0); pid = getpid(); proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); diff --git a/control.c b/control.c index 750f1284..7e1a869b 100644 --- a/control.c +++ b/control.c @@ -23,10 +23,11 @@ #include #include #include +#include #include "tmux.h" -/* Control offsets. */ +/* Control client offset. */ struct control_offset { u_int pane; @@ -34,13 +35,16 @@ struct control_offset { int flags; #define CONTROL_OFFSET_OFF 0x1 - RB_ENTRY(control_offset) entry; + RB_ENTRY(control_offset) entry; }; RB_HEAD(control_offsets, control_offset); -/* Control state. */ +/* Control client state. */ struct control_state { - struct control_offsets offsets; + struct control_offsets offsets; + + struct bufferevent *read_event; + struct bufferevent *write_event; }; /* Compare client offsets. */ @@ -146,18 +150,24 @@ control_set_pane_off(struct client *c, struct window_pane *wp) void control_write(struct client *c, const char *fmt, ...) { - va_list ap; + struct control_state *cs = c->control_state; + va_list ap; + char *s; va_start(ap, fmt); - file_vprint(c, fmt, ap); - file_print(c, "\n"); + xvasprintf(&s, fmt, ap); va_end(ap); + + bufferevent_write(cs->write_event, s, strlen(s)); + bufferevent_write(cs->write_event, "\n", 1); + free(s); } /* Write output from a pane. */ void control_write_output(struct client *c, struct window_pane *wp) { + struct control_state *cs = c->control_state; struct control_offset *co; struct evbuffer *message; u_char *new_data; @@ -165,11 +175,6 @@ control_write_output(struct client *c, struct window_pane *wp) if (c->flags & CLIENT_CONTROL_NOOUTPUT) return; - - /* - * Only write input if the window pane is linked to a window belonging - * to the client's session. - */ if (winlink_find_by_window(&c->session->windows, wp->window) == NULL) return; @@ -193,15 +198,15 @@ control_write_output(struct client *c, struct window_pane *wp) else evbuffer_add_printf(message, "%c", new_data[i]); } - evbuffer_add(message, "", 1); + evbuffer_add(message, "\n", 1); - control_write(c, "%s", EVBUFFER_DATA(message)); + bufferevent_write_buffer(cs->write_event, message); evbuffer_free(message); window_pane_update_used_data(wp, &co->offset, new_size, 1); } -/* Control error callback. */ +/* Control client error callback. */ static enum cmd_retval control_error(struct cmdq_item *item, void *data) { @@ -216,18 +221,27 @@ control_error(struct cmdq_item *item, void *data) return (CMD_RETURN_NORMAL); } -/* Control input callback. Read lines and fire commands. */ +/* Control client error callback. */ static void -control_callback(__unused struct client *c, __unused const char *path, - int read_error, int closed, struct evbuffer *buffer, __unused void *data) +control_error_callback(__unused struct bufferevent *bufev, + __unused short what, void *data) { + struct client *c = data; + + c->flags |= CLIENT_EXIT; +} + +/* Control client input callback. Read lines and fire commands. */ +static void +control_read_callback(__unused struct bufferevent *bufev, void *data) +{ + struct client *c = data; + struct control_state *cs = c->control_state; + struct evbuffer *buffer = cs->read_event->input; char *line, *error; struct cmdq_state *state; enum cmd_parse_status status; - if (closed || read_error != 0) - c->flags |= CLIENT_EXIT; - for (;;) { line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF); if (line == NULL) @@ -255,13 +269,30 @@ control_start(struct client *c) { struct control_state *cs; + if (c->flags & CLIENT_CONTROLCONTROL) { + close(c->out_fd); + c->out_fd = -1; + } else + setblocking(c->out_fd, 0); + setblocking(c->fd, 0); + cs = c->control_state = xcalloc(1, sizeof *cs); RB_INIT(&cs->offsets); - file_read(c, "-", control_callback, c); + cs->read_event = bufferevent_new(c->fd, control_read_callback, NULL, + control_error_callback, c); + bufferevent_enable(cs->read_event, EV_READ); if (c->flags & CLIENT_CONTROLCONTROL) - file_print(c, "\033P1000p"); + cs->write_event = cs->read_event; + else { + cs->write_event = bufferevent_new(c->out_fd, NULL, NULL, + control_error_callback, c); + } + bufferevent_enable(cs->write_event, EV_WRITE); + + if (c->flags & CLIENT_CONTROLCONTROL) + control_write(c, "\033P1000p"); } /* Stop control mode. */ @@ -270,6 +301,10 @@ control_stop(struct client *c) { struct control_state *cs = c->control_state; + if (~c->flags & CLIENT_CONTROLCONTROL) + bufferevent_free(cs->write_event); + bufferevent_free(cs->read_event); + control_free_offsets(c); free(cs); } diff --git a/file.c b/file.c index 69dbdee4..0d25db03 100644 --- a/file.c +++ b/file.c @@ -242,7 +242,9 @@ file_write(struct client *c, const char *path, int flags, const void *bdata, cf->path = xstrdup("-"); fd = STDOUT_FILENO; - if (c == NULL || c->flags & CLIENT_ATTACHED) { + if (c == NULL || + (c->flags & CLIENT_ATTACHED) || + (c->flags & CLIENT_CONTROL)) { cf->error = EBADF; goto done; } @@ -311,7 +313,9 @@ file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata) cf->path = xstrdup("-"); fd = STDIN_FILENO; - if (c == NULL || c->flags & CLIENT_ATTACHED) { + if (c == NULL || + (c->flags & CLIENT_ATTACHED) || + (c->flags & CLIENT_CONTROL)) { cf->error = EBADF; goto done; } diff --git a/server-client.c b/server-client.c index 5c8f4940..d4fa92dc 100644 --- a/server-client.c +++ b/server-client.c @@ -223,7 +223,7 @@ server_client_create(int fd) c->environ = environ_create(); c->fd = -1; - c->cwd = NULL; + c->out_fd = -1; c->queue = cmdq_new(); RB_INIT(&c->windows); @@ -338,6 +338,8 @@ server_client_lost(struct client *c) proc_remove_peer(c->peer); c->peer = NULL; + if (c->out_fd != -1) + close(c->out_fd); if (c->fd != -1) { close(c->fd); c->fd = -1; @@ -1573,10 +1575,9 @@ server_client_check_pane_buffer(struct window_pane *wp) out: /* * If there is data remaining, and there are no clients able to consume - * it, do not read any more. This is true when 1) there are attached - * clients 2) all the clients are control clients 3) all of them have - * either the OFF flag set, or are otherwise not able to accept any - * more data for this pane. + * it, do not read any more. This is true when there are attached + * clients, all of which are control clients which are not able to + * accept any more data. */ if (off) bufferevent_disable(wp->event, EV_READ); @@ -1969,6 +1970,7 @@ server_client_dispatch(struct imsg *imsg, void *arg) case MSG_IDENTIFY_TTYNAME: case MSG_IDENTIFY_CWD: case MSG_IDENTIFY_STDIN: + case MSG_IDENTIFY_STDOUT: case MSG_IDENTIFY_ENVIRON: case MSG_IDENTIFY_CLIENTPID: case MSG_IDENTIFY_DONE: @@ -2179,6 +2181,12 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) c->fd = imsg->fd; log_debug("client %p IDENTIFY_STDIN %d", c, imsg->fd); break; + case MSG_IDENTIFY_STDOUT: + if (datalen != 0) + fatalx("bad MSG_IDENTIFY_STDOUT size"); + c->out_fd = imsg->fd; + log_debug("client %p IDENTIFY_STDOUT %d", c, imsg->fd); + break; case MSG_IDENTIFY_ENVIRON: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_ENVIRON string"); @@ -2207,11 +2215,9 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) c->name = name; log_debug("client %p name is %s", c, c->name); - if (c->flags & CLIENT_CONTROL) { - close(c->fd); - c->fd = -1; + if (c->flags & CLIENT_CONTROL) control_start(c); - } else if (c->fd != -1) { + else if (c->fd != -1) { if (tty_init(&c->tty, c) != 0) { close(c->fd); c->fd = -1; @@ -2219,6 +2225,8 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) tty_resize(&c->tty); c->flags |= CLIENT_TERMINAL; } + close(c->out_fd); + c->out_fd = -1; } /* @@ -2335,7 +2343,8 @@ void server_client_set_flags(struct client *c, const char *flags) { char *s, *copy, *next; - int flag, not; + uint64_t flag; + int not; s = copy = xstrdup (flags); while ((next = strsep(&s, ",")) != NULL) { diff --git a/tmux.h b/tmux.h index a0369e34..4187ddd8 100644 --- a/tmux.h +++ b/tmux.h @@ -498,6 +498,7 @@ enum msgtype { MSG_IDENTIFY_CLIENTPID, MSG_IDENTIFY_CWD, MSG_IDENTIFY_FEATURES, + MSG_IDENTIFY_STDOUT, MSG_COMMAND = 200, MSG_DETACH, @@ -967,6 +968,7 @@ struct window_pane { int fd; struct bufferevent *event; + struct window_pane_offset offset; size_t base_offset; @@ -1581,6 +1583,7 @@ struct client { pid_t pid; int fd; + int out_fd; struct event event; int retval;