tmux  3.2a
About: tmux is a terminal multiplexer that lets you switch easily between several programs in one terminal.
  Fossies Dox: tmux-3.2a.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

layout-custom.c
Go to the documentation of this file.
1 /* $OpenBSD$ */
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 *
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[8192], *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 /* Check layout sizes fit. */
120 static int
122 {
123  struct layout_cell *lcchild;
124  u_int n = 0;
125 
126  switch (lc->type) {
127  case LAYOUT_WINDOWPANE:
128  break;
129  case LAYOUT_LEFTRIGHT:
130  TAILQ_FOREACH(lcchild, &lc->cells, entry) {
131  if (lcchild->sy != lc->sy)
132  return (0);
133  if (!layout_check(lcchild))
134  return (0);
135  n += lcchild->sx + 1;
136  }
137  if (n - 1 != lc->sx)
138  return (0);
139  break;
140  case LAYOUT_TOPBOTTOM:
141  TAILQ_FOREACH(lcchild, &lc->cells, entry) {
142  if (lcchild->sx != lc->sx)
143  return (0);
144  if (!layout_check(lcchild))
145  return (0);
146  n += lcchild->sy + 1;
147  }
148  if (n - 1 != lc->sy)
149  return (0);
150  break;
151  }
152  return (1);
153 }
154 
155 /* Parse a layout string and arrange window as layout. */
156 int
157 layout_parse(struct window *w, const char *layout)
158 {
159  struct layout_cell *lc, *lcchild;
160  struct window_pane *wp;
161  u_int npanes, ncells, sx = 0, sy = 0;
162  u_short csum;
163 
164  /* Check validity. */
165  if (sscanf(layout, "%hx,", &csum) != 1)
166  return (-1);
167  layout += 5;
168  if (csum != layout_checksum(layout))
169  return (-1);
170 
171  /* Build the layout. */
172  lc = layout_construct(NULL, &layout);
173  if (lc == NULL)
174  return (-1);
175  if (*layout != '\0')
176  goto fail;
177 
178  /* Check this window will fit into the layout. */
179  for (;;) {
180  npanes = window_count_panes(w);
181  ncells = layout_count_cells(lc);
182  if (npanes > ncells)
183  goto fail;
184  if (npanes == ncells)
185  break;
186 
187  /* Fewer panes than cells - close the bottom right. */
188  lcchild = layout_find_bottomright(lc);
189  layout_destroy_cell(w, lcchild, &lc);
190  }
191 
192  /*
193  * It appears older versions of tmux were able to generate layouts with
194  * an incorrect top cell size - if it is larger than the top child then
195  * correct that (if this is still wrong the check code will catch it).
196  */
197  switch (lc->type) {
198  case LAYOUT_WINDOWPANE:
199  break;
200  case LAYOUT_LEFTRIGHT:
201  TAILQ_FOREACH(lcchild, &lc->cells, entry) {
202  sy = lcchild->sy + 1;
203  sx += lcchild->sx + 1;
204  }
205  break;
206  case LAYOUT_TOPBOTTOM:
207  TAILQ_FOREACH(lcchild, &lc->cells, entry) {
208  sx = lcchild->sx + 1;
209  sy += lcchild->sy + 1;
210  }
211  break;
212  }
213  if (lc->type != LAYOUT_WINDOWPANE && (lc->sx != sx || lc->sy != sy)) {
214  log_debug("fix layout %u,%u to %u,%u", lc->sx, lc->sy, sx,sy);
215  layout_print_cell(lc, __func__, 0);
216  lc->sx = sx - 1; lc->sy = sy - 1;
217  }
218 
219  /* Check the new layout. */
220  if (!layout_check(lc))
221  return (-1);
222 
223  /* Resize to the layout size. */
224  window_resize(w, lc->sx, lc->sy, -1, -1);
225 
226  /* Destroy the old layout and swap to the new. */
228  w->layout_root = lc;
229 
230  /* Assign the panes into the cells. */
231  wp = TAILQ_FIRST(&w->panes);
232  layout_assign(&wp, lc);
233 
234  /* Update pane offsets and sizes. */
236  layout_fix_panes(w, NULL);
238 
239  layout_print_cell(lc, __func__, 0);
240 
241  notify_window("window-layout-changed", w);
242 
243  return (0);
244 
245 fail:
246  layout_free_cell(lc);
247  return (-1);
248 }
249 
250 /* Assign panes into cells. */
251 static void
252 layout_assign(struct window_pane **wp, struct layout_cell *lc)
253 {
254  struct layout_cell *lcchild;
255 
256  switch (lc->type) {
257  case LAYOUT_WINDOWPANE:
258  layout_make_leaf(lc, *wp);
259  *wp = TAILQ_NEXT(*wp, entry);
260  return;
261  case LAYOUT_LEFTRIGHT:
262  case LAYOUT_TOPBOTTOM:
263  TAILQ_FOREACH(lcchild, &lc->cells, entry)
264  layout_assign(wp, lcchild);
265  return;
266  }
267 }
268 
269 /* Construct a cell from all or part of a layout tree. */
270 static struct layout_cell *
271 layout_construct(struct layout_cell *lcparent, const char **layout)
272 {
273  struct layout_cell *lc, *lcchild;
274  u_int sx, sy, xoff, yoff;
275  const char *saved;
276 
277  if (!isdigit((u_char) **layout))
278  return (NULL);
279  if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4)
280  return (NULL);
281 
282  while (isdigit((u_char) **layout))
283  (*layout)++;
284  if (**layout != 'x')
285  return (NULL);
286  (*layout)++;
287  while (isdigit((u_char) **layout))
288  (*layout)++;
289  if (**layout != ',')
290  return (NULL);
291  (*layout)++;
292  while (isdigit((u_char) **layout))
293  (*layout)++;
294  if (**layout != ',')
295  return (NULL);
296  (*layout)++;
297  while (isdigit((u_char) **layout))
298  (*layout)++;
299  if (**layout == ',') {
300  saved = *layout;
301  (*layout)++;
302  while (isdigit((u_char) **layout))
303  (*layout)++;
304  if (**layout == 'x')
305  *layout = saved;
306  }
307 
308  lc = layout_create_cell(lcparent);
309  lc->sx = sx;
310  lc->sy = sy;
311  lc->xoff = xoff;
312  lc->yoff = yoff;
313 
314  switch (**layout) {
315  case ',':
316  case '}':
317  case ']':
318  case '\0':
319  return (lc);
320  case '{':
321  lc->type = LAYOUT_LEFTRIGHT;
322  break;
323  case '[':
324  lc->type = LAYOUT_TOPBOTTOM;
325  break;
326  default:
327  goto fail;
328  }
329 
330  do {
331  (*layout)++;
332  lcchild = layout_construct(lc, layout);
333  if (lcchild == NULL)
334  goto fail;
335  TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry);
336  } while (**layout == ',');
337 
338  switch (lc->type) {
339  case LAYOUT_LEFTRIGHT:
340  if (**layout != '}')
341  goto fail;
342  break;
343  case LAYOUT_TOPBOTTOM:
344  if (**layout != ']')
345  goto fail;
346  break;
347  default:
348  goto fail;
349  }
350  (*layout)++;
351 
352  return (lc);
353 
354 fail:
355  layout_free_cell(lc);
356  return (NULL);
357 }
size_t strlcat(char *, const char *, size_t)
static void layout_assign(struct window_pane **, struct layout_cell *)
static struct layout_cell * layout_construct(struct layout_cell *, const char **)
static u_short layout_checksum(const char *)
Definition: layout-custom.c:47
static struct layout_cell * layout_find_bottomright(struct layout_cell *)
Definition: layout-custom.c:37
int layout_parse(struct window *w, const char *layout)
char * layout_dump(struct layout_cell *root)
Definition: layout-custom.c:61
static int layout_append(struct layout_cell *, char *, size_t)
Definition: layout-custom.c:75
static int layout_check(struct layout_cell *lc)
void layout_destroy_cell(struct window *w, struct layout_cell *lc, struct layout_cell **lcroot)
Definition: layout.c:428
void layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
Definition: layout.c:96
void layout_free_cell(struct layout_cell *lc)
Definition: layout.c:73
struct layout_cell * layout_create_cell(struct layout_cell *lcparent)
Definition: layout.c:51
void layout_fix_offsets(struct window *w)
Definition: layout.c:231
void layout_fix_panes(struct window *w, struct window_pane *skip)
Definition: layout.c:289
u_int layout_count_cells(struct layout_cell *lc)
Definition: layout.c:314
void layout_make_leaf(struct layout_cell *lc, struct window_pane *wp)
Definition: layout.c:177
void log_debug(const char *msg,...)
Definition: log.c:130
void notify_window(const char *name, struct window *w)
Definition: notify.c:253
void recalculate_sizes(void)
Definition: resize.c:355
struct window_pane * wp
Definition: tmux.h:1153
u_int xoff
Definition: tmux.h:1150
u_int sx
Definition: tmux.h:1147
u_int sy
Definition: tmux.h:1148
u_int yoff
Definition: tmux.h:1151
enum layout_type type
Definition: tmux.h:1143
struct layout_cells cells
Definition: tmux.h:1154
u_int sy
Definition: tmux.h:969
u_int sx
Definition: tmux.h:968
u_int id
Definition: tmux.h:959
Definition: tmux.h:1041
struct layout_cell * layout_root
Definition: tmux.h:1059
struct window_panes panes
Definition: tmux.h:1056
@ LAYOUT_TOPBOTTOM
Definition: tmux.h:1134
@ LAYOUT_LEFTRIGHT
Definition: tmux.h:1133
@ LAYOUT_WINDOWPANE
Definition: tmux.h:1135
u_int window_count_panes(struct window *)
Definition: window.c:773
void window_resize(struct window *, u_int, u_int, int, int)
Definition: window.c:409
int xsnprintf(char *str, size_t len, const char *fmt,...)
Definition: xmalloc.c:135
int xasprintf(char **ret, const char *fmt,...)
Definition: xmalloc.c:109