Coverage Report

Created: 2026-05-30 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/session.c
Line
Count
Source
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
0
RB_GENERATE(sessions, session, entry, session_cmp);
Unexecuted instantiation: sessions_RB_REMOVE_COLOR
Unexecuted instantiation: sessions_RB_REMOVE
Unexecuted instantiation: sessions_RB_INSERT
Unexecuted instantiation: sessions_RB_FIND
Unexecuted instantiation: sessions_RB_NFIND
Unexecuted instantiation: sessions_RB_MINMAX
46
0
47
0
int
48
0
session_group_cmp(struct session_group *s1, struct session_group *s2)
49
0
{
50
0
  return (strcmp(s1->name, s2->name));
51
0
}
52
0
RB_GENERATE(session_groups, session_group, entry, session_group_cmp);
Unexecuted instantiation: session_groups_RB_REMOVE_COLOR
Unexecuted instantiation: session_groups_RB_REMOVE
Unexecuted instantiation: session_groups_RB_INSERT
Unexecuted instantiation: session_groups_RB_FIND
Unexecuted instantiation: session_groups_RB_NFIND
Unexecuted instantiation: session_groups_RB_MINMAX
53
0
54
0
/*
55
0
 * Find if session is still alive. This is true if it is still on the global
56
0
 * sessions list.
57
0
 */
58
0
int
59
0
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
/* Lock session if it has timed out. */
233
static void
234
session_lock_timer(__unused int fd, __unused short events, void *arg)
235
0
{
236
0
  struct session  *s = arg;
237
238
0
  if (s->attached == 0)
239
0
    return;
240
241
0
  log_debug("session %s locked, activity time %lld", s->name,
242
0
      (long long)s->activity_time.tv_sec);
243
244
0
  server_lock_session(s);
245
0
  recalculate_sizes();
246
0
}
247
248
/* Update activity time. */
249
void
250
session_update_activity(struct session *s, struct timeval *from)
251
0
{
252
0
  struct timeval   tv;
253
254
0
  if (from == NULL)
255
0
    gettimeofday(&s->activity_time, NULL);
256
0
  else
257
0
    memcpy(&s->activity_time, from, sizeof s->activity_time);
258
259
0
  log_debug("session $%u %s activity %lld.%06d", s->id,
260
0
      s->name, (long long)s->activity_time.tv_sec,
261
0
      (int)s->activity_time.tv_usec);
262
263
0
  if (evtimer_initialized(&s->lock_timer))
264
0
    evtimer_del(&s->lock_timer);
265
0
  else
266
0
    evtimer_set(&s->lock_timer, session_lock_timer, s);
267
268
0
  if (s->attached != 0) {
269
0
    timerclear(&tv);
270
0
    tv.tv_sec = options_get_number(s->options, "lock-after-time");
271
0
    if (tv.tv_sec != 0)
272
0
      evtimer_add(&s->lock_timer, &tv);
273
0
  }
274
0
}
275
276
/* Find the next usable session. */
277
struct session *
278
session_next_session(struct session *s, struct sort_criteria *sort_crit)
279
0
{
280
0
  struct session  **l;
281
0
  u_int     n, i;
282
283
0
  if (RB_EMPTY(&sessions) || !session_alive(s))
284
0
    return (NULL);
285
286
0
  l = sort_get_sessions(&n, sort_crit);
287
0
  for (i = 0; i < n; i++) {
288
0
    if (l[i] == s)
289
0
      break;
290
0
  }
291
0
  if (i == n)
292
0
    fatalx("session %s not found in sorted list", s->name);
293
0
  i++;
294
0
  if (i == n)
295
0
    i = 0;
296
0
  return (l[i]);
297
0
}
298
299
/* Find the previous usable session. */
300
struct session *
301
session_previous_session(struct session *s, struct sort_criteria *sort_crit)
302
0
{
303
0
  struct session  **l;
304
0
  u_int     n, i;
305
306
0
  if (RB_EMPTY(&sessions) || !session_alive(s))
307
0
    return (NULL);
308
309
0
  l = sort_get_sessions(&n, sort_crit);
310
0
  for (i = 0; i < n; i++) {
311
0
    if (l[i] == s)
312
0
      break;
313
0
  }
314
0
  if (i == n)
315
0
    fatalx("session %s not found in sorted list", s->name);
316
0
  if (i == 0)
317
0
    i = n;
318
0
  i--;
319
0
  return (l[i]);
320
0
}
321
322
/* Attach a window to a session. */
323
struct winlink *
324
session_attach(struct session *s, struct window *w, int idx, char **cause)
325
0
{
326
0
  struct winlink  *wl;
327
328
0
  if ((wl = winlink_add(&s->windows, idx)) == NULL) {
329
0
    xasprintf(cause, "index in use: %d", idx);
330
0
    return (NULL);
331
0
  }
332
0
  wl->session = s;
333
0
  winlink_set_window(wl, w);
334
0
  notify_session_window("window-linked", s, w);
335
336
0
  session_group_synchronize_from(s);
337
0
  return (wl);
338
0
}
339
340
/* Detach a window from a session. */
341
int
342
session_detach(struct session *s, struct winlink *wl)
343
0
{
344
0
  if (s->curw == wl &&
345
0
      session_last(s) != 0 &&
346
0
      session_previous(s, 0) != 0)
347
0
    session_next(s, 0);
348
349
0
  wl->flags &= ~WINLINK_ALERTFLAGS;
350
0
  notify_session_window("window-unlinked", s, wl->window);
351
0
  winlink_stack_remove(&s->lastw, wl);
352
0
  winlink_remove(&s->windows, wl);
353
354
0
  session_group_synchronize_from(s);
355
356
0
  if (RB_EMPTY(&s->windows))
357
0
    return (1);
358
0
  return (0);
359
0
}
360
361
/* Return if session has window. */
362
int
363
session_has(struct session *s, struct window *w)
364
0
{
365
0
  struct winlink  *wl;
366
367
0
  TAILQ_FOREACH(wl, &w->winlinks, wentry) {
368
0
    if (wl->session == s)
369
0
      return (1);
370
0
  }
371
0
  return (0);
372
0
}
373
374
/*
375
 * Return 1 if a window is linked outside this session (not including session
376
 * groups). The window must be in this session!
377
 */
