Coverage Report

Created: 2026-08-31 06:23

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