diff --git a/client.c b/client.c
index 10b77ad3..ed182dbb 100644
--- a/client.c
+++ b/client.c
@@ -35,6 +35,7 @@
 
 struct imsgbuf	client_ibuf;
 struct event	client_event;
+struct event	client_stdin;
 enum {
 	CLIENT_EXIT_NONE,
 	CLIENT_EXIT_DETACHED,
@@ -56,6 +57,7 @@ void		client_send_environ(void);
 void		client_write_server(enum msgtype, void *, size_t);
 void		client_update_event(void);
 void		client_signal(int, short, void *);
+void		client_stdin_callback(int, short, void *);
 void		client_callback(int, short, void *);
 int		client_dispatch_attached(void);
 int		client_dispatch_wait(void *);
@@ -229,6 +231,11 @@ client_main(int argc, char **argv, int flags)
 	imsg_init(&client_ibuf, fd);
 	event_set(&client_event, fd, EV_READ, client_callback, shell_cmd);
 
+	/* Create stdin handler. */
+	setblocking(STDIN_FILENO, 0);
+	event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST,
+	    client_stdin_callback, NULL);
+
 	/* Establish signal handlers. */
 	set_signals(client_signal);
 
@@ -257,6 +264,7 @@ client_main(int argc, char **argv, int flags)
 
 	/* Set the event and dispatch. */
 	client_update_event();
+	event_add (&client_stdin, NULL);
 	event_dispatch();
 
 	/* Print the exit message, if any, and exit. */
@@ -268,6 +276,7 @@ client_main(int argc, char **argv, int flags)
 		if (client_exittype == MSG_DETACHKILL && ppid > 1)
 			kill(ppid, SIGHUP);
 	}
+	setblocking(STDIN_FILENO, 1);
 	return (client_exitval);
 }
 
@@ -289,20 +298,11 @@ client_send_identify(int flags)
 	    strlcpy(data.term, term, sizeof data.term) >= sizeof data.term)
 		*data.term = '\0';
 
-	if ((fd = dup(STDOUT_FILENO)) == -1)
-		fatal("dup failed");
-	imsg_compose(&client_ibuf,
-	    MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0);
-
-	if ((fd = dup(STDERR_FILENO)) == -1)
-		fatal("dup failed");
-	imsg_compose(&client_ibuf,
-	    MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0);
-
 	if ((fd = dup(STDIN_FILENO)) == -1)
 		fatal("dup failed");
 	imsg_compose(&client_ibuf,
 	    MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data);
+	client_update_event();
 }
 
 /* Forward entire environment to server. */
@@ -324,6 +324,7 @@ void
 client_write_server(enum msgtype type, void *buf, size_t len)
 {
 	imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);
+	client_update_event();
 }
 
 /* Update client event based on whether it needs to read or read and write. */
@@ -423,6 +424,23 @@ lost_server:
 	event_loopexit(NULL);
 }
 
+/* Callback for client stdin read events. */
+/* ARGSUSED */
+void
+client_stdin_callback(unused int fd, unused short events, unused void *data1)
+{
+	struct msg_stdin_data	data;
+
+	data.size = read(STDIN_FILENO, data.data, sizeof data.data);
+	if (data.size < 0 && (errno == EINTR || errno == EAGAIN))
+		return;
+
+	client_write_server(MSG_STDIN, &data, sizeof data);
+	if (data.size <= 0)
+		event_del(&client_stdin);
+	client_update_event();
+}
+
 /* Dispatch imsgs when in wait state (before MSG_READY). */
 int
 client_dispatch_wait(void *data)
@@ -431,11 +449,10 @@ client_dispatch_wait(void *data)
 	ssize_t			n, datalen;
 	struct msg_shell_data	shelldata;
 	struct msg_exit_data	exitdata;
+	struct msg_stdout_data	stdoutdata;
+	struct msg_stderr_data	stderrdata;
 	const char             *shellcmd = data;
 
