diff --git a/cfg.c b/cfg.c
index 827e4c18..416dbf7a 100644
--- a/cfg.c
+++ b/cfg.c
@@ -286,8 +286,6 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int quiet)
 		}
 		free(buf);
 
-		if (cmdlist == NULL)
-			continue;
 		new_item = cmdq_get_command(cmdlist, NULL, NULL, 0);
 		if (item != NULL)
 			cmdq_insert_after(item, new_item);
diff --git a/cmd-find.c b/cmd-find.c
index 27d2a394..b0174182 100644
--- a/cmd-find.c
+++ b/cmd-find.c
@@ -587,8 +587,6 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
 
 	/* Try special characters. */
 	if (strcmp(pane, "!") == 0) {
-		if (fs->w->last == NULL)
-			return (-1);
 		fs->wp = fs->w->last;
 		if (fs->wp == NULL)
 			return (-1);
@@ -912,16 +910,12 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags)
 	 */
 	fs->w = wp->window;
 	if (cmd_find_best_session_with_window(fs) != 0) {
-		if (wp != NULL) {
-			/*
-			 * The window may have been destroyed but the pane
-			 * still on all_window_panes due to something else
-			 * holding a reference.
-			 */
-			goto unknown_pane;
-		}
-		cmd_find_clear_state(fs, 0);
-		return (-1);
+		/*
+		 * The window may have been destroyed but the pane
+		 * still on all_window_panes due to something else
+		 * holding a reference.
+		 */
+		goto unknown_pane;
 	}
 	fs->wl = fs->s->curw;
 	fs->w = fs->wl->window;
diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c
index a3cabdc0..74f97d1d 100644
--- a/cmd-load-buffer.c
+++ b/cmd-load-buffer.c
@@ -59,7 +59,8 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
 	struct client			*c = item->client;
 	FILE				*f;
 	const char			*path, *bufname;
-	char				*pdata, *new_pdata, *cause, *file;
+	char				*pdata = NULL, *new_pdata, *cause;
+	char				*file;
 	size_t				 psize;
 	int				 ch, error;
 
@@ -89,8 +90,7 @@ cmd_load_buffer_exec(struct cmd *self, struct cmdq_item *item)
 	f = fopen(file, "rb");
 	if (f == NULL) {
 		cmdq_error(item, "%s: %s", file, strerror(errno));
-		free(file);
-		return (CMD_RETURN_ERROR);
+		goto error;
 	}
 
 	pdata = NULL;
diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c
index 9c2290d6..7683df31 100644
--- a/cmd-pipe-pane.c
+++ b/cmd-pipe-pane.c
@@ -35,6 +35,7 @@
 
 static enum cmd_retval	cmd_pipe_pane_exec(struct cmd *, struct cmdq_item *);
 
+static void cmd_pipe_pane_read_callback(struct bufferevent *, void *);
 static void cmd_pipe_pane_write_callback(struct bufferevent *, void *);
 static void cmd_pipe_pane_error_callback(struct bufferevent *, short, void *);
 
