Coverage Report

Created: 2026-10-05 06:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/screen-write.c
Line
Count
Source
1
/* $OpenBSD: screen-write.c,v 1.298 2026/10/02 15:20:41 nicm Exp $ */
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 struct screen_write_citem *screen_write_collect_trim(
27
        struct screen_write_ctx *, u_int, u_int, u_int, int *);
28
static void screen_write_collect_insert(struct screen_write_ctx *,
29
        struct screen_write_citem *);
30
static void screen_write_collect_insert_clear(struct screen_write_ctx *,
31
        u_int, u_int, u_int);
32
static void screen_write_collect_clear(struct screen_write_ctx *, u_int,
33
        u_int);
34
static void screen_write_collect_scroll(struct screen_write_ctx *, u_int);
35
static void screen_write_collect_flush(struct screen_write_ctx *, int,
36
        const char *);
37
static int  screen_write_overwrite(struct screen_write_ctx *,
38
        struct grid_cell *, u_int);
39
static int  screen_write_combine(struct screen_write_ctx *,
40
        const struct grid_cell *);
41
static void screen_write_sync_flush_dirty(struct window_pane *);
42
static void screen_write_sync_apply_scroll(struct screen_write_ctx *,
43
        struct tty_ctx *);
44
45
struct screen_write_citem {
46
  u_int       x;
47
  int       wrapped;
48
49
  enum { TEXT, CLEAR }    type;
50
  u_int       used;
51
  u_int       bg;
52
53
  struct grid_cell    gc;
54
55
  TAILQ_ENTRY(screen_write_citem) entry;
56
};
57
struct screen_write_cline {
58
  char        *data;
59
  TAILQ_HEAD(, screen_write_citem) items;
60
};
61
TAILQ_HEAD(, screen_write_citem)  screen_write_citem_freelist =
62
    TAILQ_HEAD_INITIALIZER(screen_write_citem_freelist);
63
64
static struct screen_write_citem *
65
screen_write_get_citem(void)
66
48.4k
{
67
48.4k
    struct screen_write_citem *ci;
68
69
48.4k
    ci = TAILQ_FIRST(&screen_write_citem_freelist);
70
48.4k
    if (ci != NULL) {
71
48.1k
      TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry);
72
48.1k
      memset(ci, 0, sizeof *ci);
73
48.1k
      return (ci);
74
48.1k
    }
75
318
    return (xcalloc(1, sizeof *ci));
76
48.4k
}
77
78
static void
79
screen_write_free_citem(struct screen_write_citem *ci)
80
33.8k
{
81
33.8k
  TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry);
82
33.8k
}
83
84
static void
85
screen_write_offset_timer(__unused int fd, __unused short events, void *data)
86
0
{
87
0
  struct window *w = data;
88
89
0
  tty_update_window_offset(w);
90
0
}
91
92
/* Set cursor position. */
93
static void
94
screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy)
95
90.0k
{
96
90.0k
  struct window_pane  *wp = ctx->wp;
97
90.0k
  struct window   *w;
98
90.0k
  struct screen   *s = ctx->s;
99
90.0k
  struct timeval     tv = { .tv_usec = 10000 };
100
101
90.0k
  if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy)
102
11.4k
    return;
103
104
78.6k
  if (cx != -1) {
105
72.4k
    if ((u_int)cx > screen_size_x(s)) /* allow last column */
106
124
      cx = screen_size_x(s) - 1;
107
72.4k
    s->cx = cx;
108
72.4k
  }
109
78.6k
  if (cy != -1) {
110
27.8k
    if ((u_int)cy > screen_size_y(s) - 1)
111
0
      cy = screen_size_y(s) - 1;
112
27.8k
    s->cy = cy;
113
27.8k
  }
114
115
78.6k
  if (wp == NULL)
116
0
    return;
117
78.6k
  w = wp->window;
118
119
78.6k
  if (!event_initialized(&w->offset_timer))
120
78.6k
    evtimer_set(&w->offset_timer, screen_write_offset_timer, w);
121
78.6k
  if (!evtimer_pending(&w->offset_timer, NULL))
122
78.6k
    evtimer_add(&w->offset_timer, &tv);
123
78.6k
}
124
125
/* Redraw lines. */
126
static void
127
screen_write_redraw_cb(const struct tty_ctx *ttyctx, u_int py, u_int ny)
128
2.75k
{
129
2.75k
  struct window_pane  *wp = ttyctx->arg;
130
2.75k
  int      x0, y0, x1, y1;
131
132
2.75k
  if (wp == NULL)
133
0
    return;
134
135
2.75k
  x0 = wp->xoff;
136
2.75k
  y0 = wp->yoff + (int)py;
137
2.75k
  x1 = x0 + (int)wp->sx;
138
2.75k
  y1 = y0 + (int)ny;
139
2.75k
  if (x0 < 0)
140
0
    x0 = 0;
141
2.75k
  if (y0 < 0)
142
0
    y0 = 0;
143
2.75k
  if (x1 > x0 && y1 > y0)
144
2.75k
    redraw_damage_window(wp->window, x0, y0, x1 - x0, y1 - y0);
145
2.75k
}
146
147
/* Update context for client. */
148
static int
149
screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
150
0
{
151
0
  struct window_pane  *wp = ttyctx->arg;
152
153
0
  if (ttyctx->flags & TTY_CTX_INVISIBLE_PANES) {
154
0
    if (session_has(c->session, wp->window))
155
0
      return (1);
156
0
    return (0);
157
0
  }
158
159
0
  if (c->session->curw->window != wp->window)
160
0
    return (0);
161
0
  if (wp->layout_cell == NULL)
162
0
    return (0);
163
164
0
  if (wp->flags & (PANE_REDRAW|PANE_DROP))
165
0
    return (-1);
166
0
  if (c->flags & CLIENT_REDRAWWINDOW) {
167
    /*
168
     * Redraw is already deferred to redraw the window - redraw this
169
     * one also when that happens.
170
     */
171
0
    log_debug("%s: adding %%%u to deferred redraw", __func__,
172
0
        wp->id);
173
0
    wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR);
174
0
    return (-1);
175
0
  }
176
177
0
  if (tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, &ttyctx->wsx,
178
0
      &ttyctx->wsy))
179
0
    ttyctx->flags |= TTY_CTX_WINDOW_BIGGER;
180
0
  else
181
0
    ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER;
182
183
0
  ttyctx->xoff = ttyctx->rxoff = wp->xoff;
184
0
  ttyctx->yoff = ttyctx->ryoff = wp->yoff;
185
186
0
  if (status_at_line(c) == 0)
187
0
    ttyctx->yoff += status_line_size(c);
188
189
0
  return (1);
190
0
}
191
192
/* Return 1 if a menu or floating pane overlaps this pane. */
193
static int
194
screen_write_pane_is_obscured(struct screen_write_ctx *ctx)
195
38.8k
{
196
38.8k
  struct window_pane  *wp = ctx->wp;
197
38.8k
  struct menu_data  *md;
198
199
38.8k
  if (ctx->wp == NULL)
200
0
    return (0);
201
38.8k
  if (ctx->flags & SCREEN_WRITE_CHECKED_IF_OBSCURED) {
202
35.6k
    if (ctx->flags & SCREEN_WRITE_OBSCURED)
203
0
      return (1);
204
35.6k
    return (0);
205
35.6k
  }
206
3.16k
  ctx->flags |= SCREEN_WRITE_CHECKED_IF_OBSCURED;
207
208
3.16k
  md = wp->window->menu;
209
3.16k
  if (md != NULL &&
210
0
      (int)menu_x(md) < wp->xoff + (int)wp->sx &&
211
0
      (int)(menu_x(md) + menu_width(md)) > wp->xoff &&
212
0
      (int)menu_y(md) < wp->yoff + (int)wp->sy &&
213
0
      (int)(menu_y(md) + menu_height(md)) > wp->yoff) {
214
0
    ctx->flags |= SCREEN_WRITE_OBSCURED;
215
0
    return (1);
216
0
  }
217
218
3.16k
  if (ctx->wp->xoff < 0 ||
219
3.16k
      ctx->wp->yoff < 0 ||
220
3.16k
      ctx->wp->xoff + ctx->wp->sx > ctx->wp->window->sx ||
221
3.16k
      ctx->wp->yoff + ctx->wp->sy > ctx->wp->window->sy) {
222
0
    ctx->flags |= SCREEN_WRITE_OBSCURED;
223
0
    return (1);
224
0
  }
225
226
3.16k
  while ((wp = TAILQ_PREV(wp, window_panes, zentry)) != NULL) {
227
0
    if (window_pane_floating_overlaps(wp, ctx->wp)) {
228
0
      ctx->flags |= SCREEN_WRITE_OBSCURED;
229
0
      return (1);
230
0
    }
231
0
  }
232
3.16k
  return (0);
233
3.16k
}
234
235
/* Allocate dirty lines for this screen size. */
236
static bitstr_t *
237
screen_write_sync_allocate_dirty(struct window_pane *wp, u_int sy)
238
9.20k
{
239
9.20k
  bitstr_t  *bs = wp->sync_dirty;
240
241
9.20k
  if (bs != NULL && wp->sync_dirty_size == sy)
242
8.79k
    return (bs);
243
244
407
  free(bs);
245
407
  bs = wp->sync_dirty = bit_alloc(sy);
246
407
  if (bs == NULL)
247
0
    fatal("bit_alloc failed");
248
407
  if (wp->sync_dirty_size != 0) {
249
0
    bit_nset(bs, 0, sy - 1);
250
0
    wp->sync_scrolled = 0;
251
0
  }
252
407
  wp->sync_dirty_size = sy;
253
407
  return (bs);
254
407
}
255
256
/* Should we draw to the TTY? */
257
static int
258
screen_write_should_draw_lines(struct screen_write_ctx *ctx, u_int y, u_int ny)
259
99.2k
{
260
99.2k
  struct window_pane  *wp = ctx->wp;
261
99.2k
  struct screen   *s = ctx->s;
262
99.2k
  u_int      sy = screen_size_y(s);
263
99.2k
  bitstr_t    *bs;
264
265
99.2k
  if (wp != NULL && (wp->flags & (PANE_REDRAW|PANE_DROP)))
266
0
    return (0);
267
99.2k
  if (s->mode & MODE_SYNC) {
268
8.12k
    if (wp != NULL && y < sy && ny != 0) {
269
8.12k
      if (ny > sy - y)
270
0
        ny = sy - y;
271
8.12k
      bs = screen_write_sync_allocate_dirty(wp, sy);
272
8.12k
      bit_nset(bs, y, y + ny - 1);
273
8.12k
    }
274
8.12k
    return (0);
275
8.12k
  }
276
91.1k
  return (1);
277
99.2k
}
278
279
/* Should we draw this line to the TTY? */
280
static int
281
screen_write_should_draw_line(struct screen_write_ctx *ctx, u_int y)
282
88.0k
{
283
88.0k
  return (screen_write_should_draw_lines(ctx, y, 1));
284
88.0k
}
285
286
/* Set up context for TTY command. */
287
static void
288
screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
289
    int is_sync, int check_obscured)
290
71.1k
{
291
71.1k
  struct screen   *s = ctx->s;
292
71.1k
  struct colour_palette *palette = NULL;
293
294
71.1k
  memset(ttyctx, 0, sizeof *ttyctx);
295
296
71.1k
  ttyctx->s = s;
297
71.1k
  ttyctx->sx = screen_size_x(s);
298
71.1k
  ttyctx->sy = screen_size_y(s);
299
300
71.1k
  ttyctx->ocx = s->cx;
301
71.1k
  ttyctx->ocy = s->cy;
302
71.1k
  ttyctx->orlower = s->rlower;
303
71.1k
  ttyctx->orupper = s->rupper;
304
305
71.1k
  if (check_obscured && screen_write_pane_is_obscured(ctx))
306
0
    ttyctx->flags |= TTY_CTX_PANE_OBSCURED;
307
308
71.1k
  memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults);
309
71.1k
  ttyctx->style_ctx.defaults = &ttyctx->defaults;
310
71.1k
  ttyctx->style_ctx.hyperlinks = ctx->s->hyperlinks;
311
312
71.1k
  if (ctx->init_ctx_cb != NULL) {
313
0
    ctx->init_ctx_cb(ctx, ttyctx);
314
0
    if (ttyctx->style_ctx.palette != NULL) {
315
0
      palette = ttyctx->style_ctx.palette;
316
0
      if (ttyctx->defaults.fg == 8)
317
0
        ttyctx->defaults.fg = palette->fg;
318
0
      if (ttyctx->defaults.bg == 8)
319
0
        ttyctx->defaults.bg = palette->bg;
320
0
    }
321
71.1k
  } else {
322
71.1k
    ttyctx->redraw_cb = screen_write_redraw_cb;
323
71.1k
    if (ctx->wp != NULL) {
324
71.1k
      tty_default_colours(&ttyctx->defaults, ctx->wp,
325
71.1k
          &ttyctx->style_ctx.dim);
326
71.1k
      ttyctx->style_ctx.palette = &ctx->wp->palette;
327
71.1k
      ttyctx->set_client_cb = screen_write_set_client_cb;
328
71.1k
      ttyctx->arg = ctx->wp;
329
71.1k
    }
330
71.1k
  }
331
332
71.1k
  if (~ctx->flags & SCREEN_WRITE_SYNC) {
333
    /*
334
     * For the active pane showing its base screen, only use
335
     * synchronized updates if requested (commands that move the
336
     * cursor); for other panes or a pane in a mode, always use it,
337
     * since the cursor will have to move.
338
     */
339
5.02k
    if (ctx->wp != NULL && (ctx->wp != ctx->wp->window->active ||
340
0
        ctx->wp->screen != &ctx->wp->base))
341
5.02k
      ttyctx->flags |= TTY_CTX_SYNC;
342
0
    else if (is_sync)
343
0
      ttyctx->flags |= TTY_CTX_SYNC;
344
5.02k
    tty_write(tty_cmd_syncstart, ttyctx);
345
5.02k
    ctx->flags |= SCREEN_WRITE_SYNC;
346
5.02k
  }
347
71.1k
}
348
349
/* Make write list. */
350
void
351
screen_write_make_list(struct screen *s)
352
12.8k
{
353
12.8k
  u_int y;
354
355
12.8k
  s->write_list = xcalloc(screen_size_y(s), sizeof *s->write_list);
356
334k
  for (y = 0; y < screen_size_y(s); y++)
357
321k
    TAILQ_INIT(&s->write_list[y].items);
358
12.8k
}
359
360
/* Free write list. */
361
void
362
screen_write_free_list(struct screen *s)
363
12.8k
{
364
12.8k
  struct screen_write_cline *cl;
365
12.8k
  struct screen_write_citem *ci, *ci1;
366
12.8k
  u_int        y;
367
368
334k
  for (y = 0; y < screen_size_y(s); y++) {
369
321k
    cl = &s->write_list[y];
370
321k
    TAILQ_FOREACH_SAFE(ci, &cl->items, entry, ci1) {
371
0
      TAILQ_REMOVE(&cl->items, ci, entry);
372
0
      screen_write_free_citem(ci);
373
0
    }
374
321k
    free(cl->data);
375
321k
  }
376
12.8k
  free(s->write_list);
377
12.8k
}
378
379
/* Set up for writing. */
380
static void
381
screen_write_init(struct screen_write_ctx *ctx, struct screen *s)
382
13.3k
{
383
13.3k
  memset(ctx, 0, sizeof *ctx);
384
385
13.3k
  ctx->s = s;
386
387
13.3k
  if (ctx->s->write_list == NULL)
388
12.1k
    screen_write_make_list(ctx->s);
389
13.3k
  ctx->item = screen_write_get_citem();
390
391
13.3k
  ctx->scrolled = 0;
392
13.3k
  ctx->bg = 8;
393
13.3k
}
394
395
/* Initialize writing with a pane. */
396
void
397
screen_write_start_pane(struct screen_write_ctx *ctx, struct window_pane *wp,
398
    struct screen *s)
399
13.3k
{
400
13.3k
  if (s == NULL)
401
1.12k
    s = wp->screen;
402
13.3k
  screen_write_init(ctx, s);
403
13.3k
  ctx->wp = wp;
404
405
13.3k
  if (log_get_level() != 0) {
406
0
    log_debug("%s: size %ux%u, pane %%%u (at %u,%u)",
407
0
        __func__, screen_size_x(ctx->s), screen_size_y(ctx->s),
408
0
        wp->id, wp->xoff, wp->yoff);
409
0
  }
410
13.3k
}
411
412
/* Initialize writing with a callback. */
413
void
414
screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s,
415
    screen_write_init_ctx_cb cb, void *arg)
