diff --git a/alerts.c b/alerts.c
index 4cc5c3eb..d3c5df05 100644
--- a/alerts.c
+++ b/alerts.c
@@ -314,10 +314,11 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option)
 			tty_putcode(&c->tty, TTYC_BEL);
 		if (visual == VISUAL_OFF)
 			continue;
-		if (c->session->curw == wl)
-			status_message_set(c, -1, 1, "%s in current window", type);
-		else {
-			status_message_set(c, -1, 1, "%s in window %d", type,
+		if (c->session->curw == wl) {
+			status_message_set(c, -1, 1, 0, "%s in current window",
+			    type);
+		} else {
+			status_message_set(c, -1, 1, 0, "%s in window %d", type,
 			    wl->idx);
 		}
 	}
diff --git a/cfg.c b/cfg.c
index 3ef46e66..8ec5a95b 100644
--- a/cfg.c
+++ b/cfg.c
@@ -102,6 +102,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
 	struct cmd_parse_input	 pi;
 	struct cmd_parse_result	*pr;
 	struct cmdq_item	*new_item0;
+	struct cmdq_state	*state;
 
 	if (new_item != NULL)
 		*new_item = NULL;
@@ -135,12 +136,19 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
 		return (0);
 	}
 
-	new_item0 = cmdq_get_command(pr->cmdlist, NULL);
+	if (item != NULL)
+		state = cmdq_copy_state(cmdq_get_state(item));
+	else
+		state = cmdq_new_state(NULL, NULL, 0);
+	cmdq_add_format(state, "current_file", "%s", pi.file);
+
+	new_item0 = cmdq_get_command(pr->cmdlist, state);
 	if (item != NULL)
 		new_item0 = cmdq_insert_after(item, new_item0);
 	else
 		new_item0 = cmdq_append(NULL, new_item0);
 	cmd_list_free(pr->cmdlist);
+	cmdq_free_state(state);
 
 	if (new_item != NULL)
 		*new_item = new_item0;
@@ -155,6 +163,7 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
 	struct cmd_parse_input	 pi;
 	struct cmd_parse_result	*pr;
 	struct cmdq_item	*new_item0;
+	struct cmdq_state	*state;
 
 	if (new_item != NULL)
 		*new_item = NULL;
@@ -181,12 +190,19 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
 		return (0);
 	}
 
-	new_item0 = cmdq_get_command(pr->cmdlist, NULL);
+	if (item != NULL)
+		state = cmdq_copy_state(cmdq_get_state(item));
+	else
+		state = cmdq_new_state(NULL, NULL, 0);
+	cmdq_add_format(state, "current_file", "%s", pi.file);
+
+	new_item0 = cmdq_get_command(pr->cmdlist, state);
 	if (item != NULL)
 		new_item0 = cmdq_insert_after(item, new_item0);
 	else
 		new_item0 = cmdq_append(NULL, new_item0);
 	cmd_list_free(pr->cmdlist);
+	cmdq_free_state(state);
 
 	if (new_item != NULL)
 		*new_item = new_item0;
diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c
index a58469ac..81209ee3 100644
--- a/cmd-choose-tree.c
+++ b/cmd-choose-tree.c
@@ -30,9 +30,9 @@ const struct cmd_entry cmd_choose_tree_entry = {
 	.name = "choose-tree",
 	.alias = NULL,
 
-	.args = { "F:Gf:NO:rst:wZ", 0, 1 },
-	.usage = "[-GNrswZ] [-F format] [-f filter] [-O sort-order] "
-	         CMD_TARGET_PANE_USAGE " [template]",
+	.args = { "F:f:GK:NO:rst:wZ", 0, 1 },
+	.usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] "
+		 "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
 
 	.target = { 't', CMD_FIND_PANE, 0 },
 
@@ -44,9 +44,9 @@ const struct cmd_entry cmd_choose_client_entry = {
 	.name = "choose-client",
 	.alias = NULL,
 
-	.args = { "F:f:NO:rt:Z", 0, 1 },
-	.usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
-	         CMD_TARGET_PANE_USAGE " [template]",
+	.args = { "F:f:K:NO:rt:Z", 0, 1 },
+	.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
+		 "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
 
 	.target = { 't', CMD_FIND_PANE, 0 },
 
@@ -58,9 +58,9 @@ const struct cmd_entry cmd_choose_buffer_entry = {
 	.name = "choose-buffer",
 	.alias = NULL,
 
-	.args = { "F:f:NO:rt:Z", 0, 1 },
-	.usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
-	         CMD_TARGET_PANE_USAGE " [template]",
+	.args = { "F:f:K:NO:rt:Z", 0, 1 },
+	.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
+		 "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
 
 	.target = { 't', CMD_FIND_PANE, 0 },
 
diff --git a/cmd-display-message.c b/cmd-display-message.c
index fc9c4851..0522d37f 100644
--- a/cmd-display-message.c
+++ b/cmd-display-message.c
@@ -39,11 +39,11 @@ const struct cmd_entry cmd_display_message_entry = {
 	.name = "display-message",
 	.alias = "display",
 
-	.args = { "acd:Ipt:F:v", 0, 1 },
-	.usage = "[-aIpv] [-c target-client] [-d delay] [-F format] "
+	.args = { "acd:INpt:F:v", 0, 1 },
+	.usage = "[-aINpv] [-c target-client] [-d delay] [-F format] "
 		 CMD_TARGET_PANE_USAGE " [message]",
 
-	.target = { 't', CMD_FIND_PANE, 0 },
+	.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
 
 	.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL,
 	.exec = cmd_display_message_exec
@@ -73,6 +73,8 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
 	int			 flags;
 
 	if (args_has(args, 'I')) {
+		if (wp == NULL)
+			return (CMD_RETURN_NORMAL);
 		if (window_pane_start_input(wp, item, &cause) != 0) {
 			cmdq_error(item, "%s", cause);
 			free(cause);
@@ -109,8 +111,10 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
 	 */
 	if (tc != NULL && tc->session == s)
 		c = tc;
-	else
+	else if (s != NULL)
 		c = cmd_find_best_client(s);
+	else
+		c = NULL;
 	if (args_has(args, 'v'))
 		flags = FORMAT_VERBOSE;
 	else
@@ -124,10 +128,14 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
 	}
 
 	msg = format_expand_time(ft, template);
-	if (args_has(args, 'p'))
+	if (cmdq_get_client(item) == NULL)
+		cmdq_error(item, "%s", msg);
+	else if (args_has(args, 'p'))
 		cmdq_print(item, "%s", msg);
-	else if (tc != NULL)
-		status_message_set(tc, delay, 0, "%s", msg);
+	else if (tc != NULL) {
+		status_message_set(tc, delay, 0, args_has(args, 'N'), "%s",
+		    msg);
+	}
 	free(msg);
 
 	format_free(ft);
diff --git a/cmd-list-keys.c b/cmd-list-keys.c
index dd82e57e..ca4bf752 100644
--- a/cmd-list-keys.c
+++ b/cmd-list-keys.c
@@ -113,9 +113,10 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
 		else
 			note = xstrdup(bd->note);
 		tmp = utf8_padcstr(key, keywidth + 1);
-		if (args_has(args, '1') && tc != NULL)
-			status_message_set(tc, -1, 1, "%s%s%s", prefix, tmp, note);
-		else
+		if (args_has(args, '1') && tc != NULL) {
+			status_message_set(tc, -1, 1, 0, "%s%s%s", prefix, tmp,
+			    note);
+		} else
 			cmdq_print(item, "%s%s%s", prefix, tmp, note);
 		free(tmp);
 		free(note);
diff --git a/cmd-queue.c b/cmd-queue.c
index 05f439f5..54163919 100644
--- a/cmd-queue.c
+++ b/cmd-queue.c
@@ -276,7 +276,7 @@ cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft)
 	const struct cmd_entry	*entry;
 
 	if (item->cmd != NULL) {
-		entry = cmd_get_entry (item->cmd);
+		entry = cmd_get_entry(item->cmd);
 		format_add(ft, "command", "%s", entry->name);
 	}
 	if (item->state->formats != NULL)
@@ -862,7 +862,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
 		c->retval = 1;
 	} else {
 		*msg = toupper((u_char) *msg);
-		status_message_set(c, -1, 1, "%s", msg);
+		status_message_set(c, -1, 1, 0, "%s", msg);
 	}
 
 	free(msg);
diff --git a/cmd-run-shell.c b/cmd-run-shell.c
index 4f30d05d..56d5f723 100644
--- a/cmd-run-shell.c
+++ b/cmd-run-shell.c
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
+#include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -190,8 +191,12 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
 			    &error);
 		}
 		if (status == CMD_PARSE_ERROR) {
-		       cmdq_error(cdata->item, "%s", error);
-		       free(error);
+			if (cdata->item == NULL) {
+				*error = toupper((u_char)*error);
+				status_message_set(c, -1, 1, 0, "%s", error);
+			} else
+				cmdq_error(cdata->item, "%s", error);
+			free(error);
 		}
 	}
 
diff --git a/control-notify.c b/control-notify.c
index cc706ac2..6ff0e436 100644
--- a/control-notify.c
+++ b/control-notify.c
@@ -171,6 +171,17 @@ control_notify_client_session_changed(struct client *cc)
 	}
 }
 
