diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/use-this-issue-template.md
similarity index 82%
rename from .github/ISSUE_TEMPLATE.md
rename to .github/ISSUE_TEMPLATE/use-this-issue-template.md
index 8bf1e66a..bc95fa2e 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE/use-this-issue-template.md
@@ -1,3 +1,12 @@
+---
+name: Use this issue template
+about: Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
+title: ''
+labels: ''
+assignees: ''
+
+---
+
 ### Issue description
 
 Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
diff --git a/.gitignore b/.gitignore
index ec49a6de..bf012d5f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,23 +1,24 @@
-*.o
-*~
-*.diff
-*.patch
 *.core
-core
-tags
+*.dSYM
+*.diff
+*.o
+*.patch
+*.swp
+*~
 .deps/
-compat/.dirstamp
-aclocal.m4
-autom4te.cache/
-config.log
-config.status
-etc/
-tmux
+.dirstamp
 Makefile
 Makefile.in
-configure
-tmux.1.*
-*.dSYM
+aclocal.m4
+autom4te.cache/
 cmd-parse.c
+compat/.dirstamp
+config.log
+config.status
+configure
+core
+etc/
 fuzz/*-fuzzer
-.dirstamp
+tags
+tmux
+tmux.1.*
diff --git a/SYNCING b/SYNCING
index 07be40c4..51b78010 100644
--- a/SYNCING
+++ b/SYNCING
@@ -1,17 +1,17 @@
 Preamble
 ========
 
-Tmux portable relies on  repositories "tmux" and "tmux-openbsd".
+Tmux portable relies on  repositories "tmux" and "tmux-obsd".
 Here's a description of them:
 
 * "tmux" is the portable version, the one which contains code for other
   operating systems, and autotools, etc., which isn't found or needed in the
   OpenBSD base system.
 
-* "tmux-openbsd" is the version of tmux in OpenBSD base system which provides
+* "tmux-obsd" is the version of tmux in OpenBSD base system which provides
   the basis of the portable tmux version.
 
-Note:  The "tmux-openbsd" repository is actually handled by "git cvsimport"
+Note:  The "tmux-obsd" repository is actually handled by "git cvsimport"
 running at 15 minute intervals, so a commit made to OpenBSD's tmux CVS
 repository will take at least that long to appear in this git repository.
 (It might take longer, depending on the CVS mirror used to import the
@@ -34,11 +34,11 @@ this information has ever been set before.
 Cloning repositories
 ====================
 
-This involves having both tmux and tmux-openbsd cloned, as in:
+This involves having both tmux and tmux-obsd cloned, as in:
 
 % cd /some/where/useful
 % git clone https://github.com/tmux/tmux.git
-% git clone https://github.com/ThomasAdam/tmux-openbsd.git
+% git clone https://github.com/ThomasAdam/tmux-obsd.git
 
 Note that you do not need additional checkouts to manage the sync -- an
 existing clone of either repositories will suffice.  So if you already have
@@ -47,30 +47,30 @@ these checkouts existing, skip that.
 Adding in git-remotes
 =====================
 
-Because the portable "tmux" git repository and the "tmux-openbsd"
+Because the portable "tmux" git repository and the "tmux-obsd"
 repository do not inherently share any history between each other, the
 history has been faked between them.  This "faking of history" is something
 which has to be told to git for the purposes of comparing the "tmux" and
-"tmux-openbsd" repositories for syncing.  To do this, we must reference the
-clone of the "tmux-openbsd" repository from the "tmux" repository, as
+"tmux-obsd" repositories for syncing.  To do this, we must reference the
+clone of the "tmux-obsd" repository from the "tmux" repository, as
 shown by the following command:
 
 % cd /path/to/tmux
-% git remote add obsd-tmux file:///path/to/tmux-openbsd
+% git remote add obsd-tmux file:///path/to/tmux-obsd
 
 So that now, the remote "obsd-tmux" can be used to reference branches and
-commits from the "tmux-openbsd" repository, but from the context of the
+commits from the "tmux-obsd" repository, but from the context of the
 portable "tmux" repository, which makes sense because it's the "tmux"
 repository which will have the updates applied to them.
 
 Fetching updates
 ================
 
-To ensure the latest commits from "tmux-openbsd" can be found from within
-"tmux", we have to ensure the "master" branch from "tmux-openbsd" is
+To ensure the latest commits from "tmux-obsd" can be found from within
+"tmux", we have to ensure the "master" branch from "tmux-obsd" is
 up-to-date first, and then reference that update in "tmux", as in:
 
-% cd /path/to/tmux-openbsd
+% cd /path/to/tmux-obsd
 % git checkout master
 % git pull
 
@@ -82,9 +82,9 @@ Then back in "tmux":
 Creating the necessary branches
 ===============================
 
-Now that "tmux" can see commits and branches from "tmux-openbsd" by way
+Now that "tmux" can see commits and branches from "tmux-obsd" by way
 of the remote name "obsd-tmux", we can now create the master branch from
-"tmux-openbsd" in the "tmux" repository:
+"tmux-obsd" in the "tmux" repository:
 
 % git checkout -b obsd-master obsd-tmux/master
 
@@ -92,7 +92,7 @@ Adding in the fake history points
 =================================
 
 To tie both the "master" branch from "tmux" and the "obsd-master"
-branch from "tmux-openbsd" together, the fake history points added to the
+branch from "tmux-obsd" together, the fake history points added to the
 "tmux" repository need to be added.  To do this, we must add an
 additional refspec line, as in:
 
diff --git a/alerts.c b/alerts.c
index d3c5df05..81c41fa6 100644
--- a/alerts.c
+++ b/alerts.c
@@ -315,11 +315,11 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option)
 		if (visual == VISUAL_OFF)
 			continue;
 		if (c->session->curw == wl) {
-			status_message_set(c, -1, 1, 0, "%s in current window",
-			    type);
+			status_message_set(c, -1, 1, 0, 0,
+			    "%s in current window", type);
 		} else {
-			status_message_set(c, -1, 1, 0, "%s in window %d", type,
-			    wl->idx);
+			status_message_set(c, -1, 1, 0, 0,
+			    "%s in window %d", type, wl->idx);
 		}
 	}
 }
diff --git a/client.c b/client.c
index 26e48df3..a556a86f 100644
--- a/client.c
+++ b/client.c
@@ -266,8 +266,13 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
 			if (cmd_list_any_have(pr->cmdlist, CMD_STARTSERVER))
 				flags |= CLIENT_STARTSERVER;
 			cmd_list_free(pr->cmdlist);
-		} else
+		} else {
+			fprintf(stderr, "%s\n", pr->error);
+			args_free_values(values, argc);
+			free(values);
 			free(pr->error);
+			return 1;
+		}
 		args_free_values(values, argc);
 		free(values);
 	}
diff --git a/cmd-break-pane.c b/cmd-break-pane.c
index 9c4b1508..a5582e46 100644
--- a/cmd-break-pane.c
+++ b/cmd-break-pane.c
@@ -99,7 +99,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
 
 	w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel);
 	options_set_parent(wp->options, w->options);
-	wp->flags |= PANE_STYLECHANGED;
+	wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
 	TAILQ_INSERT_HEAD(&w->panes, wp, entry);
 	w->active = wp;
 	w->latest = tc;
diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c
index 8f7250e8..81e1f7f9 100644
--- a/cmd-capture-pane.c
+++ b/cmd-capture-pane.c
@@ -39,8 +39,8 @@ const struct cmd_entry cmd_capture_pane_entry = {
 	.name = "capture-pane",
 	.alias = "capturep",
 
-	.args = { "ab:CeE:JNpPqS:Tt:", 0, 0, NULL },
-	.usage = "[-aCeJNpPqT] " CMD_BUFFER_USAGE " [-E end-line] "
+	.args = { "ab:CeE:JMNpPqS:Tt:", 0, 0, NULL },
+	.usage = "[-aCeJMNpPqT] " CMD_BUFFER_USAGE " [-E end-line] "
 		 "[-S start-line] " CMD_TARGET_PANE_USAGE,
 
 	.target = { 't', CMD_FIND_PANE, 0 },
@@ -107,14 +107,16 @@ static char *
 cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
     struct window_pane *wp, size_t *len)
 {
-	struct grid		*gd;
-	const struct grid_line	*gl;
-	struct grid_cell	*gc = NULL;
-	int			 n, join_lines, flags = 0;
-	u_int			 i, sx, top, bottom, tmp;
-	char			*cause, *buf, *line;
-	const char		*Sflag, *Eflag;
-	size_t			 linelen;
+	struct grid			*gd;
+	const struct grid_line		*gl;
+	struct screen			*s;
+	struct grid_cell		*gc = NULL;
+	struct window_mode_entry	*wme;
+	int				 n, join_lines, flags = 0;
+	u_int				 i, sx, top, bottom, tmp;
+	char				*cause, *buf, *line;
+	const char			*Sflag, *Eflag;
+	size_t				 linelen;
 
 	sx = screen_size_x(&wp->base);
 	if (args_has(args, 'a')) {
@@ -126,8 +128,20 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
 			}
 			return (xstrdup(""));
 		}
-	} else
+		s = &wp->base;
+	} else if (args_has(args, 'M')) {
+		wme = TAILQ_FIRST(&wp->modes);
+		if (wme != NULL && wme->mode->get_screen != NULL) {
+			s = wme->mode->get_screen (wme);
+			gd = s->grid;
+		} else {
+			s = &wp->base;
+			gd = wp->base.grid;
+		}
+	} else {
+		s = &wp->base;
 		gd = wp->base.grid;
+	}
 
 	Sflag = args_get(args, 'S');
 	if (Sflag != NULL && strcmp(Sflag, "-") == 0)
@@ -181,7 +195,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
 
 	buf = NULL;
 	for (i = top; i <= bottom; i++) {
-		line = grid_string_cells(gd, 0, i, sx, &gc, flags, wp->screen);
+		line = grid_string_cells(gd, 0, i, sx, &gc, flags, s);
 		linelen = strlen(line);
 
 		buf = cmd_capture_pane_append(buf, len, line, linelen);
diff --git a/cmd-display-menu.c b/cmd-display-menu.c
index 2f77fc7f..5e742ce1 100644
--- a/cmd-display-menu.c
+++ b/cmd-display-menu.c
@@ -41,7 +41,7 @@ const struct cmd_entry cmd_display_menu_entry = {
 	.args = { "b:c:C:H:s:S:MOt:T:x:y:", 1, -1, cmd_display_menu_args_parse },
 	.usage = "[-MO] [-b border-lines] [-c target-client] "
 		 "[-C starting-choice] [-H selected-style] [-s style] "
-		 "[-S border-style] " CMD_TARGET_PANE_USAGE "[-T title] "
+		 "[-S border-style] " CMD_TARGET_PANE_USAGE " [-T title] "
 		 "[-x position] [-y position] name key command ...",
 
 	.target = { 't', CMD_FIND_PANE, 0 },
@@ -58,7 +58,7 @@ const struct cmd_entry cmd_display_popup_entry = {
 	.usage = "[-BCE] [-b border-lines] [-c target-client] "
 		 "[-d start-directory] [-e environment] [-h height] "
 		 "[-s style] [-S border-style] " CMD_TARGET_PANE_USAGE
-		 "[-T title] [-w width] [-x position] [-y position] "
+		 " [-T title] [-w width] [-x position] [-y position] "
 		 "[shell-command]",
 
 	.target = { 't', CMD_FIND_PANE, 0 },
diff --git a/cmd-display-message.c b/cmd-display-message.c
index 512509f0..9ba6d13e 100644
--- a/cmd-display-message.c
+++ b/cmd-display-message.c
@@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = {
 	.name = "display-message",
 	.alias = "display",
 
-	.args = { "ac:d:lINpt:F:v", 0, 1, NULL },
-	.usage = "[-aIlNpv] [-c target-client] [-d delay] [-F format] "
+	.args = { "aCc:d:lINpt:F:v", 0, 1, NULL },
+	.usage = "[-aCIlNpv] [-c target-client] [-d delay] [-F format] "
 		 CMD_TARGET_PANE_USAGE " [message]",
 
 	.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -69,6 +69,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
 	const char		*template;
 	char			*msg, *cause;
 	int			 delay = -1, flags, Nflag = args_has(args, 'N');
+	int			 Cflag = args_has(args, 'C');
 	struct format_tree	*ft;
 	u_int			 count = args_count(args);
 	struct evbuffer		*evb;
@@ -150,7 +151,7 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
 		server_client_print(tc, 0, evb);
 		evbuffer_free(evb);
 	} else if (tc != NULL)
-		status_message_set(tc, delay, 0, Nflag, "%s", msg);
+		status_message_set(tc, delay, 0, Nflag, Cflag, "%s", msg);
 	free(msg);
 
 	format_free(ft);
diff --git a/cmd-if-shell.c b/cmd-if-shell.c
index 205a8ce1..81518939 100644
--- a/cmd-if-shell.c
+++ b/cmd-if-shell.c
@@ -157,7 +157,7 @@ cmd_if_shell_callback(struct job *job)
 	if (cmdlist == NULL) {
 		if (cdata->item == NULL) {
 			*error = toupper((u_char)*error);
-			status_message_set(c, -1, 1, 0, "%s", error);
+			status_message_set(c, -1, 1, 0, 0, "%s", error);
 		} else
 			cmdq_error(cdata->item, "%s", error);
 		free(error);
diff --git a/cmd-join-pane.c b/cmd-join-pane.c
index da1ba9ae..6b6421fa 100644
--- a/cmd-join-pane.c
+++ b/cmd-join-pane.c
@@ -149,7 +149,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
 
 	src_wp->window = dst_w;
 	options_set_parent(src_wp->options, dst_w->options);
-	src_wp->flags |= PANE_STYLECHANGED;
+	src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
 	if (flags & SPAWN_BEFORE)
 		TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry);
 	else
diff --git a/cmd-list-keys.c b/cmd-list-keys.c
index 395b147c..ddfc0e0c 100644
--- a/cmd-list-keys.c
+++ b/cmd-list-keys.c
@@ -91,7 +91,7 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
 	struct key_binding	*bd;
 	const char		*key;
 	char			*tmp, *note;
-	int	                 found = 0;
+	int			 found = 0;
 
 	table = key_bindings_get_table(tablename, 0);
 	if (table == NULL)
@@ -114,8 +114,8 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
 			note = xstrdup(bd->note);
 		tmp = utf8_padcstr(key, keywidth + 1);
 		if (args_has(args, '1') && tc != NULL) {
-			status_message_set(tc, -1, 1, 0, "%s%s%s", prefix, tmp,
-			    note);
+			status_message_set(tc, -1, 1, 0, 0, "%s%s%s", prefix,
+			    tmp, note);
 		} else
 			cmdq_print(item, "%s%s%s", prefix, tmp, note);
 		free(tmp);
@@ -298,8 +298,8 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
 			free(cp);
 
 			if (args_has(args, '1') && tc != NULL) {
-				status_message_set(tc, -1, 1, 0, "bind-key %s",
-				    tmp);
+				status_message_set(tc, -1, 1, 0, 0,
+				    "bind-key %s", tmp);
 			} else
 				cmdq_print(item, "bind-key %s", tmp);
 			free(key);
@@ -321,6 +321,31 @@ out:
 	return (CMD_RETURN_NORMAL);
 }
 
+static void
+cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
+    const char *template, struct cmdq_item *item)
+{
+	const char	*s;
+	char		*line;
+
+	format_add(ft, "command_list_name", "%s", entry->name);
+	if (entry->alias != NULL)
+		s = entry->alias;
+	else
+		s = "";
+	format_add(ft, "command_list_alias", "%s", s);
+	if (entry->usage != NULL)
+		s = entry->usage;
+	else
+		s = "";
+	format_add(ft, "command_list_usage", "%s", s);
+
+	line = format_expand(ft, template);
+	if (*line != '\0')
+		cmdq_print(item, "%s", line);
+	free(line);
+}
+
 static enum cmd_retval
 cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
 {
@@ -328,8 +353,8 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
 	const struct cmd_entry	**entryp;
 	const struct cmd_entry	 *entry;
 	struct format_tree	 *ft;
-	const char		 *template, *s, *command;
-	char			 *line;
+	const char		 *template,  *command;
+	char			 *cause;
 
 	if ((template = args_get(args, 'F')) == NULL) {
 		template = "#{command_list_name}"
@@ -341,30 +366,19 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
 	format_defaults(ft, NULL, NULL, NULL, NULL);
 
 	command = args_string(args, 0);
-	for (entryp = cmd_table; *entryp != NULL; entryp++) {
-		entry = *entryp;
-		if (command != NULL &&
-		    (strcmp(entry->name, command) != 0 &&
-		    (entry->alias == NULL ||
-		    strcmp(entry->alias, command) != 0)))
-		    continue;
-
-		format_add(ft, "command_list_name", "%s", entry->name);
-		if (entry->alias != NULL)
-			s = entry->alias;
-		else
-			s = "";
-		format_add(ft, "command_list_alias", "%s", s);
-		if (entry->usage != NULL)
-			s = entry->usage;
-		else
-			s = "";
-		format_add(ft, "command_list_usage", "%s", s);
-
-		line = format_expand(ft, template);
-		if (*line != '\0')
-			cmdq_print(item, "%s", line);
-		free(line);
+	if (command == NULL) {
+		for (entryp = cmd_table; *entryp != NULL; entryp++)
+			cmd_list_single_command(*entryp, ft, template, item);
+	} else {
+		entry = cmd_find(command, &cause);
+		if (entry != NULL)
+			cmd_list_single_command(entry, ft, template, item);
+		else {
+			cmdq_error(item, "%s", cause);
+			free(cause);
+			format_free(ft);
+			return (CMD_RETURN_ERROR);
+		}
 	}
 
 	format_free(ft);
diff --git a/cmd-queue.c b/cmd-queue.c
index 26c7ec32..2a221516 100644
--- a/cmd-queue.c
+++ b/cmd-queue.c
@@ -892,7 +892,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, 0, "%s", msg);
+		status_message_set(c, -1, 1, 0, 0, "%s", msg);
 	}
 
 	free(msg);
diff --git a/cmd-run-shell.c b/cmd-run-shell.c
index 9e224c4e..be4c7cac 100644
--- a/cmd-run-shell.c
+++ b/cmd-run-shell.c
@@ -204,7 +204,7 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
 	if (cmdlist == NULL) {
 		if (cdata->item == NULL) {
 			*error = toupper((u_char)*error);
-			status_message_set(c, -1, 1, 0, "%s", error);
+			status_message_set(c, -1, 1, 0, 0, "%s", error);
 		} else
 			cmdq_error(cdata->item, "%s", error);
 		free(error);
diff --git a/cmd-select-pane.c b/cmd-select-pane.c
index 135729f5..3cabe07e 100644
--- a/cmd-select-pane.c
+++ b/cmd-select-pane.c
@@ -149,12 +149,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
 		markedwp = marked_pane.wp;
 
 		if (lastwp != NULL) {
-			lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
+			lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
+			    PANE_THEMECHANGED);
 			server_redraw_window_borders(lastwp->window);
 			server_status_window(lastwp->window);
 		}
 		if (markedwp != NULL) {
-			markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
+			markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
+			    PANE_THEMECHANGED);
 			server_redraw_window_borders(markedwp->window);
 			server_status_window(markedwp->window);
 		}
@@ -169,7 +171,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
 			return (CMD_RETURN_ERROR);
 		}
 		options_set_string(oo, "window-active-style", 0, "%s", style);
-		wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
+		wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED);
 	}
 	if (args_has(args, 'g')) {
 		cmdq_print(item, "%s", options_get_string(oo, "window-style"));
diff --git a/cmd-send-keys.c b/cmd-send-keys.c
index c270fbd1..35b3f140 100644
--- a/cmd-send-keys.c
+++ b/cmd-send-keys.c
@@ -217,7 +217,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
 	if (args_has(args, 'R')) {
 		colour_palette_clear(&wp->palette);
 		input_reset(wp->ictx, 1);
-		wp->flags |= (PANE_STYLECHANGED|PANE_REDRAW);
+		wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED|PANE_REDRAW);
 	}
 
 	if (count == 0) {
diff --git a/cmd-split-window.c b/cmd-split-window.c
index cae21af4..5cddbbd4 100644
--- a/cmd-split-window.c
+++ b/cmd-split-window.c
@@ -42,7 +42,7 @@ const struct cmd_entry cmd_split_window_entry = {
 	.args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1, NULL },
 	.usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] "
 		 "[-F format] [-l size] " CMD_TARGET_PANE_USAGE
-		 "[shell-command]",
+		 " [shell-command]",
 
 	.target = { 't', CMD_FIND_PANE, 0 },
 
diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c
index 6931bd16..4680f598 100644
--- a/cmd-swap-pane.c
+++ b/cmd-swap-pane.c
@@ -101,10 +101,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
 
 	src_wp->window = dst_w;
 	options_set_parent(src_wp->options, dst_w->options);
-	src_wp->flags |= PANE_STYLECHANGED;
+	src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
 	dst_wp->window = src_w;
 	options_set_parent(dst_wp->options, src_w->options);
-	dst_wp->flags |= PANE_STYLECHANGED;
+	dst_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
 
 	sx = src_wp->sx; sy = src_wp->sy;
 	xoff = src_wp->xoff; yoff = src_wp->yoff;
diff --git a/cmd.c b/cmd.c
index 92fd15b6..8f69ed92 100644
--- a/cmd.c
+++ b/cmd.c
@@ -444,7 +444,7 @@ cmd_get_alias(const char *name)
 }
 
 /* Look up a command entry by name. */
