Coverage Report

Created: 2025-08-24 07:01

/src/tmux/window.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/ioctl.h>
21
22
#include <ctype.h>
23
#include <errno.h>
24
#include <fcntl.h>
25
#include <fnmatch.h>
26
#include <regex.h>
27
#include <signal.h>
28
#include <stdint.h>
29
#include <stdlib.h>
30
#include <string.h>
31
#include <time.h>
32
#include <unistd.h>
33
34
#include "tmux.h"
35
36
/*
37
 * Each window is attached to a number of panes, each of which is a pty. This
38
 * file contains code to handle them.
39
 *
40
 * A pane has two buffers attached, these are filled and emptied by the main
41
 * server poll loop. Output data is received from pty's in screen format,
42
 * translated and returned as a series of escape sequences and strings via
43
 * input_parse (in input.c). Input data is received as key codes and written
44
 * directly via input_key.
45
 *
46
 * Each pane also has a "virtual" screen (screen.c) which contains the current
47
 * state and is redisplayed when the window is reattached to a client.
48
 *
49
 * Windows are stored directly on a global array and wrapped in any number of
50
 * winlink structs to be linked onto local session RB trees. A reference count
51
 * is maintained and a window removed from the global list and destroyed when
52
 * it reaches zero.
53
 */
54
55
/* Global window list. */
56
struct windows windows;
57
58
/* Global panes tree. */
59
struct window_pane_tree all_window_panes;
60
static u_int  next_window_pane_id;
61
static u_int  next_window_id;
62
static u_int  next_active_point;
63
64
struct window_pane_input_data {
65
  struct cmdq_item  *item;
66
  u_int      wp;
67
  struct client_file  *file;
68
};
69
70
static struct window_pane *window_pane_create(struct window *, u_int, u_int,
71
        u_int);
72
static void window_pane_destroy(struct window_pane *);
73
static void window_pane_full_size_offset(struct window_pane *wp,
74
        u_int *xoff, u_int *yoff, u_int *sx, u_int *sy);
75
76
RB_GENERATE(windows, window, entry, window_cmp);
77
RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
78
RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp);
79
80
int
81
window_cmp(struct window *w1, struct window *w2)
82
0
{
83
0
  return (w1->id - w2->id);
84
0
}
85
86
int
87
winlink_cmp(struct winlink *wl1, struct winlink *wl2)
88
0
{
89
0
  return (wl1->idx - wl2->idx);
90
0
}
91
92
int
93
window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2)
94
0
{
95
0
  return (wp1->id - wp2->id);
96
0
}
97
98
struct winlink *
99
winlink_find_by_window(struct winlinks *wwl, struct window *w)
100
0
{
101
0
  struct winlink  *wl;
102
103
0
  RB_FOREACH(wl, winlinks, wwl) {
104
0
    if (wl->window == w)
105
0
      return (wl);
106
0
  }
107
108
0
  return (NULL);
109
0
}
110
111
struct winlink *
112
winlink_find_by_index(struct winlinks *wwl, int idx)
113
0
{
114
0
  struct winlink  wl;
115
116
0
  if (idx < 0)
117
0
    fatalx("bad index");
118
119
0
  wl.idx = idx;
120
0
  return (RB_FIND(winlinks, wwl, &wl));
121
0
}
122
123
struct winlink *
124
winlink_find_by_window_id(struct winlinks *wwl, u_int id)
125
0
{
126
0
  struct winlink *wl;
127
128
0
  RB_FOREACH(wl, winlinks, wwl) {
129
0
    if (wl->window->id == id)
130
0
      return (wl);
131
0
  }
132
0
  return (NULL);
133
0
}
134
135
static int
136
winlink_next_index(struct winlinks *wwl, int idx)
137
0
{
138
0
  int i;
139
140
0
  i = idx;
141
0
  do {
142
0
    if (winlink_find_by_index(wwl, i) == NULL)
143
0
      return (i);
144
0
    if (i == INT_MAX)
145
0
      i = 0;
146
0
    else
147
0
      i++;
148
0
  } while (i != idx);
149
0
  return (-1);
150
0
}
151
152
u_int
153
winlink_count(struct winlinks *wwl)
154
0
{
155
0
  struct winlink  *wl;
156
0
  u_int    n;
157
158
0
  n = 0;
159
0
  RB_FOREACH(wl, winlinks, wwl)
160
0
    n++;
161
162
0
  return (n);
163
0
}
164
165
struct winlink *
166
winlink_add(struct winlinks *wwl, int idx)
167
0
{
168
0
  struct winlink  *wl;
169
170
0
  if (idx < 0) {
171
0
    if ((idx = winlink_next_index(wwl, -idx - 1)) == -1)
172
0
      return (NULL);
173
0
  } else if (winlink_find_by_index(wwl, idx) != NULL)
174
0
    return (NULL);
175
176
0
  wl = xcalloc(1, sizeof *wl);
177
0
  wl->idx = idx;
178
0
  RB_INSERT(winlinks, wwl, wl);
179
180
0
  return (wl);
181
0
}
182
183
void
184
winlink_set_window(struct winlink *wl, struct window *w)
185
0
{
186
0
  if (wl->window != NULL) {
187
0
    TAILQ_REMOVE(&wl->window->winlinks, wl, wentry);
188
0
    window_remove_ref(wl->window, __func__);
189
0
  }
190
0
  TAILQ_INSERT_TAIL(&w->winlinks, wl, wentry);
191
0
  wl->window = w;
192
0
  window_add_ref(w, __func__);
193
0
}
194
195
void
196
winlink_remove(struct winlinks *wwl, struct winlink *wl)
197
0
{
198
0
  struct window *w = wl->window;
199
200
0
  if (w != NULL) {
201
0
    TAILQ_REMOVE(&w->winlinks, wl, wentry);
202
0
    window_remove_ref(w, __func__);
203
0
  }
204
205
0
  RB_REMOVE(winlinks, wwl, wl);
206
0
  free(wl);
207
0
}
208
209
struct winlink *
210
winlink_next(struct winlink *wl)
211
0
{
212
0
  return (RB_NEXT(winlinks, wwl, wl));
213
0
}
214
215
struct winlink *
216
winlink_previous(struct winlink *wl)
217
0
{
218
0
  return (RB_PREV(winlinks, wwl, wl));
219
0
}
220
221
struct winlink *
222
winlink_next_by_number(struct winlink *wl, struct session *s, int n)
223
0
{
224
0
  for (; n > 0; n--) {
225
0
    if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL)
226
0
      wl = RB_MIN(winlinks, &s->windows);
227
0
  }
228
229
0
  return (wl);
230
0
}
231
232
struct winlink *
233
winlink_previous_by_number(struct winlink *wl, struct session *s, int n)
234
0
{
235
0
  for (; n > 0; n--) {
236
0
    if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL)
237
0
      wl = RB_MAX(winlinks, &s->windows);
238
0
  }
239
240
0
  return (wl);
