From dd7d04be95f9c08fab3a277e0356eaecd0810fa7 Mon Sep 17 00:00:00 2001
From: Nicholas Marriott <nicholas.marriott@gmail.com>
Date: Mon, 20 Jan 2025 13:00:07 +0000
Subject: [PATCH 01/25] Update issue templates

---
 .github/ISSUE_TEMPLATE/custom.md | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)
 create mode 100644 .github/ISSUE_TEMPLATE/custom.md

diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md
new file mode 100644
index 00000000..0aeab3a8
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/custom.md
@@ -0,0 +1,31 @@
+---
+name: Custom issue template
+about: Describe this issue template's purpose here.
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+### Issue description
+
+Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
+before opening an issue.
+
+If you have upgraded, make sure your issue is not covered in the CHANGES file
+for your version: https://raw.githubusercontent.com/tmux/tmux/master/CHANGES
+
+Describe the problem and the steps to reproduce. Add a minimal tmux config if
+necessary. Screenshots can be helpful, but no more than one or two.
+
+Do not report bugs (crashes, incorrect behaviour) without reproducing on a tmux
+built from the latest code in Git.
+
+### Required information
+
+Please provide the following information:
+
+* tmux version (`tmux -V`).
+* Platform (`uname -sp`).
+* $TERM inside and outside of tmux (`echo $TERM`).
+* Logs from tmux (`tmux kill-server; tmux -vv new`).

From 0c9f8ff18950a70309411dd61768cb272b55c68e Mon Sep 17 00:00:00 2001
From: Nicholas Marriott <nicholas.marriott@gmail.com>
Date: Mon, 20 Jan 2025 13:02:24 +0000
Subject: [PATCH 02/25] Update issue templates

---
 .../ISSUE_TEMPLATE/{custom.md => use-this-issue-template.md}  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
 rename .github/ISSUE_TEMPLATE/{custom.md => use-this-issue-template.md} (87%)

diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/use-this-issue-template.md
similarity index 87%
rename from .github/ISSUE_TEMPLATE/custom.md
rename to .github/ISSUE_TEMPLATE/use-this-issue-template.md
index 0aeab3a8..bc95fa2e 100644
--- a/.github/ISSUE_TEMPLATE/custom.md
+++ b/.github/ISSUE_TEMPLATE/use-this-issue-template.md
@@ -1,6 +1,6 @@
 ---
-name: Custom issue template
-about: Describe this issue template's purpose here.
+name: Use this issue template
+about: Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
 title: ''
 labels: ''
 assignees: ''

From 6ab268c7bb8b6941796ee6f2ad4b628f54a4f28d Mon Sep 17 00:00:00 2001
From: Nicholas Marriott <nicholas.marriott@gmail.com>
Date: Mon, 20 Jan 2025 13:04:08 +0000
Subject: [PATCH 03/25] Remove old issue template.

---
 .github/ISSUE_TEMPLATE.md | 22 ----------------------
 1 file changed, 22 deletions(-)
 delete mode 100644 .github/ISSUE_TEMPLATE.md

diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index 8bf1e66a..00000000
--- a/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,22 +0,0 @@
-### Issue description
-
-Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
-before opening an issue.
-
-If you have upgraded, make sure your issue is not covered in the CHANGES file
-for your version: https://raw.githubusercontent.com/tmux/tmux/master/CHANGES
-
-Describe the problem and the steps to reproduce. Add a minimal tmux config if
-necessary. Screenshots can be helpful, but no more than one or two.
-
-Do not report bugs (crashes, incorrect behaviour) without reproducing on a tmux
-built from the latest code in Git.
-
-### Required information
-
-Please provide the following information:
-
-* tmux version (`tmux -V`).
-* Platform (`uname -sp`).
-* $TERM inside and outside of tmux (`echo $TERM`).
-* Logs from tmux (`tmux kill-server; tmux -vv new`).

From 244bb726e26d7e590b04b7f57dd75fc431b2a492 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Mon, 27 Jan 2025 09:05:22 +0000
Subject: [PATCH 04/25] Add some missing spaces, from Ilya Grigoriev.

---
 cmd-display-menu.c | 4 ++--
 cmd-split-window.c | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/cmd-display-menu.c b/cmd-display-menu.c
index 73728b91..ac136766 100644
--- a/cmd-display-menu.c
+++ b/cmd-display-menu.c
@@ -42,7 +42,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 },
@@ -59,7 +59,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-split-window.c b/cmd-split-window.c
index 82ff22af..128e9e8b 100644
--- a/cmd-split-window.c
+++ b/cmd-split-window.c
@@ -43,7 +43,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 },
 