416
0
{
417
0
  screen_write_init(ctx, s);
418
419
0
  ctx->init_ctx_cb = cb;
420
0
  ctx->arg = arg;
421
422
0
  if (log_get_level() != 0) {
423
0
    log_debug("%s: size %ux%u, with callback", __func__,
424
0
        screen_size_x(ctx->s), screen_size_y(ctx->s));
425
0
  }
426
0
}
427
428
/* Initialize writing. */
429
void
430
screen_write_start(struct screen_write_ctx *ctx, struct screen *s)
431
0
{
432
0
  screen_write_init(ctx, s);
433
434
0
  if (log_get_level() != 0) {
435
0
    log_debug("%s: size %ux%u, no pane", __func__,
436
0
        screen_size_x(ctx->s), screen_size_y(ctx->s));
437
0
  }
438
0
}
439
440
/* Finish writing. */
441
void
442
screen_write_stop(struct screen_write_ctx *ctx)
443
13.3k
{
444
13.3k
  screen_write_collect_end(ctx);
445
13.3k
  screen_write_collect_flush(ctx, 0, __func__);
446
447
13.3k
  screen_write_free_citem(ctx->item);
448
13.3k
}
449
450
/* Reset screen state. */
451
void
452
screen_write_reset(struct screen_write_ctx *ctx)
453
1.86k
{
454
1.86k
  struct screen *s = ctx->s;
455
456
1.86k
  screen_reset_tabs(s);
457
1.86k
  screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1);
458
459
1.86k
  s->mode = MODE_CURSOR|MODE_WRAP;
460
461
1.86k
  if (options_get_number(global_options, "extended-keys") == 2)
462
0
    s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED;
463
464
1.86k
  screen_write_clearscreen(ctx, 8);
465
1.86k
  screen_write_set_cursor(ctx, 0, 0);
466
1.86k
}
467
468
/* Write character. */
469
void
470
screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
471
    u_char ch)
472
0
{
473
0
  struct grid_cell  gc;
474
475
0
  memcpy(&gc, gcp, sizeof gc);
476
477
0
  utf8_set(&gc.data, ch);
478
0
  screen_write_cell(ctx, &gc);
479
0
}
480
481
/* Calculate string length. */
482
size_t
483
screen_write_strlen(const char *fmt, ...)
484
0
{
485
0
  va_list     ap;
486
0
  char           *msg;
487
0
  struct utf8_data  ud;
488
0
  u_char           *ptr;
489
0
  size_t      left, size = 0;
490
0
  enum utf8_state   more;
491
492
0
  va_start(ap, fmt);
493
0
  xvasprintf(&msg, fmt, ap);
494
0
  va_end(ap);
495
496
0
  ptr = msg;
497
0
  while (*ptr != '\0') {
498
0
    if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) {
499
0
      ptr++;
500
501
0
      left = strlen(ptr);
502
0
      if (left < (size_t)ud.size - 1)
503
0
        break;
504
0
      while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE)
505
0
        ptr++;
506
0
      ptr++;
507
508
0
      if (more == UTF8_DONE)
509
0
        size += ud.width;
510
0
    } else {
511
0
      if (*ptr == '\t' || (*ptr > 0x1f && *ptr < 0x7f))
512
0
        size++;
513
0
      ptr++;
514
0
    }
515
0
  }
516
517
0
  free(msg);
518
0
  return (size);
519
0
}
520
521
/* Write string wrapped over lines. */
522
int
523
screen_write_text(struct screen_write_ctx *ctx, u_int cx, u_int width,
524
    u_int lines, int more, const struct grid_cell *gcp, const char *fmt, ...)
525
0
{
526
0
  struct screen   *s = ctx->s;
527
0
  va_list      ap;
528
0
  char      *tmp;
529
0
  u_int      cy = s->cy, i, end, next, idx = 0, at, left;
530
0
  struct utf8_data  *text;
531
0
  struct grid_cell   gc;
532
533
0
  memcpy(&gc, gcp, sizeof gc);
534
535
0
  va_start(ap, fmt);
536
0
  xvasprintf(&tmp, fmt, ap);
537
0
  va_end(ap);
538
539
0
  text = utf8_fromcstr(tmp);
540
0
  free(tmp);
541
542
0
  left = (cx + width) - s->cx;
543
0
  for (;;) {
544
    /* Find the end of what can fit on the line. */
545
0
    at = 0;
546
0
    for (end = idx; text[end].size != 0; end++) {
547
0
      if (text[end].size == 1 && text[end].data[0] == '\n')
548
0
        break;
549
0
      if (at + text[end].width > left)
550
0
        break;
551
0
      at += text[end].width;
552
0
    }
553
554
    /*
555
     * If we're on a space, that's the end. If not, walk back to
556
     * try and find one.
557
     */
558
0
    if (text[end].size == 0)
559
0
      next = end;
560
0
    else if (text[end].size == 1 && text[end].data[0] == '\n')
561
0
      next = end + 1;
562
0
    else if (text[end].size == 1 && text[end].data[0] == ' ')
563
0
      next = end + 1;
564
0
    else {
565
0
      for (i = end; i > idx; i--) {
566
0
        if (text[i].size == 1 && text[i].data[0] == ' ')
567
0
          break;
568
0
      }
569
0
      if (i != idx) {
570
0
        next = i + 1;
571
0
        end = i;
572
0
      } else
573
0
        next = end;
574
0
    }
575
576
    /* Print the line. */
577
0
    for (i = idx; i < end; i++) {
578
0
      utf8_copy(&gc.data, &text[i]);
579
0
      screen_write_cell(ctx, &gc);
580
0
    }
581
582
    /* If at the bottom, stop. */
583
0
    idx = next;
584
0
    if (s->cy == cy + lines - 1 || text[idx].size == 0)
585
0
      break;
586
587
0
    screen_write_cursormove(ctx, cx, s->cy + 1, 0);
588
0
    left = width;
589
0
  }
590
591
  /*
592
   * Fail if on the last line and there is more to come or at the end, or
593
   * if the text was not entirely consumed.
594
   */
595
0
  if ((s->cy == cy + lines - 1 && (!more || s->cx == cx + width)) ||
596
0
      text[idx].size != 0) {
597
0
    free(text);
598
0
    return (0);
599
0
  }
600
0
  free(text);
601
602
  /*
603
   * If no more to come, move to the next line. Otherwise, leave on
604
   * the same line (except if at the end).
605
   */
606
0
  if (!more || s->cx == cx + width)
607
0
    screen_write_cursormove(ctx, cx, s->cy + 1, 0);
608
0
  return (1);
609
0
}
610
611
/* Write simple string (no maximum length). */
612
void
613
screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
614
    const char *fmt, ...)
615
0
{
616
0
  va_list ap;
617
618
0
  va_start(ap, fmt);
619
0
  screen_write_vnputs(ctx, -1, gcp, fmt, ap);
620
0
  va_end(ap);
621
0
}
622
623
/* Write string with length limit (-1 for unlimited). */
624
void
625
screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen,
626
    const struct grid_cell *gcp, const char *fmt, ...)
627
0
{
628
0
  va_list ap;
629
630
0
  va_start(ap, fmt);
631
0
  screen_write_vnputs(ctx, maxlen, gcp, fmt, ap);
632
0
  va_end(ap);
633
0
}
634
635
void
636
screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
637
    const struct grid_cell *gcp, const char *fmt, va_list ap)
638
0
{
639
0
  struct grid_cell  gc;
640
0
  struct utf8_data       *ud = &gc.data;
641
0
  char           *msg;
642
0
  u_char           *ptr;
643
0
  size_t      left, size = 0;
644
0
  enum utf8_state   more;
645
646
0
  memcpy(&gc, gcp, sizeof gc);
647
0
  xvasprintf(&msg, fmt, ap);
648
649
0
  ptr = msg;
650
0
  while (*ptr != '\0') {
651
0
    if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) {
652
0
      ptr++;
653
654
0
      left = strlen(ptr);
655
0
      if (left < (size_t)ud->size - 1)
656
0
        break;
657
0
      while ((more = utf8_append(ud, *ptr)) == UTF8_MORE)
658
0
        ptr++;
659
0
      ptr++;
660
661
0
      if (more != UTF8_DONE)
662
0
        continue;
663
0
      if (maxlen > 0 && size + ud->width > (size_t)maxlen) {
664
0
        while (size < (size_t)maxlen) {
665
0
          screen_write_putc(ctx, &gc, ' ');
666
0
          size++;
667
0
        }
668
0
        break;
669
0
      }
670
0
      size += ud->width;
671
0
      screen_write_cell(ctx, &gc);
672
0
    } else {
673
0
      if (maxlen > 0 && size + 1 > (size_t)maxlen)
674
0
        break;
675
676
0
      if (*ptr == '\001')
677
0
        gc.attr ^= GRID_ATTR_CHARSET;
678
0
      else if (*ptr == '\n') {
679
0
        screen_write_linefeed(ctx, 0, 8);
680
0
        screen_write_carriagereturn(ctx);
681
0
      } else if (*ptr == '\t' || (*ptr > 0x1f && *ptr < 0x7f)) {
682
0
        size++;
683
0
        screen_write_putc(ctx, &gc, *ptr);
684
0
      }
685
0
      ptr++;
686
0
    }
687
0
  }
688
689
0
  free(msg);
690
0
}
691
692
/*
693
 * Copy from another screen but without the selection stuff. Assumes the target
694
 * region is already big enough.
695
 */
696
void
697
screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
698
    u_int px, u_int py, u_int nx, u_int ny)
699
0
{
700
0
  struct screen   *s = ctx->s;
701
0
  struct window_pane  *wp = ctx->wp;
702
0
  struct tty_ctx     ttyctx;
703
0
  struct grid   *gd = src->grid;
704
0
  struct grid_line  *gl, *sgl;
705
0
  struct grid_cell   gc;
706
0
  u_int      xx, yy, cx = s->cx, cy = s->cy;
707
0
  int      xoff = 0, yoff = 0;
708
0
  struct visible_ranges *r;
709
710
0
  if (nx == 0 || ny == 0)
711
0
    return;
712
0
  if (wp != NULL) {
713
0
    xoff = wp->xoff;
714
0
    yoff = wp->yoff;
715
0
  }
716
717
0
  for (yy = py; yy < py + ny; yy++) {
718
0
    if (yy >= gd->hsize + gd->sy)
719
0
      break;
720
0
    s->cx = cx;
721
0
    screen_write_initctx(ctx, &ttyctx, 0, 0);
722
0
    r = window_visible_ranges(wp, xoff + s->cx, s->cy + yoff, nx,
723
0
        NULL);
724
0
    for (xx = px; xx < px + nx; xx++) {
725
0
      gl = grid_get_line(gd, yy);
726
0
      sgl = grid_get_line(s->grid, s->cy);
727
0
      if (xx >= gl->cellsize && s->cx >= sgl->cellsize)
728
0
        break;
729
730
0
      grid_get_cell(gd, xx, yy, &gc);
731
0
      if (xx + gc.data.width > px + nx)
732
0
        break;
733
0
      grid_view_set_cell(s->grid, s->cx, s->cy, &gc);
734
735
0
      if (!window_position_is_visible(r, xoff + s->cx))
736
0
        break;
737
0
      ttyctx.cell = &gc;
738
0
      ttyctx.flags &= TTY_CTX_SYNC;
739
0
      tty_write(tty_cmd_cell, &ttyctx);
740
0
      ttyctx.ocx++;
741
742
0
      s->cx++;
743
0
    }
744
0
    s->cy++;
745
0
  }
746
747
0
  s->cx = cx;
748
0
  s->cy = cy;
749
0
}
750
751
/* Select character set for drawing border lines. */
752
static void
753
screen_write_box_border_set(enum box_lines lines, int cell_type,
754
    struct grid_cell *gc)
755
0
{
756
0
  switch (lines) {
757
0
  case BOX_LINES_NONE:
758
0
    break;
759
0
  case BOX_LINES_DOUBLE:
760
0
    gc->attr &= ~GRID_ATTR_CHARSET;
761
0
    utf8_copy(&gc->data, tty_acs_double_borders(cell_type));
762
0
    break;
763
0
  case BOX_LINES_HEAVY:
764
0
    gc->attr &= ~GRID_ATTR_CHARSET;
765
0
    utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
766
0
    break;
767
0
  case BOX_LINES_ROUNDED:
768
0
    gc->attr &= ~GRID_ATTR_CHARSET;
769
0
    utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type));
770
0
    break;
771
0
  case BOX_LINES_SIMPLE:
772
0
    gc->attr &= ~GRID_ATTR_CHARSET;
773
0
    utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
774
0
    break;
775
0
  case BOX_LINES_PADDED:
776
0
    gc->attr &= ~GRID_ATTR_CHARSET;
777
0
    utf8_set(&gc->data, PADDED_BORDERS[cell_type]);
778
0
    break;
779
0
  case BOX_LINES_SINGLE:
780
0
  case BOX_LINES_DEFAULT:
781
0
    gc->attr |= GRID_ATTR_CHARSET;
782
0
    utf8_set(&gc->data, CELL_BORDERS[cell_type]);
783
0
    break;
784
0
  }
785
0
}
786
787
/* Draw a horizontal line on screen. */
788
void
789
screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right,
790
   enum box_lines lines, const struct grid_cell *border_gc)
791
0
{
792
0
  struct screen   *s = ctx->s;
793
0
  struct grid_cell   gc;
794
0
  u_int      cx, cy, i;
795
796
0
  cx = s->cx;
797
0
  cy = s->cy;
798
799
0
  if (border_gc != NULL)
800
0
    memcpy(&gc, border_gc, sizeof gc);
801
0
  else
802
0
    memcpy(&gc, &grid_default_cell, sizeof gc);
803
0
  gc.attr |= GRID_ATTR_CHARSET;
804
805
0
  if (left)
806
0
    screen_write_box_border_set(lines, CELL_URD, &gc);
807
0
  else
808
0
    screen_write_box_border_set(lines, CELL_LR, &gc);
809
0
  screen_write_cell(ctx, &gc);
810
811
0
  screen_write_box_border_set(lines, CELL_LR, &gc);
812
0
  for (i = 1; i < nx - 1; i++)
813
0
    screen_write_cell(ctx, &gc);
814
815
0
  if (right)
816
0
    screen_write_box_border_set(lines, CELL_ULD, &gc);
817
0
  else
818
0
    screen_write_box_border_set(lines, CELL_LR, &gc);
819
0
  screen_write_cell(ctx, &gc);
820
821
0
  screen_write_set_cursor(ctx, cx, cy);
822
0
}
823
824
/* Draw a vertical line on screen. */
825
void
826
screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom,
827
    const struct grid_cell *gcp)
828
0
{
829
0
  struct screen   *s = ctx->s;
830
0
  struct grid_cell   gc;
831
0
  u_int      cx, cy, i;
832
833
0
  cx = s->cx;
834
0
  cy = s->cy;
835
836
0
  if (gcp != NULL)
837
0
    memcpy(&gc, gcp, sizeof gc);
838
0
  else
839
0
    memcpy(&gc, &grid_default_cell, sizeof gc);
840
0
  gc.attr |= GRID_ATTR_CHARSET;
841
842
0
  screen_write_putc(ctx, &gc, top ? 'w' : 'x');
843
0
  for (i = 1; i < ny - 1; i++) {
844
0
    screen_write_set_cursor(ctx, cx, cy + i);
845
0
    screen_write_putc(ctx, &gc, 'x');
846
0
  }
847
0
  screen_write_set_cursor(ctx, cx, cy + ny - 1);
848
0
  screen_write_putc(ctx, &gc, bottom ? 'v' : 'x');
849
850
0
  screen_write_set_cursor(ctx, cx, cy);
851
0
}
852
853
/* Draw a menu on screen. */
854
void
855
screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice,
856
    enum box_lines lines, const struct grid_cell *menu_gc,
857
    const struct grid_cell *border_gc, const struct grid_cell *choice_gc)
858
0
{
859
0
  struct screen   *s = ctx->s;
860
0
  struct grid_cell   default_gc;
861
0
  const struct grid_cell  *gc = &default_gc;
862
0
  u_int      border, cx = s->cx, cy = s->cy, i, j, width;
863
0
  const char    *name;
864
865
0
  memcpy(&default_gc, menu_gc, sizeof default_gc);
866
867
0
  if (lines == BOX_LINES_NONE) {
868
0
    border = 0;
869
0
    width = menu->item_width;
870
0
  } else {
871
0
    border = 1;
872
0
    width = menu->width;
873
0
    screen_write_box(ctx, width + 4, menu->count + 2, lines,
874
0
        border_gc, menu->title);
875
0
  }
876
877
0
  for (i = 0; i < menu->count; i++) {
878
0
    name = menu->items[i].name;
879
0
    if (name == NULL) {
880
0
      screen_write_cursormove(ctx, cx, cy + border + i, 0);
881
0
      screen_write_hline(ctx, width + 2 + (2 * border), 1, 1,
882
0
          lines, border_gc);
883
0
      continue;
884
0
    }
885
886
0
    if (choice >= 0 && i == (u_int)choice && *name != '-')
887
0
      gc = choice_gc;
888
889
0
    screen_write_cursormove(ctx, cx + border, cy + border + i, 0);
890
0
    for (j = 0; j < width + 2; j++)
891
0
      screen_write_putc(ctx, gc, ' ');
892
893
0
    screen_write_cursormove(ctx, cx + border + 1, cy + border + i,
894
0
        0);
895
0
    if (*name == '-') {
896
0
      default_gc.attr |= GRID_ATTR_DIM;
897
0
      format_draw(ctx, gc, width, name + 1, NULL, 0);
898
0
      default_gc.attr &= ~GRID_ATTR_DIM;
899
0
      continue;
900
0
    }
901
902
0
    format_draw(ctx, gc, width, name, NULL, 0);
903
0
    gc = &default_gc;
904
0
  }
905
906
0
  screen_write_set_cursor(ctx, cx, cy);
907
0
}
908
909
/* Draw a box on screen. */
910
void
911
screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny,
912
    enum box_lines lines, const struct grid_cell *gcp, const char *title)