241
0
}
242
243
void
244
winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
245
0
{
246
0
  if (wl == NULL)
247
0
    return;
248
249
0
  winlink_stack_remove(stack, wl);
250
0
  TAILQ_INSERT_HEAD(stack, wl, sentry);
251
0
  wl->flags |= WINLINK_VISITED;
252
0
}
253
254
void
255
winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
256
0
{
257
0
  if (wl != NULL && (wl->flags & WINLINK_VISITED)) {
258
0
    TAILQ_REMOVE(stack, wl, sentry);
259
0
    wl->flags &= ~WINLINK_VISITED;
260
0
  }
261
0
}
262
263
struct window *
264
window_find_by_id_str(const char *s)
265
0
{
266
0
  const char  *errstr;
267
0
  u_int    id;
268
269
0
  if (*s != '@')
270
0
    return (NULL);
271
272
0
  id = strtonum(s + 1, 0, UINT_MAX, &errstr);
273
0
  if (errstr != NULL)
274
0
    return (NULL);
275
0
  return (window_find_by_id(id));
276
0
}
277
278
struct window *
279
window_find_by_id(u_int id)
280
0
{
281
0
  struct window w;
282
283
0
  w.id = id;
284
0
  return (RB_FIND(windows, &windows, &w));
285
0
}
286
287
void
288
window_update_activity(struct window *w)
289
23.3k
{
290
23.3k
  gettimeofday(&w->activity_time, NULL);
291
23.3k
  alerts_queue(w, WINDOW_ACTIVITY);
292
23.3k
}
293
294
struct window *
295
window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
296
11.6k
{
297
11.6k
  struct window *w;
298
299
11.6k
  if (xpixel == 0)
300
11.6k
    xpixel = DEFAULT_XPIXEL;
301
11.6k
  if (ypixel == 0)
302
11.6k
    ypixel = DEFAULT_YPIXEL;
303
304
11.6k
  w = xcalloc(1, sizeof *w);
305
11.6k
  w->name = xstrdup("");
306
11.6k
  w->flags = 0;
307
308
11.6k
  TAILQ_INIT(&w->panes);
309
11.6k
  TAILQ_INIT(&w->last_panes);
310
11.6k
  w->active = NULL;
311
312
11.6k
  w->lastlayout = -1;
313
11.6k
  w->layout_root = NULL;
314
315
11.6k
  w->sx = sx;
316
11.6k
  w->sy = sy;
317
11.6k
  w->manual_sx = sx;
318
11.6k
  w->manual_sy = sy;
319
11.6k
  w->xpixel = xpixel;
320
11.6k
  w->ypixel = ypixel;
321
322
11.6k
  w->options = options_create(global_w_options);
323
324
11.6k
  w->references = 0;
325
11.6k
  TAILQ_INIT(&w->winlinks);
326
327
11.6k
  w->id = next_window_id++;
328
11.6k
  RB_INSERT(windows, &windows, w);
329
330
11.6k
  window_set_fill_character(w);
331
11.6k
  window_update_activity(w);
332
333
11.6k
  log_debug("%s: @%u create %ux%u (%ux%u)", __func__, w->id, sx, sy,
334
11.6k
      w->xpixel, w->ypixel);
335
11.6k
  return (w);
336
11.6k
}
337
338
static void
339
window_destroy(struct window *w)
340
11.6k
{
341
11.6k
  log_debug("window @%u destroyed (%d references)", w->id, w->references);
342
343
11.6k
  window_unzoom(w, 0);
344
11.6k
  RB_REMOVE(windows, &windows, w);
345
346
11.6k
  if (w->layout_root != NULL)
347
0
    layout_free_cell(w->layout_root);
348
11.6k
  if (w->saved_layout_root != NULL)
349
0
    layout_free_cell(w->saved_layout_root);
350
11.6k
  free(w->old_layout);
351
352
11.6k
  window_destroy_panes(w);
353
354
11.6k
  if (event_initialized(&w->name_event))
355
0
    evtimer_del(&w->name_event);
356
357
11.6k
  if (event_initialized(&w->alerts_timer))
358
11.6k
    evtimer_del(&w->alerts_timer);
359
11.6k
  if (event_initialized(&w->offset_timer))
360
3.35k
    event_del(&w->offset_timer);
361
362
11.6k
  options_free(w->options);
363
11.6k
  free(w->fill_character);
364
365
11.6k
  free(w->name);
366
11.6k
  free(w);
367
11.6k
}
368
369
int
370
window_pane_destroy_ready(struct window_pane *wp)
371
0
{
372
0
  int n;
373
374
0
  if (wp->pipe_fd != -1) {
375
0
    if (EVBUFFER_LENGTH(wp->pipe_event->output) != 0)
376
0
      return (0);
377
0
    if (ioctl(wp->fd, FIONREAD, &n) != -1 && n > 0)
378
0
      return (0);
379
0
  }
380
381
0
  if (~wp->flags & PANE_EXITED)
382
0
    return (0);
383
0
  return (1);
384
0
}
385
386
void
387
window_add_ref(struct window *w, const char *from)
388
12.4k
{
389
12.4k
  w->references++;
390
12.4k
  log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references);
391
12.4k
}
392
393
void
394
window_remove_ref(struct window *w, const char *from)
395
12.4k
{
396
12.4k
  w->references--;
397
12.4k
  log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references);
398
399
12.4k
  if (w->references == 0)
400
11.6k
    window_destroy(w);
401
12.4k
}
402
403
void
404
window_set_name(struct window *w, const char *new_name)
405
787
{
406
787
  free(w->name);
407
787
  utf8_stravis(&w->name, new_name, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
408
787
  notify_window("window-renamed", w);
409
787
}
410
411
void
412
window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
413
0
{
414
0
  if (xpixel == 0)
415
0
    xpixel = DEFAULT_XPIXEL;
416
0
  if (ypixel == 0)
417
0
    ypixel = DEFAULT_YPIXEL;
418
419
0
  log_debug("%s: @%u resize %ux%u (%ux%u)", __func__, w->id, sx, sy,
420
0
      xpixel == -1 ? w->xpixel : (u_int)xpixel,
421
0
      ypixel == -1 ? w->ypixel : (u_int)ypixel);
422
0
  w->sx = sx;
423
0
  w->sy = sy;
424
0
  if (xpixel != -1)
425
0
    w->xpixel = xpixel;
426
0
  if (ypixel != -1)
427
0
    w->ypixel = ypixel;
428
0
}
429
430
void
431
window_pane_send_resize(struct window_pane *wp, u_int sx, u_int sy)
432
0
{
433
0
  struct window *w = wp->window;
434
0
  struct winsize   ws;
435
436
0
  if (wp->fd == -1)
437
0
    return;
438
439
0
  log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, sx, sy);
440
441
0
  memset(&ws, 0, sizeof ws);
442
0
  ws.ws_col = sx;
443
0
  ws.ws_row = sy;
444
0
  ws.ws_xpixel = w->xpixel * ws.ws_col;
445
0
  ws.ws_ypixel = w->ypixel * ws.ws_row;
446
0
  if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
447
#ifdef __sun
448
    /*
449
     * Some versions of Solaris apparently can return an error when
450
     * resizing; don't know why this happens, can't reproduce on
451
     * other platforms and ignoring it doesn't seem to cause any
452
     * issues.
453
     */
454
    if (errno != EINVAL && errno != ENXIO)
455
#endif
456
0
    fatal("ioctl failed");
457
0
}
458
459
int
460
window_has_pane(struct window *w, struct window_pane *wp)
461
0
{
462
0
  struct window_pane  *wp1;
463
464
0
  TAILQ_FOREACH(wp1, &w->panes, entry) {
465
0
    if (wp1 == wp)
466
0
      return (1);
467
0
  }
468
0
  return (0);
469
0
}
470
471
void
472
window_update_focus(struct window *w)
473
0
{
474
0
  if (w != NULL) {
475
0
    log_debug("%s: @%u", __func__, w->id);
476
0
    window_pane_update_focus(w->active);
477
0
  }
478
0
}
479
480
void
481
window_pane_update_focus(struct window_pane *wp)
482
0
{
483
0
  struct client *c;
484
0
  int    focused = 0;
485
486
0
  if (wp != NULL && (~wp->flags & PANE_EXITED)) {
487
0
    if (wp != wp->window->active)
488
0
      focused = 0;
489
0
    else {
490
0
      TAILQ_FOREACH(c, &clients, entry) {
491
0
        if (c->session != NULL &&
492
0
            c->session->attached != 0 &&
493
0
            (c->flags & CLIENT_FOCUSED) &&
494
0
            c->session->curw->window == wp->window &&
495
0
            c->overlay_draw == NULL) {
496
0
          focused = 1;
497
0
          break;
498
0
        }
499
0
      }
500
0
    }
501
0
    if (!focused && (wp->flags & PANE_FOCUSED)) {
502
0
      log_debug("%s: %%%u focus out", __func__, wp->id);
503
0
      if (wp->base.mode & MODE_FOCUSON)
504
0
        bufferevent_write(wp->event, "\033[O", 3);
505
0
      notify_pane("pane-focus-out", wp);
506
0
      wp->flags &= ~PANE_FOCUSED;
507
0
    } else if (focused && (~wp->flags & PANE_FOCUSED)) {
508
0
      log_debug("%s: %%%u focus in", __func__, wp->id);
509
0
      if (wp->base.mode & MODE_FOCUSON)
510
0
        bufferevent_write(wp->event, "\033[I", 3);
511
0
      notify_pane("pane-focus-in", wp);
512
0
      wp->flags |= PANE_FOCUSED;
513
0
    } else
514
0
      log_debug("%s: %%%u focus unchanged", __func__, wp->id);
515
0
  }
516
0
}
517
518
int
519
window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
520
0
{
521
0
  struct window_pane *lastwp;
522
523
0
  log_debug("%s: pane %%%u", __func__, wp->id);
524
525
0
  if (wp == w->active)
526
0
    return (0);
527
0
  lastwp = w->active;
528
529
0
  window_pane_stack_remove(&w->last_panes, wp);
530
0
  window_pane_stack_push(&w->last_panes, lastwp);
531
532
0
  w->active = wp;
533
0
  w->active->active_point = next_active_point++;
534
0
  w->active->flags |= PANE_CHANGED;
535
536
0
  if (options_get_number(global_options, "focus-events")) {
537
0
    window_pane_update_focus(lastwp);
538
0
    window_pane_update_focus(w->active);
539
0
  }
540
541
0
  tty_update_window_offset(w);
542
543
0
  if (notify)
544
0
    notify_window("window-pane-changed", w);
545
0
  return (1);
546
0
}
547
548
static int
549
window_pane_get_palette(struct window_pane *wp, int c)
550
0
{
551
0
  if (wp == NULL)
552
0
    return (-1);
553
0
  return (colour_palette_get(&wp->palette, c));
554
0
}
555
556
void
557
window_redraw_active_switch(struct window *w, struct window_pane *wp)
558
0
{
559
0
  struct grid_cell  *gc1, *gc2;
560
0
  int      c1, c2;
561
562
0
  if (wp == w->active)
563
0
    return;
564
565
0
  for (;;) {
566
    /*
567
     * If the active and inactive styles or palettes are different,
568
     * need to redraw the panes.
569
     */
570
0
    gc1 = &wp->cached_gc;
571
0
    gc2 = &wp->cached_active_gc;
572
0
    if (!grid_cells_look_equal(gc1, gc2))
573
0
      wp->flags |= PANE_REDRAW;
574
0
    else {
575
0
      c1 = window_pane_get_palette(wp, gc1->fg);
576
0
      c2 = window_pane_get_palette(wp, gc2->fg);
577
0
      if (c1 != c2)
578
0
        wp->flags |= PANE_REDRAW;
579
0
      else {
580
0
        c1 = window_pane_get_palette(wp, gc1->bg);
581
0
        c2 = window_pane_get_palette(wp, gc2->bg);
582
0
        if (c1 != c2)
583
0
          wp->flags |= PANE_REDRAW;
584
0
      }
585
0
    }
586
0
    if (wp == w->active)
587
0
      break;
588
0
    wp = w->active;
589
0
  }
590
0
}
591
592
struct window_pane *
593
window_get_active_at(struct window *w, u_int x, u_int y)
594
0
{
595
0
  struct window_pane  *wp;
596
0
  u_int      xoff, yoff, sx, sy;
597
598
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
599
0
    if (!window_pane_visible(wp))
600
0
      continue;
601
0
    window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
602
0
    if (x < xoff || x > xoff + sx)
603
0
      continue;
604
0
    if (y < yoff || y > yoff + sy)
605
0
      continue;
606
0
    return (wp);
607
0
  }
