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

Annotation of src/usr.bin/tmux/window.c, Revision 1.63

1.63    ! nicm        1: /* $OpenBSD: window.c,v 1.62 2011/01/08 01:52:37 nicm Exp $ */
1.1       nicm        2:
                      3: /*
                      4:  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
                      5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
                      7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
                     15:  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
                     16:  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     17:  */
                     18:
                     19: #include <sys/types.h>
                     20: #include <sys/ioctl.h>
                     21:
                     22: #include <errno.h>
                     23: #include <fcntl.h>
1.5       nicm       24: #include <fnmatch.h>
1.1       nicm       25: #include <paths.h>
1.8       nicm       26: #include <pwd.h>
1.1       nicm       27: #include <signal.h>
                     28: #include <stdint.h>
                     29: #include <stdlib.h>
                     30: #include <string.h>
                     31: #include <termios.h>
                     32: #include <unistd.h>
                     33: #include <util.h>
                     34:
                     35: #include "tmux.h"
                     36:
                     37: /*
1.14      nicm       38:  * Each window is attached to a number of panes, each of which is a pty. This
1.1       nicm       39:  * file contains code to handle them.
                     40:  *
                     41:  * A pane has two buffers attached, these are filled and emptied by the main
                     42:  * server poll loop. Output data is received from pty's in screen format,
                     43:  * translated and returned as a series of escape sequences and strings via
                     44:  * input_parse (in input.c). Input data is received as key codes and written
                     45:  * directly via input_key.
                     46:  *
                     47:  * Each pane also has a "virtual" screen (screen.c) which contains the current
                     48:  * state and is redisplayed when the window is reattached to a client.
                     49:  *
                     50:  * Windows are stored directly on a global array and wrapped in any number of
                     51:  * winlink structs to be linked onto local session RB trees. A reference count
                     52:  * is maintained and a window removed from the global list and destroyed when
                     53:  * it reaches zero.
                     54:  */
                     55:
                     56: /* Global window list. */
                     57: struct windows windows;
                     58:
1.37      nicm       59: void   window_pane_read_callback(struct bufferevent *, void *);
                     60: void   window_pane_error_callback(struct bufferevent *, short, void *);
                     61:
1.1       nicm       62: RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
                     63:
                     64: int
                     65: winlink_cmp(struct winlink *wl1, struct winlink *wl2)
                     66: {
                     67:        return (wl1->idx - wl2->idx);
1.12      nicm       68: }
                     69:
                     70: struct winlink *
                     71: winlink_find_by_window(struct winlinks *wwl, struct window *w)
                     72: {
                     73:        struct winlink  *wl;
                     74:
                     75:        RB_FOREACH(wl, winlinks, wwl) {
                     76:                if (wl->window == w)
                     77:                        return (wl);
                     78:        }
                     79:
                     80:        return (NULL);
1.1       nicm       81: }
                     82:
                     83: struct winlink *
                     84: winlink_find_by_index(struct winlinks *wwl, int idx)
                     85: {
                     86:        struct winlink  wl;
                     87:
                     88:        if (idx < 0)
                     89:                fatalx("bad index");
                     90:
                     91:        wl.idx = idx;
                     92:        return (RB_FIND(winlinks, wwl, &wl));
                     93: }
                     94:
                     95: int
1.22      nicm       96: winlink_next_index(struct winlinks *wwl, int idx)
1.1       nicm       97: {
1.22      nicm       98:        int     i;
1.1       nicm       99:
1.22      nicm      100:        i = idx;
                    101:        do {
1.1       nicm      102:                if (winlink_find_by_index(wwl, i) == NULL)
                    103:                        return (i);
1.22      nicm      104:                if (i == INT_MAX)
                    105:                        i = 0;
                    106:                else
                    107:                        i++;
                    108:        } while (i != idx);
                    109:        return (-1);
1.1       nicm      110: }
                    111:
                    112: u_int
                    113: winlink_count(struct winlinks *wwl)
                    114: {
                    115:        struct winlink  *wl;
                    116:        u_int            n;
                    117:
                    118:        n = 0;
                    119:        RB_FOREACH(wl, winlinks, wwl)
                    120:                n++;
                    121:
                    122:        return (n);
                    123: }
                    124:
                    125: struct winlink *
