Coverage Report

Created: 2026-01-17 06:24

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
  char      *expanded;
454
0
  int      pane_status = rctx->pane_status, sb_w = 0;
455
0
  int      pane_scrollbars = rctx->pane_scrollbars;
456
0
  u_int      width, i, cell_type, px, py;
457
0
  struct screen_write_ctx  ctx;
458
0
  struct screen    old;
459
460
0
  if (window_pane_show_scrollbar(wp, pane_scrollbars))
461
0
    sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad;
462
463
0
  ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
464
0
  format_defaults(ft, c, c->session, c->session->curw, wp);
465
466
0
  if (wp == server_client_get_pane(c))
467
0
    style_apply(&gc, w->options, "pane-active-border-style", ft);
468
0
  else
469
0
    style_apply(&gc, w->options, "pane-border-style", ft);
470
0
  fmt = options_get_string(wp->options, "pane-border-format");
471
472
0
  expanded = format_expand_time(ft, fmt);
473
0
  if (wp->sx < 4)
474
0
    wp->status_size = width = 0;
475
0
  else
476
0
    wp->status_size = width = wp->sx + sb_w - 2;
477
478
0
  memcpy(&old, &wp->status_screen, sizeof old);
479
0
  screen_init(&wp->status_screen, width, 1, 0);
480
0
  wp->status_screen.mode = 0;
481
482
0
  screen_write_start(&ctx, &wp->status_screen);
483
484
0
  for (i = 0; i < width; i++) {
485
0
    px = wp->xoff + 2 + i;
486
0
    if (pane_status == PANE_STATUS_TOP)
487
0
      py = wp->yoff - 1;
488
0
    else
489
0
      py = wp->yoff + wp->sy;
490
0
    cell_type = screen_redraw_type_of_cell(rctx, px, py);
491
0
    screen_redraw_border_set(w, wp, pane_lines, cell_type, &gc);
492
0
    screen_write_cell(&ctx, &gc);
493
0
  }
494
0
  gc.attr &= ~GRID_ATTR_CHARSET;
495
496
0
  screen_write_cursormove(&ctx, 0, 0, 0);
497
0
  format_draw(&ctx, &gc, width, expanded, NULL, 0);
498
0
  screen_write_stop(&ctx);
499
500
0
  free(expanded);
501
0
  format_free(ft);
502
503
0
  if (grid_compare(wp->status_screen.grid, old.grid) == 0) {
504
0
    screen_free(&old);
505
0
    return (0);
506
0
  }
507
0
  screen_free(&old);
508
0
  return (1);
509
0
}
510
511
/* Draw pane status. */
512
static void
513
screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
514
0
{
515
0
  struct client   *c = ctx->c;
516
0
  struct window   *w = c->session->curw->window;
517
0
  struct tty    *tty = &c->tty;
518
0
  struct window_pane  *wp;
519
0
  struct screen   *s;
520
0
  u_int      i, x, width, xoff, yoff, size;
521
522
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
523
524
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
525
0
    if (!window_pane_visible(wp))
526
0
      continue;
527
0
    s = &wp->status_screen;
528
529
0
    size = wp->status_size;
530
0
    if (ctx->pane_status == PANE_STATUS_TOP)
531
0
      yoff = wp->yoff - 1;
532
0
    else
533
0
      yoff = wp->yoff + wp->sy;
534
0
    xoff = wp->xoff + 2;
535
536
0
    if (xoff + size <= ctx->ox ||
537
0
        xoff >= ctx->ox + ctx->sx ||
538
0
        yoff < ctx->oy ||
539
0
        yoff >= ctx->oy + ctx->sy)
540
0
      continue;
541
542
0
    if (xoff >= ctx->ox && xoff + size <= ctx->ox + ctx->sx) {
543
      /* All visible. */
544
0
      i = 0;
545
0
      x = xoff - ctx->ox;
546
0
      width = size;
547
0
    } else if (xoff < ctx->ox && xoff + size > ctx->ox + ctx->sx) {
548
      /* Both left and right not visible. */
549
0
      i = ctx->ox;
550
0
      x = 0;
551
0
      width = ctx->sx;
552
0
    } else if (xoff < ctx->ox) {
553
      /* Left not visible. */
554
0
      i = ctx->ox - xoff;
555
0
      x = 0;
556
0
      width = size - i;
557
0
    } else {
558
      /* Right not visible. */
559
0
      i = 0;
560
0
      x = xoff - ctx->ox;
561
0
      width = size - x;
562
0
    }
563
564
0
    if (ctx->statustop)
565
0
      yoff += ctx->statuslines;
566
0
    tty_draw_line(tty, s, i, 0, width, x, yoff - ctx->oy,
567
0
        &grid_default_cell, NULL);
568
0
  }
