Coverage Report

Created: 2026-07-09 06:08

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_free(struct window_pane *);
75
static void window_pane_scrollbar_timer(int, short, void *);
76
static void window_pane_full_size_offset(struct window_pane *wp,
77
        int *xoff, int *yoff, u_int *sx, u_int *sy);
78
79
77.8k
RB_GENERATE(windows, window, entry, window_cmp);
Unexecuted instantiation: windows_RB_REMOVE_COLOR
windows_RB_REMOVE
Line
Count
Source
79
RB_GENERATE(windows, window, entry, window_cmp);
windows_RB_INSERT
Line
Count
Source
79
RB_GENERATE(windows, window, entry, window_cmp);
Unexecuted instantiation: windows_RB_FIND
Unexecuted instantiation: windows_RB_NFIND
Unexecuted instantiation: windows_RB_MINMAX
80
77.8k
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
81
77.8k
RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp);
Unexecuted instantiation: window_pane_tree_RB_REMOVE_COLOR
window_pane_tree_RB_REMOVE
Line
Count
Source
81
RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp);
window_pane_tree_RB_INSERT
Line
Count
Source
81
RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp);
Unexecuted instantiation: window_pane_tree_RB_FIND
Unexecuted instantiation: window_pane_tree_RB_NFIND
Unexecuted instantiation: window_pane_tree_RB_MINMAX
82
77.8k
83
77.8k
struct window_pane_prompt {
84
77.8k
  u_int      wp_id;
85
77.8k
  struct client   *c;
86
77.8k
  status_prompt_input_cb   inputcb;
87
77.8k
  prompt_free_cb     freecb;
88
77.8k
  void      *data;
89
77.8k
};
90
77.8k
91
77.8k
int
92
77.8k
window_cmp(struct window *w1, struct window *w2)
93
77.8k
{
94
0
  return (w1->id - w2->id);
95
0
}
96
97
int
98
winlink_cmp(struct winlink *wl1, struct winlink *wl2)
99
0
{
100
0
  return (wl1->idx - wl2->idx);
101
0
}
102
103
int
104
window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2)
105
0
{
106
0
  return (wp1->id - wp2->id);
107
0
}
108
109
struct winlink *
110
winlink_find_by_window(struct winlinks *wwl, struct window *w)
111
0
{
112
0
  struct winlink  *wl;
113
114
0
  RB_FOREACH(wl, winlinks, wwl) {
115
0
    if (wl->window == w)
116
0
      return (wl);
117
0
  }
118
119
0
  return (NULL);
120
0
}
121
122
struct winlink *
123
winlink_find_by_index(struct winlinks *wwl, int idx)
124
0
{
125
0
  struct winlink  wl;
126
127
0
  if (idx < 0)
128
0
    fatalx("bad index");
129
130
0
  wl.idx = idx;
131
0
  return (RB_FIND(winlinks, wwl, &wl));
132
0
}
133
134
struct winlink *
135
winlink_find_by_window_id(struct winlinks *wwl, u_int id)
136
0
{
137
0
  struct winlink *wl;
138
139
0
  RB_FOREACH(wl, winlinks, wwl) {
140
0
    if (wl->window->id == id)
141
0
      return (wl);
142
0
  }
143
0
  return (NULL);
144
0
}
145
146
static int
147
winlink_next_index(struct winlinks *wwl, int idx)
148
0
{
149
0
  int i;
150
151
0
  i = idx;
152
0
  do {
153
0
    if (winlink_find_by_index(wwl, i) == NULL)
154
0
      return (i);
155
0
    if (i == INT_MAX)
156
0
      i = 0;
157
0
    else
158
0
      i++;
159
0
  } while (i != idx);
160
0
  return (-1);
161
0
}
162
163
u_int
164
winlink_count(struct winlinks *wwl)
165
0
{
166
0
  struct winlink  *wl;
167
0
  u_int    n;
168
169
0
  n = 0;
170
0
  RB_FOREACH(wl, winlinks, wwl)
171
0
    n++;
172
173
0
  return (n);
174
0
}
175
176
struct winlink *
177
winlink_add(struct winlinks *wwl, int idx)
178
0
{
179
0
  struct winlink  *wl;
180
181
0
  if (idx < 0) {
182
0
    if ((idx = winlink_next_index(wwl, -idx - 1)) == -1)
183
0
      return (NULL);
184
0
  } else if (winlink_find_by_index(wwl, idx) != NULL)
185
0
    return (NULL);
186
187
0
  wl = xcalloc(1, sizeof *wl);
188
0
  wl->idx = idx;
189
0
  RB_INSERT(winlinks, wwl, wl);
190
191
0
  return (wl);
192
0
}
193
194
void
195
winlink_set_window(struct winlink *wl, struct window *w)
196
0
{
197
0
  if (wl->window != NULL) {
198
0
    TAILQ_REMOVE(&wl->window->winlinks, wl, wentry);
199
0
    window_remove_ref(wl->window, __func__);
200
0
  }
201
0
  TAILQ_INSERT_TAIL(&w->winlinks, wl, wentry);
202
0
  wl->window = w;
203
0
  window_add_ref(w, __func__);
204
0
}
205
206
void
207
winlink_remove(struct winlinks *wwl, struct winlink *wl)
208
0
{
209
0
  struct window *w = wl->window;
210
211
0
  if (w != NULL) {
212
0
    TAILQ_REMOVE(&w->winlinks, wl, wentry);
213
0
    window_remove_ref(w, __func__);
214
0
  }
215
216
0
  RB_REMOVE(winlinks, wwl, wl);
217
0
  free(wl);
218
0
}
219
220
struct winlink *
221
winlink_next(struct winlink *wl)
222
0
{
223
0
  return (RB_NEXT(winlinks, wwl, wl));
224
0
}
225
226
struct winlink *
227
winlink_previous(struct winlink *wl)
228
0
{
229
0
  return (RB_PREV(winlinks, wwl, wl));
230
0
}
231
232
struct winlink *
233
winlink_next_by_number(struct winlink *wl, struct session *s, int n)
234
0
{
235
0
  for (; n > 0; n--) {
236
0
    if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL)
237
0
      wl = RB_MIN(winlinks, &s->windows);
238
0
  }
239
240
0
  return (wl);
241
0
}
242
243
struct winlink *
244
winlink_previous_by_number(struct winlink *wl, struct session *s, int n)
245
0
{
246
0
  for (; n > 0; n--) {
247
0
    if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL)
248
0
      wl = RB_MAX(winlinks, &s->windows);
249
0
  }
250
251
0
  return (wl);
252
0
}
253
254
void
255
winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
256
0
{
257
0
  if (wl == NULL)
258
0
    return;
259
260
0
  winlink_stack_remove(stack, wl);
261
0
  TAILQ_INSERT_HEAD(stack, wl, sentry);
262
0
  wl->flags |= WINLINK_VISITED;
263
0
}
264
265
void
266
winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
267
0
{
268
0
  if (wl != NULL && (wl->flags & WINLINK_VISITED)) {
269
0
    TAILQ_REMOVE(stack, wl, sentry);
270
0
    wl->flags &= ~WINLINK_VISITED;
271
0
  }
272
0
}
273
274
struct window *
275
window_find_by_id_str(const char *s)
276
0
{
277
0
  const char  *errstr;
278
0
  u_int    id;
279
280
0
  if (*s != '@')
281
0
    return (NULL);
282
283
0
  id = strtonum(s + 1, 0, UINT_MAX, &errstr);
284
0
  if (errstr != NULL)
285
0
    return (NULL);
286
0
  return (window_find_by_id(id));
287
0
}
288
289
struct window *
290
window_find_by_id(u_int id)
291
0
{
292
0
  struct window w;
293
294
0
  w.id = id;
295
0
  return (RB_FIND(windows, &windows, &w));
296
0
}
297
298
void
299
window_update_activity(struct window *w)
300
22.2k
{
301
22.2k
  gettimeofday(&w->activity_time, NULL);
302
22.2k
  alerts_queue(w, WINDOW_ACTIVITY);
303
22.2k
}
304
305
struct window *
306
window_create(u_int sx, u_int sy, u_int xpixel, u_int ypixel)
307
11.1k
{
308
11.1k
  struct window *w;
309
310
11.1k
  if (xpixel == 0)
311
11.1k
    xpixel = DEFAULT_XPIXEL;
312
11.1k
  if (ypixel == 0)
313
11.1k
    ypixel = DEFAULT_YPIXEL;
314
315
11.1k
  w = xcalloc(1, sizeof *w);
316
11.1k
  w->name = xstrdup("");
317
11.1k
  w->flags = 0;
318
319
11.1k
  TAILQ_INIT(&w->panes);
320
11.1k
  TAILQ_INIT(&w->z_index);
321
11.1k
  TAILQ_INIT(&w->last_panes);
322
11.1k
  w->active = NULL;
323
324
11.1k
  w->lastlayout = -1;
325
11.1k
  w->layout_root = NULL;
326
327
11.1k
  w->sx = sx;
328
11.1k
  w->sy = sy;
329
11.1k
  w->manual_sx = sx;
330
11.1k
  w->manual_sy = sy;
331
11.1k
  w->xpixel = xpixel;
332
11.1k
  w->ypixel = ypixel;
333
334
11.1k
  w->options = options_create(global_w_options);
335
11.1k
  w->sb = options_get_number(w->options, "pane-scrollbars");
336
11.1k
  w->sb_pos = options_get_number(w->options, "pane-scrollbars-position");
337
338
11.1k
  w->references = 0;
339
11.1k
  TAILQ_INIT(&w->winlinks);
340
341
11.1k
  w->id = next_window_id++;
342
11.1k
  RB_INSERT(windows, &windows, w);
343
344
11.1k
  window_set_fill_character(w);
345
346
11.1k
  if (gettimeofday(&w->creation_time, NULL) != 0)
347
0
    fatal("gettimeofday failed");
348
11.1k
  window_update_activity(w);
349
350
11.1k
  log_debug("%s: @%u create %ux%u (%ux%u)", __func__, w->id, sx, sy,
351
11.1k
      w->xpixel, w->ypixel);
352
11.1k
  return (w);
353
11.1k
}
354
355
static void
356
window_destroy(struct window *w)
357
11.1k
{
358
11.1k
  log_debug("window @%u destroyed (%d references)", w->id, w->references);
359
360
11.1k
  window_unzoom(w, 0);
361
11.1k
  RB_REMOVE(windows, &windows, w);
362
363
11.1k
  layout_free_cell(w->layout_root, 0);
364
11.1k
  layout_free_cell(w->saved_layout_root, 0);
365
11.1k
  free(w->old_layout);
366
367
11.1k
  window_destroy_panes(w);
368
369
11.1k
  if (event_initialized(&w->name_event))
370
11.1k
    evtimer_del(&w->name_event);
371
372
11.1k
  if (event_initialized(&w->alerts_timer))
373
11.1k
    evtimer_del(&w->alerts_timer);
374
11.1k
  if (event_initialized(&w->offset_timer))
375
3.38k
    event_del(&w->offset_timer);
376
377
11.1k
  options_free(w->options);
378
11.1k
  free(w->fill_character);
379
380
11.1k
  free(w->name);
381
11.1k
  free(w);
382
11.1k
}
383
384
int
385
window_pane_destroy_ready(struct window_pane *wp)
386
0
{
387
0
  int n;
388
389
0
  if (wp->pipe_fd != -1 && EVBUFFER_LENGTH(wp->pipe_event->output) != 0)
390
0
    return (0);
391
0
  if (ioctl(wp->fd, FIONREAD, &n) != -1 && n > 0)
392
0
    return (0);
393
394
0
  if (~wp->flags & PANE_EXITED)
395
0
    return (0);
396
397
  /*
398
   * If a command queue item is blocked on this pane, wait for the
399
   * child's exit status before destroying it.
400
   */
401
0
  if (wp->wait_item != NULL && (~wp->flags & PANE_STATUSREADY))
402
0
    return (0);
403
0
  if (wp->editor != NULL && (~wp->flags & PANE_STATUSREADY))
404
0
    return (0);
405
0
  return (1);
406
0
}
407
408
void
409
window_add_ref(struct window *w, const char *from)
410
12.5k
{
411
12.5k
  w->references++;
412
12.5k
  log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references);
