Coverage Report

Created: 2023-06-07 06:04

/src/tmux/screen-redraw.c
Line
Count
Source (jump to first uncovered line)
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
34
0
#define START_ISOLATE "\342\201\246"
35
0
#define END_ISOLATE   "\342\201\251"
36
37
/* Border in relation to a pane. */
38
enum screen_redraw_border_type {
39
  SCREEN_REDRAW_OUTSIDE,
40
  SCREEN_REDRAW_INSIDE,
41
  SCREEN_REDRAW_BORDER_LEFT,
42
  SCREEN_REDRAW_BORDER_RIGHT,
43
  SCREEN_REDRAW_BORDER_TOP,
44
  SCREEN_REDRAW_BORDER_BOTTOM
45
};
46
0
#define BORDER_MARKERS "  +,.-"
47
48
/* Get cell border character. */
49
static void
50
screen_redraw_border_set(struct window *w, struct window_pane *wp,
51
    enum pane_lines pane_lines, int cell_type, struct grid_cell *gc)
52
0
{
53
0
  u_int idx;
54
55
0
  if (cell_type == CELL_OUTSIDE && w->fill_character != NULL) {
56
0
    utf8_copy(&gc->data, &w->fill_character[0]);
57
0
    return;
58
0
  }
59
60
0
  switch (pane_lines) {
61
0
  case PANE_LINES_NUMBER:
62
0
    if (cell_type == CELL_OUTSIDE) {
63
0
      gc->attr |= GRID_ATTR_CHARSET;
64
0
      utf8_set(&gc->data, CELL_BORDERS[CELL_OUTSIDE]);
65
0
      break;
66
0
    }
67
0
    gc->attr &= ~GRID_ATTR_CHARSET;
68
0
    if (wp != NULL && window_pane_index(wp, &idx) == 0)
69
0
      utf8_set(&gc->data, '0' + (idx % 10));
70
0
    else
71
0
      utf8_set(&gc->data, '*');
72
0
    break;
73
0
  case PANE_LINES_DOUBLE:
74
0
    gc->attr &= ~GRID_ATTR_CHARSET;
75
0
    utf8_copy(&gc->data, tty_acs_double_borders(cell_type));
76
0
    break;
77
0
  case PANE_LINES_HEAVY:
78
0
    gc->attr &= ~GRID_ATTR_CHARSET;
79
0
    utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
80
0
    break;
81
0
  case PANE_LINES_SIMPLE:
82
0
    gc->attr &= ~GRID_ATTR_CHARSET;
83
0
    utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
84
0
    break;
85
0
  default:
86
0
    gc->attr |= GRID_ATTR_CHARSET;
87
0
    utf8_set(&gc->data, CELL_BORDERS[cell_type]);
88
0
    break;
89
0
  }
90
0
}
91
92
/* Return if window has only two panes. */
93
static int
94
screen_redraw_two_panes(struct window *w, int direction)
95
0
{
96
0
  struct window_pane  *wp;
97
98
0
  wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
99
0
  if (wp == NULL)
100
0
    return (0); /* one pane */
101
0
  if (TAILQ_NEXT(wp, entry) != NULL)
102
0
    return (0); /* more than two panes */
103
0
  if (direction == 0 && wp->xoff == 0)
104
0
    return (0);
105
0
  if (direction == 1 && wp->yoff == 0)
106
0
    return (0);
107
0
  return (1);
108
0
}
109
110
/* Check if cell is on the border of a pane. */
111
static enum screen_redraw_border_type
112
screen_redraw_pane_border(struct window_pane *wp, u_int px, u_int py,
113
    int pane_status)
