Coverage Report

Created: 2026-07-30 07:05

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