Coverage Report

Created: 2025-08-24 07:01

/src/tmux/layout.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD$ */
2
3
/*
4
 * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
5
 * Copyright (c) 2016 Stephen Kent <smkent@smkent.net>
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 */
19
20
#include <sys/types.h>
21
22
#include <stdlib.h>
23
24
#include "tmux.h"
25
26
/*
27
 * The window layout is a tree of cells each of which can be one of: a
28
 * left-right container for a list of cells, a top-bottom container for a list
29
 * of cells, or a container for a window pane.
30
 *
31
 * Each window has a pointer to the root of its layout tree (containing its
32
 * panes), every pane has a pointer back to the cell containing it, and each
33
 * cell a pointer to its parent cell.
34
 */
35
36
static u_int  layout_resize_check(struct window *, struct layout_cell *,
37
        enum layout_type);
38
static int  layout_resize_pane_grow(struct window *, struct layout_cell *,
39
        enum layout_type, int, int);
40
static int  layout_resize_pane_shrink(struct window *, struct layout_cell *,
41
        enum layout_type, int);
42
static u_int  layout_new_pane_size(struct window *, u_int,
43
        struct layout_cell *, enum layout_type, u_int, u_int,
44
        u_int);
45
static int  layout_set_size_check(struct window *, struct layout_cell *,
46
        enum layout_type, int);
47
static void layout_resize_child_cells(struct window *,
48
        struct layout_cell *);
49
50
struct layout_cell *
51
layout_create_cell(struct layout_cell *lcparent)
52
0
{
53
0
  struct layout_cell  *lc;
54
55
0
  lc = xmalloc(sizeof *lc);
56
0
  lc->type = LAYOUT_WINDOWPANE;
57
0
  lc->parent = lcparent;
58
59
0
  TAILQ_INIT(&lc->cells);
60
61
0
  lc->sx = UINT_MAX;
62
0
  lc->sy = UINT_MAX;
63
64
0
  lc->xoff = UINT_MAX;
65
0
  lc->yoff = UINT_MAX;
66
67
0
  lc->wp = NULL;
68
69
0
  return (lc);
70
0
}
71
72
void
73
layout_free_cell(struct layout_cell *lc)
74
0
{
75
0
  struct layout_cell  *lcchild;
76
77
0
  switch (lc->type) {
78
0
  case LAYOUT_LEFTRIGHT:
79
0
  case LAYOUT_TOPBOTTOM:
80
0
    while (!TAILQ_EMPTY(&lc->cells)) {
81
0
      lcchild = TAILQ_FIRST(&lc->cells);
82
0
      TAILQ_REMOVE(&lc->cells, lcchild, entry);
83
0
      layout_free_cell(lcchild);
84
0
    }
85
0
    break;
86
0
  case LAYOUT_WINDOWPANE:
87
0
    if (lc->wp != NULL)
88
0
      lc->wp->layout_cell = NULL;
89
0
    break;
90
0
  }
91
92
0
  free(lc);
93
0
}
94
95
void
96
layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
97
0
{
98
0
  struct layout_cell  *lcchild;
99
0
  const char    *type;
100
101
0
  switch (lc->type) {
102
0
  case LAYOUT_LEFTRIGHT:
103
0
    type = "LEFTRIGHT";
104
0
    break;
105
0
  case LAYOUT_TOPBOTTOM:
106
0
    type = "TOPBOTTOM";
107
0
    break;
108
0
  case LAYOUT_WINDOWPANE:
109
0
    type = "WINDOWPANE";
110
0
    break;
111
0
  default:
112
0
    type = "UNKNOWN";
113
0
    break;
114
0
  }
115
0
  log_debug("%s:%*s%p type %s [parent %p] wp=%p [%u,%u %ux%u]", hdr, n,
116
0
      " ", lc, type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx,
117
0
      lc->sy);
118
0
  switch (lc->type) {
119
0
  case LAYOUT_LEFTRIGHT:
120
0
  case LAYOUT_TOPBOTTOM:
121
0
    TAILQ_FOREACH(lcchild, &lc->cells, entry)
122
0
          layout_print_cell(lcchild, hdr, n + 1);
123
0
    break;
124
0
  case LAYOUT_WINDOWPANE:
125
0
    break;
126
0
  }
127
0
}
128
129
struct layout_cell *
130
layout_search_by_border(struct layout_cell *lc, u_int x, u_int y)
131
0
{
132
0
  struct layout_cell  *lcchild, *last = NULL;
133
134
0
  TAILQ_FOREACH(lcchild, &lc->cells, entry) {
135
0
    if (x >= lcchild->xoff && x < lcchild->xoff + lcchild->sx &&
136
0
        y >= lcchild->yoff && y < lcchild->yoff + lcchild->sy) {
137
      /* Inside the cell - recurse. */
138
0
      return (layout_search_by_border(lcchild, x, y));
139
0
    }
140
141
0
    if (last == NULL) {
142
0
      last = lcchild;
143
0
      continue;
144
0
    }
145
146
0
    switch (lc->type) {
147
0
    case LAYOUT_LEFTRIGHT:
148
0
      if (x < lcchild->xoff && x >= last->xoff + last->sx)
149
0
        return (last);
150
0
      break;
151
0
    case LAYOUT_TOPBOTTOM:
152
0
      if (y < lcchild->yoff && y >= last->yoff + last->sy)
153
0
        return (last);
154
0
      break;
155
0
    case LAYOUT_WINDOWPANE:
156
0
      break;
157
0
    }
158
159
0
    last = lcchild;
160
0
  }
161
162
0
  return (NULL);
163
0
}
164
165
void
166
layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff,
167
    u_int yoff)