913
0
{
914
0
  struct screen   *s = ctx->s;
915
0
  struct grid_cell   gc;
916
0
  u_int      cx, cy, i;
917
918
0
  cx = s->cx;
919
0
  cy = s->cy;
920
921
0
  if (gcp != NULL)
922
0
    memcpy(&gc, gcp, sizeof gc);
923
0
  else
924
0
    memcpy(&gc, &grid_default_cell, sizeof gc);
925
926
0
  gc.attr |= GRID_ATTR_CHARSET;
927
0
  gc.flags |= GRID_FLAG_NOPALETTE;
928
929
  /* Draw top border */
930
0
  screen_write_box_border_set(lines, CELL_RD, &gc);
931
0
  screen_write_cell(ctx, &gc);
932
0
  screen_write_box_border_set(lines, CELL_LR, &gc);
933
0
  for (i = 1; i < nx - 1; i++)
934
0
    screen_write_cell(ctx, &gc);
935
0
  screen_write_box_border_set(lines, CELL_LD, &gc);
936
0
  screen_write_cell(ctx, &gc);
937
938
  /* Draw bottom border */
939
0
  screen_write_set_cursor(ctx, cx, cy + ny - 1);
940
0
  screen_write_box_border_set(lines, CELL_RU, &gc);
941
0
  screen_write_cell(ctx, &gc);
942
0
  screen_write_box_border_set(lines, CELL_LR, &gc);
943
0
  for (i = 1; i < nx - 1; i++)
944
0
    screen_write_cell(ctx, &gc);
945
0
  screen_write_box_border_set(lines, CELL_LU, &gc);
946
0
  screen_write_cell(ctx, &gc);
947
948
  /* Draw sides */
949
0
  screen_write_box_border_set(lines, CELL_UD, &gc);
950
0
  for (i = 1; i < ny - 1; i++) {
951
    /* left side */
952
0
    screen_write_set_cursor(ctx, cx, cy + i);
953
0
    screen_write_cell(ctx, &gc);
954
    /* right side */
955
0
    screen_write_set_cursor(ctx, cx + nx - 1, cy + i);
956
0
    screen_write_cell(ctx, &gc);
957
0
  }
958
959
0
  if (title != NULL) {
960
0
    gc.attr &= ~GRID_ATTR_CHARSET;
961
0
    screen_write_cursormove(ctx, cx + 2, cy, 0);
962
0
    format_draw(ctx, &gc, nx - 4, title, NULL, 0);
963
0
  }
964
965
0
  screen_write_set_cursor(ctx, cx, cy);
966
0
}
967
968
/*
969
 * Write a preview version of a window. Assumes target area is big enough and
970
 * already cleared.
971
 */
972
void
973
screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
974
    u_int ny)
975
0
{
976
0
  struct screen   *s = ctx->s;
977
0
  struct grid_cell   gc;
978
0
  u_int      cx, cy, px, py;
979
980
0
  cx = s->cx;
981
0
  cy = s->cy;
982
983
  /*
984
   * If the cursor is on, pick the area around the cursor, otherwise use
985
   * the top left.
986
   */
987
0
  if (src->mode & MODE_CURSOR) {
988
0
    px = src->cx;
989
0
    if (px < nx / 3)
990
0
      px = 0;
991
0
    else
992
0
      px = px - nx / 3;
993
0
    if (px + nx > screen_size_x(src)) {
994
0
      if (nx > screen_size_x(src))
995
0
        px = 0;
996
0
      else
997
0
        px = screen_size_x(src) - nx;
998
0
    }
999
0
    py = src->cy;
1000
0
    if (py < ny / 3)
1001
0
      py = 0;
1002
0
    else
1003
0
      py = py - ny / 3;
1004
0
    if (py + ny > screen_size_y(src)) {
1005
0
      if (ny > screen_size_y(src))
1006
0
        py = 0;
1007
0
      else
1008
0
        py = screen_size_y(src) - ny;
1009
0
    }
1010
0
  } else {
1011
0
    px = 0;
1012
0
    py = 0;
1013
0
  }
1014
1015
0
  screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny);
1016
1017
0
  if (src->mode & MODE_CURSOR) {
1018
0
    grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
1019
0
    gc.attr |= GRID_ATTR_REVERSE;
1020
0
    screen_write_set_cursor(ctx, cx + (src->cx - px),
1021
0
        cy + (src->cy - py));
1022
0
    screen_write_cell(ctx, &gc);
1023
0
  }
1024
0
}
1025
1026
/* Set a mode. */
1027
void
1028
screen_write_mode_set(struct screen_write_ctx *ctx, int mode)
1029
3.33k
{
1030
3.33k
  struct screen *s = ctx->s;
1031
1032
3.33k
  s->mode |= mode;
1033
1034
3.33k
  if (log_get_level() != 0)
1035
0
    log_debug("%s: %s", __func__, screen_mode_to_string(mode));
1036
3.33k
}
1037
1038
/* Clear a mode. */
1039
void
1040
screen_write_mode_clear(struct screen_write_ctx *ctx, int mode)
1041
3.33k
{
1042
3.33k
  struct screen *s = ctx->s;
1043
1044
3.33k
  s->mode &= ~mode;
1045
1046
3.33k
  if (log_get_level() != 0)
1047
0
    log_debug("%s: %s", __func__, screen_mode_to_string(mode));
1048
3.33k
}
1049
1050
/* Sync timeout callback. */
1051
static void
1052
screen_write_sync_callback(__unused int fd, __unused short events, void *arg)
1053
0
{
1054
0
  struct window_pane  *wp = arg;
1055
1056
0
  log_debug("%s: %%%u sync timer expired", __func__, wp->id);
1057
0
  evtimer_del(&wp->sync_timer);
1058
1059
0
  if (wp->base.mode & MODE_SYNC) {
1060
0
    wp->base.mode &= ~MODE_SYNC;
1061
0
    screen_write_sync_flush_dirty(wp);
1062
0
  }
1063
0
}
1064
1065
/* Start sync mode. */
1066
void
1067
screen_write_start_sync(struct window_pane *wp)
1068
572
{
1069
572
  struct timeval  tv = { .tv_sec = 1, .tv_usec = 0 };
1070
1071
572
  if (wp == NULL)
1072
0
    return;
1073
1074
572
  wp->base.mode |= MODE_SYNC;
1075
572
  if (!event_initialized(&wp->sync_timer))
1076
572
    evtimer_set(&wp->sync_timer, screen_write_sync_callback, wp);
1077
572
  evtimer_add(&wp->sync_timer, &tv);
1078
1079
572
  log_debug("%s: %%%u started sync mode", __func__, wp->id);
1080
572
}
1081
1082
/* Stop sync mode. */
1083
void
1084
screen_write_stop_sync(struct window_pane *wp)
1085
12.3k
{
1086
12.3k
  if (wp == NULL || (~wp->base.mode & MODE_SYNC))
1087
11.9k
    return;
1088
1089
468
  if (event_initialized(&wp->sync_timer))
1090
468
    evtimer_del(&wp->sync_timer);
1091
468
  wp->base.mode &= ~MODE_SYNC;
1092
1093
468
  screen_write_sync_flush_dirty(wp);
1094
1095
468
  log_debug("%s: %%%u stopped sync mode", __func__, wp->id);
1096
468
}
1097
1098
/* Flush pending output before clearing sync mode. */
1099
void
1100
screen_write_end_sync(struct screen_write_ctx *ctx)
1101
250
{
1102
250
  struct window_pane  *wp = ctx->wp;
1103
1104
250
  if (wp == NULL)
1105
0
    return;
1106
250
  if (wp->base.mode & MODE_SYNC)
1107
184
    screen_write_collect_flush(ctx, 0, __func__);
1108
250
  screen_write_stop_sync(wp);
1109
250
}
1110
1111
/* Cursor up by ny. */
1112
void
1113
screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
1114
1.69k
{
1115
1.69k
  struct screen *s = ctx->s;
1116
1.69k
  u_int    cx = s->cx, cy = s->cy;
1117
1118
1.69k
  if (ny == 0)
1119
0
    ny = 1;
1120
1121
1.69k
  if (cy < s->rupper) {
1122
    /* Above region. */
1123
287
    if (ny > cy)
1124
221
      ny = cy;
1125
1.40k
  } else {
1126
    /* Below region. */
1127
1.40k
    if (ny > cy - s->rupper)
1128
1.20k
      ny = cy - s->rupper;
1129
1.40k
  }
1130
1.69k
  if (cx == screen_size_x(s))
1131
67
    cx--;
1132
1133
1.69k
  cy -= ny;
1134
1135
1.69k
  screen_write_set_cursor(ctx, cx, cy);
1136
1.69k
}
1137
1138
/* Cursor down by ny. */
1139
void
1140
screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
1141
2.07k
{
1142
2.07k
  struct screen *s = ctx->s;
1143
2.07k
  u_int    cx = s->cx, cy = s->cy;
1144
1145
2.07k
  if (ny == 0)
1146
0
    ny = 1;
1147
1148
2.07k
  if (cy > s->rlower) {
1149
    /* Below region. */
1150
419
    if (ny > screen_size_y(s) - 1 - cy)
1151
224
      ny = screen_size_y(s) - 1 - cy;
1152
1.65k
  } else {
1153
    /* Above region. */
1154
1.65k
    if (ny > s->rlower - cy)
1155
874
      ny = s->rlower - cy;
1156
1.65k
  }
1157
2.07k
  if (cx == screen_size_x(s))
1158
66
      cx--;
1159
2.00k
  else if (ny == 0)
1160
948
    return;
1161
1162
1.12k
  cy += ny;
1163
1164
1.12k
  screen_write_set_cursor(ctx, cx, cy);
1165
1.12k
}
1166
1167
/* Cursor right by nx. */
1168
void
1169
screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
1170
1.57k
{
1171
1.57k
  struct screen *s = ctx->s;
1172
1.57k
  u_int    cx = s->cx, cy = s->cy;
1173
1174
1.57k
  if (nx == 0)
1175
0
    nx = 1;
1176
1177
1.57k
  if (nx > screen_size_x(s) - 1 - cx)
1178
696
    nx = screen_size_x(s) - 1 - cx;
1179
1.57k
  if (nx == 0)
1180
540
    return;
1181
1182
1.03k
  cx += nx;
1183
1184
1.03k
  screen_write_set_cursor(ctx, cx, cy);
1185
1.03k
}
1186
1187
/* Cursor left by nx. */
1188
void
1189
screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx)
1190
766
{
1191
766
  struct screen *s = ctx->s;
1192
766
  u_int    cx = s->cx, cy = s->cy;
1193
1194
766
  if (nx == 0)
1195
0
    nx = 1;
1196
1197
766
  if (nx > cx)
1198
570
    nx = cx;
1199
766
  if (nx == 0)
1200
568
    return;
1201
1202
198
  cx -= nx;
1203
1204
198
  screen_write_set_cursor(ctx, cx, cy);
1205
198
}
1206
1207
/* Backspace; cursor left unless at start of wrapped line when can move up. */
1208
void
1209
screen_write_backspace(struct screen_write_ctx *ctx)
1210
4.52k
{
1211
4.52k
  struct screen   *s = ctx->s;
1212
4.52k
  struct grid_line  *gl;
1213
4.52k
  u_int      cx = s->cx, cy = s->cy;
1214
1215
4.52k
  if (cx == 0) {
1216
627
    if (cy == 0)
1217
233
      return;
1218
394
    gl = grid_get_line(s->grid, s->grid->hsize + cy - 1);
1219
394
    if (gl->flags & GRID_LINE_WRAPPED) {
1220
194
      cy--;
1221
194
      cx = screen_size_x(s) - 1;
1222
194
    }
1223
394
  } else
1224
3.89k
    cx--;
1225
1226
4.28k
  screen_write_set_cursor(ctx, cx, cy);
1227
4.28k
}
1228
1229
/* Is this cell a single ASCII character? */
1230
static int
1231
screen_write_cell_is_single(const struct grid_cell *gc)
1232
0
{
1233
0
  if (gc->data.width != 1)
1234
0
    return (0);
1235
0
  if (gc->data.size != 1)
1236
0
    return (0);
1237
0
  if (*gc->data.data < 0x20 || *gc->data.data == 0x7f)
1238
0
    return (0);
1239
0
  if (gc->flags & GRID_FLAG_CLEARED)
1240
0
    return (0);
1241
0
  if (gc->flags & GRID_FLAG_PADDING)
1242
0
    return (0);
1243
0
  if (gc->flags & GRID_FLAG_TAB)
1244
0
    return (0);
1245
0
  return (1);
1246
0
}
1247
1248
/* Redraw all visible cells on a line. */
1249
static void
1250
screen_write_redraw_line(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
1251
    u_int yy)
1252
1.31k
{
1253
1.31k
  struct window_pane  *wp = ctx->wp;
1254
1.31k
  struct screen   *s = ctx->s;
1255
1.31k
  struct grid_cell   gc, ngc;
1256
1.31k
  u_int      sx = screen_size_x(s), cx, i;
1257
1.31k
  int      xoff = wp->xoff, yoff = wp->yoff;
1258
1.31k
  struct visible_ranges *r;
1259
1.31k
  struct visible_range  *ri;
1260
1261
1.31k
  r = window_visible_ranges(wp, xoff, yoff + yy, sx, NULL);
1262
2.62k
  for (i = 0; i < r->used; i++) {
1263
1.31k
    ri = &r->ranges[i];
1264
1.31k
    if (ri->nx == 0)
1265
0
      continue;
1266
1267
1.31k
    cx = ri->px - xoff;
1268
1.31k
    if (cx >= sx)
1269
0
      continue;
1270
1.31k
    if (cx + ri->nx > sx)
1271
0
      ttyctx->n = sx - cx;
1272
1.31k
    else
1273
1.31k
      ttyctx->n = ri->nx;
1274
1.31k
    if (ttyctx->n == 0)
1275
0
      continue;
1276
1.31k
    ttyctx->ocx = cx;
1277
1.31k
    ttyctx->ocy = yy;
1278
1279
1.31k
    if (ttyctx->n != 1) {
1280
1.31k
      tty_write(tty_cmd_redrawline, ttyctx);
1281
1.31k
      continue;
1282
1.31k
    }
1283
1284
0
    grid_view_get_cell(s->grid, cx, yy, &gc);
1285
0
    if (!screen_write_cell_is_single(&gc)) {
1286
0
      tty_write(tty_cmd_redrawline, ttyctx);
1287
0
      continue;
1288
0
    }
1289
0
    if (~gc.flags & GRID_FLAG_SELECTED)
1290
0
      ttyctx->cell = &gc;
1291
0
    else {
1292
0
      screen_select_cell(s, &ngc, &gc);
1293
0
      ttyctx->cell = &ngc;
1294
0
    }
1295
0
    tty_write(tty_cmd_cell, ttyctx);
1296
0
  }
1297
1.31k
}
1298
1299
/* Redraw dirty lines. */
1300
static void
1301
screen_write_sync_flush_dirty(struct window_pane *wp)
1302
468
{
1303
468
  struct screen_write_ctx  ctx;
1304
468
  struct tty_ctx     ttyctx;
1305
468
  struct screen   *s = &wp->base;
1306
468
  u_int      y, sy = screen_size_y(s), lines = 0;
1307
1308
468
  if (wp->sync_dirty == NULL)
1309
333
    return;
1310
1311
135
  screen_write_start_pane(&ctx, wp, s);
1312
135
  screen_write_initctx(&ctx, &ttyctx, 1, 1);
1313
1314
135
  if (wp->sync_scrolled != 0)
1315
44
    screen_write_sync_apply_scroll(&ctx, &ttyctx);
1316
1317
135
  if (~wp->flags & PANE_REDRAW) {
1318
3.51k
    for (y = 0; y < sy; y++) {
1319
3.37k
      if (bit_test(wp->sync_dirty, y)) {
1320
568
        screen_write_redraw_line(&ctx, &ttyctx, y);
1321
568
        lines++;
1322
568
      }
1323
3.37k
    }
1324
135
  }
1325
135
  log_debug("%s: %%%u had %u dirty lines", __func__, wp->id, lines);
1326
1327
135
  screen_write_stop(&ctx);
1328
135
  screen_write_sync_clear_dirty(wp);
1329
135
}
1330
1331
/* Clear pending synchronized output. */
1332
void
1333
screen_write_sync_clear_dirty(struct window_pane *wp)
1334
12.2k
{
1335
12.2k
  if (wp != NULL) {
1336
12.2k
    free(wp->sync_dirty);
1337
12.2k
    wp->sync_dirty = NULL;
1338
12.2k
    wp->sync_dirty_size = 0;
1339
12.2k
    wp->sync_scrolled = 0;
1340
12.2k
  }
1341
12.2k
}
1342
1343
/* Defer scrolling until the synchronized update ends. */
1344
static void
1345
screen_write_sync_scroll_dirty(struct screen_write_ctx *ctx)
1346
1.20k
{
1347
1.20k
  struct window_pane  *wp = ctx->wp;
1348
1.20k
  struct screen   *s = ctx->s;
1349
1.20k
  u_int      n = ctx->scrolled, y, sy = screen_size_y(s);
1350
1.20k
  u_int      ry = s->rlower + 1 - s->rupper;
1351
1.20k
  bitstr_t    *bs;
1352
1353
1.20k
  if (n > ry)
1354
78
    n = ry;
1355
1.20k
  if (wp == NULL ||
1356
1.20k
      n == ry ||
1357
1.08k
      s->rlower >= sy ||
1358
1.08k
      window_pane_scrollbar_overlay_visible(wp)) {
1359
123
    screen_write_should_draw_lines(ctx, s->rupper, ry);
1360
123
    return;
1361
123
  }
1362
1.08k
  if (wp->flags & (PANE_REDRAW|PANE_DROP))
1363
0
    return;
1364
1.08k
  bs = screen_write_sync_allocate_dirty(wp, sy);
1365
1366
1.08k
  if (wp->sync_scrolled != 0 &&
1367
684
      (wp->sync_rupper != s->rupper ||
1368
643
      wp->sync_rlower != s->rlower ||
1369
609
      wp->sync_bg != ctx->bg)) {
1370
197
    log_debug("%s: %%%u region changed, redrawing all", __func__,
1371
197
        wp->id);
1372
197
    bit_nset(bs, 0, sy - 1);
1373
197
    wp->sync_scrolled = 0;
1374
197
    return;
1375
197
  }
1376
1377
  /* Keep dirty lines aligned with the scrolled grid. */
1378
16.3k
  for (y = s->rupper; y + n <= s->rlower; y++) {
1379
15.4k
    if (bit_test(bs, y + n))
1380
10.0k
      bit_set(bs, y);
1381
5.42k
    else
1382
5.42k
      bit_clear(bs, y);
1383
15.4k
  }
1384
884
  bit_nset(bs, s->rlower + 1 - n, s->rlower);
1385
1386
884
  wp->sync_rupper = s->rupper;
1387
884
  wp->sync_rlower = s->rlower;
1388
884
  wp->sync_bg = ctx->bg;
1389
884
  wp->sync_scrolled += n;
1390
884
  if (wp->sync_scrolled >= ry) {
1391
116
    bit_nset(bs, s->rupper, s->rlower);
1392
116
    wp->sync_scrolled = 0;
1393
116
  }
1394
884
  window_pane_scrollbar_redraw(wp);
1395
1396
884
  log_debug("%s: %%%u deferred scroll of %u (region %u-%u)", __func__,
1397
884
      wp->id, wp->sync_scrolled, s->rupper, s->rlower);
1398
884
}
1399
1400
/* Redraw the scrolled lines for a client which cannot scroll them. */
1401
static void
1402
screen_write_sync_redraw_cb(const struct tty_ctx *ttyctx, u_int py, u_int ny)
1403
0
{
1404
0
  struct window_pane  *wp = ttyctx->arg;
1405
1406
0
  bit_nset(wp->sync_dirty, py, py + ny - 1);
1407
0
}
1408
1409
/* Send the deferred scroll to the client. */
1410
static void
1411
screen_write_sync_apply_scroll(struct screen_write_ctx *ctx,
1412
    struct tty_ctx *ttyctx)