569
0
  tty_cursor(tty, 0, 0);
570
0
}
571
572
/* Update status line and change flags if unchanged. */
573
static uint64_t
574
screen_redraw_update(struct screen_redraw_ctx *ctx, uint64_t flags)
575
0
{
576
0
  struct client     *c = ctx->c;
577
0
  struct window     *w = c->session->curw->window;
578
0
  struct window_pane    *wp;
579
0
  int        redraw;
580
0
  enum pane_lines      lines;
581
582
0
  if (c->message_string != NULL)
583
0
    redraw = status_message_redraw(c);
584
0
  else if (c->prompt_string != NULL)
585
0
    redraw = status_prompt_redraw(c);
586
0
  else
587
0
    redraw = status_redraw(c);
588
0
  if (!redraw && (~flags & CLIENT_REDRAWSTATUSALWAYS))
589
0
    flags &= ~CLIENT_REDRAWSTATUS;
590
591
0
  if (c->overlay_draw != NULL)
592
0
    flags |= CLIENT_REDRAWOVERLAY;
593
594
0
  if (ctx->pane_status != PANE_STATUS_OFF) {
595
0
    lines = ctx->pane_lines;
596
0
    redraw = 0;
597
0
    TAILQ_FOREACH(wp, &w->panes, entry) {
598
0
      if (screen_redraw_make_pane_status(c, wp, ctx, lines))
599
0
        redraw = 1;
600
0
    }
601
0
    if (redraw)
602
0
      flags |= CLIENT_REDRAWBORDERS;
603
0
  }
604
605
0
  return (flags);
606
0
}
607
608
/* Set up redraw context. */
609
static void
610
screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx)
611
0
{
612
0
  struct session  *s = c->session;
613
0
  struct options  *oo = s->options;
614
0
  struct window *w = s->curw->window;
615
0
  struct options  *wo = w->options;
616
0
  u_int    lines;
617
618
0
  memset(ctx, 0, sizeof *ctx);
619
0
  ctx->c = c;
620
621
0
  lines = status_line_size(c);
622
0
  if (c->message_string != NULL || c->prompt_string != NULL)
623
0
    lines = (lines == 0) ? 1 : lines;
624
0
  if (lines != 0 && options_get_number(oo, "status-position") == 0)
625
0
    ctx->statustop = 1;
626
0
  ctx->statuslines = lines;
627
628
0
  ctx->pane_status = options_get_number(wo, "pane-border-status");
629
0
  ctx->pane_lines = options_get_number(wo, "pane-border-lines");
630
631
0
  ctx->pane_scrollbars = options_get_number(wo, "pane-scrollbars");
632
0
  ctx->pane_scrollbars_pos = options_get_number(wo,
633
0
      "pane-scrollbars-position");
634
635
0
  tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy);
636
637
0
  log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name,
638
0
      w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->statuslines,
639
0
      ctx->statustop);
640
0
}
641
642
/* Redraw entire screen. */
643
void
644
screen_redraw_screen(struct client *c)
645
0
{
646
0
  struct screen_redraw_ctx  ctx;
647
0
  uint64_t      flags;
648
649
0
  if (c->flags & CLIENT_SUSPENDED)
650
0
    return;
651
652
0
  screen_redraw_set_context(c, &ctx);
653
654
0
  flags = screen_redraw_update(&ctx, c->flags);
655
0
  if ((flags & CLIENT_ALLREDRAWFLAGS) == 0)
656
0
    return;
657
658
0
  tty_sync_start(&c->tty);
659
0
  tty_update_mode(&c->tty, c->tty.mode, NULL);
660
661
0
  if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) {
662
0
    log_debug("%s: redrawing borders", c->name);
663
0
    screen_redraw_draw_borders(&ctx);
664
0
    if (ctx.pane_status != PANE_STATUS_OFF)
665
0
      screen_redraw_draw_pane_status(&ctx);
666
0
    screen_redraw_draw_pane_scrollbars(&ctx);
667
0
  }
