Sync OpenBSD patchset 732:

Custom layouts. list-windows command displays the layout as a string (such as
"bb62,159x48,0,0{79x48,0,0,79x48,80,0}") and it can be applied to another
window (with the same number of panes or fewer) using select-layout.
This commit is contained in:
Tiago Cunha 2010-07-02 02:54:52 +00:00
parent e4573de97b
commit e4703bacb5
7 changed files with 388 additions and 65 deletions

View File

@ -1,4 +1,4 @@
/* $Id: cmd-list-windows.c,v 1.42 2009-11-14 17:56:39 tcunha Exp $ */ /* $Id: cmd-list-windows.c,v 1.43 2010-07-02 02:54:52 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -45,6 +45,7 @@ cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx)
struct cmd_target_data *data = self->data; struct cmd_target_data *data = self->data;
struct session *s; struct session *s;
struct winlink *wl; struct winlink *wl;
char *layout;
if ((s = cmd_find_session(ctx, data->target)) == NULL) if ((s = cmd_find_session(ctx, data->target)) == NULL)
return (-1); return (-1);
@ -52,6 +53,9 @@ cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx)
RB_FOREACH(wl, winlinks, &s->windows) { RB_FOREACH(wl, winlinks, &s->windows) {
ctx->print(ctx, "%d: %s [%ux%u]", ctx->print(ctx, "%d: %s [%ux%u]",
wl->idx, wl->window->name, wl->window->sx, wl->window->sy); wl->idx, wl->window->name, wl->window->sx, wl->window->sy);
layout = layout_dump(wl->window);
ctx->print(ctx, " layout: %s", layout);
xfree(layout);
} }
return (0); return (0);

View File

@ -1,4 +1,4 @@
/* $Id: cmd-select-layout.c,v 1.11 2010-05-14 14:16:37 tcunha Exp $ */ /* $Id: cmd-select-layout.c,v 1.12 2010-07-02 02:54:52 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@ -79,13 +79,16 @@ cmd_select_layout_exec(struct cmd *self, struct cmd_ctx *ctx)
layout = wl->window->lastlayout; layout = wl->window->lastlayout;
if (layout == -1) if (layout == -1)
return (0); return (0);
} else if ((layout = layout_set_lookup(data->arg)) == -1) { } else if ((layout = layout_set_lookup(data->arg)) != -1) {
ctx->error(ctx, "unknown layout or ambiguous: %s", data->arg); layout = layout_set_select(wl->window, layout);
return (-1); ctx->info(ctx, "arranging in: %s", layout_set_name(layout));
} else {
if (layout_parse(wl->window, data->arg) == -1) {
ctx->error(ctx, "can't set layout: %s", data->arg);
return (-1);
}
ctx->info(ctx, "arranging in: %s", data->arg);
} }
layout = layout_set_select(wl->window, layout);
ctx->info(ctx, "arranging in: %s", layout_set_name(layout));
return (0); return (0);
} }

264
layout-custom.c Normal file
View File

@ -0,0 +1,264 @@
/* $Id: layout-custom.c,v 1.1 2010-07-02 02:54:52 tcunha Exp $ */
/*
* Copyright (c) 2010 Nicholas Marriott <nicm@users.sourceforge.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <ctype.h>
#include <string.h>
#include "tmux.h"
u_short layout_checksum(const char *);
int layout_append(struct layout_cell *, char *, size_t);
struct layout_cell *layout_construct(struct layout_cell *, const char **);
void layout_assign(struct window_pane **, struct layout_cell *);
/* Calculate layout checksum. */
u_short
layout_checksum(const char *layout)
{
u_short csum;
csum = 0;
for (; *layout != '\0'; layout++) {
csum = (csum >> 1) + ((csum & 1) << 15);
csum += *layout;
}
return (csum);
}
/* Dump layout as a string. */
char *
layout_dump(struct window *w)
{
char layout[BUFSIZ], *out;
*layout = '\0';
if (layout_append(w->layout_root, layout, sizeof layout) != 0)
return (NULL);
xasprintf(&out, "%4x,%s", layout_checksum(layout), layout);
return (out);
}
/* Append information for a single cell. */
int
layout_append(struct layout_cell *lc, char *buf, size_t len)
{
struct layout_cell *lcchild;
char tmp[64];
size_t tmplen;
const char *brackets = "][";
if (len == 0)
return (-1);
tmplen = xsnprintf(tmp, sizeof tmp,
"%ux%u,%u,%u", lc->sx, lc->sy, lc->xoff, lc->yoff);
if (tmplen > (sizeof tmp) - 1)
return (-1);
if (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(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);
}
/* Parse a layout string and arrange window as layout. */
int
layout_parse(struct window *w, const char *layout)
{
struct layout_cell *lc, *lcchild;
struct window_pane *wp;
u_int npanes, ncells, sx, sy;
u_short csum;
/* Check validity. */
if (sscanf(layout, "%hx,", &csum) != 1)
return (-1);
layout += 5;
if (csum != layout_checksum(layout))
return (-1);
/* Build the layout. */
lc = layout_construct(NULL, &layout);
if (lc == NULL)
return (-1);
if (*layout != '\0')
goto fail;
/* Check this window will fit into the layout. */
for (;;) {
npanes = window_count_panes(w);
ncells = layout_count_cells(lc);
if (npanes > ncells)
goto fail;
if (npanes == ncells)
break;
/* Fewer panes than cells - close the bottom right. */
lcchild = layout_find_bottomright(lc);
layout_destroy_cell(lcchild, &lc);
}
/* Save the old window size and resize to the layout size. */
sx = w->sx; sy = w->sy;
window_resize(w, lc->sx, lc->sy);
/* Destroy the old layout and swap to the new. */
layout_free_cell(w->layout_root);
w->layout_root = lc;
/* Assign the panes into the cells. */
wp = TAILQ_FIRST(&w->panes);
layout_assign(&wp, lc);
/* Update pane offsets and sizes. */
layout_fix_offsets(lc);
layout_fix_panes(w, lc->sx, lc->sy);
/* Then resize the layout back to the original window size. */
layout_resize(w, sx, sy);
window_resize(w, sx, sy);
layout_print_cell(lc, __func__, 0);
return (0);
fail:
layout_free_cell(lc);
return (-1);
}
/* Assign panes into cells. */
void
layout_assign(struct window_pane **wp, struct layout_cell *lc)
{
struct layout_cell *lcchild;
switch (lc->type) {
case LAYOUT_WINDOWPANE:
layout_make_leaf(lc, *wp);
*wp = TAILQ_NEXT(*wp, entry);
return;
case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &lc->cells, entry)
layout_assign(wp, lcchild);
return;
}
}
/* Construct a cell from all or part of a layout tree. */
struct layout_cell *
layout_construct(struct layout_cell *lcparent, const char **layout)
{
struct layout_cell *lc, *lcchild;
u_int sx, sy, xoff, yoff;
if (!isdigit((u_char) **layout))
return (NULL);
if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4)
return (NULL);
while (isdigit((u_char) **layout))
(*layout)++;
if (**layout != 'x')
return (NULL);
(*layout)++;
while (isdigit((u_char) **layout))
(*layout)++;
if (**layout != ',')
return (NULL);
(*layout)++;
while (isdigit((u_char) **layout))
(*layout)++;
if (**layout != ',')
return (NULL);
(*layout)++;
while (isdigit((u_char) **layout))
(*layout)++;
lc = layout_create_cell(lcparent);
lc->sx = sx;
lc->sy = sy;
lc->xoff = xoff;
lc->yoff = yoff;
switch (**layout) {
case ',':
case '}':
case ']':
case '\0':
return (lc);
case '{':
lc->type = LAYOUT_LEFTRIGHT;
break;
case '[':
lc->type = LAYOUT_TOPBOTTOM;
break;
default:
goto fail;
}
do {
(*layout)++;
lcchild = layout_construct(lc, layout);
if (lcchild == NULL)
goto fail;
TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry);
} while (**layout == ',');
switch (lc->type) {
case LAYOUT_LEFTRIGHT:
if (**layout != '}')
goto fail;
break;
case LAYOUT_TOPBOTTOM:
if (**layout != ']')
goto fail;
break;
default:
goto fail;
}
(*layout)++;
return (lc);
fail:
layout_free_cell(lc);
return (NULL);
}

