Coverage Report

Created: 2026-10-28 06:09

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.171 2026/10/21 07:38:05 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
180
#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, side_owner, above, left;
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
  /*
597
   * A corner cell can be shared by four tiled panes, two on each side,
598
   * and only one pane is kept for each side. So that none is lost
599
   * whatever order panes are marked in, a pane keeps its corner only on
600
   * the side clockwise from it: a pane above and to the left of the cell
601
   * is the top pane, above and to the right is the right pane, and so on.
602
   */
603
0
  side_owner = (mask & (REDRAW_BORDER_U|REDRAW_BORDER_D));
604
0
  if (!floating &&
605
0
      (mask == REDRAW_BORDER_L ||
606
0
      mask == REDRAW_BORDER_R ||
607
0
      mask == REDRAW_BORDER_U ||
608
0
      mask == REDRAW_BORDER_D)) {
609
0
    above = (wy >= wp->yoff + (int)wp->sy);
610
0
    left = (wx >= wp->xoff + (int)wp->sx);
611
0
    top_owner = (above && left);
612
0
    bottom_owner = (!above && !left);
613
0
    side_owner = (above != left);
614
0
  }
615
616
0
  if (top_owner) {
617
0
    bc->data.b.top_wp = wp;
618
0
    bc->data.b.top_lines = pane_lines;
619
0
  }
620
0
  if (bottom_owner) {
621
0
    bc->data.b.bottom_wp = wp;
622
0
    bc->data.b.bottom_lines = pane_lines;
623
0
  }
624
625
0
  if (side_owner) {
626
0
    if (wx < wp->xoff) {
627
0
      bc->data.b.right_wp = wp;
628
0
      bc->data.b.right_lines = pane_lines;
629
0
    } else if (wx >= wp->xoff + (int)wp->sx) {
630
0
      bc->data.b.left_wp = wp;
631
0
      bc->data.b.left_lines = pane_lines;
632
0
    }
633
0
  }
634
635
0
  mask |= bc->data.b.cell_mask;
636
0
  bc->data.b.cell_mask = mask;
637
0
  bc->data.b.cell_type = redraw_get_cell_type(mask);
638
0
}
639
640
/*
641
 * Mark border cells for a pane status line, keeping the border cell type for
642
 * drawing.
643
 */
644
static void
645
redraw_mark_border_status(struct redraw_build_ctx *bctx, struct window_pane *wp,
646
    __unused int left, int right, int top, int bottom)
647
0
{
648
0
  struct redraw_build_cell  *bc;
649
0
  u_int        x, y, off = 0;
650
0
  int        pane_status, wy, sx, ex, wx, cell_type;
651
652
0
  pane_status = window_pane_get_pane_status(wp);
653
0
  if (pane_status == PANE_STATUS_OFF)
654
0
    return;
655
0
  if (pane_status == PANE_STATUS_TOP)
656
0
    wy = top;
657
0
  else
658
0
    wy = bottom;
659
660
0
  sx = wp->xoff + 2;
661
0
  ex = right - 1;
662
0
  if (sx > ex)
663
0
    return;
664
665
0
  for (wx = sx; wx <= ex; wx++, off++) {
666
0
    if (!redraw_window_to_scene(bctx, wx, wy, &x, &y))
667
0
      continue;
668
0
    bc = redraw_get_build_cell(bctx, x, y);
669
0
    if (bc->data.type != REDRAW_SPAN_BORDER)
670
0
      continue;
671
0
    cell_type = bc->data.b.cell_type;
672
673
0
    bc->data.type = REDRAW_SPAN_STATUS;
674
0
    bc->data.st.wp = wp;
675
0
    bc->data.st.offset = off;
676
0
    bc->data.st.cell_type = cell_type;
677
0
  }
678
0
}
679
680
/* Mark existing border cells where indicator arrows will be drawn. */
681
static void
682
redraw_mark_border_arrows(struct redraw_build_ctx *bctx, struct window_pane *wp,
683
    int left, int right, int top, int bottom)
684
0
{
685
0
  struct redraw_build_cell  *bc;
686
0
  u_int        x, y;
687
0
  int        wx, wy;
688
689
0
  if (bctx->ind != PANE_BORDER_ARROWS && bctx->ind != PANE_BORDER_BOTH)
690
0
    return;
691
692
0
  wx = wp->xoff + 1;
693
0
  if (wx >= left && wx <= right) {
694
0
    wy = top;
695
0
    if (redraw_window_to_scene(bctx, wx, wy, &x, &y)) {
696
0
      bc = redraw_get_build_cell(bctx, x, y);
697
0
      if (bc->data.type == REDRAW_SPAN_BORDER)
698
0
        bc->data.b.flags |= REDRAW_BORDER_IS_ARROW;
699
0
    }
700
0
    wy = bottom;
701
0
    if (redraw_window_to_scene(bctx, wx, wy, &x, &y)) {
702
0
      bc = redraw_get_build_cell(bctx, x, y);
703
0
      if (bc->data.type == REDRAW_SPAN_BORDER)
704
0
        bc->data.b.flags |= REDRAW_BORDER_IS_ARROW;
705
0
    }
706
0
  }
707
708
0
  wy = wp->yoff + 1;
709
0
  if (wy >= top && wy <= bottom) {
710
0
    wx = left;
711
0
    if (redraw_window_to_scene(bctx, wx, wy, &x, &y)) {
712
0
      bc = redraw_get_build_cell(bctx, x, y);
713
0
      if (bc->data.type == REDRAW_SPAN_BORDER)
714
0
        bc->data.b.flags |= REDRAW_BORDER_IS_ARROW;
715
0
    }
716
0
    wx = right;
717
0
    if (redraw_window_to_scene(bctx, wx, wy, &x, &y)) {
718
0
      bc = redraw_get_build_cell(bctx, x, y);
719
0
      if (bc->data.type == REDRAW_SPAN_BORDER)
720
0
        bc->data.b.flags |= REDRAW_BORDER_IS_ARROW;
721
0
    }
722
0
  }
723
0
}
724
725
/* Mark pane borders. */
726
static void
727
redraw_mark_pane_borders(struct redraw_build_ctx *bctx, struct window_pane *wp,
728
    int sb_w, int sb_left)
729
0
{
730
0
  enum pane_lines pane_lines = window_pane_get_pane_lines(wp);
731
0
  int   pane_status, left, right, top, bottom, wx, wy;
732
0
  int   mark_top, mark_bottom, mark_left, mark_right, mask = 0;
733
0
  int   floating = window_pane_is_floating(wp);
734
735
0
  if (floating && pane_lines == PANE_LINES_NONE)
736
0
    return;
737
0
  pane_status = window_pane_get_pane_status(wp);
738
739
0
  left = wp->xoff - 1;
740
0
  right = wp->xoff + wp->sx;
741
0
  if (sb_w != 0) {
742
0
    if (sb_left)
743
0
      left -= sb_w;
744
0
    else
745
0
      right += sb_w;
746
0
  }
747
0
  top = wp->yoff - 1;
748
0
  bottom = wp->yoff + wp->sy;
749
750
0
  mark_left = (left >= 0);
751
0
  mark_top = (top >= 0);
752
753
0
  if (floating) {
754
0
    mark_right = (right < (int)bctx->w->sx);
755
0
    mark_bottom = (bottom < (int)bctx->w->sy);
756
0
    if (left < 0)
757
0
      left = 0;
758
0
    if (right >= (int)bctx->w->sx)
759
0
      right = (int)bctx->w->sx - 1;
760
0
    if (top < 0)
761
0
      top = 0;
762
0
    if (bottom >= (int)bctx->w->sy)
763
0
      bottom = (int)bctx->w->sy - 1;
764
0
  } else {
765
0
    mark_right = (right <= (int)bctx->w->sx);
766
0
    mark_bottom = (bottom <= (int)bctx->w->sy);
767
0
    if (pane_status == PANE_STATUS_TOP && bottom < (int)bctx->w->sy)
768
0
      mark_bottom = 0;
769
0
    else if (pane_status == PANE_STATUS_BOTTOM)
770
0
      mark_top = 0;
771
0
  }
772
773
0
  if (mark_top) {
774
0
    for (wx = left; wx <= right; wx++) {
775
0
      mask = 0;
776
0
      if (wx > left)
777
0
        mask |= REDRAW_BORDER_L;
778
0
      if (wx < right)
779
0
        mask |= REDRAW_BORDER_R;
780
0
      redraw_mark_border_cell(bctx, wx, top, wp, 0, 1, mask,
781
0
          pane_lines, floating);
782
0
    }
783
0
  }
784
0
  if (mark_bottom) {
785
0
    for (wx = left; wx <= right; wx++) {
786
0
      mask = 0;
787
0
      if (wx > left)
788
0
        mask |= REDRAW_BORDER_L;
789
0
      if (wx < right)
790
0
        mask |= REDRAW_BORDER_R;
791
0
      redraw_mark_border_cell(bctx, wx, bottom, wp, 1, 0,
792
0
          mask, pane_lines, floating);
793
0
    }
794
0
  }
795
0
  if (mark_left) {
796
0
    for (wy = top; wy <= bottom; wy++) {
797
0
      mask = 0;
798
0
      if (wy > top)
799
0
        mask |= REDRAW_BORDER_U;
800
0
      if (wy < bottom)
801
0
        mask |= REDRAW_BORDER_D;
802
0
      redraw_mark_border_cell(bctx, left, wy, wp, 0, 0, mask,
803
0
          pane_lines, floating);
804
0
    }
805
0
  }
806
0
  if (mark_right) {
807
0
    for (wy = top; wy <= bottom; wy++) {
808
0
      mask = 0;
809
0
      if (wy > top)
810
0
        mask |= REDRAW_BORDER_U;
811
0
      if (wy < bottom)
812
0
        mask |= REDRAW_BORDER_D;
813
0
      redraw_mark_border_cell(bctx, right, wy, wp, 0, 0, mask,
814
0
          pane_lines, floating);
815
0
    }
816
0
  }
817
818
0
  redraw_mark_border_status(bctx, wp, left, right, top, bottom);
819
0
  redraw_mark_border_arrows(bctx, wp, left, right, top, bottom);
820
0
}
821
822
/*
823
 * Mark an entire pane in the build grid. Floating panes overwrite anything
824
 * already below them.
825
 */
