Coverage Report

Created: 2026-04-27 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/screen-redraw.c
Line
Count
Source
1
/* $OpenBSD$ */
2
3
/*
4
 * Copyright (c) 2007 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 <stdlib.h>
22
#include <string.h>
23
24
#include "tmux.h"
25
26
static void screen_redraw_draw_borders(struct screen_redraw_ctx *);
27
static void screen_redraw_draw_panes(struct screen_redraw_ctx *);
28
static void screen_redraw_draw_status(struct screen_redraw_ctx *);
29
static void screen_redraw_draw_pane(struct screen_redraw_ctx *,
30
        struct window_pane *);
31
static void screen_redraw_set_context(struct client *,
32
        struct screen_redraw_ctx *);
33
static void screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx *);
34
static void screen_redraw_draw_scrollbar(struct screen_redraw_ctx *,
35
        struct window_pane *, int, int, int, u_int, u_int, u_int);
36
static void screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *,
37
        struct window_pane *);
38
39
0
#define START_ISOLATE "\342\201\246"
40
0
#define END_ISOLATE   "\342\201\251"
41
42
/* Border in relation to a pane. */
43
enum screen_redraw_border_type {
44
  SCREEN_REDRAW_OUTSIDE,
45
  SCREEN_REDRAW_INSIDE,
46
  SCREEN_REDRAW_BORDER_LEFT,
47
  SCREEN_REDRAW_BORDER_RIGHT,
48
  SCREEN_REDRAW_BORDER_TOP,
49
  SCREEN_REDRAW_BORDER_BOTTOM
50
};
51
0
#define BORDER_MARKERS "  +,.-"
52
53
/* Get cell border character. */
54
static void
55
screen_redraw_border_set(struct window *w, struct window_pane *wp,
56
    enum pane_lines pane_lines, int cell_type, struct grid_cell *gc)
57
0
{
58
0
  u_int idx;
59
60
0
  if (cell_type == CELL_OUTSIDE && w->fill_character != NULL) {
61
0
    utf8_copy(&gc->data, &w->fill_character[0]);
62
0
    return;
63
0
  }
64
65
0
  switch (pane_lines) {
66
0
  case PANE_LINES_NUMBER:
67
0
    if (cell_type == CELL_OUTSIDE) {
68
0
      gc->attr |= GRID_ATTR_CHARSET;
69
0
      utf8_set(&gc->data, CELL_BORDERS[CELL_OUTSIDE]);
70
0
      break;
71
0
    }
72
0
    gc->attr &= ~GRID_ATTR_CHARSET;
73
0
    if (wp != NULL && window_pane_index(wp, &idx) == 0)
74
0
      utf8_set(&gc->data, '0' + (idx % 10));
75
0
    else
76
0
      utf8_set(&gc->data, '*');
77
0
    break;
78
0
  case PANE_LINES_DOUBLE:
79
0
    gc->attr &= ~GRID_ATTR_CHARSET;
80
0
    utf8_copy(&gc->data, tty_acs_double_borders(cell_type));
81
0
    break;
82
0
  case PANE_LINES_HEAVY:
83
0
    gc->attr &= ~GRID_ATTR_CHARSET;
84
0
    utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
85
0
    break;
86
0
  case PANE_LINES_SIMPLE:
87
0
    gc->attr &= ~GRID_ATTR_CHARSET;
88
0
    utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
89
0
    break;
90
0
  case PANE_LINES_SPACES:
91
0
    gc->attr &= ~GRID_ATTR_CHARSET;
92
0
    utf8_set(&gc->data, ' ');
93
0
    break;
94
0
  default:
95
0
    gc->attr |= GRID_ATTR_CHARSET;
96
0
    utf8_set(&gc->data, CELL_BORDERS[cell_type]);
97
0
    break;
98
0
  }
99
0
}
100
101
/* Return if window has only two panes. */
102
static int
103
screen_redraw_two_panes(struct window *w, int direction)
104
0
{
105
0
  struct window_pane  *wp;
106
107
0
  wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
108
0
  if (wp == NULL)
109
0
    return (0); /* one pane */
110
0
  if (TAILQ_NEXT(wp, entry) != NULL)
111
0
    return (0); /* more than two panes */
112
0
  if (direction == 0 && wp->xoff == 0)
113
0
    return (0);
114
0
  if (direction == 1 && wp->yoff == 0)
115
0
    return (0);
116
0
  return (1);
117
0
}
118
119
/* Check if cell is on the border of a pane. */
120
static enum screen_redraw_border_type
121
screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp,
122
    u_int px, u_int py)
123
0
{
124
0
  struct options  *oo = wp->window->options;
125
0
  u_int    ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy;
126
0
  int    hsplit = 0, vsplit = 0, pane_status = ctx->pane_status;
127
0
  int    pane_scrollbars = ctx->pane_scrollbars, sb_w = 0;
128
0
  int    sb_pos;
129
130
0
  if (pane_scrollbars != 0)
131
0
    sb_pos = ctx->pane_scrollbars_pos;
132
0
  else
133
0
    sb_pos = 0;
134
135
  /* Inside pane. */
136
0
  if (px >= wp->xoff && px < ex && py >= wp->yoff && py < ey)
137
0
    return (SCREEN_REDRAW_INSIDE);
138
139
  /* Get pane indicator. */
140
0
  switch (options_get_number(oo, "pane-border-indicators")) {
141
0
  case PANE_BORDER_COLOUR:
142
0
  case PANE_BORDER_BOTH:
143
0
    hsplit = screen_redraw_two_panes(wp->window, 0);
144
0
    vsplit = screen_redraw_two_panes(wp->window, 1);
145
0
    break;
146
0
  }
147
148
  /* Are scrollbars enabled? */
149
0
  if (window_pane_show_scrollbar(wp, pane_scrollbars))
150
0
    sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
151
152
  /*
153
   * Left/right borders. The wp->sy / 2 test is to colour only half the
154
   * active window's border when there are two panes.
155
   */
156
0
  if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
157
0
    if (sb_pos == PANE_SCROLLBARS_LEFT) {
158
0
      if (wp->xoff - sb_w == 0 && px == wp->sx + sb_w)
159
0
        if (!hsplit || (hsplit && py <= wp->sy / 2))
160
0
          return (SCREEN_REDRAW_BORDER_RIGHT);
161
0
      if (wp->xoff - sb_w != 0) {
162
0
        if (px == wp->xoff - sb_w - 1 &&
163
0
            (!hsplit || (hsplit && py > wp->sy / 2)))
164
0
          return (SCREEN_REDRAW_BORDER_LEFT);
165
0
        if (px == wp->xoff + wp->sx + sb_w - 1)
166
0
          return (SCREEN_REDRAW_BORDER_RIGHT);
167
0
      }
168
0
    } else { /* sb_pos == PANE_SCROLLBARS_RIGHT or disabled */
169
0
      if (wp->xoff == 0 && px == wp->sx + sb_w)
170
0
        if (!hsplit || (hsplit && py <= wp->sy / 2))
171
0
          return (SCREEN_REDRAW_BORDER_RIGHT);
172
0
      if (wp->xoff != 0) {
173
0
        if (px == wp->xoff - 1 &&
174
0
            (!hsplit || (hsplit && py > wp->sy / 2)))
175
0
          return (SCREEN_REDRAW_BORDER_LEFT);
176
0
        if (px == wp->xoff + wp->sx + sb_w)
177
0
          return (SCREEN_REDRAW_BORDER_RIGHT);
178
0
      }
179
0
    }
180
0
  }