View File

@ -1,4 +1,4 @@
/* $Id: layout-string.c,v 1.3 2010-01-08 16:28:04 tcunha Exp $ */ /* $Id: layout-string.c,v 1.4 2010-07-02 02:54:52 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@ -36,7 +36,6 @@ struct layout_cell *layout_find_right(struct layout_cell *);
struct layout_cell *layout_find_topleft(struct layout_cell *); struct layout_cell *layout_find_topleft(struct layout_cell *);
struct layout_cell *layout_find_topright(struct layout_cell *); struct layout_cell *layout_find_topright(struct layout_cell *);
struct layout_cell *layout_find_bottomleft(struct layout_cell *); struct layout_cell *layout_find_bottomleft(struct layout_cell *);
struct layout_cell *layout_find_bottomright(struct layout_cell *);
/* Find the cell; returns NULL if string not understood. */ /* Find the cell; returns NULL if string not understood. */
struct layout_cell * struct layout_cell *

130
layout.c
View File

@ -1,4 +1,4 @@
/* $Id: layout.c,v 1.18 2010-01-08 16:31:35 tcunha Exp $ */ /* $Id: layout.c,v 1.19 2010-07-02 02:54:52 tcunha Exp $ */
/* /*
* Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
@ -218,6 +218,27 @@ layout_fix_panes(struct window *w, u_int wsx, u_int wsy)
} }
} }
/* Count the number of available cells in a layout. */
u_int
layout_count_cells(struct layout_cell *lc)
{
struct layout_cell *lcchild;
u_int n;
switch (lc->type) {
case LAYOUT_WINDOWPANE:
return (1);
case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM:
n = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry)
n += layout_count_cells(lcchild);
return (n);
default:
fatalx("bad layout type");
}
}
/* Calculate how much size is available to be removed from a cell. */ /* Calculate how much size is available to be removed from a cell. */
u_int u_int
layout_resize_check(struct layout_cell *lc, enum layout_type type) layout_resize_check(struct layout_cell *lc, enum layout_type type)
@ -302,6 +323,56 @@ layout_resize_adjust(struct layout_cell *lc, enum layout_type type, int change)
} }
} }
/* Destroy a cell and redistribute the space. */
void
layout_destroy_cell(struct layout_cell *lc, struct layout_cell **lcroot)
{
struct layout_cell *lcother, *lcparent;
/*
* If no parent, this is the last pane so window close is imminent and
* there is no need to resize anything.
*/
lcparent = lc->parent;
if (lcparent == NULL) {
layout_free_cell(lc);
*lcroot = NULL;
return;
}
/* Merge the space into the previous or next cell. */
if (lc == TAILQ_FIRST(&lcparent->cells))
lcother = TAILQ_NEXT(lc, entry);
else
lcother = TAILQ_PREV(lc, layout_cells, entry);
if (lcparent->type == LAYOUT_LEFTRIGHT)
layout_resize_adjust(lcother, lcparent->type, lc->sx + 1);
else
layout_resize_adjust(lcother, lcparent->type, lc->sy + 1);
/* Remove this from the parent's list. */
TAILQ_REMOVE(&lcparent->cells, lc, entry);
layout_free_cell(lc);
/*
* If the parent now has one cell, remove the parent from the tree and
* replace it by that cell.
*/
lc = TAILQ_FIRST(&lcparent->cells);
if (TAILQ_NEXT(lc, entry) == NULL) {
TAILQ_REMOVE(&lcparent->cells, lc, entry);
lc->parent = lcparent->parent;
if (lc->parent == NULL) {
lc->xoff = 0; lc->yoff = 0;
*lcroot = lc;
} else
TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
layout_free_cell(lcparent);
}
}
void void
layout_init(struct window *w) layout_init(struct window *w)
{ {
@ -597,59 +668,16 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size)
return (lcnew); return (lcnew);
} }
/* Destroy the layout associated with a pane and redistribute the space. */ /* Destroy the cell associated with a pane. */
void void
layout_close_pane(struct window_pane *wp) layout_close_pane(struct window_pane *wp)
{ {
struct layout_cell *lc, *lcother, *lcparent; /* Remove the cell. */
layout_destroy_cell(wp->layout_cell, &wp->window->layout_root);
lc = wp->layout_cell;
lcparent = lc->parent;
/*
* If no parent, this is the last pane so window close is imminent and
* there is no need to resize anything.
*/
if (lcparent == NULL) {
layout_free_cell(lc);
wp->window->layout_root = NULL;
return;
}
/* Merge the space into the previous or next cell. */
if (lc == TAILQ_FIRST(&lcparent->cells))
lcother = TAILQ_NEXT(lc, entry);
else
lcother = TAILQ_PREV(lc, layout_cells, entry);
if (lcparent->type == LAYOUT_LEFTRIGHT)
layout_resize_adjust(lcother, lcparent->type, lc->sx + 1);
else
layout_resize_adjust(lcother, lcparent->type, lc->sy + 1);
/* Remove this from the parent's list. */
TAILQ_REMOVE(&lcparent->cells, lc, entry);
layout_free_cell(lc);
/*
* If the parent now has one cell, remove the parent from the tree and
* replace it by that cell.
*/
lc = TAILQ_FIRST(&lcparent->cells);
if (TAILQ_NEXT(lc, entry) == NULL) {
TAILQ_REMOVE(&lcparent->cells, lc, entry);
lc->parent = lcparent->parent;
if (lc->parent == NULL) {
lc->xoff = 0; lc->yoff = 0;
wp->window->layout_root = lc;
} else
TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
layout_free_cell(lcparent);
}
/* Fix pane offsets and sizes. */ /* Fix pane offsets and sizes. */
layout_fix_offsets(wp->window->layout_root); if (wp->window->layout_root != NULL) {
layout_fix_panes(wp->window, wp->window->sx, wp->window->sy); layout_fix_offsets(wp->window->layout_root);
layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
}
} }

