Coverage Report

Created: 2026-10-17 06:19

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