181
182
  /* Top/bottom borders. */
183
0
  if (vsplit && pane_status == PANE_STATUS_OFF && sb_w == 0) {
184
0
    if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2)
185
0
      return (SCREEN_REDRAW_BORDER_BOTTOM);
186
0
    if (wp->yoff != 0 && py == wp->yoff - 1 && px > wp->sx / 2)
187
0
      return (SCREEN_REDRAW_BORDER_TOP);
188
0
  } else {
189
0
    if (sb_pos == PANE_SCROLLBARS_LEFT) {
190
0
      if ((wp->xoff - sb_w == 0 || px >= wp->xoff - sb_w) &&
191
0
          (px <= ex || (sb_w != 0 && px < ex + sb_w))) {
192
0
        if (wp->yoff != 0 && py == wp->yoff - 1)
193
0
          return (SCREEN_REDRAW_BORDER_TOP);
194
0
        if (py == ey)
195
0
          return (SCREEN_REDRAW_BORDER_BOTTOM);
196
0
      }
197
0
    } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
198
0
      if ((wp->xoff == 0 || px >= wp->xoff) &&
199
0
          (px <= ex || (sb_w != 0 && px < ex + sb_w))) {
200
0
        if (pane_status != PANE_STATUS_BOTTOM &&
201
0
            wp->yoff != 0 &&
202
0
            py == wp->yoff - 1)
203
0
          return (SCREEN_REDRAW_BORDER_TOP);
204
0
        if (pane_status != PANE_STATUS_TOP && py == ey)
205
0
          return (SCREEN_REDRAW_BORDER_BOTTOM);
206
0
      }
207
0
    }
208
0
  }
209
210
  /* Outside pane. */
211
0
  return (SCREEN_REDRAW_OUTSIDE);
212
0
}
213
214
/* Check if a cell is on a border. */
215
static int
216
screen_redraw_cell_border(struct screen_redraw_ctx *ctx, u_int px, u_int py)
217
0
{
218
0
  struct client   *c = ctx->c;
219
0
  struct window   *w = c->session->curw->window;
220
0
  struct window_pane  *wp;
221
0
  u_int      sy = w->sy;
222
223
0
  if (ctx->pane_status == PANE_STATUS_BOTTOM)
224
0
    sy--;
225
226
  /* Outside the window? */
227
0
  if (px > w->sx || py > sy)
228
0
    return (0);
229
230
  /* On the window border? */
231
0
  if (px == w->sx || py == sy)
232
0
    return (1);
233
234
  /* Check all the panes. */
235
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
236
0
    if (!window_pane_visible(wp))
237
0
      continue;
238
0
    switch (screen_redraw_pane_border(ctx, wp, px, py)) {
239
0
    case SCREEN_REDRAW_INSIDE:
240
0
      return (0);
241
0
    case SCREEN_REDRAW_OUTSIDE:
242
0
      break;
243
0
    default:
244
0
      return (1);
245
0
    }
246
0
  }
247
248
0
  return (0);