114
0
{
115
0
  struct options  *oo = wp->window->options;
116
0
  int    split = 0;
117
0
  u_int    ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy;
118
119
  /* Inside pane. */
120
0
  if (px >= wp->xoff && px < ex && py >= wp->yoff && py < ey)
121
0
    return (SCREEN_REDRAW_INSIDE);
122
123
  /* Get pane indicator. */
124
0
  switch (options_get_number(oo, "pane-border-indicators")) {
125
0
  case PANE_BORDER_COLOUR:
126
0
  case PANE_BORDER_BOTH:
127
0
    split = 1;
128
0
    break;
129
0
  }
130
131
  /* Left/right borders. */
132
0
  if (pane_status == PANE_STATUS_OFF) {
133
0
    if (screen_redraw_two_panes(wp->window, 0) && split) {
134
0
      if (wp->xoff == 0 && px == wp->sx && py <= wp->sy / 2)
135
0
        return (SCREEN_REDRAW_BORDER_RIGHT);
136
0
      if (wp->xoff != 0 &&
137
0
          px == wp->xoff - 1 &&
138
0
          py > wp->sy / 2)
139
0
        return (SCREEN_REDRAW_BORDER_LEFT);
140
0
    } else {
141
0
      if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
142
0
        if (wp->xoff != 0 && px == wp->xoff - 1)
143
0
          return (SCREEN_REDRAW_BORDER_LEFT);
144
0
        if (px == ex)
145
0
          return (SCREEN_REDRAW_BORDER_RIGHT);
146
0
      }
147
0
    }
148
0
  } else {
149
0
    if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
150
0
      if (wp->xoff != 0 && px == wp->xoff - 1)
151
0
        return (SCREEN_REDRAW_BORDER_LEFT);
152
0
      if (px == ex)
153
0
        return (SCREEN_REDRAW_BORDER_RIGHT);
154
0
    }
155
0
  }
156
157
  /* Top/bottom borders. */
158
0
  if (pane_status == PANE_STATUS_OFF) {
159
0
    if (screen_redraw_two_panes(wp->window, 1) && split) {
160
0
      if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2)
161
0
        return (SCREEN_REDRAW_BORDER_BOTTOM);
162
0
      if (wp->yoff != 0 &&
163
0
          py == wp->yoff - 1 &&
164
0
          px > wp->sx / 2)
165
0
        return (SCREEN_REDRAW_BORDER_TOP);
166
0
    } else {
167
0
      if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
168
0
        if (wp->yoff != 0 && py == wp->yoff - 1)
169
0
          return (SCREEN_REDRAW_BORDER_TOP);
170
0
        if (py == ey)
171
0
          return (SCREEN_REDRAW_BORDER_BOTTOM);
172
0
      }
173
0
    }
174
0
  } else if (pane_status == PANE_STATUS_TOP) {
175
0
    if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
176
0
      if (wp->yoff != 0 && py == wp->yoff - 1)
177
0
        return (SCREEN_REDRAW_BORDER_TOP);
178
0
    }
179
0
  } else {
180
0
    if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
181
0
      if (py == ey)
182
0
        return (SCREEN_REDRAW_BORDER_BOTTOM);
183
0
    }
184
0
  }
185
186
  /* Outside pane. */
187
0
  return (SCREEN_REDRAW_OUTSIDE);
188
0
}
189
190
/* Check if a cell is on a border. */
191
static int
192
screen_redraw_cell_border(struct client *c, u_int px, u_int py, int pane_status)
193
0
{
194
0
  struct window   *w = c->session->curw->window;
195
0
  struct window_pane  *wp;
196
197
  /* Outside the window? */
198
0
  if (px > w->sx || py > w->sy)
199
0
    return (0);
200
201
  /* On the window border? */
202
0
  if (px == w->sx || py == w->sy)
203
0
    return (1);
204
205
  /* Check all the panes. */
206
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
207
0
    if (!window_pane_visible(wp))
208
0
      continue;
209
0
    switch (screen_redraw_pane_border(wp, px, py, pane_status)) {
210
0
    case SCREEN_REDRAW_INSIDE:
211
0
      return (0);
212
0
    case SCREEN_REDRAW_OUTSIDE:
213
0
      break;
214
0
    default:
215
0
      return (1);
216
0
    }
217
0
  }
218
219
0
  return (0);
220
0
}
221
222
/* Work out type of border cell from surrounding cells. */
223
static int
224
screen_redraw_type_of_cell(struct client *c, u_int px, u_int py,
225
    int pane_status)