168
0
{
169
0
  lc->sx = sx;
170
0
  lc->sy = sy;
171
172
0
  lc->xoff = xoff;
173
0
  lc->yoff = yoff;
174
0
}
175
176
void
177
layout_make_leaf(struct layout_cell *lc, struct window_pane *wp)
178
0
{
179
0
  lc->type = LAYOUT_WINDOWPANE;
180
181
0
  TAILQ_INIT(&lc->cells);
182
183
0
  wp->layout_cell = lc;
184
0
  lc->wp = wp;
185
0
}
186
187
void
188
layout_make_node(struct layout_cell *lc, enum layout_type type)
189
0
{
190
0
  if (type == LAYOUT_WINDOWPANE)
191
0
    fatalx("bad layout type");
192
0
  lc->type = type;
193
194
0
  TAILQ_INIT(&lc->cells);
195
196
0
  if (lc->wp != NULL)
197
0
    lc->wp->layout_cell = NULL;
198
0
  lc->wp = NULL;
199
0
}
200
201
/* Fix cell offsets for a child cell. */
202
static void
203
layout_fix_offsets1(struct layout_cell *lc)
204
0
{
205
0
  struct layout_cell  *lcchild;
206
0
  u_int      xoff, yoff;
207
208
0
  if (lc->type == LAYOUT_LEFTRIGHT) {
209
0
    xoff = lc->xoff;
210
0
    TAILQ_FOREACH(lcchild, &lc->cells, entry) {
211
0
      lcchild->xoff = xoff;
212
0
      lcchild->yoff = lc->yoff;
213
0
      if (lcchild->type != LAYOUT_WINDOWPANE)
214
0
        layout_fix_offsets1(lcchild);
215
0
      xoff += lcchild->sx + 1;
216
0
    }
217
0
  } else {
218
0
    yoff = lc->yoff;
219
0
    TAILQ_FOREACH(lcchild, &lc->cells, entry) {
220
0
      lcchild->xoff = lc->xoff;
221
0
      lcchild->yoff = yoff;
222
0
      if (lcchild->type != LAYOUT_WINDOWPANE)
223
0
        layout_fix_offsets1(lcchild);
224
0
      yoff += lcchild->sy + 1;
225
0
    }
226
0
  }
227
0
}
228
229
/* Update cell offsets based on their sizes. */
230
void
231
layout_fix_offsets(struct window *w)
232
0
{
233
0
  struct layout_cell      *lc = w->layout_root;
234
235
0
  lc->xoff = 0;
236
0
  lc->yoff = 0;
237
238
0
  layout_fix_offsets1(lc);
239
0
}
240
241
/* Is this a top cell? */
242
static int
243
layout_cell_is_top(struct window *w, struct layout_cell *lc)
244
0
{
245
0
  struct layout_cell  *next;
246
247
0
  while (lc != w->layout_root) {
248
0
    next = lc->parent;
249
0
    if (next->type == LAYOUT_TOPBOTTOM &&
250
0
        lc != TAILQ_FIRST(&next->cells))
251
0
      return (0);
252
0
    lc = next;
253
0
  }
254
0
  return (1);
255
0
}
256
257
/* Is this a bottom cell? */
258
static int
259
layout_cell_is_bottom(struct window *w, struct layout_cell *lc)
260
0
{
261
0
  struct layout_cell  *next;
262
263
0
  while (lc != w->layout_root) {
264
0
    next = lc->parent;
265
0
    if (next->type == LAYOUT_TOPBOTTOM &&
266
0
        lc != TAILQ_LAST(&next->cells, layout_cells))
267
0
      return (0);
268
0
    lc = next;
269
0
  }
270
0
  return (1);
271
0
}
272
273
/*
274
 * Returns 1 if we need to add an extra line for the pane status line. This is
275
 * the case for the most upper or lower panes only.
276
 */
277
static int
278
layout_add_horizontal_border(struct window *w, struct layout_cell *lc,
279
    int status)
