1*0a6a1f1dSLionel Sambuc /* Id */
2eda6f593SDavid van Moolenbroek
3eda6f593SDavid van Moolenbroek /*
4eda6f593SDavid van Moolenbroek * Copyright (c) 2010 Nicholas Marriott <nicm@users.sourceforge.net>
5eda6f593SDavid van Moolenbroek *
6eda6f593SDavid van Moolenbroek * Permission to use, copy, modify, and distribute this software for any
7eda6f593SDavid van Moolenbroek * purpose with or without fee is hereby granted, provided that the above
8eda6f593SDavid van Moolenbroek * copyright notice and this permission notice appear in all copies.
9eda6f593SDavid van Moolenbroek *
10eda6f593SDavid van Moolenbroek * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11eda6f593SDavid van Moolenbroek * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12eda6f593SDavid van Moolenbroek * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13eda6f593SDavid van Moolenbroek * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14eda6f593SDavid van Moolenbroek * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15eda6f593SDavid van Moolenbroek * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16eda6f593SDavid van Moolenbroek * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17eda6f593SDavid van Moolenbroek */
18eda6f593SDavid van Moolenbroek
19eda6f593SDavid van Moolenbroek #include <sys/types.h>
20eda6f593SDavid van Moolenbroek
21eda6f593SDavid van Moolenbroek #include <ctype.h>
22eda6f593SDavid van Moolenbroek #include <string.h>
23eda6f593SDavid van Moolenbroek
24eda6f593SDavid van Moolenbroek #include "tmux.h"
25eda6f593SDavid van Moolenbroek
26eda6f593SDavid van Moolenbroek struct layout_cell *layout_find_bottomright(struct layout_cell *);
27eda6f593SDavid van Moolenbroek u_short layout_checksum(const char *);
28eda6f593SDavid van Moolenbroek int layout_append(struct layout_cell *, char *, size_t);
29eda6f593SDavid van Moolenbroek struct layout_cell *layout_construct(struct layout_cell *, const char **);
30eda6f593SDavid van Moolenbroek void layout_assign(struct window_pane **, struct layout_cell *);
31eda6f593SDavid van Moolenbroek
32eda6f593SDavid van Moolenbroek /* Find the bottom-right cell. */
33eda6f593SDavid van Moolenbroek struct layout_cell *
layout_find_bottomright(struct layout_cell * lc)34eda6f593SDavid van Moolenbroek layout_find_bottomright(struct layout_cell *lc)
35eda6f593SDavid van Moolenbroek {
36eda6f593SDavid van Moolenbroek if (lc->type == LAYOUT_WINDOWPANE)
37eda6f593SDavid van Moolenbroek return (lc);
38eda6f593SDavid van Moolenbroek lc = TAILQ_LAST(&lc->cells, layout_cells);
39eda6f593SDavid van Moolenbroek return (layout_find_bottomright(lc));
40eda6f593SDavid van Moolenbroek }
41eda6f593SDavid van Moolenbroek
42eda6f593SDavid van Moolenbroek /* Calculate layout checksum. */
43eda6f593SDavid van Moolenbroek u_short
layout_checksum(const char * layout)44eda6f593SDavid van Moolenbroek layout_checksum(const char *layout)
45eda6f593SDavid van Moolenbroek {
46eda6f593SDavid van Moolenbroek u_short csum;
47eda6f593SDavid van Moolenbroek
48eda6f593SDavid van Moolenbroek csum = 0;
49eda6f593SDavid van Moolenbroek for (; *layout != '\0'; layout++) {
50eda6f593SDavid van Moolenbroek csum = (csum >> 1) + ((csum & 1) << 15);
51eda6f593SDavid van Moolenbroek csum += *layout;
52eda6f593SDavid van Moolenbroek }
53eda6f593SDavid van Moolenbroek return (csum);
54eda6f593SDavid van Moolenbroek }
55eda6f593SDavid van Moolenbroek
56eda6f593SDavid van Moolenbroek /* Dump layout as a string. */
57eda6f593SDavid van Moolenbroek char *
layout_dump(struct window * w)58eda6f593SDavid van Moolenbroek layout_dump(struct window *w)
59eda6f593SDavid van Moolenbroek {
60eda6f593SDavid van Moolenbroek char layout[BUFSIZ], *out;
61eda6f593SDavid van Moolenbroek
62eda6f593SDavid van Moolenbroek *layout = '\0';
63eda6f593SDavid van Moolenbroek if (layout_append(w->layout_root, layout, sizeof layout) != 0)
64eda6f593SDavid van Moolenbroek return (NULL);
65eda6f593SDavid van Moolenbroek
66*0a6a1f1dSLionel Sambuc xasprintf(&out, "%04x,%s", layout_checksum(layout), layout);
67eda6f593SDavid van Moolenbroek return (out);
68eda6f593SDavid van Moolenbroek }
69eda6f593SDavid van Moolenbroek
70eda6f593SDavid van Moolenbroek /* Append information for a single cell. */
71eda6f593SDavid van Moolenbroek int
layout_append(struct layout_cell * lc,char * buf,size_t len)72eda6f593SDavid van Moolenbroek layout_append(struct layout_cell *lc, char *buf, size_t len)
73eda6f593SDavid van Moolenbroek {
74eda6f593SDavid van Moolenbroek struct layout_cell *lcchild;
75eda6f593SDavid van Moolenbroek char tmp[64];
76eda6f593SDavid van Moolenbroek size_t tmplen;
77eda6f593SDavid van Moolenbroek const char *brackets = "][";
78eda6f593SDavid van Moolenbroek
79eda6f593SDavid van Moolenbroek if (len == 0)
80eda6f593SDavid van Moolenbroek return (-1);
81eda6f593SDavid van Moolenbroek
82*0a6a1f1dSLionel Sambuc if (lc->wp != NULL) {
83*0a6a1f1dSLionel Sambuc tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u,%u",
84*0a6a1f1dSLionel Sambuc lc->sx, lc->sy, lc->xoff, lc->yoff, lc->wp->id);
85*0a6a1f1dSLionel Sambuc } else {
86*0a6a1f1dSLionel Sambuc tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u",
87*0a6a1f1dSLionel Sambuc lc->sx, lc->sy, lc->xoff, lc->yoff);
88*0a6a1f1dSLionel Sambuc }
89eda6f593SDavid van Moolenbroek if (tmplen > (sizeof tmp) - 1)
90eda6f593SDavid van Moolenbroek return (-1);
91eda6f593SDavid van Moolenbroek if (strlcat(buf, tmp, len) >= len)
92eda6f593SDavid van Moolenbroek return (-1);
93eda6f593SDavid van Moolenbroek
94eda6f593SDavid van Moolenbroek switch (lc->type) {
95eda6f593SDavid van Moolenbroek case LAYOUT_LEFTRIGHT:
96eda6f593SDavid van Moolenbroek brackets = "}{";
97eda6f593SDavid van Moolenbroek /* FALLTHROUGH */
98eda6f593SDavid van Moolenbroek case LAYOUT_TOPBOTTOM:
99eda6f593SDavid van Moolenbroek if (strlcat(buf, &brackets[1], len) >= len)
100eda6f593SDavid van Moolenbroek return (-1);
101eda6f593SDavid van Moolenbroek TAILQ_FOREACH(lcchild, &lc->cells, entry) {
102eda6f593SDavid van Moolenbroek if (layout_append(lcchild, buf, len) != 0)
103eda6f593SDavid van Moolenbroek return (-1);
104eda6f593SDavid van Moolenbroek if (strlcat(buf, ",", len) >= len)
105eda6f593SDavid van Moolenbroek return (-1);
106eda6f593SDavid van Moolenbroek }
107eda6f593SDavid van Moolenbroek buf[strlen(buf) - 1] = brackets[0];
108eda6f593SDavid van Moolenbroek break;
109eda6f593SDavid van Moolenbroek case LAYOUT_WINDOWPANE:
110eda6f593SDavid van Moolenbroek break;
111eda6f593SDavid van Moolenbroek }
112eda6f593SDavid van Moolenbroek
113eda6f593SDavid van Moolenbroek return (0);
114eda6f593SDavid van Moolenbroek }
115eda6f593SDavid van Moolenbroek
116eda6f593SDavid van Moolenbroek /* Parse a layout string and arrange window as layout. */
117eda6f593SDavid van Moolenbroek int
layout_parse(struct window * w,const char * layout)118eda6f593SDavid van Moolenbroek layout_parse(struct window *w, const char *layout)
119eda6f593SDavid van Moolenbroek {
120eda6f593SDavid van Moolenbroek struct layout_cell *lc, *lcchild;
121eda6f593SDavid van Moolenbroek struct window_pane *wp;
122eda6f593SDavid van Moolenbroek u_int npanes, ncells, sx, sy;
123eda6f593SDavid van Moolenbroek u_short csum;
124eda6f593SDavid van Moolenbroek
125eda6f593SDavid van Moolenbroek /* Check validity. */
126eda6f593SDavid van Moolenbroek if (sscanf(layout, "%hx,", &csum) != 1)
127eda6f593SDavid van Moolenbroek return (-1);
128eda6f593SDavid van Moolenbroek layout += 5;
129eda6f593SDavid van Moolenbroek if (csum != layout_checksum(layout))
130eda6f593SDavid van Moolenbroek return (-1);
131eda6f593SDavid van Moolenbroek
132eda6f593SDavid van Moolenbroek /* Build the layout. */
133eda6f593SDavid van Moolenbroek lc = layout_construct(NULL, &layout);
134eda6f593SDavid van Moolenbroek if (lc == NULL)
135eda6f593SDavid van Moolenbroek return (-1);
136eda6f593SDavid van Moolenbroek if (*layout != '\0')
137eda6f593SDavid van Moolenbroek goto fail;
138eda6f593SDavid van Moolenbroek
139eda6f593SDavid van Moolenbroek /* Check this window will fit into the layout. */
140eda6f593SDavid van Moolenbroek for (;;) {
141eda6f593SDavid van Moolenbroek npanes = window_count_panes(w);
142eda6f593SDavid van Moolenbroek ncells = layout_count_cells(lc);
143eda6f593SDavid van Moolenbroek if (npanes > ncells)
144eda6f593SDavid van Moolenbroek goto fail;
145eda6f593SDavid van Moolenbroek if (npanes == ncells)
146eda6f593SDavid van Moolenbroek break;
147eda6f593SDavid van Moolenbroek
148eda6f593SDavid van Moolenbroek /* Fewer panes than cells - close the bottom right. */
149eda6f593SDavid van Moolenbroek lcchild = layout_find_bottomright(lc);
150eda6f593SDavid van Moolenbroek layout_destroy_cell(lcchild, &lc);
151eda6f593SDavid van Moolenbroek }
152eda6f593SDavid van Moolenbroek
153eda6f593SDavid van Moolenbroek /* Save the old window size and resize to the layout size. */
154eda6f593SDavid van Moolenbroek sx = w->sx; sy = w->sy;
155eda6f593SDavid van Moolenbroek window_resize(w, lc->sx, lc->sy);
156eda6f593SDavid van Moolenbroek
157eda6f593SDavid van Moolenbroek /* Destroy the old layout and swap to the new. */
158eda6f593SDavid van Moolenbroek layout_free_cell(w->layout_root);
159eda6f593SDavid van Moolenbroek w->layout_root = lc;
160eda6f593SDavid van Moolenbroek
161eda6f593SDavid van Moolenbroek /* Assign the panes into the cells. */
162eda6f593SDavid van Moolenbroek wp = TAILQ_FIRST(&w->panes);
163eda6f593SDavid van Moolenbroek layout_assign(&wp, lc);
164eda6f593SDavid van Moolenbroek
165eda6f593SDavid van Moolenbroek /* Update pane offsets and sizes. */
166eda6f593SDavid van Moolenbroek layout_fix_offsets(lc);
167eda6f593SDavid van Moolenbroek layout_fix_panes(w, lc->sx, lc->sy);
168eda6f593SDavid van Moolenbroek
169eda6f593SDavid van Moolenbroek /* Then resize the layout back to the original window size. */
170eda6f593SDavid van Moolenbroek layout_resize(w, sx, sy);
171eda6f593SDavid van Moolenbroek window_resize(w, sx, sy);
172eda6f593SDavid van Moolenbroek
173eda6f593SDavid van Moolenbroek layout_print_cell(lc, __func__, 0);
174eda6f593SDavid van Moolenbroek
175*0a6a1f1dSLionel Sambuc notify_window_layout_changed(w);
176*0a6a1f1dSLionel Sambuc
177eda6f593SDavid van Moolenbroek return (0);
178eda6f593SDavid van Moolenbroek
179eda6f593SDavid van Moolenbroek fail:
180eda6f593SDavid van Moolenbroek layout_free_cell(lc);
181eda6f593SDavid van Moolenbroek return (-1);
182eda6f593SDavid van Moolenbroek }
183eda6f593SDavid van Moolenbroek
184eda6f593SDavid van Moolenbroek /* Assign panes into cells. */
185eda6f593SDavid van Moolenbroek void
layout_assign(struct window_pane ** wp,struct layout_cell * lc)186eda6f593SDavid van Moolenbroek layout_assign(struct window_pane **wp, struct layout_cell *lc)
187eda6f593SDavid van Moolenbroek {
188eda6f593SDavid van Moolenbroek struct layout_cell *lcchild;
189eda6f593SDavid van Moolenbroek
190eda6f593SDavid van Moolenbroek switch (lc->type) {
191eda6f593SDavid van Moolenbroek case LAYOUT_WINDOWPANE:
192eda6f593SDavid van Moolenbroek layout_make_leaf(lc, *wp);
193eda6f593SDavid van Moolenbroek *wp = TAILQ_NEXT(*wp, entry);
194eda6f593SDavid van Moolenbroek return;
195eda6f593SDavid van Moolenbroek case LAYOUT_LEFTRIGHT:
196eda6f593SDavid van Moolenbroek case LAYOUT_TOPBOTTOM:
197eda6f593SDavid van Moolenbroek TAILQ_FOREACH(lcchild, &lc->cells, entry)
198eda6f593SDavid van Moolenbroek layout_assign(wp, lcchild);
199eda6f593SDavid van Moolenbroek return;
200eda6f593SDavid van Moolenbroek }
201eda6f593SDavid van Moolenbroek }
202eda6f593SDavid van Moolenbroek
203eda6f593SDavid van Moolenbroek /* Construct a cell from all or part of a layout tree. */
204eda6f593SDavid van Moolenbroek struct layout_cell *
layout_construct(struct layout_cell * lcparent,const char ** layout)205eda6f593SDavid van Moolenbroek layout_construct(struct layout_cell *lcparent, const char **layout)
206eda6f593SDavid van Moolenbroek {
207eda6f593SDavid van Moolenbroek struct layout_cell *lc, *lcchild;
208eda6f593SDavid van Moolenbroek u_int sx, sy, xoff, yoff;
209*0a6a1f1dSLionel Sambuc const char *saved;
210eda6f593SDavid van Moolenbroek
211eda6f593SDavid van Moolenbroek if (!isdigit((u_char) **layout))
212eda6f593SDavid van Moolenbroek return (NULL);
213eda6f593SDavid van Moolenbroek if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4)
214eda6f593SDavid van Moolenbroek return (NULL);
215eda6f593SDavid van Moolenbroek
216eda6f593SDavid van Moolenbroek while (isdigit((u_char) **layout))
217eda6f593SDavid van Moolenbroek (*layout)++;
218eda6f593SDavid van Moolenbroek if (**layout != 'x')
219eda6f593SDavid van Moolenbroek return (NULL);
220eda6f593SDavid van Moolenbroek (*layout)++;
221eda6f593SDavid van Moolenbroek while (isdigit((u_char) **layout))
222eda6f593SDavid van Moolenbroek (*layout)++;
223eda6f593SDavid van Moolenbroek if (**layout != ',')
224eda6f593SDavid van Moolenbroek return (NULL);
225eda6f593SDavid van Moolenbroek (*layout)++;
226eda6f593SDavid van Moolenbroek while (isdigit((u_char) **layout))
227eda6f593SDavid van Moolenbroek (*layout)++;
228eda6f593SDavid van Moolenbroek if (**layout != ',')
229eda6f593SDavid van Moolenbroek return (NULL);
230eda6f593SDavid van Moolenbroek (*layout)++;
231eda6f593SDavid van Moolenbroek while (isdigit((u_char) **layout))
232eda6f593SDavid van Moolenbroek (*layout)++;
233*0a6a1f1dSLionel Sambuc if (**layout == ',') {
234*0a6a1f1dSLionel Sambuc saved = *layout;
235*0a6a1f1dSLionel Sambuc (*layout)++;
236*0a6a1f1dSLionel Sambuc while (isdigit((u_char) **layout))
237*0a6a1f1dSLionel Sambuc (*layout)++;
238*0a6a1f1dSLionel Sambuc if (**layout == 'x')
239*0a6a1f1dSLionel Sambuc *layout = saved;
240*0a6a1f1dSLionel Sambuc }
241eda6f593SDavid van Moolenbroek
242eda6f593SDavid van Moolenbroek lc = layout_create_cell(lcparent);
243eda6f593SDavid van Moolenbroek lc->sx = sx;
244eda6f593SDavid van Moolenbroek lc->sy = sy;
245eda6f593SDavid van Moolenbroek lc->xoff = xoff;
246eda6f593SDavid van Moolenbroek lc->yoff = yoff;
247eda6f593SDavid van Moolenbroek
248eda6f593SDavid van Moolenbroek switch (**layout) {
249eda6f593SDavid van Moolenbroek case ',':
250eda6f593SDavid van Moolenbroek case '}':
251eda6f593SDavid van Moolenbroek case ']':
252eda6f593SDavid van Moolenbroek case '\0':
253eda6f593SDavid van Moolenbroek return (lc);
254eda6f593SDavid van Moolenbroek case '{':
255eda6f593SDavid van Moolenbroek lc->type = LAYOUT_LEFTRIGHT;
256eda6f593SDavid van Moolenbroek break;
257eda6f593SDavid van Moolenbroek case '[':
258eda6f593SDavid van Moolenbroek lc->type = LAYOUT_TOPBOTTOM;
259eda6f593SDavid van Moolenbroek break;
260eda6f593SDavid van Moolenbroek default:
261eda6f593SDavid van Moolenbroek goto fail;
262eda6f593SDavid van Moolenbroek }
263eda6f593SDavid van Moolenbroek
264eda6f593SDavid van Moolenbroek do {
265eda6f593SDavid van Moolenbroek (*layout)++;
266eda6f593SDavid van Moolenbroek lcchild = layout_construct(lc, layout);
267eda6f593SDavid van Moolenbroek if (lcchild == NULL)
268eda6f593SDavid van Moolenbroek goto fail;
269eda6f593SDavid van Moolenbroek TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry);
270eda6f593SDavid van Moolenbroek } while (**layout == ',');
271eda6f593SDavid van Moolenbroek
272eda6f593SDavid van Moolenbroek switch (lc->type) {
273eda6f593SDavid van Moolenbroek case LAYOUT_LEFTRIGHT:
274eda6f593SDavid van Moolenbroek if (**layout != '}')
275eda6f593SDavid van Moolenbroek goto fail;
276eda6f593SDavid van Moolenbroek break;
277eda6f593SDavid van Moolenbroek case LAYOUT_TOPBOTTOM:
278eda6f593SDavid van Moolenbroek if (**layout != ']')
279eda6f593SDavid van Moolenbroek goto fail;
280eda6f593SDavid van Moolenbroek break;
281eda6f593SDavid van Moolenbroek default:
282eda6f593SDavid van Moolenbroek goto fail;
283eda6f593SDavid van Moolenbroek }
284eda6f593SDavid van Moolenbroek (*layout)++;
285eda6f593SDavid van Moolenbroek
286eda6f593SDavid van Moolenbroek return (lc);
287eda6f593SDavid van Moolenbroek
288eda6f593SDavid van Moolenbroek fail:
289eda6f593SDavid van Moolenbroek layout_free_cell(lc);
290eda6f593SDavid van Moolenbroek return (NULL);
291eda6f593SDavid van Moolenbroek }
292