Coverage Report

Created: 2026-10-28 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/session.c
Line
Count
Source
1
/* $OpenBSD: session.c,v 1.109 2026/09/25 08:46:06 nicm Exp $ */
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
39.5k
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
sessions_RB_MINMAX
Line
Count
Source
45
RB_GENERATE(sessions, session, entry, session_cmp);
46
39.5k
47
39.5k
int
48
39.5k
session_group_cmp(struct session_group *s1, struct session_group *s2)
49
39.5k
{
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
0
  s->cwd = xstrdup(cwd);
120
121
0
  TAILQ_INIT(&s->lastw);
122
0
  RB_INIT(&s->windows);
123
124
0
  s->environ = env;
125
0
  s->options = oo;
126
127
0
  status_update_cache(s);
128
129
0
  s->tio = NULL;
130
0
  if (tio != NULL) {
131
0
    s->tio = xmalloc(sizeof *s->tio);
132
0
    memcpy(s->tio, tio, sizeof *s->tio);
133
0
  }
134
135
0
  if (name != NULL) {
136
0
    s->name = xstrdup(name);
137
0
    s->id = next_session_id++;
138
0
  } else {
139
0
    do {
140
0
      s->id = next_session_id++;
141
0
      free(s->name);
142
0
      if (prefix != NULL)
143
0
        xasprintf(&s->name, "%s-%u", prefix, s->id);
144
0
      else
145
0
        xasprintf(&s->name, "%u", s->id);
146
0
    } while (RB_FIND(sessions, &sessions, s) != NULL);
147
0
  }
148
0
  RB_INSERT(sessions, &sessions, s);
149
150
0
  log_debug("new session %s $%u", s->name, s->id);
151
152
0
  if (gettimeofday(&s->creation_time, NULL) != 0)
153
0
    fatal("gettimeofday failed");
154
0
  session_update_activity(s, &s->creation_time);
155
156
0
  return (s);
157
0
}
158
159
/* Add a reference to a session. */
160
void
161
session_add_ref(struct session *s, const char *from)
162
0
{
163
0
  s->references++;
164
0
  log_debug("%s: %s %s, now %d", __func__, s->name, from, s->references);
165
0
}
166
167
/* Remove a reference from a session. */
168
void
169
session_remove_ref(struct session *s, const char *from)
170
0
{
171
0
  s->references--;
172
0
  log_debug("%s: %s %s, now %d", __func__, s->name, from, s->references);
173
174
0
  if (s->references == 0)
175
0
    event_once(-1, EV_TIMEOUT, session_free, s, NULL);
176
0
}
177
178
/* Free session. */
179
static void
180
session_free(__unused int fd, __unused short events, void *arg)
181
0
{
182
0
  struct session  *s = arg;
183
184
0
  log_debug("session %s freed (%d references)", s->name, s->references);
185
186
0
  if (s->references == 0) {
187
0
    environ_free(s->environ);
188
0
    options_free(s->options);
189
190
0
    free(s->name);
191
0
    free(s);
192
0
  }
193
0
}
194
195
/* Destroy a session. */
196
void
197
session_destroy(struct session *s, int notify, const char *from)
198
0
{
199
0
  struct winlink  *wl;
200
201
0
  log_debug("session %s destroyed (%s)", s->name, from);
202
203
0
  if (s->curw == NULL)
204
0
    return;
205
0
  s->curw = NULL;
206
207
0
  RB_REMOVE(sessions, &sessions, s);
208
0
  if (notify)
209
0
    events_fire_session("session-closed", s);
210
211
0
  free(s->tio);
212
213
0
  if (event_initialized(&s->lock_timer))
214
0
    event_del(&s->lock_timer);
215
216
0
  session_group_remove(s);
217
218
0
  while (!TAILQ_EMPTY(&s->lastw))
219
0
    winlink_stack_remove(&s->lastw, TAILQ_FIRST(&s->lastw));
220
0
  while (!RB_EMPTY(&s->windows)) {
221
0
    wl = RB_ROOT(&s->windows);
222
0
    events_fire_winlink("window-unlinked", wl);
223
0
    winlink_remove(&s->windows, wl);
224
0
  }
225
226
0
  free((void *)s->cwd);
227
228
0
  session_remove_ref(s, __func__);
229
0
}
230
231
/* Lock session if it has timed out. */
232
static void
233
session_lock_timer(__unused int fd, __unused short events, void *arg)
234
0
{
235
0
  struct session  *s = arg;
236
237
0
  if (s->attached == 0)
238
0
    return;
239
240
0
  log_debug("session %s locked, activity time %lld", s->name,
241
0
      (long long)s->activity_time.tv_sec);
242
243
0
  server_lock_session(s);
244
0
  recalculate_sizes();
245
0
}
246
247
/* Update activity time. */
248
void
249
session_update_activity(struct session *s, struct timeval *from)
250
0
{
251
0
  struct timeval   tv;
252
253
0
  if (from == NULL)
254
0
    gettimeofday(&s->activity_time, NULL);
255
0
  else
256
0
    memcpy(&s->activity_time, from, sizeof s->activity_time);
257
258
0
  log_debug("session $%u %s activity %lld.%06d", s->id,
259
0
      s->name, (long long)s->activity_time.tv_sec,
260
0
      (int)s->activity_time.tv_usec);
261
262
0
  if (evtimer_initialized(&s->lock_timer))
263
0
    evtimer_del(&s->lock_timer);
264
0
  else
265
0
    evtimer_set(&s->lock_timer, session_lock_timer, s);
266
267
0
  if (s->attached != 0) {
268
0
    timerclear(&tv);
269
0
    tv.tv_sec = options_get_number(s->options, "lock-after-time");
270
0
    if (tv.tv_sec != 0)
271
0
      evtimer_add(&s->lock_timer, &tv);
272
0
  }
273
0
}
274
275
/* Find the next usable session. */
276
struct session *
277
session_next_session(struct session *s, struct sort_criteria *sort_crit)
278
0
{
279
0
  struct session  **l;
280
0
  u_int     n, i;
281
282
0
  if (RB_EMPTY(&sessions) || !session_alive(s))
283
0
    return (NULL);
284
285
0
  l = sort_get_sessions(&n, sort_crit);
286
0
  for (i = 0; i < n; i++) {
287
0
    if (l[i] == s)
288
0
      break;
289
0
  }
290
0
  if (i == n)
291
0
    fatalx("session %s not found in sorted list", s->name);
292
0
  i++;
293
0
  if (i == n)
294
0
    i = 0;
295
0
  return (l[i]);
296
0
}
297
298
/* Find the previous usable session. */
299
struct session *
300
session_previous_session(struct session *s, struct sort_criteria *sort_crit)
301
0
{
302
0
  struct session  **l;
303
0
  u_int     n, i;
304
305
0
  if (RB_EMPTY(&sessions) || !session_alive(s))
306
0
    return (NULL);
307
308
0
  l = sort_get_sessions(&n, sort_crit);
309
0
  for (i = 0; i < n; i++) {
310
0
    if (l[i] == s)
311
0
      break;
312
0
  }
313
0
  if (i == n)
314
0
    fatalx("session %s not found in sorted list", s->name);
315
0
  if (i == 0)
316
0
    i = n;
317
0
  i--;
318
0
  return (l[i]);
319
0
}
320
321
/* Attach a window to a session. */
322
struct winlink *
323
session_attach(struct session *s, struct window *w, int idx, char **cause)
324
0
{
325
0
  struct winlink  *wl;
326
327
0
  if ((wl = winlink_add(&s->windows, idx)) == NULL) {
328
0
    xasprintf(cause, "index in use: %d", idx);
329
0
    return (NULL);
330
0
  }
331
0
  wl->session = s;
332
0
  winlink_set_window(wl, w);
333
0
  events_fire_winlink("window-linked", wl);
334
335
0
  session_group_synchronize_from(s);
336
0
  return (wl);
337
0
}
338
339
/*
340
 * Detach a window from a session. Returns 1 if the window has not been
341
 * detached - the caller must destroy the session.
342
 */