280
0
{
281
0
  if (status == PANE_STATUS_TOP)
282
0
    return (layout_cell_is_top(w, lc));
283
0
  if (status == PANE_STATUS_BOTTOM)
284
0
    return (layout_cell_is_bottom(w, lc));
285
0
  return (0);
286
0
}
287
288
/* Update pane offsets and sizes based on their cells. */
289
void
290
layout_fix_panes(struct window *w, struct window_pane *skip)
291
1.73k
{
292
1.73k
  struct window_pane  *wp;
293
1.73k
  struct layout_cell  *lc;
294
1.73k
  int      status, scrollbars, sb_pos, sb_w, sb_pad;
295
1.73k
  u_int      sx, sy;
296
297
1.73k
  status = options_get_number(w->options, "pane-border-status");
298
1.73k
  scrollbars = options_get_number(w->options, "pane-scrollbars");
299
1.73k
  sb_pos = options_get_number(w->options, "pane-scrollbars-position");
300
301
1.73k
  TAILQ_FOREACH(wp, &w->panes, entry) {
302
1.73k
    if ((lc = wp->layout_cell) == NULL || wp == skip)
303
1.73k
      continue;
304
305
0
    wp->xoff = lc->xoff;
306
0
    wp->yoff = lc->yoff;
307
0
    sx = lc->sx;
308
0
    sy = lc->sy;
309
310
0
    if (layout_add_horizontal_border(w, lc, status)) {
311
0
      if (status == PANE_STATUS_TOP)
312
0
        wp->yoff++;
313
0
      sy--;
314
0
    }
315
316
0
    if (window_pane_show_scrollbar(wp, scrollbars)) {
317
0
      sb_w = wp->scrollbar_style.width;
318
0
      sb_pad = wp->scrollbar_style.pad;
319
0
      if (sb_w < 1)
320
0
        sb_w = 1;
321
0
      if (sb_pad < 0)
322
0
        sb_pad = 0;
323
0
      if (sb_pos == PANE_SCROLLBARS_LEFT) {
324
0
        if ((int)sx - sb_w < PANE_MINIMUM) {
325
0
          wp->xoff = wp->xoff +
326
0
              (int)sx - PANE_MINIMUM;
327
0
          sx = PANE_MINIMUM;
328
0
        } else {
329
0
          sx = sx - sb_w - sb_pad;
330
0
          wp->xoff = wp->xoff + sb_w + sb_pad;
331
0
        }
332
0
      } else /* sb_pos == PANE_SCROLLBARS_RIGHT */
333
0
        if ((int)sx - sb_w - sb_pad < PANE_MINIMUM)
334
0
          sx = PANE_MINIMUM;
335
0
        else
336
0
          sx = sx - sb_w - sb_pad;
337
0
      wp->flags |= PANE_REDRAWSCROLLBAR;
338
0
    }
339
340
0
    window_pane_resize(wp, sx, sy);
341
0
  }
342
1.73k
}
343
344
/* Count the number of available cells in a layout. */
345
u_int
346
layout_count_cells(struct layout_cell *lc)
347
0
{
348
0
  struct layout_cell  *lcchild;
349
0
  u_int      count;
350
351
0
  switch (lc->type) {
352
0
  case LAYOUT_WINDOWPANE:
353
0
    return (1);
354
0
  case LAYOUT_LEFTRIGHT:
355
0
  case LAYOUT_TOPBOTTOM:
356
0
    count = 0;
357
0
    TAILQ_FOREACH(lcchild, &lc->cells, entry)
358
0
      count += layout_count_cells(lcchild);
359
0
    return (count);
360
0
  default:
361
0
    fatalx("bad layout type");
362
0
  }
363
0
}
364
365
/* Calculate how much size is available to be removed from a cell. */
366
static u_int
367
layout_resize_check(struct window *w, struct layout_cell *lc,
368
    enum layout_type type)
369
0
{
370
0
  struct layout_cell  *lcchild;
371
0
  struct style    *sb_style = &w->active->scrollbar_style;
372
0
  u_int      available, minimum;
373
0
  int      status, scrollbars;
374
375
0
  status = options_get_number(w->options, "pane-border-status");
376
0
  scrollbars = options_get_number(w->options, "pane-scrollbars");
377
378
0
  if (lc->type == LAYOUT_WINDOWPANE) {
379
    /* Space available in this cell only. */
380
0
    if (type == LAYOUT_LEFTRIGHT) {
381
0
      available = lc->sx;
382
0
      if (scrollbars)
383
0
        minimum = PANE_MINIMUM + sb_style->width +
384
0
            sb_style->pad;
385
0
      else
386
0
        minimum = PANE_MINIMUM;
387
0
    } else {
388
0
      available = lc->sy;
389
0
      if (layout_add_horizontal_border(w, lc, status))
390
0
        minimum = PANE_MINIMUM + 1;
391
0
      else
392
0
        minimum = PANE_MINIMUM;
393
0
    }
394
0
    if (available > minimum)
395
0
      available -= minimum;
396
0
    else
397
0
      available = 0;
398
0
  } else if (lc->type == type) {
399
    /* Same type: total of available space in all child cells. */
400
0
    available = 0;
401
0
    TAILQ_FOREACH(lcchild, &lc->cells, entry)
402
0
      available += layout_resize_check(w, lcchild, type);
403
0
  } else {
404
    /* Different type: minimum of available space in child cells. */
405
0
    minimum = UINT_MAX;
406
0
    TAILQ_FOREACH(lcchild, &lc->cells, entry) {
407
0
      available = layout_resize_check(w, lcchild, type);
408
0
      if (available < minimum)
409
0
        minimum = available;
410
0
    }
411
0
    available = minimum;
412
0
  }
413
414
0
  return (available);
415
0
}
416
417
/*
418
 * Adjust cell size evenly, including altering its children. This function
419
 * expects the change to have already been bounded to the space available.
420
 */
421
void
422
layout_resize_adjust(struct window *w, struct layout_cell *lc,
423
    enum layout_type type, int change)
424
0
{
425
0
  struct layout_cell  *lcchild;
426
427
  /* Adjust the cell size. */
428
0
  if (type == LAYOUT_LEFTRIGHT)
429
0
    lc->sx += change;
430
0
  else
431
0
    lc->sy += change;
432
433
  /* If this is a leaf cell, that is all that is necessary. */
434
0
  if (type == LAYOUT_WINDOWPANE)
435
0
    return;
436
437
  /* Child cell runs in a different direction. */
438
0
  if (lc->type != type) {
439
0
    TAILQ_FOREACH(lcchild, &lc->cells, entry)
440
0
      layout_resize_adjust(w, lcchild, type, change);
441
0
    return;
442
0
  }
443
444
  /*
445
   * Child cell runs in the same direction. Adjust each child equally
446
   * until no further change is possible.
447
   */
448
0
  while (change != 0) {
449
0
    TAILQ_FOREACH(lcchild, &lc->cells, entry) {
450
0
      if (change == 0)
451
0
        break;
452
0
      if (change > 0) {
453
0
        layout_resize_adjust(w, lcchild, type, 1);
454
0
        change--;
455
0
        continue;
456
0
      }
457
0
      if (layout_resize_check(w, lcchild, type) > 0) {
458
0
        layout_resize_adjust(w, lcchild, type, -1);
459
0
        change++;
460
0
      }
461
0
    }
462
0
  }
463
0
}
464
465
/* Destroy a cell and redistribute the space. */
466
void
467
layout_destroy_cell(struct window *w, struct layout_cell *lc,
468
    struct layout_cell **lcroot)