226
0
{
227
0
  struct window *w = c->session->curw->window;
228
0
  u_int    sx = w->sx, sy = w->sy;
229
0
  int    borders = 0;
230
231
  /* Is this outside the window? */
232
0
  if (px > sx || py > sy)
233
0
    return (CELL_OUTSIDE);
234
235
  /*
236
   * Construct a bitmask of whether the cells to the left (bit 4), right,
237
   * top, and bottom (bit 1) of this cell are borders.
238
   */
239
0
  if (px == 0 || screen_redraw_cell_border(c, px - 1, py, pane_status))
240
0
    borders |= 8;
241
0
  if (px <= sx && screen_redraw_cell_border(c, px + 1, py, pane_status))
242
0
    borders |= 4;
243
0
  if (pane_status == PANE_STATUS_TOP) {
244
0
    if (py != 0 &&
245
0
        screen_redraw_cell_border(c, px, py - 1, pane_status))
246
0
      borders |= 2;
247
0
    if (screen_redraw_cell_border(c, px, py + 1, pane_status))
248
0
      borders |= 1;
249
0
  } else if (pane_status == PANE_STATUS_BOTTOM) {
250
0
    if (py == 0 ||
251
0
        screen_redraw_cell_border(c, px, py - 1, pane_status))
252
0
      borders |= 2;
253
0
    if (py != sy - 1 &&
254
0
        screen_redraw_cell_border(c, px, py + 1, pane_status))
255
0
      borders |= 1;
256
0
  } else {
257
0
    if (py == 0 ||
258
0
        screen_redraw_cell_border(c, px, py - 1, pane_status))
259
0
      borders |= 2;
260
0
    if (screen_redraw_cell_border(c, px, py + 1, pane_status))
261
0
      borders |= 1;
262
0
  }
263
264
  /*
265
   * Figure out what kind of border this cell is. Only one bit set
266
   * doesn't make sense (can't have a border cell with no others
267
   * connected).
268
   */
269
0
  switch (borders) {
270
0
  case 15:  /* 1111, left right top bottom */
271
0
    return (CELL_JOIN);
272
0
  case 14:  /* 1110, left right top */
273
0
    return (CELL_BOTTOMJOIN);
274
0
  case 13:  /* 1101, left right bottom */
275
0
    return (CELL_TOPJOIN);
276
0
  case 12:  /* 1100, left right */
277
0
    return (CELL_LEFTRIGHT);
278
0
  case 11:  /* 1011, left top bottom */
279
0
    return (CELL_RIGHTJOIN);
280
0
  case 10:  /* 1010, left top */
281
0
    return (CELL_BOTTOMRIGHT);
282
0
  case 9:   /* 1001, left bottom */
283
0
    return (CELL_TOPRIGHT);
284
0
  case 7:   /* 0111, right top bottom */
285
0
    return (CELL_LEFTJOIN);
286
0
  case 6:   /* 0110, right top */
287
0
    return (CELL_BOTTOMLEFT);
288
0
  case 5:   /* 0101, right bottom */
289
0
    return (CELL_TOPLEFT);
290
0
  case 3:   /* 0011, top bottom */
291
0
    return (CELL_TOPBOTTOM);
292
0
  }
293
0
  return (CELL_OUTSIDE);
294
0
}
295
296
/* Check if cell inside a pane. */
297
static int
298
screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status,
299
    struct window_pane **wpp)
300
0
{
301
0
  struct window   *w = c->session->curw->window;
302
0
  struct window_pane  *wp, *active;
303
0
  int      border;
304
0
  u_int      right, line;
305
306
0
  *wpp = NULL;
307
308
0
  if (px > w->sx || py > w->sy)
309
0
    return (CELL_OUTSIDE);
310
0
  if (px == w->sx || py == w->sy) /* window border */
311
0
    return (screen_redraw_type_of_cell(c, px, py, pane_status));
312
313
0
  if (pane_status != PANE_STATUS_OFF) {
314
0
    active = wp = server_client_get_pane(c);
315
0
    do {
316
0
      if (!window_pane_visible(wp))
317
0
        goto next1;
318
319
0
      if (pane_status == PANE_STATUS_TOP)
320
0
        line = wp->yoff - 1;
321
0
      else
322
0
        line = wp->yoff + wp->sy;
323
0
      right = wp->xoff + 2 + wp->status_size - 1;
324
325
0
      if (py == line && px >= wp->xoff + 2 && px <= right)
326
0
        return (CELL_INSIDE);
327
328
0
    next1:
329
0
      wp = TAILQ_NEXT(wp, entry);
330
0
      if (wp == NULL)
331
0
        wp = TAILQ_FIRST(&w->panes);
332
0
    } while (wp != active);
333
0
  }
334
335
0
  active = wp = server_client_get_pane(c);
336
0
  do {
337
0
    if (!window_pane_visible(wp))
338
0
      goto next2;
339
0
    *wpp = wp;
340
341
    /*
342
     * If definitely inside, return. If not on border, skip.
343
     * Otherwise work out the cell.
344
     */
345
0
    border = screen_redraw_pane_border(wp, px, py, pane_status);
346
0
    if (border == SCREEN_REDRAW_INSIDE)
347
0
      return (CELL_INSIDE);
348
0
    if (border == SCREEN_REDRAW_OUTSIDE)
349
0
      goto next2;
350
0
    return (screen_redraw_type_of_cell(c, px, py, pane_status));
351
352
0
  next2:
353
0
    wp = TAILQ_NEXT(wp, entry);
354
0
    if (wp == NULL)
355
0
      wp = TAILQ_FIRST(&w->panes);
356
0
  } while (wp != active);
357
358
0
  return (CELL_OUTSIDE);
359
0
}
360
361
/* Check if the border of a particular pane. */
362
static int
363
screen_redraw_check_is(u_int px, u_int py, int pane_status,
364
    struct window_pane *wp)
