Coverage Report

Created: 2026-10-17 06:19

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.170 2026/10/16 14:26:08 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 is 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
/* Draw phases. */
76
enum redraw_phase {
77
  REDRAW_TEXT,
78
  REDRAW_IMAGES
79
};
80
0
#define REDRAW_PHASE_COUNT 2
81
82
/* Border connections to adjacent cells. */
83
0
#define REDRAW_BORDER_L 0x1
84
0
#define REDRAW_BORDER_R 0x2
85
0
#define REDRAW_BORDER_U 0x4
86
0
#define REDRAW_BORDER_D 0x8
87
88
/* Span flags. */
89
0
#define REDRAW_BORDER_IS_ARROW 0x1
90
0
#define REDRAW_SCROLLBAR_LEFT 0x2
91
0
#define REDRAW_SCROLLBAR_RIGHT 0x4
92
0
#define REDRAW_SCROLLBAR_OVERLAY 0x8
93
94
/* Draw operations. */
95
0
#define REDRAW_PANE 0x1
96
0
#define REDRAW_OUTSIDE 0x2
97
0
#define REDRAW_EMPTY 0x4
98
0
#define REDRAW_PANE_BORDER 0x8
99
0
#define REDRAW_PANE_STATUS 0x10
100
0
#define REDRAW_PANE_SCROLLBAR 0x20
101
0
#define REDRAW_STATUS 0x40
102
0
#define REDRAW_MENU 0x80
103
0
#define REDRAW_SIDE_STATUS 0x100
104
105
/* Draw everything. */
106
0
#define REDRAW_ALL 0x7fffffff
107
0
#define REDRAW_IS_ALL(flags) ((flags) == REDRAW_ALL)
108
109
/* UTF-8 isolate characters. */
110
0
#define REDRAW_START_ISOLATE "\342\201\246"
111
0
#define REDRAW_END_ISOLATE "\342\201\251"
112
113
/* Data for a span. */
114
struct redraw_span_data {
115
  enum redraw_span_type type;
116
117
  union {
118
    struct {
119
      /* Pane this span belongs to. */
120
      struct window_pane  *wp;
121
122
      /* Position of span inside the pane. */
123
      u_int      px;
124
      u_int      py;
125
    } p; /* pane */
126
127
    struct {
128
      /* Adjacent panes on each side. */
129
      struct window_pane  *top_wp;
130
      struct window_pane  *bottom_wp;
131
      struct window_pane  *left_wp;
132
      struct window_pane  *right_wp;
133
134
      /*
135
       * The pane this span belongs if that is known when the
136
       * scene is built. This is used for the half coloured
137
       * active pane indicator.
138
       */
139
      struct window_pane  *style_wp;
140
141
      /* Cell type for this span. */
142
      int      cell_type;
143
      int      cell_mask;
144
145
      /* Line styles for this span. */
146
      enum pane_lines    top_lines;
147
      enum pane_lines    bottom_lines;
148
      enum pane_lines    left_lines;
149
      enum pane_lines    right_lines;
150
151
      /* Flags for this span. */
152
      int      flags;
153
    } b; /* pane border */
154
155
    struct {
156
      /* The pane and the offset into the status line. */
157
      struct window_pane  *wp;
158
      u_int      offset;
159
      int      cell_type;
160
    } st; /* pane status line */
161
162
    struct {
163
      /* Pane this span belongs to. */
164
      struct window_pane  *wp;
165
166
      /* Line within the scrollbar. */
167
      u_int      y;
168
169
      /* Full height of scrollbar. */
170
      u_int      height;
171
172
      /* Flags for this span. */
173
      int      flags;
174
    } sb; /* pane scrollbar */
175
176
    struct {
177
      /* Menu this span belongs to. */
178
      struct menu_data  *md;
179
180
      /* Position of span inside the menu. */
181
      u_int      px;
182
      u_int      py;
183
    } m; /* menu */
184
  };
185
};
186
187
/* A span of cells of the same type inside a line. */
188
struct redraw_span {
189
  u_int       x;
190
  u_int       width;
191
  struct redraw_span_data   data;
192
193
  TAILQ_ENTRY(redraw_span)  entry;
194
};
195
TAILQ_HEAD(redraw_spans, redraw_span);
196
197
/* A visible line on the client. */
198
struct redraw_line {
199
  struct redraw_spans spans[REDRAW_SPAN_TYPES];
200
};
201
202
/* A scene representing all the spans on the client. */
203
struct redraw_scene {
204
  struct client   *c;
205
  struct window   *w;
206
  struct redraw_line  *lines;
207
208
  uint64_t     generation;
209
  u_int      sx;
210
  u_int      sy;
211
  u_int      ox;
212
  u_int      oy;
213
};
214
215
/* A damaged rectangle in a window. */
216
struct redraw_damage {
217
  u_int        x;
218
  u_int        y;
219
  u_int        sx;
220
  u_int        sy;
221
222
  int        flags;
223
0
#define REDRAW_DAMAGE_SCROLL 0x1
224
225
  TAILQ_ENTRY(redraw_damage)   entry;
226
};
227
228
/*
229
 * If there are more damage rectangles than this, they are collapsed into
230
 * one.
231
 */
232
174
#define REDRAW_DAMAGE_MAX 16
233
234
/* Cell for building the scene. */
235
struct redraw_build_cell {
236
  struct redraw_span_data  data;
237
};
238
static struct redraw_build_cell *redraw_cells;
239
static size_t      redraw_ncells;
240
241
/*
242
 * We can reuse the same pane status lines during one damage redraw, but when
243
 * we enter a new one, the client or format variables may have changed, so we
244
 * need to make them again. The generation is increased so this happens.
245
 */
246
static u_int  redraw_status_generation;
247
248
/* Context for building the scene. */
249
struct redraw_build_ctx {
250
  struct client       *c;
251
  struct window       *w;
252
253
  u_int          ox;
254
  u_int          oy;
255
  u_int          sx;
256
  u_int          sy;
257
258
  int          ind;
259
260
  struct redraw_build_cell    *cells;
261
};
262
263
/* Context for redrawing. */
264
struct redraw_draw_ctx {
265
  struct redraw_scene *scene;
266
267
  struct window_pane  *active;
268
  struct window_pane  *marked;
269
270
  u_int      status_lines;
271
  u_int      side_left;
272
  enum pane_lines    pane_lines;
273
  struct grid_cell   default_gc;
274
275
  int      flags;
276
0
#define REDRAW_ISOLATES 0x1
277
0
#define REDRAW_DEFAULT_SET 0x2
278
0
#define REDRAW_STATUS_TOP 0x4
279
};
280
281
/* Make redraw flags into a string. */
282
static const char *
283
redraw_flags_to_string(int flags)
284
0
{
285
0
  static char s[128];
286
287
0
  *s = '\0';
288
0
  if (flags & REDRAW_STATUS)
289
0
    strlcat(s, "status ", sizeof s);
290
0
  if (flags & REDRAW_PANE)
291
0
    strlcat(s, "pane ", sizeof s);
292
0
  if (flags & REDRAW_PANE_BORDER)
293
0
    strlcat(s, "border ", sizeof s);
294
0
  if (flags & REDRAW_PANE_STATUS)
295
0
    strlcat(s, "pane-status ", sizeof s);
296
0
  if (flags & REDRAW_PANE_SCROLLBAR)
297
0
    strlcat(s, "scrollbar ", sizeof s);
298
0
  if (flags & REDRAW_MENU)
299
0
    strlcat(s, "menu ", sizeof s);
300
0
  if (REDRAW_IS_ALL(flags))
301
0
    strlcat(s, "all ", sizeof s);
302
0
  if (*s != '\0')
303
0
    s[strlen(s) - 1] = '\0';
304
0
  return (s);
305
0
}
306
307
/* Get window offset and expand size to cover any part outside the window. */
308
static void
309
redraw_get_window_offset(struct client *c, u_int *ox, u_int *oy, u_int *sx,
310
    u_int *sy)
311
0
{
312
0
  u_int tty_sx, tty_sy;
313
314
0
  tty_window_offset(&c->tty, ox, oy, sx, sy);
315
316
0
  tty_sx = c->tty.sx - status_side_size(c);
317
0
  tty_sy = c->tty.sy - status_line_size(c);
318
0
  if (*sx < tty_sx)
319
0
    *sx = tty_sx;
320
0
  if (*sy < tty_sy)
321
0
    *sy = tty_sy;
322
0
}
323
324
/* Initialize the context for building scene. */
325
static void
326
redraw_set_context(struct client *c, struct redraw_build_ctx *bctx)
327
0
{
328
0
  struct session  *s = c->session;
329
0
  struct window *w = s->curw->window;
330
331
0
  memset(bctx, 0, sizeof *bctx);
332
0
  bctx->c = c;
333
0
  bctx->w = w;
334
0
  redraw_get_window_offset(c, &bctx->ox, &bctx->oy, &bctx->sx, &bctx->sy);
335
336
0
  bctx->ind = options_get_number(w->options, "pane-border-indicators");
337
0
}
338
339
/* Return a cell. */
340
static struct redraw_build_cell *
341
redraw_get_build_cell(struct redraw_build_ctx *bctx, u_int x, u_int y)
342
0
{
343
0
  return (&bctx->cells[(y * bctx->sx) + x]);
344
0
}
345
346
/* Reset cell to either empty or outside the window. */
347
static void
348
redraw_reset_cell(struct redraw_build_ctx *bctx, u_int x, u_int y)
349
0
{
350
0
  struct redraw_build_cell  *bc = redraw_get_build_cell(bctx, x, y);
351
0
  struct window     *w = bctx->w;
352
353
0
  memset(bc, 0, sizeof *bc);
354
0
  if (bctx->ox + x < w->sx && bctx->oy + y < w->sy)
355
0
    bc->data.type = REDRAW_SPAN_EMPTY;
356
0
  else
357
0
    bc->data.type = REDRAW_SPAN_OUTSIDE;
358
0
}
359
360
/* Convert window position to scene position. Return 0 if outside the scene. */
361
static int
362
redraw_window_to_scene(struct redraw_build_ctx *bctx, int wx, int wy,
363
    u_int *x, u_int *y)
364
0
{
365
0
  int sx, sy;
366
367
0
  if (wx < 0 || wy < 0)
368
0
    return (0);
369
0
  if ((u_int)wx > bctx->w->sx || (u_int)wy > bctx->w->sy)
370
0
    return (0);
371
0
  if (wx < (int)bctx->ox || wy < (int)bctx->oy)
372
0
    return (0);
373
374
0
  sx = wx - (int)bctx->ox;
375
0
  sy = wy - (int)bctx->oy;
376
377
0
  if ((u_int)sx >= bctx->sx || (u_int)sy >= bctx->sy)
378
0
    return (0);
379
0
  *x = sx;
380
0
  *y = sy;
381
0
  return (1);
382
0
}
383
384
/*
385
 * Convert pane position to scene position. Return 0 if outside the scene. A
386
 * floating pane is clipped to the window edge.
387
 */
388
static int
389
redraw_pane_to_scene(struct redraw_build_ctx *bctx, struct window_pane *wp,
390
    int px, int py, u_int *x, u_int *y)