-static const struct cmd_entry *
+const struct cmd_entry *
 cmd_find(const char *name, char **cause)
 {
 	const struct cmd_entry	**loop, *entry, *found = NULL;
diff --git a/colour.c b/colour.c
index 5fd91c9c..903090d3 100644
--- a/colour.c
+++ b/colour.c
@@ -182,6 +182,46 @@ colour_tostring(int c)
 	return ("invalid");
 }
 
+/* Convert background colour to theme. */
+enum client_theme
+colour_totheme(int c)
+{
+	int	r, g, b, brightness;
+
+	if (c == -1)
+		return (THEME_UNKNOWN);
+
+	if (c & COLOUR_FLAG_RGB) {
+		r = (c >> 16) & 0xff;
+		g = (c >> 8) & 0xff;
+		b = (c >> 0) & 0xff;
+
+		brightness = r + g + b;
+		if (brightness > 382)
+			return (THEME_LIGHT);
+		return (THEME_DARK);
+	}
+
+	if (c & COLOUR_FLAG_256)
+		return (colour_totheme(colour_256toRGB(c)));
+
+	switch (c) {
+	case 0:
+	case 90:
+		return (THEME_DARK);
+	case 7:
+	case 97:
+		return (THEME_LIGHT);
+	default:
+		if (c >= 0 && c <= 7)
+			return (colour_totheme(colour_256toRGB(c)));
+		if (c >= 90 && c <= 97)
+			return (colour_totheme(colour_256toRGB(8 + c - 90)));
+		break;
+	}
+	return (THEME_UNKNOWN);
+}
+
 /* Convert colour from string. */
 int
 colour_fromstring(const char *s)