249
0
}
250
251
/* Work out type of border cell from surrounding cells. */
252
static int
253
screen_redraw_type_of_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py)
254
0
{
255
0
  struct client *c = ctx->c;
256
0
  int    pane_status = ctx->pane_status;
257
0
  struct window *w = c->session->curw->window;
258
0
  u_int    sx = w->sx, sy = w->sy;
259
0
  int    borders = 0;
260
261
0
  if (pane_status == PANE_STATUS_BOTTOM)
262
0
    sy--;
263
264
  /* Is this outside the window? */
265
0
  if (px > sx || py > sy)
266
0
    return (CELL_OUTSIDE);
267
268
  /*
269
   * Construct a bitmask of whether the cells to the left (bit 8), right,
270
   * top, and bottom (bit 1) of this cell are borders.
271
   *
272
   * bits 8 4 2 1:     2
273
   *       8 + 4
274
   *         1
275
   */
276
0
  if (px == 0 || screen_redraw_cell_border(ctx, px - 1, py))
277
0
    borders |= 8;
278
0
  if (px <= sx && screen_redraw_cell_border(ctx, px + 1, py))
279
0
    borders |= 4;
280
0
  if (pane_status == PANE_STATUS_TOP) {
281
0
    if (py != 0 &&
282
0
        screen_redraw_cell_border(ctx, px, py - 1))
283
0
      borders |= 2;
284
0
    if (screen_redraw_cell_border(ctx, px, py + 1))
285
0
      borders |= 1;
286
0
  } else if (pane_status == PANE_STATUS_BOTTOM) {
287
0
    if (py == 0 ||
288
0
        screen_redraw_cell_border(ctx, px, py - 1))
289
0
      borders |= 2;
290
0
    if (py != sy &&
291
0
        screen_redraw_cell_border(ctx, px, py + 1))
292
0
      borders |= 1;
293
0
  } else {
294
0
    if (py == 0 ||
295
0
        screen_redraw_cell_border(ctx, px, py - 1))
296
0
      borders |= 2;
297
0
    if (screen_redraw_cell_border(ctx, px, py + 1))
298
0
      borders |= 1;
299
0
  }
300
301
  /*
302
   * Figure out what kind of border this cell is. Only one bit set
303
   * doesn't make sense (can't have a border cell with no others
304
   * connected).
305
   */
306
0
  switch (borders) {
307
0
  case 15:  /* 1111, left right top bottom */
308
0
    return (CELL_JOIN);
309
0
  case 14:  /* 1110, left right top */
310
0
    return (CELL_BOTTOMJOIN);
311
0
  case 13:  /* 1101, left right bottom */
312
0
    return (CELL_TOPJOIN);
313
0
  case 12:  /* 1100, left right */
314
0
    return (CELL_LEFTRIGHT);
315
0
  case 11:  /* 1011, left top bottom */
316
0
    return (CELL_RIGHTJOIN);
317
0
  case 10:  /* 1010, left top */
318
0
    return (CELL_BOTTOMRIGHT);
319
0
  case 9:   /* 1001, left bottom */
320
0
    return (CELL_TOPRIGHT);
321
0
  case 7:   /* 0111, right top bottom */
322
0
    return (CELL_LEFTJOIN);
323
0
  case 6:   /* 0110, right top */
324
0
    return (CELL_BOTTOMLEFT);
325
0
  case 5:   /* 0101, right bottom */
326
0
    return (CELL_TOPLEFT);
327
0
  case 3:   /* 0011, top bottom */
328
0
    return (CELL_TOPBOTTOM);
329
0
  }
330
0
  return (CELL_OUTSIDE);
331
0
}
332
333
/* Check if cell inside a pane. */
334
static int
335
screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py,
336
    struct window_pane **wpp)
337
0
{
338
0
  struct client   *c = ctx->c;
339
0
  struct window   *w = c->session->curw->window;
340
0
  struct window_pane  *wp, *active;
341
0
  int      pane_status = ctx->pane_status;
342
0
  u_int      sx = w->sx, sy = w->sy;
343
0
  int      border, pane_scrollbars = ctx->pane_scrollbars;
344
0
  u_int      right, line;
345
0
  int      sb_pos = ctx->pane_scrollbars_pos;
346
0
  int      sb_w;
347
348
0
  *wpp = NULL;
349
350
0
  if (px > sx || py > sy)
351
0
    return (CELL_OUTSIDE);
352
0
  if (px == sx || py == sy) /* window border */
353
0
    return (screen_redraw_type_of_cell(ctx, px, py));
354
355
0
  if (pane_status != PANE_STATUS_OFF) {
356
0
    active = wp = server_client_get_pane(c);
357
0
    do {
358
0
      if (!window_pane_visible(wp))
359
0
        goto next1;
360
361
0
      if (pane_status == PANE_STATUS_TOP)
362
0
        line = wp->yoff - 1;
363
0
      else
364
0
        line = wp->yoff + sy;
365
0
      right = wp->xoff + 2 + wp->status_size - 1;
366
367
0
      if (py == line && px >= wp->xoff + 2 && px <= right)
368
0
        return (CELL_INSIDE);
369
370
0
    next1:
371
0
      wp = TAILQ_NEXT(wp, entry);
372
0
      if (wp == NULL)
373
0
        wp = TAILQ_FIRST(&w->panes);
374
0
    } while (wp != active);
375
0
  }
376
377
0
  active = wp = server_client_get_pane(c);
378
0
  do {
379
0
    if (!window_pane_visible(wp))
380
0
      goto next2;
381
0
    *wpp = wp;
382
383
    /* Check if CELL_SCROLLBAR */
384
0
    if (window_pane_show_scrollbar(wp, pane_scrollbars)) {
385
0
      if (pane_status == PANE_STATUS_TOP)
386
0
        line = wp->yoff - 1;
387
0
      else
388
0
        line = wp->yoff + wp->sy;
389
390
      /*
391
       * Check if py could lie within a scrollbar. If the
392
       * pane is at the top then py == 0 to sy; if the pane
393
       * is not at the top, then yoff to yoff + sy.
394
       */
395
0
      sb_w = wp->scrollbar_style.width +
396
0
          wp->scrollbar_style.pad;
397
0
      if ((pane_status && py != line) ||
398
0
          (wp->yoff == 0 && py < wp->sy) ||
399
0
           (py >= wp->yoff && py < wp->yoff + wp->sy)) {
400
        /* Check if px lies within a scrollbar. */
401
0
        if ((sb_pos == PANE_SCROLLBARS_RIGHT &&
402
0
             (px >= wp->xoff + wp->sx &&
403
0
              px < wp->xoff + wp->sx + sb_w)) ||
404
0
            (sb_pos == PANE_SCROLLBARS_LEFT &&
405
0
             (px >= wp->xoff - sb_w &&
406
0
              px < wp->xoff)))
407
0
          return (CELL_SCROLLBAR);
408
0
      }
409
0
    }
410
411
    /*
412
     * If definitely inside, return. If not on border, skip.
413
     * Otherwise work out the cell.
414
     */
415
0
    border = screen_redraw_pane_border(ctx, wp, px, py);
416
0
    if (border == SCREEN_REDRAW_INSIDE)
417
0
      return (CELL_INSIDE);
418
0
    if (border == SCREEN_REDRAW_OUTSIDE)
419
0
      goto next2;
420
0
    return (screen_redraw_type_of_cell(ctx, px, py));
421
422
0
  next2:
423
0
    wp = TAILQ_NEXT(wp, entry);
424
0
    if (wp == NULL)
425
0
      wp = TAILQ_FIRST(&w->panes);
426
0
  } while (wp != active);
427
428
0
  return (CELL_OUTSIDE);
429
0
}
430
431
/* Check if the border of a particular pane. */
432
static int
433
screen_redraw_check_is(struct screen_redraw_ctx *ctx, u_int px, u_int py,
434
    struct window_pane *wp)
