Improved layout code.

Each window now has a tree of layout cells associated with it. In this tree,
each node is either a horizontal or vertical cell containing a list of other
cells running from left-to-right or top-to-bottom, or a leaf cell which is
associated with a pane.

The major functional changes are:

- panes may now be split arbitrarily both horizontally (splitw -h, C-b %) and
  vertically (splitw -v, C-b ");
- panes may be resized both horizontally and vertically (resizep -L/-R/-U/-D,
  bound to C-b left/right/up/down and C-b M-left/right/up/down);
- layouts are now applied and then may be modified by resizing or splitting
  panes, rather than being fixed and reapplied when the window is resized or
  panes are added;
- manual-vertical layout is no longer necessary, and active-only layout is gone
  (but may return in future);
- the main-pane layouts now reduce the size of the main pane to fit all panes
  if possible.

Thanks to all who tested.
This commit is contained in:
Nicholas Marriott 2009-07-19 13:21:40 +00:00
parent fc6a65c620
commit 6036bdd06c
25 changed files with 1298 additions and 636 deletions

View File

@ -27,7 +27,7 @@ SRCS= attributes.c buffer-poll.c buffer.c cfg.c client-fn.c \
cmd-switch-client.c cmd-unbind-key.c cmd-unlink-window.c \
cmd-up-pane.c cmd-display-message.c cmd.c \
colour.c grid-view.c grid.c input-keys.c \
input.c key-bindings.c key-string.c layout-manual.c layout.c log.c \
input.c key-bindings.c key-string.c layout-set.c layout.c log.c \
mode-key.c names.c options-cmd.c options.c paste.c procname.c \
resize.c screen-redraw.c screen-write.c screen.c server-fn.c \
server-msg.c server.c session.c status.c tmux.c tty-keys.c tty-term.c \

View File

@ -74,17 +74,17 @@ cmd_break_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
if (wl->window->active == NULL)
wl->window->active = TAILQ_NEXT(wp, entry);
}
layout_refresh(wl->window, 0);
layout_close_pane(wp);
w = wp->window = window_create1(s->sx, s->sy);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
w->active = wp;
w->name = default_window_name(w);
layout_init(w);
wl = session_attach(s, w, -1, &cause); /* can't fail */
if (!(data->chflags & CMD_CHFLAG('d')))
session_select(s, wl->idx);
layout_refresh(w, 0);
server_redraw_session(s);

View File

@ -76,8 +76,8 @@ cmd_choose_window_exec(struct cmd *self, struct cmd_ctx *ctx)
idx++;
window_choose_add(wl->window->active,
wm->idx, "%3d: %s [%ux%u %s] (%u panes)", wm->idx, w->name,
w->sx, w->sy, layout_name(w), window_count_panes(w));
wm->idx, "%3d: %s [%ux%u] (%u panes)",
wm->idx, w->name, w->sx, w->sy, window_count_panes(w));
}
cdata = xmalloc(sizeof *cdata);

View File

@ -54,7 +54,6 @@ cmd_down_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
w->active = TAILQ_NEXT(w->active, entry);
if (w->active == NULL)
w->active = TAILQ_FIRST(&w->panes);
layout_refresh(w, 1);
} while (!window_pane_visible(w->active));
return (0);

View File

@ -66,8 +66,8 @@ cmd_kill_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
return (0);
}
layout_close_pane(wp);
window_remove_pane(wl->window, wp);
server_redraw_window(wl->window);
layout_refresh(wl->window, 0);
return (0);
}

View File

@ -78,9 +78,8 @@ cmd_list_windows_exec(struct cmd *self, struct cmd_ctx *ctx)
else
name = "unknown";
ctx->print(ctx,
" %s [%ux%u %s] [history %u/%u, %llu bytes]",
name, wp->sx, wp->sy, layout_name(w), gd->hsize,
gd->hlimit, size);
" %s [%ux%u] [history %u/%u, %llu bytes]",
name, wp->sx, wp->sy, gd->hsize, gd->hlimit, size);
}
}

View File

@ -44,12 +44,13 @@ cmd_next_layout_exec(struct cmd *self, struct cmd_ctx *ctx)
{
struct cmd_target_data *data = self->data;
struct winlink *wl;
u_int layout;
if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
return (-1);
layout_next(wl->window);
ctx->info(ctx, "layout now: %s", layout_name(wl->window));
layout = layout_set_next(wl->window);
ctx->info(ctx, "arranging in: %s", layout_set_name(layout));
return (0);
}

View File

@ -44,12 +44,13 @@ cmd_previous_layout_exec(struct cmd *self, struct cmd_ctx *ctx)
{
struct cmd_target_data *data = self->data;
struct winlink *wl;
u_int layout;
if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
return (-1);
layout_previous(wl->window);
ctx->info(ctx, "layout now: %s", layout_name(wl->window));
layout = layout_set_previous(wl->window);
ctx->info(ctx, "arranging in: %s", layout_set_name(layout));
return (0);
}

View File

@ -32,7 +32,8 @@ int cmd_resize_pane_exec(struct cmd *, struct cmd_ctx *);
const struct cmd_entry cmd_resize_pane_entry = {
"resize-pane", "resizep",
CMD_PANE_WINDOW_USAGE "[-DU] [adjustment]",
CMD_ARG01, CMD_CHFLAG('D')|CMD_CHFLAG('U'),
CMD_ARG01,
CMD_CHFLAG('D')|CMD_CHFLAG('L')|CMD_CHFLAG('R')|CMD_CHFLAG('U'),
cmd_resize_pane_init,
cmd_pane_parse,
cmd_resize_pane_exec,
@ -50,15 +51,31 @@ cmd_resize_pane_init(struct cmd *self, int key)
cmd_pane_init(self, key);
data = self->data;
if (key == KEYC_ADDCTL(KEYC_UP))
data->chflags |= CMD_CHFLAG('U');
if (key == KEYC_ADDCTL(KEYC_DOWN))
data->chflags |= CMD_CHFLAG('D');
if (key == KEYC_ADDCTL(KEYC_LEFT))
data->chflags |= CMD_CHFLAG('L');
if (key == KEYC_ADDCTL(KEYC_RIGHT))
data->chflags |= CMD_CHFLAG('R');
if (key == KEYC_ADDESC(KEYC_UP))
if (key == KEYC_ADDESC(KEYC_UP)) {
data->chflags |= CMD_CHFLAG('U');
data->arg = xstrdup("5");
}
if (key == KEYC_ADDESC(KEYC_DOWN)) {
data->chflags |= CMD_CHFLAG('D');
data->arg = xstrdup("5");
}
if (key == KEYC_ADDESC(KEYC_LEFT)) {
data->chflags |= CMD_CHFLAG('L');
data->arg = xstrdup("5");
}
if (key == KEYC_ADDESC(KEYC_RIGHT)) {
data->chflags |= CMD_CHFLAG('R');
data->arg = xstrdup("5");
}
}
int
@ -92,12 +109,14 @@ cmd_resize_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
}
}
if (!(data->chflags & CMD_CHFLAG('D')))
adjust = -adjust;
if (layout_resize(wp, adjust) != 0) {
ctx->error(ctx, "layout %s "
"does not support resizing", layout_name(wp->window));
return (-1);
if (data->chflags & (CMD_CHFLAG('L')|CMD_CHFLAG('R'))) {
if (data->chflags & CMD_CHFLAG('L'))
adjust = -adjust;
layout_resize_pane(wp, LAYOUT_LEFTRIGHT, adjust);
} else {
if (data->chflags & CMD_CHFLAG('U'))
adjust = -adjust;
layout_resize_pane(wp, LAYOUT_TOPBOTTOM, adjust);
}
server_redraw_window(wl->window);

View File

@ -70,6 +70,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx)
wp = TAILQ_FIRST(&w->panes);
TAILQ_REMOVE(&w->panes, wp, entry);
layout_free(w);
window_destroy_panes(w);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
window_pane_resize(wp, w->sx, w->sy);
@ -78,6 +79,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_ctx *ctx)
xfree(cause);
return (-1);
}
layout_init(w);
screen_reinit(&wp->base);
recalculate_sizes();

View File