413
12.5k
}
414
415
void
416
window_remove_ref(struct window *w, const char *from)
417
12.5k
{
418
12.5k
  w->references--;
419
12.5k
  log_debug("%s: @%u %s, now %d", __func__, w->id, from, w->references);
420
421
12.5k
  if (w->references == 0)
422
11.1k
    window_destroy(w);
423
12.5k
}
424
425
void
426
window_pane_add_ref(struct window_pane *wp, const char *from)
427
0
{
428
0
  wp->references++;
429
0
  log_debug("%s: %%%u %s, now %d", __func__, wp->id, from,
430
0
      wp->references);
431
0
}
432
433
void
434
window_pane_remove_ref(struct window_pane *wp, const char *from)
435
11.1k
{
436
11.1k
  wp->references--;
437
11.1k
  log_debug("%s: %%%u %s, now %d", __func__, wp->id, from,
438
11.1k
      wp->references);
439
440
11.1k
  if (wp->references == 0)
441
11.1k
    window_pane_free(wp);
442
11.1k
}
443
444
void
445
window_set_name(struct window *w, const char *new_name, int untrusted)
446
1.42k
{
447
1.42k
  char  *name;
448
449
1.42k
  name = clean_name(new_name, untrusted);
450
1.42k
  if (name != NULL) {
451
1.42k
    free(w->name);
452
1.42k
    w->name = name;
453
1.42k
    notify_window("window-renamed", w);
454
1.42k
  }
455
1.42k
}
456
457
void
458
window_resize(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
459
0
{
460
0
  if (xpixel == 0)
461
0
    xpixel = DEFAULT_XPIXEL;
462
0
  if (ypixel == 0)
463
0
    ypixel = DEFAULT_YPIXEL;
464
465
0
  log_debug("%s: @%u resize %ux%u (%ux%u)", __func__, w->id, sx, sy,
466
0
      xpixel == -1 ? w->xpixel : (u_int)xpixel,
467
0
      ypixel == -1 ? w->ypixel : (u_int)ypixel);
468
0
  w->sx = sx;
469
0
  w->sy = sy;
470
0
  if (xpixel != -1)
471
0
    w->xpixel = xpixel;
472
0
  if (ypixel != -1)
473
0
    w->ypixel = ypixel;
474
0
}
475
476
void
477
window_pane_send_resize(struct window_pane *wp, u_int sx, u_int sy)
478
0
{
479
0
  struct window *w = wp->window;
480
0
  struct winsize   ws;
481
482
0
  if (wp->fd == -1)
483
0
    return;
484
485
0
  log_debug("%s: %%%u resize to %u,%u", __func__, wp->id, sx, sy);
486
487
0
  memset(&ws, 0, sizeof ws);
488
0
  ws.ws_col = sx;
489
0
  ws.ws_row = sy;
490
0
  ws.ws_xpixel = w->xpixel * ws.ws_col;
491
0
  ws.ws_ypixel = w->ypixel * ws.ws_row;
492
0
  if (ioctl(wp->fd, TIOCSWINSZ, &ws) == -1)
493
#ifdef __sun
494
    /*
495
     * Some versions of Solaris apparently can return an error when
496
     * resizing; don't know why this happens, can't reproduce on
497
     * other platforms and ignoring it doesn't seem to cause any
498
     * issues.
499
     */
500
    if (errno != EINVAL && errno != ENXIO)
501
#endif
502
0
    fatal("ioctl failed");
503
0
}
504
505
int
506
window_has_floating_panes(struct window *w)
507
0
{
508
0
  struct window_pane  *wp;
509
510
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
511
0
    if (window_pane_is_floating(wp))
512
0
      return (1);
513
0
  }
514
0
  return (0);
515
0
}
516
517
int
518
window_has_pane(struct window *w, struct window_pane *wp)
519
0
{
520
0
  struct window_pane  *wp1;
521
522
0
  TAILQ_FOREACH(wp1, &w->panes, entry) {
523
0
    if (wp1 == wp)
524
0
      return (1);
525
0
  }
526
0
  return (0);
527
0
}
528
529
void
530
window_update_focus(struct window *w)
531
0
{
532
0
  if (w != NULL) {
533
0
    log_debug("%s: @%u", __func__, w->id);
534
0
    window_pane_update_focus(w->active);
535
0
  }
536
0
}
537
538
void
539
window_pane_update_focus(struct window_pane *wp)
540
0
{
541
0
  struct client *c;
542
0
  int    focused = 0;
543
544
0
  if (wp != NULL && (~wp->flags & PANE_EXITED)) {
545
0
    if (wp != wp->window->active)
546
0
      focused = 0;
547
0
    else {
548
0
      TAILQ_FOREACH(c, &clients, entry) {
549
0
        if (c->session != NULL &&
550
0
            c->session->attached != 0 &&
551
0
            (c->flags & CLIENT_FOCUSED) &&
552
0
            c->session->curw->window == wp->window &&
553
0
            c->overlay_draw == NULL) {
554
0
          focused = 1;
555
0
          break;
556
0
        }
557
0
      }
558
0
    }
559
0
    if (!focused && (wp->flags & PANE_FOCUSED)) {
560
0
      log_debug("%s: %%%u focus out", __func__, wp->id);
561
0
      if (wp->base.mode & MODE_FOCUSON)
562
0
        bufferevent_write(wp->event, "\033[O", 3);
563
0
      notify_pane("pane-focus-out", wp);
564
0
      wp->flags &= ~PANE_FOCUSED;
565
0
    } else if (focused && (~wp->flags & PANE_FOCUSED)) {
566
0
      log_debug("%s: %%%u focus in", __func__, wp->id);
567
0
      if (wp->base.mode & MODE_FOCUSON)
568
0
        bufferevent_write(wp->event, "\033[I", 3);
569
0
      notify_pane("pane-focus-in", wp);
570
0
      wp->flags |= PANE_FOCUSED;
571
0
    } else
572
0
      log_debug("%s: %%%u focus unchanged", __func__, wp->id);
573
0
  }
574
0
}
575
576
int
577
window_set_active_pane(struct window *w, struct window_pane *wp, int notify)
578
0
{
579
0
  struct window_pane *lastwp;
580
581
0
  log_debug("%s: pane %%%u", __func__, wp->id);
582
583
0
  if (wp == w->active)
584
0
    return (0);
585
0
  if (w->flags & WINDOW_ZOOMED)
586
0
    window_unzoom(w, 1);
587
0
  lastwp = w->active;
588
589
0
  window_pane_stack_remove(&w->last_panes, wp);
590
0
  window_pane_stack_push(&w->last_panes, lastwp);
591
592
0
  w->active = wp;
593
0
  w->active->active_point = next_active_point++;
594
0
  w->active->flags |= PANE_CHANGED;
595
596
0
  if (options_get_number(global_options, "focus-events")) {
597
0
    window_pane_update_focus(lastwp);
598
0
    window_pane_update_focus(w->active);
599
0
  }
600
601
0
  tty_update_window_offset(w);
602
0
  server_redraw_window(w);
603
604
0
  if (notify)
605
0
    notify_window("window-pane-changed", w);
606
0
  return (1);
607
0
}
608
609
static int
610
window_pane_get_palette(struct window_pane *wp, int c)
611
0
{
612
0
  if (wp == NULL)
613
0
    return (-1);
614
0
  return (colour_palette_get(&wp->palette, c));
615
0
}
616
617
void
618
window_redraw_active_switch(struct window *w, struct window_pane *wp)
619
0
{
620
0
  struct grid_cell  *gc1, *gc2;
621
0
  int      c1, c2;
622
623
0
  if (wp == w->active)
624
0
    return;
625
626
0
  for (;;) {
627
    /*
628
     * If the active and inactive styles or palettes are different,
629
     * need to redraw the panes.
630
     */
631
0
    gc1 = &wp->cached_gc;
632
0
    gc2 = &wp->cached_active_gc;
633
0
    if (!grid_cells_look_equal(gc1, gc2))
634
0
      wp->flags |= PANE_REDRAW;
635
0
    else if (wp->cached_dim != wp->cached_active_dim)
636
0
      wp->flags |= PANE_REDRAW;
637
0
    else {
638
0
      c1 = window_pane_get_palette(wp, gc1->fg);
639
0
      c2 = window_pane_get_palette(wp, gc2->fg);
640
0
      if (c1 != c2)
641
0
        wp->flags |= PANE_REDRAW;
642
0
      else {
643
0
        c1 = window_pane_get_palette(wp, gc1->bg);
644
0
        c2 = window_pane_get_palette(wp, gc2->bg);
645
0
        if (c1 != c2)
646
0
          wp->flags |= PANE_REDRAW;
647
0
      }
648
0
    }
649
0
    if (wp == w->active)
650
0
      break;
651
652
    /* If the pane is floating, move to the front. */
653
0
    if (window_pane_is_floating(wp)) {
654
0
      TAILQ_REMOVE(&w->z_index, wp, zentry);
655
0
      TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
656
0
      wp->flags |= PANE_REDRAW;
657
0
      redraw_invalidate_scene(w);
658
0
    }
659
660
0
    wp = w->active;
661
0
    if (wp == NULL)
662
0
      break;
663
0
  }
664
0
}
665
666
struct window_pane *
667
window_get_active_at(struct window *w, u_int x, u_int y)
668
0
{
669
0
  struct window_pane  *wp;
670
0
  int      pane_status, xoff, yoff;
671
0
  u_int      sx, sy;
672
673
0
  pane_status = window_get_pane_status(w);
674
675
0
  if (pane_status == PANE_STATUS_TOP) {
676
    /*
677
     * Prefer a pane's top border status line over the pane above's
678
     * bottom border.
679
     */
680
0
    TAILQ_FOREACH(wp, &w->z_index, zentry) {
681
0
      if (!window_pane_is_visible(wp) ||
682
0
          window_pane_is_floating(wp))
683
0
        continue;
684
685
0
      window_pane_full_size_offset(wp, &xoff, &yoff, &sx,
686
0
          &sy);
687
0
      if ((int)x < xoff || x > xoff + sx)
688
0
        continue;
689
0
      if ((int)y == yoff - 1)
690
0
        return (wp);
691
0
    }
692
0
  }
693
694
0
  TAILQ_FOREACH(wp, &w->z_index, zentry) {
695
0
    if (!window_pane_is_visible(wp))
696
0
      continue;
697
0
    window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
698
0
    if (!window_pane_is_floating(wp)) {
699
      /*
700
       * Tiled - to and including the right border, excluding
701
       * the bottom border.
702
       */
703
0
      if ((int)x < xoff || x > xoff + sx)
704
0
        continue;
705
0
      if (pane_status == PANE_STATUS_TOP) {
706
0
        if ((int)y < yoff - 1 || y > yoff + sy)
707
0
          continue;
708
0
      } else {
709
0
        if ((int)y < yoff || y > yoff + sy)
710
0
          continue;
711
0
      }
712
0
    } else {
713
0
      if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE) {
714
0
        if ((int)x < xoff || (int)x >= xoff + (int)sx)
715
0
          continue;
716
0
        if ((int)y < yoff || (int)y >= yoff + (int)sy)
717
0
          continue;
718
0
      } else {
719
        /* Floating - include all borders. */
720
0
        if ((int)x < xoff - 1 || x > xoff + sx)
721
0
          continue;
722
0
        if ((int)y < yoff - 1 || y > yoff + sy)
723
0
          continue;
724
0
      }
725
0
    }
726
0
    return (wp);
727
0
  }
728
0
  return (NULL);