From 4c12ac9fb80f8f8d2096ba2463b448e9865b70cd Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Mon, 27 Jan 2025 09:16:05 +0000
Subject: [PATCH 05/25] Make list-commands command show only one command if an
 argument is given, from Ilya Grigoriev in GitHub issue 4352.

---
 cmd-list-keys.c | 68 +++++++++++++++++++++++++++++--------------------
 cmd.c           |  2 +-
 tmux.h          |  1 +
 3 files changed, 43 insertions(+), 28 deletions(-)

diff --git a/cmd-list-keys.c b/cmd-list-keys.c
index 395b147c..b5050f09 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)
@@ -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.c b/cmd.c
index 9e2ce036..64f63c92 100644
--- a/cmd.c
+++ b/cmd.c
@@ -445,7 +445,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/tmux.h b/tmux.h
index 558bc009..1b6d889a 100644
--- a/tmux.h
+++ b/tmux.h
@@ -2597,6 +2597,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 *);

From 80eb460fc919e843c4fc53a08c2df84fa1654b98 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Mon, 10 Feb 2025 08:14:32 +0000
Subject: [PATCH 06/25] Add display-message -C flag to update pane while
 message is displayed, GitHub issue 4363 from Vitaly Ostrosablin.

---
 alerts.c              | 8 ++++----
 cmd-display-message.c | 7 ++++---
 cmd-if-shell.c        | 2 +-
 cmd-list-keys.c       | 8 ++++----
 cmd-queue.c           | 2 +-
 cmd-run-shell.c       | 2 +-
 mode-tree.c           | 2 +-
 status.c              | 6 ++++--
 tmux.1                | 5 ++++-
 tmux.h                | 2 +-
 window-customize.c    | 4 ++--
 11 files changed, 27 insertions(+), 21 deletions(-)

diff --git a/alerts.c b/alerts.c
index b5ea0cf4..a2b4d654 100644
--- a/alerts.c
+++ b/alerts.c
@@ -316,11 +316,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/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-list-keys.c b/cmd-list-keys.c
index b5050f09..ddfc0e0c 100644
--- a/cmd-list-keys.c
+++ b/cmd-list-keys.c
@@ -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);
diff --git a/cmd-queue.c b/cmd-queue.c
index 60d685f8..a6dfc592 100644
--- a/cmd-queue.c
+++ b/cmd-queue.c
@@ -893,7 +893,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/mode-tree.c b/mode-tree.c
index e1170a3d..d43cf1b3 100644
--- a/mode-tree.c
+++ b/mode-tree.c
@@ -1297,7 +1297,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/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 2cb3b3c6..6f9c68e1 100644
--- a/tmux.1
+++ b/tmux.1
@@ -6742,7 +6742,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
@@ -6765,6 +6765,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
+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 1b6d889a..065bb042 100644
--- a/tmux.h
+++ b/tmux.h
@@ -2847,7 +2847,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 *);
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);
 }

From 5d1a6acc84bb024aadf4288b4e3a9e7cc0289e83 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Mon, 10 Feb 2025 08:18:23 +0000
Subject: [PATCH 07/25] Align index numbers in trees, from David Mandelberg,
 GitHub issue 4360.

---
 mode-tree.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/mode-tree.c b/mode-tree.c
index d43cf1b3..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;

From c4b9716873a0a5ecc68697c73793eaa553192d1b Mon Sep 17 00:00:00 2001
From: Nicholas Marriott <nicholas.marriott@gmail.com>
Date: Thu, 13 Feb 2025 16:24:16 +0000
Subject: [PATCH 08/25] Look for imsg_add not _init now.

---
 configure.ac | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

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)

From 47a56c11f26a66c97ce90af97f20a7b724dd515d Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Thu, 13 Feb 2025 16:31:25 +0000
Subject: [PATCH 09/25] Add a note about C-r and C-s behaviour, GitHub issue
 4309.

Also add a missing word, from jmc@.
---
 tmux.1 | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/tmux.1 b/tmux.1
index 6f9c68e1..ed6e7a53 100644
--- a/tmux.1
+++ b/tmux.1
@@ -2329,6 +2329,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
@@ -6766,7 +6775,7 @@ option is used; a delay of zero waits for a key press.
 ignores key presses and closes only after the delay expires.
 If
 .Fl C
-given, the pane will continue to be updated while the message is displayed.
+is given, the pane will continue to be updated while the message is displayed.
 If
 .Fl l
 is given,