1413
44
{
1414
44
  struct window_pane  *wp = ctx->wp;
1415
44
  struct screen   *s = ctx->s;
1416
44
  u_int      sy = screen_size_y(s), n = wp->sync_scrolled;
1417
44
  tty_ctx_redraw_cb  redraw_cb;
1418
1419
44
  wp->sync_scrolled = 0;
1420
44
  if (wp->flags & PANE_REDRAW)
1421
0
    return;
1422
44
  if (wp->sync_rlower >= sy || wp->sync_rupper > wp->sync_rlower) {
1423
0
    bit_nset(wp->sync_dirty, 0, sy - 1);
1424
0
    return;
1425
0
  }
1426
44
  if ((ttyctx->flags & TTY_CTX_PANE_OBSCURED) ||
1427
44
      window_pane_scrollbar_overlay_visible(wp)) {
1428
0
    bit_nset(wp->sync_dirty, wp->sync_rupper, wp->sync_rlower);
1429
0
    return;
1430
0
  }
1431
1432
44
  log_debug("%s: %%%u scroll of %u (region %u-%u)", __func__, wp->id, n,
1433
44
      wp->sync_rupper, wp->sync_rlower);
1434
1435
44
  ttyctx->orupper = wp->sync_rupper;
1436
44
  ttyctx->orlower = wp->sync_rlower;
1437
44
  if (wp->yoff + wp->sy > wp->window->sy)
1438
0
    ttyctx->orlower -= (wp->yoff + wp->sy - wp->window->sy);
1439
44
  ttyctx->n = n;
1440
44
  ttyctx->bg = wp->sync_bg;
1441
44
  redraw_cb = ttyctx->redraw_cb;
1442
44
  ttyctx->redraw_cb = screen_write_sync_redraw_cb;
1443
44
  tty_write(tty_cmd_scrollup, ttyctx);
1444
1445
44
  ttyctx->redraw_cb = redraw_cb;
1446
44
  ttyctx->orupper = s->rupper;
1447
44
  ttyctx->orlower = s->rlower;
1448
44
  ttyctx->n = 0;
1449
44
}
1450
1451
/* Redraw all visible cells in a pane. */
1452
static void
1453
screen_write_redraw_pane(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
1454
0
{
1455
0
  struct screen *s = ctx->s;
1456
0
  u_int    yy;
1457
1458
0
  for (yy = 0; yy < screen_size_y(s); yy++)
1459
0
    screen_write_redraw_line(ctx, ttyctx, yy);
1460
0
}
1461
1462
/* VT100 alignment test. */
1463
void
1464
screen_write_alignmenttest(struct screen_write_ctx *ctx)
1465
1.34k
{
1466
1.34k
  struct screen   *s = ctx->s;
1467
1.34k
  struct tty_ctx     ttyctx;
1468
1.34k
  struct grid_cell   gc;
1469
1.34k
  u_int      xx, yy;
1470
1471
1.34k
  memcpy(&gc, &grid_default_cell, sizeof gc);
1472
1.34k
  utf8_set(&gc.data, 'E');
1473
1474
#ifdef ENABLE_SIXEL
1475
  if (image_free_all(s) && ctx->wp != NULL)
1476
    ctx->wp->flags |= PANE_REDRAW;
1477
#endif
1478
1479
35.0k
  for (yy = 0; yy < screen_size_y(s); yy++) {
1480
2.72M
    for (xx = 0; xx < screen_size_x(s); xx++)
1481
2.69M
      grid_view_set_cell(s->grid, xx, yy, &gc);
1482
33.6k
  }
1483
1484
1.34k
  screen_write_set_cursor(ctx, 0, 0);
1485
1486
1.34k
  s->rupper = 0;
1487
1.34k
  s->rlower = screen_size_y(s) - 1;
1488
1489
1.34k
  screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1);
1490
1491
1.34k
  screen_write_initctx(ctx, &ttyctx, 1, 1);
1492
1493
1.34k
  if (!screen_write_should_draw_lines(ctx, 0, screen_size_y(s)))
1494
194
    return;
1495
1.15k
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
1496
1.15k
    tty_write(tty_cmd_alignmenttest, &ttyctx);
1497
1.15k
    return;
1498
1.15k
  }
1499
1500
0
  screen_write_redraw_pane(ctx, &ttyctx);
1501
0
}
1502
1503
/* Insert nx characters. */
1504
void
1505
screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1506
1.81k
{
1507
1.81k
  struct screen *s = ctx->s;
1508
1.81k
  struct tty_ctx   ttyctx;
1509
1510
1.81k
  if (nx == 0)
1511
0
    nx = 1;
1512
1513
1.81k
  if (nx > screen_size_x(s) - s->cx)
1514
250
    nx = screen_size_x(s) - s->cx;
1515
1.81k
  if (nx == 0)
1516
194
    return;
1517
1518
1.62k
  if (s->cx > screen_size_x(s) - 1)
1519
0
    return;
1520
1521
#ifdef ENABLE_SIXEL
1522
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1523
    ctx->wp->flags |= PANE_REDRAW;
1524
#endif
1525
1526
1.62k
  screen_write_initctx(ctx, &ttyctx, 0, 1);
1527
1.62k
  ttyctx.bg = bg;
1528
1529
1.62k
  grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg);
1530
1531
1.62k
  screen_write_collect_flush(ctx, 0, __func__);
1532
1.62k
  ttyctx.n = nx;
1533
1534
1.62k
  if (!screen_write_should_draw_line(ctx, s->cy))
1535
195
    return;
1536
1.42k
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
1537
1.42k
    tty_write(tty_cmd_insertcharacter, &ttyctx);
1538
1.42k
    return;
1539
1.42k
  }
1540
1541
0
  screen_write_redraw_line(ctx, &ttyctx, s->cy);
1542
0
}
1543
1544
/* Delete nx characters. */
1545
void
1546
screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1547
1.47k
{
1548
1.47k
  struct screen *s = ctx->s;
1549
1.47k
  struct tty_ctx   ttyctx;
1550
1551
1.47k
  if (nx == 0)
1552
0
    nx = 1;
1553
1554
1.47k
  if (nx > screen_size_x(s) - s->cx)
1555
246
    nx = screen_size_x(s) - s->cx;
1556
1.47k
  if (nx == 0)
1557
194
    return;
1558
1559
1.28k
  if (s->cx > screen_size_x(s) - 1)
1560
0
    return;
1561
1562
#ifdef ENABLE_SIXEL
1563
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1564
    ctx->wp->flags |= PANE_REDRAW;
1565
#endif
1566
1567
1.28k
  screen_write_initctx(ctx, &ttyctx, 0, 1);
1568
1.28k
  ttyctx.bg = bg;
1569
1570
1.28k
  grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg);
1571
1572
1.28k
  screen_write_collect_flush(ctx, 0, __func__);
1573
1.28k
  ttyctx.n = nx;
1574
1575
1.28k
  if (!screen_write_should_draw_line(ctx, s->cy))
1576
194
    return;
1577
1.08k
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
1578
1.08k
    tty_write(tty_cmd_deletecharacter, &ttyctx);
1579
1.08k
    return;
1580
1.08k
  }
1581
1582
0
  screen_write_redraw_line(ctx, &ttyctx, s->cy);
1583
0
}
1584
1585
/* Clear nx characters. */
1586
void
1587
screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1588
1.36k
{
1589
1.36k
  struct screen *s = ctx->s;
1590
1.36k
  struct tty_ctx   ttyctx;
1591
1592
1.36k
  if (nx == 0)
1593
0
    nx = 1;
1594
1595
1.36k
  if (nx > screen_size_x(s) - s->cx)
1596
248
    nx = screen_size_x(s) - s->cx;
1597
1.36k
  if (nx == 0)
1598
194
    return;
1599
1600
1.17k
  if (s->cx > screen_size_x(s) - 1)
1601
0
    return;
1602
1603
#ifdef ENABLE_SIXEL
1604
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1605
    ctx->wp->flags |= PANE_REDRAW;
1606
#endif
1607
1608
1.17k
  screen_write_initctx(ctx, &ttyctx, 0, 1);
1609
1.17k
  ttyctx.bg = bg;
1610
1611
1.17k
  grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg);
1612
1613
1.17k
  screen_write_collect_flush(ctx, 0, __func__);
1614
1.17k
  ttyctx.n = nx;
1615
1616
1.17k
  if (!screen_write_should_draw_line(ctx, s->cy))
1617
195
    return;
1618
977
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
1619
977
    tty_write(tty_cmd_clearcharacter, &ttyctx);
1620
977
    return;
1621
977
  }
1622
1623
0
  screen_write_redraw_line(ctx, &ttyctx, s->cy);
1624
0
}
1625
1626
/* Insert ny lines. */
1627
void
1628
screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1629
1.90k
{
1630
1.90k
  struct screen *s = ctx->s;
1631
1.90k
  struct grid *gd = s->grid;
1632
1.90k
  struct tty_ctx   ttyctx;
1633
1.90k
  u_int    sy = screen_size_y(s);
1634
1635
1.90k
  if (ny == 0)
1636
0
    ny = 1;
1637
1638
#ifdef ENABLE_SIXEL
1639
  if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
1640
    ctx->wp->flags |= PANE_REDRAW;
1641
#endif
1642
1643
1.90k
  if (s->cy < s->rupper || s->cy > s->rlower) {
1644
795
    if (ny > sy - s->cy)
1645
81
      ny = sy - s->cy;
1646
795
    if (ny == 0)
1647
0
      return;
1648
1649
795
    screen_write_initctx(ctx, &ttyctx, 1, 1);
1650
795
    ttyctx.bg = bg;
1651
1652
795
    grid_view_insert_lines(gd, s->cy, ny, bg);
1653
1654
795
    screen_write_collect_flush(ctx, 0, __func__);
1655
795
    ttyctx.n = ny;
1656
1657
795
    if (!screen_write_should_draw_lines(ctx, s->cy, sy - s->cy))
1658
194
      return;
1659
601
    if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
1660
601
      tty_write(tty_cmd_insertline, &ttyctx);
1661
601
      return;
1662
601
    }
1663
1664
0
    screen_write_redraw_pane(ctx, &ttyctx);
1665
0
    return;
1666
601
  }
1667
1668
1.10k
  if (ny > s->rlower + 1 - s->cy)
1669
121
    ny = s->rlower + 1 - s->cy;
1670
1.10k
  if (ny == 0)
1671
0
    return;
1672
1673
1.10k
  screen_write_initctx(ctx, &ttyctx, 1, 1);
1674
1.10k
  ttyctx.bg = bg;
1675
1676
1.10k
  if (s->cy < s->rupper || s->cy > s->rlower)
1677
0
    grid_view_insert_lines(gd, s->cy, ny, bg);
1678
1.10k
  else
1679
1.10k
    grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg);
1680
1681
1.10k
  screen_write_collect_flush(ctx, 0, __func__);
1682
1.10k
  ttyctx.n = ny;
1683
1684
1.10k
  if (!screen_write_should_draw_lines(ctx, s->cy, s->rlower + 1 - s->cy))
1685
200
    return;
1686
905
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
1687
905
    tty_write(tty_cmd_insertline, &ttyctx);
1688
905
    return;
1689
905
  }
1690
1691
0
  screen_write_redraw_pane(ctx, &ttyctx);
1692
0
}
1693
1694
/* Delete ny lines. */
1695
void
1696
screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1697
1.65k
{
1698
1.65k
  struct screen *s = ctx->s;
1699
1.65k
  struct grid *gd = s->grid;
1700
1.65k
  struct tty_ctx   ttyctx;
1701
1.65k
  u_int    sy = screen_size_y(s), ry;
1702
1703
1.65k
  if (ny == 0)
1704
0
    ny = 1;
1705
1706
#ifdef ENABLE_SIXEL
1707
  if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
1708
    ctx->wp->flags |= PANE_REDRAW;
1709
#endif
1710
1711
1.65k
  if (s->cy < s->rupper || s->cy > s->rlower) {
1712
705
    if (ny > sy - s->cy)
1713
107
      ny = sy - s->cy;
1714
705
    if (ny == 0)
1715
0
      return;
1716
1717
705
    screen_write_initctx(ctx, &ttyctx, 1, 1);
1718
705
    ttyctx.bg = bg;
1719
1720
705
    grid_view_delete_lines(gd, s->cy, ny, bg);
1721
1722
705
    screen_write_collect_flush(ctx, 0, __func__);
1723
705
    ttyctx.n = ny;
1724
1725
705
    ry = s->rlower + 1 - s->rupper;
1726
705
    if (!screen_write_should_draw_lines(ctx, s->rupper, ry))
1727
202
      return;
1728
503
    if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
1729
503
      tty_write(tty_cmd_deleteline, &ttyctx);
1730
503
      return;
1731
503
    }
1732
1733
0
    screen_write_redraw_pane(ctx, &ttyctx);
1734
0
    return;
1735
503
  }
1736
1737
945
  ry = s->rlower + 1 - s->cy;
1738
945
  if (ny > ry)
1739
116
    ny = ry;
1740
945
  if (ny == 0)
1741
0
    return;
1742
1743
945
  screen_write_initctx(ctx, &ttyctx, 1, 1);
1744
945
  ttyctx.bg = bg;
1745
1746
945
  if (s->cy < s->rupper || s->cy > s->rlower)
1747
0
    grid_view_delete_lines(gd, s->cy, ny, bg);
1748
945
  else
1749
945
    grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg);
1750
1751
945
  screen_write_collect_flush(ctx, 0, __func__);
1752
945
  ttyctx.n = ny;
1753
1754
945
  if (!screen_write_should_draw_lines(ctx, s->cy, ry))
1755
198
    return;
1756
747
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
1757
747
    tty_write(tty_cmd_deleteline, &ttyctx);
1758
747
    return;
1759
747
  }
1760
1761
0
  screen_write_redraw_pane(ctx, &ttyctx);
