[BACK]Return to layout.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/layout.c, Revision 1.37

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