826
static void
827
redraw_mark_pane(struct redraw_build_ctx *bctx, struct window_pane *wp)
828
0
{
829
0
  struct style  *sb_style = &wp->scrollbar_style;
830
0
  int    sb_w = 0, sb_left = 0, overlay = 0;
831
832
0
  if (!window_pane_is_visible(wp))
833
0
    return;
834
835
0
  if (window_pane_scrollbar_visible(wp)) {
836
0
    overlay = window_pane_scrollbar_overlay(wp);
837
0
    if (!overlay)
838
0
      sb_w = sb_style->width + sb_style->pad;
839
0
    else {
840
0
      sb_w = sb_style->width + sb_style->pad;
841
0
      if (sb_w > (int)wp->sx) {
842
0
        sb_w = sb_style->width;
843
0
        if (sb_w > (int)wp->sx)
844
0
          sb_w = wp->sx;
845
0
      }
846
0
    }
847
0
  }
848
0
  if (sb_w != 0 && bctx->w->sb_pos == PANE_SCROLLBARS_LEFT)
849
0
    sb_left = 1;
850
851
0
  redraw_mark_pane_inside(bctx, wp);
852
0
  redraw_mark_pane_borders(bctx, wp, overlay ? 0 : sb_w, sb_left);
853
0
  redraw_mark_pane_scrollbar(bctx, wp, sb_w, sb_left, overlay);
854
0
}
855
856
/* Choose the pane that will provide the border style for two-pane layouts. */
857
static void
858
redraw_mark_two_pane_colours(struct redraw_build_ctx *bctx)
859
0
{
860
0
  struct redraw_build_cell  *bc;
861
0
  struct redraw_span_data   *sd;
862
0
  enum layout_type     type;
863
0
  u_int        x, y, wx, wy;
864
865
0
  if (bctx->ind != PANE_BORDER_COLOUR && bctx->ind != PANE_BORDER_BOTH)
866
0
    return;
867
0
  if (!redraw_check_two_pane_colours(bctx->w, &type))
868
0
    return;
869
870
0
  for (y = 0; y < bctx->sy; y++) {
871
0
    for (x = 0; x < bctx->sx; x++) {
872
0
      bc = redraw_get_build_cell(bctx, x, y);
873
0
      if (bc->data.type != REDRAW_SPAN_BORDER)
874
0
        continue;
875
0
      sd = &bc->data;
876
877
0
      wx = bctx->ox + x;
878
0
      wy = bctx->oy + y;
879
880
0
      if (type == LAYOUT_LEFTRIGHT &&
881
0
          sd->b.left_wp != NULL &&
882
0
          sd->b.right_wp != NULL) {
883
0
        if (wy <= bctx->w->sy / 2)
884
0
          sd->b.style_wp = sd->b.left_wp;
885
0
        else
886
0
          sd->b.style_wp = sd->b.right_wp;
887
0
      } else if (type == LAYOUT_TOPBOTTOM &&
888
0
          sd->b.top_wp != NULL &&
889
0
          sd->b.bottom_wp != NULL) {
890
0
        if (wx <= bctx->w->sx / 2)
891
0
          sd->b.style_wp = sd->b.top_wp;
892
0
        else
893
0
          sd->b.style_wp = sd->b.bottom_wp;
894
0
      }
895
0
    }
896
0
  }
897
0
}
898
899
/* Mark the window menu above all panes. */
900
static void
901
redraw_mark_menu(struct redraw_build_ctx *bctx)
902
0
{
903
0
  struct menu_data    *md = bctx->w->menu;
904
0
  struct redraw_build_cell  *bc;
905
0
  u_int        px, py, x, y, sx, sy;
906
907
0
  if (md == NULL)
908
0
    return;
909
910
0
  sx = menu_width(md);
911
0
  sy = menu_height(md);
912
0
  for (py = 0; py < sy; py++) {
913
0
    for (px = 0; px < sx; px++) {
914
0
      if (!redraw_window_to_scene(bctx, menu_x(md) + px,
915
0
          menu_y(md) + py, &x, &y))
916
0
        continue;
917
0
      bc = redraw_get_build_cell(bctx, x, y);
918
0
      memset(bc, 0, sizeof *bc);
919
0
      bc->data.type = REDRAW_SPAN_MENU;
920
0
      bc->data.m.md = md;
921
0
      bc->data.m.px = px;
922
0
      bc->data.m.py = py;
923
0
    }
924
0
  }
925
0
}
926
927
/* Return true if two adjacent build cells can be joined into one span. */
928
static int
929
redraw_compare_data(struct redraw_build_cell *a, struct redraw_build_cell *b)
930
0
{
931
0
  struct redraw_span_data *ad = &a->data, *bd = &b->data;
932
933
0
  if (ad->type != bd->type)
934
0
    return (0);
935
936
0
  switch (ad->type) {
937
0
  case REDRAW_SPAN_PANE:
938
0
    if (ad->p.wp != bd->p.wp ||
939
0
        ad->p.py != bd->p.py ||
940
0
        ad->p.px + 1 != bd->p.px)
941
0
      return (0);
942
0
    return (1);
943
0
  case REDRAW_SPAN_BORDER:
944
0
    if (ad->b.top_wp != bd->b.top_wp ||
945
0
        ad->b.bottom_wp != bd->b.bottom_wp ||
946
0
        ad->b.left_wp != bd->b.left_wp ||
947
0
        ad->b.right_wp != bd->b.right_wp ||
948
0
        ad->b.style_wp != bd->b.style_wp ||
949
0
        ad->b.top_lines != bd->b.top_lines ||
950
0
        ad->b.bottom_lines != bd->b.bottom_lines ||
951
0
        ad->b.left_lines != bd->b.left_lines ||
952
0
        ad->b.right_lines != bd->b.right_lines ||
953
0
        ad->b.cell_type != bd->b.cell_type ||
954
0
        ad->b.cell_mask != bd->b.cell_mask ||
955
0
        ad->b.flags != bd->b.flags)
956
0
      return (0);
957
0
    if (ad->b.flags & REDRAW_BORDER_IS_ARROW)
958
0
      return (0);
959
0
    return (1);
960
0
  case REDRAW_SPAN_STATUS:
961
0
    if (ad->st.wp != bd->st.wp ||
962
0
        ad->st.offset + 1 != bd->st.offset ||
963
0
        ad->st.cell_type != bd->st.cell_type)
964
0
      return (0);
965
0
    return (1);
966
0
  case REDRAW_SPAN_SCROLLBAR:
967
0
    if (ad->sb.wp != bd->sb.wp ||
968
0
        ad->sb.y != bd->sb.y ||
969
0
        ad->sb.height != bd->sb.height ||
970
0
        ad->sb.flags != bd->sb.flags)
971
0
      return (0);
972
0
    return (1);
973
0
  case REDRAW_SPAN_MENU:
974
0
    if (ad->m.md != bd->m.md ||
975
0
        ad->m.py != bd->m.py ||
976
0
        ad->m.px + 1 != bd->m.px)
977
0
      return (0);
978
0
    return (1);
979
0
  case REDRAW_SPAN_OUTSIDE:
980
0
  case REDRAW_SPAN_EMPTY:
981
0
    return (1);
982
0
  }
983
0
  return (0);
984
0
}
985
986
/* Build the temporary cells for a redraw scene. */
987
static void
988
redraw_build_cells(struct redraw_build_ctx *bctx)
989
0
{
990
0
  struct window   *w = bctx->w;
991
0
  struct window_pane  *wp;
992
0
  size_t       ncells;
993
0
  u_int      x, y;
994
995
0
  if (bctx->sx != 0 && bctx->sy > SIZE_MAX / bctx->sx)
996
0
    fatalx("%s: too many cells", __func__);
997
0
  ncells = (size_t)bctx->sx * bctx->sy;
998
0
  if (ncells > redraw_ncells) {
999
0
    redraw_cells = xreallocarray(redraw_cells, ncells,
1000
0
        sizeof *redraw_cells);
1001
0
    redraw_ncells = ncells;
1002
0
  }
1003
1004
0
  bctx->cells = redraw_cells;
1005
0
  for (y = 0; y < bctx->sy; y++) {
1006
0
    for (x = 0; x < bctx->sx; x++)
1007
0
      redraw_reset_cell(bctx, x, y);
1008
0
  }
1009
1010
0
  TAILQ_FOREACH_REVERSE(wp, &w->z_index, window_panes_zindex, zentry)
1011
0
    redraw_mark_pane(bctx, wp);
1012
0
  redraw_mark_two_pane_colours(bctx);
1013
0
  redraw_mark_menu(bctx);
1014
0
}
1015
1016
/*
1017
 * Build and return a redraw scene for a client. The caller owns the scene and
1018
 * must free it with redraw_free_scene.
1019
 */
