Merge remote-tracking branch 'refs/remotes/tmux-openbsd/master'

* refs/remotes/tmux-openbsd/master:
  Show match count in copy mode, GitHub issue 5399.
  Add window-default-command option to override default-command per window, from Meriel Sartha Mittelbach.
  Do not change active pane if joining a pane to the same window. Reported by David le Blanc.
This commit is contained in:
tmux update bot
2026-10-07 13:58:41 +00:00
5 changed files with 136 additions and 25 deletions

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-join-pane.c,v 1.76 2026/10/02 12:48:52 nicm Exp $ */ /* $OpenBSD: cmd-join-pane.c,v 1.77 2026/10/07 12:02:21 nicm Exp $ */
/* /*
* Copyright (c) 2011 George Nachman <tmux@georgester.com> * Copyright (c) 2011 George Nachman <tmux@georgester.com>
@@ -503,7 +503,8 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
layout_close_pane(src_wp); layout_close_pane(src_wp);
server_client_remove_pane(src_wp); server_client_remove_pane(src_wp);
window_lost_pane(src_w, src_wp); if (src_w != dst_w)
window_lost_pane(src_w, src_wp);
TAILQ_REMOVE(&src_w->panes, src_wp, entry); TAILQ_REMOVE(&src_w->panes, src_wp, entry);
TAILQ_REMOVE(&src_w->z_index, src_wp, zentry); TAILQ_REMOVE(&src_w->z_index, src_wp, zentry);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: options-table.c,v 1.249 2026/10/05 09:03:11 nicm Exp $ */ /* $OpenBSD: options-table.c,v 1.251 2026/10/07 12:59:50 nicm Exp $ */
/* /*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1414,8 +1414,12 @@ const struct options_table_entry options_table[] = {
"#{t/p:top_line_time}#{?#{e|>:#{top_line_time},0}, ,}" "#{t/p:top_line_time}#{?#{e|>:#{top_line_time},0}, ,}"
"[#{copy_position}/#{copy_position_limit}]" "[#{copy_position}/#{copy_position_limit}]"
"#{?search_timed_out, (timed out)," "#{?search_timed_out, (timed out),"
"#{?search_count, (#{search_count}" "#{?search_count_present, ("
"#{?search_count_partial,+,} results),}}", "#{?#{==:#{search_count},0},0 matches,"
"#{?search_count_partial,#{search_count}+ matches,"
"#{?#{==:#{search_count},1},1 of 1 match,"
"#{?search_count_current,#{search_count_current} of "
"#{search_count} matches,#{search_count} matches}}}}),}}",
.text = "Format of the position indicator in copy mode." .text = "Format of the position indicator in copy mode."
}, },
@@ -1777,6 +1781,14 @@ const struct options_table_entry options_table[] = {
.text = "Default style of the active pane." .text = "Default style of the active pane."
}, },
{ .name = "window-default-command",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "",
.text = "Default command to run in new panes. If set, "
"overrides 'default-command'."
},
{ .name = "window-pane-current-status-format", { .name = "window-pane-current-status-format",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW,

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: spawn.c,v 1.54 2026/10/02 13:20:42 nicm Exp $ */ /* $OpenBSD: spawn.c,v 1.55 2026/10/07 12:55:41 nicm Exp $ */
/* /*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -377,7 +377,9 @@ spawn_pane(struct spawn_context *sc, char **cause)
* directory. * directory.
*/ */
if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN)) { if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN)) {
cmd = options_get_string(s->options, "default-command"); cmd = options_get_string(w->options, "window-default-command");
if (cmd == NULL || *cmd == '\0')
cmd = options_get_string(s->options, "default-command");
if (cmd != NULL && *cmd != '\0') { if (cmd != NULL && *cmd != '\0') {
argc = 1; argc = 1;
argv = (char **)&cmd; argv = (char **)&cmd;

13
tmux.1
View File

@@ -1,4 +1,4 @@
.\" $OpenBSD: tmux.1,v 1.1177 2026/10/06 17:45:26 schwarze Exp $ .\" $OpenBSD: tmux.1,v 1.1179 2026/10/07 12:59:50 nicm Exp $
.\" .\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> .\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\" .\"
@@ -14,7 +14,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd $Mdocdate: October 6 2026 $ .Dd $Mdocdate: October 7 2026 $
.Dt TMUX 1 .Dt TMUX 1
.Os .Os
.Sh NAME .Sh NAME
@@ -3621,6 +3621,8 @@ is the command to execute.
If If
.Ar shell\-command .Ar shell\-command
is not specified, the value of the is not specified, the value of the
.Ic window\-default\-command
or
.Ic default\-command .Ic default\-command
option is used. option is used.
.Fl c .Fl c
@@ -6181,6 +6183,11 @@ see the
.Sx STYLES .Sx STYLES
section. section.
.Pp .Pp
.It Ic window\-default\-command Ar shell\-command
Per-window version of the
.Ic default\-command
option.
.Pp
.It Ic window\-status\-activity\-style Ar style .It Ic window\-status\-activity\-style Ar style
Set status line style for windows with an activity alert. Set status line style for windows with an activity alert.
For how to specify For how to specify
@@ -7483,7 +7490,9 @@ The following variables are available, where appropriate:
.It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane"
.It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane"
.It Li "search_count" Ta "" Ta "Count of search results" .It Li "search_count" Ta "" Ta "Count of search results"
.It Li "search_count_current" Ta "" Ta "Index of the current search result"
.It Li "search_count_partial" Ta "" Ta "1 if search count is partial count" .It Li "search_count_partial" Ta "" Ta "1 if search count is partial count"
.It Li "search_count_present" Ta "" Ta "1 if search count is available"
.It Li "search_match" Ta "" Ta "Search match if any" .It Li "search_match" Ta "" Ta "Search match if any"
.It Li "search_present" Ta "" Ta "1 if search started in copy mode" .It Li "search_present" Ta "" Ta "1 if search started in copy mode"
.It Li "selection_active" Ta "" Ta "1 if selection started and changes with the cursor in copy mode" .It Li "selection_active" Ta "" Ta "1 if selection started and changes with the cursor in copy mode"

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window-copy.c,v 1.435 2026/10/06 08:26:23 nicm Exp $ */ /* $OpenBSD: window-copy.c,v 1.436 2026/10/07 12:59:50 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -87,6 +87,10 @@ static int window_copy_last_regex(struct grid *, u_int, u_int, u_int,
int); int);
static int window_copy_search_mark_at(struct window_copy_mode_data *, static int window_copy_search_mark_at(struct window_copy_mode_data *,
u_int, u_int, u_int *); u_int, u_int, u_int *);
static int window_copy_grow_searchpos(struct window_copy_mode_data *,
u_int, u_int, u_int);
static void window_copy_search_set_index(struct window_copy_mode_data *,
u_int, u_int);
static char *window_copy_stringify(struct grid *, u_int, u_int, u_int, static char *window_copy_stringify(struct grid *, u_int, u_int, u_int,
char *, u_int *); char *, u_int *);
static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *, static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *,
@@ -341,6 +345,10 @@ struct window_copy_mode_data {
char *searchstr; char *searchstr;
u_char *searchmark; u_char *searchmark;
int searchcount; int searchcount;
int searchindex;
u_int *searchpos;
u_int searchposcount;
u_int searchpossize;
int searchmore; int searchmore;
int searchall; int searchall;
int searchx; int searchx;
@@ -581,6 +589,8 @@ window_copy_common_init(struct window_mode_entry *wme)
data->searchstr = NULL; data->searchstr = NULL;
} }
data->searchx = data->searchy = data->searcho = -1; data->searchx = data->searchy = data->searcho = -1;
data->searchcount = -1;
data->searchindex = -1;
data->searchall = 1; data->searchall = 1;
data->jumptype = WINDOW_COPY_OFF; data->jumptype = WINDOW_COPY_OFF;
@@ -682,6 +692,7 @@ window_copy_free(struct window_mode_entry *wme)
evtimer_del(&data->refresh_timer); evtimer_del(&data->refresh_timer);
free(data->searchmark); free(data->searchmark);
free(data->searchpos);
free(data->searchstr); free(data->searchstr);
free(data->jumpchar); free(data->jumpchar);
@@ -1168,8 +1179,12 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft)
format_add(ft, "search_present", "%d", data->searchmark != NULL); format_add(ft, "search_present", "%d", data->searchmark != NULL);
format_add(ft, "search_timed_out", "%d", data->timeout); format_add(ft, "search_timed_out", "%d", data->timeout);
if (data->searchcount != -1) { if (data->searchcount != -1) {
format_add(ft, "search_count_present", "1");
format_add(ft, "search_count", "%d", data->searchcount); format_add(ft, "search_count", "%d", data->searchcount);
format_add(ft, "search_count_partial", "%d", data->searchmore); format_add(ft, "search_count_partial", "%d", data->searchmore);
if (data->searchindex != -1)
format_add(ft, "search_count_current", "%d",
data->searchindex);
} }
format_add_cb(ft, "search_match", window_copy_search_match_cb); format_add_cb(ft, "search_match", window_copy_search_match_cb);
@@ -1194,9 +1209,11 @@ window_copy_size_changed(struct window_mode_entry *wme)
struct screen *s = &data->screen; struct screen *s = &data->screen;
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
int search = (data->searchmark != NULL); int search = (data->searchmark != NULL);
int searchindex = data->searchindex;
window_copy_clear_selection(wme); window_copy_clear_selection(wme);
window_copy_clear_marks(wme); window_copy_clear_marks(wme);
data->searchindex = searchindex;
screen_write_start(&ctx, s); screen_write_start(&ctx, s);
window_copy_write_lines(wme, &ctx, 0, screen_size_y(s)); window_copy_write_lines(wme, &ctx, 0, screen_size_y(s));
@@ -2983,20 +3000,16 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
data->searchregex = 0; data->searchregex = 0;
free(data->searchstr); free(data->searchstr);
data->searchstr = xstrdup(arg0); data->searchstr = xstrdup(arg0);
if (!window_copy_search_up(wme, 0)) { if (!window_copy_search_up(wme, 0))
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW); return (WINDOW_COPY_CMD_REDRAW);
}
break; break;
case '+': case '+':
data->searchtype = WINDOW_COPY_SEARCHDOWN; data->searchtype = WINDOW_COPY_SEARCHDOWN;
data->searchregex = 0; data->searchregex = 0;
free(data->searchstr); free(data->searchstr);
data->searchstr = xstrdup(arg0); data->searchstr = xstrdup(arg0);
if (!window_copy_search_down(wme, 0)) { if (!window_copy_search_down(wme, 0))
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW); return (WINDOW_COPY_CMD_REDRAW);
}
break; break;
} }
return (action); return (action);
@@ -3040,20 +3053,16 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
data->searchregex = 0; data->searchregex = 0;
free(data->searchstr); free(data->searchstr);
data->searchstr = xstrdup(arg0); data->searchstr = xstrdup(arg0);
if (!window_copy_search_down(wme, 0)) { if (!window_copy_search_down(wme, 0))
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW); return (WINDOW_COPY_CMD_REDRAW);
}
break; break;
case '-': case '-':
data->searchtype = WINDOW_COPY_SEARCHUP; data->searchtype = WINDOW_COPY_SEARCHUP;
data->searchregex = 0; data->searchregex = 0;
free(data->searchstr); free(data->searchstr);
data->searchstr = xstrdup(arg0); data->searchstr = xstrdup(arg0);
if (!window_copy_search_up(wme, 0)) { if (!window_copy_search_up(wme, 0))
window_copy_clear_marks(wme);
return (WINDOW_COPY_CMD_REDRAW); return (WINDOW_COPY_CMD_REDRAW);
}
} }
return (action); return (action);
} }
@@ -4698,6 +4707,7 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
return (0); return (0);
if (data->searchall || wp->searchstr == NULL || if (data->searchall || wp->searchstr == NULL ||
data->searchpos == NULL ||
wp->searchregex != regex) { wp->searchregex != regex) {
visible_only = 0; visible_only = 0;
data->searchall = 0; data->searchall = 0;
@@ -4775,8 +4785,8 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
fx = data->cx; fx = data->cx;
fy = screen_hsize(data->backing) - data->oy + data->cy; fy = screen_hsize(data->backing) - data->oy + data->cy;
} }
if (direction) { if (direction) {
window_copy_search_set_index(data, fx, fy);
/* /*
* When in Emacs mode, position the cursor just after * When in Emacs mode, position the cursor just after
* the mark. * the mark.
@@ -4810,8 +4820,12 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex)
window_copy_move_left(s, &fx, &fy, 0); window_copy_move_left(s, &fx, &fy, 0);
} }
} }
fx = data->cx;
fy = screen_hsize(data->backing) - data->oy + data->cy;
window_copy_search_set_index(data, fx, fy);
} }
} } else if (!visible_only)
window_copy_search_marks(wme, &ss, regex, 0);
window_copy_redraw_screen(wme); window_copy_redraw_screen(wme);
screen_free(&ss); screen_free(&ss);
@@ -4854,6 +4868,51 @@ window_copy_clip_width(u_int width, u_int b, u_int sx, u_int sy)
return ((b + width > sx * sy) ? (sx * sy) - b : width); return ((b + width > sx * sy) ? (sx * sy) - b : width);
} }
static int
window_copy_grow_searchpos(struct window_copy_mode_data *data, u_int px,
u_int py, u_int sx)
{
u_int pos;
if (py > (UINT_MAX - px) / sx) {
free(data->searchpos);
data->searchpos = NULL;
data->searchposcount = 0;
data->searchpossize = 0;
return (0);
}
if (data->searchposcount == data->searchpossize) {
data->searchpossize *= 2;
data->searchpos = xreallocarray(data->searchpos,
data->searchpossize, sizeof *data->searchpos);
}
pos = py * sx + px;
data->searchpos[data->searchposcount++] = pos;
return (1);
}
static void
window_copy_search_set_index(struct window_copy_mode_data *data, u_int px,
u_int py)
{
u_int position, left = 0, right = data->searchposcount, middle;
u_int sx = data->backing->grid->sx;
data->searchindex = -1;
if (py > (UINT_MAX - px) / sx)
return;
position = py * sx + px;
while (left < right) {
middle = left + ((right - left) / 2);
if (data->searchpos[middle] < position)
left = middle + 1;
else
right = middle;
}
if (left < data->searchposcount && data->searchpos[left] == position)
data->searchindex = left + 1;
}
static u_int static u_int
window_copy_search_mark_match(struct window_copy_mode_data *data, u_int px, window_copy_search_mark_match(struct window_copy_mode_data *data, u_int px,
u_int py, u_int width, int regex) u_int py, u_int width, int regex)
@@ -4894,7 +4953,8 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp,
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
struct grid *gd = s->grid; struct grid *gd = s->grid;
struct grid_cell gc; struct grid_cell gc;
int found, cis, stopped = 0; int found, cis, stopped = 0, oldindex;
int trackpos = 0;
int cflags = REG_EXTENDED; int cflags = REG_EXTENDED;
u_int px, py, nfound = 0, width; u_int px, py, nfound = 0, width;
u_int ssize = 1, start, end, sx = gd->sx; u_int ssize = 1, start, end, sx = gd->sx;
@@ -4939,6 +4999,14 @@ window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp,
start = 0; start = 0;
end = gd->hsize + sy; end = gd->hsize + sy;
stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT; stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT;
oldindex = data->searchindex;
data->searchindex = -1;
free(data->searchpos);
data->searchpos = xreallocarray(NULL, 64,
sizeof *data->searchpos);
data->searchposcount = 0;
data->searchpossize = 64;
trackpos = 1;
} }
again: again:
@@ -4964,6 +5032,9 @@ again:
break; break;
} }
nfound++; nfound++;
if (!visible_only && trackpos)
trackpos = window_copy_grow_searchpos(data, px, py,
sx);
px += window_copy_search_mark_match(data, px, py, width, px += window_copy_search_mark_match(data, px, py, width,
regex); regex);
} }
@@ -4992,6 +5063,14 @@ again:
if (!visible_only) { if (!visible_only) {
if (stopped) { if (stopped) {
data->searchindex = -1;
if (trackpos) {
/* Keep this non-NULL to avoid another full search. */
data->searchpos = xreallocarray(data->searchpos, 1,
sizeof *data->searchpos);
data->searchpossize = 1;
}
data->searchposcount = 0;
if (nfound > 1000) if (nfound > 1000)
data->searchcount = 1000; data->searchcount = 1000;
else if (nfound > 100) else if (nfound > 100)
@@ -5004,6 +5083,9 @@ again:
} else { } else {
data->searchcount = nfound; data->searchcount = nfound;
data->searchmore = 0; data->searchmore = 0;
if (data->searchindex == -1 && oldindex > 0 &&
oldindex <= data->searchcount)
data->searchindex = oldindex;
} }
} }
@@ -5021,7 +5103,12 @@ window_copy_clear_marks(struct window_mode_entry *wme)
struct window_copy_mode_data *data = wme->data; struct window_copy_mode_data *data = wme->data;
data->searchcount = -1; data->searchcount = -1;
data->searchindex = -1;
data->searchmore = 0; data->searchmore = 0;
free(data->searchpos);
data->searchpos = NULL;
data->searchposcount = 0;
data->searchpossize = 0;
free(data->searchmark); free(data->searchmark);
data->searchmark = NULL; data->searchmark = NULL;