Coverage Report

Created: 2026-08-31 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/screen-redraw.c
Line
Count
Source
1
/* $OpenBSD: screen-redraw.c,v 1.157 2026/07/24 08:49:23 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
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
21
#include <stdint.h>
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include "tmux.h"
26
27
/*
28
 * Draw the visible area of a window to a client.
29
 *
30
 * A scene is built for the client and cached (in struct client). When the
31
 * offset or size of the visible part of the window changes, the scene is
32
 * invalidated. It is also invalidated when the generation number is increased;
33
 * this is done at various points, such as when a pane is moved or resized. The
34
 * scene only includes the part of the client used for the window: panes, pane
35
 * status lines, borders, scrollbars, and any area outside the window. The
36
 * client status line and overlay are not included.
37
 *
38
 * A scene is made from spans. A span is a horizontal run of cells on one
39
 * visible line that can be drawn in the same way. Each span has a type, for
40
 * example: pane content or pane border cells or pane status line. A span also
41
 * includes enough additional data to draw it, but does not include items such
42
 * as the style and content - those are determined when it is drawn.
43
 *
44
 * Scenes are built in two stages:
45
 *
46
 * 1) redraw_build_cells fills a temporary grid of struct redraw_build_cell
47
 *    objects, one per visible cell. It marks pane contents, scrollbars,
48
 *    borders, pane status lines and any cells outside the window. Border cells
49
 *    may belong to multiple panes, so they may be marked multiple times, with
50
 *    each pane adding its own state.
51
 *
52
 * 2) redraw_make_scene takes the grid of struct redraw_build_cell objects and
53
 *    converts them into spans by joining adjacent cells that have the same
54
 *    type and data. These spans together make up the scene (struct
55
 *    redraw_scene).
56
 *
57
 * Once generated, a scene is redrawn by looping over some or all of the spans
58
 * (in redraw_draw), working out the style and content, and writing to the
59
 * client terminal. Until it is invalidated, the scene may be redrawn multiple
60
 * times without being rebuilt.
61
 */
62
63
/* Type of span in the scene. */
64
enum redraw_span_type {
65
  REDRAW_SPAN_PANE, /* inside a pane */
66
  REDRAW_SPAN_OUTSIDE,  /* outside the window */
67
  REDRAW_SPAN_EMPTY,  /* inside the window but nothing visible */
68
  REDRAW_SPAN_STATUS, /* pane status line */
69
  REDRAW_SPAN_BORDER, /* pane border */
70
  REDRAW_SPAN_SCROLLBAR,  /* pane scrollbar */
71
  REDRAW_SPAN_MENU, /* window menu */
72
};
73
0
#define REDRAW_SPAN_TYPES 7
74
75
/* Border connections to adjacent cells. */
76
0
#define REDRAW_BORDER_L 0x1
77
0
#define REDRAW_BORDER_R 0x2
78
0
#define REDRAW_BORDER_U 0x4
79
0
#define REDRAW_BORDER_D 0x8
80
81
/* Span flags. */
82
0
#define REDRAW_BORDER_IS_ARROW 0x1
83
0
#define REDRAW_SCROLLBAR_LEFT 0x2
84
0
#define REDRAW_SCROLLBAR_RIGHT 0x4
85
0
#define REDRAW_SCROLLBAR_OVERLAY 0x8
86
87
/* Draw operations. */
88
0
#define REDRAW_PANE 0x1
89
0
#define REDRAW_OUTSIDE 0x2
90
0
#define REDRAW_EMPTY 0x4
91
0
#define REDRAW_PANE_BORDER 0x8
92
0
#define REDRAW_PANE_STATUS 0x10
93
0
#define REDRAW_PANE_SCROLLBAR 0x20
94
0
#define REDRAW_STATUS 0x40
95
0
#define REDRAW_MENU 0x80
96
0
#define REDRAW_OVERLAY 0x100
97
98
/* Draw everything. */
99
0
#define REDRAW_ALL 0x7fffffff
100
0
#define REDRAW_IS_ALL(flags) ((flags) == REDRAW_ALL)
101
102
/* UTF-8 isolate characters. */
103
0
#define REDRAW_START_ISOLATE "\342\201\246"
104
0
#define REDRAW_END_ISOLATE "\342\201\251"
105
106
/* Data for a span. */
107
struct redraw_span_data {
108
  enum redraw_span_type type;
109
110
  union {
111
    struct {
112
      /* Pane this span belongs to. */
113
      struct window_pane  *wp;
114
115
      /* Position of span inside the pane. */
116
      u_int      px;
117
      u_int      py;
118
    } p; /* pane */
119
120
    struct {
121
      /* Adjacent panes on each side. */
122
      struct window_pane  *top_wp;
123
      struct window_pane  *bottom_wp;
124
      struct window_pane  *left_wp;
125
      struct window_pane  *right_wp;
126
127
      /*
128
       * The pane this span belongs if that is known when the
129
       * scene is built. This is used for the half coloured
130
       * active pane indicator.
131
       */
132
      struct window_pane  *style_wp;
133
134
      /* Cell type for this span. */
135
      int      cell_type;
136
      int      cell_mask;
137
138
      /* Line styles for this span. */
139
      enum pane_lines    top_lines;
140
      enum pane_lines    bottom_lines;
141
      enum pane_lines    left_lines;
142
      enum pane_lines    right_lines;
143
144
      /* Flags for this span. */
145
      int      flags;
146
    } b; /* pane border */
147
148
    struct {
149
      /* The pane and the offset into the status line. */
150
      struct window_pane  *wp;
151
      u_int      offset;
152
      int      cell_type;
153
    } st; /* pane status line */
154
155
    struct {
156
      /* Pane this span belongs to. */
157
      struct window_pane  *wp;
158
159
      /* Line within the scrollbar. */
160
      u_int      y;
161
162
      /* Full height of scrollbar. */
163
      u_int      height;
164
165
      /* Flags for this span. */
166
      int      flags;
167
    } sb; /* pane scrollbar */
168
169
    struct {
170
      /* Menu this span belongs to. */
171
      struct menu_data  *md;
172
173
      /* Position of span inside the menu. */
174
      u_int      px;
175
      u_int      py;
176
    } m; /* menu */
177
  };
178
};
179
180
/* A span of cells of the same type inside a line. */
181
struct redraw_span {
182
  u_int       x;
183
  u_int       width;
184
  struct redraw_span_data   data;
185
186
  TAILQ_ENTRY(redraw_span)  entry;
187
};
188
TAILQ_HEAD(redraw_spans, redraw_span);
189
190
/* A visible line on the client. */
191
struct redraw_line {
192
  struct redraw_spans spans[REDRAW_SPAN_TYPES];
193
};
194
195
/* A scene representing all the spans on the client. */
196
struct redraw_scene {
197
  struct client   *c;
198
  struct window   *w;
199
  struct redraw_line  *lines;
200
201
  uint64_t     generation;
202
  u_int      sx;
203
  u_int      sy;
204
  u_int      ox;
205
  u_int      oy;
206
};
207
208
/* Cell for building the scene. */
209
struct redraw_build_cell {
210
  struct redraw_span_data  data;
211
};
212
213
static struct redraw_build_cell *redraw_cells;
214
static size_t      redraw_ncells;
215
216
/* Context for building the scene. */
217
struct redraw_build_ctx {
218
  struct client       *c;
219
  struct window       *w;
220
221
  u_int          ox;
222
  u_int          oy;
223
  u_int          sx;
224
  u_int          sy;
225
226
  int          ind;
227
228
  struct redraw_build_cell    *cells;
229
};
230
231
/* Context for redrawing. */
232
struct redraw_draw_ctx {
233
  struct redraw_scene *scene;
234
235
  struct window_pane  *active;
236
  struct window_pane  *marked;
237
238
  u_int      status_lines;
239
  enum pane_lines    pane_lines;
240
  struct grid_cell   default_gc;
241
242
  int      flags;
243
0
#define REDRAW_ISOLATES 0x1
244
0
#define REDRAW_DEFAULT_SET 0x2
245
0
#define REDRAW_STATUS_TOP 0x4
246
};
247
248
/* Make redraw flags into a string. */
249
static const char *
250
redraw_flags_to_string(int flags)
251
0
{
252
0
  static char s[128];
253
254
0
  *s = '\0';
255
0
  if (flags & REDRAW_STATUS)
256
0
    strlcat(s, "status ", sizeof s);
257
0
  if (flags & REDRAW_PANE)
258
0
    strlcat(s, "pane ", sizeof s);
259
0
  if (flags & REDRAW_PANE_BORDER)
260
0
    strlcat(s, "border ", sizeof s);
261
0
  if (flags & REDRAW_PANE_STATUS)
262
0
    strlcat(s, "pane-status ", sizeof s);
263
0
  if (flags & REDRAW_PANE_SCROLLBAR)
264
0
    strlcat(s, "scrollbar ", sizeof s);
265
0
  if (flags & REDRAW_MENU)
266
0
    strlcat(s, "menu ", sizeof s);
267
0
  if (flags & REDRAW_OVERLAY)
268
0
    strlcat(s, "overlay ", sizeof s);
269
0
  if (REDRAW_IS_ALL(flags))
270
0
    strlcat(s, "all ", sizeof s);
271
0
  if (*s != '\0')
272
0
    s[strlen(s) - 1] = '\0';
273
0
  return (s);
274
0
}
275
276
/* Get window offset and expand size to cover any part outside the window. */
277
static void
278
redraw_get_window_offset(struct client *c, u_int *ox, u_int *oy, u_int *sx,
279
    u_int *sy)