391
0
{
392
0
  int wx = wp->xoff + px, wy = wp->yoff + py;
393
0
  int left, right, top, bottom;
394
395
0
  if (window_pane_is_floating(wp)) {
396
0
    left = wp->xoff - 1;
397
0
    right = wp->xoff + wp->sx;
398
0
    top = wp->yoff - 1;
399
0
    bottom = wp->yoff + wp->sy;
400
401
0
    if (left < 0 && wx < 0)
402
0
      return (0);
403
0
    if (right > (int)bctx->w->sx && wx >= (int)bctx->w->sx)
404
0
      return (0);
405
0
    if (top < 0 && wy < 0)
406
0
      return (0);
407
0
    if (bottom > (int)bctx->w->sy && wy >= (int)bctx->w->sy)
408
0
      return (0);
409
0
  }
410
0
  return (redraw_window_to_scene(bctx, wx, wy, x, y));
411
0
}
412
413
/* Convert redraw border mask to a border cell type. */
414
static int
415
redraw_get_cell_type(int mask)
416
0
{
417
0
  switch (mask) {
418
0
  case REDRAW_BORDER_L|REDRAW_BORDER_R|REDRAW_BORDER_U|REDRAW_BORDER_D:
419
0
    return (CELL_LRUD);
420
0
  case REDRAW_BORDER_L|REDRAW_BORDER_R|REDRAW_BORDER_U:
421
0
    return (CELL_LRU);
422
0
  case REDRAW_BORDER_L|REDRAW_BORDER_R|REDRAW_BORDER_D:
423
0
    return (CELL_LRD);
424
0
  case REDRAW_BORDER_L|REDRAW_BORDER_R:
425
0
  case REDRAW_BORDER_L:
426
0
  case REDRAW_BORDER_R:
427
0
    return (CELL_LR);
428
0
  case REDRAW_BORDER_L|REDRAW_BORDER_U|REDRAW_BORDER_D:
429
0
    return (CELL_ULD);
430
0
  case REDRAW_BORDER_L|REDRAW_BORDER_U:
431
0
    return (CELL_LU);
432
0
  case REDRAW_BORDER_L|REDRAW_BORDER_D:
433
0
    return (CELL_LD);
434
0
  case REDRAW_BORDER_R|REDRAW_BORDER_U|REDRAW_BORDER_D:
435
0
    return (CELL_URD);
436
0
  case REDRAW_BORDER_R|REDRAW_BORDER_U:
437
0
    return (CELL_RU);
438
0
  case REDRAW_BORDER_R|REDRAW_BORDER_D:
439
0
    return (CELL_RD);
440
0
  case REDRAW_BORDER_U|REDRAW_BORDER_D:
441
0
  case REDRAW_BORDER_U:
442
0
  case REDRAW_BORDER_D:
443
0
    return (CELL_UD);
444
0
  }
445
0
  return (CELL_NONE);
446
0
}
447
448
/* Return if there are two panes for the border colour indicator. */
449
static int
450
redraw_check_two_pane_colours(struct window *w, enum layout_type *type)
451
0
{
452
0
  struct window_pane  *wp;
453
0
  u_int      count = 0;
454
455
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
456
0
    if (window_pane_is_floating(wp) || wp->layout_cell == NULL)
457
0
      continue;
458
0
    count++;
459
0
    if (count > 2 || wp->layout_cell->parent == NULL)
460
0
      return (0);
461
0
    *type = wp->layout_cell->parent->type;
462
0
  }
463
0
  return (count == 2);
464
0
}
465
466
/* Mark pane inside data. */
467
static void
468
redraw_mark_pane_inside(struct redraw_build_ctx *bctx, struct window_pane *wp)
469
0
{
470
0
  struct redraw_build_cell  *bc;
471
0
  u_int        px, py, x, y;
472
473
0
  for (py = 0; py < wp->sy; py++) {
474
0
    for (px = 0; px < wp->sx; px++) {
475
0
      if (!redraw_pane_to_scene(bctx, wp, px, py, &x, &y))
476
0
        continue;
477
0
      bc = redraw_get_build_cell(bctx, x, y);
478
0
      memset(bc, 0, sizeof *bc);
479
0
      bc->data.type = REDRAW_SPAN_PANE;
480
0
      bc->data.p.wp = wp;
481
0
      bc->data.p.px = px;
482
0
      bc->data.p.py = py;
483
0
    }
484
0
  }
485
0
}
486
487
/* Mark scrollbar data. */
488
static void
489
redraw_mark_pane_scrollbar(struct redraw_build_ctx *bctx,
490
    struct window_pane *wp, int sb_w, int sb_left, int overlay)
491
0
{
492
0
  struct redraw_build_cell  *bc;
493
0
  u_int        x, y;
494
0
  int        wx, wy, sx, ex;
495
0
  u_int        sy;
496
497
0
  if (sb_w == 0)
498
0
    return;
499
500
0
  if (overlay && sb_left) {
501
0
    sx = wp->xoff;
502
0
    ex = sx + sb_w - 1;
503
0
  } else if (overlay) {
504
0
    ex = wp->xoff + (int)wp->sx - 1;
505
0
    sx = ex - sb_w + 1;
506
0
  } else if (sb_left) {
507
0
    sx = wp->xoff - sb_w;
508
0
    ex = wp->xoff - 1;
509
0
  } else {
510
0
    sx = wp->xoff + (int)wp->sx;
511
0
    ex = sx + sb_w - 1;
512
0
  }
513
514
0
  for (sy = 0; sy < wp->sy; sy++) {
515
0
    wy = wp->yoff + (int)sy;
516
0
    for (wx = sx; wx <= ex; wx++) {
517
0
      if (!redraw_window_to_scene(bctx, wx, wy, &x,
518
0
          &y))
519
0
        continue;
520
0
      bc = redraw_get_build_cell(bctx, x, y);
521
0
      memset(bc, 0, sizeof *bc);
522
0
      bc->data.type = REDRAW_SPAN_SCROLLBAR;
523
0
      bc->data.sb.wp = wp;
524
0
      bc->data.sb.y = sy;
525
0
      bc->data.sb.height = wp->sy;
526
0
      if (sb_left)
527
0
        bc->data.sb.flags |= REDRAW_SCROLLBAR_LEFT;
528
0
      else
529
0
        bc->data.sb.flags |= REDRAW_SCROLLBAR_RIGHT;
530
0
      if (overlay)
531
0
        bc->data.sb.flags |= REDRAW_SCROLLBAR_OVERLAY;
532
0
    }
533
0
  }
534
0
}
535
536
/*
537
 * Return if span data belongs to pane, that is: is the cell adjacent to this
538
 * pane?
539
 */
540
static int
541
redraw_data_has_pane(struct redraw_span_data *data, struct window_pane *wp)
542
0
{
543
0
  if (data->b.top_wp == wp)
544
0
    return (1);
545
0
  if (data->b.bottom_wp == wp)
546
0
    return (1);
547
0
  if (data->b.left_wp == wp)
548
0
    return (1);
549
0
  if (data->b.right_wp == wp)
550
0
    return (1);
551
0
  return (0);
552
0
}
553
554
/*
555
 * Mark one border cell. If a non-border cell is marked as a border, replace
556
 * it. If it is already a border and this is not a floating pane, merge the
557
 * border mask and pane ownership.
558
 */
559
static void
560
redraw_mark_border_cell(struct redraw_build_ctx *bctx, int wx, int wy,
561
    struct window_pane *wp, int top_owner, int bottom_owner, int mask,
562
    enum pane_lines pane_lines, int floating)
563
0
{
564
0
  struct redraw_build_cell  *bc;
565
0
  u_int        x, y;
566
0
  int        reset = 0;
567
568
0
  if (!redraw_window_to_scene(bctx, wx, wy, &x, &y))
569
0
    return;
570
0
  bc = redraw_get_build_cell(bctx, x, y);
571
572
  /*
573
   * If this is a tiled pane, only empty and border cells may be marked.
574
   * Border cells are merged and empty cells are updated.
575
   *
576
   * Floating panes may mark any existing cell type. All cells are reset
577
   * except borders that already belong to this pane, they need to be
578
   * merged.
579
   */
580
0
  if (!floating) {
581
0
    if (bc->data.type == REDRAW_SPAN_EMPTY ||
582
0
        bc->data.type == REDRAW_SPAN_OUTSIDE)
583
0
      reset = 1;
584
0
    else if (bc->data.type != REDRAW_SPAN_BORDER)
585
0
      return;
586
0
  } else {
587
0
    if (bc->data.type != REDRAW_SPAN_BORDER ||
588
0
        !redraw_data_has_pane(&bc->data, wp))
589
0
      reset = 1;
590
0
  }
591
0
  if (reset) {
592
0
    memset(bc, 0, sizeof *bc);
593
0
    bc->data.type = REDRAW_SPAN_BORDER;
594
0
  }
595
596
0
  if (top_owner) {
597
0
    bc->data.b.top_wp = wp;
598
0
    bc->data.b.top_lines = pane_lines;
599
0
  }
600
0
  if (bottom_owner) {
601
0
    bc->data.b.bottom_wp = wp;
602
0
    bc->data.b.bottom_lines = pane_lines;
603
0
  }
604
605
0
  if (mask & (REDRAW_BORDER_U|REDRAW_BORDER_D)) {
606
0
    if (wx < wp->xoff) {
607
0
      bc->data.b.right_wp = wp;
608
0
      bc->data.b.right_lines = pane_lines;
609
0
    } else if (wx >= wp->xoff + (int)wp->sx) {
610
0
      bc->data.b.left_wp = wp;
611
0
      bc->data.b.left_lines = pane_lines;
612
0
    }
613
0
  }
614
615
0
  mask |= bc->data.b.cell_mask;
616
0
  bc->data.b.cell_mask = mask;
617
0
  bc->data.b.cell_type = redraw_get_cell_type(mask);
618
0
}
619
620
/*
621
 * Mark border cells for a pane status line, keeping the border cell type for
622
 * drawing.
623
 */
624
static void
625
redraw_mark_border_status(struct redraw_build_ctx *bctx, struct window_pane *wp,
626
    __unused int left, int right, int top, int bottom)
627
0
{
628
0
  struct redraw_build_cell  *bc;
629
0
  u_int        x, y, off = 0;
630
0
  int        pane_status, wy, sx, ex, wx, cell_type;
631
632
0
  pane_status = window_pane_get_pane_status(wp);
633
0
  if (pane_status == PANE_STATUS_OFF)
634
0
    return;
635
0
  if (pane_status == PANE_STATUS_TOP)
636
0
    wy = top;
637
0
  else
638
0
    wy = bottom;
639
640
0
  sx = wp->xoff + 2;
641
0
  ex = right - 1;
642
0
  if (sx > ex)
643
0
    return;
644
645
0
  for (wx = sx; wx <= ex; wx++, off++) {
646
0
    if (!redraw_window_to_scene(bctx, wx, wy, &x, &y))
647
0
      continue;
648
0
    bc = redraw_get_build_cell(bctx, x, y);
649
0
    if (bc->data.type != REDRAW_SPAN_BORDER)
650
0
      continue;
651
0
    cell_type = bc->data.b.cell_type;
652
653
0
    bc->data.type = REDRAW_SPAN_STATUS;
654
0
    bc->data.st.wp = wp;
655
0
    bc->data.st.offset = off;
656
0
    bc->data.st.cell_type = cell_type;
657
0
  }
658
0
}
659
660
/* Mark existing border cells where indicator arrows will be drawn. */
661
static void
662
redraw_mark_border_arrows(struct redraw_build_ctx *bctx, struct window_pane *wp,
663
    int left, int right, int top, int bottom)
664
0
{
665
0
  struct redraw_build_cell  *bc;
666
0
  u_int        x, y;
667
0
  int        wx, wy;
668
669
0
  if (bctx->ind != PANE_BORDER_ARROWS && bctx->ind != PANE_BORDER_BOTH)
670
0
    return;
671
672
0
  wx = wp->xoff + 1;
673
0
  if (wx >= left && wx <= right) {
674
0
    wy = top;
675
0
    if (redraw_window_to_scene(bctx, wx, wy, &x, &y)) {
676
0
      bc = redraw_get_build_cell(bctx, x, y);
677
0
      if (bc->data.type == REDRAW_SPAN_BORDER)
678
0
        bc->data.b.flags |= REDRAW_BORDER_IS_ARROW;
679
0
    }
680
0
    wy = bottom;
681
0
    if (redraw_window_to_scene(bctx, wx, wy, &x, &y)) {
682
0
      bc = redraw_get_build_cell(bctx, x, y);
683
0
      if (bc->data.type == REDRAW_SPAN_BORDER)
684
0
        bc->data.b.flags |= REDRAW_BORDER_IS_ARROW;
685
0
    }
686
0
  }
687
688
0
  wy = wp->yoff + 1;
689
0
  if (wy >= top && wy <= bottom) {
690
0
    wx = left;
691
0
    if (redraw_window_to_scene(bctx, wx, wy, &x, &y)) {
692
0
      bc = redraw_get_build_cell(bctx, x, y);
693
0
      if (bc->data.type == REDRAW_SPAN_BORDER)
694
0
        bc->data.b.flags |= REDRAW_BORDER_IS_ARROW;
695
0
    }
696
0
    wx = right;
697
0
    if (redraw_window_to_scene(bctx, wx, wy, &x, &y)) {
698
0
      bc = redraw_get_build_cell(bctx, x, y);
699
0
      if (bc->data.type == REDRAW_SPAN_BORDER)
700
0
        bc->data.b.flags |= REDRAW_BORDER_IS_ARROW;
701
0
    }
702
0
  }
703
0
}
704
705
/* Mark pane borders. */
706
static void
707
redraw_mark_pane_borders(struct redraw_build_ctx *bctx, struct window_pane *wp,
708
    int sb_w, int sb_left)