1.63    ! nicm      126: winlink_add(struct winlinks *wwl, int idx)
1.1       nicm      127: {
                    128:        struct winlink  *wl;
                    129:
1.22      nicm      130:        if (idx < 0) {
                    131:                if ((idx = winlink_next_index(wwl, -idx - 1)) == -1)
                    132:                        return (NULL);
                    133:        } else if (winlink_find_by_index(wwl, idx) != NULL)
1.1       nicm      134:                return (NULL);
                    135:
                    136:        wl = xcalloc(1, sizeof *wl);
                    137:        wl->idx = idx;
                    138:        RB_INSERT(winlinks, wwl, wl);
                    139:
1.63    ! nicm      140:        return (wl);
        !           141: }
        !           142:
        !           143: void
        !           144: winlink_set_window(struct winlink *wl, struct window *w)
        !           145: {
        !           146:        wl->window = w;
1.1       nicm      147:        w->references++;
                    148: }
                    149:
                    150: void
                    151: winlink_remove(struct winlinks *wwl, struct winlink *wl)
                    152: {
                    153:        struct window   *w = wl->window;
                    154:
                    155:        RB_REMOVE(winlinks, wwl, wl);
1.40      nicm      156:        if (wl->status_text != NULL)
                    157:                xfree(wl->status_text);
1.1       nicm      158:        xfree(wl);
                    159:
1.63    ! nicm      160:        if (w != NULL) {
        !           161:                if (w->references == 0)
        !           162:                        fatal("bad reference count");
        !           163:                w->references--;
        !           164:                if (w->references == 0)
        !           165:                        window_destroy(w);
        !           166:        }
1.1       nicm      167: }
                    168:
                    169: struct winlink *
1.41      nicm      170: winlink_next(struct winlink *wl)
1.1       nicm      171: {
                    172:        return (RB_NEXT(winlinks, wwl, wl));
                    173: }
                    174:
                    175: struct winlink *
1.41      nicm      176: winlink_previous(struct winlink *wl)
1.1       nicm      177: {
                    178:        return (RB_PREV(winlinks, wwl, wl));
1.52      nicm      179: }
                    180:
                    181: struct winlink *
1.53      nicm      182: winlink_next_by_number(struct winlink *wl, struct session *s, int n)
1.52      nicm      183: {
                    184:        for (; n > 0; n--) {
                    185:                if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL)
1.53      nicm      186:                        wl = RB_MIN(winlinks, &s->windows);
1.52      nicm      187:        }
                    188:
                    189:        return (wl);
                    190: }
                    191:
                    192: struct winlink *
1.53      nicm      193: winlink_previous_by_number(struct winlink *wl, struct session *s, int n)
1.52      nicm      194: {
                    195:        for (; n > 0; n--) {
                    196:                if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL)
1.53      nicm      197:                        wl = RB_MAX(winlinks, &s->windows);
1.52      nicm      198:        }
                    199:
                    200:        return (wl);
1.1       nicm      201: }
                    202:
                    203: void
                    204: winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
                    205: {
                    206:        if (wl == NULL)
                    207:                return;
                    208:
                    209:        winlink_stack_remove(stack, wl);
1.28      nicm      210:        TAILQ_INSERT_HEAD(stack, wl, sentry);
1.1       nicm      211: }
                    212:
                    213: void
                    214: winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
                    215: {
                    216:        struct winlink  *wl2;
                    217:
                    218:        if (wl == NULL)
                    219:                return;
1.42      nicm      220:
1.28      nicm      221:        TAILQ_FOREACH(wl2, stack, sentry) {
1.1       nicm      222:                if (wl2 == wl) {
1.28      nicm      223:                        TAILQ_REMOVE(stack, wl, sentry);
1.1       nicm      224:                        return;
                    225:                }
                    226:        }
                    227: }
                    228:
                    229: int
                    230: window_index(struct window *s, u_int *i)
                    231: {
                    232:        for (*i = 0; *i < ARRAY_LENGTH(&windows); (*i)++) {
                    233:                if (s == ARRAY_ITEM(&windows, *i))
                    234:                        return (0);
                    235:        }
                    236:        return (-1);
                    237: }
                    238:
                    239: struct window *
                    240: window_create1(u_int sx, u_int sy)
                    241: {
                    242:        struct window   *w;
                    243:        u_int            i;
                    244:
1.38      nicm      245:        w = xcalloc(1, sizeof *w);
1.1       nicm      246:        w->name = NULL;
                    247:        w->flags = 0;
                    248:
                    249:        TAILQ_INIT(&w->panes);
                    250:        w->active = NULL;
1.14      nicm      251:
1.17      nicm      252:        w->lastlayout = -1;
1.14      nicm      253:        w->layout_root = NULL;
1.42      nicm      254:
1.1       nicm      255:        w->sx = sx;
                    256:        w->sy = sy;
                    257:
1.38      nicm      258:        queue_window_name(w);
                    259:
1.7       nicm      260:        options_init(&w->options, &global_w_options);
1.1       nicm      261:
                    262:        for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
                    263:                if (ARRAY_ITEM(&windows, i) == NULL) {
                    264:                        ARRAY_SET(&windows, i, w);
                    265:                        break;
                    266:                }
                    267:        }
                    268:        if (i == ARRAY_LENGTH(&windows))
                    269:                ARRAY_ADD(&windows, w);
                    270:        w->references = 0;
                    271:
                    272:        return (w);
                    273: }
                    274:
                    275: struct window *