From 250c88efdccd892bdbc99bfb565ace6fa90e5d0d Mon Sep 17 00:00:00 2001
From: Nicholas Marriott <nicholas.marriott@gmail.com>
Date: Thu, 20 Feb 2025 13:30:26 +0000
Subject: [PATCH 10/25] Add .swp, from Nikola Tadic.

---
 .gitignore | 35 ++++++++++++++++++-----------------
 1 file changed, 18 insertions(+), 17 deletions(-)

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.*

From 18331e39bf7e88132fd0f1eafb4424347b6efb4a Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Thu, 20 Feb 2025 13:32:07 +0000
Subject: [PATCH 11/25] Reset overlay_resize pointer when clearing overlay.

---
 server-client.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/server-client.c b/server-client.c
index 4581e9bc..847a8576 100644
--- a/server-client.c
+++ b/server-client.c
@@ -156,6 +156,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);

From 084e6ee9ec0cc58a8f3e673858c74a2b3952081b Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Thu, 20 Feb 2025 13:39:58 +0000
Subject: [PATCH 12/25] Add a -M flag to capture-pane to use the copy mode
 screen, GitHub issue 4358.

---
 cmd-capture-pane.c | 38 ++++++++++++++++++++++++++------------
 tmux.1             |  5 ++++-
 tmux.h             |  1 +
 window-copy.c      | 11 +++++++++++
 4 files changed, 42 insertions(+), 13 deletions(-)

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/tmux.1 b/tmux.1
index ed6e7a53..5c979758 100644
--- a/tmux.1
+++ b/tmux.1
@@ -2554,7 +2554,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
@@ -2573,6 +2573,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
diff --git a/tmux.h b/tmux.h
index 065bb042..cd93097e 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1059,6 +1059,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. */
diff --git a/window-copy.c b/window-copy.c
index 1cda6d39..7dcc8432 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)
 {

From 27ee0c9c3bddcc7ab4ccc8b4b85f83a8e186781f Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Wed, 26 Feb 2025 07:39:50 +0000
Subject: [PATCH 13/25] Add the width of the scrollbars to the calculation of
 the width of the window panes when finding the adjacent panes, GitHub issue
 4370 from Michael Grant.

---
 window.c | 94 +++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 70 insertions(+), 24 deletions(-)

diff --git a/window.c b/window.c
index 35fc370b..a6354e20 100644
--- a/window.c
+++ b/window.c
@@ -1349,6 +1349,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.
@@ -1360,6 +1390,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);
@@ -1369,7 +1400,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;
@@ -1381,20 +1414,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;
@@ -1417,6 +1451,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);
@@ -1426,7 +1461,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;
@@ -1442,16 +1479,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;
@@ -1474,6 +1512,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);
@@ -1482,24 +1521,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;
@@ -1522,6 +1564,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);
@@ -1530,7 +1573,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;
 
@@ -1538,16 +1583,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;

From 9a8f46e554d848c9b1114a98e14fd17d705f5f84 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Wed, 26 Feb 2025 07:42:52 +0000
Subject: [PATCH 14/25] Fix colouring of pane border when scrollbars are
 enabled, GitHub issue 4378 from Michael Grant.

---
 screen-redraw.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/screen-redraw.c b/screen-redraw.c
index c1e1481c..fb530007 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);

From d938ab5dd7698e9f4dd92986d5b9746b2cd12834 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Wed, 26 Feb 2025 07:47:46 +0000
Subject: [PATCH 15/25] If command parsing fails in the client, report the
 error rather than trying to send the command to the server. GitHub issue 4372
 from Nikola Tadic.

---
 client.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/client.c b/client.c
index fcc49c39..9d16e1e9 100644
--- a/client.c
+++ b/client.c
@@ -267,8 +267,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);
 	}

From f224d61f3781a246a75e48f222755246920adcb9 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Wed, 26 Feb 2025 07:50:36 +0000
Subject: [PATCH 16/25] Document the use of ';' as a modifier separator, from
 Matt Liggett in GitHub issue 4384.

---
 tmux.1 | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/tmux.1 b/tmux.1
index 5c979758..e17a92b3 100644
--- a/tmux.1
+++ b/tmux.1
@@ -5867,6 +5867,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,

From 21f7db4c4dbbc1837667a7d813663036f9288ea6 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Wed, 26 Feb 2025 08:55:27 +0000
Subject: [PATCH 17/25] Do not allow meta prefix on paste start and end
 sequences, GitHub issue 4387.