343
int
344
session_detach(struct session *s, struct winlink *wl)
345
0
{
346
0
  if (RB_MIN(winlinks, &s->windows) == wl &&
347
0
      RB_MAX(winlinks, &s->windows) == wl)
348
0
    return (1);
349
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
  events_fire_winlink("window-unlinked", wl);
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
  return (0);
363
0
}
364
365
/* Return if session has window. */
366
int
367
session_has(struct session *s, struct window *w)
368
0
{
369
0
  struct winlink  *wl;
370
371
0
  TAILQ_FOREACH(wl, &w->winlinks, wentry) {
372
0
    if (wl->session == s)
373
0
      return (1);
374
0
  }
375
0
  return (0);
376
0
}
377
378
/*
379
 * Return 1 if a window is linked outside this session (not including session
380
 * groups). The window must be in this session!
381
 */
382
int
383
session_is_linked(struct session *s, struct window *w)
384
0
{
385
0
  struct session_group  *sg;
386
387
0
  if ((sg = session_group_contains(s)) != NULL)
388
0
    return (w->references != session_group_count(sg));
389
0
  return (w->references != 1);
390
0
}
391
392
static struct winlink *
393
session_next_alert(struct winlink *wl)
394
0
{
395
0
  while (wl != NULL) {
396
0
    if (wl->flags & WINLINK_ALERTFLAGS)
397
0
      break;
398
0
    wl = winlink_next(wl);
399
0
  }
400
0
  return (wl);
401
0
}
402
403
/* Move session to next window. */
404
int
405
session_next(struct session *s, int alert)
406
0
{
407
0
  struct winlink  *wl;
408
409
0
  if (s->curw == NULL)
410
0
    return (-1);
411
412
0
  wl = winlink_next(s->curw);
413
0
  if (alert)
414
0
    wl = session_next_alert(wl);
415
0
  if (wl == NULL) {
416
0
    wl = RB_MIN(winlinks, &s->windows);
417
0
    if (alert && ((wl = session_next_alert(wl)) == NULL))
418
0
      return (-1);
419
0
  }
420
0
  return (session_set_current(s, wl));
421
0
}
422
423
static struct winlink *
424
session_previous_alert(struct winlink *wl)
425
0
{
426
0
  while (wl != NULL) {
427
0
    if (wl->flags & WINLINK_ALERTFLAGS)
428
0
      break;
429
0
    wl = winlink_previous(wl);
430
0
  }
431
0
  return (wl);
432
0
}
433
434
/* Move session to previous window. */
435
int
436
session_previous(struct session *s, int alert)
437
0
{
438
0
  struct winlink  *wl;
439
440
0
  if (s->curw == NULL)
441
0
    return (-1);
442
443
0
  wl = winlink_previous(s->curw);
444
0
  if (alert)
445
0
    wl = session_previous_alert(wl);
446
0
  if (wl == NULL) {
447
0
    wl = RB_MAX(winlinks, &s->windows);
448
0
    if (alert && (wl = session_previous_alert(wl)) == NULL)
449
0
      return (-1);
450
0
  }
451
0
  return (session_set_current(s, wl));
452
0
}
453
454
/* Move session to specific window. */
455
int
456
session_select(struct session *s, int idx)
457
0
{
458
0
  struct winlink  *wl;
459
460
0
  wl = winlink_find_by_index(&s->windows, idx);
461
0
  return (session_set_current(s, wl));
462
0
}
463
464
/* Move session to last used window. */
465
int
466
session_last(struct session *s)
467
0
{
468
0
  struct winlink  *wl;
469
470
0
  wl = TAILQ_FIRST(&s->lastw);
471
0
  if (wl == NULL)
472
0
    return (-1);
473
0
  if (wl == s->curw)
474
0
    return (1);
475
476
0
  return (session_set_current(s, wl));
477
0
}
478
479
/* Fire session window changed. */
480
static void
481
session_fire_window_changed(struct session *s, struct winlink *wl,
482
    struct winlink *old)