709
0
{
710
0
  enum pane_lines pane_lines = window_pane_get_pane_lines(wp);
711
0
  int   pane_status, left, right, top, bottom, wx, wy;
712
0
  int   mark_top, mark_bottom, mark_left, mark_right, mask = 0;
713
0
  int   floating = window_pane_is_floating(wp);
714
715
0
  if (floating && pane_lines == PANE_LINES_NONE)
716
0
    return;
717
0
  pane_status = window_pane_get_pane_status(wp);
718
719
0
  left = wp->xoff - 1;
720
0
  right = wp->xoff + wp->sx;
721
0
  if (sb_w != 0) {
722
0
    if (sb_left)
723
0
      left -= sb_w;
724
0
    else
725
0
      right += sb_w;
726
0
  }
727
0
  top = wp->yoff - 1;
728
0
  bottom = wp->yoff + wp->sy;
729
730
0
  mark_left = (left >= 0);
731
0
  mark_top = (top >= 0);
732
733
0
  if (floating) {
734
0
    mark_right = (right < (int)bctx->w->sx);
735
0
    mark_bottom = (bottom < (int)bctx->w->sy);
736
0
    if (left < 0)
737
0
      left = 0;
738
0
    if (right >= (int)bctx->w->sx)
739
0
      right = (int)bctx->w->sx - 1;
740
0
    if (top < 0)
741
0
      top = 0;
742
0
    if (bottom >= (int)bctx->w->sy)
743
0
      bottom = (int)bctx->w->sy - 1;
744
0
  } else {
745
0
    mark_right = (right <= (int)bctx->w->sx);
746
0
    mark_bottom = (bottom <= (int)bctx->w->sy);
747
0
    if (pane_status == PANE_STATUS_TOP && bottom < (int)bctx->w->sy)
748
0
      mark_bottom = 0;
749
0
    else if (pane_status == PANE_STATUS_BOTTOM)
750
0
      mark_top = 0;
751
0
  }
752
753
0
  if (mark_top) {
754
0
    for (wx = left; wx <= right; wx++) {
755
0
      mask = 0;
756
0
      if (wx > left)
757
0
        mask |= REDRAW_BORDER_L;
758
0
      if (wx < right)
759
0
        mask |= REDRAW_BORDER_R;
760
0
      redraw_mark_border_cell(bctx, wx, top, wp, 0, 1, mask,
761
0
          pane_lines, floating);
762
0
    }
763
0
  }
764
0
  if (mark_bottom) {
765
0
    for (wx = left; wx <= right; wx++) {
766
0
      mask = 0;
767
0
      if (wx > left)
768
0
        mask |= REDRAW_BORDER_L;
769
0
      if (wx < right)
770
0
        mask |= REDRAW_BORDER_R;
771
0
      redraw_mark_border_cell(bctx, wx, bottom, wp, 1, 0,
772
0
          mask, pane_lines, floating);
773
0
    }
774
0
  }
775
0
  if (mark_left) {
776
0
    for (wy = top; wy <= bottom; wy++) {
777
0
      mask = 0;
778
0
      if (wy > top)
779
0
        mask |= REDRAW_BORDER_U;
780
0
      if (wy < bottom)
781
0
        mask |= REDRAW_BORDER_D;
782
0
      redraw_mark_border_cell(bctx, left, wy, wp, 0, 0, mask,
783
0
          pane_lines, floating);
784
0
    }
785
0
  }
786
0
  if (mark_right) {
787
0
    for (wy = top; wy <= bottom; wy++) {
788
0
      mask = 0;
789
0
      if (wy > top)
790
0
        mask |= REDRAW_BORDER_U;
791
0
      if (wy < bottom)
792
0
        mask |= REDRAW_BORDER_D;
793
0
      redraw_mark_border_cell(bctx, right, wy, wp, 0, 0, mask,
794
0
          pane_lines, floating);
795
0
    }
796
0
  }
797
798
0
  redraw_mark_border_status(bctx, wp, left, right, top, bottom);
799
0
  redraw_mark_border_arrows(bctx, wp, left, right, top, bottom);
800
0
}
801
802
/*
803
 * Mark an entire pane in the build grid. Floating panes overwrite anything
804
 * already below them.
805
 */
806
static void
807
redraw_mark_pane(struct redraw_build_ctx *bctx, struct window_pane *wp)
808
0
{
809
0
  struct style  *sb_style = &wp->scrollbar_style;
810
0
  int    sb_w = 0, sb_left = 0, overlay = 0;
811
812
0
  if (!window_pane_is_visible(wp))
813
0
    return;
814
815
0
  if (window_pane_scrollbar_visible(wp)) {
816
0
    overlay = window_pane_scrollbar_overlay(wp);
817
0
    if (!overlay)
818
0
      sb_w = sb_style->width + sb_style->pad;
819
0
    else {
820
0
      sb_w = sb_style->width + sb_style->pad;
821
0
      if (sb_w > (int)wp->sx) {
822
0
        sb_w = sb_style->width;
823
0
        if (sb_w > (int)wp->sx)
824
0
          sb_w = wp->sx;
825
0
      }
826
0
    }
827
0
  }
828
0
  if (sb_w != 0 && bctx->w->sb_pos == PANE_SCROLLBARS_LEFT)
829
0
    sb_left = 1;
830
831
0
  redraw_mark_pane_inside(bctx, wp);
832
0
  redraw_mark_pane_borders(bctx, wp, overlay ? 0 : sb_w, sb_left);
833
0
  redraw_mark_pane_scrollbar(bctx, wp, sb_w, sb_left, overlay);
834
0
}
835
836
/* Choose the pane that will provide the border style for two-pane layouts. */
837
static void
838
redraw_mark_two_pane_colours(struct redraw_build_ctx *bctx)
839
0
{
840
0
  struct redraw_build_cell  *bc;
841
0
  struct redraw_span_data   *sd;
842
0
  enum layout_type     type;
843
0
  u_int        x, y, wx, wy;
844
845
0
  if (bctx->ind != PANE_BORDER_COLOUR && bctx->ind != PANE_BORDER_BOTH)
846
0
    return;
847
0
  if (!redraw_check_two_pane_colours(bctx->w, &type))
848
0
    return;
849
850
0
  for (y = 0; y < bctx->sy; y++) {
851
0
    for (x = 0; x < bctx->sx; x++) {
852
0
      bc = redraw_get_build_cell(bctx, x, y);
853
0
      if (bc->data.type != REDRAW_SPAN_BORDER)
854
0
        continue;
855
0
      sd = &bc->data;
856
857
0
      wx = bctx->ox + x;
858
0
      wy = bctx->oy + y;
859
860
0
      if (type == LAYOUT_LEFTRIGHT &&
861
0
          sd->b.left_wp != NULL &&
862
0
          sd->b.right_wp != NULL) {
863
0
        if (wy <= bctx->w->sy / 2)
864
0
          sd->b.style_wp = sd->b.left_wp;
865
0
        else
866
0
          sd->b.style_wp = sd->b.right_wp;
867
0
      } else if (type == LAYOUT_TOPBOTTOM &&
868
0
          sd->b.top_wp != NULL &&
869
0
          sd->b.bottom_wp != NULL) {
870
0
        if (wx <= bctx->w->sx / 2)
871
0
          sd->b.style_wp = sd->b.top_wp;
872
0
        else
873
0
          sd->b.style_wp = sd->b.bottom_wp;
874
0
      }
875
0
    }
876
0
  }
877
0
}
878
879
/* Mark the window menu above all panes. */
880
static void
881
redraw_mark_menu(struct redraw_build_ctx *bctx)
882
0
{
883
0
  struct menu_data    *md = bctx->w->menu;
884
0
  struct redraw_build_cell  *bc;
885
0
  u_int        px, py, x, y, sx, sy;
886
887
0
  if (md == NULL)
888
0
    return;
889
890
0
  sx = menu_width(md);
891
0
  sy = menu_height(md);
892
0
  for (py = 0; py < sy; py++) {
893
0
    for (px = 0; px < sx; px++) {
894
0
      if (!redraw_window_to_scene(bctx, menu_x(md) + px,
895
0
          menu_y(md) + py, &x, &y))
896
0
        continue;
897
0
      bc = redraw_get_build_cell(bctx, x, y);
898
0
      memset(bc, 0, sizeof *bc);
899
0
      bc->data.type = REDRAW_SPAN_MENU;
900
0
      bc->data.m.md = md;
901
0
      bc->data.m.px = px;
902
0
      bc->data.m.py = py;
903
0
    }
904
0
  }
905
0
}
906
907
/* Return true if two adjacent build cells can be joined into one span. */
908
static int
909
redraw_compare_data(struct redraw_build_cell *a, struct redraw_build_cell *b)
910
0
{
911
0
  struct redraw_span_data *ad = &a->data, *bd = &b->data;
912
913
0
  if (ad->type != bd->type)
914
0
    return (0);
915
916
0
  switch (ad->type) {
917
0
  case REDRAW_SPAN_PANE:
918
0
    if (ad->p.wp != bd->p.wp ||
919
0
        ad->p.py != bd->p.py ||
920
0
        ad->p.px + 1 != bd->p.px)
921
0
      return (0);
922
0
    return (1);
923
0
  case REDRAW_SPAN_BORDER:
924
0
    if (ad->b.top_wp != bd->b.top_wp ||
925
0
        ad->b.bottom_wp != bd->b.bottom_wp ||
926
0
        ad->b.left_wp != bd->b.left_wp ||
927
0
        ad->b.right_wp != bd->b.right_wp ||
928
0
        ad->b.style_wp != bd->b.style_wp ||
929
0
        ad->b.top_lines != bd->b.top_lines ||
930
0
        ad->b.bottom_lines != bd->b.bottom_lines ||
931
0
        ad->b.left_lines != bd->b.left_lines ||
932
0
        ad->b.right_lines != bd->b.right_lines ||
933
0
        ad->b.cell_type != bd->b.cell_type ||
934
0
        ad->b.cell_mask != bd->b.cell_mask ||
935
0
        ad->b.flags != bd->b.flags)
936
0
      return (0);
937
0
    if (ad->b.flags & REDRAW_BORDER_IS_ARROW)
938
0
      return (0);
939
0
    return (1);
940
0
  case REDRAW_SPAN_STATUS:
941
0
    if (ad->st.wp != bd->st.wp ||
942
0
        ad->st.offset + 1 != bd->st.offset ||
943
0
        ad->st.cell_type != bd->st.cell_type)
944
0
      return (0);
945
0
    return (1);
946
0
  case REDRAW_SPAN_SCROLLBAR:
947
0
    if (ad->sb.wp != bd->sb.wp ||
948
0
        ad->sb.y != bd->sb.y ||
949
0
        ad->sb.height != bd->sb.height ||
950
0
        ad->sb.flags != bd->sb.flags)
951
0
      return (0);
952
0
    return (1);
953
0
  case REDRAW_SPAN_MENU:
954
0
    if (ad->m.md != bd->m.md ||
955
0
        ad->m.py != bd->m.py ||
956
0
        ad->m.px + 1 != bd->m.px)
957
0
      return (0);
958
0
    return (1);
959
0
  case REDRAW_SPAN_OUTSIDE:
960
0
  case REDRAW_SPAN_EMPTY:
961
0
    return (1);
962
0
  }
963
0
  return (0);
964
0
}
965
966
/* Build the temporary cells for a redraw scene. */
967
static void
968
redraw_build_cells(struct redraw_build_ctx *bctx)
969
0
{
970
0
  struct window   *w = bctx->w;
971
0
  struct window_pane  *wp;
972
0
  size_t       ncells;
973
0
  u_int      x, y;
974
975
0
  if (bctx->sx != 0 && bctx->sy > SIZE_MAX / bctx->sx)
976
0
    fatalx("%s: too many cells", __func__);
977
0
  ncells = (size_t)bctx->sx * bctx->sy;
978
0
  if (ncells > redraw_ncells) {
979
0
    redraw_cells = xreallocarray(redraw_cells, ncells,
980
0
        sizeof *redraw_cells);
981
0
    redraw_ncells = ncells;
982
0
  }
983
984
0
  bctx->cells = redraw_cells;
985
0
  for (y = 0; y < bctx->sy; y++) {
986
0
    for (x = 0; x < bctx->sx; x++)
987
0
      redraw_reset_cell(bctx, x, y);
988
0
  }
989
990
0
  TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry)