1020
static struct redraw_scene *
1021
redraw_make_scene(struct client *c)
1022
0
{
1023
0
  struct session      *s = c->session;
1024
0
  struct window     *w = s->curw->window;
1025
0
  struct redraw_build_ctx    bctx;
1026
0
  struct redraw_scene   *scene;
1027
0
  struct redraw_build_cell  *bc, *last;
1028
0
  struct redraw_line    *line;
1029
0
  struct redraw_span    *span;
1030
0
  enum redraw_span_type    type;
1031
0
  u_int        x, y, x0;
1032
1033
0
  if (c->flags & CLIENT_SUSPENDED)
1034
0
    return (NULL);
1035
1036
0
  redraw_set_context(c, &bctx);
1037
1038
0
  log_debug("%s: building @%u scene (%ux%u %u,%u; generation %llu)",
1039
0
      c->name, w->id, bctx.sx, bctx.sy, bctx.ox, bctx.oy,
1040
0
      (unsigned long long)w->redraw_scene_generation);
1041
1042
0
  redraw_build_cells(&bctx);
1043
1044
0
  scene = xcalloc(1, sizeof *scene);
1045
0
  scene->c = c;
1046
0
  scene->w = w;
1047
0
  scene->lines = xcalloc(bctx.sy, sizeof *scene->lines);
1048
0
  scene->generation = w->redraw_scene_generation;
1049
0
  scene->sx = bctx.sx;
1050
0
  scene->sy = bctx.sy;
1051
0
  scene->ox = bctx.ox;
1052
0
  scene->oy = bctx.oy;
1053
1054
0
  for (y = 0; y < bctx.sy; y++) {
1055
0
    line = &scene->lines[y];
1056
0
    for (type = 0; type < REDRAW_SPAN_TYPES; type++)
1057
0
      TAILQ_INIT(&line->spans[type]);
1058
1059
0
    x = 0;
1060
0
    while (x < bctx.sx) {
1061
0
      x0 = x;
1062
0
      last = redraw_get_build_cell(&bctx, x, y);
1063
0
      x++;
1064
1065
0
      while (x < bctx.sx) {
1066
0
        bc = redraw_get_build_cell(&bctx, x, y);
1067
0
        if (!redraw_compare_data(last, bc))
1068
0
          break;
1069
0
        last = bc;
1070
0
        x++;
1071
0
      }
1072
0
      bc = redraw_get_build_cell(&bctx, x0, y);
1073
0
      type = bc->data.type;
1074
1075
0
      span = xcalloc(1, sizeof *span);
1076
0
      span->x = x0;
1077
0
      span->width = x - x0;
1078
0
      span->data = bc->data;
1079
1080
0
      TAILQ_INSERT_TAIL(&line->spans[type], span, entry);
1081
0
    }
1082
0
  }
1083
1084
0
  log_debug("%s: finished building @%u scene", c->name, w->id);
1085
0
  return (scene);
1086
0
}
1087
1088
/* Free a scene. */
1089
void
1090
redraw_free_scene(struct redraw_scene *scene)
1091
0
{
1092
0
  struct redraw_spans *spans;
1093
0
  struct redraw_span  *span, *span1;
1094
0
  u_int      y, type;
1095
1096
0
  if (scene == NULL)
1097
0
    return;
1098
1099
0
  for (y = 0; y < scene->sy; y++) {
1100
0
    for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
1101
0
      spans = &scene->lines[y].spans[type];
1102
0
      TAILQ_FOREACH_SAFE(span, spans, entry, span1) {
1103
0
        TAILQ_REMOVE(spans, span, entry);
1104
0
        free(span);
1105
0
      }
1106
0
    }
1107
0
  }
1108
0
  free(scene->lines);
1109
0
  free(scene);
1110
0
}
1111
1112
/* Does a client's cached scene show this window? */
1113
int
1114
redraw_client_has_window(struct client *c, struct window *w)
1115
0
{
1116
0
  return (c->redraw_scene != NULL && c->redraw_scene->w == w);
1117
0
}
1118
1119
/*
1120
 * Mark a window's cached redraw scenes as out of date. Generations are unique
1121
 * across all windows so a scene can never match a new window that reuses the
1122
 * address of a freed one.
1123
 */
1124
void
1125
redraw_invalidate_scene(struct window *w)
1126
72.2k
{
1127
72.2k
  static uint64_t next_generation;
1128
1129
72.2k
  w->redraw_scene_generation = ++next_generation;
1130
72.2k
}
1131
1132
/* Free all pending damage for a window. */
1133
void
1134
redraw_free_damage(struct window *w)
1135
16.2k
{
1136
16.2k
  struct redraw_damage  *rd, *rd1;
1137
1138
16.2k
  TAILQ_FOREACH_SAFE(rd, &w->damage, entry, rd1) {
1139
180
    TAILQ_REMOVE(&w->damage, rd, entry);
1140
180
    free(rd);
1141
180
  }
1142
16.2k
  w->damage_count = 0;
1143
16.2k
  w->scroll_pending = 0;
1144
16.2k
}
1145
1146
/* Collapse all pending damage for a window into one rectangle. */
1147
static void
1148
redraw_collapse_damage(struct window *w)
1149
0
{
1150
0
  struct redraw_damage  *rd, *rd1, *first;
1151
0
  u_int      x0, y0, x1, y1;
1152
1153
0
  first = TAILQ_FIRST(&w->damage);
1154
0
  if (first == NULL)
1155
0
    return;
1156
0
  x0 = first->x;
1157
0
  y0 = first->y;
1158
0
  x1 = first->x + first->sx;
1159
0
  y1 = first->y + first->sy;
1160
1161
0
  TAILQ_FOREACH_SAFE(rd, &w->damage, entry, rd1) {
1162
0
    if (rd->x < x0)
1163
0
      x0 = rd->x;
1164
0
    if (rd->y < y0)
1165
0
      y0 = rd->y;
1166
0
    if (rd->x + rd->sx > x1)
1167
0
      x1 = rd->x + rd->sx;
1168
0
    if (rd->y + rd->sy > y1)
1169
0
      y1 = rd->y + rd->sy;
1170
0
    first->flags &= rd->flags;
1171
0
    if (rd != first) {
1172
0
      TAILQ_REMOVE(&w->damage, rd, entry);
1173
0
      free(rd);
1174
0
    }
1175
0
  }
1176
1177
0
  first->x = x0;
1178
0
  first->y = y0;
1179
0
  first->sx = x1 - x0;
1180
0
  first->sy = y1 - y0;
1181
0
  w->damage_count = 1;
1182
0
}
1183
1184
/* Record window damage, merging nearby rectangles and limiting the count. */
1185
static void
1186
redraw_apply_window_damage(struct window *w, u_int x, u_int y, u_int sx,
1187
    u_int sy, int flags)
1188
2.82k
{
1189
2.82k
  struct redraw_damage  *rd;
1190
2.82k
  u_int      x0, y0, x1, y1, area, union_area;
1191
1192
2.82k
  if (x >= w->sx || y >= w->sy)
1193
0
    return;
1194
2.82k
  if (x + sx > w->sx)
1195
0
    sx = w->sx - x;
1196
2.82k
  if (y + sy > w->sy)
1197
0
    sy = w->sy - y;
1198
2.82k
  if (sx == 0 || sy == 0)
1199
0
    return;
1200
1201
2.82k
  TAILQ_FOREACH(rd, &w->damage, entry) {
1202
2.64k
    if (x > rd->x + rd->sx || rd->x > x + sx ||
1203
2.64k
        y > rd->y + rd->sy || rd->y > y + sy)
1204
0
      continue;
1205
1206
2.64k
    x0 = (x < rd->x) ? x : rd->x;
1207
2.64k
    y0 = (y < rd->y) ? y : rd->y;
1208
2.64k
    x1 = (x + sx > rd->x + rd->sx) ? x + sx : rd->x + rd->sx;
1209
2.64k
    y1 = (y + sy > rd->y + rd->sy) ? y + sy : rd->y + rd->sy;
1210
1211
2.64k
    area = sx * sy + rd->sx * rd->sy;
1212
2.64k
    union_area = (x1 - x0) * (y1 - y0);
1213
2.64k
    if (union_area > 2 * area)
1214
0
      continue;
1215
1216
2.64k
    rd->x = x0;
1217
2.64k
    rd->y = y0;
1218
2.64k
    rd->sx = x1 - x0;
1219
2.64k
    rd->sy = y1 - y0;
1220
2.64k
    rd->flags &= flags;
1221
2.64k
    return;
1222
2.64k
  }
1223
1224
180
  rd = xcalloc(1, sizeof *rd);
1225
180
  rd->x = x;
1226
180
  rd->y = y;
1227
180
  rd->sx = sx;
1228
180
  rd->sy = sy;
1229
180
  rd->flags = flags;
1230
180
  TAILQ_INSERT_TAIL(&w->damage, rd, entry);
1231
180
  if (++w->damage_count > REDRAW_DAMAGE_MAX)
1232
0
    redraw_collapse_damage(w);
1233
180
}
1234
1235
/* Record ordinary window damage. */
1236
void
1237
redraw_damage_window(struct window *w, u_int x, u_int y, u_int sx, u_int sy)
1238
2.82k
{
1239
2.82k
  redraw_apply_window_damage(w, x, y, sx, sy, 0);
1240
2.82k
}
1241
1242
/* Record damage caused by terminal scrolling. */
1243
void
1244
redraw_damage_scroll(struct window *w, u_int x, u_int y, u_int sx, u_int sy)
1245
0
{
1246
0
  if (!w->scroll_pending) {
1247
0
    w->scroll_generation++;
1248
0
    w->scroll_pending = 1;
1249
0
  }
1250
0
  redraw_apply_window_damage(w, x, y, sx, sy, REDRAW_DAMAGE_SCROLL);
1251
0
}
1252
1253
/* Remember whether this client used terminal scrolling for the damage. */
1254
void
1255
redraw_scroll_result(struct tty *tty, const struct tty_ctx *ctx, int failed)
1256
0
{
1257
0
  struct window_pane  *wp = ctx->arg;
1258
0
  struct window   *w;
1259
1260
0
  if (wp == NULL)
1261
0
    return;
1262
0
  w = wp->window;
1263
0
  if (!w->scroll_pending)
1264
0
    return;
1265
0
  if (tty->scroll_window != w ||
1266
0
      tty->scroll_generation != w->scroll_generation) {
1267
0
    tty->scroll_window = w;
1268
0
    tty->scroll_generation = w->scroll_generation;
1269
0
    tty->scroll_failed = 0;
1270
0
  }
1271
0
  if (failed)
1272
0
    tty->scroll_failed = 1;
1273
0
}
1274
1275
/* Mark all cached redraw scenes as out of date. */
1276
void
1277
redraw_invalidate_all_scenes(void)
1278
0
{
1279
0
  struct window *w;
1280
1281
0
  RB_FOREACH(w, windows, &windows)
1282
0
    redraw_invalidate_scene(w);
1283
0
}
1284
1285
/* Get the cached redraw scene, rebuilding it if needed. */
1286
static struct redraw_scene *
1287
redraw_get_scene(struct client *c)
1288
0
{
1289
0
  struct redraw_scene *scene = c->redraw_scene;
1290
0
  struct window   *w = c->session->curw->window;
1291
0
  const char    *reason = NULL;
1292
0
  u_int      ox, oy, sx, sy;
1293
1294
0
  redraw_get_window_offset(c, &ox, &oy, &sx, &sy);
1295
0
  if (scene == NULL)
1296
0
    reason = "missing";
1297
0
  else if (scene->w != w)
1298
0
    reason = "window changed";
1299
0
  else if (scene->generation != w->redraw_scene_generation)
1300
0
    reason = "generation changed";
1301
0
  else if (scene->ox != ox || scene->oy != oy)
1302
0
    reason = "offset changed";
1303
0
  else if (scene->sx != sx || scene->sy != sy)
1304
0
    reason = "size changed";
1305
0
  if (reason != NULL) {
1306
0
    log_debug("%s: @%u scene invalid: %s", c->name, w->id, reason);
1307
0
    redraw_free_scene(scene);
1308
0
    scene = redraw_make_scene(c);
1309
0
    c->redraw_scene = scene;
1310
0
  }
1311
0
  return (scene);
1312
0
}
1313
1314
/* Draw a pane span. */
1315
static void
1316
redraw_draw_pane_span(struct redraw_draw_ctx *dctx,
1317
    struct redraw_span *span, u_int x, u_int y, u_int n)
