session.c 17 KB

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