991
0
    redraw_mark_pane(bctx, wp);
992
0
  redraw_mark_two_pane_colours(bctx);
993
0
  redraw_mark_menu(bctx);
994
0
}
995
996
/*
997
 * Build and return a redraw scene for a client. The caller owns the scene and
998
 * must free it with redraw_free_scene.
999
 */
1000
static struct redraw_scene *
1001
redraw_make_scene(struct client *c)
1002
0
{
1003
0
  struct session      *s = c->session;
1004
0
  struct window     *w = s->curw->window;
1005
0
  struct redraw_build_ctx    bctx;
1006
0
  struct redraw_scene   *scene;
1007
0
  struct redraw_build_cell  *bc, *last;
1008
0
  struct redraw_line    *line;
1009
0
  struct redraw_span    *span;
1010
0
  enum redraw_span_type    type;
1011
0
  u_int        x, y, x0;
1012
1013
0
  if (c->flags & CLIENT_SUSPENDED)
1014
0
    return (NULL);
1015
1016
0
  redraw_set_context(c, &bctx);
1017
1018
0
  log_debug("%s: building @%u scene (%ux%u %u,%u; generation %llu)",
1019
0
      c->name, w->id, bctx.sx, bctx.sy, bctx.ox, bctx.oy,
1020
0
      (unsigned long long)w->redraw_scene_generation);
1021
1022
0
  redraw_build_cells(&bctx);
1023
1024
0
  scene = xcalloc(1, sizeof *scene);
1025
0
  scene->c = c;
1026
0
  scene->w = w;
1027
0
  scene->lines = xcalloc(bctx.sy, sizeof *scene->lines);
1028
0
  scene->generation = w->redraw_scene_generation;
1029
0
  scene->sx = bctx.sx;
1030
0
  scene->sy = bctx.sy;
1031
0
  scene->ox = bctx.ox;
1032
0
  scene->oy = bctx.oy;
1033
1034
0
  for (y = 0; y < bctx.sy; y++) {
1035
0
    line = &scene->lines[y];
1036
0
    for (type = 0; type < REDRAW_SPAN_TYPES; type++)
1037
0
      TAILQ_INIT(&line->spans[type]);
1038
1039
0
    x = 0;
1040
0
    while (x < bctx.sx) {
1041
0
      x0 = x;
1042
0
      last = redraw_get_build_cell(&bctx, x, y);
1043
0
      x++;
1044
1045
0
      while (x < bctx.sx) {
1046
0
        bc = redraw_get_build_cell(&bctx, x, y);
1047
0
        if (!redraw_compare_data(last, bc))
1048
0
          break;
1049
0
        last = bc;
1050
0
        x++;
1051
0
      }
1052
0
      bc = redraw_get_build_cell(&bctx, x0, y);
1053
0
      type = bc->data.type;
1054
1055
0
      span = xcalloc(1, sizeof *span);
1056
0
      span->x = x0;
1057
0
      span->width = x - x0;
1058
0
      span->data = bc->data;
1059
1060
0
      TAILQ_INSERT_TAIL(&line->spans[type], span, entry);
1061
0
    }
1062
0
  }
1063
1064
0
  log_debug("%s: finished building @%u scene", c->name, w->id);
1065
0
  return (scene);
1066
0
}
1067
1068
/* Free a scene. */
1069
void
1070
redraw_free_scene(struct redraw_scene *scene)
1071
0
{
1072
0
  struct redraw_spans *spans;
1073
0
  struct redraw_span  *span, *span1;
1074
0
  u_int      y, type;
1075
1076
0
  if (scene == NULL)
1077
0
    return;
1078
1079
0
  for (y = 0; y < scene->sy; y++) {
1080
0
    for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
1081
0
      spans = &scene->lines[y].spans[type];
1082
0
      TAILQ_FOREACH_SAFE(span, spans, entry, span1) {
1083
0
        TAILQ_REMOVE(spans, span, entry);
1084
0
        free(span);
1085
0
      }
1086
0
    }
1087
0
  }
1088
0
  free(scene->lines);
1089
0
  free(scene);
1090
0
}
1091
1092
/* Does a client's cached scene show this window? */
1093
int
1094
redraw_client_has_window(struct client *c, struct window *w)
1095
0
{
1096
0
  return (c->redraw_scene != NULL && c->redraw_scene->w == w);
1097
0
}
1098
1099
/*
1100
 * Mark a window's cached redraw scenes as out of date. Generations are unique
1101
 * across all windows so a scene can never match a new window that reuses the
1102
 * address of a freed one.
1103
 */
1104
void
1105
redraw_invalidate_scene(struct window *w)
1106
71.4k
{
1107
71.4k
  static uint64_t next_generation;
1108
1109
71.4k
  w->redraw_scene_generation = ++next_generation;
1110
71.4k
}
1111
1112
/* Free all pending damage for a window. */
1113
void
1114
redraw_free_damage(struct window *w)
1115
16.2k
{
1116
16.2k
  struct redraw_damage  *rd, *rd1;
1117
1118
16.2k
  TAILQ_FOREACH_SAFE(rd, &w->damage, entry, rd1) {
1119
174
    TAILQ_REMOVE(&w->damage, rd, entry);
1120
174
    free(rd);
1121
174
  }
1122
16.2k
  w->damage_count = 0;
1123
16.2k
  w->scroll_pending = 0;
1124
16.2k
}
1125
1126
/* Collapse all pending damage for a window into one rectangle. */
1127
static void
1128
redraw_collapse_damage(struct window *w)
1129
0
{
1130
0
  struct redraw_damage  *rd, *rd1, *first;
1131
0
  u_int      x0, y0, x1, y1;
1132
1133
0
  first = TAILQ_FIRST(&w->damage);
1134
0
  if (first == NULL)
1135
0
    return;
1136
0
  x0 = first->x;
1137
0
  y0 = first->y;
1138
0
  x1 = first->x + first->sx;
1139
0
  y1 = first->y + first->sy;
1140
1141
0
  TAILQ_FOREACH_SAFE(rd, &w->damage, entry, rd1) {
1142
0
    if (rd->x < x0)
1143
0
      x0 = rd->x;
1144
0
    if (rd->y < y0)
1145
0
      y0 = rd->y;
1146
0
    if (rd->x + rd->sx > x1)
1147
0
      x1 = rd->x + rd->sx;
1148
0
    if (rd->y + rd->sy > y1)
1149
0
      y1 = rd->y + rd->sy;
1150
0
    first->flags &= rd->flags;
1151
0
    if (rd != first) {
1152
0
      TAILQ_REMOVE(&w->damage, rd, entry);
1153
0
      free(rd);
1154
0
    }
1155
0
  }
1156
1157
0
  first->x = x0;
1158
0
  first->y = y0;
1159
0
  first->sx = x1 - x0;
1160
0
  first->sy = y1 - y0;
1161
0
  w->damage_count = 1;
1162
0
}
1163
1164
/* Record window damage, merging nearby rectangles and limiting the count. */
1165
static void
1166
redraw_apply_window_damage(struct window *w, u_int x, u_int y, u_int sx,
1167
    u_int sy, int flags)
1168
2.73k
{
1169
2.73k
  struct redraw_damage  *rd;
1170
2.73k
  u_int      x0, y0, x1, y1, area, union_area;
1171
1172
2.73k
  if (x >= w->sx || y >= w->sy)
1173
0
    return;
1174
2.73k
  if (x + sx > w->sx)
1175
0
    sx = w->sx - x;
1176
2.73k
  if (y + sy > w->sy)
1177
0
    sy = w->sy - y;
1178
2.73k
  if (sx == 0 || sy == 0)
1179
0
    return;
1180
1181
2.73k
  TAILQ_FOREACH(rd, &w->damage, entry) {
1182
2.55k
    if (x > rd->x + rd->sx || rd->x > x + sx ||
1183
2.55k
        y > rd->y + rd->sy || rd->y > y + sy)
1184
0
      continue;
1185
1186
2.55k
    x0 = (x < rd->x) ? x : rd->x;
1187
2.55k
    y0 = (y < rd->y) ? y : rd->y;
1188
2.55k
    x1 = (x + sx > rd->x + rd->sx) ? x + sx : rd->x + rd->sx;
1189
2.55k
    y1 = (y + sy > rd->y + rd->sy) ? y + sy : rd->y + rd->sy;
1190
1191
2.55k
    area = sx * sy + rd->sx * rd->sy;
1192
2.55k
    union_area = (x1 - x0) * (y1 - y0);
1193
2.55k
    if (union_area > 2 * area)
1194
0
      continue;
1195
1196
2.55k
    rd->x = x0;
1197
2.55k
    rd->y = y0;
1198
2.55k
    rd->sx = x1 - x0;
1199
2.55k
    rd->sy = y1 - y0;
1200
2.55k
    rd->flags &= flags;
1201
2.55k
    return;
1202
2.55k
  }
1203
1204
174
  rd = xcalloc(1, sizeof *rd);
1205
174
  rd->x = x;
1206
174
  rd->y = y;
1207
174
  rd->sx = sx;
1208
174
  rd->sy = sy;
1209
174
  rd->flags = flags;
1210
174
  TAILQ_INSERT_TAIL(&w->damage, rd, entry);
1211
174
  if (++w->damage_count > REDRAW_DAMAGE_MAX)
1212
0
    redraw_collapse_damage(w);
1213
174
}
1214
1215
/* Record ordinary window damage. */
1216
void
1217
redraw_damage_window(struct window *w, u_int x, u_int y, u_int sx, u_int sy)
1218
2.73k
{
1219
2.73k
  redraw_apply_window_damage(w, x, y, sx, sy, 0);
1220
2.73k
}
1221
1222
/* Record damage caused by terminal scrolling. */
1223
void
1224
redraw_damage_scroll(struct window *w, u_int x, u_int y, u_int sx, u_int sy)
1225
0
{
1226
0
  if (!w->scroll_pending) {
1227
0
    w->scroll_generation++;
1228
0
    w->scroll_pending = 1;
1229
0
  }
1230
0
  redraw_apply_window_damage(w, x, y, sx, sy, REDRAW_DAMAGE_SCROLL);
1231
0
}
1232
1233
/* Remember whether this client used terminal scrolling for the damage. */
1234
void
1235
redraw_scroll_result(struct tty *tty, const struct tty_ctx *ctx, int failed)
1236
0
{
1237
0
  struct window_pane  *wp = ctx->arg;
1238
0
  struct window   *w;
1239
1240
0
  if (wp == NULL)
1241
0
    return;
1242
0
  w = wp->window;
1243
0
  if (!w->scroll_pending)
1244
0
    return;
1245
0
  if (tty->scroll_window != w ||
1246
0
      tty->scroll_generation != w->scroll_generation) {
1247
0
    tty->scroll_window = w;
1248
0
    tty->scroll_generation = w->scroll_generation;
1249
0
    tty->scroll_failed = 0;
1250
0
  }
1251
0
  if (failed)
1252
0
    tty->scroll_failed = 1;
1253
0
}
1254
1255
/* Mark all cached redraw scenes as out of date. */
1256
void
1257
redraw_invalidate_all_scenes(void)
1258
0
{
1259
0
  struct window *w;
1260
1261
0
  RB_FOREACH(w, windows, &windows)
1262
0
    redraw_invalidate_scene(w);
1263
0
}
1264
1265
/* Get the cached redraw scene, rebuilding it if needed. */
1266
static struct redraw_scene *
1267
redraw_get_scene(struct client *c)
1268
0
{
1269
0
  struct redraw_scene *scene = c->redraw_scene;
1270
0
  struct window   *w = c->session->curw->window;
1271
0
  const char    *reason = NULL;
1272
0
  u_int      ox, oy, sx, sy;
1273
1274
0
  redraw_get_window_offset(c, &ox, &oy, &sx, &sy);
1275
0
  if (scene == NULL)
1276
0
    reason = "missing";
1277
0
  else if (scene->w != w)
1278
0
    reason = "window changed";
1279
0
  else if (scene->generation != w->redraw_scene_generation)
1280
0
    reason = "generation changed";
1281
0
  else if (scene->ox != ox || scene->oy != oy)
1282
0
    reason = "offset changed";
1283
0
  else if (scene->sx != sx || scene->sy != sy)
1284
0
    reason = "size changed";
1285
0
  if (reason != NULL) {
1286
0
    log_debug("%s: @%u scene invalid: %s", c->name, w->id, reason);
1287
0
    redraw_free_scene(scene);
1288
0
    scene = redraw_make_scene(c);
1289
0
    c->redraw_scene = scene;
1290
0
  }
1291
0
  return (scene);
1292
0
}
1293
1294
/* Draw a pane span. */
1295
static void
1296
redraw_draw_pane_span(struct redraw_draw_ctx *dctx,
1297
    struct redraw_span *span, u_int x, u_int y, u_int n)