483
0
{
484
0
  struct event_payload  *ep;
485
0
  struct cmd_find_state  fs;
486
487
0
  ep = event_payload_create();
488
0
  cmd_find_from_winlink(&fs, wl, 0);
489
0
  event_payload_set_target(ep, &fs);
490
0
  event_payload_set_session(ep, "session", s);
491
0
  event_payload_set_window(ep, "window", wl->window);
492
0
  event_payload_set_window(ep, "new_window", wl->window);
493
0
  event_payload_set_int(ep, "window_index", wl->idx);
494
0
  event_payload_set_int(ep, "new_window_index", wl->idx);
495
0
  if (old != NULL) {
496
0
    event_payload_set_window(ep, "old_window", old->window);
497
0
    event_payload_set_int(ep, "old_window_index", old->idx);
498
0
  }
499
0
  events_fire("session-window-changed", ep);
500
0
}
501
502
/* Set current winlink to wl .*/
503
int
504
session_set_current(struct session *s, struct winlink *wl)
505
0
{
506
0
  struct winlink  *old = s->curw;
507
508
0
  if (wl == NULL)
509
0
    return (-1);
510
0
  if (wl == s->curw)
511
0
    return (1);
512
513
0
  winlink_stack_remove(&s->lastw, wl);
514
0
  winlink_stack_push(&s->lastw, s->curw);
515
0
  s->curw = wl;
516
0
  if (options_get_number(global_options, "focus-events")) {
517
0
    if (old != NULL)
518
0
      window_update_focus(old->window);
519
0
    window_update_focus(wl->window);
520
0
  }
521
0
  winlink_clear_flags(wl);
522
0
  window_update_activity(wl->window);
523
0
  tty_update_window_offset(wl->window);
524
0
  session_fire_window_changed(s, wl, old);
525
0
  return (0);
526
0
}
527
528
/* Find the session group containing a session. */
529
struct session_group *
530
session_group_contains(struct session *target)
531
0
{
532
0
  struct session_group  *sg;
533
0
  struct session    *s;
534
535
0
  RB_FOREACH(sg, session_groups, &session_groups) {
536
0
    TAILQ_FOREACH(s, &sg->sessions, gentry) {
537
0
      if (s == target)
538
0
        return (sg);
539
0
    }
540
0
  }
541
0
  return (NULL);
542
0
}
543
544
/* Find session group by name. */
545
struct session_group *
546
session_group_find(const char *name)
547
0
{
548
0
  struct session_group  sg;
549
550
0
  sg.name = name;
551
0
  return (RB_FIND(session_groups, &session_groups, &sg));
552
0
}
553
554
/* Create a new session group. */
555
struct session_group *
556
session_group_new(const char *name)
557
0
{
558
0
  struct session_group  *sg;
559
560
0
  if ((sg = session_group_find(name)) != NULL)
561
0
    return (sg);
562
563
0
  sg = xcalloc(1, sizeof *sg);
564
0
  sg->name = xstrdup(name);
565
0
  TAILQ_INIT(&sg->sessions);
566
567
0
  RB_INSERT(session_groups, &session_groups, sg);
568
0
  return (sg);
569
0
}
570
571
/* Fire session group changed. */
572
static void
573
session_group_fire(const char *name, struct session_group *sg,
574
    struct session *s)
