Coverage Report

Created: 2026-07-16 06:53

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.106 2026/07/13 13:01:14 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
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
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
/* Detach a window from a session. */
340
int
341
session_detach(struct session *s, struct winlink *wl)
342
0
{
343
0
  if (s->curw == wl &&
344
0
      session_last(s) != 0 &&
345
0
      session_previous(s, 0) != 0)
346
0
    session_next(s, 0);
347
348
0
  wl->flags &= ~WINLINK_ALERTFLAGS;
349
0
  events_fire_winlink("window-unlinked", wl);
350
0
  winlink_stack_remove(&s->lastw, wl);
351
0
  winlink_remove(&s->windows, wl);
352
353
0
  session_group_synchronize_from(s);
354
355
0
  if (RB_EMPTY(&s->windows))
356
0
    return (1);
357
0
  return (0);
358
0
}
359
360
/* Return if session has window. */
361
int
362
session_has(struct session *s, struct window *w)
363
0
{
364
0
  struct winlink  *wl;
365
366
0
  TAILQ_FOREACH(wl, &w->winlinks, wentry) {
367
0
    if (wl->session == s)
368
0
      return (1);
369
0
  }
370
0
  return (0);
371
0
}
372
373
/*
374
 * Return 1 if a window is linked outside this session (not including session
375
 * groups). The window must be in this session!
376
 */
377
int
378
session_is_linked(struct session *s, struct window *w)
379
0
{
380
0
  struct session_group  *sg;
381
382
0
  if ((sg = session_group_contains(s)) != NULL)
383
0
    return (w->references != session_group_count(sg));
384
0
  return (w->references != 1);
385
0
}
386
387
static struct winlink *
388
session_next_alert(struct winlink *wl)
389
0
{
390
0
  while (wl != NULL) {
391
0
    if (wl->flags & WINLINK_ALERTFLAGS)
392
0
      break;
393
0
    wl = winlink_next(wl);
394
0
  }
395
0
  return (wl);
396
0
}
397
398
/* Move session to next window. */
399
int
400
session_next(struct session *s, int alert)
401
0
{
402
0
  struct winlink  *wl;
403
404
0
  if (s->curw == NULL)
405
0
    return (-1);
406
407
0
  wl = winlink_next(s->curw);
408
0
  if (alert)
409
0
    wl = session_next_alert(wl);
410
0
  if (wl == NULL) {
411
0
    wl = RB_MIN(winlinks, &s->windows);
412
0
    if (alert && ((wl = session_next_alert(wl)) == NULL))
413
0
      return (-1);
414
0
  }
415
0
  return (session_set_current(s, wl));
416
0
}
417
418
static struct winlink *
419
session_previous_alert(struct winlink *wl)
420
0
{
421
0
  while (wl != NULL) {
422
0
    if (wl->flags & WINLINK_ALERTFLAGS)
423
0
      break;
424
0
    wl = winlink_previous(wl);
425
0
  }
426
0
  return (wl);
427
0
}
428
429
/* Move session to previous window. */
430
int
431
session_previous(struct session *s, int alert)
432
0
{
433
0
  struct winlink  *wl;
434
435
0
  if (s->curw == NULL)
436
0
    return (-1);
437
438
0
  wl = winlink_previous(s->curw);
439
0
  if (alert)
440
0
    wl = session_previous_alert(wl);
441
0
  if (wl == NULL) {
442
0
    wl = RB_MAX(winlinks, &s->windows);
443
0
    if (alert && (wl = session_previous_alert(wl)) == NULL)
444
0
      return (-1);
445
0
  }
446
0
  return (session_set_current(s, wl));
447
0
}
448
449
/* Move session to specific window. */
450
int
451
session_select(struct session *s, int idx)
452
0
{
453
0
  struct winlink  *wl;
454
455
0
  wl = winlink_find_by_index(&s->windows, idx);
456
0
  return (session_set_current(s, wl));
457
0
}
458
459
/* Move session to last used window. */
460
int
461
session_last(struct session *s)
462
0
{
463
0
  struct winlink  *wl;
464
465
0
  wl = TAILQ_FIRST(&s->lastw);
466
0
  if (wl == NULL)
467
0
    return (-1);
468
0
  if (wl == s->curw)
469
0
    return (1);
470
471
0
  return (session_set_current(s, wl));
472
0
}
473
474
/* Fire session window changed. */
475
static void
476
session_fire_window_changed(struct session *s, struct winlink *wl,
477
    struct winlink *old)