608
0
  return (NULL);
609
0
}
610
611
struct window_pane *
612
window_find_string(struct window *w, const char *s)
613
0
{
614
0
  u_int x, y, top = 0, bottom = w->sy - 1;
615
0
  int status;
616
617
0
  x = w->sx / 2;
618
0
  y = w->sy / 2;
619
620
0
  status = options_get_number(w->options, "pane-border-status");
621
0
  if (status == PANE_STATUS_TOP)
622
0
    top++;
623
0
  else if (status == PANE_STATUS_BOTTOM)
624
0
    bottom--;
625
626
0
  if (strcasecmp(s, "top") == 0)
627
0
    y = top;
628
0
  else if (strcasecmp(s, "bottom") == 0)
629
0
    y = bottom;
630
0
  else if (strcasecmp(s, "left") == 0)
631
0
    x = 0;
632
0
  else if (strcasecmp(s, "right") == 0)
633
0
    x = w->sx - 1;
634
0
  else if (strcasecmp(s, "top-left") == 0) {
635
0
    x = 0;
636
0
    y = top;
637
0
  } else if (strcasecmp(s, "top-right") == 0) {
638
0
    x = w->sx - 1;
639
0
    y = top;
640
0
  } else if (strcasecmp(s, "bottom-left") == 0) {
641
0
    x = 0;
642
0
    y = bottom;
643
0
  } else if (strcasecmp(s, "bottom-right") == 0) {
644
0
    x = w->sx - 1;
645
0
    y = bottom;
646
0
  } else
647
0
    return (NULL);
648
649
0
  return (window_get_active_at(w, x, y));
650
0
}
651
652
int
653
window_zoom(struct window_pane *wp)
654
0
{
655
0
  struct window   *w = wp->window;
656
0
  struct window_pane  *wp1;
657
658
0
  if (w->flags & WINDOW_ZOOMED)
659
0
    return (-1);
660
661
0
  if (window_count_panes(w) == 1)
662
0
    return (-1);
663
664
0
  if (w->active != wp)
665
0
    window_set_active_pane(w, wp, 1);
666
667
0
  TAILQ_FOREACH(wp1, &w->panes, entry) {
668
0
    wp1->saved_layout_cell = wp1->layout_cell;
669
0
    wp1->layout_cell = NULL;
670
0
  }
671
672
0
  w->saved_layout_root = w->layout_root;
673
0
  layout_init(w, wp);
674
0
  w->flags |= WINDOW_ZOOMED;
675
0
  notify_window("window-layout-changed", w);
676
677
0
  return (0);
678
0
}
679
680
int
681
window_unzoom(struct window *w, int notify)
682
11.6k
{
683
11.6k
  struct window_pane  *wp;
684
685
11.6k
  if (!(w->flags & WINDOW_ZOOMED))
686
11.6k
    return (-1);
687
688
0
  w->flags &= ~WINDOW_ZOOMED;
689
0
  layout_free(w);
690
0
  w->layout_root = w->saved_layout_root;
691
0
  w->saved_layout_root = NULL;
692
693
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
694
0
    wp->layout_cell = wp->saved_layout_cell;
695
0
    wp->saved_layout_cell = NULL;
696
0
  }
697
0
  layout_fix_panes(w, NULL);
698
699
0
  if (notify)
700
0
    notify_window("window-layout-changed", w);
701
702
0
  return (0);
703
11.6k
}
704
705
int
706
window_push_zoom(struct window *w, int always, int flag)
707
0
{
708
0
  log_debug("%s: @%u %d", __func__, w->id,
709
0
      flag && (w->flags & WINDOW_ZOOMED));
710
0
  if (flag && (always || (w->flags & WINDOW_ZOOMED)))
711
0
    w->flags |= WINDOW_WASZOOMED;
712
0
  else
713
0
    w->flags &= ~WINDOW_WASZOOMED;
714
0
  return (window_unzoom(w, 1) == 0);
715
0
}
716
717
int
718
window_pop_zoom(struct window *w)
719
0
{
720
0
  log_debug("%s: @%u %d", __func__, w->id,
721
0
      !!(w->flags & WINDOW_WASZOOMED));
722
0
  if (w->flags & WINDOW_WASZOOMED)
723
0
    return (window_zoom(w->active) == 0);
724
0
  return (0);
725
0
}
726
727
struct window_pane *
728
window_add_pane(struct window *w, struct window_pane *other, u_int hlimit,
729
    int flags)
730
11.6k
{
731
11.6k
  struct window_pane  *wp;
732
733
11.6k
  if (other == NULL)
734
11.6k
    other = w->active;
735
736
11.6k
  wp = window_pane_create(w, w->sx, w->sy, hlimit);
737
11.6k
  if (TAILQ_EMPTY(&w->panes)) {
738
11.6k
    log_debug("%s: @%u at start", __func__, w->id);
739
11.6k
    TAILQ_INSERT_HEAD(&w->panes, wp, entry);
740
11.6k
  } else if (flags & SPAWN_BEFORE) {
741
0
    log_debug("%s: @%u before %%%u", __func__, w->id, wp->id);
742
0
    if (flags & SPAWN_FULLSIZE)
743
0
      TAILQ_INSERT_HEAD(&w->panes, wp, entry);
744
0
    else
745
0
      TAILQ_INSERT_BEFORE(other, wp, entry);
746
0
  } else {
747
0
    log_debug("%s: @%u after %%%u", __func__, w->id, wp->id);
748
0
    if (flags & SPAWN_FULLSIZE)
749
0
      TAILQ_INSERT_TAIL(&w->panes, wp, entry);
750
0
    else
751
0
      TAILQ_INSERT_AFTER(&w->panes, other, wp, entry);
752
0
  }
753
11.6k
  return (wp);
754
11.6k
}
755
756
void
757
window_lost_pane(struct window *w, struct window_pane *wp)
758
0
{
759
0
  log_debug("%s: @%u pane %%%u", __func__, w->id, wp->id);
760
761
0
  if (wp == marked_pane.wp)
762
0
    server_clear_marked();
763
764
0
  window_pane_stack_remove(&w->last_panes, wp);
765
0
  if (wp == w->active) {
766
0
    w->active = TAILQ_FIRST(&w->last_panes);
767
0
    if (w->active == NULL) {
768
0
      w->active = TAILQ_PREV(wp, window_panes, entry);
769
0
      if (w->active == NULL)
770
0
        w->active = TAILQ_NEXT(wp, entry);
771
0
    }
772
0
    if (w->active != NULL) {
773
0
      window_pane_stack_remove(&w->last_panes, w->active);
774
0
      w->active->flags |= PANE_CHANGED;
775
0
      notify_window("window-pane-changed", w);
776
0
      window_update_focus(w);
777
0
    }
778
0
  }
779
0
}
780
781
void
782
window_remove_pane(struct window *w, struct window_pane *wp)
783
0
{
784
0
  window_lost_pane(w, wp);
785
786
0
  TAILQ_REMOVE(&w->panes, wp, entry);
787
0
  window_pane_destroy(wp);
788
0
}
789
790
struct window_pane *
791
window_pane_at_index(struct window *w, u_int idx)
792
0
{
793
0
  struct window_pane  *wp;
794
0
  u_int      n;
795
796
0
  n = options_get_number(w->options, "pane-base-index");
797
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
798
0
    if (n == idx)
799
0
      return (wp);
800
0
    n++;
801
0
  }