280
0
{
281
0
  u_int tty_sx, tty_sy;
282
283
0
  tty_window_offset(&c->tty, ox, oy, sx, sy);
284
285
0
  tty_sx = c->tty.sx;
286
0
  tty_sy = c->tty.sy - status_line_size(c);
287
0
  if (*sx < tty_sx)
288
0
    *sx = tty_sx;
289
0
  if (*sy < tty_sy)
290
0
    *sy = tty_sy;
291
0
}
292
293
/* Initialize the context for building scene. */
294
static void
295
redraw_set_context(struct client *c, struct redraw_build_ctx *bctx)
296
0
{
297
0
  struct session  *s = c->session;
298
0
  struct window *w = s->curw->window;
299
300
0
  memset(bctx, 0, sizeof *bctx);
301
0
  bctx->c = c;
302
0
  bctx->w = w;
303
0
  redraw_get_window_offset(c, &bctx->ox, &bctx->oy, &bctx->sx, &bctx->sy);
304
305
0
  bctx->ind = options_get_number(w->options, "pane-border-indicators");
306
0
}
307
308
/* Return a cell. */
309
static struct redraw_build_cell *
310
redraw_get_build_cell(struct redraw_build_ctx *bctx, u_int x, u_int y)
311
0
{
312
0
  return (&bctx->cells[(y * bctx->sx) + x]);
313
0
}
314
315
/* Reset cell to either empty or outside the window. */
316
static void
317
redraw_reset_cell(struct redraw_build_ctx *bctx, u_int x, u_int y)
318
0
{
319
0
  struct redraw_build_cell  *bc = redraw_get_build_cell(bctx, x, y);
320
0
  struct window     *w = bctx->w;
321
322
0
  memset(bc, 0, sizeof *bc);
323
0
  if (bctx->ox + x < w->sx && bctx->oy + y < w->sy)
324
0
    bc->data.type = REDRAW_SPAN_EMPTY;
325
0
  else
326
0
    bc->data.type = REDRAW_SPAN_OUTSIDE;
327
0
}
328
329
/* Convert window position to scene position. Return 0 if outside the scene. */
330
static int
331
redraw_window_to_scene(struct redraw_build_ctx *bctx, int wx, int wy,
332
    u_int *x, u_int *y)
333
0
{
334
0
  int sx, sy;
335
336
0
  if (wx < 0 || wy < 0)
337
0
    return (0);
338
0
  if ((u_int)wx > bctx->w->sx || (u_int)wy > bctx->w->sy)
339
0
    return (0);
340
0
  if (wx < (int)bctx->ox || wy < (int)bctx->oy)
341
0
    return (0);
342
343
0
  sx = wx - (int)bctx->ox;
344
0
  sy = wy - (int)bctx->oy;
345
346
0
  if ((u_int)sx >= bctx->sx || (u_int)sy >= bctx->sy)
347
0
    return (0);
348
0
  *x = sx;
349
0
  *y = sy;
350
0
  return (1);
351
0
}
352
353
/*
354
 * Convert pane position to scene position. Return 0 if outside the scene. A
355
 * floating pane is clipped to the window edge.
356
 */
357
static int
358
redraw_pane_to_scene(struct redraw_build_ctx *bctx, struct window_pane *wp,
359
    int px, int py, u_int *x, u_int *y)
360
0
{
361
0
  int wx = wp->xoff + px, wy = wp->yoff + py;
362
0
  int left, right, top, bottom;
363
364
0
  if (window_pane_is_floating(wp)) {
365
0
    left = wp->xoff - 1;
366
0
    right = wp->xoff + wp->sx;
367
0
    top = wp->yoff - 1;
368
0
    bottom = wp->yoff + wp->sy;
369
370
0
    if (left < 0 && wx < 0)
371
0
      return (0);
372
0
    if (right > (int)bctx->w->sx && wx >= (int)bctx->w->sx)
373
0
      return (0);
374
0
    if (top < 0 && wy < 0)
375
0
      return (0);
376
0
    if (bottom > (int)bctx->w->sy && wy >= (int)bctx->w->sy)
377
0
      return (0);
378
0
  }
379
0
  return (redraw_window_to_scene(bctx, wx, wy, x, y));
380
0
}
381
382
/* Convert redraw border mask to a border cell type. */
383
static int
384
redraw_get_cell_type(int mask)
385
0
{
386
0
  switch (mask) {
387
0
  case REDRAW_BORDER_L|REDRAW_BORDER_R|REDRAW_BORDER_U|REDRAW_BORDER_D:
388
0
    return (CELL_LRUD);
389
0
  case REDRAW_BORDER_L|REDRAW_BORDER_R|REDRAW_BORDER_U:
390
0
    return (CELL_LRU);
391
0
  case REDRAW_BORDER_L|REDRAW_BORDER_R|REDRAW_BORDER_D:
392
0
    return (CELL_LRD);
393
0
  case REDRAW_BORDER_L|REDRAW_BORDER_R:
394
0
  case REDRAW_BORDER_L:
395
0
  case REDRAW_BORDER_R:
396
0
    return (CELL_LR);
397
0
  case REDRAW_BORDER_L|REDRAW_BORDER_U|REDRAW_BORDER_D:
398
0
    return (CELL_ULD);
399
0
  case REDRAW_BORDER_L|REDRAW_BORDER_U:
400
0
    return (CELL_LU);
401
0
  case REDRAW_BORDER_L|REDRAW_BORDER_D:
402
0
    return (CELL_LD);
403
0
  case REDRAW_BORDER_R|REDRAW_BORDER_U|REDRAW_BORDER_D:
404
0
    return (CELL_URD);
405
0
  case REDRAW_BORDER_R|REDRAW_BORDER_U:
406
0
    return (CELL_RU);
407
0
  case REDRAW_BORDER_R|REDRAW_BORDER_D:
408
0
    return (CELL_RD);
409
0
  case REDRAW_BORDER_U|REDRAW_BORDER_D:
410
0
  case REDRAW_BORDER_U:
411
0
  case REDRAW_BORDER_D:
412
0
    return (CELL_UD);
413
0
  }
414
0
  return (CELL_NONE);
415
0
}
416
417
/* Return if there are two panes for the border colour indicator. */
418
static int
419
redraw_check_two_pane_colours(struct window *w, enum layout_type *type)
420
0
{
421
0
  struct window_pane  *wp;
422
0
  u_int      count = 0;
423
424
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
425
0
    if (window_pane_is_floating(wp) || wp->layout_cell == NULL)
426
0
      continue;
427
0
    count++;
428
0
    if (count > 2 || wp->layout_cell->parent == NULL)
429
0
      return (0);
430
0
    *type = wp->layout_cell->parent->type;
431
0
  }
432
0
  return (count == 2);
433
0
}
434
435
/* Mark pane inside data. */
436
static void
437
redraw_mark_pane_inside(struct redraw_build_ctx *bctx, struct window_pane *wp)
438
0
{
439
0
  struct redraw_build_cell  *bc;
440
0
  u_int        px, py, x, y;
441
442
0
  for (py = 0; py < wp->sy; py++) {
443
0
    for (px = 0; px < wp->sx; px++) {
444
0
      if (!redraw_pane_to_scene(bctx, wp, px, py, &x, &y))
445
0
        continue;
446
0
      bc = redraw_get_build_cell(bctx, x, y);
447
0
      memset(bc, 0, sizeof *bc);
448
0
      bc->data.type = REDRAW_SPAN_PANE;
449
0
      bc->data.p.wp = wp;
450
0
      bc->data.p.px = px;
451
0
      bc->data.p.py = py;
452
0
    }
453
0
  }
454
0
}
455
456
/* Mark scrollbar data. */
457
static void
458
redraw_mark_pane_scrollbar(struct redraw_build_ctx *bctx,
459
    struct window_pane *wp, int sb_w, int sb_left, int overlay)
460
0
{
461
0
  struct redraw_build_cell  *bc;
462
0
  u_int        x, y;
463
0
  int        wx, wy, sx, ex;
464
0
  u_int        sy;
465
466
0
  if (sb_w == 0)
467
0
    return;
468
469
0
  if (overlay && sb_left) {
470
0
    sx = wp->xoff;
471
0
    ex = sx + sb_w - 1;
472
0
  } else if (overlay) {
473
0
    ex = wp->xoff + (int)wp->sx - 1;
474
0
    sx = ex - sb_w + 1;
475
0
  } else if (sb_left) {
476
0
    sx = wp->xoff - sb_w;
477
0
    ex = wp->xoff - 1;
478
0
  } else {
479
0
    sx = wp->xoff + (int)wp->sx;
480
0
    ex = sx + sb_w - 1;
481
0
  }
482
483
0
  for (sy = 0; sy < wp->sy; sy++) {
484
0
    wy = wp->yoff + (int)sy;
485
0
    for (wx = sx; wx <= ex; wx++) {
486
0
      if (!redraw_window_to_scene(bctx, wx, wy, &x,
487
0
          &y))
488
0
        continue;
489
0
      bc = redraw_get_build_cell(bctx, x, y);
490
0
      memset(bc, 0, sizeof *bc);
491
0
      bc->data.type = REDRAW_SPAN_SCROLLBAR;
492
0
      bc->data.sb.wp = wp;
493
0
      bc->data.sb.y = sy;
494
0
      bc->data.sb.height = wp->sy;
495
0
      if (sb_left)
496
0
        bc->data.sb.flags |= REDRAW_SCROLLBAR_LEFT;
497
0
      else
498
0
        bc->data.sb.flags |= REDRAW_SCROLLBAR_RIGHT;
499
0
      if (overlay)
500
0
        bc->data.sb.flags |= REDRAW_SCROLLBAR_OVERLAY;
501
0
    }
502
0
  }
503
0
}
504
505
/*
506
 * Return if span data belongs to pane, that is: is the cell adjacent to this
507
 * pane?
508
 */
509
static int
510
redraw_data_has_pane(struct redraw_span_data *data, struct window_pane *wp)
511
0
{
512
0
  if (data->b.top_wp == wp)
513
0
    return (1);
514
0
  if (data->b.bottom_wp == wp)
515
0
    return (1);
516
0
  if (data->b.left_wp == wp)
517
0
    return (1);
518
0
  if (data->b.right_wp == wp)
519
0
    return (1);
520
0
  return (0);
521
0
}
522
523
/*
524
 * Mark one border cell. If a non-border cell is marked as a border, replace
525
 * it. If it is already a border and this is not a floating pane, merge the
526
 * border mask and pane ownership.
527
 */