1318
0
{
1319
0
  struct redraw_scene *scene = dctx->scene;
1320
0
  struct client   *c = scene->c;
1321
0
  struct tty    *tty = &c->tty;
1322
0
  struct window_pane  *wp = span->data.p.wp;
1323
0
  struct screen   *s = wp->screen;
1324
0
  struct grid_cell   defaults;
1325
0
  struct tty_style_ctx   style_ctx;
1326
0
  u_int      px, py;
1327
1328
0
  tty_default_colours(&defaults, wp, &style_ctx.dim);
1329
0
  style_ctx.defaults = &defaults;
1330
0
  style_ctx.palette = &wp->palette;
1331
0
  style_ctx.hyperlinks = s->hyperlinks;
1332
1333
0
  px = span->data.p.px + (x - dctx->side_left - span->x);
1334
0
  py = span->data.p.py;
1335
0
  tty_draw_line(tty, s, px, py, n, x, y, &style_ctx);
1336
0
}
1337
1338
/* Get default border style for spans without a pane. */
1339
static void
1340
redraw_get_default_border_style(struct redraw_draw_ctx *dctx,
1341
    struct grid_cell *gc, enum pane_lines *pane_lines)
1342
0
{
1343
0
  struct redraw_scene *scene = dctx->scene;
1344
0
  struct client   *c = scene->c;
1345
0
  struct session    *s = c->session;
1346
0
  struct options    *oo = scene->w->options;
1347
0
  struct format_tree  *ft;
1348
0
  struct grid_cell  *dgc = &dctx->default_gc;
1349
1350
0
  if (~dctx->flags & REDRAW_DEFAULT_SET) {
1351
0
    ft = format_create_defaults(NULL, c, s, s->curw, NULL);
1352
0
    memcpy(dgc, &grid_default_cell, sizeof *dgc);
1353
0
    style_add(dgc, oo, "pane-border-style", ft);
1354
0
    format_free(ft);
1355
0
    dctx->pane_lines = options_get_number(oo, "pane-border-lines");
1356
0
    dctx->flags |= REDRAW_DEFAULT_SET;
1357
0
  }
1358
0
  memcpy(gc, dgc, sizeof *gc);
1359
0
  *pane_lines = dctx->pane_lines;
1360
0
}
1361
1362
/*
1363
 * For this border span, pick the pane whose border style should colour it.
1364
 * Prefer an explicitly assigned style owner, then the active adjacent pane,
1365
 * then any adjacent pane.
1366
 */
1367
static struct window_pane *
1368
redraw_get_pane_for_border_style(struct redraw_draw_ctx *dctx,
1369
    struct redraw_span *span)
1370
0
{
1371
0
  struct window_pane  *active = dctx->active;
1372
1373
0
  if (span->data.type != REDRAW_SPAN_BORDER)
1374
0
    return (NULL);
1375
1376
0
  if (span->data.b.style_wp != NULL)
1377
0
    return (span->data.b.style_wp);
1378
0
  if (active != NULL && redraw_data_has_pane(&span->data, active))
1379
0
    return (active);
1380
1381
0
  if (span->data.b.top_wp != NULL)
1382
0
    return (span->data.b.top_wp);
1383
0
  if (span->data.b.bottom_wp != NULL)
1384
0
    return (span->data.b.bottom_wp);
1385
0
  if (span->data.b.left_wp != NULL)
1386
0
    return (span->data.b.left_wp);
1387
0
  if (span->data.b.right_wp != NULL)
1388
0
    return (span->data.b.right_wp);
1389
0
  return (NULL);
1390
0
}
1391
1392
/* Draw arrow indicator if this border span is an arrow cell. */
1393
static void
1394
redraw_draw_border_arrow(struct redraw_draw_ctx *dctx,
1395
    struct redraw_span *span, struct grid_cell *gc)
1396
0
{
1397
0
  struct window_pane  *active = dctx->active;
1398
0
  char       ch;
1399
1400
0
  if (span->data.type != REDRAW_SPAN_BORDER || active == NULL)
1401
0
    return;
1402
0
  if (~span->data.b.flags & REDRAW_BORDER_IS_ARROW)
1403
0
    return;
1404
1405
0
  if (span->data.b.left_wp == active)
1406
0
    ch = ',';
1407
0
  else if (span->data.b.right_wp == active)
1408
0
    ch = '+';
1409
0
  else if (span->data.b.top_wp == active)
1410
0
    ch = '-';
1411
0
  else if (span->data.b.bottom_wp == active)
1412
0
    ch = '.';
1413
0
  else
1414
0
    return;
1415
1416
0
  utf8_set(&gc->data, ch);
1417
0
  gc->attr |= GRID_ATTR_CHARSET;
1418
0
}
1419
1420
/* Draw a border span. */
1421
static void
1422
redraw_draw_border_span(struct redraw_draw_ctx *dctx,
1423
    struct redraw_span *span, u_int x, u_int y, u_int n)
1424
0
{
1425
0
  struct redraw_scene *scene = dctx->scene;
1426
0
  struct client   *c = scene->c;
1427
0
  struct tty    *tty = &c->tty;
1428
0
  struct window   *w = scene->w;
1429
0
  struct window_pane  *wp = NULL;
1430
0
  struct grid_cell   gc;
1431
0
  enum pane_lines    pane_lines;
1432
0
  u_int      i, cell_type;
1433
0
  int      isolates = 0;
1434
1435
0
  if (span->data.type != REDRAW_SPAN_BORDER)
1436
0
    cell_type = CELL_NONE;
1437
0
  else {
1438
0
    wp = redraw_get_pane_for_border_style(dctx, span);
1439
0
    cell_type = span->data.b.cell_type;
1440
0
  }
1441
1442
0
  if (wp == NULL) {
1443
0
    redraw_get_default_border_style(dctx, &gc, &pane_lines);
1444
0
    if (span->data.type == REDRAW_SPAN_OUTSIDE)
1445
0
      window_get_fill_cell(w, 0, &gc);
1446
0
    else if (span->data.type == REDRAW_SPAN_EMPTY)
1447
0
      window_get_fill_cell(w, 1, &gc);
1448
0
    else {
1449
0
      if (span->data.type != REDRAW_SPAN_BORDER)
1450
0
        pane_lines = PANE_LINES_SINGLE;
1451
0
      window_get_border_cell(NULL, pane_lines, cell_type, &gc);
1452
0
    }
1453
0
  } else {
1454
0
    window_pane_get_border_style(wp, c, &gc);
1455
0
    window_pane_get_border_cell(wp, cell_type, &gc);
1456
0
  }
1457
1458
0
  if (span->data.type == REDRAW_SPAN_BORDER &&
1459
0
      dctx->marked != NULL &&
1460
0
      redraw_data_has_pane(&span->data, dctx->marked))
1461
0
    gc.attr ^= GRID_ATTR_REVERSE;
1462
0
  redraw_draw_border_arrow(dctx, span, &gc);
1463
1464
0
  if (cell_type == CELL_UD && (dctx->flags & REDRAW_ISOLATES))
1465
0
    isolates = 1;
1466
0
  tty_cursor(tty, x, y);
1467
0
  if (isolates)
1468
0
    tty_puts(tty, REDRAW_END_ISOLATE);
1469
0
  for (i = 0; i < n; i++)
1470
0
    tty_cell(tty, &gc, NULL);
1471
0
  if (isolates)
1472
0
    tty_puts(tty, REDRAW_START_ISOLATE);
1473
0
}
1474
1475
/* Draw a pane status span. */
1476
static void
1477
redraw_draw_status_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 client   *c = scene->c;
1482
0
  struct tty    *tty = &c->tty;
1483
0
  struct window_pane  *wp = span->data.st.wp;
1484
0
  struct screen   *s = &wp->status_screen;
1485
0
  u_int      px, sx = screen_size_x(s);
1486
1487
0
  px = span->data.st.offset + (x - dctx->side_left - span->x);