469
0
{
470
0
  struct layout_cell     *lcother, *lcparent;
471
472
  /*
473
   * If no parent, this is the last pane so window close is imminent and
474
   * there is no need to resize anything.
475
   */
476
0
  lcparent = lc->parent;
477
0
  if (lcparent == NULL) {
478
0
    layout_free_cell(lc);
479
0
    *lcroot = NULL;
480
0
    return;
481
0
  }
482
483
  /* Merge the space into the previous or next cell. */
484
0
  if (lc == TAILQ_FIRST(&lcparent->cells))
485
0
    lcother = TAILQ_NEXT(lc, entry);
486
0
  else
487
0
    lcother = TAILQ_PREV(lc, layout_cells, entry);
488
0
  if (lcother != NULL && lcparent->type == LAYOUT_LEFTRIGHT)
489
0
    layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1);
490
0
  else if (lcother != NULL)
491
0
    layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1);
492
493
  /* Remove this from the parent's list. */
494
0
  TAILQ_REMOVE(&lcparent->cells, lc, entry);
495
0
  layout_free_cell(lc);
496
497
  /*
498
   * If the parent now has one cell, remove the parent from the tree and
499
   * replace it by that cell.
500
   */
501
0
  lc = TAILQ_FIRST(&lcparent->cells);
502
0
  if (TAILQ_NEXT(lc, entry) == NULL) {
503
0
    TAILQ_REMOVE(&lcparent->cells, lc, entry);
504
505
0
    lc->parent = lcparent->parent;
506
0
    if (lc->parent == NULL) {
507
0
      lc->xoff = 0; lc->yoff = 0;
508
0
      *lcroot = lc;
509
0
    } else
510
0
      TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
511
512
0
    layout_free_cell(lcparent);
513
0
  }
514
0
}
515
516
void
517
layout_init(struct window *w, struct window_pane *wp)
518
0
{
519
0
  struct layout_cell  *lc;
520
521
0
  lc = w->layout_root = layout_create_cell(NULL);
522
0
  layout_set_size(lc, w->sx, w->sy, 0, 0);
523
0
  layout_make_leaf(lc, wp);
524
0
  layout_fix_panes(w, NULL);
525
0
}
526
527
void
528
layout_free(struct window *w)
529
0
{
530
0
  layout_free_cell(w->layout_root);
531
0
}
532
533
/* Resize the entire layout after window resize. */
534
void
535
layout_resize(struct window *w, u_int sx, u_int sy)
536
0
{
537
0
  struct layout_cell  *lc = w->layout_root;
538
0
  int      xlimit, ylimit, xchange, ychange;
539
540
  /*
541
   * Adjust horizontally. Do not attempt to reduce the layout lower than
542
   * the minimum (more than the amount returned by layout_resize_check).
543
   *
544
   * This can mean that the window size is smaller than the total layout
545
   * size: redrawing this is handled at a higher level, but it does leave
546
   * a problem with growing the window size here: if the current size is
547
   * < the minimum, growing proportionately by adding to each pane is
548
   * wrong as it would keep the layout size larger than the window size.
549
   * Instead, spread the difference between the minimum and the new size
550
   * out proportionately - this should leave the layout fitting the new
551
   * window size.
552
   */
553
0
  xchange = sx - lc->sx;
554
0
  xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
555
0
  if (xchange < 0 && xchange < -xlimit)
556
0
    xchange = -xlimit;
557
0
  if (xlimit == 0) {
558
0
    if (sx <= lc->sx) /* lc->sx is minimum possible */
559
0
      xchange = 0;
560
0
    else
561
0
      xchange = sx - lc->sx;
562
0
  }
563
0
  if (xchange != 0)
564
0
    layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, xchange);
565
566
  /* Adjust vertically in a similar fashion. */
567
0
  ychange = sy - lc->sy;
568
0
  ylimit = layout_resize_check(w, lc, LAYOUT_TOPBOTTOM);
569
0
  if (ychange < 0 && ychange < -ylimit)
570
0
    ychange = -ylimit;
571
0
  if (ylimit == 0) {
572
0
    if (sy <= lc->sy) /* lc->sy is minimum possible */
573
0
      ychange = 0;
574
0
    else
575
0
      ychange = sy - lc->sy;
576
0
  }
577
0
  if (ychange != 0)
578
0
    layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange);
579
580
  /* Fix cell offsets. */
581
0
  layout_fix_offsets(w);
582
0
  layout_fix_panes(w, NULL);
583
0
}
584
585
/* Resize a pane to an absolute size. */
586
void
587
layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
588
    u_int new_size)
589
0
{
590
0
  struct layout_cell     *lc, *lcparent;
591
0
  int     change, size;
592
593
0
  lc = wp->layout_cell;
594
595
  /* Find next parent of the same type. */
596
0
  lcparent = lc->parent;
597
0
  while (lcparent != NULL && lcparent->type != type) {
598
0
    lc = lcparent;
599
0
    lcparent = lc->parent;
600
0
  }
601
0
  if (lcparent == NULL)
602
0
    return;
603
604
  /* Work out the size adjustment. */
605
0
  if (type == LAYOUT_LEFTRIGHT)
606
0
    size = lc->sx;
607
0
  else
608
0
    size = lc->sy;
609
0
  if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
610
0
    change = size - new_size;
611
0
  else
612
0
    change = new_size - size;
613
614
  /* Resize the pane. */
615
0
  layout_resize_pane(wp, type, change, 1);
616
0
}
617
618
void
619
layout_resize_layout(struct window *w, struct layout_cell *lc,
620
    enum layout_type type, int change, int opposite)
