1 /* $OpenBSD: layout-custom.c,v 1.9 2016/07/15 09:27:35 nicm Exp $ */ 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 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 *, 29 size_t); 30 static struct layout_cell *layout_construct(struct layout_cell *, 31 const char **); 32 static void layout_assign(struct window_pane **, 33 struct layout_cell *); 34 35 /* Find the bottom-right cell. */ 36 static struct layout_cell * 37 layout_find_bottomright(struct layout_cell *lc) 38 { 39 if (lc->type == LAYOUT_WINDOWPANE) 40 return (lc); 41 lc = TAILQ_LAST(&lc->cells, layout_cells); 42 return (layout_find_bottomright(lc)); 43 } 44 45 /* Calculate layout checksum. */ 46 static u_short 47 layout_checksum(const char *layout) 48 { 49 u_short csum; 50 51 csum = 0; 52 for (; *layout != '\0'; layout++) { 53 csum = (csum >> 1) + ((csum & 1) << 15); 54 csum += *layout; 55 } 56 return (csum); 57 } 58 59 /* Dump layout as a string. */ 60 char * 61 layout_dump(struct layout_cell *root) 62 { 63 char layout[BUFSIZ], *out; 64 65 *layout = '\0'; 66 if (layout_append(root, layout, sizeof layout) != 0) 67 return (NULL); 68 69 xasprintf(&out, "%04hx,%s", layout_checksum(layout), layout); 70 return (out); 71 } 72 73 /* Append information for a single cell. */ 74 static int 75 layout_append(struct layout_cell *lc, char *buf, size_t len) 76 { 77 struct layout_cell *lcchild; 78 char tmp[64]; 79 size_t tmplen; 80 const char *brackets = "]["; 81 82 if (len == 0) 83 return (-1); 84 85 if (lc->wp != NULL) { 86 tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u,%u", 87 lc->sx, lc->sy, lc->xoff, lc->yoff, lc->wp->id); 88 } else { 89 tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u", 90 lc->sx, lc->sy, lc->xoff, lc->yoff); 91 } 92 if (tmplen > (sizeof tmp) - 1) 93 return (-1); 94 if (strlcat(buf, tmp, len) >= len) 95 return (-1); 96 97 switch (lc->type) { 98 case LAYOUT_LEFTRIGHT: 99 brackets = "}{"; 100 /* FALLTHROUGH */ 101 case LAYOUT_TOPBOTTOM: 102 if (strlcat(buf, &brackets[1], len) >= len) 103 return (-1); 104 TAILQ_FOREACH(lcchild, &lc->cells, entry) { 105 if (layout_append(lcchild, buf, len) != 0) 106 return (-1); 107 if (strlcat(buf, ",", len) >= len) 108 return (-1); 109 } 110 buf[strlen(buf) - 1] = brackets[0]; 111 break; 112 case LAYOUT_WINDOWPANE: 113 break; 114 } 115 116 return (0); 117 } 118 119 /* Parse a layout string and arrange window as layout. */ 120 int 121 layout_parse(struct window *w, const char *layout) 122 { 123 struct layout_cell *lc, *lcchild; 124 struct window_pane *wp; 125 u_int npanes, ncells, sx, sy; 126 u_short csum; 127 128 /* Check validity. */ 129 if (sscanf(layout, "%hx,", &csum) != 1) 130 return (-1); 131 layout += 5; 132 if (csum != layout_checksum(layout)) 133 return (-1); 134 135 /* Build the layout. */ 136 lc = layout_construct(NULL, &layout); 137 if (lc == NULL) 138 return (-1); 139 if (*layout != '\0') 140 goto fail; 141 142 /* Check this window will fit into the layout. */ 143 for (;;) { 144 npanes = window_count_panes(w); 145 ncells = layout_count_cells(lc); 146 if (npanes > ncells) 147 goto fail; 148 if (npanes == ncells) 149 break; 150 151 /* Fewer panes than cells - close the bottom right. */ 152 lcchild = layout_find_bottomright(lc); 153 layout_destroy_cell(lcchild, &lc); 154 } 155 156 /* Save the old window size and resize to the layout size. */ 157 sx = w->sx; sy = w->sy; 158 window_resize(w, lc->sx, lc->sy); 159 160 /* Destroy the old layout and swap to the new. */ 161 layout_free_cell(w->layout_root); 162 w->layout_root = lc; 163 164 /* Assign the panes into the cells. */ 165 wp = TAILQ_FIRST(&w->panes); 166 layout_assign(&wp, lc); 167 168 /* Update pane offsets and sizes. */ 169 layout_fix_offsets(lc); 170 layout_fix_panes(w, lc->sx, lc->sy); 171 172 /* Then resize the layout back to the original window size. */ 173 layout_resize(w, sx, sy); 174 window_resize(w, sx, sy); 175 176 layout_print_cell(lc, __func__, 0); 177 178 notify_window_layout_changed(w); 179 180 return (0); 181 182 fail: 183 layout_free_cell(lc); 184 return (-1); 185 } 186 187 /* Assign panes into cells. */ 188 static void 189 layout_assign(struct window_pane **wp, struct layout_cell *lc) 190 { 191 struct layout_cell *lcchild; 192 193 switch (lc->type) { 194 case LAYOUT_WINDOWPANE: 195 layout_make_leaf(lc, *wp); 196 *wp = TAILQ_NEXT(*wp, entry); 197 return; 198 case LAYOUT_LEFTRIGHT: 199 case LAYOUT_TOPBOTTOM: 200 TAILQ_FOREACH(lcchild, &lc->cells, entry) 201 layout_assign(wp, lcchild); 202 return; 203 } 204 } 205 206 /* Construct a cell from all or part of a layout tree. */ 207 static struct layout_cell * 208 layout_construct(struct layout_cell *lcparent, const char **layout) 209 { 210 struct layout_cell *lc, *lcchild; 211 u_int sx, sy, xoff, yoff; 212 const char *saved; 213 214 if (!isdigit((u_char) **layout)) 215 return (NULL); 216 if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4) 217 return (NULL); 218 219 while (isdigit((u_char) **layout)) 220 (*layout)++; 221 if (**layout != 'x') 222 return (NULL); 223 (*layout)++; 224 while (isdigit((u_char) **layout)) 225 (*layout)++; 226 if (**layout != ',') 227 return (NULL); 228 (*layout)++; 229 while (isdigit((u_char) **layout)) 230 (*layout)++; 231 if (**layout != ',') 232 return (NULL); 233 (*layout)++; 234 while (isdigit((u_char) **layout)) 235 (*layout)++; 236 if (**layout == ',') { 237 saved = *layout; 238 (*layout)++; 239 while (isdigit((u_char) **layout)) 240 (*layout)++; 241 if (**layout == 'x') 242 *layout = saved; 243 } 244 245 lc = layout_create_cell(lcparent); 246 lc->sx = sx; 247 lc->sy = sy; 248 lc->xoff = xoff; 249 lc->yoff = yoff; 250 251 switch (**layout) { 252 case ',': 253 case '}': 254 case ']': 255 case '\0': 256 return (lc); 257 case '{': 258 lc->type = LAYOUT_LEFTRIGHT; 259 break; 260 case '[': 261 lc->type = LAYOUT_TOPBOTTOM; 262 break; 263 default: 264 goto fail; 265 } 266 267 do { 268 (*layout)++; 269 lcchild = layout_construct(lc, layout); 270 if (lcchild == NULL) 271 goto fail; 272 TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry); 273 } while (**layout == ','); 274 275 switch (lc->type) { 276 case LAYOUT_LEFTRIGHT: 277 if (**layout != '}') 278 goto fail; 279 break; 280 case LAYOUT_TOPBOTTOM: 281 if (**layout != ']') 282 goto fail; 283 break; 284 default: 285 goto fail; 286 } 287 (*layout)++; 288 289 return (lc); 290 291 fail: 292 layout_free_cell(lc); 293 return (NULL); 294 } 295