-	if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
-		fatalx("imsg_read failed");
-
 	for (;;) {
 		if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
 			fatalx("imsg_get failed");
@@ -443,6 +460,7 @@ client_dispatch_wait(void *data)
 			return (0);
 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
 
+		log_debug("got %d from server", imsg.hdr.type);
 		switch (imsg.hdr.type) {
 		case MSG_EXIT:
 		case MSG_SHUTDOWN:
@@ -459,14 +477,30 @@ client_dispatch_wait(void *data)
 			if (datalen != 0)
 				fatalx("bad MSG_READY size");
 
+			event_del(&client_stdin);
 			client_attached = 1;
 			break;
+		case MSG_STDOUT:
+			if (datalen != sizeof stdoutdata)
+				fatalx("bad MSG_STDOUT");
+			memcpy(&stdoutdata, imsg.data, sizeof stdoutdata);
+
+			fwrite(stdoutdata.data, stdoutdata.size, 1, stdout);
+			break;
+		case MSG_STDERR:
+			if (datalen != sizeof stderrdata)
+				fatalx("bad MSG_STDERR");
+			memcpy(&stderrdata, imsg.data, sizeof stderrdata);
+
+			fwrite(stderrdata.data, stderrdata.size, 1, stderr);
+			break;
 		case MSG_VERSION:
 			if (datalen != 0)
 				fatalx("bad MSG_VERSION size");
 
-			log_warnx("protocol version mismatch (client %u, "
-			    "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
+			fprintf(stderr, "protocol version mismatch "
+			    "(client %u, server %u)\n", PROTOCOL_VERSION,
+			    imsg.hdr.peerid);
 			client_exitval = 1;
 
 			imsg_free(&imsg);
diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c
index 373fd295..0df0cb26 100644
--- a/cmd-load-buffer.c
+++ b/cmd-load-buffer.c
@@ -31,7 +31,7 @@
  */
 
 int	cmd_load_buffer_exec(struct cmd *, struct cmd_ctx *);
-void	cmd_load_buffer_callback(struct client *, void *);
+void	cmd_load_buffer_callback(struct client *, int, void *);
 
 const struct cmd_entry cmd_load_buffer_entry = {
 	"load-buffer", "loadb",
@@ -54,8 +54,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
 	char		*pdata, *new_pdata, *cause;
 	size_t		 psize;
 	u_int		 limit;
-	int		 ch, buffer;
-	int		*buffer_ptr;
+	int		 ch, error, buffer, *buffer_ptr;
 
 	if (!args_has(args, 'b'))
 		buffer = -1;
@@ -70,27 +69,16 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
 
 	path = args->argv[0];
 	if (strcmp(path, "-") == 0) {
-		if (c == NULL) {
-			ctx->error(ctx, "%s: can't read from stdin", path);
-			return (-1);
-		}
-		if (c->flags & CLIENT_TERMINAL) {
-			ctx->error(ctx, "%s: stdin is a tty", path);
-			return (-1);
-		}
-		if (c->stdin_fd == -1) {
-			ctx->error(ctx, "%s: can't read from stdin", path);
-			return (-1);
-		}
-
 		buffer_ptr = xmalloc(sizeof *buffer_ptr);
 		*buffer_ptr = buffer;
 
-		c->stdin_data = buffer_ptr;
-		c->stdin_callback = cmd_load_buffer_callback;
-
-		c->references++;
-		bufferevent_enable(c->stdin_event, EV_READ);
+		error = server_set_stdin_callback (c, cmd_load_buffer_callback,
+		    buffer_ptr, &cause);
+		if (error != 0) {
+			ctx->error(ctx, "%s: %s", path, cause);
+			xfree(cause);
+			return (-1);
+		}
 		return (1);
 	}
 
@@ -154,35 +142,36 @@ error:
 }
 
 void
-cmd_load_buffer_callback(struct client *c, void *data)
+cmd_load_buffer_callback(struct client *c, int closed, void *data)
 {
 	int	*buffer = data;
 	char	*pdata;
 	size_t	 psize;
 	u_int	 limit;
 
-	/*
-	 * Event callback has already checked client is not dead and reduced
-	 * its reference count. But tell it to exit.
-	 */
+	if (!closed)
+		return;
+	c->stdin_callback = NULL;
+
+	c->references--;
 	c->flags |= CLIENT_EXIT;
 
-	psize = EVBUFFER_LENGTH(c->stdin_event->input);
+	psize = EVBUFFER_LENGTH(c->stdin_data);
 	if (psize == 0 || (pdata = malloc(psize + 1)) == NULL) {
 		xfree(data);
 		return;
 	}
-	bufferevent_read(c->stdin_event, pdata, psize);
+	memcpy(pdata, EVBUFFER_DATA(c->stdin_data), psize);
 	pdata[psize] = '\0';
+	evbuffer_drain(c->stdin_data, psize);
 
 	limit = options_get_number(&global_options, "buffer-limit");
 	if (*buffer == -1)
 		paste_add(&global_buffers, pdata, psize, limit);
 	else if (paste_replace(&global_buffers, *buffer, pdata, psize) != 0) {
 		/* No context so can't use server_client_msg_error. */
-		evbuffer_add_printf(
-		    c->stderr_event->output, "no buffer %d\n", *buffer);
-		bufferevent_enable(c->stderr_event, EV_WRITE);
+		evbuffer_add_printf(c->stderr_data, "no buffer %d\n", *buffer);
+		server_push_stderr(c);
 	}
 
 	xfree(data);
diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c
index 0ce2ef09..1e3cbe6d 100644
--- a/cmd-save-buffer.c
+++ b/cmd-save-buffer.c
@@ -79,7 +79,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_ctx *ctx)
 			ctx->error(ctx, "%s: can't write to stdout", path);
 			return (-1);
 		}
-		bufferevent_write(c->stdout_event, pb->data, pb->size);
+		evbuffer_add(c->stdout_data, pb->data, pb->size);
+		server_push_stdout(c);
 	} else {
 		if (c != NULL)
 			wd = c->cwd;
diff --git a/server-client.c b/server-client.c
index 6e2484e0..449190fd 100644
--- a/server-client.c
+++ b/server-client.c
@@ -34,9 +34,6 @@ void	server_client_check_exit(struct client *);
 void	server_client_check_redraw(struct client *);
 void	server_client_set_title(struct client *);
 void	server_client_reset_state(struct client *);
-void	server_client_in_callback(struct bufferevent *, short, void *);
-void	server_client_out_callback(struct bufferevent *, short, void *);
-void	server_client_err_callback(struct bufferevent *, short, void *);
 
 int	server_client_msg_dispatch(struct client *);
 void	server_client_msg_command(struct client *, struct msg_command_data *);
@@ -66,9 +63,9 @@ server_client_create(int fd)
 		fatal("gettimeofday failed");
 	memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
 
-	c->stdin_event = NULL;
-	c->stdout_event = NULL;
-	c->stderr_event = NULL;
+	c->stdin_data = evbuffer_new ();
+	c->stdout_data = evbuffer_new ();
+	c->stderr_data = evbuffer_new ();
 
 	c->tty.fd = -1;
 	c->title = NULL;
@@ -143,24 +140,9 @@ server_client_lost(struct client *c)
 	if (c->flags & CLIENT_TERMINAL)
 		tty_free(&c->tty);
 
-	if (c->stdin_event != NULL)
-		bufferevent_free(c->stdin_event);
-	if (c->stdin_fd != -1) {
-		setblocking(c->stdin_fd, 1);
-		close(c->stdin_fd);
-	}
-	if (c->stdout_event != NULL)
-		bufferevent_free(c->stdout_event);
-	if (c->stdout_fd != -1) {
-		setblocking(c->stdout_fd, 1);
-		close(c->stdout_fd);
-	}
-	if (c->stderr_event != NULL)
-		bufferevent_free(c->stderr_event);
-	if (c->stderr_fd != -1) {
-		setblocking(c->stderr_fd, 1);
-		close(c->stderr_fd);
-	}
+	evbuffer_free (c->stdin_data);
+	evbuffer_free (c->stdout_data);
+	evbuffer_free (c->stderr_data);
 
 	status_free_jobs(&c->status_new);
 	status_free_jobs(&c->status_old);
@@ -239,6 +221,9 @@ server_client_callback(int fd, short events, void *data)
 			goto client_lost;
 	}
 
+	server_push_stdout(c);
+	server_push_stderr(c);
+
 	server_update_event(c);
 	return;
 
@@ -602,11 +587,11 @@ server_client_check_exit(struct client *c)
 	if (!(c->flags & CLIENT_EXIT))
 		return;
 
-	if (c->stdout_fd != -1 && c->stdout_event != NULL &&
-	    EVBUFFER_LENGTH(c->stdout_event->output) != 0)
+	if (EVBUFFER_LENGTH(c->stdin_data) != 0)
 		return;
-	if (c->stderr_fd != -1 && c->stderr_event != NULL &&
-	    EVBUFFER_LENGTH(c->stderr_event->output) != 0)
+	if (EVBUFFER_LENGTH(c->stdout_data) != 0)
+		return;
+	if (EVBUFFER_LENGTH(c->stderr_data) != 0)
 		return;
 
 	exitdata.retcode = c->retcode;
@@ -685,55 +670,6 @@ server_client_set_title(struct client *c)
 	xfree(title);
 }
 