435
0
{
436
0
  enum screen_redraw_border_type  border;
437
438
0
  border = screen_redraw_pane_border(ctx, wp, px, py);
439
0
  if (border != SCREEN_REDRAW_INSIDE && border != SCREEN_REDRAW_OUTSIDE)
440
0
    return (1);
441
0
  return (0);
442
0
}
443
444
/* Update pane status. */
445
static int
446
screen_redraw_make_pane_status(struct client *c, struct window_pane *wp,
447
    struct screen_redraw_ctx *rctx, enum pane_lines pane_lines)
448
0
{
449
0
  struct window   *w = wp->window;
450
0
  struct grid_cell   gc;
451
0
  const char    *fmt;
452
0
  struct format_tree  *ft;
453
0
  struct style_line_entry *sle = &wp->border_status_line;
454
0
  char      *expanded;
455
0
  int      pane_status = rctx->pane_status, sb_w = 0;
456
0
  int      pane_scrollbars = rctx->pane_scrollbars;
457
0
  u_int      width, i, cell_type, px, py;
458
0
  struct screen_write_ctx  ctx;
459
0
  struct screen    old;
460
461
0
  if (window_pane_show_scrollbar(wp, pane_scrollbars))
462
0
    sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
463
464
0
  ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
465
0
  format_defaults(ft, c, c->session, c->session->curw, wp);
466
467
0
  if (wp == server_client_get_pane(c))
468
0
    style_apply(&gc, w->options, "pane-active-border-style", ft);
469
0
  else
470
0
    style_apply(&gc, w->options, "pane-border-style", ft);
471
0
  fmt = options_get_string(wp->options, "pane-border-format");
472
473
0
  expanded = format_expand_time(ft, fmt);
474
0
  if (wp->sx < 4)
475
0
    wp->status_size = width = 0;
476
0
  else
477
0
    wp->status_size = width = wp->sx + sb_w - 2;
478
479
0
  memcpy(&old, &wp->status_screen, sizeof old);
480
0
  screen_init(&wp->status_screen, width, 1, 0);
481
0
  wp->status_screen.mode = 0;
482
483
0
  screen_write_start(&ctx, &wp->status_screen);
484
485
0
  for (i = 0; i < width; i++) {
486
0
    px = wp->xoff + 2 + i;
487
0
    if (pane_status == PANE_STATUS_TOP)
488
0
      py = wp->yoff - 1;
489
0
    else
490
0
      py = wp->yoff + wp->sy;
491
0
    cell_type = screen_redraw_type_of_cell(rctx, px, py);
492
0
    screen_redraw_border_set(w, wp, pane_lines, cell_type, &gc);
493
0
    screen_write_cell(&ctx, &gc);
494
0
  }
495
0
  gc.attr &= ~GRID_ATTR_CHARSET;
496
497
0
  screen_write_cursormove(&ctx, 0, 0, 0);
498
0
  style_ranges_free(&sle->ranges);
499
0
  format_draw(&ctx, &gc, width, expanded, &sle->ranges, 0);
500
501
0
  screen_write_stop(&ctx);
502
0
  format_free(ft);
503
504
0
  free(sle->expanded);
505
0
  sle->expanded = expanded;
506
507
0
  if (grid_compare(wp->status_screen.grid, old.grid) == 0) {
508
0
    screen_free(&old);
509
0
    return (0);
510
0
  }
511
0
  screen_free(&old);
512
0
  return (1);
513
0
}
514
515
/* Draw pane status. */
516
static void
517
screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
518
0
{
519
0
  struct client   *c = ctx->c;
520
0
  struct window   *w = c->session->curw->window;
521
0
  struct tty    *tty = &c->tty;
522
0
  struct window_pane  *wp;
523
0
  struct screen   *s;
524
0
  u_int      i, x, width, xoff, yoff, size;
525
526
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
527
528
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
529
0
    if (!window_pane_visible(wp))
530
0
      continue;
531
0
    s = &wp->status_screen;
532
533
0
    size = wp->status_size;
534
0
    if (ctx->pane_status == PANE_STATUS_TOP)
535
0
      yoff = wp->yoff - 1;
536
0
    else
537
0
      yoff = wp->yoff + wp->sy;
538
0
    xoff = wp->xoff + 2;
539
540
0
    if (xoff + size <= ctx->ox ||
541
0
        xoff >= ctx->ox + ctx->sx ||
542
0
        yoff < ctx->oy ||
543
0
        yoff >= ctx->oy + ctx->sy)
544
0
      continue;
545
546
0
    if (xoff >= ctx->ox && xoff + size <= ctx->ox + ctx->sx) {
547
      /* All visible. */
548
0
      i = 0;
549
0
      x = xoff - ctx->ox;
550
0
      width = size;
551
0
    } else if (xoff < ctx->ox && xoff + size > ctx->ox + ctx->sx) {
552
      /* Both left and right not visible. */
553
0
      i = ctx->ox;
554
0
      x = 0;
555
0
      width = ctx->sx;
556
0
    } else if (xoff < ctx->ox) {
557
      /* Left not visible. */
558
0
      i = ctx->ox - xoff;
559
0
      x = 0;
560
0
      width = size - i;
561
0
    } else {
562
      /* Right not visible. */
563
0
      i = 0;
564
0
      x = xoff - ctx->ox;
565
0
      width = size - x;
566
0
    }
567
568
0
    if (ctx->statustop)
569
0
      yoff += ctx->statuslines;
570
0
    tty_draw_line(tty, s, i, 0, width, x, yoff - ctx->oy,
571
0
        &grid_default_cell, NULL);
572
0
  }
573
0
  tty_cursor(tty, 0, 0);