668
0
  if (flags & CLIENT_REDRAWWINDOW) {
669
0
    log_debug("%s: redrawing panes", c->name);
670
0
    screen_redraw_draw_panes(&ctx);
671
0
    screen_redraw_draw_pane_scrollbars(&ctx);
672
0
  }
673
0
  if (ctx.statuslines != 0 &&
674
0
      (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) {
675
0
    log_debug("%s: redrawing status", c->name);
676
0
    screen_redraw_draw_status(&ctx);
677
0
  }
678
0
  if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) {
679
0
    log_debug("%s: redrawing overlay", c->name);
680
0
    c->overlay_draw(c, c->overlay_data, &ctx);
681
0
  }
682
683
0
  tty_reset(&c->tty);
684
0
}
685
686
/* Redraw a single pane and its scrollbar. */
687
void
688
screen_redraw_pane(struct client *c, struct window_pane *wp,
689
    int redraw_scrollbar_only)
690
0
{
691
0
  struct screen_redraw_ctx  ctx;
692
693
0
  if (!window_pane_visible(wp))
694
0
    return;
695
696
0
  screen_redraw_set_context(c, &ctx);
697
0
  tty_sync_start(&c->tty);
698
0
  tty_update_mode(&c->tty, c->tty.mode, NULL);
699
700
0
  if (!redraw_scrollbar_only)
701
0
    screen_redraw_draw_pane(&ctx, wp);
702
703
0
  if (window_pane_show_scrollbar(wp, ctx.pane_scrollbars))
704
0
    screen_redraw_draw_pane_scrollbar(&ctx, wp);
705
706
0
  tty_reset(&c->tty);
707
0
}
708
709
/* Get border cell style. */
710
static const struct grid_cell *
711
screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x,
712
    u_int y, struct window_pane *wp)
713
0
{
714
0
  struct client   *c = ctx->c;
715
0
  struct session    *s = c->session;
716
0
  struct window   *w = s->curw->window;
717
0
  struct window_pane  *active = server_client_get_pane(c);
718
0
  struct options    *oo = w->options;
719
0
  struct format_tree  *ft;
720
721
0
  if (wp->border_gc_set)
722
0
    return (&wp->border_gc);
723
0
  wp->border_gc_set = 1;
724
725
0
  ft = format_create_defaults(NULL, c, s, s->curw, wp);
726
0
  if (screen_redraw_check_is(ctx, x, y, active))
727
0
    style_apply(&wp->border_gc, oo, "pane-active-border-style", ft);
728
0
  else
729
0
    style_apply(&wp->border_gc, oo, "pane-border-style", ft);
730
0
  format_free(ft);
731
732
0
  return (&wp->border_gc);
733
0
}
734
735
/* Draw arrow indicator if enabled. */
736
static void
737
screen_redraw_draw_border_arrows(struct screen_redraw_ctx *ctx, u_int i,
738
    u_int j, u_int cell_type, struct window_pane *wp,
739
    struct window_pane *active, struct grid_cell *gc)