-/*
- * Error callback for client stdin. Caller must increase reference count when
- * enabling event!
- */
-void
-server_client_in_callback(
-    unused struct bufferevent *bufev, unused short what, void *data)
-{
-	struct client	*c = data;
-
-	c->references--;
-	if (c->flags & CLIENT_DEAD)
-		return;
-
-	bufferevent_disable(c->stdin_event, EV_READ|EV_WRITE);
-	setblocking(c->stdin_fd, 1);
-	close(c->stdin_fd);
-	c->stdin_fd = -1;
-
-	if (c->stdin_callback != NULL)
-		c->stdin_callback(c, c->stdin_data);
-}
-
-/* Error callback for client stdout. */
-void
-server_client_out_callback(
-    unused struct bufferevent *bufev, unused short what, unused void *data)
-{
-	struct client	*c = data;
-
-	bufferevent_disable(c->stdout_event, EV_READ|EV_WRITE);
-	setblocking(c->stdout_fd, 1);
-	close(c->stdout_fd);
-	c->stdout_fd = -1;
-}
-
-/* Error callback for client stderr. */
-void
-server_client_err_callback(
-    unused struct bufferevent *bufev, unused short what, unused void *data)
-{
-	struct client	*c = data;
-
-	bufferevent_disable(c->stderr_event, EV_READ|EV_WRITE);
-	setblocking(c->stderr_fd, 1);
-	close(c->stderr_fd);
-	c->stderr_fd = -1;
-}
-
 /* Dispatch message from client. */
 int
 server_client_msg_dispatch(struct client *c)