1298
0
{
1299
0
  struct redraw_scene *scene = dctx->scene;
1300
0
  struct client   *c = scene->c;
1301
0
  struct tty    *tty = &c->tty;
1302
0
  struct window_pane  *wp = span->data.p.wp;
1303
0
  struct screen   *s = wp->screen;
1304
0
  struct grid_cell   defaults;
1305
0
  struct tty_style_ctx   style_ctx;
1306
0
  u_int      px, py;
1307
1308
0
  tty_default_colours(&defaults, wp, &style_ctx.dim);
1309
0
  style_ctx.defaults = &defaults;
1310
0
  style_ctx.palette = &wp->palette;
1311
0
  style_ctx.hyperlinks = s->hyperlinks;
1312
1313
0
  px = span->data.p.px + (x - dctx->side_left - span->x);
1314
0
  py = span->data.p.py;
1315
0
  tty_draw_line(tty, s, px, py, n, x, y, &style_ctx);
1316
0
}
1317
1318
/* Get default border style for spans without a pane. */
1319
static void
1320
redraw_get_default_border_style(struct redraw_draw_ctx *dctx,
1321
    struct grid_cell *gc, enum pane_lines *pane_lines)
1322
0
{
1323
0
  struct redraw_scene *scene = dctx->scene;
1324
0
  struct client   *c = scene->c;
1325
0
  struct session    *s = c->session;
1326
0
  struct options    *oo = scene->w->options;
1327
0
  struct format_tree  *ft;
1328
0
  struct grid_cell  *dgc = &dctx->default_gc;
1329
1330
0
  if (~dctx->flags & REDRAW_DEFAULT_SET) {
1331
0
    ft = format_create_defaults(NULL, c, s, s->curw, NULL);
1332
0
    memcpy(dgc, &grid_default_cell, sizeof *dgc);
1333
0
    style_add(dgc, oo, "pane-border-style", ft);
1334
0
    format_free(ft);
1335
0
    dctx->pane_lines = options_get_number(oo, "pane-border-lines");
1336
0
    dctx->flags |= REDRAW_DEFAULT_SET;
1337
0
  }
1338
0
  memcpy(gc, dgc, sizeof *gc);
1339
0
  *pane_lines = dctx->pane_lines;
1340
0
}
1341
1342
/*
1343
 * For this border span, pick the pane whose border style should colour it.
1344
 * Prefer an explicitly assigned style owner, then the active adjacent pane,
1345
 * then any adjacent pane.
1346
 */
1347
static struct window_pane *
1348
redraw_get_pane_for_border_style(struct redraw_draw_ctx *dctx,
1349
    struct redraw_span *span)
1350
0
{
1351
0
  struct window_pane  *active = dctx->active;
1352
1353
0
  if (span->data.type != REDRAW_SPAN_BORDER)
1354
0
    return (NULL);
1355
1356
0
  if (span->data.b.style_wp != NULL)
1357
0
    return (span->data.b.style_wp);
1358
0
  if (active != NULL && redraw_data_has_pane(&span->data, active))
1359
0
    return (active);
1360
1361
0
  if (span->data.b.top_wp != NULL)
1362
0
    return (span->data.b.top_wp);
1363
0
  if (span->data.b.bottom_wp != NULL)
1364
0
    return (span->data.b.bottom_wp);
1365
0
  if (span->data.b.left_wp != NULL)
1366
0
    return (span->data.b.left_wp);
1367
0
  if (span->data.b.right_wp != NULL)
1368
0
    return (span->data.b.right_wp);
1369
0
  return (NULL);
1370
0
}
1371
1372
/* Draw arrow indicator if this border span is an arrow cell. */
1373
static void
1374
redraw_draw_border_arrow(struct redraw_draw_ctx *dctx,
1375
    struct redraw_span *span, struct grid_cell *gc)
1376
0
{
1377
0
  struct window_pane  *active = dctx->active;
1378
0
  char       ch;
1379
1380
0
  if (span->data.type != REDRAW_SPAN_BORDER || active == NULL)
1381
0
    return;
1382
0
  if (~span->data.b.flags & REDRAW_BORDER_IS_ARROW)
1383
0
    return;
1384
1385
0
  if (span->data.b.left_wp == active)
1386
0
    ch = ',';
1387
0
  else if (span->data.b.right_wp == active)
1388
0
    ch = '+';
1389
0
  else if (span->data.b.top_wp == active)
1390
0
    ch = '-';
1391
0
  else if (span->data.b.bottom_wp == active)
1392
0
    ch = '.';
1393
0
  else
1394
0
    return;
1395
1396
0
  utf8_set(&gc->data, ch);
1397
0
  gc->attr |= GRID_ATTR_CHARSET;
1398
0
}
1399
1400
/* Draw a border span. */
1401
static void
1402
redraw_draw_border_span(struct redraw_draw_ctx *dctx,
1403
    struct redraw_span *span, u_int x, u_int y, u_int n)
1404
0
{
1405
0
  struct redraw_scene *scene = dctx->scene;
1406
0
  struct client   *c = scene->c;
1407
0
  struct tty    *tty = &c->tty;
1408
0
  struct window   *w = scene->w;
1409
0
  struct window_pane  *wp = NULL;
1410
0
  struct grid_cell   gc;
1411
0
  enum pane_lines    pane_lines;
1412
0
  u_int      i, cell_type;
1413
0
  int      isolates = 0;
1414
1415
0
  if (span->data.type != REDRAW_SPAN_BORDER)
1416
0
    cell_type = CELL_NONE;
1417
0
  else {
1418
0
    wp = redraw_get_pane_for_border_style(dctx, span);
1419
0
    cell_type = span->data.b.cell_type;
1420
0
  }
1421
1422
0
  if (wp == NULL) {
1423
0
    redraw_get_default_border_style(dctx, &gc, &pane_lines);
1424
0
    if (span->data.type == REDRAW_SPAN_OUTSIDE)
1425
0
      window_get_fill_cell(w, 0, &gc);
1426
0
    else if (span->data.type == REDRAW_SPAN_EMPTY)
1427
0
      window_get_fill_cell(w, 1, &gc);
1428
0
    else {
1429
0
      if (span->data.type != REDRAW_SPAN_BORDER)
1430
0
        pane_lines = PANE_LINES_SINGLE;
1431
0
      window_get_border_cell(NULL, pane_lines, cell_type, &gc);
1432
0
    }
1433
0
  } else {
1434
0
    window_pane_get_border_style(wp, c, &gc);
1435
0
    window_pane_get_border_cell(wp, cell_type, &gc);
1436
0
  }
1437
1438
0
  if (span->data.type == REDRAW_SPAN_BORDER &&
1439
0
      dctx->marked != NULL &&
1440
0
      redraw_data_has_pane(&span->data, dctx->marked))
1441
0
    gc.attr ^= GRID_ATTR_REVERSE;
1442
0
  redraw_draw_border_arrow(dctx, span, &gc);
1443
1444
0
  if (cell_type == CELL_UD && (dctx->flags & REDRAW_ISOLATES))
1445
0
    isolates = 1;
1446
0
  tty_cursor(tty, x, y);
1447
0
  if (isolates)
1448
0
    tty_puts(tty, REDRAW_END_ISOLATE);
1449
0
  for (i = 0; i < n; i++)
1450
0
    tty_cell(tty, &gc, NULL);
1451
0
  if (isolates)
1452
0
    tty_puts(tty, REDRAW_START_ISOLATE);
1453
0
}
1454
1455
/* Draw a pane status span. */
1456
static void
1457
redraw_draw_status_span(struct redraw_draw_ctx *dctx,
1458
    struct redraw_span *span, u_int x, u_int y, u_int n)
1459
0
{
1460
0
  struct redraw_scene *scene = dctx->scene;
1461
0
  struct client   *c = scene->c;
1462
0
  struct tty    *tty = &c->tty;
1463
0
  struct window_pane  *wp = span->data.st.wp;
1464
0
  struct screen   *s = &wp->status_screen;
1465
0
  u_int      px, sx = screen_size_x(s);
1466
1467
0
  px = span->data.st.offset + (x - dctx->side_left - span->x);
1468
0
  if (px < sx) {
1469
0
    if (n > sx - px)
1470
0
      n = sx - px;
1471
0
    tty_draw_line(tty, s, px, 0, n, x, y, NULL);
1472
0
  }
1473
0
}
1474
1475
/* Draw a scrollbar span. */
1476
static void
1477
redraw_draw_scrollbar_span(struct redraw_draw_ctx *dctx,
1478
    struct redraw_span *span, u_int x, u_int y, u_int n)
1479
0
{
1480
0
  struct redraw_scene *scene = dctx->scene;
1481
0
  struct window_pane  *wp = span->data.sb.wp;
1482
0
  struct screen   *s = wp->screen;
1483
0
  struct tty    *tty = &scene->c->tty;
1484
0
  struct style    *sb_style = &wp->scrollbar_style;
1485
0
  struct grid_cell   gc, slgc, pad_gc, *gcp;
1486
0
  double       pct_view;
1487
0
  u_int      total_height, slider_h, slider_y;
1488
0
  u_int      sb_h = span->data.sb.height;
1489
0
  u_int      sb_y = span->data.sb.y;
1490
0
  u_int      i, off, sb_w, sb_pad;
1491
0
  int      cm_y, cm_size;
1492
1493
0
  if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE) {
1494
0
    total_height = screen_size_y(s) + screen_hsize(s);
1495
0
    if (total_height == 0)
1496
0
      return;
1497
0
    pct_view = (double)sb_h / total_height;
1498
0
    slider_h = (double)sb_h * pct_view;
1499
0
    slider_y = sb_h - slider_h;
1500
0
  } else {
1501
0
    if (TAILQ_FIRST(&wp->modes) == NULL)
1502
0
      return;
1503
0
    if (window_copy_get_current_offset(wp, &cm_y, &cm_size) == 0)
1504
0
      return;
1505
0
    total_height = cm_size + sb_h;
1506
0
    if (total_height == 0)
1507
0
      return;
1508
0
    pct_view = (double)sb_h / total_height;
1509
0
    slider_h = (double)sb_h * pct_view;
1510
0
    slider_y = (sb_h + 1) * ((double)cm_y / total_height);
1511
0
  }
1512
1513
0
  if (slider_h < 1)
1514
0
    slider_h = 1;