574
0
}
575
576
/* Update status line and change flags if unchanged. */
577
static uint64_t
578
screen_redraw_update(struct screen_redraw_ctx *ctx, uint64_t flags)
579
0
{
580
0
  struct client     *c = ctx->c;
581
0
  struct window     *w = c->session->curw->window;
582
0
  struct window_pane    *wp;
583
0
  int        redraw;
584
0
  enum pane_lines      lines;
585
586
0
  if (c->message_string != NULL)
587
0
    redraw = status_message_redraw(c);
588
0
  else if (c->prompt_string != NULL)
589
0
    redraw = status_prompt_redraw(c);
590
0
  else
591
0
    redraw = status_redraw(c);
592
0
  if (!redraw && (~flags & CLIENT_REDRAWSTATUSALWAYS))
593
0
    flags &= ~CLIENT_REDRAWSTATUS;
594
595
0
  if (c->overlay_draw != NULL)
596
0
    flags |= CLIENT_REDRAWOVERLAY;
597
598
0
  if (ctx->pane_status != PANE_STATUS_OFF) {
599
0
    lines = ctx->pane_lines;
600
0
    redraw = 0;
601
0
    TAILQ_FOREACH(wp, &w->panes, entry) {
602
0
      if (screen_redraw_make_pane_status(c, wp, ctx, lines))
603
0
        redraw = 1;
604
0
    }
605
0
    if (redraw)
606
0
      flags |= CLIENT_REDRAWBORDERS;
607
0
  }
608
609
0
  return (flags);
610
0
}
611
612
/* Set up redraw context. */
613
static void
614
screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx)
615
0
{
616
0
  struct session  *s = c->session;
617
0
  struct options  *oo = s->options;
618
0
  struct window *w = s->curw->window;
619
0
  struct options  *wo = w->options;
620
0
  u_int    lines;
621
622
0
  memset(ctx, 0, sizeof *ctx);
623
0
  ctx->c = c;
624
625
0
  lines = status_line_size(c);
626
0
  if (c->message_string != NULL || c->prompt_string != NULL)
627
0
    lines = (lines == 0) ? 1 : lines;
628
0
  if (lines != 0 && options_get_number(oo, "status-position") == 0)
629
0
    ctx->statustop = 1;
630
0
  ctx->statuslines = lines;
631
632
0
  ctx->pane_status = options_get_number(wo, "pane-border-status");
633
0
  ctx->pane_lines = options_get_number(wo, "pane-border-lines");
634
635
0
  ctx->pane_scrollbars = options_get_number(wo, "pane-scrollbars");
636
0
  ctx->pane_scrollbars_pos = options_get_number(wo,
637
0
      "pane-scrollbars-position");
638
639
0
  tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy);
640
641
0
  log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name,
642
0
      w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->statuslines,
643
0
      ctx->statustop);
644
0
}
645
646
/* Redraw entire screen. */
647
void
648
screen_redraw_screen(struct client *c)
649
0
{
650
0
  struct screen_redraw_ctx  ctx;
651
0
  uint64_t      flags;
652
653
0
  if (c->flags & CLIENT_SUSPENDED)
654
0
    return;
655
656
0
  screen_redraw_set_context(c, &ctx);
657
658
0
  flags = screen_redraw_update(&ctx, c->flags);
659
0
  if ((flags & CLIENT_ALLREDRAWFLAGS) == 0)
660
0
    return;
661
662
0
  tty_sync_start(&c->tty);
663
0
  tty_update_mode(&c->tty, c->tty.mode, NULL);
664
665
0
  if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) {
666
0
    log_debug("%s: redrawing borders", c->name);
667
0
    screen_redraw_draw_borders(&ctx);
668
0
    if (ctx.pane_status != PANE_STATUS_OFF)
669
0
      screen_redraw_draw_pane_status(&ctx);
670
0
    screen_redraw_draw_pane_scrollbars(&ctx);
671
0
  }
672
0
  if (flags & CLIENT_REDRAWWINDOW) {
673
0
    log_debug("%s: redrawing panes", c->name);
674
0
    screen_redraw_draw_panes(&ctx);
675
0
    screen_redraw_draw_pane_scrollbars(&ctx);
676
0
  }
677
0
  if (ctx.statuslines != 0 &&
678
0
      (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) {
679
0
    log_debug("%s: redrawing status", c->name);
680
0
    screen_redraw_draw_status(&ctx);
681
0
  }
682
0
  if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) {
683
0
    log_debug("%s: redrawing overlay", c->name);
684
0
    c->overlay_draw(c, c->overlay_data, &ctx);
685
0
  }
686
687
0
  tty_reset(&c->tty);
688
0
}
689
690
/* Redraw a single pane and its scrollbar. */
691
void
692
screen_redraw_pane(struct client *c, struct window_pane *wp,
693
    int redraw_scrollbar_only)
694
0
{
695
0
  struct screen_redraw_ctx  ctx;
696
697
0
  if (!window_pane_visible(wp))
698
0
    return;
699
700
0
  screen_redraw_set_context(c, &ctx);
701
0
  tty_sync_start(&c->tty);
702
0
  tty_update_mode(&c->tty, c->tty.mode, NULL);
703
704
0
  if (!redraw_scrollbar_only)
705
0
    screen_redraw_draw_pane(&ctx, wp);
706
707
0
  if (window_pane_show_scrollbar(wp, ctx.pane_scrollbars))
708
0
    screen_redraw_draw_pane_scrollbar(&ctx, wp);
709
710
0
  tty_reset(&c->tty);
711
0
}
712
713
/* Get border cell style. */
714
static const struct grid_cell *
715
screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x,
716
    u_int y, struct window_pane *wp)
