mirror of
https://github.com/tmux/tmux.git
synced 2026-08-03 14:08:59 +00:00
Add a version number (,v2) to the list-windows serialization string.
This commit is contained in:
@@ -61,6 +61,7 @@ static int layout_append_geometry(char *, size_t, u_int,
|
||||
static int layout_check(struct layout_cell *);
|
||||
static int layout_check_geometry(u_int, u_int, int, int);
|
||||
static int layout_parse_char(struct layout_parse_ctx *, char);
|
||||
static int layout_parse_version(struct layout_parse_ctx *);
|
||||
static int layout_construct(struct layout_parse_ctx *,
|
||||
struct layout_cell *, struct layout_cell **);
|
||||
static void layout_assign(struct window *, struct window_pane **,
|
||||
@@ -137,7 +138,7 @@ layout_dump(struct window *w, struct layout_cell *root)
|
||||
{
|
||||
char layout[LAYOUT_STRING_MAX], *out;
|
||||
|
||||
*layout = '\0';
|
||||
strlcpy(layout, "v2:", sizeof layout);
|
||||
if (layout_append(w, root, layout, sizeof layout) != 0)
|
||||
return (NULL);
|
||||
|
||||
@@ -585,6 +586,21 @@ layout_parse_checksum(struct layout_parse_ctx *ctx, u_short *csum)
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Parse an optional outer layout version. */
|
||||
static int
|
||||
layout_parse_version(struct layout_parse_ctx *ctx)
|
||||
{
|
||||
layout_skip_space(ctx);
|
||||
if (ctx->ptr == ctx->end || *ctx->ptr != 'v')
|
||||
return (0);
|
||||
if ((size_t)(ctx->end - ctx->ptr) < 3 || ctx->ptr[1] != '2' ||
|
||||
ctx->ptr[2] != ':')
|
||||
return (-1);
|
||||
ctx->ptr += 3;
|
||||
layout_skip_space(ctx);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Construct one cell, accepting an optional checksum before nested cells. */
|
||||
static int
|
||||
layout_construct(struct layout_parse_ctx *ctx, struct layout_cell *parent,
|
||||
@@ -900,7 +916,7 @@ layout_prepare(struct window *w, const char *layout, char **cause)
|
||||
const char *body;
|
||||
u_int npanes, ncells, old_ncells;
|
||||
u_short csum;
|
||||
int has_checksum, pane_ids = 0;
|
||||
int has_checksum, pane_ids = 0, version;
|
||||
|
||||
if (strlen(layout) >= LAYOUT_STRING_MAX) {
|
||||
*cause = xstrdup("invalid layout");
|
||||
@@ -917,6 +933,11 @@ layout_prepare(struct window *w, const char *layout, char **cause)
|
||||
*cause = xstrdup("invalid layout checksum");
|
||||
return (NULL);
|
||||
}
|
||||
version = layout_parse_version(&ctx);
|
||||
if (version == -1) {
|
||||
*cause = xstrdup("invalid layout version");
|
||||
return (NULL);
|
||||
}
|
||||
if (layout_construct(&ctx, NULL, &root) != 0) {
|
||||
*cause = xstrdup("invalid layout");
|
||||
layout_free_cell(root, 0);
|
||||
@@ -928,6 +949,11 @@ layout_prepare(struct window *w, const char *layout, char **cause)
|
||||
layout_free_cell(root, 0);
|
||||
return (NULL);
|
||||
}
|
||||
if (version == 1 && ctx.format != LAYOUT_FORMAT) {
|
||||
*cause = xstrdup("invalid layout version");
|
||||
layout_free_cell(root, 0);
|
||||
return (NULL);
|
||||
}
|
||||
if (layout_resolve_relative(root,
|
||||
root->type == LAYOUT_WINDOWPANE ? w->sx : root->sx,
|
||||
root->type == LAYOUT_WINDOWPANE ? w->sy : root->sy) != 0) {
|
||||
|
||||
@@ -38,11 +38,12 @@ sleep 1
|
||||
# Default control output is legacy; window-layout-new output is current.
|
||||
awk '/^layout:/ { print }' "$OUT1" | while IFS= read -r line; do
|
||||
case "$line" in
|
||||
*window-layout-new*|*%0,*|*';'*) exit 1 ;;
|
||||
*window-layout-new*|*v2:*|*%0,*|*';'*) exit 1 ;;
|
||||
esac
|
||||
done || exit 1
|
||||
[ "$(grep -ac '^layout:' "$OUT1")" -eq 6 ] || exit 1
|
||||
grep -a '^layout:.*window-layout-new.*%0,' "$OUT2" >/dev/null || exit 1
|
||||
grep -a '^layout:.*window-layout-new.*,v2:.*%0,' "$OUT2" >/dev/null ||
|
||||
exit 1
|
||||
|
||||
# Toggling the flag changes subsequent format expansion for the same client.
|
||||
printf '%s\n' "refresh-client -f window-layout-new" >&3
|
||||
@@ -50,7 +51,8 @@ printf '%s\n' "display-message -p 'new:#{client_flags}:#{window_layout}'" >&3
|
||||
printf '%s\n' "refresh-client -f '!window-layout-new'" >&3
|
||||
printf '%s\n' "display-message -p 'legacy:#{client_flags}:#{window_layout}'" >&3
|
||||
sleep 1
|
||||
grep -a '^new:.*window-layout-new.*%0,' "$OUT1" >/dev/null || exit 1
|
||||
grep -a '^new:.*window-layout-new.*,v2:.*%0,' "$OUT1" >/dev/null ||
|
||||
exit 1
|
||||
grep -a '^legacy:' "$OUT1" | tail -1 | grep -v '%0,' >/dev/null || exit 1
|
||||
|
||||
# One layout change is formatted independently for each connected client.
|
||||
@@ -62,10 +64,12 @@ LEGACY=$(grep -a '^%layout-change ' "$OUT1" | tail -1)
|
||||
CURRENT=$(grep -a '^%layout-change ' "$OUT2" | tail -1)
|
||||
[ -n "$LEGACY" ] || exit 1
|
||||
[ -n "$CURRENT" ] || exit 1
|
||||
case "$LEGACY" in *%0,*|*';'*) exit 1 ;; esac
|
||||
case "$CURRENT" in *%0,*) ;; *) exit 1 ;; esac
|
||||
case "$LEGACY" in *v2:*|*%0,*|*';'*) exit 1 ;; esac
|
||||
case "$CURRENT" in *v2:*%0,*) ;; *) exit 1 ;; esac
|
||||
[ "$(printf '%s' "$CURRENT" | awk '{ print gsub(/%0,/, "") }')" -ge 2 ] ||
|
||||
exit 1
|
||||
[ "$(printf '%s' "$CURRENT" | awk '{ print gsub(/v2:/, "") }')" -ge 2 ] ||
|
||||
exit 1
|
||||
|
||||
exec 3>&-
|
||||
exec 4>&-
|
||||
|
||||
@@ -26,6 +26,21 @@ must_fail()
|
||||
return 0
|
||||
}
|
||||
|
||||
versioned()
|
||||
{
|
||||
body="v2:$1"
|
||||
checksum=$(printf %s "$body" | od -An -tu1 | awk '
|
||||
{ for (i = 1; i <= NF; i++) {
|
||||
if ($i == 9 || $i == 10 || $i == 11 || $i == 12 ||
|
||||
$i == 13 || $i == 32)
|
||||
continue
|
||||
checksum = int(checksum / 2) + (checksum % 2) * 32768
|
||||
checksum = (checksum + $i) % 65536
|
||||
} }
|
||||
END { printf "%04x", checksum }')
|
||||
printf '%s,%s' "$checksum" "$body"
|
||||
}
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
sleep 0.5
|
||||
$TMUX new-session -d -x 80 -y 24 || exit 1
|
||||
@@ -36,20 +51,37 @@ legacy='89f5,80x24,0,0{39x24,0,0,0,40x24,40,0,1}'
|
||||
$TMUX select-layout "$legacy" || fail "legacy layout was rejected"
|
||||
layout=$($TMUX display-message -p '#{window_layout}')
|
||||
must_equal "$layout" \
|
||||
'93fc,80x24+0+0{%0,0:39x24+0+0;%1,1:40x24+40+0}'
|
||||
'cc3c,v2:80x24+0+0{%0,0:39x24+0+0;%1,1:40x24+40+0}'
|
||||
|
||||
# Both current and legacy layouts may omit the outer checksum.
|
||||
$TMUX select-layout '80x24+0+0{%0,0:39x24+0+0;%1,1:40x24+40+0}' || \
|
||||
fail "checksumless current layout was rejected"
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" "$layout"
|
||||
$TMUX select-layout \
|
||||
'v2:80x24+0+0{%0,0:39x24+0+0;%1,1:40x24+40+0}' || \
|
||||
fail "checksumless versioned layout was rejected"
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" "$layout"
|
||||
$TMUX select-layout '80x24,0,0{39x24,0,0,0,40x24,40,0,1}' || \
|
||||
fail "checksumless legacy layout was rejected"
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" "$layout"
|
||||
must_fail $TMUX select-layout \
|
||||
'0000,80x24+0+0{%0,0:39x24+0+0;%1,1:40x24+40+0}'
|
||||
must_fail $TMUX select-layout \
|
||||
'0000,v2:80x24+0+0{%0,0:39x24+0+0;%1,1:40x24+40+0}'
|
||||
# The old checksum for the unversioned cell does not validate v2:.
|
||||
must_fail $TMUX select-layout \
|
||||
'93fc,v2:80x24+0+0{%0,0:39x24+0+0;%1,1:40x24+40+0}'
|
||||
must_fail $TMUX select-layout \
|
||||
'v1:80x24+0+0{%0,0:39x24+0+0;%1,1:40x24+40+0}'
|
||||
must_fail $TMUX select-layout \
|
||||
'v3:80x24+0+0{%0,0:39x24+0+0;%1,1:40x24+40+0}'
|
||||
must_fail $TMUX select-layout \
|
||||
'v2:80x24,0,0{39x24,0,0,0,40x24,40,0,1}'
|
||||
must_fail $TMUX select-layout \
|
||||
'80x24+0+0{v2:%0,0:39x24+0+0;%1,1:40x24+40+0}'
|
||||
|
||||
# Top-bottom containers are accepted in both current and legacy formats.
|
||||
topbottom='081e,80x24+0+0[%0,0:80x11+0+0;%1,1:80x12+0+12]'
|
||||
topbottom=$(versioned '80x24+0+0[%0,0:80x11+0+0;%1,1:80x12+0+12]')
|
||||
$TMUX select-layout "$topbottom" || fail "top-bottom layout was rejected"
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" "$topbottom"
|
||||
must_equal "$($TMUX list-panes -F '#{pane_width}x#{pane_height},#{pane_left},#{pane_top}')" \
|
||||
@@ -72,7 +104,7 @@ $TMUX select-layout "$layout" || fail "layout could not be restored"
|
||||
# tiled geometry.
|
||||
$TMUX resize-pane -t %0 -Z || fail "tiled zoom failed"
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" \
|
||||
'8e46,80x24+0+0{%0,0:39x24+0+0:z;%1,1:40x24+40+0}'
|
||||
"$(versioned '80x24+0+0{%0,0:39x24+0+0:z;%1,1:40x24+40+0}')"
|
||||
$TMUX resize-pane -t %0 -Z || fail "tiled unzoom failed"
|
||||
$TMUX select-layout "$layout" || fail "layout could not be restored"
|
||||
|
||||
@@ -115,7 +147,7 @@ must_fail $TMUX select-layout "$badnested"
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" "$layout"
|
||||
|
||||
# Hidden state is retained in the serialized cell flags.
|
||||
hidden='659f,80x24+0+0{%0,0:39x24+0+0;%1,1:40x24+40+0:h}'
|
||||
hidden=$(versioned '80x24+0+0{%0,0:39x24+0+0;%1,1:40x24+40+0:h}')
|
||||
$TMUX select-layout "$hidden" || fail "hidden layout was rejected"
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" "$hidden"
|
||||
|
||||
@@ -228,7 +260,7 @@ $TMUX resize-pane -t %0 -Z || fail "unzoom failed"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
sleep 0.5
|
||||
$TMUX new-session -d -x 80 -y 24 || exit 1
|
||||
legacy_negative='a9af,%0,0:20x8+0+0'
|
||||
legacy_negative=$(versioned '%0,0:20x8+0+0')
|
||||
$TMUX select-layout '20x8,-10,-20,0' || fail "legacy negative layout failed"
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" "$legacy_negative"
|
||||
deep='%0,0:1x1+0+0'
|
||||
@@ -262,43 +294,45 @@ $TMUX select-layout "$relative" || fail "relative geometry was rejected"
|
||||
must_equal "$($TMUX display-message -p -t %1 '#{pane_left},#{pane_top}')" \
|
||||
'80,10'
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" \
|
||||
'0041,120x40+0+0{%0,1:120x40+0+0;%1,0:30x10+80+10:f}'
|
||||
"$(versioned '120x40+0+0{%0,1:120x40+0+0;%1,0:30x10+80+10:f}')"
|
||||
|
||||
bottomright='120x40+0+0{%0,1:120x40+0+0;%1,0:30x10-0-0:f}'
|
||||
$TMUX select-layout "$bottomright" || fail "bottom-right geometry failed"
|
||||
must_equal "$($TMUX display-message -p -t %1 '#{pane_left},#{pane_top}')" \
|
||||
'90,30'
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" \
|
||||
'2242,120x40+0+0{%0,1:120x40+0+0;%1,0:30x10+90+30:f}'
|
||||
"$(versioned '120x40+0+0{%0,1:120x40+0+0;%1,0:30x10+90+30:f}')"
|
||||
|
||||
absolute='3a22,120x40+0+0{%0,1:120x40+0+0;%1,0:20x8+-10+-20:f}'
|
||||
$TMUX select-layout "$absolute" || fail "absolute negative geometry failed"
|
||||
absolute_body='120x40+0+0{%0,1:120x40+0+0;%1,0:20x8+-10+-20:f}'
|
||||
$TMUX select-layout "3a22,$absolute_body" || \
|
||||
fail "absolute negative geometry failed"
|
||||
must_equal "$($TMUX display-message -p -t %1 '#{pane_left},#{pane_top}')" \
|
||||
'-10,-20'
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" "$absolute"
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" \
|
||||
"$(versioned "$absolute_body")"
|
||||
|
||||
hiddenfloat='120x40+0+0{%0,1:120x40+0+0;%1,0:20x8+-10+-20:fh}'
|
||||
$TMUX select-layout "$hiddenfloat" || fail "hidden floating layout failed"
|
||||
must_equal "$($TMUX display-message -p -t %1 '#{pane_left},#{pane_top},#{pane_width},#{pane_height}')" \
|
||||
'-10,-20,20,8'
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" \
|
||||
'9d83,120x40+0+0{%0,1:120x40+0+0;%1,0:20x8+-10+-20:fh}'
|
||||
"$(versioned '120x40+0+0{%0,1:120x40+0+0;%1,0:20x8+-10+-20:fh}')"
|
||||
must_fail $TMUX select-layout \
|
||||
'120x40+0+0{%0,1:120x40+0+0;%1,0:20x8+-10+-20:fhz}'
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" \
|
||||
'9d83,120x40+0+0{%0,1:120x40+0+0;%1,0:20x8+-10+-20:fh}'
|
||||
"$(versioned '120x40+0+0{%0,1:120x40+0+0;%1,0:20x8+-10+-20:fh}')"
|
||||
must_fail $TMUX select-layout \
|
||||
'120x40+0+0{%0,1:120x40+0+0;%1,0:10000x8-10000+0:f}'
|
||||
|
||||
defaults='fcb6,120x40{%0,1:120x40;%1,0:30x10++80+10:f}'
|
||||
$TMUX select-layout "$defaults" || fail "default or doubled offsets failed"
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" \
|
||||
'0041,120x40+0+0{%0,1:120x40+0+0;%1,0:30x10+80+10:f}'
|
||||
"$(versioned '120x40+0+0{%0,1:120x40+0+0;%1,0:30x10+80+10:f}')"
|
||||
|
||||
oneoffset='8f93,120x40{%0,1:120x40;%1,0:30x10+80:f}'
|
||||
$TMUX select-layout "$oneoffset" || fail "single offset default failed"
|
||||
must_equal "$($TMUX display-message -p '#{window_layout}')" \
|
||||
'5fb9,120x40+0+0{%0,1:120x40+0+0;%1,0:30x10+80+0:f}'
|
||||
"$(versioned '120x40+0+0{%0,1:120x40+0+0;%1,0:30x10+80+0:f}')"
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
sleep 0.5
|
||||
@@ -338,7 +372,7 @@ $TMUX new-pane -d -x 24 -y 8 -X 50 -Y 12 'sleep 100' || \
|
||||
|
||||
layout=$($TMUX display-message -p '#{window_layout}')
|
||||
must_equal "$layout" \
|
||||
'a6f2,120x40+0+0{%0,2:60x40+0+0;%1,3:59x40+61+0;%3,0:22x6+51+13:f;%2,1:28x8+11+6:f}'
|
||||
"$(versioned '120x40+0+0{%0,2:60x40+0+0;%1,3:59x40+61+0;%3,0:22x6+51+13:f;%2,1:28x8+11+6:f}')"
|
||||
case "$layout" in
|
||||
*'<'*|*'>'*) fail "obsolete floating delimiters were emitted" ;;
|
||||
esac
|
||||
|
||||
30
tmux.1
30
tmux.1
@@ -2684,8 +2684,8 @@ For example:
|
||||
.Bd -literal -offset indent
|
||||
$ tmux list\-windows
|
||||
0: ksh [159x48]
|
||||
layout: 8ebd,159x48+0+0{%0,0:79x48+0+0;%1,1:79x48+80+0}
|
||||
$ tmux select\-layout \[aq]8ebd,159x48+0+0{%0,0:79x48+0+0;%1,1:79x48+80+0}\[aq]
|
||||
layout: aadd,v2:159x48+0+0{%0,0:79x48+0+0;%1,1:79x48+80+0}
|
||||
$ tmux select\-layout \[aq]aadd,v2:159x48+0+0{%0,0:79x48+0+0;%1,1:79x48+80+0}\[aq]
|
||||
.Ed
|
||||
.Pp
|
||||
.Nm
|
||||
@@ -3979,9 +3979,20 @@ spreads the current pane and any panes next to it out evenly.
|
||||
may be a serialized layout with an optional outer checksum followed by one
|
||||
root layout cell:
|
||||
.Bd -literal -offset indent
|
||||
layout = [checksum,]cell
|
||||
layout = [checksum,][v2:]cell
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Ql v2:
|
||||
prefix identifies the current format and is included in generated current
|
||||
layouts.
|
||||
It may be omitted when reading a current layout for compatibility with older
|
||||
output.
|
||||
An explicit
|
||||
.Ql v2:
|
||||
prefix may not be used with legacy cell syntax and unknown versions are
|
||||
rejected.
|
||||
.Pp
|
||||
A layout cell is either a container cell or a pane cell.
|
||||
Geometry gives a cell's size and position.
|
||||
.Nm
|
||||
@@ -4043,8 +4054,9 @@ A layout cannot be applied to a target window with more panes than it has pane
|
||||
cells.
|
||||
.Pp
|
||||
If a window has only one pane, the root layout cell is itself a pane cell, so
|
||||
the pane ID, z-index, geometry and any flags immediately follow the outer
|
||||
checksum.
|
||||
the pane ID, z-index, geometry and any flags immediately follow the
|
||||
.Ql v2:
|
||||
prefix.
|
||||
The z-index is zero for the front pane and increases towards the back.
|
||||
It is present for every pane, including tiled panes.
|
||||
Z-indexes must be unique and contiguous from zero, with all floating panes
|
||||
@@ -4071,7 +4083,7 @@ flags cannot be combined.
|
||||
.Pp
|
||||
For example, this layout contains two tiled panes and one floating pane:
|
||||
.Bd -literal -offset indent
|
||||
6939,120x40+0+0{%0,1:60x40+0+0;%1,2:59x40+61+0;%2,0:28x8+11+6:f}
|
||||
7749,v2:120x40+0+0{%0,1:60x40+0+0;%1,2:59x40+61+0;%2,0:28x8+11+6:f}
|
||||
.Ed
|
||||
.Pp
|
||||
The legacy format uses commas rather than semicolons to separate child cells
|
||||
@@ -4090,12 +4102,14 @@ before pane IDs were added.
|
||||
.Pp
|
||||
The checksum is optional.
|
||||
If present, it is four hexadecimal digits and covers all non-whitespace text
|
||||
after its comma; a layout with an incorrect checksum is rejected.
|
||||
after its comma, including any version prefix; a layout with an incorrect
|
||||
checksum is rejected.
|
||||
Whitespace, including newlines, may appear between layout tokens and does not
|
||||
change the checksum, so a layout may be indented for readability.
|
||||
A checksum may also appear before any nested cell; nested checksums are
|
||||
accepted and checked but are not generated by
|
||||
.Ic list\-windows .
|
||||
Version prefixes are accepted only on the outer layout, not on nested cells.
|
||||
A layout must use one form consistently.
|
||||
In a legacy layout, panes are treated as tiled, visible and unzoomed.
|
||||
Outside control mode,
|
||||
@@ -4120,7 +4134,7 @@ window-layouts = @window-id:layout [@window-id:layout ...]
|
||||
.Ed
|
||||
.Pp
|
||||
Whitespace, including newlines, may appear between the window records.
|
||||
Each window layout retains its own checksum.
|
||||
Each window layout retains its own checksum and version prefix.
|
||||
All records are checked before any window is changed.
|
||||
Unknown or duplicate window IDs are rejected.
|
||||
.Tg selectp
|
||||
|
||||
Reference in New Issue
Block a user