575
0
{
576
0
  struct event_payload  *ep;
577
0
  struct cmd_find_state  fs;
578
579
0
  ep = event_payload_create();
580
0
  if (session_alive(s)) {
581
0
    cmd_find_from_session(&fs, s, 0);
582
0
    event_payload_set_target(ep, &fs);
583
0
  }
584
0
  event_payload_set_session(ep, "session", s);
585
0
  event_payload_set_string(ep, "group", "%s", sg->name);
586
0
  event_payload_set_uint(ep, "group_size", session_group_count(sg));
587
0
  events_fire(name, ep);
588
0
}
589
590
/* Add a session to a session group. */
591
void
592
session_group_add(struct session_group *sg, struct session *s)
593
0
{
594
0
  if (session_group_contains(s) == NULL) {
595
0
    TAILQ_INSERT_TAIL(&sg->sessions, s, gentry);
596
0
    session_group_fire("session-added-to-group", sg, s);
597
0
  }
598
0
}
599
600
/* Remove a session from its group and destroy the group if empty. */
601
static void
602
session_group_remove(struct session *s)
603
0
{
604
0
  struct session_group  *sg;
605
606
0
  if ((sg = session_group_contains(s)) == NULL)
607
0
    return;
608
0
  session_group_fire("session-removed-from-group", sg, s);
609
0
  TAILQ_REMOVE(&sg->sessions, s, gentry);
610
0
  if (TAILQ_EMPTY(&sg->sessions)) {
611
0
    RB_REMOVE(session_groups, &session_groups, sg);
612
0
    free((void *)sg->name);
613
0
    free(sg);
614
0
  }
615
0
}
616
617
/* Count number of sessions in session group. */
618
u_int
619
session_group_count(struct session_group *sg)
620
0
{
621
0
  struct session  *s;
622
0
  u_int    n;
623
624
0
  n = 0;
625
0
  TAILQ_FOREACH(s, &sg->sessions, gentry)
626
0
    n++;
627
0
  return (n);
628
0
}
629
630
/* Count number of clients attached to sessions in session group. */
631
u_int
632
session_group_attached_count(struct session_group *sg)
633
0
{
634
0
  struct session  *s;
635
0
  u_int    n;
636
637
0
  n = 0;
638
0
  TAILQ_FOREACH(s, &sg->sessions, gentry)
639
0
    n += s->attached;
640
0
  return (n);
641
0
}
642
643
/* Synchronize a session to its session group. */
644
void
645
session_group_synchronize_to(struct session *s)
646
0
{
647
0
  struct session_group  *sg;
648
0
  struct session    *target;
649
650
0
  if ((sg = session_group_contains(s)) == NULL)
651
0
    return;
652
653
0
  target = NULL;
654
0
  TAILQ_FOREACH(target, &sg->sessions, gentry) {
655
0
    if (target != s)
656
0
      break;
657
0
  }
658
0
  if (target != NULL)
659
0
    session_group_synchronize1(target, s);
660
0
}
661
662
/* Synchronize a session group to a session. */
663
void
664
session_group_synchronize_from(struct session *target)
665
0
{
666
0
  struct session_group  *sg;
667
0
  struct session    *s;
668
669
0
  if ((sg = session_group_contains(target)) == NULL)
670
0
    return;
671
672
0
  TAILQ_FOREACH(s, &sg->sessions, gentry) {
673
0
    if (s != target)
674
0
      session_group_synchronize1(target, s);
675
0
  }
676
0
}
677
678
/*
679
 * Synchronize a session with a target session. This means destroying all
680
 * winlinks then recreating them, then updating the current window, last window
681
 * stack and alerts.
682
 */