740
0
{
741
0
  struct client   *c = ctx->c;
742
0
  struct session    *s = c->session;
743
0
  struct window   *w = s->curw->window;
744
0
  struct options    *oo = w->options;
745
0
  u_int      x = ctx->ox + i, y = ctx->oy + j;
746
0
  int      value, arrows = 0, border;
747
748
0
  if (wp == NULL)
749
0
    return;
750
0
  if (i != wp->xoff + 1 && j != wp->yoff + 1)
751
0
    return;
752
753
0
  value = options_get_number(oo, "pane-border-indicators");
754
0
  if (value != PANE_BORDER_ARROWS && value != PANE_BORDER_BOTH)
755
0
    return;
756
757
0
  border = screen_redraw_pane_border(ctx, active, x, y);
758
0
  if (border == SCREEN_REDRAW_INSIDE)
759
0
    return;
760
761
0
  if (i == wp->xoff + 1) {
762
0
    if (border == SCREEN_REDRAW_OUTSIDE) {
763
0
      if (screen_redraw_two_panes(wp->window, 1)) {
764
0
        if (active == TAILQ_FIRST(&w->panes))
765
0
          border = SCREEN_REDRAW_BORDER_BOTTOM;
766
0
        else
767
0
          border = SCREEN_REDRAW_BORDER_TOP;
768
0
        arrows = 1;
769
0
      }
770
0
    } else {
771
0
      if (cell_type == CELL_LEFTRIGHT)
772
0
        arrows = 1;
773
0
      else if (cell_type == CELL_TOPJOIN &&
774
0
          border == SCREEN_REDRAW_BORDER_BOTTOM)
775
0
        arrows = 1;
776
0
      else if (cell_type == CELL_BOTTOMJOIN &&
777
0
          border == SCREEN_REDRAW_BORDER_TOP)
778
0
        arrows = 1;
779
0
    }
780
0
  }
781
0
  if (j == wp->yoff + 1) {
782
0
    if (border == SCREEN_REDRAW_OUTSIDE) {
783
0
      if (screen_redraw_two_panes(wp->window, 0)) {
784
0
        if (active == TAILQ_FIRST(&w->panes))
785
0
          border = SCREEN_REDRAW_BORDER_RIGHT;
786
0
        else
787
0
          border = SCREEN_REDRAW_BORDER_LEFT;
788
0
        arrows = 1;
789
0
      }
790
0
    } else {
791
0
      if (cell_type == CELL_TOPBOTTOM)
792
0
        arrows = 1;
793
0
      else if (cell_type == CELL_LEFTJOIN &&
794
0
          border == SCREEN_REDRAW_BORDER_RIGHT)
795
0
        arrows = 1;
796
0
      else if (cell_type == CELL_RIGHTJOIN &&
797
0
          border == SCREEN_REDRAW_BORDER_LEFT)
798
0
        arrows = 1;
799
0
    }
800
0
  }
801
0
  if (arrows) {
802
0
    gc->attr |= GRID_ATTR_CHARSET;
803
0
    utf8_set(&gc->data, BORDER_MARKERS[border]);
804
0
  }
805
0
}
806
807
/* Draw a border cell. */
808
static void
809
screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
810
0
{
811
0
  struct client   *c = ctx->c;
812
0
  struct session    *s = c->session;
813
0
  struct window   *w = s->curw->window;
814
0
  struct options    *oo = w->options;
815
0
  struct tty    *tty = &c->tty;
816
0
  struct format_tree  *ft;
817
0
  struct window_pane  *wp, *active = server_client_get_pane(c);
818
0
  struct grid_cell   gc;
819
0
  const struct grid_cell  *tmp;
820
0
  struct overlay_ranges  r;
821
0
  u_int      cell_type, x = ctx->ox + i, y = ctx->oy + j;
822
0
  int      isolates;
823
824
0
  if (c->overlay_check != NULL) {
825
0
    c->overlay_check(c, c->overlay_data, x, y, 1, &r);
826
0
    if (r.nx[0] + r.nx[1] == 0)
827
0
      return;
828
0
  }
829
830
0
  cell_type = screen_redraw_check_cell(ctx, x, y, &wp);
831
0
  if (cell_type == CELL_INSIDE || cell_type == CELL_SCROLLBAR)
832
0
    return;
833
834
0
  if (wp == NULL) {
835
0
    if (!ctx->no_pane_gc_set) {
836
0
      ft = format_create_defaults(NULL, c, s, s->curw, NULL);
837
0
      memcpy(&ctx->no_pane_gc, &grid_default_cell, sizeof gc);
838
0
      style_add(&ctx->no_pane_gc, oo, "pane-border-style",
839
0
          ft);
840
0
      format_free(ft);
841
0
      ctx->no_pane_gc_set = 1;
842
0
    }
843
0
    memcpy(&gc, &ctx->no_pane_gc, sizeof gc);
844
0
  } else {
845
0
    tmp = screen_redraw_draw_borders_style(ctx, x, y, wp);
846
0
    if (tmp == NULL)
847
0
      return;
848
0
    memcpy(&gc, tmp, sizeof gc);
849
850
0
    if (server_is_marked(s, s->curw, marked_pane.wp) &&
851
0
        screen_redraw_check_is(ctx, x, y, marked_pane.wp))
852
0
      gc.attr ^= GRID_ATTR_REVERSE;
853
0
  }
854
0
  screen_redraw_border_set(w, wp, ctx->pane_lines, cell_type, &gc);
855
856
0
  if (cell_type == CELL_TOPBOTTOM &&
857
0
      (c->flags & CLIENT_UTF8) &&
858
0
      tty_term_has(tty->term, TTYC_BIDI))
859
0
    isolates = 1;
860
0
  else
861
0
    isolates = 0;
862
863
0
  if (ctx->statustop)
864
0
    tty_cursor(tty, i, ctx->statuslines + j);
865
0
  else
866
0
    tty_cursor(tty, i, j);
867
0
  if (isolates)
868
0
    tty_puts(tty, END_ISOLATE);
869
870
0
  screen_redraw_draw_border_arrows(ctx, i, j, cell_type, wp, active, &gc);
871
872
0
  tty_cell(tty, &gc, &grid_default_cell, NULL, NULL);
873
0
  if (isolates)
874
0
    tty_puts(tty, START_ISOLATE);
875
0
}
876
877
/* Draw the borders. */
878
static void
879
screen_redraw_draw_borders(struct screen_redraw_ctx *ctx)
880
0
{
881
0
  struct client   *c = ctx->c;
882
0
  struct session    *s = c->session;
883
0
  struct window   *w = s->curw->window;
884
0
  struct window_pane  *wp;
885
0
  u_int      i, j;
886
887
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
888
889
0
  TAILQ_FOREACH(wp, &w->panes, entry)
890
0
    wp->border_gc_set = 0;
891
892
0
  for (j = 0; j < c->tty.sy - ctx->statuslines; j++) {
893
0
    for (i = 0; i < c->tty.sx; i++)
894
0
      screen_redraw_draw_borders_cell(ctx, i, j);
895
0
  }
896
0
}
897
898
/* Draw the panes. */
899
static void
900
screen_redraw_draw_panes(struct screen_redraw_ctx *ctx)
901
0
{
902
0
  struct client   *c = ctx->c;
903
0
  struct window   *w = c->session->curw->window;
904
0
  struct window_pane  *wp;
905
906
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
907
908
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
909
0
    if (window_pane_visible(wp))
910
0
      screen_redraw_draw_pane(ctx, wp);
911
0
  }