1.23      nicm      276: window_create(const char *name, const char *cmd, const char *shell,
                    277:     const char *cwd, struct environ *env, struct termios *tio,
                    278:     u_int sx, u_int sy, u_int hlimit,char **cause)
1.1       nicm      279: {
1.14      nicm      280:        struct window           *w;
                    281:        struct window_pane      *wp;
1.1       nicm      282:
                    283:        w = window_create1(sx, sy);
1.16      nicm      284:        wp = window_add_pane(w, hlimit);
1.14      nicm      285:        layout_init(w);
1.23      nicm      286:        if (window_pane_spawn(wp, cmd, shell, cwd, env, tio, cause) != 0) {
1.1       nicm      287:                window_destroy(w);
                    288:                return (NULL);
                    289:        }
                    290:        w->active = TAILQ_FIRST(&w->panes);
                    291:        if (name != NULL) {
                    292:                w->name = xstrdup(name);
                    293:                options_set_number(&w->options, "automatic-rename", 0);
                    294:        } else
                    295:                w->name = default_window_name(w);
                    296:        return (w);
                    297: }
                    298:
                    299: void
                    300: window_destroy(struct window *w)
                    301: {
                    302:        u_int   i;
                    303:
                    304:        if (window_index(w, &i) != 0)
                    305:                fatalx("index not found");
                    306:        ARRAY_SET(&windows, i, NULL);
                    307:        while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL)
                    308:                ARRAY_TRUNC(&windows, 1);
                    309:
1.14      nicm      310:        if (w->layout_root != NULL)
                    311:                layout_free(w);
                    312:
1.38      nicm      313:        evtimer_del(&w->name_timer);
                    314:
1.1       nicm      315:        options_free(&w->options);
                    316:
                    317:        window_destroy_panes(w);
                    318:
                    319:        if (w->name != NULL)
                    320:                xfree(w->name);
                    321:        xfree(w);
                    322: }
                    323:
1.15      nicm      324: void
1.1       nicm      325: window_resize(struct window *w, u_int sx, u_int sy)
                    326: {
                    327:        w->sx = sx;
                    328:        w->sy = sy;
                    329: }
                    330:
                    331: void
                    332: window_set_active_pane(struct window *w, struct window_pane *wp)
                    333: {
1.59      nicm      334:        if (wp == w->active)
                    335:                return;
1.58      nicm      336:        w->last = w->active;
1.1       nicm      337:        w->active = wp;
1.10      nicm      338:        while (!window_pane_visible(w->active)) {
1.1       nicm      339:                w->active = TAILQ_PREV(w->active, window_panes, entry);
1.10      nicm      340:                if (w->active == NULL)
                    341:                        w->active = TAILQ_LAST(&w->panes, window_panes);
                    342:                if (w->active == wp)
                    343:                        return;
1.29      nicm      344:        }
                    345: }
                    346:
                    347: void
                    348: window_set_active_at(struct window *w, u_int x, u_int y)
                    349: {
                    350:        struct window_pane      *wp;
                    351:
                    352:        TAILQ_FOREACH(wp, &w->panes, entry) {
1.59      nicm      353:                if (wp == w->active || !window_pane_visible(wp))
1.29      nicm      354:                        continue;
                    355:                if (x < wp->xoff || x >= wp->xoff + wp->sx)
                    356:                        continue;
                    357:                if (y < wp->yoff || y >= wp->yoff + wp->sy)
                    358:                        continue;
                    359:                window_set_active_pane(w, wp);
                    360:                break;
1.10      nicm      361:        }
1.1       nicm      362: }
                    363:
                    364: struct window_pane *