802
0
  return (NULL);
803
0
}
804
805
struct window_pane *
806
window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n)
807
0
{
808
0
  for (; n > 0; n--) {
809
0
    if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
810
0
      wp = TAILQ_FIRST(&w->panes);
811
0
  }
812
813
0
  return (wp);
814
0
}
815
816
struct window_pane *
817
window_pane_previous_by_number(struct window *w, struct window_pane *wp,
818
    u_int n)
819
0
{
820
0
  for (; n > 0; n--) {
821
0
    if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL)
822
0
      wp = TAILQ_LAST(&w->panes, window_panes);
823
0
  }
824
825
0
  return (wp);
826
0
}
827
828
int
829
window_pane_index(struct window_pane *wp, u_int *i)
830
0
{
831
0
  struct window_pane  *wq;
832
0
  struct window   *w = wp->window;
833
834
0
  *i = options_get_number(w->options, "pane-base-index");
835
0
  TAILQ_FOREACH(wq, &w->panes, entry) {
836
0
    if (wp == wq) {
837
0
      return (0);
838
0
    }
839
0
    (*i)++;
840
0
  }
841
842
0
  return (-1);
843
0
}
844
845
u_int
846
window_count_panes(struct window *w)
847
0
{
848
0
  struct window_pane  *wp;
849
0
  u_int      n;
850
851
0
  n = 0;
852
0
  TAILQ_FOREACH(wp, &w->panes, entry)
853
0
    n++;
854
0
  return (n);
855
0
}
856
857
void
858
window_destroy_panes(struct window *w)
859
11.6k
{
860
11.6k
  struct window_pane  *wp;
861
862
11.6k
  while (!TAILQ_EMPTY(&w->last_panes)) {
863
0
    wp = TAILQ_FIRST(&w->last_panes);
864
0
    window_pane_stack_remove(&w->last_panes, wp);
865
0
  }
866
867
23.3k
  while (!TAILQ_EMPTY(&w->panes)) {
868
11.6k
    wp = TAILQ_FIRST(&w->panes);
869
11.6k
    TAILQ_REMOVE(&w->panes, wp, entry);
870
11.6k
    window_pane_destroy(wp);
871
11.6k
  }
872
11.6k
}
873
874
const char *
875
window_printable_flags(struct winlink *wl, int escape)
876
0
{
877
0
  struct session  *s = wl->session;
878
0
  static char  flags[32];
879
0
  int    pos;
880
881
0
  pos = 0;
882
0
  if (wl->flags & WINLINK_ACTIVITY) {
883
0
    flags[pos++] = '#';
884
0
    if (escape)
885
0
      flags[pos++] = '#';
886
0
  }
887
0
  if (wl->flags & WINLINK_BELL)
888
0
    flags[pos++] = '!';
889
0
  if (wl->flags & WINLINK_SILENCE)
890
0
    flags[pos++] = '~';
891
0
  if (wl == s->curw)
892
0
    flags[pos++] = '*';
893
0
  if (wl == TAILQ_FIRST(&s->lastw))
894
0
    flags[pos++] = '-';
895
0
  if (server_check_marked() && wl == marked_pane.wl)
896
0
    flags[pos++] = 'M';
897
0
  if (wl->window->flags & WINDOW_ZOOMED)
898
0
    flags[pos++] = 'Z';
899
0
  flags[pos] = '\0';
900
0
  return (flags);
901
0
}
902
903
struct window_pane *
904
window_pane_find_by_id_str(const char *s)
905
0
{
906
0
  const char  *errstr;
907
0
  u_int    id;
908
909
0
  if (*s != '%')
910
0
    return (NULL);
911
912
0
  id = strtonum(s + 1, 0, UINT_MAX, &errstr);
913
0
  if (errstr != NULL)
914
0
    return (NULL);
915
0
  return (window_pane_find_by_id(id));
916
0
}
917
918
struct window_pane *
919
window_pane_find_by_id(u_int id)
920
0
{
921
0
  struct window_pane  wp;
922
923
0
  wp.id = id;
924
0
  return (RB_FIND(window_pane_tree, &all_window_panes, &wp));
925
0
}
926
927
static struct window_pane *
928
window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
929
11.6k
{
930
11.6k
  struct window_pane  *wp;
931
11.6k
  char       host[HOST_NAME_MAX + 1];
932
933
11.6k
  wp = xcalloc(1, sizeof *wp);
934
11.6k
  wp->window = w;
935
11.6k
  wp->options = options_create(w->options);
936
11.6k
  wp->flags = (PANE_STYLECHANGED|PANE_THEMECHANGED);
937
938
11.6k
  wp->id = next_window_pane_id++;
939
11.6k
  RB_INSERT(window_pane_tree, &all_window_panes, wp);
940
941
11.6k
  wp->fd = -1;
942
943
11.6k
  TAILQ_INIT(&wp->modes);
944
945
11.6k
  TAILQ_INIT (&wp->resize_queue);
946
947
11.6k
  wp->sx = sx;
948
11.6k
  wp->sy = sy;
949
950
11.6k
  wp->pipe_fd = -1;
951
952
11.6k
  wp->control_bg = -1;
953
11.6k
  wp->control_fg = -1;
954
955
11.6k
  style_set_scrollbar_style_from_option(&wp->scrollbar_style,
956
11.6k
      wp->options);
957
958
11.6k
  colour_palette_init(&wp->palette);
959
11.6k
  colour_palette_from_option(&wp->palette, wp->options);
960
961
11.6k
  screen_init(&wp->base, sx, sy, hlimit);
962
11.6k
  wp->screen = &wp->base;
963
11.6k
  window_pane_default_cursor(wp);
964
965
11.6k
  screen_init(&wp->status_screen, 1, 1, 0);
966
967
11.6k
  if (gethostname(host, sizeof host) == 0)
968
11.6k
    screen_set_title(&wp->base, host);
969
970
11.6k
  return (wp);
971
11.6k
}
972
973
static void
974
window_pane_destroy(struct window_pane *wp)
975
11.6k
{
976
11.6k
  struct window_pane_resize *r;
977
11.6k
  struct window_pane_resize *r1;
978
979
11.6k
  window_pane_reset_mode_all(wp);
980
11.6k
  free(wp->searchstr);
981
982
11.6k
  if (wp->fd != -1) {
983
#ifdef HAVE_UTEMPTER
984
    utempter_remove_record(wp->fd);
985
    kill(getpid(), SIGCHLD);
986
#endif
987
11.6k
    bufferevent_free(wp->event);
988
11.6k
    close(wp->fd);
989
11.6k
  }
990
11.6k
  if (wp->ictx != NULL)
991
11.6k
    input_free(wp->ictx);
992
993
11.6k
  screen_free(&wp->status_screen);
994
995
11.6k
  screen_free(&wp->base);
996
997
11.6k
  if (wp->pipe_fd != -1) {
998
0
    bufferevent_free(wp->pipe_event);
999
0
    close(wp->pipe_fd);
1000
0
  }
1001
1002
11.6k
  if (event_initialized(&wp->resize_timer))
1003
0
    event_del(&wp->resize_timer);
1004
11.6k
  TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) {
1005
0
    TAILQ_REMOVE(&wp->resize_queue, r, entry);
1006
0
    free(r);
1007
0
  }
1008
1009
11.6k
  RB_REMOVE(window_pane_tree, &all_window_panes, wp);
1010
1011
11.6k
  options_free(wp->options);
1012
11.6k
  free((void *)wp->cwd);
1013
11.6k
  free(wp->shell);
1014
11.6k
  cmd_free_argv(wp->argc, wp->argv);
1015
11.6k
  colour_palette_free(&wp->palette);
1016
11.6k
  free(wp);