24
tmux.1
View File

@ -1,4 +1,4 @@
.\" $Id: tmux.1,v 1.262 2010-07-02 02:49:19 tcunha Exp $ .\" $Id: tmux.1,v 1.263 2010-07-02 02:54:52 tcunha Exp $
.\" .\"
.\" Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> .\" Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
.\" .\"
@ -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: June 27 2010 $ .Dd $Mdocdate: June 29 2010 $
.Dt TMUX 1 .Dt TMUX 1
.Os .Os
.Sh NAME .Sh NAME
@ -877,6 +877,24 @@ Panes are spread out as evenly as possible over the window in both rows and
columns. columns.
.El .El
.Pp .Pp
In addition,
.Ic select-layout
may be used to apply a previously used layout - the
.Ic list-windows
command displays the layout of each window in a form suitable for use with
.Ic select-layout .
For example:
.Bd -literal -offset indent
$ tmux list-windows
0: ksh [159x48]
layout: bb62,159x48,0,0{79x48,0,0,79x48,80,0}
$ tmux select-layout bb62,159x48,0,0{79x48,0,0,79x48,80,0}
.Ed
.Nm
automatically adjusts the size of the layout for the current window size.
Note that a layout cannot be applied to a window with more panes than that
from which the layout was originally defined.
.Pp
Commands related to windows and panes are as follows: Commands related to windows and panes are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Xo Ic break-pane .It Xo Ic break-pane
@ -1224,7 +1242,7 @@ or downward (numerically higher).
Choose a specific layout for a window. Choose a specific layout for a window.
If If
.Ar layout-name .Ar layout-name
is not given, the last layout used (if any) is reapplied. is not given, the last preset layout used (if any) is reapplied.
.It Xo Ic select-pane .It Xo Ic select-pane
.Op Fl DLRU .Op Fl DLRU
.Op Fl t Ar target-pane .Op Fl t Ar target-pane