@@ -42,8 +43,8 @@ const struct cmd_entry cmd_pipe_pane_entry = {
 	.name = "pipe-pane",
 	.alias = "pipep",
 
-	.args = { "ot:", 0, 1 },
-	.usage = "[-o] " CMD_TARGET_PANE_USAGE " [command]",
+	.args = { "IOot:", 0, 1 },
+	.usage = "[-IOo] " CMD_TARGET_PANE_USAGE " [command]",
 
 	.target = { 't', CMD_FIND_PANE, 0 },
 
@@ -60,7 +61,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
 	struct session		*s = item->target.s;
 	struct winlink		*wl = item->target.wl;
 	char			*cmd;
-	int			 old_fd, pipe_fd[2], null_fd;
+	int			 old_fd, pipe_fd[2], null_fd, in, out;
 	struct format_tree	*ft;
 	sigset_t		 set, oldset;
 
@@ -90,6 +91,15 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
 	if (args_has(self->args, 'o') && old_fd != -1)
 		return (CMD_RETURN_NORMAL);
 
+	/* What do we want to do? Neither -I or -O is -O. */
+	if (args_has(self->args, 'I')) {
+		in = 1;
+		out = args_has(self->args, 'O');
+	} else {
+		in = 0;
+		out = 1;
+	}
+
 	/* Open the new pipe. */
 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_fd) != 0) {
 		cmdq_error(item, "socketpair error: %s", strerror(errno));
@@ -118,19 +128,25 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
 		sigprocmask(SIG_SETMASK, &oldset, NULL);
 		close(pipe_fd[0]);
 
-		if (dup2(pipe_fd[1], STDIN_FILENO) == -1)
-			_exit(1);
-		if (pipe_fd[1] != STDIN_FILENO)
-			close(pipe_fd[1]);
-
 		null_fd = open(_PATH_DEVNULL, O_WRONLY, 0);
-		if (dup2(null_fd, STDOUT_FILENO) == -1)
-			_exit(1);
+		if (out) {
+			if (dup2(pipe_fd[1], STDIN_FILENO) == -1)
+				_exit(1);
+		} else {
+			if (dup2(null_fd, STDIN_FILENO) == -1)
+				_exit(1);
+		}
+		if (in) {
+			if (dup2(pipe_fd[1], STDOUT_FILENO) == -1)
+				_exit(1);
+			if (pipe_fd[1] != STDOUT_FILENO)
+				close(pipe_fd[1]);
+		} else {
+			if (dup2(null_fd, STDOUT_FILENO) == -1)
+				_exit(1);
+		}
 		if (dup2(null_fd, STDERR_FILENO) == -1)
 			_exit(1);
-		if (null_fd != STDOUT_FILENO && null_fd != STDERR_FILENO)
-			close(null_fd);
-
 		closefrom(STDERR_FILENO + 1);
 
 		execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
@@ -143,24 +159,46 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
 		wp->pipe_fd = pipe_fd[0];
 		wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
 
-		wp->pipe_event = bufferevent_new(wp->pipe_fd, NULL,
-		    cmd_pipe_pane_write_callback, cmd_pipe_pane_error_callback,
-		    wp);
-		bufferevent_enable(wp->pipe_event, EV_WRITE);
-
 		setblocking(wp->pipe_fd, 0);
+		wp->pipe_event = bufferevent_new(wp->pipe_fd,
+		    cmd_pipe_pane_read_callback,
+		    cmd_pipe_pane_write_callback,
+		    cmd_pipe_pane_error_callback,
+		    wp);
+		if (out)
+			bufferevent_enable(wp->pipe_event, EV_WRITE);
+		if (in)
+			bufferevent_enable(wp->pipe_event, EV_READ);
 
 		free(cmd);
 		return (CMD_RETURN_NORMAL);
 	}
 }
 
+static void
+cmd_pipe_pane_read_callback(__unused struct bufferevent *bufev, void *data)
+{
+	struct window_pane	*wp = data;
+	struct evbuffer		*evb = wp->pipe_event->input;
+	size_t			 available;
+
+	available = EVBUFFER_LENGTH(evb);
+	log_debug("%%%u pipe read %zu", wp->id, available);
+
+	bufferevent_write(wp->event, EVBUFFER_DATA(evb), available);
+	evbuffer_drain(evb, available);
+
+	if (window_pane_destroy_ready(wp))
+		server_destroy_pane(wp, 1);
+}
+
 static void
 cmd_pipe_pane_write_callback(__unused struct bufferevent *bufev, void *data)
 {
 	struct window_pane	*wp = data;
 
 	log_debug("%%%u pipe empty", wp->id);
+
 	if (window_pane_destroy_ready(wp))
 		server_destroy_pane(wp, 1);
 }
diff --git a/input.c b/input.c
index 42ff7f3b..cce4df1f 100644
--- a/input.c
+++ b/input.c
@@ -85,6 +85,7 @@ struct input_ctx {
 	u_int			param_list_len;
 
 	struct utf8_data	utf8data;
+	int			utf8started;
 
 	int			ch;
 	int			last;
@@ -146,9 +147,7 @@ static void	input_csi_dispatch_sgr_256(struct input_ctx *, int, u_int *);
 static void	input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *);
 static void	input_csi_dispatch_sgr(struct input_ctx *);
 static int	input_dcs_dispatch(struct input_ctx *);