1762
0
}
1763
1764
/* Clear line at cursor. */
1765
void
1766
screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
1767
898
{
1768
898
  struct screen     *s = ctx->s;
1769
898
  struct grid_line    *gl;
1770
898
  u_int        sx = screen_size_x(s);
1771
898
  struct screen_write_citem *ci = ctx->item;
1772
898
  struct osc133_data     od;
1773
898
  u_int        flags;
1774
1775
898
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1776
898
  if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
1777
523
    return;
1778
1779
#ifdef ENABLE_SIXEL
1780
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1781
    ctx->wp->flags |= PANE_REDRAW;
1782
#endif
1783
1784
375
  flags = gl->flags & GRID_LINE_OSC133_FLAGS;
1785
375
  memcpy(&od, &gl->osc133_data, sizeof od);
1786
375
  grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1787
375
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1788
375
  gl->flags |= flags;
1789
375
  memcpy(&gl->osc133_data, &od, sizeof gl->osc133_data);
1790
1791
375
  screen_write_collect_clear(ctx, s->cy, 1);
1792
375
  ci->x = 0;
1793
375
  ci->used = sx;
1794
375
  ci->type = CLEAR;
1795
375
  ci->bg = bg;
1796
375
  TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1797
375
  ctx->item = screen_write_get_citem();
1798
375
}
1799
1800
/* Clear to end of line from cursor. */
1801
void
1802
screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
1803
1.63k
{
1804
1.63k
  struct screen     *s = ctx->s;
1805
1.63k
  struct grid_line    *gl;
1806
1.63k
  u_int        sx = screen_size_x(s);
1807
1.63k
  struct screen_write_citem *ci = ctx->item;
1808
1809
1.63k
  if (s->cx == 0) {
1810
746
    screen_write_clearline(ctx, bg);
1811
746
    return;
1812
746
  }
1813
1814
891
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1815
891
  if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
1816
395
    return;
1817
1818
#ifdef ENABLE_SIXEL
1819
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1820
    ctx->wp->flags |= PANE_REDRAW;
1821
#endif
1822
1823
496
  grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
1824
1825
496
  ci->x = s->cx;
1826
496
  ci->used = sx - s->cx;
1827
496
  ci->type = CLEAR;
1828
496
  ci->bg = bg;
1829
496
  screen_write_collect_insert(ctx, ci);
1830
496
}
1831
1832
/* Clear to start of line from cursor. */
1833
void
1834
screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
1835
140
{
1836
140
  struct screen      *s = ctx->s;
1837
140
  u_int        sx = screen_size_x(s);
1838
140
  struct screen_write_citem *ci = ctx->item;
1839
1840
140
  if (s->cx >= sx - 1) {
1841
66
    screen_write_clearline(ctx, bg);
1842
66
    return;
1843
66
  }
1844
1845
#ifdef ENABLE_SIXEL
1846
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1847
    ctx->wp->flags |= PANE_REDRAW;
1848
#endif
1849
1850
74
  if (s->cx > sx - 1)
1851
0
    grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1852
74
  else
1853
74
    grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1854
1855
74
  ci->x = 0;
1856
74
  ci->used = s->cx + 1;
1857
74
  ci->type = CLEAR;
1858
74
  ci->bg = bg;
1859
74
  screen_write_collect_insert(ctx, ci);
1860
74
}
1861
1862
/* Move cursor to px,py. */
1863
void
1864
screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
1865
    int origin)
1866
4.85k
{
1867
4.85k
  struct screen *s = ctx->s;
1868
1869
4.85k
  if (origin && py != -1 && (s->mode & MODE_ORIGIN)) {
1870
503
    if ((u_int)py > s->rlower - s->rupper)
1871
109
      py = s->rlower;
1872
394
    else
1873
394
      py += s->rupper;
1874
503
  }
1875
1876
4.85k
  if (px != -1 && (u_int)px > screen_size_x(s) - 1)
1877
313
    px = screen_size_x(s) - 1;
1878
4.85k
  if (py != -1 && (u_int)py > screen_size_y(s) - 1)
1879
288
    py = screen_size_y(s) - 1;
1880
1881
4.85k
  log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py);
1882
4.85k
  screen_write_set_cursor(ctx, px, py);
1883
4.85k
}
1884
1885
/* Reverse index (up with scroll). */
1886
void
1887
screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
1888
805
{
1889
805
  struct screen *s = ctx->s;
1890
805
  struct tty_ctx   ttyctx;
1891
805
  u_int    ry;
1892
1893
805
  if (s->cy != s->rupper) {
1894
391
    if (s->cy > 0)
1895
196
      screen_write_set_cursor(ctx, -1, s->cy - 1);
1896
391
    return;
1897
391
  }
1898
1899
#ifdef ENABLE_SIXEL
1900
  if (image_free_all(s) && ctx->wp != NULL)
1901
    ctx->wp->flags |= PANE_REDRAW;
1902
#endif
1903
1904
414
  grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
1905
414
  screen_write_collect_flush(ctx, 0, __func__);
1906
1907
414
  screen_write_initctx(ctx, &ttyctx, 1, 1);
1908
414
  ttyctx.bg = bg;
1909
1910
414
  ry = s->rlower + 1 - s->rupper;
1911
414
  if (!screen_write_should_draw_lines(ctx, s->rupper, ry))
1912
197
    return;
1913
217
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
1914
217
    tty_write(tty_cmd_reverseindex, &ttyctx);
1915
217
    return;
1916
217
  }
1917
1918
0
  screen_write_redraw_pane(ctx, &ttyctx);
1919
0
}
1920
1921
/* Set scroll region. */
1922
void
1923
screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper,
1924
    u_int rlower)
1925
3.29k
{
1926
3.29k
  struct screen *s = ctx->s;
1927
1928
3.29k
  if (rupper > screen_size_y(s) - 1)
1929
125
    rupper = screen_size_y(s) - 1;
1930
3.29k
  if (rlower > screen_size_y(s) - 1)
1931
128
    rlower = screen_size_y(s) - 1;
1932
3.29k
  if (rupper >= rlower)  /* cannot be one line */
1933
215
    return;
1934
1935
3.08k
  screen_write_collect_flush(ctx, 0, __func__);
1936
1937
  /* Cursor moves to top-left. */
1938
3.08k
  screen_write_set_cursor(ctx, 0, 0);
1939
1940
3.08k
  s->rupper = rupper;
1941
3.08k
  s->rlower = rlower;
1942
3.08k
}
1943
1944
/* Line feed. */
1945
void
1946
screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
1947
10.3k
{
1948
10.3k
  struct screen   *s = ctx->s;
1949
10.3k
  struct grid   *gd = s->grid;
1950
10.3k
  struct grid_line  *gl;
1951
#ifdef ENABLE_SIXEL
1952
  int      redraw = 0;
1953
#endif
1954
10.3k
  u_int      rupper = s->rupper, rlower = s->rlower;
1955
1956
10.3k
  gl = grid_get_line(gd, gd->hsize + s->cy);
1957
10.3k
  if (wrapped)
1958
1.41k
    gl->flags |= GRID_LINE_WRAPPED;
1959
1960
10.3k
  log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1961
10.3k
      rupper, rlower);
1962
1963
10.3k
  if (bg != ctx->bg) {
1964
646
    screen_write_collect_flush(ctx, 1, __func__);
1965
646
    ctx->bg = bg;
1966
646
  }
1967
1968
10.3k
  if (s->cy != s->rlower) {
1969
5.14k
    if (s->cy < screen_size_y(s) - 1)
1970
4.95k
      screen_write_set_cursor(ctx, -1, s->cy + 1);
1971
5.14k
    return;
1972
5.14k
  }
1973
1974
#ifdef ENABLE_SIXEL
1975
  if (rlower == screen_size_y(s) - 1)
1976
    redraw = image_scroll_up(s, 1);
1977
  else
1978
    redraw = image_check_line(s, rupper, rlower - rupper);
1979
  if (redraw && ctx->wp != NULL)
1980
    ctx->wp->flags |= PANE_REDRAW;
1981
#endif
1982
1983
5.18k
  grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1984
5.18k
  screen_write_collect_scroll(ctx, bg);
1985
5.18k
  ctx->scrolled++;
1986
5.18k
}
1987
1988
/* Scroll up. */
1989
void
1990
screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1991
1.48k
{
1992
1.48k
  struct screen *s = ctx->s;
1993
1.48k
  struct grid *gd = s->grid;
1994
1.48k
  u_int    i;
1995
1996
1.48k
  if (lines == 0)
1997
0
    lines = 1;
1998
1.48k
  else if (lines > s->rlower - s->rupper + 1)
1999
525
    lines = s->rlower - s->rupper + 1;
2000
2001
1.48k
  if (bg != ctx->bg) {
2002
98
    screen_write_collect_flush(ctx, 1, __func__);
2003
98
    ctx->bg = bg;
2004
98
  }
2005
2006
#ifdef ENABLE_SIXEL
2007
  if (image_scroll_up(s, lines) && ctx->wp != NULL)
2008
    ctx->wp->flags |= PANE_REDRAW;
2009
#endif
2010
2011
17.3k
  for (i = 0; i < lines; i++) {
2012
15.8k
    grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
2013
15.8k
    screen_write_collect_scroll(ctx, bg);
2014
15.8k
  }
2015
1.48k
  ctx->scrolled += lines;
2016
1.48k
}
2017
2018
/* Scroll down. */
2019
void
2020
screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
2021
729
{
2022
729
  struct screen *s = ctx->s;
2023
729
  struct grid *gd = s->grid;
2024
729
  struct tty_ctx   ttyctx;
2025
729
  u_int    i, ry;
2026
2027
729
  screen_write_initctx(ctx, &ttyctx, 1, 1);
2028
729
  ttyctx.bg = bg;
2029
2030
729
  if (lines == 0)
2031
0
    lines = 1;
2032
729
  else if (lines > s->rlower - s->rupper + 1)
2033
125
    lines = s->rlower - s->rupper + 1;
2034
2035
#ifdef ENABLE_SIXEL
2036
  if (image_free_all(s) && ctx->wp != NULL)
2037
    ctx->wp->flags |= PANE_REDRAW;
2038
#endif
2039
2040
3.82k
  for (i = 0; i < lines; i++)
2041
3.09k
    grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg);
2042
2043
729
  screen_write_collect_flush(ctx, 0, __func__);
2044
729
  ttyctx.n = lines;
2045
2046
729
  ry = s->rlower + 1 - s->rupper;
2047
729
  if (!screen_write_should_draw_lines(ctx, s->rupper, ry))
2048
194
    return;
2049
535
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
2050
535
    tty_write(tty_cmd_scrolldown, &ttyctx);
2051
535
    return;
2052
535
  }
2053
2054
0
  screen_write_redraw_pane(ctx, &ttyctx);
2055
0
}
2056
2057
/* Carriage return (cursor to start of line). */
2058
void
2059
screen_write_carriagereturn(struct screen_write_ctx *ctx)
2060
4.22k
{
2061
4.22k
  screen_write_set_cursor(ctx, 0, -1);
2062
4.22k
}
2063
2064
/* Clear to end of screen from cursor. */
2065
void
2066
screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
2067
1.98k
{
2068
1.98k
  struct screen   *s = ctx->s;
2069
1.98k
  struct grid   *gd = s->grid;
2070
1.98k
  struct tty_ctx     ttyctx;
2071
1.98k
  u_int      sx = screen_size_x(s), sy = screen_size_y(s);
2072
1.98k
  u_int      y, i, xoff, yoff, ocx, ocy;
2073
1.98k
  struct visible_ranges *r;
2074
1.98k
  struct visible_range  *ri;
2075
2076
#ifdef ENABLE_SIXEL
2077
  if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
2078
    ctx->wp->flags |= PANE_REDRAW;
2079
#endif
2080
2081
1.98k
  screen_write_initctx(ctx, &ttyctx, 1, 1);
2082
1.98k
  ttyctx.bg = bg;
2083
2084
  /* Scroll into history if it is enabled and clearing entire screen. */
2085
1.98k
  if (s->cx == 0 &&
2086
1.30k
      s->cy == 0 &&
2087
604
      (gd->flags & GRID_HISTORY) &&
2088
0
      ctx->wp != NULL &&
2089
0
      options_get_number(ctx->wp->options, "scroll-on-clear"))
2090
0
    grid_view_clear_history(gd, bg);
2091
1.98k
  else {
2092
1.98k
    if (s->cx <= sx - 1)
2093
1.78k
      grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg);
2094
1.98k
    grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg);
2095
1.98k
  }
2096
2097
1.98k
  screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1));
2098
1.98k
  screen_write_collect_flush(ctx, 0, __func__);
2099
2100
1.98k
  if (!screen_write_should_draw_lines(ctx, s->cy, sy - s->cy))
2101
199
    return;
2102
1.78k
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) {
2103
1.78k
    tty_write(tty_cmd_clearendofscreen, &ttyctx);
2104
1.78k
    return;
2105
1.78k
  }
2106
2107
  /* Can't just clear screen, must avoid floating windows. */
2108
0
  ocx = s->cx;
2109
0
  ocy = s->cy;
2110
0
  if (ctx->wp != NULL) {
2111
0
    xoff = ctx->wp->xoff;
2112
0
    yoff = ctx->wp->yoff;
2113
0
  } else {
2114
0
    xoff = 0;
2115
0
    yoff = 0;
2116
0
  }
2117
2118
  /* First line (containing the cursor). */
2119
0
  if (s->cx <= sx - 1) {
2120
0
    r = window_visible_ranges(ctx->wp, xoff + s->cx, yoff + s->cy,
2121
0
        sx - s->cx, NULL);
2122
0
    for (i = 0; i < r->used; i++) {
2123
0
      ri = &r->ranges[i];
2124
0
      if (ri->nx == 0)
2125
0
        continue;
2126
0
      screen_write_collect_insert_clear(ctx, ri->px - xoff,
2127
0
          ri->nx, bg);
2128
0
    }
2129
0
  }
2130
2131
  /* Below cursor to bottom. */
2132
0
  for (y = s->cy + 1; y < sy; y++) {
2133
0
    screen_write_set_cursor(ctx, 0, y);
2134
0
    r = window_visible_ranges(ctx->wp, xoff, yoff + y, sx, NULL);
2135
0
    for (i = 0; i < r->used; i++) {
2136
0
      ri = &r->ranges[i];
2137
0
      if (ri->nx == 0)
2138
0
        continue;
2139
0
      screen_write_collect_insert_clear(ctx, ri->px - xoff,
2140
0
          ri->nx, bg);
2141
0
    }
2142
0
  }
2143
0
  screen_write_set_cursor(ctx, ocx, ocy);
2144
0
}
2145
2146
/* Clear to start of screen. */
2147
void
2148
screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
2149
433
{
2150
433
  struct screen   *s = ctx->s;
2151
433
  struct tty_ctx     ttyctx;
2152
433
  u_int      sx = screen_size_x(s);
2153
433
  u_int      y, i, xoff, yoff, ocx, ocy;
2154
433
  struct visible_ranges *r;
2155
433
  struct visible_range  *ri;
2156
2157
#ifdef ENABLE_SIXEL
2158
  if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL)
2159
    ctx->wp->flags |= PANE_REDRAW;
2160
#endif
2161
2162
433
  screen_write_initctx(ctx, &ttyctx, 1, 1);
2163
433
  ttyctx.bg = bg;
2164
2165
433
  if (s->cy > 0)
2166
99
    grid_view_clear(s->grid, 0, 0, sx, s->cy, bg);
2167
433
  if (s->cx > sx - 1)
2168
66
    grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
2169
367
  else
2170
367
    grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
2171
2172
433
  screen_write_collect_clear(ctx, 0, s->cy);
2173
433
  screen_write_collect_flush(ctx, 0, __func__);
2174
2175
433
  if (!screen_write_should_draw_lines(ctx, 0, s->cy + 1))
2176
69
    return;
2177
364
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) {
2178
364
    tty_write(tty_cmd_clearstartofscreen, &ttyctx);
2179
364
    return;
2180
364
  }
2181
2182
  /* Can't just clear screen, must avoid floating windows. */
2183
0
  ocx = s->cx;
2184
0
  ocy = s->cy;
2185
0
  if (ctx->wp != NULL) {
2186
0
    xoff = ctx->wp->xoff;
2187
0
    yoff = ctx->wp->yoff;
2188
0
  } else {
2189
0
    xoff = 0;
2190
0
    yoff = 0;
2191
0
  }
2192
2193
  /* Top to above the cursor. */
2194
0
  for (y = 0; y < s->cy; y++) {
2195
0
    screen_write_set_cursor(ctx, 0, y);
2196
0
    r = window_visible_ranges(ctx->wp, xoff, yoff + y, sx, NULL);
2197
0
    for (i = 0; i < r->used; i++) {
2198
0
      ri = &r->ranges[i];
2199
0
      if (ri->nx == 0)
2200
0
        continue;
2201
0
      screen_write_collect_insert_clear(ctx, ri->px - xoff,
2202
0
          ri->nx, bg);
2203
0
    }
2204
0
  }
2205
2206
  /* Last line (containing the cursor). */
2207
0
  screen_write_set_cursor(ctx, 0, s->cy);
2208
0
  r = window_visible_ranges(ctx->wp, xoff, yoff + ocy, s->cx + 1, NULL);
2209
0
  for (i = 0; i < r->used; i++) {
2210
0
    ri = &r->ranges[i];
2211
0
    if (ri->nx == 0)
2212
0
      continue;
2213
0
    screen_write_collect_insert_clear(ctx, ri->px - xoff, ri->nx,
2214
0
        bg);
2215
0
  }