@ -59,6 +59,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmd_ctx *ctx)
struct winlink *wl;
struct window *w;
struct window_pane *wp, *wp2;
struct layout_cell *lc;
u_int sx, sy, xoff, yoff;
if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
@ -70,42 +71,56 @@ cmd_rotate_window_exec(struct cmd *self, struct cmd_ctx *ctx)
TAILQ_REMOVE(&w->panes, wp, entry);
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
lc = wp->layout_cell;
xoff = wp->xoff; yoff = wp->yoff;
sx = wp->sx; sy = wp->sy;
TAILQ_FOREACH(wp, &w->panes, entry) {
if ((wp2 = TAILQ_NEXT(wp, entry)) == NULL)
break;
wp->layout_cell = wp2->layout_cell;
if (wp->layout_cell != NULL)
wp->layout_cell->wp = wp;
wp->xoff = wp2->xoff; wp->yoff = wp2->yoff;
window_pane_resize(wp, wp2->sx, wp2->sy);
}
wp->layout_cell = lc;
if (wp->layout_cell != NULL)
wp->layout_cell->wp = wp;
wp->xoff = xoff; wp->yoff = yoff;
window_pane_resize(wp, sx, sy);
if ((wp = TAILQ_PREV(w->active, window_panes, entry)) == NULL)
wp = TAILQ_LAST(&w->panes, window_panes);
window_set_active_pane(w, wp);
server_redraw_window(w);
} else {
wp = TAILQ_FIRST(&w->panes);
TAILQ_REMOVE(&w->panes, wp, entry);
TAILQ_INSERT_TAIL(&w->panes, wp, entry);
lc = wp->layout_cell;
xoff = wp->xoff; yoff = wp->yoff;
sx = wp->sx; sy = wp->sy;
TAILQ_FOREACH_REVERSE(wp, &w->panes, window_panes, entry) {
if ((wp2 = TAILQ_PREV(wp, window_panes, entry)) == NULL)
break;
wp->layout_cell = wp2->layout_cell;
if (wp->layout_cell != NULL)
wp->layout_cell->wp = wp;
wp->xoff = wp2->xoff; wp->yoff = wp2->yoff;
window_pane_resize(wp, wp2->sx, wp2->sy);
}
wp->layout_cell = lc;
if (wp->layout_cell != NULL)
wp->layout_cell->wp = wp;
wp->xoff = xoff; wp->yoff = yoff;
window_pane_resize(wp, sx, sy);
if ((wp = TAILQ_NEXT(w->active, entry)) == NULL)
wp = TAILQ_FIRST(&w->panes);
window_set_active_pane(w, wp);
server_redraw_window(w);
}
layout_refresh(w, 0);
return (0);
}

View File

@ -49,17 +49,17 @@ cmd_select_layout_init(struct cmd *self, int key)
data = self->data;
switch (key) {
case KEYC_ADDESC('0'):
data->arg = xstrdup("manual-vertical");
break;
case KEYC_ADDESC('1'):
data->arg = xstrdup("even-horizontal");
break;
case KEYC_ADDESC('2'):
data->arg = xstrdup("even-vertical");
break;
case KEYC_ADDESC('9'):
data->arg = xstrdup("active-only");
case KEYC_ADDESC('3'):
data->arg = xstrdup("main-horizontal");
break;
case KEYC_ADDESC('4'):
data->arg = xstrdup("main-vertical");
break;
}
}
@ -74,13 +74,13 @@ cmd_select_layout_exec(struct cmd *self, struct cmd_ctx *ctx)
if ((wl = cmd_find_window(ctx, data->target, NULL)) == NULL)
return (-1);
if ((layout = layout_lookup(data->arg)) == -1) {
ctx->error(ctx, "unknown or ambiguous layout: %s", data->arg);
return (-1);
}
if ((layout = layout_set_lookup(data->arg)) == -1) {
ctx->error(ctx, "unknown or ambiguous layout: %s", data->arg);
return (-1);
}
if (layout_select(wl->window, layout) == 0)
ctx->info(ctx, "layout now: %s", layout_name(wl->window));
layout = layout_set_select(wl->window, layout);
ctx->info(ctx, "arranging in: %s", layout_set_name(layout));
return (0);
}

View File

@ -63,7 +63,6 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
return (-1);
}
window_set_active_pane(wl->window, wp);
layout_refresh(wl->window, 1);
return (0);
}

View File

