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