365
0
{
366
0
  enum screen_redraw_border_type  border;
367
368
0
  border = screen_redraw_pane_border(wp, px, py, pane_status);
369
0
  if (border != SCREEN_REDRAW_INSIDE && border != SCREEN_REDRAW_OUTSIDE)
370
0
    return (1);
371
0
  return (0);
372
0
}
373
374
/* Update pane status. */
375
static int
376
screen_redraw_make_pane_status(struct client *c, struct window_pane *wp,
377
    struct screen_redraw_ctx *rctx, enum pane_lines pane_lines)
378
0
{
379
0
  struct window   *w = wp->window;
380
0
  struct grid_cell   gc;
381
0
  const char    *fmt;
382
0
  struct format_tree  *ft;
383
0
  char      *expanded;
384
0
  int      pane_status = rctx->pane_status;
385
0
  u_int      width, i, cell_type, px, py;
386
0
  struct screen_write_ctx  ctx;
387
0
  struct screen    old;
388
389
0
  ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
390
0
  format_defaults(ft, c, c->session, c->session->curw, wp);
391
392
0
  if (wp == server_client_get_pane(c))
393
0
    style_apply(&gc, w->options, "pane-active-border-style", ft);
394
0
  else
395
0
    style_apply(&gc, w->options, "pane-border-style", ft);
396
0
  fmt = options_get_string(wp->options, "pane-border-format");
397
398
0
  expanded = format_expand_time(ft, fmt);
399
0
  if (wp->sx < 4)
400
0
    wp->status_size = width = 0;
401
0
  else
402
0
    wp->status_size = width = wp->sx - 4;
403
404
0
  memcpy(&old, &wp->status_screen, sizeof old);
405
0
  screen_init(&wp->status_screen, width, 1, 0);
406
0
  wp->status_screen.mode = 0;
407
408
0
  screen_write_start(&ctx, &wp->status_screen);
409
410
0
  for (i = 0; i < width; i++) {
411
0
    px = wp->xoff + 2 + i;
412
0
    if (rctx->pane_status == PANE_STATUS_TOP)
413
0
      py = wp->yoff - 1;
414
0
    else
415
0
      py = wp->yoff + wp->sy;
416
0
    cell_type = screen_redraw_type_of_cell(c, px, py, pane_status);
417
0
    screen_redraw_border_set(w, wp, pane_lines, cell_type, &gc);
418
0
    screen_write_cell(&ctx, &gc);
419
0
  }
420
0
  gc.attr &= ~GRID_ATTR_CHARSET;
421
422
0
  screen_write_cursormove(&ctx, 0, 0, 0);
423
0
  format_draw(&ctx, &gc, width, expanded, NULL, 0);
424
0
  screen_write_stop(&ctx);
425
426
0
  free(expanded);
427
0
  format_free(ft);
428
429
0
  if (grid_compare(wp->status_screen.grid, old.grid) == 0) {
430
0
    screen_free(&old);
431
0
    return (0);
432
0
  }
433
0
  screen_free(&old);
434
0
  return (1);
435
0
}
436
437
/* Draw pane status. */
438
static void
439
screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
440
0
{
441
0
  struct client   *c = ctx->c;
442
0
  struct window   *w = c->session->curw->window;
443
0
  struct tty    *tty = &c->tty;
444
0
  struct window_pane  *wp;
445
0
  struct screen   *s;
446
0
  u_int      i, x, width, xoff, yoff, size;
447
448
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
449
450
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
451
0
    if (!window_pane_visible(wp))
452
0
      continue;
453
0
    s = &wp->status_screen;
454
455
0
    size = wp->status_size;
456
0
    if (ctx->pane_status == PANE_STATUS_TOP)
457
0
      yoff = wp->yoff - 1;
458
0
    else
459
0
      yoff = wp->yoff + wp->sy;
460
0
    xoff = wp->xoff + 2;
461
462
0
    if (xoff + size <= ctx->ox ||
463
0
        xoff >= ctx->ox + ctx->sx ||
464
0
        yoff < ctx->oy ||
465
0
        yoff >= ctx->oy + ctx->sy)
466
0
      continue;
467
468
0
    if (xoff >= ctx->ox && xoff + size <= ctx->ox + ctx->sx) {
469
      /* All visible. */
470
0
      i = 0;
471
0
      x = xoff - ctx->ox;
472
0
      width = size;
473
0
    } else if (xoff < ctx->ox && xoff + size > ctx->ox + ctx->sx) {
474
      /* Both left and right not visible. */
475
0
      i = ctx->ox;
476
0
      x = 0;
477
0
      width = ctx->sx;
478
0
    } else if (xoff < ctx->ox) {
479
      /* Left not visible. */
480
0
      i = ctx->ox - xoff;
481
0
      x = 0;
482
0
      width = size - i;
483
0
    } else {
484
      /* Right not visible. */
485
0
      i = 0;
486
0
      x = xoff - ctx->ox;
487
0
      width = size - x;
488
0
    }
489
490
0
    if (ctx->statustop)
491
0
      yoff += ctx->statuslines;
492
0
    tty_draw_line(tty, s, i, 0, width, x, yoff - ctx->oy,
493
0
        &grid_default_cell, NULL);
494
0
  }
