From 27ee0c9c3bddcc7ab4ccc8b4b85f83a8e186781f Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Wed, 26 Feb 2025 07:39:50 +0000
Subject: [PATCH 1/6] 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 2/6] 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 3/6] 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 4/6] 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 5/6] 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 6/6] 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,