729
0
}
730
731
struct window_pane *
732
window_find_string(struct window *w, const char *s)
733
0
{
734
0
  u_int x, y, top = 0, bottom = w->sy - 1;
735
0
  int status;
736
737
0
  x = w->sx / 2;
738
0
  y = w->sy / 2;
739
740
0
  status = window_get_pane_status(w);
741
0
  if (status == PANE_STATUS_TOP)
742
0
    top++;
743
0
  else if (status == PANE_STATUS_BOTTOM)
744
0
    bottom--;
745
746
0
  if (strcasecmp(s, "top") == 0)
747
0
    y = top;
748
0
  else if (strcasecmp(s, "bottom") == 0)
749
0
    y = bottom;
750
0
  else if (strcasecmp(s, "left") == 0)
751
0
    x = 0;
752
0
  else if (strcasecmp(s, "right") == 0)
753
0
    x = w->sx - 1;
754
0
  else if (strcasecmp(s, "top-left") == 0) {
755
0
    x = 0;
756
0
    y = top;
757
0
  } else if (strcasecmp(s, "top-right") == 0) {
758
0
    x = w->sx - 1;
759
0
    y = top;
760
0
  } else if (strcasecmp(s, "bottom-left") == 0) {
761
0
    x = 0;
762
0
    y = bottom;
763
0
  } else if (strcasecmp(s, "bottom-right") == 0) {
764
0
    x = w->sx - 1;
765
0
    y = bottom;
766
0
  } else
767
0
    return (NULL);
768
769
0
  return (window_get_active_at(w, x, y));
770
0
}
771
772
int
773
window_zoom(struct window_pane *wp)
774
0
{
775
0
  struct window   *w = wp->window;
776
0
  struct window_pane  *wp1;
777
778
0
  if (w->flags & WINDOW_ZOOMED)
779
0
    return (-1);
780
0
  if (window_count_panes(w, 1) == 1)
781
0
    return (-1);
782
783
0
  if (w->active != wp)
784
0
    window_set_active_pane(w, wp, 1);
785
0
  wp->flags |= PANE_ZOOMED;
786
787
0
  TAILQ_FOREACH(wp1, &w->panes, entry) {
788
0
    wp1->saved_layout_cell = wp1->layout_cell;
789
0
    wp1->layout_cell = NULL;
790
0
  }
791
792
0
  w->saved_layout_root = w->layout_root;
793
0
  layout_init(w, wp);
794
0
  w->flags |= WINDOW_ZOOMED;
795
0
  notify_window("window-layout-changed", w);
796
797
0
  redraw_invalidate_scene(w);
798
0
  return (0);
799
0
}
800
801
int
802
window_unzoom(struct window *w, int notify)
803
11.1k
{
804
11.1k
  struct window_pane  *wp;
805
806
11.1k
  if (!(w->flags & WINDOW_ZOOMED))
807
11.1k
    return (-1);
808
809
0
  w->flags &= ~WINDOW_ZOOMED;
810
0
  layout_free(w, 0);
811
0
  w->layout_root = w->saved_layout_root;
812
0
  w->saved_layout_root = NULL;
813
814
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
815
0
    wp->layout_cell = wp->saved_layout_cell;
816
0
    wp->saved_layout_cell = NULL;
817
0
    wp->flags &= ~PANE_ZOOMED;
818
0
  }
819
0
  layout_fix_panes(w, NULL);
820
821
0
  if (notify)
822
0
    notify_window("window-layout-changed", w);
823
824
0
  redraw_invalidate_scene(w);
825
0
  return (0);
826
11.1k
}
827
828
int
829
window_push_zoom(struct window *w, int always, int flag)
830
0
{
831
0
  log_debug("%s: @%u %d", __func__, w->id,
832
0
      flag && (w->flags & WINDOW_ZOOMED));
833
0
  if (flag && (always || (w->flags & WINDOW_ZOOMED)))
834
0
    w->flags |= WINDOW_WASZOOMED;
835
0
  else
836
0
    w->flags &= ~WINDOW_WASZOOMED;
837
0
  return (window_unzoom(w, 1) == 0);
838
0
}
839
840
int
841
window_pop_zoom(struct window *w)
842
0
{
843
0
  log_debug("%s: @%u %d", __func__, w->id,
844
0
      !!(w->flags & WINDOW_WASZOOMED));
845
0
  if (w->flags & WINDOW_WASZOOMED)
846
0
    return (window_zoom(w->active) == 0);
847
0
  return (0);
848
0
}
849
850
struct window_pane *
851
window_add_pane(struct window *w, struct window_pane *other, u_int hlimit,
852
    int flags)
853
11.1k
{
854
11.1k
  struct window_pane  *wp;
855
856
11.1k
  if (other == NULL)
857
11.1k
    other = w->active;
858
859
11.1k
  wp = window_pane_create(w, w->sx, w->sy, hlimit);
860
11.1k
  if (TAILQ_EMPTY(&w->panes)) {
861
11.1k
    log_debug("%s: @%u at start", __func__, w->id);
862
11.1k
    TAILQ_INSERT_HEAD(&w->panes, wp, entry);
863
11.1k
  } else if (flags & SPAWN_BEFORE) {
864
0
    log_debug("%s: @%u before %%%u", __func__, w->id, wp->id);
865
0
    if (flags & SPAWN_FULLSIZE)
866
0
      TAILQ_INSERT_HEAD(&w->panes, wp, entry);
867
0
    else
868
0
      TAILQ_INSERT_BEFORE(other, wp, entry);
869
0
  } else {
870
0
    log_debug("%s: @%u after %%%u", __func__, w->id, wp->id);
871
0
    if (flags & (SPAWN_FULLSIZE|SPAWN_FLOATING))
872
0
      TAILQ_INSERT_TAIL(&w->panes, wp, entry);
873
0
    else
874
0
      TAILQ_INSERT_AFTER(&w->panes, other, wp, entry);
875
0
  }
876
11.1k
  if (~flags & SPAWN_FLOATING)
877
11.1k
    TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
878
0
  else {
879
0
    TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
880
0
  }
881
11.1k
  redraw_invalidate_scene(w);
882
11.1k
  return (wp);
883
11.1k
}
884
885
void
886
window_lost_pane(struct window *w, struct window_pane *wp)
887
0
{
888
0
  log_debug("%s: @%u pane %%%u", __func__, w->id, wp->id);
889
890
0
  if (wp == marked_pane.wp)
891
0
    server_clear_marked();
892
893
0
  window_pane_stack_remove(&w->last_panes, wp);
894
0
  if (wp == w->active) {
895
0
    w->active = TAILQ_FIRST(&w->last_panes);
896
0
    if (w->active == NULL) {
897
0
      w->active = TAILQ_PREV(wp, window_panes, entry);
898
0
      if (w->active == NULL)
899
0
        w->active = TAILQ_NEXT(wp, entry);
900
0
    }
901
0
    if (w->active != NULL) {
902
0
      window_pane_stack_remove(&w->last_panes, w->active);
903
0
      w->active->flags |= PANE_CHANGED;
904
0
      notify_window("window-pane-changed", w);
905
0
      window_update_focus(w);
906
0
    }
907
0
  }
908
0
  redraw_invalidate_scene(w);
909
0
}
910
911
void
912
window_remove_pane(struct window *w, struct window_pane *wp)
913
0
{
914
0
  window_lost_pane(w, wp);
915
0
  TAILQ_REMOVE(&w->panes, wp, entry);
916
0
  TAILQ_REMOVE(&w->z_index, wp, zentry);
917
0
  redraw_invalidate_scene(w);
918
0
  window_pane_destroy(wp);
919
0
}
920
921
struct window_pane *
922
window_pane_at_index(struct window *w, u_int idx)
923
0
{
924
0
  struct window_pane  *wp;
925
0
  u_int      n;
926
927
0
  n = options_get_number(w->options, "pane-base-index");
928
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
929
0
    if (n == idx)
930
0
      return (wp);
931
0
    n++;
932
0
  }
933
0
  return (NULL);
934
0
}
935
936
struct window_pane *
937
window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n)
938
0
{
939
0
  for (; n > 0; n--) {
940
0
    if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
941
0
      wp = TAILQ_FIRST(&w->panes);
942
0
  }
943
944
0
  return (wp);
945
0
}
946
947
struct window_pane *
948
window_pane_previous_by_number(struct window *w, struct window_pane *wp,
949
    u_int n)
950
0
{
951
0
  for (; n > 0; n--) {
952
0
    if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL)
953
0
      wp = TAILQ_LAST(&w->panes, window_panes);
954
0
  }
955
956
0
  return (wp);
957
0
}
958
959
int
960
window_pane_index(struct window_pane *wp, u_int *i)
961
0
{
962
0
  struct window   *w = wp->window;
963
0
  struct window_pane  *wq;
964
965
0
  *i = options_get_number(w->options, "pane-base-index");
966
0
  TAILQ_FOREACH(wq, &w->panes, entry) {
967
0
    if (wp == wq) {
968
0
      return (0);
969
0
    }
970
0
    (*i)++;
971
0
  }
972
973
0
  return (-1);
974
0
}
975
976
int
977
window_pane_zindex(struct window_pane *wp, u_int *i)
978
0
{
979
0
  struct window   *w = wp->window;
980
0
  struct window_pane  *wq;
981
982
0
  *i = 0;
983
0
  TAILQ_FOREACH(wq, &w->z_index, zentry) {
984
0
    if (wq == wp) {
985
0
      if (!window_pane_is_floating(wp))
986
0
        (*i)++;
987
0
      return (0);
988
0
    }
989
0
    if (window_pane_is_floating(wq))
990
0
      (*i)++;
991
0
  }
992
993
0
  return (-1);
994
0
}
995
996
u_int
997
window_count_panes(struct window *w, int with_floating)
998
0
{
999
0
  struct window_pane  *wp;
1000
0
  u_int      n = 0;
1001
1002
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
1003
0
    if (with_floating || !window_pane_is_floating(wp))
1004
0
      n++;
1005
0
  }
1006
0
  return (n);