9
tmux.h
View File

@ -1,4 +1,4 @@
/* $Id: tmux.h,v 1.567 2010-07-02 02:52:13 tcunha Exp $ */ /* $Id: tmux.h,v 1.568 2010-07-02 02:54:52 tcunha Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@ -1849,9 +1849,11 @@ struct window_pane *window_pane_find_left(struct window_pane *);
struct window_pane *window_pane_find_right(struct window_pane *); struct window_pane *window_pane_find_right(struct window_pane *);
/* layout.c */ /* layout.c */
u_int layout_count_cells(struct layout_cell *);
struct layout_cell *layout_create_cell(struct layout_cell *); struct layout_cell *layout_create_cell(struct layout_cell *);
void layout_free_cell(struct layout_cell *); void layout_free_cell(struct layout_cell *);
void layout_print_cell(struct layout_cell *, const char *, u_int); void layout_print_cell(struct layout_cell *, const char *, u_int);
void layout_destroy_cell(struct layout_cell *, struct layout_cell **);
void layout_set_size( void layout_set_size(
struct layout_cell *, u_int, u_int, u_int, u_int); struct layout_cell *, u_int, u_int, u_int, u_int);
void layout_make_leaf( void layout_make_leaf(
@ -1872,6 +1874,10 @@ struct layout_cell *layout_split_pane(
struct window_pane *, enum layout_type, int); struct window_pane *, enum layout_type, int);
void layout_close_pane(struct window_pane *); void layout_close_pane(struct window_pane *);
/* layout-custom.c */
char *layout_dump(struct window *);
int layout_parse(struct window *, const char *);
/* layout-set.c */ /* layout-set.c */
const char *layout_set_name(u_int); const char *layout_set_name(u_int);
int layout_set_lookup(const char *); int layout_set_lookup(const char *);
@ -1882,6 +1888,7 @@ void layout_set_active_changed(struct window *);
/* layout-string.c */ /* layout-string.c */
struct layout_cell *layout_find_string(struct window *, const char *); struct layout_cell *layout_find_string(struct window *, const char *);
struct layout_cell *layout_find_bottomright(struct layout_cell *);
/* window-clock.c */ /* window-clock.c */
extern const struct window_mode window_clock_mode; extern const struct window_mode window_clock_mode;