1.16      nicm      365: window_add_pane(struct window *w, u_int hlimit)
1.1       nicm      366: {
                    367:        struct window_pane      *wp;
                    368:
1.14      nicm      369:        wp = window_pane_create(w, w->sx, w->sy, hlimit);
1.1       nicm      370:        if (TAILQ_EMPTY(&w->panes))
                    371:                TAILQ_INSERT_HEAD(&w->panes, wp, entry);
                    372:        else
                    373:                TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry);
                    374:        return (wp);
                    375: }
                    376:
                    377: void
                    378: window_remove_pane(struct window *w, struct window_pane *wp)
                    379: {
1.57      nicm      380:        if (wp == w->active) {
1.58      nicm      381:                w->active = w->last;
                    382:                w->last = NULL;
                    383:                if (w->active == NULL) {
                    384:                        w->active = TAILQ_PREV(wp, window_panes, entry);
                    385:                        if (w->active == NULL)
                    386:                                w->active = TAILQ_NEXT(wp, entry);
                    387:                }
                    388:        } else if (wp == w->last)
                    389:                w->last = NULL;
1.1       nicm      390:
                    391:        TAILQ_REMOVE(&w->panes, wp, entry);
                    392:        window_pane_destroy(wp);
                    393: }
                    394:
                    395: struct window_pane *
                    396: window_pane_at_index(struct window *w, u_int idx)
                    397: {
                    398:        struct window_pane      *wp;
                    399:        u_int                    n;
                    400:
                    401:        n = 0;
                    402:        TAILQ_FOREACH(wp, &w->panes, entry) {
                    403:                if (n == idx)
                    404:                        return (wp);
                    405:                n++;
                    406:        }
                    407:        return (NULL);
1.53      nicm      408: }
                    409:
                    410: struct window_pane *
                    411: window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n)
                    412: {
                    413:        for (; n > 0; n--) {
                    414:                if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
                    415:                        wp = TAILQ_FIRST(&w->panes);
                    416:        }
                    417:
                    418:        return (wp);
                    419: }
                    420:
                    421: struct window_pane *
                    422: window_pane_previous_by_number(struct window *w, struct window_pane *wp,
                    423:     u_int n)
                    424: {
                    425:        for (; n > 0; n--) {
                    426:                if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL)
                    427:                        wp = TAILQ_LAST(&w->panes, window_panes);
                    428:        }
                    429:
                    430:        return (wp);
1.13      nicm      431: }
                    432:
                    433: u_int
                    434: window_pane_index(struct window *w, struct window_pane *wp)
                    435: {
                    436:        struct window_pane      *wq;
                    437:        u_int                    n;
                    438:
                    439:        n = 0;
                    440:        TAILQ_FOREACH(wq, &w->panes, entry) {
                    441:                if (wp == wq)
                    442:                        break;
                    443:                n++;
                    444:        }
                    445:        return (n);
1.1       nicm      446: }
                    447:
                    448: u_int
                    449: window_count_panes(struct window *w)
                    450: {
                    451:        struct window_pane      *wp;
                    452:        u_int                    n;
                    453:
                    454:        n = 0;
                    455:        TAILQ_FOREACH(wp, &w->panes, entry)
                    456:                n++;
                    457:        return (n);
                    458: }
                    459:
                    460: void
                    461: window_destroy_panes(struct window *w)
                    462: {
                    463:        struct window_pane      *wp;
                    464:
                    465:        while (!TAILQ_EMPTY(&w->panes)) {
                    466:                wp = TAILQ_FIRST(&w->panes);
                    467:                TAILQ_REMOVE(&w->panes, wp, entry);
                    468:                window_pane_destroy(wp);
                    469:        }
1.61      nicm      470: }
                    471:
                    472: /* Return list of printable window flag symbols. No flags is just a space. */
                    473: char *
                    474: window_printable_flags(struct session *s, struct winlink *wl)
                    475: {
                    476:        char    flags[BUFSIZ];
                    477:        int     pos;
                    478:
                    479:        pos = 0;
                    480:        if (wl->flags & WINLINK_ACTIVITY)
                    481:                flags[pos++] = '#';
                    482:        if (wl->flags & WINLINK_BELL)
                    483:                flags[pos++] = '!';
                    484:        if (wl->flags & WINLINK_CONTENT)
                    485:                flags[pos++] = '+';
                    486:        if (wl->flags & WINLINK_SILENCE)
                    487:                flags[pos++] = '~';
                    488:        if (wl == s->curw)
                    489:                flags[pos++] = '*';
                    490:        if (wl == TAILQ_FIRST(&s->lastw))
                    491:                flags[pos++] = '-';
                    492:        if (pos == 0)
                    493:                flags[pos++] = ' ';
                    494:        flags[pos] = '\0';
                    495:        return (xstrdup(flags));
1.1       nicm      496: }
                    497:
                    498: struct window_pane *
                    499: window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
                    500: {
                    501:        struct window_pane      *wp;
                    502:
                    503:        wp = xcalloc(1, sizeof *wp);
                    504:        wp->window = w;
                    505:
                    506:        wp->cmd = NULL;
1.23      nicm      507:        wp->shell = NULL;
1.1       nicm      508:        wp->cwd = NULL;
                    509:
                    510:        wp->fd = -1;
1.37      nicm      511:        wp->event = NULL;
1.1       nicm      512:
                    513:        wp->mode = NULL;
1.14      nicm      514:
                    515:        wp->layout_cell = NULL;
1.1       nicm      516:
                    517:        wp->xoff = 0;
1.42      nicm      518:        wp->yoff = 0;
1.1       nicm      519:
                    520:        wp->sx = sx;
                    521:        wp->sy = sy;
                    522:
1.32      nicm      523:        wp->pipe_fd = -1;
                    524:        wp->pipe_off = 0;
1.36      nicm      525:        wp->pipe_event = NULL;
1.32      nicm      526:
1.9       nicm      527:        wp->saved_grid = NULL;
                    528:
1.1       nicm      529:        screen_init(&wp->base, sx, sy, hlimit);
                    530:        wp->screen = &wp->base;
                    531:
                    532:        input_init(wp);
                    533:
                    534:        return (wp);
                    535: }
                    536:
                    537: void
                    538: window_pane_destroy(struct window_pane *wp)
                    539: {
1.55      nicm      540:        window_pane_reset_mode(wp);
                    541:
1.37      nicm      542:        if (wp->fd != -1) {
1.1       nicm      543:                close(wp->fd);
1.37      nicm      544:                bufferevent_free(wp->event);
                    545:        }
1.1       nicm      546:
                    547:        input_free(wp);
                    548:
                    549:        screen_free(&wp->base);
1.9       nicm      550:        if (wp->saved_grid != NULL)
                    551:                grid_destroy(wp->saved_grid);
1.1       nicm      552:
1.32      nicm      553:        if (wp->pipe_fd != -1) {
                    554:                close(wp->pipe_fd);
1.36      nicm      555:                bufferevent_free(wp->pipe_event);
1.32      nicm      556:        }
                    557:
1.1       nicm      558:        if (wp->cwd != NULL)
                    559:                xfree(wp->cwd);
1.23      nicm      560:        if (wp->shell != NULL)
                    561:                xfree(wp->shell);
1.1       nicm      562:        if (wp->cmd != NULL)
                    563:                xfree(wp->cmd);
                    564:        xfree(wp);
                    565: }
                    566:
                    567: int