495
0
  tty_cursor(tty, 0, 0);
496
0
}
497
498
/* Update status line and change flags if unchanged. */
499
static int
500
screen_redraw_update(struct client *c, int flags)
501
0
{
502
0
  struct window     *w = c->session->curw->window;
503
0
  struct window_pane    *wp;
504
0
  struct options      *wo = w->options;
505
0
  int        redraw;
506
0
  enum pane_lines      lines;
507
0
  struct screen_redraw_ctx   ctx;
508
509
0
  if (c->message_string != NULL)
510
0
    redraw = status_message_redraw(c);
511
0
  else if (c->prompt_string != NULL)
512
0
    redraw = status_prompt_redraw(c);
513
0
  else
514
0
    redraw = status_redraw(c);
515
0
  if (!redraw && (~flags & CLIENT_REDRAWSTATUSALWAYS))
516
0
    flags &= ~CLIENT_REDRAWSTATUS;
517
518
0
  if (c->overlay_draw != NULL)
519
0
    flags |= CLIENT_REDRAWOVERLAY;
520
521
0
  if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) {
522
0
    screen_redraw_set_context(c, &ctx);
523
0
    lines = options_get_number(wo, "pane-border-lines");
524
0
    redraw = 0;
525
0
    TAILQ_FOREACH(wp, &w->panes, entry) {
526
0
      if (screen_redraw_make_pane_status(c, wp, &ctx, lines))
527
0
        redraw = 1;
528
0
    }
529
0
    if (redraw)
530
0
      flags |= CLIENT_REDRAWBORDERS;
531
0
  }
532
0
  return (flags);
533
0
}
534
535
/* Set up redraw context. */
536
static void
537
screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx)
538
0
{
539
0
  struct session  *s = c->session;
540
0
  struct options  *oo = s->options;
541
0
  struct window *w = s->curw->window;
542
0
  struct options  *wo = w->options;
543
0
  u_int    lines;
544
545
0
  memset(ctx, 0, sizeof *ctx);
546
0
  ctx->c = c;
547
548
0
  lines = status_line_size(c);
549
0
  if (c->message_string != NULL || c->prompt_string != NULL)
550
0
    lines = (lines == 0) ? 1 : lines;
551
0
  if (lines != 0 && options_get_number(oo, "status-position") == 0)
552
0
    ctx->statustop = 1;
553
0
  ctx->statuslines = lines;
554
555
0
  ctx->pane_status = options_get_number(wo, "pane-border-status");
556
0
  ctx->pane_lines = options_get_number(wo, "pane-border-lines");
557
558
0
  tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy);