@ -39,13 +39,14 @@ struct cmd_split_window_data {
char *target;
char *cmd;
int flag_detached;
int flag_horizontal;
int percentage;
int lines;
int size;
};
const struct cmd_entry cmd_split_window_entry = {
"split-window", "splitw",
"[-d] [-p percentage|-l lines] [-t target-window] [command]",
"[-dhv] [-p percentage|-l size] [-t target-window] [command]",
0, 0,
cmd_split_window_init,
cmd_split_window_parse,
@ -57,7 +58,7 @@ const struct cmd_entry cmd_split_window_entry = {
};
void
cmd_split_window_init(struct cmd *self, unused int arg)
cmd_split_window_init(struct cmd *self, int key)
{
struct cmd_split_window_data *data;
@ -65,50 +66,63 @@ cmd_split_window_init(struct cmd *self, unused int arg)
data->target = NULL;
data->cmd = NULL;
data->flag_detached = 0;
data->flag_horizontal = 0;
data->percentage = -1;
data->lines = -1;
data->size = -1;
switch (key) {
case '%':
data->flag_horizontal = 1;
break;
case '"':
data->flag_horizontal = 0;
break;
}
}
int
cmd_split_window_parse(struct cmd *self, int argc, char **argv, char **cause)
{
struct cmd_split_window_data *data;
int opt, n;
int opt;
const char *errstr;
self->entry->init(self, 0);
data = self->data;
while ((opt = getopt(argc, argv, "dl:p:t:")) != -1) {
while ((opt = getopt(argc, argv, "dhl:p:t:v")) != -1) {
switch (opt) {
case 'd':
data->flag_detached = 1;
break;
case 'h':
data->flag_horizontal = 1;
break;
case 't':
if (data->target == NULL)
data->target = xstrdup(optarg);
break;
case 'l':
if (data->percentage == -1 && data->lines == -1) {
n = strtonum(optarg, 1, INT_MAX, &errstr);
if (errstr != NULL) {
xasprintf(cause, "lines %s", errstr);
goto error;
}
data->lines = n;
if (data->percentage != -1 || data->size != -1)
break;
data->size = strtonum(optarg, 1, INT_MAX, &errstr);
if (errstr != NULL) {
xasprintf(cause, "size %s", errstr);
goto error;
}
break;
case 'p':
if (data->lines == -1 && data->percentage == -1) {
n = strtonum(optarg, 1, 100, &errstr);
if (errstr != NULL) {
xasprintf(
cause, "percentage %s", errstr);
goto error;
}
data->percentage = n;
if (data->size != -1 || data->percentage != -1)
break;
data->percentage = strtonum(optarg, 1, 100, &errstr);
if (errstr != NULL) {
xasprintf(cause, "percentage %s", errstr);
goto error;
}
break;
case 'v':
data->flag_horizontal = 0;
break;
default:
goto usage;
}
@ -142,7 +156,8 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
const char **env;
char *cmd, *cwd, *cause;
u_int hlimit;
int lines;
int size;
enum layout_type type;
if ((wl = cmd_find_window(ctx, data->target, &s)) == NULL)
return (-1);
@ -158,19 +173,27 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
else
cwd = ctx->cmdclient->cwd;
lines = -1;
if (data->lines != -1)
lines = data->lines;
size = -1;
if (data->size != -1)
size = data->size;
else if (data->percentage != -1)
lines = (w->active->sy * data->percentage) / 100;
size = (w->active->sy * data->percentage) / 100;
hlimit = options_get_number(&s->options, "history-limit");
wp = window_add_pane(w, lines, cmd, cwd, env, hlimit, &cause);
if (wp == NULL) {
ctx->error(ctx, "create pane failed: %s", cause);
xfree(cause);
return (-1);
type = LAYOUT_TOPBOTTOM;
if (data->flag_horizontal)
type = LAYOUT_LEFTRIGHT;
wp = window_add_pane(w, hlimit, &cause);
if (wp == NULL)
goto error;
if (window_pane_spawn(wp, cmd, cwd, env, &cause) != 0)
goto error;
if (layout_split_pane(w->active, type, size, wp) != 0) {
cause = xstrdup("pane too small");
goto error;
}
server_redraw_window(w);
if (!data->flag_detached) {
@ -179,9 +202,15 @@ cmd_split_window_exec(struct cmd *self, struct cmd_ctx *ctx)
server_redraw_session(s);
} else
server_status_session(s);
layout_refresh(w, 0);
return (0);
error:
if (wp != NULL)
window_remove_pane(w, wp);
ctx->error(ctx, "create pane failed: %s", cause);
xfree(cause);
return (-1);
}
void
@ -228,6 +257,8 @@ cmd_split_window_print(struct cmd *self, char *buf, size_t len)
return (off);
if (off < len && data->flag_detached)
off += xsnprintf(buf + off, len - off, " -d");
if (off < len && data->flag_horizontal)
off += xsnprintf(buf + off, len - off, " -h");
if (off < len && data->target != NULL)
off += cmd_prarg(buf + off, len - off, " -t ", data->target);
if (off < len && data->cmd != NULL)

View File

@ -157,6 +157,7 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
struct winlink *wl;
struct window *w;
struct window_pane *tmp_wp, *src_wp, *dst_wp;
struct layout_cell *lc;
u_int xx, yy;
if (data == NULL)
@ -207,12 +208,15 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
else
TAILQ_INSERT_AFTER(&w->panes, tmp_wp, src_wp, entry);
lc = src_wp->layout_cell;
xx = src_wp->xoff;
yy = src_wp->yoff;
src_wp->xoff = dst_wp->xoff;
src_wp->yoff = dst_wp->yoff;
dst_wp->xoff = xx;
dst_wp->yoff = yy;
src_wp->layout_cell = dst_wp->layout_cell;
if (src_wp->layout_cell != NULL)
src_wp->layout_cell->wp = src_wp;
dst_wp->layout_cell = lc;
if (dst_wp->layout_cell != NULL)
dst_wp->layout_cell->wp = dst_wp;
xx = src_wp->sx;
yy = src_wp->sy;
@ -224,8 +228,8 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
if (!window_pane_visible(tmp_wp))
tmp_wp = src_wp;
window_set_active_pane(w, tmp_wp);
layout_refresh(w, 0);
}
server_redraw_window(w);
return (0);
}

View File

@ -54,7 +54,6 @@ cmd_up_pane_exec(struct cmd *self, struct cmd_ctx *ctx)
w->active = TAILQ_PREV(w->active, window_panes, entry);
if (w->active == NULL)
w->active = TAILQ_LAST(&w->panes, window_panes);
layout_refresh(w, 1);
} while (!window_pane_visible(w->active));
return (0);

View File

@ -93,7 +93,8 @@ key_bindings_init(void)
} table[] = {
{ ' ', 0, &cmd_next_layout_entry },
{ '!', 0, &cmd_break_pane_entry },
{ '"', 0, &cmd_split_window_entry },
{ '"', 0, &cmd_split_window_entry },
{ '%', 0, &cmd_split_window_entry },
{ '#', 0, &cmd_list_buffers_entry },
{ '&', 0, &cmd_confirm_before_entry },
{ ',', 0, &cmd_command_prompt_entry },
@ -132,10 +133,10 @@ key_bindings_init(void)
{ '{', 0, &cmd_swap_pane_entry },
{ '}', 0, &cmd_swap_pane_entry },
{ '\002', 0, &cmd_send_prefix_entry },
{ KEYC_ADDESC('0'), 0, &cmd_select_layout_entry },
{ KEYC_ADDESC('1'), 0, &cmd_select_layout_entry },
{ KEYC_ADDESC('2'), 0, &cmd_select_layout_entry },
{ KEYC_ADDESC('9'), 0, &cmd_select_layout_entry },
{ KEYC_ADDESC('3'), 0, &cmd_select_layout_entry },
{ KEYC_ADDESC('4'), 0, &cmd_select_layout_entry },
{ KEYC_PPAGE, 0, &cmd_scroll_mode_entry },
{ KEYC_ADDESC('n'), 0, &cmd_next_window_entry },
{ KEYC_ADDESC('p'), 0, &cmd_previous_window_entry },
@ -143,8 +144,12 @@ key_bindings_init(void)
{ KEYC_DOWN, 0, &cmd_down_pane_entry },
{ KEYC_ADDESC(KEYC_UP), 1, &cmd_resize_pane_entry },
{ KEYC_ADDESC(KEYC_DOWN), 1, &cmd_resize_pane_entry },
{ KEYC_ADDESC(KEYC_LEFT), 1, &cmd_resize_pane_entry },
{ KEYC_ADDESC(KEYC_RIGHT),1, &cmd_resize_pane_entry },
{ KEYC_ADDCTL(KEYC_UP), 1, &cmd_resize_pane_entry },
{ KEYC_ADDCTL(KEYC_DOWN), 1, &cmd_resize_pane_entry },
{ KEYC_ADDCTL(KEYC_DOWN), 1, &cmd_resize_pane_entry },
{ KEYC_ADDCTL(KEYC_LEFT), 1, &cmd_resize_pane_entry },
{ KEYC_ADDCTL(KEYC_RIGHT),1, &cmd_resize_pane_entry },
{ KEYC_ADDESC('o'), 0, &cmd_rotate_window_entry },
{ '\017', 0, &cmd_rotate_window_entry },
};

View File

@ -1,172 +0,0 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 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 "tmux.h"
void layout_manual_v_update_offsets(struct window *);
void
layout_manual_v_refresh(struct window *w, unused int active_only)
{
struct window_pane *wp;
u_int npanes, total, height;
int left;
if (active_only)
return;
if (TAILQ_EMPTY(&w->panes))
return;
/* Check the new size. */
npanes = window_count_panes(w);
if (w->sy <= PANE_MINIMUM * npanes) {
/*
* Make the first pane the smaller of the minimum and total (it
* must fit to be visible) and the rest the minimum size.
*/
height = PANE_MINIMUM;
if (height > w->sy)
height = w->sy + 1;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == TAILQ_FIRST(&w->panes))
wp->sy = height - 1;
else
wp->sy = PANE_MINIMUM - 1;
}
/* And increase the first by the rest if possible. */
if (w->sy >= PANE_MINIMUM)
TAILQ_FIRST(&w->panes)->sy += 1 + w->sy % PANE_MINIMUM;
} else {
/* In theory they will all fit. Find the current total. */
total = 0;
TAILQ_FOREACH(wp, &w->panes, entry)
total += wp->sy;
total += npanes - 1;
/* Growing or shrinking? */
left = w->sy - total;
if (left > 0) {
/* Growing. Expand evenly. */
while (left > 0) {
TAILQ_FOREACH(wp, &w->panes, entry) {
wp->sy++;
if (--left == 0)
break;
}
}
} else {
/* Shrinking. Reduce evenly down to minimum. */
while (left < 0) {
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp->sy <= PANE_MINIMUM - 1)
continue;
wp->sy--;
if (++left == 0)
break;
}
}
}
}
/* Now do the resize. */
TAILQ_FOREACH(wp, &w->panes, entry) {
wp->sy--;
window_pane_resize(wp, w->sx, wp->sy + 1);
}
/* Fill in the offsets. */
layout_manual_v_update_offsets(w);
/* Switch the active window if necessary. */
window_set_active_pane(w, w->active);
}
void
layout_manual_v_resize(struct window_pane *wp, int adjust)
{
struct window *w = wp->window;
struct window_pane *wq;
if (adjust > 0) {
/*
* If this is not the last pane, keep trying to increase size
* and remove it from the next panes. If it is the last, do
* so on the previous pane.
*/
if (TAILQ_NEXT(wp, entry) == NULL) {
if (wp == TAILQ_FIRST(&w->panes)) {
/* Only one pane. */
return;
}
wp = TAILQ_PREV(wp, window_panes, entry);
}
while (adjust-- > 0) {
wq = wp;
while ((wq = TAILQ_NEXT(wq, entry)) != NULL) {
if (wq->sy <= PANE_MINIMUM)
continue;
window_pane_resize(wq, wq->sx, wq->sy - 1);
break;
}
if (wq == NULL)
break;
window_pane_resize(wp, wp->sx, wp->sy + 1);
}
} else {
adjust = -adjust;
/*
* If this is not the last pane, keep trying to reduce size
* and add to the following pane. If it is the last, do so on
* the previous pane.
*/
wq = TAILQ_NEXT(wp, entry);
if (wq == NULL) {
if (wp == TAILQ_FIRST(&w->panes)) {
/* Only one pane. */
return;
}
wq = wp;
wp = TAILQ_PREV(wq, window_panes, entry);
}
while (adjust-- > 0) {
if (wp->sy <= PANE_MINIMUM)
break;
window_pane_resize(wq, wq->sx, wq->sy + 1);
window_pane_resize(wp, wp->sx, wp->sy - 1);
}
}
layout_manual_v_update_offsets(w);
}
void
layout_manual_v_update_offsets(struct window *w)
{
struct window_pane *wp;
u_int yoff;
yoff = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
wp->xoff = 0;
wp->yoff = yoff;
yoff += wp->sy + 1;
}
}

436
layout-set.c Normal file
View File