683
static void
684
session_group_synchronize1(struct session *target, struct session *s)
685
0
{
686
0
  struct winlinks    old_windows, *ww;
687
0
  struct winlink_stack   old_lastw;
688
0
  struct winlink    *wl, *wl2;
689
690
  /* Don't do anything if the session is empty (it'll be destroyed). */
691
0
  ww = &target->windows;
692
0
  if (RB_EMPTY(ww))
693
0
    return;
694
695
  /* If the current window has vanished, move to the next now. */
696
0
  if (s->curw != NULL &&
697
0
      winlink_find_by_index(ww, s->curw->idx) == NULL &&
698
0
      session_last(s) != 0 && session_previous(s, 0) != 0)
699
0
    session_next(s, 0);
700
701
  /* Save the old pointer and reset it. */
702
0
  memcpy(&old_windows, &s->windows, sizeof old_windows);
703
0
  RB_INIT(&s->windows);
704
705
  /* Link all the windows from the target. */
706
0
  RB_FOREACH(wl, winlinks, ww) {
707
0
    wl2 = winlink_add(&s->windows, wl->idx);
708
0
    wl2->session = s;
709
0
    winlink_set_window(wl2, wl->window);
710
0
    events_fire_winlink("window-linked", wl2);
711
0
    wl2->flags |= wl->flags & WINLINK_ALERTFLAGS;
712
0
  }
713
714
  /* Fix up the current window. */
715
0
  if (s->curw != NULL)
716
0
    s->curw = winlink_find_by_index(&s->windows, s->curw->idx);
717
0
  else if (target->curw != NULL)
718
0
    s->curw = winlink_find_by_index(&s->windows, target->curw->idx);
719
0
  if (s->curw == NULL)
720
0
    s->curw = RB_MIN(winlinks, &s->windows);
721
722
  /* Fix up the last window stack. */
723
0
  memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
724
0
  TAILQ_INIT(&s->lastw);
725
0
  TAILQ_FOREACH(wl, &old_lastw, sentry) {
726
0
    wl2 = winlink_find_by_index(&s->windows, wl->idx);
727
0
    if (wl2 != NULL) {
728
0
      TAILQ_INSERT_TAIL(&s->lastw, wl2, sentry);
729
0
      wl2->flags |= WINLINK_VISITED;
730
0
    }
731
0
  }
732
733
  /* Then free the old winlinks list. */
734
0
  while (!RB_EMPTY(&old_windows)) {
735
0
    wl = RB_ROOT(&old_windows);
736
0
    wl2 = winlink_find_by_window_id(&s->windows, wl->window->id);
737
0
    if (wl2 == NULL)
738
0
      events_fire_winlink("window-unlinked", wl);
739
0
    winlink_remove(&old_windows, wl);
740
0
  }
741
0
}
742
743
/* Renumber the windows across winlinks attached to a specific session. */
744
void
745
session_renumber_windows(struct session *s)
746
0
{
747
0
  struct winlink    *wl, *wl1, *wl_new;
748
0
  struct winlinks    old_wins;
749
0
  struct winlink_stack   old_lastw;
750
0
  u_int      n;
751
0
  int      new_idx, new_curw_idx, marked_idx = -1;
752
753
  /* Start renumbering from the base-index if it's set. */
754
0
  new_idx = options_get_number(s->options, "base-index");
755
756
  /* Leave the indexes alone if they do not all fit from there. */
757
0
  n = winlink_count(&s->windows);
758
0
  if (n > (u_int)INT_MAX - (u_int)new_idx + 1)
759
0
    return;
760
761
  /* Save and replace old window list. */
762
0
  memcpy(&old_wins, &s->windows, sizeof old_wins);
763
0
  RB_INIT(&s->windows);
764
765
0
  new_curw_idx = 0;
766
767
  /* Go through the winlinks and assign new indexes. */
768
0
  RB_FOREACH(wl, winlinks, &old_wins) {
769
0
    wl_new = winlink_add(&s->windows, new_idx);
770
0
    wl_new->session = s;
771
0
    winlink_set_window(wl_new, wl->window);
772
0
    wl_new->flags |= wl->flags & WINLINK_ALERTFLAGS;
773
774
0
    if (wl == marked_pane.wl)
775
0
      marked_idx = wl_new->idx;
776
0
    if (wl == s->curw)
777
0
      new_curw_idx = wl_new->idx;
778
779
0
    if (RB_NEXT(winlinks, &old_wins, wl) != NULL)
780
0
      new_idx++;
781
0
  }
782
783
  /* Fix the stack of last windows now. */
784
0
  memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
785
0
  TAILQ_INIT(&s->lastw);
786
0
  TAILQ_FOREACH(wl, &old_lastw, sentry) {
787
0
    wl->flags &= ~WINLINK_VISITED;
788
0
    wl_new = winlink_find_by_window(&s->windows, wl->window);
789
0
    if (wl_new != NULL) {
790
0
      TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry);
791
0
      wl_new->flags |= WINLINK_VISITED;
792
0
    }
793
0
  }
