From 6db6a30ab508c4a924245e2d8ccb7bac39838d5c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 26 Nov 2025 10:41:17 +0000 Subject: [PATCH 01/30] Need signal.h for utempter, from Yasuhiro Kimura. --- server-fn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/server-fn.c b/server-fn.c index 6ab7fa48..29802a60 100644 --- a/server-fn.c +++ b/server-fn.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include From ec4b5b52afa6e98f313896815018926911e39160 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Wed, 26 Nov 2025 19:00:17 +0000 Subject: [PATCH 02/30] Version and CHANGES. --- CHANGES | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index eb0080ec..4abece81 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +CHANGES FROM 3.6 TO 3.6a + +* Fix compile error on FreeBSD (from Yasuhiro Kimura, issue 4701). + CHANGES FROM 3.5a TO 3.6 * Add seconds options for clock mode (issue 4697). diff --git a/configure.ac b/configure.ac index 4be9e03a..200e82de 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # configure.ac -AC_INIT([tmux], 3.6) +AC_INIT([tmux], 3.6a) AC_PREREQ([2.60]) AC_CONFIG_AUX_DIR(etc) From 0af04295f3cc622c711711e1d0b1befd06837493 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 26 Nov 2025 19:02:03 +0000 Subject: [PATCH 03/30] Newer libevents do not allow event_del on a zero'd event. --- tty.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tty.c b/tty.c index 889bee2c..71293f6b 100644 --- a/tty.c +++ b/tty.c @@ -35,6 +35,8 @@ static int tty_log_fd = -1; +static void tty_start_timer_callback(int, short, void *); +static void tty_clipboard_query_callback(int, short, void *); static void tty_set_italics(struct tty *); static int tty_try_colour(struct tty *, int, const char *); static void tty_force_cursor_colour(struct tty *, int); @@ -296,6 +298,8 @@ tty_open(struct tty *tty, char **cause) if (tty->out == NULL) fatal("out of memory"); + evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty); + evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); evtimer_set(&tty->timer, tty_timer_callback, tty); tty_start_tty(tty); @@ -327,7 +331,6 @@ tty_start_start_timer(struct tty *tty) log_debug("%s: start timer started", c->name); evtimer_del(&tty->start_timer); - evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); evtimer_add(&tty->start_timer, &tv); } @@ -445,6 +448,7 @@ tty_stop_tty(struct tty *tty) tty->flags &= ~TTY_STARTED; evtimer_del(&tty->start_timer); + evtimer_del(&tty->clipboard_timer); event_del(&tty->timer); tty->flags &= ~TTY_BLOCK; @@ -3228,6 +3232,5 @@ tty_clipboard_query(struct tty *tty) tty_putcode_ss(tty, TTYC_MS, "", "?"); tty->flags |= TTY_OSC52QUERY; - evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty); evtimer_add(&tty->clipboard_timer, &tv); } From 2a0b078e1519bd710cfee9975bd6b0f885063dac Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 26 Nov 2025 18:57:18 +0000 Subject: [PATCH 04/30] Place cursor on correct line if message-line is not 0, reported by Alexis Hildebrandt. --- server-client.c | 14 +++++++------- status.c | 13 +++++++++---- tmux.h | 1 + 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/server-client.c b/server-client.c index b67ae7c2..047365a1 100644 --- a/server-client.c +++ b/server-client.c @@ -2901,8 +2901,8 @@ server_client_reset_state(struct client *c) struct window_pane *wp = server_client_get_pane(c), *loop; struct screen *s = NULL; struct options *oo = c->session->options; - int mode = 0, cursor, flags, n; - u_int cx = 0, cy = 0, ox, oy, sx, sy; + int mode = 0, cursor, flags; + u_int cx = 0, cy = 0, ox, oy, sx, sy, n; if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; @@ -2934,13 +2934,13 @@ server_client_reset_state(struct client *c) if (c->prompt_string != NULL) { n = options_get_number(oo, "status-position"); if (n == 0) - cy = 0; + cy = status_prompt_line_at(c); else { - n = status_line_size(c); - if (n == 0) - cy = tty->sy - 1; - else + n = status_line_size(c) - status_prompt_line_at(c); + if (n <= tty->sy) cy = tty->sy - n; + else + cy = tty->sy - 1; } cx = c->prompt_cursor; } else if (c->overlay_draw == NULL) { diff --git a/status.c b/status.c index 2786db7e..0551b547 100644 --- a/status.c +++ b/status.c @@ -264,14 +264,19 @@ status_line_size(struct client *c) } /* Get the prompt line number for client's session. 1 means at the bottom. */ -static u_int +u_int status_prompt_line_at(struct client *c) { struct session *s = c->session; + u_int line, lines; - if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL)) - return (1); - return (options_get_number(s->options, "message-line")); + lines = status_line_size(c); + if (lines == 0) + return (0); + line = options_get_number(s->options, "message-line"); + if (line >= lines) + return (lines - 1); + return (line); } /* Get window at window list position. */ diff --git a/tmux.h b/tmux.h index b2f10ca7..2184e6c6 100644 --- a/tmux.h +++ b/tmux.h @@ -2943,6 +2943,7 @@ extern u_int status_prompt_hsize[]; void status_timer_start(struct client *); void status_timer_start_all(void); void status_update_cache(struct session *); +u_int status_prompt_line_at(struct client *); int status_at_line(struct client *); u_int status_line_size(struct client *); struct style_range *status_get_range(struct client *, u_int, u_int); From 640e1a76438317303aefcd8b76d35a11e59594f6 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 27 Nov 2025 07:26:09 +0000 Subject: [PATCH 05/30] Update CHANGES. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index 4abece81..d6b3013e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,9 @@ CHANGES FROM 3.6 TO 3.6a +* Newer libevents do not allow event_del on a zero'd event (issue 4706). + +* Place cursor on correct line if message-line is not 0 (issue 4707)). + * Fix compile error on FreeBSD (from Yasuhiro Kimura, issue 4701). CHANGES FROM 3.5a TO 3.6 From dfaf47d97c3ddc1f2879414ce32d91cf7b0e70ce Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Nov 2025 09:14:17 +0000 Subject: [PATCH 06/30] session_index was never actually implemented, remove from man page. --- tmux.1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/tmux.1 b/tmux.1 index 51fb4002..832c0881 100644 --- a/tmux.1 +++ b/tmux.1 @@ -6133,7 +6133,6 @@ The following variables are available, where appropriate: .It Li "insert_flag" Ta "" Ta "Pane insert flag" .It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag" .It Li "keypad_flag" Ta "" Ta "Pane keypad flag" -.It Li "last_session_index" Ta "" Ta "Index of last session" .It Li "last_window_index" Ta "" Ta "Index of last window in session" .It Li "line" Ta "" Ta "Line number in the list" .It Li "loop_last_flag" Ta "" Ta "1 if last window, pane, session, client in the W:, P:, S:, or L: loop" @@ -6225,7 +6224,6 @@ The following variables are available, where appropriate: .It Li "session_group_size" Ta "" Ta "Size of session group" .It Li "session_grouped" Ta "" Ta "1 if session in a group" .It Li "session_id" Ta "" Ta "Unique session ID" -.It Li "session_index" Ta "" Ta "Index of session" .It Li "session_last_attached" Ta "" Ta "Time session last attached" .It Li "session_many_attached" Ta "" Ta "1 if multiple clients attached" .It Li "session_marked" Ta "" Ta "1 if this session contains the marked pane" From 55d660a548cbeb8cb3b6975cc67fa1b0b031a4e8 Mon Sep 17 00:00:00 2001 From: nicm Date: Fri, 28 Nov 2025 09:42:48 +0000 Subject: [PATCH 07/30] Do not remove TERM etc for commands run from config file, reported by Dennis Eriksen. --- environ.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/environ.c b/environ.c index fb1c4902..0b4b3c31 100644 --- a/environ.c +++ b/environ.c @@ -263,11 +263,6 @@ environ_for_session(struct session *s, int no_TERM) environ_set(env, "TERM_PROGRAM", 0, "%s", "tmux"); environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion()); environ_set(env, "COLORTERM", 0, "truecolor"); - } else { - environ_unset(env, "TERM"); - environ_unset(env, "TERM_PROGRAM"); - environ_unset(env, "TERM_PROGRAM_VERSION"); - environ_unset(env, "COLORTERM"); } if (s != NULL) From 04f32073c0e1cea022634823b4fca5e2afd95405 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Dec 2025 08:04:26 +0000 Subject: [PATCH 08/30] Add horizontal border case to server_client_check_mouse_in_pane to fix mouse resizing. GitHub issue 4720 from Michael Grant, reported by someone in GitHub issue 4715. --- server-client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index 184717a4..a48b9802 100644 --- a/server-client.c +++ b/server-client.c @@ -617,7 +617,8 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py, line = wp->yoff + wp->sy; /* Check if point is within the pane or scrollbar. */ - if (((pane_status != PANE_STATUS_OFF && py != line) || + if (((pane_status != PANE_STATUS_OFF && + py != line && py != wp->yoff + wp->sy) || (wp->yoff == 0 && py < wp->sy) || (py >= wp->yoff && py < wp->yoff + wp->sy)) && ((sb_pos == PANE_SCROLLBARS_RIGHT && From e4c552f5a5526009d84917c0bc129e7503fe7590 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Dec 2025 08:14:29 +0000 Subject: [PATCH 09/30] Change noattr to be an explicit attribute in the style so that it works correctly and does not delete attributes set in the style itself, GitHub issue 4713. --- attributes.c | 5 +++-- screen.c | 6 +++++- style.c | 35 +++++++++++++++++++---------------- tmux.h | 1 + 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/attributes.c b/attributes.c index b839f06d..8eaa8897 100644 --- a/attributes.c +++ b/attributes.c @@ -31,7 +31,7 @@ attributes_tostring(int attr) if (attr == 0) return ("none"); - len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", (attr & GRID_ATTR_CHARSET) ? "acs," : "", (attr & GRID_ATTR_BRIGHT) ? "bright," : "", (attr & GRID_ATTR_DIM) ? "dim," : "", @@ -45,7 +45,8 @@ attributes_tostring(int attr) (attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "", (attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "", (attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "", - (attr & GRID_ATTR_OVERLINE) ? "overline," : ""); + (attr & GRID_ATTR_OVERLINE) ? "overline," : "", + (attr & GRID_ATTR_NOATTR) ? "noattr," : ""); if (len > 0) buf[len - 1] = '\0'; diff --git a/screen.c b/screen.c index 626356b3..2b73cbce 100644 --- a/screen.c +++ b/screen.c @@ -577,8 +577,12 @@ screen_select_cell(struct screen *s, struct grid_cell *dst, if (COLOUR_DEFAULT(dst->bg)) dst->bg = src->bg; utf8_copy(&dst->data, &src->data); - dst->attr = src->attr; dst->flags = src->flags; + + if (dst->attr & GRID_ATTR_NOATTR) + dst->attr |= (src->attr & GRID_ATTR_CHARSET); + else + dst->attr |= src->attr; } /* Reflow wrapped lines. */ diff --git a/style.c b/style.c index ef3bb225..4acc17dd 100644 --- a/style.c +++ b/style.c @@ -218,20 +218,23 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) sy->gc.attr = 0; else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) { if (strcmp(tmp + 2, "attr") == 0) - value = 0xffff & ~GRID_ATTR_CHARSET; - else if ((value = attributes_fromstring(tmp + 2)) == -1) - goto error; - sy->gc.attr &= ~value; + sy->gc.attr |= GRID_ATTR_NOATTR; + else { + value = attributes_fromstring(tmp + 2); + if (value == -1) + goto error; + sy->gc.attr &= ~value; + } } else if (end > 6 && strncasecmp(tmp, "width=", 6) == 0) { - n = strtonum(tmp + 6, 0, UINT_MAX, &errstr); - if (errstr != NULL) - goto error; - sy->width = (int)n; + n = strtonum(tmp + 6, 0, UINT_MAX, &errstr); + if (errstr != NULL) + goto error; + sy->width = (int)n; } else if (end > 4 && strncasecmp(tmp, "pad=", 4) == 0) { - n = strtonum(tmp + 4, 0, UINT_MAX, &errstr); - if (errstr != NULL) - goto error; - sy->pad = (int)n; + n = strtonum(tmp + 4, 0, UINT_MAX, &errstr); + if (errstr != NULL) + goto error; + sy->pad = (int)n; } else { if ((value = attributes_fromstring(tmp)) == -1) goto error; @@ -344,13 +347,13 @@ style_tostring(struct style *sy) attributes_tostring(gc->attr)); comma = ","; } - if (sy->width >= 0) { - xsnprintf(s + off, sizeof s - off, "%swidth=%u", comma, + if (sy->width >= 0) { + xsnprintf(s + off, sizeof s - off, "%swidth=%u", comma, sy->width); comma = ","; } - if (sy->pad >= 0) { - xsnprintf(s + off, sizeof s - off, "%spad=%u", comma, + if (sy->pad >= 0) { + xsnprintf(s + off, sizeof s - off, "%spad=%u", comma, sy->pad); comma = ","; } diff --git a/tmux.h b/tmux.h index 20d3678a..170183e7 100644 --- a/tmux.h +++ b/tmux.h @@ -718,6 +718,7 @@ struct colour_palette { #define GRID_ATTR_UNDERSCORE_4 0x800 #define GRID_ATTR_UNDERSCORE_5 0x1000 #define GRID_ATTR_OVERLINE 0x2000 +#define GRID_ATTR_NOATTR 0x4000 /* All underscore attributes. */ #define GRID_ATTR_ALL_UNDERSCORE \ From bfecbb068594bfbbc7ad8782abab4f5b11f89779 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 1 Dec 2025 08:21:04 +0000 Subject: [PATCH 10/30] Fix combine-test.result, GitHub issue 4717. --- regress/combine-test.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/regress/combine-test.result b/regress/combine-test.result index 0d2afb5e..a08539fb 100644 --- a/regress/combine-test.result +++ b/regress/combine-test.result @@ -4,7 +4,7 @@ Λ̊1 🏻2 👍🏻3 -👍🏻 👍🏻4 +👍🏻 👍🏻4 🤷‍♂️5 ♂️7 🤷‍♂️8 From b2d6ebaa1067ddc7aa7b760318faa24822c77f8c Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 1 Dec 2025 08:21:04 +0000 Subject: [PATCH 11/30] Fix combine-test.result, GitHub issue 4717. --- regress/combine-test.result | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/regress/combine-test.result b/regress/combine-test.result index 0d2afb5e..a08539fb 100644 --- a/regress/combine-test.result +++ b/regress/combine-test.result @@ -4,7 +4,7 @@ Λ̊1 🏻2 👍🏻3 -👍🏻 👍🏻4 +👍🏻 👍🏻4 🤷‍♂️5 ♂️7 🤷‍♂️8 From 322adfbdde11c51af29def9ae85870c46bed1fd1 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 2 Dec 2025 08:20:32 +0000 Subject: [PATCH 12/30] Add a get-clipboard option which when enabled (the default is off) uses the same mechanism as palette requests to request clipboard from the terminal and forward to the requesting pane. Remove the now-redundant forward-to-pane ability from "refresh-client -l". GitHub issue 4275. --- cmd-refresh-client.c | 39 +++---------------- input.c | 91 +++++++++++++++++++++++++++++++++++++------- options-table.c | 15 ++++++++ tmux.1 | 35 +++++++++++++---- tmux.h | 9 ++++- tty-keys.c | 45 +++++++++------------- tty.c | 17 +++------ 7 files changed, 158 insertions(+), 93 deletions(-) diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c index 399c83de..cb30749e 100644 --- a/cmd-refresh-client.c +++ b/cmd-refresh-client.c @@ -34,7 +34,7 @@ const struct cmd_entry cmd_refresh_client_entry = { .name = "refresh-client", .alias = "refresh", - .args = { "A:B:cC:Df:r:F:l::LRSt:U", 0, 1, NULL }, + .args = { "A:B:cC:Df:r:F:lLRSt:U", 0, 1, NULL }, .usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] " "[-C XxY] [-f flags] [-r pane:report] " CMD_TARGET_CLIENT_USAGE " [adjustment]", @@ -163,37 +163,6 @@ out: free(copy); } -static enum cmd_retval -cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item) -{ - struct args *args = cmd_get_args(self); - struct client *tc = cmdq_get_target_client(item); - const char *p; - u_int i; - struct cmd_find_state fs; - - p = args_get(args, 'l'); - if (p == NULL) { - if (tc->flags & CLIENT_CLIPBOARDBUFFER) - return (CMD_RETURN_NORMAL); - tc->flags |= CLIENT_CLIPBOARDBUFFER; - } else { - if (cmd_find_target(&fs, item, p, CMD_FIND_PANE, 0) != 0) - return (CMD_RETURN_ERROR); - for (i = 0; i < tc->clipboard_npanes; i++) { - if (tc->clipboard_panes[i] == fs.wp->id) - break; - } - if (i != tc->clipboard_npanes) - return (CMD_RETURN_NORMAL); - tc->clipboard_panes = xreallocarray(tc->clipboard_panes, - tc->clipboard_npanes + 1, sizeof *tc->clipboard_panes); - tc->clipboard_panes[tc->clipboard_npanes++] = fs.wp->id; - } - tty_clipboard_query(&tc->tty); - return (CMD_RETURN_NORMAL); -} - static void cmd_refresh_report(struct tty *tty, const char *value) { @@ -284,8 +253,10 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (args_has(args, 'l')) - return (cmd_refresh_client_clipboard(self, item)); + if (args_has(args, 'l')) { + tty_clipboard_query(&tc->tty); + return (CMD_RETURN_NORMAL); + } if (args_has(args, 'F')) /* -F is an alias for -f */ server_client_set_flags(tc, args_get(args, 'F')); diff --git a/input.c b/input.c index 139f9fd9..ce888887 100644 --- a/input.c +++ b/input.c @@ -3032,18 +3032,41 @@ input_osc_133(struct input_ctx *ictx, const char *p) } } +/* Handle OSC 52 reply. */ +static void +input_osc_52_reply(struct input_ctx *ictx) +{ + struct paste_buffer *pb; + int state; + const char *buf; + size_t len; + + state = options_get_number(global_options, "get-clipboard"); + if (state == 0) + return; + if (state == 1) { + if ((pb = paste_get_top(NULL)) == NULL) + return; + buf = paste_buffer_data(pb, &len); + if (ictx->input_end == INPUT_END_BEL) + input_reply_clipboard(ictx->event, buf, len, "\007"); + else + input_reply_clipboard(ictx->event, buf, len, "\033\\"); + return; + } + input_add_request(ictx, INPUT_REQUEST_CLIPBOARD, ictx->input_end); +} + /* Handle the OSC 52 sequence for setting the clipboard. */ static void input_osc_52(struct input_ctx *ictx, const char *p) { struct window_pane *wp = ictx->wp; + size_t len; char *end; - const char *buf = NULL; - size_t len = 0; u_char *out; int outlen, state; struct screen_write_ctx ctx; - struct paste_buffer *pb; const char* allow = "cpqs01234567"; char flags[sizeof "cpqs01234567"] = ""; u_int i, j = 0; @@ -3068,12 +3091,7 @@ input_osc_52(struct input_ctx *ictx, const char *p) log_debug("%s: %.*s %s", __func__, (int)(end - p - 1), p, flags); if (strcmp(end, "?") == 0) { - if ((pb = paste_get_top(NULL)) != NULL) - buf = paste_buffer_data(pb, &len); - if (ictx->input_end == INPUT_END_BEL) - input_reply_clipboard(ictx->event, buf, len, "\007"); - else - input_reply_clipboard(ictx->event, buf, len, "\033\\"); + input_osc_52_reply(ictx); return; } @@ -3132,6 +3150,7 @@ input_osc_104(struct input_ctx *ictx, const char *p) free(copy); } +/* Send a clipboard reply. */ void input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len, const char *end) @@ -3268,6 +3287,9 @@ input_add_request(struct input_ctx *ictx, enum input_request_type type, int idx) xsnprintf(s, sizeof s, "\033]4;%d;?\033\\", idx); tty_puts(&c->tty, s); break; + case INPUT_REQUEST_CLIPBOARD: + tty_putcode_ss(&c->tty, TTYC_MS, "", "?"); + break; case INPUT_REQUEST_QUEUE: break; } @@ -3275,6 +3297,39 @@ input_add_request(struct input_ctx *ictx, enum input_request_type type, int idx) return (0); } +/* Handle a palette reply. */ +static void +input_request_palette_reply(struct input_request *ir, void *data) +{ + struct input_request_palette_data *pd = data; + + input_osc_colour_reply(ir->ictx, 0, 4, pd->idx, pd->c, ir->end); +} + +/* Handle a clipboard reply. */ +static void +input_request_clipboard_reply(struct input_request *ir, void *data) +{ + struct input_ctx *ictx = ir->ictx; + struct input_request_clipboard_data *cd = data; + int state; + char *copy; + + state = options_get_number(global_options, "get-clipboard"); + if (state == 0 || state == 1) + return; + if (state == 3) { + copy = xmalloc(cd->len); + memcpy(copy, cd->buf, cd->len); + paste_add(NULL, copy, cd->len); + } + + if (ir->idx == INPUT_END_BEL) + input_reply_clipboard(ictx->event, cd->buf, cd->len, "\007"); + else + input_reply_clipboard(ictx->event, cd->buf, cd->len, "\033\\"); +} + /* Handle a reply to a request. */ void input_request_reply(struct client *c, enum input_request_type type, void *data) @@ -3284,11 +3339,18 @@ input_request_reply(struct client *c, enum input_request_type type, void *data) int complete = 0; TAILQ_FOREACH_SAFE(ir, &c->input_requests, centry, ir1) { - if (ir->type == type && pd->idx == ir->idx) { + if (ir->type != type) { + input_free_request(ir); + continue; + } + if (type == INPUT_REQUEST_PALETTE && pd->idx == ir->idx) { + found = ir; + break; + } + if (type == INPUT_REQUEST_CLIPBOARD) { found = ir; break; } - input_free_request(ir); } if (found == NULL) return; @@ -3298,8 +3360,11 @@ input_request_reply(struct client *c, enum input_request_type type, void *data) break; if (ir->type == INPUT_REQUEST_QUEUE) input_send_reply(ir->ictx, ir->data); - else if (ir == found && ir->type == INPUT_REQUEST_PALETTE) { - input_osc_colour_reply(ir->ictx, 0, 4, pd->idx, pd->c, ir->end); + else if (ir == found) { + if (ir->type == INPUT_REQUEST_PALETTE) + input_request_palette_reply(ir, data); + else if (ir->type == INPUT_REQUEST_CLIPBOARD) + input_request_clipboard_reply(ir, data); complete = 1; } input_free_request(ir); diff --git a/options-table.c b/options-table.c index 980f8684..b328a57f 100644 --- a/options-table.c +++ b/options-table.c @@ -85,6 +85,9 @@ static const char *options_table_popup_border_lines_list[] = { static const char *options_table_set_clipboard_list[] = { "off", "external", "on", NULL }; +static const char *options_table_get_clipboard_list[] = { + "off", "buffer", "request", "both", NULL +}; static const char *options_table_window_size_list[] = { "largest", "smallest", "manual", "latest", NULL }; @@ -406,6 +409,18 @@ const struct options_table_entry options_table[] = { .text = "Whether to send focus events to applications." }, + { .name = "get-clipboard", + .type = OPTIONS_TABLE_CHOICE, + .scope = OPTIONS_TABLE_SERVER, + .choices = options_table_get_clipboard_list, + .default_num = 1, + .text = "When an application requests the clipboard, whether to " + "ignore the request ('off'); respond with the newest buffer " + "('buffer'); request the clipboard from the most recently " + "used terminal ('request'); or to request the clipboard, " + "create a buffer, and send it to the application ('both')." + }, + { .name = "history-file", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SERVER, diff --git a/tmux.1 b/tmux.1 index 832c0881..dac3a648 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1361,12 +1361,11 @@ and sets an environment variable for the newly created session; it may be specified multiple times. .Tg refresh .It Xo Ic refresh-client -.Op Fl cDLRSU +.Op Fl cDlLRSU .Op Fl A Ar pane:state .Op Fl B Ar name:what:format .Op Fl C Ar size .Op Fl f Ar flags -.Op Fl l Op Ar target-pane .Op Fl r Ar pane:report .Op Fl t Ar target-client .Op Ar adjustment @@ -1487,11 +1486,7 @@ a colon, then a report escape sequence. .Fl l requests the clipboard from the client using the .Xr xterm 1 -escape sequence. -If -.Ar target-pane -is given, the clipboard is sent (in encoded form), otherwise it is stored in a -new paste buffer. +escape sequence and stores it in a new paste buffer. .Pp .Fl L , .Fl R , @@ -4239,6 +4234,32 @@ passed through to applications running in .Nm . Attached clients should be detached and attached again after changing this option. +.It Xo Ic get-clipboard +.Op Ic both | request | buffer | off +.Xc +Controls the behaviour when an application requests the clipboard from +.Nm . +.Pp +If +.Ic off , +the request is ignored; +if +.Ic buffer , +.Nm +responds with the newest paste buffer; +.Ic request +causes +.Nm +to request the clipboard from the most recently used client (if possible) and +send the reply (if any) back to the application; +.Ic buffer +is the same as +.Ic request +but also creates a paste buffer. +.Pp +See also the +.Ic set-clipboard +option. .It Ic history-file Ar path If not empty, a file to which .Nm diff --git a/tmux.h b/tmux.h index 170183e7..9cd11609 100644 --- a/tmux.h +++ b/tmux.h @@ -1098,6 +1098,7 @@ struct window_mode_entry { /* Type of request to client. */ enum input_request_type { INPUT_REQUEST_PALETTE, + INPUT_REQUEST_CLIPBOARD, INPUT_REQUEST_QUEUE }; @@ -1107,6 +1108,12 @@ struct input_request_palette_data { int c; }; +/* Clipboard request reply data. */ +struct input_request_clipboard_data { + char *buf; + size_t len; +}; + /* Request sent to client on behalf of pane. */ TAILQ_HEAD(input_requests, input_request); @@ -1975,7 +1982,7 @@ struct client { #define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL #define CLIENT_CONTROL_WAITEXIT 0x200000000ULL #define CLIENT_WINDOWSIZECHANGED 0x400000000ULL -#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL +/* 0x800000000ULL unused */ #define CLIENT_BRACKETPASTING 0x1000000000ULL #define CLIENT_ASSUMEPASTING 0x2000000000ULL #define CLIENT_REDRAWSCROLLBARS 0x4000000000ULL diff --git a/tty-keys.c b/tty-keys.c index 77254591..7474621c 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -1301,12 +1301,11 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size, static int tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size) { - struct client *c = tty->client; - struct window_pane *wp; - size_t end, terminator = 0, needed; - char *copy, *out; - int outlen; - u_int i; + struct client *c = tty->client; + size_t end, terminator = 0, needed; + char *copy, *out; + int outlen; + struct input_request_clipboard_data cd; *size = 0; @@ -1364,12 +1363,6 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size) buf++; end--; - /* If we did not request this, ignore it. */ - if (~tty->flags & TTY_OSC52QUERY) - return (0); - tty->flags &= ~TTY_OSC52QUERY; - evtimer_del(&tty->clipboard_timer); - /* It has to be a string so copy it. */ copy = xmalloc(end + 1); memcpy(copy, buf, end); @@ -1384,22 +1377,22 @@ tty_keys_clipboard(struct tty *tty, const char *buf, size_t len, size_t *size) return (0); } free(copy); - - /* Create a new paste buffer and forward to panes. */ log_debug("%s: %.*s", __func__, outlen, out); - if (c->flags & CLIENT_CLIPBOARDBUFFER) { - paste_add(NULL, out, outlen); - c->flags &= ~CLIENT_CLIPBOARDBUFFER; - } - for (i = 0; i < c->clipboard_npanes; i++) { - wp = window_pane_find_by_id(c->clipboard_panes[i]); - if (wp != NULL) - input_reply_clipboard(wp->event, out, outlen, "\033\\"); - } - free(c->clipboard_panes); - c->clipboard_panes = NULL; - c->clipboard_npanes = 0; + /* Set reply if any. */ + cd.buf = out; + cd.len = outlen; + input_request_reply(c, INPUT_REQUEST_CLIPBOARD, &cd); + + /* Create a buffer if requested. */ + if (tty->flags & TTY_OSC52QUERY) { + paste_add(NULL, out, outlen); + out = NULL; + evtimer_del(&tty->clipboard_timer); + tty->flags &= ~TTY_OSC52QUERY; + } + + free(out); return (0); } diff --git a/tty.c b/tty.c index cfff58c4..177d4953 100644 --- a/tty.c +++ b/tty.c @@ -3090,12 +3090,6 @@ static void tty_clipboard_query_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; - struct client *c = tty->client; - - c->flags &= ~CLIENT_CLIPBOARDBUFFER; - free(c->clipboard_panes); - c->clipboard_panes = NULL; - c->clipboard_npanes = 0; tty->flags &= ~TTY_OSC52QUERY; } @@ -3105,10 +3099,9 @@ tty_clipboard_query(struct tty *tty) { struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT }; - if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY)) - return; - tty_putcode_ss(tty, TTYC_MS, "", "?"); - - tty->flags |= TTY_OSC52QUERY; - evtimer_add(&tty->clipboard_timer, &tv); + if ((tty->flags & TTY_STARTED) && (~tty->flags & TTY_OSC52QUERY)) { + tty_putcode_ss(tty, TTYC_MS, "", "?"); + tty->flags |= TTY_OSC52QUERY; + evtimer_add(&tty->clipboard_timer, &tv); + } } From 2c78a5acebe5adfd90ca9f890d24d5bf3ee5e293 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Dec 2025 08:04:26 +0000 Subject: [PATCH 13/30] Add horizontal border case to server_client_check_mouse_in_pane to fix mouse resizing. GitHub issue 4720 from Michael Grant, reported by someone in GitHub issue 4715. --- server-client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server-client.c b/server-client.c index 047365a1..2afec6a9 100644 --- a/server-client.c +++ b/server-client.c @@ -613,7 +613,8 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py, line = wp->yoff + wp->sy; /* Check if point is within the pane or scrollbar. */ - if (((pane_status != PANE_STATUS_OFF && py != line) || + if (((pane_status != PANE_STATUS_OFF && + py != line && py != wp->yoff + wp->sy) || (wp->yoff == 0 && py < wp->sy) || (py >= wp->yoff && py < wp->yoff + wp->sy)) && ((sb_pos == PANE_SCROLLBARS_RIGHT && From a40f98df0ae80a04d57935b6e0a1913db8ad9bef Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 1 Dec 2025 08:14:29 +0000 Subject: [PATCH 14/30] Change noattr to be an explicit attribute in the style so that it works correctly and does not delete attributes set in the style itself, GitHub issue 4713. --- attributes.c | 5 +++-- screen.c | 6 +++++- style.c | 35 +++++++++++++++++++---------------- tmux.h | 1 + 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/attributes.c b/attributes.c index b839f06d..8eaa8897 100644 --- a/attributes.c +++ b/attributes.c @@ -31,7 +31,7 @@ attributes_tostring(int attr) if (attr == 0) return ("none"); - len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + len = xsnprintf(buf, sizeof buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", (attr & GRID_ATTR_CHARSET) ? "acs," : "", (attr & GRID_ATTR_BRIGHT) ? "bright," : "", (attr & GRID_ATTR_DIM) ? "dim," : "", @@ -45,7 +45,8 @@ attributes_tostring(int attr) (attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "", (attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "", (attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "", - (attr & GRID_ATTR_OVERLINE) ? "overline," : ""); + (attr & GRID_ATTR_OVERLINE) ? "overline," : "", + (attr & GRID_ATTR_NOATTR) ? "noattr," : ""); if (len > 0) buf[len - 1] = '\0'; diff --git a/screen.c b/screen.c index a7a13af2..d82784c5 100644 --- a/screen.c +++ b/screen.c @@ -594,8 +594,12 @@ screen_select_cell(struct screen *s, struct grid_cell *dst, if (COLOUR_DEFAULT(dst->bg)) dst->bg = src->bg; utf8_copy(&dst->data, &src->data); - dst->attr = src->attr; dst->flags = src->flags; + + if (dst->attr & GRID_ATTR_NOATTR) + dst->attr |= (src->attr & GRID_ATTR_CHARSET); + else + dst->attr |= src->attr; } /* Reflow wrapped lines. */ diff --git a/style.c b/style.c index ef3bb225..4acc17dd 100644 --- a/style.c +++ b/style.c @@ -218,20 +218,23 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in) sy->gc.attr = 0; else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) { if (strcmp(tmp + 2, "attr") == 0) - value = 0xffff & ~GRID_ATTR_CHARSET; - else if ((value = attributes_fromstring(tmp + 2)) == -1) - goto error; - sy->gc.attr &= ~value; + sy->gc.attr |= GRID_ATTR_NOATTR; + else { + value = attributes_fromstring(tmp + 2); + if (value == -1) + goto error; + sy->gc.attr &= ~value; + } } else if (end > 6 && strncasecmp(tmp, "width=", 6) == 0) { - n = strtonum(tmp + 6, 0, UINT_MAX, &errstr); - if (errstr != NULL) - goto error; - sy->width = (int)n; + n = strtonum(tmp + 6, 0, UINT_MAX, &errstr); + if (errstr != NULL) + goto error; + sy->width = (int)n; } else if (end > 4 && strncasecmp(tmp, "pad=", 4) == 0) { - n = strtonum(tmp + 4, 0, UINT_MAX, &errstr); - if (errstr != NULL) - goto error; - sy->pad = (int)n; + n = strtonum(tmp + 4, 0, UINT_MAX, &errstr); + if (errstr != NULL) + goto error; + sy->pad = (int)n; } else { if ((value = attributes_fromstring(tmp)) == -1) goto error; @@ -344,13 +347,13 @@ style_tostring(struct style *sy) attributes_tostring(gc->attr)); comma = ","; } - if (sy->width >= 0) { - xsnprintf(s + off, sizeof s - off, "%swidth=%u", comma, + if (sy->width >= 0) { + xsnprintf(s + off, sizeof s - off, "%swidth=%u", comma, sy->width); comma = ","; } - if (sy->pad >= 0) { - xsnprintf(s + off, sizeof s - off, "%spad=%u", comma, + if (sy->pad >= 0) { + xsnprintf(s + off, sizeof s - off, "%spad=%u", comma, sy->pad); comma = ","; } diff --git a/tmux.h b/tmux.h index 2184e6c6..dd62382e 100644 --- a/tmux.h +++ b/tmux.h @@ -727,6 +727,7 @@ struct colour_palette { #define GRID_ATTR_UNDERSCORE_4 0x800 #define GRID_ATTR_UNDERSCORE_5 0x1000 #define GRID_ATTR_OVERLINE 0x2000 +#define GRID_ATTR_NOATTR 0x4000 /* All underscore attributes. */ #define GRID_ATTR_ALL_UNDERSCORE \ From 52917abe21c1f3c930399946c3b0bccbcc604439 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 2 Dec 2025 14:26:30 +0000 Subject: [PATCH 15/30] Update CHANGES. --- CHANGES | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index d6b3013e..d2b47190 100644 --- a/CHANGES +++ b/CHANGES @@ -1,8 +1,14 @@ CHANGES FROM 3.6 TO 3.6a +* Fix horizontal mouse resizing when pane status lines are on (from Michael + Grant, issue 4720). + +* Fix noattr so it does not delete attributes set in the style itself (issue + 4713). + * Newer libevents do not allow event_del on a zero'd event (issue 4706). -* Place cursor on correct line if message-line is not 0 (issue 4707)). +* Place cursor on correct line if message-line is not 0 (issue 4707). * Fix compile error on FreeBSD (from Yasuhiro Kimura, issue 4701). From ee9cf1bbaad7408807e491b42b4813424b4080fb Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 3 Dec 2025 07:35:32 +0000 Subject: [PATCH 16/30] Fix the size caluation for left-right windows used to spread out cells horizontally evenly. From Michael Grant in GitHub issue 4724. --- layout.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/layout.c b/layout.c index 2832039c..d3df5e7f 100644 --- a/layout.c +++ b/layout.c @@ -1100,9 +1100,8 @@ int layout_spread_cell(struct window *w, struct layout_cell *parent) { struct layout_cell *lc; - struct style *sb_style = &w->active->scrollbar_style; u_int number, each, size, this, remainder; - int change, changed, status, scrollbars; + int change, changed, status; number = 0; TAILQ_FOREACH (lc, &parent->cells, entry) @@ -1110,14 +1109,9 @@ layout_spread_cell(struct window *w, struct layout_cell *parent) if (number <= 1) return (0); status = options_get_number(w->options, "pane-border-status"); - scrollbars = options_get_number(w->options, "pane-scrollbars"); - if (parent->type == LAYOUT_LEFTRIGHT) { - if (scrollbars) - size = parent->sx - sb_style->width + sb_style->pad; - else - size = parent->sx; - } + if (parent->type == LAYOUT_LEFTRIGHT) + size = parent->sx; else if (parent->type == LAYOUT_TOPBOTTOM) { if (layout_add_horizontal_border(w, parent, status)) size = parent->sy - 1; From 820df0f551adc056024ed1d927b31c72bcde262d Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 3 Dec 2025 07:38:46 +0000 Subject: [PATCH 17/30] Add check that the pane is not in alternate screen mode when in copy-mode. From Michael Grant in GitHub issue 4728. --- window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/window.c b/window.c index 16f1ecb3..80bc57c2 100644 --- a/window.c +++ b/window.c @@ -1769,7 +1769,7 @@ window_pane_mode(struct window_pane *wp) int window_pane_show_scrollbar(struct window_pane *wp, int sb_option) { - if (SCREEN_IS_ALTERNATE(wp->screen)) + if (SCREEN_IS_ALTERNATE(&wp->base)) return (0); if (sb_option == PANE_SCROLLBARS_ALWAYS || (sb_option == PANE_SCROLLBARS_MODAL && From a28dbe3a599ef9baa3878bbb734489f3e352349e Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 3 Dec 2025 07:41:38 +0000 Subject: [PATCH 18/30] Improve code readability in colour_palette_get and colour_palette_set. GitHub issue 4730 from Pavel Roskin. --- colour.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/colour.c b/colour.c index 791c5fd5..88198e96 100644 --- a/colour.c +++ b/colour.c @@ -1082,22 +1082,22 @@ colour_palette_free(struct colour_palette *p) /* Get a colour from a palette. */ int -colour_palette_get(struct colour_palette *p, int c) +colour_palette_get(struct colour_palette *p, int n) { if (p == NULL) return (-1); - if (c >= 90 && c <= 97) - c = 8 + c - 90; - else if (c & COLOUR_FLAG_256) - c &= ~COLOUR_FLAG_256; - else if (c >= 8) + if (n >= 90 && n <= 97) + n = 8 + n - 90; + else if (n & COLOUR_FLAG_256) + n &= ~COLOUR_FLAG_256; + else if (n >= 8) return (-1); - if (p->palette != NULL && p->palette[c] != -1) - return (p->palette[c]); - if (p->default_palette != NULL && p->default_palette[c] != -1) - return (p->default_palette[c]); + if (p->palette != NULL && p->palette[n] != -1) + return (p->palette[n]); + if (p->default_palette != NULL && p->default_palette[n] != -1) + return (p->default_palette[n]); return (-1); } @@ -1107,15 +1107,14 @@ colour_palette_set(struct colour_palette *p, int n, int c) { u_int i; - if (p == NULL || n > 255) + if (p == NULL || n < 0 || n > 255) return (0); if (c == -1 && p->palette == NULL) return (0); - if (c != -1 && p->palette == NULL) { - if (p->palette == NULL) - p->palette = xcalloc(256, sizeof *p->palette); + if (p->palette == NULL) { + p->palette = xcalloc(256, sizeof *p->palette); for (i = 0; i < 256; i++) p->palette[i] = -1; } From 2fe1378d3a5bf867895df2344f9f7dbf6d7b88a7 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Dec 2025 06:02:27 +0000 Subject: [PATCH 19/30] Allow characters to be combined in either order, reported by Jake Stewart in GitHub issue 4726. --- screen-write.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/screen-write.c b/screen-write.c index 43afb50d..39c15b39 100644 --- a/screen-write.c +++ b/screen-write.c @@ -2057,6 +2057,8 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc) case HANGULJAMO_STATE_NOT_HANGULJAMO: if (utf8_should_combine(&last.data, ud)) force_wide = 1; + else if (utf8_should_combine(ud, &last.data)) + force_wide = 1; else if (!utf8_has_zwj(&last.data)) return (0); break; From 1f2210a3cec0d3d6e912b31d369bcc5cd62e7046 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Dec 2025 06:04:21 +0000 Subject: [PATCH 20/30] Add a missing skin tone, from Jake Stewart in GitHub issue 4736. --- utf8-combined.c | 1 + 1 file changed, 1 insertion(+) diff --git a/utf8-combined.c b/utf8-combined.c index 635ae92c..1eee3b82 100644 --- a/utf8-combined.c +++ b/utf8-combined.c @@ -122,6 +122,7 @@ utf8_should_combine(const struct utf8_data *with, const struct utf8_data *add) case 0x1F47C: case 0x1F481: case 0x1F482: + case 0x1F483: case 0x1F485: case 0x1F486: case 0x1F487: From 9d6c69ebdef6521d830b6911e25b9d969efa9559 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Dec 2025 14:45:32 +0000 Subject: [PATCH 21/30] Fix y offset of mouse if status at top. GitHub issue 4738 from Michael Grant. --- screen-redraw.c | 5 +++++ server-client.c | 6 +++++- window-copy.c | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index f76854e8..db518708 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1024,6 +1024,11 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx, int sx = ctx->sx, sy = ctx->sy, xoff = wp->xoff; int yoff = wp->yoff; + if (ctx->statustop) { + sb_y += ctx->statuslines; + sy += ctx->statuslines; + } + /* Set up style for slider. */ gc = sb_style->gc; memcpy(&slgc, &gc, sizeof slgc); diff --git a/server-client.c b/server-client.c index a48b9802..2732f1fe 100644 --- a/server-client.c +++ b/server-client.c @@ -1275,7 +1275,11 @@ have_event: if (c->tty.mouse_scrolling_flag == 0 && where == SCROLLBAR_SLIDER) { c->tty.mouse_scrolling_flag = 1; - c->tty.mouse_slider_mpos = sl_mpos; + if (m->statusat == 0) { + c->tty.mouse_slider_mpos = sl_mpos + + m->statuslines; + } else + c->tty.mouse_slider_mpos = sl_mpos; } break; case WHEEL: diff --git a/window-copy.c b/window-copy.c index f056cebb..2d528496 100644 --- a/window-copy.c +++ b/window-copy.c @@ -627,7 +627,7 @@ window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp, new_slider_y = sb_top - wp->yoff + (sb_height - slider_height); } else { /* Slider is somewhere in the middle. */ - new_slider_y = my - wp->yoff - sl_mpos + 1; + new_slider_y = my - wp->yoff - sl_mpos; } if (TAILQ_FIRST(&wp->modes) == NULL || From bd16b22daca9dba1f6aec63ff2b8fc63b849ef95 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Dec 2025 20:49:57 +0000 Subject: [PATCH 22/30] Do not read over buffer if format is a single #, and do not loop forever if UTF-8 is unfinished in a format. Reported by Giorgi Kobakhia im GitHub issue 4735. --- format-draw.c | 2 -- format.c | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/format-draw.c b/format-draw.c index efc6ab1a..c8cb74b6 100644 --- a/format-draw.c +++ b/format-draw.c @@ -1104,8 +1104,6 @@ format_width(const char *expanded) more = utf8_append(&ud, *cp); if (more == UTF8_DONE) width += ud.width; - else - cp -= ud.have; } else if (*cp > 0x1f && *cp < 0x7f) { width++; cp++; diff --git a/format.c b/format.c index b5272422..afcf7535 100644 --- a/format.c +++ b/format.c @@ -5541,7 +5541,8 @@ format_expand1(struct format_expand_state *es, const char *fmt) buf[off++] = *fmt++; continue; } - fmt++; + if (*fmt++ == '\0') + break; ch = (u_char)*fmt++; switch (ch) { From 33c1ba154940461029eb45a9ba047bf8cf7307e7 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Dec 2025 06:02:27 +0000 Subject: [PATCH 23/30] Allow characters to be combined in either order, reported by Jake Stewart in GitHub issue 4726. --- screen-write.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/screen-write.c b/screen-write.c index 76beffbc..a2755d35 100644 --- a/screen-write.c +++ b/screen-write.c @@ -2152,6 +2152,8 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc) case HANGULJAMO_STATE_NOT_HANGULJAMO: if (utf8_should_combine(&last.data, ud)) force_wide = 1; + else if (utf8_should_combine(ud, &last.data)) + force_wide = 1; else if (!utf8_has_zwj(&last.data)) return (0); break; From 3b57077d015a33ec9f7f4571f90f1cfd25132245 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Dec 2025 06:04:21 +0000 Subject: [PATCH 24/30] Add a missing skin tone, from Jake Stewart in GitHub issue 4736. --- utf8-combined.c | 1 + 1 file changed, 1 insertion(+) diff --git a/utf8-combined.c b/utf8-combined.c index 635ae92c..1eee3b82 100644 --- a/utf8-combined.c +++ b/utf8-combined.c @@ -122,6 +122,7 @@ utf8_should_combine(const struct utf8_data *with, const struct utf8_data *add) case 0x1F47C: case 0x1F481: case 0x1F482: + case 0x1F483: case 0x1F485: case 0x1F486: case 0x1F487: From 2fc123cf4a72d32f7b8bdbce1223cf583d808354 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Thu, 4 Dec 2025 20:54:44 +0000 Subject: [PATCH 25/30] Update CHANGES. --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index d2b47190..e4657965 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ CHANGES FROM 3.6 TO 3.6a +* Add a missing skin tone (from Jake Stewart, issue 4736). + +* Allow characters to be combined in either order (issue 4726, reported by Jake + Stewart). + * Fix horizontal mouse resizing when pane status lines are on (from Michael Grant, issue 4720). From 987e05ff3162ba943abb77525035c6f27a6b5ba3 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Dec 2025 22:50:34 +0000 Subject: [PATCH 26/30] Allow drag in alternate screen again, GitHub issue 4743 reported by Brad King. --- key-bindings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-bindings.c b/key-bindings.c index 7f8b20e0..a5e1f9c8 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -441,7 +441,7 @@ key_bindings_init(void) "bind -n MouseDown1Pane { select-pane -t=; send -M }", /* Mouse button 1 drag on pane. */ - "bind -n MouseDrag1Pane { if -F '#{||:#{alternate_on},#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }", + "bind -n MouseDrag1Pane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }", /* Mouse wheel up on pane. */ "bind -n WheelUpPane { if -F '#{||:#{alternate_on},#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -e } }", From ff207eb5834515580694a4623f7d45233794f7f1 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Dec 2025 14:45:32 +0000 Subject: [PATCH 27/30] Fix y offset of mouse if status at top. GitHub issue 4738 from Michael Grant. --- screen-redraw.c | 5 +++++ server-client.c | 6 +++++- window-copy.c | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/screen-redraw.c b/screen-redraw.c index 96081839..1c1b8503 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1028,6 +1028,11 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx, int sx = ctx->sx, sy = ctx->sy, xoff = wp->xoff; int yoff = wp->yoff; + if (ctx->statustop) { + sb_y += ctx->statuslines; + sy += ctx->statuslines; + } + /* Set up style for slider. */ gc = sb_style->gc; memcpy(&slgc, &gc, sizeof slgc); diff --git a/server-client.c b/server-client.c index 2afec6a9..7f1942c7 100644 --- a/server-client.c +++ b/server-client.c @@ -1271,7 +1271,11 @@ have_event: if (c->tty.mouse_scrolling_flag == 0 && where == SCROLLBAR_SLIDER) { c->tty.mouse_scrolling_flag = 1; - c->tty.mouse_slider_mpos = sl_mpos; + if (m->statusat == 0) { + c->tty.mouse_slider_mpos = sl_mpos + + m->statuslines; + } else + c->tty.mouse_slider_mpos = sl_mpos; } break; case WHEEL: diff --git a/window-copy.c b/window-copy.c index f056cebb..2d528496 100644 --- a/window-copy.c +++ b/window-copy.c @@ -627,7 +627,7 @@ window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp, new_slider_y = sb_top - wp->yoff + (sb_height - slider_height); } else { /* Slider is somewhere in the middle. */ - new_slider_y = my - wp->yoff - sl_mpos + 1; + new_slider_y = my - wp->yoff - sl_mpos; } if (TAILQ_FIRST(&wp->modes) == NULL || From 01962e25dc25b732361fa0b94ce630651891cd33 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Dec 2025 22:50:34 +0000 Subject: [PATCH 28/30] Allow drag in alternate screen again, GitHub issue 4743 reported by Brad King. --- key-bindings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/key-bindings.c b/key-bindings.c index 7f8b20e0..a5e1f9c8 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -441,7 +441,7 @@ key_bindings_init(void) "bind -n MouseDown1Pane { select-pane -t=; send -M }", /* Mouse button 1 drag on pane. */ - "bind -n MouseDrag1Pane { if -F '#{||:#{alternate_on},#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }", + "bind -n MouseDrag1Pane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }", /* Mouse wheel up on pane. */ "bind -n WheelUpPane { if -F '#{||:#{alternate_on},#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -e } }", From faebe7a70a43958ea69a4ddfa1812da3f97bd621 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 4 Dec 2025 20:49:57 +0000 Subject: [PATCH 29/30] Do not read over buffer if format is a single #, and do not loop forever if UTF-8 is unfinished in a format. Reported by Giorgi Kobakhia im GitHub issue 4735. --- format-draw.c | 2 -- format.c | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/format-draw.c b/format-draw.c index efc6ab1a..c8cb74b6 100644 --- a/format-draw.c +++ b/format-draw.c @@ -1104,8 +1104,6 @@ format_width(const char *expanded) more = utf8_append(&ud, *cp); if (more == UTF8_DONE) width += ud.width; - else - cp -= ud.have; } else if (*cp > 0x1f && *cp < 0x7f) { width++; cp++; diff --git a/format.c b/format.c index 17a9dd53..3d498b34 100644 --- a/format.c +++ b/format.c @@ -5545,7 +5545,8 @@ format_expand1(struct format_expand_state *es, const char *fmt) buf[off++] = *fmt++; continue; } - fmt++; + if (*fmt++ == '\0') + break; ch = (u_char)*fmt++; switch (ch) { From cc117b5048f77a4842820f8ebbe3a86e5c077224 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 5 Dec 2025 05:39:46 +0000 Subject: [PATCH 30/30] Update CHANGES. --- CHANGES | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index e4657965..bcd1e1c6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,12 @@ CHANGES FROM 3.6 TO 3.6a +* Fix a buffer overread and an infinite loop in format processing (reported by + Giorgi Kobakhia, issue 4735). + +* Allow drag in alternate screen again (issue 4743 reported by Brad King). + +* Fix y offset of mouse if status at top (issue 4738 from Michael Grant). + * Add a missing skin tone (from Jake Stewart, issue 4736). * Allow characters to be combined in either order (issue 4726, reported by Jake