diff --git a/configure.ac b/configure.ac
index 4f90fcd5..98b67a4d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -638,9 +638,9 @@ else
 	AC_LIBOBJ(err)
 fi
 
-# Look for imsg_init in libutil.
-AC_SEARCH_LIBS(imsg_init, util, found_imsg_init=yes, found_imsg_init=no)
-if test "x$found_imsg_init" = xyes; then
+# Look for imsg_add in libutil.
+AC_SEARCH_LIBS(imsg_add, util, found_imsg_add=yes, found_imsg_add=no)
+if test "x$found_imsg_add" = xyes; then
 	AC_DEFINE(HAVE_IMSG)
 else
 	AC_LIBOBJ(imsg)
diff --git a/format.c b/format.c
index 6a3d0775..7853a2ba 100644
--- a/format.c
+++ b/format.c
@@ -1544,6 +1544,23 @@ format_cb_client_written(struct format_tree *ft)
 	return (NULL);
 }
 
+/* Callback for client_theme. */
+static void *
+format_cb_client_theme(struct format_tree *ft)
+{
+	if (ft->c != NULL) {
+		switch (ft->c->theme) {
+		case THEME_DARK:
+			return (xstrdup("dark"));
+		case THEME_LIGHT:
+			return (xstrdup("light"));
+		case THEME_UNKNOWN:
+			return (NULL);
+		}
+	}
+	return (NULL);
+}
+
 /* Callback for config_files. */
 static void *
 format_cb_config_files(__unused struct format_tree *ft)