1017
11.6k
}
1018
1019
static void
1020
window_pane_read_callback(__unused struct bufferevent *bufev, void *data)
1021
0
{
1022
0
  struct window_pane    *wp = data;
1023
0
  struct evbuffer     *evb = wp->event->input;
1024
0
  struct window_pane_offset *wpo = &wp->pipe_offset;
1025
0
  size_t         size = EVBUFFER_LENGTH(evb);
1026
0
  char        *new_data;
1027
0
  size_t         new_size;
1028
0
  struct client     *c;
1029
1030
0
  if (wp->pipe_fd != -1) {
1031
0
    new_data = window_pane_get_new_data(wp, wpo, &new_size);
1032
0
    if (new_size > 0) {
1033
0
      bufferevent_write(wp->pipe_event, new_data, new_size);
1034
0
      window_pane_update_used_data(wp, wpo, new_size);
1035
0
    }
1036
0
  }
1037
1038
0
  log_debug("%%%u has %zu bytes", wp->id, size);
1039
0
  TAILQ_FOREACH(c, &clients, entry) {
1040
0
    if (c->session != NULL && (c->flags & CLIENT_CONTROL))
1041
0
      control_write_output(c, wp);
1042
0
  }
1043
0
  input_parse_pane(wp);
1044
0
  bufferevent_disable(wp->event, EV_READ);
1045
0
}
1046
1047
static void
1048
window_pane_error_callback(__unused struct bufferevent *bufev,
1049
    __unused short what, void *data)
1050
0
{
1051
0
  struct window_pane *wp = data;
1052
1053
0
  log_debug("%%%u error", wp->id);
1054
0
  wp->flags |= PANE_EXITED;
1055
1056
0
  if (window_pane_destroy_ready(wp))
1057
0
    server_destroy_pane(wp, 1);
1058
0
}
1059
1060
void
1061
window_pane_set_event(struct window_pane *wp)
1062
0
{
1063
0
  setblocking(wp->fd, 0);
1064
1065
0
  wp->event = bufferevent_new(wp->fd, window_pane_read_callback,
1066
0
      NULL, window_pane_error_callback, wp);
1067
0
  if (wp->event == NULL)
1068
0
    fatalx("out of memory");
1069
0
  wp->ictx = input_init(wp, wp->event, &wp->palette);
1070
1071
0
  bufferevent_enable(wp->event, EV_READ|EV_WRITE);
1072
0
}
1073
1074
void
1075
window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
1076
0
{
1077
0
  struct window_mode_entry  *wme;
1078
0
  struct window_pane_resize *r;
1079
1080
0
  if (sx == wp->sx && sy == wp->sy)
1081
0
    return;
1082
1083
0
  r = xmalloc(sizeof *r);
1084
0
  r->sx = sx;
1085
0
  r->sy = sy;
1086
0
  r->osx = wp->sx;
1087
0
  r->osy = wp->sy;
1088
0
  TAILQ_INSERT_TAIL (&wp->resize_queue, r, entry);
1089
1090
0
  wp->sx = sx;
1091
0
  wp->sy = sy;
1092
1093
0
  log_debug("%s: %%%u resize %ux%u", __func__, wp->id, sx, sy);
1094
0
  screen_resize(&wp->base, sx, sy, wp->base.saved_grid == NULL);
1095
1096
0
  wme = TAILQ_FIRST(&wp->modes);
1097
0
  if (wme != NULL && wme->mode->resize != NULL)
1098
0
    wme->mode->resize(wme, sx, sy);
1099
0
}
1100
1101
int
1102
window_pane_set_mode(struct window_pane *wp, struct window_pane *swp,
1103
    const struct window_mode *mode, struct cmd_find_state *fs,
1104
    struct args *args)
1105
0
{
1106
0
  struct window_mode_entry  *wme;
1107
0
  struct window     *w = wp->window;
1108
1109
0
  if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode)
1110
0
    return (1);
1111
1112
0
  TAILQ_FOREACH(wme, &wp->modes, entry) {
1113
0
    if (wme->mode == mode)
1114
0
      break;
1115
0
  }
1116
0
  if (wme != NULL) {
1117
0
    TAILQ_REMOVE(&wp->modes, wme, entry);
1118
0
    TAILQ_INSERT_HEAD(&wp->modes, wme, entry);
1119
0
  } else {
1120
0
    wme = xcalloc(1, sizeof *wme);
1121
0
    wme->wp = wp;
1122
0
    wme->swp = swp;
1123
0
    wme->mode = mode;
1124
0
    wme->prefix = 1;
1125
0
    TAILQ_INSERT_HEAD(&wp->modes, wme, entry);
1126
0
    wme->screen = wme->mode->init(wme, fs, args);
1127
0
  }
1128
0
  wp->screen = wme->screen;
1129
1130
0
  wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED);
1131
0
  layout_fix_panes(w, NULL);
1132
1133
0
  server_redraw_window_borders(wp->window);
1134
0
  server_status_window(wp->window);
1135
0
  notify_pane("pane-mode-changed", wp);
1136
1137
0
  return (0);
1138
0
}
1139
1140
void
1141
window_pane_reset_mode(struct window_pane *wp)
1142
0
{
1143
0
  struct window_mode_entry  *wme, *next;
1144
0
  struct window     *w = wp->window;
1145
1146
0
  if (TAILQ_EMPTY(&wp->modes))
1147
0
    return;
1148
1149
0
  wme = TAILQ_FIRST(&wp->modes);
1150
0
  TAILQ_REMOVE(&wp->modes, wme, entry);
1151
0
  wme->mode->free(wme);
1152
0
  free(wme);
1153
1154
0
  next = TAILQ_FIRST(&wp->modes);
1155
0
  if (next == NULL) {
1156
0
    wp->flags &= ~PANE_UNSEENCHANGES;
1157
0
    log_debug("%s: no next mode", __func__);
1158
0
    wp->screen = &wp->base;
1159
0
  } else {
1160
0
    log_debug("%s: next mode is %s", __func__, next->mode->name);
1161
0
    wp->screen = next->screen;
1162
0
    if (next->mode->resize != NULL)
1163
0
      next->mode->resize(next, wp->sx, wp->sy);
1164
0
  }
1165
1166
0
  wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED);
1167
0
  layout_fix_panes(w, NULL);
1168
1169
0
  server_redraw_window_borders(wp->window);
1170
0
  server_status_window(wp->window);
1171
0
  notify_pane("pane-mode-changed", wp);
1172
0
}
1173
1174
void
1175
window_pane_reset_mode_all(struct window_pane *wp)
1176
11.6k
{
1177
11.6k
  while (!TAILQ_EMPTY(&wp->modes))
1178
0
    window_pane_reset_mode(wp);
1179
11.6k
}
1180
1181
static void
1182
window_pane_copy_paste(struct window_pane *wp, char *buf, size_t len)
1183
0
{
1184
0
  struct window_pane  *loop;
1185
1186
0
  TAILQ_FOREACH(loop, &wp->window->panes, entry) {
1187
0
    if (loop != wp &&
1188
0
        TAILQ_EMPTY(&loop->modes) &&
1189
0
        loop->fd != -1 &&
1190
0
        (~loop->flags & PANE_INPUTOFF) &&
1191
0
        window_pane_visible(loop) &&
1192
0
        options_get_number(loop->options, "synchronize-panes")) {
1193
0
      log_debug("%s: %.*s", __func__, (int)len, buf);
1194
0
      bufferevent_write(loop->event, buf, len);
1195
0
    }
1196
0
  }
1197
0
}
1198
1199
static void
1200
window_pane_copy_key(struct window_pane *wp, key_code key)
1201
0
{
1202
0
  struct window_pane  *loop;
1203
1204
0
  TAILQ_FOREACH(loop, &wp->window->panes, entry) {
1205
0
    if (loop != wp &&
1206
0
        TAILQ_EMPTY(&loop->modes) &&
1207
0
        loop->fd != -1 &&
1208
0
        (~loop->flags & PANE_INPUTOFF) &&
1209
0
        window_pane_visible(loop) &&
1210
0
        options_get_number(loop->options, "synchronize-panes"))
1211
0
      input_key_pane(loop, key, NULL);
1212
0
  }
1213
0
}
1214
1215
void
1216
window_pane_paste(struct window_pane *wp, key_code key, char *buf, size_t len)
1217
0
{
1218
0
  if (!TAILQ_EMPTY(&wp->modes))
1219
0
    return;
1220
1221
0
  if (wp->fd == -1 || wp->flags & PANE_INPUTOFF)
1222
0
    return;
1223
1224
0
  if (KEYC_IS_PASTE(key) && (~wp->screen->mode & MODE_BRACKETPASTE))
1225
0
    return;
1226
1227
0
  log_debug("%s: %.*s", __func__, (int)len, buf);
1228
0
  bufferevent_write(wp->event, buf, len);
1229
1230
0
  if (options_get_number(wp->options, "synchronize-panes"))
1231
0
    window_pane_copy_paste(wp, buf, len);
1232
0
}
1233
1234
int
1235
window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
1236
    struct winlink *wl, key_code key, struct mouse_event *m)