2216
0
  screen_write_set_cursor(ctx, ocx, ocy);
2217
0
}
2218
2219
/* Clear entire screen. */
2220
void
2221
screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
2222
2.70k
{
2223
2.70k
  struct screen   *s = ctx->s;
2224
2.70k
  struct tty_ctx     ttyctx;
2225
2.70k
  u_int      sx = screen_size_x(s), sy = screen_size_y(s);
2226
2.70k
  u_int      y, i, xoff, yoff, ocx, ocy;
2227
2.70k
  struct visible_ranges *r;
2228
2.70k
  struct visible_range  *ri;
2229
2230
#ifdef ENABLE_SIXEL
2231
  if (image_free_all(s) && ctx->wp != NULL)
2232
    ctx->wp->flags |= PANE_REDRAW;
2233
#endif
2234
2235
2.70k
  screen_write_initctx(ctx, &ttyctx, 1, 1);
2236
2.70k
  ttyctx.bg = bg;
2237
2238
  /* Scroll into history if it is enabled. */
2239
2.70k
  if ((s->grid->flags & GRID_HISTORY) &&
2240
0
      ctx->wp != NULL &&
2241
0
      options_get_number(ctx->wp->options, "scroll-on-clear"))
2242
0
    grid_view_clear_history(s->grid, bg);
2243
2.70k
  else
2244
2.70k
    grid_view_clear(s->grid, 0, 0, sx, sy, bg);
2245
2246
2.70k
  screen_write_collect_clear(ctx, 0, sy);
2247
2248
2.70k
  if (!screen_write_should_draw_lines(ctx, 0, sy))
2249
206
    return;
2250
2.49k
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) {
2251
2.49k
    tty_write(tty_cmd_clearscreen, &ttyctx);
2252
2.49k
    return;
2253
2.49k
  }
2254
2255
  /* Can't just clear screen, must avoid floating windows. */
2256
0
  ocx = s->cx;
2257
0
  ocy = s->cy;
2258
0
  if (ctx->wp != NULL) {
2259
0
    xoff = ctx->wp->xoff;
2260
0
    yoff = ctx->wp->yoff;
2261
0
  } else {
2262
0
    xoff = 0;
2263
0
    yoff = 0;
2264
0
  }
2265
2266
  /* Clear every line. */
2267
0
  for (y = 0; y < sy; y++) {
2268
0
    screen_write_set_cursor(ctx, 0, y);
2269
0
    r = window_visible_ranges(ctx->wp, xoff, yoff + y, sx, NULL);
2270
0
    for (i = 0; i < r->used; i++) {
2271
0
      ri = &r->ranges[i];
2272
0
      if (ri->nx == 0)
2273
0
        continue;
2274
0
      screen_write_collect_insert_clear(ctx, ri->px - xoff,
2275
0
          ri->nx, bg);
2276
0
    }
2277
0
  }
2278
0
  screen_write_set_cursor(ctx, ocx, ocy);
2279
0
}
2280
2281
/* Clear entire history. */
2282
void
2283
screen_write_clearhistory(struct screen_write_ctx *ctx)
2284
286
{
2285
286
  grid_clear_history(ctx->s->grid);
2286
286
}
2287
2288
/* Force a full redraw. */
2289
void
2290
screen_write_fullredraw(struct screen_write_ctx *ctx)
2291
1.93k
{
2292
1.93k
  struct tty_ctx   ttyctx;
2293
2294
1.93k
  screen_write_collect_flush(ctx, 0, __func__);
2295
2296
1.93k
  screen_write_initctx(ctx, &ttyctx, 1, 0);
2297
1.93k
  if (ttyctx.redraw_cb != NULL)
2298
1.93k
    ttyctx.redraw_cb(&ttyctx, 0, ttyctx.sy);
2299
1.93k
}
2300
2301
/* Trim collected items. */
2302
static struct screen_write_citem *
2303
screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x,
2304
    u_int used, int *wrapped)
2305
11.5k
{
2306
11.5k
  struct screen_write_cline *cl = &ctx->s->write_list[y];
2307
11.5k
  struct screen_write_citem *ci, *ci2, *tmp, *before = NULL;
2308
11.5k
  u_int        sx = x, ex = x + used - 1;
2309
11.5k
  u_int        csx, cex;
2310
2311
11.5k
  if (TAILQ_EMPTY(&cl->items))
2312
4.60k
    return (NULL);
2313
19.3k
  TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
2314
19.3k
    csx = ci->x;
2315
19.3k
    cex = ci->x + ci->used - 1;
2316
2317
    /* Item is entirely before. */
2318
19.3k
    if (cex < sx) {
2319
13.2k
      log_debug("%s: %p %u-%u before %u-%u", __func__, ci,
2320
13.2k
          csx, cex, sx, ex);
2321
13.2k
      continue;
2322
13.2k
    }
2323
2324
    /* Item is entirely after. */
2325
6.09k
    if (csx > ex) {
2326
1.20k
      log_debug("%s: %p %u-%u after %u-%u", __func__, ci,
2327
1.20k
          csx, cex, sx, ex);
2328
1.20k
      before = ci;
2329
1.20k
      break;
2330
1.20k
    }
2331
2332
    /* Item is entirely inside. */
2333
4.89k
    if (csx >= sx && cex <= ex) {
2334
1.86k
      log_debug("%s: %p %u-%u inside %u-%u", __func__, ci,
2335
1.86k
          csx, cex, sx, ex);
2336
1.86k
      TAILQ_REMOVE(&cl->items, ci, entry);
2337
1.86k
      screen_write_free_citem(ci);
2338
1.86k
      if (csx == 0 && ci->wrapped && wrapped != NULL)
2339
195
        *wrapped = 1;
2340
1.86k
      continue;
2341
1.86k
    }
2342
2343
    /* Item under the start. */
2344
3.03k
    if (csx < sx && cex >= sx && cex <= ex) {
2345
384
      log_debug("%s: %p %u-%u start %u-%u", __func__, ci,
2346
384
          csx, cex, sx, ex);
2347
384
      ci->used = sx - csx;
2348
384
      log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
2349
384
          ci->x + ci->used + 1);
2350
384
      continue;
2351
384
    }
2352
2353
    /* Item covers the end. */
2354
2.65k
    if (cex > ex && csx >= sx && csx <= ex) {
2355
614
      log_debug("%s: %p %u-%u end %u-%u", __func__, ci,
2356
614
          csx, cex, sx, ex);
2357
614
      ci->x = ex + 1;
2358
614
      ci->used = cex - ex;
2359
614
      log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
2360
614
          ci->x + ci->used + 1);
2361
614
      before = ci;
2362
614
      break;
2363
614
    }
2364
2365
    /* Item must cover both sides. */
2366
2.03k
    log_debug("%s: %p %u-%u under %u-%u", __func__, ci,
2367
2.03k
        csx, cex, sx, ex);
2368
2.03k
    ci2 = screen_write_get_citem();
2369
2.03k
    ci2->type = ci->type;
2370
2.03k
    ci2->bg = ci->bg;
2371
2.03k
    memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc);
2372
2.03k
    TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry);
2373
2374
2.03k
    ci->used = sx - csx;
2375
2.03k
    ci2->x = ex + 1;
2376
2.03k
    ci2->used = cex - ex;
2377
2378
2.03k
    log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci,
2379
2.03k
        ci->x, ci->x + ci->used - 1, ci, ci2->x,
2380
2.03k
        ci2->x + ci2->used - 1, ci2);
2381
2.03k
    before = ci2;
2382
2.03k
    break;
2383
2.65k
  }
2384
6.96k
  return (before);
2385
11.5k
}
2386
2387
/* Clear collected lines. */
2388
static void
2389
screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
2390
27.9k
{
2391
27.9k
  struct screen_write_cline *cl;
2392
27.9k
  u_int        i;
2393
2394
192k
  for (i = y; i < y + n; i++) {
2395
164k
    cl = &ctx->s->write_list[i];
2396
164k
    TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry);
2397
164k
  }
2398
27.9k
}
2399
2400
/* Scroll collected lines up. */
2401
static void
2402
screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg)
2403
21.0k
{
2404
21.0k
  struct screen     *s = ctx->s;
2405
21.0k
  struct screen_write_cline *cl;
2406
21.0k
  u_int        y;
2407
21.0k
  char        *saved;
2408
21.0k
  struct screen_write_citem *ci;
2409
2410
21.0k
  log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
2411
21.0k
      s->rupper, s->rlower);
2412
2413
21.0k
  screen_write_collect_clear(ctx, s->rupper, 1);
2414
21.0k
  saved = ctx->s->write_list[s->rupper].data;
2415
502k
  for (y = s->rupper; y < s->rlower; y++) {
2416
481k
    cl = &ctx->s->write_list[y + 1];
2417
481k
    TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry);
2418
481k
    ctx->s->write_list[y].data = cl->data;
2419
481k
  }
2420
21.0k
  ctx->s->write_list[s->rlower].data = saved;
2421
2422
21.0k
  ci = screen_write_get_citem();
2423
21.0k
  ci->x = 0;
2424
21.0k
  ci->used = screen_size_x(s);
2425
21.0k
  ci->type = CLEAR;
2426
21.0k
  ci->bg = bg;
2427
21.0k
  TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry);
2428
21.0k
}
2429
2430
/* Flush collected scrolling. */
2431
static int
2432
screen_write_collect_flush_scrolled(struct screen_write_ctx *ctx)
2433
1.86k
{
2434
1.86k
  struct window_pane  *wp = ctx->wp;
2435
1.86k
  struct screen   *s = ctx->s;
2436
1.86k
  struct tty_ctx     ttyctx;
2437
2438
1.86k
  screen_write_initctx(ctx, &ttyctx, 1, 1);
2439
1.86k
  if (ttyctx.flags & TTY_CTX_PANE_OBSCURED && wp != NULL) {
2440
0
    screen_write_redraw_pane(ctx, &ttyctx);
2441
0
    return 0;
2442
0
  }
2443
1.86k
  if (wp != NULL && window_pane_scrollbar_overlay_visible(wp)) {
2444
0
    wp->flags |= PANE_REDRAW;
2445
0
    return 0;
2446
0
  }
2447
2448
1.86k
  log_debug("%s: scrolled %u (region %u-%u)", __func__, ctx->scrolled,
2449
1.86k
      s->rupper, s->rlower);
2450
1.86k
  if (ctx->scrolled > s->rlower - s->rupper + 1)
2451
132
    ctx->scrolled = s->rlower - s->rupper + 1;
2452
2453
1.86k
  if (wp != NULL && wp->yoff + wp->sy > wp->window->sy)
2454
0
    ttyctx.orlower -= (wp->yoff + wp->sy - wp->window->sy);
2455
1.86k
  ttyctx.n = ctx->scrolled;
2456
1.86k
  ttyctx.bg = ctx->bg;
2457
1.86k
  tty_write(tty_cmd_scrollup, &ttyctx);
2458
2459
1.86k
  if (wp != NULL)
2460
1.86k
    window_pane_scrollbar_redraw(wp);
2461
1.86k
  return 1;
2462
1.86k
}
2463
2464
/* Flush a collected line. */
2465
static u_int
2466
screen_write_collect_flush_line(struct screen_write_ctx *ctx, u_int y)
2467
1.93M
{
2468
1.93M
  struct window_pane    *wp = ctx->wp;
2469
1.93M
  struct screen     *s = ctx->s;
2470
1.93M
  struct screen_write_citem *ci, *tmp;
2471
1.93M
  struct screen_write_cline *cl = &s->write_list[y];
2472
1.93M
  u_int        last = UINT_MAX, items = 0, wsx, wsy;
2473
1.93M
  u_int        w_length, i;
2474
1.93M
  int        w_start, w_end, xoff, yoff, written;
2475
1.93M
  int        r_start, r_end, c_start, c_end;
2476
1.93M
  struct tty_ctx       ttyctx;
2477
1.93M
  struct visible_ranges   *r;
2478
1.93M
  struct visible_range    *ri;
2479
2480
1.93M
  if (wp != NULL) {
2481
1.93M
    wsx = wp->window->sx;
2482
1.93M
    wsy = wp->window->sy;
2483
1.93M
    xoff = wp->xoff;
2484
1.93M
    yoff = wp->yoff;
2485
1.93M
  } else {
2486
0
    wsx = screen_size_x(s);
2487
0
    wsy = screen_size_y(s);
2488
0
    xoff = 0;
2489
0
    yoff = 0;
2490
0
  }
2491
1.93M
  if (y + yoff >= wsy)
2492
0
    return (0);
2493
2494
1.93M
  r = window_visible_ranges(wp, 0, y + yoff, wsx, NULL);
2495
1.93M
  TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
2496
15.3k
    log_debug("collect list: x=%u (last %u), y=%u, used=%u", ci->x,
2497
15.3k
        last, y, ci->used);
2498
15.3k
    if (last != UINT_MAX && ci->x <= last)
2499
0
      fatalx("collect list bad order: %u <= %u", ci->x, last);
2500
2501
15.3k
    w_length = 0;
2502
15.3k
    written = 0;
2503
30.6k
    for (i = 0; i < r->used; i++) {
2504
15.3k
      ri = &r->ranges[i];
2505
15.3k
      if (ri->nx == 0)
2506
0
        continue;
2507
2508
15.3k
      r_start = ri->px;
2509
15.3k
      r_end = ri->px + ri->nx;
2510
15.3k
      c_start = ci->x;
2511
15.3k
      c_end = ci->x + ci->used;
2512
2513
15.3k
      if (c_start + xoff >= r_end || c_end + xoff <= r_start)
2514
0
        continue;
2515
15.3k
      if (r_start > c_start + xoff)
2516
0
        w_start = r_start - xoff;
2517
15.3k
      else
2518
15.3k
        w_start = c_start;
2519
15.3k
      if (c_end + xoff > r_end)
2520
0
        w_end = r_end - xoff;
2521
15.3k
      else
2522
15.3k
        w_end = c_end;
2523
15.3k
      if (w_end <= w_start)
2524
0
        continue;
2525
15.3k
      w_length = w_end - w_start;
2526
15.3k
      if (w_length <= 0)
2527
0
        continue;
2528
2529
15.3k
      screen_write_set_cursor(ctx, w_start, y);
2530
15.3k
      if (ci->type == CLEAR) {
2531
8.86k
        screen_write_initctx(ctx, &ttyctx, 1, 0);
2532
8.86k
        ttyctx.bg = ci->bg;
2533
8.86k
        ttyctx.n = w_length;
2534
8.86k
        tty_write(tty_cmd_clearcharacter, &ttyctx);
2535
8.86k
      } else {
2536
6.48k
        screen_write_initctx(ctx, &ttyctx, 0, 1);
2537
6.48k
        if (ttyctx.flags & TTY_CTX_PANE_OBSCURED) {
2538
0
          ttyctx.n = w_length;
2539
0
          tty_write(tty_cmd_redrawline, &ttyctx);
2540
6.48k
        } else {
2541
6.48k
          ttyctx.cell = &ci->gc;
2542
6.48k
          if (ci->wrapped)
2543
424
            ttyctx.flags |= TTY_CTX_WRAPPED;
2544
6.48k
          ttyctx.data.data = cl->data + w_start;
2545
6.48k
          ttyctx.data.size = w_length;
2546
6.48k
          tty_write(tty_cmd_cells, &ttyctx);
2547
6.48k
        }
2548
6.48k
      }
2549
15.3k
      items++;
2550
15.3k
      written = 1;
2551
15.3k
    }
2552
15.3k
    if (written) {
2553
15.3k
      last = ci->x;
2554
15.3k
      TAILQ_REMOVE(&cl->items, ci, entry);
2555
15.3k
      screen_write_free_citem(ci);
2556
15.3k
    }
2557
15.3k
  }
2558
1.93M
  return (items);
2559
1.93M
}
2560
2561
/* Flush collected lines. */
2562
static void
2563
screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
2564
    const char *from)
2565
118k
{
2566
118k
  struct screen     *s = ctx->s;
2567
118k
  struct window_pane    *wp = ctx->wp;
2568
118k
  u_int        y, cx, cy, items = 0;
2569
118k
  struct screen_write_citem *ci, *tmp;
2570
118k
  struct screen_write_cline *cl;
2571
2572
118k
  if (wp != NULL && (wp->flags & (PANE_REDRAW|PANE_DROP)))
2573
0
    goto discard;
2574
118k
  if (s->mode & MODE_SYNC) {
2575
7.38k
    if (ctx->scrolled != 0)
2576
1.20k
      screen_write_sync_scroll_dirty(ctx);
2577
191k
    for (y = 0; y < screen_size_y(s); y++) {
2578
184k
      cl = &s->write_list[y];
2579
184k
      if (!TAILQ_EMPTY(&cl->items))
2580
2.83k
        screen_write_should_draw_line(ctx, y);
2581
184k
    }
2582
7.38k
    goto discard;
2583
7.38k
  }
2584
2585
111k
  if (ctx->scrolled != 0) {
2586
1.86k
    if (!screen_write_collect_flush_scrolled(ctx))
2587
0
      goto discard;
2588
1.86k
    ctx->scrolled = 0;
2589
1.86k
  }
2590
111k
  ctx->bg = 8;
2591
2592
111k
  if (scroll_only)
2593
34.1k
    return;
2594
2595
77.4k
  cx = s->cx; cy = s->cy;
2596
2.01M
  for (y = 0; y < screen_size_y(s); y++)
2597
1.93M
    items += screen_write_collect_flush_line(ctx, y);
2598
77.4k
  s->cx = cx; s->cy = cy;
2599
2600
77.4k
  log_debug("%s: flushed %u items (%s)", __func__, items, from);
2601
77.4k
  return;
2602
2603
7.38k
discard:
2604
191k
  for (y = 0; y < screen_size_y(s); y++) {
2605
184k
    cl = &s->write_list[y];
2606
184k
    TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
2607
3.29k
      TAILQ_REMOVE(&cl->items, ci, entry);
2608
3.29k
      screen_write_free_citem(ci);
2609
3.29k
    }
2610
184k
  }