1488
0
  if (px < sx) {
1489
0
    if (n > sx - px)
1490
0
      n = sx - px;
1491
0
    tty_draw_line(tty, s, px, 0, n, x, y, NULL);
1492
0
  }
1493
0
}
1494
1495
/* Draw a scrollbar span. */
1496
static void
1497
redraw_draw_scrollbar_span(struct redraw_draw_ctx *dctx,
1498
    struct redraw_span *span, u_int x, u_int y, u_int n)
1499
0
{
1500
0
  struct redraw_scene *scene = dctx->scene;
1501
0
  struct window_pane  *wp = span->data.sb.wp;
1502
0
  struct screen   *s = wp->screen;
1503
0
  struct tty    *tty = &scene->c->tty;
1504
0
  struct style    *sb_style = &wp->scrollbar_style;
1505
0
  struct grid_cell   gc, slgc, pad_gc, *gcp;
1506
0
  double       pct_view;
1507
0
  u_int      total_height, slider_h, slider_y;
1508
0
  u_int      sb_h = span->data.sb.height;
1509
0
  u_int      sb_y = span->data.sb.y;
1510
0
  u_int      i, off, sb_w, sb_pad;
1511
0
  int      cm_y, cm_size;
1512
1513
0
  if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE) {
1514
0
    total_height = screen_size_y(s) + screen_hsize(s);
1515
0
    if (total_height == 0)
1516
0
      return;
1517
0
    pct_view = (double)sb_h / total_height;
1518
0
    slider_h = (double)sb_h * pct_view;
1519
0
    slider_y = sb_h - slider_h;
1520
0
  } else {
1521
0
    if (TAILQ_FIRST(&wp->modes) == NULL)
1522
0
      return;
1523
0
    if (window_copy_get_current_offset(wp, &cm_y, &cm_size) == 0)
1524
0
      return;
1525
0
    total_height = cm_size + sb_h;
1526
0
    if (total_height == 0)
1527
0
      return;
1528
0
    pct_view = (double)sb_h / total_height;
1529
0
    slider_h = (double)sb_h * pct_view;
1530
0
    slider_y = (sb_h + 1) * ((double)cm_y / total_height);
1531
0
  }
1532
1533
0
  if (slider_h < 1)
1534
0
    slider_h = 1;
1535
0
  if (slider_y >= sb_h)
1536
0
    slider_y = sb_h - 1;
1537
1538
0
  wp->sb_slider_y = slider_y;
1539
0
  wp->sb_slider_h = slider_h;
1540
1541
0
  gc = sb_style->gc;
1542
0
  memcpy(&slgc, &gc, sizeof slgc);
1543
0
  slgc.fg = gc.bg;
1544
0
  slgc.bg = gc.fg;
1545
0
  tty_default_colours(&pad_gc, wp, NULL);
1546
1547
0
  sb_w = sb_style->width;
1548
0
  sb_pad = sb_style->pad;
1549
0
  off = x - dctx->side_left - span->x;
1550
1551
0
  tty_cursor(tty, x, y);
1552
0
  for (i = 0; i < n; i++) {
1553
0
    if (span->data.sb.flags & REDRAW_SCROLLBAR_LEFT) {
1554
0
      if (off + i >= sb_w && off + i < sb_w + sb_pad) {
1555
0
        tty_cell(tty, &pad_gc, NULL);
1556
0
        continue;
1557
0
      }
1558
0
    } else {
1559
0
      if (off + i < sb_pad) {
1560
0
        tty_cell(tty, &pad_gc, NULL);
1561
0
        continue;
1562
0
      }
1563
0
    }
1564
1565
0
    if (sb_y >= slider_y && sb_y < slider_y + slider_h)
1566
0
      gcp = &slgc;
1567
0
    else
1568
0
      gcp = &gc;
1569
0
    tty_cell(tty, gcp, NULL);
1570
0
  }
1571
0
}
1572
1573
/* Draw a menu span. */
1574
static void
1575
redraw_draw_menu_span(struct redraw_draw_ctx *dctx,
1576
    struct redraw_span *span, u_int x, u_int y, u_int n)
1577
0
{
1578
0
  struct redraw_scene *scene = dctx->scene;
1579
0
  struct tty    *tty = &scene->c->tty;
1580
0
  struct screen   *s = menu_screen(span->data.m.md);
1581
0
  u_int      px;
1582
1583
#ifdef ENABLE_IMAGES
1584
  image_redraw_start(tty, x, y, n, 1);
1585
#endif
1586
0
  px = span->data.m.px + (x - dctx->side_left - span->x);
1587
0
  tty_draw_line(tty, s, px, span->data.m.py, n, x, y, NULL);
1588
0
}
1589
1590
/* Draw a span. */
1591
static void
1592
redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
1593
    u_int y, enum redraw_phase phase)
1594
0
{
1595
0
  struct redraw_span_data *data = &span->data;
1596
0
  enum redraw_span_type  type = data->type;
1597
0
  u_int      x = dctx->side_left + span->x;
1598
0
  u_int      width = span->width;
1599
1600
0
  if (type == REDRAW_SPAN_STATUS && ~data->st.wp->flags & PANE_NEWSTATUS)
1601
0
    return;
1602
0
  if (phase != REDRAW_TEXT && type != REDRAW_SPAN_PANE)
1603
0
    return;
1604
1605
0
  switch (span->data.type) {
1606
0
  case REDRAW_SPAN_PANE:
1607
0
    if (phase == REDRAW_TEXT)
1608
0
      redraw_draw_pane_span(dctx, span, x, y, width);
1609
#ifdef ENABLE_IMAGES
1610
    else {
1611
      image_draw_line(&dctx->scene->c->tty, data->p.wp->screen,
1612
          data->p.px, data->p.py, width, x, y);
1613
    }
1614
#endif
1615
0
    break;
1616
0
  case REDRAW_SPAN_BORDER:
1617
0
  case REDRAW_SPAN_EMPTY:
1618
0
  case REDRAW_SPAN_OUTSIDE:
1619
0
    redraw_draw_border_span(dctx, span, x, y, width);
1620
0
    break;
1621
0
  case REDRAW_SPAN_STATUS:
1622
0
    redraw_draw_status_span(dctx, span, x, y, width);
1623
0
    break;
1624
0
  case REDRAW_SPAN_SCROLLBAR:
1625
0
    redraw_draw_scrollbar_span(dctx, span, x, y, width);
1626
0
    break;
1627
0
  case REDRAW_SPAN_MENU:
1628
0
    redraw_draw_menu_span(dctx, span, x, y, width);
1629
0
    break;
1630
0
  }
1631
0
}
1632
1633
/* Draw pane lines. */
1634
static void
1635
redraw_draw_pane_lines(struct redraw_draw_ctx *dctx, struct window_pane *wp,
1636
    int flags)
1637
0
{
1638
0
  struct redraw_scene *scene = dctx->scene;
1639
0
  struct redraw_line  *line;
1640
0
  struct redraw_spans *spans;
1641
0
  struct redraw_span  *span;
1642
0
  enum redraw_phase  phase;
1643
0
  u_int      cy;
1644
0
  int      y, top, bottom;
1645
1646
0
  top = wp->yoff - (int)scene->oy;
1647
0
  if (top < 0)
1648
0
    top = 0;
1649
0
  bottom = wp->yoff + (int)wp->sy - (int)scene->oy;
1650
0
  if (bottom < 0)
1651
0
    bottom = 0;
1652
0
  if (bottom > (int)scene->sy)
1653
0
    bottom = scene->sy;
1654
1655
#ifdef ENABLE_IMAGES
1656
  /* Erase only pane-owned spans so overlapping panes remain intact. */
1657
  if (flags & REDRAW_PANE) {
1658
    for (y = top; y < bottom; y++) {
1659
      line = &scene->lines[y];
1660
      if (dctx->flags & REDRAW_STATUS_TOP)
1661
        cy = dctx->status_lines + y;
1662
      else
1663
        cy = y;
1664
      spans = &line->spans[REDRAW_SPAN_PANE];
1665
      TAILQ_FOREACH(span, spans, entry) {
1666
        if (span->data.p.wp == wp) {
1667
          image_redraw_start(&scene->c->tty,
1668
              dctx->side_left + span->x, cy,
1669
              span->width, 1);
1670
        }
1671
      }
1672
    }
1673
  }
1674
#endif
1675
1676
  /* Draw each phase. */
1677
0
  for (phase = 0; phase < REDRAW_PHASE_COUNT; phase++) {
1678
0
    for (y = top; y < bottom; y++) {
1679
0
      line = &scene->lines[y];
1680
0
      if (dctx->flags & REDRAW_STATUS_TOP)
1681
0
        cy = dctx->status_lines + y;
1682
0
      else
1683
0
        cy = y;
1684
0
      if (flags & REDRAW_PANE) {
1685
0
        spans = &line->spans[REDRAW_SPAN_PANE];
1686
0
        TAILQ_FOREACH(span, spans, entry) {
1687
0
          if (span->data.p.wp != wp)
1688
0
            continue;
1689
0
          redraw_draw_span(dctx, span, cy, phase);
1690
0
        }
1691
0
      }
1692
0
      if (phase == REDRAW_TEXT &&
1693
0
          (flags & REDRAW_PANE_SCROLLBAR)) {
1694
0
        spans = &line->spans[REDRAW_SPAN_SCROLLBAR];
1695
0
        TAILQ_FOREACH(span, spans, entry) {
1696
0
          if (span->data.sb.wp != wp)
1697
0
            continue;
1698
0
          redraw_draw_span(dctx, span, cy, phase);
1699
0
        }
1700
0
      }
1701
0
    }
1702
#ifdef ENABLE_IMAGES
1703
    image_draw_flush(&scene->c->tty);
1704
#endif
1705
0
  }
1706
0
}
1707
1708
/* Return whether to draw a span type in this phase for these flags. */
1709
static int
1710
redraw_should_draw(enum redraw_span_type type, int flags,
1711
    enum redraw_phase phase)