528
static void
529
redraw_mark_border_cell(struct redraw_build_ctx *bctx, int wx, int wy,
530
    struct window_pane *wp, int top_owner, int bottom_owner, int mask,
531
    enum pane_lines pane_lines, int floating)
532
0
{
533
0
  struct redraw_build_cell  *bc;
534
0
  u_int        x, y;
535
0
  int        reset = 0;
536
537
0
  if (!redraw_window_to_scene(bctx, wx, wy, &x, &y))
538
0
    return;
539
0
  bc = redraw_get_build_cell(bctx, x, y);
540
541
  /*
542
   * If this is a tiled pane, only empty and border cells may be marked.
543
   * Border cells are merged and empty cells are updated.
544
   *
545
   * Floating panes may mark any existing cell type. All cells are reset
546
   * except borders that already belong to this pane, they need to be
547
   * merged.
548
   */
549
0
  if (!floating) {
550
0
    if (bc->data.type == REDRAW_SPAN_EMPTY ||
551
0
        bc->data.type == REDRAW_SPAN_OUTSIDE)
552
0
      reset = 1;
553
0
    else if (bc->data.type != REDRAW_SPAN_BORDER)
554
0
      return;
555
0
  } else {
556
0
    if (bc->data.type != REDRAW_SPAN_BORDER ||
557
0
        !redraw_data_has_pane(&bc->data, wp))
558
0
      reset = 1;
559
0
  }
560
0
  if (reset) {
561
0
    memset(bc, 0, sizeof *bc);
562
0
    bc->data.type = REDRAW_SPAN_BORDER;
563
0
  }
564
565
0
  if (top_owner) {
566
0
    bc->data.b.top_wp = wp;
567
0
    bc->data.b.top_lines = pane_lines;
568
0
  }
569
0
  if (bottom_owner) {
570
0
    bc->data.b.bottom_wp = wp;
571
0
    bc->data.b.bottom_lines = pane_lines;
572
0
  }
573
574
0
  if (mask & (REDRAW_BORDER_U|REDRAW_BORDER_D)) {
575
0
    if (wx < wp->xoff) {
576
0
      bc->data.b.right_wp = wp;
577
0
      bc->data.b.right_lines = pane_lines;
578
0
    } else if (wx >= wp->xoff + (int)wp->sx) {
579
0
      bc->data.b.left_wp = wp;
580
0
      bc->data.b.left_lines = pane_lines;
581
0
    }
582
0
  }
583
584
0
  mask |= bc->data.b.cell_mask;
585
0
  bc->data.b.cell_mask = mask;
586
0
  bc->data.b.cell_type = redraw_get_cell_type(mask);
587
0
}
588
589
/*
590
 * Mark border cells for a pane status line, keeping the border cell type for
591
 * drawing.
592
 */
593
static void
594
redraw_mark_border_status(struct redraw_build_ctx *bctx, struct window_pane *wp,
595
    __unused int left, int right, int top, int bottom)
596
0
{
597
0
  struct redraw_build_cell  *bc;
598
0
  u_int        x, y, off = 0;
599
0
  int        pane_status, wy, sx, ex, wx, cell_type;
600
601
0
  pane_status = window_pane_get_pane_status(wp);
602
0
  if (pane_status == PANE_STATUS_OFF)
603
0
    return;
604
0
  if (pane_status == PANE_STATUS_TOP)
605
0
    wy = top;
606
0
  else
607
0
    wy = bottom;
608
609
0
  sx = wp->xoff + 2;
610
0
  ex = right - 1;
611
0
  if (sx > ex)
612
0
    return;
613
614
0
  for (wx = sx; wx <= ex; wx++, off++) {
615
0
    if (!redraw_window_to_scene(bctx, wx, wy, &x, &y))
616
0
      continue;
617
0
    bc = redraw_get_build_cell(bctx, x, y);
618
0
    if (bc->data.type != REDRAW_SPAN_BORDER)
619
0
      continue;
620
0
    cell_type = bc->data.b.cell_type;
621
622
0
    bc->data.type = REDRAW_SPAN_STATUS;
623
0
    bc->data.st.wp = wp;
624
0
    bc->data.st.offset = off;
625
0
    bc->data.st.cell_type = cell_type;
626
0
  }
627
0
}
628
629
/* Mark existing border cells where indicator arrows will be drawn. */
630
static void
631
redraw_mark_border_arrows(struct redraw_build_ctx *bctx, struct window_pane *wp,
632
    int left, int right, int top, int bottom)
633
0
{
634
0
  struct redraw_build_cell  *bc;
635
0
  u_int        x, y;
636
0
  int        wx, wy;
637
638
0
  if (bctx->ind != PANE_BORDER_ARROWS && bctx->ind != PANE_BORDER_BOTH)
639
0
    return;
640
641
0
  wx = wp->xoff + 1;
642
0
  if (wx >= left && wx <= right) {
643
0
    wy = top;
644
0
    if (redraw_window_to_scene(bctx, wx, wy, &x, &y)) {
645
0
      bc = redraw_get_build_cell(bctx, x, y);
646
0
      if (bc->data.type == REDRAW_SPAN_BORDER)
647
0
        bc->data.b.flags |= REDRAW_BORDER_IS_ARROW;
648
0
    }
649
0
    wy = bottom;
650
0
    if (redraw_window_to_scene(bctx, wx, wy, &x, &y)) {
651
0
      bc = redraw_get_build_cell(bctx, x, y);
652
0
      if (bc->data.type == REDRAW_SPAN_BORDER)
653
0
        bc->data.b.flags |= REDRAW_BORDER_IS_ARROW;
654
0
    }
655
0
  }
656
657
0
  wy = wp->yoff + 1;
658
0
  if (wy >= top && wy <= bottom) {
659
0
    wx = left;
660
0
    if (redraw_window_to_scene(bctx, wx, wy, &x, &y)) {
661
0
      bc = redraw_get_build_cell(bctx, x, y);
662
0
      if (bc->data.type == REDRAW_SPAN_BORDER)
663
0
        bc->data.b.flags |= REDRAW_BORDER_IS_ARROW;
664
0
    }
665
0
    wx = right;
666
0
    if (redraw_window_to_scene(bctx, wx, wy, &x, &y)) {
667
0
      bc = redraw_get_build_cell(bctx, x, y);
668
0
      if (bc->data.type == REDRAW_SPAN_BORDER)
669
0
        bc->data.b.flags |= REDRAW_BORDER_IS_ARROW;
670
0
    }
671
0
  }
672
0
}
673
674
/* Mark pane borders. */
675
static void
676
redraw_mark_pane_borders(struct redraw_build_ctx *bctx, struct window_pane *wp,
677
    int sb_w, int sb_left)
678
0
{
679
0
  enum pane_lines pane_lines = window_pane_get_pane_lines(wp);
680
0
  int   pane_status, left, right, top, bottom, wx, wy;
681
0
  int   mark_top, mark_bottom, mark_left, mark_right, mask = 0;
682
0
  int   floating = window_pane_is_floating(wp);
683
684
0
  if (floating && pane_lines == PANE_LINES_NONE)
685
0
    return;
686
0
  pane_status = window_pane_get_pane_status(wp);
687
688
0
  left = wp->xoff - 1;
689
0
  right = wp->xoff + wp->sx;
690
0
  if (sb_w != 0) {
691
0
    if (sb_left)
692
0
      left -= sb_w;
693
0
    else
694
0
      right += sb_w;
695
0
  }
696
0
  top = wp->yoff - 1;
697
0
  bottom = wp->yoff + wp->sy;
698
699
0
  mark_left = (left >= 0);
700
0
  mark_top = (top >= 0);
701
702
0
  if (floating) {
703
0
    mark_right = (right < (int)bctx->w->sx);
704
0
    mark_bottom = (bottom < (int)bctx->w->sy);
705
0
    if (left < 0)
706
0
      left = 0;
707
0
    if (right >= (int)bctx->w->sx)
708
0
      right = (int)bctx->w->sx - 1;
709
0
    if (top < 0)
710
0
      top = 0;
711
0
    if (bottom >= (int)bctx->w->sy)
712
0
      bottom = (int)bctx->w->sy - 1;
713
0
  } else {
714
0
    mark_right = (right <= (int)bctx->w->sx);
715
0
    mark_bottom = (bottom <= (int)bctx->w->sy);
716
0
    if (pane_status == PANE_STATUS_TOP)
717
0
      mark_bottom = 0;
718
0
    else if (pane_status == PANE_STATUS_BOTTOM)
719
0
      mark_top = 0;
720
0
  }
721
722
0
  if (mark_top) {
723
0
    for (wx = left; wx <= right; wx++) {
724
0
      mask = 0;
725
0
      if (wx > left)
726
0
        mask |= REDRAW_BORDER_L;
727
0
      if (wx < right)
728
0
        mask |= REDRAW_BORDER_R;
729
0
      redraw_mark_border_cell(bctx, wx, top, wp, 0, 1, mask,
730
0
          pane_lines, floating);
731
0
    }
732
0
  }
733
0
  if (mark_bottom) {
734
0
    for (wx = left; wx <= right; wx++) {
735
0
      mask = 0;
736
0
      if (wx > left)
737
0
        mask |= REDRAW_BORDER_L;
738
0
      if (wx < right)
739
0
        mask |= REDRAW_BORDER_R;
740
0
      redraw_mark_border_cell(bctx, wx, bottom, wp, 1, 0,
741
0
          mask, pane_lines, floating);
742
0
    }
743
0
  }
744
0
  if (mark_left) {
745
0
    for (wy = top; wy <= bottom; wy++) {
746
0
      mask = 0;
747
0
      if (wy > top)
748
0
        mask |= REDRAW_BORDER_U;
749
0
      if (wy < bottom)
750
0
        mask |= REDRAW_BORDER_D;
751
0
      redraw_mark_border_cell(bctx, left, wy, wp, 0, 0, mask,
752
0
          pane_lines, floating);
753
0
    }
754
0
  }
755
0
  if (mark_right) {
756
0
    for (wy = top; wy <= bottom; wy++) {
757
0
      mask = 0;
758
0
      if (wy > top)
759
0
        mask |= REDRAW_BORDER_U;
760
0
      if (wy < bottom)
761
0
        mask |= REDRAW_BORDER_D;
762
0
      redraw_mark_border_cell(bctx, right, wy, wp, 0, 0, mask,
763
0
          pane_lines, floating);
764
0
    }
765
0
  }
766
767
0
  redraw_mark_border_status(bctx, wp, left, right, top, bottom);
768
0
  redraw_mark_border_arrows(bctx, wp, left, right, top, bottom);
769
0
}
770
771
/*
772
 * Mark an entire pane in the build grid. Floating panes overwrite anything
773
 * already below them.
774
 */