1007
0
}
1008
1009
void
1010
window_destroy_panes(struct window *w)
1011
11.1k
{
1012
11.1k
  struct window_pane  *wp;
1013
1014
11.1k
  while (!TAILQ_EMPTY(&w->last_panes)) {
1015
0
    wp = TAILQ_FIRST(&w->last_panes);
1016
0
    window_pane_stack_remove(&w->last_panes, wp);
1017
0
  }
1018
1019
22.2k
  while (!TAILQ_EMPTY(&w->panes)) {
1020
11.1k
    wp = TAILQ_FIRST(&w->panes);
1021
11.1k
    TAILQ_REMOVE(&w->panes, wp, entry);
1022
11.1k
    TAILQ_REMOVE(&w->z_index, wp, zentry);
1023
11.1k
    window_pane_destroy(wp);
1024
11.1k
  }
1025
11.1k
}
1026
1027
const char *
1028
window_printable_flags(struct winlink *wl, int escape)
1029
0
{
1030
0
  struct session  *s = wl->session;
1031
0
  static char  flags[32];
1032
0
  u_int    pos = 0;
1033
1034
0
  if (wl->flags & WINLINK_ACTIVITY) {
1035
0
    flags[pos++] = '#';
1036
0
    if (escape)
1037
0
      flags[pos++] = '#';
1038
0
  }
1039
0
  if (wl->flags & WINLINK_BELL)
1040
0
    flags[pos++] = '!';
1041
0
  if (wl->flags & WINLINK_SILENCE)
1042
0
    flags[pos++] = '~';
1043
0
  if (wl == s->curw)
1044
0
    flags[pos++] = '*';
1045
0
  if (wl == TAILQ_FIRST(&s->lastw))
1046
0
    flags[pos++] = '-';
1047
0
  if (server_check_marked() && wl == marked_pane.wl)
1048
0
    flags[pos++] = 'M';
1049
0
  if (wl->window->flags & WINDOW_ZOOMED)
1050
0
    flags[pos++] = 'Z';
1051
0
  flags[pos] = '\0';
1052
0
  return (flags);
1053
0
}
1054
1055
const char *
1056
window_pane_printable_flags(struct window_pane *wp)
1057
0
{
1058
0
  struct window *w = wp->window;
1059
0
  static char  flags[32];
1060
0
  int    pos = 0;
1061
1062
0
  if (wp == w->active)
1063
0
    flags[pos++] = '*';
1064
0
  if (wp == TAILQ_FIRST(&w->last_panes))
1065
0
    flags[pos++] = '-';
1066
0
  if (wp->flags & PANE_ZOOMED)
1067
0
    flags[pos++] = 'Z';
1068
0
  if (window_pane_is_floating(wp))
1069
0
    flags[pos++] = 'F';
1070
0
  flags[pos] = '\0';
1071
0
  return (flags);
1072
0
}
1073
1074
struct window_pane *
1075
window_pane_find_by_id_str(const char *s)
1076
0
{
1077
0
  const char  *errstr;
1078
0
  u_int    id;
1079
1080
0
  if (*s != '%')
1081
0
    return (NULL);
1082
1083
0
  id = strtonum(s + 1, 0, UINT_MAX, &errstr);
1084
0
  if (errstr != NULL)
1085
0
    return (NULL);
1086
0
  return (window_pane_find_by_id(id));
1087
0
}
1088
1089
struct window_pane *
1090
window_pane_find_by_id(u_int id)
1091
0
{
1092
0
  struct window_pane  wp;
1093
1094
0
  wp.id = id;
1095
0
  return (RB_FIND(window_pane_tree, &all_window_panes, &wp));
1096
0
}
1097
1098
static struct window_pane *
1099
window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
1100
11.1k
{
1101
11.1k
  struct window_pane  *wp;
1102
11.1k
  char       host[HOST_NAME_MAX + 1];
1103
1104
11.1k
  wp = xcalloc(1, sizeof *wp);
1105
11.1k
  wp->references = 1;
1106
11.1k
  wp->window = w;
1107
11.1k
  wp->options = options_create(w->options);
1108
11.1k
  wp->flags = PANE_STYLECHANGED;
1109
1110
11.1k
  wp->id = next_window_pane_id++;
1111
11.1k
  RB_INSERT(window_pane_tree, &all_window_panes, wp);
1112
1113
11.1k
  wp->fd = -1;
1114
1115
11.1k
  TAILQ_INIT(&wp->modes);
1116
1117
11.1k
  TAILQ_INIT (&wp->resize_queue);
1118
1119
11.1k
  wp->sx = sx;
1120
11.1k
  wp->sy = sy;
1121
1122
11.1k
  wp->pipe_fd = -1;
1123
1124
11.1k
  wp->control_bg = -1;
1125
11.1k
  wp->control_fg = -1;
1126
1127
11.1k
  style_set_scrollbar_style_from_option(&wp->scrollbar_style,
1128
11.1k
      wp->options);
1129
1130
11.1k
  colour_palette_init(&wp->palette);
1131
11.1k
  colour_palette_from_option(&wp->palette, wp->options);
1132
1133
11.1k
  screen_init(&wp->base, sx, sy, hlimit);
1134
11.1k
  wp->screen = &wp->base;
1135
11.1k
  window_pane_default_cursor(wp);
1136
1137
11.1k
  screen_init(&wp->status_screen, 1, 1, 0);
1138
11.1k
  style_ranges_init(&wp->border_status_line.ranges);
1139
11.1k
  evtimer_set(&wp->sb_auto_timer, window_pane_scrollbar_timer, wp);
1140
1141
11.1k
  if (gethostname(host, sizeof host) == 0)
1142
11.1k
    screen_set_title(&wp->base, host, 0);
1143
1144
11.1k
  return (wp);
1145
11.1k
}
1146
1147
void
1148
window_pane_wait_finish(struct window_pane *wp)
1149
11.1k
{
1150
11.1k
  struct cmdq_item  *item = wp->wait_item;
1151
11.1k
  struct client   *c;
1152
11.1k
  int      retval = 0;
1153
1154
11.1k
  if (item == NULL)
1155
11.1k
    return;
1156
0
  wp->wait_item = NULL;
1157
1158
0
  if (wp->flags & PANE_STATUSREADY) {
1159
0
    if (WIFEXITED(wp->status))
1160
0
      retval = WEXITSTATUS(wp->status);
1161
0
    else if (WIFSIGNALED(wp->status))
1162
0
      retval = WTERMSIG(wp->status) + 128;
1163
0
  }
1164
1165
0
  c = cmdq_get_client(item);
1166
0
  if (c != NULL && c->session == NULL)
1167
0
    c->retval = retval;
1168
0
  cmdq_continue(item);
1169
0
}
1170
1171
static void
1172
window_pane_free_modes(struct window_pane *wp)
1173
11.1k
{
1174
11.1k
  struct window_mode_entry  *wme;
1175
1176
11.1k
  while (!TAILQ_EMPTY(&wp->modes)) {
1177
0
    wme = TAILQ_FIRST(&wp->modes);
1178
0
    TAILQ_REMOVE(&wp->modes, wme, entry);
1179
0
    wme->mode->free(wme);
1180
0
    free(wme);
1181
0
  }
1182
1183
11.1k
  wp->screen = &wp->base;
1184
11.1k
}
1185
1186
static void
1187
window_pane_scrollbar_timer(__unused int fd, __unused short events, void *arg)
1188
0
{
1189
0
  struct window_pane  *wp = arg;
1190
1191
0
  wp->sb_auto_hover = 0;
1192
0
  window_pane_scrollbar_hide(wp);
1193
0
}
1194
1195
static int
1196
window_pane_scrollbar_auto_hide(struct window_pane *wp)
1197
0
{
1198
0
  return (wp->window->sb == PANE_SCROLLBARS_MODAL ||
1199
0
      wp->window->sb == PANE_SCROLLBARS_AUTOHIDE);
1200
0
}
1201
1202
int
1203
window_pane_scrollbar_overlay_visible(struct window_pane *wp)
1204
3.56k
{
1205
3.56k
  return (window_pane_scrollbar_overlay(wp) &&
1206
0
      window_pane_scrollbar_visible(wp));
1207
3.56k
}
1208
1209
void
1210
window_pane_scrollbar_redraw(struct window_pane *wp)
1211
1.78k
{
1212
1.78k
  if (window_pane_scrollbar_overlay_visible(wp)) {
1213
0
    wp->flags |= PANE_REDRAW;
1214
0
    return;
1215
0
  }
1216
1.78k
  wp->flags |= PANE_REDRAWSCROLLBAR;
1217
1.78k
}
1218
1219
static void
1220
window_pane_scrollbar_redraw_visibility(struct window_pane *wp)
1221
0
{
1222
0
  redraw_invalidate_scene(wp->window);
1223
0
  wp->flags |= PANE_REDRAW;
1224
0
  server_redraw_window(wp->window);
1225
0
}
1226
1227
static void
1228
window_pane_destroy(struct window_pane *wp)
1229
11.1k
{
1230
11.1k
  window_pane_wait_finish(wp);
1231
11.1k
  spawn_editor_finish(wp);
1232
1233
11.1k
  window_pane_clear_prompt(wp);
1234
11.1k
  RB_REMOVE(window_pane_tree, &all_window_panes, wp);
1235
11.1k
  wp->flags |= PANE_DESTROYED;
1236
1237
11.1k
  window_pane_free_modes(wp);
1238
11.1k
  screen_write_clear_dirty(wp);
1239
1240
11.1k
  if (wp->fd != -1) {
1241
#ifdef HAVE_UTEMPTER
1242
    utempter_remove_record(wp->fd);
1243
    kill(getpid(), SIGCHLD);
1244
#endif
1245
11.1k
    bufferevent_free(wp->event);
1246
11.1k
    wp->event = NULL;
1247
11.1k
    close(wp->fd);
1248
11.1k
    wp->fd = -1;
1249
11.1k
  }
1250
11.1k
  if (wp->ictx != NULL) {
1251
11.1k
    input_free(wp->ictx);
1252
11.1k
    wp->ictx = NULL;
1253
11.1k
  }
1254
1255
11.1k
  if (wp->pipe_fd != -1) {
1256
0
    bufferevent_free(wp->pipe_event);
1257
0
    wp->pipe_event = NULL;
1258
0
    close(wp->pipe_fd);
1259
0
    wp->pipe_fd = -1;
1260
0
  }
1261
1262
11.1k
  if (event_initialized(&wp->resize_timer))
1263
0
    event_del(&wp->resize_timer);
1264
11.1k
  if (event_initialized(&wp->sync_timer))
1265
34
    event_del(&wp->sync_timer);
1266
11.1k
  if (event_initialized(&wp->sb_auto_timer))
1267
11.1k
    event_del(&wp->sb_auto_timer);
1268
11.1k
  window_pane_clear_resizes(wp, NULL);
1269
1270
11.1k
  window_pane_remove_ref(wp, __func__);
1271
11.1k
}
1272
1273
static void
1274
window_pane_free(struct window_pane *wp)
1275
11.1k
{
1276
11.1k
  log_debug("pane %%%u freed (%d references)", wp->id, wp->references);
1277
1278
11.1k
  free(wp->searchstr);
1279
1280
11.1k
  screen_free(&wp->status_screen);
1281
11.1k
  screen_free(&wp->base);
1282
1283
11.1k
  options_free(wp->options);
1284
11.1k
  free((void *)wp->cwd);
1285
11.1k
  free(wp->shell);
1286
11.1k
  cmd_free_argv(wp->argc, wp->argv);
1287
11.1k
  colour_palette_free(&wp->palette);
1288
11.1k
  style_ranges_free(&wp->border_status_line.ranges);
1289
11.1k
  free(wp);
1290
11.1k
}
1291
1292
static void
1293
window_pane_read_callback(__unused struct bufferevent *bufev, void *data)
1294
0
{
1295
0
  struct window_pane    *wp = data;
1296
0
  struct evbuffer     *evb = wp->event->input;
1297
0
  struct window_pane_offset *wpo = &wp->pipe_offset;
1298
0
  size_t         size = EVBUFFER_LENGTH(evb);
1299
0
  char        *new_data;
1300
0
  size_t         new_size;
1301
0
  struct client     *c;
1302
1303
0
  if (wp->pipe_fd != -1) {
1304
0
    new_data = window_pane_get_new_data(wp, wpo, &new_size);
1305
0
    if (new_size > 0) {
1306
0
      bufferevent_write(wp->pipe_event, new_data, new_size);
1307
0
      window_pane_update_used_data(wp, wpo, new_size);
1308
0
    }
1309
0
  }
1310
1311
0
  log_debug("%%%u has %zu bytes", wp->id, size);
1312
0
  TAILQ_FOREACH(c, &clients, entry) {
1313
0
    if (c->session != NULL && (c->flags & CLIENT_CONTROL))
1314
0
      control_write_output(c, wp);
1315
0
  }
1316
0
  input_parse_pane(wp);
1317
0
  bufferevent_disable(wp->event, EV_READ);
1318
0
}
1319
1320
static void
1321
window_pane_error_callback(__unused struct bufferevent *bufev,
1322
    __unused short what, void *data)
1323
0
{
1324
0
  struct window_pane *wp = data;
1325
1326
0
  log_debug("%%%u error", wp->id);
1327
0
  wp->flags |= PANE_EXITED;
1328
1329
0
  if (window_pane_destroy_ready(wp))
1330
0
    server_destroy_pane(wp, 1);
1331
0
}
1332
1333
void
1334
window_pane_set_event(struct window_pane *wp)
1335
0
{
1336
0
  setblocking(wp->fd, 0);
1337
1338
0
  wp->event = bufferevent_new(wp->fd, window_pane_read_callback,
1339
0
      NULL, window_pane_error_callback, wp);
1340
0
  if (wp->event == NULL)
1341
0
    fatalx("out of memory");
1342
0
  wp->ictx = input_init(wp, wp->event, &wp->palette, NULL);
1343
1344
0
  bufferevent_enable(wp->event, EV_READ|EV_WRITE);
1345
0
}
1346
1347
void
1348
window_pane_clear_resizes(struct window_pane *wp,
1349
    struct window_pane_resize *except)