1.23      nicm      568: window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell,
1.21      nicm      569:     const char *cwd, struct environ *env, struct termios *tio, char **cause)
1.1       nicm      570: {
1.47      nicm      571:        struct winsize   ws;
                    572:        char            *argv0;
                    573:        const char      *ptr;
                    574:        struct termios   tio2;
1.1       nicm      575:
1.37      nicm      576:        if (wp->fd != -1) {
1.1       nicm      577:                close(wp->fd);
1.37      nicm      578:                bufferevent_free(wp->event);
                    579:        }
1.1       nicm      580:        if (cmd != NULL) {
                    581:                if (wp->cmd != NULL)
                    582:                        xfree(wp->cmd);
                    583:                wp->cmd = xstrdup(cmd);
                    584:        }
1.23      nicm      585:        if (shell != NULL) {
                    586:                if (wp->shell != NULL)
                    587:                        xfree(wp->shell);
                    588:                wp->shell = xstrdup(shell);
                    589:        }
1.1       nicm      590:        if (cwd != NULL) {
                    591:                if (wp->cwd != NULL)
                    592:                        xfree(wp->cwd);
                    593:                wp->cwd = xstrdup(cwd);
                    594:        }
                    595:
                    596:        memset(&ws, 0, sizeof ws);
                    597:        ws.ws_col = screen_size_x(&wp->base);
                    598:        ws.ws_row = screen_size_y(&wp->base);
                    599:
1.42      nicm      600:        switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) {
1.1       nicm      601:        case -1:
                    602:                wp->fd = -1;
                    603:                xasprintf(cause, "%s: %s", cmd, strerror(errno));
                    604:                return (-1);
                    605:        case 0:
                    606:                if (chdir(wp->cwd) != 0)
                    607:                        chdir("/");
1.25      nicm      608:
                    609:                if (tcgetattr(STDIN_FILENO, &tio2) != 0)
                    610:                        fatal("tcgetattr failed");
                    611:                if (tio != NULL)
                    612:                        memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc);
                    613:                tio2.c_cc[VERASE] = '\177';
                    614:                if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0)
                    615:                        fatal("tcgetattr failed");
1.18      nicm      616:
1.56      nicm      617:                closefrom(STDERR_FILENO + 1);
                    618:
1.47      nicm      619:                environ_push(env);
1.18      nicm      620:
1.54      nicm      621:                clear_signals(1);
1.1       nicm      622:                log_close();
                    623:
1.8       nicm      624:                if (*wp->cmd != '\0') {
1.24      nicm      625:                        /* Set SHELL but only if it is currently not useful. */
                    626:                        shell = getenv("SHELL");
                    627:                        if (shell == NULL || *shell == '\0' || areshell(shell))
                    628:                                setenv("SHELL", wp->shell, 1);
                    629:
1.8       nicm      630:                        execl(_PATH_BSHELL, "sh", "-c", wp->cmd, (char *) NULL);
                    631:                        fatal("execl failed");
                    632:                }
                    633:
                    634:                /* No command; fork a login shell. */
1.23      nicm      635:                ptr = strrchr(wp->shell, '/');
                    636:                if (ptr != NULL && *(ptr + 1) != '\0')
