Coverage Report

Created: 2026-09-01 06:24

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