717
0
{
718
0
  struct client   *c = ctx->c;
719
0
  struct session    *s = c->session;
720
0
  struct window   *w = s->curw->window;
721
0
  struct window_pane  *active = server_client_get_pane(c);
722
0
  struct options    *oo = w->options;
723
0
  struct format_tree  *ft;
724
725
0
  if (wp->border_gc_set)
726
0
    return (&wp->border_gc);
727
0
  wp->border_gc_set = 1;
728
729
0
  ft = format_create_defaults(NULL, c, s, s->curw, wp);
730
0
  if (screen_redraw_check_is(ctx, x, y, active))
731
0
    style_apply(&wp->border_gc, oo, "pane-active-border-style", ft);
732
0
  else
733
0
    style_apply(&wp->border_gc, oo, "pane-border-style", ft);
734
0
  format_free(ft);
735
736
0
  return (&wp->border_gc);
737
0
}
738
739
/* Draw arrow indicator if enabled. */
740
static void
741
screen_redraw_draw_border_arrows(struct screen_redraw_ctx *ctx, u_int i,
742
    u_int j, u_int cell_type, struct window_pane *wp,
743
    struct window_pane *active, struct grid_cell *gc)
744
0
{
745
0
  struct client   *c = ctx->c;
746
0
  struct session    *s = c->session;
747
0
  struct window   *w = s->curw->window;
748
0
  struct options    *oo = w->options;
749
0
  u_int      x = ctx->ox + i, y = ctx->oy + j;
750
0
  int      value, arrows = 0, border;
751
752
0
  if (wp == NULL)
753
0
    return;
754
0
  if (i != wp->xoff + 1 && j != wp->yoff + 1)
755
0
    return;
756
757
0
  value = options_get_number(oo, "pane-border-indicators");
758
0
  if (value != PANE_BORDER_ARROWS && value != PANE_BORDER_BOTH)
759
0
    return;
760
761
0
  border = screen_redraw_pane_border(ctx, active, x, y);
762
0
  if (border == SCREEN_REDRAW_INSIDE)
763
0
    return;
764
765
0
  if (i == wp->xoff + 1) {
766
0
    if (border == SCREEN_REDRAW_OUTSIDE) {
767
0
      if (screen_redraw_two_panes(wp->window, 1)) {
768
0
        if (active == TAILQ_FIRST(&w->panes))
769
0
          border = SCREEN_REDRAW_BORDER_BOTTOM;
770
0
        else
771
0
          border = SCREEN_REDRAW_BORDER_TOP;
772
0
        arrows = 1;
773
0
      }
774
0
    } else {
775
0
      if (cell_type == CELL_LEFTRIGHT)
776
0
        arrows = 1;
777
0
      else if (cell_type == CELL_TOPJOIN &&
778
0
          border == SCREEN_REDRAW_BORDER_BOTTOM)
779
0
        arrows = 1;
780
0
      else if (cell_type == CELL_BOTTOMJOIN &&
781
0
          border == SCREEN_REDRAW_BORDER_TOP)
782
0
        arrows = 1;
783
0
    }
784
0
  }
785
0
  if (j == wp->yoff + 1) {
786
0
    if (border == SCREEN_REDRAW_OUTSIDE) {
787
0
      if (screen_redraw_two_panes(wp->window, 0)) {
788
0
        if (active == TAILQ_FIRST(&w->panes))
789
0
          border = SCREEN_REDRAW_BORDER_RIGHT;
790
0
        else
791
0
          border = SCREEN_REDRAW_BORDER_LEFT;
792
0
        arrows = 1;
793
0
      }
794
0
    } else {
795
0
      if (cell_type == CELL_TOPBOTTOM)
796
0
        arrows = 1;
797
0
      else if (cell_type == CELL_LEFTJOIN &&
798
0
          border == SCREEN_REDRAW_BORDER_RIGHT)
799
0
        arrows = 1;
800
0
      else if (cell_type == CELL_RIGHTJOIN &&
801
0
          border == SCREEN_REDRAW_BORDER_LEFT)
802
0
        arrows = 1;
803
0
    }
804
0
  }
805
0
  if (arrows) {
806
0
    gc->attr |= GRID_ATTR_CHARSET;
807
0
    utf8_set(&gc->data, BORDER_MARKERS[border]);
808
0
  }
809
0
}
810
811
/* Draw a border cell. */
812
static void
813
screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
814
0
{
815
0
  struct client   *c = ctx->c;
816
0
  struct session    *s = c->session;
817
0
  struct window   *w = s->curw->window;
818
0
  struct options    *oo = w->options;
819
0
  struct tty    *tty = &c->tty;
820
0
  struct format_tree  *ft;
821
0
  struct window_pane  *wp, *active = server_client_get_pane(c);
822
0
  struct grid_cell   gc;
823
0
  const struct grid_cell  *tmp;
824
0
  u_int      cell_type;
825
0
  u_int      x = ctx->ox + i, y = ctx->oy + j;
826
0
  int      isolates;
827
0
  struct visible_ranges *r;
828
829
0
  if (c->overlay_check != NULL) {
830
0
    r = c->overlay_check(c, c->overlay_data, x, y, 1);
831
0
    if (server_client_ranges_is_empty(r))
832
0
      return;
833
0
  }
834
835
0
  cell_type = screen_redraw_check_cell(ctx, x, y, &wp);
836
0
  if (cell_type == CELL_INSIDE || cell_type == CELL_SCROLLBAR)
837
0
    return;
838
839
0
  if (wp == NULL) {
840
0
    if (!ctx->no_pane_gc_set) {
841
0
      ft = format_create_defaults(NULL, c, s, s->curw, NULL);
842
0
      memcpy(&ctx->no_pane_gc, &grid_default_cell, sizeof gc);
843
0
      style_add(&ctx->no_pane_gc, oo, "pane-border-style",
844
0
          ft);
845
0
      format_free(ft);
846
0
      ctx->no_pane_gc_set = 1;
847
0
    }
848
0
    memcpy(&gc, &ctx->no_pane_gc, sizeof gc);
849
0
  } else {
850
0
    tmp = screen_redraw_draw_borders_style(ctx, x, y, wp);
851
0
    if (tmp == NULL)
852
0
      return;
853
0
    memcpy(&gc, tmp, sizeof gc);
854
855
0
    if (server_is_marked(s, s->curw, marked_pane.wp) &&
856
0
        screen_redraw_check_is(ctx, x, y, marked_pane.wp))
857
0
      gc.attr ^= GRID_ATTR_REVERSE;
858
0
  }
859
0
  screen_redraw_border_set(w, wp, ctx->pane_lines, cell_type, &gc);
860
861
0
  if (cell_type == CELL_TOPBOTTOM &&
862
0
      (c->flags & CLIENT_UTF8) &&
863
0
      tty_term_has(tty->term, TTYC_BIDI))
864
0
    isolates = 1;
865
0
  else
866
0
    isolates = 0;
867
868
0
  if (ctx->statustop)
869
0
    tty_cursor(tty, i, ctx->statuslines + j);
870
0
  else
871
0
    tty_cursor(tty, i, j);
872
0
  if (isolates)
873
0
    tty_puts(tty, END_ISOLATE);
874
875
0
  screen_redraw_draw_border_arrows(ctx, i, j, cell_type, wp, active, &gc);
876
877
0
  tty_cell(tty, &gc, &grid_default_cell, NULL, NULL);
878
0
  if (isolates)
879
0
    tty_puts(tty, START_ISOLATE);
880
0
}
881
882
/* Draw the borders. */
883
static void
884
screen_redraw_draw_borders(struct screen_redraw_ctx *ctx)
885
0
{
886
0
  struct client   *c = ctx->c;
887
0
  struct session    *s = c->session;
888
0
  struct window   *w = s->curw->window;
889
0
  struct window_pane  *wp;
890
0
  u_int      i, j;
891
892
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
893
894
0
  TAILQ_FOREACH(wp, &w->panes, entry)
895
0
    wp->border_gc_set = 0;
896
897
0
  for (j = 0; j < c->tty.sy - ctx->statuslines; j++) {
898
0
    for (i = 0; i < c->tty.sx; i++)
899
0
      screen_redraw_draw_borders_cell(ctx, i, j);
900
0
  }
901
0
}
902
903
/* Draw the panes. */
904
static void
905
screen_redraw_draw_panes(struct screen_redraw_ctx *ctx)
906
0
{
907
0
  struct client   *c = ctx->c;
908
0
  struct window   *w = c->session->curw->window;
909
0
  struct window_pane  *wp;
910
911
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
912
913
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
914
0
    if (window_pane_visible(wp))
915
0
      screen_redraw_draw_pane(ctx, wp);
916
0
  }