@@ -742,6 +678,7 @@ server_client_msg_dispatch(struct client *c)
 	struct msg_command_data	 commanddata;
 	struct msg_identify_data identifydata;
 	struct msg_environ_data	 environdata;
+	struct msg_stdin_data	 stdindata;
 	ssize_t			 n, datalen;
 
 	if ((n = imsg_read(&c->ibuf)) == -1 || n == 0)
@@ -777,42 +714,23 @@ server_client_msg_dispatch(struct client *c)
 				fatalx("MSG_IDENTIFY missing fd");
 			memcpy(&identifydata, imsg.data, sizeof identifydata);
 
-			c->stdin_fd = imsg.fd;
-			c->stdin_event = bufferevent_new(c->stdin_fd,
-			    NULL, NULL, server_client_in_callback, c);
-			if (c->stdin_event == NULL)
-				fatalx("failed to create stdin event");
-			setblocking(c->stdin_fd, 0);
-
 			server_client_msg_identify(c, &identifydata, imsg.fd);
 			break;
-		case MSG_STDOUT:
-			if (datalen != 0)
-				fatalx("bad MSG_STDOUT size");
-			if (imsg.fd == -1)
-				fatalx("MSG_STDOUT missing fd");
-
-			c->stdout_fd = imsg.fd;
-			c->stdout_event = bufferevent_new(c->stdout_fd,
-			    NULL, NULL, server_client_out_callback, c);
-			if (c->stdout_event == NULL)
-				fatalx("failed to create stdout event");
-			setblocking(c->stdout_fd, 0);
-
-			break;
-		case MSG_STDERR:
-			if (datalen != 0)
-				fatalx("bad MSG_STDERR size");
-			if (imsg.fd == -1)
-				fatalx("MSG_STDERR missing fd");
-
-			c->stderr_fd = imsg.fd;
-			c->stderr_event = bufferevent_new(c->stderr_fd,
-			    NULL, NULL, server_client_err_callback, c);
-			if (c->stderr_event == NULL)
-				fatalx("failed to create stderr event");
-			setblocking(c->stderr_fd, 0);
+		case MSG_STDIN:
+			if (datalen != sizeof stdindata)
+				fatalx("bad MSG_STDIN size");
+			memcpy(&stdindata, imsg.data, sizeof stdindata);
 
+			if (c->stdin_callback == NULL)
+				break;
+			if (stdindata.size <= 0)
+				c->stdin_closed = 1;
+			else {
+				evbuffer_add(c->stdin_data, stdindata.data,
+				    stdindata.size);
+			}
+			c->stdin_callback(c, c->stdin_closed,
+			    c->stdin_callback_data);
 			break;
 		case MSG_RESIZE:
 			if (datalen != 0)