1712
0
{
1713
0
  if (phase != REDRAW_TEXT && type != REDRAW_SPAN_PANE)
1714
0
    return (0);
1715
0
  if (REDRAW_IS_ALL(flags))
1716
0
    return (1);
1717
1718
0
  switch (type) {
1719
0
  case REDRAW_SPAN_PANE:
1720
0
    return (flags & REDRAW_PANE);
1721
0
  case REDRAW_SPAN_OUTSIDE:
1722
0
    return (flags & REDRAW_OUTSIDE);
1723
0
  case REDRAW_SPAN_EMPTY:
1724
0
    return (flags & REDRAW_EMPTY);
1725
0
  case REDRAW_SPAN_BORDER:
1726
0
    return (flags & REDRAW_PANE_BORDER);
1727
0
  case REDRAW_SPAN_STATUS:
1728
0
    return (flags & REDRAW_PANE_STATUS);
1729
0
  case REDRAW_SPAN_SCROLLBAR:
1730
0
    return (flags & REDRAW_PANE_SCROLLBAR);
1731
0
  case REDRAW_SPAN_MENU:
1732
0
    return (flags & REDRAW_MENU);
1733
0
  default:
1734
0
    return (0);
1735
0
  }
1736
0
}
1737
1738
/* Draw lines. */
1739
static void
1740
redraw_draw_lines(struct redraw_draw_ctx *dctx, int flags)
1741
0
{
1742
0
  struct redraw_scene *scene = dctx->scene;
1743
0
  struct redraw_line  *line;
1744
0
  struct redraw_spans *spans;
1745
0
  struct redraw_span  *span;
1746
0
  enum redraw_phase  phase;
1747
0
  u_int      y, cy, type;
1748
1749
  /* Draw each phase. */
1750
0
  for (phase = 0; phase < REDRAW_PHASE_COUNT; phase++) {
1751
0
    for (y = 0; y < scene->sy; y++) {
1752
0
      line = &scene->lines[y];
1753
0
      if (dctx->flags & REDRAW_STATUS_TOP)
1754
0
        cy = dctx->status_lines + y;
1755
0
      else
1756
0
        cy = y;
1757
0
      for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
1758
0
        if (!redraw_should_draw(type, flags, phase))
1759
0
          continue;
1760
0
        spans = &line->spans[type];
1761
0
        TAILQ_FOREACH(span, spans, entry)
1762
0
          redraw_draw_span(dctx, span, cy, phase);
1763
0
      }
1764
0
    }
1765
#ifdef ENABLE_IMAGES
1766
    image_draw_flush(&scene->c->tty);
1767
#endif
1768
0
  }
1769
0
}
1770
1771
/* Draw menu spans. */
1772
static void
1773
redraw_draw_menu_lines(struct redraw_draw_ctx *dctx)
1774
0
{
1775
0
  struct redraw_scene *scene = dctx->scene;
1776
0
  struct redraw_line  *line;
1777
0
  struct redraw_span  *span;
1778
0
  u_int      y, cy;
1779
1780
0
  for (y = 0; y < scene->sy; y++) {
1781
0
    line = &scene->lines[y];
1782
0
    if (dctx->flags & REDRAW_STATUS_TOP)
1783
0
      cy = dctx->status_lines + y;
1784
0
    else
1785
0
      cy = y;
1786
0
    TAILQ_FOREACH(span, &line->spans[REDRAW_SPAN_MENU], entry)
1787
0
      redraw_draw_span(dctx, span, cy, REDRAW_TEXT);
1788
0
  }
1789
0
}
1790
1791
/* Get line for pane status line. */
1792
static int
1793
redraw_pane_status_line(struct redraw_draw_ctx *dctx,
1794
    struct window_pane *wp, u_int *line)
1795
0
{
1796
0
  struct redraw_scene *scene = dctx->scene;
1797
0
  int      pane_status, wy;
1798
1799
0
  pane_status = window_pane_get_pane_status(wp);
1800
0
  if (pane_status == PANE_STATUS_OFF)
1801
0
    return (0);
1802
1803
0
  if (pane_status == PANE_STATUS_TOP)
1804
0
    wy = (int)wp->yoff - 1;
1805
0
  else
1806
0
    wy = (int)wp->yoff + wp->sy;
1807
0
  if (wy < 0 || wy < (int)scene->oy)
1808
0
    return (0);
1809
0
  if ((u_int)wy >= scene->oy + scene->sy)
1810
0
    return (0);
1811
0
  *line = wy - scene->oy;
1812
0
  return (1);
1813
0
}
1814
1815
/* Get available width for pane status line. */
1816
static u_int
1817
redraw_pane_status_width(struct redraw_draw_ctx *dctx,
1818
    struct window_pane *wp, struct redraw_span **first)
1819
0
{
1820
0
  struct redraw_scene *scene = dctx->scene;
1821
0
  struct redraw_span  *span;
1822
0
  u_int      y, width = 0, end;
1823
1824
0
  if (!redraw_pane_status_line(dctx, wp, &y))
1825
0
    return (0);
1826
1827
0
  *first = NULL;
1828
0
  TAILQ_FOREACH(span, &scene->lines[y].spans[REDRAW_SPAN_STATUS], entry) {
1829
0
    if (span->data.st.wp == wp) {
1830
0
      if (*first == NULL)
1831
0
          *first = span;
1832
0
      end = span->data.st.offset + span->width;
1833
0
      if (end > width)
1834
0
        width = end;
1835
0
    }
1836
0
  }
1837
0
  return (width);
1838
0
}
1839
1840
/* Set up draw context. */
1841
static void
1842
redraw_set_draw_context(struct redraw_draw_ctx *dctx,
1843
    struct redraw_scene *scene)