621
0
{
622
0
  int needed, size;
623
624
  /* Grow or shrink the cell. */
625
0
  needed = change;
626
0
  while (needed != 0) {
627
0
    if (change > 0) {
628
0
      size = layout_resize_pane_grow(w, lc, type, needed,
629
0
          opposite);
630
0
      needed -= size;
631
0
    } else {
632
0
      size = layout_resize_pane_shrink(w, lc, type, needed);
633
0
      needed += size;
634
0
    }
635
636
0
    if (size == 0) /* no more change possible */
637
0
      break;
638
0
  }
639
640
  /* Fix cell offsets. */
641
0
  layout_fix_offsets(w);
642
0
  layout_fix_panes(w, NULL);
643
0
  notify_window("window-layout-changed", w);
644
0
}
645
646
/* Resize a single pane within the layout. */
647
void
648
layout_resize_pane(struct window_pane *wp, enum layout_type type, int change,
649
    int opposite)
650
0
{
651
0
  struct layout_cell  *lc, *lcparent;
652
653
0
  lc = wp->layout_cell;
654
655
  /* Find next parent of the same type. */
656
0
  lcparent = lc->parent;
657
0
  while (lcparent != NULL && lcparent->type != type) {
658
0
    lc = lcparent;
659
0
    lcparent = lc->parent;
660
0
  }
661
0
  if (lcparent == NULL)
662
0
    return;
663
664
  /* If this is the last cell, move back one. */
665
0
  if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
666
0
    lc = TAILQ_PREV(lc, layout_cells, entry);
667
668
0
  layout_resize_layout(wp->window, lc, type, change, opposite);
669
0
}
670
671
/* Helper function to grow pane. */
672
static int
673
layout_resize_pane_grow(struct window *w, struct layout_cell *lc,
674
    enum layout_type type, int needed, int opposite)
675
0
{
676
0
  struct layout_cell  *lcadd, *lcremove;
677
0
  u_int      size = 0;
678
679
  /* Growing. Always add to the current cell. */
680
0
  lcadd = lc;
681
682
  /* Look towards the tail for a suitable cell for reduction. */
683
0
  lcremove = TAILQ_NEXT(lc, entry);
684
0
  while (lcremove != NULL) {
685
0
    size = layout_resize_check(w, lcremove, type);
686
0
    if (size > 0)
687
0
      break;
688
0
    lcremove = TAILQ_NEXT(lcremove, entry);
689
0
  }
690
691
  /* If none found, look towards the head. */
692
0
  if (opposite && lcremove == NULL) {
693
0
    lcremove = TAILQ_PREV(lc, layout_cells, entry);
694
0
    while (lcremove != NULL) {
695
0
      size = layout_resize_check(w, lcremove, type);
696
0
      if (size > 0)
697
0
        break;
698
0
      lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
699
0
    }
700
0
  }
701
0
  if (lcremove == NULL)
702
0
    return (0);
703
704
  /* Change the cells. */
705
0
  if (size > (u_int) needed)
706
0
    size = needed;
707
0
  layout_resize_adjust(w, lcadd, type, size);
708
0
  layout_resize_adjust(w, lcremove, type, -size);
709
0
  return (size);
710
0
}
711
712
/* Helper function to shrink pane. */
713
static int
714
layout_resize_pane_shrink(struct window *w, struct layout_cell *lc,
715
    enum layout_type type, int needed)
716
0
{
717
0
  struct layout_cell  *lcadd, *lcremove;
718
0
  u_int      size;
719
720
  /* Shrinking. Find cell to remove from by walking towards head. */
721
0
  lcremove = lc;
722
0
  do {
723
0
    size = layout_resize_check(w, lcremove, type);
724
0
    if (size != 0)
725
0
      break;
726
0
    lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
727
0
  } while (lcremove != NULL);
728
0
  if (lcremove == NULL)
729
0
    return (0);
730
731
  /* And add onto the next cell (from the original cell). */
732
0
  lcadd = TAILQ_NEXT(lc, entry);
733
0
  if (lcadd == NULL)
734
0
    return (0);
735
736
  /* Change the cells. */
737
0
  if (size > (u_int) -needed)
738
0
    size = -needed;
739
0
  layout_resize_adjust(w, lcadd, type, size);
740
0
  layout_resize_adjust(w, lcremove, type, -size);
741
0
  return (size);
742
0
}
743
744
/* Assign window pane to newly split cell. */
745
void
746
layout_assign_pane(struct layout_cell *lc, struct window_pane *wp,
747
    int do_not_resize)
748
0
{
749
0
  layout_make_leaf(lc, wp);
750
0
  if (do_not_resize)
751
0
    layout_fix_panes(wp->window, wp);
752
0
  else
753
0
    layout_fix_panes(wp->window, NULL);
754
0
}
755
756
/* Calculate the new pane size for resized parent. */
757
static u_int
758
layout_new_pane_size(struct window *w, u_int previous, struct layout_cell *lc,
759
    enum layout_type type, u_int size, u_int count_left, u_int size_left)