@@ -2881,6 +2898,9 @@ static const struct format_table_entry format_table[] = {
 	{ "client_termtype", FORMAT_TABLE_STRING,
 	  format_cb_client_termtype
 	},
+	{ "client_theme", FORMAT_TABLE_STRING,
+	  format_cb_client_theme
+	},
 	{ "client_tty", FORMAT_TABLE_STRING,
 	  format_cb_client_tty
 	},
diff --git a/input-keys.c b/input-keys.c
index 097feeb2..18f15afa 100644
--- a/input-keys.c
+++ b/input-keys.c
@@ -53,9 +53,15 @@ static struct input_key_entry input_key_defaults[] = {
 	{ .key = KEYC_PASTE_START,
 	  .data = "\033[200~"
 	},
+	{ .key = KEYC_PASTE_START|KEYC_IMPLIED_META,
+	  .data = "\033[200~"
+	},
 	{ .key = KEYC_PASTE_END,
 	  .data = "\033[201~"
 	},
+	{ .key = KEYC_PASTE_END|KEYC_IMPLIED_META,
+	  .data = "\033[201~"
+	},
 
 	/* Function keys. */
 	{ .key = KEYC_F1,
@@ -307,6 +313,12 @@ static struct input_key_entry input_key_defaults[] = {
 	{ .key = KEYC_DC|KEYC_BUILD_MODIFIERS,
 	  .data = "\033[3;_~"
 	},
+	{ .key = KEYC_REPORT_DARK_THEME,
+	  .data = "\033[?997;1n"
+	},
+	{ .key = KEYC_REPORT_LIGHT_THEME,
+	  .data = "\033[?997;2n"
+	},
 };
 static const key_code input_key_modifiers[] = {
 	0,
diff --git a/input.c b/input.c
index 6216cfb8..2c7b77b8 100644
--- a/input.c
+++ b/input.c
@@ -133,7 +133,7 @@ static void printflike(2, 3) input_reply(struct input_ctx *, const char *, ...);
 static void	input_set_state(struct input_ctx *,
 		    const struct input_transition *);
 static void	input_reset_cell(struct input_ctx *);
-
+static void	input_report_current_theme(struct input_ctx *);
 static void	input_osc_4(struct input_ctx *, const char *);
 static void	input_osc_8(struct input_ctx *, const char *);
 static void	input_osc_10(struct input_ctx *, const char *);
@@ -243,6 +243,7 @@ enum input_csi_type {
 	INPUT_CSI_DECSTBM,
 	INPUT_CSI_DL,
 	INPUT_CSI_DSR,
+	INPUT_CSI_DSR_PRIVATE,
 	INPUT_CSI_ECH,
 	INPUT_CSI_ED,
 	INPUT_CSI_EL,
@@ -251,6 +252,7 @@ enum input_csi_type {
 	INPUT_CSI_IL,
 	INPUT_CSI_MODOFF,
 	INPUT_CSI_MODSET,
+	INPUT_CSI_QUERY_PRIVATE,
 	INPUT_CSI_RCP,
 	INPUT_CSI_REP,
 	INPUT_CSI_RM,
@@ -259,8 +261,8 @@ enum input_csi_type {
 	INPUT_CSI_SD,
 	INPUT_CSI_SGR,
 	INPUT_CSI_SM,
-	INPUT_CSI_SM_PRIVATE,
 	INPUT_CSI_SM_GRAPHICS,
+	INPUT_CSI_SM_PRIVATE,
 	INPUT_CSI_SU,
 	INPUT_CSI_TBC,
 	INPUT_CSI_VPA,
@@ -304,6 +306,8 @@ static const struct input_table_entry input_csi_table[] = {
 	{ 'm', ">", INPUT_CSI_MODSET },
 	{ 'n', "",  INPUT_CSI_DSR },
 	{ 'n', ">", INPUT_CSI_MODOFF },
+	{ 'n', "?", INPUT_CSI_DSR_PRIVATE },
+	{ 'p', "?$", INPUT_CSI_QUERY_PRIVATE },
 	{ 'q', " ", INPUT_CSI_DECSCUSR },
 	{ 'q', ">", INPUT_CSI_XDA },
 	{ 'r', "",  INPUT_CSI_DECSTBM },
@@ -1531,6 +1535,20 @@ input_csi_dispatch(struct input_ctx *ictx)
 		if (n != -1)
 			screen_write_deleteline(sctx, n, bg);
 		break;
+	case INPUT_CSI_DSR_PRIVATE:
+		switch (input_get(ictx, 0, 0, 0)) {
+		case 996:
+			input_report_current_theme(ictx);
+			break;
+		}
+		break;
+	case INPUT_CSI_QUERY_PRIVATE:
+		switch (input_get(ictx, 0, 0, 0)) {
+		case 2031:
+			input_reply(ictx, "\033[?2031;2$y");
+			break;
+		}
+		break;
 	case INPUT_CSI_DSR:
 		switch (input_get(ictx, 0, 0, 0)) {
 		case -1:
@@ -1781,6 +1799,9 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
 		case 2004:
 			screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
 			break;
+		case 2031:
+			screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
+			break;
 		default:
 			log_debug("%s: unknown '%c'", __func__, ictx->ch);
 			break;
@@ -1876,6 +1897,9 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
 		case 2004:
 			screen_write_mode_set(sctx, MODE_BRACKETPASTE);
 			break;
+		case 2031:
+			screen_write_mode_set(sctx, MODE_THEME_UPDATES);
+			break;
 		default:
 			log_debug("%s: unknown '%c'", __func__, ictx->ch);
 			break;
@@ -2697,84 +2721,6 @@ bad:
 	free(id);
 }
 
-/*
- * Get a client with a foreground for the pane. There isn't much to choose
- * between them so just use the first.
- */
-static int
-input_get_fg_client(struct window_pane *wp)
-{
-	struct window	*w = wp->window;
-	struct client	*loop;
-
-	TAILQ_FOREACH(loop, &clients, entry) {
-		if (loop->flags & CLIENT_UNATTACHEDFLAGS)
-			continue;
-		if (loop->session == NULL || !session_has(loop->session, w))
-			continue;
-		if (loop->tty.fg == -1)
-			continue;
-		return (loop->tty.fg);
-	}
-	return (-1);
-}
-
-/* Get a client with a background for the pane. */
-static int
-input_get_bg_client(struct window_pane *wp)
-{
-	struct window	*w = wp->window;
-	struct client	*loop;
-
-	TAILQ_FOREACH(loop, &clients, entry) {
-		if (loop->flags & CLIENT_UNATTACHEDFLAGS)
-			continue;
-		if (loop->session == NULL || !session_has(loop->session, w))
-			continue;
-		if (loop->tty.bg == -1)
-			continue;
-		return (loop->tty.bg);
-	}
-	return (-1);
-}
-
-/*
- * If any control mode client exists that has provided a bg color, return it.
- * Otherwise, return -1.
- */
-static int
-input_get_bg_control_client(struct window_pane *wp)
-{
-	struct client	*c;
-
-	if (wp->control_bg == -1)
-		return (-1);
-
-	TAILQ_FOREACH(c, &clients, entry) {
-		if (c->flags & CLIENT_CONTROL)
-			return (wp->control_bg);
-	}
-	return (-1);
-}
-
-/*
- * If any control mode client exists that has provided a fg color, return it.
- * Otherwise, return -1.
- */
-static int
-input_get_fg_control_client(struct window_pane *wp)
-{
-	struct client	*c;
-
-	if (wp->control_fg == -1)
-		return (-1);
-
-	TAILQ_FOREACH(c, &clients, entry) {
-		if (c->flags & CLIENT_CONTROL)
-			return (wp->control_fg);
-	}
-	return (-1);
-}
 
 /* Handle the OSC 10 sequence for setting and querying foreground colour. */
 static void
@@ -2787,11 +2733,11 @@ input_osc_10(struct input_ctx *ictx, const char *p)
 	if (strcmp(p, "?") == 0) {
 		if (wp == NULL)
 			return;
-		c = input_get_fg_control_client(wp);
+		c = window_pane_get_fg_control_client(wp);
 		if (c == -1) {
 			tty_default_colours(&defaults, wp);
 			if (COLOUR_DEFAULT(defaults.fg))
-				c = input_get_fg_client(wp);
+				c = window_pane_get_fg(wp);
 			else
 				c = defaults.fg;
 		}
@@ -2832,20 +2778,12 @@ static void
 input_osc_11(struct input_ctx *ictx, const char *p)
 {
 	struct window_pane	*wp = ictx->wp;
-	struct grid_cell	 defaults;
 	int			 c;
 
 	if (strcmp(p, "?") == 0) {
 		if (wp == NULL)
 			return;
-		c = input_get_bg_control_client(wp);
-		if (c == -1) {
-			tty_default_colours(&defaults, wp);
-			if (COLOUR_DEFAULT(defaults.bg))
-				c = input_get_bg_client(wp);
-			else
-				c = defaults.bg;
-		}
+		c = window_pane_get_bg(wp);
 		input_osc_colour_reply(ictx, 11, c);
 		return;
 	}
@@ -2857,7 +2795,7 @@ input_osc_11(struct input_ctx *ictx, const char *p)
 	if (ictx->palette != NULL) {
 		ictx->palette->bg = c;
 		if (wp != NULL)
-			wp->flags |= PANE_STYLECHANGED;
+			wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
 		screen_write_fullredraw(&ictx->ctx);
 	}
 }
@@ -2873,7 +2811,7 @@ input_osc_111(struct input_ctx *ictx, const char *p)
 	if (ictx->palette != NULL) {
 		ictx->palette->bg = 8;
 		if (wp != NULL)
-			wp->flags |= PANE_STYLECHANGED;
+			wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
 		screen_write_fullredraw(&ictx->ctx);
 	}
 }
@@ -3064,3 +3002,18 @@ input_set_buffer_size(size_t buffer_size)
 	log_debug("%s: %lu -> %lu", __func__, input_buffer_size, buffer_size);
 	input_buffer_size = buffer_size;
 }
+
+static void
+input_report_current_theme(struct input_ctx *ictx)
+{
+	switch (window_pane_get_theme(ictx->wp)) {
+		case THEME_DARK:
+			input_reply(ictx, "\033[?997;1n");
+			break;
+		case THEME_LIGHT:
+			input_reply(ictx, "\033[?997;2n");
+			break;
+		case THEME_UNKNOWN:
+			break;
+	}
+}
diff --git a/mode-tree.c b/mode-tree.c
index e1170a3d..24cbea04 100644
--- a/mode-tree.c
+++ b/mode-tree.c
@@ -66,6 +66,7 @@ struct mode_tree_data {
 	u_int			  line_size;
 
 	u_int			  depth;
+	u_int			  maxdepth;
 
 	u_int			  width;
 	u_int			  height;
@@ -196,6 +197,8 @@ mode_tree_build_lines(struct mode_tree_data *mtd,
 	int			 flat = 1;
 
 	mtd->depth = depth;
+	if (depth > mtd->maxdepth)
+		mtd->maxdepth = depth;
 	TAILQ_FOREACH(mti, mtl, entry) {
 		mtd->line_list = xreallocarray(mtd->line_list,
 		    mtd->line_size + 1, sizeof *mtd->line_list);
@@ -528,6 +531,7 @@ mode_tree_build(struct mode_tree_data *mtd)
 	TAILQ_INIT(&mtd->saved);
 
 	mode_tree_clear_lines(mtd);
+	mtd->maxdepth = 0;
 	mode_tree_build_lines(mtd, &mtd->children, 0);
 
 	if (mtd->line_list != NULL && tag == UINT64_MAX)
@@ -658,7 +662,7 @@ mode_tree_draw(struct mode_tree_data *mtd)
 	char			*text, *start, *key;
 	const char		*tag, *symbol;
 	size_t			 size, n;
-	int			 keylen, pad;
+	int			 keylen, pad, namelen[mtd->maxdepth + 1];
 
 	if (mtd->line_size == 0)
 		return;
@@ -682,6 +686,15 @@ mode_tree_draw(struct mode_tree_data *mtd)
 			keylen = mti->keylen + 3;
 	}
 
+	for (i = 0; i < mtd->maxdepth + 1; i++)
+		namelen[i] = 0;
+	for (i = 0; i < mtd->line_size; i++) {
+		line = &mtd->line_list[i];
+		mti = line->item;
+		if ((int)strlen(mti->name) > namelen[line->depth])
+			namelen[line->depth] = strlen(mti->name);
+	}
+
 	for (i = 0; i < mtd->line_size; i++) {
 		if (i < mtd->offset)
 			continue;
@@ -731,8 +744,9 @@ mode_tree_draw(struct mode_tree_data *mtd)
 			tag = "*";
 		else
 			tag = "";
-		xasprintf(&text, "%-*s%s%s%s%s", keylen, key, start, mti->name,
-		    tag, (mti->text != NULL) ? ": " : "" );
+		xasprintf(&text, "%-*s%s%*s%s%s", keylen, key, start,
+		    namelen[line->depth], mti->name, tag,
+		    (mti->text != NULL) ? ": " : "" );
 		width = utf8_cstrwidth(text);
 		if (width > w)
 			width = w;
@@ -1297,7 +1311,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, 0, "%s", error);
+				status_message_set(c, -1, 1, 0, 0, "%s", error);
 			}
 			free(error);
 		}
diff --git a/options-table.c b/options-table.c
index 39215350..a461b3ea 100644
--- a/options-table.c
+++ b/options-table.c
@@ -1454,6 +1454,8 @@ const struct options_table_entry options_table[] = {
 	OPTIONS_TABLE_HOOK("client-focus-out", ""),
 	OPTIONS_TABLE_HOOK("client-resized", ""),
 	OPTIONS_TABLE_HOOK("client-session-changed", ""),
+	OPTIONS_TABLE_HOOK("client-light-theme", ""),
+	OPTIONS_TABLE_HOOK("client-dark-theme", ""),
 	OPTIONS_TABLE_HOOK("command-error", ""),
 	OPTIONS_TABLE_PANE_HOOK("pane-died", ""),
 	OPTIONS_TABLE_PANE_HOOK("pane-exited", ""),
diff --git a/options.c b/options.c
index 4beb9898..5541a376 100644
--- a/options.c
+++ b/options.c
@@ -1165,7 +1165,7 @@ options_push_changes(const char *name)
 	if (strcmp(name, "window-style") == 0 ||
 	    strcmp(name, "window-active-style") == 0) {
 		RB_FOREACH(wp, window_pane_tree, &all_window_panes)
-			wp->flags |= PANE_STYLECHANGED;
+			wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
 	}
 	if (strcmp(name, "pane-colours") == 0) {
 		RB_FOREACH(wp, window_pane_tree, &all_window_panes)
diff --git a/screen-redraw.c b/screen-redraw.c
index 34fd0069..0d2acad6 100644
--- a/screen-redraw.c
+++ b/screen-redraw.c
@@ -433,11 +433,15 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp,
 	const char		*fmt;
 	struct format_tree	*ft;
 	char			*expanded;
-	int			 pane_status = rctx->pane_status;
+	int			 pane_status = rctx->pane_status, sb_w = 0;
+	int			 pane_scrollbars = rctx->pane_scrollbars;
 	u_int			 width, i, cell_type, px, py;
 	struct screen_write_ctx	 ctx;
 	struct screen		 old;
 
+	if (window_pane_show_scrollbar(wp, pane_scrollbars))
+		sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
+
 	ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
 	format_defaults(ft, c, c->session, c->session->curw, wp);
 
@@ -451,7 +455,7 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp,
 	if (wp->sx < 4)
 		wp->status_size = width = 0;
 	else
-		wp->status_size = width = wp->sx - 4;
+		wp->status_size = width = wp->sx + sb_w - 2;
 
 	memcpy(&old, &wp->status_screen, sizeof old);
 	screen_init(&wp->status_screen, width, 1, 0);
diff --git a/server-client.c b/server-client.c
index 0f8ba245..30cf5280 100644
--- a/server-client.c
+++ b/server-client.c
@@ -56,11 +56,11 @@ static void	server_client_set_title(struct client *);
 static void	server_client_set_path(struct client *);
 static void	server_client_reset_state(struct client *);
 static void	server_client_update_latest(struct client *);
-
 static void	server_client_dispatch(struct imsg *, void *);
 static void	server_client_dispatch_command(struct client *, struct imsg *);
 static void	server_client_dispatch_identify(struct client *, struct imsg *);
 static void	server_client_dispatch_shell(struct client *);
+static void	server_client_report_theme(struct client *, enum client_theme);
 
 /* Compare client windows. */
 static int
@@ -152,6 +152,7 @@ server_client_clear_overlay(struct client *c)
 	c->overlay_draw = NULL;
 	c->overlay_key = NULL;
 	c->overlay_free = NULL;
+	c->overlay_resize = NULL;
 	c->overlay_data = NULL;
 
 	c->tty.flags &= ~(TTY_FREEZE|TTY_NOCURSOR);
@@ -299,6 +300,7 @@ server_client_create(int fd)
 
 	c->tty.sx = 80;
 	c->tty.sy = 24;
+	c->theme = THEME_UNKNOWN;
 
 	status_init(c);
 	c->flags |= CLIENT_FOCUSED;
@@ -400,6 +402,7 @@ server_client_set_session(struct client *c, struct session *s)
 		recalculate_sizes();
 		window_update_focus(s->curw->window);
 		session_update_activity(s, NULL);
+		session_theme_changed(s);
 		gettimeofday(&s->last_attached_time, NULL);
 		s->curw->flags &= ~WINLINK_ALERTFLAGS;
 		s->curw->window->latest = c;
@@ -2242,13 +2245,13 @@ out:
 static int
 server_client_is_bracket_paste(struct client *c, key_code key)
 {
-	if (key == KEYC_PASTE_START) {
+	if ((key & KEYC_MASK_KEY) == KEYC_PASTE_START) {
 		c->flags |= CLIENT_BRACKETPASTING;
 		log_debug("%s: bracket paste on", c->name);
 		return (0);
 	}
 
-	if (key == KEYC_PASTE_END) {
+	if ((key & KEYC_MASK_KEY) == KEYC_PASTE_END) {
 		c->flags &= ~CLIENT_BRACKETPASTING;
 		log_debug("%s: bracket paste off", c->name);
 		return (0);
@@ -2383,6 +2386,16 @@ server_client_key_callback(struct cmdq_item *item, void *data)
 		event->key = key;
 	}
 
+	/* Handle theme reporting keys. */
+	if (key == KEYC_REPORT_LIGHT_THEME) {
+		server_client_report_theme(c, THEME_LIGHT);
+		goto out;
+	}
+	if (key == KEYC_REPORT_DARK_THEME) {
+		server_client_report_theme(c, THEME_DARK);
+		goto out;
+	}
+
 	/* Find affected pane. */
 	if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m, 0) != 0)
 		cmd_find_from_client(&fs, c, 0);
@@ -2673,6 +2686,12 @@ server_client_loop(void)
 		}
 		check_window_name(w);
 	}
+
+	/* Send theme updates. */
+	RB_FOREACH(w, windows, &windows) {
+		TAILQ_FOREACH(wp, &w->panes, entry)
+			window_pane_send_theme_update(wp);
+	}
 }
 
 /* Check if window needs to be resized. */
@@ -3909,3 +3928,21 @@ out:
 	if (!parse)
 		free(msg);
 }
+
+static void
+server_client_report_theme(struct client *c, enum client_theme theme)
+{
+	if (theme == THEME_LIGHT) {
+		c->theme = THEME_LIGHT;
+		notify_client("client-light-theme", c);
+	} else {
+		c->theme = THEME_DARK;
+		notify_client("client-dark-theme", c);
+	}
+
+	/*
+	 * Request foreground and background colour again. Don't forward 2031 to
+	 * panes until a response is received.
+	 */
+	tty_puts(&c->tty, "\033]10;?\033\\\033]11;?\033\\");
+}
diff --git a/session.c b/session.c
index e4aca34d..e6a26d27 100644
--- a/session.c
+++ b/session.c
@@ -751,3 +751,16 @@ session_renumber_windows(struct session *s)
 	RB_FOREACH_SAFE(wl, winlinks, &old_wins, wl1)
 		winlink_remove(&old_wins, wl);
 }
+
+/* Set the PANE_THEMECHANGED flag for every pane in this session. */
+void
+session_theme_changed(struct session *s)
+{
+	struct window_pane	*wp;
+	struct winlink		*wl;
+
+	RB_FOREACH(wl, winlinks, &s->windows) {
+		TAILQ_FOREACH(wp, &wl->window->panes, entry)
+			wp->flags |= PANE_THEMECHANGED;
+	}
+}
diff --git a/status.c b/status.c
index 32833f8c..745ee3ae 100644
--- a/status.c
+++ b/status.c
@@ -470,7 +470,7 @@ status_redraw(struct client *c)
 /* Set a status line message. */
 void
 status_message_set(struct client *c, int delay, int ignore_styles,
-    int ignore_keys, const char *fmt, ...)
+    int ignore_keys, int no_freeze, const char *fmt, ...)
 {
 	struct timeval	 tv;
 	va_list		 ap;
@@ -514,7 +514,9 @@ status_message_set(struct client *c, int delay, int ignore_styles,
 		c->message_ignore_keys = ignore_keys;
 	c->message_ignore_styles = ignore_styles;
 
-	c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE);
+	if (!no_freeze)
+		c->tty.flags |= TTY_FREEZE;
+	c->tty.flags |= TTY_NOCURSOR;
 	c->flags |= CLIENT_REDRAWSTATUS;
 }
 
diff --git a/tmux.1 b/tmux.1
index c2cc679a..483e3987 100644
--- a/tmux.1
+++ b/tmux.1
@@ -2331,6 +2331,15 @@ repeats the last search and
 does the same but reverses the direction (forward becomes backward and backward
 becomes forward).
 .Pp
+The default incremental search key bindings,
+.Ql C-r
+and
+.Ql C-s ,
+are designed to emulate
+.Xr emacs 1 .
+When first pressed they allow a new search term to be entered; if pressed with
+an empty search term they repeat the previously used search term.
+.Pp
 The
 .Ql next-prompt
 and
@@ -2547,7 +2556,7 @@ but a different format may be specified with
 .Fl F .
 .Tg capturep
 .It Xo Ic capture-pane
-.Op Fl aAepPqCJN
+.Op Fl aepPqCJMN
 .Op Fl b Ar buffer-name
 .Op Fl E Ar end-line
 .Op Fl S Ar start-line
@@ -2566,6 +2575,9 @@ is given, the alternate screen is used, and the history is not accessible.
 If no alternate screen exists, an error will be returned unless
 .Fl q
 is given.
+Similarly, if the pane is in a mode,
+.Fl M
+uses the screen for the mode.
 If
 .Fl e
 is given, the output includes escape sequences for text and background
@@ -5857,6 +5869,12 @@ with
 .Ql bar/
 throughout.
 .Pp
+Multiple modifiers may be separated with a semicolon (;) as in
+.Ql #{T;=10:status-left} ,
+which limits the resulting
+.Xr strftime 3 -expanded
+string to at most 10 characters.
+.Pp
 In addition, the last line of a shell command's output may be inserted using
 .Ql #() .
 For example,
@@ -6744,7 +6762,7 @@ The following keys are available in menus:
 .El
 .Tg display
 .It Xo Ic display-message
-.Op Fl aIlNpv
+.Op Fl aCIlNpv
 .Op Fl c Ar target-client
 .Op Fl d Ar delay
 .Op Fl t Ar target-pane
@@ -6767,6 +6785,9 @@ option is used; a delay of zero waits for a key press.
 .Ql N
 ignores key presses and closes only after the delay expires.
 If
+.Fl C
+is given, the pane will continue to be updated while the message is displayed.
+If
 .Fl l
 is given,
 .Ar message
diff --git a/tmux.h b/tmux.h
index 904a5350..f7c59308 100644
--- a/tmux.h
+++ b/tmux.h
@@ -176,7 +176,8 @@ struct winlink;
 
 /* Is this a paste key? */
 #define KEYC_IS_PASTE(key) \
-	((key) == KEYC_PASTE_START || (key) == KEYC_PASTE_END)
+	(((key) & KEYC_MASK_KEY) == KEYC_PASTE_START || \
+	 ((key) & KEYC_MASK_KEY) == KEYC_PASTE_END)
 
 /* Multiple click timeout. */
 #define KEYC_CLICK_TIMEOUT 300
@@ -377,6 +378,10 @@ enum {
 	KEYC_KP_ZERO,
 	KEYC_KP_PERIOD,
 
+	/* Theme reporting. */
+	KEYC_REPORT_DARK_THEME,
+	KEYC_REPORT_LIGHT_THEME,
+
 	/* End of special keys. */
 	KEYC_BASE_END
 };
@@ -644,6 +649,7 @@ enum tty_code_code {
 #define MODE_CURSOR_VERY_VISIBLE 0x10000
 #define MODE_CURSOR_BLINKING_SET 0x20000
 #define MODE_KEYS_EXTENDED_2 0x40000
+#define MODE_THEME_UPDATES 0x80000
 
 #define ALL_MODES 0xffffff
 #define ALL_MOUSE_MODES (MODE_MOUSE_STANDARD|MODE_MOUSE_BUTTON|MODE_MOUSE_ALL)
@@ -1090,6 +1096,7 @@ struct window_mode {
 			     struct mouse_event *);
 	void		 (*formats)(struct window_mode_entry *,
 			     struct format_tree *);
+	struct screen	*(*get_screen)(struct window_mode_entry *);
 };
 
 /* Active window mode. */
@@ -1154,8 +1161,9 @@ struct window_pane {
 #define PANE_STATUSDRAWN 0x400
 #define PANE_EMPTY 0x800
 #define PANE_STYLECHANGED 0x1000
-#define PANE_UNSEENCHANGES 0x2000
-#define PANE_REDRAWSCROLLBAR 0x4000
+#define PANE_THEMECHANGED 0x2000
+#define PANE_UNSEENCHANGES 0x4000
+#define PANE_REDRAWSCROLLBAR 0x8000
 
 	u_int		 sb_slider_y;
 	u_int		 sb_slider_h;
@@ -1863,6 +1871,16 @@ struct overlay_ranges {
 	u_int	nx[OVERLAY_MAX_RANGES];
 };
 
+/*
+ * Client theme, this is worked out from the background colour if not reported
+ * by terminal.
+ */
+enum client_theme {
+	THEME_UNKNOWN,
+	THEME_LIGHT,
+	THEME_DARK
+};
+
 /* Client connection. */
 typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
 typedef void (*prompt_free_cb)(void *);
@@ -1922,6 +1940,7 @@ struct client {
 	struct mouse_event	 click_event;
 
 	struct status_line	 status;
+	enum client_theme	 theme;
 
 #define CLIENT_TERMINAL 0x1
 #define CLIENT_LOGIN 0x2
@@ -2641,6 +2660,7 @@ int		 cmd_find_from_nothing(struct cmd_find_state *, int);
 
 /* cmd.c */
 extern const struct cmd_entry *cmd_table[];
+const struct cmd_entry *cmd_find(const char *, char **);
 void printflike(3, 4) cmd_log_argv(int, char **, const char *, ...);
 void		 cmd_prepend_argv(int *, char ***, const char *);
 void		 cmd_append_argv(int *, char ***, const char *);
@@ -2890,7 +2910,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 printflike(5, 6) status_message_set(struct client *, int, int, int,
+void printflike(6, 7) status_message_set(struct client *, int, int, int, int,
 	     const char *, ...);
 void	 status_message_clear(struct client *);
 int	 status_message_redraw(struct client *);
@@ -2942,7 +2962,8 @@ int	 colour_join_rgb(u_char, u_char, u_char);
 void	 colour_split_rgb(int, u_char *, u_char *, u_char *);
 int	 colour_force_rgb(int);
 const char *colour_tostring(int);
-int	 colour_fromstring(const char *s);
+enum client_theme colour_totheme(int);
+int	 colour_fromstring(const char *);
 int	 colour_256toRGB(int);
 int	 colour_256to16(int);
 int	 colour_byname(const char *);
@@ -3243,6 +3264,13 @@ void		 window_set_fill_character(struct window *);
 void		 window_pane_default_cursor(struct window_pane *);
 int		 window_pane_mode(struct window_pane *);
 int		 window_pane_show_scrollbar(struct window_pane *, int);
+int		 window_pane_get_bg(struct window_pane *);
+int		 window_pane_get_fg(struct window_pane *);
+int		 window_pane_get_fg_control_client(struct window_pane *);
+int		 window_pane_get_bg_control_client(struct window_pane *);
+int		 window_get_bg_client(struct window_pane *);
+enum client_theme window_pane_get_theme(struct window_pane *);
+void		 window_pane_send_theme_update(struct window_pane *);
 
 /* layout.c */
 u_int		 layout_count_cells(struct layout_cell *);
@@ -3440,11 +3468,11 @@ void		 session_group_synchronize_from(struct session *);
 u_int		 session_group_count(struct session_group *);
 u_int		 session_group_attached_count(struct session_group *);
 void		 session_renumber_windows(struct session *);
+void		 session_theme_changed(struct session *);
 
 /* utf8.c */
 enum utf8_state	 utf8_towc (const struct utf8_data *, wchar_t *);
 enum utf8_state	 utf8_fromwc(wchar_t wc, struct utf8_data *);
-int		 utf8_in_table(wchar_t, const wchar_t *, u_int);
 void		 utf8_update_width_cache(void);
 utf8_char	 utf8_build_one(u_char);
 enum utf8_state	 utf8_from_data(const struct utf8_data *, utf8_char *);
diff --git a/tty-keys.c b/tty-keys.c
index 0de31c5d..7b0da5a2 100644
--- a/tty-keys.c
+++ b/tty-keys.c
@@ -208,11 +208,15 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
 	{ "\033[O", KEYC_FOCUS_OUT },
 
 	/* Paste keys. */
-	{ "\033[200~", KEYC_PASTE_START },
-	{ "\033[201~", KEYC_PASTE_END },
+	{ "\033[200~", KEYC_PASTE_START|KEYC_IMPLIED_META },
+	{ "\033[201~", KEYC_PASTE_END|KEYC_IMPLIED_META },
 
 	/* Extended keys. */
 	{ "\033[1;5Z", '\011'|KEYC_CTRL|KEYC_SHIFT },
+
+	/* Theme reporting. */
+	{ "\033[?997;1n", KEYC_REPORT_DARK_THEME },
+	{ "\033[?997;2n", KEYC_REPORT_LIGHT_THEME },
 };
 
 /* Default xterm keys. */
@@ -791,10 +795,12 @@ tty_keys_next(struct tty *tty)
 	switch (tty_keys_colours(tty, buf, len, &size, &tty->fg, &tty->bg)) {
 	case 0:		/* yes */
 		key = KEYC_UNKNOWN;
+		session_theme_changed(tty->client->session);
 		goto complete_key;
 	case -1:	/* no, or not valid */
 		break;
 	case 1:		/* partial */
+		session_theme_changed(tty->client->session);
 		goto partial_key;
 	}
 
@@ -931,6 +937,11 @@ partial_key:
 	delay = options_get_number(global_options, "escape-time");
 	if (delay == 0)
 		delay = 1;
+	if ((tty->flags & TTY_ALL_REQUEST_FLAGS) != TTY_ALL_REQUEST_FLAGS) {
+		log_debug("%s: increasing delay for active DA query", c->name);
+		if (delay < 500)
+			delay = 500;
+	}
 	tv.tv_sec = delay / 1000;
 	tv.tv_usec = (delay % 1000) * 1000L;
 
diff --git a/tty.c b/tty.c
index ecca6cd9..ea2fa941 100644
--- a/tty.c
+++ b/tty.c
@@ -356,6 +356,11 @@ tty_start_tty(struct tty *tty)
 	if (tty_term_has(tty->term, TTYC_ENBP))
 		tty_putcode(tty, TTYC_ENBP);
 
+	if (tty->term->flags & TERM_VT100LIKE) {
+		/* Subscribe to theme changes and request theme now. */
+		tty_puts(tty, "\033[?2031h\033[?996n");
+	}
+
 	evtimer_set(&tty->start_timer, tty_start_timer_callback, tty);
 	evtimer_add(&tty->start_timer, &tv);
 
@@ -468,6 +473,9 @@ tty_stop_tty(struct tty *tty)
 		tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG));
 	tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
 
+	if (tty->term->flags & TERM_VT100LIKE)
+		tty_raw(tty, "\033[?2031l");
+
 	setblocking(c->fd, 1);
 }
 
diff --git a/window-copy.c b/window-copy.c
index 1cda6d39..3a4f59bb 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -40,6 +40,7 @@ static void	window_copy_free(struct window_mode_entry *);
 static void	window_copy_resize(struct window_mode_entry *, u_int, u_int);
 static void	window_copy_formats(struct window_mode_entry *,
 		    struct format_tree *);
+static struct screen *window_copy_get_screen(struct window_mode_entry *);
 static void	window_copy_scroll1(struct window_mode_entry *,
 		    struct window_pane *wp, int, u_int, int);
 static void	window_copy_pageup1(struct window_mode_entry *, int);
@@ -160,6 +161,7 @@ const struct window_mode window_copy_mode = {
 	.key_table = window_copy_key_table,
 	.command = window_copy_command,
 	.formats = window_copy_formats,
+	.get_screen = window_copy_get_screen
 };
 
 const struct window_mode window_view_mode = {
@@ -171,6 +173,7 @@ const struct window_mode window_view_mode = {
 	.key_table = window_copy_key_table,
 	.command = window_copy_command,
 	.formats = window_copy_formats,
+	.get_screen = window_copy_get_screen
 };
 
 enum {
@@ -972,6 +975,14 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft)
 	    window_copy_cursor_hyperlink_cb);
 }
 
+static struct screen *
+window_copy_get_screen(struct window_mode_entry *wme)
+{
+	struct window_copy_mode_data	*data = wme->data;
+
+	return (data->backing);
+}
+
 static void
 window_copy_size_changed(struct window_mode_entry *wme)
 {
@@ -4252,6 +4263,8 @@ window_copy_match_at_cursor(struct window_copy_mode_data *data)
 			buf = xrealloc(buf, len + 2);
 			buf[len] = '\t';
 			len++;
+		} else if (gc.flags & GRID_FLAG_PADDING) {
+			/* nothing to do */
 		} else {
 			buf = xrealloc(buf, len + gc.data.size + 1);
 			memcpy(buf + len, gc.data.data, gc.data.size);
diff --git a/window-customize.c b/window-customize.c
index 387254e0..c49e57af 100644
--- a/window-customize.c
+++ b/window-customize.c
@@ -1000,7 +1000,7 @@ window_customize_set_option_callback(struct client *c, void *itemdata,
 
 fail:
 	*cause = toupper((u_char)*cause);
-	status_message_set(c, -1, 1, 0, "%s", cause);
+	status_message_set(c, -1, 1, 0, 0, "%s", cause);
 	free(cause);
 	return (0);
 }
@@ -1203,7 +1203,7 @@ window_customize_set_command_callback(struct client *c, void *itemdata,
 
 fail:
 	*error = toupper((u_char)*error);
-	status_message_set(c, -1, 1, 0, "%s", error);
+	status_message_set(c, -1, 1, 0, 0, "%s", error);
 	free(error);
 	return (0);
 }
diff --git a/window.c b/window.c
index 11a00172..bc4d6afb 100644
--- a/window.c
+++ b/window.c
@@ -70,6 +70,8 @@ struct window_pane_input_data {
 static struct window_pane *window_pane_create(struct window *, u_int, u_int,
 		    u_int);
 static void	window_pane_destroy(struct window_pane *);
+static void	window_pane_full_size_offset(struct window_pane *wp,
+		    u_int *xoff, u_int *yoff, u_int *sx, u_int *sy);
 
 RB_GENERATE(windows, window, entry, window_cmp);
 RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
@@ -591,34 +593,15 @@ struct window_pane *
 window_get_active_at(struct window *w, u_int x, u_int y)
 {
 	struct window_pane	*wp;
-	int			 pane_scrollbars;
-	u_int			 sb_pos, sb_w, xoff, sx;
-
-	pane_scrollbars = options_get_number(w->options, "pane-scrollbars");
-	sb_pos = options_get_number(w->options, "pane-scrollbars-position");
+	u_int			 xoff, yoff, sx, sy;
 
 	TAILQ_FOREACH(wp, &w->panes, entry) {
 		if (!window_pane_visible(wp))
 			continue;
-
-		if (pane_scrollbars == PANE_SCROLLBARS_ALWAYS ||
-		    (pane_scrollbars == PANE_SCROLLBARS_MODAL &&
-		     window_pane_mode(wp) != WINDOW_PANE_NO_MODE)) {
-			sb_w = wp->scrollbar_style.width +
-			    wp->scrollbar_style.pad;
-		} else
-			sb_w = 0;
-
-		if (sb_pos == PANE_SCROLLBARS_LEFT) {
-			xoff = wp->xoff - sb_w;
-			sx = wp->sx + sb_w;
-		} else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
-			xoff = wp->xoff;
-			sx = wp->sx + sb_w;
-		}
+		window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
 		if (x < xoff || x > xoff + sx)
 			continue;
-		if (y < wp->yoff || y > wp->yoff + wp->sy)
+		if (y < yoff || y > yoff + sy)
 			continue;
 		return (wp);
 	}
@@ -950,7 +933,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
 	wp = xcalloc(1, sizeof *wp);
 	wp->window = w;
 	wp->options = options_create(w->options);
-	wp->flags = PANE_STYLECHANGED;
+	wp->flags = (PANE_STYLECHANGED|PANE_THEMECHANGED);
 
 	wp->id = next_window_pane_id++;
 	RB_INSERT(window_pane_tree, &all_window_panes, wp);
@@ -1359,6 +1342,36 @@ window_pane_choose_best(struct window_pane **list, u_int size)
 	return (best);
 }
 
+/*
+ * Get full size and offset of a window pane including the area of the
+ * scrollbars if they were visible but not including the border(s).
+ */
+static void
+window_pane_full_size_offset(struct window_pane *wp, u_int *xoff, u_int *yoff,
+    u_int *sx, u_int *sy)
+{
+	struct window		*w = wp->window;
+	int			 pane_scrollbars;
+	u_int			 sb_w, sb_pos;
+
+	pane_scrollbars = options_get_number(w->options, "pane-scrollbars");
+	sb_pos = options_get_number(w->options, "pane-scrollbars-position");
+
+	if (window_pane_show_scrollbar(wp, pane_scrollbars))
+		sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
+	else
+		sb_w = 0;
+	if (sb_pos == PANE_SCROLLBARS_LEFT) {
+		*xoff = wp->xoff - sb_w;
+		*sx = wp->sx + sb_w;
+	} else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
+		*xoff = wp->xoff;
+		*sx = wp->sx + sb_w;
+	}
+	*yoff = wp->yoff;
+	*sy = wp->sy;
+}
+
 /*
  * Find the pane directly above another. We build a list of those adjacent to
  * top edge and then choose the best.
@@ -1370,6 +1383,7 @@ window_pane_find_up(struct window_pane *wp)
 	struct window_pane	*next, *best, **list;
 	u_int			 edge, left, right, end, size;
 	int			 status, found;
+	u_int			 xoff, yoff, sx, sy;
 
 	if (wp == NULL)
 		return (NULL);
@@ -1379,7 +1393,9 @@ window_pane_find_up(struct window_pane *wp)
 	list = NULL;
 	size = 0;
 
-	edge = wp->yoff;
+	window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
+
+	edge = yoff;
 	if (status == PANE_STATUS_TOP) {
 		if (edge == 1)
 			edge = w->sy + 1;
@@ -1391,20 +1407,21 @@ window_pane_find_up(struct window_pane *wp)
 			edge = w->sy + 1;
 	}
 
-	left = wp->xoff;
-	right = wp->xoff + wp->sx;
+	left = xoff;
+	right = xoff + sx;
 
 	TAILQ_FOREACH(next, &w->panes, entry) {
+		window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy);
 		if (next == wp)
 			continue;
-		if (next->yoff + next->sy + 1 != edge)
+		if (yoff + sy + 1 != edge)
 			continue;
-		end = next->xoff + next->sx - 1;
+		end = xoff + sx - 1;
 
 		found = 0;
-		if (next->xoff < left && end > right)
+		if (xoff < left && end > right)
 			found = 1;
-		else if (next->xoff >= left && next->xoff <= right)
+		else if (xoff >= left && xoff <= right)
 			found = 1;
 		else if (end >= left && end <= right)
 			found = 1;
@@ -1427,6 +1444,7 @@ window_pane_find_down(struct window_pane *wp)
 	struct window_pane	*next, *best, **list;
 	u_int			 edge, left, right, end, size;
 	int			 status, found;
+	u_int			 xoff, yoff, sx, sy;
 
 	if (wp == NULL)
 		return (NULL);
@@ -1436,7 +1454,9 @@ window_pane_find_down(struct window_pane *wp)
 	list = NULL;
 	size = 0;
 
-	edge = wp->yoff + wp->sy + 1;
+	window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
+
+	edge = yoff + sy + 1;
 	if (status == PANE_STATUS_TOP) {
 		if (edge >= w->sy)
 			edge = 1;
@@ -1452,16 +1472,17 @@ window_pane_find_down(struct window_pane *wp)
 	right = wp->xoff + wp->sx;
 
 	TAILQ_FOREACH(next, &w->panes, entry) {
+		window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy);
 		if (next == wp)
 			continue;
-		if (next->yoff != edge)
+		if (yoff != edge)
 			continue;
-		end = next->xoff + next->sx - 1;
+		end = xoff + sx - 1;
 
 		found = 0;
-		if (next->xoff < left && end > right)
+		if (xoff < left && end > right)
 			found = 1;
-		else if (next->xoff >= left && next->xoff <= right)
+		else if (xoff >= left && xoff <= right)
 			found = 1;
 		else if (end >= left && end <= right)
 			found = 1;
@@ -1484,6 +1505,7 @@ window_pane_find_left(struct window_pane *wp)
 	struct window_pane	*next, *best, **list;
 	u_int			 edge, top, bottom, end, size;
 	int			 found;
+	u_int			 xoff, yoff, sx, sy;
 
 	if (wp == NULL)
 		return (NULL);
@@ -1492,24 +1514,27 @@ window_pane_find_left(struct window_pane *wp)
 	list = NULL;
 	size = 0;
 
-	edge = wp->xoff;
+	window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
+
+	edge = xoff;
 	if (edge == 0)
 		edge = w->sx + 1;
 
-	top = wp->yoff;
-	bottom = wp->yoff + wp->sy;
+	top = yoff;
+	bottom = yoff + sy;
 
 	TAILQ_FOREACH(next, &w->panes, entry) {
+		window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy);
 		if (next == wp)
 			continue;
-		if (next->xoff + next->sx + 1 != edge)
+		if (xoff + sx + 1 != edge)
 			continue;
-		end = next->yoff + next->sy - 1;
+		end = yoff + sy - 1;
 
 		found = 0;
-		if (next->yoff < top && end > bottom)
+		if (yoff < top && end > bottom)
 			found = 1;
-		else if (next->yoff >= top && next->yoff <= bottom)
+		else if (yoff >= top && yoff <= bottom)
 			found = 1;
 		else if (end >= top && end <= bottom)
 			found = 1;
@@ -1532,6 +1557,7 @@ window_pane_find_right(struct window_pane *wp)
 	struct window_pane	*next, *best, **list;
 	u_int			 edge, top, bottom, end, size;
 	int			 found;
+	u_int			 xoff, yoff, sx, sy;
 
 	if (wp == NULL)
 		return (NULL);
@@ -1540,7 +1566,9 @@ window_pane_find_right(struct window_pane *wp)
 	list = NULL;
 	size = 0;
 
-	edge = wp->xoff + wp->sx + 1;
+	window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
+
+	edge = xoff + sx + 1;
 	if (edge >= w->sx)
 		edge = 0;
 
@@ -1548,16 +1576,17 @@ window_pane_find_right(struct window_pane *wp)
 	bottom = wp->yoff + wp->sy;
 
 	TAILQ_FOREACH(next, &w->panes, entry) {
+		window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy);
 		if (next == wp)
 			continue;
-		if (next->xoff != edge)
+		if (xoff != edge)
 			continue;
-		end = next->yoff + next->sy - 1;
+		end = yoff + sy - 1;
 
 		found = 0;
-		if (next->yoff < top && end > bottom)
+		if (yoff < top && end > bottom)
 			found = 1;
-		else if (next->yoff >= top && next->yoff <= bottom)
+		else if (yoff >= top && yoff <= bottom)
 			found = 1;
 		else if (end >= top && end <= bottom)
 			found = 1;
@@ -1723,6 +1752,8 @@ window_set_fill_character(struct window *w)
 		ud = utf8_fromcstr(value);
 		if (ud != NULL && ud[0].width == 1)
 			w->fill_character = ud;
+		else
+			free(ud);
 	}
 }
 
@@ -1756,3 +1787,162 @@ window_pane_show_scrollbar(struct window_pane *wp, int sb_option)
 		return (1);
 	return (0);
 }
+
+int
+window_pane_get_bg(struct window_pane *wp)
+{
+	int			c;
+	struct grid_cell	defaults;
+
+	c = window_pane_get_bg_control_client(wp);
+	if (c == -1) {
+		tty_default_colours(&defaults, wp);
+		if (COLOUR_DEFAULT(defaults.bg))
+			c = window_get_bg_client(wp);
+		else
+			c = defaults.bg;
+	}
+	return (c);
+}
+
+/* Get a client with a background for the pane. */
+int
+window_get_bg_client(struct window_pane *wp)
+{
+	struct window	*w = wp->window;
+	struct client	*loop;
+
+	TAILQ_FOREACH(loop, &clients, entry) {
+		if (loop->flags & CLIENT_UNATTACHEDFLAGS)
+			continue;
+		if (loop->session == NULL || !session_has(loop->session, w))
+			continue;
+		if (loop->tty.bg == -1)
+			continue;
+		return (loop->tty.bg);
+	}
+	return (-1);
+}
+
+/*
+ * If any control mode client exists that has provided a bg color, return it.
+ * Otherwise, return -1.
+ */
+int
+window_pane_get_bg_control_client(struct window_pane *wp)
+{
+	struct client	*c;
+
+	if (wp->control_bg == -1)
+		return (-1);
+
+	TAILQ_FOREACH(c, &clients, entry) {
+		if (c->flags & CLIENT_CONTROL)
+			return (wp->control_bg);
+	}
+	return (-1);
+}
+
+/*
+ * Get a client with a foreground for the pane. There isn't much to choose
+ * between them so just use the first.
+ */
+int
+window_pane_get_fg(struct window_pane *wp)
+{
+	struct window	*w = wp->window;
+	struct client	*loop;
+
+	TAILQ_FOREACH(loop, &clients, entry) {
+		if (loop->flags & CLIENT_UNATTACHEDFLAGS)
+			continue;
+		if (loop->session == NULL || !session_has(loop->session, w))
+			continue;
+		if (loop->tty.fg == -1)
+			continue;
+		return (loop->tty.fg);
+	}
+	return (-1);
+}
+
+/*
+ * If any control mode client exists that has provided a fg color, return it.
+ * Otherwise, return -1.
+ */
+int
+window_pane_get_fg_control_client(struct window_pane *wp)
+{
+	struct client	*c;
+
+	if (wp->control_fg == -1)
+		return (-1);
+
+	TAILQ_FOREACH(c, &clients, entry) {
+		if (c->flags & CLIENT_CONTROL)
+			return (wp->control_fg);
+	}
+	return (-1);
+}
+
+enum client_theme
+window_pane_get_theme(struct window_pane *wp)
+{
+	struct window		*w = wp->window;
+	struct client		*loop;
+	enum client_theme	 theme;
+	int			 found_light = 0, found_dark = 0;
+
+	/*
+	 * Derive theme from pane background color, if it's not the default
+	 * colour.
+	 */
+	theme = colour_totheme(window_pane_get_bg(wp));
+	if (theme != THEME_UNKNOWN)
+		return (theme);
+
+	/* Try to find a client that has a theme. */
+	TAILQ_FOREACH(loop, &clients, entry) {
+		if (loop->flags & CLIENT_UNATTACHEDFLAGS)
+			continue;
+		if (loop->session == NULL || !session_has(loop->session, w))
+			continue;
+		switch (loop->theme) {
+		case THEME_LIGHT:
+			found_light = 1;
+			break;
+		case THEME_DARK:
+			found_dark = 1;
+			break;
+		case THEME_UNKNOWN:
+			break;
+		}
+	}
+
+	if (found_dark && !found_light)
+		return (THEME_DARK);
+	if (found_light && !found_dark)
+		return (THEME_LIGHT);
+	return (THEME_UNKNOWN);
+}
+
+void
+window_pane_send_theme_update(struct window_pane *wp)
+{
+	if (~wp->flags & PANE_THEMECHANGED)
+		return;
+	if (~wp->screen->mode & MODE_THEME_UPDATES)
+		return;
+
+	switch (window_pane_get_theme(wp)) {
+	case THEME_LIGHT:
+		input_key_pane(wp, KEYC_REPORT_LIGHT_THEME, NULL);
+		break;
+	case THEME_DARK:
+		input_key_pane(wp, KEYC_REPORT_DARK_THEME, NULL);
+		break;
+	case THEME_UNKNOWN:
+		break;
+	}
+
+	wp->flags &= ~PANE_THEMECHANGED;
+}