1.8       nicm      637:                        xasprintf(&argv0, "-%s", ptr + 1);
                    638:                else
1.23      nicm      639:                        xasprintf(&argv0, "-%s", wp->shell);
1.24      nicm      640:                setenv("SHELL", wp->shell, 1);
1.23      nicm      641:                execl(wp->shell, argv0, (char *) NULL);
1.1       nicm      642:                fatal("execl failed");
                    643:        }
                    644:
1.62      nicm      645:        setblocking(wp->fd, 0);
                    646:
1.37      nicm      647:        wp->event = bufferevent_new(wp->fd,
                    648:            window_pane_read_callback, NULL, window_pane_error_callback, wp);
                    649:        bufferevent_enable(wp->event, EV_READ|EV_WRITE);
1.1       nicm      650:
                    651:        return (0);
                    652: }
                    653:
1.41      nicm      654: /* ARGSUSED */
1.15      nicm      655: void
1.37      nicm      656: window_pane_read_callback(unused struct bufferevent *bufev, void *data)
                    657: {
1.46      nicm      658:        struct window_pane     *wp = data;
                    659:        char                   *new_data;
                    660:        size_t                  new_size;
                    661:
                    662:        new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off;
                    663:        if (wp->pipe_fd != -1 && new_size > 0) {
                    664:                new_data = EVBUFFER_DATA(wp->event->input);
                    665:                bufferevent_write(wp->pipe_event, new_data, new_size);
                    666:        }
                    667:
                    668:        input_parse(wp);
1.37      nicm      669:
1.46      nicm      670:        wp->pipe_off = EVBUFFER_LENGTH(wp->event->input);
1.60      nicm      671:
                    672:        /*
                    673:         * If we get here, we're not outputting anymore, so set the silence
                    674:         * flag on the window.
                    675:         */
                    676:        wp->window->flags |= WINDOW_SILENCE;
                    677:        if (gettimeofday(&wp->window->silence_timer, NULL) != 0)
                    678:                fatal("gettimeofday failed.");
1.37      nicm      679: }
                    680:
1.41      nicm      681: /* ARGSUSED */
1.37      nicm      682: void
                    683: window_pane_error_callback(
                    684:     unused struct bufferevent *bufev, unused short what, void *data)
                    685: {
                    686:        struct window_pane *wp = data;
                    687:
1.39      nicm      688:        server_destroy_pane(wp);
1.37      nicm      689: }
                    690:
                    691: void
1.1       nicm      692: window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
                    693: {
                    694:        struct winsize  ws;
                    695:
                    696:        if (sx == wp->sx && sy == wp->sy)
1.15      nicm      697:                return;
1.1       nicm      698:        wp->sx = sx;
                    699:        wp->sy = sy;
                    700:
                    701:        memset(&ws, 0, sizeof ws);
                    702:        ws.ws_col = sx;
                    703:        ws.ws_row = sy;
                    704:
                    705:        screen_resize(&wp->base, sx, sy);
                    706:        if (wp->mode != NULL)
                    707:                wp->mode->resize(wp, sx, sy);
                    708:
                    709:        if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
                    710:                fatal("ioctl failed");
1.44      nicm      711: }
                    712:
                    713: /*
                    714:  * Enter alternative screen mode. A copy of the visible screen is saved and the
                    715:  * history is not updated
                    716:  */
                    717: void
                    718: window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc)
                    719: {
                    720:        struct screen   *s = &wp->base;
                    721:        u_int            sx, sy;
                    722:
                    723:        if (wp->saved_grid != NULL)
                    724:                return;
                    725:        if (!options_get_number(&wp->window->options, "alternate-screen"))
                    726:                return;
                    727:        sx = screen_size_x(s);
                    728:        sy = screen_size_y(s);
                    729:
                    730:        wp->saved_grid = grid_create(sx, sy, 0);
                    731:        grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy);
                    732:        wp->saved_cx = s->cx;
                    733:        wp->saved_cy = s->cy;
                    734:        memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell);
                    735:
                    736:        grid_view_clear(s->grid, 0, 0, sx, sy);
                    737:
                    738:        wp->base.grid->flags &= ~GRID_HISTORY;
                    739:
                    740:        wp->flags |= PANE_REDRAW;
                    741: }
                    742:
                    743: /* Exit alternate screen mode and restore the copied grid. */
                    744: void
                    745: window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc)
                    746: {
                    747:        struct screen   *s = &wp->base;
                    748:        u_int            sx, sy;
                    749:
                    750:        if (wp->saved_grid == NULL)
                    751:                return;
                    752:        if (!options_get_number(&wp->window->options, "alternate-screen"))
                    753:                return;
                    754:        sx = screen_size_x(s);
                    755:        sy = screen_size_y(s);
                    756:
                    757:        /*
                    758:         * If the current size is bigger, temporarily resize to the old size
                    759:         * before copying back.
                    760:         */
                    761:        if (sy > wp->saved_grid->sy)
                    762:                screen_resize(s, sx, wp->saved_grid->sy);
                    763:
                    764:        /* Restore the grid, cursor position and cell. */
                    765:        grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy);
                    766:        s->cx = wp->saved_cx;
                    767:        if (s->cx > screen_size_x(s) - 1)
                    768:                s->cx = screen_size_x(s) - 1;
                    769:        s->cy = wp->saved_cy;
                    770:        if (s->cy > screen_size_y(s) - 1)
                    771:                s->cy = screen_size_y(s) - 1;
                    772:        memcpy(gc, &wp->saved_cell, sizeof *gc);
                    773:
                    774:        /*
                    775:         * Turn history back on (so resize can use it) and then resize back to
                    776:         * the current size.
                    777:         */
                    778:        wp->base.grid->flags |= GRID_HISTORY;
                    779:        if (sy > wp->saved_grid->sy)
                    780:                screen_resize(s, sx, sy);
                    781:
                    782:        grid_destroy(wp->saved_grid);
                    783:        wp->saved_grid = NULL;
                    784:
                    785:        wp->flags |= PANE_REDRAW;