1350
11.5k
{
1351
11.5k
  struct window_pane_resize *r, *r1;
1352
1353
11.5k
  TAILQ_FOREACH_SAFE(r, &wp->resize_queue, entry, r1) {
1354
0
    if (r == except)
1355
0
      continue;
1356
0
    TAILQ_REMOVE(&wp->resize_queue, r, entry);
1357
0
    free(r);
1358
0
  }
1359
11.5k
}
1360
1361
void
1362
window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
1363
0
{
1364
0
  struct window_mode_entry  *wme;
1365
0
  struct window_pane_resize *r;
1366
1367
0
  if (sx == wp->sx && sy == wp->sy)
1368
0
    return;
1369
1370
0
  screen_write_stop_sync(wp);
1371
1372
0
  r = xmalloc(sizeof *r);
1373
0
  r->sx = sx;
1374
0
  r->sy = sy;
1375
0
  r->osx = wp->sx;
1376
0
  r->osy = wp->sy;
1377
0
  TAILQ_INSERT_TAIL (&wp->resize_queue, r, entry);
1378
1379
0
  wp->sx = sx;
1380
0
  wp->sy = sy;
1381
1382
0
  log_debug("%s: %%%u resize %ux%u", __func__, wp->id, sx, sy);
1383
0
  screen_resize(&wp->base, sx, sy, wp->base.saved_grid == NULL);
1384
1385
0
  wme = TAILQ_FIRST(&wp->modes);
1386
0
  if (wme != NULL && wme->mode->resize != NULL)
1387
0
    wme->mode->resize(wme, sx, sy);
1388
0
}
1389
1390
int
1391
window_pane_set_mode(struct window_pane *wp, struct window_pane *swp,
1392
    const struct window_mode *mode, struct cmd_find_state *fs,
1393
    struct args *args)
1394
0
{
1395
0
  struct window_mode_entry  *wme;
1396
0
  struct window     *w = wp->window;
1397
1398
0
  if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode)
1399
0
    return (1);
1400
1401
0
  TAILQ_FOREACH(wme, &wp->modes, entry) {
1402
0
    if (wme->mode == mode)
1403
0
      break;
1404
0
  }
1405
0
  if (wme != NULL) {
1406
0
    TAILQ_REMOVE(&wp->modes, wme, entry);
1407
0
    TAILQ_INSERT_HEAD(&wp->modes, wme, entry);
1408
0
  } else {
1409
0
    wme = xcalloc(1, sizeof *wme);
1410
0
    wme->wp = wp;
1411
0
    wme->swp = swp;
1412
0
    wme->mode = mode;
1413
0
    wme->prefix = 1;
1414
0
    TAILQ_INSERT_HEAD(&wp->modes, wme, entry);
1415
0
    wme->screen = wme->mode->init(wme, fs, args);
1416
0
  }
1417
0
  wme->kill = args != NULL ? args_has(args, 'k') : 0;
1418
0
  wp->screen = wme->screen;
1419
1420
0
  wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED);
1421
0
  layout_fix_panes(w, NULL);
1422
1423
0
  server_redraw_window_borders(wp->window);
1424
0
  server_status_window(wp->window);
1425
0
  notify_pane("pane-mode-changed", wp);
1426
1427
0
  return (0);
1428
0
}
1429
1430
void
1431
window_pane_reset_mode(struct window_pane *wp)
1432
0
{
1433
0
  struct window_mode_entry  *wme, *next;
1434
0
  struct window     *w = wp->window;
1435
0
  int        kill;
1436
1437
0
  if (TAILQ_EMPTY(&wp->modes))
1438
0
    return;
1439
1440
0
  wme = TAILQ_FIRST(&wp->modes);
1441
0
  kill = wme->kill;
1442
0
  TAILQ_REMOVE(&wp->modes, wme, entry);
1443
0
  wme->mode->free(wme);
1444
0
  free(wme);
1445
1446
0
  next = TAILQ_FIRST(&wp->modes);
1447
0
  if (next == NULL) {
1448
0
    wp->flags &= ~PANE_UNSEENCHANGES;
1449
0
    log_debug("%s: no next mode", __func__);
1450
0
    wp->screen = &wp->base;
1451
0
  } else {
1452
0
    log_debug("%s: next mode is %s", __func__, next->mode->name);
1453
0
    wp->screen = next->screen;
1454
0
    if (next->mode->resize != NULL)
1455
0
      next->mode->resize(next, wp->sx, wp->sy);
1456
0
  }
1457
1458
0
  wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR|PANE_CHANGED);
1459
0
  layout_fix_panes(w, NULL);
1460
1461
0
  server_redraw_window_borders(wp->window);
1462
0
  server_status_window(wp->window);
1463
0
  notify_pane("pane-mode-changed", wp);
1464
1465
0
  if (kill)
1466
0
    server_kill_pane(wp);
1467
0
}
1468
1469
/* Reset all modes. */
1470
void
1471
window_pane_reset_mode_all(struct window_pane *wp)
1472
0
{
1473
0
  while (!TAILQ_EMPTY(&wp->modes))
1474
0
    window_pane_reset_mode(wp);
1475
0
}
1476
1477
/* Prompt input callback. */
1478
static enum prompt_result
1479
window_pane_prompt_input_callback(void *data, const char *s,
1480
    enum prompt_key_result key)
1481
0
{
1482
0
  struct window_pane_prompt *wpp = data;
1483
1484
0
  if (wpp->inputcb != NULL)
1485
0
    return (wpp->inputcb(wpp->c, wpp->data, s, key));
1486
0
  return (PROMPT_CLOSE);
1487
0
}
1488
1489
/* Prompt free callback. */
1490
static void
1491
window_pane_prompt_free_callback(void *data)
1492
0
{
1493
0
  struct window_pane_prompt *wpp = data;
1494
0
  struct window_pane    *wp;
1495
1496
0
  wp = window_pane_find_by_id(wpp->wp_id);
1497
0
  if (wp != NULL && wp->prompt_data == wpp)
1498
0
    wp->prompt_data = NULL;
1499
0
  if (wpp->freecb != NULL)
1500
0
    wpp->freecb(wpp->data);
1501
0
  free(wpp);
1502
0
}
1503
1504
/* Open a prompt owned by a pane, drawn over the pane instead of the status. */
1505
void
1506
window_pane_set_prompt(struct window_pane *wp, struct client *c,
1507
    struct cmd_find_state *fs, const char *msg, const char *input,
1508
    status_prompt_input_cb inputcb, prompt_free_cb freecb, void *data,
1509
    int flags, enum prompt_type type)
1510
0
{
1511
0
  struct session      *s = NULL;
1512
0
  struct prompt_create_data  pd;
1513
0
  struct window_pane_prompt *wpp;
1514
1515
0
  if (c != NULL)
1516
0
    s = c->session;
1517
1518
0
  window_pane_clear_prompt(wp);
1519
1520
0
  wpp = xcalloc(1, sizeof *wpp);
1521
0
  wpp->wp_id = wp->id;
1522
0
  wpp->c = c;
1523
0
  wpp->inputcb = inputcb;
1524
0
  wpp->freecb = freecb;
1525
0
  wpp->data = data;
1526
1527
0
  memset(&pd, 0, sizeof pd);
1528
0
  prompt_set_options(&pd, s);
1529
0
  pd.fs = fs;
1530
0
  pd.prompt = msg;
1531
0
  pd.input = input;
1532
0
  pd.type = type;
1533
0
  pd.flags = flags;
1534
0
  pd.inputcb = window_pane_prompt_input_callback;
1535
0
  pd.freecb = window_pane_prompt_free_callback;
1536
0
  pd.data = wpp;
1537
1538
0
  wp->prompt = prompt_create(&pd);
1539
0
  wp->prompt_data = wpp;
1540
0
  wp->flags |= PANE_REDRAW;
1541
1542
0
  prompt_incremental_start(wp->prompt);
1543
0
}
1544
1545
/* Close a pane prompt. */
1546
void
1547
window_pane_clear_prompt(struct window_pane *wp)
1548
11.1k
{
1549
11.1k
  struct prompt *prompt = wp->prompt;
1550
1551
11.1k
  if (prompt == NULL)
1552
11.1k
    return;
1553
0
  wp->prompt = NULL;
1554
0
  prompt_free(prompt);
1555
0
  wp->flags |= PANE_REDRAW;
1556
0
}
1557
1558
/* Does this pane have an open prompt? */
1559
int
1560
window_pane_has_prompt(struct window_pane *wp)
1561
0
{
1562
0
  return (wp->prompt != NULL);
1563
0
}
1564
1565
/* Replace the message and input of an open pane prompt. */
1566
void
1567
window_pane_update_prompt(struct window_pane *wp, const char *msg,
1568
    const char *input)
1569
0
{
1570
0
  if (wp->prompt != NULL) {
1571
0
    prompt_update(wp->prompt, msg, input);
1572
0
    wp->flags |= PANE_REDRAW;
1573
0
  }
1574
0
}
1575
1576
/*
1577
 * Pass a key to a pane prompt. The client is set transiently for the duration
1578
 * of the key in case the prompt or pane is destroyed by the callback.
1579
 */
1580
enum prompt_key_result
1581
window_pane_prompt_key(struct window_pane *wp, struct client *c, key_code key,
1582
    struct mouse_event *m)
1583
0
{
1584
0
  struct prompt     *prompt = wp->prompt;
1585
0
  struct window_pane_prompt *wpp = wp->prompt_data;
1586
0
  enum prompt_key_result     result;
1587
0
  u_int        wp_id = wp->id, x, y, py;
1588
0
  int        redraw = 0;
1589
1590
0
  if (prompt == NULL)
1591
0
    return (PROMPT_KEY_NOT_HANDLED);
1592
1593
0
  if (wpp != NULL)
1594
0
    wpp->c = c;
1595
0
  if (KEYC_IS_MOUSE(key)) {
1596
0
    if (m == NULL ||
1597
0
        MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1 ||
1598
0
        MOUSE_DRAG(m->b) ||
1599
0
        MOUSE_RELEASE(m->b) ||
1600
0
        cmd_mouse_at(wp, m, &x, &y, 0) != 0)
1601
0
      result = PROMPT_KEY_NOT_HANDLED;
1602
0
    else {
1603
0
      if (c != NULL && status_at_line(c) == 0)
1604
0
        py = 0;
1605
0
      else
1606
0
        py = wp->sy - 1;
1607
0
      if (y == py) {
1608
0
        result = prompt_mouse(prompt, x, 0, wp->sx,
1609
0
            &redraw);
1610
0
      } else
1611
0
        result = PROMPT_KEY_NOT_HANDLED;
1612
0
    }
1613
0
  } else
1614
0
    result = prompt_key(prompt, key, &redraw);
1615
1616
0
  wp = window_pane_find_by_id(wp_id);
1617
0
  if (wp == NULL)
1618
0
    return (result);
1619
0
  if (wpp != NULL && wp->prompt_data == wpp)
1620
0
    wpp->c = NULL;
1621
1622
  /*
1623
   * Only an explicit close or the prompt marking itself closed ends it;
1624
   * cursor movement and editing keep it open.
1625
   */
1626
0
  if (wp->prompt == prompt &&
1627
0
      (result == PROMPT_KEY_CLOSE || prompt_closed(prompt)))
1628
0
    window_pane_clear_prompt(wp);
1629
1630
0
  if (redraw || wp->prompt != prompt)
1631
0
    wp->flags |= PANE_REDRAW;
1632
1633
0
  return (result);
1634
0
}
1635
1636
static void
1637
window_pane_copy_paste(struct window_pane *wp, char *buf, size_t len)
1638
0
{
1639
0
  struct window_pane  *loop;
1640
1641
0
  TAILQ_FOREACH(loop, &wp->window->panes, entry) {
1642
0
    if (loop != wp &&
1643
0
        TAILQ_EMPTY(&loop->modes) &&
1644
0
        loop->fd != -1 &&
1645
0
        (~loop->flags & PANE_INPUTOFF) &&
1646
0
        window_pane_is_visible(loop) &&
1647
0
        options_get_number(loop->options, "synchronize-panes")) {
1648
0
      log_debug("%s: %.*s", __func__, (int)len, buf);
1649
0
      bufferevent_write(loop->event, buf, len);
1650
0
    }
1651
0
  }
1652
0
}
1653
1654
static void
1655
window_pane_copy_key(struct window_pane *wp, key_code key)
1656
0
{
1657
0
  struct window_pane  *loop;
1658
1659
0
  TAILQ_FOREACH(loop, &wp->window->panes, entry) {
1660
0
    if (loop != wp &&
1661
0
        TAILQ_EMPTY(&loop->modes) &&
1662
0
        loop->fd != -1 &&
1663
0
        (~loop->flags & PANE_INPUTOFF) &&
1664
0
        window_pane_is_visible(loop) &&
1665
0
        options_get_number(loop->options, "synchronize-panes"))
1666
0
      input_key_pane(loop, key, NULL);
1667
0
  }
1668
0
}
1669
1670
void
1671
window_pane_paste(struct window_pane *wp, key_code key, char *buf, size_t len)
1672
0
{
1673
0
  if (!TAILQ_EMPTY(&wp->modes))
1674
0
    return;
1675
1676
0
  if (wp->fd == -1 || wp->flags & PANE_INPUTOFF)
1677
0
    return;
1678
1679
0
  if (KEYC_IS_PASTE(key) && (~wp->screen->mode & MODE_BRACKETPASTE))
1680
0
    return;
1681
1682
0
  log_debug("%s: %.*s", __func__, (int)len, buf);
1683
0
  bufferevent_write(wp->event, buf, len);
1684
1685
0
  if (options_get_number(wp->options, "synchronize-panes"))
1686
0
    window_pane_copy_paste(wp, buf, len);
1687
0
}
1688
1689
int
1690
window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
1691
    struct winlink *wl, key_code key, struct mouse_event *m)
