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