Coverage Report

Created: 2026-07-30 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/window-panes.c
Line
Count
Source
1
/* $OpenBSD: window-panes.c,v 1.4 2026/07/29 14:06:32 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2026 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 ANY
13
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
21
#include <stdlib.h>
22
#include <string.h>
23
24
#include "tmux.h"
25
26
static struct screen  *window_panes_init(struct window_mode_entry *,
27
           struct cmdq_item *, struct cmd_find_state *,
28
           struct args *);
29
static void    window_panes_free(struct window_mode_entry *);
30
static void    window_panes_resize(struct window_mode_entry *, u_int,
31
           u_int);
32
static void    window_panes_key(struct window_mode_entry *,
33
           struct client *, struct session *,
34
           struct winlink *, key_code, struct mouse_event *);
35
36
const struct window_mode window_panes_mode = {
37
  .name = "panes-mode",
38
  .flags = WINDOW_MODE_HIDE_PANE_STATUS|WINDOW_MODE_NO_STACK|
39
      WINDOW_MODE_FILL_WINDOW|WINDOW_MODE_HIDE_SCROLLBARS,
40
41
  .init = window_panes_init,
42
  .free = window_panes_free,
43
  .resize = window_panes_resize,
44
  .key = window_panes_key,
45
};
46
47
struct window_panes_area {
48
  u_int id;
49
  u_int x;
50
  u_int y;
51
  u_int sx;
52
  u_int sy;
53
};
54
55
struct window_panes_modedata {
56
  struct window_pane    *wp;
57
  struct session      *session;
58
  u_int        source_session;
59
  u_int        source_window;
60
  struct screen      screen;
61
  struct screen     *preview;
62
63
  struct event       timer;
64
65
  struct args_command_state *state;
66
  u_int        delay;
67
  int        ignore_keys;
68
  int        zoomed;
69
70
  struct window_panes_area  *areas;
71
  u_int        areas_size;
72
};
73
74
0
#define WINDOW_PANES_BORDER_L 0x1
75
0
#define WINDOW_PANES_BORDER_R 0x2
76
0
#define WINDOW_PANES_BORDER_U 0x4
77
0
#define WINDOW_PANES_BORDER_D 0x8
78
79
static int
80
window_panes_get_source(struct window_panes_modedata *data, struct session **sp,
81
    struct winlink **wlp, struct window **wp)
82
0
{
83
0
  struct session  *s;
84
0
  struct window *w;
85
0
  struct winlink  *wl = NULL;
86
87
0
  w = window_find_by_id(data->source_window);
88
0
  if (w == NULL)
89
0
    return (0);
90
91
0
  s = session_find_by_id(data->source_session);
92
0
  if (s != NULL)
93
0
    wl = winlink_find_by_window(&s->windows, w);
94
0
  if (wl == NULL)
95
0
    s = data->session;
96
0
  if (s != NULL)
97
0
    wl = winlink_find_by_window(&s->windows, w);
98
99
0
  if (sp != NULL)
100
0
    *sp = s;
101
0
  if (wlp != NULL)
102
0
    *wlp = wl;
103
0
  if (wp != NULL)
104
0
    *wp = w;
105
0
  return (1);
106
0
}
107
108
static void
109
window_panes_set_preview(struct window_panes_modedata *data)
110
0
{
111
0
  struct window_pane  *wp = data->wp;
112
0
  struct screen   *src = &wp->base, *dst;
113
0
  struct screen_write_ctx  ctx;
114
0
  u_int      sx = screen_size_x(src), sy = screen_size_y(src);
115
116
0
  data->preview = dst = xmalloc(sizeof *data->preview);
117
0
  screen_init(dst, sx, sy, 0);
118
0
  screen_write_start(&ctx, dst);
119
0
  screen_write_fast_copy(&ctx, src, 0, src->grid->hsize, sx, sy);
120
0
  screen_write_stop(&ctx);
121
0
  dst->mode = src->mode;
122
0
  dst->cx = src->cx;
123
0
  dst->cy = src->cy;
124
0
}
125
126
static void
127
window_panes_free_areas(struct window_panes_modedata *data)
128
0
{
129
0
  free(data->areas);
130
0
  data->areas = NULL;
131
0
  data->areas_size = 0;
132
0
}
133
134
static void
135
window_panes_add_area(struct window_panes_modedata *data,
136
    struct window_pane *wp, u_int x, u_int y, u_int sx, u_int sy)
137
0
{
138
0
  struct window_panes_area  *area;
139
140
0
  data->areas = xreallocarray(data->areas, data->areas_size + 1,
141
0
      sizeof *data->areas);
142
0
  area = &data->areas[data->areas_size++];
143
0
  area->id = wp->id;
144
0
  area->x = x;
145
0
  area->y = y;
146
0
  area->sx = sx;
147
0
  area->sy = sy;
148
0
}
149
150
static int
151
window_panes_pane_floating(struct window_pane *wp)
152
0
{
153
0
  struct layout_cell  *lc = wp->saved_layout_cell;
154
155
0
  if (lc == NULL)
156
0
    lc = wp->layout_cell;
157
0
  if (lc == NULL || (~lc->flags & LAYOUT_CELL_FLOATING))
158
0
    return (0);
159
0
  return (1);
160
0
}
161
162
static int
163
window_panes_pane_visible(struct window_pane *wp)
164
0
{
165
0
  if (wp->saved_layout_cell != NULL)
166
0
    return (1);
167
0
  return (window_pane_is_visible(wp));
168
0
}
169
170
static int
171
window_panes_get_geometry(struct window_pane *wp, struct layout_cell *root,
172
    u_int osx, u_int osy, u_int dsx, u_int dsy, u_int *xp, u_int *yp,
173
    u_int *sxp, u_int *syp)
174
0
{
175
0
  struct layout_cell  *lc = wp->saved_layout_cell;
176
0
  int      status;
177
0
  u_int      x, y, sx, sy, x2, y2;
178
179
0
  if (lc == NULL)
180
0
    lc = wp->layout_cell;
181
0
  if (lc == NULL || osx == 0 || osy == 0 || dsx == 0 || dsy == 0)
182
0
    return (0);
183
184
0
  if (osx <= dsx && osy <= dsy) {
185
0
    x = lc->g.xoff;
186
0
    y = lc->g.yoff;
187
0
    x2 = x + lc->g.sx;
188
0
    y2 = y + lc->g.sy;
189
0
  } else {
190
0
    x = (u_int)lc->g.xoff * dsx / osx;
191
0
    y = (u_int)lc->g.yoff * dsy / osy;
192
0
    x2 = ((u_int)lc->g.xoff + lc->g.sx) * dsx / osx;
193
0
    y2 = ((u_int)lc->g.yoff + lc->g.sy) * dsy / osy;
194
0
  }
195
196
0
  if (x >= dsx || y >= dsy)
197
0
    return (0);
198
0
  if (x2 <= x)
199
0
    x2 = x + 1;
200
0
  if (y2 <= y)
201
0
    y2 = y + 1;
202
0
  if (x2 > dsx)
203
0
    x2 = dsx;
204
0
  if (y2 > dsy)
205
0
    y2 = dsy;
206
207
0
  sx = x2 - x;
208
0
  sy = y2 - y;
209
0
  if (sx == 0 || sy == 0)
210
0
    return (0);
211
212
0
  status = window_get_pane_status(wp->window);
213
0
  if (layout_add_horizontal_border(root, lc, status) && sy > 1) {
214
0
    if (status == PANE_STATUS_TOP)
215
0
      y++;
216
0
    sy--;
217
0
  }
218
219
0
  *xp = x;
220
0
  *yp = y;
221
0
  *sxp = sx;
222
0
  *syp = sy;
223
0
  return (1);
224
0
}
225
226
static void
227
window_panes_get_border_cell(struct window_panes_modedata *data,
228
    struct grid_cell *gc)
229
0
{
230
0
  struct format_tree  *ft;
231
0
  struct window_pane  *wp = data->wp;
232
0
  struct session    *s = data->session;
233
234
0
  memcpy(gc, &grid_default_cell, sizeof *gc);
235
0
  ft = format_create_defaults(NULL, NULL, s, s->curw, wp);
236
0
  style_apply(gc, wp->window->options, "display-panes-border-style", ft);
237
0
  format_free(ft);
238
0
}
239
240
static int
241
window_panes_map_x(u_int x, u_int osx, u_int dsx)
242
0
{
243
0
  if (osx <= dsx)
244
0
    return (x);
245
0
  return (x * dsx / osx);
246
0
}
247
248
static int
249
window_panes_map_y(u_int y, u_int osy, u_int dsy)
250
0
{
251
0
  if (osy <= dsy)
252
0
    return (y);
253
0
  return (y * dsy / osy);
254
0
}
255
256
static struct layout_cell *
257
window_panes_next_tiled_cell(struct layout_cell *lc)
258
0
{
259
0
  struct layout_cell  *next;
260
261
0
  next = TAILQ_NEXT(lc, entry);
262
0
  while (next != NULL) {
263
0
    if (~next->flags & LAYOUT_CELL_FLOATING)
264
0
      return (next);
265
0
    next = TAILQ_NEXT(next, entry);
266
0
  }
267
0
  return (NULL);
268
0
}
269
270
static void
271
window_panes_mark_border(u_char *map, u_int dsx, u_int dsy, u_int x, u_int y,
272
    u_char mask)
273
0
{
274
0
  if (x < dsx && y < dsy)
275
0
    map[y * dsx + x] |= mask;
276
0
}
277
278
static void
279
window_panes_mark_vline(u_char *map, u_int dsx, u_int dsy, int x, int y, int y2)
280
0
{
281
0
  u_char  mask;
282
0
  int yy;
283
284
0
  if (x < 0 || (u_int)x >= dsx || y2 <= y)
285
0
    return;
286
0
  if (y < 0)
287
0
    y = 0;
288
0
  if ((u_int)y2 > dsy)
289
0
    y2 = dsy;
290
291
0
  for (yy = y; yy < y2; yy++) {
292
0
    mask = 0;
293
0
    if (yy > y)
294
0
      mask |= WINDOW_PANES_BORDER_U;
295
0
    if (yy + 1 < y2)
296
0
      mask |= WINDOW_PANES_BORDER_D;
297
0
    if (mask == 0)
298
0
      mask = WINDOW_PANES_BORDER_U|WINDOW_PANES_BORDER_D;
299
0
    window_panes_mark_border(map, dsx, dsy, x, yy, mask);
300
0
  }
301
0
}
302
303
static void
304
window_panes_mark_hline(u_char *map, u_int dsx, u_int dsy, int x, int x2, int y)
305
0
{
306
0
  u_char  mask;
307
0
  int xx;
308
309
0
  if (y < 0 || (u_int)y >= dsy || x2 <= x)
310
0
    return;
311
0
  if (x < 0)
312
0
    x = 0;
313
0
  if ((u_int)x2 > dsx)
314
0
    x2 = dsx;
315
316
0
  for (xx = x; xx < x2; xx++) {
317
0
    mask = 0;
318
0
    if (xx > x)
319
0
      mask |= WINDOW_PANES_BORDER_L;
320
0
    if (xx + 1 < x2)
321
0
      mask |= WINDOW_PANES_BORDER_R;
322
0
    if (mask == 0)
323
0
      mask = WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_R;
324
0
    window_panes_mark_border(map, dsx, dsy, xx, y, mask);
325
0
  }
326
0
}
327
328
static void
329
window_panes_mark_borders_cell(u_char *map, struct layout_cell *lc, u_int osx,
330
    u_int osy, u_int dsx, u_int dsy)
331
0
{
332
0
  struct layout_cell  *lcchild, *lcnext;
333
0
  int      x, y, x2, y2;
334
335
0
  if (lc->type == LAYOUT_WINDOWPANE)
336
0
    return;
337
338
0
  TAILQ_FOREACH(lcchild, &lc->cells, entry) {
339
0
    window_panes_mark_borders_cell(map, lcchild, osx, osy, dsx,
340
0
        dsy);
341
0
    if (lcchild->flags & LAYOUT_CELL_FLOATING)
342
0
      continue;
343
0
    lcnext = window_panes_next_tiled_cell(lcchild);
344
0
    if (lcnext == NULL)
345
0
      continue;
346
347
0
    if (lc->type == LAYOUT_LEFTRIGHT) {
348
0
      x = window_panes_map_x(lcchild->g.xoff + lcchild->g.sx,
349
0
          osx, dsx);
350
0
      y = window_panes_map_y(lc->g.yoff, osy, dsy);
351
0
      y2 = window_panes_map_y(lc->g.yoff + lc->g.sy, osy,
352
0
          dsy);
353
0
      window_panes_mark_vline(map, dsx, dsy, x, y, y2);
354
0
    } else {
355
0
      x = window_panes_map_x(lc->g.xoff, osx, dsx);
356
0
      x2 = window_panes_map_x(lc->g.xoff + lc->g.sx, osx,
357
0
          dsx);
358
0
      y = window_panes_map_y(lcchild->g.yoff + lcchild->g.sy,
359
0
          osy, dsy);
360
0
      window_panes_mark_hline(map, dsx, dsy, x, x2, y);
361
0
    }
362
0
  }
363
0
}
364
365
static void
366
window_panes_mark_pane_status_borders(u_char *map, struct window *w,
367
    struct layout_cell *root, u_int osx, u_int osy, u_int dsx, u_int dsy)
368
0
{
369
0
  struct window_pane  *wp;
370
0
  struct layout_cell  *lc;
371
0
  int      status, x, y, x2, y2;
372
373
0
  status = window_get_pane_status(w);
374
0
  if (status != PANE_STATUS_TOP && status != PANE_STATUS_BOTTOM)
375
0
    return;
376
377
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
378
0
    if (!window_panes_pane_visible(wp))
379
0
      continue;
380
381
0
    lc = wp->saved_layout_cell;
382
0
    if (lc == NULL)
383
0
      lc = wp->layout_cell;
384
0
    if (lc == NULL ||
385
0
        !layout_add_horizontal_border(root, lc, status))
386
0
      continue;
387
388
0
    x = window_panes_map_x(lc->g.xoff, osx, dsx);
389
0
    x2 = window_panes_map_x(lc->g.xoff + lc->g.sx, osx, dsx);
390
0
    if (status == PANE_STATUS_TOP)
391
0
      y = window_panes_map_y(lc->g.yoff, osy, dsy);
392
0
    else {
393
0
      y2 = window_panes_map_y(lc->g.yoff + lc->g.sy, osy,
394
0
          dsy);
395
0
      y = y2 - 1;
396
0
    }
397
0
    window_panes_mark_hline(map, dsx, dsy, x, x2, y);
398
0
  }
399
0
}
400
401
static int
402
window_panes_get_floating_borders(struct window_pane *wp, u_int osx, u_int osy,
403
    u_int dsx, u_int dsy, int *xp, int *yp, int *x2p, int *y2p)
404
0
{
405
0
  struct layout_cell  *lc;
406
407
0
  lc = wp->saved_layout_cell;
408
0
  if (lc == NULL)
409
0
    lc = wp->layout_cell;
410
0
  if (lc == NULL || (~lc->flags & LAYOUT_CELL_FLOATING))
411
0
    return (0);
412
413
0
  if (lc->g.xoff == 0)
414
0
    *xp = -1;
415
0
  else
416
0
    *xp = window_panes_map_x(lc->g.xoff - 1, osx, dsx);
417
0
  if (lc->g.yoff == 0)
418
0
    *yp = -1;
419
0
  else
420
0
    *yp = window_panes_map_y(lc->g.yoff - 1, osy, dsy);
421
0
  *x2p = window_panes_map_x(lc->g.xoff + lc->g.sx, osx, dsx);
422
0
  *y2p = window_panes_map_y(lc->g.yoff + lc->g.sy, osy, dsy);
423
0
  return (1);
424
0
}
425
426
static int
427
window_panes_clip_floating_pane(struct window_pane *wp, u_int osx, u_int osy,
428
    u_int dsx, u_int dsy, u_int *xp, u_int *yp, u_int *sxp, u_int *syp)
429
0
{
430
0
  int x, y, x2, y2, bx, by, bx2, by2;
431
432
0
  if (!window_panes_get_floating_borders(wp, osx, osy, dsx, dsy, &x, &y,
433
0
      &x2, &y2))
434
0
    return (1);
435
436
0
  bx = *xp;
437
0
  by = *yp;
438
0
  bx2 = bx + *sxp - 1;
439
0
  by2 = by + *syp - 1;
440
441
0
  if (x >= 0 && bx <= x)
442
0
    bx = x + 1;
443
0
  if (y >= 0 && by <= y)
444
0
    by = y + 1;
445
0
  if ((u_int)x2 < dsx && bx2 >= x2)
446
0
    bx2 = x2 - 1;
447
0
  if ((u_int)y2 < dsy && by2 >= y2)
448
0
    by2 = y2 - 1;
449
0
  if (bx2 < bx || by2 < by)
450
0
    return (0);
451
452
0
  *xp = bx;
453
0
  *yp = by;
454
0
  *sxp = bx2 - bx + 1;
455
0
  *syp = by2 - by + 1;
456
0
  return (1);
457
0
}
458
459
static int
460
window_panes_border_cell_type(u_char mask)
461
0
{
462
0
  switch (mask) {
463
0
  case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_R|
464
0
      WINDOW_PANES_BORDER_U|WINDOW_PANES_BORDER_D:
465
0
    return (CELL_LRUD);
466
0
  case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_R|WINDOW_PANES_BORDER_U:
467
0
    return (CELL_LRU);
468
0
  case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_R|WINDOW_PANES_BORDER_D:
469
0
    return (CELL_LRD);
470
0
  case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_R:
471
0
  case WINDOW_PANES_BORDER_L:
472
0
  case WINDOW_PANES_BORDER_R:
473
0
    return (CELL_LR);
474
0
  case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_U|WINDOW_PANES_BORDER_D:
475
0
    return (CELL_ULD);
476
0
  case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_U:
477
0
    return (CELL_LU);
478
0
  case WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_D:
479
0
    return (CELL_LD);
480
0
  case WINDOW_PANES_BORDER_R|WINDOW_PANES_BORDER_U|WINDOW_PANES_BORDER_D:
481
0
    return (CELL_URD);
482
0
  case WINDOW_PANES_BORDER_R|WINDOW_PANES_BORDER_U:
483
0
    return (CELL_RU);
484
0
  case WINDOW_PANES_BORDER_R|WINDOW_PANES_BORDER_D:
485
0
    return (CELL_RD);
486
0
  case WINDOW_PANES_BORDER_U|WINDOW_PANES_BORDER_D:
487
0
  case WINDOW_PANES_BORDER_U:
488
0
  case WINDOW_PANES_BORDER_D:
489
0
    return (CELL_UD);
490
0
  }
491
0
  return (CELL_NONE);
492
0
}
493
494
static int
495
window_panes_border_has_horizontal(u_char mask)
496
0
{
497
0
  return ((mask & (WINDOW_PANES_BORDER_L|WINDOW_PANES_BORDER_R)) != 0);
498
0
}
499
500
static int
501
window_panes_border_has_vertical(u_char mask)
502
0
{
503
0
  return ((mask & (WINDOW_PANES_BORDER_U|WINDOW_PANES_BORDER_D)) != 0);
504
0
}
505
506
static void
507
window_panes_mark_border_joins_cell(u_char *map, struct layout_cell *lc,
508
    u_int osx, u_int osy, u_int dsx, u_int dsy)
509
0
{
510
0
  struct layout_cell  *lcchild, *lcnext;
511
0
  int      x, y, x2, y2;
512
513
0
  if (lc->type == LAYOUT_WINDOWPANE)
514
0
    return;
515
516
0
  TAILQ_FOREACH(lcchild, &lc->cells, entry) {
517
0
    window_panes_mark_border_joins_cell(map, lcchild, osx, osy,
518
0
        dsx, dsy);
519
0
    if (lcchild->flags & LAYOUT_CELL_FLOATING)
520
0
      continue;
521
0
    lcnext = window_panes_next_tiled_cell(lcchild);
522
0
    if (lcnext == NULL)
523
0
      continue;
524
525
0
    if (lc->type == LAYOUT_LEFTRIGHT) {
526
0
      x = window_panes_map_x(lcchild->g.xoff + lcchild->g.sx,
527
0
          osx, dsx);
528
0
      y = window_panes_map_y(lc->g.yoff, osy, dsy);
529
0
      y2 = window_panes_map_y(lc->g.yoff + lc->g.sy, osy,
530
0
          dsy);
531
0
      if (x < 0 || (u_int)x >= dsx)
532
0
        continue;
533
0
      if (y > 0 &&
534
0
          window_panes_border_has_horizontal(
535
0
          map[(y - 1) * dsx + x])) {
536
0
        window_panes_mark_border(map, dsx, dsy, x,
537
0
            y - 1, WINDOW_PANES_BORDER_D);
538
0
        window_panes_mark_border(map, dsx, dsy, x, y,
539
0
            WINDOW_PANES_BORDER_U);
540
0
      }
541
0
      if ((u_int)y2 < dsy &&
542
0
          window_panes_border_has_horizontal(
543
0
          map[y2 * dsx + x])) {
544
0
        window_panes_mark_border(map, dsx, dsy, x, y2,
545
0
            WINDOW_PANES_BORDER_U);
546
0
        window_panes_mark_border(map, dsx, dsy, x,
547
0
            y2 - 1, WINDOW_PANES_BORDER_D);
548
0
      }
549
0
    } else {
550
0
      x = window_panes_map_x(lc->g.xoff, osx, dsx);
551
0
      x2 = window_panes_map_x(lc->g.xoff + lc->g.sx, osx,
552
0
          dsx);
553
0
      y = window_panes_map_y(lcchild->g.yoff + lcchild->g.sy,
554
0
          osy, dsy);
555
0
      if (y < 0 || (u_int)y >= dsy)
556
0
        continue;
557
0
      if (x > 0 &&
558
0
          window_panes_border_has_vertical(
559
0
          map[y * dsx + x - 1])) {
560
0
        window_panes_mark_border(map, dsx, dsy, x - 1,
561
0
            y, WINDOW_PANES_BORDER_R);
562
0
        window_panes_mark_border(map, dsx, dsy, x, y,
563
0
            WINDOW_PANES_BORDER_L);
564
0
      }
565
0
      if ((u_int)x2 < dsx &&
566
0
          window_panes_border_has_vertical(
567
0
          map[y * dsx + x2])) {
568
0
        window_panes_mark_border(map, dsx, dsy, x2, y,
569
0
            WINDOW_PANES_BORDER_L);
570
0
        window_panes_mark_border(map, dsx, dsy, x2 - 1,
571
0
            y, WINDOW_PANES_BORDER_R);
572
0
      }
573
0
    }
574
0
  }
575
0
}
576
577
static void
578
window_panes_draw_borders(struct screen_write_ctx *ctx, struct window *w,
579
    struct layout_cell *lc, const struct grid_cell *gc, u_int osx, u_int osy,
580
    u_int dsx, u_int dsy)
581
0
{
582
0
  struct grid_cell   border_gc;
583
0
  u_char      *map;
584
0
  u_int      xx, yy;
585
0
  int      cell_type;
586
587
0
  if (dsx == 0 || dsy == 0)
588
0
    return;
589
590
0
  map = xcalloc(dsx, dsy);
591
0
  window_panes_mark_borders_cell(map, lc, osx, osy, dsx, dsy);
592
0
  window_panes_mark_pane_status_borders(map, w, lc, osx, osy, dsx,
593
0
      dsy);
594
0
  window_panes_mark_border_joins_cell(map, lc, osx, osy, dsx, dsy);
595
596
0
  for (yy = 0; yy < dsy; yy++) {
597
0
    for (xx = 0; xx < dsx; xx++) {
598
0
      if (map[yy * dsx + xx] == 0)
599
0
        continue;
600
0
      cell_type = window_panes_border_cell_type(
601
0
          map[yy * dsx + xx]);
602
0
      memcpy(&border_gc, gc, sizeof border_gc);
603
0
      border_gc.attr |= GRID_ATTR_CHARSET;
604
0
      utf8_set(&border_gc.data, CELL_BORDERS[cell_type]);
605
0
      screen_write_cursormove(ctx, xx, yy, 0);
606
0
      screen_write_cell(ctx, &border_gc);
607
0
    }
608
0
  }
609
0
  free(map);
610
0
}
611
612
static void
613
window_panes_draw_floating_border(struct screen_write_ctx *ctx,
614
    struct window_pane *wp, const struct grid_cell *gc, u_int osx, u_int osy,
615
    u_int dsx, u_int dsy)
616
0
{
617
0
  struct grid_cell   border_gc;
618
0
  u_char      *map;
619
0
  u_int      xx, yy;
620
0
  int      x, y, x2, y2, cell_type;
621
622
0
  if (dsx == 0 || dsy == 0)
623
0
    return;
624
0
  if (!window_panes_get_floating_borders(wp, osx, osy, dsx, dsy, &x, &y,
625
0
      &x2, &y2))
626
0
    return;
627
628
0
  map = xcalloc(dsx, dsy);
629
0
  window_panes_mark_hline(map, dsx, dsy, x, x2 + 1, y);
630
0
  window_panes_mark_hline(map, dsx, dsy, x, x2 + 1, y2);
631
0
  window_panes_mark_vline(map, dsx, dsy, x, y, y2 + 1);
632
0
  window_panes_mark_vline(map, dsx, dsy, x2, y, y2 + 1);
633
634
0
  for (yy = 0; yy < dsy; yy++) {
635
0
    for (xx = 0; xx < dsx; xx++) {
636
0
      if (map[yy * dsx + xx] == 0)
637
0
        continue;
638
0
      cell_type = window_panes_border_cell_type(
639
0
          map[yy * dsx + xx]);
640
0
      memcpy(&border_gc, gc, sizeof border_gc);
641
0
      border_gc.attr |= GRID_ATTR_CHARSET;
642
0
      utf8_set(&border_gc.data, CELL_BORDERS[cell_type]);
643
0
      screen_write_cursormove(ctx, xx, yy, 0);
644
0
      screen_write_cell(ctx, &border_gc);
645
0
    }
646
0
  }
647
0
  free(map);
648
0
}
649
650
static void
651
window_panes_clear_floating_area(struct screen_write_ctx *ctx,
652
    struct window_pane *wp, u_int osx, u_int osy, u_int dsx, u_int dsy)
653
0
{
654
0
  struct grid_cell  gc;
655
0
  int     x, y, x2, y2, xx, yy;
656
657
0
  if (!window_panes_get_floating_borders(wp, osx, osy, dsx, dsy, &x, &y,
658
0
      &x2, &y2))
659
0
    return;
660
661
0
  memcpy(&gc, &grid_default_cell, sizeof gc);
662
0
  if (x < 0)
663
0
    x = 0;
664
0
  if (y < 0)
665
0
    y = 0;
666
0
  if ((u_int)x2 >= dsx)
667
0
    x2 = dsx - 1;
668
0
  if ((u_int)y2 >= dsy)
669
0
    y2 = dsy - 1;
670
0
  if (x2 < x || y2 < y)
671
0
    return;
672
673
0
  for (yy = y; yy <= y2; yy++) {
674
0
    screen_write_cursormove(ctx, x, yy, 0);
675
0
    for (xx = x; xx <= x2; xx++)
676
0
      screen_write_putc(ctx, &gc, ' ');
677
0
  }
678
0
}
679
680
static void
681
window_panes_draw_format(struct window_panes_modedata *data,
682
    struct screen_write_ctx *ctx, struct window_pane *wp, u_int x, u_int y,
683
    u_int sx, const struct grid_cell *gc)
684
0
{
685
0
  struct session  *s = data->session;
686
0
  struct winlink  *wl = s->curw;
687
0
  const char  *format;
688
0
  char    *expanded;
689
690
0
  if (sx == 0)
691
0
    return;
692
0
  format = options_get_string(data->wp->window->options,
693
0
      "display-panes-format");
694
0
  if (*format == '\0')
695
0
    return;
696
697
0
  window_panes_get_source(data, &s, &wl, NULL);
698
0
  if (s == NULL)
699
0
    return;
700
701
0
  expanded = format_single(NULL, format, NULL, s, wl, wp);
702
0
  if (*expanded != '\0') {
703
0
    screen_write_cursormove(ctx, x, y, 0);
704
0
    format_draw(ctx, gc, sx, expanded, NULL, 0);
705
0
  }
706
0
  free(expanded);
707
0
}
708
709
static void
710
window_panes_draw_number(struct window_panes_modedata *data,
711
    struct screen_write_ctx *ctx, struct window_pane *wp, u_int pane, u_int x,
712
    u_int y, u_int sx, u_int sy)
713
0
{
714
0
  struct session    *s = data->session;
715
0
  struct window   *w = wp->window;
716
0
  struct winlink    *wl = s->curw;
717
0
  struct options    *oo = data->wp->window->options;
718
0
  struct grid_cell   fgc, bgc;
719
0
  struct format_tree  *ft;
720
0
  const char    *name;
721
0
  char       buf[16], lbuf[16] = { 0 }, *ptr;
722
0
  size_t       len, llen = 0, width;
723
0
  u_int      cx, cy, px, py, idx, i, j, format;
724
725
0
  len = xsnprintf(buf, sizeof buf, "%u", pane);
726
0
  if (pane > 9 && pane < 35)
727
0
    llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10));
728
0
  if (sx < len)
729
0
    return;
730
731
0
  window_panes_get_source(data, &s, &wl, NULL);
732
0
  if (s != NULL) {
733
0
    if (wl == NULL)
734
0
      wl = s->curw;
735
0
  }
736
0
  if (w->active == wp)
737
0
    name = "display-panes-active-colour";
738
0
  else
739
0
    name = "display-panes-colour";
740
0
  ft = format_create_defaults(NULL, NULL, s, wl, wp);
741
0
  style_apply(&fgc, oo, name, ft);
742
0
  format_free(ft);
743
744
0
  memcpy(&bgc, &grid_default_cell, sizeof bgc);
745
0
  bgc.bg = fgc.fg;
746
747
0
  format = 0;
748
0
  if (options_get_string(oo, "display-panes-format")[0] != '\0')
749
0
    format = 1;
750
0
  width = (len * 6) - 1;
751
0
  if (sx < width || sy < (format ? 7 : 5)) {
752
0
    width = len;
753
0
    if (llen != 0 && sx >= len + llen + 1)
754
0
      width += llen + 1;
755
0
    cx = x + (sx - width) / 2;
756
0
    cy = y + sy / 2;
757
0
    screen_write_cursormove(ctx, cx, cy, 0);
758
0
    screen_write_puts(ctx, &fgc, "%s", buf);
759
0
    if (width > len)
760
0
      screen_write_puts(ctx, &fgc, " %s", lbuf);
761
0
    if (format && sy > 1)
762
0
      window_panes_draw_format(data, ctx, wp, x, y, sx, &fgc);
763
0
    return;
764
0
  }
765
766
0
  px = (sx - width) / 2;
767
0
  py = (sy - 5) / 2;
768
0
  for (ptr = buf; *ptr != '\0'; ptr++) {
769
0
    if (*ptr < '0' || *ptr > '9')
770
0
      continue;
771
0
    idx = *ptr - '0';
772
773
0
    for (j = 0; j < 5; j++) {
774
0
      for (i = 0; i < 5; i++) {
775
0
        if (!window_clock_table[idx][j][i])
776
0
          continue;
777
0
        screen_write_cursormove(ctx, x + px + i,
778
0
            y + py + j, 0);
779
0
        screen_write_putc(ctx, &bgc, ' ');
780
0
      }
781
0
    }
782
0
    px += 6;
783
0
  }
784
785
0
  if (sy <= 6)
786
0
    return;
787
0
  window_panes_draw_format(data, ctx, wp, x, y, sx, &fgc);
788
0
  if (llen != 0) {
789
0
    cx = x + px - llen - 1;
790
0
    cy = y + py + 5;
791
0
    screen_write_cursormove(ctx, cx, cy, 0);
792
0
    screen_write_puts(ctx, &fgc, "%s", lbuf);
793
0
  }
794
0
}
795
796
static void
797
window_panes_draw_pane(struct window_panes_modedata *data,
798
    struct screen_write_ctx *ctx, struct window_pane *wp,
799
    struct layout_cell *root, u_int osx, u_int osy, u_int dsx, u_int dsy)
800
0
{
801
0
  struct screen   *s = &wp->base;
802
0
  u_int      pane, x, y, sx, sy;
803
804
0
  if (!window_panes_pane_visible(wp))
805
0
    return;
806
0
  if (!window_panes_get_geometry(wp, root, osx, osy, dsx, dsy, &x, &y,
807
0
      &sx, &sy))
808
0
    return;
809
0
  if (!window_panes_clip_floating_pane(wp, osx, osy, dsx, dsy, &x, &y,
810
0
      &sx, &sy))
811
0
    return;
812
0
  if (window_pane_index(wp, &pane) != 0)
813
0
    return;
814
0
  window_panes_add_area(data, wp, x, y, sx, sy);
815
816
0
  screen_write_cursormove(ctx, x, y, 0);
817
0
  if (data->preview != NULL &&
818
0
      wp == data->wp &&
819
0
      sx <= screen_size_x(data->preview) &&
820
0
      sy <= screen_size_y(data->preview))
821
0
    s = data->preview;
822
0
  if (osx <= dsx && osy <= dsy)
823
0
    screen_write_fast_copy(ctx, s, 0, s->grid->hsize, sx, sy);
824
0
  else
825
0
    screen_write_preview(ctx, s, sx, sy);
826
0
  window_panes_draw_number(data, ctx, wp, pane, x, y, sx, sy);
827
0
}
828
829
static void
830
window_panes_draw_screen(struct window_mode_entry *wme)
831
0
{
832
0
  struct window_panes_modedata  *data = wme->data;
833
0
  struct window     *w;
834
0
  struct window_pane    *wp;
835
0
  struct screen_write_ctx    ctx;
836
0
  struct layout_cell    *root;
837
0
  struct grid_cell     border_gc;
838
0
  u_int        osx, osy, sx, sy;
839
840
0
  if (!window_panes_get_source(data, NULL, NULL, &w))
841
0
    return;
842
0
  root = w->saved_layout_root;
843
0
  if (root == NULL)
844
0
    root = w->layout_root;
845
0
  if (root == NULL)
846
0
    return;
847
848
0
  osx = root->g.sx;
849
0
  osy = root->g.sy;
850
0
  sx = screen_size_x(&data->screen);
851
0
  sy = screen_size_y(&data->screen);
852
853
0
  window_panes_free_areas(data);
854
0
  screen_write_start(&ctx, &data->screen);
855
0
  screen_write_clearscreen(&ctx, 8);
856
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
857
0
    if (window_panes_pane_floating(wp))
858
0
      continue;
859
0
    window_panes_draw_pane(data, &ctx, wp, root, osx, osy, sx, sy);
860
0
  }
861
0
  window_panes_get_border_cell(data, &border_gc);
862
0
  window_panes_draw_borders(&ctx, w, root, &border_gc, osx, osy, sx, sy);
863
0
  TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry) {
864
0
    if (!window_panes_pane_floating(wp))
865
0
      continue;
866
0
    window_panes_clear_floating_area(&ctx, wp, osx, osy, sx, sy);
867
0
    window_panes_draw_pane(data, &ctx, wp, root, osx, osy, sx, sy);
868
0
    window_panes_draw_floating_border(&ctx, wp, &border_gc, osx,
869
0
        osy, sx, sy);
870
0
  }
871
0
  screen_write_stop(&ctx);
872
873
0
  data->wp->flags |= PANE_REDRAW;
874
0
}
875
876
static void
877
window_panes_timer_callback(__unused int fd, __unused short events, void *arg)
878
0
{
879
0
  struct window_mode_entry  *wme = arg;
880
881
0
  window_pane_reset_mode(wme->wp);
882
0
}
883
884
static struct screen *
885
window_panes_init(struct window_mode_entry *wme, struct cmdq_item *item,
886
    __unused struct cmd_find_state *fs, struct args *args)
887
0
{
888
0
  struct window_pane    *wp = wme->wp;
889
0
  struct window     *w = wp->window;
890
0
  struct window_panes_modedata  *data;
891
0
  struct cmd      *self;
892
0
  struct cmd_find_state   *source, *target;
893
0
  struct session      *s;
894
0
  struct timeval       tv;
895
0
  char        *cause;
896
0
  u_int        sx = screen_size_x(&wp->base);
897
0
  u_int        sy = screen_size_y(&wp->base);
898
0
  u_int        delay;
899
900
0
  if (item == NULL)
901
0
    return (NULL);
902
0
  self = cmdq_get_cmd(item);
903
0
  if (self == NULL)
904
0
    return (NULL);
905
906
0
  source = cmdq_get_source(item);
907
0
  target = cmdq_get_target(item);
908
0
  s = target->s;
909
910
0
  if (!args_has(args, 'd'))
911
0
    delay = options_get_number(w->options, "display-panes-time");
912
0
  else {
913
0
    delay = args_strtonum(args, 'd', 0, UINT_MAX, &cause);
914
0
    if (cause != NULL) {
915
0
      cmdq_error(item, "delay %s", cause);
916
0
      free(cause);
917
0
      return (NULL);
918
0
    }
919
0
  }
920
921
0
  wme->data = data = xcalloc(1, sizeof *data);
922
0
  data->wp = wp;
923
0
  data->session = s;
924
925
0
  screen_init(&data->screen, sx, sy, 0);
926
0
  data->screen.mode &= ~MODE_CURSOR;
927
928
0
  data->state = args_make_commands_prepare(self, item, 0,
929
0
      "select-pane -t \"%%%\"", 0, 0);
930
0
  if (args_has(args, 's')) {
931
0
    data->source_session = source->s->id;
932
0
    data->source_window = source->w->id;
933
0
  } else {
934
0
    data->source_session = target->s->id;
935
0
    data->source_window = target->w->id;
936
0
  }
937
938
0
  data->delay = delay;
939
0
  data->ignore_keys = args_has(args, 'N');
940
941
0
  if (args_has(args, 'Z'))
942
0
    data->zoomed = -1;
943
0
  else {
944
0
    data->zoomed = (w->flags & WINDOW_ZOOMED);
945
0
    if (!data->zoomed)
946
0
      window_panes_set_preview(data);
947
0
    if (!data->zoomed && window_zoom(wp) == 0)
948
0
      server_redraw_window(w);
949
0
  }
950
951
0
  evtimer_set(&data->timer, window_panes_timer_callback, wme);
952
0
  if (data->delay != 0) {
953
0
    tv.tv_sec = data->delay / 1000;
954
0
    tv.tv_usec = (data->delay % 1000) * 1000;
955
0
    evtimer_add(&data->timer, &tv);
956
0
  }
957
958
0
  window_panes_draw_screen(wme);
959
0
  return (&data->screen);
960
0
}
961
962
static void
963
window_panes_free(struct window_mode_entry *wme)
964
0
{
965
0
  struct window_panes_modedata  *data = wme->data;
966
0
  struct window     *w = wme->wp->window;
967
968
0
  evtimer_del(&data->timer);
969
970
0
  if (data->zoomed == 0)
971
0
    server_unzoom_window(w);
972
0
  server_redraw_window(w);
973
0
  server_redraw_window_borders(w);
974
0
  server_status_window(w);
975
976
0
  if (data->state != NULL)
977
0
    args_make_commands_free(data->state);
978
0
  window_panes_free_areas(data);
979
0
  if (data->preview != NULL) {
980
0
    screen_free(data->preview);
981
0
    free(data->preview);
982
0
  }
983
0
  screen_free(&data->screen);
984
0
  free(data);
985
0
}
986
987
static void
988
window_panes_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
989
0
{
990
0
  struct window_panes_modedata  *data = wme->data;
991
992
0
  screen_resize(&data->screen, sx, sy, 0);
993
0
  window_panes_draw_screen(wme);
994
0
}
995
996
static void
997
window_panes_run_command(struct window_panes_modedata *data, struct client *c,
998
  struct window_pane *wp)
999
0
{
1000
0
  struct cmdq_item  *new_item;
1001
0
  struct cmd_list   *cmdlist;
1002
0
  char      *expanded, *error;
1003
1004
0
  xasprintf(&expanded, "%%%u", wp->id);
1005
0
  cmdlist = args_make_commands(data->state, 1, &expanded, &error);
1006
0
  if (cmdlist == NULL) {
1007
0
    cmdq_append(c, cmdq_get_error(error));
1008
0
    free(error);
1009
0
  } else {
1010
0
    new_item = cmdq_get_command(cmdlist, NULL);
1011
0
    cmdq_append(c, new_item);
1012
0
  }
1013
0
  free(expanded);
1014
0
}
1015
1016
static struct window_pane *
1017
window_panes_find_pane(struct window_panes_modedata *data, u_int x, u_int y)
1018
0
{
1019
0
  struct window_panes_area  *area;
1020
0
  u_int      i;
1021
1022
0
  for (i = data->areas_size; i > 0; i--) {
1023
0
    area = &data->areas[i - 1];
1024
0
    if (x < area->x || x >= area->x + area->sx)
1025
0
      continue;
1026
0
    if (y < area->y || y >= area->y + area->sy)
1027
0
      continue;
1028
0
    return (window_pane_find_by_id(area->id));
1029
0
  }
1030
0
  return (NULL);
1031
0
}
1032
1033
static struct window_pane *
1034
window_panes_key_pane(struct window_panes_modedata *data, key_code key)
1035
0
{
1036
0
  struct window   *w;
1037
0
  u_int      index;
1038
1039
0
  if (key >= '0' && key <= '9')
1040
0
    index = key - '0';
1041
0
  else if ((key & KEYC_MASK_MODIFIERS) == 0) {
1042
0
    key &= KEYC_MASK_KEY;
1043
0
    if (key < 'a' || key > 'z')
1044
0
      return (NULL);
1045
0
    index = 10 + (key - 'a');
1046
0
  } else
1047
0
    return (NULL);
1048
0
  if (!window_panes_get_source(data, NULL, NULL, &w))
1049
0
    return (NULL);
1050
0
  return (window_pane_at_index(w, index));
1051
0
}
1052
1053
static struct window_pane *
1054
window_panes_get_target(struct window_mode_entry *wme, key_code key,
1055
    struct mouse_event *m)
1056
0
{
1057
0
  struct window_panes_modedata  *data = wme->data;
1058
0
  u_int        x, y;
1059
1060
0
  if (data->ignore_keys)
1061
0
    return (NULL);
1062
1063
0
  if (KEYC_IS_MOUSE(key)) {
1064
0
    if (key != KEYC_MOUSEDOWN1_PANE ||
1065
0
        m == NULL ||
1066
0
        cmd_mouse_at(wme->wp, m, &x, &y, 0) != 0)
1067
0
      return (NULL);
1068
0
    return (window_panes_find_pane(data, x, y));
1069
0
  }
1070
1071
0
  return (window_panes_key_pane(data, key));
1072
0
}
1073
1074
static void
1075
window_panes_key(struct window_mode_entry *wme, struct client *c,
1076
    __unused struct session *s, __unused struct winlink *wl, key_code key,
1077
    struct mouse_event *m)
1078
0
{
1079
0
  struct window_pane    *wp = wme->wp, *target = NULL;
1080
0
  struct window_panes_modedata  *data = wme->data;
1081
1082
0
  if (key == '\033' || key == 'q') {
1083
0
    window_pane_reset_mode(wp);
1084
0
    return;
1085
0
  }
1086
1087
0
  target = window_panes_get_target(wme, key, m);
1088
0
  if (target == NULL) {
1089
0
    if (!data->ignore_keys && !KEYC_IS_MOUSE(key))
1090
0
      window_pane_reset_mode(wp);
1091
0
    return;
1092
0
  }
1093
1094
0
  if (wp->window->flags & WINDOW_ZOOMED)
1095
0
    window_unzoom(wp->window, 1);
1096
0
  window_panes_run_command(data, c, target);
1097
0
  window_pane_reset_mode(wp);
1098
0
}