layout-custom.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /* $OpenBSD$ */
  2. /*
  3. * Copyright (c) 2010 Nicholas Marriott <nicholas.marriott@gmail.com>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
  14. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  15. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #include <sys/types.h>
  18. #include <ctype.h>
  19. #include <string.h>
  20. #include "tmux.h"
  21. struct layout_cell *layout_find_bottomright(struct layout_cell *);
  22. u_short layout_checksum(const char *);
  23. int layout_append(struct layout_cell *, char *, size_t);
  24. struct layout_cell *layout_construct(struct layout_cell *, const char **);
  25. void layout_assign(struct window_pane **, struct layout_cell *);
  26. /* Find the bottom-right cell. */
  27. struct layout_cell *
  28. layout_find_bottomright(struct layout_cell *lc)
  29. {
  30. if (lc->type == LAYOUT_WINDOWPANE)
  31. return (lc);
  32. lc = TAILQ_LAST(&lc->cells, layout_cells);
  33. return (layout_find_bottomright(lc));
  34. }
  35. /* Calculate layout checksum. */
  36. u_short
  37. layout_checksum(const char *layout)
  38. {
  39. u_short csum;
  40. csum = 0;
  41. for (; *layout != '\0'; layout++) {
  42. csum = (csum >> 1) + ((csum & 1) << 15);
  43. csum += *layout;
  44. }
  45. return (csum);
  46. }
  47. /* Dump layout as a string. */
  48. char *
  49. layout_dump(struct layout_cell *root)
  50. {
  51. char layout[BUFSIZ], *out;
  52. *layout = '\0';
  53. if (layout_append(root, layout, sizeof layout) != 0)
  54. return (NULL);
  55. xasprintf(&out, "%04x,%s", layout_checksum(layout), layout);
  56. return (out);
  57. }
  58. /* Append information for a single cell. */
  59. int
  60. layout_append(struct layout_cell *lc, char *buf, size_t len)
  61. {
  62. struct layout_cell *lcchild;
  63. char tmp[64];
  64. size_t tmplen;
  65. const char *brackets = "][";
  66. if (len == 0)
  67. return (-1);
  68. if (lc->wp != NULL) {
  69. tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u,%u",
  70. lc->sx, lc->sy, lc->xoff, lc->yoff, lc->wp->id);
  71. } else {
  72. tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u",
  73. lc->sx, lc->sy, lc->xoff, lc->yoff);
  74. }
  75. if (tmplen > (sizeof tmp) - 1)
  76. return (-1);
  77. if (strlcat(buf, tmp, len) >= len)
  78. return (-1);
  79. switch (lc->type) {
  80. case LAYOUT_LEFTRIGHT:
  81. brackets = "}{";
  82. /* FALLTHROUGH */
  83. case LAYOUT_TOPBOTTOM:
  84. if (strlcat(buf, &brackets[1], len) >= len)
  85. return (-1);
  86. TAILQ_FOREACH(lcchild, &lc->cells, entry) {
  87. if (layout_append(lcchild, buf, len) != 0)
  88. return (-1);
  89. if (strlcat(buf, ",", len) >= len)
  90. return (-1);
  91. }
  92. buf[strlen(buf) - 1] = brackets[0];
  93. break;
  94. case LAYOUT_WINDOWPANE:
  95. break;
  96. }
  97. return (0);
  98. }
  99. /* Parse a layout string and arrange window as layout. */
  100. int
  101. layout_parse(struct window *w, const char *layout)
  102. {
  103. struct layout_cell *lc, *lcchild;
  104. struct window_pane *wp;
  105. u_int npanes, ncells, sx, sy;
  106. u_short csum;
  107. /* Check validity. */
  108. if (sscanf(layout, "%hx,", &csum) != 1)
  109. return (-1);
  110. layout += 5;
  111. if (csum != layout_checksum(layout))
  112. return (-1);
  113. /* Build the layout. */
  114. lc = layout_construct(NULL, &layout);
  115. if (lc == NULL)
  116. return (-1);
  117. if (*layout != '\0')
  118. goto fail;
  119. /* Check this window will fit into the layout. */
  120. for (;;) {
  121. npanes = window_count_panes(w);
  122. ncells = layout_count_cells(lc);
  123. if (npanes > ncells)
  124. goto fail;
  125. if (npanes == ncells)
  126. break;
  127. /* Fewer panes than cells - close the bottom right. */
  128. lcchild = layout_find_bottomright(lc);
  129. layout_destroy_cell(lcchild, &lc);
  130. }
  131. /* Save the old window size and resize to the layout size. */
  132. sx = w->sx; sy = w->sy;
  133. window_resize(w, lc->sx, lc->sy);
  134. /* Destroy the old layout and swap to the new. */
  135. layout_free_cell(w->layout_root);
  136. w->layout_root = lc;
  137. /* Assign the panes into the cells. */
  138. wp = TAILQ_FIRST(&w->panes);
  139. layout_assign(&wp, lc);
  140. /* Update pane offsets and sizes. */
  141. layout_fix_offsets(lc);
  142. layout_fix_panes(w, lc->sx, lc->sy);
  143. /* Then resize the layout back to the original window size. */
  144. layout_resize(w, sx, sy);
  145. window_resize(w, sx, sy);
  146. layout_print_cell(lc, __func__, 0);
  147. notify_window_layout_changed(w);
  148. return (0);
  149. fail:
  150. layout_free_cell(lc);
  151. return (-1);
  152. }
  153. /* Assign panes into cells. */
  154. void
  155. layout_assign(struct window_pane **wp, struct layout_cell *lc)
  156. {
  157. struct layout_cell *lcchild;
  158. switch (lc->type) {
  159. case LAYOUT_WINDOWPANE:
  160. layout_make_leaf(lc, *wp);
  161. *wp = TAILQ_NEXT(*wp, entry);
  162. return;
  163. case LAYOUT_LEFTRIGHT:
  164. case LAYOUT_TOPBOTTOM:
  165. TAILQ_FOREACH(lcchild, &lc->cells, entry)
  166. layout_assign(wp, lcchild);
  167. return;
  168. }
  169. }
  170. /* Construct a cell from all or part of a layout tree. */
  171. struct layout_cell *
  172. layout_construct(struct layout_cell *lcparent, const char **layout)
  173. {
  174. struct layout_cell *lc, *lcchild;
  175. u_int sx, sy, xoff, yoff;
  176. const char *saved;
  177. if (!isdigit((u_char) **layout))
  178. return (NULL);
  179. if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4)
  180. return (NULL);
  181. while (isdigit((u_char) **layout))
  182. (*layout)++;
  183. if (**layout != 'x')
  184. return (NULL);
  185. (*layout)++;
  186. while (isdigit((u_char) **layout))
  187. (*layout)++;
  188. if (**layout != ',')
  189. return (NULL);
  190. (*layout)++;
  191. while (isdigit((u_char) **layout))
  192. (*layout)++;
  193. if (**layout != ',')
  194. return (NULL);
  195. (*layout)++;
  196. while (isdigit((u_char) **layout))
  197. (*layout)++;
  198. if (**layout == ',') {
  199. saved = *layout;
  200. (*layout)++;
  201. while (isdigit((u_char) **layout))
  202. (*layout)++;
  203. if (**layout == 'x')
  204. *layout = saved;
  205. }
  206. lc = layout_create_cell(lcparent);
  207. lc->sx = sx;
  208. lc->sy = sy;
  209. lc->xoff = xoff;
  210. lc->yoff = yoff;
  211. switch (**layout) {
  212. case ',':
  213. case '}':
  214. case ']':
  215. case '\0':
  216. return (lc);
  217. case '{':
  218. lc->type = LAYOUT_LEFTRIGHT;
  219. break;
  220. case '[':
  221. lc->type = LAYOUT_TOPBOTTOM;
  222. break;
  223. default:
  224. goto fail;
  225. }
  226. do {
  227. (*layout)++;
  228. lcchild = layout_construct(lc, layout);
  229. if (lcchild == NULL)
  230. goto fail;
  231. TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry);
  232. } while (**layout == ',');
  233. switch (lc->type) {
  234. case LAYOUT_LEFTRIGHT:
  235. if (**layout != '}')
  236. goto fail;
  237. break;
  238. case LAYOUT_TOPBOTTOM:
  239. if (**layout != ']')
  240. goto fail;
  241. break;
  242. default:
  243. goto fail;
  244. }
  245. (*layout)++;
  246. return (lc);
  247. fail:
  248. layout_free_cell(lc);
  249. return (NULL);
  250. }