Add control mode flag window-layout-v2 to return new list-windows layout format.

This commit is contained in:
Michael Grant
2026-07-05 14:03:54 +01:00
parent 2c26557b90
commit fb8f266a21
9 changed files with 134 additions and 17 deletions

View File

@@ -97,6 +97,7 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
struct cmdq_item *item, int type)
{
struct args *args = cmd_get_args(self);
struct client *c = cmdq_get_client(item);
struct window_pane *wp, **l;
u_int i, n;
struct format_tree *ft;
@@ -147,7 +148,7 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
wp = l[i];
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, wl, wp);
format_defaults(ft, c, s, wl, wp);
if (filter != NULL) {
expanded = format_expand(ft, filter);

View File

@@ -53,6 +53,7 @@ static enum cmd_retval
cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *c = cmdq_get_client(item);
struct session **l;
u_int n, i;
struct format_tree *ft;
@@ -76,7 +77,7 @@ cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item)
for (i = 0; i < n; i++) {
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", i);
format_defaults(ft, NULL, l[i], NULL, NULL);
format_defaults(ft, c, l[i], NULL, NULL);
if (filter != NULL) {
expanded = format_expand(ft, filter);

View File

@@ -59,6 +59,7 @@ static enum cmd_retval
cmd_list_windows_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *c = cmdq_get_client(item);
struct cmd_find_state *target = cmdq_get_target(item);
struct winlink *wl, **l;
struct session *s;
@@ -94,7 +95,7 @@ cmd_list_windows_exec(struct cmd *self, struct cmdq_item *item)
s = wl->session;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_add(ft, "line", "%u", n);
format_defaults(ft, NULL, s, wl, NULL);
format_defaults(ft, c, s, wl, NULL);
if (filter != NULL) {
expanded = format_expand(ft, filter);

View File

@@ -57,19 +57,20 @@ control_notify_window_layout_changed(struct window *w)
* and we don't need to inform the client about the layout change
* because the whole window will go away soon.
*/
wl = TAILQ_FIRST(&w->winlinks);
if (wl == NULL || w->layout_root == NULL)
if (TAILQ_FIRST(&w->winlinks) == NULL || w->layout_root == NULL)
return;
cp = format_single(NULL, template, NULL, NULL, wl, NULL);
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL)
continue;
s = c->session;
if (winlink_find_by_window_id(&s->windows, w->id) != NULL)
wl = winlink_find_by_window_id(&s->windows, w->id);
if (wl != NULL) {
cp = format_single(NULL, template, c, s, wl, NULL);
control_write(c, "%s", cp);
free(cp);
}
}
free(cp);
}
void

View File

@@ -839,6 +839,15 @@ format_cb_window_active_clients_list(struct format_tree *ft)
}
/* Callback for window_layout. */
static char *
format_window_layout(struct format_tree *ft, struct layout_cell *root)
{
if (ft->c != NULL && (ft->c->flags & CLIENT_CONTROL) &&
(ft->c->flags & CLIENT_CONTROL_WINDOWLAYOUTV2) == 0)
return (layout_dump_legacy(root));
return (layout_dump(ft->w, root));
}
static void *
format_cb_window_layout(struct format_tree *ft)
{
@@ -848,8 +857,8 @@ format_cb_window_layout(struct format_tree *ft)
return (NULL);
if (w->saved_layout_root != NULL)
return (layout_dump(w, w->saved_layout_root));
return (layout_dump(w, w->layout_root));
return (format_window_layout(ft, w->saved_layout_root));
return (format_window_layout(ft, w->layout_root));
}
/* Callback for window_visible_layout. */
@@ -861,7 +870,7 @@ format_cb_window_visible_layout(struct format_tree *ft)
if (w == NULL)
return (NULL);
return (layout_dump(w, w->layout_root));
return (format_window_layout(ft, w->layout_root));
}
/* Callback for pane_start_command. */

View File