+void
+control_notify_client_detached(struct client *cc)
+{
+	struct client	*c;
+
+	TAILQ_FOREACH(c, &clients, entry) {
+		if (CONTROL_SHOULD_NOTIFY_CLIENT(c))
+			control_write(c, "%%client-detached %s", cc->name);
+	}
+}
+
 void
 control_notify_session_renamed(struct session *s)
 {
diff --git a/format.c b/format.c
index 0e8ea8f3..5a12502e 100644
--- a/format.c
+++ b/format.c
@@ -100,6 +100,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
 #define FORMAT_QUOTE_STYLE 0x2000
 #define FORMAT_WINDOW_NAME 0x4000
 #define FORMAT_SESSION_NAME 0x8000
+#define FORMAT_CHARACTER 0x10000
 
 /* Limit on recursion. */
 #define FORMAT_LOOP_LIMIT 10
@@ -3522,7 +3523,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
 
 	/*
 	 * Modifiers are a ; separated list of the forms:
-	 *      l,m,C,b,d,n,t,w,q,E,T,S,W,P,<,>
+	 *      l,m,C,a,b,d,n,t,w,q,E,T,S,W,P,<,>
 	 *	=a
 	 *	=/a
 	 *      =/a/
@@ -3539,7 +3540,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
 			cp++;
 
 		/* Check single character modifiers with no arguments. */
-		if (strchr("lbdnwETSWP<>", cp[0]) != NULL &&
+		if (strchr("labdnwETSWP<>", cp[0]) != NULL &&
 		    format_is_end(cp[1])) {
 			format_add_modifier(&list, count, cp, 1, NULL, 0);
 			cp++;
@@ -3956,7 +3957,7 @@ format_replace_expression(struct format_modifier *mexp,
 		mright = (long long)mright;
 	}
 	format_log(es, "expression left side is: %.*f", prec, mleft);
-	format_log(es, "expression right side is:  %.*f", prec, mright);
+	format_log(es, "expression right side is: %.*f", prec, mright);
 
 	switch (operator) {
 	case ADD:
@@ -4016,10 +4017,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
 {
 	struct format_tree		 *ft = es->ft;
 	struct window_pane		 *wp = ft->wp;
-	const char			 *errptr, *copy, *cp, *marker = NULL;
+	const char			 *errstr, *copy, *cp, *marker = NULL;
 	const char			 *time_format = NULL;
 	char				 *copy0, *condition, *found, *new;
-	char				 *value, *left, *right;
+	char				 *value, *left, *right, c;
 	size_t				  valuelen;
 	int				  modifiers = 0, limit = 0, width = 0;
 	int				  j;
@@ -4063,8 +4064,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
 				if (fm->argc < 1)
 					break;
 				limit = strtonum(fm->argv[0], INT_MIN, INT_MAX,
-				    &errptr);
-				if (errptr != NULL)
+				    &errstr);
+				if (errstr != NULL)
 					limit = 0;
 				if (fm->argc >= 2 && fm->argv[1] != NULL)
 					marker = fm->argv[1];
@@ -4073,8 +4074,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
 				if (fm->argc < 1)
 					break;
 				width = strtonum(fm->argv[0], INT_MIN, INT_MAX,
-				    &errptr);
-				if (errptr != NULL)
+				    &errstr);
+				if (errstr != NULL)
 					width = 0;
 				break;
 			case 'w':
@@ -4088,6 +4089,9 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
 			case 'l':
 				modifiers |= FORMAT_LITERAL;
 				break;
+			case 'a':
+				modifiers |= FORMAT_CHARACTER;
+				break;
 			case 'b':
 				modifiers |= FORMAT_BASENAME;
 				break;
@@ -4154,6 +4158,18 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
 		goto done;
 	}
 
+	/* Is this a character? */
+	if (modifiers & FORMAT_CHARACTER) {
+		new = format_expand1(es, copy);
+		c = strtonum(new, 32, 126, &errstr);
+		if (errstr != NULL)
+			value = xstrdup("");
+		else
+			xasprintf(&value, "%c", c);
+		free (new);
+		goto done;
+	}
+
 	/* Is this a loop, comparison or condition? */
 	if (modifiers & FORMAT_SESSIONS) {
 		value = format_loop_sessions(es, copy);
diff --git a/grid-reader.c b/grid-reader.c
index ae2f4d2b..89fe90fb 100644
--- a/grid-reader.c
+++ b/grid-reader.c
@@ -71,7 +71,7 @@ grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all)
 
 /* Move cursor back one position. */
 void
-grid_reader_cursor_left(struct grid_reader *gr)
+grid_reader_cursor_left(struct grid_reader *gr, int wrap)
 {
 	struct grid_cell	gc;
 
@@ -81,7 +81,9 @@ grid_reader_cursor_left(struct grid_reader *gr)
 			break;
 		gr->cx--;
 	}
-	if (gr->cx == 0 && gr->cy > 0) {
+	if (gr->cx == 0 && gr->cy > 0 &&
+	    (wrap ||
+	     grid_get_line(gr->gd, gr->cy - 1)->flags & GRID_LINE_WRAPPED)) {
 		grid_reader_cursor_up(gr);
 		grid_reader_cursor_end_of_line(gr, 0, 0);
 	} else if (gr->cx > 0)
@@ -363,3 +365,25 @@ grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc)
 	}
 	return 0;
 }
+
+/* Jump back to the first non-blank character of the line. */
+void
+grid_reader_cursor_back_to_indentation(struct grid_reader *gr)
+{
+	struct grid_cell	gc;
+	u_int			px, py, xx, yy;
+
+	yy = gr->gd->hsize + gr->gd->sy - 1;
+	grid_reader_cursor_start_of_line(gr, 1);
+
+	for (py = gr->cy; py <= yy; py++) {
+		xx = grid_line_length(gr->gd, py);
+		for (px = 0; px < xx; px++) {
+			grid_get_cell(gr->gd, px, py, &gc);
+			if (gc.data.size != 1 || *gc.data.data != ' ')
+				break;
+		}
+		if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)
+			break;
+	}
+}
diff --git a/input-keys.c b/input-keys.c
index 39a72cdc..a3252855 100644
--- a/input-keys.c
+++ b/input-keys.c
@@ -428,6 +428,14 @@ input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m)
 	return (input_key(wp->screen, wp->event, key));
 }
 
+static void
+input_key_write(const char *from, struct bufferevent *bev, const char *data,
+    size_t size)
+{
+	log_debug("%s: %.*s", from, (int)size, data);
+	bufferevent_write(bev, data, size);
+}
+
 /* Translate a key code into an output key sequence. */
 int
 input_key(struct screen *s, struct bufferevent *bev, key_code key)
@@ -444,7 +452,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
 	/* Literal keys go as themselves (can't be more than eight bits). */
 	if (key & KEYC_LITERAL) {
 		ud.data[0] = (u_char)key;
-		bufferevent_write(bev, &ud.data[0], 1);
+		input_key_write(__func__, bev, &ud.data[0], 1);
 		return (0);
 	}
 
@@ -463,16 +471,16 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
 	justkey = (key & ~(KEYC_META|KEYC_IMPLIED_META));
 	if (justkey <= 0x7f) {
 		if (key & KEYC_META)
-			bufferevent_write(bev, "\033", 1);
+			input_key_write(__func__, bev, "\033", 1);
 		ud.data[0] = justkey;
-		bufferevent_write(bev, &ud.data[0], 1);
+		input_key_write(__func__, bev, &ud.data[0], 1);
 		return (0);
 	}
 	if (justkey > 0x7f && justkey < KEYC_BASE) {
 		if (key & KEYC_META)
-			bufferevent_write(bev, "\033", 1);
+			input_key_write(__func__, bev, "\033", 1);
 		utf8_to_data(justkey, &ud);
-		bufferevent_write(bev, ud.data, ud.size);
+		input_key_write(__func__, bev, ud.data, ud.size);
 		return (0);
 	}
 
@@ -494,8 +502,8 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
 	if (ike != NULL) {
 		log_debug("found key 0x%llx: \"%s\"", key, ike->data);
 		if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META))
-			bufferevent_write(bev, "\033", 1);
-		bufferevent_write(bev, ike->data, strlen(ike->data));
+			input_key_write(__func__, bev, "\033", 1);
+		input_key_write(__func__, bev, ike->data, strlen(ike->data));
 		return (0);
 	}
 
@@ -560,7 +568,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
 		goto missing;
 	}
 	xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier);
-	bufferevent_write(bev, tmp, strlen(tmp));
+	input_key_write(__func__, bev, tmp, strlen(tmp));
 	return (0);
 
 missing:
@@ -656,5 +664,5 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m)
 	if (!input_key_get_mouse(s, m, x, y, &buf, &len))
 		return;
 	log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id);
-	bufferevent_write(wp->event, buf, len);
+	input_key_write(__func__, wp->event, buf, len);
 }