912
0
}
913
914
/* Draw the status line. */
915
static void
916
screen_redraw_draw_status(struct screen_redraw_ctx *ctx)
917
0
{
918
0
  struct client *c = ctx->c;
919
0
  struct window *w = c->session->curw->window;
920
0
  struct tty  *tty = &c->tty;
921
0
  struct screen *s = c->status.active;
922
0
  u_int    i, y;
923
924
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
925
926
0
  if (ctx->statustop)
927
0
    y = 0;
928
0
  else
929
0
    y = c->tty.sy - ctx->statuslines;
930
0
  for (i = 0; i < ctx->statuslines; i++) {
931
0
    tty_draw_line(tty, s, 0, i, UINT_MAX, 0, y + i,
932
0
        &grid_default_cell, NULL);
933
0
  }
934
0
}
935
936
/* Draw one pane. */
937
static void
938
screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
939
0
{
940
0
  struct client   *c = ctx->c;
941
0
  struct window   *w = c->session->curw->window;
942
0
  struct tty    *tty = &c->tty;
943
0
  struct screen   *s = wp->screen;
944
0
  struct colour_palette *palette = &wp->palette;
945
0
  struct grid_cell   defaults;
946
0
  u_int      i, j, top, x, y, width;
947
948
0
  if (wp->base.mode & MODE_SYNC)
949
0
    screen_write_stop_sync(wp);
950
951
0
  log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id);