1515
0
  if (slider_y >= sb_h)
1516
0
    slider_y = sb_h - 1;
1517
1518
0
  wp->sb_slider_y = slider_y;
1519
0
  wp->sb_slider_h = slider_h;
1520
1521
0
  gc = sb_style->gc;
1522
0
  memcpy(&slgc, &gc, sizeof slgc);
1523
0
  slgc.fg = gc.bg;
1524
0
  slgc.bg = gc.fg;
1525
0
  tty_default_colours(&pad_gc, wp, NULL);
1526
1527
0
  sb_w = sb_style->width;
1528
0
  sb_pad = sb_style->pad;
1529
0
  off = x - dctx->side_left - span->x;
1530
1531
0
  tty_cursor(tty, x, y);
1532
0
  for (i = 0; i < n; i++) {
1533
0
    if (span->data.sb.flags & REDRAW_SCROLLBAR_LEFT) {
1534
0
      if (off + i >= sb_w && off + i < sb_w + sb_pad) {
1535
0
        tty_cell(tty, &pad_gc, NULL);
1536
0
        continue;
1537
0
      }
1538
0
    } else {
1539
0
      if (off + i < sb_pad) {
1540
0
        tty_cell(tty, &pad_gc, NULL);
1541
0
        continue;
1542
0
      }
1543
0
    }
1544
1545
0
    if (sb_y >= slider_y && sb_y < slider_y + slider_h)
1546
0
      gcp = &slgc;
1547
0
    else
1548
0
      gcp = &gc;
1549
0
    tty_cell(tty, gcp, NULL);
1550
0
  }
1551
0
}
1552
1553
/* Draw a menu span. */
1554
static void
1555
redraw_draw_menu_span(struct redraw_draw_ctx *dctx,
1556
    struct redraw_span *span, u_int x, u_int y, u_int n)
1557
0
{
1558
0
  struct redraw_scene *scene = dctx->scene;
1559
0
  struct tty    *tty = &scene->c->tty;
1560
0
  struct screen   *s = menu_screen(span->data.m.md);
1561
0
  u_int      px;
1562
1563
0
  px = span->data.m.px + (x - dctx->side_left - span->x);
1564
0
  tty_draw_line(tty, s, px, span->data.m.py, n, x, y, NULL);
1565
0
}
1566
1567
/* Draw a span. */
1568
static void
1569
redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
1570
    u_int y, enum redraw_phase phase)
1571
0
{
1572
0
  struct redraw_span_data *data = &span->data;
1573
0
  enum redraw_span_type  type = data->type;
1574
0
  u_int      x = dctx->side_left + span->x;
1575
0
  u_int      width = span->width;
1576
1577
0
  if (type == REDRAW_SPAN_STATUS && ~data->st.wp->flags & PANE_NEWSTATUS)
1578
0
    return;
1579
0
  if (phase != REDRAW_TEXT && type != REDRAW_SPAN_PANE)
1580
0
    return;
1581
1582
0
  switch (span->data.type) {
1583
0
  case REDRAW_SPAN_PANE:
1584
0
    if (phase == REDRAW_TEXT)
1585
0
      redraw_draw_pane_span(dctx, span, x, y, width);
1586
#ifdef ENABLE_IMAGES
1587
    else {
1588
      image_draw_line(&dctx->scene->c->tty, data->p.wp->screen,
1589
          data->p.px, data->p.py, width, x, y);
1590
    }
1591
#endif
1592
0
    break;
1593
0
  case REDRAW_SPAN_BORDER:
1594
0
  case REDRAW_SPAN_EMPTY:
1595
0
  case REDRAW_SPAN_OUTSIDE:
1596
0
    redraw_draw_border_span(dctx, span, x, y, width);
1597
0
    break;
1598
0
  case REDRAW_SPAN_STATUS:
1599
0
    redraw_draw_status_span(dctx, span, x, y, width);
1600
0
    break;
1601
0
  case REDRAW_SPAN_SCROLLBAR:
1602
0
    redraw_draw_scrollbar_span(dctx, span, x, y, width);
1603
0
    break;
1604
0
  case REDRAW_SPAN_MENU:
1605
0
    redraw_draw_menu_span(dctx, span, x, y, width);
1606
0
    break;
1607
0
  }
1608
0
}
1609
1610
/* Draw pane lines. */
1611
static void
1612
redraw_draw_pane_lines(struct redraw_draw_ctx *dctx, struct window_pane *wp,
1613
    int flags)
1614
0
{
1615
0
  struct redraw_scene *scene = dctx->scene;
1616
0
  struct redraw_line  *line;
1617
0
  struct redraw_spans *spans;
1618
0
  struct redraw_span  *span;
1619
0
  enum redraw_phase  phase;
1620
0
  u_int      cy;
1621
0
  int      y, top, bottom;
1622
1623
0
  top = wp->yoff - (int)scene->oy;
1624
0
  if (top < 0)
1625
0
    top = 0;
1626
0
  bottom = wp->yoff + (int)wp->sy - (int)scene->oy;
1627
0
  if (bottom < 0)
1628
0
    bottom = 0;
1629
0
  if (bottom > (int)scene->sy)
1630
0
    bottom = scene->sy;
1631
1632
#ifdef ENABLE_IMAGES
1633
  /* Erase only pane-owned spans so overlapping panes remain intact. */
1634
  if (flags & REDRAW_PANE) {
1635
    for (y = top; y < bottom; y++) {
1636
      line = &scene->lines[y];
1637
      if (dctx->flags & REDRAW_STATUS_TOP)
1638
        cy = dctx->status_lines + y;
1639
      else
1640
        cy = y;
1641
      spans = &line->spans[REDRAW_SPAN_PANE];
1642
      TAILQ_FOREACH(span, spans, entry) {
1643
        if (span->data.p.wp == wp) {
1644
          image_redraw_start(&scene->c->tty,
1645
              dctx->side_left + span->x, cy,
1646
              span->width, 1);
1647
        }
1648
      }
1649
    }
1650
  }
1651
#endif
1652
1653
  /* Draw each phase. */
1654
0
  for (phase = 0; phase < REDRAW_PHASE_COUNT; phase++) {
1655
0
    for (y = top; y < bottom; y++) {
1656
0
      line = &scene->lines[y];
1657
0
      if (dctx->flags & REDRAW_STATUS_TOP)
1658
0
        cy = dctx->status_lines + y;
1659
0
      else
1660
0
        cy = y;
1661
0
      if (flags & REDRAW_PANE) {
1662
0
        spans = &line->spans[REDRAW_SPAN_PANE];
1663
0
        TAILQ_FOREACH(span, spans, entry) {
1664
0
          if (span->data.p.wp != wp)
1665
0
            continue;
1666
0
          redraw_draw_span(dctx, span, cy, phase);
1667
0
        }
1668
0
      }
1669
0
      if (phase == REDRAW_TEXT &&
1670
0
          (flags & REDRAW_PANE_SCROLLBAR)) {
1671
0
        spans = &line->spans[REDRAW_SPAN_SCROLLBAR];
1672
0
        TAILQ_FOREACH(span, spans, entry) {
1673
0
          if (span->data.sb.wp != wp)
1674
0
            continue;
1675
0
          redraw_draw_span(dctx, span, cy, phase);
1676
0
        }
1677
0
      }
1678
0
    }
1679
#ifdef ENABLE_IMAGES
1680
    image_draw_flush(&scene->c->tty);
1681
#endif
1682
0
  }
1683
0
}
1684
1685
/* Return whether to draw a span type in this phase for these flags. */
1686
static int
1687
redraw_should_draw(enum redraw_span_type type, int flags,
1688
    enum redraw_phase phase)
1689
0
{
1690
0
  if (phase != REDRAW_TEXT && type != REDRAW_SPAN_PANE)
1691
0
    return (0);
1692
0
  if (REDRAW_IS_ALL(flags))
1693
0
    return (1);
1694
1695
0
  switch (type) {
1696
0
  case REDRAW_SPAN_PANE:
1697
0
    return (flags & REDRAW_PANE);
1698
0
  case REDRAW_SPAN_OUTSIDE:
1699
0
    return (flags & REDRAW_OUTSIDE);
1700
0
  case REDRAW_SPAN_EMPTY:
1701
0
    return (flags & REDRAW_EMPTY);
1702
0
  case REDRAW_SPAN_BORDER:
1703
0
    return (flags & REDRAW_PANE_BORDER);
1704
0
  case REDRAW_SPAN_STATUS:
1705
0
    return (flags & REDRAW_PANE_STATUS);
1706
0
  case REDRAW_SPAN_SCROLLBAR:
1707
0
    return (flags & REDRAW_PANE_SCROLLBAR);
1708
0
  case REDRAW_SPAN_MENU:
1709
0
    return (flags & REDRAW_MENU);
1710
0
  default:
1711
0
    return (0);
1712
0
  }
1713
0
}
1714
1715
/* Draw lines. */
1716
static void
1717
redraw_draw_lines(struct redraw_draw_ctx *dctx, int flags)
1718
0
{
1719
0
  struct redraw_scene *scene = dctx->scene;
1720
0
  struct redraw_line  *line;
1721
0
  struct redraw_spans *spans;
1722
0
  struct redraw_span  *span;
1723
0
  enum redraw_phase  phase;
1724
0
  u_int      y, cy, type;
1725
1726
  /* Draw each phase. */
1727
0
  for (phase = 0; phase < REDRAW_PHASE_COUNT; phase++) {
1728
0
    for (y = 0; y < scene->sy; y++) {
1729
0
      line = &scene->lines[y];
1730
0
      if (dctx->flags & REDRAW_STATUS_TOP)
1731
0
        cy = dctx->status_lines + y;
1732
0
      else
1733
0
        cy = y;
1734
0
      for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
1735
0
        if (!redraw_should_draw(type, flags, phase))
1736
0
          continue;
1737
0
        spans = &line->spans[type];
1738
0
        TAILQ_FOREACH(span, spans, entry)
1739
0
          redraw_draw_span(dctx, span, cy, phase);
1740
0
      }
1741
0
    }
1742
#ifdef ENABLE_IMAGES
1743
    image_draw_flush(&scene->c->tty);
1744
#endif
1745
0
  }
1746
0
}
1747
1748
/* Draw menu spans. */
1749
static void
1750
redraw_draw_menu_lines(struct redraw_draw_ctx *dctx)
1751
0
{
1752
0
  struct redraw_scene *scene = dctx->scene;
1753
0
  struct redraw_line  *line;
1754
0
  struct redraw_span  *span;
1755
0
  u_int      y, cy;
1756
1757
0
  for (y = 0; y < scene->sy; y++) {
1758
0
    line = &scene->lines[y];
1759
0
    if (dctx->flags & REDRAW_STATUS_TOP)
1760
0
      cy = dctx->status_lines + y;
1761
0
    else
1762
0
      cy = y;
1763
0
    TAILQ_FOREACH(span, &line->spans[REDRAW_SPAN_MENU], entry)
1764
0
      redraw_draw_span(dctx, span, cy, REDRAW_TEXT);
1765
0
  }
1766
0
}
1767
1768
/* Get line for pane status line. */
1769
static int
1770
redraw_pane_status_line(struct redraw_draw_ctx *dctx,
1771
    struct window_pane *wp, u_int *line)
1772
0
{
1773
0
  struct redraw_scene *scene = dctx->scene;
1774
0
  int      pane_status, wy;
1775
1776
0
  pane_status = window_pane_get_pane_status(wp);
1777
0
  if (pane_status == PANE_STATUS_OFF)
1778
0
    return (0);
1779
1780
0
  if (pane_status == PANE_STATUS_TOP)
1781
0
    wy = (int)wp->yoff - 1;
1782
0
  else
1783
0
    wy = (int)wp->yoff + wp->sy;
1784
0
  if (wy < 0 || wy < (int)scene->oy)
1785
0
    return (0);
1786
0
  if ((u_int)wy >= scene->oy + scene->sy)
1787
0
    return (0);
1788
0
  *line = wy - scene->oy;
1789
0
  return (1);
1790
0
}
1791
1792
/* Get available width for pane status line. */
1793
static u_int
1794
redraw_pane_status_width(struct redraw_draw_ctx *dctx,
1795
    struct window_pane *wp, struct redraw_span **first)