559
560
0
  log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name,
561
0
      w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->statuslines,
562
0
      ctx->statustop);
563
0
}
564
565
/* Redraw entire screen. */
566
void
567
screen_redraw_screen(struct client *c)
568
0
{
569
0
  struct screen_redraw_ctx  ctx;
570
0
  int       flags;
571
572
0
  if (c->flags & CLIENT_SUSPENDED)
573
0
    return;
574
575
0
  flags = screen_redraw_update(c, c->flags);
576
0
  if ((flags & CLIENT_ALLREDRAWFLAGS) == 0)
577
0
    return;
578
579
0
  screen_redraw_set_context(c, &ctx);
580
0
  tty_sync_start(&c->tty);
581
0
  tty_update_mode(&c->tty, c->tty.mode, NULL);
582
583
0
  if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) {
584
0
    log_debug("%s: redrawing borders", c->name);
585
0
    if (ctx.pane_status != PANE_STATUS_OFF)
586
0
      screen_redraw_draw_pane_status(&ctx);
587
0
    screen_redraw_draw_borders(&ctx);
588
0
  }
589
0
  if (flags & CLIENT_REDRAWWINDOW) {
590
0
    log_debug("%s: redrawing panes", c->name);
591
0
    screen_redraw_draw_panes(&ctx);
592
0
  }
593
0
  if (ctx.statuslines != 0 &&
594
0
      (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) {
595
0
    log_debug("%s: redrawing status", c->name);
596
0
    screen_redraw_draw_status(&ctx);
597
0
  }
598
0
  if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) {
599
0
    log_debug("%s: redrawing overlay", c->name);
600
0
    c->overlay_draw(c, c->overlay_data, &ctx);
601
0
  }
602
603
0
  tty_reset(&c->tty);
604
0
}
605
606
/* Redraw a single pane. */
607
void
608
screen_redraw_pane(struct client *c, struct window_pane *wp)
609
0
{
610
0
  struct screen_redraw_ctx   ctx;
611
612
0
  if (!window_pane_visible(wp))
613
0
    return;
614
615
0
  screen_redraw_set_context(c, &ctx);
616
0
  tty_sync_start(&c->tty);
617
0
  tty_update_mode(&c->tty, c->tty.mode, NULL);
618
619
0
  screen_redraw_draw_pane(&ctx, wp);
620
621
0
  tty_reset(&c->tty);
622
0
}
623
624
/* Get border cell style. */
625
static const struct grid_cell *
626
screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x,
627
    u_int y, struct window_pane *wp)