@ -0,0 +1,436 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2009 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 <string.h>
#include "tmux.h"
/*
* Set window layouts - predefined methods to arrange windows. These are one-off
* and generate a layout tree.
*/
void layout_set_even_h(struct window *);
void layout_set_even_v(struct window *);
void layout_set_main_h(struct window *);
void layout_set_main_v(struct window *);
const struct {
const char *name;
void (*arrange)(struct window *);
} layout_sets[] = {
{ "even-horizontal", layout_set_even_h },
{ "even-vertical", layout_set_even_v },
{ "main-horizontal", layout_set_main_h },
{ "main-vertical", layout_set_main_v },
};
const char *
layout_set_name(u_int layout)
{
return (layout_sets[layout].name);
}
int
layout_set_lookup(const char *name)
{
u_int i;
int matched = -1;
for (i = 0; i < nitems(layout_sets); i++) {
if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) {
if (matched != -1) /* ambiguous */
return (-1);
matched = i;
}
}
return (matched);
}
u_int
layout_set_select(struct window *w, u_int layout)
{
if (layout > nitems(layout_sets) - 1)
layout = nitems(layout_sets) - 1;
if (layout_sets[layout].arrange != NULL)
layout_sets[layout].arrange(w);
w->layout = layout;
return (layout);
}
u_int
layout_set_next(struct window *w)
{
u_int layout = w->layout;
if (layout_sets[layout].arrange != NULL)
layout_sets[layout].arrange(w);
w->layout++;
if (w->layout > nitems(layout_sets) - 1)
w->layout = 0;
return (layout);
}
u_int
layout_set_previous(struct window *w)
{
u_int layout = w->layout;
if (layout_sets[layout].arrange != NULL)
layout_sets[layout].arrange(w);
if (w->layout == 0)
w->layout = nitems(layout_sets) - 1;
else
w->layout--;
return (layout);
}
void
layout_set_even_h(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcnew;
u_int i, n, width, xoff;
layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */
n = window_count_panes(w);
if (n <= 1)
return;
/* How many can we fit? */
if (w->sx / n < PANE_MINIMUM + 1) {
width = PANE_MINIMUM + 1;
n = UINT_MAX;
} else
width = w->sx / n;
/* Free the old root and construct a new. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, w->sx, w->sy, 0, 0);
layout_make_node(lc, LAYOUT_LEFTRIGHT);
/* Build new leaf cells. */
i = xoff = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
/* Create child cell. */
lcnew = layout_create_cell(lc);
layout_set_size(lcnew, width - 1, w->sy, xoff, 0);
layout_make_leaf(lcnew, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
i++;
xoff += width;
}
/* Allocate any remaining space. */
if (w->sx > xoff - 1) {
lc = TAILQ_LAST(&lc->cells, layout_cells);
layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, w->sx - (xoff - 1));
}
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_panes(w, w->sx, w->sy);
layout_print_cell(w->layout_root, __func__, 1);
server_redraw_window(w);
}
void
layout_set_even_v(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcnew;
u_int i, n, height, yoff;
layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */
n = window_count_panes(w);
if (n <= 1)
return;
/* How many can we fit? */
if (w->sy / n < PANE_MINIMUM + 1) {
height = PANE_MINIMUM + 1;
n = UINT_MAX;
} else
height = w->sy / n;
/* Free the old root and construct a new. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, w->sx, w->sy, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM);
/* Build new leaf cells. */
i = yoff = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
/* Create child cell. */
lcnew = layout_create_cell(lc);
layout_set_size(lcnew, w->sx, height - 1, 0, yoff);
layout_make_leaf(lcnew, wp);
TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
i++;
yoff += height;
}
/* Allocate any remaining space. */
if (w->sy > yoff - 1) {
lc = TAILQ_LAST(&lc->cells, layout_cells);
layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, w->sy - (yoff - 1));
}
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_panes(w, w->sx, w->sy);
layout_print_cell(w->layout_root, __func__, 1);
server_redraw_window(w);
}
void
layout_set_main_h(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lcrow, *lcchild;
u_int n, mainheight, width, height, used;
u_int i, j, columns, rows, totalrows;
layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */
n = window_count_panes(w);
if (n <= 1)
return;
n--; /* take off main pane */
/* How many rows and columns will be needed? */
columns = w->sx / (PANE_MINIMUM + 1); /* maximum columns */
rows = 1 + (n - 1) / columns;
columns = 1 + (n - 1) / rows;
width = w->sx / columns;
/* Get the main pane height and add one for separator line. */
mainheight = options_get_number(&w->options, "main-pane-height") + 1;
if (mainheight < PANE_MINIMUM + 1)
mainheight = PANE_MINIMUM + 1;
/* Try and make everything fit. */
totalrows = rows * (PANE_MINIMUM + 1) - 1;
if (mainheight + totalrows > w->sy) {
if (totalrows + PANE_MINIMUM + 1 > w->sy)
mainheight = PANE_MINIMUM + 2;
else
mainheight = w->sy - totalrows;
height = PANE_MINIMUM + 1;
} else
height = (w->sy - mainheight) / rows;
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, w->sx, mainheight + rows * height, 0, 0);
layout_make_node(lc, LAYOUT_TOPBOTTOM);
/* Create the main pane. */
lcmain = layout_create_cell(lc);
layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0);
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
/* Create a grid of the remaining cells. */
wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
for (j = 0; j < rows; j++) {
/* If this is the last cell, all done. */
if (wp == NULL)
break;
/* Create the new row. */
lcrow = layout_create_cell(lc);
layout_set_size(lcrow, w->sx, height - 1, 0, 0);
TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
/* If only one column, just use the row directly. */
if (columns == 1) {
layout_make_leaf(lcrow, wp);
wp = TAILQ_NEXT(wp, entry);
continue;
}
/* Add in the columns. */
layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
for (i = 0; i < columns; i++) {
/* Create and add a pane cell. */
lcchild = layout_create_cell(lcrow);
layout_set_size(lcchild, width - 1, height - 1, 0, 0);
layout_make_leaf(lcchild, wp);
TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
/* Move to the next cell. */
if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
break;
}
/* Adjust the row to fit the full width if necessary. */
if (i == columns)
i--;
used = ((i + 1) * width) - 1;
if (w->sx <= used)
continue;
lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used);
}
/* Adjust the last row height to fit if necessary. */
used = mainheight + (rows * height) - 1;
if (w->sy > used) {
lcrow = TAILQ_LAST(&lc->cells, layout_cells);
layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used);
}
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_panes(w, w->sx, w->sy);
layout_print_cell(w->layout_root, __func__, 1);
server_redraw_window(w);
}
void
layout_set_main_v(struct window *w)
{
struct window_pane *wp;
struct layout_cell *lc, *lcmain, *lccolumn, *lcchild;
u_int n, mainwidth, width, height, used;
u_int i, j, columns, rows, totalcolumns;
layout_print_cell(w->layout_root, __func__, 1);
/* Get number of panes. */
n = window_count_panes(w);
if (n <= 1)
return;
n--; /* take off main pane */
/* How many rows and columns will be needed? */
rows = w->sy / (PANE_MINIMUM + 1); /* maximum rows */
columns = 1 + (n - 1) / rows;
rows = 1 + (n - 1) / columns;
height = w->sy / rows;
/* Get the main pane width and add one for separator line. */
mainwidth = options_get_number(&w->options, "main-pane-width") + 1;
if (mainwidth < PANE_MINIMUM + 1)
mainwidth = PANE_MINIMUM + 1;
/* Try and make everything fit. */
totalcolumns = columns * (PANE_MINIMUM + 1) - 1;
if (mainwidth + totalcolumns > w->sx) {
if (totalcolumns + PANE_MINIMUM + 1 > w->sx)
mainwidth = PANE_MINIMUM + 2;
else
mainwidth = w->sx - totalcolumns;
width = PANE_MINIMUM + 1;
} else
width = (w->sx - mainwidth) / columns;
/* Free old tree and create a new root. */
layout_free(w);
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, mainwidth + columns * width, w->sy, 0, 0);
layout_make_node(lc, LAYOUT_LEFTRIGHT);
/* Create the main pane. */
lcmain = layout_create_cell(lc);
layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0);
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
/* Create a grid of the remaining cells. */
wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
for (j = 0; j < columns; j++) {
/* If this is the last cell, all done. */
if (wp == NULL)
break;
/* Create the new column. */
lccolumn = layout_create_cell(lc);
layout_set_size(lccolumn, width - 1, w->sy, 0, 0);
TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry);
/* If only one row, just use the row directly. */
if (rows == 1) {
layout_make_leaf(lccolumn, wp);
wp = TAILQ_NEXT(wp, entry);
continue;
}
/* Add in the rows. */
layout_make_node(lccolumn, LAYOUT_TOPBOTTOM);
for (i = 0; i < rows; i++) {
/* Create and add a pane cell. */
lcchild = layout_create_cell(lccolumn);
layout_set_size(lcchild, width - 1, height - 1, 0, 0);
layout_make_leaf(lcchild, wp);
TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry);
/* Move to the next cell. */
if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
break;
}
/* Adjust the column to fit the full height if necessary. */
if (i == rows)
i--;
used = ((i + 1) * height) - 1;
if (w->sy <= used)
continue;
lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells);
layout_resize_adjust(lcchild, LAYOUT_TOPBOTTOM, w->sy - used);
}
/* Adjust the last column width to fit if necessary. */
used = mainwidth + (columns * width) - 1;
if (w->sx > used) {
lccolumn = TAILQ_LAST(&lc->cells, layout_cells);
layout_resize_adjust(lccolumn, LAYOUT_LEFTRIGHT, w->sx - used);
}
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_panes(w, w->sx, w->sy);
layout_print_cell(w->layout_root, __func__, 1);
server_redraw_window(w);
}

869
layout.c
View File