-static int	input_utf8_open(struct input_ctx *);
-static int	input_utf8_add(struct input_ctx *);
-static int	input_utf8_close(struct input_ctx *);
+static int	input_top_bit_set(struct input_ctx *);
 
 /* Command table comparison function. */
 static int	input_table_compare(const void *, const void *);
@@ -314,9 +313,6 @@ static const struct input_transition input_state_osc_string_table[];
 static const struct input_transition input_state_apc_string_table[];
 static const struct input_transition input_state_rename_string_table[];
 static const struct input_transition input_state_consume_st_table[];
-static const struct input_transition input_state_utf8_three_table[];
-static const struct input_transition input_state_utf8_two_table[];
-static const struct input_transition input_state_utf8_one_table[];
 
 /* ground state definition. */
 static const struct input_state input_state_ground = {
@@ -437,27 +433,6 @@ static const struct input_state input_state_consume_st = {
 	input_state_consume_st_table
 };
 
-/* utf8_three state definition. */
-static const struct input_state input_state_utf8_three = {
-	"utf8_three",
-	NULL, NULL,
-	input_state_utf8_three_table
-};
-
-/* utf8_two state definition. */
-static const struct input_state input_state_utf8_two = {
-	"utf8_two",
-	NULL, NULL,
-	input_state_utf8_two_table
-};
-
-/* utf8_one state definition. */
-static const struct input_state input_state_utf8_one = {
-	"utf8_one",
-	NULL, NULL,
-	input_state_utf8_one_table
-};
-
 /* ground state table. */
 static const struct input_transition input_state_ground_table[] = {
 	INPUT_STATE_ANYWHERE,
@@ -467,11 +442,7 @@ static const struct input_transition input_state_ground_table[] = {
 	{ 0x1c, 0x1f, input_c0_dispatch, NULL },
 	{ 0x20, 0x7e, input_print,	 NULL },
 	{ 0x7f, 0x7f, NULL,		 NULL },
-	{ 0x80, 0xc1, NULL,		 NULL },
-	{ 0xc2, 0xdf, input_utf8_open,	 &input_state_utf8_one },
-	{ 0xe0, 0xef, input_utf8_open,	 &input_state_utf8_two },
-	{ 0xf0, 0xf4, input_utf8_open,	 &input_state_utf8_three },
-	{ 0xf5, 0xff, NULL,		 NULL },
+	{ 0x80, 0xff, input_top_bit_set, NULL },
 
 	{ -1, -1, NULL, NULL }
 };
@@ -717,39 +688,6 @@ static const struct input_transition input_state_consume_st_table[] = {
 	{ -1, -1, NULL, NULL }
 };
 
-/* utf8_three state table. */
-static const struct input_transition input_state_utf8_three_table[] = {
-	/* No INPUT_STATE_ANYWHERE */
-
-	{ 0x00, 0x7f, NULL,		&input_state_ground },
-	{ 0x80, 0xbf, input_utf8_add,	&input_state_utf8_two },
-	{ 0xc0, 0xff, NULL,		&input_state_ground },
-
-	{ -1, -1, NULL, NULL }
-};
-
-/* utf8_two state table. */
-static const struct input_transition input_state_utf8_two_table[] = {
-	/* No INPUT_STATE_ANYWHERE */
-
-	{ 0x00, 0x7f, NULL,	      &input_state_ground },
-	{ 0x80, 0xbf, input_utf8_add, &input_state_utf8_one },
-	{ 0xc0, 0xff, NULL,	      &input_state_ground },
-
-	{ -1, -1, NULL, NULL }
-};
-
-/* utf8_one state table. */
-static const struct input_transition input_state_utf8_one_table[] = {
-	/* No INPUT_STATE_ANYWHERE */
-
-	{ 0x00, 0x7f, NULL,		&input_state_ground },
-	{ 0x80, 0xbf, input_utf8_close, &input_state_ground },
-	{ 0xc0, 0xff, NULL,		&input_state_ground },
-
-	{ -1, -1, NULL, NULL }
-};
-
 /* Input table compare. */
 static int
 input_table_compare(const void *key, const void *value)
@@ -1059,6 +997,8 @@ input_print(struct input_ctx *ictx)
 {
 	int	set;
 
+	ictx->utf8started = 0; /* can't be valid UTF-8 */
+
 	set = ictx->cell.set == 0 ? ictx->cell.g0set : ictx->cell.g1set;
 	if (set == 1)
 		ictx->cell.cell.attr |= GRID_ATTR_CHARSET;
@@ -1132,6 +1072,8 @@ input_c0_dispatch(struct input_ctx *ictx)
 	struct window_pane	*wp = ictx->wp;
 	struct screen		*s = sctx->s;
 
+	ictx->utf8started = 0; /* can't be valid UTF-8 */
+
 	log_debug("%s: '%c'", __func__, ictx->ch);
 
 	switch (ictx->ch) {
@@ -2064,48 +2006,30 @@ input_exit_rename(struct input_ctx *ictx)
 
 /* Open UTF-8 character. */
 static int
-input_utf8_open(struct input_ctx *ictx)
+input_top_bit_set(struct input_ctx *ictx)
 {
 	struct utf8_data	*ud = &ictx->utf8data;
 
-	if (utf8_open(ud, ictx->ch) != UTF8_MORE)
-		fatalx("UTF-8 open invalid %#x", ictx->ch);
-
-	log_debug("%s %hhu", __func__, ud->size);
 	ictx->last = -1;
 
-	return (0);
-}
-
-/* Append to UTF-8 character. */
-static int
-input_utf8_add(struct input_ctx *ictx)
-{
-	struct utf8_data	*ud = &ictx->utf8data;
-
-	if (utf8_append(ud, ictx->ch) != UTF8_MORE)
-		fatalx("UTF-8 add invalid %#x", ictx->ch);
-
-	log_debug("%s", __func__);
-
-	return (0);
-}
-
-/* Close UTF-8 string. */
-static int
-input_utf8_close(struct input_ctx *ictx)
-{
-	struct utf8_data	*ud = &ictx->utf8data;
-
-	if (utf8_append(ud, ictx->ch) != UTF8_DONE) {
-		/*
-		 * An error here could be invalid UTF-8 or it could be a
-		 * nonprintable character for which we can't get the
-		 * width. Drop it.
-		 */
+	if (!ictx->utf8started) {
+		if (utf8_open(ud, ictx->ch) != UTF8_MORE)
+			return (0);
+		ictx->utf8started = 1;
 		return (0);
 	}
 
+	switch (utf8_append(ud, ictx->ch)) {
+	case UTF8_MORE:
+		return (0);
+	case UTF8_ERROR:
+		ictx->utf8started = 0;
+		return (0);
+	case UTF8_DONE:
+		break;
+	}
+	ictx->utf8started = 0;
+
 	log_debug("%s %hhu '%*s' (width %hhu)", __func__, ud->size,
 	    (int)ud->size, ud->data, ud->width);
 
diff --git a/layout.c b/layout.c
index 2c6fe2b2..4ccd6d79 100644
--- a/layout.c
+++ b/layout.c
@@ -97,9 +97,24 @@ void
 layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
 {
 	struct layout_cell	*lcchild;
+	const char		*type;
 
-	log_debug("%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n,
-	    " ", lc, lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx,
+	switch (lc->type) {
+	case LAYOUT_LEFTRIGHT:
+		type = "LEFTRIGHT";
+		break;
+	case LAYOUT_TOPBOTTOM:
+		type = "TOPBOTTOM";
+		break;
+	case LAYOUT_WINDOWPANE:
+		type = "WINDOWPANE";
+		break;
+	default:
+		type = "UNKNOWN";
+		break;
+	}
+	log_debug("%s:%*s%p type %s [parent %p] wp=%p [%u,%u %ux%u]", hdr, n,
+	    " ", lc, type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx,
 	    lc->sy);
 	switch (lc->type) {
 	case LAYOUT_LEFTRIGHT:
diff --git a/screen-write.c b/screen-write.c
index 1687e8c8..75080d8b 100644
--- a/screen-write.c
+++ b/screen-write.c
@@ -1297,10 +1297,12 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
 			grid_view_get_cell(s->grid, xx, s->cy, &gc);
 			if (~gc.flags & GRID_FLAG_PADDING)
 				break;
-			grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
+			grid_view_set_cell(s->grid, xx, s->cy,
+			    &grid_default_cell);
 		}
 		if (gc.data.width > 1)
-			grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
+			grid_view_set_cell(s->grid, xx, s->cy,
+			    &grid_default_cell);
 	}
 
 	memcpy(&gc, &ci->gc, sizeof gc);
@@ -1333,7 +1335,7 @@ screen_write_collect_add(struct screen_write_ctx *ctx,
 	 */
 
 	collect = 1;
-	if (gc->data.width != 1 || gc->data.size != 1)
+	if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f)
 		collect = 0;
 	else if (gc->attr & GRID_ATTR_CHARSET)
 		collect = 0;
diff --git a/tmux.1 b/tmux.1
index 78932d2e..5387e882 100644
--- a/tmux.1
+++ b/tmux.1
@@ -1804,15 +1804,15 @@ If
 .Fl a
 is used, move to the next window with an alert.
 .It Xo Ic pipe-pane
-.Op Fl o
+.Op Fl IOo
 .Op Fl t Ar target-pane
 .Op Ar shell-command
 .Xc
 .D1 (alias: Ic pipep )
-Pipe any output sent by the program in
+Pipe output sent by the program in
 .Ar target-pane
-to a shell command.
-A pane may only be piped to one command at a time, any existing pipe is
+to a shell command or vice versa.
+A pane may only be connected to one command at a time, any existing pipe is
 closed before
 .Ar shell-command
 is executed.
@@ -1825,6 +1825,25 @@ If no
 .Ar shell-command
 is given, the current pipe (if any) is closed.
 .Pp
+.Fl I
+and
+.Fl O
+specify which of the
+.Ar shell-command
+output streams are connected to the pane:
+with
+.Fl I
+stdout is connected (so anything
+.Ar shell-command
+prints is written to the pane as if it were typed);
+with
+.Fl O
+stdin is connected (so any output in the pane is piped to
+.Ar shell-command ) .
+Both may be used together and if neither are specified,
+.Fl O
+is used.
+.Pp
 The
 .Fl o
 option only opens a new pipe if no previous pipe exists, allowing a pipe to
diff --git a/tmux.c b/tmux.c
index 818da3b0..5b73079e 100644
--- a/tmux.c
+++ b/tmux.c
@@ -44,7 +44,7 @@ int		 ptm_fd = -1;
 const char	*shell_command;
 
 static __dead void	 usage(void);
-static char		*make_label(const char *);
+static char		*make_label(const char *, char **);
 
 static const char	*getshell(void);
 static int		 checkshell(const char *);
@@ -106,12 +106,13 @@ areshell(const char *shell)
 }
 
 static char *
-make_label(const char *label)
+make_label(const char *label, char **cause)
 {
 	char		*base, resolved[PATH_MAX], *path, *s;
 	struct stat	 sb;
 	uid_t		 uid;
-	int		 saved_errno;
+
+	*cause = NULL;
 
 	if (label == NULL)
 		label = "default";
@@ -121,11 +122,16 @@ make_label(const char *label)
 		xasprintf(&base, "%s/tmux-%ld", s, (long)uid);
 	else
 		xasprintf(&base, "%s/tmux-%ld", _PATH_TMP, (long)uid);
-
-	if (mkdir(base, S_IRWXU) != 0 && errno != EEXIST)
+	if (realpath(base, resolved) == NULL &&
+	    strlcpy(resolved, base, sizeof resolved) >= sizeof resolved) {
+		errno = ERANGE;
+		free(base);
 		goto fail;
+	}
 
-	if (lstat(base, &sb) != 0)
+	if (mkdir(resolved, S_IRWXU) != 0 && errno != EEXIST)
+		goto fail;
+	if (lstat(resolved, &sb) != 0)
 		goto fail;
 	if (!S_ISDIR(sb.st_mode)) {
 		errno = ENOTDIR;
@@ -135,18 +141,11 @@ make_label(const char *label)
 		errno = EACCES;
 		goto fail;
 	}
-
-	if (realpath(base, resolved) == NULL)
-		strlcpy(resolved, base, sizeof resolved);
 	xasprintf(&path, "%s/%s", resolved, label);
-
-	free(base);
 	return (path);
 
 fail:
-	saved_errno = errno;
-	free(base);
-	errno = saved_errno;
+	xasprintf(cause, "error creating %s (%s)", resolved, strerror(errno));
 	return (NULL);
 }
 
@@ -188,7 +187,7 @@ find_home(void)
 int
 main(int argc, char **argv)
 {
-	char					*path, *label, **var;
+	char					*path, *label, *cause, **var;
 	char					 tmp[PATH_MAX];
 	const char				*s, *shell, *cwd;
 	int					 opt, flags, keys;
@@ -341,8 +340,11 @@ main(int argc, char **argv)
 			path[strcspn(path, ",")] = '\0';
 		}
 	}
-	if (path == NULL && (path = make_label(label)) == NULL) {
-		fprintf(stderr, "can't create socket: %s\n", strerror(errno));
+	if (path == NULL && (path = make_label(label, &cause)) == NULL) {
+		if (cause != NULL) {
+			fprintf(stderr, "%s\n", cause);
+			free(cause);
+		}
 		exit(1);
 	}
 	socket_path = path;
diff --git a/tty.c b/tty.c
index 0dee5008..01208a25 100644
--- a/tty.c
+++ b/tty.c
@@ -878,12 +878,37 @@ tty_draw_pane(struct tty *tty, const struct window_pane *wp, u_int py, u_int ox,
 	tty_draw_line(tty, wp, wp->screen, py, ox, oy);
 }
 
+static const struct grid_cell *
+tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
+{
+	static struct grid_cell	new;
+	u_int			n;
+
+	/* Characters less than 0x7f are always fine, no matter what. */
+	if (gc->data.size == 1 && *gc->data.data < 0x7f)
+		return (gc);
+
+	/* UTF-8 terminal and a UTF-8 character - fine. */
+	if (tty->flags & TTY_UTF8)
+		return (gc);
+
+	/* Replace by the right number of underscores. */
+	n = gc->data.width;
+	if (n > UTF8_SIZE)
+		n = UTF8_SIZE;
+	memcpy(&new, gc, sizeof new);
+	new.data.size = n;
+	memset(new.data.data, '_', n);
+	return (&new);
+}
+
 void
 tty_draw_line(struct tty *tty, const struct window_pane *wp,
     struct screen *s, u_int py, u_int ox, u_int oy)
 {
 	struct grid		*gd = s->grid;
 	struct grid_cell	 gc, last;
+	const struct grid_cell	*gcp;
 	u_int			 i, j, ux, sx, nx, width;
 	int			 flags, cleared = 0;
 	char			 buf[512];
@@ -934,18 +959,15 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp,
 
 	for (i = 0; i < sx; i++) {
 		grid_view_get_cell(gd, i, py, &gc);
+		gcp = tty_check_codeset(tty, &gc);
 		if (len != 0 &&
-		    (((~tty->flags & TTY_UTF8) &&
-		    (gc.data.size != 1 ||
-		    *gc.data.data >= 0x7f ||
-		    gc.data.width != 1)) ||
-		    (gc.attr & GRID_ATTR_CHARSET) ||
-		    gc.flags != last.flags ||
-		    gc.attr != last.attr ||
-		    gc.fg != last.fg ||
-		    gc.bg != last.bg ||
-		    ux + width + gc.data.width >= screen_size_x(s) ||
-		    (sizeof buf) - len < gc.data.size)) {
+		    ((gcp->attr & GRID_ATTR_CHARSET) ||
+		    gcp->flags != last.flags ||
+		    gcp->attr != last.attr ||
+		    gcp->fg != last.fg ||
+		    gcp->bg != last.bg ||
+		    ux + width + gcp->data.width >= screen_size_x(s) ||
+		    (sizeof buf) - len < gcp->data.size)) {
 			tty_attributes(tty, &last, wp);
 			tty_putn(tty, buf, len, width);
 			ux += width;
@@ -954,35 +976,27 @@ tty_draw_line(struct tty *tty, const struct window_pane *wp,
 			width = 0;
 		}
 
-		if (gc.flags & GRID_FLAG_SELECTED)
-			screen_select_cell(s, &last, &gc);
+		if (gcp->flags & GRID_FLAG_SELECTED)
+			screen_select_cell(s, &last, gcp);
 		else
-			memcpy(&last, &gc, sizeof last);
-		if (ux + gc.data.width > screen_size_x(s))
-			for (j = 0; j < gc.data.width; j++) {
+			memcpy(&last, gcp, sizeof last);
+		if (ux + gcp->data.width > screen_size_x(s)) {
+			tty_attributes(tty, &last, wp);
+			for (j = 0; j < gcp->data.width; j++) {
 				if (ux + j > screen_size_x(s))
 					break;
 				tty_putc(tty, ' ');
 				ux++;
 			}
-		else if (((~tty->flags & TTY_UTF8) &&
-		    (gc.data.size != 1 ||
-		    *gc.data.data >= 0x7f ||
-		    gc.data.width != 1)) ||
-		    (gc.attr & GRID_ATTR_CHARSET)) {
+		} else if (gcp->attr & GRID_ATTR_CHARSET) {
 			tty_attributes(tty, &last, wp);
-			if (~tty->flags & TTY_UTF8) {
-				for (j = 0; j < gc.data.width; j++)
-					tty_putc(tty, '_');
-			} else {
-				for (j = 0; j < gc.data.size; j++)
-					tty_putc(tty, gc.data.data[j]);
-			}
+			for (j = 0; j < gcp->data.size; j++)
+				tty_putc(tty, gcp->data.data[j]);
 			ux += gc.data.width;
 		} else {
-			memcpy(buf + len, gc.data.data, gc.data.size);
-			len += gc.data.size;
-			width += gc.data.width;
+			memcpy(buf + len, gcp->data.data, gcp->data.size);
+			len += gcp->data.size;
+			width += gcp->data.width;
 		}
 	}
 	if (len != 0) {
@@ -1409,7 +1423,7 @@ static void
 tty_cell(struct tty *tty, const struct grid_cell *gc,
     const struct window_pane *wp)
 {
-	u_int	i;
+	const struct grid_cell	*gcp;
 
 	/* Skip last character if terminal is stupid. */
 	if ((tty->term->flags & TERM_EARLYWRAP) &&
@@ -1425,22 +1439,16 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
 	tty_attributes(tty, gc, wp);
 
 	/* Get the cell and if ASCII write with putc to do ACS translation. */
-	if (gc->data.size == 1) {
-		if (*gc->data.data < 0x20 || *gc->data.data == 0x7f)
+	gcp = tty_check_codeset(tty, gc);
+	if (gcp->data.size == 1) {
+		if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f)
 			return;
-		tty_putc(tty, *gc->data.data);
-		return;
-	}
-
-	/* If not UTF-8, write _. */
-	if (!(tty->flags & TTY_UTF8)) {
-		for (i = 0; i < gc->data.width; i++)
-			tty_putc(tty, '_');
+		tty_putc(tty, *gcp->data.data);
 		return;
 	}
 
 	/* Write the data. */
-	tty_putn(tty, gc->data.data, gc->data.size, gc->data.width);
+	tty_putn(tty, gcp->data.data, gcp->data.size, gcp->data.width);
 }
 
 void