1.1       nicm      786: }
                    787:
                    788: int
                    789: window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode)
                    790: {
                    791:        struct screen   *s;
                    792:
1.15      nicm      793:        if (wp->mode != NULL)
1.1       nicm      794:                return (1);
                    795:        wp->mode = mode;
                    796:
                    797:        if ((s = wp->mode->init(wp)) != NULL)
                    798:                wp->screen = s;
1.34      nicm      799:        wp->flags |= PANE_REDRAW;
1.1       nicm      800:        return (0);
                    801: }
                    802:
                    803: void
                    804: window_pane_reset_mode(struct window_pane *wp)
                    805: {
                    806:        if (wp->mode == NULL)
                    807:                return;
                    808:
                    809:        wp->mode->free(wp);
                    810:        wp->mode = NULL;
                    811:
                    812:        wp->screen = &wp->base;
1.34      nicm      813:        wp->flags |= PANE_REDRAW;
1.1       nicm      814: }
                    815:
                    816: void
1.51      nicm      817: window_pane_key(struct window_pane *wp, struct session *sess, int key)
1.1       nicm      818: {
1.27      nicm      819:        struct window_pane      *wp2;
                    820:
1.30      nicm      821:        if (!window_pane_visible(wp))
1.3       nicm      822:                return;
                    823:
1.1       nicm      824:        if (wp->mode != NULL) {
                    825:                if (wp->mode->key != NULL)
1.51      nicm      826:                        wp->mode->key(wp, sess, key);
1.27      nicm      827:                return;
1.30      nicm      828:        }
1.27      nicm      829:
1.30      nicm      830:        if (wp->fd == -1)
                    831:                return;
1.27      nicm      832:        input_key(wp, key);
                    833:        if (options_get_number(&wp->window->options, "synchronize-panes")) {
                    834:                TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
                    835:                        if (wp2 == wp || wp2->mode != NULL)
                    836:                                continue;
                    837:                        if (wp2->fd != -1 && window_pane_visible(wp2))
                    838:                                input_key(wp2, key);
                    839:                }
                    840:        }
1.1       nicm      841: }
                    842:
                    843: void
                    844: window_pane_mouse(
1.51      nicm      845:     struct window_pane *wp, struct session *sess, struct mouse_event *m)
1.1       nicm      846: {
1.30      nicm      847:        if (!window_pane_visible(wp))
1.3       nicm      848:                return;
                    849:
1.31      nicm      850:        if (m->x < wp->xoff || m->x >= wp->xoff + wp->sx)
1.1       nicm      851:                return;
1.31      nicm      852:        if (m->y < wp->yoff || m->y >= wp->yoff + wp->sy)
1.1       nicm      853:                return;
1.31      nicm      854:        m->x -= wp->xoff;
                    855:        m->y -= wp->yoff;
1.1       nicm      856:
                    857:        if (wp->mode != NULL) {
                    858:                if (wp->mode->mouse != NULL)
1.51      nicm      859:                        wp->mode->mouse(wp, sess, m);
1.30      nicm      860:        } else if (wp->fd != -1)
1.31      nicm      861:                input_mouse(wp, m);
1.10      nicm      862: }
                    863:
                    864: int
                    865: window_pane_visible(struct window_pane *wp)
                    866: {
                    867:        struct window   *w = wp->window;
                    868:
                    869:        if (wp->xoff >= w->sx || wp->yoff >= w->sy)
                    870:                return (0);
                    871:        if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy)
                    872:                return (0);
                    873:        return (1);
1.1       nicm      874: }
                    875:
                    876: char *