---
 server-client.c | 4 ++--
 tmux.h          | 3 ++-
 tty-keys.c      | 4 ++--
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/server-client.c b/server-client.c
index 847a8576..7b017697 100644
--- a/server-client.c
+++ b/server-client.c
@@ -2247,13 +2247,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);
diff --git a/tmux.h b/tmux.h
index cd93097e..52b85cb1 100644
--- a/tmux.h
+++ b/tmux.h
@@ -167,7 +167,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
diff --git a/tty-keys.c b/tty-keys.c
index 0de31c5d..77ab4ae1 100644
--- a/tty-keys.c
+++ b/tty-keys.c
@@ -208,8 +208,8 @@ 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 },

From 91c0de60b4fb0962699eaf7c1b32ae441767e963 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Wed, 26 Feb 2025 09:02:00 +0000
Subject: [PATCH 18/25] Also need the implied meta paste keys in the list for
 output.

---
 input-keys.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/input-keys.c b/input-keys.c
index fa4e3f8e..900dea07 100644
--- a/input-keys.c
+++ b/input-keys.c
@@ -54,9 +54,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,

From 3543d79048f4fc0e72c57f109a201baf4ebfaf3e Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Tue, 4 Mar 2025 08:03:19 +0000
Subject: [PATCH 19/25] Free fill character string if it cannot be used, GitHub
 issue 4394.

---
 window.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/window.c b/window.c
index a6354e20..dc220462 100644
--- a/window.c
+++ b/window.c
@@ -1759,6 +1759,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);
 	}
 }
 

From eaf70c955b37560de5a147674b55400c72c27f1e Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Tue, 4 Mar 2025 08:45:04 +0000
Subject: [PATCH 20/25] Add mode 2031 support to automatically report dark or
 light theme. tmux will guess the theme from the background colour on
 terminals which do not themselves support the escape sequence. Written by
 Jonathan Slenders, GitHub issue 4353.

---
 cmd-break-pane.c  |   2 +-
 cmd-join-pane.c   |   2 +-
 cmd-select-pane.c |   8 ++-
 cmd-send-keys.c   |   2 +-
 cmd-swap-pane.c   |   4 +-
 colour.c          |  40 ++++++++++++
 format.c          |  20 ++++++
 input-keys.c      |   6 ++
 input.c           | 139 +++++++++++++--------------------------
 options-table.c   |   2 +
 options.c         |   2 +-
 server-client.c   |  38 ++++++++++-
 session.c         |  13 ++++
 tmux.h            |  34 +++++++++-
 tty-keys.c        |   6 ++
 tty.c             |   8 +++
 window.c          | 161 +++++++++++++++++++++++++++++++++++++++++++++-
 17 files changed, 380 insertions(+), 107 deletions(-)

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-join-pane.c b/cmd-join-pane.c
index 627424ec..3300498f 100644
--- a/cmd-join-pane.c
+++ b/cmd-join-pane.c
@@ -150,7 +150,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-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-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/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/format.c b/format.c
index 7cd5b2a8..39a4a9a7 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)
@@ -2877,6 +2894,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 900dea07..91415064 100644
--- a/input-keys.c
+++ b/input-keys.c
@@ -314,6 +314,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 b6eef671..02bd5458 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 },
@@ -1527,6 +1531,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:
@@ -1777,6 +1795,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;
@@ -1872,6 +1893,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;
@@ -2660,84 +2684,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
@@ -2750,11 +2696,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;
 		}
@@ -2795,20 +2741,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;
 	}
@@ -2820,7 +2758,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);
 	}
 }
@@ -2836,7 +2774,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);
 	}
 }
@@ -3027,3 +2965,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/options-table.c b/options-table.c
index bea0bc4c..6de2fb02 100644
--- a/options-table.c
+++ b/options-table.c
@@ -1455,6 +1455,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/server-client.c b/server-client.c
index 7b017697..8ab00fbf 100644
--- a/server-client.c
+++ b/server-client.c
@@ -60,11 +60,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
@@ -304,6 +304,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;
@@ -405,6 +406,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;
@@ -2388,6 +2390,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);
@@ -2678,6 +2690,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 +3927,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 a3adafdd..e9664183 100644
--- a/session.c
+++ b/session.c
@@ -753,3 +753,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/tmux.h b/tmux.h
index 52b85cb1..9d239510 100644
--- a/tmux.h
+++ b/tmux.h
@@ -369,6 +369,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
 };
