Coverage Report

Created: 2026-07-16 06:53

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