917
0
}
918
919
/* Draw the status line. */
920
static void
921
screen_redraw_draw_status(struct screen_redraw_ctx *ctx)
922
0
{
923
0
  struct client *c = ctx->c;
924
0
  struct window *w = c->session->curw->window;
925
0
  struct tty  *tty = &c->tty;
926
0
  struct screen *s = c->status.active;
927
0
  u_int    i, y;
928
929
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
930
931
0
  if (ctx->statustop)
932
0
    y = 0;
933
0
  else
934
0
    y = c->tty.sy - ctx->statuslines;
935
0
  for (i = 0; i < ctx->statuslines; i++) {
936
0
    tty_draw_line(tty, s, 0, i, UINT_MAX, 0, y + i,
937
0
        &grid_default_cell, NULL);
938
0
  }
939
0
}
940
941
/* Draw one pane. */
942
static void
943
screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
944
0
{
945
0
  struct client   *c = ctx->c;
946
0
  struct window   *w = c->session->curw->window;
947
0
  struct tty    *tty = &c->tty;
948
0
  struct screen   *s = wp->screen;
949
0
  struct colour_palette *palette = &wp->palette;
950
0
  struct grid_cell   defaults;
951
0
  struct visible_ranges *r;
952
0
  struct visible_range  *rr = NULL;
953
0
  u_int      i, j, k, top, x, y, width, used;
954
955
0
  if (wp->base.mode & MODE_SYNC)
956
0
    screen_write_stop_sync(wp);
957
958
0
  log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id);
959
960
0
  if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx)
961
0
    return;
962
0
  if (ctx->statustop)
963
0
    top = ctx->statuslines;
964
0
  else
965
0
    top = 0;
966
0
  for (j = 0; j < wp->sy; j++) {
967
0
    if (wp->yoff + j < ctx->oy || wp->yoff + j >= ctx->oy + ctx->sy)
968
0
      continue;
969
0
    y = top + wp->yoff + j - ctx->oy;
970
971
0
    if (wp->xoff >= ctx->ox &&
972
0
        wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
973
      /* All visible. */
974
0
      i = 0;
975
0
      x = wp->xoff - ctx->ox;
976
0
      width = wp->sx;
977
0
    } else if (wp->xoff < ctx->ox &&
978
0
        wp->xoff + wp->sx > ctx->ox + ctx->sx) {
979
      /* Both left and right not visible. */
980
0
      i = ctx->ox;
981
0
      x = 0;
982
0
      width = ctx->sx;
983
0
    } else if (wp->xoff < ctx->ox) {
984
      /* Left not visible. */
985
0
      i = ctx->ox - wp->xoff;
986
0
      x = 0;
987
0
      width = wp->sx - i;
988
0
    } else {
989
      /* Right not visible. */
990
0
      i = 0;
991
0
      x = wp->xoff - ctx->ox;
992
0
      width = ctx->sx - x;
993
0
    }
994
0
    log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u",
995
0
        __func__, c->name, wp->id, i, j, x, y, width);
996
997
0
    tty_default_colours(&defaults, wp);
998
999
0
    r = tty_check_overlay_range(tty, x, y, width);
1000
0
    used = r->used;
1001
1002
0
    rr = xreallocarray(rr, used, sizeof *rr);
1003
0
    memcpy(rr, r->ranges, used * sizeof *rr);
1004
1005
0
    for (k = 0; k < used; k++) {
1006
0
      if (rr[k].nx != 0) {
1007
0
        tty_draw_line(tty, s, rr[k].px - wp->xoff, j,
1008
0
            rr[k].nx, rr[k].px, y, &defaults, palette);
1009
0
      }
1010
0
    }
1011
0
  }
1012
1013
#ifdef ENABLE_SIXEL
1014
  tty_draw_images(c, wp, s);
1015
#endif
1016
0
  free(rr);