1796
0
{
1797
0
  struct redraw_scene *scene = dctx->scene;
1798
0
  struct redraw_span  *span;
1799
0
  u_int      y, width = 0, end;
1800
1801
0
  if (!redraw_pane_status_line(dctx, wp, &y))
1802
0
    return (0);
1803
1804
0
  *first = NULL;
1805
0
  TAILQ_FOREACH(span, &scene->lines[y].spans[REDRAW_SPAN_STATUS], entry) {
1806
0
    if (span->data.st.wp == wp) {
1807
0
      if (*first == NULL)
1808
0
          *first = span;
1809
0
      end = span->data.st.offset + span->width;
1810
0
      if (end > width)
1811
0
        width = end;
1812
0
    }
1813
0
  }
1814
0
  return (width);
1815
0
}
1816
1817
/* Set up draw context. */
1818
static void
1819
redraw_set_draw_context(struct redraw_draw_ctx *dctx,
1820
    struct redraw_scene *scene)
1821
0
{
1822
0
  struct client *c = scene->c;
1823
0
  struct session  *s = c->session;
1824
0
  struct options  *oo = s->options;
1825
0
  struct tty  *tty = &c->tty;
1826
0
  u_int    lines;
1827
1828
0
  memset(dctx, 0, sizeof *dctx);
1829
0
  dctx->scene = scene;
1830
1831
0
  if (server_is_marked(s, s->curw, marked_pane.wp))
1832
0
    dctx->marked = marked_pane.wp;
1833
0
  dctx->active = s->curw->window->active;
1834
1835
0
  lines = status_line_size(c);
1836
0
  if (options_get_number(oo, "status-position") == 0)
1837
0
    dctx->flags |= REDRAW_STATUS_TOP;
1838
0
  dctx->status_lines = lines;
1839
1840
0
  if (status_side_at_column(c) == 0)
1841
0
    dctx->side_left = status_side_size(c);
1842
1843
0
  if ((c->flags & CLIENT_UTF8) && tty_term_has(tty->term, TTYC_BIDI))
1844
0
    dctx->flags |= REDRAW_ISOLATES;
1845
0
}
1846
1847
/* Build a pane prompt. */
1848
static void
1849
redraw_make_pane_prompt(struct window_pane *wp, struct screen *screen)
1850
0
{
1851
0
  struct screen_write_ctx  ctx;
1852
0
  struct prompt_draw_data  pdd;
1853
1854
0
  screen_init(screen, wp->sx, 1, 0);
1855
0
  screen_write_start(&ctx, screen);
1856
0
  pdd.ctx = &ctx;
1857
0
  pdd.cursor_x = &wp->prompt_cx;
1858
0
  pdd.area_x = 0;
1859
0
  pdd.area_width = wp->sx;
1860
0
  pdd.prompt_line = 0;
1861
0
  prompt_draw(wp->prompt, &pdd);
1862
0
  screen_write_stop(&ctx);
1863
0
}
1864
1865
/* Draw a pane's prompt over its content. */
1866
static void
1867
redraw_draw_pane_prompt(struct redraw_draw_ctx *dctx, struct window_pane *wp)
1868
0
{
1869
0
  struct redraw_scene *scene = dctx->scene;
1870
0
  struct client   *c = scene->c;
1871
0
  struct tty    *tty = &c->tty;
1872
0
  struct screen    screen;
1873
0
  int      ox = scene->ox, oy = scene->oy;
1874
0
  int      sx = scene->sx, sy = scene->sy;
1875
0
  int      line, cy, px, offset, width, wy;
1876
0
  u_int      side_left = dctx->side_left;
1877
1878
0
  if (wp->prompt == NULL || wp->sx == 0 || wp->sy == 0)
1879
0
    return;
1880
1881
0
  if (~dctx->flags & REDRAW_STATUS_TOP)
1882
0
    wy = wp->yoff + (int)wp->sy - 1;
1883
0
  else
1884
0
    wy = wp->yoff;
1885
0
  if (wy < oy || wy >= oy + sy)
1886
0
    return;
1887
0
  line = wy - oy;
1888
0
  if (dctx->flags & REDRAW_STATUS_TOP)
1889
0
    cy = dctx->status_lines + line;
1890
0
  else
1891
0
    cy = line;
1892
1893
0
  if (wp->xoff + (int)wp->sx <= ox || wp->xoff >= ox + sx)
1894
0
    return;
1895
0
  if (wp->xoff < ox) {
1896
0
    offset = ox - wp->xoff;
1897
0
    px = 0;
1898
0
  } else {
1899
0
    offset = 0;
1900
0
    px = wp->xoff - ox;
1901
0
  }
1902
0
  width = wp->sx - offset;
1903
0
  if (px + width > sx)
1904
0
    width = sx - px;
1905
1906
0
  redraw_make_pane_prompt(wp, &screen);
1907
0
  tty_draw_line(tty, &screen, offset, 0, width, side_left + px, cy, NULL);
1908
0
  screen_free(&screen);
1909
0
}
1910
1911
/* Draw scene to client. */
1912
static void
1913
redraw_draw(struct client *c, struct window_pane *wp, int flags)
1914
0
{
1915
0
  struct redraw_draw_ctx   dctx;
1916
0
  struct session    *s = c->session;
1917
0
  struct window   *w = s->curw->window;
1918
0
  struct tty    *tty = &c->tty;
1919
0
  struct screen   *sl;
1920
0
  struct redraw_scene *scene;
1921
0
  struct window_pane  *loop;
1922
0
  u_int      width, i, y, lines, side_width;
1923
0
  int      side_x;
1924
0
  struct redraw_span  *first;
1925
0
  int      redraw;
1926
1927
0
  if (c->flags & CLIENT_SUSPENDED)
1928
0
    return;
1929
1930
0
  if (flags & REDRAW_STATUS) {
1931
0
    if (c->message_string != NULL)
1932
0
      redraw = status_message_redraw(c);
1933
0
    else if (c->prompt != NULL)
1934
0
      redraw = status_prompt_redraw(c);
1935
0
    else
1936
0
      redraw = status_redraw(c);
1937
0
    if (!redraw &&
1938
0
        (~c->flags & CLIENT_REDRAWSTATUSALWAYS) &&
1939
0
        !REDRAW_IS_ALL(flags)) {
1940
0
      flags &= ~REDRAW_STATUS;
1941
0
      if (flags == 0)
1942
0
        return;
1943
0
    }
1944
0
  }
1945
1946
0
  if (flags & REDRAW_SIDE_STATUS) {
1947
0
    if (!status_side_redraw(c) &&
1948
0
        (~c->flags & CLIENT_REDRAWSTATUSALWAYS) &&
1949
0
        !REDRAW_IS_ALL(flags)) {
1950
0
      flags &= ~REDRAW_SIDE_STATUS;
1951
0
      if (flags == 0)
1952
0
        return;
1953
0
    }
1954
0
  }
1955
1956
0
  if (log_get_level() != 0) {
1957
0
    log_debug("%s: starting @%u redraw (%s)", c->name, w->id,
1958
0
        redraw_flags_to_string(flags));
1959
0
  }
1960
1961
0
  scene = redraw_get_scene(c);
1962
0
  if (scene == NULL)
1963
0
    return;
1964
0
  redraw_set_draw_context(&dctx, scene);
1965
0
  if (w->menu != NULL)
1966
0
    menu_update(w->menu);
1967
1968
0
  if (flags & (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS)) {
1969
0
    TAILQ_FOREACH(loop, &scene->w->panes, entry) {
1970
0
      loop->border_gc_set = 0;
1971
0
      loop->active_border_gc_set = 0;
1972
0
    }
1973
0
  }
1974
1975
0
  if (flags & REDRAW_PANE_STATUS) {
1976
0
    redraw = 0;
1977
0
    TAILQ_FOREACH(loop, &scene->w->panes, entry) {
1978
0
      if (REDRAW_IS_ALL(flags))
1979
0
        loop->flags |= PANE_NEWSTATUS;
1980
0
      else
1981
0
        loop->flags &= ~PANE_NEWSTATUS;
1982
1983
0
      width = redraw_pane_status_width(&dctx, loop, &first);
1984
0
      if (width == 0)
1985
0
        continue;
1986
1987
0
      if (window_make_pane_status(loop, c, width, first)) {
1988
0
        loop->flags |= PANE_NEWSTATUS;
1989
0
        redraw = 1;
1990
0
      }
1991
0
    }
1992
0
    if (!redraw && !REDRAW_IS_ALL(flags)) {
1993
0
      flags &= ~REDRAW_PANE_STATUS;
1994
0
      if (flags == 0)
1995
0
        return;
1996
0
    }
1997
0
  }
1998
1999
0
  if (flags & REDRAW_PANE) {
2000
0
    if (wp != NULL) {
2001
0
      if (wp->base.mode & MODE_SYNC)
2002
0
        screen_write_stop_sync(wp);
2003
0
      screen_write_sync_clear_dirty(wp);
2004
0
    } else {
2005
0
      TAILQ_FOREACH(loop, &scene->w->panes, entry) {
2006
0
        if (!window_pane_is_visible(loop))
2007
0
          continue;
2008
0
        if (loop->base.mode & MODE_SYNC)
2009
0
          screen_write_stop_sync(loop);
2010
0
        screen_write_sync_clear_dirty(loop);
2011
0
      }
2012
0
    }
2013
0
  }
2014
0
  tty_sync_start(tty); /* end in server_client_reset_state */
2015
#ifdef ENABLE_IMAGES
2016
  if (wp == NULL && (flags & REDRAW_PANE)) {
2017
    y = 0;
2018
    if (dctx.flags & REDRAW_STATUS_TOP)
2019
      y = dctx.status_lines;
2020
    image_redraw_start(tty, dctx.side_left, y, scene->sx,
2021
        scene->sy);
2022
  }
2023
#endif
2024
0
  tty_update_mode(tty, tty->mode & ~CURSOR_MODES, NULL);
2025
2026
0
  if (wp != NULL)
2027
0
    redraw_draw_pane_lines(&dctx, wp, flags);
2028
0
  else
2029
0
    redraw_draw_lines(&dctx, flags);
2030
2031
0
  if (flags & REDRAW_PANE) {
2032
0
    if (wp != NULL)
2033
0
      redraw_draw_pane_prompt(&dctx, wp);
2034
0
    else {
2035
0
      TAILQ_FOREACH(loop, &scene->w->panes, entry) {
2036
0
        if (window_pane_is_visible(loop))
2037
0
          redraw_draw_pane_prompt(&dctx, loop);
2038
0
      }
2039
0
    }
2040
0
  }
2041
0
  if (w->menu != NULL && (flags & REDRAW_MENU))
2042
0
    redraw_draw_menu_lines(&dctx);
2043
2044
0
  if (flags & REDRAW_SIDE_STATUS) {
2045
0
    side_width = status_side_size(c);
2046
0
    side_x = status_side_at_column(c);
2047
0
    if (side_width != 0 && side_x != -1) {
2048
0
      if (dctx.flags & REDRAW_STATUS_TOP)
2049
0
        y = dctx.status_lines;
2050
0
      else
2051
0
        y = 0;
2052
0
      for (i = 0; i < status_side_rows(c); i++) {
2053
0
        tty_draw_line(tty, &c->side_status.screen, 0, i,
2054
0
            side_width, side_x, y + i, NULL);
2055
0
      }
2056
0
    }
2057
0
  }
2058
2059
0
  if (flags & REDRAW_STATUS) {
2060
0
    lines = dctx.status_lines;
2061
0
    if (c->message_string != NULL || c->prompt != NULL)
2062
0
      lines = (lines == 0 ? 1 : lines);
2063
0
    if (dctx.flags & REDRAW_STATUS_TOP)
2064
0
      y = 0;
2065
0
    else
2066
0
      y = c->tty.sy - lines;
2067
0
    sl = c->status.active;
2068
0
    for (i = 0; i < lines; i++)
2069
0
      tty_draw_line(tty, sl, 0, i, tty->sx, 0, y + i, NULL);
2070
0
  }
2071
2072
0
  tty_reset(tty);
2073
2074
0
  log_debug("%s: finished @%u redraw", c->name, scene->w->id);
2075
0
}
2076
2077
/* Get border cell type beneath status cell at offset x in pane status line. */
2078
int
2079
redraw_get_status_border_cell_type(struct redraw_span **spanp, u_int x)
2080
0
{
2081
0
  struct redraw_span  *span = *spanp;
2082
0
  struct window_pane  *wp;
2083
0
  u_int      start, end;
2084
2085
0
  if (span == NULL || span->data.type != REDRAW_SPAN_STATUS)
2086
0
    return (CELL_LR);
2087
0
  wp = span->data.st.wp;
2088
0
  for (; span != NULL; span = TAILQ_NEXT(span, entry)) {
2089
0
    if (span->data.type != REDRAW_SPAN_STATUS)
2090
0
      continue;
2091
0
    if (span->data.st.wp != wp)
2092
0
      continue;
2093
2094
0
    start = span->data.st.offset;
2095
0
    end = start + span->width;
2096
0
    if (x >= start && x < end) {
2097
0
      *spanp = span;
2098
0
      return (span->data.st.cell_type);
2099
0
    }
2100
2101
0
    if (start > x) {
2102
0
      *spanp = span;
2103
0
      break;
2104
0
    }
2105
0
  }
2106
0
  if (span == NULL)
2107
0
    *spanp = NULL;
2108
0
  return (CELL_LR);
2109
0
}
2110
2111
/* Draw screen. */
2112
void
2113
redraw_screen(struct client *c)
2114
0
{
2115
0
  int flags = 0;
2116
2117
0
  if (c->flags & CLIENT_REDRAWWINDOW)
2118
0
    redraw_draw(c, NULL, REDRAW_ALL);
2119
0
  else {
2120
0
    if (c->flags & CLIENT_REDRAWBORDERS)
2121
0
      flags |= (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS);
2122
0
    if (c->flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))
2123
0
      flags |= (REDRAW_STATUS|REDRAW_PANE_STATUS|
2124
0
          REDRAW_SIDE_STATUS);
2125
0
    if (c->flags & CLIENT_REDRAWMENU)
2126
0
      flags |= REDRAW_MENU;
2127
0
    if (c->session->curw->window->menu != NULL)
2128
0
      flags |= REDRAW_MENU;
2129
0
    if (flags != 0)
2130
0
      redraw_draw(c, NULL, flags);
2131
0
  }
