Coverage Report

Created: 2025-07-13 06:52

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