@ -18,349 +18,632 @@
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include "tmux.h"
/*
* Each layout has two functions, _refresh to relayout the panes and _resize to
* resize a single pane.
* The window layout is a tree of cells each of which can be one of: a
* left-right container for a list of cells, a top-bottom container for a list
* of cells, or a container for a window pane.
*
* Second argument (int) to _refresh is 1 if the only change has been that the
* active pane has changed. If 0 then panes, active pane or both may have
* changed.
* Each window has a pointer to the root of its layout tree (containing its
* panes), every pane has a pointer back to the cell containing it, and each
* cell a pointer to its parent cell.
*/
void layout_active_only_refresh(struct window *, int);
void layout_even_h_refresh(struct window *, int);
void layout_even_v_refresh(struct window *, int);
void layout_main_h_refresh(struct window *, int);
void layout_main_v_refresh(struct window *, int);
int layout_resize_pane_grow(struct layout_cell *, enum layout_type, int);
int layout_resize_pane_shrink(struct layout_cell *, enum layout_type, int);
const struct {
const char *name;
void (*refresh)(struct window *, int);
void (*resize)(struct window_pane *, int);
} layouts[] = {
{ "manual-vertical", layout_manual_v_refresh, layout_manual_v_resize },
{ "active-only", layout_active_only_refresh, NULL },
{ "even-horizontal", layout_even_h_refresh, NULL },
{ "even-vertical", layout_even_v_refresh, NULL },
{ "main-horizontal", layout_main_h_refresh, NULL },
{ "main-vertical", layout_main_v_refresh, NULL },
};
const char *
layout_name(struct window *w)
struct layout_cell *
layout_create_cell(struct layout_cell *lcparent)
{
return (layouts[w->layout].name);
struct layout_cell *lc;
lc = xmalloc(sizeof *lc);
lc->type = LAYOUT_WINDOWPANE;
lc->parent = lcparent;
TAILQ_INIT(&lc->cells);
lc->sx = UINT_MAX;
lc->sy = UINT_MAX;
lc->xoff = UINT_MAX;
lc->yoff = UINT_MAX;
lc->wp = NULL;
return (lc);
}
int
layout_lookup(const char *name)
void
layout_free_cell(struct layout_cell *lc)
{
u_int i;
int matched = -1;
struct layout_cell *lcchild;
for (i = 0; i < nitems(layouts); i++) {
if (strncmp(layouts[i].name, name, strlen(name)) == 0) {
if (matched != -1) /* ambiguous */
return (-1);
matched = i;
switch (lc->type) {
case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM:
while (!TAILQ_EMPTY(&lc->cells)) {
lcchild = TAILQ_FIRST(&lc->cells);
TAILQ_REMOVE(&lc->cells, lcchild, entry);
layout_free_cell(lcchild);
}
break;
case LAYOUT_WINDOWPANE:
if (lc->wp != NULL)
lc->wp->layout_cell = NULL;
break;
}
return (matched);
}
int
layout_select(struct window *w, u_int layout)
{
if (layout > nitems(layouts) - 1 || layout == w->layout)
return (-1);
w->layout = layout;
layout_refresh(w, 0);
return (0);
xfree(lc);
}
void
layout_next(struct window *w)
layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
{
w->layout++;
if (w->layout > nitems(layouts) - 1)
w->layout = 0;
layout_refresh(w, 0);
struct layout_cell *lcchild;
log_debug(
"%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n, " ", lc,
lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx, lc->sy);
switch (lc->type) {
case LAYOUT_LEFTRIGHT:
case LAYOUT_TOPBOTTOM:
TAILQ_FOREACH(lcchild, &lc->cells, entry)
layout_print_cell(lcchild, hdr, n + 1);
break;
case LAYOUT_WINDOWPANE:
break;
}
}
void
layout_previous(struct window *w)
layout_set_size(
struct layout_cell *lc, u_int sx, u_int sy, u_int xoff, u_int yoff)
{
if (w->layout == 0)
w->layout = nitems(layouts) - 1;
else
w->layout--;
layout_refresh(w, 0);
lc->sx = sx;
lc->sy = sy;
lc->xoff = xoff;
lc->yoff = yoff;
}
void
layout_refresh(struct window *w, int active_only)
layout_make_leaf(struct layout_cell *lc, struct window_pane *wp)
{
layouts[w->layout].refresh(w, active_only);
server_redraw_window(w);
}
lc->type = LAYOUT_WINDOWPANE;
int
layout_resize(struct window_pane *wp, int adjust)
{
struct window *w = wp->window;
TAILQ_INIT(&lc->cells);
if (layouts[w->layout].resize == NULL)
return (-1);
layouts[w->layout].resize(wp, adjust);
return (0);
wp->layout_cell = lc;
lc->wp = wp;
}
void
layout_active_only_refresh(struct window *w, unused int active_only)
layout_make_node(struct layout_cell *lc, enum layout_type type)
{
if (type == LAYOUT_WINDOWPANE)
fatalx("bad layout type");
lc->type = type;
TAILQ_INIT(&lc->cells);
if (lc->wp != NULL)
lc->wp->layout_cell = NULL;
lc->wp = NULL;
}
/* Fix cell offsets based on their sizes. */
void
layout_fix_offsets(struct layout_cell *lc)
{
struct layout_cell *lcchild;
u_int xoff, yoff;
if (lc->type == LAYOUT_LEFTRIGHT) {
xoff = lc->xoff;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
lcchild->xoff = xoff;
lcchild->yoff = lc->yoff;
if (lcchild->type != LAYOUT_WINDOWPANE)
layout_fix_offsets(lcchild);
xoff += lcchild->sx + 1;
}
} else {
yoff = lc->yoff;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
lcchild->xoff = lc->xoff;
lcchild->yoff = yoff;
if (lcchild->type != LAYOUT_WINDOWPANE)
layout_fix_offsets(lcchild);
yoff += lcchild->sy + 1;
}
}
}
/* Update pane offsets and sizes based on their cells. */
void
layout_fix_panes(struct window *w, u_int wsx, u_int wsy)
{
struct window_pane *wp;
u_int xoff;
struct layout_cell *lc;
u_int sx, sy;
xoff = w->sx;
TAILQ_FOREACH(wp, &w->panes, entry) {
/* Put the active pane on screen and the rest to the right. */
if (wp == w->active)
wp->xoff = 0;
if ((lc = wp->layout_cell) == NULL)
continue;
wp->xoff = lc->xoff;
wp->yoff = lc->yoff;
/*
* Layout cells are limited by the smallest size of other cells
* within the same row or column; if this isn't the case
* resizing becomes difficult.
*
* However, panes do not have to take up their entire cell, so
* they can be cropped to the window edge if the layout
* overflows and they are partly visible.
*
* This stops cells being hidden unnecessarily.
*/
/*
* Work out the horizontal size. If the pane is actually
* outside the window or the entire pane is already visible,
* don't crop.
*/
if (lc->xoff >= wsx || lc->xoff + lc->sx < wsx)
sx = lc->sx;
else {
wp->xoff = xoff;
xoff += w->sx;
sx = wsx - lc->xoff;
if (sx < 1)
sx = lc->sx;
}
wp->yoff = 0;
window_pane_resize(wp, w->sx, w->sy);
}
}
void
layout_even_h_refresh(struct window *w, int active_only)
{
struct window_pane *wp;
u_int i, n, width, xoff;
if (active_only)
return;
/* If the screen is too small, show active only. */
if (w->sx < PANE_MINIMUM || w->sy < PANE_MINIMUM) {
layout_active_only_refresh(w, active_only);
return;
}
/* Get number of panes. */
n = window_count_panes(w);
if (n == 0)
return;
/* How many can we fit? */
if (w->sx / n < PANE_MINIMUM) {
width = PANE_MINIMUM;
n = UINT_MAX;
} else
width = w->sx / n;
/* Fit the panes. */
i = xoff = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
wp->xoff = xoff;
wp->yoff = 0;
if (i != n - 1)
window_pane_resize(wp, width - 1, w->sy);
else
window_pane_resize(wp, width, w->sy);
i++;
xoff += width;
}
/* Any space left? */
while (xoff++ < w->sx) {
wp = TAILQ_LAST(&w->panes, window_panes);
window_pane_resize(wp, wp->sx + 1, wp->sy);
}
}
void
layout_even_v_refresh(struct window *w, int active_only)
{
struct window_pane *wp;
u_int i, n, height, yoff;
if (active_only)
return;
/* If the screen is too small, show active only. */
if (w->sx < PANE_MINIMUM || w->sy < PANE_MINIMUM) {
layout_active_only_refresh(w, active_only);
return;
}
/* Get number of panes. */
n = window_count_panes(w);
if (n == 0)
return;
/* How many can we fit? */
if (w->sy / n < PANE_MINIMUM) {
height = PANE_MINIMUM;
n = UINT_MAX;
} else
height = w->sy / n;
/* Fit the panes. */
i = yoff = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
wp->xoff = 0;
wp->yoff = yoff;
if (i != n - 1)
window_pane_resize(wp, w->sx, height - 1);
else
window_pane_resize(wp, w->sx, height);
i++;
yoff += height;
}
/* Any space left? */
while (yoff++ < w->sy) {
wp = TAILQ_LAST(&w->panes, window_panes);
window_pane_resize(wp, wp->sx, wp->sy + 1);
}
}
void
layout_main_v_refresh(struct window *w, int active_only)
{
struct window_pane *wp;
u_int i, n, mainwidth, height, yoff;
if (active_only)
return;
/* Get number of panes. */
n = window_count_panes(w);
if (n == 0)
return;
/* Get the main pane width and add one for separator line. */
mainwidth = options_get_number(&w->options, "main-pane-width") + 1;
/* Need >1 pane and minimum columns; if fewer, display active only. */
if (n == 1 ||
w->sx < mainwidth + PANE_MINIMUM || w->sy < PANE_MINIMUM) {
layout_active_only_refresh(w, active_only);
return;
}
n--;
/* How many can we fit, not including first? */
if (w->sy / n < PANE_MINIMUM) {
height = PANE_MINIMUM;
n = w->sy / PANE_MINIMUM;
} else
height = w->sy / n;
/* Fit the panes. */
i = yoff = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == TAILQ_FIRST(&w->panes)) {
wp->xoff = 0;
wp->yoff = 0;
window_pane_resize(wp, mainwidth - 1, w->sy);
continue;
/*
* Similarly for the vertical size; the minimum vertical size
* is two because scroll regions cannot be one line.
*/
if (lc->yoff >= wsy || lc->yoff + lc->sy < wsy)
sy = lc->sy;
else {
sy = wsy - lc->yoff;
if (sy < 2)
sy = lc->sy;
}
wp->xoff = mainwidth;
wp->yoff = yoff;
if (i != n - 1)
window_pane_resize(wp, w->sx - mainwidth, height - 1);
else
window_pane_resize(wp, w->sx - mainwidth, height);
window_pane_resize(wp, sx, sy);
}
}
i++;
yoff += height;
/* Calculate how much size is available to be removed from a cell. */
u_int
layout_resize_check(struct layout_cell *lc, enum layout_type type)
{
struct layout_cell *lcchild;
u_int available, minimum;
if (lc->type == LAYOUT_WINDOWPANE) {
/* Space available in this cell only. */
if (type == LAYOUT_LEFTRIGHT)
available = lc->sx;
else
available = lc->sy;
if (available > PANE_MINIMUM)
available -= PANE_MINIMUM;
else
available = 0;
} else if (lc->type == type) {
/* Same type: total of available space in all child cells. */
available = 0;
TAILQ_FOREACH(lcchild, &lc->cells, entry)
available += layout_resize_check(lcchild, type);
} else {
/* Different type: minimum of available space in child cells. */
minimum = UINT_MAX;
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
available = layout_resize_check(lcchild, type);
if (available < minimum)
minimum = available;
}
available = minimum;
}
/* Any space left? */
while (yoff++ < w->sy) {
wp = TAILQ_LAST(&w->panes, window_panes);
while (wp != NULL && wp == TAILQ_FIRST(&w->panes))
wp = TAILQ_PREV(wp, window_panes, entry);
if (wp == NULL)
return (available);
}
/*
* Adjust cell size evenly, including altering its children. This function
* expects the change to have already been bounded to the space available.
*/
void
layout_resize_adjust(struct layout_cell *lc, enum layout_type type, int change)
{
struct layout_cell *lcchild;
/* Adjust the cell size. */
if (type == LAYOUT_LEFTRIGHT)
lc->sx += change;
else
lc->sy += change;
/* If this is a leaf cell, that is all that is necessary. */
if (type == LAYOUT_WINDOWPANE)
return;
/* Child cell runs in a different direction. */
if (lc->type != type) {
TAILQ_FOREACH(lcchild, &lc->cells, entry)
layout_resize_adjust(lcchild, type, change);
return;
}
/*
* Child cell runs in the same direction. Adjust each child equally
* until no further change is possible.
*/
while (change != 0) {
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
if (change == 0)
break;
if (change > 0) {
layout_resize_adjust(lcchild, type, 1);
change--;
continue;
}
if (layout_resize_check(lcchild, type) > 0) {
layout_resize_adjust(lcchild, type, -1);
change++;
}
}
}
}
void
layout_init(struct window *w)
{
struct layout_cell *lc;
lc = w->layout_root = layout_create_cell(NULL);
layout_set_size(lc, w->sx, w->sy, 0, 0);
layout_make_leaf(lc, TAILQ_FIRST(&w->panes));
layout_fix_panes(w, w->sx, w->sy);
}
void
layout_free(struct window *w)
{
layout_free_cell(w->layout_root);
}
/* Resize the entire layout after window resize. */
void
layout_resize(struct window *w, u_int sx, u_int sy)
{
struct layout_cell *lc = w->layout_root;
int xlimit, ylimit, xchange, ychange;
/*
* Adjust horizontally. Do not attempt to reduce the layout lower than
* the minimum (more than the amount returned by layout_resize_check).
*
* This can mean that the window size is smaller than the total layout
* size: redrawing this is handled at a higher level, but it does leave
* a problem with growing the window size here: if the current size is
* < the minimum, growing proportionately by adding to each pane is
* wrong as it would keep the layout size larger than the window size.
* Instead, spread the difference between the minimum and the new size
* out proportionately - this should leave the layout fitting the new
* window size.
*/
xchange = sx - w->sx;
xlimit = layout_resize_check(lc, LAYOUT_LEFTRIGHT);
if (xchange < 0 && xchange < -xlimit)
xchange = -xlimit;
if (xlimit == 0) {
if (sx <= lc->sx) /* lc->sx is minimum possible */
xchange = 0;
else
xchange = sx - lc->sx;
}
if (xchange != 0)
layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, xchange);
/* Adjust vertically in a similar fashion. */
ychange = sy - w->sy;
ylimit = layout_resize_check(lc, LAYOUT_TOPBOTTOM);
if (ychange < 0 && ychange < -ylimit)
ychange = -ylimit;
if (ylimit == 0) {
if (sy <= lc->sy) /* lc->sy is minimum possible */
ychange = 0;
else
ychange = sy - lc->sy;
}
if (ychange != 0)
layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, ychange);
/* Fix cell offsets. */
layout_fix_offsets(lc);
layout_fix_panes(w, sx, sy);
}
/* Resize a single pane within the layout. */
void
layout_resize_pane(struct window_pane *wp, enum layout_type type, int change)
{
struct layout_cell *lc, *lcparent;
int needed, size;
lc = wp->layout_cell;
/* Find next parent of the same type. */
lcparent = lc->parent;
while (lcparent != NULL && lcparent->type != type) {
lc = lcparent;
lcparent = lc->parent;
}
if (lcparent == NULL)
return;
/* If this is the last cell, move back one. */
if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
lc = TAILQ_PREV(lc, layout_cells, entry);
/* Grow or shrink the cell. */
needed = change;
while (needed != 0) {
if (change > 0) {
size = layout_resize_pane_grow(lc, type, needed);
needed -= size;
} else {
size = layout_resize_pane_shrink(lc, type, needed);
needed += size;
}
if (size == 0) /* no more change possible */
break;
window_pane_resize(wp, wp->sx, wp->sy + 1);
}
/* Fix cell offsets. */
layout_fix_offsets(wp->window->layout_root);
layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
}
void
layout_main_h_refresh(struct window *w, int active_only)
int
layout_resize_pane_grow(
struct layout_cell *lc, enum layout_type type, int needed)
{
struct window_pane *wp;
u_int i, n, mainheight, width, xoff;
struct layout_cell *lcadd, *lcremove;
u_int size;
if (active_only)
return;
/* Get number of panes. */
n = window_count_panes(w);
if (n == 0)
return;
/* Get the main pane height and add one for separator line. */
mainheight = options_get_number(&w->options, "main-pane-height") + 1;
/* Need >1 pane and minimum rows; if fewer, display active only. */
if (n == 1 ||
w->sy < mainheight + PANE_MINIMUM || w->sx < PANE_MINIMUM) {
layout_active_only_refresh(w, active_only);
return;
}
n--;
/* How many can we fit, not including first? */
if (w->sx / n < PANE_MINIMUM) {
width = PANE_MINIMUM;
n = w->sx / PANE_MINIMUM;
} else
width = w->sx / n;
/* Fit the panes. */
i = xoff = 0;
TAILQ_FOREACH(wp, &w->panes, entry) {
if (wp == TAILQ_FIRST(&w->panes)) {
wp->xoff = 0;
wp->yoff = 0;
window_pane_resize(wp, w->sx, mainheight - 1);
continue;
}
wp->xoff = xoff;
wp->yoff = mainheight;
if (i != n - 1)
window_pane_resize(wp, width - 1, w->sy - mainheight);
else
window_pane_resize(wp, width - 1, w->sy - mainheight);
i++;
xoff += width;
}
/* Any space left? */
while (xoff++ < w->sx + 1) {
wp = TAILQ_LAST(&w->panes, window_panes);
while (wp != NULL && wp == TAILQ_FIRST(&w->panes))
wp = TAILQ_PREV(wp, window_panes, entry);
if (wp == NULL)
/* Growing. Always add to the current cell. */
lcadd = lc;
/* Look towards the tail for a suitable cell for reduction. */
lcremove = TAILQ_NEXT(lc, entry);
while (lcremove != NULL) {
size = layout_resize_check(lcremove, type);
if (size > 0)
break;
window_pane_resize(wp, wp->sx + 1, wp->sy);
lcremove = TAILQ_NEXT(lcremove, entry);
}
/* If none found, look towards the head. */
if (lcremove == NULL) {
lcremove = TAILQ_PREV(lc, layout_cells, entry);
while (lcremove != NULL) {
size = layout_resize_check(lcremove, type);
if (size > 0)
break;
lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
}
if (lcremove == NULL)
return (0);
}
/* Change the cells. */
if (size > (u_int) needed)
size = needed;
layout_resize_adjust(lcadd, type, size);
layout_resize_adjust(lcremove, type, -size);
return (size);
}
int
layout_resize_pane_shrink(
struct layout_cell *lc, enum layout_type type, int needed)
{
struct layout_cell *lcadd, *lcremove;
u_int size;
/* Shrinking. Find cell to remove from by walking towards head. */
lcremove = lc;
do {
size = layout_resize_check(lcremove, type);
if (size != 0)
break;
lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
} while (lcremove != NULL);
if (lcremove == NULL)
return (0);
/* And add onto the next cell (from the original cell). */
lcadd = TAILQ_NEXT(lc, entry);
if (lcadd == NULL)
return (0);
/* Change the cells. */
if (size > (u_int) -needed)
size = -needed;
layout_resize_adjust(lcadd, type, size);
layout_resize_adjust(lcremove, type, -size);
return (size);
}
/* Split a pane into two. size is a hint, or -1 for default half/half split. */
int
layout_split_pane(struct window_pane *wp,
enum layout_type type, int size, struct window_pane *new_wp)
{
struct layout_cell *lc, *lcparent, *lcnew;
u_int sx, sy, xoff, yoff, size1, size2;
lc = wp->layout_cell;
/* Copy the old cell size. */
sx = lc->sx;
sy = lc->sy;
xoff = lc->xoff;
yoff = lc->yoff;
/* Check there is enough space for the two new panes. */
switch (type) {
case LAYOUT_LEFTRIGHT:
if (sx < PANE_MINIMUM * 2 + 1)
return (-1);
break;
case LAYOUT_TOPBOTTOM:
if (sy < PANE_MINIMUM * 2 + 1)
return (-1);
break;
default:
fatalx("bad layout type");
}
if (lc->parent != NULL && lc->parent->type == type) {
/*
* If the parent exists and is of the same type as the split,
* create a new cell and insert it after this one.
*/
/* Create the new child cell. */
lcnew = layout_create_cell(lc->parent);
TAILQ_INSERT_AFTER(&lc->parent->cells, lc, lcnew, entry);
} else {
/*
* Otherwise create a new parent and insert it.
*/
/* Create and insert the replacement parent. */
lcparent = layout_create_cell(lc->parent);
layout_make_node(lcparent, type);
layout_set_size(lcparent, sx, sy, xoff, yoff);
if (lc->parent == NULL)
wp->window->layout_root = lcparent;
else
TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
/* Insert the old cell. */
lc->parent = lcparent;
TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
/* Create the new child cell. */
lcnew = layout_create_cell(lcparent);
TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
}
/* Set new cell sizes. size is the target size or -1 for middle split,
* size1 is the size of the top/left and size2 the bottom/right.
*/
switch (type) {
case LAYOUT_LEFTRIGHT:
if (size < 0)
size2 = ((sx + 1) / 2) - 1;
else
size2 = size;
if (size2 < PANE_MINIMUM)
size2 = PANE_MINIMUM;
else if (size2 > sx - 2)
size2 = sx - 2;
size1 = sx - 1 - size2;
layout_set_size(lc, size1, sy, xoff, yoff);
layout_set_size(lcnew, size2, sy, xoff + lc->sx + 1, yoff);
break;
case LAYOUT_TOPBOTTOM:
if (size < 0)
size2 = ((sy + 1) / 2) - 1;
else
size2 = size;
if (size2 < PANE_MINIMUM)
size2 = PANE_MINIMUM;
else if (size2 > sy - 2)
size2 = sy - 2;
size1 = sy - 1 - size2;
layout_set_size(lc, sx, size1, xoff, yoff);
layout_set_size(lcnew, sx, size2, xoff, yoff + lc->sy + 1);
break;
default:
fatalx("bad layout type");
}
/* Assign the panes. */
layout_make_leaf(lc, wp);
layout_make_leaf(lcnew, new_wp);
/* Fix pane offsets and sizes. */
layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
return (0);
}
/* Destroy the layout associated with a pane and redistribute the space. */
void
layout_close_pane(struct window_pane *wp)
{
struct layout_cell *lc, *lcother, *lcparent;
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. */
layout_fix_offsets(wp->window->layout_root);
layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
}

View File

@ -132,6 +132,7 @@ recalculate_sizes(void)
log_debug(
"window size %u,%u (was %u,%u)", ssx, ssy, w->sx, w->sy);
layout_resize(w, ssx, ssy);
window_resize(w, ssx, ssy);
/*
@ -148,6 +149,5 @@ recalculate_sizes(void)
}
server_redraw_window(w);
layout_refresh(w, 0);
}
}

View File

@ -840,7 +840,9 @@ server_handle_client(struct client *c)
/* Ensure cursor position and mode settings. */
status = options_get_number(&c->session->options, "status");
if (wp->yoff + s->cy < c->tty.sy - status)
if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status)
tty_cursor(&c->tty, 0, 0, 0, 0);
else
tty_cursor(&c->tty, s->cx, s->cy, wp->xoff, wp->yoff);
mode = s->mode;
@ -1072,9 +1074,9 @@ server_check_window(struct window *w)
* pane dies).
*/
if (wp->fd == -1 && !flag) {
layout_close_pane(wp);
window_remove_pane(w, wp);
server_redraw_window(w);
layout_refresh(w, 0);
} else
destroyed = 0;
wp = wq;