478
0
{
479
0
  struct event_payload  *ep;
480
0
  struct cmd_find_state  fs;
481
482
0
  ep = event_payload_create();
483
0
  cmd_find_from_winlink(&fs, wl, 0);
484
0
  event_payload_set_target(ep, &fs);
485
0
  event_payload_set_session(ep, "session", s);
486
0
  event_payload_set_window(ep, "window", wl->window);
487
0
  event_payload_set_window(ep, "new_window", wl->window);
488
0
  event_payload_set_int(ep, "window_index", wl->idx);
489
0
  event_payload_set_int(ep, "new_window_index", wl->idx);
490
0
  if (old != NULL) {
491
0
    event_payload_set_window(ep, "old_window", old->window);
492
0
    event_payload_set_int(ep, "old_window_index", old->idx);
493
0
  }
494
0
  events_fire("session-window-changed", ep);
495
0
}
496
497
/* Set current winlink to wl .*/
498
int
499
session_set_current(struct session *s, struct winlink *wl)
500
0
{
501
0
  struct winlink  *old = s->curw;
502
503
0
  if (wl == NULL)
504
0
    return (-1);
505
0
  if (wl == s->curw)
506
0
    return (1);
507
508
0
  winlink_stack_remove(&s->lastw, wl);
509
0
  winlink_stack_push(&s->lastw, s->curw);
510
0
  s->curw = wl;
511
0
  if (options_get_number(global_options, "focus-events")) {
512
0
    if (old != NULL)
513
0
      window_update_focus(old->window);
514
0
    window_update_focus(wl->window);
515
0
  }
516
0
  winlink_clear_flags(wl);
517
0
  window_update_activity(wl->window);
518
0
  tty_update_window_offset(wl->window);
519
0
  session_fire_window_changed(s, wl, old);
520
0
  return (0);
521
0
}
522
523
/* Find the session group containing a session. */
524
struct session_group *
525
session_group_contains(struct session *target)
526
0
{
527
0
  struct session_group  *sg;
528
0
  struct session    *s;
529
530
0
  RB_FOREACH(sg, session_groups, &session_groups) {
531
0
    TAILQ_FOREACH(s, &sg->sessions, gentry) {
532
0
      if (s == target)
533
0
        return (sg);
534
0
    }
535
0
  }
536
0
  return (NULL);
537
0
}
538
539
/* Find session group by name. */
540
struct session_group *
541
session_group_find(const char *name)
542
0
{
543
0
  struct session_group  sg;
544
545
0
  sg.name = name;
546
0
  return (RB_FIND(session_groups, &session_groups, &sg));
547
0
}
548
549
/* Create a new session group. */
550
struct session_group *
551
session_group_new(const char *name)
552
0
{
553
0
  struct session_group  *sg;
554
555
0
  if ((sg = session_group_find(name)) != NULL)
556
0
    return (sg);
557
558
0
  sg = xcalloc(1, sizeof *sg);
559
0
  sg->name = xstrdup(name);
560
0
  TAILQ_INIT(&sg->sessions);
561
562
0
  RB_INSERT(session_groups, &session_groups, sg);
563
0
  return (sg);
564
0
}
565
566
/* Fire session group changed. */
567
static void
568
session_group_fire(const char *name, struct session_group *sg,
569
    struct session *s)
