Compare commits

...

5 Commits

Author SHA1 Message Date
Nicholas Marriott
03436d273a Bump version. 2026-09-11 09:06:39 +01:00
Nicholas Marriott
7fdb1c90b7 Fix tests. 2026-09-11 09:06:20 +01:00
nicm
55012ded8d Add a nesting limit for v1 layouts, reported by M Khalilov, fix based on
issue 5572 from Afonso Januário. Also tweak some language while here.
2026-09-11 09:05:55 +01:00
nicm
de04ae2c65 Add remain-on-exit failed-key and a -D flag to new-pane to have a modal
pane wait for Escape/C-c. Both to allow better compatibility with
popups.
2026-09-11 09:05:41 +01:00
Nicholas Marriott
3b931d9c20 Note changes from Heon Jeong. 2026-09-11 09:05:39 +01:00
11 changed files with 96 additions and 38 deletions

View File

@@ -130,6 +130,8 @@ CHANGES FROM 3.7c TO 3.8
* Add capture-pane -I to include the time each line entered history.
* Various memory leak fixes from Heon Jeong.
* Add a default C-b T binding to change the current pane title.
* Menus now belong to the window, so appear on all clients.

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-split-window.c,v 1.150 2026/08/20 09:19:24 nicm Exp $ */
/* $OpenBSD: cmd-split-window.c,v 1.151 2026/09/10 11:02:18 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -40,8 +40,8 @@ const struct cmd_entry cmd_new_pane_entry = {
.name = "new-pane",
.alias = "newp",
.args = { "AbB:Cc:de:EfF:hIkl:KLMm:Op:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
.usage = "[-AbCdefhIkKLMOPvWZ] [-B border-lines] "
.args = { "AbB:Cc:Dde:EfF:hIkl:KLMm:Op:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
.usage = "[-AbCDefhIkKLMOPvWZ] [-B border-lines] "
"[-c start-directory] [-e environment] "
"[-F format] [-l size] [-m message] [-p percentage] "
"[-s style] [-S active-border-style] "
@@ -219,6 +219,8 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
new_wp->flags |= PANE_CAPTUREALLKEYS;
if (args_has(args, 'C') && args_has(args, 'O'))
new_wp->flags |= PANE_CLOSEONCLICK;
if (args_has(args, 'D') && args_has(args, 'O'))
new_wp->flags |= PANE_CLOSEONCANCEL;
style = args_get(args, 's');
if (style != NULL) {

View File

@@ -1,6 +1,6 @@
# configure.ac
AC_INIT([tmux], 3.8-rc)
AC_INIT([tmux], 3.8-rc2)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: layout-custom.c,v 1.40 2026/09/09 07:03:39 nicm Exp $ */
/* $OpenBSD: layout-custom.c,v 1.41 2026/09/09 09:01:19 nicm Exp $ */
/*
* Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -28,8 +28,8 @@
#include "tmux.h"
/*
* Layouts can be represented as strings in a JSON format (v2). The legacy
* format (v1) will be removed in the future and should no longer be used.
* Layouts can be represented as strings in a JSON format (v2). The version 1
* format will be removed in the future and should no longer be used.
*
* The current (v2) format is JSON. The top level has two keys:
* "V": version number, currently 2
@@ -57,6 +57,9 @@
* "z": z-index, if a floating pane
*/
/* Maximum nesting depth for version 1 layouts. */
#define LAYOUT_V1_MAX_DEPTH 1000
/* Layout string. */
struct layout_string {
char *dat;
@@ -362,7 +365,7 @@ layout_append_v2(struct layout_cell *lc, struct layout_string *ls)
return (0);
}
/* Append information for a single cell in the legacy (v1) format. */
/* Append information for a single cell in the version 1 format. */
static int
layout_append_v1(struct layout_cell *lc, struct layout_string *ls)
{
@@ -778,7 +781,7 @@ layout_assign(struct window *w, struct layout_parse_ctx *pctx)
layout_assign_fallback(w, w->layout_root);
}
/* Construct a cell from the legacy (v1) format. */
/* Construct a cell from the version 1 format. */
static struct layout_cell *
layout_construct_cell(struct layout_cell *lcparent, const char **layout)
{
@@ -827,12 +830,15 @@ layout_construct_cell(struct layout_cell *lcparent, const char **layout)
return (lc);
}
/* Construct a layout from the legacy (v1) format. */
/* Construct a layout from the version 1 format. */
static struct layout_cell *
layout_construct_v1(struct layout_cell *lcparent, const char **layout)
layout_construct_v1(struct layout_cell *lcparent, const char **layout, u_int depth)
{
struct layout_cell *lc, *lcchild;
if (depth > LAYOUT_V1_MAX_DEPTH)
return (NULL);
lc = layout_construct_cell(lcparent, layout);
if (lc == NULL)
return (NULL);
@@ -855,7 +861,7 @@ layout_construct_v1(struct layout_cell *lcparent, const char **layout)
do {
(*layout)++;
lcchild = layout_construct_v1(lc, layout);
lcchild = layout_construct_v1(lc, layout, depth + 1);
if (lcchild == NULL)
goto fail;
TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry);
@@ -1068,7 +1074,8 @@ layout_construct(const char *input, struct layout_parse_ctx *pctx)
*pctx->cause = xstrdup("invalid layout checksum");
return (-1);
}
if ((pctx->root = layout_construct_v1(NULL, &input)) == NULL) {
pctx->root = layout_construct_v1(NULL, &input, 0);
if (pctx->root == NULL) {
*pctx->cause = xstrdup("invalid layout");
return (-1);
}

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: options-table.c,v 1.244 2026/09/01 12:49:49 nicm Exp $ */
/* $OpenBSD: options-table.c,v 1.245 2026/09/10 11:02:18 nicm Exp $ */
/*
* Copyright (c) 2011 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -91,7 +91,7 @@ static const char *options_table_window_size_list[] = {
"largest", "smallest", "manual", "latest", NULL
};
static const char *options_table_remain_on_exit_list[] = {
"off", "on", "failed", "key", NULL
"off", "on", "failed", "key", "failed-key", NULL
};
static const char *options_table_destroy_unattached_list[] = {
"off", "on", "keep-last", "keep-group", NULL
@@ -1698,7 +1698,8 @@ const struct options_table_entry options_table[] = {
.choices = options_table_remain_on_exit_list,
.default_num = 0,
.text = "Whether panes should remain ('on'), remain until a key is "
"pressed ('key') or be automatically killed ('off' or "
"pressed after any exit ('key') or after a failure "
"('failed-key'), or be automatically killed ('off' or "
"'failed') when the program inside exits."
},

View File

@@ -30,8 +30,11 @@ killw
EOF
sleep 1
$TMUX has || exit 1
$TMUX lsp -aF '#{pane_id} #{window_layout}' >$TMP || exit 1
cat <<EOF|cmp -s $TMP - || exit 1
# Use a control client to request legacy layouts, keeping only pane lines
# from the control protocol output.
$TMUX -C lsp -aF '#{pane_id} #{window_layout}' |
grep '^%[0-9]' >$TMP || exit 1
cat <<EOF|cmp $TMP - || exit 1
%0 f5ab,200x200,0,0[200x50,0,0,0,200x149,0,51,3]
%3 f5ab,200x200,0,0[200x50,0,0,0,200x149,0,51,3]
%2 dcbd,200x200,0,0[200x100,0,0,2,200x99,0,101,4]

View File

@@ -104,6 +104,25 @@ assert_alive()
fi
}
# Compare layout geometry and pane ordering, ignoring the active pane and
# last-pane history in the JSON layout. Selection is checked separately.
pane_layout()
{
$TMUX display-message -p -t "$1" '#{window_layout}' |
sed 's/,"a":true//g; s/,"l":[0-9][0-9]*//g'
}
check_layout()
{
out=$(pane_layout "$1")
if [ "$out" != "$2" ]; then
echo "Layout for '$1' wrong."
echo "Expected: '$2'"
echo "But got: '$out'"
exit 1
fi
}
# ---------------------------------------------------------------------------
# split-window geometry.
@@ -309,12 +328,12 @@ check_ok kill-pane -t "$p6"
# Zoom and unzoom preserve the exact tiled layout. Selecting another pane
# without -Z unzooms, while -Z transfers zoom to the selected pane.
layout=$($TMUX display-message -p -t P:0 '#{window_layout}')
layout=$(pane_layout P:0)
check_ok select-pane -t "$p0"
check_ok resize-pane -Z -t "$p0"
check_ok select-pane -t "$p2"
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_active}' '0:1'
check_fmt P:0 '#{window_layout}' "$layout"
check_layout P:0 "$layout"
check_ok select-pane -t "$p0"
check_ok resize-pane -Z -t "$p0"
@@ -322,7 +341,7 @@ check_ok select-pane -Z -t "$p2"
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'1:1:1'
check_ok resize-pane -Z -t "$p2"
check_fmt P:0 '#{window_layout}' "$layout"
check_layout P:0 "$layout"
# Directional selection temporarily restores the full layout to find its
# neighbour, then follows the same unzoom or -Z transfer rules.
@@ -340,7 +359,7 @@ check_ok select-pane -D -Z -t "$p0"
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'1:1:1'
check_ok resize-pane -Z -t "$p2"
check_fmt P:0 '#{window_layout}' "$layout"
check_layout P:0 "$layout"
# The last-pane path has separate zoom handling, both with and without -Z.
check_ok select-pane -t "$p0"
@@ -357,7 +376,7 @@ check_ok select-pane -l -Z -t P:0
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'1:1:1'
check_ok resize-pane -Z -t "$p0"
check_fmt P:0 '#{window_layout}' "$layout"
check_layout P:0 "$layout"
# Killing either a hidden ordinary pane or the zoom target unzooms.
check_ok new-window -d -t P:12 -n zoom-kill 'cat'

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: server-client.c,v 1.511 2026/09/08 10:20:08 nicm Exp $ */
/* $OpenBSD: server-client.c,v 1.512 2026/09/10 11:02:18 nicm Exp $ */
/*
* Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1391,15 +1391,19 @@ server_client_repeat_time(struct client *c, struct key_binding *bd)
return (repeat);
}
/* Handle a key press on a dead pane waiting for a key. */
/* Handle a key press which closes a dead pane. */
static int
server_client_handle_dead_key(struct window_pane *wp, key_code key)
{
int remain_on_exit;
if (wp == NULL ||
(~wp->flags & PANE_EXITED) ||
KEYC_IS_MOUSE(key) ||
KEYC_IS_PASTE(key) ||
options_get_number(wp->options, "remain-on-exit") != 3)
KEYC_IS_PASTE(key))
return (0);
remain_on_exit = options_get_number(wp->options, "remain-on-exit");
if (remain_on_exit != 3 && remain_on_exit != 4)
return (0);
options_set_number(wp->options, "remain-on-exit", 0);
server_destroy_pane(wp, 0);
@@ -1756,9 +1760,10 @@ server_client_handle_key0(struct client *c, struct key_event *event,
}
/*
* Key presses in overlay mode, for panes capturing all keys and in the
* command prompt are a special case. The queue might be blocked so they
* need to be processed immediately rather than queued.
* Key presses in overlay mode, dead panes waiting for a key, modal cancel
* keys, panes capturing all keys and the command prompt are special cases.
* The queue might be blocked so they need to be processed immediately
* rather than queued.
*/
if (~c->flags & CLIENT_READONLY) {
if (c->message_string != NULL) {
@@ -1780,12 +1785,18 @@ server_client_handle_key0(struct client *c, struct key_event *event,
server_client_clear_overlay(c);
wp = s->curw->window->active;
if (server_client_handle_dead_key(wp, event->key))
return (0);
if (wp != NULL && wp == wp->window->modal &&
(wp->flags & PANE_CLOSEONCANCEL) &&
(event->key == '\033' || event->key == ('c'|KEYC_CTRL))) {
server_kill_pane(wp);
return (0);
}
if (wp != NULL &&
(wp->flags & PANE_CAPTUREALLKEYS) &&
TAILQ_EMPTY(&wp->modes) &&
!KEYC_IS_MOUSE(event->key)) {
if (server_client_handle_dead_key(wp, event->key))
return (0);
if (~wp->flags & PANE_EXITED) {
window_pane_key(wp, c, s, s->curw, event->key,
&event->m);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: server-fn.c,v 1.152 2026/09/03 19:12:36 nicm Exp $ */
/* $OpenBSD: server-fn.c,v 1.153 2026/09/10 11:02:18 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -386,6 +386,7 @@ server_destroy_pane(struct window_pane *wp, int notify)
case 0:
break;
case 2:
case 4:
if (WIFEXITED(wp->status) && WEXITSTATUS(wp->status) == 0)
break;
/* FALLTHROUGH */

19
tmux.1
View File

@@ -1,4 +1,4 @@
.\" $OpenBSD: tmux.1,v 1.1168 2026/09/09 07:03:39 nicm Exp $
.\" $OpenBSD: tmux.1,v 1.1169 2026/09/10 11:02:18 nicm Exp $
.\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\"
@@ -14,7 +14,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd $Mdocdate: September 9 2026 $
.Dd $Mdocdate: September 10 2026 $
.Dt TMUX 1
.Os
.Sh NAME
@@ -3655,7 +3655,7 @@ but a different format may be specified with
.Fl F .
.Tg newp
.It Xo Ic new\-pane
.Op Fl AbCdefhIkKLMOPvWZ
.Op Fl AbCDefhIkKLMOPvWZ
.Op Fl B Ar border\-lines
.Op Fl c Ar start\-directory
.Op Fl e Ar environment
@@ -3717,6 +3717,13 @@ all keys, including the prefix key, are passed directly to the modal pane.
With
.Fl C ,
the modal pane is closed when the mouse is clicked outside it.
With
.Fl D ,
the modal pane is closed when
.Ql Escape
or
.Ql C-c
is pressed.
.Pp
The
.Fl L
@@ -6328,7 +6335,7 @@ uses when the colour with that index is requested.
The index may be from zero to 255.
.Pp
.It Xo Ic remain\-on\-exit
.Op Ic on | off | failed | key
.Op Ic on | off | failed | key | failed\-key
.Xc
A pane with this flag set is not destroyed when the program running in it
exits.
@@ -6338,6 +6345,10 @@ then only when the program exit status is not zero.
If set to
.Ic key ,
the pane stays open and closes when a key is pressed.
If set to
.Ic failed\-key ,
the pane stays open and closes when a key is pressed only if the program exit
status is not zero.
The pane may be reactivated with the
.Ic respawn\-pane
command.

3
tmux.h
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.h,v 1.1438 2026/09/09 08:30:05 nicm Exp $ */
/* $OpenBSD: tmux.h,v 1.1439 2026/09/10 11:02:18 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1339,6 +1339,7 @@ struct window_pane {
#define PANE_CLOSEONCLICK 0x80000
#define PANE_CAPTUREALLKEYS 0x100000
#define PANE_FLOATOVERZOOM 0x200000
#define PANE_CLOSEONCANCEL 0x400000
bitstr_t *sync_dirty;
u_int sync_dirty_size;