1692
0
{
1693
0
  struct window_mode_entry  *wme;
1694
1695
0
  if (KEYC_IS_MOUSE(key) && m == NULL)
1696
0
    return (-1);
1697
1698
0
  wme = TAILQ_FIRST(&wp->modes);
1699
0
  if (wme != NULL) {
1700
    /*
1701
     * No mode uses mouse motion events, so drop them here rather
1702
     * than passing them on and causing a redraw on every movement.
1703
     */
1704
0
    if (KEYC_IS_TYPE(key, KEYC_TYPE_MOUSEMOVE))
1705
0
      return (0);
1706
0
    if (wme->mode->key != NULL && c != NULL) {
1707
0
      key &= ~KEYC_MASK_FLAGS;
1708
0
      wme->mode->key(wme, c, s, wl, key, m);
1709
0
    }
1710
0
    return (0);
1711
0
  }
1712
1713
0
  if (wp->fd == -1 || wp->flags & PANE_INPUTOFF)
1714
0
    return (0);
1715
1716
0
  if (input_key_pane(wp, key, m) != 0)
1717
0
    return (-1);
1718
1719
0
  if (KEYC_IS_MOUSE(key))
1720
0
    return (0);
1721
0
  if (options_get_number(wp->options, "synchronize-panes"))
1722
0
    window_pane_copy_key(wp, key);
1723
0
  return (0);
1724
0
}
1725
1726
int
1727
window_pane_is_visible(struct window_pane *wp)
1728
0
{
1729
0
  if (~wp->window->flags & WINDOW_ZOOMED)
1730
0
    return (1);
1731
0
  return (wp == wp->window->active);
1732
0
}
1733
1734
int
1735
window_pane_exited(struct window_pane *wp)
1736
0
{
1737
0
  return (wp->fd == -1 || (wp->flags & PANE_EXITED));
1738
0
}
1739
1740
u_int
1741
window_pane_search(struct window_pane *wp, const char *term, int regex,
1742
    int ignore)
1743
0
{
1744
0
  struct screen *s = &wp->base;
1745
0
  regex_t    r;
1746
0
  char    *new = NULL, *line;
1747
0
  u_int    i;
1748
0
  int    flags = 0, found;
1749
0
  size_t     n;
1750
1751
0
  if (!regex) {
1752
0
    if (ignore)
1753
0
      flags |= FNM_CASEFOLD;
1754
0
    xasprintf(&new, "*%s*", term);
1755
0
  } else {
1756
0
    if (ignore)
1757
0
      flags |= REG_ICASE;
1758
0
    if (regcomp(&r, term, flags|REG_EXTENDED) != 0)
1759
0
      return (0);
1760
0
  }
1761
1762
0
  for (i = 0; i < screen_size_y(s); i++) {
1763
0
    line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
1764
0
    for (n = strlen(line); n > 0; n--) {
1765
0
      if (!isspace((u_char)line[n - 1]))
1766
0
        break;
1767
0
      line[n - 1] = '\0';
1768
0
    }
1769
0
    log_debug("%s: %s", __func__, line);
1770
0
    if (!regex)
1771
0
      found = (fnmatch(new, line, flags) == 0);
1772
0
    else
1773
0
      found = (regexec(&r, line, 0, NULL, 0) == 0);
1774
0
    free(line);
1775
0
    if (found)
1776
0
      break;
1777
0
  }
1778
0
  if (!regex)
1779
0
    free(new);
1780
0
  else
1781
0
    regfree(&r);
1782
1783
0
  if (i == screen_size_y(s))
1784
0
    return (0);
1785
0
  return (i + 1);
1786
0
}
1787
1788
/* Get MRU pane from a list. */
1789
static struct window_pane *
1790
window_pane_choose_best(struct window_pane **list, u_int size)
1791
0
{
1792
0
  struct window_pane  *next, *best;
1793
0
  u_int      i;
1794
1795
0
  if (size == 0)
1796
0
    return (NULL);
1797
1798
0
  best = list[0];
1799
0
  for (i = 1; i < size; i++) {
1800
0
    next = list[i];
1801
0
    if (next->active_point > best->active_point)
1802
0
      best = next;
1803
0
  }
1804
0
  return (best);
1805
0
}
1806
1807
/*
1808
 * Get full size and offset of a window pane including the area of the
1809
 * scrollbars if they were visible but not including the border(s).
1810
 */
1811
static void
1812
window_pane_full_size_offset(struct window_pane *wp, int *xoff, int *yoff,
1813
    u_int *sx, u_int *sy)
1814
0
{
1815
0
  struct window   *w = wp->window;
1816
0
  u_int      sb_w;
1817
1818
0
  if (window_pane_scrollbar_reserve(wp))
1819
0
    sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
1820
0
  else
1821
0
    sb_w = 0;
1822
0
  if (w->sb_pos == PANE_SCROLLBARS_LEFT) {
1823
0
    *xoff = wp->xoff - sb_w;
1824
0
    *sx = wp->sx + sb_w;
1825
0
  } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
1826
0
    *xoff = wp->xoff;
1827
0
    *sx = wp->sx + sb_w;
1828
0
  }
1829
0
  *yoff = wp->yoff;
1830
0
  *sy = wp->sy;
1831
0
}
1832
1833
/*
1834
 * Find the pane directly above another. We build a list of those adjacent to
1835
 * top edge and then choose the best.
1836
 */
1837
struct window_pane *
1838
window_pane_find_up(struct window_pane *wp)
1839
0
{
1840
0
  struct window   *w;
1841
0
  struct window_pane  *next, *best, **list;
1842
0
  int      edge, left, right, end, status, found;
1843
0
  int      xoff, yoff;
1844
0
  u_int      size, sx, sy;
1845
1846
0
  if (wp == NULL)
1847
0
    return (NULL);
1848
0
  w = wp->window;
1849
0
  status = window_get_pane_status(w);
1850
1851
0
  list = NULL;
1852
0
  size = 0;
1853
1854
0
  window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
1855
1856
0
  edge = yoff;
1857
0
  if (status == PANE_STATUS_TOP) {
1858
0
    if (edge == 1)
1859
0
      edge = (int)w->sy + 1;
1860
0
  } else if (status == PANE_STATUS_BOTTOM) {
1861
0
    if (edge == 0)
1862
0
      edge = (int)w->sy;
1863
0
  } else {
1864
0
    if (edge == 0)
1865
0
      edge = (int)w->sy + 1;
1866
0
  }
1867
1868
0
  left = xoff;
1869
0
  right = xoff + (int)sx;
1870
1871
0
  TAILQ_FOREACH(next, &w->panes, entry) {
1872
0
    window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy);
1873
0
    if (next == wp)
1874
0
      continue;
1875
0
    if (yoff + (int)sy + 1 != edge)
1876
0
      continue;
1877
0
    end = xoff + (int)sx - 1;
1878
1879
0
    found = 0;
1880
0
    if (xoff < left && end > right)
1881
0
      found = 1;
1882
0
    else if (xoff >= left && xoff <= right)
1883
0
      found = 1;
1884
0
    else if (end >= left && end <= right)
1885
0
      found = 1;
1886
0
    if (!found)
1887
0
      continue;
1888
0
    list = xreallocarray(list, size + 1, sizeof *list);
1889
0
    list[size++] = next;
1890
0
  }
1891
1892
0
  best = window_pane_choose_best(list, size);
1893
0
  free(list);
1894
0
  return (best);
1895
0
}
1896
1897
/* Find the pane directly below another. */
1898
struct window_pane *
1899
window_pane_find_down(struct window_pane *wp)
1900
0
{
1901
0
  struct window   *w;
1902
0
  struct window_pane  *next, *best, **list;
1903
0
  int      edge, left, right, end, status, found;
1904
0
  int      xoff, yoff;
1905
0
  u_int      size, sx, sy;
1906
1907
0
  if (wp == NULL)
1908
0
    return (NULL);
1909
0
  w = wp->window;
1910
0
  status = window_get_pane_status(w);
1911
1912
0
  list = NULL;
1913
0
  size = 0;
1914
1915
0
  window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
1916
1917
0
  edge = yoff + (int)sy + 1;
1918
0
  if (status == PANE_STATUS_TOP) {
1919
0
    if (edge >= (int)w->sy)
1920
0
      edge = 1;
1921
0
  } else if (status == PANE_STATUS_BOTTOM) {
1922
0
    if (edge >= (int)w->sy - 1)
1923
0
      edge = 0;
1924
0
  } else {
1925
0
    if (edge >= (int)w->sy)
1926
0
      edge = 0;
1927
0
  }
1928
1929
0
  left = wp->xoff;
1930
0
  right = wp->xoff + (int)wp->sx;
1931
1932
0
  TAILQ_FOREACH(next, &w->panes, entry) {
1933
0
    window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy);
1934
0
    if (next == wp)
1935
0
      continue;
1936
0
    if (yoff != edge)
1937
0
      continue;
1938
0
    end = xoff + (int)sx - 1;
1939
1940
0
    found = 0;
1941
0
    if (xoff < left && end > right)
1942
0
      found = 1;
1943
0
    else if (xoff >= left && xoff <= right)
1944
0
      found = 1;
1945
0
    else if (end >= left && end <= right)
1946
0
      found = 1;
1947
0
    if (!found)
1948
0
      continue;
1949
0
    list = xreallocarray(list, size + 1, sizeof *list);
1950
0
    list[size++] = next;
1951
0
  }
1952
1953
0
  best = window_pane_choose_best(list, size);
1954
0
  free(list);
1955
0
  return (best);
1956
0
}
1957
1958
/* Find the pane directly to the left of another. */
1959
struct window_pane *
1960
window_pane_find_left(struct window_pane *wp)
1961
0
{
1962
0
  struct window   *w;
1963
0
  struct window_pane  *next, *best, **list;
1964
0
  int      edge, top, bottom, end, found;
1965
0
  int      xoff, yoff;
1966
0
  u_int      size, sx, sy;
1967
1968
0
  if (wp == NULL)
1969
0
    return (NULL);
1970
0
  w = wp->window;
1971
1972
0
  list = NULL;
1973
0
  size = 0;
1974
1975
0
  window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
1976
1977
0
  edge = xoff;
1978
0
  if (edge == 0)
1979
0
    edge = (int)w->sx + 1;
1980
1981
0
  top = yoff;
1982
0
  bottom = yoff + (int)sy;
1983
1984
0
  TAILQ_FOREACH(next, &w->panes, entry) {
1985
0
    window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy);
1986
0
    if (next == wp)
1987
0
      continue;
1988
0
    if (xoff + (int)sx + 1 != edge)
1989
0
      continue;
1990
0
    end = yoff + (int)sy - 1;
1991
1992
0
    found = 0;
1993
0
    if (yoff < top && end > bottom)
1994
0
      found = 1;
1995
0
    else if (yoff >= top && yoff <= bottom)
1996
0
      found = 1;
1997
0
    else if (end >= top && end <= bottom)
1998
0
      found = 1;
1999
0
    if (!found)
2000
0
      continue;
2001
0
    list = xreallocarray(list, size + 1, sizeof *list);
2002
0
    list[size++] = next;
2003
0
  }
2004
2005
0
  best = window_pane_choose_best(list, size);
2006
0
  free(list);
2007
0
  return (best);
2008
0
}
2009
2010
/* Find the pane directly to the right of another. */
2011
struct window_pane *
2012
window_pane_find_right(struct window_pane *wp)
2013
0
{
2014
0
  struct window   *w;
2015
0
  struct window_pane  *next, *best, **list;
2016
0
  int      edge, top, bottom, end, found;
2017
0
  int      xoff, yoff;
2018
0
  u_int      size, sx, sy;
2019
2020
0
  if (wp == NULL)
2021
0
    return (NULL);
2022
0
  w = wp->window;
2023
2024
0
  list = NULL;
2025
0
  size = 0;
2026
2027
0
  window_pane_full_size_offset(wp, &xoff, &yoff, &sx, &sy);
2028
2029
0
  edge = xoff + (int)sx + 1;
2030
0
  if (edge >= (int)w->sx)
2031
0
    edge = 0;
2032
2033
0
  top = wp->yoff;
2034
0
  bottom = wp->yoff + (int)wp->sy;
2035
2036
0
  TAILQ_FOREACH(next, &w->panes, entry) {
2037
0
    window_pane_full_size_offset(next, &xoff, &yoff, &sx, &sy);
2038
0
    if (next == wp)
2039
0
      continue;
2040
0
    if (xoff != edge)
2041
0
      continue;
2042
0
    end = yoff + (int)sy - 1;
2043
2044
0
    found = 0;
2045
0
    if (yoff < top && end > bottom)
2046
0
      found = 1;
2047
0
    else if (yoff >= top && yoff <= bottom)
2048
0
      found = 1;
2049
0
    else if (end >= top && end <= bottom)
2050
0
      found = 1;
2051
0
    if (!found)
2052
0
      continue;
2053
0
    list = xreallocarray(list, size + 1, sizeof *list);
2054
0
    list[size++] = next;
2055
0
  }
2056
2057
0
  best = window_pane_choose_best(list, size);
2058
0
  free(list);
2059
0
  return (best);
2060
0
}
2061
2062
/* Add window to stack. */
2063
void
2064
window_pane_stack_push(struct window_panes *stack, struct window_pane *wp)
2065
0
{
2066
0
  if (wp != NULL) {
2067
0
    window_pane_stack_remove(stack, wp);
2068
0
    TAILQ_INSERT_HEAD(stack, wp, sentry);
2069
0
    wp->flags |= PANE_VISITED;
2070
0
  }
2071
0
}
2072
2073
/* Remove window from stack. */
2074
void
2075
window_pane_stack_remove(struct window_panes *stack, struct window_pane *wp)
2076
0
{
2077
0
  if (wp != NULL && (wp->flags & PANE_VISITED)) {
2078
0
    TAILQ_REMOVE(stack, wp, sentry);
2079
0
    wp->flags &= ~PANE_VISITED;
2080
0
  }
2081
0
}
2082
2083
/* Clear alert flags for a winlink */
2084
void
2085
winlink_clear_flags(struct winlink *wl)
2086
0
{
2087
0
  struct winlink  *loop;
2088
2089
0
  wl->window->flags &= ~WINDOW_ALERTFLAGS;
2090
0
  TAILQ_FOREACH(loop, &wl->window->winlinks, wentry) {
2091
0
    if ((loop->flags & WINLINK_ALERTFLAGS) != 0) {
2092
0
      loop->flags &= ~WINLINK_ALERTFLAGS;
2093
0
      server_status_session(loop->session);
2094
0
    }
2095
0
  }
2096
0
}
2097
2098
/* Shuffle window indexes up. */
2099
int
2100
winlink_shuffle_up(struct session *s, struct winlink *wl, int before)
2101
0
{
2102
0
  int  idx, last;
2103
2104
0
  if (wl == NULL)
2105
0
    return (-1);
2106
0
  if (before)
2107
0
    idx = wl->idx;
2108
0
  else
2109
0
    idx = wl->idx + 1;
2110
2111
  /* Find the next free index. */
2112
0
  for (last = idx; last < INT_MAX; last++) {
2113
0
    if (winlink_find_by_index(&s->windows, last) == NULL)
2114
0
      break;
2115
0
  }
2116
0
  if (last == INT_MAX)
2117
0
    return (-1);
2118
2119
  /* Move everything from last - 1 to idx up a bit. */
2120
0
  for (; last > idx; last--) {
2121
0
    wl = winlink_find_by_index(&s->windows, last - 1);
2122
0
    RB_REMOVE(winlinks, &s->windows, wl);
2123
0
    wl->idx++;
2124
0
    RB_INSERT(winlinks, &s->windows, wl);
2125
0
  }
2126
2127
0
  return (idx);
2128
0
}
2129
2130
static void
2131
window_pane_input_callback(struct client *c, __unused const char *path,
2132
    int error, int closed, struct evbuffer *buffer, void *data)
2133
0
{
2134
0
  struct window_pane_input_data *cdata = data;
2135
0
  struct window_pane    *wp;
2136
0
  u_char        *buf = EVBUFFER_DATA(buffer);
2137
0
  size_t         len = EVBUFFER_LENGTH(buffer);
2138
2139
0
  wp = window_pane_find_by_id(cdata->wp);
2140
0
  if (cdata->file != NULL && (wp == NULL || c->flags & CLIENT_DEAD)) {
2141
0
    if (wp == NULL) {
2142
0
      c->retval = 1;
2143
0
      c->flags |= CLIENT_EXIT;
2144
0
    }
2145
0
    file_cancel(cdata->file);
2146
0
  } else if (cdata->file == NULL || closed || error != 0) {
2147
0
    cmdq_continue(cdata->item);
2148
0
    server_client_unref(c);
2149
0
    free(cdata);
2150
0
  } else
2151
0
    input_parse_buffer(wp, buf, len);
2152
0
  evbuffer_drain(buffer, len);
2153
0
}
2154
2155
int
2156
window_pane_start_input(struct window_pane *wp, struct cmdq_item *item,
2157
    char **cause)
2158
0
{
2159
0
  struct client     *c = cmdq_get_client(item);
2160
0
  struct window_pane_input_data *cdata;
2161
2162
0
  if (~wp->flags & PANE_EMPTY) {
2163
0
    *cause = xstrdup("pane is not empty");
2164
0
    return (-1);
2165
0
  }
2166
0
  if (c->flags & (CLIENT_DEAD|CLIENT_EXITED))
2167
0
    return (1);
2168
0
  if (c->session != NULL)
2169
0
    return (1);
2170
2171
0
  cdata = xmalloc(sizeof *cdata);
2172
0
  cdata->item = item;
2173
0
  cdata->wp = wp->id;
2174
0
  cdata->file = file_read(c, "-", window_pane_input_callback, cdata);
2175
0
  c->references++;
2176
2177
0
  return (0);
2178
0
}
2179
2180
void *
2181
window_pane_get_new_data(struct window_pane *wp,
2182
    struct window_pane_offset *wpo, size_t *size)
2183
0
{
2184
0
  size_t  used = wpo->used - wp->base_offset;
2185
2186
0
  *size = EVBUFFER_LENGTH(wp->event->input) - used;
2187
0
  return (EVBUFFER_DATA(wp->event->input) + used);
2188
0
}
2189
2190
void
2191
window_pane_update_used_data(struct window_pane *wp,
2192
    struct window_pane_offset *wpo, size_t size)
2193
0
{
2194
0
  size_t  used = wpo->used - wp->base_offset;
2195
2196
0
  if (size > EVBUFFER_LENGTH(wp->event->input) - used)
2197
0
    size = EVBUFFER_LENGTH(wp->event->input) - used;
2198
0
  wpo->used += size;
2199
0
}
2200
2201
void
2202
window_set_fill_character(struct window *w)
2203
11.1k
{
2204
11.1k
  const char    *value;
2205
11.1k
  struct utf8_data  *ud;
2206
2207
11.1k
  free(w->fill_character);
2208
11.1k
  w->fill_character = NULL;
2209
2210
11.1k
  value = options_get_string(w->options, "fill-character");
2211
11.1k
  if (*value != '\0' && utf8_isvalid(value)) {
2212
0
    ud = utf8_fromcstr(value);
2213
0
    if (ud != NULL && ud[0].width == 1)
2214
0
      w->fill_character = ud;
2215
0
    else
2216
0
      free(ud);
2217
0
  }
2218
11.1k
}
2219
2220
void
2221
window_pane_default_cursor(struct window_pane *wp)
2222
11.1k
{
2223
11.1k
  screen_set_default_cursor(wp->screen, wp->options);
2224
11.1k
}
2225
2226
int
2227
window_pane_mode(struct window_pane *wp)
2228
0
{
2229
0
  if (TAILQ_FIRST(&wp->modes) != NULL) {
2230
0
    if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode)
2231
0
      return (WINDOW_PANE_COPY_MODE);
2232
0
    if (TAILQ_FIRST(&wp->modes)->mode == &window_view_mode)
2233
0
      return (WINDOW_PANE_VIEW_MODE);
2234
0
  }
2235
0
  return (WINDOW_PANE_NO_MODE);
2236
0
}
2237
2238
int
2239
window_pane_show_scrollbar(struct window_pane *wp)
2240
3.56k
{
2241
3.56k
  if (SCREEN_IS_ALTERNATE(&wp->base))
2242
0
    return (0);
2243
3.56k
  if (wp->window->sb == PANE_SCROLLBARS_ALWAYS ||
2244
3.56k
      wp->window->sb == PANE_SCROLLBARS_AUTOHIDE ||
2245
3.56k
      (wp->window->sb == PANE_SCROLLBARS_MODAL &&
2246
0
      window_pane_mode(wp) != WINDOW_PANE_NO_MODE))
2247
0
    return (1);
2248
3.56k
  return (0);
2249
3.56k
}
2250
2251
int
2252
window_pane_scrollbar_reserve(struct window_pane *wp)
2253
0
{
2254
0
  if (!window_pane_show_scrollbar(wp))
2255
0
    return (0);
2256
0
  return (wp->window->sb == PANE_SCROLLBARS_ALWAYS);
2257
0
}
2258
2259
int
2260
window_pane_scrollbar_overlay(struct window_pane *wp)
2261
3.56k
{
2262
3.56k
  if (!window_pane_show_scrollbar(wp))
2263
3.56k
    return (0);
2264
0
  return (window_pane_scrollbar_auto_hide(wp));
2265
3.56k
}
2266
2267
int
2268
window_pane_scrollbar_visible(struct window_pane *wp)
2269
0
{
2270
0
  if (!window_pane_show_scrollbar(wp))
2271
0
    return (0);
2272
0
  if (!window_pane_scrollbar_auto_hide(wp))
2273
0
    return (1);
2274
0
  return (wp->sb_auto_visible);
2275
0
}
2276
2277
void
2278
window_pane_scrollbar_start_timer(struct window_pane *wp)
2279
0
{
2280
0
  struct timeval  tv;
2281
0
  u_int   delay;
2282
2283
0
  if (!window_pane_scrollbar_auto_hide(wp) || !wp->sb_auto_visible)
2284
0
    return;
2285
2286
0
  delay = options_get_number(wp->window->options,
2287
0
      "pane-scrollbars-timeout");
2288
0
  tv.tv_sec = delay / 1000;
2289
0
  tv.tv_usec = (delay % 1000) * 1000L;
2290
0
  evtimer_del(&wp->sb_auto_timer);
2291
0
  evtimer_add(&wp->sb_auto_timer, &tv);
2292
0
}
2293
2294
void
2295
window_pane_scrollbar_show(struct window_pane *wp, int start_timer)
2296
0
{
2297
0
  int changed = 0;
2298
0
  if (!window_pane_scrollbar_auto_hide(wp))
2299
0
    return;
2300
0
  if (!window_pane_show_scrollbar(wp))
2301
0
    return;
2302
0
  if (!wp->sb_auto_visible) {
2303
0
    wp->sb_auto_visible = 1;
2304
0
    changed = 1;
2305
0
  }
2306
0
  evtimer_del(&wp->sb_auto_timer);
2307
0
  if (start_timer)
2308
0
    window_pane_scrollbar_start_timer(wp);
2309
0
  if (changed)
2310
0
    window_pane_scrollbar_redraw_visibility(wp);
2311
0
}
2312
2313
void
2314
window_pane_scrollbar_hide(struct window_pane *wp)
2315
0
{
2316
0
  if (event_initialized(&wp->sb_auto_timer))
2317
0
    evtimer_del(&wp->sb_auto_timer);
2318
0
  wp->sb_auto_hover = 0;
2319
0
  if (!wp->sb_auto_visible)
2320
0
    return;
2321
0
  wp->sb_auto_visible = 0;
2322
0
  window_pane_scrollbar_redraw_visibility(wp);
2323
0
}
2324
2325
int
2326
window_pane_get_bg(struct window_pane *wp)
2327
217
{
2328
217
  int     c;
2329
217
  struct grid_cell  defaults;
2330
2331
217
  c = window_pane_get_bg_control_client(wp);
2332
217
  if (c == -1) {
2333
217
    tty_default_colours(&defaults, wp, NULL);
2334
217
    if (COLOUR_DEFAULT(defaults.bg))
2335
217
      c = window_get_bg_client(wp);
2336
0
    else
2337
0
      c = defaults.bg;
2338
217
  }
2339
217
  return (c);
2340
217
}
2341
2342
/* Get a client with a background for the pane. */
2343
int
2344
window_get_bg_client(struct window_pane *wp)
2345
217
{
2346
217
  struct window *w = wp->window;
2347
217
  struct client *loop;
2348
2349
217
  TAILQ_FOREACH(loop, &clients, entry) {
2350
0
    if (loop->flags & CLIENT_UNATTACHEDFLAGS)
2351
0
      continue;
2352
0
    if (loop->session == NULL || !session_has(loop->session, w))
2353
0
      continue;
2354
0
    if (loop->tty.bg == -1)
2355
0
      continue;
2356
0
    return (loop->tty.bg);
2357
0
  }
2358
217
  return (-1);
2359
217
}
2360
2361
/*
2362
 * If any control mode client exists that has provided a bg color, return it.
2363
 * Otherwise, return -1.
2364
 */
2365
int
2366
window_pane_get_bg_control_client(struct window_pane *wp)
2367
217
{
2368
217
  struct client *c;
2369
2370
217
  if (wp->control_bg == -1)
2371
217
    return (-1);
2372
2373
0
  TAILQ_FOREACH(c, &clients, entry) {
2374
0
    if (c->flags & CLIENT_CONTROL)
2375
0
      return (wp->control_bg);
2376
0
  }
2377
0
  return (-1);
2378
0
}
2379
2380
/*
2381
 * Get a client with a foreground for the pane. There isn't much to choose
2382
 * between them so just use the first.
2383
 */
2384
int
2385
window_pane_get_fg(struct window_pane *wp)
2386
66
{
2387
66
  struct window *w = wp->window;
2388
66
  struct client *loop;
2389
2390
66
  TAILQ_FOREACH(loop, &clients, entry) {
2391
0
    if (loop->flags & CLIENT_UNATTACHEDFLAGS)
2392
0
      continue;
2393
0
    if (loop->session == NULL || !session_has(loop->session, w))
2394
0
      continue;
2395
0
    if (loop->tty.fg == -1)
2396
0
      continue;
2397
0
    return (loop->tty.fg);
2398
0
  }
2399
66
  return (-1);
2400
66
}
2401
2402
/*
2403
 * If any control mode client exists that has provided a fg color, return it.
2404
 * Otherwise, return -1.
2405
 */
2406
int
2407
window_pane_get_fg_control_client(struct window_pane *wp)
2408
66
{
2409
66
  struct client *c;
2410
2411
66
  if (wp->control_fg == -1)
2412
66
    return (-1);
2413
2414
0
  TAILQ_FOREACH(c, &clients, entry) {
2415
0
    if (c->flags & CLIENT_CONTROL)
2416
0
      return (wp->control_fg);
2417
0
  }
2418
0
  return (-1);
2419
0
}
2420
2421
enum client_theme
2422
window_pane_get_theme(struct window_pane *wp)
2423
151
{
2424
151
  struct window   *w;
2425
151
  struct client   *loop;
2426
151
  int      found_light = 0, found_dark = 0;
2427
2428
151
  if (wp == NULL)
2429
0
    return (THEME_UNKNOWN);
2430
151
  w = wp->window;
2431
2432
  /*
2433
   * Prefer a theme reported by an attached client with mode 2031 or DSR
2434
   * 996: the terminal knows its own light or dark mode.
2435
   */
2436
151
  TAILQ_FOREACH(loop, &clients, entry) {
2437
0
    if (loop->flags & CLIENT_UNATTACHEDFLAGS)
2438
0
      continue;
2439
0
    if (loop->session == NULL || !session_has(loop->session, w))
2440
0
      continue;
2441
0
    switch (loop->theme) {
2442
0
    case THEME_LIGHT:
2443
0
      found_light = 1;
2444
0
      break;
2445
0
    case THEME_DARK:
2446
0
      found_dark = 1;
2447
0
      break;
2448
0
    case THEME_UNKNOWN:
2449
0
      break;
2450
0
    }
2451
0
  }
2452
151
  if (found_dark && !found_light)
2453
0
    return (THEME_DARK);
2454
151
  if (found_light && !found_dark)
2455
0
    return (THEME_LIGHT);
2456
2457
  /*
2458
   * Otherwise guess from the pane background colour, for terminals which
2459
   * do not report a theme themselves.
2460
   */
2461
151
  return (colour_totheme(window_pane_get_bg(wp)));
2462
151
}
2463
2464
void
2465
window_pane_send_theme_update(struct window_pane *wp)
2466
0
{
2467
0
  enum client_theme theme;
2468
2469
0
  if (wp == NULL || window_pane_exited(wp))
2470
0
    return;
2471
0
  if (~wp->flags & PANE_THEMECHANGED)
2472
0
    return;
2473
0
  if (~wp->screen->mode & MODE_THEME_UPDATES)
2474
0
    return;
2475
2476
0
  theme = window_pane_get_theme(wp);
2477
0
  if (theme == wp->last_theme)
2478
0
    return;
2479
0
  wp->last_theme = theme;
2480
0
  wp->flags &= ~PANE_THEMECHANGED;
2481
2482
0
  switch (theme) {
2483
0
  case THEME_LIGHT:
2484
0
    log_debug("%s: %%%u light theme", __func__, wp->id);
2485
0
    bufferevent_write(wp->event, "\033[?997;2n", 9);
2486
0
    break;
2487
0
  case THEME_DARK:
2488
0
    log_debug("%s: %%%u dark theme", __func__, wp->id);
2489
0
    bufferevent_write(wp->event, "\033[?997;1n", 9);
2490
0
    break;
2491
0
  case THEME_UNKNOWN:
2492
0
    log_debug("%s: %%%u unknown theme", __func__, wp->id);
2493
0
    break;
2494
0
  }
2495
0
}
2496
2497
struct style_range *
2498
window_pane_status_get_range(struct window_pane *wp, u_int x, u_int y)
2499
0
{
2500
0
  struct style_ranges *srs;
2501
0
  u_int      line;
2502
0
  int      pane_status;
2503
2504
0
  if (wp == NULL)
2505
0
    return (NULL);
2506
0
  srs = &wp->border_status_line.ranges;
2507
2508
0
  pane_status = window_pane_get_pane_status(wp);
2509
0
  if (pane_status == PANE_STATUS_TOP)
2510
0
    line = wp->yoff - 1;
2511
0
  else if (pane_status == PANE_STATUS_BOTTOM)
2512
0
    line = wp->yoff + wp->sy;
2513
0
  if (pane_status == PANE_STATUS_OFF || line != y)
2514
0
    return (NULL);
2515
2516
  /*
2517
   * The border formats start 2 off but that isn't reflected in
2518
   * the stored bounds of the range.
2519
   */
2520
0
  return (style_ranges_get_range(srs, x - wp->xoff - 2));
2521
0
}
2522
2523
enum pane_lines
2524
window_get_pane_lines(struct window *w)
2525
0
{
2526
0
  struct options  *oo;
2527
2528
0
  oo = w->options;
2529
0
  return (options_get_number(oo, "pane-border-lines"));
2530
0
}
2531
2532
enum pane_lines
2533
window_pane_get_pane_lines(struct window_pane *wp)
2534
0
{
2535
0
  struct options  *oo;
2536
2537
0
  if (!window_pane_is_floating(wp))
2538
0
    oo = wp->window->options;
2539
0
  else
2540
0
    oo = wp->options;
2541
0
  return (options_get_number(oo, "pane-border-lines"));
2542
0
}
2543
2544
int
2545
window_get_pane_status(struct window *w)
2546
768
{
2547
768
  int status;
2548
2549
768
  status = options_get_number(w->options, "pane-border-status");
2550
768
  if (status == PANE_STATUS_TOP_FLOATING ||
2551
768
      status == PANE_STATUS_BOTTOM_FLOATING)
2552
0
    return (PANE_STATUS_OFF);
2553
768
  return (status);
2554
768
}
2555
2556
int
2557
window_pane_get_pane_status(struct window_pane *wp)
2558
0
{
2559
0
  int status;
2560
2561
0
  if (!window_pane_is_floating(wp))
2562
0
    return (window_get_pane_status(wp->window));
2563
0
  if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE)
2564
0
    return (PANE_STATUS_OFF);
2565
2566
0
  status = options_get_number(wp->options, "pane-border-status");
2567
0
  if (status == PANE_STATUS_TOP_FLOATING)
2568
0
    return (PANE_STATUS_TOP);
2569
0
  if (status == PANE_STATUS_BOTTOM_FLOATING)
2570
0
    return (PANE_STATUS_BOTTOM);
2571
0
  return (status);
2572
0
}
2573
2574
int
2575
window_pane_is_floating(struct window_pane *wp)
2576
0
{
2577
0
  struct layout_cell  *lc = wp->layout_cell;
2578
2579
0
  if (lc == NULL || (lc->flags & LAYOUT_CELL_FLOATING) == 0)
2580
0
    return (0);
2581
0
  return (1);
2582
0
}