Home | History | Annotate | Line # | Download | only in dist
layout-custom.c revision 1.1.1.10
      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      jmmv layout_parse(struct window *w, const char *layout)
    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      jmmv 	if (sscanf(layout, "%hx,", &csum) != 1)
    166       1.1      jmmv 		return (-1);
    167       1.1      jmmv 	layout += 5;
    168       1.1      jmmv 	if (csum != layout_checksum(layout))
    169       1.1      jmmv 		return (-1);
    170       1.1      jmmv 
    171       1.1      jmmv 	/* Build the layout. */
    172       1.1      jmmv 	lc = layout_construct(NULL, &layout);
    173       1.1      jmmv 	if (lc == NULL)
    174       1.1      jmmv 		return (-1);
    175       1.1      jmmv 	if (*layout != '\0')
    176       1.1      jmmv 		goto fail;
    177       1.1      jmmv 
    178       1.1      jmmv 	/* Check this window will fit into the layout. */
    179       1.1      jmmv 	for (;;) {
    180       1.1      jmmv 		npanes = window_count_panes(w);
    181       1.1      jmmv 		ncells = layout_count_cells(lc);
    182       1.1      jmmv 		if (npanes > ncells)
    183       1.1      jmmv 			goto fail;
    184       1.1      jmmv 		if (npanes == ncells)
    185       1.1      jmmv 			break;
    186       1.1      jmmv 
    187       1.1      jmmv 		/* Fewer panes than cells - close the bottom right. */
    188       1.1      jmmv 		lcchild = layout_find_bottomright(lc);
    189   1.1.1.7  christos 		layout_destroy_cell(w, lcchild, &lc);
    190       1.1      jmmv 	}
    191       1.1      jmmv 
    192   1.1.1.9  christos 	/*
    193   1.1.1.9  christos 	 * It appears older versions of tmux were able to generate layouts with
    194   1.1.1.9  christos 	 * an incorrect top cell size - if it is larger than the top child then
    195   1.1.1.9  christos 	 * correct that (if this is still wrong the check code will catch it).
    196   1.1.1.9  christos 	 */
    197   1.1.1.9  christos 	switch (lc->type) {
    198   1.1.1.9  christos 	case LAYOUT_WINDOWPANE:
    199   1.1.1.9  christos 		break;
    200   1.1.1.9  christos 	case LAYOUT_LEFTRIGHT:
    201   1.1.1.9  christos 		TAILQ_FOREACH(lcchild, &lc->cells, entry) {
    202   1.1.1.9  christos 			sy = lcchild->sy + 1;
    203   1.1.1.9  christos 			sx += lcchild->sx + 1;
    204   1.1.1.9  christos 		}
    205   1.1.1.9  christos 		break;
    206   1.1.1.9  christos 	case LAYOUT_TOPBOTTOM:
    207   1.1.1.9  christos 		TAILQ_FOREACH(lcchild, &lc->cells, entry) {
    208   1.1.1.9  christos 			sx = lcchild->sx + 1;
    209   1.1.1.9  christos 			sy += lcchild->sy + 1;
    210   1.1.1.9  christos 		}
    211   1.1.1.9  christos 		break;
    212   1.1.1.9  christos 	}
    213   1.1.1.9  christos 	if (lc->type != LAYOUT_WINDOWPANE && (lc->sx != sx || lc->sy != sy)) {
    214   1.1.1.9  christos 		log_debug("fix layout %u,%u to %u,%u", lc->sx, lc->sy, sx,sy);
    215   1.1.1.9  christos 		layout_print_cell(lc, __func__, 0);
    216   1.1.1.9  christos 		lc->sx = sx - 1; lc->sy = sy - 1;
    217   1.1.1.9  christos 	}
    218   1.1.1.9  christos 
    219   1.1.1.9  christos 	/* Check the new layout. */
    220   1.1.1.9  christos 	if (!layout_check(lc))
    221   1.1.1.9  christos 		return (-1);
    222   1.1.1.9  christos 
    223   1.1.1.9  christos 	/* Resize to the layout size. */
    224  1.1.1.10  christos 	window_resize(w, lc->sx, lc->sy, -1, -1);
    225       1.1      jmmv 
    226       1.1      jmmv 	/* Destroy the old layout and swap to the new. */
    227       1.1      jmmv 	layout_free_cell(w->layout_root);
    228       1.1      jmmv 	w->layout_root = lc;
    229       1.1      jmmv 
    230       1.1      jmmv 	/* Assign the panes into the cells. */
    231       1.1      jmmv 	wp = TAILQ_FIRST(&w->panes);
    232       1.1      jmmv 	layout_assign(&wp, lc);
    233       1.1      jmmv 
    234       1.1      jmmv 	/* Update pane offsets and sizes. */
    235   1.1.1.9  christos 	layout_fix_offsets(w);
    236   1.1.1.8  christos 	layout_fix_panes(w);
    237   1.1.1.9  christos 	recalculate_sizes();
    238       1.1      jmmv 
    239       1.1      jmmv 	layout_print_cell(lc, __func__, 0);
    240       1.1      jmmv 
    241   1.1.1.7  christos 	notify_window("window-layout-changed", w);
    242   1.1.1.3  christos 
    243       1.1      jmmv 	return (0);
    244       1.1      jmmv 
    245       1.1      jmmv fail:
    246       1.1      jmmv 	layout_free_cell(lc);
    247       1.1      jmmv 	return (-1);
    248       1.1      jmmv }
    249       1.1      jmmv 
    250       1.1      jmmv /* Assign panes into cells. */
    251   1.1.1.7  christos static void
    252       1.1      jmmv layout_assign(struct window_pane **wp, struct layout_cell *lc)
    253       1.1      jmmv {
    254       1.1      jmmv 	struct layout_cell	*lcchild;
    255       1.1      jmmv 
    256       1.1      jmmv 	switch (lc->type) {
    257       1.1      jmmv 	case LAYOUT_WINDOWPANE:
    258       1.1      jmmv 		layout_make_leaf(lc, *wp);
    259       1.1      jmmv 		*wp = TAILQ_NEXT(*wp, entry);
    260       1.1      jmmv 		return;
    261       1.1      jmmv 	case LAYOUT_LEFTRIGHT:
    262       1.1      jmmv 	case LAYOUT_TOPBOTTOM:
    263       1.1      jmmv 		TAILQ_FOREACH(lcchild, &lc->cells, entry)
    264       1.1      jmmv 			layout_assign(wp, lcchild);
    265       1.1      jmmv 		return;
    266       1.1      jmmv 	}
    267       1.1      jmmv }
    268       1.1      jmmv 
    269       1.1      jmmv /* Construct a cell from all or part of a layout tree. */
    270   1.1.1.7  christos static struct layout_cell *
    271       1.1      jmmv layout_construct(struct layout_cell *lcparent, const char **layout)
    272       1.1      jmmv {
    273       1.1      jmmv 	struct layout_cell     *lc, *lcchild;
    274       1.1      jmmv 	u_int			sx, sy, xoff, yoff;
    275   1.1.1.3  christos 	const char	       *saved;
    276       1.1      jmmv 
    277       1.1      jmmv 	if (!isdigit((u_char) **layout))
    278       1.1      jmmv 		return (NULL);
    279       1.1      jmmv 	if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4)
    280       1.1      jmmv 		return (NULL);
    281       1.1      jmmv 
    282       1.1      jmmv 	while (isdigit((u_char) **layout))
    283       1.1      jmmv 		(*layout)++;
    284       1.1      jmmv 	if (**layout != 'x')
    285       1.1      jmmv 		return (NULL);
    286       1.1      jmmv 	(*layout)++;
    287       1.1      jmmv 	while (isdigit((u_char) **layout))
    288       1.1      jmmv 		(*layout)++;
    289       1.1      jmmv 	if (**layout != ',')
    290       1.1      jmmv 		return (NULL);
    291       1.1      jmmv 	(*layout)++;
    292       1.1      jmmv 	while (isdigit((u_char) **layout))
    293       1.1      jmmv 		(*layout)++;
    294       1.1      jmmv 	if (**layout != ',')
    295       1.1      jmmv 		return (NULL);
    296       1.1      jmmv 	(*layout)++;
    297       1.1      jmmv 	while (isdigit((u_char) **layout))
    298       1.1      jmmv 		(*layout)++;
    299   1.1.1.3  christos 	if (**layout == ',') {
    300   1.1.1.3  christos 		saved = *layout;
    301   1.1.1.3  christos 		(*layout)++;
    302   1.1.1.3  christos 		while (isdigit((u_char) **layout))
    303   1.1.1.3  christos 			(*layout)++;
    304   1.1.1.3  christos 		if (**layout == 'x')
    305   1.1.1.3  christos 			*layout = saved;
    306   1.1.1.3  christos 	}
    307       1.1      jmmv 
    308       1.1      jmmv 	lc = layout_create_cell(lcparent);
    309       1.1      jmmv 	lc->sx = sx;
    310       1.1      jmmv 	lc->sy = sy;
    311       1.1      jmmv 	lc->xoff = xoff;
    312       1.1      jmmv 	lc->yoff = yoff;
    313       1.1      jmmv 
    314       1.1      jmmv 	switch (**layout) {
    315       1.1      jmmv 	case ',':
    316       1.1      jmmv 	case '}':
    317       1.1      jmmv 	case ']':
    318       1.1      jmmv 	case '\0':
    319       1.1      jmmv 		return (lc);
    320       1.1      jmmv 	case '{':
    321       1.1      jmmv 		lc->type = LAYOUT_LEFTRIGHT;
    322       1.1      jmmv 		break;
    323       1.1      jmmv 	case '[':
    324       1.1      jmmv 		lc->type = LAYOUT_TOPBOTTOM;
    325       1.1      jmmv 		break;
    326       1.1      jmmv 	default:
    327       1.1      jmmv 		goto fail;
    328       1.1      jmmv 	}
    329       1.1      jmmv 
    330       1.1      jmmv 	do {
    331       1.1      jmmv 		(*layout)++;
    332       1.1      jmmv 		lcchild = layout_construct(lc, layout);
    333       1.1      jmmv 		if (lcchild == NULL)
    334       1.1      jmmv 			goto fail;
    335       1.1      jmmv 		TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry);
    336       1.1      jmmv 	} while (**layout == ',');
    337       1.1      jmmv 
    338       1.1      jmmv 	switch (lc->type) {
    339       1.1      jmmv 	case LAYOUT_LEFTRIGHT:
    340       1.1      jmmv 		if (**layout != '}')
    341       1.1      jmmv 			goto fail;
    342       1.1      jmmv 		break;
    343       1.1      jmmv 	case LAYOUT_TOPBOTTOM:
    344       1.1      jmmv 		if (**layout != ']')
    345       1.1      jmmv 			goto fail;
    346       1.1      jmmv 		break;
    347       1.1      jmmv 	default:
    348       1.1      jmmv 		goto fail;
    349       1.1      jmmv 	}
    350       1.1      jmmv 	(*layout)++;
    351       1.1      jmmv 
    352       1.1      jmmv 	return (lc);
    353       1.1      jmmv 
    354       1.1      jmmv fail:
    355       1.1      jmmv 	layout_free_cell(lc);
    356       1.1      jmmv 	return (NULL);
    357       1.1      jmmv }
    358