81
tmux.1
View File

@ -393,15 +393,17 @@ each pane takes up a certain area of the display and is a separate terminal.
A window may be split into panes using the
.Ic split-window
command.
.Pp
Panes are numbered beginning from zero; in horizontal layouts zero is the
leftmost pane and in vertical the topmost.
.Pp
Panes may be arranged using several layouts.
The layout may be cycled with the
.Ic next-layout
Windows may be split horizontally (with the
.Fl h
flag) or vertically.
Panes may be resized with the
.Ic resize-pane
command (bound to
.Ql C-space
.Ql C-up ,
.Ql C-down
.Ql C-left
and
.Ql C-right
by default), the current pane may be changed with the
.Ic up-pane
and
@ -410,12 +412,22 @@ commands and the
.Ic rotate-window
and
.Ic swap-pane
commands may be used to swap panes without changing the window layout.
commands may be used to swap panes without changing their position.
Panes are numbered beginning from zero in the order they are created.
.Pp
A number of preset
.Em layouts
are available.
These may be selected with the
.Ic select-layout
command or cycled with
.Ic next-layout
(bound to
.Ql C-space
by default); once a layout is chosen, panes within it may be moved and resized as normal.
.Pp
The following layouts are supported:
.Bl -tag -width Ds
.It Ic active-only
Only the active pane is shown \(en all other panes are hidden.
.It Ic even-horizontal
Panes are spread out evenly from left to right across the window.
.It Ic even-vertical
@ -434,11 +446,6 @@ bottom along the right.
See the
.Em main-pane-width
window option.
.It Ic manual
Manual layout splits windows vertically (running across); only with this layout
may panes be resized using the
.Ic resize-pane
command.
.El
.Sh STATUS LINE
.Nm
@ -980,7 +987,7 @@ Rename the current window, or the window at
if specified, to
.Ar new-name .
.It Xo Ic resize-pane
.Op Fl DU
.Op Fl DLUR
.Op Fl p Ar pane-index
.Op Fl t Ar target-window
.Op Ar adjustment
@ -988,11 +995,15 @@ if specified, to
.D1 (alias: Ic resizep )
Resize a pane, upward with
.Fl U
(the default) or downward with
.Fl D .
(the default), downward with
.Fl D ,
to the left with
.Fl L
and to the right with
.Fl R.
The
.Ar adjustment
is given in lines (the default is 1).
is given in lines or cells (the default is 1).
.It Xo Ic respawn-window
.Op Fl k
.Op Fl t Ar target-window
@ -1520,38 +1531,30 @@ is used.
Execute commands from
.Ar path .
.It Xo Ic split-window
.Op Fl d
.Op Fl dhv
.Oo Fl l
.Ar lines |
.Ar size |
.Fl p Ar percentage Oc
.Op Fl t Ar target-window
.Op Ar command
.Xc
.D1 (alias: splitw )
Creates a new window by splitting it vertically.
Creates a new pane by splitting the active pane:
.Fl h
does a horizontal split and
.Fl v
a vertical split; if neither is specified,
.Fl v
is assumed.
The
.Fl l
and
.Fl p
options specify the size of the new window in lines, or as a percentage,
respectively.
options specify the size of the new window in lines (for vertical split) or in
cells (for horizontal split), or as a percentage, respectively.
All other options have the same meaning as in the
.Ic new-window
command.
.Pp
A few notes with regard to panes:
.Bl -enum -compact
.It
If attempting to split a window with less than eight lines, an error will be
shown.
.It
If the window is resized, as many panes are shown as can fit without reducing
them below four lines.
.It
The minimum pane size is four lines (including the separator line).
.It
The panes are indexed from top (0) to bottom, with no numbers skipped.
.El
.It Xo Ic start-server
.Xc
.D1 (alias: Ic start )

81
tmux.h
View File

@ -47,8 +47,11 @@ extern const char *__progname;
/* Default prompt history length. */
#define PROMPT_HISTORY 100
/* Minimum pane size. */
#define PANE_MINIMUM 5 /* includes separator line */
/*
* Minimum layout cell size, NOT including separator line. The scroll region
* cannot be one line in height so this must be at least two.
*/
#define PANE_MINIMUM 2
/* Automatic name refresh interval, in milliseconds. */
#define NAME_INTERVAL 500
@ -592,6 +595,7 @@ struct window_mode {
/* Child window structure. */
struct window_pane {
struct window *window;
struct layout_cell *layout_cell;
u_int sx;
u_int sy;
@ -635,7 +639,9 @@ struct window {
struct window_pane *active;
struct window_panes panes;
u_int layout;
struct layout_cell *layout_root;
u_int sx;
u_int sy;
@ -664,6 +670,34 @@ struct winlink {
RB_HEAD(winlinks, winlink);
SLIST_HEAD(winlink_stack, winlink);
/* Layout direction. */
enum layout_type {
LAYOUT_LEFTRIGHT,
LAYOUT_TOPBOTTOM,
LAYOUT_WINDOWPANE
};
/* Layout cells queue. */
TAILQ_HEAD(layout_cells, layout_cell);
/* Layout cell. */
struct layout_cell {
enum layout_type type;
struct layout_cell *parent;
u_int sx;
u_int sy;
u_int xoff;
u_int yoff;
struct window_pane *wp;
struct layout_cells cells;
TAILQ_ENTRY(layout_cell) entry;
};
/* Paste buffer. */
struct paste_buffer {
char *data;
@ -1446,8 +1480,7 @@ struct window *window_create(const char *, const char *,
void window_destroy(struct window *);
int window_resize(struct window *, u_int, u_int);
void window_set_active_pane(struct window *, struct window_pane *);
struct window_pane *window_add_pane(struct window *, int,
const char *, const char *, const char **, u_int, char **);
struct window_pane *window_add_pane(struct window *, u_int, char **);
void window_remove_pane(struct window *, struct window_pane *);
struct window_pane *window_pane_at_index(struct window *, u_int);
u_int window_pane_index(struct window *, struct window_pane *);
@ -1467,20 +1500,38 @@ void window_pane_mouse(struct window_pane *,
struct client *, u_char, u_char, u_char);
int window_pane_visible(struct window_pane *);
char *window_pane_search(
struct window_pane *, const char *, u_int *);
struct window_pane *, const char *, u_int *);
/* layout.c */
const char * layout_name(struct window *);
int layout_lookup(const char *);
void layout_refresh(struct window *, int);
int layout_resize(struct window_pane *, int);
int layout_select(struct window *, u_int);
void layout_next(struct window *);
void layout_previous(struct window *);
struct layout_cell *layout_create_cell(struct layout_cell *);
void layout_free_cell(struct layout_cell *);
void layout_print_cell(struct layout_cell *, const char *, u_int);
void layout_set_size(
struct layout_cell *, u_int, u_int, u_int, u_int);
void layout_make_leaf(
struct layout_cell *, struct window_pane *);
void layout_make_node(struct layout_cell *, enum layout_type);
void layout_fix_offsets(struct layout_cell *);
void layout_fix_panes(struct window *, u_int, u_int);
u_int layout_resize_check(struct layout_cell *, enum layout_type);
void layout_resize_adjust(
struct layout_cell *, enum layout_type, int);
void layout_init(struct window *);
void layout_free(struct window *);
void layout_resize(struct window *, u_int, u_int);
void layout_resize_pane(
struct window_pane *, enum layout_type, int);
int layout_split_pane(struct window_pane *,
enum layout_type, int, struct window_pane *);
void layout_close_pane(struct window_pane *);
/* layout-manual.c */
void layout_manual_v_refresh(struct window *, int);
void layout_manual_v_resize(struct window_pane *, int);
/* layout-set.c */
const char *layout_set_name(u_int);
int layout_set_lookup(const char *);
u_int layout_set_select(struct window *, u_int);
u_int layout_set_next(struct window *);
u_int layout_set_previous(struct window *);
void layout_set_active_changed(struct window *);
/* window-clock.c */
extern const struct window_mode window_clock_mode;

View File

@ -35,7 +35,7 @@
#include "tmux.h"
/*
* Each window is attached to one or two panes, each of which is a pty. This
* Each window is attached to a number of panes, each of which is a pty. This
* file contains code to handle them.
*
* A pane has two buffers attached, these are filled and emptied by the main
@ -230,8 +230,10 @@ window_create1(u_int sx, u_int sy)
TAILQ_INIT(&w->panes);
w->active = NULL;
w->layout = 0;
w->layout = 0;
w->layout_root = NULL;
w->sx = sx;
w->sy = sy;
@ -254,15 +256,20 @@ struct window *
window_create(const char *name, const char *cmd, const char *cwd,
const char **envp, u_int sx, u_int sy, u_int hlimit, char **cause)
{
struct window *w;
struct window *w;
struct window_pane *wp;
w = window_create1(sx, sy);
if (window_add_pane(w, -1, cmd, cwd, envp, hlimit, cause) == NULL) {
if ((wp = window_add_pane(w, hlimit, cause)) == NULL) {
window_destroy(w);
return (NULL);
}
layout_init(w);
if (window_pane_spawn(wp, cmd, cwd, envp, cause) != 0) {
window_destroy(w);
return (NULL);
}
w->active = TAILQ_FIRST(&w->panes);
if (name != NULL) {
w->name = xstrdup(name);
options_set_number(&w->options, "automatic-rename", 0);
@ -282,6 +289,9 @@ window_destroy(struct window *w)
while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL)
ARRAY_TRUNC(&windows, 1);
if (w->layout_root != NULL)
layout_free(w);
options_free(&w->options);
window_destroy_panes(w);
@ -304,7 +314,6 @@ void
window_set_active_pane(struct window *w, struct window_pane *wp)
{
w->active = wp;
while (!window_pane_visible(w->active)) {
w->active = TAILQ_PREV(w->active, window_panes, entry);
if (w->active == NULL)
@ -315,41 +324,15 @@ window_set_active_pane(struct window *w, struct window_pane *wp)
}
struct window_pane *
window_add_pane(struct window *w, int wanty, const char *cmd,
const char *cwd, const char **envp, u_int hlimit, char **cause)
window_add_pane(struct window *w, u_int hlimit, unused char **cause)
{
struct window_pane *wp;
u_int sizey;
if (TAILQ_EMPTY(&w->panes))
wanty = w->sy;
else {
sizey = w->active->sy - 1; /* for separator */
if (sizey < PANE_MINIMUM * 2) {
*cause = xstrdup("pane too small");
return (NULL);
}
if (wanty == -1)
wanty = sizey / 2;
if (wanty < PANE_MINIMUM)
wanty = PANE_MINIMUM;
if ((u_int) wanty > sizey - PANE_MINIMUM)
wanty = sizey - PANE_MINIMUM;
window_pane_resize(w->active, w->sx, sizey - wanty);
}
wp = window_pane_create(w, w->sx, wanty, hlimit);
wp = window_pane_create(w, w->sx, w->sy, hlimit);
if (TAILQ_EMPTY(&w->panes))
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
else
TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry);
if (window_pane_spawn(wp, cmd, cwd, envp, cause) != 0) {
window_remove_pane(w, wp);
return (NULL);
}
return (wp);
}
@ -435,6 +418,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
wp->mode = NULL;
wp->layout_cell = NULL;
wp->xoff = 0;
wp->yoff = 0;