775
static void
776
redraw_mark_pane(struct redraw_build_ctx *bctx, struct window_pane *wp)
777
0
{
778
0
  int sb_w = 0, sb_left = 0, overlay = 0;
779
780
0
  if (!window_pane_is_visible(wp))
781
0
    return;
782
783
0
  if (window_pane_scrollbar_visible(wp)) {
784
0
    overlay = window_pane_scrollbar_overlay(wp);
785
0
    if (overlay) {
786
0
      sb_w = wp->scrollbar_style.width +
787
0
          wp->scrollbar_style.pad;
788
0
      if (sb_w > (int)wp->sx) {
789
0
        sb_w = wp->scrollbar_style.width;
790
0
        if (sb_w > (int)wp->sx)
791
0
          sb_w = wp->sx;
792
0
      }
793
0
    } else
794
0
      sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
795
0
  }
796
0
  if (sb_w != 0 && bctx->w->sb_pos == PANE_SCROLLBARS_LEFT)
797
0
    sb_left = 1;
798
799
0
  redraw_mark_pane_inside(bctx, wp);
800
0
  redraw_mark_pane_borders(bctx, wp, overlay ? 0 : sb_w, sb_left);
801
0
  redraw_mark_pane_scrollbar(bctx, wp, sb_w, sb_left, overlay);
802
0
}
803
804
/* Choose the pane that will provide the border style for two-pane layouts. */
805
static void
806
redraw_mark_two_pane_colours(struct redraw_build_ctx *bctx)
807
0
{
808
0
  struct redraw_build_cell  *bc;
809
0
  struct redraw_span_data   *sd;
810
0
  enum layout_type     type;
811
0
  u_int        x, y, wx, wy;
812
813
0
  if (bctx->ind != PANE_BORDER_COLOUR && bctx->ind != PANE_BORDER_BOTH)
814
0
    return;
815
0
  if (!redraw_check_two_pane_colours(bctx->w, &type))
816
0
    return;
817
818
0
  for (y = 0; y < bctx->sy; y++) {
819
0
    for (x = 0; x < bctx->sx; x++) {
820
0
      bc = redraw_get_build_cell(bctx, x, y);
821
0
      if (bc->data.type != REDRAW_SPAN_BORDER)
822
0
        continue;
823
0
      sd = &bc->data;
824
825
0
      wx = bctx->ox + x;
826
0
      wy = bctx->oy + y;
827
828
0
      if (type == LAYOUT_LEFTRIGHT &&
829
0
          sd->b.left_wp != NULL &&
830
0
          sd->b.right_wp != NULL) {
831
0
        if (wy <= bctx->w->sy / 2)
832
0
          sd->b.style_wp = sd->b.left_wp;
833
0
        else
834
0
          sd->b.style_wp = sd->b.right_wp;
835
0
      } else if (type == LAYOUT_TOPBOTTOM &&
836
0
          sd->b.top_wp != NULL &&
837
0
          sd->b.bottom_wp != NULL) {
838
0
        if (wx <= bctx->w->sx / 2)
839
0
          sd->b.style_wp = sd->b.top_wp;
840
0
        else
841
0
          sd->b.style_wp = sd->b.bottom_wp;
842
0
      }
843
0
    }
844
0
  }
845
0
}
846
847
/* Mark the window menu above all panes. */
848
static void
849
redraw_mark_menu(struct redraw_build_ctx *bctx)
850
0
{
851
0
  struct menu_data    *md = bctx->w->menu;
852
0
  struct redraw_build_cell  *bc;
853
0
  u_int        px, py, x, y, sx, sy;
854
855
0
  if (md == NULL)
856
0
    return;
857
858
0
  sx = menu_width(md);
859
0
  sy = menu_height(md);
860
0
  for (py = 0; py < sy; py++) {
861
0
    for (px = 0; px < sx; px++) {
862
0
      if (!redraw_window_to_scene(bctx, menu_x(md) + px,
863
0
          menu_y(md) + py, &x, &y))
864
0
        continue;
865
0
      bc = redraw_get_build_cell(bctx, x, y);
866
0
      memset(bc, 0, sizeof *bc);
867
0
      bc->data.type = REDRAW_SPAN_MENU;
868
0
      bc->data.m.md = md;
869
0
      bc->data.m.px = px;
870
0
      bc->data.m.py = py;
871
0
    }
872
0
  }
873
0
}
874
875
/* Return true if two adjacent build cells can be joined into one span. */
876
static int
877
redraw_compare_data(struct redraw_build_cell *a, struct redraw_build_cell *b)
878
0
{
879
0
  struct redraw_span_data *ad = &a->data, *bd = &b->data;
880
881
0
  if (ad->type != bd->type)
882
0
    return (0);
883
884
0
  switch (ad->type) {
885
0
  case REDRAW_SPAN_PANE:
886
0
    if (ad->p.wp != bd->p.wp ||
887
0
        ad->p.py != bd->p.py ||
888
0
        ad->p.px + 1 != bd->p.px)
889
0
      return (0);
890
0
    return (1);
891
0
  case REDRAW_SPAN_BORDER:
892
0
    if (ad->b.top_wp != bd->b.top_wp ||
893
0
        ad->b.bottom_wp != bd->b.bottom_wp ||
894
0
        ad->b.left_wp != bd->b.left_wp ||
895
0
        ad->b.right_wp != bd->b.right_wp ||
896
0
        ad->b.style_wp != bd->b.style_wp ||
897
0
        ad->b.top_lines != bd->b.top_lines ||
898
0
        ad->b.bottom_lines != bd->b.bottom_lines ||
899
0
        ad->b.left_lines != bd->b.left_lines ||
900
0
        ad->b.right_lines != bd->b.right_lines ||
901
0
        ad->b.cell_type != bd->b.cell_type ||
902
0
        ad->b.cell_mask != bd->b.cell_mask ||
903
0
        ad->b.flags != bd->b.flags)
904
0
      return (0);
905
0
    if (ad->b.flags & REDRAW_BORDER_IS_ARROW)
906
0
      return (0);
907
0
    return (1);
908
0
  case REDRAW_SPAN_STATUS:
909
0
    if (ad->st.wp != bd->st.wp ||
910
0
        ad->st.offset + 1 != bd->st.offset ||
911
0
        ad->st.cell_type != bd->st.cell_type)
912
0
      return (0);
913
0
    return (1);
914
0
  case REDRAW_SPAN_SCROLLBAR:
915
0
    if (ad->sb.wp != bd->sb.wp ||
916
0
        ad->sb.y != bd->sb.y ||
917
0
        ad->sb.height != bd->sb.height ||
918
0
        ad->sb.flags != bd->sb.flags)
919
0
      return (0);
920
0
    return (1);
921
0
  case REDRAW_SPAN_MENU:
922
0
    if (ad->m.md != bd->m.md ||
923
0
        ad->m.py != bd->m.py ||
924
0
        ad->m.px + 1 != bd->m.px)
925
0
      return (0);
926
0
    return (1);
927
0
  case REDRAW_SPAN_OUTSIDE:
928
0
  case REDRAW_SPAN_EMPTY:
929
0
    return (1);
930
0
  }
931
0
  return (0);
932
0
}
933
934
/* Build the temporary cells for a redraw scene. */
935
static void
936
redraw_build_cells(struct redraw_build_ctx *bctx)
937
0
{
938
0
  struct window   *w = bctx->w;
939
0
  struct window_pane  *wp;
940
0
  size_t       ncells;
941
0
  u_int      x, y;
942
943
0
  if (bctx->sx != 0 && bctx->sy > SIZE_MAX / bctx->sx)
944
0
    fatalx("%s: too many cells", __func__);
945
0
  ncells = (size_t)bctx->sx * bctx->sy;
946
0
  if (ncells > redraw_ncells) {
947
0
    redraw_cells = xreallocarray(redraw_cells, ncells,
948
0
        sizeof *redraw_cells);
949
0
    redraw_ncells = ncells;
950
0
  }
951
952
0
  bctx->cells = redraw_cells;
953
0
  for (y = 0; y < bctx->sy; y++) {
954
0
    for (x = 0; x < bctx->sx; x++)
955
0
      redraw_reset_cell(bctx, x, y);
956
0
  }
957
958
0
  TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry)
959
0
    redraw_mark_pane(bctx, wp);
960
0
  redraw_mark_two_pane_colours(bctx);
961
0
  redraw_mark_menu(bctx);
962
0
}
963
964
/*
965
 * Build and return a redraw scene for a client. The caller owns the scene and
966
 * must free it with redraw_free_scene.
967
 */