@@ -879,10 +797,11 @@ server_client_msg_error(struct cmd_ctx *ctx, const char *fmt, ...)
 	va_list	ap;
 
 	va_start(ap, fmt);
-	evbuffer_add_vprintf(ctx->cmdclient->stderr_event->output, fmt, ap);
+	evbuffer_add_vprintf(ctx->cmdclient->stderr_data, fmt, ap);
 	va_end(ap);
 
-	bufferevent_write(ctx->cmdclient->stderr_event, "\n", 1);
+	evbuffer_add(ctx->cmdclient->stderr_data, "\n", 1);
+	server_push_stderr(ctx->cmdclient);
 	ctx->cmdclient->retcode = 1;
 }
 
@@ -893,10 +812,11 @@ server_client_msg_print(struct cmd_ctx *ctx, const char *fmt, ...)
 	va_list	ap;
 
 	va_start(ap, fmt);
-	evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap);
+	evbuffer_add_vprintf(ctx->cmdclient->stdout_data, fmt, ap);
 	va_end(ap);
 
-	bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1);
+	evbuffer_add(ctx->cmdclient->stdout_data, "\n", 1);
+	server_push_stdout(ctx->cmdclient);
 }
 
 /* Callback to send print message to client, if not quiet. */
@@ -909,10 +829,11 @@ server_client_msg_info(struct cmd_ctx *ctx, const char *fmt, ...)
 		return;
 
 	va_start(ap, fmt);
-	evbuffer_add_vprintf(ctx->cmdclient->stdout_event->output, fmt, ap);
+	evbuffer_add_vprintf(ctx->cmdclient->stdout_data, fmt, ap);
 	va_end(ap);
 
-	bufferevent_write(ctx->cmdclient->stdout_event, "\n", 1);
+	evbuffer_add(ctx->cmdclient->stdout_data, "\n", 1);
+	server_push_stdout(ctx->cmdclient);
 }
 
 /* Handle command message. */
@@ -969,8 +890,6 @@ void
 server_client_msg_identify(
     struct client *c, struct msg_identify_data *data, int fd)
 {
-	int	tty_fd;
-
 	c->cwd = NULL;
 	data->cwd[(sizeof data->cwd) - 1] = '\0';
 	if (*data->cwd != '\0')
@@ -978,10 +897,8 @@ server_client_msg_identify(
 
 	if (!isatty(fd))
 	    return;
-	if ((tty_fd = dup(fd)) == -1)
-		fatal("dup failed");
 	data->term[(sizeof data->term) - 1] = '\0';
-	tty_init(&c->tty, tty_fd, data->term);
+	tty_init(&c->tty, fd, data->term);
 	if (data->flags & IDENTIFY_UTF8)
 		c->tty.flags |= TTY_UTF8;
 	if (data->flags & IDENTIFY_256COLOURS)
diff --git a/server-fn.c b/server-fn.c
index dd79461c..9f51a9e7 100644
--- a/server-fn.c
+++ b/server-fn.c
@@ -46,17 +46,21 @@ server_fill_environ(struct session *s, struct environ *env)
 	environ_set(env, "TMUX", var);
 }
 
-void
+int
 server_write_client(
     struct client *c, enum msgtype type, const void *buf, size_t len)
 {
 	struct imsgbuf	*ibuf = &c->ibuf;
+	int              error;
 
 	if (c->flags & CLIENT_BAD)
-		return;
+		return (-1);
 	log_debug("writing %d to client %d", type, c->ibuf.fd);
-	imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, (void *) buf, len);
-	server_update_event(c);
+	error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1,
+	    (void *) buf, len);
+	if (error == 1)
+		server_update_event(c);
+	return (error == 1 ? 0 : -1);
 }
 
 void
@@ -502,3 +506,71 @@ server_update_event(struct client *c)
 	event_set(&c->event, c->ibuf.fd, events, server_client_callback, c);
 	event_add(&c->event, NULL);
 }