952
953
0
  if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx)
954
0
    return;
955
0
  if (ctx->statustop)
956
0
    top = ctx->statuslines;
957
0
  else
958
0
    top = 0;
959
0
  for (j = 0; j < wp->sy; j++) {
960
0
    if (wp->yoff + j < ctx->oy || wp->yoff + j >= ctx->oy + ctx->sy)
961
0
      continue;
962
0
    y = top + wp->yoff + j - ctx->oy;
963
964
0
    if (wp->xoff >= ctx->ox &&
965
0
        wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
966
      /* All visible. */
967
0
      i = 0;
968
0
      x = wp->xoff - ctx->ox;
969
0
      width = wp->sx;
970
0
    } else if (wp->xoff < ctx->ox &&
971
0
        wp->xoff + wp->sx > ctx->ox + ctx->sx) {
972
      /* Both left and right not visible. */
973
0
      i = ctx->ox;
974
0
      x = 0;
975
0
      width = ctx->sx;
976
0
    } else if (wp->xoff < ctx->ox) {
977
      /* Left not visible. */
978
0
      i = ctx->ox - wp->xoff;
979
0
      x = 0;
980
0
      width = wp->sx - i;
981
0
    } else {
982
      /* Right not visible. */
983
0
      i = 0;
984
0
      x = wp->xoff - ctx->ox;
985
0
      width = ctx->sx - x;
986
0
    }
987
0
    log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u",
988
0
        __func__, c->name, wp->id, i, j, x, y, width);
989
990
0
    tty_default_colours(&defaults, wp);
991
0
    tty_draw_line(tty, s, i, j, width, x, y, &defaults, palette);
992
0
  }
993
994
#ifdef ENABLE_SIXEL
995
  tty_draw_images(c, wp, s);
996
#endif
997
0
}
998
999
/* Draw the panes scrollbars */
1000
static void
1001
screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx *ctx)
1002
0
{
1003
0
  struct client   *c = ctx->c;
1004
0
  struct window   *w = c->session->curw->window;
1005
0
  struct window_pane  *wp;
1006
1007
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
1008
1009
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
1010
0
    if (window_pane_show_scrollbar(wp, ctx->pane_scrollbars) &&
1011
0
        window_pane_visible(wp))
1012
0
      screen_redraw_draw_pane_scrollbar(ctx, wp);
1013
0
  }
1014
0
}
1015
1016
/* Draw pane scrollbar. */
1017
void
1018
screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx *ctx,
1019
    struct window_pane *wp)
1020
0
{
1021
0
  struct screen *s = wp->screen;
1022
0
  double     percent_view;
1023
0
  u_int    sb = ctx->pane_scrollbars, total_height, sb_h = wp->sy;
1024
0
  u_int    sb_pos = ctx->pane_scrollbars_pos, slider_h, slider_y;
1025
0
  int    sb_w = wp->scrollbar_style.width;
1026
0
  int    sb_pad = wp->scrollbar_style.pad;
1027
0
  int    cm_y, cm_size, xoff = wp->xoff, ox = ctx->ox;
1028
0
  int    sb_x, sb_y = (int)(wp->yoff - ctx->oy); /* sb top */
1029
1030
0
  if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE) {
1031
0
    if (sb == PANE_SCROLLBARS_MODAL)
1032
0
      return;
1033
    /* Show slider at the bottom of the scrollbar. */
1034
0
    total_height = screen_size_y(s) + screen_hsize(s);
1035
0
    percent_view = (double)sb_h / total_height;
1036
0
    slider_h = (double)sb_h * percent_view;
1037
0
    slider_y = sb_h - slider_h;
1038
0
  } else {
1039
0
    if (TAILQ_FIRST(&wp->modes) == NULL)
1040
0
      return;
1041
0
    if (window_copy_get_current_offset(wp, &cm_y, &cm_size) == 0)
1042
0
      return;
1043
0
    total_height = cm_size + sb_h;
1044
0
    percent_view = (double)sb_h / (cm_size + sb_h);
1045
0
    slider_h = (double)sb_h * percent_view;
1046
0
    slider_y = (sb_h + 1) * ((double)cm_y / total_height);
1047
0
  }
