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