760
0
{
761
0
  u_int new_size, min, max, available;
762
763
  /* If this is the last cell, it can take all of the remaining size. */
764
0
  if (count_left == 1)
765
0
    return (size_left);
766
767
  /* How much is available in this parent? */
768
0
  available = layout_resize_check(w, lc, type);
769
770
  /*
771
   * Work out the minimum size of this cell and the new size
772
   * proportionate to the previous size.
773
   */
774
0
  min = (PANE_MINIMUM + 1) * (count_left - 1);
775
0
  if (type == LAYOUT_LEFTRIGHT) {
776
0
    if (lc->sx - available > min)
777
0
      min = lc->sx - available;
778
0
    new_size = (lc->sx * size) / previous;
779
0
  } else {
780
0
    if (lc->sy - available > min)
781
0
      min = lc->sy - available;
782
0
    new_size = (lc->sy * size) / previous;
783
0
  }
784
785
  /* Check against the maximum and minimum size. */
786
0
  max = size_left - min;
787
0
  if (new_size > max)
788
0
    new_size = max;
789
0
  if (new_size < PANE_MINIMUM)
790
0
    new_size = PANE_MINIMUM;
791
0
  return (new_size);
792
0
}
793
794
/* Check if the cell and all its children can be resized to a specific size. */
795
static int
796
layout_set_size_check(struct window *w, struct layout_cell *lc,
797
    enum layout_type type, int size)
798
0
{
799
0
  struct layout_cell  *lcchild;
800
0
  u_int      new_size, available, previous, count, idx;
801
802
  /* Cells with no children must just be bigger than minimum. */
803
0
  if (lc->type == LAYOUT_WINDOWPANE)
804
0
    return (size >= PANE_MINIMUM);
805
0
  available = size;
806
807
  /* Count number of children. */
808
0
  count = 0;
809
0
  TAILQ_FOREACH(lcchild, &lc->cells, entry)
810
0
    count++;
811
812
  /* Check new size will work for each child. */
813
0
  if (lc->type == type) {
814
0
    if (available < (count * 2) - 1)
815
0
      return (0);
816
817
0
    if (type == LAYOUT_LEFTRIGHT)
818
0
      previous = lc->sx;
819
0
    else
820
0
      previous = lc->sy;
821
822
0
    idx = 0;
823
0
    TAILQ_FOREACH(lcchild, &lc->cells, entry) {
824
0
      new_size = layout_new_pane_size(w, previous, lcchild,
825
0
          type, size, count - idx, available);
826
0
      if (idx == count - 1) {
827
0
        if (new_size > available)
828
0
          return (0);
829
0
        available -= new_size;
830
0
      } else {
831
0
        if (new_size + 1 > available)
832
0
          return (0);
833
0
        available -= new_size + 1;
834
0
      }
835
0
      if (!layout_set_size_check(w, lcchild, type, new_size))
836
0
        return (0);
837
0
      idx++;
838
0
    }
839
0
  } else {
840
0
    TAILQ_FOREACH(lcchild, &lc->cells, entry) {
841
0
      if (lcchild->type == LAYOUT_WINDOWPANE)
842
0
        continue;
843
0
      if (!layout_set_size_check(w, lcchild, type, size))
844
0
        return (0);
845
0
    }
846
0
  }
847
848
0
  return (1);
849
0
}
850
851
/* Resize all child cells to fit within the current cell. */
852
static void
853
layout_resize_child_cells(struct window *w, struct layout_cell *lc)
854
0
{
855
0
  struct layout_cell  *lcchild;
856
0
  u_int      previous, available, count, idx;
857
858
0
  if (lc->type == LAYOUT_WINDOWPANE)
859
0
    return;
860
861
  /* What is the current size used? */
862
0
  count = 0;
863
0
  previous = 0;
864
0
  TAILQ_FOREACH(lcchild, &lc->cells, entry) {
865
0
    count++;
866
0
    if (lc->type == LAYOUT_LEFTRIGHT)
867
0
      previous += lcchild->sx;
868
0
    else if (lc->type == LAYOUT_TOPBOTTOM)
869
0
      previous += lcchild->sy;
870
0
  }
871
0
  previous += (count - 1);
872
873
  /* And how much is available? */
874
0
  available = 0;
875
0
  if (lc->type == LAYOUT_LEFTRIGHT)
876
0
    available = lc->sx;
877
0
  else if (lc->type == LAYOUT_TOPBOTTOM)
878
0
    available = lc->sy;
879
880
  /* Resize children into the new size. */
881
0
  idx = 0;
882
0
  TAILQ_FOREACH(lcchild, &lc->cells, entry) {
883
0
    if (lc->type == LAYOUT_TOPBOTTOM) {
884
0
      lcchild->sx = lc->sx;
885
0
      lcchild->xoff = lc->xoff;
886
0
    } else {
887
0
      lcchild->sx = layout_new_pane_size(w, previous, lcchild,
888
0
          lc->type, lc->sx, count - idx, available);
889
0
      available -= (lcchild->sx + 1);
890
0
    }
891
0
    if (lc->type == LAYOUT_LEFTRIGHT)
892
0
      lcchild->sy = lc->sy;
893
0
    else {
894
0
      lcchild->sy = layout_new_pane_size(w, previous, lcchild,
895
0
          lc->type, lc->sy, count - idx, available);
896
0
      available -= (lcchild->sy + 1);
897
0
    }
898
0
    layout_resize_child_cells(w, lcchild);
899
0
    idx++;
900
0
  }
901
0
}
902
903
/*
904
 * Split a pane into two. size is a hint, or -1 for default half/half
905
 * split. This must be followed by layout_assign_pane before much else happens!
906
 */
907
struct layout_cell *
908
layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
909
    int flags)