378
int
379
session_is_linked(struct session *s, struct window *w)
380
0
{
381
0
  struct session_group  *sg;
382
383
0
  if ((sg = session_group_contains(s)) != NULL)
384
0
    return (w->references != session_group_count(sg));
385
0
  return (w->references != 1);
386
0
}
387
388
static struct winlink *
389
session_next_alert(struct winlink *wl)
390
0
{
391
0
  while (wl != NULL) {
392
0
    if (wl->flags & WINLINK_ALERTFLAGS)
393
0
      break;
394
0
    wl = winlink_next(wl);
395
0
  }
396
0
  return (wl);
397
0
}
398
399
/* Move session to next window. */
400
int
401
session_next(struct session *s, int alert)
402
0
{
403
0
  struct winlink  *wl;
404
405
0
  if (s->curw == NULL)
406
0
    return (-1);
407
408
0
  wl = winlink_next(s->curw);
409
0
  if (alert)
410
0
    wl = session_next_alert(wl);
411
0
  if (wl == NULL) {
412
0
    wl = RB_MIN(winlinks, &s->windows);
413
0
    if (alert && ((wl = session_next_alert(wl)) == NULL))
414
0
      return (-1);
415
0
  }
416
0
  return (session_set_current(s, wl));
417
0
}
418
419
static struct winlink *
420
session_previous_alert(struct winlink *wl)
421
0
{
422
0
  while (wl != NULL) {
423
0
    if (wl->flags & WINLINK_ALERTFLAGS)
424
0
      break;
425
0
    wl = winlink_previous(wl);
426
0
  }
427
0
  return (wl);
428
0
}
429
430
/* Move session to previous window. */
431
int
432
session_previous(struct session *s, int alert)
433
0
{
434
0
  struct winlink  *wl;
435
436
0
  if (s->curw == NULL)
437
0
    return (-1);
438
439
0
  wl = winlink_previous(s->curw);
440
0
  if (alert)
441
0
    wl = session_previous_alert(wl);
442
0
  if (wl == NULL) {
443
0
    wl = RB_MAX(winlinks, &s->windows);
444
0
    if (alert && (wl = session_previous_alert(wl)) == NULL)
445
0
      return (-1);
446
0
  }
447
0
  return (session_set_current(s, wl));
448
0
}
449
450
/* Move session to specific window. */
451
int
452
session_select(struct session *s, int idx)
453
0
{
454
0
  struct winlink  *wl;
455
456
0
  wl = winlink_find_by_index(&s->windows, idx);
457
0
  return (session_set_current(s, wl));
458
0
}
459
460
/* Move session to last used window. */
461
int
462
session_last(struct session *s)
463
0
{
464
0
  struct winlink  *wl;
465
466
0
  wl = TAILQ_FIRST(&s->lastw);
467
0
  if (wl == NULL)
468
0
    return (-1);
469
0
  if (wl == s->curw)
470
0
    return (1);
471
472
0
  return (session_set_current(s, wl));
473
0
}
474
475
/* Set current winlink to wl .*/
476
int
477
session_set_current(struct session *s, struct winlink *wl)
478
0
{
479
0
  struct winlink  *old = s->curw;
480
481
0
  if (wl == NULL)
482
0
    return (-1);
483
0
  if (wl == s->curw)
484
0
    return (1);
485
486
0
  winlink_stack_remove(&s->lastw, wl);
487
0
  winlink_stack_push(&s->lastw, s->curw);
488
0
  s->curw = wl;
489
0
  if (options_get_number(global_options, "focus-events")) {
490
0
    if (old != NULL)
491
0
      window_update_focus(old->window);
492
0
    window_update_focus(wl->window);
493
0
  }
494
0
  winlink_clear_flags(wl);
495
0
  window_update_activity(wl->window);
496
0
  tty_update_window_offset(wl->window);
497
0
  notify_session("session-window-changed", s);
498
0
  return (0);
499
0
}
500
501
/* Find the session group containing a session. */
502
struct session_group *
503
session_group_contains(struct session *target)
504
0
{
505
0
  struct session_group  *sg;
506
0
  struct session    *s;
507
508
0
  RB_FOREACH(sg, session_groups, &session_groups) {
509
0
    TAILQ_FOREACH(s, &sg->sessions, gentry) {
510
0
      if (s == target)
511
0
        return (sg);
512
0
    }
513
0
  }
514
0
  return (NULL);
515
0
}
516
517
/* Find session group by name. */
518
struct session_group *
519
session_group_find(const char *name)
520
0
{
521
0
  struct session_group  sg;
522
523
0
  sg.name = name;
524
0
  return (RB_FIND(session_groups, &session_groups, &sg));
525
0
}
526
527
/* Create a new session group. */
528
struct session_group *
529
session_group_new(const char *name)
530
0
{
531
0
  struct session_group  *sg;
532
533
0
  if ((sg = session_group_find(name)) != NULL)
534
0
    return (sg);
535
536
0
  sg = xcalloc(1, sizeof *sg);
537
0
  sg->name = xstrdup(name);
538
0
  TAILQ_INIT(&sg->sessions);
539
540
0
  RB_INSERT(session_groups, &session_groups, sg);
541
0
  return (sg);
542
0
}
543
544
/* Add a session to a session group. */
545
void
546
session_group_add(struct session_group *sg, struct session *s)
547
0
{
548
0
  if (session_group_contains(s) == NULL)
549
0
    TAILQ_INSERT_TAIL(&sg->sessions, s, gentry);
550
0
}
551
552
/* Remove a session from its group and destroy the group if empty. */
553
static void
554
session_group_remove(struct session *s)
555
0
{
556
0
  struct session_group  *sg;
557
558
0
  if ((sg = session_group_contains(s)) == NULL)
559
0
    return;
560
0
  TAILQ_REMOVE(&sg->sessions, s, gentry);
561
0
  if (TAILQ_EMPTY(&sg->sessions)) {
562
0
    RB_REMOVE(session_groups, &session_groups, sg);
563
0
    free((void *)sg->name);
564
0
    free(sg);
565
0
  }
566
0
}
567
568
/* Count number of sessions in session group. */
569
u_int
570
session_group_count(struct session_group *sg)
571
0
{
572
0
  struct session  *s;
573
0
  u_int    n;
574
575
0
  n = 0;
576
0
  TAILQ_FOREACH(s, &sg->sessions, gentry)
577
0
    n++;
578
0
  return (n);
579
0
}
580
581
/* Count number of clients attached to sessions in session group. */
582
u_int
583
session_group_attached_count(struct session_group *sg)
584
0
{
585
0
  struct session  *s;
586
0
  u_int    n;
587
588
0
  n = 0;
589
0
  TAILQ_FOREACH(s, &sg->sessions, gentry)
590
0
    n += s->attached;
591
0
  return (n);
592
0
}
593
594
/* Synchronize a session to its session group. */
595
void
596
session_group_synchronize_to(struct session *s)
597
0
{
598
0
  struct session_group  *sg;
599
0
  struct session    *target;
600
601
0
  if ((sg = session_group_contains(s)) == NULL)
602
0
    return;
603
604
0
  target = NULL;
605
0
  TAILQ_FOREACH(target, &sg->sessions, gentry) {
606
0
    if (target != s)
607
0
      break;
608
0
  }
609
0
  if (target != NULL)
610
0
    session_group_synchronize1(target, s);
611
0
}
612
613
/* Synchronize a session group to a session. */
614
void
615
session_group_synchronize_from(struct session *target)
616
0
{
617
0
  struct session_group  *sg;
618
0
  struct session    *s;
619
620
0
  if ((sg = session_group_contains(target)) == NULL)
621
0
    return;
622
623
0
  TAILQ_FOREACH(s, &sg->sessions, gentry) {
624
0
    if (s != target)
625
0
      session_group_synchronize1(target, s);
626
0
  }
627
0
}
628
629
/*
630
 * Synchronize a session with a target session. This means destroying all
631
 * winlinks then recreating them, then updating the current window, last window
632
 * stack and alerts.
633
 */