diff --git a/key-string.c b/key-string.c
index 194fdef2..8d60f132 100644
--- a/key-string.c
+++ b/key-string.c
@@ -45,9 +45,9 @@ static const struct {
 	{ "F11",	KEYC_F11|KEYC_IMPLIED_META },
 	{ "F12",	KEYC_F12|KEYC_IMPLIED_META },
 	{ "IC",		KEYC_IC|KEYC_IMPLIED_META },
-	{ "Insert",     KEYC_IC|KEYC_IMPLIED_META },
+	{ "Insert",	KEYC_IC|KEYC_IMPLIED_META },
 	{ "DC",		KEYC_DC|KEYC_IMPLIED_META },
-	{ "Delete",     KEYC_DC|KEYC_IMPLIED_META },
+	{ "Delete",	KEYC_DC|KEYC_IMPLIED_META },
 	{ "Home",	KEYC_HOME|KEYC_IMPLIED_META },
 	{ "End",	KEYC_END|KEYC_IMPLIED_META },
 	{ "NPage",	KEYC_NPAGE|KEYC_IMPLIED_META },
@@ -70,7 +70,7 @@ static const struct {
 	{ "Right",	KEYC_RIGHT|KEYC_CURSOR|KEYC_IMPLIED_META },
 
 	/* Numeric keypad. */
-	{ "KP/", 	KEYC_KP_SLASH|KEYC_KEYPAD },
+	{ "KP/",	KEYC_KP_SLASH|KEYC_KEYPAD },
 	{ "KP*",	KEYC_KP_STAR|KEYC_KEYPAD },
 	{ "KP-",	KEYC_KP_MINUS|KEYC_KEYPAD },
 	{ "KP7",	KEYC_KP_SEVEN|KEYC_KEYPAD },
@@ -164,7 +164,7 @@ key_string_get_modifiers(const char **string)
 key_code
 key_string_lookup_string(const char *string)
 {
-	static const char	*other = "!#()+,-.0123456789:;<=>'\r\t";
+	static const char	*other = "!#()+,-.0123456789:;<=>'\r\t\177";
 	key_code		 key, modifiers;
 	u_int			 u, i;
 	struct utf8_data	 ud, *udp;
@@ -181,8 +181,8 @@ key_string_lookup_string(const char *string)
 
 	/* Is this a hexadecimal value? */
 	if (string[0] == '0' && string[1] == 'x') {
-	        if (sscanf(string + 2, "%x", &u) != 1)
-	                return (KEYC_UNKNOWN);
+		if (sscanf(string + 2, "%x", &u) != 1)
+			return (KEYC_UNKNOWN);
 		mlen = wctomb(m, u);
 		if (mlen <= 0 || mlen > MB_LEN_MAX)
 			return (KEYC_UNKNOWN);
@@ -238,11 +238,12 @@ key_string_lookup_string(const char *string)
 	}
 
 	/* Convert the standard control keys. */
-	if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && !strchr(other, key)) {
+	if (key < KEYC_BASE && (modifiers & KEYC_CTRL) &&
+	    strchr(other, key) == NULL) {
 		if (key >= 97 && key <= 122)
 			key -= 96;
 		else if (key >= 64 && key <= 95)
-			key -= 64;
+                       key -= 64;
 		else if (key == 32)
 			key = 0;
 		else if (key == 63)
diff --git a/log.c b/log.c
index f87cab92..26569974 100644
--- a/log.c
+++ b/log.c
@@ -110,15 +110,16 @@ log_vwrite(const char *msg, va_list ap)
 		return;
 
 	if (vasprintf(&fmt, msg, ap) == -1)
-		exit(1);
-	if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1)
-		exit(1);
+		return;
+	if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) {
+		free(fmt);
+		return;
+	}
 
 	gettimeofday(&tv, NULL);
 	if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec,
-	    (int)tv.tv_usec, out) == -1)
-		exit(1);
-	fflush(log_file);
+	    (int)tv.tv_usec, out) != -1)
+		fflush(log_file);
 
 	free(out);
 	free(fmt);
