From 49172b256cf530751f45b6faaa32ccc3e2805b7f Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 3 May 2026 14:55:43 +0000 Subject: [PATCH 1/4] Free working stuff when R formats fail. --- format.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/format.c b/format.c index b3f3ef22..56a75e22 100644 --- a/format.c +++ b/format.c @@ -5261,8 +5261,12 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, else { value = xstrdup(""); for (i = 0; i < nrep; i++) { - if (!format_check_time(es)) + if (!format_check_time(es)) { + free(right); + free(left); + free(value); goto fail; + } xasprintf(&new, "%s%s", value, left); free(value); value = new; From 1e96d9a87f00e49e6996672c85464d1e9e42c414 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 3 May 2026 14:57:09 +0000 Subject: [PATCH 2/4] Do not check for NULL after dereferencing, from alexarama at yahoo dot com in GitHub issue 5051. --- window.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/window.c b/window.c index 00dafb52..a3b602fd 100644 --- a/window.c +++ b/window.c @@ -1998,13 +1998,15 @@ struct style_range * window_pane_border_status_get_range(struct window_pane *wp, u_int x, u_int y) { struct style_ranges *srs; - struct window *w = wp->window; - struct options *wo = w->options; + struct window *w; + struct options *wo; u_int line; int pane_status; if (wp == NULL) return (NULL); + w = wp->window; + wo = w->options; srs = &wp->border_status_line.ranges; pane_status = options_get_number(wo, "pane-border-status"); From 637d4c30834a7e54d8c7ed32532ecacca756a1cc Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 3 May 2026 15:01:21 +0000 Subject: [PATCH 3/4] Fix control client hang on exit after toggling no-output, GitHub issue 5049 from Aaron Campbell. --- control.c | 1 + 1 file changed, 1 insertion(+) diff --git a/control.c b/control.c index 30536183..f7fb761c 100644 --- a/control.c +++ b/control.c @@ -301,6 +301,7 @@ control_reset_offsets(struct client *c) struct control_pane *cp, *cp1; RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) { + control_discard_pane(c, cp); RB_REMOVE(control_panes, &cs->panes, cp); free(cp); } From d52fabce72134971ad6c9652cbabd10800505d16 Mon Sep 17 00:00:00 2001 From: nicm Date: Sun, 3 May 2026 15:02:48 +0000 Subject: [PATCH 4/4] Allow the indicator in tree mode to be customized by two new options: tree-mode-preview-format and tree-mode-preview-style. --- options-table.c | 22 ++++++++++ tmux.1 | 33 +++++++++----- window-tree.c | 114 +++++++++++++++++++++++++++--------------------- 3 files changed, 108 insertions(+), 61 deletions(-) diff --git a/options-table.c b/options-table.c index 9be6e16f..c5624fbe 100644 --- a/options-table.c +++ b/options-table.c @@ -1488,6 +1488,28 @@ const struct options_table_entry options_table[] = { "A value of 0 means no limit." }, + { .name = "tree-mode-preview-format", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, + .default_str = "#{?pane_format," + "#{pane_index}:#{pane_title}," + "#{window_index}:#{window_name}}", + .text = "Format of the preview indicator in tree mode." + }, + + { .name = "tree-mode-preview-style", + .type = OPTIONS_TABLE_STRING, + .scope = OPTIONS_TABLE_WINDOW, + .default_str = "fg=#{?#{||:" + "#{&&:#{pane_format},#{pane_active}}," + "#{&&:#{window_format},#{window_active}}}," + "#{display-panes-active-colour}," + "#{display-panes-colour}}", + .flags = OPTIONS_TABLE_IS_STYLE, + .separator = ",", + .text = "Style of preview indicator in tree mode." + }, + { .name = "window-active-style", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, diff --git a/tmux.1 b/tmux.1 index 7d18261f..6b51ec8f 100644 --- a/tmux.1 +++ b/tmux.1 @@ -5146,17 +5146,6 @@ section. .It Ic copy\-mode\-position\-format Ar format Format of the position indicator in copy mode. .Pp -.It Xo Ic mode\-keys -.Op Ic vi | emacs -.Xc -Use vi or emacs-style key bindings in copy mode. -The default is emacs, unless -.Ev VISUAL -or -.Ev EDITOR -contains -.Ql vi . -.Pp .It Ic copy\-mode\-position\-style Ar style Set the style of the position indicator in copy mode. For how to specify @@ -5219,6 +5208,17 @@ and .Ic hybrid , they use absolute line numbers. .Pp +.It Xo Ic mode\-keys +.Op Ic vi | emacs +.Xc +Use vi or emacs-style key bindings in copy mode. +The default is emacs, unless +.Ev VISUAL +or +.Ev EDITOR +contains +.Ql vi . +.Pp .It Ic mode\-style Ar style Set window modes style. For how to specify @@ -5463,6 +5463,17 @@ A value of 0 (the default) means no limit. When a limit is set, panes are arranged to not exceed this number of columns, with additional panes stacked in extra rows. .Pp +.It Ic tree\-mode\-preview\-format Ar format +Format of the preview indicator in tree mode. +.Pp +.It Ic tree\-mode\-preview\-style Ar style +Set the style of the preview indicator in tree mode. +For how to specify +.Ar style , +see the +.Sx STYLES +section. +.Pp .It Ic window\-status\-activity\-style Ar style Set status line style for windows with an activity alert. For how to specify diff --git a/window-tree.c b/window-tree.c index f9672cca..b58d5ed5 100644 --- a/window-tree.c +++ b/window-tree.c @@ -399,44 +399,49 @@ static void window_tree_draw_label(struct screen_write_ctx *ctx, u_int px, u_int py, u_int sx, u_int sy, const struct grid_cell *gc, const char *label) { - size_t len; - u_int ox, oy; + u_int width, ox, oy; + char *new_label = NULL; - len = strlen(label); - if (sx == 0 || sy == 1 || len > sx) + if (sx < 5 || sy < 3) return; - ox = (sx - len + 1) / 2; + width = format_width(label); + if (width > sx - 4) { + label = new_label = format_trim_left(label, sx - 4); + width = format_width(new_label); + } + if (width == 0) + return; + ox = (sx - width + 1) / 2; oy = (sy + 1) / 2; - if (ox > 1 && ox + len < sx - 1 && sy >= 3) { - screen_write_cursormove(ctx, px + ox - 1, py + oy - 1, 0); - screen_write_box(ctx, len + 2, 3, BOX_LINES_DEFAULT, NULL, - NULL); - } + screen_write_cursormove(ctx, px + ox - 2, py + oy - 1, 0); + screen_write_box(ctx, width + 4, 3, BOX_LINES_DEFAULT, + NULL, NULL); + screen_write_cursormove(ctx, px + ox - 1, py + oy, 0); + screen_write_clearcharacter(ctx, width + 2, 8); screen_write_cursormove(ctx, px + ox, py + oy, 0); - screen_write_puts(ctx, gc, "%s", label); + format_draw(ctx, gc, width, label, NULL, 0); + free(new_label); } static void window_tree_draw_session(struct window_tree_modedata *data, struct session *s, struct screen_write_ctx *ctx, u_int sx, u_int sy) { - struct options *oo = s->options; struct winlink *wl; struct window *w; u_int cx = ctx->s->cx, cy = ctx->s->cy; u_int loop, total, visible, each, width, offset; u_int current, start, end, remaining, i; struct grid_cell gc; - int colour, active_colour, left, right; + int left, right; char *label; + const char *format; + struct format_tree *ft; + struct options *oo; total = winlink_count(&s->windows); - memcpy(&gc, &grid_default_cell, sizeof gc); - colour = options_get_number(oo, "display-panes-colour"); - active_colour = options_get_number(oo, "display-panes-active-colour"); - if (sx / total < 24) { visible = sx / 24; if (visible == 0) @@ -516,11 +521,13 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s, continue; } w = wl->window; + oo = w->options; - if (wl == s->curw) - gc.fg = active_colour; - else - gc.fg = colour; + ft = format_create(NULL, NULL, FORMAT_WINDOW|w->id, 0); + format_defaults(ft, NULL, s, wl, NULL); + + memcpy(&gc, &grid_default_cell, sizeof gc); + style_apply(&gc, oo, "tree-mode-preview-style", ft); if (left) offset = 3 + (i * each); @@ -534,17 +541,19 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s, screen_write_cursormove(ctx, cx + offset, cy, 0); screen_write_preview(ctx, &w->active->base, width, sy); - xasprintf(&label, " %u:%s ", wl->idx, w->name); - if (strlen(label) > width) { + format = options_get_string(oo, "tree-mode-preview-format"); + if (*format != '\0') { + label = format_expand(ft, format); + if (*label != '\0') { + window_tree_draw_label(ctx, cx + offset, cy, + width, sy, &gc, label); + } free(label); - xasprintf(&label, " %u ", wl->idx); } - window_tree_draw_label(ctx, cx + offset, cy, width, sy, &gc, - label); - free(label); if (loop != end - 1) { - screen_write_cursormove(ctx, cx + offset + width, cy, 0); + screen_write_cursormove(ctx, cx + offset + width, cy, + 0); screen_write_vline(ctx, sy, 0, 0); } loop++; @@ -555,23 +564,22 @@ window_tree_draw_session(struct window_tree_modedata *data, struct session *s, static void window_tree_draw_window(struct window_tree_modedata *data, struct session *s, - struct window *w, struct screen_write_ctx *ctx, u_int sx, u_int sy) + struct winlink *wl, struct screen_write_ctx *ctx, u_int sx, u_int sy) { - struct options *oo = s->options; + struct window *w = wl->window; struct window_pane *wp; u_int cx = ctx->s->cx, cy = ctx->s->cy; u_int loop, total, visible, each, width, offset; - u_int current, start, end, remaining, i, pane_idx; + u_int current, start, end, remaining, i; struct grid_cell gc; - int colour, active_colour, left, right; + int left, right; char *label; + const char *format; + struct format_tree *ft; + struct options *oo; total = window_count_panes(w); - memcpy(&gc, &grid_default_cell, sizeof gc); - colour = options_get_number(oo, "display-panes-colour"); - active_colour = options_get_number(oo, "display-panes-active-colour"); - if (sx / total < 24) { visible = sx / 24; if (visible == 0) @@ -650,11 +658,13 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s, loop++; continue; } + oo = wp->options; - if (wp == w->active) - gc.fg = active_colour; - else - gc.fg = colour; + ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, 0); + format_defaults(ft, NULL, s, wl, wp); + + memcpy(&gc, &grid_default_cell, sizeof gc); + style_apply(&gc, oo, "tree-mode-preview-style", ft); if (left) offset = 3 + (i * each); @@ -668,15 +678,19 @@ window_tree_draw_window(struct window_tree_modedata *data, struct session *s, screen_write_cursormove(ctx, cx + offset, cy, 0); screen_write_preview(ctx, &wp->base, width, sy); - if (window_pane_index(wp, &pane_idx) != 0) - pane_idx = loop; - xasprintf(&label, " %u ", pane_idx); - window_tree_draw_label(ctx, cx + offset, cy, each, sy, &gc, - label); - free(label); + format = options_get_string(oo, "tree-mode-preview-format"); + if (*format != '\0') { + label = format_expand(ft, format); + if (*label != '\0') { + window_tree_draw_label(ctx, cx + offset, cy, + width, sy, &gc, label); + } + free(label); + } if (loop != end - 1) { - screen_write_cursormove(ctx, cx + offset + width, cy, 0); + screen_write_cursormove(ctx, cx + offset + width, cy, + 0); screen_write_vline(ctx, sy, 0, 0); } loop++; @@ -691,10 +705,10 @@ window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, { struct window_tree_itemdata *item = itemdata; struct session *sp; - struct winlink *wlp; + struct winlink *wl; struct window_pane *wp; - window_tree_pull_item(item, &sp, &wlp, &wp); + window_tree_pull_item(item, &sp, &wl, &wp); if (wp == NULL) return; @@ -705,7 +719,7 @@ window_tree_draw(void *modedata, void *itemdata, struct screen_write_ctx *ctx, window_tree_draw_session(modedata, sp, ctx, sx, sy); break; case WINDOW_TREE_WINDOW: - window_tree_draw_window(modedata, sp, wlp->window, ctx, sx, sy); + window_tree_draw_window(modedata, sp, wl, ctx, sx, sy); break; case WINDOW_TREE_PANE: screen_write_preview(ctx, &wp->base, sx, sy);