From 34fd261a4f777250a0fe66652c2049f6a51d6988 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 8 Jun 2026 20:38:54 +0000 Subject: [PATCH 1/4] Add a -k flag to choose commands to kill the pane when the mode is exited (useful with floating panes). --- cmd-choose-tree.c | 16 ++++++++-------- window.c | 6 ++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index 186672f4..1dfb04f5 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -33,8 +33,8 @@ const struct cmd_entry cmd_choose_tree_entry = { .name = "choose-tree", .alias = NULL, - .args = { "F:f:GK:NO:rst:wyZ", 0, 1, cmd_choose_tree_args_parse }, - .usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] " + .args = { "F:f:GK:kNO:rst:wyZ", 0, 1, cmd_choose_tree_args_parse }, + .usage = "[-GkNrswZ] [-F format] [-f filter] [-K key-format] " "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -47,8 +47,8 @@ const struct cmd_entry cmd_choose_client_entry = { .name = "choose-client", .alias = NULL, - .args = { "F:f:K:NO:rt:yZ", 0, 1, cmd_choose_tree_args_parse }, - .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] " + .args = { "F:f:K:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse }, + .usage = "[-kNrZ] [-F format] [-f filter] [-K key-format] " "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -61,8 +61,8 @@ const struct cmd_entry cmd_choose_buffer_entry = { .name = "choose-buffer", .alias = NULL, - .args = { "F:f:K:NO:rt:yZ", 0, 1, cmd_choose_tree_args_parse }, - .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] " + .args = { "F:f:K:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse }, + .usage = "[-kNrZ] [-F format] [-f filter] [-K key-format] " "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -75,8 +75,8 @@ const struct cmd_entry cmd_customize_mode_entry = { .name = "customize-mode", .alias = NULL, - .args = { "F:f:Nt:yZ", 0, 0, NULL }, - .usage = "[-NZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE, + .args = { "F:f:kNt:yZ", 0, 0, NULL }, + .usage = "[-kNZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/window.c b/window.c index c0d9078c..ddd1d6f0 100644 --- a/window.c +++ b/window.c @@ -1213,6 +1213,7 @@ window_pane_set_mode(struct window_pane *wp, struct window_pane *swp, TAILQ_INSERT_HEAD(&wp->modes, wme, entry); wme->screen = wme->mode->init(wme, fs, args); } + wme->kill = args_has(args, 'k'); wp->screen = wme->screen; wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED); @@ -1230,11 +1231,13 @@ window_pane_reset_mode(struct window_pane *wp) { struct window_mode_entry *wme, *next; struct window *w = wp->window; + int kill; if (TAILQ_EMPTY(&wp->modes)) return; wme = TAILQ_FIRST(&wp->modes); + kill = wme->kill; TAILQ_REMOVE(&wp->modes, wme, entry); wme->mode->free(wme); free(wme); @@ -1257,6 +1260,9 @@ window_pane_reset_mode(struct window_pane *wp) server_redraw_window_borders(wp->window); server_status_window(wp->window); notify_pane("pane-mode-changed", wp); + + if (kill) + server_kill_pane(wp); } void From fe986a52d6bc9f11b90e52f677d679b839fa4643 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 8 Jun 2026 20:41:21 +0000 Subject: [PATCH 2/4] When entering or leaving the alternate screen, discard any pending resizes. Improves flicking with scrollbars and programs that leave and enter the alternate screen on every WINCH like nano. GitHub issue 4772. Cvs: ---------------------------------------------------------------------- --- screen-write.c | 15 +++++++++++---- screen.c | 12 ++++++++---- server-client.c | 5 +---- tmux.h | 6 ++++-- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/screen-write.c b/screen-write.c index 47d6ebc9..9aa86c41 100644 --- a/screen-write.c +++ b/screen-write.c @@ -2765,16 +2765,22 @@ void screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc, int cursor) { - struct tty_ctx ttyctx; - struct window_pane *wp = ctx->wp; + struct tty_ctx ttyctx; + struct window_pane *wp = ctx->wp; + struct window_pane_resize *r, *r1; if (wp != NULL && !options_get_number(wp->options, "alternate-screen")) return; screen_write_collect_flush(ctx, 0, __func__); - screen_alternate_on(ctx->s, gc, cursor); + if (!screen_alternate_on(ctx->s, gc, cursor)) + return; if (wp != NULL) { + TAILQ_FOREACH_SAFE (r, &wp->resize_queue, entry, r1) { + TAILQ_REMOVE(&wp->resize_queue, r, entry); + free(r); + } layout_fix_panes(wp->window, NULL); server_redraw_window_borders(wp->window); } @@ -2796,7 +2802,8 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc, return; screen_write_collect_flush(ctx, 0, __func__); - screen_alternate_off(ctx->s, gc, cursor); + if (!screen_alternate_off(ctx->s, gc, cursor)) + return; if (wp != NULL) { layout_fix_panes(wp->window, NULL); diff --git a/screen.c b/screen.c index 3d9ead65..459ebcb2 100644 --- a/screen.c +++ b/screen.c @@ -655,13 +655,13 @@ screen_reflow(struct screen *s, u_int new_x, u_int *cx, u_int *cy, int cursor) * Enter alternative screen mode. A copy of the visible screen is saved and the * history is not updated. */ -void +int screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) { u_int sx, sy; if (SCREEN_IS_ALTERNATE(s)) - return; + return 0; sx = screen_size_x(s); sy = screen_size_y(s); @@ -677,10 +677,12 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) s->saved_flags = s->grid->flags; s->grid->flags &= ~GRID_HISTORY; + + return 1; } /* Exit alternate screen mode and restore the copied grid. */ -void +int screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) { u_int sx = screen_size_x(s), sy = screen_size_y(s); @@ -709,7 +711,7 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) s->cx = screen_size_x(s) - 1; if (s->cy > screen_size_y(s) - 1) s->cy = screen_size_y(s) - 1; - return; + return 0; } /* Restore the saved grid. */ @@ -731,6 +733,8 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) s->cx = screen_size_x(s) - 1; if (s->cy > screen_size_y(s) - 1) s->cy = screen_size_y(s) - 1; + + return 1; } /* Get mode as a string. */ diff --git a/server-client.c b/server-client.c index 481d5a71..a819e6f9 100644 --- a/server-client.c +++ b/server-client.c @@ -1590,10 +1590,7 @@ server_client_resize_timer(__unused int fd, __unused short events, void *data) static void server_client_check_pane_resize(struct window_pane *wp) { - struct window_pane_resize *r; - struct window_pane_resize *r1; - struct window_pane_resize *first; - struct window_pane_resize *last; + struct window_pane_resize *r, *r1, *first, *last; struct timeval tv = { .tv_usec = 250000 }; if (TAILQ_EMPTY(&wp->resize_queue)) diff --git a/tmux.h b/tmux.h index 887d21bb..b618b3be 100644 --- a/tmux.h +++ b/tmux.h @@ -1147,6 +1147,8 @@ struct window_mode_entry { struct screen *screen; u_int prefix; + int kill; + TAILQ_ENTRY(window_mode_entry) entry; }; @@ -3335,8 +3337,8 @@ void screen_hide_selection(struct screen *); int screen_check_selection(struct screen *, u_int, u_int); int screen_select_cell(struct screen *, struct grid_cell *, const struct grid_cell *); -void screen_alternate_on(struct screen *, struct grid_cell *, int); -void screen_alternate_off(struct screen *, struct grid_cell *, int); +int screen_alternate_on(struct screen *, struct grid_cell *, int); +int screen_alternate_off(struct screen *, struct grid_cell *, int); const char *screen_mode_to_string(int); const char *screen_print(struct screen *, int); From ea51cdb3f2c946da8ef9d9fb36fe6208c29724b8 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 8 Jun 2026 20:42:39 +0000 Subject: [PATCH 3/4] Add display-panes-format to change the top-of-pane text shown with display-panes. --- cmd-display-panes.c | 47 ++++++++++++++++++++++++++++++++++++--------- options-table.c | 8 ++++++++ 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/cmd-display-panes.c b/cmd-display-panes.c index efa7c7e4..5a1782ff 100644 --- a/cmd-display-panes.c +++ b/cmd-display-panes.c @@ -76,6 +76,40 @@ cmd_display_panes_put(struct screen_redraw_ctx *ctx, } } +static void +cmd_display_panes_draw_format(struct screen_redraw_ctx *ctx, + struct window_pane *wp, u_int xoff, u_int yoff, u_int sx, + const struct grid_cell *gc) +{ + struct client *c = ctx->c; + struct tty *tty = &c->tty; + struct session *s = c->session; + struct screen screen; + struct screen_write_ctx sctx; + struct visible_ranges *r; + struct visible_range *ri; + const char *format; + char *expanded; + u_int i, px = ctx->ox + xoff; + + format = options_get_string(s->options, "display-panes-format"); + expanded = format_single(NULL, format, c, s, s->curw, wp); + + screen_init(&screen, sx, 1, 0); + screen_write_start(&sctx, &screen); + format_draw(&sctx, gc, sx, expanded, NULL, 0); + screen_write_stop(&sctx); + free(expanded); + + r = screen_redraw_get_visible_ranges(wp, px, wp->yoff, sx, NULL); + for (i = 0; i < r->used; i++) { + ri = &r->ranges[i]; + tty_draw_line(tty, &screen, ri->px - px, 0, ri->nx, + ri->px - ctx->ox, yoff, gc, NULL); + } + screen_free(&screen); +} + static void cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) @@ -89,8 +123,8 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy; u_int cx, cy; int colour, active_colour; - char buf[16], lbuf[16], rbuf[16], *ptr; - size_t len, llen, rlen; + char buf[16], lbuf[16], *ptr; + size_t len, llen; if (wp->xoff + (int)wp->sx <= ctx->ox || wp->xoff >= ctx->ox + (int)ctx->sx || @@ -159,7 +193,6 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, bgc.bg = colour; } - rlen = xsnprintf(rbuf, sizeof rbuf, "%ux%u", wp->sx, wp->sy); if (pane > 9 && pane < 35) llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10)); else @@ -207,13 +240,9 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx, if (sy <= 6) goto out; - tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL); - if (rlen != 0 && sx >= rlen) { - cx = xoff + sx - rlen; - cy = yoff; - cmd_display_panes_put(ctx, wp, cx, cy, rbuf, rlen); - } + cmd_display_panes_draw_format(ctx, wp, xoff, yoff, sx, &fgc); if (llen != 0) { + tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL); cx = xoff + sx / 2 + len * 3 - llen - 1; cy = yoff + py + 5; cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen); diff --git a/options-table.c b/options-table.c index 42e835b7..81f28be6 100644 --- a/options-table.c +++ b/options-table.c @@ -648,6 +648,14 @@ const struct options_table_entry options_table[] = { .text = "Colour of not active panes for 'display-panes'." }, + { .name = "display-panes-format", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_SESSION, + .default_str = "#[align=right]#{pane_width}x#{pane_height}", + .text = "Format of text shown by 'display-panes', expanded for each " + "pane." + }, + { .name = "display-panes-time", .type = OPTIONS_TABLE_NUMBER, .scope = OPTIONS_TABLE_SESSION, From df7c2e605be8918cd663959b96a98125b120c021 Mon Sep 17 00:00:00 2001 From: nicm Date: Mon, 8 Jun 2026 20:44:57 +0000 Subject: [PATCH 4/4] Add -T to new-pane to set the title; GitHub issue 5176 from Michael Grant. --- cmd-split-window.c | 16 +++++++++++----- tmux.1 | 26 ++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/cmd-split-window.c b/cmd-split-window.c index aa90256d..cc6427b5 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -39,11 +39,11 @@ const struct cmd_entry cmd_new_pane_entry = { .name = "new-pane", .alias = "newp", - .args = { "bc:de:EfF:hIkl:Lm:p:PR:s:S:t:vx:X:y:Y:Z", 0, -1, NULL }, + .args = { "bc:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vx:X:y:Y:Z", 0, -1, NULL }, .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] " "[-F format] [-l size] [-m message] [-p percentage] " "[-s style] [-S active-border-style] " - "[-R inactive-border-style] [-x width] [-y height] " + "[-R inactive-border-style] [-T title] [-x width] [-y height] " "[-X x-position] [-Y y-position] " CMD_TARGET_PANE_USAGE " " "[shell-command [argument ...]]", @@ -57,11 +57,11 @@ const struct cmd_entry cmd_split_window_entry = { .name = "split-window", .alias = "splitw", - .args = { "bc:de:EfF:hIkl:m:p:PR:s:S:t:vZ", 0, -1, NULL }, + .args = { "bc:de:EfF:hIkl:m:p:PR:s:S:t:T:vZ", 0, -1, NULL }, .usage = "[-bdefhIklPvZ] [-c start-directory] [-e environment] " "[-F format] [-l size] [-m message] [-p percentage] " "[-s style] [-S active-border-style] " - "[-R inactive-border-style] " CMD_TARGET_PANE_USAGE " " + "[-R inactive-border-style] [-T title] " CMD_TARGET_PANE_USAGE " " "[shell-command [argument ...]]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -86,7 +86,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state fs; int input, empty, is_floating, flags = 0; const char *template, *style; - char *cause = NULL, *cp; + char *cause = NULL, *cp, *title; struct args_value *av; u_int count = args_count(args); @@ -195,6 +195,12 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) "remain-on-exit-format", 0, "%s", args_get(args, 'm')); } + if (args_has(args, 'T')) { + title = format_single_from_target(item, args_get(args, 'T')); + screen_set_title(&new_wp->base, title); + notify_pane("pane-title-changed", new_wp); + free(title); + } if (input) { switch (window_pane_start_input(new_wp, item, &cause)) { diff --git a/tmux.1 b/tmux.1 index 2b1d543f..fce6e1d3 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2741,7 +2741,7 @@ the end of the visible pane. The default is to capture only the visible contents of the pane. .It Xo .Ic choose\-client -.Op Fl NryZ +.Op Fl kNryZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl K Ar key\-format @@ -2814,10 +2814,12 @@ specifies the format for each item in the list and a format for each shortcut key; both are evaluated once for each line. .Fl N starts without the preview or if given twice with the larger preview. +.Fl k +kills the pane when the mode is exited. This command works only if at least one client is attached. .It Xo .Ic choose\-tree -.Op Fl GNrswyZ +.Op Fl GkNrswyZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl K Ar key\-format @@ -2907,10 +2909,12 @@ starts without the preview or if given twice with the larger preview. .Fl G includes all sessions in any session groups in the tree rather than only the first. +.Fl k +kills the pane when the mode is exited. This command works only if at least one client is attached. .It Xo .Ic customize\-mode -.Op Fl NZ +.Op Fl kNZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl t Ar target\-pane @@ -2958,6 +2962,8 @@ If a filter would lead to an empty list, it is ignored. specifies the format for each item in the tree. .Fl N starts without the option information. +.Fl k +kills the pane when the mode is exited. This command works only if at least one client is attached. .It Xo .Tg displayp @@ -3339,6 +3345,7 @@ but a different format may be specified with .Op Fl s Ar style .Op Fl S Ar active\-border\-style .Op Fl t Ar target\-pane +.Op Fl T Ar title .Op Ar shell\-command Op Ar argument ... .Xc .D1 Pq alias: Ic newp @@ -3357,6 +3364,8 @@ sets the border style when the pane is active and .Fl R sets the border style when the pane is inactive (see .Sx STYLES ) . +.Fl T +sets the pane title. .Pp .Fl h does a horizontal split and @@ -3754,6 +3763,7 @@ the command behaves like .Op Fl s Ar style .Op Fl S Ar active\-border\-style .Op Fl t Ar target\-pane +.Op Fl T Ar title .Op Ar shell\-command Op Ar argument ... .Xc .D1 Pq alias: Ic splitw @@ -4792,6 +4802,12 @@ command to show the indicator for the active pane. Set the colour used by the .Ic display\-panes command to show the indicators for inactive panes. +.It Ic display\-panes\-format Ar format +Set the +.Ar format +of the text shown by the +.Ic display\-panes +command, expanded for each pane. .It Ic display\-panes\-time Ar time Set the time in milliseconds for which the indicators shown by the .Ic display\-panes @@ -7615,7 +7631,7 @@ The buffer commands are as follows: .Bl -tag -width Ds .It Xo .Ic choose\-buffer -.Op Fl NryZ +.Op Fl kNryZ .Op Fl F Ar format .Op Fl f Ar filter .Op Fl K Ar key\-format @@ -7686,6 +7702,8 @@ specifies the format for each item in the list and a format for each shortcut key; both are evaluated once for each line. .Fl N starts without the preview. +.Fl k +kills the pane when the mode is exited. This command works only if at least one client is attached. .Tg clearhist .It Xo Ic clear\-history