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