@@ -54,6 +54,8 @@ static struct layout_cell *layout_find_bottomright(struct layout_cell *);
static u_short layout_checksum(const char *, const char *);
static int layout_append(struct window *,
struct layout_cell *, char *, size_t);
static int layout_append_legacy(struct layout_cell *, char *,
size_t);
static int layout_append_geometry(char *, size_t, u_int,
u_int, int, int);
static int layout_check(struct layout_cell *);
@@ -144,6 +146,83 @@ layout_dump(struct window *w, struct layout_cell *root)
return (out);
}
/* Dump layout using the legacy syntax. */
char *
layout_dump_legacy(struct layout_cell *root)
{
char layout[LAYOUT_STRING_MAX], *out;
*layout = '\0';
if (layout_append_legacy(root, layout, sizeof layout) != 0)
return (NULL);
xasprintf(&out, "%04hx,%s",
layout_checksum(layout, layout + strlen(layout)), layout);
return (out);
}
/* Append information for a single cell using the legacy syntax. */
static int
layout_append_legacy(struct layout_cell *lc, char *buf, size_t len)
{
struct layout_cell *lcchild;
char tmp[96];
const char *brackets = "][";
size_t tmplen;
u_int sx, sy;
int xoff, yoff;
if (len == 0)
return (-1);
if (lc == NULL)
return (0);
sx = lc->sx;
sy = lc->sy;
xoff = lc->xoff;
yoff = lc->yoff;
if (lc->flags & LAYOUT_CELL_HIDDEN) {
if (lc->saved_sx != UINT_MAX)
sx = lc->saved_sx;
if (lc->saved_sy != UINT_MAX)
sy = lc->saved_sy;
if (lc->saved_xoff != INT_MAX)
xoff = lc->saved_xoff;
if (lc->saved_yoff != INT_MAX)
yoff = lc->saved_yoff;
}
if (lc->wp != NULL) {
tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%d,%d,%u", sx, sy,
xoff, yoff, lc->wp->id);
} else {
tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%d,%d", sx, sy,
xoff, yoff);
}
if (tmplen > (sizeof tmp) - 1 || strlcat(buf, tmp, len) >= len)
return (-1);
switch (lc->type) {
case LAYOUT_LEFTRIGHT:
brackets = "}{";
/* FALLTHROUGH */
case LAYOUT_TOPBOTTOM:
if (strlcat(buf, &brackets[1], len) >= len)
return (-1);
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (layout_append_legacy(lcchild, buf, len) != 0)
return (-1);
if (strlcat(buf, ",", len) >= len)
return (-1);
}
buf[strlen(buf) - 1] = brackets[0];
break;
case LAYOUT_WINDOWPANE:
break;
}
return (0);
}
/* Append information for a single cell. */
static int
layout_append(struct window *w, struct layout_cell *lc, char *buf, size_t len)

View File

@@ -2851,6 +2851,8 @@ server_client_control_flags(struct client *c, const char *next)
return (CLIENT_CONTROL_NOOUTPUT);
if (strcmp(next, "wait-exit") == 0)
return (CLIENT_CONTROL_WAITEXIT);
if (strcmp(next, "window-layout-v2") == 0)
return (CLIENT_CONTROL_WINDOWLAYOUTV2);
return (0);
}
@@ -2919,6 +2921,8 @@ server_client_get_flags(struct client *c)
strlcat(s, "no-output,", sizeof s);
if (c->flags & CLIENT_CONTROL_WAITEXIT)
strlcat(s, "wait-exit,", sizeof s);
if (c->flags & CLIENT_CONTROL_WINDOWLAYOUTV2)
strlcat(s, "window-layout-v2,", sizeof s);
if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
xsnprintf(tmp, sizeof tmp, "pause-after=%u,",
c->pause_age / 1000);

30
tmux.1
View File

