From b6be437bab89fb73cb9dda208321cf3d1263f71c Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Jun 2026 19:13:34 +0000 Subject: [PATCH 1/5] Change send-keys -K to insert keys in the right place in the queue, like we already do for key bindings. GitHub issue 3476. --- cmd-send-keys.c | 17 +++++++++++++---- server-client.c | 38 ++++++++++++++++++++++++++++++++++---- tmux.h | 4 ++++ 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 86e7daedc..6be3f2ead 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -70,6 +70,7 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, struct key_table *table = NULL; struct key_binding *bd; struct key_event *event; + struct cmdq_item *new_after = after; if (args_has(args, 'K')) { if (tc == NULL) @@ -77,10 +78,16 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, event = xcalloc(1, sizeof *event); event->key = key|KEYC_SENT; memset(&event->m, 0, sizeof event->m); - if (server_client_handle_key(tc, event) == 0) { - free(event->buf); - free(event); + if (after == NULL) { + if (server_client_handle_key(tc, event) != 0) + return (item); + } else { + if (server_client_handle_key_after(tc, event, after, + &new_after) != 0) + return (new_after); } + free(event->buf); + free(event); return (item); } @@ -229,8 +236,10 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item) if (count == 0) { if (args_has(args, 'N') || args_has(args, 'R')) return (CMD_RETURN_NORMAL); + after = args_has(args, 'K') ? item : NULL; for (; np != 0; np--) - cmd_send_keys_inject_key(item, NULL, args, event->key); + after = cmd_send_keys_inject_key(item, after, args, + event->key); return (CMD_RETURN_NORMAL); } diff --git a/server-client.c b/server-client.c index 7b141dce9..9d180d0ed 100644 --- a/server-client.c +++ b/server-client.c @@ -1168,11 +1168,11 @@ server_client_repeat_time(struct client *c, struct key_binding *bd) static enum cmd_retval server_client_key_callback(struct cmdq_item *item, void *data) { - struct client *c = cmdq_get_client(item); struct key_event *event = data; + struct client *c, *ec = event->client; key_code key = event->key; struct mouse_event *m = &event->m; - struct session *s = c->session; + struct session *s; struct winlink *wl; struct window_pane *wp; struct window_mode_entry *wme; @@ -1184,6 +1184,12 @@ server_client_key_callback(struct cmdq_item *item, void *data) struct cmd_find_state fs; key_code key0, prefix, prefix2; + if (ec != NULL) + c = ec; + else + c = cmdq_get_client(item); + s = c->session; + /* Check the client is good to accept input. */ if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) goto out; @@ -1430,14 +1436,17 @@ paste_key: out: if (s != NULL && key != KEYC_FOCUS_OUT) server_client_update_latest(c); + if (ec != NULL) + server_client_unref(ec); free(event->buf); free(event); return (CMD_RETURN_NORMAL); } /* Handle a key event. */ -int -server_client_handle_key(struct client *c, struct key_event *event) +static int +server_client_handle_key0(struct client *c, struct key_event *event, + struct cmdq_item *after, struct cmdq_item **next) { struct session *s = c->session; struct cmdq_item *item; @@ -1491,10 +1500,31 @@ server_client_handle_key(struct client *c, struct key_event *event) * previous keys. */ item = cmdq_get_callback(server_client_key_callback, event); + if (after != NULL) { + event->client = c; + c->references++; + item = cmdq_insert_after(after, item); + if (next != NULL) + *next = item; + return (1); + } cmdq_append(c, item); return (1); } +int +server_client_handle_key(struct client *c, struct key_event *event) +{ + return (server_client_handle_key0(c, event, NULL, NULL)); +} + +int +server_client_handle_key_after(struct client *c, struct key_event *event, + struct cmdq_item *after, struct cmdq_item **next) +{ + return (server_client_handle_key0(c, event, after, next)); +} + /* Client functions that need to happen every loop. */ void server_client_loop(void) diff --git a/tmux.h b/tmux.h index 82197751c..48f7c59f8 100644 --- a/tmux.h +++ b/tmux.h @@ -1585,6 +1585,8 @@ struct mouse_event { /* Key event. */ struct key_event { + struct client *client; + key_code key; struct mouse_event m; @@ -3036,6 +3038,8 @@ void server_client_set_key_table(struct client *, const char *); const char *server_client_get_key_table(struct client *); int server_client_check_nested(struct client *); int server_client_handle_key(struct client *, struct key_event *); +int server_client_handle_key_after(struct client *, struct key_event *, + struct cmdq_item *, struct cmdq_item **); struct client *server_client_create(int); int server_client_open(struct client *, char **); void server_client_unref(struct client *); From 11b6e7844a8bf9e4485df56e4f7f1c79e7f1edb9 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Jun 2026 19:50:23 +0000 Subject: [PATCH 2/5] When MODE_SYNC is on, all terminal updates need to deferred, not just scrolling. Check the flag before each one. GitHub issue 4983. --- screen-write.c | 40 ++++++++++++++++++++++++++++++++++++---- server-client.c | 13 ++++++++----- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/screen-write.c b/screen-write.c index 84e6c5263..a5be7fe99 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1158,7 +1158,8 @@ screen_write_redraw_line(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, ttyctx->ocx = cx; ttyctx->ocy = yy; - tty_write(tty_cmd_cell, ttyctx); + if (~s->mode & MODE_SYNC) + tty_write(tty_cmd_cell, ttyctx); } } } @@ -1200,6 +1201,8 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx) screen_write_initctx(ctx, &ttyctx, 1, 1); + if (s->mode & MODE_SYNC) + return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_alignmenttest, &ttyctx); return; @@ -1234,6 +1237,8 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = nx; + if (s->mode & MODE_SYNC) + return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_insertcharacter, &ttyctx); return; @@ -1268,6 +1273,8 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = nx; + if (s->mode & MODE_SYNC) + return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_deletecharacter, &ttyctx); return; @@ -1302,6 +1309,8 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = nx; + if (s->mode & MODE_SYNC) + return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_clearcharacter, &ttyctx); return; @@ -1336,6 +1345,8 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = ny; + if (s->mode & MODE_SYNC) + return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_insertline, &ttyctx); return; @@ -1361,6 +1372,8 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = ny; + if (s->mode & MODE_SYNC) + return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_insertline, &ttyctx); return; @@ -1395,6 +1408,8 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = ny; + if (s->mode & MODE_SYNC) + return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_deleteline, &ttyctx); return; @@ -1420,6 +1435,8 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = ny; + if (s->mode & MODE_SYNC) + return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_deleteline, &ttyctx); return; @@ -1541,6 +1558,8 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg) screen_write_initctx(ctx, &ttyctx, 1, 1); ttyctx.bg = bg; + if (s->mode & MODE_SYNC) + return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_reverseindex, &ttyctx); return; @@ -1651,6 +1670,8 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg) screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = lines; + if (s->mode & MODE_SYNC) + return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) { tty_write(tty_cmd_scrolldown, &ttyctx); return; @@ -1697,6 +1718,8 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1)); screen_write_collect_flush(ctx, 0, __func__); + if (s->mode & MODE_SYNC) + return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) { tty_write(tty_cmd_clearendofscreen, &ttyctx); return; @@ -1766,6 +1789,8 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) screen_write_collect_clear(ctx, 0, s->cy); screen_write_collect_flush(ctx, 0, __func__); + if (s->mode & MODE_SYNC) + return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) { tty_write(tty_cmd_clearstartofscreen, &ttyctx); return; @@ -1834,6 +1859,8 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) screen_write_collect_clear(ctx, 0, sy); + if (s->mode & MODE_SYNC) + return; if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) { tty_write(tty_cmd_clearscreen, &ttyctx); return; @@ -2482,7 +2509,8 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) if (s->mode & MODE_INSERT) { screen_write_collect_flush(ctx, 0, __func__); ttyctx.n = width; - tty_write(tty_cmd_insertcharacter, &ttyctx); + if (~s->mode & MODE_SYNC) + tty_write(tty_cmd_insertcharacter, &ttyctx); } /* If not writing, done now. */ @@ -2506,7 +2534,8 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) for (i = 0, vis = 0; i < r->used; i++) vis += r->ranges[i].nx; if (vis >= width) { - tty_write(tty_cmd_cell, &ttyctx); + if (~s->mode & MODE_SYNC) + tty_write(tty_cmd_cell, &ttyctx); return; } @@ -2515,6 +2544,8 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) * spaces in the visible regions. */ utf8_set(&tmp_gc.data, ' '); + if (s->mode & MODE_SYNC) + return; for (i = 0; i < r->used; i++) { ri = &r->ranges[i]; if (ri->nx == 0) @@ -2656,7 +2687,8 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc) ttyctx.cell = &last; if (force_wide) ttyctx.flags |= TTY_CTX_CELL_INVALIDATE; - tty_write(tty_cmd_cell, &ttyctx); + if (~s->mode & MODE_SYNC) + tty_write(tty_cmd_cell, &ttyctx); screen_write_set_cursor(ctx, cx, cy); return (1); diff --git a/server-client.c b/server-client.c index 9d180d0ed..e968fad7f 100644 --- a/server-client.c +++ b/server-client.c @@ -1779,7 +1779,7 @@ 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; + int mode = 0, cursor, flags, pane_mode = 0; u_int cx = 0, cy = 0, ox, oy, sx, sy, n; struct visible_ranges *r; @@ -1824,6 +1824,8 @@ server_client_reset_state(struct client *c) cx = c->prompt_cursor; } else if (wp != NULL && c->overlay_draw == NULL) { cursor = 0; + pane_mode = wp->base.mode; + tty_window_offset(tty, &ox, &oy, &sx, &sy); if (wp->xoff + (int)s->cx >= (int)ox && wp->xoff + (int)s->cx <= (int)ox + (int)sx && @@ -1842,13 +1844,14 @@ server_client_reset_state(struct client *c) cy += status_line_size(c); } - if (!cursor) + if ((pane_mode & MODE_SYNC) || !cursor) mode &= ~MODE_CURSOR; } else if (c->overlay_mode == NULL || s == NULL) mode &= ~MODE_CURSOR; - - log_debug("%s: cursor to %u,%u", __func__, cx, cy); - tty_cursor(tty, cx, cy); + if (~pane_mode & MODE_SYNC) { + log_debug("%s: cursor to %u,%u", __func__, cx, cy); + tty_cursor(tty, cx, cy); + } /* * Set mouse mode if requested. To support dragging, always use button From 574a4eccb5aa4def982beba11dc8616354520e6e Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 11 Jun 2026 23:01:31 +0000 Subject: [PATCH 3/5] Make buffer creation time sort oldest first like it used to, but change windows to match sessions and panes as newest first. --- sort.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sort.c b/sort.c index fda6a90ba..212da22ea 100644 --- a/sort.c +++ b/sort.c @@ -65,7 +65,12 @@ sort_buffer_cmp(const void *a0, const void *b0) result = strcmp(pa->name, pb->name); break; case SORT_CREATION: - result = pa->order - pb->order; + if (pa->order > pb->order) + result = -1; + else if (pa->order < pb->order) + result = 1; + else + result = 0; break; case SORT_SIZE: result = pa->size - pb->size; @@ -251,11 +256,11 @@ sort_winlink_cmp(const void *a0, const void *b0) break; case SORT_CREATION: if (timercmp(&wa->creation_time, &wb->creation_time, >)) { - result = -1; + result = 1; break; } if (timercmp(&wa->creation_time, &wb->creation_time, <)) { - result = 1; + result = -1; break; } break; From a92653f2b23c527502ea0d2332fa3c22f0cea8ec Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Fri, 12 Jun 2026 10:16:44 +0100 Subject: [PATCH 4/5] base -> bash from tanushnry at gmail dot com. --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index c1bd088f7..556dfedcf 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -63,7 +63,7 @@ tmux -vv -Ltest -f/dev/null new Or if you need your configuration: -~~~base +~~~bash tmux kill-server tmux -vv new ~~~ From b3deb9ec86499678c97ca55b3ef5e92365dea475 Mon Sep 17 00:00:00 2001 From: jsg Date: Fri, 12 Jun 2026 13:16:36 +0000 Subject: [PATCH 5/5] fix tty_draw_line() indentation; ok nicm@ --- tty-draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tty-draw.c b/tty-draw.c index a2868aa4c..ec96409da 100644 --- a/tty-draw.c +++ b/tty-draw.c @@ -181,7 +181,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, bg = ngc.bg; } } - tty_attributes(tty, &last, style_ctx); + tty_attributes(tty, &last, style_ctx); log_debug("%s: clearing %u padding cells", __func__, cx); tty_draw_line_clear(tty, atx, aty, cx, style_ctx->defaults, bg, 0); if (cx == ex)