968
static struct redraw_scene *
969
redraw_make_scene(struct client *c)
970
0
{
971
0
  struct session      *s = c->session;
972
0
  struct window     *w = s->curw->window;
973
0
  struct redraw_build_ctx    bctx;
974
0
  struct redraw_scene   *scene;
975
0
  struct redraw_build_cell  *bc, *last;
976
0
  struct redraw_line    *line;
977
0
  struct redraw_span    *span;
978
0
  enum redraw_span_type    type;
979
0
  u_int        x, y, x0;
980
981
0
  if (c->flags & CLIENT_SUSPENDED)
982
0
    return (NULL);
983
984
0
  redraw_set_context(c, &bctx);
985
986
0
  log_debug("%s: building @%u scene (%ux%u %u,%u; generation %llu)",
987
0
      c->name, w->id, bctx.sx, bctx.sy, bctx.ox, bctx.oy,
988
0
      (unsigned long long)w->redraw_scene_generation);
989
990
0
  redraw_build_cells(&bctx);
991
992
0
  scene = xcalloc(1, sizeof *scene);
993
0
  scene->c = c;
994
0
  scene->w = w;
995
0
  scene->lines = xcalloc(bctx.sy, sizeof *scene->lines);
996
0
  scene->generation = w->redraw_scene_generation;
997
0
  scene->sx = bctx.sx;
998
0
  scene->sy = bctx.sy;
999
0
  scene->ox = bctx.ox;
1000
0
  scene->oy = bctx.oy;
1001
1002
0
  for (y = 0; y < bctx.sy; y++) {
1003
0
    line = &scene->lines[y];
1004
0
    for (type = 0; type < REDRAW_SPAN_TYPES; type++)
1005
0
      TAILQ_INIT(&line->spans[type]);
1006
1007
0
    x = 0;
1008
0
    while (x < bctx.sx) {
1009
0
      x0 = x;
1010
0
      last = redraw_get_build_cell(&bctx, x, y);
1011
0
      x++;
1012
1013
0
      while (x < bctx.sx) {
1014
0
        bc = redraw_get_build_cell(&bctx, x, y);
1015
0
        if (!redraw_compare_data(last, bc))
1016
0
          break;
1017
0
        last = bc;
1018
0
        x++;
1019
0
      }
1020
0
      bc = redraw_get_build_cell(&bctx, x0, y);
1021
0
      type = bc->data.type;
1022
1023
0
      span = xcalloc(1, sizeof *span);
1024
0
      span->x = x0;
1025
0
      span->width = x - x0;
1026
0
      span->data = bc->data;
1027
1028
0
      TAILQ_INSERT_TAIL(&line->spans[type], span, entry);
1029
0
    }
1030
0
  }
1031
1032
0
  log_debug("%s: finished building @%u scene", c->name, w->id);
1033
0
  return (scene);
1034
0
}
1035
1036
/* Free a scene. */
1037
void
1038
redraw_free_scene(struct redraw_scene *scene)
1039
0
{
1040
0
  struct redraw_spans *spans;
1041
0
  struct redraw_span  *span, *span1;
1042
0
  u_int      y, type;
1043
1044
0
  if (scene == NULL)
1045
0
    return;
1046
1047
0
  for (y = 0; y < scene->sy; y++) {
1048
0
    for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
1049
0
      spans = &scene->lines[y].spans[type];
1050
0
      TAILQ_FOREACH_SAFE(span, spans, entry, span1) {
1051
0
        TAILQ_REMOVE(spans, span, entry);
1052
0
        free(span);
1053
0
      }
1054
0
    }
1055
0
  }
1056
0
  free(scene->lines);
1057
0
  free(scene);
1058
0
}
1059
1060
/* Mark a window's cached redraw scenes as out of date. */
1061
void
1062
redraw_invalidate_scene(struct window *w)
1063
12.4k
{
1064
12.4k
  w->redraw_scene_generation++;
1065
12.4k
}
1066
1067
/* Mark all cached redraw scenes as out of date. */
1068
void
1069
redraw_invalidate_all_scenes(void)
1070
0
{
1071
0
  struct window *w;
1072
1073
0
  RB_FOREACH(w, windows, &windows)
1074
0
    redraw_invalidate_scene(w);
1075
0
}
1076
1077
/* Get the cached redraw scene, rebuilding it if needed. */
1078
static struct redraw_scene *
1079
redraw_get_scene(struct client *c)
1080
0
{
1081
0
  struct redraw_scene *scene = c->redraw_scene;
1082
0
  struct window   *w = c->session->curw->window;
1083
0
  const char    *reason = NULL;
1084
0
  u_int      ox, oy, sx, sy;
1085
1086
0
  redraw_get_window_offset(c, &ox, &oy, &sx, &sy);
1087
0
  if (scene == NULL)
1088
0
    reason = "missing";
1089
0
  else if (scene->w != w)
1090
0
    reason = "window changed";
1091
0
  else if (scene->generation != w->redraw_scene_generation)
1092
0
    reason = "generation changed";
1093
0
  else if (scene->ox != ox || scene->oy != oy)
1094
0
    reason = "offset changed";
1095
0
  else if (scene->sx != sx || scene->sy != sy)
1096
0
    reason = "size changed";
1097
0
  if (reason != NULL) {
1098
0
    log_debug("%s: @%u scene invalid: %s", c->name, w->id, reason);
1099
0
    redraw_free_scene(scene);
1100
0
    scene = redraw_make_scene(c);
1101
0
    c->redraw_scene = scene;
1102
0
  }
1103
0
  return (scene);
1104
0
}
1105
1106
/* Draw a pane span. */
1107
static void
1108
redraw_draw_pane_span(struct redraw_draw_ctx *dctx,
1109
    struct redraw_span *span, u_int x, u_int y, u_int n)
1110
0
{
1111
0
  struct redraw_scene *scene = dctx->scene;
1112
0
  struct client   *c = scene->c;
1113
0
  struct tty    *tty = &c->tty;
1114
0
  struct window_pane  *wp = span->data.p.wp;
1115
0
  struct screen   *s = wp->screen;
1116
0
  struct grid_cell   defaults;
1117
0
  struct tty_style_ctx   style_ctx;
1118
0
  u_int      px, py;
1119
1120
0
  tty_default_colours(&defaults, wp, &style_ctx.dim);
1121
0
  style_ctx.defaults = &defaults;
1122
0
  style_ctx.palette = &wp->palette;
1123
0
  style_ctx.hyperlinks = s->hyperlinks;
1124
1125
0
  px = span->data.p.px + (x - span->x);
1126
0
  py = span->data.p.py;
1127
0
  tty_draw_line(tty, s, px, py, n, x, y, &style_ctx);
1128
0
}
1129
1130
/* Get default border style for spans without a pane. */
1131
static void
1132
redraw_get_default_border_style(struct redraw_draw_ctx *dctx,
1133
    struct grid_cell *gc, enum pane_lines *pane_lines)
1134
0
{
1135
0
  struct redraw_scene *scene = dctx->scene;
1136
0
  struct client   *c = scene->c;
1137
0
  struct session    *s = c->session;
1138
0
  struct options    *oo = scene->w->options;
1139
0
  struct format_tree  *ft;
1140
0
  struct grid_cell  *dgc = &dctx->default_gc;
1141
1142
0
  if (~dctx->flags & REDRAW_DEFAULT_SET) {
1143
0
    ft = format_create_defaults(NULL, c, s, s->curw, NULL);
1144
0
    memcpy(dgc, &grid_default_cell, sizeof *dgc);
1145
0
    style_add(dgc, oo, "pane-border-style", ft);
1146
0
    format_free(ft);
1147
0
    dctx->pane_lines = options_get_number(oo, "pane-border-lines");
1148
0
    dctx->flags |= REDRAW_DEFAULT_SET;
1149
0
  }
1150
0
  memcpy(gc, dgc, sizeof *gc);
1151
0
  *pane_lines = dctx->pane_lines;
1152
0
}
1153
1154
/*
1155
 * For this border span, pick the pane whose border style should colour it.
1156
 * Prefer an explicitly assigned style owner, then the active adjacent pane,
1157
 * then any adjacent pane.
1158
 */
1159
static struct window_pane *
1160
redraw_get_pane_for_border_style(struct redraw_draw_ctx *dctx,
1161
    struct redraw_span *span)
1162
0
{
1163
0
  struct window_pane  *active = dctx->active;
1164
1165
0
  if (span->data.type != REDRAW_SPAN_BORDER)
1166
0
    return (NULL);
1167
1168
0
  if (span->data.b.style_wp != NULL)
1169
0
    return (span->data.b.style_wp);
1170
0
  if (active != NULL && redraw_data_has_pane(&span->data, active))
1171
0
    return (active);
1172
1173
0
  if (span->data.b.top_wp != NULL)
1174
0
    return (span->data.b.top_wp);
1175
0
  if (span->data.b.bottom_wp != NULL)
1176
0
    return (span->data.b.bottom_wp);
1177
0
  if (span->data.b.left_wp != NULL)
1178
0
    return (span->data.b.left_wp);
1179
0
  if (span->data.b.right_wp != NULL)
1180
0
    return (span->data.b.right_wp);
1181
0
  return (NULL);
1182
0
}
1183
1184
/* Draw arrow indicator if this border span is an arrow cell. */
1185
static void
1186
redraw_draw_border_arrow(struct redraw_draw_ctx *dctx,
1187
    struct redraw_span *span, struct grid_cell *gc)
1188
0
{
1189
0
  struct window_pane  *active = dctx->active;
1190
0
  char       ch;
1191
1192
0
  if (span->data.type != REDRAW_SPAN_BORDER || active == NULL)
1193
0
    return;
1194
0
  if (~span->data.b.flags & REDRAW_BORDER_IS_ARROW)
1195
0
    return;
1196
1197
0
  if (span->data.b.left_wp == active)
1198
0
    ch = ',';
1199
0
  else if (span->data.b.right_wp == active)
1200
0
    ch = '+';
1201
0
  else if (span->data.b.top_wp == active)
1202
0
    ch = '-';
1203
0
  else if (span->data.b.bottom_wp == active)
1204
0
    ch = '.';
1205
0
  else
1206
0
    return;
1207
1208
0
  utf8_set(&gc->data, ch);
1209
0
  gc->attr |= GRID_ATTR_CHARSET;
1210
0
}
1211
1212
/* Draw a border span. */
1213
static void
1214
redraw_draw_border_span(struct redraw_draw_ctx *dctx,
1215
    struct redraw_span *span, u_int x, u_int y, u_int n)