1237
0
{
1238
0
  struct window_mode_entry  *wme;
1239
1240
0
  if (KEYC_IS_MOUSE(key) && m == NULL)
1241
0
    return (-1);
1242
1243
0
  wme = TAILQ_FIRST(&wp->modes);
1244
0
  if (wme != NULL) {
1245
0
    if (wme->mode->key != NULL && c != NULL) {
1246
0
      key &= ~KEYC_MASK_FLAGS;
1247
0
      wme->mode->key(wme, c, s, wl, key, m);
1248
0
    }
1249
0
    return (0);
1250
0
  }
1251
1252
0
  if (wp->fd == -1 || wp->flags & PANE_INPUTOFF)
1253
0
    return (0);
1254
1255
0
  if (input_key_pane(wp, key, m) != 0)
1256
0
    return (-1);
1257
1258
0
  if (KEYC_IS_MOUSE(key))
1259
0
    return (0);
1260
0
  if (options_get_number(wp->options, "synchronize-panes"))
1261
0
    window_pane_copy_key(wp, key);
1262
0
  return (0);
1263
0
}
1264
1265
int
1266
window_pane_visible(struct window_pane *wp)
1267
0
{
1268
0
  if (~wp->window->flags & WINDOW_ZOOMED)
1269
0
    return (1);
1270
0
  return (wp == wp->window->active);
1271
0
}
1272
1273
int
1274
window_pane_exited(struct window_pane *wp)
1275
0
{
1276
0
  return (wp->fd == -1 || (wp->flags & PANE_EXITED));
1277
0
}
1278
1279
u_int
1280
window_pane_search(struct window_pane *wp, const char *term, int regex,
1281
    int ignore)
1282
0
{
1283
0
  struct screen *s = &wp->base;
1284
0
  regex_t    r;
1285
0
  char    *new = NULL, *line;
1286
0
  u_int    i;
1287
0
  int    flags = 0, found;
1288
0
  size_t     n;
1289
1290
0
  if (!regex) {
1291
0
    if (ignore)
1292
0
      flags |= FNM_CASEFOLD;
1293
0
    xasprintf(&new, "*%s*", term);
1294
0
  } else {
1295
0
    if (ignore)
1296
0
      flags |= REG_ICASE;
1297
0
    if (regcomp(&r, term, flags|REG_EXTENDED) != 0)
1298
0
      return (0);
1299
0
  }
1300
1301
0
  for (i = 0; i < screen_size_y(s); i++) {
1302
0
    line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
1303
0
    for (n = strlen(line); n > 0; n--) {
1304
0
      if (!isspace((u_char)line[n - 1]))
1305
0
        break;
1306
0
      line[n - 1] = '\0';
1307
0
    }
1308
0
    log_debug("%s: %s", __func__, line);
1309
0
    if (!regex)
1310
0
      found = (fnmatch(new, line, flags) == 0);
1311
0
    else
1312
0
      found = (regexec(&r, line, 0, NULL, 0) == 0);
1313
0
    free(line);
1314
0
    if (found)
1315
0
      break;
1316
0
  }
1317
0
  if (!regex)
1318
0
    free(new);
1319
0
  else
1320
0
    regfree(&r);
1321
1322
0
  if (i == screen_size_y(s))
1323
0
    return (0);
1324
0
  return (i + 1);
1325
0
}
1326
1327
/* Get MRU pane from a list. */
1328
static struct window_pane *
1329
window_pane_choose_best(struct window_pane **list, u_int size)
1330
0
{
1331
0
  struct window_pane  *next, *best;
1332
0
  u_int      i;
1333
1334
0
  if (size == 0)
1335
0
    return (NULL);
1336
1337
0
  best = list[0];
1338
0
  for (i = 1; i < size; i++) {
1339
0
    next = list[i];
1340
0
    if (next->active_point > best->active_point)
1341
0
      best = next;
1342
0
  }
1343
0
  return (best);
1344
0
}
1345
1346
/*
1347
 * Get full size and offset of a window pane including the area of the
1348
 * scrollbars if they were visible but not including the border(s).
1349
 */
1350
static void
1351
window_pane_full_size_offset(struct window_pane *wp, u_int *xoff, u_int *yoff,
1352
    u_int *sx, u_int *sy)
1353
0
{
1354
0
  struct window   *w = wp->window;
1355
0
  int      pane_scrollbars;
1356
0
  u_int      sb_w, sb_pos;
1357
1358
0
  pane_scrollbars = options_get_number(w->options, "pane-scrollbars");
1359
0
  sb_pos = options_get_number(w->options, "pane-scrollbars-position");
1360
1361
0
  if (window_pane_show_scrollbar(wp, pane_scrollbars))
1362
0
    sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
1363
0
  else
1364
0
    sb_w = 0;
1365
0
  if (sb_pos == PANE_SCROLLBARS_LEFT) {
1366
0
    *xoff = wp->xoff - sb_w;
1367
0
    *sx = wp->sx + sb_w;
1368
0
  } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
1369
0
    *xoff = wp->xoff;
1370
0
    *sx = wp->sx + sb_w;
1371
0
  }
1372
0
  *yoff = wp->yoff;
1373
0
  *sy = wp->sy;
1374
0
}
1375
1376
/*
1377
 * Find the pane directly above another. We build a list of those adjacent to
1378
 * top edge and then choose the best.
1379
 */
1380
struct window_pane *
1381
window_pane_find_up(struct window_pane *wp)
1382
0
{
1383
0
  struct window   *w;
1384
0
  struct window_pane  *next, *best, **list;
1385
0
  u_int      edge, left, right, end, size;
1386
0
  int      status, found;
1387
0
  u_int      xoff, yoff, sx, sy;
1388
1389
0
  if (wp == NULL)
1390
0
    return (NULL);
1391
0
  w = wp->window;
1392
0
  status = options_get_number(w->options, "pane-border-status");
1393
1394
0
  list = NULL;
1395
0
  size = 0;
1396
1397
0
  window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
1398
1399
0
  edge = yoff;
1400
0
  if (status == PANE_STATUS_TOP) {
1401
0
    if (edge == 1)
1402
0
      edge = w->sy + 1;
1403
0
  } else if (status == PANE_STATUS_BOTTOM) {
1404
0
    if (edge == 0)
1405
0
      edge = w->sy;
1406
0
  } else {
1407
0
    if (edge == 0)
1408
0
      edge = w->sy + 1;
1409
0
  }
1410
1411
0
  left = xoff;
1412
0
  right = xoff + sx;
1413
1414
0
  TAILQ_FOREACH(next, &w->panes, entry) {
1415
0
    window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy);
1416
0
    if (next == wp)
1417
0
      continue;
1418
0
    if (yoff + sy + 1 != edge)
1419
0
      continue;
1420
0
    end = xoff + sx - 1;
1421
1422
0
    found = 0;
1423
0
    if (xoff < left && end > right)
1424
0
      found = 1;
1425
0
    else if (xoff >= left && xoff <= right)
1426
0
      found = 1;
1427
0
    else if (end >= left && end <= right)
1428
0
      found = 1;
1429
0
    if (!found)
1430
0
      continue;
1431
0
    list = xreallocarray(list, size + 1, sizeof *list);
1432
0
    list[size++] = next;
1433
0
  }
1434
1435
0
  best = window_pane_choose_best(list, size);
1436
0
  free(list);
1437
0
  return (best);
1438
0
}
1439
1440
/* Find the pane directly below another. */
1441
struct window_pane *
1442
window_pane_find_down(struct window_pane *wp)
1443
0
{
1444
0
  struct window   *w;
1445
0
  struct window_pane  *next, *best, **list;
1446
0
  u_int      edge, left, right, end, size;
1447
0
  int      status, found;
1448
0
  u_int      xoff, yoff, sx, sy;
1449
1450
0
  if (wp == NULL)
1451
0
    return (NULL);
1452
0
  w = wp->window;
1453
0
  status = options_get_number(w->options, "pane-border-status");
1454
1455
0
  list = NULL;
1456
0
  size = 0;
1457
1458
0
  window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
1459
1460
0
  edge = yoff + sy + 1;
1461
0
  if (status == PANE_STATUS_TOP) {
1462
0
    if (edge >= w->sy)
1463
0
      edge = 1;
1464
0
  } else if (status == PANE_STATUS_BOTTOM) {
1465
0
    if (edge >= w->sy - 1)
1466
0
      edge = 0;
1467
0
  } else {
1468
0
    if (edge >= w->sy)
1469
0
      edge = 0;
1470
0
  }
1471
1472
0
  left = wp->xoff;
1473
0
  right = wp->xoff + wp->sx;
1474
1475
0
  TAILQ_FOREACH(next, &w->panes, entry) {
1476
0
    window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy);
1477
0
    if (next == wp)
1478
0
      continue;
1479
0
    if (yoff != edge)
1480
0
      continue;
1481
0
    end = xoff + sx - 1;
1482
1483
0
    found = 0;
1484
0
    if (xoff < left && end > right)
1485
0
      found = 1;
1486
0
    else if (xoff >= left && xoff <= right)
1487
0
      found = 1;
1488
0
    else if (end >= left && end <= right)
1489
0
      found = 1;
1490
0
    if (!found)
1491
0
      continue;
1492
0
    list = xreallocarray(list, size + 1, sizeof *list);
1493
0
    list[size++] = next;
1494
0
  }