+
+/* Push stdout to client if possible. */
+void
+server_push_stdout(struct client *c)
+{
+	struct msg_stdout_data data;
+	size_t                 size;
+
+	size = EVBUFFER_LENGTH(c->stdout_data);
+	if (size == 0)
+		return;
+	if (size > sizeof data.data)
+		size = sizeof data.data;
+
+	memcpy(data.data, EVBUFFER_DATA(c->stdout_data), size);
+	data.size = size;
+
+	if (server_write_client(c, MSG_STDOUT, &data, sizeof data) == 0)
+		evbuffer_drain(c->stdout_data, size);
+}
+
+/* Push stderr to client if possible. */
+void
+server_push_stderr(struct client *c)
+{
+	struct msg_stderr_data data;
+	size_t                 size;
+
+	size = EVBUFFER_LENGTH(c->stderr_data);
+	if (size == 0)
+		return;
+	if (size > sizeof data.data)
+		size = sizeof data.data;
+
+	memcpy(data.data, EVBUFFER_DATA(c->stderr_data), size);
+	data.size = size;
+
+	if (server_write_client(c, MSG_STDERR, &data, sizeof data) == 0)
+		evbuffer_drain(c->stderr_data, size);
+}
+
+/* Set stdin callback. */
+int
+server_set_stdin_callback(struct client *c, void (*cb)(struct client *, int,
+    void *), void *cb_data, char **cause)
+{
+	if (c == NULL) {
+		*cause = xstrdup("no client with stdin");
+		return (-1);
+	}
+	if (c->flags & CLIENT_TERMINAL) {
+		*cause = xstrdup("stdin is a tty");
+		return (-1);
+	}
+	if (c->stdin_callback != NULL) {
+		*cause = xstrdup("stdin in use");
+		return (-1);
+	}
+
+	c->stdin_callback_data = cb_data;
+	c->stdin_callback = cb;
+
+	c->references++;
+
+	if (c->stdin_closed)
+		c->stdin_callback (c, 1, c->stdin_callback_data);
+	return (0);
+}
diff --git a/tmux.h b/tmux.h
index 9f99c66a..5d903624 100644
--- a/tmux.h
+++ b/tmux.h
@@ -19,7 +19,7 @@
 #ifndef TMUX_H
 #define TMUX_H
 
-#define PROTOCOL_VERSION 6
+#define PROTOCOL_VERSION 7
 
 #include <sys/param.h>
 #include <sys/time.h>
@@ -365,7 +365,7 @@ enum msgtype {
 	MSG_EXITED,
 	MSG_EXITING,
 	MSG_IDENTIFY,
-	MSG_PRINT,
+	MSG_STDIN,
 	MSG_READY,
 	MSG_RESIZE,
 	MSG_SHUTDOWN,
@@ -421,6 +421,21 @@ struct msg_exit_data {
 	int		retcode;
 };
 
+struct msg_stdin_data {
+	ssize_t	size;
+	char	data[BUFSIZ];
+};
+
+struct msg_stdout_data {
+	ssize_t	size;
+	char	data[BUFSIZ];
+};
+
+struct msg_stderr_data {
+	ssize_t	size;
+	char	data[BUFSIZ];
+};
+
 /* Mode key commands. */
 enum mode_key_cmd {
 	MODEKEY_NONE,
@@ -1157,16 +1172,12 @@ struct client {
 
 	struct tty	 tty;
 
-	int		 stdin_fd;
-	void		*stdin_data;
-	void		(*stdin_callback)(struct client *, void *);
-	struct bufferevent *stdin_event;
-
-	int		 stdout_fd;
-	struct bufferevent *stdout_event;
-
-	int		 stderr_fd;
-	struct bufferevent *stderr_event;
+	void		(*stdin_callback)(struct client *, int, void *);
+	void		*stdin_callback_data;
+	struct evbuffer	*stdin_data;
+	int              stdin_closed;
+	struct evbuffer	*stdout_data;
+	struct evbuffer	*stderr_data;
 
 	struct event	 repeat_timer;
 
@@ -1730,7 +1741,7 @@ void	 server_window_loop(void);
 
 /* server-fn.c */
 void	 server_fill_environ(struct session *, struct environ *);
-void	 server_write_client(
+int	 server_write_client(
 	     struct client *, enum msgtype, const void *, size_t);
 void	 server_write_session(
 	     struct session *, enum msgtype, const void *, size_t);
@@ -1758,6 +1769,10 @@ void	 server_check_unattached (void);
 void	 server_set_identify(struct client *);
 void	 server_clear_identify(struct client *);
 void	 server_update_event(struct client *);
+void	 server_push_stdout(struct client *);
+void	 server_push_stderr(struct client *);
+int	 server_set_stdin_callback(struct client *, void (*)(struct client *,
+	     int, void *), void *, char **);
 
 /* status.c */
 int	 status_out_cmp(struct status_out *, struct status_out *);