2132
0
}
2133
2134
/* Draw a single pane. */
2135
void
2136
redraw_pane(struct client *c, struct window_pane *wp)
2137
0
{
2138
0
  redraw_draw(c, wp, REDRAW_PANE|REDRAW_PANE_SCROLLBAR);
2139
0
  if (c->session->curw->window->menu != NULL)
2140
0
    redraw_draw(c, NULL, REDRAW_MENU);
2141
0
}
2142
2143
/* Draw a pane's scrollbar. */
2144
void
2145
redraw_pane_scrollbar(struct client *c, struct window_pane *wp)
2146
0
{
2147
0
  redraw_draw(c, wp, REDRAW_PANE_SCROLLBAR);
2148
0
}
2149
2150
/* Rebuild damaged pane status. */
2151
static void
2152
redraw_damage_refresh_status(struct redraw_draw_ctx *dctx,
2153
    struct window_pane *wp)
2154
0
{
2155
0
  struct redraw_span  *first;
2156
0
  u_int      g = wp->status_generation, width;
2157
2158
0
  if ((wp->flags & PANE_NEWSTATUS) && g == redraw_status_generation)
2159
0
    return;
2160
0
  width = redraw_pane_status_width(dctx, wp, &first);
2161
0
  if (width != 0) {
2162
0
    window_make_pane_status(wp, dctx->scene->c, width, first);
2163
0
    wp->flags |= PANE_NEWSTATUS;
2164
0
    wp->status_generation = redraw_status_generation;
2165
0
  }
2166
0
}
2167
2168
/* Draw a pane's prompt over a damaged span. */
2169
static void
2170
redraw_damage_draw_pane_prompt(struct redraw_draw_ctx *dctx,
2171
    struct redraw_span *span, u_int y)
2172
0
{
2173
0
  struct redraw_scene *scene = dctx->scene;
2174
0
  struct window_pane  *wp = span->data.p.wp;
2175
0
  struct tty    *tty = &scene->c->tty;
2176
0
  struct screen    screen;
2177
0
  u_int      px = span->data.p.px, width, prompt_y;
2178
0
  u_int      x = dctx->side_left + span->x;
2179
2180
0
  if (wp->prompt == NULL || wp->sx == 0 || wp->sy == 0)
2181
0
    return;
2182
0
  if (dctx->flags & REDRAW_STATUS_TOP)
2183
0
    prompt_y = 0;
2184
0
  else
2185
0
    prompt_y = wp->sy - 1;
2186
0
  if (span->data.p.py != prompt_y)
2187
0
    return;
2188
0
  redraw_make_pane_prompt(wp, &screen);
2189
0
  if (px < screen_size_x(&screen)) {
2190
0
    width = span->width;
2191
0
    if (width > screen_size_x(&screen) - px)
2192
0
      width = screen_size_x(&screen) - px;
2193
0
    tty_draw_line(tty, &screen, px, 0, width, x, y, NULL);
2194
0
  }
2195
0
  screen_free(&screen);
2196
0
}
2197
2198
/* Draw the spans intersecting a damaged rectangle. */
2199
static void
2200
redraw_draw_damage_rectangle(struct redraw_draw_ctx *dctx, u_int x, u_int y,
2201
    u_int sx, u_int sy)
2202
0
{
2203
0
  struct redraw_scene *scene = dctx->scene;
2204
0
  struct redraw_line  *line;
2205
0
  struct redraw_spans *spans;
2206
0
  struct redraw_span  *span;
2207
0
  enum redraw_phase  phase;
2208
0
  u_int      cy, yy, type;
2209
2210
0
  if (x >= scene->sx || y >= scene->sy)
2211
0
    return;
2212
0
  if (x + sx > scene->sx)
2213
0
    sx = scene->sx - x;
2214
0
  if (y + sy > scene->sy)
2215
0
    sy = scene->sy - y;
2216
0
  if (sx == 0 || sy == 0)
2217
0
    return;
2218
2219
#ifdef ENABLE_IMAGES
2220
  /* Clear old image pixels on all damaged spans. */
2221
  for (yy = y; yy < y + sy; yy++) {
2222
    line = &scene->lines[yy];
2223
    if (dctx->flags & REDRAW_STATUS_TOP)
2224
      cy = dctx->status_lines + yy;
2225
    else
2226
      cy = yy;
2227
    for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
2228
      if (type == REDRAW_SPAN_STATUS)
2229
        continue;
2230
      spans = &line->spans[type];
2231
      TAILQ_FOREACH(span, spans, entry) {
2232
        if (span->x >= x + sx)
2233
          continue;
2234
        if (span->x + span->width <= x)
2235
          continue;
2236
        image_redraw_start(&scene->c->tty,
2237
            dctx->side_left + span->x, cy,
2238
            span->width, 1);
2239
      }
2240
    }
2241
  }
2242
#endif
2243
2244
  /* Draw each phase in the damaged spans. */
2245
0
  for (phase = 0; phase < REDRAW_PHASE_COUNT; phase++) {
2246
0
    for (yy = y; yy < y + sy; yy++) {
2247
0
      line = &scene->lines[yy];
2248
0
      if (dctx->flags & REDRAW_STATUS_TOP)
2249
0
        cy = dctx->status_lines + yy;
2250
0
      else
2251
0
        cy = yy;
2252
0
      for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
2253
0
        if (phase != REDRAW_TEXT &&
2254
0
            type != REDRAW_SPAN_PANE)
2255
0
          continue;
2256
0
        if (type == REDRAW_SPAN_STATUS)
2257
0
          continue;
2258
0
        spans = &line->spans[type];
2259
0
        TAILQ_FOREACH(span, spans, entry) {
2260
0
          if (span->x >= x + sx)
2261
0
            continue;
2262
0
          if (span->x + span->width <= x)
2263
0
            continue;
2264
0
          redraw_draw_span(dctx, span, cy, phase);
2265
0
        }
2266
0
      }
2267
0
    }
2268
#ifdef ENABLE_IMAGES
2269
    image_draw_flush(&scene->c->tty);
2270
#endif
2271
0
  }
2272
2273
  /* Draw prompts and pane status lines last. */
2274
0
  for (yy = y; yy < y + sy; yy++) {
2275
0
    line = &scene->lines[yy];
2276
0
    if (dctx->flags & REDRAW_STATUS_TOP)
2277
0
      cy = dctx->status_lines + yy;
2278
0
    else
2279
0
      cy = yy;
2280
0
    spans = &line->spans[REDRAW_SPAN_PANE];
2281
0
    TAILQ_FOREACH(span, spans, entry) {
2282
0
      if (span->x >= x + sx)
2283
0
        continue;
2284
0
      if (span->x + span->width <= x)
2285
0
        continue;
2286
0
      redraw_damage_draw_pane_prompt(dctx, span, cy);
2287
0
    }
2288
0
    spans = &line->spans[REDRAW_SPAN_STATUS];
2289
0
    TAILQ_FOREACH(span, spans, entry) {
2290
0
      if (span->x >= x + sx)
2291
0
        continue;
2292
0
      if (span->x + span->width <= x)
2293
0
        continue;
2294
0
      redraw_damage_refresh_status(dctx, span->data.st.wp);
2295
0
      redraw_draw_span(dctx, span, cy, REDRAW_TEXT);
2296
0
    }
2297
0
  }
2298
0
}
2299
2300
/* Draw pending window damage on this client. */
2301
void
2302
redraw_client_damage(struct client *c)
2303
0
{
2304
0
  struct window   *w = c->session->curw->window;
2305
0
  struct tty    *tty = &c->tty;
2306
0
  struct window_pane  *wp;
2307
0
  struct redraw_scene *scene;
2308
0
  struct redraw_draw_ctx   dctx;
2309
0
  struct redraw_damage  *rd;
2310
0
  u_int      ox, oy, sx, sy, x0, y0, x1, y1;
2311
2312
0
  if (TAILQ_EMPTY(&w->damage))
2313
0
    return;
2314
0
  redraw_status_generation++;
2315
2316
0
  scene = redraw_get_scene(c);
2317
0
  if (scene == NULL)
2318
0
    return;
2319
0
  redraw_set_draw_context(&dctx, scene);
2320
0
  redraw_get_window_offset(c, &ox, &oy, &sx, &sy);
2321
2322
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
2323
0
    wp->border_gc_set = 0;
2324
0
    wp->active_border_gc_set = 0;
2325
0
  }
2326
2327
0
  tty_sync_start(tty);
2328
0
  tty_update_mode(tty, tty->mode & ~CURSOR_MODES, NULL);
2329
2330
0
  TAILQ_FOREACH(rd, &w->damage, entry) {
2331
#ifdef ENABLE_IMAGES
2332
    if ((rd->flags & REDRAW_DAMAGE_SCROLL) &&
2333
        (image_backend_get_flags(tty) & IMAGE_BACKEND_SCROLLS) &&
2334
        tty->scroll_window == w &&
2335
        tty->scroll_generation == w->scroll_generation &&
2336
        !tty->scroll_failed)
2337
      continue;
2338
#endif
2339
0
    x0 = (rd->x > ox) ? rd->x : ox;
2340
0
    y0 = (rd->y > oy) ? rd->y : oy;
2341
0
    x1 = (rd->x + rd->sx < ox + sx) ? rd->x + rd->sx : ox + sx;
2342
0
    y1 = (rd->y + rd->sy < oy + sy) ? rd->y + rd->sy : oy + sy;
2343
0
    if (x0 < x1 && y0 < y1) {
2344
0
      redraw_draw_damage_rectangle(&dctx, x0 - ox, y0 - oy,
2345
0
          x1 - x0, y1 - y0);
2346
0
    }
2347
0
  }
2348
0
}