1844
0
{
1845
0
  struct client *c = scene->c;
1846
0
  struct session  *s = c->session;
1847
0
  struct options  *oo = s->options;
1848
0
  struct tty  *tty = &c->tty;
1849
0
  u_int    lines;
1850
1851
0
  memset(dctx, 0, sizeof *dctx);
1852
0
  dctx->scene = scene;
1853
1854
0
  if (server_is_marked(s, s->curw, marked_pane.wp))
1855
0
    dctx->marked = marked_pane.wp;
1856
0
  dctx->active = s->curw->window->active;
1857
1858
0
  lines = status_line_size(c);
1859
0
  if (options_get_number(oo, "status-position") == 0)
1860
0
    dctx->flags |= REDRAW_STATUS_TOP;
1861
0
  dctx->status_lines = lines;
1862
1863
0
  if (status_side_at_column(c) == 0)
1864
0
    dctx->side_left = status_side_size(c);
1865
1866
0
  if ((c->flags & CLIENT_UTF8) && tty_term_has(tty->term, TTYC_BIDI))
1867
0
    dctx->flags |= REDRAW_ISOLATES;
1868
0
}
1869
1870
/* Build a pane prompt. */
1871
static void
1872
redraw_make_pane_prompt(struct window_pane *wp, struct screen *screen)
1873
0
{
1874
0
  struct screen_write_ctx  ctx;
1875
0
  struct prompt_draw_data  pdd;
1876
1877
0
  screen_init(screen, wp->sx, 1, 0);
1878
0
  screen_write_start(&ctx, screen);
1879
0
  pdd.ctx = &ctx;
1880
0
  pdd.cursor_x = &wp->prompt_cx;
1881
0
  pdd.area_x = 0;
1882
0
  pdd.area_width = wp->sx;
1883
0
  pdd.prompt_line = 0;
1884
0
  prompt_draw(wp->prompt, &pdd);
1885
0
  screen_write_stop(&ctx);
1886
0
}
1887
1888
/* Draw a pane's prompt over its content. */
1889
static void
1890
redraw_draw_pane_prompt(struct redraw_draw_ctx *dctx, struct window_pane *wp)
1891
0
{
1892
0
  struct redraw_scene *scene = dctx->scene;
1893
0
  struct client   *c = scene->c;
1894
0
  struct tty    *tty = &c->tty;
1895
0
  struct screen    screen;
1896
0
  int      ox = scene->ox, oy = scene->oy;
1897
0
  int      sx = scene->sx, sy = scene->sy;
1898
0
  int      line, cy, px, offset, width, wy;
1899
0
  u_int      side_left = dctx->side_left;
1900
1901
0
  if (wp->prompt == NULL || wp->sx == 0 || wp->sy == 0)
1902
0
    return;
1903
1904
0
  if (~dctx->flags & REDRAW_STATUS_TOP)
1905
0
    wy = wp->yoff + (int)wp->sy - 1;
1906
0
  else
1907
0
    wy = wp->yoff;
1908
0
  if (wy < oy || wy >= oy + sy)
1909
0
    return;
1910
0
  line = wy - oy;
1911
0
  if (dctx->flags & REDRAW_STATUS_TOP)
1912
0
    cy = dctx->status_lines + line;
1913
0
  else
1914
0
    cy = line;
1915
1916
0
  if (wp->xoff + (int)wp->sx <= ox || wp->xoff >= ox + sx)
1917
0
    return;
1918
0
  if (wp->xoff < ox) {
1919
0
    offset = ox - wp->xoff;
1920
0
    px = 0;
1921
0
  } else {
1922
0
    offset = 0;
1923
0
    px = wp->xoff - ox;
1924
0
  }
1925
0
  width = wp->sx - offset;
1926
0
  if (px + width > sx)
1927
0
    width = sx - px;
1928
1929
#ifdef ENABLE_IMAGES
1930
  image_redraw_start(tty, side_left + px, cy, width, 1);
1931
#endif
1932
0
  redraw_make_pane_prompt(wp, &screen);
1933
0
  tty_draw_line(tty, &screen, offset, 0, width, side_left + px, cy, NULL);
1934
0
  screen_free(&screen);
1935
0
}
1936
1937
/* Draw scene to client. */
1938
static void
1939
redraw_draw(struct client *c, struct window_pane *wp, int flags)
1940
0
{
1941
0
  struct redraw_draw_ctx   dctx;
1942
0
  struct session    *s = c->session;
1943
0
  struct window   *w = s->curw->window;
1944
0
  struct tty    *tty = &c->tty;
1945
0
  struct screen   *sl;
1946
0
  struct redraw_scene *scene;
1947
0
  struct window_pane  *loop;
1948
0
  u_int      width, i, y, lines, side_width;
1949
0
  int      side_x;
1950
0
  struct redraw_span  *first;
1951
0
  int      redraw;
1952
1953
0
  if (c->flags & CLIENT_SUSPENDED)
1954
0
    return;
1955
1956
0
  if (flags & REDRAW_STATUS) {
1957
0
    if (c->message_string != NULL)
1958
0
      redraw = status_message_redraw(c);
1959
0
    else if (c->prompt != NULL)
1960
0
      redraw = status_prompt_redraw(c);
1961
0
    else
1962
0
      redraw = status_redraw(c);
1963
0
    if (!redraw &&
1964
0
        (~c->flags & CLIENT_REDRAWSTATUSALWAYS) &&
1965
0
        !REDRAW_IS_ALL(flags)) {
1966
0
      flags &= ~REDRAW_STATUS;
1967
0
      if (flags == 0)
1968
0
        return;
1969
0
    }
1970
0
  }
1971
1972
0
  if (flags & REDRAW_SIDE_STATUS) {
1973
0
    if (!status_side_redraw(c) &&
1974
0
        (~c->flags & CLIENT_REDRAWSTATUSALWAYS) &&
1975
0
        !REDRAW_IS_ALL(flags)) {
1976
0
      flags &= ~REDRAW_SIDE_STATUS;
1977
0
      if (flags == 0)
1978
0
        return;
1979
0
    }
1980
0
  }
1981
1982
0
  if (log_get_level() != 0) {
1983
0
    log_debug("%s: starting @%u redraw (%s)", c->name, w->id,
1984
0
        redraw_flags_to_string(flags));
1985
0
  }
1986
1987
0
  scene = redraw_get_scene(c);
1988
0
  if (scene == NULL)
1989
0
    return;
1990
0
  redraw_set_draw_context(&dctx, scene);
1991
0
  if (w->menu != NULL)
1992
0
    menu_update(w->menu);
1993
1994
0
  if (flags & (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS)) {
1995
0
    TAILQ_FOREACH(loop, &scene->w->panes, entry) {
1996
0
      loop->border_gc_set = 0;
1997
0
      loop->active_border_gc_set = 0;
1998
0
    }
1999
0
  }
2000
2001
0
  if (flags & REDRAW_PANE_STATUS) {
2002
0
    redraw = 0;
2003
0
    TAILQ_FOREACH(loop, &scene->w->panes, entry) {
2004
0
      if (REDRAW_IS_ALL(flags))
2005
0
        loop->flags |= PANE_NEWSTATUS;
2006
0
      else
2007
0
        loop->flags &= ~PANE_NEWSTATUS;
2008
2009
0
      width = redraw_pane_status_width(&dctx, loop, &first);
2010
0
      if (width == 0)
2011
0
        continue;
2012
2013
0
      if (window_make_pane_status(loop, c, width, first)) {
2014
0
        loop->flags |= PANE_NEWSTATUS;
2015
0
        redraw = 1;
2016
0
      }
2017
0
    }
2018
0
    if (!redraw && !REDRAW_IS_ALL(flags)) {
2019
0
      flags &= ~REDRAW_PANE_STATUS;
2020
0
      if (flags == 0)
2021
0
        return;
2022
0
    }
2023
0
  }
2024
2025
0
  if (flags & REDRAW_PANE) {
2026
0
    if (wp != NULL) {
2027
0
      if (wp->base.mode & MODE_SYNC)
2028
0
        screen_write_stop_sync(wp);
2029
0
      screen_write_sync_clear_dirty(wp);
2030
0
    } else {
2031
0
      TAILQ_FOREACH(loop, &scene->w->panes, entry) {
2032
0
        if (!window_pane_is_visible(loop))
2033
0
          continue;
2034
0
        if (loop->base.mode & MODE_SYNC)
2035
0
          screen_write_stop_sync(loop);
2036
0
        screen_write_sync_clear_dirty(loop);
2037
0
      }
2038
0
    }
2039
0
  }
2040
0
  tty_sync_start(tty); /* end in server_client_reset_state */
2041
#ifdef ENABLE_IMAGES
2042
  if (wp == NULL && (flags & REDRAW_PANE)) {
2043
    y = 0;
2044
    if (dctx.flags & REDRAW_STATUS_TOP)
2045
      y = dctx.status_lines;
2046
    image_redraw_start(tty, dctx.side_left, y, scene->sx,
2047
        scene->sy);
2048
  }
2049
#endif
2050
0
  tty_update_mode(tty, tty->mode & ~CURSOR_MODES, NULL);
2051
2052
0
  if (wp != NULL)
2053
0
    redraw_draw_pane_lines(&dctx, wp, flags);
2054
0
  else
2055
0
    redraw_draw_lines(&dctx, flags);
2056
2057
0
  if (flags & REDRAW_PANE) {
2058
0
    if (wp != NULL)
2059
0
      redraw_draw_pane_prompt(&dctx, wp);
2060
0
    else {
2061
0
      TAILQ_FOREACH(loop, &scene->w->panes, entry) {
2062
0
        if (window_pane_is_visible(loop))
2063
0
          redraw_draw_pane_prompt(&dctx, loop);
2064
0
      }
2065
0
    }
2066
0
  }
2067
0
  if (w->menu != NULL && (flags & REDRAW_MENU))
2068
0
    redraw_draw_menu_lines(&dctx);
2069
2070
0
  if (flags & REDRAW_SIDE_STATUS) {
2071
0
    side_width = status_side_size(c);
2072
0
    side_x = status_side_at_column(c);
2073
0
    if (side_width != 0 && side_x != -1) {
2074
0
      if (dctx.flags & REDRAW_STATUS_TOP)
2075
0
        y = dctx.status_lines;
2076
0
      else
2077
0
        y = 0;
2078
0
      for (i = 0; i < status_side_rows(c); i++) {
2079
0
        tty_draw_line(tty, &c->side_status.screen, 0, i,
2080
0
            side_width, side_x, y + i, NULL);
2081
0
      }
2082
0
    }
2083
0
  }
2084
2085
0
  if (flags & REDRAW_STATUS) {
2086
0
    lines = dctx.status_lines;
2087
0
    if (c->message_string != NULL || c->prompt != NULL)
2088
0
      lines = (lines == 0 ? 1 : lines);
2089
0
    if (dctx.flags & REDRAW_STATUS_TOP)
2090
0
      y = 0;
2091
0
    else
2092
0
      y = c->tty.sy - lines;
2093
0
    sl = c->status.active;
2094
0
    for (i = 0; i < lines; i++)
2095
0
      tty_draw_line(tty, sl, 0, i, tty->sx, 0, y + i, NULL);
2096
0
  }
2097
2098
#ifdef ENABLE_IMAGES
2099
  image_redraw_finish(tty);
2100
#endif
2101
2102
0
  tty_reset(tty);
2103
2104
0
  log_debug("%s: finished @%u redraw", c->name, scene->w->id);
2105
0
}
2106
2107
/* Get border cell type beneath status cell at offset x in pane status line. */
2108
int
2109
redraw_get_status_border_cell_type(struct redraw_span **spanp, u_int x)
2110
0
{
2111
0
  struct redraw_span  *span = *spanp;
2112
0
  struct window_pane  *wp;
2113
0
  u_int      start, end;
2114
2115
0
  if (span == NULL || span->data.type != REDRAW_SPAN_STATUS)
2116
0
    return (CELL_LR);
2117
0
  wp = span->data.st.wp;
2118
0
  for (; span != NULL; span = TAILQ_NEXT(span, entry)) {
2119
0
    if (span->data.type != REDRAW_SPAN_STATUS)
2120
0
      continue;
2121
0
    if (span->data.st.wp != wp)
2122
0
      continue;
2123
2124
0
    start = span->data.st.offset;
2125
0
    end = start + span->width;
2126
0
    if (x >= start && x < end) {
2127
0
      *spanp = span;
2128
0
      return (span->data.st.cell_type);
2129
0
    }
2130
2131
0
    if (start > x) {
2132
0
      *spanp = span;
2133
0
      break;
2134
0
    }
2135
0
  }
2136
0
  if (span == NULL)
2137
0
    *spanp = NULL;
2138
0
  return (CELL_LR);
2139
0
}
2140
2141
/* Draw screen. */
2142
void
2143
redraw_screen(struct client *c)
2144
0
{
2145
0
  int flags = 0;
2146
2147
0
  if (c->flags & CLIENT_REDRAWWINDOW)
2148
0
    redraw_draw(c, NULL, REDRAW_ALL);
2149
0
  else {
2150
0
    if (c->flags & CLIENT_REDRAWBORDERS)
2151
0
      flags |= (REDRAW_PANE_BORDER|REDRAW_PANE_STATUS);
2152
0
    if (c->flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))
2153
0
      flags |= (REDRAW_STATUS|REDRAW_PANE_STATUS|
2154
0
          REDRAW_SIDE_STATUS);
2155
0
    if (c->flags & CLIENT_REDRAWMENU)
2156
0
      flags |= REDRAW_MENU;
2157
0
    if (c->session->curw->window->menu != NULL)
2158
0
      flags |= REDRAW_MENU;
2159
0
    if (flags != 0)
2160
0
      redraw_draw(c, NULL, flags);
2161
0
  }