910
0
{
911
0
  struct layout_cell  *lc, *lcparent, *lcnew, *lc1, *lc2;
912
0
  struct style    *sb_style = &wp->scrollbar_style;
913
0
  u_int      sx, sy, xoff, yoff, size1, size2, minimum;
914
0
  u_int      new_size, saved_size, resize_first = 0;
915
0
  int      full_size = (flags & SPAWN_FULLSIZE), status;
916
0
  int      scrollbars;
917
918
  /*
919
   * If full_size is specified, add a new cell at the top of the window
920
   * layout. Otherwise, split the cell for the current pane.
921
   */
922
0
  if (full_size)
923
0
    lc = wp->window->layout_root;
924
0
  else
925
0
    lc = wp->layout_cell;
926
0
  status = options_get_number(wp->window->options, "pane-border-status");
927
0
  scrollbars = options_get_number(wp->window->options, "pane-scrollbars");
928
929
  /* Copy the old cell size. */
930
0
  sx = lc->sx;
931
0
  sy = lc->sy;
932
0
  xoff = lc->xoff;
933
0
  yoff = lc->yoff;
934
935
  /* Check there is enough space for the two new panes. */
936
0
  switch (type) {
937
0
  case LAYOUT_LEFTRIGHT:
938
0
    if (scrollbars) {
939
0
      minimum = PANE_MINIMUM * 2 + sb_style->width +
940
0
          sb_style->pad;
941
0
    } else
942
0
      minimum = PANE_MINIMUM * 2 + 1;
943
0
    if (sx < minimum)
944
0
      return (NULL);
945
0
    break;
946
0
  case LAYOUT_TOPBOTTOM:
947
0
    if (layout_add_horizontal_border(wp->window, lc, status))
948
0
      minimum = PANE_MINIMUM * 2 + 2;
949
0
    else
950
0
      minimum = PANE_MINIMUM * 2 + 1;
951
0
    if (sy < minimum)
952
0
      return (NULL);
953
0
    break;
954
0
  default:
955
0
    fatalx("bad layout type");
956
0
  }
957
958
  /*
959
   * Calculate new cell sizes. size is the target size or -1 for middle
960
   * split, size1 is the size of the top/left and size2 the bottom/right.
961
   */
962
0
  if (type == LAYOUT_LEFTRIGHT)
963
0
    saved_size = sx;
964
0
  else
965
0
    saved_size = sy;
966
0
  if (size < 0)
967
0
    size2 = ((saved_size + 1) / 2) - 1;
968
0
  else if (flags & SPAWN_BEFORE)
969
0
    size2 = saved_size - size - 1;
970
0
  else
971
0
    size2 = size;
972
0
  if (size2 < PANE_MINIMUM)
973
0
    size2 = PANE_MINIMUM;
974
0
  else if (size2 > saved_size - 2)
975
0
    size2 = saved_size - 2;
976
0
  size1 = saved_size - 1 - size2;
977
978
  /* Which size are we using? */
979
0
  if (flags & SPAWN_BEFORE)
980
0
    new_size = size2;
981
0
  else
982
0
    new_size = size1;
983
984
  /* Confirm there is enough space for full size pane. */
985
0
  if (full_size && !layout_set_size_check(wp->window, lc, type, new_size))
986
0
    return (NULL);
987
988
0
  if (lc->parent != NULL && lc->parent->type == type) {
989
    /*
990
     * If the parent exists and is of the same type as the split,
991
     * create a new cell and insert it after this one.
992
     */
993
0
    lcparent = lc->parent;
994
0
    lcnew = layout_create_cell(lcparent);
995
0
    if (flags & SPAWN_BEFORE)
996
0
      TAILQ_INSERT_BEFORE(lc, lcnew, entry);
997
0
    else
998
0
      TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
999
0
  } else if (full_size && lc->parent == NULL && lc->type == type) {
1000
    /*
1001
     * If the new full size pane is the same type as the root
1002
     * split, insert the new pane under the existing root cell
1003
     * instead of creating a new root cell. The existing layout
1004
     * must be resized before inserting the new cell.
1005
     */
1006
0
    if (lc->type == LAYOUT_LEFTRIGHT) {
1007
0
      lc->sx = new_size;
1008
0
      layout_resize_child_cells(wp->window, lc);
1009
0
      lc->sx = saved_size;
1010
0
    } else if (lc->type == LAYOUT_TOPBOTTOM) {
1011
0
      lc->sy = new_size;
1012
0
      layout_resize_child_cells(wp->window, lc);
1013
0
      lc->sy = saved_size;
1014
0
    }
1015
0
    resize_first = 1;
1016
1017
    /* Create the new cell. */
1018
0
    lcnew = layout_create_cell(lc);
1019
0
    size = saved_size - 1 - new_size;
1020
0
    if (lc->type == LAYOUT_LEFTRIGHT)
1021
0
      layout_set_size(lcnew, size, sy, 0, 0);
1022
0
    else if (lc->type == LAYOUT_TOPBOTTOM)
1023
0
      layout_set_size(lcnew, sx, size, 0, 0);
1024
0
    if (flags & SPAWN_BEFORE)
1025
0
      TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry);
1026
0
    else
1027
0
      TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
1028
0
  } else {
1029
    /*
1030
     * Otherwise create a new parent and insert it.
1031
     */
1032
1033
    /* Create and insert the replacement parent. */
1034
0
    lcparent = layout_create_cell(lc->parent);
1035
0
    layout_make_node(lcparent, type);
1036
0
    layout_set_size(lcparent, sx, sy, xoff, yoff);
1037
0
    if (lc->parent == NULL)
1038
0
      wp->window->layout_root = lcparent;
1039
0
    else
1040
0
      TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
1041
1042
    /* Insert the old cell. */
1043
0
    lc->parent = lcparent;
1044
0
    TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
1045
1046
    /* Create the new child cell. */
1047
0
    lcnew = layout_create_cell(lcparent);
1048
0
    if (flags & SPAWN_BEFORE)
1049
0
      TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry);