570
0
{
571
0
  struct event_payload  *ep;
572
0
  struct cmd_find_state  fs;
573
574
0
  ep = event_payload_create();
575
0
  if (session_alive(s)) {
576
0
    cmd_find_from_session(&fs, s, 0);
577
0
    event_payload_set_target(ep, &fs);
578
0
  }
579
0
  event_payload_set_session(ep, "session", s);
580
0
  event_payload_set_string(ep, "group", "%s", sg->name);
581
0
  event_payload_set_uint(ep, "group_size", session_group_count(sg));
582
0
  events_fire(name, ep);
583
0
}
584
585
/* Add a session to a session group. */
586
void
587
session_group_add(struct session_group *sg, struct session *s)
588
0
{
589
0
  if (session_group_contains(s) == NULL) {
590
0
    TAILQ_INSERT_TAIL(&sg->sessions, s, gentry);
591
0
    session_group_fire("session-added-to-group", sg, s);
592
0
  }
593
0
}
594
595
/* Remove a session from its group and destroy the group if empty. */
596
static void
597
session_group_remove(struct session *s)
598
0
{
599
0
  struct session_group  *sg;
600
601
0
  if ((sg = session_group_contains(s)) == NULL)
602
0
    return;
603
0
  session_group_fire("session-removed-from-group", sg, s);
604
0
  TAILQ_REMOVE(&sg->sessions, s, gentry);
605
0
  if (TAILQ_EMPTY(&sg->sessions)) {
606
0
    RB_REMOVE(session_groups, &session_groups, sg);
607
0
    free((void *)sg->name);
608
0
    free(sg);
609
0
  }
610
0
}
611
612
/* Count number of sessions in session group. */
613
u_int
614
session_group_count(struct session_group *sg)
615
0
{
616
0
  struct session  *s;
617
0
  u_int    n;
618
619
0
  n = 0;
620
0
  TAILQ_FOREACH(s, &sg->sessions, gentry)
621
0
    n++;
622
0
  return (n);
623
0
}
624
625
/* Count number of clients attached to sessions in session group. */
626
u_int
627
session_group_attached_count(struct session_group *sg)
628
0
{
629
0
  struct session  *s;
630
0
  u_int    n;
631
632
0
  n = 0;
633
0
  TAILQ_FOREACH(s, &sg->sessions, gentry)
634
0
    n += s->attached;
635
0
  return (n);
636
0
}
637
638
/* Synchronize a session to its session group. */
639
void
640
session_group_synchronize_to(struct session *s)
641
0
{
642
0
  struct session_group  *sg;
643
0
  struct session    *target;
644
645
0
  if ((sg = session_group_contains(s)) == NULL)
646
0
    return;
647
648
0
  target = NULL;
649
0
  TAILQ_FOREACH(target, &sg->sessions, gentry) {
650
0
    if (target != s)
651
0
      break;
652
0
  }
653
0
  if (target != NULL)
654
0
    session_group_synchronize1(target, s);
655
0
}
656
657
/* Synchronize a session group to a session. */
658
void
659
session_group_synchronize_from(struct session *target)
660
0
{
661
0
  struct session_group  *sg;
662
0
  struct session    *s;
663
664
0
  if ((sg = session_group_contains(target)) == NULL)
665
0
    return;
666
667
0
  TAILQ_FOREACH(s, &sg->sessions, gentry) {
668
0
    if (s != target)
669
0
      session_group_synchronize1(target, s);
670
0
  }
671
0
}
672
673
/*
674
 * Synchronize a session with a target session. This means destroying all
675
 * winlinks then recreating them, then updating the current window, last window
676
 * stack and alerts.
677
 */