634
static void
635
session_group_synchronize1(struct session *target, struct session *s)
636
0
{
637
0
  struct winlinks    old_windows, *ww;
638
0
  struct winlink_stack   old_lastw;
639
0
  struct winlink    *wl, *wl2;
640
641
  /* Don't do anything if the session is empty (it'll be destroyed). */
642
0
  ww = &target->windows;
643
0
  if (RB_EMPTY(ww))
644
0
    return;
645
646
  /* If the current window has vanished, move to the next now. */
647
0
  if (s->curw != NULL &&
648
0
      winlink_find_by_index(ww, s->curw->idx) == NULL &&
649
0
      session_last(s) != 0 && session_previous(s, 0) != 0)
650
0
    session_next(s, 0);
651
652
  /* Save the old pointer and reset it. */
653
0
  memcpy(&old_windows, &s->windows, sizeof old_windows);
654
0
  RB_INIT(&s->windows);
655
656
  /* Link all the windows from the target. */
657
0
  RB_FOREACH(wl, winlinks, ww) {
658
0
    wl2 = winlink_add(&s->windows, wl->idx);
659
0
    wl2->session = s;
660
0
    winlink_set_window(wl2, wl->window);
661
0
    notify_session_window("window-linked", s, wl2->window);
662
0
    wl2->flags |= wl->flags & WINLINK_ALERTFLAGS;
663
0
  }
664
665
  /* Fix up the current window. */
666
0
  if (s->curw != NULL)
667
0
    s->curw = winlink_find_by_index(&s->windows, s->curw->idx);
668
0
  else
669
0
    s->curw = winlink_find_by_index(&s->windows, target->curw->idx);
670
671
  /* Fix up the last window stack. */
672
0
  memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
673
0
  TAILQ_INIT(&s->lastw);
674
0
  TAILQ_FOREACH(wl, &old_lastw, sentry) {
675
0
    wl2 = winlink_find_by_index(&s->windows, wl->idx);
676
0
    if (wl2 != NULL) {
677
0
      TAILQ_INSERT_TAIL(&s->lastw, wl2, sentry);
678
0
      wl2->flags |= WINLINK_VISITED;
679
0
    }
680
0
  }
681
682
  /* Then free the old winlinks list. */
683
0
  while (!RB_EMPTY(&old_windows)) {
684
0
    wl = RB_ROOT(&old_windows);
685
0
    wl2 = winlink_find_by_window_id(&s->windows, wl->window->id);
686
0
    if (wl2 == NULL)
687
0
      notify_session_window("window-unlinked", s, wl->window);
688
0
    winlink_remove(&old_windows, wl);
689
0
  }
690
0
}
691
692
/* Renumber the windows across winlinks attached to a specific session. */
693
void
694
session_renumber_windows(struct session *s)
695
0
{
696
0
  struct winlink    *wl, *wl1, *wl_new;
697
0
  struct winlinks    old_wins;
698
0
  struct winlink_stack   old_lastw;
699
0
  int      new_idx, new_curw_idx, marked_idx = -1;
700
701
  /* Save and replace old window list. */
702
0
  memcpy(&old_wins, &s->windows, sizeof old_wins);
703
0
  RB_INIT(&s->windows);
704
705
  /* Start renumbering from the base-index if it's set. */
706
0
  new_idx = options_get_number(s->options, "base-index");
707
0
  new_curw_idx = 0;
708
709
  /* Go through the winlinks and assign new indexes. */
710
0
  RB_FOREACH(wl, winlinks, &old_wins) {
711
0
    wl_new = winlink_add(&s->windows, new_idx);
712
0
    wl_new->session = s;
713
0
    winlink_set_window(wl_new, wl->window);
714
0
    wl_new->flags |= wl->flags & WINLINK_ALERTFLAGS;
715
716
0
    if (wl == marked_pane.wl)
717
0
      marked_idx = wl_new->idx;
718
0
    if (wl == s->curw)
719
0
      new_curw_idx = wl_new->idx;
720
721
0
    new_idx++;
722
0
  }
723
724
  /* Fix the stack of last windows now. */
725
0
  memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
726
0
  TAILQ_INIT(&s->lastw);
727
0
  TAILQ_FOREACH(wl, &old_lastw, sentry) {
728
0
    wl->flags &= ~WINLINK_VISITED;
729
0
    wl_new = winlink_find_by_window(&s->windows, wl->window);
730
0
    if (wl_new != NULL) {
731
0
      TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry);
732
0
      wl_new->flags |= WINLINK_VISITED;
733
0
    }
734
0
  }