1017
0
}
1018
1019
/* Draw the panes scrollbars */
1020
static void
1021
screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx *ctx)
1022
0
{
1023
0
  struct client   *c = ctx->c;
1024
0
  struct window   *w = c->session->curw->window;
1025
0
  struct window_pane  *wp;
1026
1027
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
1028
1029
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
1030
0
    if (window_pane_show_scrollbar(wp, ctx->pane_scrollbars) &&
1031
0
        window_pane_visible(wp))
1032
0
      screen_redraw_draw_pane_scrollbar(ctx, wp);
1033
0
  }
1034
0
}
1035
1036
/* Draw pane scrollbar. */
1037
void
1038
screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *ctx,
1039
    struct window_pane *wp)
1040
0
{
1041
0
  struct screen *s = wp->screen;
1042
0
  double     percent_view;
1043
0
  u_int    sb = ctx->pane_scrollbars, total_height, sb_h = wp->sy;
1044
0
  u_int    sb_pos = ctx->pane_scrollbars_pos, slider_h, slider_y;
1045
0
  int    sb_w = wp->scrollbar_style.width;
1046
0
  int    sb_pad = wp->scrollbar_style.pad;
1047
0
  int    cm_y, cm_size, xoff = wp->xoff, ox = ctx->ox;
1048
0
  int    sb_x, sb_y = (int)(wp->yoff - ctx->oy); /* sb top */
1049
1050
0
  if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE) {
1051
0
    if (sb == PANE_SCROLLBARS_MODAL)
1052
0
      return;
1053
    /* Show slider at the bottom of the scrollbar. */
1054
0
    total_height = screen_size_y(s) + screen_hsize(s);
1055
0
    percent_view = (double)sb_h / total_height;
1056
0
    slider_h = (double)sb_h * percent_view;
1057
0
    slider_y = sb_h - slider_h;
1058
0
  } else {
1059
0
    if (TAILQ_FIRST(&wp->modes) == NULL)
1060
0
      return;
1061
0
    if (window_copy_get_current_offset(wp, &cm_y, &cm_size) == 0)
1062
0
      return;
1063
0
    total_height = cm_size + sb_h;
1064
0
    percent_view = (double)sb_h / (cm_size + sb_h);
1065
0
    slider_h = (double)sb_h * percent_view;
1066
0
    slider_y = (sb_h + 1) * ((double)cm_y / total_height);
1067
0
  }
1068
1069
0
  if (sb_pos == PANE_SCROLLBARS_LEFT)
1070
0
    sb_x = xoff - sb_w - sb_pad - ox;
1071
0
  else
1072
0
    sb_x = xoff + wp->sx - ox;
1073
1074
0
  if (slider_h < 1)
1075
0
    slider_h = 1;
1076
0
  if (slider_y >= sb_h)
1077
0
    slider_y = sb_h - 1;
1078
1079
0
  screen_redraw_draw_scrollbar(ctx, wp, sb_pos, sb_x, sb_y, sb_h,
1080
0
      slider_h, slider_y);
1081
1082
  /* Store current position and height of the slider */
1083
0
  wp->sb_slider_y = slider_y;  /* top of slider y pos in scrollbar */
1084
0
  wp->sb_slider_h = slider_h;  /* height of slider */
1085
0
}
1086
1087
static void
1088
screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx,
1089
    struct window_pane *wp, int sb_pos, int sb_x, int sb_y, u_int sb_h,
1090
    u_int slider_h, u_int slider_y)
1091
0
{
1092
0
  struct client   *c = ctx->c;
1093
0
  struct tty    *tty = &c->tty;
1094
0
  struct grid_cell   gc, slgc, *gcp;
1095
0
  struct style    *sb_style = &wp->scrollbar_style;
1096
0
  u_int      i, j, imax, jmax;
1097
0
  u_int      sb_w = sb_style->width, sb_pad = sb_style->pad;
1098
0
  int      px, py, ox = ctx->ox, oy = ctx->oy;
1099
0
  int      sx = ctx->sx, sy = ctx->sy, xoff = wp->xoff;
1100
0
  int      yoff = wp->yoff;
1101
1102
0
  if (ctx->statustop) {
1103
0
    sb_y += ctx->statuslines;
1104
0
    sy += ctx->statuslines;
1105
0
  }
1106
1107
0
  gc = sb_style->gc;
1108
0
  memcpy(&slgc, &gc, sizeof slgc);
1109
0
  slgc.fg = gc.bg;
1110
0
  slgc.bg = gc.fg;
1111
1112
0
  if (sb_x >= sx || sb_y >= sy)
1113
0
    return;
1114
0
  imax = sb_w + sb_pad;
1115
0
  if ((int)imax + sb_x > sx)
1116
0
    imax = sx - sb_x;
1117
0
  jmax = sb_h;
1118
0
  if ((int)jmax + sb_y > sy)
1119
0
    jmax = sy - sb_y;
1120
1121
0
  for (j = 0; j < jmax; j++) {
1122
0
    py = sb_y + j;
1123
0
    for (i = 0; i < imax; i++) {
1124
0
      px = sb_x + i;
1125
0
      if (px < xoff - ox - (int)sb_w - (int)sb_pad ||
1126
0
          px >= sx || px < 0 ||
1127
0
          py < yoff - oy - 1 ||
1128
0
          py >= sy || py < 0)
1129
0
        continue;
1130
0
      tty_cursor(tty, px, py);
1131
0
      if ((sb_pos == PANE_SCROLLBARS_LEFT &&
1132
0
          i >= sb_w && i < sb_w + sb_pad) ||
1133
0
          (sb_pos == PANE_SCROLLBARS_RIGHT &&
1134
0
           i < sb_pad)) {
1135
0
        tty_cell(tty, &grid_default_cell,
1136
0
            &grid_default_cell, NULL, NULL);
1137
0
      } else {
1138
0
        if (j >= slider_y && j < slider_y + slider_h)
1139
0
          gcp = &slgc;
1140
0
        else
1141
0
          gcp = &gc;
1142
0
        tty_cell(tty, gcp, &grid_default_cell, NULL,
1143
            NULL);
1144
0
      }
1145
0
    }
1146
0
  }
1147
0
}