1050
0
    else
1051
0
      TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
1052
0
  }
1053
0
  if (flags & SPAWN_BEFORE) {
1054
0
    lc1 = lcnew;
1055
0
    lc2 = lc;
1056
0
  } else {
1057
0
    lc1 = lc;
1058
0
    lc2 = lcnew;
1059
0
  }
1060
1061
  /*
1062
   * Set new cell sizes. size1 is the size of the top/left and size2 the
1063
   * bottom/right.
1064
   */
1065
0
  if (!resize_first && type == LAYOUT_LEFTRIGHT) {
1066
0
    layout_set_size(lc1, size1, sy, xoff, yoff);
1067
0
    layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff);
1068
0
  } else if (!resize_first && type == LAYOUT_TOPBOTTOM) {
1069
0
    layout_set_size(lc1, sx, size1, xoff, yoff);
1070
0
    layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1);
1071
0
  }
1072
0
  if (full_size) {
1073
0
    if (!resize_first)
1074
0
      layout_resize_child_cells(wp->window, lc);
1075
0
    layout_fix_offsets(wp->window);
1076
0
  } else
1077
0
    layout_make_leaf(lc, wp);
1078
1079
0
  return (lcnew);
1080
0
}
1081
1082
/* Destroy the cell associated with a pane. */
1083
void
1084
layout_close_pane(struct window_pane *wp)
1085
0
{
1086
0
  struct window *w = wp->window;
1087
1088
  /* Remove the cell. */
1089
0
  layout_destroy_cell(w, wp->layout_cell, &w->layout_root);
1090
1091
  /* Fix pane offsets and sizes. */
1092
0
  if (w->layout_root != NULL) {
1093
0
    layout_fix_offsets(w);
1094
0
    layout_fix_panes(w, NULL);
1095
0
  }
1096
0
  notify_window("window-layout-changed", w);
1097
0
}
1098
1099
int
1100
layout_spread_cell(struct window *w, struct layout_cell *parent)
1101
0
{
1102
0
  struct layout_cell  *lc;
1103
0
  struct style    *sb_style = &w->active->scrollbar_style;
1104
0
  u_int      number, each, size, this, remainder;
1105
0
  int      change, changed, status, scrollbars;
1106
1107
0
  number = 0;
1108
0
  TAILQ_FOREACH (lc, &parent->cells, entry)
1109
0
    number++;
1110
0
  if (number <= 1)
1111
0
    return (0);
1112
0
  status = options_get_number(w->options, "pane-border-status");
1113
0
  scrollbars = options_get_number(w->options, "pane-scrollbars");
1114
1115
0
  if (parent->type == LAYOUT_LEFTRIGHT) {
1116
0
    if (scrollbars)
1117
0
      size = parent->sx - sb_style->width + sb_style->pad;
1118
0
    else
1119
0
      size = parent->sx;
1120
0
  }
1121
0
  else if (parent->type == LAYOUT_TOPBOTTOM) {
1122
0
    if (layout_add_horizontal_border(w, parent, status))
1123
0
      size = parent->sy - 1;
1124
0
    else
1125
0
      size = parent->sy;
1126
0
  } else
1127
0
    return (0);
1128
0
  if (size < number - 1)
1129
0
    return (0);
1130
0
  each = (size - (number - 1)) / number;
1131
0
  if (each == 0)
1132
0
    return (0);
1133
  /*
1134
   * Remaining space after assigning that which can be evenly
1135
   * distributed.
1136
   */
1137
0
  remainder = size - (number * (each + 1)) + 1;
1138
1139
0
  changed = 0;
1140
0
  TAILQ_FOREACH (lc, &parent->cells, entry) {
1141
0
    change = 0;
1142
0
    if (parent->type == LAYOUT_LEFTRIGHT) {
1143
0
      change = each - (int)lc->sx;
1144
0
      if (remainder > 0) {
1145
0
        change++;
1146
0
        remainder--;
1147
0
      }
1148
0
      layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
1149
0
    } else if (parent->type == LAYOUT_TOPBOTTOM) {
1150
0
      if (layout_add_horizontal_border(w, lc, status))
1151
0
        this = each + 1;
1152
0
      else
1153
0
        this = each;
1154
0
      if (remainder > 0) {
1155
0
        this++;
1156
0
        remainder--;
1157
0
      }
1158
0
      change = this - (int)lc->sy;
1159
0
      layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
1160
0
    }
1161
0
    if (change != 0)
1162
0
      changed = 1;
1163
0
  }
1164
0
  return (changed);
1165
0
}
1166
1167
void
1168
layout_spread_out(struct window_pane *wp)
1169
0
{
1170
0
  struct layout_cell  *parent;
1171
0
  struct window   *w = wp->window;
1172
1173
0
  parent = wp->layout_cell->parent;
1174
0
  if (parent == NULL)
1175
0
    return;
1176
1177
0
  do {
1178
0
    if (layout_spread_cell(w, parent)) {
1179
0
      layout_fix_offsets(w);
1180
0
      layout_fix_panes(w, NULL);
1181
0
      break;
1182
0
    }
1183
0
  } while ((parent = parent->parent) != NULL);
1184
0
}