Coverage Report

Created: 2026-06-12 06:50

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