678
static void
679
session_group_synchronize1(struct session *target, struct session *s)
680
0
{
681
0
  struct winlinks    old_windows, *ww;
682
0
  struct winlink_stack   old_lastw;
683
0
  struct winlink    *wl, *wl2;
684
685
  /* Don't do anything if the session is empty (it'll be destroyed). */
686
0
  ww = &target->windows;
687
0
  if (RB_EMPTY(ww))
688
0
    return;
689
690
  /* If the current window has vanished, move to the next now. */
691
0
  if (s->curw != NULL &&
692
0
      winlink_find_by_index(ww, s->curw->idx) == NULL &&
693
0
      session_last(s) != 0 && session_previous(s, 0) != 0)
694
0
    session_next(s, 0);
695
696
  /* Save the old pointer and reset it. */
697
0
  memcpy(&old_windows, &s->windows, sizeof old_windows);
698
0
  RB_INIT(&s->windows);
699
700
  /* Link all the windows from the target. */
701
0
  RB_FOREACH(wl, winlinks, ww) {
702
0
    wl2 = winlink_add(&s->windows, wl->idx);
703
0
    wl2->session = s;
704
0
    winlink_set_window(wl2, wl->window);
705
0
    events_fire_winlink("window-linked", wl2);
706
0
    wl2->flags |= wl->flags & WINLINK_ALERTFLAGS;
707
0
  }
708
709
  /* Fix up the current window. */
710
0
  if (s->curw != NULL)
711
0
    s->curw = winlink_find_by_index(&s->windows, s->curw->idx);
712
0
  else
713
0
    s->curw = winlink_find_by_index(&s->windows, target->curw->idx);
714
715
  /* Fix up the last window stack. */
716
0
  memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
717
0
  TAILQ_INIT(&s->lastw);
718
0
  TAILQ_FOREACH(wl, &old_lastw, sentry) {
719
0
    wl2 = winlink_find_by_index(&s->windows, wl->idx);
720
0
    if (wl2 != NULL) {
721
0
      TAILQ_INSERT_TAIL(&s->lastw, wl2, sentry);
722
0
      wl2->flags |= WINLINK_VISITED;
723
0
    }
724
0
  }
725
726
  /* Then free the old winlinks list. */
727
0
  while (!RB_EMPTY(&old_windows)) {
728
0
    wl = RB_ROOT(&old_windows);
729
0
    wl2 = winlink_find_by_window_id(&s->windows, wl->window->id);
730
0
    if (wl2 == NULL)
731
0
      events_fire_winlink("window-unlinked", wl);
732
0
    winlink_remove(&old_windows, wl);
733
0
  }
734
0
}
735
736
/* Renumber the windows across winlinks attached to a specific session. */
737
void
738
session_renumber_windows(struct session *s)
739
0
{
740
0
  struct winlink    *wl, *wl1, *wl_new;
741
0
  struct winlinks    old_wins;
742
0
  struct winlink_stack   old_lastw;
743
0
  int      new_idx, new_curw_idx, marked_idx = -1;
744
745
  /* Save and replace old window list. */
746
0
  memcpy(&old_wins, &s->windows, sizeof old_wins);
747
0
  RB_INIT(&s->windows);
748
749
  /* Start renumbering from the base-index if it's set. */
750
0
  new_idx = options_get_number(s->options, "base-index");
751
0
  new_curw_idx = 0;
752
753
  /* Go through the winlinks and assign new indexes. */
754
0
  RB_FOREACH(wl, winlinks, &old_wins) {
755
0
    wl_new = winlink_add(&s->windows, new_idx);
756
0
    wl_new->session = s;
757
0
    winlink_set_window(wl_new, wl->window);
758
0
    wl_new->flags |= wl->flags & WINLINK_ALERTFLAGS;
759
760
0
    if (wl == marked_pane.wl)
761
0
      marked_idx = wl_new->idx;
762
0
    if (wl == s->curw)
763
0
      new_curw_idx = wl_new->idx;
764
765
0
    new_idx++;
766
0
  }
767
768
  /* Fix the stack of last windows now. */
769
0
  memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
770
0
  TAILQ_INIT(&s->lastw);
771
0
  TAILQ_FOREACH(wl, &old_lastw, sentry) {
772
0
    wl->flags &= ~WINLINK_VISITED;
773
0
    wl_new = winlink_find_by_window(&s->windows, wl->window);
774
0
    if (wl_new != NULL) {
775
0
      TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry);
776
0
      wl_new->flags |= WINLINK_VISITED;
777
0
    }
778
0
  }
779
780
  /* Set the current window. */
781
0
  if (marked_idx != -1) {
782
0
    marked_pane.wl = winlink_find_by_index(&s->windows, marked_idx);
783
0
    if (marked_pane.wl == NULL)
784
0
      server_clear_marked();
785
0
  }
786
0
  s->curw = winlink_find_by_index(&s->windows, new_curw_idx);
787
788
  /* Free the old winlinks (reducing window references too). */
789
0
  RB_FOREACH_SAFE(wl, winlinks, &old_wins, wl1)
790
0
    winlink_remove(&old_wins, wl);
791
0
}
792
793
/* Set the PANE_THEMECHANGED flag for every pane in this session. */
794
void
795
session_theme_changed(struct session *s)
796
0
{
797
0
  struct window_pane  *wp;
798
0
  struct winlink    *wl;
799
800
0
  if (s != NULL) {
801
0
    RB_FOREACH(wl, winlinks, &s->windows) {
802
0
      TAILQ_FOREACH(wp, &wl->window->panes, entry)
803
0
          wp->flags |= PANE_THEMECHANGED;
804
0
    }
805
0
  }
806
0
}
807
808
/* Update history for all panes. */
809
void
810
session_update_history(struct session *s)
811
0
{
812
0
  struct winlink    *wl;
813
0
  struct window_pane  *wp;
814
0
  struct grid   *gd;
815
0
  u_int      limit, osize;
816
817
0
  limit = options_get_number(s->options, "history-limit");
818
0
  RB_FOREACH(wl, winlinks, &s->windows) {
819
0
    TAILQ_FOREACH(wp, &wl->window->panes, entry) {
820
0
      gd = wp->base.grid;
821
822
0
      osize = gd->hsize;
823
0
      gd->hlimit = limit;
824
0
      grid_collect_history(gd, 1);
825
826
0
      if (gd->hsize != osize) {
827
0
        log_debug("%s: %%%u %u -> %u", __func__, wp->id,
828
0
            osize, gd->hsize);
829
0
      }
830
0
    }
831
0
  }
832
0
}