Home | History | Annotate | Line # | Download | only in dist
      1   1.1.1.5  christos /* $OpenBSD$ */
      2       1.1      jmmv 
      3       1.1      jmmv /*
      4   1.1.1.6  christos  * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott (at) gmail.com>
      5       1.1      jmmv  *
      6       1.1      jmmv  * Permission to use, copy, modify, and distribute this software for any
      7       1.1      jmmv  * purpose with or without fee is hereby granted, provided that the above
      8       1.1      jmmv  * copyright notice and this permission notice appear in all copies.
      9       1.1      jmmv  *
     10       1.1      jmmv  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11       1.1      jmmv  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12       1.1      jmmv  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13       1.1      jmmv  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14       1.1      jmmv  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
     15       1.1      jmmv  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     16       1.1      jmmv  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17       1.1      jmmv  */
     18       1.1      jmmv 
     19       1.1      jmmv #include <sys/types.h>
     20       1.1      jmmv 
     21       1.1      jmmv #include <ctype.h>
     22       1.1      jmmv #include <string.h>
     23       1.1      jmmv 
     24       1.1      jmmv #include "tmux.h"
     25       1.1      jmmv 
     26   1.1.1.7  christos static struct layout_cell	*layout_find_bottomright(struct layout_cell *);
     27   1.1.1.7  christos static u_short			 layout_checksum(const char *);
     28   1.1.1.7  christos static int			 layout_append(struct layout_cell *, char *,
     29   1.1.1.7  christos 				     size_t);
     30   1.1.1.7  christos static struct layout_cell	*layout_construct(struct layout_cell *,
     31   1.1.1.7  christos 				     const char **);
     32   1.1.1.7  christos static void			 layout_assign(struct window_pane **,
     33   1.1.1.7  christos 				     struct layout_cell *);
     34       1.1      jmmv 
     35   1.1.1.2      jmmv /* Find the bottom-right cell. */
     36   1.1.1.7  christos static struct layout_cell *
     37   1.1.1.2      jmmv layout_find_bottomright(struct layout_cell *lc)
     38   1.1.1.2      jmmv {
     39   1.1.1.2      jmmv 	if (lc->type == LAYOUT_WINDOWPANE)
     40   1.1.1.2      jmmv 		return (lc);
     41   1.1.1.2      jmmv 	lc = TAILQ_LAST(&lc->cells, layout_cells);
     42   1.1.1.2      jmmv 	return (layout_find_bottomright(lc));
     43   1.1.1.2      jmmv }
     44   1.1.1.2      jmmv 
     45       1.1      jmmv /* Calculate layout checksum. */
     46   1.1.1.7  christos static u_short
     47       1.1      jmmv layout_checksum(const char *layout)
     48       1.1      jmmv {
     49       1.1      jmmv 	u_short	csum;
     50       1.1      jmmv 
     51       1.1      jmmv 	csum = 0;
     52       1.1      jmmv 	for (; *layout != '\0'; layout++) {
     53       1.1      jmmv 		csum = (csum >> 1) + ((csum & 1) << 15);
     54       1.1      jmmv 		csum += *layout;
     55       1.1      jmmv 	}
     56       1.1      jmmv 	return (csum);
     57       1.1      jmmv }
     58       1.1      jmmv 
     59       1.1      jmmv /* Dump layout as a string. */
     60       1.1      jmmv char *
     61   1.1.1.5  christos layout_dump(struct layout_cell *root)
     62       1.1      jmmv {
     63   1.1.1.9  christos 	char	layout[8192], *out;
     64       1.1      jmmv 
     65       1.1      jmmv 	*layout = '\0';
     66   1.1.1.5  christos 	if (layout_append(root, layout, sizeof layout) != 0)
     67       1.1      jmmv 		return (NULL);
     68       1.1      jmmv 
     69   1.1.1.7  christos 	xasprintf(&out, "%04hx,%s", layout_checksum(layout), layout);
     70       1.1      jmmv 	return (out);
     71       1.1      jmmv }
     72       1.1      jmmv 
     73       1.1      jmmv /* Append information for a single cell. */
     74   1.1.1.7  christos static int
     75       1.1      jmmv layout_append(struct layout_cell *lc, char *buf, size_t len)
     76       1.1      jmmv {
     77       1.1      jmmv 	struct layout_cell     *lcchild;
     78       1.1      jmmv 	char			tmp[64];
     79       1.1      jmmv 	size_t			tmplen;
     80       1.1      jmmv 	const char	       *brackets = "][";
     81       1.1      jmmv 
     82       1.1      jmmv 	if (len == 0)
     83       1.1      jmmv 		return (-1);
     84       1.1      jmmv 
     85   1.1.1.3  christos 	if (lc->wp != NULL) {
     86   1.1.1.3  christos 		tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u,%u",
     87   1.1.1.3  christos 		    lc->sx, lc->sy, lc->xoff, lc->yoff, lc->wp->id);
     88   1.1.1.3  christos 	} else {
     89   1.1.1.3  christos 		tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u",
     90   1.1.1.3  christos 		    lc->sx, lc->sy, lc->xoff, lc->yoff);
     91   1.1.1.3  christos 	}
     92       1.1      jmmv 	if (tmplen > (sizeof tmp) - 1)
     93       1.1      jmmv 		return (-1);
     94       1.1      jmmv 	if (strlcat(buf, tmp, len) >= len)
     95       1.1      jmmv 		return (-1);
     96       1.1      jmmv 
     97       1.1      jmmv 	switch (lc->type) {
     98       1.1      jmmv 	case LAYOUT_LEFTRIGHT:
     99       1.1      jmmv 		brackets = "}{";
    100       1.1      jmmv 		/* FALLTHROUGH */
    101       1.1      jmmv 	case LAYOUT_TOPBOTTOM:
    102       1.1      jmmv 		if (strlcat(buf, &brackets[1], len) >= len)
    103       1.1      jmmv 			return (-1);
    104       1.1      jmmv 		TAILQ_FOREACH(lcchild, &lc->cells, entry) {
    105       1.1      jmmv 			if (layout_append(lcchild, buf, len) != 0)
    106       1.1      jmmv 				return (-1);
    107       1.1      jmmv 			if (strlcat(buf, ",", len) >= len)
    108       1.1      jmmv 				return (-1);
    109       1.1      jmmv 		}
    110       1.1      jmmv 		buf[strlen(buf) - 1] = brackets[0];
    111       1.1      jmmv 		break;
    112       1.1      jmmv 	case LAYOUT_WINDOWPANE:
    113       1.1      jmmv 		break;
    114       1.1      jmmv 	}
    115       1.1      jmmv 
    116       1.1      jmmv 	return (0);
    117       1.1      jmmv }
    118       1.1      jmmv 
    119   1.1.1.9  christos /* Check layout sizes fit. */
    120   1.1.1.9  christos static int
    121   1.1.1.9  christos layout_check(struct layout_cell *lc)
    122   1.1.1.9  christos {
    123   1.1.1.9  christos 	struct layout_cell	*lcchild;
    124   1.1.1.9  christos 	u_int			 n = 0;
    125   1.1.1.9  christos 
    126   1.1.1.9  christos 	switch (lc->type) {
    127   1.1.1.9  christos 	case LAYOUT_WINDOWPANE:
    128   1.1.1.9  christos 		break;
    129   1.1.1.9  christos 	case LAYOUT_LEFTRIGHT:
    130   1.1.1.9  christos 		TAILQ_FOREACH(lcchild, &lc->cells, entry) {
    131   1.1.1.9  christos 			if (lcchild->sy != lc->sy)
    132   1.1.1.9  christos 				return (0);
    133   1.1.1.9  christos 			if (!layout_check(lcchild))
    134   1.1.1.9  christos 				return (0);
    135   1.1.1.9  christos 			n += lcchild->sx + 1;
    136   1.1.1.9  christos 		}
    137   1.1.1.9  christos 		if (n - 1 != lc->sx)
    138   1.1.1.9  christos 			return (0);
    139   1.1.1.9  christos 		break;
    140   1.1.1.9  christos 	case LAYOUT_TOPBOTTOM:
    141   1.1.1.9  christos 		TAILQ_FOREACH(lcchild, &lc->cells, entry) {
    142   1.1.1.9  christos 			if (lcchild->sx != lc->sx)
    143   1.1.1.9  christos 				return (0);
    144   1.1.1.9  christos 			if (!layout_check(lcchild))
    145   1.1.1.9  christos 				return (0);
    146   1.1.1.9  christos 			n += lcchild->sy + 1;
    147   1.1.1.9  christos 		}
    148   1.1.1.9  christos 		if (n - 1 != lc->sy)
    149   1.1.1.9  christos 			return (0);
    150   1.1.1.9  christos 		break;
    151   1.1.1.9  christos 	}
    152   1.1.1.9  christos 	return (1);
    153   1.1.1.9  christos }
    154   1.1.1.9  christos 
    155       1.1      jmmv /* Parse a layout string and arrange window as layout. */
    156       1.1      jmmv int
    157  1.1.1.12       wiz layout_parse(struct window *w, const char *layout, char **cause)
    158       1.1      jmmv {
    159       1.1      jmmv 	struct layout_cell	*lc, *lcchild;
    160       1.1      jmmv 	struct window_pane	*wp;
    161   1.1.1.9  christos 	u_int			 npanes, ncells, sx = 0, sy = 0;
    162       1.1      jmmv 	u_short			 csum;
    163       1.1      jmmv 
    164       1.1      jmmv 	/* Check validity. */
    165  1.1.1.13       wiz 	if (sscanf(layout, "%hx,", &csum) != 1) {
    166  1.1.1.13       wiz 		*cause = xstrdup("invalid layout");
    167       1.1      jmmv 		return (-1);
    168  1.1.1.13       wiz 	}
    169       1.1      jmmv 	layout += 5;
    170  1.1.1.12       wiz 	if (csum != layout_checksum(layout)) {
    171  1.1.1.12       wiz 		*cause = xstrdup("invalid layout");
    172       1.1      jmmv 		return (-1);
    173  1.1.1.12       wiz 	}
    174       1.1      jmmv 
    175       1.1      jmmv 	/* Build the layout. */
    176       1.1      jmmv 	lc = layout_construct(NULL, &layout);
    177  1.1.1.12       wiz 	if (lc == NULL) {
    178  1.1.1.12       wiz 		*cause = xstrdup("invalid layout");
    179       1.1      jmmv 		return (-1);
    180  1.1.1.12       wiz 	}
    181  1.1.1.12       wiz 	if (*layout != '\0') {
    182  1.1.1.12       wiz 		*cause = xstrdup("invalid layout");
    183       1.1      jmmv 		goto fail;
    184  1.1.1.12       wiz 	}
    185       1.1      jmmv 
    186       1.1      jmmv 	/* Check this window will fit into the layout. */
    187       1.1      jmmv 	for (;;) {
    188       1.1      jmmv 		npanes = window_count_panes(w);
    189       1.1      jmmv 		ncells = layout_count_cells(lc);
    190  1.1.1.12       wiz 		if (npanes > ncells) {
    191  1.1.1.12       wiz 			xasprintf(cause, "have %u panes but need %u", npanes,
    192  1.1.1.12       wiz 			    ncells);
    193       1.1      jmmv 			goto fail;
    194  1.1.1.12       wiz 		}
    195       1.1      jmmv 		if (npanes == ncells)
    196       1.1      jmmv 			break;
    197       1.1      jmmv 
    198       1.1      jmmv 		/* Fewer panes than cells - close the bottom right. */
    199       1.1      jmmv 		lcchild = layout_find_bottomright(lc);
    200   1.1.1.7  christos 		layout_destroy_cell(w, lcchild, &lc);
    201       1.1      jmmv 	}
    202       1.1      jmmv 
    203   1.1.1.9  christos 	/*
    204   1.1.1.9  christos 	 * It appears older versions of tmux were able to generate layouts with
    205   1.1.1.9  christos 	 * an incorrect top cell size - if it is larger than the top child then
    206   1.1.1.9  christos 	 * correct that (if this is still wrong the check code will catch it).
    207   1.1.1.9  christos 	 */
    208   1.1.1.9  christos 	switch (lc->type) {
    209   1.1.1.9  christos 	case LAYOUT_WINDOWPANE:
    210   1.1.1.9  christos 		break;
    211   1.1.1.9  christos 	case LAYOUT_LEFTRIGHT:
    212   1.1.1.9  christos 		TAILQ_FOREACH(lcchild, &lc->cells, entry) {
    213   1.1.1.9  christos 			sy = lcchild->sy + 1;
    214   1.1.1.9  christos 			sx += lcchild->sx + 1;
    215   1.1.1.9  christos 		}
    216   1.1.1.9  christos 		break;
    217   1.1.1.9  christos 	case LAYOUT_TOPBOTTOM:
    218   1.1.1.9  christos 		TAILQ_FOREACH(lcchild, &lc->cells, entry) {
    219   1.1.1.9  christos 			sx = lcchild->sx + 1;
    220   1.1.1.9  christos 			sy += lcchild->sy + 1;
    221   1.1.1.9  christos 		}
    222   1.1.1.9  christos 		break;
    223   1.1.1.9  christos 	}
    224   1.1.1.9  christos 	if (lc->type != LAYOUT_WINDOWPANE && (lc->sx != sx || lc->sy != sy)) {
    225   1.1.1.9  christos 		log_debug("fix layout %u,%u to %u,%u", lc->sx, lc->sy, sx,sy);
    226   1.1.1.9  christos 		layout_print_cell(lc, __func__, 0);
    227   1.1.1.9  christos 		lc->sx = sx - 1; lc->sy = sy - 1;
    228   1.1.1.9  christos 	}
    229   1.1.1.9  christos 
    230   1.1.1.9  christos 	/* Check the new layout. */
    231  1.1.1.12       wiz 	if (!layout_check(lc)) {
    232  1.1.1.12       wiz 		*cause = xstrdup("size mismatch after applying layout");
    233  1.1.1.14       wiz 		goto fail;
    234  1.1.1.12       wiz 	}
    235   1.1.1.9  christos 
    236   1.1.1.9  christos 	/* Resize to the layout size. */
    237  1.1.1.10  christos 	window_resize(w, lc->sx, lc->sy, -1, -1);
    238       1.1      jmmv 
    239       1.1      jmmv 	/* Destroy the old layout and swap to the new. */
    240       1.1      jmmv 	layout_free_cell(w->layout_root);
    241       1.1      jmmv 	w->layout_root = lc;
    242       1.1      jmmv 
    243       1.1      jmmv 	/* Assign the panes into the cells. */
    244       1.1      jmmv 	wp = TAILQ_FIRST(&w->panes);
    245       1.1      jmmv 	layout_assign(&wp, lc);
    246       1.1      jmmv 
    247       1.1      jmmv 	/* Update pane offsets and sizes. */
    248   1.1.1.9  christos 	layout_fix_offsets(w);
    249  1.1.1.11  christos 	layout_fix_panes(w, NULL);
    250   1.1.1.9  christos 	recalculate_sizes();
    251       1.1      jmmv 
    252       1.1      jmmv 	layout_print_cell(lc, __func__, 0);
    253       1.1      jmmv 
    254   1.1.1.7  christos 	notify_window("window-layout-changed", w);
    255   1.1.1.3  christos 
    256       1.1      jmmv 	return (0);
    257       1.1      jmmv 
    258       1.1      jmmv fail:
    259       1.1      jmmv 	layout_free_cell(lc);
    260       1.1      jmmv 	return (-1);
    261       1.1      jmmv }
    262       1.1      jmmv 
    263       1.1      jmmv /* Assign panes into cells. */
    264   1.1.1.7  christos static void
    265       1.1      jmmv layout_assign(struct window_pane **wp, struct layout_cell *lc)
    266       1.1      jmmv {
    267       1.1      jmmv 	struct layout_cell	*lcchild;
    268       1.1      jmmv 
    269       1.1      jmmv 	switch (lc->type) {
    270       1.1      jmmv 	case LAYOUT_WINDOWPANE:
    271       1.1      jmmv 		layout_make_leaf(lc, *wp);
    272       1.1      jmmv 		*wp = TAILQ_NEXT(*wp, entry);
    273       1.1      jmmv 		return;
    274       1.1      jmmv 	case LAYOUT_LEFTRIGHT:
    275       1.1      jmmv 	case LAYOUT_TOPBOTTOM:
    276       1.1      jmmv 		TAILQ_FOREACH(lcchild, &lc->cells, entry)
    277       1.1      jmmv 			layout_assign(wp, lcchild);
    278       1.1      jmmv 		return;
    279       1.1      jmmv 	}
    280       1.1      jmmv }
    281       1.1      jmmv 
    282       1.1      jmmv /* Construct a cell from all or part of a layout tree. */
    283   1.1.1.7  christos static struct layout_cell *
    284       1.1      jmmv layout_construct(struct layout_cell *lcparent, const char **layout)
    285       1.1      jmmv {
    286       1.1      jmmv 	struct layout_cell     *lc, *lcchild;
    287       1.1      jmmv 	u_int			sx, sy, xoff, yoff;
    288   1.1.1.3  christos 	const char	       *saved;
    289       1.1      jmmv 
    290       1.1      jmmv 	if (!isdigit((u_char) **layout))
    291       1.1      jmmv 		return (NULL);
    292       1.1      jmmv 	if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4)
    293       1.1      jmmv 		return (NULL);
    294       1.1      jmmv 
    295       1.1      jmmv 	while (isdigit((u_char) **layout))
    296       1.1      jmmv 		(*layout)++;
    297       1.1      jmmv 	if (**layout != 'x')
    298       1.1      jmmv 		return (NULL);
    299       1.1      jmmv 	(*layout)++;
    300       1.1      jmmv 	while (isdigit((u_char) **layout))
    301       1.1      jmmv 		(*layout)++;
    302       1.1      jmmv 	if (**layout != ',')
    303       1.1      jmmv 		return (NULL);
    304       1.1      jmmv 	(*layout)++;
    305       1.1      jmmv 	while (isdigit((u_char) **layout))
    306       1.1      jmmv 		(*layout)++;
    307       1.1      jmmv 	if (**layout != ',')
    308       1.1      jmmv 		return (NULL);
    309       1.1      jmmv 	(*layout)++;
    310       1.1      jmmv 	while (isdigit((u_char) **layout))
    311       1.1      jmmv 		(*layout)++;
    312   1.1.1.3  christos 	if (**layout == ',') {
    313   1.1.1.3  christos 		saved = *layout;
    314   1.1.1.3  christos 		(*layout)++;
    315   1.1.1.3  christos 		while (isdigit((u_char) **layout))
    316   1.1.1.3  christos 			(*layout)++;
    317   1.1.1.3  christos 		if (**layout == 'x')
    318   1.1.1.3  christos 			*layout = saved;
    319   1.1.1.3  christos 	}
    320       1.1      jmmv 
    321       1.1      jmmv 	lc = layout_create_cell(lcparent);
    322       1.1      jmmv 	lc->sx = sx;
    323       1.1      jmmv 	lc->sy = sy;
    324       1.1      jmmv 	lc->xoff = xoff;
    325       1.1      jmmv 	lc->yoff = yoff;
    326       1.1      jmmv 
    327       1.1      jmmv 	switch (**layout) {
    328       1.1      jmmv 	case ',':
    329       1.1      jmmv 	case '}':
    330       1.1      jmmv 	case ']':
    331       1.1      jmmv 	case '\0':
    332       1.1      jmmv 		return (lc);
    333       1.1      jmmv 	case '{':
    334       1.1      jmmv 		lc->type = LAYOUT_LEFTRIGHT;
    335       1.1      jmmv 		break;
    336       1.1      jmmv 	case '[':
    337       1.1      jmmv 		lc->type = LAYOUT_TOPBOTTOM;
    338       1.1      jmmv 		break;
    339       1.1      jmmv 	default:
    340       1.1      jmmv 		goto fail;
    341       1.1      jmmv 	}
    342       1.1      jmmv 
    343       1.1      jmmv 	do {
    344       1.1      jmmv 		(*layout)++;
    345       1.1      jmmv 		lcchild = layout_construct(lc, layout);
    346       1.1      jmmv 		if (lcchild == NULL)
    347       1.1      jmmv 			goto fail;
    348       1.1      jmmv 		TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry);
    349       1.1      jmmv 	} while (**layout == ',');
    350       1.1      jmmv 
    351       1.1      jmmv 	switch (lc->type) {
    352       1.1      jmmv 	case LAYOUT_LEFTRIGHT:
    353       1.1      jmmv 		if (**layout != '}')
    354       1.1      jmmv 			goto fail;
    355       1.1      jmmv 		break;
    356       1.1      jmmv 	case LAYOUT_TOPBOTTOM:
    357       1.1      jmmv 		if (**layout != ']')
    358       1.1      jmmv 			goto fail;
    359       1.1      jmmv 		break;
    360       1.1      jmmv 	default:
    361       1.1      jmmv 		goto fail;
    362       1.1      jmmv 	}
    363       1.1      jmmv 	(*layout)++;
    364       1.1      jmmv 
    365       1.1      jmmv 	return (lc);
    366       1.1      jmmv 
    367       1.1      jmmv fail:
    368       1.1      jmmv 	layout_free_cell(lc);
    369       1.1      jmmv 	return (NULL);
    370       1.1      jmmv }
    371