735
736
  /* Set the current window. */
737
0
  if (marked_idx != -1) {
738
0
    marked_pane.wl = winlink_find_by_index(&s->windows, marked_idx);
739
0
    if (marked_pane.wl == NULL)
740
0
      server_clear_marked();
741
0
  }
742
0
  s->curw = winlink_find_by_index(&s->windows, new_curw_idx);
743
744
  /* Free the old winlinks (reducing window references too). */
745
0
  RB_FOREACH_SAFE(wl, winlinks, &old_wins, wl1)
746
0
    winlink_remove(&old_wins, wl);
747
0
}
748
749
/* Set the PANE_THEMECHANGED flag for every pane in this session. */
750
void
751
session_theme_changed(struct session *s)
752
0
{
753
0
  struct window_pane  *wp;
754
0
  struct winlink    *wl;
755
756
0
  if (s != NULL) {
757
0
    RB_FOREACH(wl, winlinks, &s->windows) {
758
0
      TAILQ_FOREACH(wp, &wl->window->panes, entry)
759
0
          wp->flags |= PANE_THEMECHANGED;
760
0
    }
761
0
  }
762
0
}
763
764
/* Update history for all panes. */
765
void
766
session_update_history(struct session *s)
767
0
{
768
0
  struct winlink    *wl;
769
0
  struct window_pane  *wp;
770
0
  struct grid   *gd;
771
0
  u_int      limit, osize;
772
773
0
  limit = options_get_number(s->options, "history-limit");
774
0
  RB_FOREACH(wl, winlinks, &s->windows) {
775
0
    TAILQ_FOREACH(wp, &wl->window->panes, entry) {
776
0
      gd = wp->base.grid;
777
778
0
      osize = gd->hsize;
779
0
      gd->hlimit = limit;
780
0
      grid_collect_history(gd, 1);
781
782
0
      if (gd->hsize != osize) {
783
0
        log_debug("%s: %%%u %u -> %u", __func__, wp->id,
784
0
            osize, gd->hsize);
785
0
      }
786
0
    }
787
0
  }
788
0
}