628
0
{
629
0
  struct client   *c = ctx->c;
630
0
  struct session    *s = c->session;
631
0
  struct window   *w = s->curw->window;
632
0
  struct window_pane  *active = server_client_get_pane(c);
633
0
  struct options    *oo = w->options;
634
0
  struct format_tree  *ft;
635
636
0
  if (wp->border_gc_set)
637
0
    return (&wp->border_gc);
638
0
  wp->border_gc_set = 1;
639
640
0
  ft = format_create_defaults(NULL, c, s, s->curw, wp);
641
0
  if (screen_redraw_check_is(x, y, ctx->pane_status, active))
642
0
    style_apply(&wp->border_gc, oo, "pane-active-border-style", ft);
643
0
  else
644
0
    style_apply(&wp->border_gc, oo, "pane-border-style", ft);
645
0
  format_free(ft);
646
647
0
  return (&wp->border_gc);
648
0
}
649
650
/* Draw a border cell. */
651
static void
652
screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
653
0
{
654
0
  struct client   *c = ctx->c;
655
0
  struct session    *s = c->session;
656
0
  struct window   *w = s->curw->window;
657
0
  struct options    *oo = w->options;
658
0
  struct tty    *tty = &c->tty;
659
0
  struct format_tree  *ft;
660
0
  struct window_pane  *wp, *active = server_client_get_pane(c);
661
0
  struct grid_cell   gc;
662
0
  const struct grid_cell  *tmp;
663
0
  struct overlay_ranges  r;
664
0
  u_int      cell_type, x = ctx->ox + i, y = ctx->oy + j;
665
0
  int      arrows = 0, border;
666
0
  int      pane_status = ctx->pane_status, isolates;
667
668
0
  if (c->overlay_check != NULL) {
669
0
    c->overlay_check(c, c->overlay_data, x, y, 1, &r);
670
0
    if (r.nx[0] + r.nx[1] == 0)
671
0
      return;
672
0
  }
673
674
0
  cell_type = screen_redraw_check_cell(c, x, y, pane_status, &wp);
675
0
  if (cell_type == CELL_INSIDE)
676
0
    return;
677
678
0
  if (wp == NULL) {
679
0
    if (!ctx->no_pane_gc_set) {
680
0
      ft = format_create_defaults(NULL, c, s, s->curw, NULL);
681
0
      memcpy(&ctx->no_pane_gc, &grid_default_cell, sizeof gc);
682
0
      style_add(&ctx->no_pane_gc, oo, "pane-border-style",
683
0
          ft);
684
0
      format_free(ft);
685
0
      ctx->no_pane_gc_set = 1;
686
0
    }
687
0
    memcpy(&gc, &ctx->no_pane_gc, sizeof gc);
688
0
  } else {
689
0
    tmp = screen_redraw_draw_borders_style(ctx, x, y, wp);
690
0
    if (tmp == NULL)
691
0
      return;
692
0
    memcpy(&gc, tmp, sizeof gc);
693
694
0
    if (server_is_marked(s, s->curw, marked_pane.wp) &&
695
0
        screen_redraw_check_is(x, y, pane_status, marked_pane.wp))
696
0
      gc.attr ^= GRID_ATTR_REVERSE;
697
0
  }
698
0
  screen_redraw_border_set(w, wp, ctx->pane_lines, cell_type, &gc);
699
700
0
  if (cell_type == CELL_TOPBOTTOM &&
701
0
      (c->flags & CLIENT_UTF8) &&
702
0
      tty_term_has(tty->term, TTYC_BIDI))
703
0
    isolates = 1;
704
0
  else
705
0
    isolates = 0;
706
707
0
  if (ctx->statustop)
708
0
    tty_cursor(tty, i, ctx->statuslines + j);
709
0
  else
710
0
    tty_cursor(tty, i, j);
711
0
  if (isolates)
712
0
    tty_puts(tty, END_ISOLATE);
713
714
0
  switch (options_get_number(oo, "pane-border-indicators")) {
715
0
  case PANE_BORDER_ARROWS:
716
0
  case PANE_BORDER_BOTH:
717
0
    arrows = 1;
718
0
    break;
719
0
  }
720
721
0
  if (wp != NULL && arrows) {
722
0
    border = screen_redraw_pane_border(active, x, y, pane_status);
723
0
    if (((i == wp->xoff + 1 &&
724
0
        (cell_type == CELL_LEFTRIGHT ||
725
0
        (cell_type == CELL_TOPJOIN &&
726
0
        border == SCREEN_REDRAW_BORDER_BOTTOM) ||
727
0
        (cell_type == CELL_BOTTOMJOIN &&
728
0
        border == SCREEN_REDRAW_BORDER_TOP))) ||
729
0
        (j == wp->yoff + 1 &&
730
0
        (cell_type == CELL_TOPBOTTOM ||
731
0
        (cell_type == CELL_LEFTJOIN &&
732
0
        border == SCREEN_REDRAW_BORDER_RIGHT) ||
733
0
        (cell_type == CELL_RIGHTJOIN &&
734
0
        border == SCREEN_REDRAW_BORDER_LEFT)))) &&
735
0
        screen_redraw_check_is(x, y, pane_status, active)) {
736
0
      gc.attr |= GRID_ATTR_CHARSET;
737
0
      utf8_set(&gc.data, BORDER_MARKERS[border]);
738
0
    }
739
0
  }
740
741
0
  tty_cell(tty, &gc, &grid_default_cell, NULL, NULL);
742
0
  if (isolates)
743
0
    tty_puts(tty, START_ISOLATE);
744
0
}
745
746
/* Draw the borders. */
747
static void
748
screen_redraw_draw_borders(struct screen_redraw_ctx *ctx)
749
0
{
750
0
  struct client   *c = ctx->c;
751
0
  struct session    *s = c->session;
752
0
  struct window   *w = s->curw->window;
753
0
  struct window_pane  *wp;
754
0
  u_int      i, j;
755
756
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
757
758
0
  TAILQ_FOREACH(wp, &w->panes, entry)
759
0
    wp->border_gc_set = 0;
760
761
0
  for (j = 0; j < c->tty.sy - ctx->statuslines; j++) {
762
0
    for (i = 0; i < c->tty.sx; i++)
763
0
      screen_redraw_draw_borders_cell(ctx, i, j);
764
0
  }
765
0
}
766
767
/* Draw the panes. */
768
static void
769
screen_redraw_draw_panes(struct screen_redraw_ctx *ctx)
770
0
{
771
0
  struct client   *c = ctx->c;
772
0
  struct window   *w = c->session->curw->window;
773
0
  struct window_pane  *wp;
774
775
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
776
777
0
  TAILQ_FOREACH(wp, &w->panes, entry) {
778
0
    if (window_pane_visible(wp))
779
0
      screen_redraw_draw_pane(ctx, wp);
780
0
  }
781
0
}
782
783
/* Draw the status line. */
784
static void
785
screen_redraw_draw_status(struct screen_redraw_ctx *ctx)
786
0
{
787
0
  struct client *c = ctx->c;
788
0
  struct window *w = c->session->curw->window;
789
0
  struct tty  *tty = &c->tty;
790
0
  struct screen *s = c->status.active;
791
0
  u_int    i, y;
792
793
0
  log_debug("%s: %s @%u", __func__, c->name, w->id);
794
795
0
  if (ctx->statustop)
796
0
    y = 0;
797
0
  else
798
0
    y = c->tty.sy - ctx->statuslines;
799
0
  for (i = 0; i < ctx->statuslines; i++) {
800
0
    tty_draw_line(tty, s, 0, i, UINT_MAX, 0, y + i,
801
0
        &grid_default_cell, NULL);
802
0
  }
803
0
}
804
805
/* Draw one pane. */
806
static void
807
screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
808
0
{
809
0
  struct client   *c = ctx->c;
810
0
  struct window   *w = c->session->curw->window;
811
0
  struct tty    *tty = &c->tty;
812
0
  struct screen   *s = wp->screen;
813
0
  struct colour_palette *palette = &wp->palette;
814
0
  struct grid_cell   defaults;
815
0
  u_int      i, j, top, x, y, width;
816
817
0
  log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id);