1216
0
{
1217
0
  struct redraw_scene *scene = dctx->scene;
1218
0
  struct client   *c = scene->c;
1219
0
  struct tty    *tty = &c->tty;
1220
0
  struct window   *w = scene->w;
1221
0
  struct window_pane  *wp = NULL;
1222
0
  struct grid_cell   gc;
1223
0
  enum pane_lines    pane_lines;
1224
0
  u_int      i, cell_type;
1225
0
  int      isolates = 0;
1226
1227
0
  if (span->data.type != REDRAW_SPAN_BORDER)
1228
0
    cell_type = CELL_NONE;
1229
0
  else {
1230
0
    wp = redraw_get_pane_for_border_style(dctx, span);
1231
0
    cell_type = span->data.b.cell_type;
1232
0
  }
1233
1234
0
  if (wp == NULL) {
1235
0
    redraw_get_default_border_style(dctx, &gc, &pane_lines);
1236
0
    if (span->data.type == REDRAW_SPAN_OUTSIDE)
1237
0
      window_get_fill_cell(w, 0, &gc);
1238
0
    else if (span->data.type == REDRAW_SPAN_EMPTY)
1239
0
      window_get_fill_cell(w, 1, &gc);
1240
0
    else {
1241
0
      if (span->data.type != REDRAW_SPAN_BORDER)
1242
0
        pane_lines = PANE_LINES_SINGLE;
1243
0
      window_get_border_cell(NULL, pane_lines, cell_type, &gc);
1244
0
    }
1245
0
  } else {
1246
0
    window_pane_get_border_style(wp, c, &gc);
1247
0
    window_pane_get_border_cell(wp, cell_type, &gc);
1248
0
  }
1249
1250
0
  if (span->data.type == REDRAW_SPAN_BORDER &&
1251
0
      dctx->marked != NULL &&
1252
0
      redraw_data_has_pane(&span->data, dctx->marked))
1253
0
    gc.attr ^= GRID_ATTR_REVERSE;
1254
0
  redraw_draw_border_arrow(dctx, span, &gc);
1255
1256
0
  if (cell_type == CELL_UD && (dctx->flags & REDRAW_ISOLATES))
1257
0
    isolates = 1;
1258
0
  tty_cursor(tty, x, y);
1259
0
  if (isolates)
1260
0
    tty_puts(tty, REDRAW_END_ISOLATE);
1261
0
  for (i = 0; i < n; i++)
1262
0
    tty_cell(tty, &gc, NULL);
1263
0
  if (isolates)
1264
0
    tty_puts(tty, REDRAW_START_ISOLATE);
1265
0
}
1266
1267
/* Draw a pane status span. */
1268
static void
1269
redraw_draw_status_span(struct redraw_draw_ctx *dctx,
1270
    struct redraw_span *span, u_int x, u_int y, u_int n)
1271
0
{
1272
0
  struct redraw_scene *scene = dctx->scene;
1273
0
  struct client   *c = scene->c;
1274
0
  struct tty    *tty = &c->tty;
1275
0
  struct window_pane  *wp = span->data.st.wp;
1276
0
  struct screen   *s = &wp->status_screen;
1277
0
  u_int      px, sx = screen_size_x(s);
1278
1279
0
  px = span->data.st.offset + (x - span->x);
1280
0
  if (px < sx) {
1281
0
    if (n > sx - px)
1282
0
      n = sx - px;
1283
0
    tty_draw_line(tty, s, px, 0, n, x, y, NULL);
1284
0
  }
1285
0
}
1286
1287
/* Draw a scrollbar span. */
1288
static void
1289
redraw_draw_scrollbar_span(struct redraw_draw_ctx *dctx,
1290
    struct redraw_span *span, u_int x, u_int y, u_int n)
1291
0
{
1292
0
  struct redraw_scene *scene = dctx->scene;
1293
0
  struct window_pane  *wp = span->data.sb.wp;
1294
0
  struct screen   *s = wp->screen;
1295
0
  struct tty    *tty = &scene->c->tty;
1296
0
  struct style    *sb_style = &wp->scrollbar_style;
1297
0
  struct grid_cell   gc, slgc, pad_gc, *gcp;
1298
0
  double       pct_view;
1299
0
  u_int      total_height, slider_h, slider_y;
1300
0
  u_int      sb_h = span->data.sb.height;
1301
0
  u_int      sb_y = span->data.sb.y;
1302
0
  u_int      i, off, sb_w, sb_pad;
1303
0
  int      cm_y, cm_size;
1304
1305
0
  if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE) {
1306
0
    total_height = screen_size_y(s) + screen_hsize(s);
1307
0
    if (total_height == 0)
1308
0
      return;
1309
0
    pct_view = (double)sb_h / total_height;
1310
0
    slider_h = (double)sb_h * pct_view;
1311
0
    slider_y = sb_h - slider_h;
1312
0
  } else {
1313
0
    if (TAILQ_FIRST(&wp->modes) == NULL)
1314
0
      return;
1315
0
    if (window_copy_get_current_offset(wp, &cm_y, &cm_size) == 0)
1316
0
      return;
1317
0
    total_height = cm_size + sb_h;
1318
0
    if (total_height == 0)
1319
0
      return;
1320
0
    pct_view = (double)sb_h / total_height;
1321
0
    slider_h = (double)sb_h * pct_view;
1322
0
    slider_y = (sb_h + 1) * ((double)cm_y / total_height);
1323
0
  }
1324
1325
0
  if (slider_h < 1)
1326
0
    slider_h = 1;
1327
0
  if (slider_y >= sb_h)
1328
0
    slider_y = sb_h - 1;
1329
1330
0
  wp->sb_slider_y = slider_y;
1331
0
  wp->sb_slider_h = slider_h;
1332
1333
0
  gc = sb_style->gc;
1334
0
  memcpy(&slgc, &gc, sizeof slgc);
1335
0
  slgc.fg = gc.bg;
1336
0
  slgc.bg = gc.fg;
1337
0
  tty_default_colours(&pad_gc, wp, NULL);
1338
1339
0
  sb_w = sb_style->width;
1340
0
  sb_pad = sb_style->pad;
1341
0
  off = x - span->x;
1342
1343
0
  tty_cursor(tty, x, y);
1344
0
  for (i = 0; i < n; i++) {
1345
0
    if (span->data.sb.flags & REDRAW_SCROLLBAR_LEFT) {
1346
0
      if (off + i >= sb_w && off + i < sb_w + sb_pad) {
1347
0
        tty_cell(tty, &pad_gc, NULL);
1348
0
        continue;
1349
0
      }
1350
0
    } else {
1351
0
      if (off + i < sb_pad) {
1352
0
        tty_cell(tty, &pad_gc, NULL);
1353
0
        continue;
1354
0
      }
1355
0
    }
1356
1357
0
    if (sb_y >= slider_y && sb_y < slider_y + slider_h)
1358
0
      gcp = &slgc;
1359
0
    else
1360
0
      gcp = &gc;
1361
0
    tty_cell(tty, gcp, NULL);
1362
0
  }
1363
0
}
1364
1365
/* Draw a menu span. */
1366
static void
1367
redraw_draw_menu_span(struct redraw_draw_ctx *dctx,
1368
    struct redraw_span *span, u_int x, u_int y, u_int n)
1369
0
{
1370
0
  struct redraw_scene *scene = dctx->scene;
1371
0
  struct tty    *tty = &scene->c->tty;
1372
0
  struct screen   *s = menu_screen(span->data.m.md);
1373
0
  u_int      px;
1374
1375
0
  px = span->data.m.px + (x - span->x);
1376
0
  tty_draw_line(tty, s, px, span->data.m.py, n, x, y, NULL);
1377
0
}
1378
1379
/* Draw a span. */
1380
static void
1381
redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
1382
    u_int y)
1383
0
{
1384
0
  struct redraw_scene *scene = dctx->scene;
1385
0
  struct redraw_span_data *data = &span->data;
1386
0
  enum redraw_span_type  type = data->type;
1387
0
  struct client   *c = scene->c;
1388
0
  struct tty    *tty = &c->tty;
1389
0
  struct visible_ranges *r;
1390
0
  struct visible_range  *rr;
1391
0
  u_int      i, x, n;
1392
1393
0
  if (type == REDRAW_SPAN_STATUS && ~data->st.wp->flags & PANE_NEWSTATUS)
1394
0
    return;
1395
1396
0
  r = tty_check_overlay_range(tty, span->x, y, span->width);
1397
0
  for (i = 0; i < r->used; i++) {
1398
0
    rr = &r->ranges[i];
1399
0
    if (rr->nx == 0)
1400
0
      continue;
1401
0
    x = rr->px;
1402
0
    n = rr->nx;
1403
1404
0
    switch (span->data.type) {
1405
0
    case REDRAW_SPAN_PANE:
1406
0
      redraw_draw_pane_span(dctx, span, x, y, n);
1407
0
      break;
1408
0
    case REDRAW_SPAN_BORDER:
1409
0
    case REDRAW_SPAN_EMPTY:
1410
0
    case REDRAW_SPAN_OUTSIDE:
1411
0
      redraw_draw_border_span(dctx, span, x, y, n);
1412
0
      break;
1413
0
    case REDRAW_SPAN_STATUS:
1414
0
      redraw_draw_status_span(dctx, span, x, y, n);
1415
0
      break;
1416
0
    case REDRAW_SPAN_SCROLLBAR:
1417
0
      redraw_draw_scrollbar_span(dctx, span, x, y, n);
1418
0
      break;
1419
0
    case REDRAW_SPAN_MENU:
1420
0
      redraw_draw_menu_span(dctx, span, x, y, n);
1421
0
      break;
1422
0
    }
1423
0
  }
1424
0
}
1425
1426
/* Draw pane lines. */
1427
static void
1428
redraw_draw_pane_lines(struct redraw_draw_ctx *dctx, struct window_pane *wp,
1429
    int flags)
