[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.1

1.1     ! nicm        1: /* $OpenBSD$ */
        !             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>
        !            24: #include <paths.h>
        !            25: #include <signal.h>
        !            26: #include <stdint.h>
        !            27: #include <stdlib.h>
        !            28: #include <string.h>
        !            29: #include <termios.h>
        !            30: #include <unistd.h>
        !            31: #include <util.h>
        !            32:
        !            33: #include "tmux.h"
        !            34:
        !            35: /*
        !            36:  * Each window is attached to one or two panes, each of which is a pty. This
        !            37:  * file contains code to handle them.
        !            38:  *
        !            39:  * A pane has two buffers attached, these are filled and emptied by the main
        !            40:  * server poll loop. Output data is received from pty's in screen format,
        !            41:  * translated and returned as a series of escape sequences and strings via
        !            42:  * input_parse (in input.c). Input data is received as key codes and written
        !            43:  * directly via input_key.
        !            44:  *
        !            45:  * Each pane also has a "virtual" screen (screen.c) which contains the current
        !            46:  * state and is redisplayed when the window is reattached to a client.
        !            47:  *
        !            48:  * Windows are stored directly on a global array and wrapped in any number of
        !            49:  * winlink structs to be linked onto local session RB trees. A reference count
        !            50:  * is maintained and a window removed from the global list and destroyed when
        !            51:  * it reaches zero.
        !            52:  */
        !            53:
        !            54: /* Global window list. */
        !            55: struct windows windows;
        !            56:
        !            57: RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
        !            58:
        !            59: int
        !            60: winlink_cmp(struct winlink *wl1, struct winlink *wl2)
        !            61: {
        !            62:        return (wl1->idx - wl2->idx);
        !            63: }
        !            64:
        !            65: struct winlink *
        !            66: winlink_find_by_index(struct winlinks *wwl, int idx)
        !            67: {
        !            68:        struct winlink  wl;
        !            69:
        !            70:        if (idx < 0)
        !            71:                fatalx("bad index");
        !            72:
        !            73:        wl.idx = idx;
        !            74:        return (RB_FIND(winlinks, wwl, &wl));
        !            75: }
        !            76:
        !            77: int
        !            78: winlink_next_index(struct winlinks *wwl)
        !            79: {
        !            80:        u_int   i;
        !            81:
        !            82:        for (i = 0; i < INT_MAX; i++) {
        !            83:                if (winlink_find_by_index(wwl, i) == NULL)
        !            84:                        return (i);
        !            85:        }
        !            86:
        !            87:        fatalx("no free indexes");
        !            88: }
        !            89:
        !            90: u_int
        !            91: winlink_count(struct winlinks *wwl)
        !            92: {
        !            93:        struct winlink  *wl;
        !            94:        u_int            n;
        !            95:
        !            96:        n = 0;
        !            97:        RB_FOREACH(wl, winlinks, wwl)
        !            98:                n++;
        !            99:
        !           100:        return (n);
        !           101: }
        !           102:
        !           103: struct winlink *
        !           104: winlink_add(struct winlinks *wwl, struct window *w, int idx)
        !           105: {
        !           106:        struct winlink  *wl;
        !           107:
        !           108:        if (idx == -1)
        !           109:                idx = winlink_next_index(wwl);
        !           110:        else if (winlink_find_by_index(wwl, idx) != NULL)
        !           111:                return (NULL);
        !           112:
        !           113:        if (idx < 0)
        !           114:                fatalx("bad index");
        !           115:
        !           116:        wl = xcalloc(1, sizeof *wl);
        !           117:        wl->idx = idx;
        !           118:        wl->window = w;
        !           119:        RB_INSERT(winlinks, wwl, wl);
        !           120:
        !           121:        w->references++;
        !           122:
        !           123:        return (wl);
        !           124: }
        !           125:
        !           126: void
        !           127: winlink_remove(struct winlinks *wwl, struct winlink *wl)
        !           128: {
        !           129:        struct window   *w = wl->window;
        !           130:
        !           131:        RB_REMOVE(winlinks, wwl, wl);
        !           132:        xfree(wl);
        !           133:
        !           134:        if (w->references == 0)
        !           135:                fatal("bad reference count");
        !           136:        w->references--;
        !           137:        if (w->references == 0)
        !           138:                window_destroy(w);
        !           139: }
        !           140:
        !           141: struct winlink *
        !           142: winlink_next(unused struct winlinks *wwl, struct winlink *wl)
        !           143: {
        !           144:        return (RB_NEXT(winlinks, wwl, wl));
        !           145: }
        !           146:
        !           147: struct winlink *
        !           148: winlink_previous(unused struct winlinks *wwl, struct winlink *wl)
        !           149: {
        !           150:        return (RB_PREV(winlinks, wwl, wl));
        !           151: }
        !           152:
        !           153: void
        !           154: winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
        !           155: {
        !           156:        if (wl == NULL)
        !           157:                return;
        !           158:
        !           159:        winlink_stack_remove(stack, wl);
        !           160:        SLIST_INSERT_HEAD(stack, wl, sentry);
        !           161: }
        !           162:
        !           163: void
        !           164: winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
        !           165: {
        !           166:        struct winlink  *wl2;
        !           167:
        !           168:        if (wl == NULL)
        !           169:                return;
        !           170:
        !           171:        SLIST_FOREACH(wl2, stack, sentry) {
        !           172:                if (wl2 == wl) {
        !           173:                        SLIST_REMOVE(stack, wl, winlink, sentry);
        !           174:                        return;
        !           175:                }
        !           176:        }
        !           177: }
        !           178:
        !           179: int
        !           180: window_index(struct window *s, u_int *i)
        !           181: {
        !           182:        for (*i = 0; *i < ARRAY_LENGTH(&windows); (*i)++) {
        !           183:                if (s == ARRAY_ITEM(&windows, *i))
        !           184:                        return (0);
        !           185:        }
        !           186:        return (-1);
        !           187: }
        !           188:
        !           189: struct window *
        !           190: window_create1(u_int sx, u_int sy)
        !           191: {
        !           192:        struct window   *w;
        !           193:        u_int            i;
        !           194:
        !           195:        w = xmalloc(sizeof *w);
        !           196:        w->name = NULL;
        !           197:        w->flags = 0;
        !           198:
        !           199:        TAILQ_INIT(&w->panes);
        !           200:        w->active = NULL;
        !           201:        w->layout = 0;
        !           202:
        !           203:        w->sx = sx;
        !           204:        w->sy = sy;
        !           205:
        !           206:        options_init(&w->options, &global_window_options);
        !           207:
        !           208:        for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
        !           209:                if (ARRAY_ITEM(&windows, i) == NULL) {
        !           210:                        ARRAY_SET(&windows, i, w);
        !           211:                        break;
        !           212:                }
        !           213:        }
        !           214:        if (i == ARRAY_LENGTH(&windows))
        !           215:                ARRAY_ADD(&windows, w);
        !           216:        w->references = 0;
        !           217:
        !           218:        return (w);
        !           219: }
        !           220:
        !           221: struct window *
        !           222: window_create(const char *name, const char *cmd, const char *cwd,
        !           223:     const char **envp, u_int sx, u_int sy, u_int hlimit, char **cause)
        !           224: {
        !           225:        struct window   *w;
        !           226:
        !           227:        w = window_create1(sx, sy);
        !           228:        if (window_add_pane(w, -1, cmd, cwd, envp, hlimit, cause) == NULL) {
        !           229:                window_destroy(w);
        !           230:                return (NULL);
        !           231:        }
        !           232:        w->active = TAILQ_FIRST(&w->panes);
        !           233:
        !           234:        if (name != NULL) {
        !           235:                w->name = xstrdup(name);
        !           236:                options_set_number(&w->options, "automatic-rename", 0);
        !           237:        } else
        !           238:                w->name = default_window_name(w);
        !           239:        return (w);
        !           240: }
        !           241:
        !           242: void
        !           243: window_destroy(struct window *w)
        !           244: {
        !           245:        u_int   i;
        !           246:
        !           247:        if (window_index(w, &i) != 0)
        !           248:                fatalx("index not found");
        !           249:        ARRAY_SET(&windows, i, NULL);
        !           250:        while (!ARRAY_EMPTY(&windows) && ARRAY_LAST(&windows) == NULL)
        !           251:                ARRAY_TRUNC(&windows, 1);
        !           252:
        !           253:        options_free(&w->options);
        !           254:
        !           255:        window_destroy_panes(w);
        !           256:
        !           257:        if (w->name != NULL)
        !           258:                xfree(w->name);
        !           259:        xfree(w);
        !           260: }
        !           261:
        !           262: int
        !           263: window_resize(struct window *w, u_int sx, u_int sy)
        !           264: {
        !           265:        w->sx = sx;
        !           266:        w->sy = sy;
        !           267:
        !           268:        return (0);
        !           269: }
        !           270:
        !           271: void
        !           272: window_set_active_pane(struct window *w, struct window_pane *wp)
        !           273: {
        !           274:        w->active = wp;
        !           275:        while (w->active->flags & PANE_HIDDEN)
        !           276:                w->active = TAILQ_PREV(w->active, window_panes, entry);
        !           277: }
        !           278:
        !           279: struct window_pane *
        !           280: window_add_pane(struct window *w, int wanty, const char *cmd,
        !           281:     const char *cwd, const char **envp, u_int hlimit, char **cause)
        !           282: {
        !           283:        struct window_pane      *wp;
        !           284:        u_int                    sizey;
        !           285:
        !           286:        if (TAILQ_EMPTY(&w->panes))
        !           287:                wanty = w->sy;
        !           288:        else {
        !           289:                sizey = w->active->sy - 1; /* for separator */
        !           290:                if (sizey < PANE_MINIMUM * 2) {
        !           291:                        *cause = xstrdup("pane too small");
        !           292:                        return (NULL);
        !           293:                }
        !           294:
        !           295:                if (wanty == -1)
        !           296:                        wanty = sizey / 2;
        !           297:
        !           298:                if (wanty < PANE_MINIMUM)
        !           299:                        wanty = PANE_MINIMUM;
        !           300:                if ((u_int) wanty > sizey - PANE_MINIMUM)
        !           301:                        wanty = sizey - PANE_MINIMUM;
        !           302:
        !           303:                window_pane_resize(w->active, w->sx, sizey - wanty);
        !           304:        }
        !           305:
        !           306:        wp = window_pane_create(w, w->sx, wanty, hlimit);
        !           307:        if (TAILQ_EMPTY(&w->panes))
        !           308:                TAILQ_INSERT_HEAD(&w->panes, wp, entry);
        !           309:        else
        !           310:                TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry);
        !           311:        if (window_pane_spawn(wp, cmd, cwd, envp, cause) != 0) {
        !           312:                window_remove_pane(w, wp);
        !           313:                return (NULL);
        !           314:        }
        !           315:        return (wp);
        !           316: }
        !           317:
        !           318: void
        !           319: window_remove_pane(struct window *w, struct window_pane *wp)
        !           320: {
        !           321:        w->active = TAILQ_PREV(wp, window_panes, entry);
        !           322:        if (w->active == NULL)
        !           323:                w->active = TAILQ_NEXT(wp, entry);
        !           324:
        !           325:        TAILQ_REMOVE(&w->panes, wp, entry);
        !           326:        window_pane_destroy(wp);
        !           327: }
        !           328:
        !           329: u_int
        !           330: window_index_of_pane(struct window *w, struct window_pane *find)
        !           331: {
        !           332:        struct window_pane      *wp;
        !           333:        u_int                    n;
        !           334:
        !           335:        n = 0;
        !           336:        TAILQ_FOREACH(wp, &w->panes, entry) {
        !           337:                if (wp == find)
        !           338:                        return (n);
        !           339:                n++;
        !           340:        }
        !           341:        fatalx("unknown pane");
        !           342: }
        !           343:
        !           344: struct window_pane *
        !           345: window_pane_at_index(struct window *w, u_int idx)
        !           346: {
        !           347:        struct window_pane      *wp;
        !           348:        u_int                    n;
        !           349:
        !           350:        n = 0;
        !           351:        TAILQ_FOREACH(wp, &w->panes, entry) {
        !           352:                if (n == idx)
        !           353:                        return (wp);
        !           354:                n++;
        !           355:        }
        !           356:        return (NULL);
        !           357: }
        !           358:
        !           359: u_int
        !           360: window_count_panes(struct window *w)
        !           361: {
        !           362:        struct window_pane      *wp;
        !           363:        u_int                    n;
        !           364:
        !           365:        n = 0;
        !           366:        TAILQ_FOREACH(wp, &w->panes, entry)
        !           367:                n++;
        !           368:        return (n);
        !           369: }
        !           370:
        !           371: void
        !           372: window_destroy_panes(struct window *w)
        !           373: {
        !           374:        struct window_pane      *wp;
        !           375:
        !           376:        while (!TAILQ_EMPTY(&w->panes)) {
        !           377:                wp = TAILQ_FIRST(&w->panes);
        !           378:                TAILQ_REMOVE(&w->panes, wp, entry);
        !           379:                window_pane_destroy(wp);
        !           380:        }
        !           381: }
        !           382:
        !           383: struct window_pane *
        !           384: window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
        !           385: {
        !           386:        struct window_pane      *wp;
        !           387:
        !           388:        wp = xcalloc(1, sizeof *wp);
        !           389:        wp->window = w;
        !           390:
        !           391:        wp->cmd = NULL;
        !           392:        wp->cwd = NULL;
        !           393:
        !           394:        wp->fd = -1;
        !           395:        wp->in = buffer_create(BUFSIZ);
        !           396:        wp->out = buffer_create(BUFSIZ);
        !           397:
        !           398:        wp->mode = NULL;
        !           399:
        !           400:        wp->xoff = 0;
        !           401:        wp->yoff = 0;
        !           402:
        !           403:        wp->sx = sx;
        !           404:        wp->sy = sy;
        !           405:
        !           406:        screen_init(&wp->base, sx, sy, hlimit);
        !           407:        wp->screen = &wp->base;
        !           408:
        !           409:        input_init(wp);
        !           410:
        !           411:        return (wp);
        !           412: }
        !           413:
        !           414: void
        !           415: window_pane_destroy(struct window_pane *wp)
        !           416: {
        !           417:        if (wp->fd != -1)
        !           418:                close(wp->fd);
        !           419:
        !           420:        input_free(wp);
        !           421:
        !           422:        window_pane_reset_mode(wp);
        !           423:        screen_free(&wp->base);
        !           424:
        !           425:        buffer_destroy(wp->in);
        !           426:        buffer_destroy(wp->out);
        !           427:
        !           428:        if (wp->cwd != NULL)
        !           429:                xfree(wp->cwd);
        !           430:        if (wp->cmd != NULL)
        !           431:                xfree(wp->cmd);
        !           432:        xfree(wp);
        !           433: }
        !           434:
        !           435: int
        !           436: window_pane_spawn(struct window_pane *wp,
        !           437:     const char *cmd, const char *cwd, const char **envp, char **cause)
        !           438: {
        !           439:        struct winsize   ws;
        !           440:        int              mode;
        !           441:        const char     **envq;
        !           442:        struct timeval   tv;
        !           443:
        !           444:        if (wp->fd != -1)
        !           445:                close(wp->fd);
        !           446:        if (cmd != NULL) {
        !           447:                if (wp->cmd != NULL)
        !           448:                        xfree(wp->cmd);
        !           449:                wp->cmd = xstrdup(cmd);
        !           450:        }
        !           451:        if (cwd != NULL) {
        !           452:                if (wp->cwd != NULL)
        !           453:                        xfree(wp->cwd);
        !           454:                wp->cwd = xstrdup(cwd);
        !           455:        }
        !           456:
        !           457:        memset(&ws, 0, sizeof ws);
        !           458:        ws.ws_col = screen_size_x(&wp->base);
        !           459:        ws.ws_row = screen_size_y(&wp->base);
        !           460:
        !           461:        if (gettimeofday(&wp->window->name_timer, NULL) != 0)
        !           462:                fatal("gettimeofday");
        !           463:        tv.tv_sec = 0;
        !           464:        tv.tv_usec = NAME_INTERVAL * 1000L;
        !           465:        timeradd(&wp->window->name_timer, &tv, &wp->window->name_timer);
        !           466:
        !           467:        switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) {
        !           468:        case -1:
        !           469:                wp->fd = -1;
        !           470:                xasprintf(cause, "%s: %s", cmd, strerror(errno));
        !           471:                return (-1);
        !           472:        case 0:
        !           473:                if (chdir(wp->cwd) != 0)
        !           474:                        chdir("/");
        !           475:                for (envq = envp; *envq != NULL; envq++) {
        !           476:                        if (putenv((char *) *envq) != 0)
        !           477:                                fatal("putenv failed");
        !           478:                }
        !           479:                sigreset();
        !           480:                log_close();
        !           481:
        !           482:                execl(_PATH_BSHELL, "sh", "-c", wp->cmd, (char *) NULL);
        !           483:                fatal("execl failed");
        !           484:        }
        !           485:
        !           486:        if ((mode = fcntl(wp->fd, F_GETFL)) == -1)
        !           487:                fatal("fcntl failed");
        !           488:        if (fcntl(wp->fd, F_SETFL, mode|O_NONBLOCK) == -1)
        !           489:                fatal("fcntl failed");
        !           490:        if (fcntl(wp->fd, F_SETFD, FD_CLOEXEC) == -1)
        !           491:                fatal("fcntl failed");
        !           492:
        !           493:        return (0);
        !           494: }
        !           495:
        !           496: int
        !           497: window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
        !           498: {
        !           499:        struct winsize  ws;
        !           500:
        !           501:        if (sx == wp->sx && sy == wp->sy)
        !           502:                return (-1);
        !           503:        wp->sx = sx;
        !           504:        wp->sy = sy;
        !           505:
        !           506:        memset(&ws, 0, sizeof ws);
        !           507:        ws.ws_col = sx;
        !           508:        ws.ws_row = sy;
        !           509:
        !           510:        screen_resize(&wp->base, sx, sy);
        !           511:        if (wp->mode != NULL)
        !           512:                wp->mode->resize(wp, sx, sy);
        !           513:
        !           514:        if (wp->fd != -1 && ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
        !           515:                fatal("ioctl failed");
        !           516:        return (0);
        !           517: }
        !           518:
        !           519: int
        !           520: window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode)
        !           521: {
        !           522:        struct screen   *s;
        !           523:
        !           524:        if (wp->mode != NULL || wp->mode == mode)
        !           525:                return (1);
        !           526:
        !           527:        wp->mode = mode;
        !           528:
        !           529:        if ((s = wp->mode->init(wp)) != NULL)
        !           530:                wp->screen = s;
        !           531:        server_redraw_window(wp->window);
        !           532:        return (0);
        !           533: }
        !           534:
        !           535: void
        !           536: window_pane_reset_mode(struct window_pane *wp)
        !           537: {
        !           538:        if (wp->mode == NULL)
        !           539:                return;
        !           540:
        !           541:        wp->mode->free(wp);
        !           542:        wp->mode = NULL;
        !           543:
        !           544:        wp->screen = &wp->base;
        !           545:        server_redraw_window(wp->window);
        !           546: }
        !           547:
        !           548: void
        !           549: window_pane_parse(struct window_pane *wp)
        !           550: {
        !           551:        input_parse(wp);
        !           552: }
        !           553:
        !           554: void
        !           555: window_pane_key(struct window_pane *wp, struct client *c, int key)
        !           556: {
        !           557:        if (wp->mode != NULL) {
        !           558:                if (wp->mode->key != NULL)
        !           559:                        wp->mode->key(wp, c, key);
        !           560:        } else
        !           561:                input_key(wp, key);
        !           562: }
        !           563:
        !           564: void
        !           565: window_pane_mouse(
        !           566:     struct window_pane *wp, struct client *c, u_char b, u_char x, u_char y)
        !           567: {
        !           568:        /* XXX convert from 1-based? */
        !           569:
        !           570:        if (x < wp->xoff || x >= wp->xoff + wp->sx)
        !           571:                return;
        !           572:        if (y < wp->yoff || y >= wp->yoff + wp->sy)
        !           573:                return;
        !           574:        x -= wp->xoff;
        !           575:        y -= wp->yoff;
        !           576:
        !           577:        if (wp->mode != NULL) {
        !           578:                if (wp->mode->mouse != NULL)
        !           579:                        wp->mode->mouse(wp, c, b, x, y);
        !           580:        } else
        !           581:                input_mouse(wp, b, x, y);
        !           582: }
        !           583:
        !           584: char *
        !           585: window_pane_search(struct window_pane *wp, const char *searchstr)
        !           586: {
        !           587:        const struct grid_cell  *gc;
        !           588:        const struct grid_utf8  *gu;
        !           589:        char                    *buf, *s;
        !           590:        size_t                   off;
        !           591:        u_int                    i, j, k;
        !           592:
        !           593:        buf = xmalloc(1);
        !           594:
        !           595:        for (j = 0; j < screen_size_y(&wp->base); j++) {
        !           596:                off = 0;
        !           597:                for (i = 0; i < screen_size_x(&wp->base); i++) {
        !           598:                        gc = grid_view_peek_cell(wp->base.grid, i, j);
        !           599:                        if (gc->flags & GRID_FLAG_UTF8) {
        !           600:                                gu = grid_view_peek_utf8(wp->base.grid, i, j);
        !           601:                                buf = xrealloc(buf, 1, off + 8);
        !           602:                                for (k = 0; k < UTF8_SIZE; k++) {
        !           603:                                        if (gu->data[k] == 0xff)
        !           604:                                                break;
        !           605:                                        buf[off++] = gu->data[k];
        !           606:                                }
        !           607:                        } else {
        !           608:                                buf = xrealloc(buf, 1, off + 1);
        !           609:                                buf[off++] = gc->data;
        !           610:                        }
        !           611:                }
        !           612:                while (off > 0 && buf[off - 1] == ' ')
        !           613:                        off--;
        !           614:                buf[off] = '\0';
        !           615:
        !           616:                if ((s = strstr(buf, searchstr)) != NULL) {
        !           617:                        s = section_string(buf, off, s - buf, 40);
        !           618:                        xfree(buf);
        !           619:                        return (s);
        !           620:                }
        !           621:        }
        !           622:
        !           623:        xfree(buf);
        !           624:        return (NULL);
        !           625: }