4 * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
26 static struct layout_cell
*layout_find_bottomright(struct layout_cell
*);
27 static u_short
layout_checksum(const char *);
28 static int layout_append(struct layout_cell
*, char *,
30 static struct layout_cell
*layout_construct(struct layout_cell
*,
32 static void layout_assign(struct window_pane
**,
33 struct layout_cell
*);
35 /* Find the bottom-right cell. */
36 static struct layout_cell
*
37 layout_find_bottomright(struct layout_cell
*lc
)
39 if (lc
->type
== LAYOUT_WINDOWPANE
)
41 lc
= TAILQ_LAST(&lc
->cells
, layout_cells
);
42 return (layout_find_bottomright(lc
));
45 /* Calculate layout checksum. */
47 layout_checksum(const char *layout
)
52 for (; *layout
!= '\0'; layout
++) {
53 csum
= (csum
>> 1) + ((csum
& 1) << 15);
59 /* Dump layout as a string. */
61 layout_dump(struct layout_cell
*root
)
63 char layout
[8192], *out
;
66 if (layout_append(root
, layout
, sizeof layout
) != 0)
69 xasprintf(&out
, "%04hx,%s", layout_checksum(layout
), layout
);
73 /* Append information for a single cell. */
75 layout_append(struct layout_cell
*lc
, char *buf
, size_t len
)
77 struct layout_cell
*lcchild
;
80 const char *brackets
= "][";
86 tmplen
= xsnprintf(tmp
, sizeof tmp
, "%ux%u,%u,%u,%u",
87 lc
->sx
, lc
->sy
, lc
->xoff
, lc
->yoff
, lc
->wp
->id
);
89 tmplen
= xsnprintf(tmp
, sizeof tmp
, "%ux%u,%u,%u",
90 lc
->sx
, lc
->sy
, lc
->xoff
, lc
->yoff
);
92 if (tmplen
> (sizeof tmp
) - 1)
94 if (strlcat(buf
, tmp
, len
) >= len
)
98 case LAYOUT_LEFTRIGHT
:
101 case LAYOUT_TOPBOTTOM
:
102 if (strlcat(buf
, &brackets
[1], len
) >= len
)
104 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
105 if (layout_append(lcchild
, buf
, len
) != 0)
107 if (strlcat(buf
, ",", len
) >= len
)
110 buf
[strlen(buf
) - 1] = brackets
[0];
112 case LAYOUT_WINDOWPANE
:
119 /* Check layout sizes fit. */
121 layout_check(struct layout_cell
*lc
)
123 struct layout_cell
*lcchild
;
127 case LAYOUT_WINDOWPANE
:
129 case LAYOUT_LEFTRIGHT
:
130 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
131 if (lcchild
->sy
!= lc
->sy
)
133 if (!layout_check(lcchild
))
135 n
+= lcchild
->sx
+ 1;
140 case LAYOUT_TOPBOTTOM
:
141 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
142 if (lcchild
->sx
!= lc
->sx
)
144 if (!layout_check(lcchild
))
146 n
+= lcchild
->sy
+ 1;
155 /* Parse a layout string and arrange window as layout. */
157 layout_parse(struct window
*w
, const char *layout
, char **cause
)
159 struct layout_cell
*lc
, *lcchild
;
160 struct window_pane
*wp
;
161 u_int npanes
, ncells
, sx
= 0, sy
= 0;
164 /* Check validity. */
165 if (sscanf(layout
, "%hx,", &csum
) != 1) {
166 *cause
= xstrdup("invalid layout");
170 if (csum
!= layout_checksum(layout
)) {
171 *cause
= xstrdup("invalid layout");
175 /* Build the layout. */
176 lc
= layout_construct(NULL
, &layout
);
178 *cause
= xstrdup("invalid layout");
181 if (*layout
!= '\0') {
182 *cause
= xstrdup("invalid layout");
186 /* Check this window will fit into the layout. */
188 npanes
= window_count_panes(w
);
189 ncells
= layout_count_cells(lc
);
190 if (npanes
> ncells
) {
191 xasprintf(cause
, "have %u panes but need %u", npanes
,
195 if (npanes
== ncells
)
198 /* Fewer panes than cells - close the bottom right. */
199 lcchild
= layout_find_bottomright(lc
);
200 layout_destroy_cell(w
, lcchild
, &lc
);
204 * It appears older versions of tmux were able to generate layouts with
205 * an incorrect top cell size - if it is larger than the top child then
206 * correct that (if this is still wrong the check code will catch it).
209 case LAYOUT_WINDOWPANE
:
211 case LAYOUT_LEFTRIGHT
:
212 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
213 sy
= lcchild
->sy
+ 1;
214 sx
+= lcchild
->sx
+ 1;
217 case LAYOUT_TOPBOTTOM
:
218 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
219 sx
= lcchild
->sx
+ 1;
220 sy
+= lcchild
->sy
+ 1;
224 if (lc
->type
!= LAYOUT_WINDOWPANE
&& (lc
->sx
!= sx
|| lc
->sy
!= sy
)) {
225 log_debug("fix layout %u,%u to %u,%u", lc
->sx
, lc
->sy
, sx
,sy
);
226 layout_print_cell(lc
, __func__
, 0);
227 lc
->sx
= sx
- 1; lc
->sy
= sy
- 1;
230 /* Check the new layout. */
231 if (!layout_check(lc
)) {
232 *cause
= xstrdup("size mismatch after applying layout");
236 /* Resize to the layout size. */
237 window_resize(w
, lc
->sx
, lc
->sy
, -1, -1);
239 /* Destroy the old layout and swap to the new. */
240 layout_free_cell(w
->layout_root
);
243 /* Assign the panes into the cells. */
244 wp
= TAILQ_FIRST(&w
->panes
);
245 layout_assign(&wp
, lc
);
247 /* Update pane offsets and sizes. */
248 layout_fix_offsets(w
);
249 layout_fix_panes(w
, NULL
);
252 layout_print_cell(lc
, __func__
, 0);
254 notify_window("window-layout-changed", w
);
259 layout_free_cell(lc
);
263 /* Assign panes into cells. */
265 layout_assign(struct window_pane
**wp
, struct layout_cell
*lc
)
267 struct layout_cell
*lcchild
;
270 case LAYOUT_WINDOWPANE
:
271 layout_make_leaf(lc
, *wp
);
272 *wp
= TAILQ_NEXT(*wp
, entry
);
274 case LAYOUT_LEFTRIGHT
:
275 case LAYOUT_TOPBOTTOM
:
276 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
)
277 layout_assign(wp
, lcchild
);
282 /* Construct a cell from all or part of a layout tree. */
283 static struct layout_cell
*
284 layout_construct(struct layout_cell
*lcparent
, const char **layout
)
286 struct layout_cell
*lc
, *lcchild
;
287 u_int sx
, sy
, xoff
, yoff
;
290 if (!isdigit((u_char
) **layout
))
292 if (sscanf(*layout
, "%ux%u,%u,%u", &sx
, &sy
, &xoff
, &yoff
) != 4)
295 while (isdigit((u_char
) **layout
))
300 while (isdigit((u_char
) **layout
))
305 while (isdigit((u_char
) **layout
))
310 while (isdigit((u_char
) **layout
))
312 if (**layout
== ',') {
315 while (isdigit((u_char
) **layout
))
321 lc
= layout_create_cell(lcparent
);
334 lc
->type
= LAYOUT_LEFTRIGHT
;
337 lc
->type
= LAYOUT_TOPBOTTOM
;
345 lcchild
= layout_construct(lc
, layout
);
348 TAILQ_INSERT_TAIL(&lc
->cells
, lcchild
, entry
);
349 } while (**layout
== ',');
352 case LAYOUT_LEFTRIGHT
:
356 case LAYOUT_TOPBOTTOM
:
368 layout_free_cell(lc
);