1430
0
{
1431
0
  struct redraw_scene *scene = dctx->scene;
1432
0
  struct redraw_line  *line;
1433
0
  struct redraw_spans *spans;
1434
0
  struct redraw_span  *span;
1435
0
  u_int      cy;
1436
0
  int      y, top, bottom;
1437
1438
0
  top = wp->yoff - (int)scene->oy;
1439
0
  if (top < 0)
1440
0
    top = 0;
1441
0
  bottom = wp->yoff + (int)wp->sy - (int)scene->oy;
1442
0
  if (bottom < 0)
1443
0
    bottom = 0;
1444
0
  if (bottom > (int)scene->sy)
1445
0
    bottom = scene->sy;
1446
1447
0
  for (y = top; y < bottom; y++) {
1448
0
    line = &scene->lines[y];
1449
0
    if (dctx->flags & REDRAW_STATUS_TOP)
1450
0
      cy = dctx->status_lines + y;
1451
0
    else
1452
0
      cy = y;
1453
0
    if (flags & REDRAW_PANE) {
1454
0
      spans = &line->spans[REDRAW_SPAN_PANE];
1455
0
      TAILQ_FOREACH(span, spans, entry) {
1456
0
        if (span->data.p.wp == wp)
1457
0
          redraw_draw_span(dctx, span, cy);
1458
0
      }
1459
0
    }
1460
0
    if (flags & REDRAW_PANE_SCROLLBAR) {
1461
0
      spans = &line->spans[REDRAW_SPAN_SCROLLBAR];
1462
0
      TAILQ_FOREACH(span, spans, entry) {
1463
0
        if (span->data.sb.wp == wp)
1464
0
          redraw_draw_span(dctx, span, cy);
1465
0
      }
1466
0
    }
1467
0
  }
1468
0
}
1469
1470
/* Draw lines. */
1471
static void
1472
redraw_draw_lines(struct redraw_draw_ctx *dctx, int flags)
1473
0
{
1474
0
  struct redraw_scene *scene = dctx->scene;
1475
0
  struct redraw_line  *line;
1476
0
  struct redraw_spans *spans;
1477
0
  struct redraw_span  *span;
1478
0
  u_int      y, cy, type;
1479
1480
0
  for (y = 0; y < scene->sy; y++) {
1481
0
    line = &scene->lines[y];
1482
0
    if (dctx->flags & REDRAW_STATUS_TOP)
1483
0
      cy = dctx->status_lines + y;
1484
0
    else
1485
0
      cy = y;
1486
0
    for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
1487
0
      if (!REDRAW_IS_ALL(flags)) {
1488
0
        switch (type) {
1489
0
        case REDRAW_SPAN_PANE:
1490
0
          if (~flags & REDRAW_PANE)
1491
0
            continue;
1492
0
          break;
1493
0
        case REDRAW_SPAN_OUTSIDE:
1494
0
          if (~flags & REDRAW_OUTSIDE)
1495
0
            continue;
1496
0
          break;
1497
0
        case REDRAW_SPAN_EMPTY:
1498
0
          if (~flags & REDRAW_EMPTY)
1499
0
            continue;
1500
0
          break;
1501
0
        case REDRAW_SPAN_BORDER:
1502
0
          if (~flags & REDRAW_PANE_BORDER)
1503
0
            continue;
1504
0
          break;
1505
0
        case REDRAW_SPAN_STATUS:
1506
0
          if (~flags & REDRAW_PANE_STATUS)
1507
0
            continue;
1508
0
          break;
1509
0
        case REDRAW_SPAN_SCROLLBAR:
1510
0
          if (~flags & REDRAW_PANE_SCROLLBAR)
1511
0
            continue;
1512
0
          break;
1513
0
        case REDRAW_SPAN_MENU:
1514
0
          if (~flags & REDRAW_MENU)
1515
0
            continue;
1516
0
          break;
1517
0
        default:
1518
0
          continue;
1519
0
        }
1520
0
      }
1521
0
      spans = &line->spans[type];
1522
0
      TAILQ_FOREACH(span, spans, entry)
1523
0
        redraw_draw_span(dctx, span, cy);
1524
0
    }
1525
0
  }
1526
0
}
1527
1528
/* Draw menu spans. */
1529
static void
1530
redraw_draw_menu_lines(struct redraw_draw_ctx *dctx)
1531
0
{
1532
0
  struct redraw_scene *scene = dctx->scene;
1533
0
  struct redraw_line  *line;
1534
0
  struct redraw_span  *span;
1535
0
  u_int      y, cy;
1536
1537
0
  for (y = 0; y < scene->sy; y++) {
1538
0
    line = &scene->lines[y];
1539
0
    if (dctx->flags & REDRAW_STATUS_TOP)
1540
0
      cy = dctx->status_lines + y;
1541
0
    else
1542
0
      cy = y;
1543
0
    TAILQ_FOREACH(span, &line->spans[REDRAW_SPAN_MENU], entry)
1544
0
      redraw_draw_span(dctx, span, cy);
1545
0
  }
1546
0
}
1547
1548
/* Get line for pane status line. */
1549
static int
1550
redraw_pane_status_line(struct redraw_draw_ctx *dctx,
1551
    struct window_pane *wp, u_int *line)
1552
0
{
1553
0
  struct redraw_scene *scene = dctx->scene;
1554
0
  int      pane_status, wy;
1555
1556
0
  pane_status = window_pane_get_pane_status(wp);
1557
0
  if (pane_status == PANE_STATUS_OFF)
1558
0
    return (0);
1559
1560
0
  if (pane_status == PANE_STATUS_TOP)
1561
0
    wy = (int)wp->yoff - 1;
1562
0
  else
1563
0
    wy = (int)wp->yoff + wp->sy;
1564
0
  if (wy < 0 || wy < (int)scene->oy)
1565
0
    return (0);
1566
0
  if ((u_int)wy >= scene->oy + scene->sy)
1567
0
    return (0);
1568
0
  *line = wy - scene->oy;
1569
0
  return (1);
1570
0
}
1571
1572
/* Get available width for pane status line. */
1573
static u_int
1574
redraw_pane_status_width(struct redraw_draw_ctx *dctx,
1575
    struct window_pane *wp, struct redraw_span **first)
1576
0
{
1577
0
  struct redraw_scene *scene = dctx->scene;
1578
0
  struct redraw_span  *span;
1579
0
  u_int      y, width = 0, end;
1580
1581
0
  if (!redraw_pane_status_line(dctx, wp, &y))
1582
0
    return (0);
1583
1584
0
  *first = NULL;
1585
0
  TAILQ_FOREACH(span, &scene->lines[y].spans[REDRAW_SPAN_STATUS], entry) {
1586
0
    if (span->data.st.wp == wp) {
1587
0
      if (*first == NULL)
1588
0
          *first = span;
1589
0
      end = span->data.st.offset + span->width;
1590
0
      if (end > width)
1591
0
        width = end;
1592
0
    }
1593
0
  }
1594
0
  return (width);
1595
0
}
1596
1597
/* Set up draw context. */
1598
static void
1599
redraw_set_draw_context(struct redraw_draw_ctx *dctx,
1600
    struct redraw_scene *scene)