818
819
0
  if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx)
820
0
    return;
821
0
  if (ctx->statustop)
822
0
    top = ctx->statuslines;
823
0
  else
824
0
    top = 0;
825
0
  for (j = 0; j < wp->sy; j++) {
826
0
    if (wp->yoff + j < ctx->oy || wp->yoff + j >= ctx->oy + ctx->sy)
827
0
      continue;
828
0
    y = top + wp->yoff + j - ctx->oy;
829
830
0
    if (wp->xoff >= ctx->ox &&
831
0
        wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
832
      /* All visible. */
833
0
      i = 0;
834
0
      x = wp->xoff - ctx->ox;
835
0
      width = wp->sx;
836
0
    } else if (wp->xoff < ctx->ox &&
837
0
        wp->xoff + wp->sx > ctx->ox + ctx->sx) {
838
      /* Both left and right not visible. */
839
0
      i = ctx->ox;
840
0
      x = 0;
841
0
      width = ctx->sx;
842
0
    } else if (wp->xoff < ctx->ox) {
843
      /* Left not visible. */
844
0
      i = ctx->ox - wp->xoff;
845
0
      x = 0;
846
0
      width = wp->sx - i;
847
0
    } else {
848
      /* Right not visible. */
849
0
      i = 0;
850
0
      x = wp->xoff - ctx->ox;
851
0
      width = ctx->sx - x;
852
0
    }
853
0
    log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u",
854
0
        __func__, c->name, wp->id, i, j, x, y, width);
855
856
0
    tty_default_colours(&defaults, wp);
857
0
    tty_draw_line(tty, s, i, j, width, x, y, &defaults, palette);
858
0
  }
859
0
}