794
795
  /* Set the current window. */
796
0
  if (marked_idx != -1) {
797
0
    marked_pane.wl = winlink_find_by_index(&s->windows, marked_idx);
798
0
    if (marked_pane.wl == NULL)
799
0
      server_clear_marked();
800
0
  }
801
0
  s->curw = winlink_find_by_index(&s->windows, new_curw_idx);
802
803
  /* Free the old winlinks (reducing window references too). */
804
0
  RB_FOREACH_SAFE(wl, winlinks, &old_wins, wl1)
805
0
    winlink_remove(&old_wins, wl);
806
0
}
807
808
/* Set the PANE_THEMECHANGED flag for every pane in this session. */
809
void
810
session_theme_changed(struct session *s)
811
0
{
812
0
  struct window_pane  *wp;
813
0
  struct winlink    *wl;
814
815
0
  if (s != NULL) {
816
0
    RB_FOREACH(wl, winlinks, &s->windows) {
817
0
      TAILQ_FOREACH(wp, &wl->window->panes, entry)
818
0
          wp->flags |= PANE_THEMECHANGED;
819
0
    }
820
0
  }
821
0
}
822
823
/* Update history for all panes. */
824
void
825
session_update_history(struct session *s)
826
0
{
827
0
  struct winlink    *wl;
828
0
  struct window_pane  *wp;
829
0
  struct grid   *gd;
830
0
  u_int      limit, osize;
831
832
0
  limit = options_get_number(s->options, "history-limit");
833
0
  RB_FOREACH(wl, winlinks, &s->windows) {
834
0
    TAILQ_FOREACH(wp, &wl->window->panes, entry) {
835
0
      gd = wp->base.grid;
836
837
0
      osize = gd->hsize;
838
0
      gd->hlimit = limit;
839
0
      grid_collect_history(gd, 1);
840
841
0
      if (gd->hsize != osize) {
842
0
        log_debug("%s: %%%u %u -> %u", __func__, wp->id,
843
0
            osize, gd->hsize);
844
0
      }
845
0
    }
846
0
  }
847
0
}