1048
1049
0
  if (sb_pos == PANE_SCROLLBARS_LEFT)
1050
0
    sb_x = xoff - sb_w - sb_pad - ox;
1051
0
  else
1052
0
    sb_x = xoff + wp->sx - ox;
1053
1054
0
  if (slider_h < 1)
1055
0
    slider_h = 1;
1056
0
  if (slider_y >= sb_h)
1057
0
    slider_y = sb_h - 1;
1058
1059
0
  screen_redraw_draw_scrollbar(ctx, wp, sb_pos, sb_x, sb_y, sb_h,
1060
0
      slider_h, slider_y);
1061
1062
  /* Store current position and height of the slider */
1063
0
  wp->sb_slider_y = slider_y;  /* top of slider y pos in scrollbar */
1064
0
  wp->sb_slider_h = slider_h;  /* height of slider */
1065
0
}
1066
1067
static void
1068
screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx,
1069
    struct window_pane *wp, int sb_pos, int sb_x, int sb_y, u_int sb_h,
1070
    u_int slider_h, u_int slider_y)
1071
0
{
1072
0
  struct client   *c = ctx->c;
1073
0
  struct tty    *tty = &c->tty;
1074
0
  struct grid_cell   gc, slgc, *gcp;
1075
0
  struct style    *sb_style = &wp->scrollbar_style;
1076
0
  u_int      i, j, imax, jmax;
1077
0
  u_int      sb_w = sb_style->width, sb_pad = sb_style->pad;
1078
0
  int      px, py, ox = ctx->ox, oy = ctx->oy;
1079
0
  int      sx = ctx->sx, sy = ctx->sy, xoff = wp->xoff;
1080
0
  int      yoff = wp->yoff;
1081
1082
0
  if (ctx->statustop) {
1083
0
    sb_y += ctx->statuslines;
1084
0
    sy += ctx->statuslines;
1085
0
  }
1086
1087
  /* Set up style for slider. */
1088
0
  gc = sb_style->gc;
1089
0
  memcpy(&slgc, &gc, sizeof slgc);
1090
0
  slgc.fg = gc.bg;
1091
0
  slgc.bg = gc.fg;
1092
1093
0
  imax = sb_w + sb_pad;
1094
0
  if ((int)imax + sb_x > sx)
1095
0
    imax = sx - sb_x;
1096
0
  jmax = sb_h;
1097
0
  if ((int)jmax + sb_y > sy)
1098
0
    jmax = sy - sb_y;
1099
1100
0
  for (j = 0; j < jmax; j++) {
1101
0
    py = sb_y + j;
1102
0
    for (i = 0; i < imax; i++) {
1103
0
      px = sb_x + i;
1104
0
      if (px < xoff - ox - (int)sb_w - (int)sb_pad ||
1105
0
          px >= sx || px < 0 ||
1106
0
          py < yoff - oy - 1 ||
1107
0
          py >= sy || py < 0)
1108
0
        continue;
1109
0
      tty_cursor(tty, px, py);
1110
0
      if ((sb_pos == PANE_SCROLLBARS_LEFT &&
1111
0
          i >= sb_w && i < sb_w + sb_pad) ||
1112
0
          (sb_pos == PANE_SCROLLBARS_RIGHT &&
1113
0
           i < sb_pad)) {
1114
0
        tty_cell(tty, &grid_default_cell,
1115
0
            &grid_default_cell, NULL, NULL);
1116
0
      } else {
1117
0
        if (j >= slider_y && j < slider_y + slider_h)
1118
0
          gcp = &slgc;
1119
0
        else
1120
0
          gcp = &gc;
1121
0
        tty_cell(tty, gcp, &grid_default_cell, NULL,
1122
            NULL);
1123
0
      }
1124
0
    }
1125
0
  }
1126
0
}