@@ -636,6 +640,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)
@@ -1125,8 +1130,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;
@@ -1831,6 +1837,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 *);
@@ -1890,6 +1906,7 @@ struct client {
 	struct mouse_event	 click_event;
 
 	struct status_line	 status;
+	enum client_theme	 theme;
 
 #define CLIENT_TERMINAL 0x1
 #define CLIENT_LOGIN 0x2
@@ -2887,6 +2904,8 @@ void	 input_parse_screen(struct input_ctx *, struct screen *,
 void	 input_reply_clipboard(struct bufferevent *, const char *, size_t,
 	     const char *);
 void	 input_set_buffer_size(size_t);
+int 	 input_get_bg_client(struct window_pane *);
+int 	 input_get_bg_control_client(struct window_pane *);
 
 /* input-key.c */
 void	 input_key_build(void);
@@ -2901,7 +2920,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 *);
@@ -3198,6 +3218,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 *);
@@ -3395,6 +3422,7 @@ 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 *);
diff --git a/tty-keys.c b/tty-keys.c
index 77ab4ae1..45175171 100644
--- a/tty-keys.c
+++ b/tty-keys.c
@@ -213,6 +213,10 @@ static const struct tty_default_key_raw tty_default_raw_keys[] = {
 
 	/* 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;
 	}
 
diff --git a/tty.c b/tty.c
index 0080b7c6..fc74a164 100644
--- a/tty.c
+++ b/tty.c
@@ -351,6 +351,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);
 
@@ -463,6 +468,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.c b/window.c
index dc220462..3bb67f00 100644
--- a/window.c
+++ b/window.c
@@ -943,7 +943,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);
@@ -1794,3 +1794,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;
+}

From 4e4fe3eb391e5614b2cc1c7c5086df0f96165ad3 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Tue, 11 Mar 2025 08:14:26 +0000
Subject: [PATCH 21/25] Cleanup window_get_active_at function. GitHub issue
 4401 from Michael Grant.

---
 window.c | 27 +++++----------------------
 1 file changed, 5 insertions(+), 22 deletions(-)

diff --git a/window.c b/window.c
index 3bb67f00..b1691ac6 100644
--- a/window.c
+++ b/window.c
@@ -72,6 +72,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);
@@ -584,34 +586,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);
 	}

From 5eb30c1543a4f9201a1c6d6039d38c32646bcf0a Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Mon, 17 Mar 2025 20:33:20 +0000
Subject: [PATCH 22/25] Handle padding cells correctly for regular expression
 searching, GitHub issue 4399 from github at jyn dot dev.

---
 window-copy.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/window-copy.c b/window-copy.c
index 7dcc8432..3a4f59bb 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -4263,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);

From 817b621d2078137b4ddea78835f609a9d7bac339 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Mon, 17 Mar 2025 20:43:29 +0000
Subject: [PATCH 23/25] If there is an active query, set escape time
 temporarily to a higher value (the old default - 500). Some Windows terminals
 are very slow to respond, or the network may be slow. From github at jyn dot
 dev.

---
 tty-keys.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/tty-keys.c b/tty-keys.c
index 45175171..7b0da5a2 100644
--- a/tty-keys.c
+++ b/tty-keys.c
@@ -937,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;
 

From 111e16e7726dc7ab63baf47adf8531c8601f9657 Mon Sep 17 00:00:00 2001
From: jsg <jsg>
Date: Fri, 21 Mar 2025 02:10:42 +0000
Subject: [PATCH 24/25] remove prototypes for removed functions

---
 tmux.h | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/tmux.h b/tmux.h
index 9d239510..3a437165 100644
--- a/tmux.h
+++ b/tmux.h
@@ -2904,8 +2904,6 @@ void	 input_parse_screen(struct input_ctx *, struct screen *,
 void	 input_reply_clipboard(struct bufferevent *, const char *, size_t,
 	     const char *);
 void	 input_set_buffer_size(size_t);
-int 	 input_get_bg_client(struct window_pane *);
-int 	 input_get_bg_control_client(struct window_pane *);
 
 /* input-key.c */
 void	 input_key_build(void);
@@ -3427,7 +3425,6 @@ 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 *);

From 969f6a60c3f4c521ba25aea72d130b3353fd95da Mon Sep 17 00:00:00 2001
From: Thomas Adam <thomas.adam22@gmail.com>
Date: Fri, 21 Mar 2025 12:46:06 +0000
Subject: [PATCH 25/25] portable: SYNCING: correct tmux-openbsd

The tmux-openbsd repository is called tmux-obsd.

Noticed via Github issue #4419
---
 SYNCING | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

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: