Coverage Report

Created: 2026-05-30 06:39

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