2162
0
}
2163
2164
/* Draw a single pane. */
2165
void
2166
redraw_pane(struct client *c, struct window_pane *wp)
2167
0
{
2168
0
  redraw_draw(c, wp, REDRAW_PANE|REDRAW_PANE_SCROLLBAR);
2169
0
  if (c->session->curw->window->menu != NULL)
2170
0
    redraw_draw(c, NULL, REDRAW_MENU);
2171
0
}
2172
2173
/* Draw a pane's scrollbar. */
2174
void
2175
redraw_pane_scrollbar(struct client *c, struct window_pane *wp)
2176
0
{
2177
0
  redraw_draw(c, wp, REDRAW_PANE_SCROLLBAR);
2178
0
}
2179
2180
/* Rebuild damaged pane status. */
2181
static void
2182
redraw_damage_refresh_status(struct redraw_draw_ctx *dctx,
2183
    struct window_pane *wp)
2184
0
{
2185
0
  struct redraw_span  *first;
2186
0
  u_int      g = wp->status_generation, width;
2187
2188
0
  if ((wp->flags & PANE_NEWSTATUS) && g == redraw_status_generation)
2189
0
    return;
2190
0
  width = redraw_pane_status_width(dctx, wp, &first);
2191
0
  if (width != 0) {
2192
0
    window_make_pane_status(wp, dctx->scene->c, width, first);
2193
0
    wp->flags |= PANE_NEWSTATUS;
2194
0
    wp->status_generation = redraw_status_generation;
2195
0
  }
2196
0
}
2197
2198
/* Draw a pane's prompt over a damaged span. */
2199
static void
2200
redraw_damage_draw_pane_prompt(struct redraw_draw_ctx *dctx,
2201
    struct redraw_span *span, u_int y)
2202
0
{
2203
0
  struct redraw_scene *scene = dctx->scene;
2204
0
  struct window_pane  *wp = span->data.p.wp;
2205
0
  struct tty    *tty = &scene->c->tty;
2206
0
  struct screen    screen;
2207
0
  u_int      px = span->data.p.px, width, prompt_y;
2208
0
  u_int      x = dctx->side_left + span->x;
2209
2210
0
  if (wp->prompt == NULL || wp->sx == 0 || wp->sy == 0)
2211
0
    return;
2212
0
  if (dctx->flags & REDRAW_STATUS_TOP)
2213
0
    prompt_y = 0;
2214
0
  else
2215
0
    prompt_y = wp->sy - 1;
2216
0
  if (span->data.p.py != prompt_y)
2217
0
    return;
2218
0
  redraw_make_pane_prompt(wp, &screen);
2219
0
  if (px < screen_size_x(&screen)) {
2220
0
    width = span->width;
2221
0
    if (width > screen_size_x(&screen) - px)
2222
0
      width = screen_size_x(&screen) - px;
2223
#ifdef ENABLE_IMAGES
2224
    image_redraw_start(tty, x, y, width, 1);
2225
#endif
2226
0
    tty_draw_line(tty, &screen, px, 0, width, x, y, NULL);
2227
0
  }
2228
0
  screen_free(&screen);
2229
0
}
2230
2231
/* Draw the spans intersecting a damaged rectangle. */
2232
static void
2233
redraw_draw_damage_rectangle(struct redraw_draw_ctx *dctx, u_int x, u_int y,
2234
    u_int sx, u_int sy)
2235
0
{
2236
0
  struct redraw_scene *scene = dctx->scene;
2237
0
  struct redraw_line  *line;
2238
0
  struct redraw_spans *spans;
2239
0
  struct redraw_span  *span;
2240
0
  enum redraw_phase  phase;
2241
0
  u_int      cy, yy, type;
2242
2243
0
  if (x >= scene->sx || y >= scene->sy)
2244
0
    return;
2245
0
  if (x + sx > scene->sx)
2246
0
    sx = scene->sx - x;
2247
0
  if (y + sy > scene->sy)
2248
0
    sy = scene->sy - y;
2249
0
  if (sx == 0 || sy == 0)
2250
0
    return;
2251
2252
#ifdef ENABLE_IMAGES
2253
  /* Clear old image pixels on all damaged spans. */
2254
  for (yy = y; yy < y + sy; yy++) {
2255
    line = &scene->lines[yy];
2256
    if (dctx->flags & REDRAW_STATUS_TOP)
2257
      cy = dctx->status_lines + yy;
2258
    else
2259
      cy = yy;
2260
    for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
2261
      if (type == REDRAW_SPAN_STATUS)
2262
        continue;
2263
      spans = &line->spans[type];
2264
      TAILQ_FOREACH(span, spans, entry) {
2265
        if (span->x >= x + sx)
2266
          continue;
2267
        if (span->x + span->width <= x)
2268
          continue;
2269
        image_redraw_start(&scene->c->tty,
2270
            dctx->side_left + span->x, cy,
2271
            span->width, 1);
2272
      }
2273
    }
2274
  }
2275
#endif
2276
2277
  /* Draw each phase in the damaged spans. */
2278
0
  for (phase = 0; phase < REDRAW_PHASE_COUNT; phase++) {
2279
0
    for (yy = y; yy < y + sy; yy++) {
2280
0
      line = &scene->lines[yy];
2281
0
      if (dctx->flags & REDRAW_STATUS_TOP)
2282
0
        cy = dctx->status_lines + yy;
2283
0
      else
2284
0
        cy = yy;
2285
0
      for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
2286
0
        if (phase != REDRAW_TEXT &&
2287
0
            type != REDRAW_SPAN_PANE)
2288
0
          continue;
2289
0
        if (type == REDRAW_SPAN_STATUS)
2290
0
          continue;
2291
0
        spans = &line->spans[type];
2292
0
        TAILQ_FOREACH(span, spans, entry) {
2293
0
          if (span->x >= x + sx)
2294
0
            continue;
2295
0
          if (span->x + span->width <= x)
2296
0
            continue;
2297
0
          redraw_draw_span(dctx, span, cy, phase);
2298
0
        }
2299
0
      }
2300
0
    }
2301
#ifdef ENABLE_IMAGES
2302
    image_draw_flush(&scene->c->tty);
2303
#endif
2304
0
  }
2305
2306
  /* Draw prompts and pane status lines last. */
2307
0
  for (yy = y; yy < y + sy; yy++) {
2308
0
    line = &scene->lines[yy];
2309
0
    if (dctx->flags & REDRAW_STATUS_TOP)
2310
0
      cy = dctx->status_lines + yy;
2311
0
    else
2312
0
      cy = yy;
2313
0
    spans = &line->spans[REDRAW_SPAN_PANE];
2314
0
    TAILQ_FOREACH(span, spans, entry) {
2315
0
      if (span->x >= x + sx)
2316
0
        continue;
2317
0
      if (span->x + span->width <= x)
2318
0
        continue;
2319
0
      redraw_damage_draw_pane_prompt(dctx, span, cy);
2320
0
    }
2321
0
    spans = &line->spans[REDRAW_SPAN_STATUS];
2322
0
    TAILQ_FOREACH(span, spans, entry) {
2323
0
      if (span->x >= x + sx)
2324
0
        continue;
2325
0
      if (span->x + span->width <= x)
2326
0
        continue;
2327
0
      redraw_damage_refresh_status(dctx, span->data.st.wp);
2328
0
      redraw_draw_span(dctx, span, cy, REDRAW_TEXT);
2329
0
    }
2330
0
  }
2331
#ifdef ENABLE_IMAGES
2332
  image_redraw_finish(&scene->c->tty);
2333
#endif
2334
0
}
2335
2336
/* Draw pending window damage on this client. */
2337
void
2338
redraw_client_damage(struct client *c)
2339
0
{
2340
0
  struct window   *w = c->session->curw->window;
2341
0
  struct tty    *tty = &c->tty;
2342
0
  struct window_pane  *wp;
2343
0
  struct redraw_scene *scene;
2344
0
  struct redraw_draw_ctx   dctx;
2345
0
  struct redraw_damage  *rd;
2346
0
  u_int      ox, oy, sx, sy, x0, y0, x1, y1;
2347
2348
0
  if (TAILQ_EMPTY(&w->damage))
2349
0
    return;
2350
0
  redraw_status_generation++;
2351
2352
0
  scene = redraw_get_scene(c);
2353
0
  if (scene == NULL)
2354
0
    return;
2355
0
  redraw_set_draw_context(&dctx, scene);
2356
0
  redraw_get_window_offset(c, &ox, &oy, &sx, &sy);
2357
2358
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
2359
0
    wp->border_gc_set = 0;
2360
0
    wp->active_border_gc_set = 0;
2361
0
  }
2362
2363
0
  tty_sync_start(tty);
2364
0
  tty_update_mode(tty, tty->mode & ~CURSOR_MODES, NULL);
2365
2366
0
  TAILQ_FOREACH(rd, &w->damage, entry) {
2367
#ifdef ENABLE_IMAGES
2368
    if ((rd->flags & REDRAW_DAMAGE_SCROLL) &&
2369
        (image_backend_get_flags(tty) & IMAGE_BACKEND_SCROLLS) &&
2370
        tty->scroll_window == w &&
2371
        tty->scroll_generation == w->scroll_generation &&
2372
        !tty->scroll_failed)
2373
      continue;
2374
#endif
2375
0
    x0 = (rd->x > ox) ? rd->x : ox;
2376
0
    y0 = (rd->y > oy) ? rd->y : oy;
2377
0
    x1 = (rd->x + rd->sx < ox + sx) ? rd->x + rd->sx : ox + sx;
2378
0
    y1 = (rd->y + rd->sy < oy + sy) ? rd->y + rd->sy : oy + sy;
2379
0
    if (x0 < x1 && y0 < y1) {
2380
0
      redraw_draw_damage_rectangle(&dctx, x0 - ox, y0 - oy,
2381
0
          x1 - x0, y1 - y0);
2382
0
    }
2383
0
  }
2384
0
}