1495
1496
0
  best = window_pane_choose_best(list, size);
1497
0
  free(list);
1498
0
  return (best);
1499
0
}
1500
1501
/* Find the pane directly to the left of another. */
1502
struct window_pane *
1503
window_pane_find_left(struct window_pane *wp)
1504
0
{
1505
0
  struct window   *w;
1506
0
  struct window_pane  *next, *best, **list;
1507
0
  u_int      edge, top, bottom, end, size;
1508
0
  int      found;
1509
0
  u_int      xoff, yoff, sx, sy;
1510
1511
0
  if (wp == NULL)
1512
0
    return (NULL);
1513
0
  w = wp->window;
1514
1515
0
  list = NULL;
1516
0
  size = 0;
1517
1518
0
  window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
1519
1520
0
  edge = xoff;
1521
0
  if (edge == 0)
1522
0
    edge = w->sx + 1;
1523
1524
0
  top = yoff;
1525
0
  bottom = yoff + sy;
1526
1527
0
  TAILQ_FOREACH(next, &w->panes, entry) {
1528
0
    window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy);
1529
0
    if (next == wp)
1530
0
      continue;
1531
0
    if (xoff + sx + 1 != edge)
1532
0
      continue;
1533
0
    end = yoff + sy - 1;
1534
1535
0
    found = 0;
1536
0
    if (yoff < top && end > bottom)
1537
0
      found = 1;
1538
0
    else if (yoff >= top && yoff <= bottom)
1539
0
      found = 1;
1540
0
    else if (end >= top && end <= bottom)
1541
0
      found = 1;
1542
0
    if (!found)
1543
0
      continue;
1544
0
    list = xreallocarray(list, size + 1, sizeof *list);
1545
0
    list[size++] = next;
1546
0
  }
1547
1548
0
  best = window_pane_choose_best(list, size);
1549
0
  free(list);
1550
0
  return (best);
1551
0
}
1552
1553
/* Find the pane directly to the right of another. */
1554
struct window_pane *
1555
window_pane_find_right(struct window_pane *wp)
1556
0
{
1557
0
  struct window   *w;
1558
0
  struct window_pane  *next, *best, **list;
1559
0
  u_int      edge, top, bottom, end, size;
1560
0
  int      found;
1561
0
  u_int      xoff, yoff, sx, sy;
1562
1563
0
  if (wp == NULL)
1564
0
    return (NULL);
1565
0
  w = wp->window;
1566
1567
0
  list = NULL;
1568
0
  size = 0;
1569
1570
0
  window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
1571
1572
0
  edge = xoff + sx + 1;
1573
0
  if (edge >= w->sx)
1574
0
    edge = 0;
1575
1576
0
  top = wp->yoff;
1577
0
  bottom = wp->yoff + wp->sy;
1578
1579
0
  TAILQ_FOREACH(next, &w->panes, entry) {
1580
0
    window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy);
1581
0
    if (next == wp)
1582
0
      continue;
1583
0
    if (xoff != edge)
1584
0
      continue;
1585
0
    end = yoff + sy - 1;
1586
1587
0
    found = 0;
1588
0
    if (yoff < top && end > bottom)
1589
0
      found = 1;
1590
0
    else if (yoff >= top && yoff <= bottom)
1591
0
      found = 1;
1592
0
    else if (end >= top && end <= bottom)
1593
0
      found = 1;
1594
0
    if (!found)
1595
0
      continue;
1596
0
    list = xreallocarray(list, size + 1, sizeof *list);
1597
0
    list[size++] = next;
1598
0
  }
1599
1600
0
  best = window_pane_choose_best(list, size);
1601
0
  free(list);
1602
0
  return (best);
1603
0
}
1604
1605
void
1606
window_pane_stack_push(struct window_panes *stack, struct window_pane *wp)
1607
0
{
1608
0
  if (wp != NULL) {
1609
0
    window_pane_stack_remove(stack, wp);
1610
0
    TAILQ_INSERT_HEAD(stack, wp, sentry);
1611
0
    wp->flags |= PANE_VISITED;
1612
0
  }
1613
0
}
1614
1615
void
1616
window_pane_stack_remove(struct window_panes *stack, struct window_pane *wp)
1617
0
{
1618
0
  if (wp != NULL && (wp->flags & PANE_VISITED)) {
1619
0
    TAILQ_REMOVE(stack, wp, sentry);
1620
0
    wp->flags &= ~PANE_VISITED;
1621
0
  }
1622
0
}
1623
1624
/* Clear alert flags for a winlink */
1625
void
1626
winlink_clear_flags(struct winlink *wl)
1627
0
{
1628
0
  struct winlink  *loop;
1629
1630
0
  wl->window->flags &= ~WINDOW_ALERTFLAGS;
1631
0
  TAILQ_FOREACH(loop, &wl->window->winlinks, wentry) {
1632
0
    if ((loop->flags & WINLINK_ALERTFLAGS) != 0) {
1633
0
      loop->flags &= ~WINLINK_ALERTFLAGS;
1634
0
      server_status_session(loop->session);
1635
0
    }
1636
0
  }
1637
0
}
1638
1639
/* Shuffle window indexes up. */
1640
int
1641
winlink_shuffle_up(struct session *s, struct winlink *wl, int before)
1642
0
{
1643
0
  int  idx, last;
1644
1645
0
  if (wl == NULL)
1646
0
    return (-1);
1647
0
  if (before)
1648
0
    idx = wl->idx;
1649
0
  else
1650
0
    idx = wl->idx + 1;
1651
1652
  /* Find the next free index. */
1653
0
  for (last = idx; last < INT_MAX; last++) {
1654
0
    if (winlink_find_by_index(&s->windows, last) == NULL)
1655
0
      break;
1656
0
  }
1657
0
  if (last == INT_MAX)
1658
0
    return (-1);
1659
1660
  /* Move everything from last - 1 to idx up a bit. */
1661
0
  for (; last > idx; last--) {
1662
0
    wl = winlink_find_by_index(&s->windows, last - 1);
1663
0
    RB_REMOVE(winlinks, &s->windows, wl);
1664
0
    wl->idx++;
1665
0
    RB_INSERT(winlinks, &s->windows, wl);
1666
0
  }
1667
1668
0
  return (idx);
1669
0
}
1670
1671
static void
1672
window_pane_input_callback(struct client *c, __unused const char *path,
1673
    int error, int closed, struct evbuffer *buffer, void *data)
1674
0
{
1675
0
  struct window_pane_input_data *cdata = data;
1676
0
  struct window_pane    *wp;
1677
0
  u_char        *buf = EVBUFFER_DATA(buffer);
1678
0
  size_t         len = EVBUFFER_LENGTH(buffer);
1679
1680
0
  wp = window_pane_find_by_id(cdata->wp);
1681
0
  if (cdata->file != NULL && (wp == NULL || c->flags & CLIENT_DEAD)) {
1682
0
    if (wp == NULL) {
1683
0
      c->retval = 1;
1684
0
      c->flags |= CLIENT_EXIT;
1685
0
    }
1686
0
    file_cancel(cdata->file);
1687
0
  } else if (cdata->file == NULL || closed || error != 0) {
1688
0
    cmdq_continue(cdata->item);
1689
0
    server_client_unref(c);
1690
0
    free(cdata);
1691
0
  } else
1692
0
    input_parse_buffer(wp, buf, len);
1693
0
  evbuffer_drain(buffer, len);
1694
0
}
1695
1696
int
1697
window_pane_start_input(struct window_pane *wp, struct cmdq_item *item,
1698
    char **cause)
1699
0
{
1700
0
  struct client     *c = cmdq_get_client(item);
1701
0
  struct window_pane_input_data *cdata;
1702
1703
0
  if (~wp->flags & PANE_EMPTY) {
1704
0
    *cause = xstrdup("pane is not empty");
1705
0
    return (-1);
1706
0
  }
1707
0
  if (c->flags & (CLIENT_DEAD|CLIENT_EXITED))
1708
0
    return (1);
1709
0
  if (c->session != NULL)
1710
0
    return (1);
1711
1712
0
  cdata = xmalloc(sizeof *cdata);
1713
0
  cdata->item = item;
1714
0
  cdata->wp = wp->id;
1715
0
  cdata->file = file_read(c, "-", window_pane_input_callback, cdata);
1716
0
  c->references++;
1717
1718
0
  return (0);
1719
0
}
1720
1721
void *
1722
window_pane_get_new_data(struct window_pane *wp,
1723
    struct window_pane_offset *wpo, size_t *size)