2611
7.38k
  ctx->scrolled = 0;
2612
7.38k
  ctx->bg = 8;
2613
7.38k
}
2614
2615
/* Insert an item on current line. */
2616
static void
2617
screen_write_collect_insert(struct screen_write_ctx *ctx,
2618
    struct screen_write_citem *ci)
2619
11.5k
{
2620
11.5k
  struct screen     *s = ctx->s;
2621
11.5k
  struct screen_write_cline *cl = &s->write_list[s->cy];
2622
11.5k
  struct screen_write_citem *before;
2623
2624
11.5k
  before = screen_write_collect_trim(ctx, s->cy, ci->x, ci->used,
2625
11.5k
      &ci->wrapped);
2626
11.5k
  if (before == NULL)
2627
7.71k
    TAILQ_INSERT_TAIL(&cl->items, ci, entry);
2628
3.85k
  else
2629
3.85k
    TAILQ_INSERT_BEFORE(before, ci, entry);
2630
11.5k
  ctx->item = screen_write_get_citem();
2631
11.5k
}
2632
2633
/* Insert a clear for part of a line. */
2634
static void
2635
screen_write_collect_insert_clear(struct screen_write_ctx *ctx, u_int px,
2636
    u_int nx, u_int bg)
2637
1.24k
{
2638
1.24k
  struct screen_write_citem *ci = ctx->item;
2639
2640
1.24k
  if (nx != 0) {
2641
1.24k
    ci->x = px;
2642
1.24k
    ci->used = nx;
2643
1.24k
    ci->type = CLEAR;
2644
1.24k
    ci->bg = bg;
2645
1.24k
    screen_write_collect_insert(ctx, ci);
2646
1.24k
  }
2647
1.24k
}
2648
2649
/*
2650
 * Clear a cell that is being broken up by a write over part of it, keeping the
2651
 * background so the columns it covered do not lose their colour.
2652
 */
2653
static void
2654
screen_write_clear_cell(struct grid *gd, u_int px, u_int py)
2655
10.7k
{
2656
10.7k
  struct grid_cell   gc;
2657
10.7k
  int      bg;
2658
2659
10.7k
  grid_view_get_cell(gd, px, py, &gc);
2660
10.7k
  bg = gc.bg;
2661
2662
10.7k
  memcpy(&gc, &grid_default_cell, sizeof gc);
2663
10.7k
  gc.bg = bg;
2664
10.7k
  grid_view_set_cell(gd, px, py, &gc);
2665
10.7k
}
2666
2667
/*
2668
 * Insert clears for a range of cells already cleared in the grid. Adjacent
2669
 * cells may have come from different characters and so have different
2670
 * backgrounds, so this is done in runs of the same colour.
2671
 */
2672
static void
2673
screen_write_insert_clears(struct screen_write_ctx *ctx, u_int px, u_int nx)
2674
1.15k
{
2675
1.15k
  struct screen   *s = ctx->s;
2676
1.15k
  struct grid_cell   gc;
2677
1.15k
  u_int      xx, start = px, n;
2678
1.15k
  int      bg = 8;
2679
2680
6.83k
  for (xx = px; xx < px + nx; xx++) {
2681
5.68k
    grid_view_get_cell(s->grid, xx, s->cy, &gc);
2682
5.68k
    if (xx == start)
2683
1.15k
      bg = gc.bg;
2684
4.52k
    else if (gc.bg != bg) {
2685
89
      n = xx - start;
2686
89
      log_debug("%s: from %u, size %u", __func__, start, n);
2687
89
      screen_write_collect_insert_clear(ctx, start, n, bg);
2688
89
      start = xx;
2689
89
      bg = gc.bg;
2690
89
    }
2691
5.68k
  }
2692
1.15k
  log_debug("%s: from %u, size %u", __func__, start, xx - start);
2693
1.15k
  screen_write_collect_insert_clear(ctx, start, xx - start, bg);
2694
1.15k
}
2695
2696
/* Finish and store collected cells. */
2697
void
2698
screen_write_collect_end(struct screen_write_ctx *ctx)
2699
603k
{
2700
603k
  struct screen     *s = ctx->s;
2701
603k
  struct screen_write_citem *ci = ctx->item;
2702
603k
  struct screen_write_cline *cl = &s->write_list[s->cy];
2703
603k
  struct grid_cell     gc;
2704
603k
  u_int        xx, bx = 0, bnx = 0;
2705
2706
603k
  if (ci->used == 0)
2707
593k
    return;
2708
2709
9.75k
  ci->x = s->cx;
2710
9.75k
  screen_write_collect_insert(ctx, ci);
2711
2712
9.75k
  log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used,
2713
9.75k
      (int)ci->used, cl->data + ci->x, s->cx, s->cy);
2714
2715
9.75k
  if (s->cx != 0) {
2716
10.5k
    for (xx = s->cx; xx > 0; xx--) {
2717
10.2k
      grid_view_get_cell(s->grid, xx, s->cy, &gc);
2718
10.2k
      if (~gc.flags & GRID_FLAG_PADDING)
2719
6.83k
        break;
2720
3.43k
      screen_write_clear_cell(s->grid, xx, s->cy);
2721
3.43k
      log_debug("%s: padding erased (before) at %u (cx %u)",
2722
3.43k
          __func__, xx, s->cx);
2723
3.43k
    }
2724
7.09k
    if (xx != s->cx) {
2725
678
      if (xx == 0)
2726
259
        grid_view_get_cell(s->grid, 0, s->cy, &gc);
2727
678
      if (gc.data.width > 1 ||
2728
520
          (gc.flags & GRID_FLAG_PADDING)) {
2729
520
        screen_write_clear_cell(s->grid, xx, s->cy);
2730
520
        log_debug("%s: padding erased (before) at %u "
2731
520
            "(cx %u)", __func__, xx, s->cx);
2732
520
      }
2733
678
      bx = xx;
2734
678
      bnx = s->cx - xx;
2735
678
    }
2736
7.09k
  }
2737
2738
#ifdef ENABLE_SIXEL
2739
  if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL)
2740
    ctx->wp->flags |= PANE_REDRAW;
2741
#endif
2742
2743
9.75k
  grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
2744
9.75k
      ci->used);
2745
9.75k
  if (bnx != 0)
2746
678
    screen_write_insert_clears(ctx, bx, bnx);
2747
9.75k
  screen_write_set_cursor(ctx, s->cx + ci->used, -1);
2748
2749
12.0k
  for (xx = s->cx; xx < screen_size_x(s); xx++) {
2750
10.8k
    grid_view_get_cell(s->grid, xx, s->cy, &gc);
2751
10.8k
    if (~gc.flags & GRID_FLAG_PADDING)
2752
8.62k
      break;
2753
2.24k
    screen_write_clear_cell(s->grid, xx, s->cy);
2754
2.24k
    log_debug("%s: padding erased (after) at %u (cx %u)", __func__,
2755
2.24k
        xx, s->cx);
2756
2.24k
  }
2757
9.75k
  if (xx != s->cx)
2758
477
    screen_write_insert_clears(ctx, s->cx, xx - s->cx);
2759
9.75k
}
2760
2761
/* Write cell data, collecting if necessary. */
2762
void
2763
screen_write_collect_add(struct screen_write_ctx *ctx,
2764
    const struct grid_cell *gc)
2765
52.5k
{
2766
52.5k
  struct screen     *s = ctx->s;
2767
52.5k
  struct screen_write_citem *ci;
2768
52.5k
  u_int        sx = screen_size_x(s);
2769
52.5k
  int        collect;
2770
2771
  /*
2772
   * Don't need to check that the attributes and whatnot are still the
2773
   * same - input_parse will end the collection when anything that isn't
2774
   * a plain character is encountered.
2775
   *
2776
   * Without wrapping, collect up to but not including the last column.
2777
   * Leave that cell to screen_write_cell so repeated writes overwrite it
2778
   * and the cursor remains at the right edge.
2779
   */
2780
2781
52.5k
  collect = 1;
2782
52.5k
  if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f)
2783
10.5k
    collect = 0;
2784
41.9k
  else if (gc->flags & GRID_FLAG_TAB)
2785
492
    collect = 0;
2786
41.4k
  else if (gc->attr & GRID_ATTR_CHARSET)
2787
8.55k
    collect = 0;
2788
32.9k
  else if ((~s->mode & MODE_WRAP) &&
2789
2.37k
      s->cx + ctx->item->used >= sx - 1)
2790
1.06k
    collect = 0;
2791
31.8k
  else if (s->mode & MODE_INSERT)
2792
14.6k
    collect = 0;
2793
17.1k
  else if (s->sel != NULL)
2794
0
    collect = 0;
2795
52.5k
  if (!collect) {
2796
35.3k
    screen_write_collect_end(ctx);
2797
35.3k
    screen_write_collect_flush(ctx, 0, __func__);
2798
35.3k
    screen_write_cell(ctx, gc);
2799
35.3k
    return;
2800
35.3k
  }
2801
2802
17.1k
  if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx)
2803
603
    screen_write_collect_end(ctx);
2804
17.1k
  ci = ctx->item; /* may have changed */
2805
2806
17.1k
  if (s->cx > sx - 1) {
2807
603
    log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
2808
603
    ci->wrapped = 1;
2809
603
    screen_write_linefeed(ctx, 1, 8);
2810
603
    screen_write_set_cursor(ctx, 0, -1);
2811
603
  }
2812
2813
17.1k
  if (ci->used == 0)
2814
9.75k
    memcpy(&ci->gc, gc, sizeof ci->gc);
2815
17.1k
  if (ctx->s->write_list[s->cy].data == NULL)
2816
3.56k
    ctx->s->write_list[s->cy].data = xmalloc(screen_size_x(ctx->s));
2817
17.1k
  ctx->s->write_list[s->cy].data[s->cx + ci->used++] = gc->data.data[0];
2818
17.1k
}
2819
2820
/* Write cell data. */
2821
void
2822
screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
2823
35.3k
{
2824
35.3k
  struct screen   *s = ctx->s;
2825
35.3k
  struct window_pane  *wp = ctx->wp;
2826
35.3k
  struct grid   *gd = s->grid;
2827
35.3k
  const struct utf8_data  *ud = &gc->data;
2828
35.3k
  struct grid_line  *gl;
2829
35.3k
  struct grid_cell_entry  *gce;
2830
35.3k
  struct grid_cell   tmp_gc, now_gc;
2831
35.3k
  struct tty_ctx     ttyctx;
2832
35.3k
  u_int      sx = screen_size_x(s), sy = screen_size_y(s);
2833
35.3k
  u_int      width = ud->width, xx, not_wrap, i, n, vis;
2834
35.3k
  int      selected, skip = 1, redraw = 0;
2835
35.3k
  int      yoff = 0, xoff = 0;
2836
35.3k
  struct visible_ranges *r;
2837
35.3k
  struct visible_range  *ri;
2838
2839
  /* Ignore padding cells. */
2840
35.3k
  if (gc->flags & GRID_FLAG_PADDING)
2841
0
    return;
2842
2843
  /* Get the previous cell to check for combining. */
2844
35.3k
  if (screen_write_combine(ctx, gc) != 0)
2845
0
    return;
2846
2847
  /* Flush any existing scrolling. */
2848
35.3k
  screen_write_collect_flush(ctx, 1, __func__);
2849
2850
  /* If this character doesn't fit, ignore it. */
2851
35.3k
  if ((~s->mode & MODE_WRAP) &&
2852
2.58k
      width > 1 &&
2853
324
      (width > sx || (s->cx != sx && s->cx > sx - width)))
2854
0
    return;
2855
2856
  /* If in insert mode, make space for the cells. */
2857
35.3k
  if (s->mode & MODE_INSERT) {
2858
15.5k
    grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8);
2859
15.5k
    skip = 0;
2860
15.5k
  }
2861
2862
  /* Check this will fit on the current line and wrap if not. */
2863
35.3k
  if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
2864
811
    log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
2865
811
    screen_write_linefeed(ctx, 1, 8);
2866
811
    screen_write_set_cursor(ctx, 0, -1);
2867
811
    screen_write_collect_flush(ctx, 0, __func__);
2868
811
  }
2869
2870
  /* Sanity check cursor position. */
2871
35.3k
  if (s->cx > sx - width || s->cy > sy - 1)
2872
636
    return;
2873
34.7k
  screen_write_initctx(ctx, &ttyctx, 0, 0);
2874
2875
  /* Handle overwriting of UTF-8 characters. */
2876
34.7k
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
2877
34.7k
  if (gl->flags & GRID_LINE_EXTENDED) {
2878
25.5k
    grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
2879
25.5k
    if (screen_write_overwrite(ctx, &now_gc, width)) {
2880
744
      redraw = 1;
2881
744
      skip = 0;
2882
744
    }
2883
25.5k
  }
2884
2885
  /*
2886
   * If the new character is UTF-8 wide, fill in padding cells. Have
2887
   * already ensured there is enough room.
2888
   */
2889
58.9k
  for (xx = s->cx + 1; xx < s->cx + width; xx++) {
2890
24.2k
    log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
2891
24.2k
    grid_view_set_padding(gd, xx, s->cy, gc->bg);
2892
24.2k
    skip = 0;
2893
24.2k
  }
2894
2895
  /* If no change, do not draw. */
2896
34.7k
  if (skip) {
2897
15.1k
    if (s->cx >= gl->cellsize)
2898
3.42k
      skip = grid_cells_equal(gc, &grid_default_cell);
2899
11.7k
    else {
2900
11.7k
      gce = &gl->celldata[s->cx];
2901
11.7k
      if (gce->flags & GRID_FLAG_EXTENDED)
2902
1.33k
        skip = 0;
2903
10.3k
      else if (gc->flags != gce->flags)
2904
7.12k
        skip = 0;
2905
3.24k
      else if (gc->attr != gce->data.attr)
2906
1.91k
        skip = 0;
2907
1.32k
      else if (gc->fg != gce->data.fg)
2908
235
        skip = 0;
2909
1.09k
      else if (gc->bg != gce->data.bg)
2910
245
        skip = 0;
2911
845
      else if (gc->data.width != 1)
2912
0
        skip = 0;
2913
845
      else if (gc->data.size != 1)
2914
221
        skip = 0;
2915
624
      else if (gce->data.data != gc->data.data[0])
2916
345
        skip = 0;
2917
11.7k
    }
2918
15.1k
  }
2919
2920
  /* Update the selected flag and set the cell. */
2921
34.7k
  selected = screen_check_selection(s, s->cx, s->cy);
2922
34.7k
  if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
2923
0
    memcpy(&tmp_gc, gc, sizeof tmp_gc);
2924
0
    tmp_gc.flags |= GRID_FLAG_SELECTED;
2925
0
    grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
2926
34.7k
  } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) {
2927
0
    memcpy(&tmp_gc, gc, sizeof tmp_gc);
2928
0
    tmp_gc.flags &= ~GRID_FLAG_SELECTED;
2929
0
    grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
2930
34.7k
  } else if (!skip)
2931
34.2k
    grid_view_set_cell(gd, s->cx, s->cy, gc);
2932
34.7k
  if (selected)
2933
0
    skip = 0;
2934
2935
  /* Get visible ranges for the character before moving the cursor. */
2936
34.7k
  if (wp != NULL) {
2937
34.7k
    xoff = wp->xoff;
2938
34.7k
    yoff = wp->yoff;
2939
34.7k
  }
2940
34.7k
  r = window_visible_ranges(wp, xoff + s->cx, s->cy + yoff, width, NULL);
2941
2942
  /*
2943
   * Move the cursor. If not wrapping, stick at the last character and
2944
   * replace it.
2945
   */
2946
34.7k
  not_wrap = !(s->mode & MODE_WRAP);
2947
34.7k
  if (s->cx <= sx - not_wrap - width)
2948
33.4k
    screen_write_set_cursor(ctx, s->cx + width, -1);
2949
1.21k
  else
2950
1.21k
    screen_write_set_cursor(ctx, sx - not_wrap, -1);
2951
2952
  /* Create space for character in insert mode. */
2953
34.7k
  if (s->mode & MODE_INSERT) {
2954
15.1k
    screen_write_collect_flush(ctx, 0, __func__);
2955
15.1k
    if (wp != NULL && screen_write_pane_is_obscured(ctx)) {
2956
0
      if (screen_write_should_draw_line(ctx, s->cy))
2957
0
        screen_write_redraw_line(ctx, &ttyctx, s->cy);
2958
0
      return;
2959
0
    }
2960
15.1k
    ttyctx.n = width;
2961
15.1k
    if (screen_write_should_draw_line(ctx, s->cy))
2962
14.2k
      tty_write(tty_cmd_insertcharacter, &ttyctx);
2963
15.1k
  }