diff --git a/mode-tree.c b/mode-tree.c
index a47c0c06..ca7f33a4 100644
--- a/mode-tree.c
+++ b/mode-tree.c
@@ -46,6 +46,7 @@ struct mode_tree_data {
 	mode_tree_search_cb       searchcb;
 	mode_tree_menu_cb         menucb;
 	mode_tree_height_cb       heightcb;
+	mode_tree_key_cb	  keycb;
 
 	struct mode_tree_list	  children;
 	struct mode_tree_list	  saved;
@@ -74,6 +75,10 @@ struct mode_tree_item {
 	void				*itemdata;
 	u_int				 line;
 
+	key_code			 key;
+	const char			*keystr;
+	size_t				 keylen;
+
 	uint64_t			 tag;
 	const char			*name;
 	const char			*text;
@@ -135,6 +140,7 @@ mode_tree_free_item(struct mode_tree_item *mti)
 
 	free((void *)mti->name);
 	free((void *)mti->text);
+	free((void *)mti->keystr);
 
 	free(mti);
 }
@@ -193,6 +199,26 @@ mode_tree_build_lines(struct mode_tree_data *mtd,
 			flat = 0;
 		if (mti->expanded)
 			mode_tree_build_lines(mtd, &mti->children, depth + 1);
+
+		if (mtd->keycb != NULL) {
+			mti->key = mtd->keycb(mtd->modedata, mti->itemdata,
+			    mti->line);
+			if (mti->key == KEYC_UNKNOWN)
+				mti->key = KEYC_NONE;
+		} else if (mti->line < 10)
+			mti->key = '0' + mti->line;
+		else if (mti->line < 36)
+			mti->key = KEYC_META|('a' + mti->line - 10);
+		else
+			mti->key = KEYC_NONE;
+		if (mti->key != KEYC_NONE) {
+			mti->keystr = xstrdup(key_string_lookup_key(mti->key,
+			    0));
+			mti->keylen = strlen(mti->keystr);
+		} else {
+			mti->keystr = NULL;
+			mti->keylen = 0;
+		}
 	}
 	TAILQ_FOREACH(mti, mtl, entry) {
 		for (i = 0; i < mtd->line_size; i++) {
@@ -363,7 +389,7 @@ struct mode_tree_data *
 mode_tree_start(struct window_pane *wp, struct args *args,
     mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
     mode_tree_search_cb searchcb, mode_tree_menu_cb menucb,
-    mode_tree_height_cb heightcb, void *modedata,
+    mode_tree_height_cb heightcb, mode_tree_key_cb keycb, void *modedata,
     const struct menu_item *menu, const char **sort_list, u_int sort_size,
     struct screen **s)
 {
@@ -402,6 +428,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
 	mtd->searchcb = searchcb;
 	mtd->menucb = menucb;
 	mtd->heightcb = heightcb;
+	mtd->keycb = keycb;
 
 	TAILQ_INIT(&mtd->children);
 
@@ -596,10 +623,10 @@ mode_tree_draw(struct mode_tree_data *mtd)
 	struct screen_write_ctx	 ctx;
 	struct grid_cell	 gc0, gc;
 	u_int			 w, h, i, j, sy, box_x, box_y, width;
-	char			*text, *start, key[7];
+	char			*text, *start, *key;
 	const char		*tag, *symbol;
 	size_t			 size, n;
-	int			 keylen;
+	int			 keylen, pad;
 
 	if (mtd->line_size == 0)
 		return;
@@ -614,28 +641,30 @@ mode_tree_draw(struct mode_tree_data *mtd)
 	screen_write_start(&ctx, s);
 	screen_write_clearscreen(&ctx, 8);
 
-	if (mtd->line_size > 10)
-		keylen = 6;
-	else
-		keylen = 4;
+	keylen = 0;
+	for (i = 0; i < mtd->line_size; i++) {
+		mti = mtd->line_list[i].item;
+		if (mti->key == KEYC_NONE)
+			continue;
+		if ((int)mti->keylen + 3 > keylen)
+			keylen = mti->keylen + 3;
+	}
 
 	for (i = 0; i < mtd->line_size; i++) {
 		if (i < mtd->offset)
 			continue;
 		if (i > mtd->offset + h - 1)
 			break;
-
 		line = &mtd->line_list[i];
 		mti = line->item;
 
 		screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
 
-		if (i < 10)
-			snprintf(key, sizeof key, "(%c)  ", '0' + i);
-		else if (i < 36)
-			snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10));
+		pad = keylen - 2 - mti->keylen;
+		if (mti->key != KEYC_NONE)
+			xasprintf(&key, "(%s)%*s", mti->keystr, pad, "");
 		else
-			*key = '\0';
+			key = xstrdup("");
 
 		if (line->flat)
 			symbol = "";
@@ -698,6 +727,7 @@ mode_tree_draw(struct mode_tree_data *mtd)
 			}
 		}
 		free(text);
+		free(key);
 
 		if (mti->tagged) {
 			gc.attr ^= GRID_ATTR_BRIGHT;
@@ -951,7 +981,6 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
 	struct mode_tree_item	*current, *parent, *mti;
 	u_int			 i, x, y;
 	int			 choice;
-	key_code		 tmp;
 
 	if (KEYC_IS_MOUSE(*key) && m != NULL) {
 		if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
@@ -993,12 +1022,11 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
 	current = line->item;
 
 	choice = -1;
-	if (*key >= '0' && *key <= '9')
-		choice = (*key) - '0';
-	else if (((*key) & KEYC_MASK_MODIFIERS) == KEYC_META) {
-		tmp = (*key) & KEYC_MASK_KEY;
-		if (tmp >= 'a' && tmp <= 'z')
-			choice = 10 + (tmp - 'a');
+	for (i = 0; i < mtd->line_size; i++) {
+		if (*key == mtd->line_list[i].item->key) {
+			choice = i;
+			break;
+		}
 	}
 	if (choice != -1) {
 		if ((u_int)choice > mtd->line_size - 1) {
@@ -1176,7 +1204,7 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
 		if (status == CMD_PARSE_ERROR) {
 			if (c != NULL) {
 				*error = toupper((u_char)*error);
-				status_message_set(c, -1, 1, "%s", error);
+				status_message_set(c, -1, 1, 0, "%s", error);
 			}
 			free(error);
 		}
diff --git a/notify.c b/notify.c
index f7440cad..4b9b1eaa 100644
--- a/notify.c
+++ b/notify.c
@@ -125,6 +125,8 @@ notify_callback(struct cmdq_item *item, void *data)
 		control_notify_window_renamed(ne->window);
 	if (strcmp(ne->name, "client-session-changed") == 0)
 		control_notify_client_session_changed(ne->client);
+	if (strcmp(ne->name, "client-detached") == 0)
+		control_notify_client_detached(ne->client);
 	if (strcmp(ne->name, "session-renamed") == 0)
 		control_notify_session_renamed(ne->session);
 	if (strcmp(ne->name, "session-created") == 0)
diff --git a/screen.c b/screen.c
index db2590cc..2b9d70de 100644
--- a/screen.c
+++ b/screen.c
@@ -153,8 +153,10 @@ screen_reset_tabs(struct screen *s)
 void
 screen_set_cursor_style(struct screen *s, u_int style)
 {
-	if (style <= 6)
+	if (style <= 6) {
 		s->cstyle = style;
+		s->mode &= ~MODE_BLINKING;
+	}
 }
 
 /* Set screen cursor colour. */
diff --git a/server-client.c b/server-client.c
index 96e1b584..d3ffd682 100644
--- a/server-client.c
+++ b/server-client.c
@@ -296,6 +296,9 @@ server_client_lost(struct client *c)
 	TAILQ_REMOVE(&clients, c, entry);
 	log_debug("lost client %p", c);
 
+	if (c->flags & CLIENT_ATTACHED)
+		notify_client("client-detached", c);
+
 	if (c->flags & CLIENT_CONTROL)
 		control_stop(c);
 	if (c->flags & CLIENT_TERMINAL)
@@ -1305,7 +1308,11 @@ server_client_handle_key(struct client *c, struct key_event *event)
 	 * immediately rather than queued.
 	 */
 	if (~c->flags & CLIENT_READONLY) {
-		status_message_clear(c);
+		if (c->message_string != NULL) {
+			if (c->message_ignore_keys)
+				return (0);
+			status_message_clear(c);
+		}
 		if (c->overlay_key != NULL) {
 			switch (c->overlay_key(c, event)) {
 			case 0:
@@ -1766,9 +1773,6 @@ server_client_check_exit(struct client *c)
 		if (EVBUFFER_LENGTH(cf->buffer) != 0)
 			return;
 	}
-
-	if (c->flags & CLIENT_ATTACHED)
-		notify_client("client-detached", c);
 	c->flags |= CLIENT_EXITED;
 
 	switch (c->exit_type) {
diff --git a/status.c b/status.c
index 154d9452..f9786f4b 100644
--- a/status.c
+++ b/status.c
@@ -424,7 +424,7 @@ status_redraw(struct client *c)
 /* Set a status line message. */
 void
 status_message_set(struct client *c, int delay, int ignore_styles,
-    const char *fmt, ...)
+    int ignore_keys, const char *fmt, ...)
 {
 	struct timeval	tv;
 	va_list		ap;
@@ -433,7 +433,6 @@ status_message_set(struct client *c, int delay, int ignore_styles,
 	status_push_screen(c);
 
 	va_start(ap, fmt);
-	c->message_ignore_styles = ignore_styles;
 	xvasprintf(&c->message_string, fmt, ap);
 	va_end(ap);
 
@@ -456,6 +455,10 @@ status_message_set(struct client *c, int delay, int ignore_styles,
 		evtimer_add(&c->message_timer, &tv);
 	}
 
+	if (delay != 0)
+		c->message_ignore_keys = ignore_keys;
+	c->message_ignore_styles = ignore_styles;
+
 	c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
 	c->flags |= CLIENT_REDRAWSTATUS;
 }
diff --git a/tmux.1 b/tmux.1
index c79950ce..2343ab89 100644
--- a/tmux.1
+++ b/tmux.1
@@ -1985,12 +1985,17 @@ The default is to capture only the visible contents of the pane.
 .Op Fl NrZ
 .Op Fl F Ar format
 .Op Fl f Ar filter
+.Op Fl K Ar key-format
 .Op Fl O Ar sort-order
 .Op Fl t Ar target-pane
 .Op Ar template
 .Xc
 Put a pane into client mode, allowing a client to be selected interactively from
 a list.
+Each client is shown on one line.
+A shortcut key is shown on the left in brackets allowing for immediate choice,
+or the list may be navigated and an item chosen or otherwise manipulated using
+the keys below.
 .Fl Z
 zooms the pane.
 The following keys may be used in client mode:
@@ -2040,7 +2045,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero,
 the item in the list is not shown, otherwise it is shown.
 If a filter would lead to an empty list, it is ignored.
 .Fl F
-specifies the format for each item in the list.
+specifies the format for each item in the list and
+.Fl K
+a format for each shortcut key; both are evaluated once for each line.
 .Fl N
 starts without the preview.
 This command works only if at least one client is attached.
@@ -2049,12 +2056,17 @@ This command works only if at least one client is attached.
 .Op Fl GNrswZ
 .Op Fl F Ar format
 .Op Fl f Ar filter
+.Op Fl K Ar key-format
 .Op Fl O Ar sort-order
 .Op Fl t Ar target-pane
 .Op Ar template
 .Xc
 Put a pane into tree mode, where a session, window or pane may be chosen
-interactively from a list.
+interactively from a tree.
+Each session, window or pane is shown on one line.
+A shortcut key is shown on the left in brackets allowing for immediate choice,
+or the tree may be navigated and an item chosen or otherwise manipulated using
+the keys below.
 .Fl s
 starts with sessions collapsed and
 .Fl w
@@ -2113,7 +2125,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero,
 the item in the list is not shown, otherwise it is shown.
 If a filter would lead to an empty list, it is ignored.
 .Fl F
-specifies the format for each item in the tree.
+specifies the format for each item in the tree and
+.Fl K
+a format for each shortcut key; both are evaluated once for each line.
 .Fl N
 starts without the preview.
 .Fl G
@@ -3149,8 +3163,8 @@ The appearance and behaviour of
 may be modified by changing the value of various options.
 There are four types of option:
 .Em server options ,
-.Em session options
-.Em window options
+.Em session options ,
+.Em window options ,
 and
 .Em pane options .
 .Pp
@@ -4663,6 +4677,11 @@ For example,
 multiplies 5.5 by 3 for a result with four decimal places and
 .Ql #{e|%%:7,3}
 returns the modulus of 7 and 3.
+.Ql a
+replaces a numeric argument by its ASCII equivalent, so
+.Ql #{a:98}
+results in
+.Ql b .
 .Pp
 A limit may be placed on the length of the resultant string by prefixing it
 by an
@@ -4859,6 +4878,7 @@ The following variables are available, where appropriate:
 .It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode"
 .It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode"
 .It Li "copy_cursor_y" Ta "" Ta "Cursor Y position in copy mode"
+.It Li "current_file" Ta "" Ta "Current configuration file"
 .It Li "cursor_character" Ta "" Ta "Character at cursor in pane"
 .It Li "cursor_flag" Ta "" Ta "Pane cursor flag"
 .It Li "cursor_x" Ta "" Ta "Cursor X position in pane"
@@ -5545,7 +5565,7 @@ The following keys are also available:
 .It Li "q" Ta "Exit menu"
 .El
 .It Xo Ic display-message
-.Op Fl aIpv
+.Op Fl aINpv
 .Op Fl c Ar target-client
 .Op Fl d Ar delay
 .Op Fl t Ar target-pane
@@ -5565,6 +5585,8 @@ If
 is not given, the
 .Ic message-time
 option is used; a delay of zero waits for a key press.
+.Ql N
+ignores key presses and closes only after the delay expires.
 The format of
 .Ar message
 is described in the
@@ -5680,12 +5702,17 @@ The buffer commands are as follows:
 .Op Fl NZr
 .Op Fl F Ar format
 .Op Fl f Ar filter
+.Op Fl K Ar key-format
 .Op Fl O Ar sort-order
 .Op Fl t Ar target-pane
 .Op Ar template
 .Xc
 Put a pane into buffer mode, where a buffer may be chosen interactively from
 a list.
+Each buffer is shown on one line.
+A shortcut key is shown on the left in brackets allowing for immediate choice,
+or the list may be navigated and an item chosen or otherwise manipulated using
+the keys below.
 .Fl Z
 zooms the pane.
 The following keys may be used in buffer mode:
@@ -5733,7 +5760,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero,
 the item in the list is not shown, otherwise it is shown.
 If a filter would lead to an empty list, it is ignored.
 .Fl F
-specifies the format for each item in the list.
+specifies the format for each item in the list and
+.Fl K
+a format for each shortcut key; both are evaluated once for each line.
 .Fl N
 starts without the preview.
 This command works only if at least one client is attached.
@@ -6107,6 +6136,8 @@ A notification will never occur inside an output block.
 .Pp
 The following notifications are defined:
 .Bl -tag -width Ds
+.It Ic %client-detached Ar client
+The client has detached.
 .It Ic %client-session-changed Ar client session-id name
 The client is now attached to the session with ID
 .Ar session-id ,
diff --git a/tmux.h b/tmux.h
index ca71559f..6be5b302 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1688,6 +1688,7 @@ struct client {
 
 	uint64_t	 redraw_panes;
 
+	int		 message_ignore_keys;
 	int		 message_ignore_styles;
 	char		*message_string;
 	struct event	 message_timer;
@@ -2491,7 +2492,7 @@ struct style_range *status_get_range(struct client *, u_int, u_int);
 void	 status_init(struct client *);
 void	 status_free(struct client *);
 int	 status_redraw(struct client *);
-void status_message_set(struct client *, int, int, const char *, ...);
+void status_message_set(struct client *, int, int, int, const char *, ...);
 void	 status_message_clear(struct client *);
 int	 status_message_redraw(struct client *);
 void	 status_prompt_set(struct client *, struct cmd_find_state *,
@@ -2584,7 +2585,7 @@ void	 grid_reader_get_cursor(struct grid_reader *, u_int *, u_int *);
 u_int	 grid_reader_line_length(struct grid_reader *);
 int	 grid_reader_in_set(struct grid_reader *, const char *);
 void	 grid_reader_cursor_right(struct grid_reader *, int, int);
-void	 grid_reader_cursor_left(struct grid_reader *);
+void	 grid_reader_cursor_left(struct grid_reader *, int);
 void	 grid_reader_cursor_down(struct grid_reader *);
 void	 grid_reader_cursor_up(struct grid_reader *);
 void	 grid_reader_cursor_start_of_line(struct grid_reader *, int);
@@ -2597,6 +2598,7 @@ int	 grid_reader_cursor_jump(struct grid_reader *,
 	     const struct utf8_data *);
 int	 grid_reader_cursor_jump_back(struct grid_reader *,
 	     const struct utf8_data *);
+void	 grid_reader_cursor_back_to_indentation(struct grid_reader *);
 
 /* grid-view.c */
 void	 grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *);
@@ -2855,6 +2857,7 @@ typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *,
 typedef int (*mode_tree_search_cb)(void *, void *, const char *);
 typedef void (*mode_tree_menu_cb)(void *, struct client *, key_code);
 typedef u_int (*mode_tree_height_cb)(void *, u_int);
+typedef key_code (*mode_tree_key_cb)(void *, void *, u_int);
 typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code);
 u_int	 mode_tree_count_tagged(struct mode_tree_data *);
 void	*mode_tree_get_current(struct mode_tree_data *);
@@ -2869,7 +2872,7 @@ void	 mode_tree_up(struct mode_tree_data *, int);
 void	 mode_tree_down(struct mode_tree_data *, int);
 struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *,
 	     mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb,
-	     mode_tree_menu_cb, mode_tree_height_cb, void *,
+	     mode_tree_menu_cb, mode_tree_height_cb, mode_tree_key_cb, void *,
 	     const struct menu_item *, const char **, u_int, struct screen **);
 void	 mode_tree_zoom(struct mode_tree_data *, struct args *);
 void	 mode_tree_build(struct mode_tree_data *);
@@ -2946,6 +2949,7 @@ void	control_notify_window_unlinked(struct session *, struct window *);
 void	control_notify_window_linked(struct session *, struct window *);
 void	control_notify_window_renamed(struct window *);
 void	control_notify_client_session_changed(struct client *);
+void	control_notify_client_detached(struct client *);
 void	control_notify_session_renamed(struct session *);
 void	control_notify_session_created(struct session *);
 void	control_notify_session_closed(struct session *);
diff --git a/tty-keys.c b/tty-keys.c
index 59426772..e88ff227 100644
--- a/tty-keys.c
+++ b/tty-keys.c
@@ -61,7 +61,7 @@ static int	tty_keys_extended_device_attributes(struct tty *, const char *,
 /* Default raw keys. */
 struct tty_default_key_raw {
 	const char	       *string;
-	key_code	 	key;
+	key_code		key;
 };
 static const struct tty_default_key_raw tty_default_raw_keys[] = {
 	/* Application escape. */
@@ -262,7 +262,7 @@ static const key_code tty_default_xterm_modifiers[] = {
  */
 struct tty_default_key_code {
 	enum tty_code_code	code;
-	key_code	 	key;
+	key_code		key;
 };
 static const struct tty_default_key_code tty_default_code_keys[] = {
 	/* Function keys. */
@@ -420,7 +420,7 @@ tty_keys_add(struct tty *tty, const char *s, key_code key)
 {
 	struct tty_key	*tk;
 	size_t		 size;
-	const char     	*keystr;
+	const char	*keystr;
 
 	keystr = key_string_lookup_key(key, 1);
 	if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) {
@@ -477,7 +477,7 @@ tty_keys_build(struct tty *tty)
 	const struct tty_default_key_raw	*tdkr;
 	const struct tty_default_key_xterm	*tdkx;
 	const struct tty_default_key_code	*tdkc;
-	u_int		 			 i, j;
+	u_int					 i, j;
 	const char				*s;
 	struct options_entry			*o;
 	struct options_array_item		*a;
@@ -869,6 +869,9 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
 	size_t		 end;
 	u_int		 number, modifiers;
 	char		 tmp[64];
+	cc_t		 bspace;
+	key_code	 nkey;
+	key_code	 onlykey;
 
 	*size = 0;
 
@@ -911,38 +914,68 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len,
 	}
 	*size = end + 1;
 
-	/* Store the key and modifiers. */
-	*key = number;
+	/* Store the key. */
+	bspace = tty->tio.c_cc[VERASE];
+	if (bspace != _POSIX_VDISABLE && number == bspace)
+		nkey = KEYC_BSPACE;
+	else
+		nkey = number;
+
+	/* Update the modifiers. */
 	switch (modifiers) {
 	case 2:
-		(*key) |= KEYC_SHIFT;
+		nkey |= KEYC_SHIFT;
 		break;
 	case 3:
-		(*key) |= (KEYC_META|KEYC_IMPLIED_META);
+		nkey |= (KEYC_META|KEYC_IMPLIED_META);
 		break;
 	case 4:
-		(*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META);
+		nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META);
 		break;
 	case 5:
-		(*key) |= KEYC_CTRL;
+		nkey |= KEYC_CTRL;
 		break;
 	case 6:
-		(*key) |= (KEYC_SHIFT|KEYC_CTRL);
+		nkey |= (KEYC_SHIFT|KEYC_CTRL);
 		break;
 	case 7:
-		(*key) |= (KEYC_META|KEYC_CTRL);
+		nkey |= (KEYC_META|KEYC_CTRL);
 		break;
 	case 8:
-		(*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL);
+		nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL);
 		break;
 	default:
 		*key = KEYC_NONE;
 		break;
 	}
+
+	/*
+	 * Don't allow both KEYC_CTRL and as an implied modifier. Also convert
+	 * C-X into C-x and so on.
+	 */
+	if (nkey & KEYC_CTRL){
+		onlykey = (nkey & KEYC_MASK_KEY);
+		if (onlykey < 32)
+			onlykey = (nkey & ~KEYC_CTRL);
+		else {
+			if (onlykey >= 97 && onlykey <= 122)
+				onlykey -= 96;
+			else if (onlykey >= 64 && onlykey <= 95)
+				onlykey -= 64;
+			else if (onlykey == 32)
+				onlykey = 0;
+			else if (onlykey == 63)
+				onlykey = 127;
+			onlykey |= ((nkey & KEYC_MASK_MODIFIERS) & ~KEYC_CTRL);
+		}
+		nkey = onlykey;
+	}
+
 	if (log_get_level() != 0) {
 		log_debug("%s: extended key %.*s is %llx (%s)", c->name,
-		    (int)*size, buf, *key, key_string_lookup_key(*key, 1));
+		    (int)*size, buf, nkey, key_string_lookup_key(nkey, 1));
 	}
+	*key = nkey;
 	return (0);
 }
 
diff --git a/tty.c b/tty.c
index 399bbae8..e8a8cbaa 100644
--- a/tty.c
+++ b/tty.c
@@ -670,19 +670,10 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s)
 	if (changed != 0)
 		log_debug("%s: update mode %x to %x", c->name, tty->mode, mode);
 
-	if (changed & MODE_BLINKING) {
-		if (tty_term_has(tty->term, TTYC_CVVIS))
-			tty_putcode(tty, TTYC_CVVIS);
-		else
-			tty_putcode(tty, TTYC_CNORM);
-		changed |= MODE_CURSOR;
-	}
-	if (changed & MODE_CURSOR) {
-		if (mode & MODE_CURSOR)
-			tty_putcode(tty, TTYC_CNORM);
-		else
-			tty_putcode(tty, TTYC_CIVIS);
-	}
+	/*
+	 * The cursor blinking flag can be reset by setting the cursor style, so
+	 * set the style first.
+	 */
 	if (s != NULL && tty->cstyle != s->cstyle) {
 		if (tty_term_has(tty->term, TTYC_SS)) {
 			if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_SE))
@@ -691,7 +682,28 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s)
 				tty_putcode1(tty, TTYC_SS, s->cstyle);
 		}
 		tty->cstyle = s->cstyle;
+		changed |= (MODE_CURSOR|MODE_BLINKING);
 	}
+
+	/*
+	 * Cursor invisible (RM ?25) overrides cursor blinking (SM ?12 or RM
+	 * 34), and we need to be careful not send cnorm after cvvis since it
+	 * can undo it.
+	 */
+	if (changed & (MODE_CURSOR|MODE_BLINKING)) {
+		log_debug("%s: cursor %s, %sblinking", __func__,
+		    (mode & MODE_CURSOR) ? "on" : "off",
+		    (mode & MODE_BLINKING) ? "" : "not ");
+		if (~mode & MODE_CURSOR)
+			tty_putcode(tty, TTYC_CIVIS);
+		else if (mode & MODE_BLINKING) {
+			tty_putcode(tty, TTYC_CNORM);
+			if (tty_term_has(tty->term, TTYC_CVVIS))
+				tty_putcode(tty, TTYC_CVVIS);
+		} else
+			tty_putcode(tty, TTYC_CNORM);
+	}
+
 	if ((changed & ALL_MOUSE_MODES) &&
 	    tty_term_has(tty->term, TTYC_KMOUS)) {
 		/*
diff --git a/window-buffer.c b/window-buffer.c
index acdcb525..8d93558a 100644
--- a/window-buffer.c
+++ b/window-buffer.c
@@ -41,6 +41,17 @@ static void		 window_buffer_key(struct window_mode_entry *,
 #define WINDOW_BUFFER_DEFAULT_FORMAT \
 	"#{t/p:buffer_created}: #{buffer_sample}"
 
+#define WINDOW_BUFFER_DEFAULT_KEY_FORMAT \
+	"#{?#{e|<:#{line},10}," \
+		"#{line}" \
+	"," \
+		"#{?#{e|<:#{line},36},"	\
+	        	"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
+		"," \
+	        	"" \
+		"}" \
+	"}"
+
 static const struct menu_item window_buffer_menu_items[] = {
 	{ "Paste", 'p', NULL },
 	{ "Paste Tagged", 'P', NULL },
@@ -93,6 +104,7 @@ struct window_buffer_modedata {
 	struct mode_tree_data		 *data;
 	char				 *command;
 	char				 *format;
+	char				 *key_format;
 
 	struct window_buffer_itemdata	**item_list;
 	u_int				  item_size;
@@ -232,7 +244,8 @@ window_buffer_draw(__unused void *modedata, void *itemdata,
 		while (end != pdata + psize && *end != '\n')
 			end++;
 		buf = xreallocarray(buf, 4, end - start + 1);
-		utf8_strvis(buf, start, end - start, VIS_OCTAL|VIS_CSTYLE|VIS_TAB);
+		utf8_strvis(buf, start, end - start,
+		    VIS_OCTAL|VIS_CSTYLE|VIS_TAB);
 		if (*buf != '\0') {
 			screen_write_cursormove(ctx, cx, cy + i, 0);
 			screen_write_nputs(ctx, sx, &grid_default_cell, "%s",
@@ -275,6 +288,41 @@ window_buffer_menu(void *modedata, struct client *c, key_code key)
 	window_buffer_key(wme, c, NULL, NULL, key, NULL);
 }
 
+static key_code
+window_buffer_get_key(void *modedata, void *itemdata, u_int line)
+{
+	struct window_buffer_modedata	*data = modedata;
+	struct window_buffer_itemdata	*item = itemdata;
+	struct format_tree		*ft;
+	struct session			*s;
+	struct winlink			*wl;
+	struct window_pane		*wp;
+	struct paste_buffer		*pb;
+	char				*expanded;
+	key_code			 key;
+
+	if (cmd_find_valid_state(&data->fs)) {
+		s = data->fs.s;
+		wl = data->fs.wl;
+		wp = data->fs.wp;
+	}
+	pb = paste_get_name(item->name);
+	if (pb == NULL)
+		return KEYC_NONE;
+
+	ft = format_create(NULL, NULL, FORMAT_NONE, 0);
+	format_defaults(ft, NULL, NULL, 0, NULL);
+	format_defaults(ft, NULL, s, wl, wp);
+	format_defaults_paste_buffer(ft, pb);
+	format_add(ft, "line", "%u", line);
+
+	expanded = format_expand(ft, data->key_format);
+	key = key_string_lookup_string(expanded);
+	free(expanded);
+	format_free(ft);
+	return key;
+}
+
 static struct screen *
 window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
     struct args *args)
@@ -291,6 +339,10 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
 		data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT);
 	else
 		data->format = xstrdup(args_get(args, 'F'));
+	if (args == NULL || !args_has(args, 'K'))
+		data->key_format = xstrdup(WINDOW_BUFFER_DEFAULT_KEY_FORMAT);
+	else
+		data->key_format = xstrdup(args_get(args, 'K'));
 	if (args == NULL || args->argc == 0)
 		data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND);
 	else
@@ -298,8 +350,8 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
 
 	data->data = mode_tree_start(wp, args, window_buffer_build,
 	    window_buffer_draw, window_buffer_search, window_buffer_menu, NULL,
-	    data, window_buffer_menu_items, window_buffer_sort_list,
-	    nitems(window_buffer_sort_list), &s);
+	    window_buffer_get_key, data, window_buffer_menu_items,
+	    window_buffer_sort_list, nitems(window_buffer_sort_list), &s);
 	mode_tree_zoom(data->data, args);
 
 	mode_tree_build(data->data);
@@ -324,6 +376,7 @@ window_buffer_free(struct window_mode_entry *wme)
 	free(data->item_list);
 
 	free(data->format);
+	free(data->key_format);
 	free(data->command);
 
 	free(data);
diff --git a/window-client.c b/window-client.c
index ec3c646a..db7c6dcc 100644
--- a/window-client.c
+++ b/window-client.c
@@ -40,6 +40,17 @@ static void		 window_client_key(struct window_mode_entry *,
 #define WINDOW_CLIENT_DEFAULT_FORMAT \
 	"#{t/p:client_activity}: session #{session_name}"
 
+#define WINDOW_CLIENT_DEFAULT_KEY_FORMAT \
+	"#{?#{e|<:#{line},10}," \
+		"#{line}" \
+	"," \
+		"#{?#{e|<:#{line},36},"	\
+	        	"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
+		"," \
+	        	"" \
+		"}" \
+	"}"
+
 static const struct menu_item window_client_menu_items[] = {
 	{ "Detach", 'd', NULL },
 	{ "Detach Tagged", 'D', NULL },
@@ -87,6 +98,7 @@ struct window_client_modedata {
 
 	struct mode_tree_data		 *data;
 	char				 *format;
+	char				 *key_format;
 	char				 *command;
 
 	struct window_client_itemdata	**item_list;
@@ -252,6 +264,26 @@ window_client_menu(void *modedata, struct client *c, key_code key)
 	window_client_key(wme, c, NULL, NULL, key, NULL);
 }
 
+static key_code
+window_client_get_key(void *modedata, void *itemdata, u_int line)
+{
+	struct window_client_modedata	*data = modedata;
+	struct window_client_itemdata	*item = itemdata;
+	struct format_tree		*ft;
+	char				*expanded;
+	key_code			 key;
+
+	ft = format_create(NULL, NULL, FORMAT_NONE, 0);
+	format_defaults(ft, item->c, NULL, 0, NULL);
+	format_add(ft, "line", "%u", line);
+
+	expanded = format_expand(ft, data->key_format);
+	key = key_string_lookup_string(expanded);
+	free(expanded);
+	format_free(ft);
+	return key;
+}
+
 static struct screen *
 window_client_init(struct window_mode_entry *wme,
     __unused struct cmd_find_state *fs, struct args *args)
@@ -267,15 +299,19 @@ window_client_init(struct window_mode_entry *wme,
 		data->format = xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT);
 	else
 		data->format = xstrdup(args_get(args, 'F'));
+	if (args == NULL || !args_has(args, 'K'))
+		data->key_format = xstrdup(WINDOW_CLIENT_DEFAULT_KEY_FORMAT);
+	else
+		data->key_format = xstrdup(args_get(args, 'K'));
 	if (args == NULL || args->argc == 0)
 		data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND);
 	else
 		data->command = xstrdup(args->argv[0]);
 
 	data->data = mode_tree_start(wp, args, window_client_build,
-	    window_client_draw, NULL, window_client_menu, NULL, data,
-	    window_client_menu_items, window_client_sort_list,
-	    nitems(window_client_sort_list), &s);
+	    window_client_draw, NULL, window_client_menu, NULL,
+	    window_client_get_key, data, window_client_menu_items,
+	    window_client_sort_list, nitems(window_client_sort_list), &s);
 	mode_tree_zoom(data->data, args);
 
 	mode_tree_build(data->data);
@@ -300,6 +336,7 @@ window_client_free(struct window_mode_entry *wme)
 	free(data->item_list);
 
 	free(data->format);
+	free(data->key_format);
 	free(data->command);
 
 	free(data);
diff --git a/window-copy.c b/window-copy.c
index 3fc7ad3e..423cce8f 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -64,6 +64,8 @@ static int	window_copy_search_rl(struct grid *, struct grid *, u_int *,
 static int	window_copy_last_regex(struct grid *, u_int, u_int, u_int,
 		    u_int, u_int *, u_int *, const char *, const regex_t *,
 		    int);
+static int	window_copy_search_mark_at(struct window_copy_mode_data *,
+		    u_int, u_int, u_int *);
 static char    *window_copy_stringify(struct grid *, u_int, u_int, u_int,
 		    char *, u_int *);
 static void	window_copy_cstrtocellpos(struct grid *, u_int, u_int *,
@@ -71,16 +73,15 @@ static void	window_copy_cstrtocellpos(struct grid *, u_int, u_int *,
 static int	window_copy_search_marks(struct window_mode_entry *,
 		    struct screen *, int, int);
 static void	window_copy_clear_marks(struct window_mode_entry *);
-static void	window_copy_move_left(struct screen *, u_int *, u_int *, int);
 static int	window_copy_is_lowercase(const char *);
 static void	window_copy_search_back_overlap(struct grid *, regex_t *,
 		    u_int *, u_int *, u_int *, u_int);
 static int	window_copy_search_jump(struct window_mode_entry *,
 		    struct grid *, struct grid *, u_int, u_int, u_int, int, int,
-		    int, int, u_int *);
-static int	window_copy_search(struct window_mode_entry *, int, int, int);
-static int	window_copy_search_up(struct window_mode_entry *, int, int);
-static int	window_copy_search_down(struct window_mode_entry *, int, int);
+		    int, int);
+static int	window_copy_search(struct window_mode_entry *, int, int);
+static int	window_copy_search_up(struct window_mode_entry *, int);
+static int	window_copy_search_down(struct window_mode_entry *, int);
 static void	window_copy_goto_line(struct window_mode_entry *, const char *);
 static void	window_copy_update_cursor(struct window_mode_entry *, u_int,
 		    u_int);
@@ -275,6 +276,7 @@ struct window_copy_mode_data {
 	int		 showmark;
 
 	int		 searchtype;
+	int		 searchdirection;
 	int		 searchregex;
 	char		*searchstr;
 	u_char		*searchmark;
@@ -1718,10 +1720,10 @@ window_copy_cmd_search_again(struct window_copy_cmd_state *cs)
 
 	if (data->searchtype == WINDOW_COPY_SEARCHUP) {
 		for (; np != 0; np--)
-			window_copy_search_up(wme, data->searchregex, 1);
+			window_copy_search_up(wme, data->searchregex);
 	} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
 		for (; np != 0; np--)
-			window_copy_search_down(wme, data->searchregex, 1);
+			window_copy_search_down(wme, data->searchregex);
 	}
 	return (WINDOW_COPY_CMD_NOTHING);
 }
@@ -1735,10 +1737,10 @@ window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs)
 
 	if (data->searchtype == WINDOW_COPY_SEARCHUP) {
 		for (; np != 0; np--)
-			window_copy_search_down(wme, data->searchregex, 1);
+			window_copy_search_down(wme, data->searchregex);
 	} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
 		for (; np != 0; np--)
-			window_copy_search_up(wme, data->searchregex, 1);
+			window_copy_search_up(wme, data->searchregex);
 	}
 	return (WINDOW_COPY_CMD_NOTHING);
 }
@@ -2042,7 +2044,7 @@ window_copy_cmd_search_backward(struct window_copy_cmd_state *cs)
 		data->searchregex = 1;
 		data->timeout = 0;
 		for (; np != 0; np--)
-			window_copy_search_up(wme, 1, 0);
+			window_copy_search_up(wme, 1);
 	}
 	return (WINDOW_COPY_CMD_NOTHING);
 }
@@ -2062,7 +2064,7 @@ window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs)
 		data->searchregex = 0;
 		data->timeout = 0;
 		for (; np != 0; np--)
-			window_copy_search_up(wme, 0, 0);
+			window_copy_search_up(wme, 0);
 	}
 	return (WINDOW_COPY_CMD_NOTHING);
 }
@@ -2082,7 +2084,7 @@ window_copy_cmd_search_forward(struct window_copy_cmd_state *cs)
 		data->searchregex = 1;
 		data->timeout = 0;
 		for (; np != 0; np--)
-			window_copy_search_down(wme, 1, 0);
+			window_copy_search_down(wme, 1);
 	}
 	return (WINDOW_COPY_CMD_NOTHING);
 }
@@ -2102,7 +2104,7 @@ window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs)
 		data->searchregex = 0;
 		data->timeout = 0;
 		for (; np != 0; np--)
-			window_copy_search_down(wme, 0, 0);
+			window_copy_search_down(wme, 0);
 	}
 	return (WINDOW_COPY_CMD_NOTHING);
 }
@@ -2143,7 +2145,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
 		data->searchregex = 0;
 		free(data->searchstr);
 		data->searchstr = xstrdup(argument);
-		if (!window_copy_search_up(wme, 0, 1)) {
+		if (!window_copy_search_up(wme, 0)) {
 			window_copy_clear_marks(wme);
 			return (WINDOW_COPY_CMD_REDRAW);
 		}
@@ -2153,7 +2155,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
 		data->searchregex = 0;
 		free(data->searchstr);
 		data->searchstr = xstrdup(argument);
-		if (!window_copy_search_down(wme, 0, 0)) {
+		if (!window_copy_search_down(wme, 0)) {
 			window_copy_clear_marks(wme);
 			return (WINDOW_COPY_CMD_REDRAW);
 		}
@@ -2198,7 +2200,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
 		data->searchregex = 0;
 		free(data->searchstr);
 		data->searchstr = xstrdup(argument);
-		if (!window_copy_search_down(wme, 0, 1)) {
+		if (!window_copy_search_down(wme, 0)) {
 			window_copy_clear_marks(wme);
 			return (WINDOW_COPY_CMD_REDRAW);
 		}
@@ -2208,7 +2210,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
 		data->searchregex = 0;
 		free(data->searchstr);
 		data->searchstr = xstrdup(argument);
-		if (!window_copy_search_up(wme, 0, 1)) {
+		if (!window_copy_search_up(wme, 0)) {
 			window_copy_clear_marks(wme);
 			return (WINDOW_COPY_CMD_REDRAW);
 		}
@@ -2917,6 +2919,23 @@ window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
 		*fx = *fx - 1;
 }
 
+static void
+window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
+{
+	if (*fx == screen_size_x(s) - 1) { /* right */
+		if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */
+			if (wrapflag) {
+				*fx = 0;
+				*fy = 0;
+			}
+			return;
+		}
+		*fx = 0;
+		*fy = *fy + 1;
+	} else
+		*fx = *fx + 1;
+}
+
 static int
 window_copy_is_lowercase(const char *ptr)
 {
@@ -2979,7 +2998,7 @@ window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx,
 static int
 window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
     struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
-    int direction, int regex, u_int *foundlen)
+    int direction, int regex)
 {
 	u_int	 i, px, sx, ssize = 1;
 	int	 found = 0, cflags = REG_EXTENDED;
@@ -3004,20 +3023,15 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
 			if (regex) {
 				found = window_copy_search_lr_regex(gd,
 				    &px, &sx, i, fx, gd->sx, &reg);
-				if (found)
-					*foundlen = sx;
 			} else {
 				found = window_copy_search_lr(gd, sgd,
 				    &px, i, fx, gd->sx, cis);
-				if (found)
-					*foundlen = sgd->sx;
 			}
 			if (found)
 				break;
 			fx = 0;
 		}
 	} else {
-		*foundlen = 0;
 		for (i = fy + 1; endline < i; i--) {
 			if (regex) {
 				found = window_copy_search_rl_regex(gd,
@@ -3048,18 +3062,40 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
 		return (window_copy_search_jump(wme, gd, sgd,
 		    direction ? 0 : gd->sx - 1,
 		    direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
-		    direction, regex, foundlen));
+		    direction, regex));
 	}
 	return (0);
 }
 