1724
0
{
1725
0
  size_t  used = wpo->used - wp->base_offset;
1726
1727
0
  *size = EVBUFFER_LENGTH(wp->event->input) - used;
1728
0
  return (EVBUFFER_DATA(wp->event->input) + used);
1729
0
}
1730
1731
void
1732
window_pane_update_used_data(struct window_pane *wp,
1733
    struct window_pane_offset *wpo, size_t size)
1734
0
{
1735
0
  size_t  used = wpo->used - wp->base_offset;
1736
1737
0
  if (size > EVBUFFER_LENGTH(wp->event->input) - used)
1738
0
    size = EVBUFFER_LENGTH(wp->event->input) - used;
1739
0
  wpo->used += size;
1740
0
}
1741
1742
void
1743
window_set_fill_character(struct window *w)
1744
11.6k
{
1745
11.6k
  const char    *value;
1746
11.6k
  struct utf8_data  *ud;
1747
1748
11.6k
  free(w->fill_character);
1749
11.6k
  w->fill_character = NULL;
1750
1751
11.6k
  value = options_get_string(w->options, "fill-character");
1752
11.6k
  if (*value != '\0' && utf8_isvalid(value)) {
1753
0
    ud = utf8_fromcstr(value);
1754
0
    if (ud != NULL && ud[0].width == 1)
1755
0
      w->fill_character = ud;
1756
0
    else
1757
0
      free(ud);
1758
0
  }
1759
11.6k
}
1760
1761
void
1762
window_pane_default_cursor(struct window_pane *wp)
1763
11.6k
{
1764
11.6k
  screen_set_default_cursor(wp->screen, wp->options);
1765
11.6k
}
1766
1767
int
1768
window_pane_mode(struct window_pane *wp)
1769
0
{
1770
0
  if (TAILQ_FIRST(&wp->modes) != NULL) {
1771
0
    if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode)
1772
0
      return (WINDOW_PANE_COPY_MODE);
1773
0
    if (TAILQ_FIRST(&wp->modes)->mode == &window_view_mode)
1774
0
      return (WINDOW_PANE_VIEW_MODE);
1775
0
  }
1776
0
  return (WINDOW_PANE_NO_MODE);
1777
0
}
1778
1779
/* Return 1 if scrollbar is or should be displayed. */
1780
int
1781
window_pane_show_scrollbar(struct window_pane *wp, int sb_option)
1782
0
{
1783
0
  if (SCREEN_IS_ALTERNATE(wp->screen))
1784
0
    return (0);
1785
0
  if (sb_option == PANE_SCROLLBARS_ALWAYS ||
1786
0
      (sb_option == PANE_SCROLLBARS_MODAL &&
1787
0
      window_pane_mode(wp) != WINDOW_PANE_NO_MODE))
1788
0
    return (1);
1789
0
  return (0);
1790
0
}
1791
1792
int
1793
window_pane_get_bg(struct window_pane *wp)
1794
132
{
1795
132
  int     c;
1796
132
  struct grid_cell  defaults;
1797
1798
132
  c = window_pane_get_bg_control_client(wp);
1799
132
  if (c == -1) {
1800
132
    tty_default_colours(&defaults, wp);
1801
132
    if (COLOUR_DEFAULT(defaults.bg))
1802
132
      c = window_get_bg_client(wp);
1803
0
    else
1804
0
      c = defaults.bg;
1805
132
  }
1806
132
  return (c);
1807
132
}
1808
1809
/* Get a client with a background for the pane. */
1810
int
1811
window_get_bg_client(struct window_pane *wp)
1812
132
{
1813
132
  struct window *w = wp->window;
1814
132
  struct client *loop;
1815
1816
132
  TAILQ_FOREACH(loop, &clients, entry) {
1817
0
    if (loop->flags & CLIENT_UNATTACHEDFLAGS)
1818
0
      continue;
1819
0
    if (loop->session == NULL || !session_has(loop->session, w))
1820
0
      continue;
1821
0
    if (loop->tty.bg == -1)
1822
0
      continue;
1823
0
    return (loop->tty.bg);
1824
0
  }
1825
132
  return (-1);
1826
132
}
1827
1828
/*
1829
 * If any control mode client exists that has provided a bg color, return it.
1830
 * Otherwise, return -1.
1831
 */
1832
int
1833
window_pane_get_bg_control_client(struct window_pane *wp)
1834
132
{
1835
132
  struct client *c;
1836
1837
132
  if (wp->control_bg == -1)
1838
132
    return (-1);
1839
1840
0
  TAILQ_FOREACH(c, &clients, entry) {
1841
0
    if (c->flags & CLIENT_CONTROL)
1842
0
      return (wp->control_bg);
1843
0
  }
1844
0
  return (-1);
1845
0
}
1846
1847
/*
1848
 * Get a client with a foreground for the pane. There isn't much to choose
1849
 * between them so just use the first.
1850
 */
1851
int
1852
window_pane_get_fg(struct window_pane *wp)
1853
66
{
1854
66
  struct window *w = wp->window;
1855
66
  struct client *loop;
1856
1857
66
  TAILQ_FOREACH(loop, &clients, entry) {
1858
0
    if (loop->flags & CLIENT_UNATTACHEDFLAGS)
1859
0
      continue;
1860
0
    if (loop->session == NULL || !session_has(loop->session, w))
1861
0
      continue;
1862
0
    if (loop->tty.fg == -1)
1863
0
      continue;
1864
0
    return (loop->tty.fg);
1865
0
  }
1866
66
  return (-1);
1867
66
}
1868
1869
/*
1870
 * If any control mode client exists that has provided a fg color, return it.
1871
 * Otherwise, return -1.
1872
 */
1873
int
1874
window_pane_get_fg_control_client(struct window_pane *wp)
1875
66
{
1876
66
  struct client *c;
1877
1878
66
  if (wp->control_fg == -1)
1879
66
    return (-1);
1880
1881
0
  TAILQ_FOREACH(c, &clients, entry) {
1882
0
    if (c->flags & CLIENT_CONTROL)
1883
0
      return (wp->control_fg);
1884
0
  }
1885
0
  return (-1);
1886
0
}
1887
1888
enum client_theme
1889
window_pane_get_theme(struct window_pane *wp)
1890
66
{
1891
66
  struct window   *w;
1892
66
  struct client   *loop;
1893
66
  enum client_theme  theme;
1894
66
  int      found_light = 0, found_dark = 0;
1895
1896
66
  if (wp == NULL)
1897
0
    return (THEME_UNKNOWN);
1898
66
  w = wp->window;
1899
1900
  /*
1901
   * Derive theme from pane background color, if it's not the default
1902
   * colour.
1903
   */
1904
66
  theme = colour_totheme(window_pane_get_bg(wp));
1905
66
  if (theme != THEME_UNKNOWN)
1906
0
    return (theme);
1907
1908
  /* Try to find a client that has a theme. */
1909
66
  TAILQ_FOREACH(loop, &clients, entry) {
1910
0
    if (loop->flags & CLIENT_UNATTACHEDFLAGS)
1911
0
      continue;
1912
0
    if (loop->session == NULL || !session_has(loop->session, w))
1913
0
      continue;
1914
0
    switch (loop->theme) {
1915
0
    case THEME_LIGHT:
1916
0
      found_light = 1;
1917
0
      break;
1918
0
    case THEME_DARK:
1919
0
      found_dark = 1;
1920
0
      break;
1921
0
    case THEME_UNKNOWN:
1922
0
      break;
1923
0
    }
1924
0
  }
1925
1926
66
  if (found_dark && !found_light)
1927
0
    return (THEME_DARK);
1928
66
  if (found_light && !found_dark)
1929
0
    return (THEME_LIGHT);
1930
66
  return (THEME_UNKNOWN);
1931
66
}
1932
1933
void
1934
window_pane_send_theme_update(struct window_pane *wp)
1935
0
{
1936
0
  if (~wp->flags & PANE_THEMECHANGED)
1937
0
    return;
1938
0
  if (~wp->screen->mode & MODE_THEME_UPDATES)
1939
0
    return;
1940
1941
0
  switch (window_pane_get_theme(wp)) {
1942
0
  case THEME_LIGHT:
1943
0
    input_key_pane(wp, KEYC_REPORT_LIGHT_THEME, NULL);
1944
0
    break;
1945
0
  case THEME_DARK:
1946
0
    input_key_pane(wp, KEYC_REPORT_DARK_THEME, NULL);
1947
0
    break;
1948
0
  case THEME_UNKNOWN:
1949
0
    break;
1950
0
  }
1951
1952
0
  wp->flags &= ~PANE_THEMECHANGED;
1953
0
}