1.5       nicm      877: window_pane_search(struct window_pane *wp, const char *searchstr, u_int *lineno)
1.1       nicm      878: {
1.4       nicm      879:        struct screen   *s = &wp->base;
1.5       nicm      880:        char            *newsearchstr, *line, *msg;
1.4       nicm      881:        u_int            i;
                    882:
1.5       nicm      883:        msg = NULL;
                    884:        xasprintf(&newsearchstr, "*%s*", searchstr);
                    885:
1.4       nicm      886:        for (i = 0; i < screen_size_y(s); i++) {
                    887:                line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
1.5       nicm      888:                if (fnmatch(newsearchstr, line, 0) == 0) {
                    889:                        msg = line;
                    890:                        if (lineno != NULL)
                    891:                                *lineno = i;
                    892:                        break;
                    893:                }
1.4       nicm      894:                xfree(line);
                    895:        }
1.5       nicm      896:
                    897:        xfree(newsearchstr);
                    898:        return (msg);
1.45      nicm      899: }
                    900:
                    901: /* Find the pane directly above another. */
                    902: struct window_pane *
                    903: window_pane_find_up(struct window_pane *wp)
                    904: {
                    905:        struct window_pane     *wp2;
                    906:        u_int                   left, top;
                    907:
                    908:        if (wp == NULL || !window_pane_visible(wp))
                    909:                return (NULL);
                    910:
                    911:        top = wp->yoff;
                    912:        if (top == 0)
                    913:                top = wp->window->sy + 1;
                    914:        left = wp->xoff;
                    915:
                    916:        TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
                    917:                if (!window_pane_visible(wp2))
                    918:                        continue;
                    919:                if (wp2->yoff + wp2->sy + 1 != top)
                    920:                        continue;
                    921:                if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx)
                    922:                        return (wp2);
                    923:        }
                    924:        return (NULL);
                    925: }
                    926:
                    927: /* Find the pane directly below another. */
                    928: struct window_pane *
                    929: window_pane_find_down(struct window_pane *wp)
                    930: {
                    931:        struct window_pane     *wp2;
                    932:        u_int                   left, bottom;
                    933:
                    934:        if (wp == NULL || !window_pane_visible(wp))
                    935:                return (NULL);
                    936:
                    937:        bottom = wp->yoff + wp->sy + 1;
                    938:        if (bottom >= wp->window->sy)
                    939:                bottom = 0;
                    940:        left = wp->xoff;
                    941:
                    942:        TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
                    943:                if (!window_pane_visible(wp2))
                    944:                        continue;
                    945:                if (wp2->yoff != bottom)
                    946:                        continue;
                    947:                if (left >= wp2->xoff && left <= wp2->xoff + wp2->sx)
                    948:                        return (wp2);
                    949:        }
                    950:        return (NULL);
                    951: }
                    952:
                    953: /*
                    954:  * Find the pane directly to the left of another, adjacent to the left side and
                    955:  * containing the top edge.
                    956:  */
                    957: struct window_pane *
                    958: window_pane_find_left(struct window_pane *wp)
                    959: {
                    960:        struct window_pane     *wp2;
                    961:        u_int                   left, top;
                    962:
                    963:        if (wp == NULL || !window_pane_visible(wp))
                    964:                return (NULL);
                    965:
                    966:        left = wp->xoff;
                    967:        if (left == 0)
                    968:                left = wp->window->sx + 1;
                    969:        top = wp->yoff;
                    970:
                    971:        TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
                    972:                if (!window_pane_visible(wp2))
                    973:                        continue;
                    974:                if (wp2->xoff + wp2->sx + 1 != left)
                    975:                        continue;
                    976:                if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy)
                    977:                        return (wp2);
                    978:        }
                    979:        return (NULL);
                    980: }
                    981:
                    982: /*
                    983:  * Find the pane directly to the right of another, that is adjacent to the
                    984:  * right edge and including the top edge.
                    985:  */
                    986: struct window_pane *
                    987: window_pane_find_right(struct window_pane *wp)
                    988: {
                    989:        struct window_pane     *wp2;
                    990:        u_int                   right, top;
                    991:
                    992:        if (wp == NULL || !window_pane_visible(wp))
                    993:                return (NULL);
                    994:
                    995:        right = wp->xoff + wp->sx + 1;
                    996:        if (right >= wp->window->sx)
                    997:                right = 0;
                    998:        top = wp->yoff;
                    999:
                   1000:        TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
                   1001:                if (!window_pane_visible(wp2))
                   1002:                        continue;
                   1003:                if (wp2->xoff != right)
                   1004:                        continue;
                   1005:                if (top >= wp2->yoff && top <= wp2->yoff + wp2->sy)
                   1006:                        return (wp2);
                   1007:        }
                   1008:        return (NULL);
1.1       nicm     1009: }