@@ -1113,6 +1113,9 @@ The flags are:
the client has an independent active pane
.It ignore\-size
the client does not affect the size of other clients
.It window\-layout\-v2
the client receives the current layout serialization in control mode rather
than the legacy serialization
.It no\-detach\-on\-destroy
do not detach the client when the session it is attached to is destroyed if
there are any other sessions
@@ -3387,6 +3390,9 @@ format variable.
It is repeated for each pane from the same window.
Its format is described under
.Ic select\-layout .
In control mode, it uses the legacy format unless the client has the
.Ql window\-layout\-v2
flag.
.Tg lsw
.It Xo Ic list\-windows
.Op Fl ar
@@ -3428,6 +3434,9 @@ The default format includes the layout of each window, also available with the
format variable.
The format is described under
.Ic select\-layout .
In control mode, the legacy format is used unless the client has the
.Ql window\-layout\-v2
flag.
.Pp
Layouts for multiple windows may be produced in a form suitable for
.Ic select\-layout
@@ -4069,10 +4078,18 @@ accepted and checked but are not generated by
.Ic list\-windows .
A layout must use one form consistently.
In a legacy layout, panes are treated as tiled, visible and unzoomed.
.Ic list\-windows
and the
Outside control mode,
.Ql window_layout
format variable generate the current format with a checksum.
and
.Ql window_visible_layout
generate the current format with a checksum.
In control mode, they generate the legacy format by default; a client with the
.Ql window\-layout\-v2
flag receives the current format instead.
.Ic list\-windows
uses
.Ql window_layout
for the layout in its default output.
.Pp
A serialized layout is a layout snapshot, not a complete session restoration.
It rearranges existing panes and windows but does not create panes or windows.
@@ -7323,7 +7340,7 @@ The following variables are available, where appropriate:
.It Li "window_id" Ta "" Ta "Unique window ID"
.It Li "window_index" Ta "#I" Ta "Index of window"
.It Li "window_last_flag" Ta "" Ta "1 if window is the last used"
.It Li "window_layout" Ta "" Ta "Window layout description, including zoom and pane flags"
.It Li "window_layout" Ta "" Ta "Window layout description (legacy by default in control mode)"
.It Li "window_linked" Ta "" Ta "1 if window is linked across sessions"
.It Li "window_linked_sessions" Ta "" Ta "Number of sessions this window is linked to"
.It Li "window_linked_sessions_list" Ta "" Ta "List of sessions this window is linked to"
@@ -7336,7 +7353,7 @@ The following variables are available, where appropriate:
.It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert"
.It Li "window_stack_index" Ta "" Ta "Index in session most recent stack"
.It Li "window_start_flag" Ta "" Ta "1 if window has the lowest index"
.It Li "window_visible_layout" Ta "" Ta "Window layout description, respecting zoomed window panes"
.It Li "window_visible_layout" Ta "" Ta "Visible window layout description (legacy by default in control mode)"
.It Li "window_width" Ta "" Ta "Width of window"
.It Li "window_zoomed_flag" Ta "" Ta "1 if window is zoomed"
.It Li "wrap_flag" Ta "" Ta "Pane wrap flag"
@@ -8861,6 +8878,9 @@ The window's visible layout is
.Ar window\-visible\-layout
and the window flags are
.Ar window\-flags .
The layouts use the legacy serialization unless the client has the
.Ql window\-layout\-v2
flag, in which case they use the current serialization.
.It Ic %message Ar message
A message sent with the
.Ic display\-message

3
tmux.h
View File

@@ -2245,7 +2245,7 @@ struct client {
#define CLIENT_CONTROL_PAUSEAFTER 0x100000000ULL
#define CLIENT_CONTROL_WAITEXIT 0x200000000ULL
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
/* 0x800000000ULL unused */
#define CLIENT_CONTROL_WINDOWLAYOUTV2 0x800000000ULL
#define CLIENT_BRACKETPASTING 0x1000000000ULL
#define CLIENT_ASSUMEPASTING 0x2000000000ULL
/* 0x4000000000ULL unused */
@@ -3740,6 +3740,7 @@ int layout_insert_tile(struct window *, struct layout_cell *);
/* layout-custom.c */
struct layout_prepared;
char *layout_dump(struct window *, struct layout_cell *);
char *layout_dump_legacy(struct layout_cell *);
struct layout_prepared *layout_prepare(struct window *, const char *, char **);
void layout_free_prepared(struct layout_prepared *);
void layout_apply_prepared(struct window *, struct layout_prepared *);