+static void
+window_copy_move_after_search_mark(struct window_copy_mode_data *data,
+    u_int *fx, u_int *fy, int wrapflag)
+{
+	struct screen  *s = data->backing;
+	u_int		at, start;
+
+	if (window_copy_search_mark_at(data, *fx, *fy, &start) == 0 &&
+	    data->searchmark[start] != 0) {
+		while (window_copy_search_mark_at(data, *fx, *fy, &at) == 0) {
+			if (data->searchmark[at] != data->searchmark[start])
+				break;
+			/* Stop if not wrapping and at the end of the grid. */
+			if (!wrapflag &&
+			    *fx == screen_size_x(s) - 1 &&
+			    *fy == screen_hsize(s) + screen_size_y(s) - 1)
+				break;
+
+			window_copy_move_right(s, fx, fy, wrapflag);
+		}
+	}
+}
+
 /*
  * Search in for text searchstr. If direction is 0 then search up, otherwise
  * down.
  */
 static int
-window_copy_search(struct window_mode_entry *wme, int direction, int regex,
-    int again)
+window_copy_search(struct window_mode_entry *wme, int direction, int regex)
 {
 	struct window_pane		*wp = wme->wp;
 	struct window_copy_mode_data	*data = wme->data;
@@ -3067,12 +3103,15 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex,
 	struct screen_write_ctx		 ctx;
 	struct grid			*gd = s->grid;
 	const char			*str = data->searchstr;
-	u_int				 fx, fy, endline, i, foundlen;
-	int				 wrapflag, cis, found, visible_only;
+	u_int				 at, endline, fx, fy, start;
+	int				 cis, found, keys, visible_only;
+	int				 wrapflag;
 
 	if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0')
 		regex = 0;
 
+	data->searchdirection = direction;
+
 	if (data->timeout)
 		return (0);
 
@@ -3097,27 +3136,94 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex,
 	wrapflag = options_get_number(wp->window->options, "wrap-search");
 	cis = window_copy_is_lowercase(str);
 
-	if (direction)
+	keys = options_get_number(wp->window->options, "mode-keys");
+
+	if (direction) {
+		/*
+		 * Behave according to mode-keys. If it is emacs, search forward
+		 * leaves the cursor after the match. If it is vi, the cursor
+		 * remains at the beginning of the match, regardless of
+		 * direction, which means that we need to start the next search
+		 * after the term the cursor is currently on when searching
+		 * forward.
+		 */
+		if (keys == MODEKEY_VI) {
+			if (data->searchmark != NULL)
+				window_copy_move_after_search_mark(data, &fx,
+				    &fy, wrapflag);
+			else {
+				/*
+				 * When there are no search marks, start the
+				 * search after the current cursor position.
+				 */
+				window_copy_move_right(s, &fx, &fy, wrapflag);
+			}
+		}
 		endline = gd->hsize + gd->sy - 1;
+	}
 	else {
-		if (again)
-			window_copy_move_left(s, &fx, &fy, wrapflag);
+		window_copy_move_left(s, &fx, &fy, wrapflag);
 		endline = 0;
 	}
 
 	found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis,
-	    wrapflag, direction, regex, &foundlen);
+	    wrapflag, direction, regex);
 	if (found) {
 		window_copy_search_marks(wme, &ss, regex, visible_only);
-		if (foundlen != 0) {
-			/* Adjust for wrapped lines eating one right. */
-			i = data->cx + foundlen;
-			while (i > gd->sx - 1) {
-				i -= gd->sx;
-				window_copy_cursor_right(wme, 1);
+		fx = data->cx;
+		fy = screen_hsize(data->backing) - data->oy + data->cy;
+
+		/*
+		 * When searching forward, if the cursor is not at the beginning
+		 * of the mark, search again.
+		 */
+		if (direction &&
+		    window_copy_search_mark_at(data, fx, fy, &at) == 0 &&
+		    at > 0 &&
+		    data->searchmark[at] == data->searchmark[at - 1]) {
+			window_copy_move_after_search_mark(data, &fx, &fy,
+			    wrapflag);
+			window_copy_search_jump(wme, gd, ss.grid, fx,
+			    fy, endline, cis, wrapflag, direction,
+			    regex);
+			fx = data->cx;
+			fy = screen_hsize(data->backing) - data->oy + data->cy;
+		}
+
+		if (direction) {
+			/*
+			 * When in Emacs mode, position the cursor just after
+			 * the mark.
+			 */
+			if (keys == MODEKEY_EMACS) {
+				window_copy_move_after_search_mark(data, &fx,
+				    &fy, wrapflag);
+				data->cx = fx;
+				data->cy = fy - screen_hsize(data->backing) +
+				    data-> oy;
+			}
+		}
+		else {
+			/*
+			 * When searching backward, position the cursor at the
+			 * beginning of the mark.
+			 */
+			if (window_copy_search_mark_at(data, fx, fy,
+			        &start) == 0) {
+				while (window_copy_search_mark_at(data, fx, fy,
+				           &at) == 0 &&
+				       data->searchmark[at] ==
+				           data->searchmark[start]) {
+					data->cx = fx;
+					data->cy = fy -
+					    screen_hsize(data->backing) +
+					    data-> oy;
+					if (at == 0)
+						break;
+
+					window_copy_move_left(s, &fx, &fy, 0);
+				}
 			}
-			for (i = 0; i < foundlen; i++)
-				window_copy_cursor_right(wme, 1);
 		}
 	}
 	window_copy_redraw_screen(wme);
@@ -3302,15 +3408,15 @@ window_copy_clear_marks(struct window_mode_entry *wme)
 }
 
 static int
-window_copy_search_up(struct window_mode_entry *wme, int regex, int again)
+window_copy_search_up(struct window_mode_entry *wme, int regex)
 {
-	return (window_copy_search(wme, 0, regex, again));
+	return (window_copy_search(wme, 0, regex));
 }
 
 static int
-window_copy_search_down(struct window_mode_entry *wme, int regex, int again)
+window_copy_search_down(struct window_mode_entry *wme, int regex)
 {
-	return (window_copy_search(wme, 1, regex, again));
+	return (window_copy_search(wme, 1, regex));
 }
 
 static void
@@ -3396,9 +3502,11 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
     struct grid_cell *gc, const struct grid_cell *mgc,
     const struct grid_cell *cgc, const struct grid_cell *mkgc)
 {
+	struct window_pane		*wp = wme->wp;
 	struct window_copy_mode_data	*data = wme->data;
 	u_int				 mark, start, end, cy, cursor, current;
 	int				 inv = 0, found = 0;
+	int				 keys;
 
 	if (data->showmark && fy == data->my) {
 		gc->attr = mkgc->attr;
@@ -3425,13 +3533,16 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
 
 	cy = screen_hsize(data->backing) - data->oy + data->cy;
 	if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) {
-		if (data->searchmark[cursor] == mark)
-			found = 1;
-		else if (cursor != 0) {
-			cursor--;
-			if (data->searchmark[cursor] == mark)
+		keys = options_get_number(wp->window->options, "mode-keys");
+		if (cursor != 0 &&
+		    keys == MODEKEY_EMACS &&
+		    data->searchdirection) {
+			if (data->searchmark[cursor - 1] == mark) {
+				cursor--;
 				found = 1;
-		}
+			}
+		} else if (data->searchmark[cursor] == mark)
+			found = 1;
 		if (found) {
 			window_copy_match_start_end(data, cursor, &start, &end);
 			if (current >= start && current <= end) {
@@ -4180,23 +4291,19 @@ static void
 window_copy_cursor_back_to_indentation(struct window_mode_entry *wme)
 {
 	struct window_copy_mode_data	*data = wme->data;
-	u_int				 px, py, xx;
-	struct grid_cell		 gc;
+	struct screen			*back_s = data->backing;
+	struct grid_reader		 gr;
+	u_int				 px, py, oldy, hsize;
 
-	px = 0;
-	py = screen_hsize(data->backing) + data->cy - data->oy;
-	xx = window_copy_find_length(wme, py);
+	px = data->cx;
+	hsize = screen_hsize(back_s);
+	py = hsize + data->cy - data->oy;
+	oldy = data->cy;
 
-	while (px < xx) {
-		grid_get_cell(data->backing->grid, px, py, &gc);
-		if (gc.data.size != 1 || *gc.data.data != ' ')
-			break;
-		px++;
-	}
-
-	window_copy_update_cursor(wme, px, data->cy);
-	if (window_copy_update_selection(wme, 1, 0))
-		window_copy_redraw_lines(wme, data->cy, 1);
+	grid_reader_start(&gr, back_s->grid, px, py);
+	grid_reader_cursor_back_to_indentation(&gr);
+	grid_reader_get_cursor(&gr, &px, &py);
+	window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
 }
 
 static void
@@ -4287,7 +4394,7 @@ window_copy_cursor_left(struct window_mode_entry *wme)
 	oldy = data->cy;
 
 	grid_reader_start(&gr, back_s->grid, px, py);
-	grid_reader_cursor_left(&gr);
+	grid_reader_cursor_left(&gr, 1);
 	grid_reader_get_cursor(&gr, &px, &py);
 	window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
 }
@@ -4472,10 +4579,8 @@ window_copy_cursor_jump_back(struct window_mode_entry *wme)
 	py = hsize + data->cy - data->oy;
 	oldy = data->cy;
 
-	if (px > 0)
-		px--;
-
 	grid_reader_start(&gr, back_s->grid, px, py);
+	grid_reader_cursor_left(&gr, 0);
 	if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
 		grid_reader_get_cursor(&gr, &px, &py);
 		window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
@@ -4498,7 +4603,7 @@ window_copy_cursor_jump_to(struct window_mode_entry *wme)
 
 	grid_reader_start(&gr, back_s->grid, px, py);
 	if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
-		grid_reader_cursor_left(&gr);
+		grid_reader_cursor_left(&gr, 1);
 		grid_reader_get_cursor(&gr, &px, &py);
 		window_copy_acquire_cursor_down(wme, hsize,
 		    screen_size_y(back_s), data->oy, oldy, px, py, 0);
@@ -4518,13 +4623,9 @@ window_copy_cursor_jump_to_back(struct window_mode_entry *wme)
 	py = hsize + data->cy - data->oy;
 	oldy = data->cy;
 
-	if (px > 0)
-		px--;
-
-	if (px > 0)
-		px--;
-
 	grid_reader_start(&gr, back_s->grid, px, py);
+	grid_reader_cursor_left(&gr, 0);
+	grid_reader_cursor_left(&gr, 0);
 	if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
 		grid_reader_cursor_right(&gr, 1, 0);
 		grid_reader_get_cursor(&gr, &px, &py);
@@ -4577,7 +4678,7 @@ window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme,
 		grid_reader_cursor_right(&gr, 0, 0);
 	grid_reader_cursor_next_word_end(&gr, separators);
 	if (keys == MODEKEY_VI)
-		grid_reader_cursor_left(&gr);
+		grid_reader_cursor_left(&gr, 1);
 	grid_reader_get_cursor(&gr, &px, &py);
 	*ppx = px;
 	*ppy = py;
@@ -4607,7 +4708,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme,
 		grid_reader_cursor_right(&gr, 0, 0);
 	grid_reader_cursor_next_word_end(&gr, separators);
 	if (keys == MODEKEY_VI)
-		grid_reader_cursor_left(&gr);
+		grid_reader_cursor_left(&gr, 1);
 	grid_reader_get_cursor(&gr, &px, &py);
 	window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
 	    data->oy, oldy, px, py, no_reset);
diff --git a/window-customize.c b/window-customize.c
index a1f191b5..34a13f73 100644
--- a/window-customize.c
+++ b/window-customize.c
@@ -890,8 +890,8 @@ window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
 
 	data->data = mode_tree_start(wp, args, window_customize_build,
 	    window_customize_draw, NULL, window_customize_menu,
-	    window_customize_height, data, window_customize_menu_items, NULL, 0,
-	    &s);
+	    window_customize_height, NULL, data, window_customize_menu_items,
+	    NULL, 0, &s);
 	mode_tree_zoom(data->data, args);
 
 	mode_tree_build(data->data);
@@ -999,7 +999,7 @@ window_customize_set_option_callback(struct client *c, void *itemdata,
 
 fail:
 	*cause = toupper((u_char)*cause);
-	status_message_set(c, -1, 1, "%s", cause);
+	status_message_set(c, -1, 1, 0, "%s", cause);
 	free(cause);
 	return (0);
 }
@@ -1205,7 +1205,7 @@ window_customize_set_command_callback(struct client *c, void *itemdata,
 
 fail:
 	*error = toupper((u_char)*error);
-	status_message_set(c, -1, 1, "%s", error);
+	status_message_set(c, -1, 1, 0, "%s", error);
 	free(error);
 	return (0);
 }
diff --git a/window-tree.c b/window-tree.c
index cc822ac3..33af0413 100644
--- a/window-tree.c
+++ b/window-tree.c
@@ -56,6 +56,17 @@ static void		 window_tree_key(struct window_mode_entry *,
 		"}" \
 	"}"
 
+#define WINDOW_TREE_DEFAULT_KEY_FORMAT \
+	"#{?#{e|<:#{line},10}," \
+		"#{line}" \
+	"," \
+		"#{?#{e|<:#{line},36},"	\
+	        	"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
+		"," \
+	        	"" \
+		"}" \
+	"}"
+
 static const struct menu_item window_tree_menu_items[] = {
 	{ "Select", '\r', NULL },
 	{ "Expand", KEYC_RIGHT, NULL },
@@ -117,6 +128,7 @@ struct window_tree_modedata {
 
 	struct mode_tree_data		 *data;
 	char				 *format;
+	char				 *key_format;
 	char				 *command;
 	int				  squash_groups;
 
@@ -856,6 +868,35 @@ window_tree_menu(void *modedata, struct client *c, key_code key)
 	window_tree_key(wme, c, NULL, NULL, key, NULL);
 }
 
+static key_code
+window_tree_get_key(void *modedata, void *itemdata, u_int line)
+{
+	struct window_tree_modedata	*data = modedata;
+	struct window_tree_itemdata	*item = itemdata;
+	struct format_tree		*ft;
+	struct session			*s;
+	struct winlink			*wl;
+	struct window_pane		*wp;
+	char				*expanded;
+	key_code			 key;
+
+	ft = format_create(NULL, NULL, FORMAT_NONE, 0);
+	window_tree_pull_item(item, &s, &wl, &wp);
+	if (item->type == WINDOW_TREE_SESSION)
+		format_defaults(ft, NULL, s, NULL, NULL);
+	else if (item->type == WINDOW_TREE_WINDOW)
+		format_defaults(ft, NULL, s, wl, NULL);
+	else
+		format_defaults(ft, NULL, s, wl, wp);
+	format_add(ft, "line", "%u", line);
+
+	expanded = format_expand(ft, data->key_format);
+	key = key_string_lookup_string(expanded);
+	free(expanded);
+	format_free(ft);
+	return key;
+}
+
 static struct screen *
 window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
     struct args *args)
@@ -880,6 +921,10 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
 		data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT);
 	else
 		data->format = xstrdup(args_get(args, 'F'));
+	if (args == NULL || !args_has(args, 'K'))
+		data->key_format = xstrdup(WINDOW_TREE_DEFAULT_KEY_FORMAT);
+	else
+		data->key_format = xstrdup(args_get(args, 'K'));
 	if (args == NULL || args->argc == 0)
 		data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND);
 	else
@@ -887,9 +932,9 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
 	data->squash_groups = !args_has(args, 'G');
 
 	data->data = mode_tree_start(wp, args, window_tree_build,
-	    window_tree_draw, window_tree_search, window_tree_menu, NULL, data,
-	    window_tree_menu_items, window_tree_sort_list,
-	    nitems(window_tree_sort_list), &s);
+	    window_tree_draw, window_tree_search, window_tree_menu, NULL,
+	    window_tree_get_key, data, window_tree_menu_items,
+	    window_tree_sort_list, nitems(window_tree_sort_list), &s);
 	mode_tree_zoom(data->data, args);
 
 	mode_tree_build(data->data);
@@ -913,6 +958,7 @@ window_tree_destroy(struct window_tree_modedata *data)
 	free(data->item_list);
 
 	free(data->format);
+	free(data->key_format);
 	free(data->command);
 
 	free(data);