1601
0
{
1602
0
  struct client *c = scene->c;
1603
0
  struct session  *s = c->session;
1604
0
  struct options  *oo = s->options;
1605
0
  struct tty  *tty = &c->tty;
1606
0
  u_int    lines;
1607
1608
0
  memset(dctx, 0, sizeof *dctx);
1609
0
  dctx->scene = scene;
1610
1611
0
  if (server_is_marked(s, s->curw, marked_pane.wp))
1612
0
    dctx->marked = marked_pane.wp;
1613
0
  dctx->active = s->curw->window->active;
1614
1615
0
  lines = status_line_size(c);
1616
0
  if (options_get_number(oo, "status-position") == 0)
1617
0
    dctx->flags |= REDRAW_STATUS_TOP;
1618
0
  dctx->status_lines = lines;
1619
1620
0
  if ((c->flags & CLIENT_UTF8) && tty_term_has(tty->term, TTYC_BIDI))
1621
0
    dctx->flags |= REDRAW_ISOLATES;
1622
0
}
1623
1624
/* Draw a pane's prompt over its content. */
1625
static void
1626
redraw_draw_pane_prompt(struct redraw_draw_ctx *dctx, struct window_pane *wp)
1627
0
{
1628
0
  struct redraw_scene *scene = dctx->scene;
1629
0
  struct client   *c = scene->c;
1630
0
  struct tty    *tty = &c->tty;
1631
0
  struct screen    screen;
1632
0
  struct screen_write_ctx  ctx;
1633
0
  struct prompt_draw_data  pdd;
1634
0
  int      ox = scene->ox, oy = scene->oy;
1635
0
  int      sx = scene->sx, sy = scene->sy;
1636
0
  int      line, cy, px, offset, width, wy;
1637
1638
0
  if (wp->prompt == NULL || wp->sx == 0 || wp->sy == 0)
1639
0
    return;
1640
1641
0
  if (~dctx->flags & REDRAW_STATUS_TOP)
1642
0
    wy = wp->yoff + (int)wp->sy - 1;
1643
0
  else
1644
0
    wy = wp->yoff;
1645
0
  if (wy < oy || wy >= oy + sy)
1646
0
    return;
1647
0
  line = wy - oy;
1648
0
  if (dctx->flags & REDRAW_STATUS_TOP)
1649
0
    cy = dctx->status_lines + line;
1650
0
  else
1651
0
    cy = line;
1652
1653
0
  if (wp->xoff + (int)wp->sx <= ox || wp->xoff >= ox + sx)
1654
0
    return;
1655
0
  if (wp->xoff < ox) {
1656
0
    offset = ox - wp->xoff;
1657
0
    px = 0;
1658
0
  } else {
1659
0
    offset = 0;
1660
0
    px = wp->xoff - ox;
1661
0
  }
1662
0
  width = wp->sx - offset;
1663
0
  if (px + width > sx)
1664
0
    width = sx - px;
1665
1666
0
  screen_init(&screen, wp->sx, 1, 0);
1667
0
  screen_write_start(&ctx, &screen);
1668
0
  pdd.ctx = &ctx;
1669
0
  pdd.cursor_x = &wp->prompt_cx;
1670
0
  pdd.area_x = 0;
1671
0
  pdd.area_width = wp->sx;
1672
0
  pdd.prompt_line = 0;
1673
0
  prompt_draw(wp->prompt, &pdd);
1674
0
  screen_write_stop(&ctx);
1675
1676
0
  tty_draw_line(tty, &screen, 0, offset, width, px, cy, NULL);
1677
0
  screen_free(&screen);
1678
0
}
1679
1680
/* Draw scene to client. */
1681
static void
1682
redraw_draw(struct client *c, struct window_pane *wp, int flags)
1683
0
{
1684
0
  struct redraw_draw_ctx   dctx;
1685
0
  struct session    *s = c->session;
1686
0
  struct window   *w = s->curw->window;
1687
0
  struct tty    *tty = &c->tty;
1688
0
  struct screen   *sl;
1689
0
  struct redraw_scene *scene;
1690
0
  struct window_pane  *loop;
1691
0
  u_int      width, i, y, lines, j;
1692
0
  struct redraw_span  *first;
1693
0
  struct visible_ranges *r;
1694
0
  struct visible_range  *rr;
1695
0
  int      redraw;
1696
1697
0
  if (c->flags & CLIENT_SUSPENDED)
1698
0
    return;
1699
1700
0
  if (flags & REDRAW_STATUS) {
1701
0
    if (c->message_string != NULL)
1702
0
      redraw = status_message_redraw(c);
1703
0
    else if (c->prompt != NULL)
1704
0
      redraw = status_prompt_redraw(c);
1705
0
    else
1706
0
      redraw = status_redraw(c);
1707
0
    if (!redraw && !REDRAW_IS_ALL(flags)) {
1708
0
      flags &= ~REDRAW_STATUS;
1709
0
      if (flags == 0)
1710
0
        return;
1711
0
    }
1712
0
  }
1713
1714
0
  if (log_get_level() != 0) {
1715
0
    log_debug("%s: starting @%u redraw (%s)", c->name, w->id,
1716
0
        redraw_flags_to_string(flags));
1717
0
  }
1718
1719
0
  scene = redraw_get_scene(c);
1720
0
  if (scene == NULL)
1721
0
    return;
1722
0
  redraw_set_draw_context(&dctx, scene);
1723
0
  if (w->menu != NULL)
1724
0
    menu_update(w->menu);
1725
1726
0
  if (flags & (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS)) {
1727
0
    TAILQ_FOREACH(loop, &scene->w->panes, entry) {
1728
0
      loop->border_gc_set = 0;
1729
0
      loop->active_border_gc_set = 0;
1730
0
    }
1731
0
  }
1732
1733
0
  if (flags & REDRAW_PANE_STATUS) {
1734
0
    redraw = 0;
1735
0
    TAILQ_FOREACH(loop, &scene->w->panes, entry) {
1736
0
      if (REDRAW_IS_ALL(flags))
1737
0
        loop->flags |= PANE_NEWSTATUS;
1738
0
      else
1739
0
        loop->flags &= ~PANE_NEWSTATUS;
1740
1741
0
      width = redraw_pane_status_width(&dctx, loop,
1742
0
          &first);
1743
0
      if (width == 0)
1744
0
        continue;
1745
1746
0
      if (window_make_pane_status(loop, c, width, first)) {
1747
0
        loop->flags |= PANE_NEWSTATUS;
1748
0
        redraw = 1;
1749
0
      }
1750
0
    }
1751
0
    if (!redraw && !REDRAW_IS_ALL(flags)) {
1752
0
      flags &= ~REDRAW_PANE_STATUS;
1753
0
      if (flags == 0)
1754
0
        return;
1755
0
    }
1756
0
  }
1757
1758
0
  if (flags & REDRAW_PANE) {
1759
0
    if (wp != NULL) {
1760
0
      if (wp->base.mode & MODE_SYNC)
1761
0
        screen_write_stop_sync(wp);
1762
0
      screen_write_clear_dirty(wp);
1763
0
    } else {
1764
0
      TAILQ_FOREACH(loop, &scene->w->panes, entry) {
1765
0
        if (!window_pane_is_visible(loop))
1766
0
          continue;
1767
0
        if (loop->base.mode & MODE_SYNC)
1768
0
          screen_write_stop_sync(loop);
1769
0
        screen_write_clear_dirty(loop);
1770
0
      }
1771
0
    }
1772
0
  }
1773
0
  tty_sync_start(tty);
1774
0
  tty_update_mode(tty, tty->mode & ~CURSOR_MODES, NULL);
1775
1776
0
  if (wp != NULL)
1777
0
    redraw_draw_pane_lines(&dctx, wp, flags);
1778
0
  else
1779
0
    redraw_draw_lines(&dctx, flags);
1780
1781
0
  if (flags & REDRAW_PANE) {
1782
0
    if (wp != NULL)
1783
0
      redraw_draw_pane_prompt(&dctx, wp);
1784
0
    else {
1785
0
      TAILQ_FOREACH(loop, &scene->w->panes, entry) {
1786
0
        if (window_pane_is_visible(loop))
1787
0
          redraw_draw_pane_prompt(&dctx, loop);
1788
0
      }
1789
0
    }
1790
0
  }
1791
0
  if (w->menu != NULL && (flags & REDRAW_MENU))
1792
0
    redraw_draw_menu_lines(&dctx);
1793
1794
0
  if (flags & REDRAW_STATUS) {
1795
0
    lines = dctx.status_lines;
1796
0
    if (c->message_string != NULL || c->prompt != NULL)
1797
0
      lines = (lines == 0 ? 1 : lines);
1798
0
    if (dctx.flags & REDRAW_STATUS_TOP)
1799
0
      y = 0;
1800
0
    else
1801
0
      y = c->tty.sy - lines;
1802
0
    sl = c->status.active;
1803
0
    for (i = 0; i < lines; i++) {
1804
0
      r = tty_check_overlay_range(tty, 0, y + i, tty->sx);
1805
0
      for (j = 0; j < r->used; j++) {
1806
0
        rr = &r->ranges[j];
1807
0
        if (rr->nx == 0)
1808
0
          continue;
1809
0
        tty_draw_line(tty, sl, rr->px, i, rr->nx,
1810
0
            rr->px, y + i, NULL);
1811
0
      }
1812
0
    }
1813
0
  }
1814
0
  if (c->overlay_draw != NULL && (flags & REDRAW_OVERLAY))
1815
0
    c->overlay_draw(c, c->overlay_data);
1816
1817
0
  tty_reset(tty);
1818
0
  tty_sync_end(tty);
1819
1820
#ifdef ENABLE_SIXEL
1821
  if (wp != NULL)
1822
    tty_draw_images(c, wp);
1823
  else {
1824
    TAILQ_FOREACH(loop, &scene->w->panes, entry)
1825
      tty_draw_images(c, loop);
1826
  }
1827
#endif
1828
1829
0
  log_debug("%s: finished @%u redraw", c->name, scene->w->id);
1830
0
}
1831
1832
/* Get border cell type beneath status cell at offset x in pane status line. */
1833
int
1834
redraw_get_status_border_cell_type(struct redraw_span **spanp, u_int x)
1835
0
{
1836
0
  struct redraw_span  *span = *spanp;
1837
0
  struct window_pane  *wp;
1838
0
  u_int      start, end;
1839
1840
0
  if (span == NULL || span->data.type != REDRAW_SPAN_STATUS)
1841
0
    return (CELL_LR);
1842
0
  wp = span->data.st.wp;
1843
0
  for (; span != NULL; span = TAILQ_NEXT(span, entry)) {
1844
0
    if (span->data.type != REDRAW_SPAN_STATUS)
1845
0
      continue;
1846
0
    if (span->data.st.wp != wp)
1847
0
      continue;
1848
1849
0
    start = span->data.st.offset;
1850
0
    end = start + span->width;
1851
0
    if (x >= start && x < end) {
1852
0
      *spanp = span;
1853
0
      return (span->data.st.cell_type);
1854
0
    }
1855
1856
0
    if (start > x) {
1857
0
      *spanp = span;
1858
0
      break;
1859
0
    }
1860
0
  }
1861
0
  if (span == NULL)
1862
0
    *spanp = NULL;
1863
0
  return (CELL_LR);
1864
0
}
1865
1866
/* Draw screen. */
1867
void
1868
redraw_screen(struct client *c)
1869
0
{
1870
0
  int flags = 0;
1871
1872
0
  if (c->flags & CLIENT_REDRAWWINDOW) {
1873
0
    if (c->flags & CLIENT_REDRAWOVERLAY)
1874
0
      redraw_draw(c, NULL, REDRAW_ALL);
1875
0
    else
1876
0
      redraw_draw(c, NULL, REDRAW_ALL & ~REDRAW_OVERLAY);
1877
0
  } else {
1878
0
    if (c->flags & CLIENT_REDRAWBORDERS)
1879
0
      flags |= (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS);
1880
0
    if (c->flags & CLIENT_REDRAWSTATUS)
1881
0
      flags |= (REDRAW_STATUS|REDRAW_PANE_STATUS);
1882
0
    if (c->flags & CLIENT_REDRAWOVERLAY)
1883
0
      flags |= REDRAW_OVERLAY;
1884
0
    if (c->flags & CLIENT_REDRAWMENU)
1885
0
      flags |= REDRAW_MENU;
1886
0
    if (c->session->curw->window->menu != NULL)
1887
0
      flags |= REDRAW_MENU;
1888
0
    if (flags != 0)
1889
0
      redraw_draw(c, NULL, flags);
1890
0
  }
1891
0
}
1892
1893
/* Draw a single pane. */
1894
void
1895
redraw_pane(struct client *c, struct window_pane *wp)
1896
0
{
1897
0
  redraw_draw(c, wp, REDRAW_PANE|REDRAW_PANE_SCROLLBAR);
1898
0
  if (c->session->curw->window->menu != NULL)
1899
0
    redraw_draw(c, NULL, REDRAW_MENU);
1900
0
}
1901
1902
/* Draw a pane's scrollbar. */
1903
void
1904
redraw_pane_scrollbar(struct client *c, struct window_pane *wp)
1905
0
{
1906
0
  redraw_draw(c, wp, REDRAW_PANE_SCROLLBAR);
1907
0
}