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>