Annotation of embedaddon/tmux/session.c, revision 1.1

1.1     ! misho       1: /* $OpenBSD$ */
        !             2: 
        !             3: /*
        !             4:  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
        !             5:  *
        !             6:  * Permission to use, copy, modify, and distribute this software for any
        !             7:  * purpose with or without fee is hereby granted, provided that the above
        !             8:  * copyright notice and this permission notice appear in all copies.
        !             9:  *
        !            10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            14:  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
        !            15:  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
        !            16:  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            17:  */
        !            18: 
        !            19: #include <sys/types.h>
        !            20: #include <sys/time.h>
        !            21: 
        !            22: #include <string.h>
        !            23: #include <stdlib.h>
        !            24: #include <unistd.h>
        !            25: #include <time.h>
        !            26: 
        !            27: #include "tmux.h"
        !            28: 
        !            29: struct sessions                sessions;
        !            30: static u_int           next_session_id;
        !            31: struct session_groups  session_groups;
        !            32: 
        !            33: static void    session_free(int, short, void *);
        !            34: 
        !            35: static void    session_lock_timer(int, short, void *);
        !            36: 
        !            37: static struct winlink *session_next_alert(struct winlink *);
        !            38: static struct winlink *session_previous_alert(struct winlink *);
        !            39: 
        !            40: static void    session_group_remove(struct session *);
        !            41: static u_int   session_group_count(struct session_group *);
        !            42: static void    session_group_synchronize1(struct session *, struct session *);
        !            43: 
        !            44: static u_int   session_group_count(struct session_group *);
        !            45: static void    session_group_synchronize1(struct session *, struct session *);
        !            46: 
        !            47: RB_GENERATE(sessions, session, entry, session_cmp);
        !            48: 
        !            49: int
        !            50: session_cmp(struct session *s1, struct session *s2)
        !            51: {
        !            52:        return (strcmp(s1->name, s2->name));
        !            53: }
        !            54: 
        !            55: RB_GENERATE(session_groups, session_group, entry, session_group_cmp);
        !            56: 
        !            57: int
        !            58: session_group_cmp(struct session_group *s1, struct session_group *s2)
        !            59: {
        !            60:        return (strcmp(s1->name, s2->name));
        !            61: }
        !            62: 
        !            63: /*
        !            64:  * Find if session is still alive. This is true if it is still on the global
        !            65:  * sessions list.
        !            66:  */
        !            67: int
        !            68: session_alive(struct session *s)
        !            69: {
        !            70:        struct session *s_loop;
        !            71: 
        !            72:        RB_FOREACH(s_loop, sessions, &sessions) {
        !            73:                if (s_loop == s)
        !            74:                        return (1);
        !            75:        }
        !            76:        return (0);
        !            77: }
        !            78: 
        !            79: /* Find session by name. */
        !            80: struct session *
        !            81: session_find(const char *name)
        !            82: {
        !            83:        struct session  s;
        !            84: 
        !            85:        s.name = (char *) name;
        !            86:        return (RB_FIND(sessions, &sessions, &s));
        !            87: }
        !            88: 
        !            89: /* Find session by id parsed from a string. */
        !            90: struct session *
        !            91: session_find_by_id_str(const char *s)
        !            92: {
        !            93:        const char      *errstr;
        !            94:        u_int            id;
        !            95: 
        !            96:        if (*s != '$')
        !            97:                return (NULL);
        !            98: 
        !            99:        id = strtonum(s + 1, 0, UINT_MAX, &errstr);
        !           100:        if (errstr != NULL)
        !           101:                return (NULL);
        !           102:        return (session_find_by_id(id));
        !           103: }
        !           104: 
        !           105: /* Find session by id. */
        !           106: struct session *
        !           107: session_find_by_id(u_int id)
        !           108: {
        !           109:        struct session  *s;
        !           110: 
        !           111:        RB_FOREACH(s, sessions, &sessions) {
        !           112:                if (s->id == id)
        !           113:                        return (s);
        !           114:        }
        !           115:        return (NULL);
        !           116: }
        !           117: 
        !           118: /* Create a new session. */
        !           119: struct session *
        !           120: session_create(const char *prefix, const char *name, int argc, char **argv,
        !           121:     const char *path, const char *cwd, struct environ *env, struct termios *tio,
        !           122:     int idx, u_int sx, u_int sy, char **cause)
        !           123: {
        !           124:        struct session  *s;
        !           125:        struct winlink  *wl;
        !           126: 
        !           127:        s = xcalloc(1, sizeof *s);
        !           128:        s->references = 1;
        !           129:        s->flags = 0;
        !           130: 
        !           131:        s->cwd = xstrdup(cwd);
        !           132: 
        !           133:        s->curw = NULL;
        !           134:        TAILQ_INIT(&s->lastw);
        !           135:        RB_INIT(&s->windows);
        !           136: 
        !           137:        s->environ = environ_create();
        !           138:        if (env != NULL)
        !           139:                environ_copy(env, s->environ);
        !           140: 
        !           141:        s->options = options_create(global_s_options);
        !           142:        s->hooks = hooks_create(global_hooks);
        !           143: 
        !           144:        status_update_saved(s);
        !           145: 
        !           146:        s->tio = NULL;
        !           147:        if (tio != NULL) {
        !           148:                s->tio = xmalloc(sizeof *s->tio);
        !           149:                memcpy(s->tio, tio, sizeof *s->tio);
        !           150:        }
        !           151: 
        !           152:        s->sx = sx;
        !           153:        s->sy = sy;
        !           154: 
        !           155:        if (name != NULL) {
        !           156:                s->name = xstrdup(name);
        !           157:                s->id = next_session_id++;
        !           158:        } else {
        !           159:                s->name = NULL;
        !           160:                do {
        !           161:                        s->id = next_session_id++;
        !           162:                        free(s->name);
        !           163:                        if (prefix != NULL)
        !           164:                                xasprintf(&s->name, "%s-%u", prefix, s->id);
        !           165:                        else
        !           166:                                xasprintf(&s->name, "%u", s->id);
        !           167:                } while (RB_FIND(sessions, &sessions, s) != NULL);
        !           168:        }
        !           169:        RB_INSERT(sessions, &sessions, s);
        !           170: 
        !           171:        log_debug("new session %s $%u", s->name, s->id);
        !           172: 
        !           173:        if (gettimeofday(&s->creation_time, NULL) != 0)
        !           174:                fatal("gettimeofday failed");
        !           175:        session_update_activity(s, &s->creation_time);
        !           176: 
        !           177:        if (argc >= 0) {
        !           178:                wl = session_new(s, NULL, argc, argv, path, cwd, idx, cause);
        !           179:                if (wl == NULL) {
        !           180:                        session_destroy(s);
        !           181:                        return (NULL);
        !           182:                }
        !           183:                session_select(s, RB_ROOT(&s->windows)->idx);
        !           184:        }
        !           185: 
        !           186:        log_debug("session %s created", s->name);
        !           187: 
        !           188:        return (s);
        !           189: }
        !           190: 
        !           191: /* Remove a reference from a session. */
        !           192: void
        !           193: session_unref(struct session *s)
        !           194: {
        !           195:        log_debug("session %s has %d references", s->name, s->references);
        !           196: 
        !           197:        s->references--;
        !           198:        if (s->references == 0)
        !           199:                event_once(-1, EV_TIMEOUT, session_free, s, NULL);
        !           200: }
        !           201: 
        !           202: /* Free session. */
        !           203: static void
        !           204: session_free(__unused int fd, __unused short events, void *arg)
        !           205: {
        !           206:        struct session  *s = arg;
        !           207: 
        !           208:        log_debug("session %s freed (%d references)", s->name, s->references);
        !           209: 
        !           210:        if (s->references == 0) {
        !           211:                environ_free(s->environ);
        !           212: 
        !           213:                options_free(s->options);
        !           214:                hooks_free(s->hooks);
        !           215: 
        !           216:                free(s->name);
        !           217:                free(s);
        !           218:        }
        !           219: }
        !           220: 
        !           221: /* Destroy a session. */
        !           222: void
        !           223: session_destroy(struct session *s)
        !           224: {
        !           225:        struct winlink  *wl;
        !           226: 
        !           227:        log_debug("session %s destroyed", s->name);
        !           228:        s->curw = NULL;
        !           229: 
        !           230:        RB_REMOVE(sessions, &sessions, s);
        !           231:        notify_session("session-closed", s);
        !           232: 
        !           233:        free(s->tio);
        !           234: 
        !           235:        if (event_initialized(&s->lock_timer))
        !           236:                event_del(&s->lock_timer);
        !           237: 
        !           238:        session_group_remove(s);
        !           239: 
        !           240:        while (!TAILQ_EMPTY(&s->lastw))
        !           241:                winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw));
        !           242:        while (!RB_EMPTY(&s->windows)) {
        !           243:                wl = RB_ROOT(&s->windows);
        !           244:                notify_session_window("window-unlinked", s, wl->window);
        !           245:                winlink_remove(&s->windows, wl);
        !           246:        }
        !           247: 
        !           248:        free((void *)s->cwd);
        !           249: 
        !           250:        session_unref(s);
        !           251: }
        !           252: 
        !           253: /* Check a session name is valid: not empty and no colons or periods. */
        !           254: int
        !           255: session_check_name(const char *name)
        !           256: {
        !           257:        return (*name != '\0' && name[strcspn(name, ":.")] == '\0');
        !           258: }
        !           259: 
        !           260: /* Lock session if it has timed out. */
        !           261: static void
        !           262: session_lock_timer(__unused int fd, __unused short events, void *arg)
        !           263: {
        !           264:        struct session  *s = arg;
        !           265: 
        !           266:        if (s->flags & SESSION_UNATTACHED)
        !           267:                return;
        !           268: 
        !           269:        log_debug("session %s locked, activity time %lld", s->name,
        !           270:            (long long)s->activity_time.tv_sec);
        !           271: 
        !           272:        server_lock_session(s);
        !           273:        recalculate_sizes();
        !           274: }
        !           275: 
        !           276: /* Update activity time. */
        !           277: void
        !           278: session_update_activity(struct session *s, struct timeval *from)
        !           279: {
        !           280:        struct timeval  *last = &s->last_activity_time;
        !           281:        struct timeval   tv;
        !           282: 
        !           283:        memcpy(last, &s->activity_time, sizeof *last);
        !           284:        if (from == NULL)
        !           285:                gettimeofday(&s->activity_time, NULL);
        !           286:        else
        !           287:                memcpy(&s->activity_time, from, sizeof s->activity_time);
        !           288: 
        !           289:        log_debug("session %s activity %lld.%06d (last %lld.%06d)", s->name,
        !           290:            (long long)s->activity_time.tv_sec, (int)s->activity_time.tv_usec,
        !           291:            (long long)last->tv_sec, (int)last->tv_usec);
        !           292: 
        !           293:        if (evtimer_initialized(&s->lock_timer))
        !           294:                evtimer_del(&s->lock_timer);
        !           295:        else
        !           296:                evtimer_set(&s->lock_timer, session_lock_timer, s);
        !           297: 
        !           298:        if (~s->flags & SESSION_UNATTACHED) {
        !           299:                timerclear(&tv);
        !           300:                tv.tv_sec = options_get_number(s->options, "lock-after-time");
        !           301:                if (tv.tv_sec != 0)
        !           302:                        evtimer_add(&s->lock_timer, &tv);
        !           303:        }
        !           304: }
        !           305: 
        !           306: /* Find the next usable session. */
        !           307: struct session *
        !           308: session_next_session(struct session *s)
        !           309: {
        !           310:        struct session *s2;
        !           311: 
        !           312:        if (RB_EMPTY(&sessions) || !session_alive(s))
        !           313:                return (NULL);
        !           314: 
        !           315:        s2 = RB_NEXT(sessions, &sessions, s);
        !           316:        if (s2 == NULL)
        !           317:                s2 = RB_MIN(sessions, &sessions);
        !           318:        if (s2 == s)
        !           319:                return (NULL);
        !           320:        return (s2);
        !           321: }
        !           322: 
        !           323: /* Find the previous usable session. */
        !           324: struct session *
        !           325: session_previous_session(struct session *s)
        !           326: {
        !           327:        struct session *s2;
        !           328: 
        !           329:        if (RB_EMPTY(&sessions) || !session_alive(s))
        !           330:                return (NULL);
        !           331: 
        !           332:        s2 = RB_PREV(sessions, &sessions, s);
        !           333:        if (s2 == NULL)
        !           334:                s2 = RB_MAX(sessions, &sessions);
        !           335:        if (s2 == s)
        !           336:                return (NULL);
        !           337:        return (s2);
        !           338: }
        !           339: 
        !           340: /* Create a new window on a session. */
        !           341: struct winlink *
        !           342: session_new(struct session *s, const char *name, int argc, char **argv,
        !           343:     const char *path, const char *cwd, int idx, char **cause)
        !           344: {
        !           345:        struct window   *w;
        !           346:        struct winlink  *wl;
        !           347:        struct environ  *env;
        !           348:        const char      *shell;
        !           349:        u_int            hlimit;
        !           350: 
        !           351:        if ((wl = winlink_add(&s->windows, idx)) == NULL) {
        !           352:                xasprintf(cause, "index in use: %d", idx);
        !           353:                return (NULL);
        !           354:        }
        !           355:        wl->session = s;
        !           356: 
        !           357:        shell = options_get_string(s->options, "default-shell");
        !           358:        if (*shell == '\0' || areshell(shell))
        !           359:                shell = _PATH_BSHELL;
        !           360: 
        !           361:        hlimit = options_get_number(s->options, "history-limit");
        !           362:        env = environ_for_session(s);
        !           363:        w = window_create_spawn(name, argc, argv, path, shell, cwd, env, s->tio,
        !           364:            s->sx, s->sy, hlimit, cause);
        !           365:        if (w == NULL) {
        !           366:                winlink_remove(&s->windows, wl);
        !           367:                environ_free(env);
        !           368:                return (NULL);
        !           369:        }
        !           370:        winlink_set_window(wl, w);
        !           371:        environ_free(env);
        !           372:        notify_session_window("window-linked", s, w);
        !           373: 
        !           374:        session_group_synchronize_from(s);
        !           375:        return (wl);
        !           376: }
        !           377: 
        !           378: /* Attach a window to a session. */
        !           379: struct winlink *
        !           380: session_attach(struct session *s, struct window *w, int idx, char **cause)
        !           381: {
        !           382:        struct winlink  *wl;
        !           383: 
        !           384:        if ((wl = winlink_add(&s->windows, idx)) == NULL) {
        !           385:                xasprintf(cause, "index in use: %d", idx);
        !           386:                return (NULL);
        !           387:        }
        !           388:        wl->session = s;
        !           389:        winlink_set_window(wl, w);
        !           390:        notify_session_window("window-linked", s, w);
        !           391: 
        !           392:        session_group_synchronize_from(s);
        !           393:        return (wl);
        !           394: }
        !           395: 
        !           396: /* Detach a window from a session. */
        !           397: int
        !           398: session_detach(struct session *s, struct winlink *wl)
        !           399: {
        !           400:        if (s->curw == wl &&
        !           401:            session_last(s) != 0 &&
        !           402:            session_previous(s, 0) != 0)
        !           403:                session_next(s, 0);
        !           404: 
        !           405:        wl->flags &= ~WINLINK_ALERTFLAGS;
        !           406:        notify_session_window("window-unlinked", s, wl->window);
        !           407:        winlink_stack_remove(&s->lastw, wl);
        !           408:        winlink_remove(&s->windows, wl);
        !           409: 
        !           410:        session_group_synchronize_from(s);
        !           411: 
        !           412:        if (RB_EMPTY(&s->windows)) {
        !           413:                session_destroy(s);
        !           414:                return (1);
        !           415:        }
        !           416:        return (0);
        !           417: }
        !           418: 
        !           419: /* Return if session has window. */
        !           420: int
        !           421: session_has(struct session *s, struct window *w)
        !           422: {
        !           423:        struct winlink  *wl;
        !           424: 
        !           425:        TAILQ_FOREACH(wl, &w->winlinks, wentry) {
        !           426:                if (wl->session == s)
        !           427:                        return (1);
        !           428:        }
        !           429:        return (0);
        !           430: }
        !           431: 
        !           432: /*
        !           433:  * Return 1 if a window is linked outside this session (not including session
        !           434:  * groups). The window must be in this session!
        !           435:  */
        !           436: int
        !           437: session_is_linked(struct session *s, struct window *w)
        !           438: {
        !           439:        struct session_group    *sg;
        !           440: 
        !           441:        if ((sg = session_group_contains(s)) != NULL)
        !           442:                return (w->references != session_group_count(sg));
        !           443:        return (w->references != 1);
        !           444: }
        !           445: 
        !           446: static struct winlink *
        !           447: session_next_alert(struct winlink *wl)
        !           448: {
        !           449:        while (wl != NULL) {
        !           450:                if (wl->flags & WINLINK_ALERTFLAGS)
        !           451:                        break;
        !           452:                wl = winlink_next(wl);
        !           453:        }
        !           454:        return (wl);
        !           455: }
        !           456: 
        !           457: /* Move session to next window. */
        !           458: int
        !           459: session_next(struct session *s, int alert)
        !           460: {
        !           461:        struct winlink  *wl;
        !           462: 
        !           463:        if (s->curw == NULL)
        !           464:                return (-1);
        !           465: 
        !           466:        wl = winlink_next(s->curw);
        !           467:        if (alert)
        !           468:                wl = session_next_alert(wl);
        !           469:        if (wl == NULL) {
        !           470:                wl = RB_MIN(winlinks, &s->windows);
        !           471:                if (alert && ((wl = session_next_alert(wl)) == NULL))
        !           472:                        return (-1);
        !           473:        }
        !           474:        return (session_set_current(s, wl));
        !           475: }
        !           476: 
        !           477: static struct winlink *
        !           478: session_previous_alert(struct winlink *wl)
        !           479: {
        !           480:        while (wl != NULL) {
        !           481:                if (wl->flags & WINLINK_ALERTFLAGS)
        !           482:                        break;
        !           483:                wl = winlink_previous(wl);
        !           484:        }
        !           485:        return (wl);
        !           486: }
        !           487: 
        !           488: /* Move session to previous window. */
        !           489: int
        !           490: session_previous(struct session *s, int alert)
        !           491: {
        !           492:        struct winlink  *wl;
        !           493: 
        !           494:        if (s->curw == NULL)
        !           495:                return (-1);
        !           496: 
        !           497:        wl = winlink_previous(s->curw);
        !           498:        if (alert)
        !           499:                wl = session_previous_alert(wl);
        !           500:        if (wl == NULL) {
        !           501:                wl = RB_MAX(winlinks, &s->windows);
        !           502:                if (alert && (wl = session_previous_alert(wl)) == NULL)
        !           503:                        return (-1);
        !           504:        }
        !           505:        return (session_set_current(s, wl));
        !           506: }
        !           507: 
        !           508: /* Move session to specific window. */
        !           509: int
        !           510: session_select(struct session *s, int idx)
        !           511: {
        !           512:        struct winlink  *wl;
        !           513: 
        !           514:        wl = winlink_find_by_index(&s->windows, idx);
        !           515:        return (session_set_current(s, wl));
        !           516: }
        !           517: 
        !           518: /* Move session to last used window. */
        !           519: int
        !           520: session_last(struct session *s)
        !           521: {
        !           522:        struct winlink  *wl;
        !           523: 
        !           524:        wl = TAILQ_FIRST(&s->lastw);
        !           525:        if (wl == NULL)
        !           526:                return (-1);
        !           527:        if (wl == s->curw)
        !           528:                return (1);
        !           529: 
        !           530:        return (session_set_current(s, wl));
        !           531: }
        !           532: 
        !           533: /* Set current winlink to wl .*/
        !           534: int
        !           535: session_set_current(struct session *s, struct winlink *wl)
        !           536: {
        !           537:        if (wl == NULL)
        !           538:                return (-1);
        !           539:        if (wl == s->curw)
        !           540:                return (1);
        !           541: 
        !           542:        winlink_stack_remove(&s->lastw, wl);
        !           543:        winlink_stack_push(&s->lastw, s->curw);
        !           544:        s->curw = wl;
        !           545:        winlink_clear_flags(wl);
        !           546:        window_update_activity(wl->window);
        !           547:        return (0);
        !           548: }
        !           549: 
        !           550: /* Find the session group containing a session. */
        !           551: struct session_group *
        !           552: session_group_contains(struct session *target)
        !           553: {
        !           554:        struct session_group    *sg;
        !           555:        struct session          *s;
        !           556: 
        !           557:        RB_FOREACH(sg, session_groups, &session_groups) {
        !           558:                TAILQ_FOREACH(s, &sg->sessions, gentry) {
        !           559:                        if (s == target)
        !           560:                                return (sg);
        !           561:                }
        !           562:        }
        !           563:        return (NULL);
        !           564: }
        !           565: 
        !           566: /* Find session group by name. */
        !           567: struct session_group *
        !           568: session_group_find(const char *name)
        !           569: {
        !           570:        struct session_group    sg;
        !           571: 
        !           572:        sg.name = name;
        !           573:        return (RB_FIND(session_groups, &session_groups, &sg));
        !           574: }
        !           575: 
        !           576: /* Create a new session group. */
        !           577: struct session_group *
        !           578: session_group_new(const char *name)
        !           579: {
        !           580:        struct session_group    *sg;
        !           581: 
        !           582:        if ((sg = session_group_find(name)) != NULL)
        !           583:                return (sg);
        !           584: 
        !           585:        sg = xcalloc(1, sizeof *sg);
        !           586:        sg->name = xstrdup(name);
        !           587:        TAILQ_INIT(&sg->sessions);
        !           588: 
        !           589:        RB_INSERT(session_groups, &session_groups, sg);
        !           590:        return (sg);
        !           591: }
        !           592: 
        !           593: /* Add a session to a session group. */
        !           594: void
        !           595: session_group_add(struct session_group *sg, struct session *s)
        !           596: {
        !           597:        if (session_group_contains(s) == NULL)
        !           598:                TAILQ_INSERT_TAIL(&sg->sessions, s, gentry);
        !           599: }
        !           600: 
        !           601: /* Remove a session from its group and destroy the group if empty. */
        !           602: static void
        !           603: session_group_remove(struct session *s)
        !           604: {
        !           605:        struct session_group    *sg;
        !           606: 
        !           607:        if ((sg = session_group_contains(s)) == NULL)
        !           608:                return;
        !           609:        TAILQ_REMOVE(&sg->sessions, s, gentry);
        !           610:        if (TAILQ_EMPTY(&sg->sessions)) {
        !           611:                RB_REMOVE(session_groups, &session_groups, sg);
        !           612:                free(sg);
        !           613:        }
        !           614: }
        !           615: 
        !           616: /* Count number of sessions in session group. */
        !           617: static u_int
        !           618: session_group_count(struct session_group *sg)
        !           619: {
        !           620:        struct session  *s;
        !           621:        u_int            n;
        !           622: 
        !           623:        n = 0;
        !           624:        TAILQ_FOREACH(s, &sg->sessions, gentry)
        !           625:            n++;
        !           626:        return (n);
        !           627: }
        !           628: 
        !           629: /* Synchronize a session to its session group. */
        !           630: void
        !           631: session_group_synchronize_to(struct session *s)
        !           632: {
        !           633:        struct session_group    *sg;
        !           634:        struct session          *target;
        !           635: 
        !           636:        if ((sg = session_group_contains(s)) == NULL)
        !           637:                return;
        !           638: 
        !           639:        target = NULL;
        !           640:        TAILQ_FOREACH(target, &sg->sessions, gentry) {
        !           641:                if (target != s)
        !           642:                        break;
        !           643:        }
        !           644:        if (target != NULL)
        !           645:                session_group_synchronize1(target, s);
        !           646: }
        !           647: 
        !           648: /* Synchronize a session group to a session. */
        !           649: void
        !           650: session_group_synchronize_from(struct session *target)
        !           651: {
        !           652:        struct session_group    *sg;
        !           653:        struct session          *s;
        !           654: 
        !           655:        if ((sg = session_group_contains(target)) == NULL)
        !           656:                return;
        !           657: 
        !           658:        TAILQ_FOREACH(s, &sg->sessions, gentry) {
        !           659:                if (s != target)
        !           660:                        session_group_synchronize1(target, s);
        !           661:        }
        !           662: }
        !           663: 
        !           664: /*
        !           665:  * Synchronize a session with a target session. This means destroying all
        !           666:  * winlinks then recreating them, then updating the current window, last window
        !           667:  * stack and alerts.
        !           668:  */
        !           669: static void
        !           670: session_group_synchronize1(struct session *target, struct session *s)
        !           671: {
        !           672:        struct winlinks          old_windows, *ww;
        !           673:        struct winlink_stack     old_lastw;
        !           674:        struct winlink          *wl, *wl2;
        !           675: 
        !           676:        /* Don't do anything if the session is empty (it'll be destroyed). */
        !           677:        ww = &target->windows;
        !           678:        if (RB_EMPTY(ww))
        !           679:                return;
        !           680: 
        !           681:        /* If the current window has vanished, move to the next now. */
        !           682:        if (s->curw != NULL &&
        !           683:            winlink_find_by_index(ww, s->curw->idx) == NULL &&
        !           684:            session_last(s) != 0 && session_previous(s, 0) != 0)
        !           685:                session_next(s, 0);
        !           686: 
        !           687:        /* Save the old pointer and reset it. */
        !           688:        memcpy(&old_windows, &s->windows, sizeof old_windows);
        !           689:        RB_INIT(&s->windows);
        !           690: 
        !           691:        /* Link all the windows from the target. */
        !           692:        RB_FOREACH(wl, winlinks, ww) {
        !           693:                wl2 = winlink_add(&s->windows, wl->idx);
        !           694:                wl2->session = s;
        !           695:                winlink_set_window(wl2, wl->window);
        !           696:                notify_session_window("window-linked", s, wl2->window);
        !           697:                wl2->flags |= wl->flags & WINLINK_ALERTFLAGS;
        !           698:        }
        !           699: 
        !           700:        /* Fix up the current window. */
        !           701:        if (s->curw != NULL)
        !           702:                s->curw = winlink_find_by_index(&s->windows, s->curw->idx);
        !           703:        else
        !           704:                s->curw = winlink_find_by_index(&s->windows, target->curw->idx);
        !           705: 
        !           706:        /* Fix up the last window stack. */
        !           707:        memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
        !           708:        TAILQ_INIT(&s->lastw);
        !           709:        TAILQ_FOREACH(wl, &old_lastw, sentry) {
        !           710:                wl2 = winlink_find_by_index(&s->windows, wl->idx);
        !           711:                if (wl2 != NULL)
        !           712:                        TAILQ_INSERT_TAIL(&s->lastw, wl2, sentry);
        !           713:        }
        !           714: 
        !           715:        /* Then free the old winlinks list. */
        !           716:        while (!RB_EMPTY(&old_windows)) {
        !           717:                wl = RB_ROOT(&old_windows);
        !           718:                wl2 = winlink_find_by_window_id(&s->windows, wl->window->id);
        !           719:                if (wl2 == NULL)
        !           720:                        notify_session_window("window-unlinked", s, wl->window);
        !           721:                winlink_remove(&old_windows, wl);
        !           722:        }
        !           723: }
        !           724: 
        !           725: /* Renumber the windows across winlinks attached to a specific session. */
        !           726: void
        !           727: session_renumber_windows(struct session *s)
        !           728: {
        !           729:        struct winlink          *wl, *wl1, *wl_new;
        !           730:        struct winlinks          old_wins;
        !           731:        struct winlink_stack     old_lastw;
        !           732:        int                      new_idx, new_curw_idx;
        !           733: 
        !           734:        /* Save and replace old window list. */
        !           735:        memcpy(&old_wins, &s->windows, sizeof old_wins);
        !           736:        RB_INIT(&s->windows);
        !           737: 
        !           738:        /* Start renumbering from the base-index if it's set. */
        !           739:        new_idx = options_get_number(s->options, "base-index");
        !           740:        new_curw_idx = 0;
        !           741: 
        !           742:        /* Go through the winlinks and assign new indexes. */
        !           743:        RB_FOREACH(wl, winlinks, &old_wins) {
        !           744:                wl_new = winlink_add(&s->windows, new_idx);
        !           745:                wl_new->session = s;
        !           746:                winlink_set_window(wl_new, wl->window);
        !           747:                wl_new->flags |= wl->flags & WINLINK_ALERTFLAGS;
        !           748: 
        !           749:                if (wl == s->curw)
        !           750:                        new_curw_idx = wl_new->idx;
        !           751: 
        !           752:                new_idx++;
        !           753:        }
        !           754: 
        !           755:        /* Fix the stack of last windows now. */
        !           756:        memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
        !           757:        TAILQ_INIT(&s->lastw);
        !           758:        TAILQ_FOREACH(wl, &old_lastw, sentry) {
        !           759:                wl_new = winlink_find_by_window(&s->windows, wl->window);
        !           760:                if (wl_new != NULL)
        !           761:                        TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry);
        !           762:        }
        !           763: 
        !           764:        /* Set the current window. */
        !           765:        s->curw = winlink_find_by_index(&s->windows, new_curw_idx);
        !           766: 
        !           767:        /* Free the old winlinks (reducing window references too). */
        !           768:        RB_FOREACH_SAFE(wl, winlinks, &old_wins, wl1)
        !           769:                winlink_remove(&old_wins, wl);
        !           770: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>