2964
2965
  /* If not writing, done now. */
2966
34.7k
  if (skip || !screen_write_should_draw_line(ctx, s->cy))
2967
2.27k
    return;
2968
2969
  /* Do a full line redraw if needed. */
2970
32.4k
  if (redraw && wp != NULL) {
2971
743
    screen_write_redraw_line(ctx, &ttyctx, s->cy);
2972
743
    return;
2973
743
  }
2974
2975
  /* Work out the cell attributes. */
2976
31.6k
  if (selected)
2977
0
    screen_select_cell(s, &tmp_gc, gc);
2978
31.6k
  else
2979
31.6k
    memcpy(&tmp_gc, gc, sizeof tmp_gc);
2980
31.6k
  ttyctx.cell = &tmp_gc;
2981
2982
  /* If the cell is fully visible, it can be written entirely. */
2983
63.3k
  for (i = 0, vis = 0; i < r->used; i++)
2984
31.6k
    vis += r->ranges[i].nx;
2985
31.6k
  if (vis >= width) {
2986
31.6k
    if (screen_write_should_draw_line(ctx, s->cy))
2987
31.6k
      tty_write(tty_cmd_cell, &ttyctx);
2988
31.6k
    return;
2989
31.6k
  }
2990
2991
  /*
2992
   * Otherwise this is a wide character or tab partly obscured. Write
2993
   * spaces in the visible regions.
2994
   */
2995
0
  utf8_set(&tmp_gc.data, ' ');
2996
0
  if (!screen_write_should_draw_line(ctx, s->cy))
2997
0
    return;
2998
0
  for (i = 0; i < r->used; i++) {
2999
0
    ri = &r->ranges[i];
3000
0
    if (ri->nx == 0)
3001
0
      continue;
3002
0
    for (n = 0; n < ri->nx; n++) {
3003
0
      ttyctx.ocx = (int)ri->px - xoff + (int)n;
3004
0
      tty_write(tty_cmd_cell, &ttyctx);
3005
0
    }
3006
0
  }
3007
0
}
3008
3009
/* Combine a UTF-8 zero-width character onto the previous if necessary. */
3010
static int
3011
screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
3012
35.3k
{
3013
35.3k
  struct screen   *s = ctx->s;
3014
35.3k
  struct window_pane  *wp = ctx->wp;
3015
35.3k
  struct grid   *gd = s->grid;
3016
35.3k
  const struct utf8_data  *ud = &gc->data;
3017
35.3k
  struct options    *oo = global_options;
3018
35.3k
  u_int      i, n, cx = s->cx, cy = s->cy, vis;
3019
35.3k
  struct grid_cell   last;
3020
35.3k
  struct tty_ctx     ttyctx;
3021
35.3k
  int      force_wide = 0, zero_width = 0;
3022
35.3k
  int      xoff = 0, yoff = 0;
3023
35.3k
  struct visible_ranges *r;
3024
3025
  /* Ignore U+3164 HANGUL_FILLER entirely. */
3026
35.3k
  if (utf8_is_hangul_filler(ud))
3027
0
    return (1);
3028
3029
  /*
3030
   * Is this character which makes no sense without being combined? If
3031
   * this is true then flag it here and discard the character (return 1)
3032
   * if we cannot combine it.
3033
   */
3034
35.3k
  if (utf8_is_zwj(ud))
3035
0
    zero_width = 1;
3036
35.3k
  else if (utf8_is_vs(ud)) {
3037
0
    zero_width = 1;
3038
0
    if (options_get_number(oo, "variation-selector-always-wide"))
3039
0
      force_wide = 1;
3040
35.3k
  } else if (ud->width == 0)
3041
0
    zero_width = 1;
3042
3043
  /* Cannot combine empty character or at left. */
3044
35.3k
  if (ud->size < 2 || cx == 0)
3045
26.3k
    return (zero_width);
3046
8.99k
  log_debug("%s: character %.*s at %u,%u (width %u)", __func__,
3047
8.99k
      (int)ud->size, ud->data, cx, cy, ud->width);
3048
3049
  /* Find the cell to combine with. */
3050
8.99k
  n = 1;
3051
8.99k
  grid_view_get_cell(gd, cx - n, cy, &last);
3052
8.99k
  if (cx != 1 && (last.flags & GRID_FLAG_PADDING)) {
3053
1.94k
    n = 2;
3054
1.94k
    grid_view_get_cell(gd, cx - n, cy, &last);
3055
1.94k
  }
3056
8.99k
  if (n != last.data.width || (last.flags & GRID_FLAG_PADDING))
3057
2.13k
    return (zero_width);
3058
3059
  /*
3060
   * Check if we need to combine characters. This could be a Korean
3061
   * Hangul Jamo character, zero width (set above), a modifier character
3062
   * (with an existing Unicode character) or a previous ZWJ.
3063
   */
3064
6.85k
  if (!zero_width) {
3065
6.85k
    switch (hanguljamo_check_state(&last.data, ud)) {
3066
0
    case HANGULJAMO_STATE_NOT_COMPOSABLE:
3067
0
      return (1);
3068
0
    case HANGULJAMO_STATE_CHOSEONG:
3069
0
      return (0);
3070
0
    case HANGULJAMO_STATE_COMPOSABLE:
3071
0
      break;
3072
6.85k
    case HANGULJAMO_STATE_NOT_HANGULJAMO:
3073
6.85k
      if (utf8_should_combine(&last.data, ud))
3074
0
        force_wide = 1;
3075
6.85k
      else if (utf8_should_combine(ud, &last.data))
3076
0
        force_wide = 1;
3077
6.85k
      else if (!utf8_has_zwj(&last.data))
3078
6.85k
        return (0);
3079
0
      break;
3080
6.85k
    }
3081
6.85k
  }
3082
3083
  /* Check if this combined character would be too long. */
3084
0
  if (last.data.size + ud->size > sizeof last.data.data)
3085
0
    return (zero_width);
3086
3087
  /* Combining; flush any pending output. */
3088
0
  screen_write_collect_flush(ctx, 0, __func__);
3089
3090
0
  log_debug("%s: %.*s -> %.*s at %u,%u (offset %u, width %u)", __func__,
3091
0
      (int)ud->size, ud->data, (int)last.data.size, last.data.data,
3092
0
      cx - n, cy, n, last.data.width);
3093
3094
  /* Append the data. */
3095
0
  memcpy(last.data.data + last.data.size, ud->data, ud->size);
3096
0
  last.data.size += ud->size;
3097
3098
  /* Force the width to 2 for modifiers and variation selector. */
3099
0
  if (last.data.width == 1 && force_wide) {
3100
0
    last.data.width = 2;
3101
0
    n = 2;
3102
0
    cx++;
3103
0
  } else
3104
0
    force_wide = 0;
3105
3106
  /* Set the new cell. */
3107
0
  grid_view_set_cell(gd, cx - n, cy, &last);
3108
0
  if (force_wide)
3109
0
    grid_view_set_padding(gd, cx - 1, cy, last.bg);
3110
3111
  /*
3112
   * Check if all of this character is visible. No character will be
3113
   * obscured in the middle, only on left or right, but there could be an
3114
   * empty range in the visible ranges so we add them all up.
3115
   */
3116
0
  if (wp != NULL) {
3117
0
    xoff = wp->xoff;
3118
0
    yoff = wp->yoff;
3119
0
  }
3120
0
  r = window_visible_ranges(wp, xoff + cx - n, cy + yoff, n, NULL);
3121
0
  for (i = 0, vis = 0; i < r->used; i++)
3122
0
    vis += r->ranges[i].nx;
3123
0
  if (vis < n) {
3124
    /*
3125
     * Part of this character is obscured. Return 1 and let
3126
     * screen_write_cell write a space.
3127
     */
3128
0
    return (1);
3129
0
  }
3130
3131
  /*
3132
   * Redraw the combined cell. If forcing the cell to width 2, reset the
3133
   * cached cursor position in the tty, since we don't really know
3134
   * whether the terminal thought the character was width 1 or width 2
3135
   * and what it is going to do now.
3136
   */
3137
0
  screen_write_set_cursor(ctx, cx - n, cy);
3138
0
  screen_write_initctx(ctx, &ttyctx, 0, 0);
3139
0
  ttyctx.cell = &last;
3140
0
  if (force_wide)
3141
0
    ttyctx.flags |= TTY_CTX_CELL_INVALIDATE;
3142
0
  if (screen_write_should_draw_line(ctx, cy))
3143
0
    tty_write(tty_cmd_cell, &ttyctx);
3144
0
  screen_write_set_cursor(ctx, cx, cy);
3145
3146
0
  return (1);
3147
0
}
3148
3149
/*
3150
 * UTF-8 wide characters are a bit of an annoyance. They take up more than one
3151
 * cell on the screen, so following cells must not be drawn by marking them as
3152
 * padding.
3153
 *
3154
 * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
3155
 * character, it is necessary to also overwrite any other cells which covered
3156
 * by the same character.
3157
 */
3158
static int
3159
screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
3160
    u_int width)
3161
25.5k
{
3162
25.5k
  struct screen   *s = ctx->s;
3163
25.5k
  struct grid   *gd = s->grid;
3164
25.5k
  struct grid_cell   tmp_gc;
3165
25.5k
  u_int      xx;
3166
25.5k
  int      done = 0;
3167
3168
25.5k
  if (gc->flags & GRID_FLAG_PADDING) {
3169
    /*
3170
     * A padding cell, so clear any following and leading padding
3171
     * cells back to the character. Don't overwrite the current
3172
     * cell as that happens later anyway.
3173
     */
3174
408
    xx = s->cx + 1;
3175
2.24k
    while (--xx > 0) {
3176
2.09k
      grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
3177
2.09k
      if (~tmp_gc.flags & GRID_FLAG_PADDING)
3178
263
        break;
3179
1.83k
      log_debug("%s: padding at %u,%u", __func__, xx, s->cy);
3180
1.83k
      screen_write_clear_cell(gd, xx, s->cy);
3181
1.83k
    }
3182
3183
    /* Overwrite the character at the start of this padding. */
3184
408
    log_debug("%s: character at %u,%u", __func__, xx, s->cy);
3185
408
    screen_write_clear_cell(gd, xx, s->cy);
3186
408
    done = 1;
3187
408
  }
3188
3189
  /*
3190
   * Overwrite any padding cells that belong to any UTF-8 characters
3191
   * we'll be overwriting with the current character.
3192
   */
3193
25.5k
  if (width != 1 ||
3194
22.5k
      gc->data.width != 1 ||
3195
22.2k
      gc->flags & GRID_FLAG_PADDING) {
3196
3.76k
    xx = s->cx + width - 1;
3197
6.06k
    while (++xx < screen_size_x(s)) {
3198
5.93k
      grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
3199
5.93k
      if (~tmp_gc.flags & GRID_FLAG_PADDING)
3200
3.62k
        break;
3201
2.30k
      log_debug("%s: overwrite at %u,%u", __func__, xx,
3202
2.30k
          s->cy);
3203
2.30k
      screen_write_clear_cell(gd, xx, s->cy);
3204
2.30k
      done = 1;
3205
2.30k
    }
3206
3.76k
  }
3207
3208
25.5k
  return (done);
3209
25.5k
}
3210
3211
/* Set external clipboard. */
3212
void
3213
screen_write_setselection(struct screen_write_ctx *ctx, const char *clip,
3214
    u_char *str, u_int len)
3215
1.12k
{
3216
1.12k
  struct tty_ctx  ttyctx;
3217
3218
1.12k
  screen_write_initctx(ctx, &ttyctx, 0, 0);
3219
1.12k
  ttyctx.sel.clip = clip;
3220
1.12k
  ttyctx.sel.data = str;
3221
1.12k
  ttyctx.sel.size = len;
3222
3223
1.12k
  tty_write(tty_cmd_setselection, &ttyctx);
3224
1.12k
}
3225
3226
/* Write unmodified string. */
3227
void
3228
screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
3229
    int allow_invisible_panes)
3230
0
{
3231
0
  struct tty_ctx  ttyctx;
3232
3233
0
  screen_write_initctx(ctx, &ttyctx, 0, 0);
3234
0
  if (allow_invisible_panes)
3235
0
    ttyctx.flags |= TTY_CTX_INVISIBLE_PANES;
3236
0
  ttyctx.data.data = str;
3237
0
  ttyctx.data.size = len;
3238
3239
0
  tty_write(tty_cmd_rawstring, &ttyctx);
3240
0
}
3241
3242
#ifdef ENABLE_SIXEL
3243
/* Write a SIXEL image. */
3244
void
3245
screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
3246
    u_int bg)
3247
{
3248
  struct screen   *s = ctx->s;
3249
  struct grid   *gd = s->grid;
3250
  struct tty_ctx     ttyctx;
3251
  u_int      x, y, sx, sy, cx = s->cx, cy = s->cy, i, lines;
3252
  struct sixel_image  *new;
3253
3254
  if (screen_size_y(s) == 1)
3255
    return;
3256
3257
  sixel_size_in_cells(si, &x, &y);
3258
  if (x > screen_size_x(s) || y > screen_size_y(s) - 1) {
3259
    if (x > screen_size_x(s) - cx)
3260
      sx = screen_size_x(s) - cx;
3261
    else
3262
      sx = x;
3263
    if (y > screen_size_y(s) - 1)
3264
      sy = screen_size_y(s) - 1;
3265
    else
3266
      sy = y;
3267
    new = sixel_scale(si, 0, 0, 0, y - sy, sx, sy, 1);
3268
    sixel_free(si);
3269
    if (new == NULL)
3270
      return;
3271
    sixel_size_in_cells(new, &x, &y);
3272
    si = new;
3273
  }
3274
3275
  sy = screen_size_y(s) - cy;
3276
  if (sy <= y) {
3277
    lines = y - sy + 1;
3278
    if (image_scroll_up(s, lines) && ctx->wp != NULL)
3279
      ctx->wp->flags |= PANE_REDRAW;
3280
    for (i = 0; i < lines; i++) {
3281
      grid_view_scroll_region_up(gd, 0, screen_size_y(s) - 1,
3282
          bg);
3283
      screen_write_collect_scroll(ctx, bg);
3284
    }
3285
    ctx->scrolled += lines;
3286
    if (lines > cy)
3287
      screen_write_cursormove(ctx, -1, 0, 0);
3288
    else
3289
      screen_write_cursormove(ctx, -1, cy - lines, 0);
3290
  }
3291
  screen_write_collect_flush(ctx, 0, __func__);
3292
3293
  screen_write_initctx(ctx, &ttyctx, 0, 0);
3294
  ttyctx.image = image_store(s, si);
3295
3296
  tty_write(tty_cmd_sixelimage, &ttyctx);
3297
3298
  screen_write_cursormove(ctx, 0, cy + y, 0);
3299
}
3300
#endif
3301
3302
/* Turn alternate screen on. */
3303
void
3304
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
3305
    int cursor)
3306
937
{
3307
937
  struct tty_ctx       ttyctx;
3308
937
  struct window_pane    *wp = ctx->wp;
3309
3310
937
  if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
3311
0
    return;
3312
3313
937
  screen_write_collect_flush(ctx, 0, __func__);
3314
937
  if (!screen_alternate_on(ctx->s, gc, cursor))
3315
492
    return;
3316
3317
445
  if (wp != NULL) {
3318
445
    window_pane_clear_resizes(wp, NULL);
3319
445
    if (event_initialized(&wp->resize_timer))
3320
445
      evtimer_del(&wp->resize_timer);
3321
445
    layout_fix_panes(wp->window, NULL);
3322
445
    if (!TAILQ_EMPTY(&wp->resize_queue)) {
3323
0
      window_pane_send_resize(wp, wp->sx, wp->sy);
3324
0
      window_pane_clear_resizes(wp, NULL);
3325
0
    }
3326
445
    server_redraw_window_borders(wp->window);
3327
445
  }
3328
3329
445
  screen_write_initctx(ctx, &ttyctx, 1, 0);
3330
445
  if (ttyctx.redraw_cb != NULL)
3331
445
    ttyctx.redraw_cb(&ttyctx, 0, ttyctx.sy);
3332
445
}
3333
3334
/* Turn alternate screen off. */
3335
void
3336
screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
3337
    int cursor)
3338
827
{
3339
827
  struct tty_ctx     ttyctx;
3340
827
  struct window_pane  *wp = ctx->wp;
3341
3342
827
  if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
3343
0
    return;
3344
3345
827
  screen_write_collect_flush(ctx, 0, __func__);
3346
827
  if (!screen_alternate_off(ctx->s, gc, cursor))
3347
456
    return;
3348
3349
371
  if (wp != NULL) {
3350
371
    layout_fix_panes(wp->window, NULL);
3351
371
    server_redraw_window_borders(wp->window);
3352
371
  }
3353
3354
371
  screen_write_initctx(ctx, &ttyctx, 1, 0);
3355
371
  if (ttyctx.redraw_cb != NULL)
3356
371
    ttyctx.redraw_cb(&ttyctx, 0, ttyctx.sy);
3357
371
}