Coverage Report

Created: 2025-11-19 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/screen-write.c
Line
Count
Source
1
/* $OpenBSD$ */
2
3
/*
4
 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
21
#include <stdlib.h>
22
#include <string.h>
23
24
#include "tmux.h"
25
26
static 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_clear(struct screen_write_ctx *, u_int,
29
        u_int);
30
static void screen_write_collect_scroll(struct screen_write_ctx *, u_int);
31
static void screen_write_collect_flush(struct screen_write_ctx *, int,
32
        const char *);
33
static int  screen_write_overwrite(struct screen_write_ctx *,
34
        struct grid_cell *, u_int);
35
static int  screen_write_combine(struct screen_write_ctx *,
36
        const struct grid_cell *);
37
38
struct screen_write_citem {
39
  u_int       x;
40
  int       wrapped;
41
42
  enum { TEXT, CLEAR }    type;
43
  u_int       used;
44
  u_int       bg;
45
46
  struct grid_cell    gc;
47
48
  TAILQ_ENTRY(screen_write_citem) entry;
49
};
50
struct screen_write_cline {
51
  char        *data;
52
  TAILQ_HEAD(, screen_write_citem) items;
53
};
54
TAILQ_HEAD(, screen_write_citem)  screen_write_citem_freelist =
55
    TAILQ_HEAD_INITIALIZER(screen_write_citem_freelist);
56
57
static struct screen_write_citem *
58
screen_write_get_citem(void)
59
41.7k
{
60
41.7k
    struct screen_write_citem *ci;
61
62
41.7k
    ci = TAILQ_FIRST(&screen_write_citem_freelist);
63
41.7k
    if (ci != NULL) {
64
41.3k
        TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry);
65
41.3k
        memset(ci, 0, sizeof *ci);
66
41.3k
        return (ci);
67
41.3k
    }
68
341
    return (xcalloc(1, sizeof *ci));
69
41.7k
}
70
71
static void
72
screen_write_free_citem(struct screen_write_citem *ci)
73
29.4k
{
74
29.4k
    TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry);
75
29.4k
}
76
77
static void
78
screen_write_offset_timer(__unused int fd, __unused short events, void *data)
79
0
{
80
0
  struct window *w = data;
81
82
0
  tty_update_window_offset(w);
83
0
}
84
85
/* Set cursor position. */
86
static void
87
screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy)
88
67.9k
{
89
67.9k
  struct window_pane  *wp = ctx->wp;
90
67.9k
  struct window   *w;
91
67.9k
  struct screen   *s = ctx->s;
92
67.9k
  struct timeval     tv = { .tv_usec = 10000 };
93
94
67.9k
  if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy)
95
8.13k
    return;
96
97
59.8k
  if (cx != -1) {
98
54.7k
    if ((u_int)cx > screen_size_x(s)) /* allow last column */
99
106
      cx = screen_size_x(s) - 1;
100
54.7k
    s->cx = cx;
101
54.7k
  }
102
59.8k
  if (cy != -1) {
103
24.7k
    if ((u_int)cy > screen_size_y(s) - 1)
104
0
      cy = screen_size_y(s) - 1;
105
24.7k
    s->cy = cy;
106
24.7k
  }
107
108
59.8k
  if (wp == NULL)
109
0
    return;
110
59.8k
  w = wp->window;
111
112
59.8k
  if (!event_initialized(&w->offset_timer))
113
59.8k
    evtimer_set(&w->offset_timer, screen_write_offset_timer, w);
114
59.8k
  if (!evtimer_pending(&w->offset_timer, NULL))
115
59.8k
    evtimer_add(&w->offset_timer, &tv);
116
59.8k
}
117
118
/* Do a full redraw. */
119
static void
120
screen_write_redraw_cb(const struct tty_ctx *ttyctx)
121
2.71k
{
122
2.71k
  struct window_pane  *wp = ttyctx->arg;
123
124
2.71k
  if (wp != NULL)
125
2.71k
    wp->flags |= PANE_REDRAW;
126
2.71k
}
127
128
/* Update context for client. */
129
static int
130
screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
131
0
{
132
0
  struct window_pane  *wp = ttyctx->arg;
133
134
0
  if (ttyctx->allow_invisible_panes) {
135
0
    if (session_has(c->session, wp->window))
136
0
      return (1);
137
0
    return (0);
138
0
  }
139
140
0
  if (c->session->curw->window != wp->window)
141
0
    return (0);
142
0
  if (wp->layout_cell == NULL)
143
0
    return (0);
144
145
0
  if (wp->flags & (PANE_REDRAW|PANE_DROP))
146
0
    return (-1);
147
0
  if (c->flags & CLIENT_REDRAWPANES) {
148
    /*
149
     * Redraw is already deferred to redraw another pane - redraw
150
     * this one also when that happens.
151
     */
152
0
    log_debug("%s: adding %%%u to deferred redraw", __func__,
153
0
        wp->id);
154
0
    wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR);
155
0
    return (-1);
156
0
  }
157
158
0
  ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy,
159
0
      &ttyctx->wsx, &ttyctx->wsy);
160
161
0
  ttyctx->xoff = ttyctx->rxoff = wp->xoff;
162
0
  ttyctx->yoff = ttyctx->ryoff = wp->yoff;
163
164
0
  if (status_at_line(c) == 0)
165
0
    ttyctx->yoff += status_line_size(c);
166
167
0
  return (1);
168
0
}
169
170
/* Set up context for TTY command. */
171
static void
172
screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
173
    int sync)
174
49.7k
{
175
49.7k
  struct screen *s = ctx->s;
176
177
49.7k
  memset(ttyctx, 0, sizeof *ttyctx);
178
179
49.7k
  ttyctx->s = s;
180
49.7k
  ttyctx->sx = screen_size_x(s);
181
49.7k
  ttyctx->sy = screen_size_y(s);
182
183
49.7k
  ttyctx->ocx = s->cx;
184
49.7k
  ttyctx->ocy = s->cy;
185
49.7k
  ttyctx->orlower = s->rlower;
186
49.7k
  ttyctx->orupper = s->rupper;
187
188
49.7k
  memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults);
189
49.7k
  if (ctx->init_ctx_cb != NULL) {
190
0
    ctx->init_ctx_cb(ctx, ttyctx);
191
0
    if (ttyctx->palette != NULL) {
192
0
      if (ttyctx->defaults.fg == 8)
193
0
        ttyctx->defaults.fg = ttyctx->palette->fg;
194
0
      if (ttyctx->defaults.bg == 8)
195
0
        ttyctx->defaults.bg = ttyctx->palette->bg;
196
0
    }
197
49.7k
  } else {
198
49.7k
    ttyctx->redraw_cb = screen_write_redraw_cb;
199
49.7k
    if (ctx->wp != NULL) {
200
49.7k
      tty_default_colours(&ttyctx->defaults, ctx->wp);
201
49.7k
      ttyctx->palette = &ctx->wp->palette;
202
49.7k
      ttyctx->set_client_cb = screen_write_set_client_cb;
203
49.7k
      ttyctx->arg = ctx->wp;
204
49.7k
    }
205
49.7k
  }
206
207
49.7k
  if (~ctx->flags & SCREEN_WRITE_SYNC) {
208
    /*
209
     * For the active pane or for an overlay (no pane), we want to
210
     * only use synchronized updates if requested (commands that
211
     * move the cursor); for other panes, always use it, since the
212
     * cursor will have to move.
213
     */
214
5.04k
    if (ctx->wp != NULL) {
215
5.04k
      if (ctx->wp != ctx->wp->window->active)
216
5.04k
        ttyctx->num = 1;
217
0
      else
218
0
        ttyctx->num = sync;
219
5.04k
    } else
220
0
      ttyctx->num = 0x10|sync;
221
5.04k
    tty_write(tty_cmd_syncstart, ttyctx);
222
5.04k
    ctx->flags |= SCREEN_WRITE_SYNC;
223
5.04k
  }
224
49.7k
}
225
226
/* Make write list. */
227
void
228
screen_write_make_list(struct screen *s)
229
12.1k
{
230
12.1k
  u_int y;
231
232
12.1k
  s->write_list = xcalloc(screen_size_y(s), sizeof *s->write_list);
233
316k
  for (y = 0; y < screen_size_y(s); y++)
234
303k
    TAILQ_INIT(&s->write_list[y].items);
235
12.1k
}
236
237
/* Free write list. */
238
void
239
screen_write_free_list(struct screen *s)
240
12.1k
{
241
12.1k
  u_int y;
242
243
316k
  for (y = 0; y < screen_size_y(s); y++)
244
303k
    free(s->write_list[y].data);
245
12.1k
  free(s->write_list);
246
12.1k
}
247
248
/* Set up for writing. */
249
static void
250
screen_write_init(struct screen_write_ctx *ctx, struct screen *s)
251
12.7k
{
252
12.7k
  memset(ctx, 0, sizeof *ctx);
253
254
12.7k
  ctx->s = s;
255
256
12.7k
  if (ctx->s->write_list == NULL)
257
11.5k
    screen_write_make_list(ctx->s);
258
12.7k
  ctx->item = screen_write_get_citem();
259
260
12.7k
  ctx->scrolled = 0;
261
12.7k
  ctx->bg = 8;
262
12.7k
}
263
264
/* Initialize writing with a pane. */
265
void
266
screen_write_start_pane(struct screen_write_ctx *ctx, struct window_pane *wp,
267
    struct screen *s)
268
12.7k
{
269
12.7k
  if (s == NULL)
270
1.23k
    s = wp->screen;
271
12.7k
  screen_write_init(ctx, s);
272
12.7k
  ctx->wp = wp;
273
274
12.7k
  if (log_get_level() != 0) {
275
0
    log_debug("%s: size %ux%u, pane %%%u (at %u,%u)",
276
0
        __func__, screen_size_x(ctx->s), screen_size_y(ctx->s),
277
0
        wp->id, wp->xoff, wp->yoff);
278
0
  }
279
12.7k
}
280
281
/* Initialize writing with a callback. */
282
void
283
screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s,
284
    screen_write_init_ctx_cb cb, void *arg)
285
0
{
286
0
  screen_write_init(ctx, s);
287
288
0
  ctx->init_ctx_cb = cb;
289
0
  ctx->arg = arg;
290
291
0
  if (log_get_level() != 0) {
292
0
    log_debug("%s: size %ux%u, with callback", __func__,
293
0
        screen_size_x(ctx->s), screen_size_y(ctx->s));
294
0
  }
295
0
}
296
297
/* Initialize writing. */
298
void
299
screen_write_start(struct screen_write_ctx *ctx, struct screen *s)
300
0
{
301
0
  screen_write_init(ctx, s);
302
303
0
  if (log_get_level() != 0) {
304
0
    log_debug("%s: size %ux%u, no pane", __func__,
305
0
        screen_size_x(ctx->s), screen_size_y(ctx->s));
306
0
  }
307
0
}
308
309
/* Finish writing. */
310
void
311
screen_write_stop(struct screen_write_ctx *ctx)
312
12.7k
{
313
12.7k
  screen_write_collect_end(ctx);
314
12.7k
  screen_write_collect_flush(ctx, 0, __func__);
315
316
12.7k
  screen_write_free_citem(ctx->item);
317
12.7k
}
318
319
/* Reset screen state. */
320
void
321
screen_write_reset(struct screen_write_ctx *ctx)
322
803
{
323
803
  struct screen *s = ctx->s;
324
325
803
  screen_reset_tabs(s);
326
803
  screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1);
327
328
803
  s->mode = MODE_CURSOR|MODE_WRAP;
329
330
803
  if (options_get_number(global_options, "extended-keys") == 2)
331
0
    s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED;
332
333
803
  screen_write_clearscreen(ctx, 8);
334
803
  screen_write_set_cursor(ctx, 0, 0);
335
803
}
336
337
/* Write character. */
338
void
339
screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
340
    u_char ch)
341
0
{
342
0
  struct grid_cell  gc;
343
344
0
  memcpy(&gc, gcp, sizeof gc);
345
346
0
  utf8_set(&gc.data, ch);
347
0
  screen_write_cell(ctx, &gc);
348
0
}
349
350
/* Calculate string length. */
351
size_t
352
screen_write_strlen(const char *fmt, ...)
353
0
{
354
0
  va_list     ap;
355
0
  char                   *msg;
356
0
  struct utf8_data  ud;
357
0
  u_char                 *ptr;
358
0
  size_t      left, size = 0;
359
0
  enum utf8_state   more;
360
361
0
  va_start(ap, fmt);
362
0
  xvasprintf(&msg, fmt, ap);
363
0
  va_end(ap);
364
365
0
  ptr = msg;
366
0
  while (*ptr != '\0') {
367
0
    if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) {
368
0
      ptr++;
369
370
0
      left = strlen(ptr);
371
0
      if (left < (size_t)ud.size - 1)
372
0
        break;
373
0
      while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE)
374
0
        ptr++;
375
0
      ptr++;
376
377
0
      if (more == UTF8_DONE)
378
0
        size += ud.width;
379
0
    } else {
380
0
      if (*ptr == '\t' || (*ptr > 0x1f && *ptr < 0x7f))
381
0
        size++;
382
0
      ptr++;
383
0
    }
384
0
  }
385
386
0
  free(msg);
387
0
  return (size);
388
0
}
389
390
/* Write string wrapped over lines. */
391
int
392
screen_write_text(struct screen_write_ctx *ctx, u_int cx, u_int width,
393
    u_int lines, int more, const struct grid_cell *gcp, const char *fmt, ...)
394
0
{
395
0
  struct screen   *s = ctx->s;
396
0
  va_list      ap;
397
0
  char      *tmp;
398
0
  u_int      cy = s->cy, i, end, next, idx = 0, at, left;
399
0
  struct utf8_data  *text;
400
0
  struct grid_cell   gc;
401
402
0
  memcpy(&gc, gcp, sizeof gc);
403
404
0
  va_start(ap, fmt);
405
0
  xvasprintf(&tmp, fmt, ap);
406
0
  va_end(ap);
407
408
0
  text = utf8_fromcstr(tmp);
409
0
  free(tmp);
410
411
0
  left = (cx + width) - s->cx;
412
0
  for (;;) {
413
    /* Find the end of what can fit on the line. */
414
0
    at = 0;
415
0
    for (end = idx; text[end].size != 0; end++) {
416
0
      if (text[end].size == 1 && text[end].data[0] == '\n')
417
0
        break;
418
0
      if (at + text[end].width > left)
419
0
        break;
420
0
      at += text[end].width;
421
0
    }
422
423
    /*
424
     * If we're on a space, that's the end. If not, walk back to
425
     * try and find one.
426
     */
427
0
    if (text[end].size == 0)
428
0
      next = end;
429
0
    else if (text[end].size == 1 && text[end].data[0] == '\n')
430
0
      next = end + 1;
431
0
    else if (text[end].size == 1 && text[end].data[0] == ' ')
432
0
      next = end + 1;
433
0
    else {
434
0
      for (i = end; i > idx; i--) {
435
0
        if (text[i].size == 1 && text[i].data[0] == ' ')
436
0
          break;
437
0
      }
438
0
      if (i != idx) {
439
0
        next = i + 1;
440
0
        end = i;
441
0
      } else
442
0
        next = end;
443
0
    }
444
445
    /* Print the line. */
446
0
    for (i = idx; i < end; i++) {
447
0
      utf8_copy(&gc.data, &text[i]);
448
0
      screen_write_cell(ctx, &gc);
449
0
    }
450
451
    /* If at the bottom, stop. */
452
0
    idx = next;
453
0
    if (s->cy == cy + lines - 1 || text[idx].size == 0)
454
0
      break;
455
456
0
    screen_write_cursormove(ctx, cx, s->cy + 1, 0);
457
0
    left = width;
458
0
  }
459
460
  /*
461
   * Fail if on the last line and there is more to come or at the end, or
462
   * if the text was not entirely consumed.
463
   */
464
0
  if ((s->cy == cy + lines - 1 && (!more || s->cx == cx + width)) ||
465
0
      text[idx].size != 0) {
466
0
    free(text);
467
0
    return (0);
468
0
  }
469
0
  free(text);
470
471
  /*
472
   * If no more to come, move to the next line. Otherwise, leave on
473
   * the same line (except if at the end).
474
   */
475
0
  if (!more || s->cx == cx + width)
476
0
    screen_write_cursormove(ctx, cx, s->cy + 1, 0);
477
0
  return (1);
478
0
}
479
480
/* Write simple string (no maximum length). */
481
void
482
screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
483
    const char *fmt, ...)
484
0
{
485
0
  va_list ap;
486
487
0
  va_start(ap, fmt);
488
0
  screen_write_vnputs(ctx, -1, gcp, fmt, ap);
489
0
  va_end(ap);
490
0
}
491
492
/* Write string with length limit (-1 for unlimited). */
493
void
494
screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen,
495
    const struct grid_cell *gcp, const char *fmt, ...)
496
0
{
497
0
  va_list ap;
498
499
0
  va_start(ap, fmt);
500
0
  screen_write_vnputs(ctx, maxlen, gcp, fmt, ap);
501
0
  va_end(ap);
502
0
}
503
504
void
505
screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
506
    const struct grid_cell *gcp, const char *fmt, va_list ap)
507
0
{
508
0
  struct grid_cell  gc;
509
0
  struct utf8_data       *ud = &gc.data;
510
0
  char             *msg;
511
0
  u_char           *ptr;
512
0
  size_t      left, size = 0;
513
0
  enum utf8_state   more;
514
515
0
  memcpy(&gc, gcp, sizeof gc);
516
0
  xvasprintf(&msg, fmt, ap);
517
518
0
  ptr = msg;
519
0
  while (*ptr != '\0') {
520
0
    if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) {
521
0
      ptr++;
522
523
0
      left = strlen(ptr);
524
0
      if (left < (size_t)ud->size - 1)
525
0
        break;
526
0
      while ((more = utf8_append(ud, *ptr)) == UTF8_MORE)
527
0
        ptr++;
528
0
      ptr++;
529
530
0
      if (more != UTF8_DONE)
531
0
        continue;
532
0
      if (maxlen > 0 && size + ud->width > (size_t)maxlen) {
533
0
        while (size < (size_t)maxlen) {
534
0
          screen_write_putc(ctx, &gc, ' ');
535
0
          size++;
536
0
        }
537
0
        break;
538
0
      }
539
0
      size += ud->width;
540
0
      screen_write_cell(ctx, &gc);
541
0
    } else {
542
0
      if (maxlen > 0 && size + 1 > (size_t)maxlen)
543
0
        break;
544
545
0
      if (*ptr == '\001')
546
0
        gc.attr ^= GRID_ATTR_CHARSET;
547
0
      else if (*ptr == '\n') {
548
0
        screen_write_linefeed(ctx, 0, 8);
549
0
        screen_write_carriagereturn(ctx);
550
0
      } else if (*ptr == '\t' || (*ptr > 0x1f && *ptr < 0x7f)) {
551
0
        size++;
552
0
        screen_write_putc(ctx, &gc, *ptr);
553
0
      }
554
0
      ptr++;
555
0
    }
556
0
  }
557
558
0
  free(msg);
559
0
}
560
561
/*
562
 * Copy from another screen but without the selection stuff. Assumes the target
563
 * region is already big enough.
564
 */
565
void
566
screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
567
    u_int px, u_int py, u_int nx, u_int ny)
568
0
{
569
0
  struct screen   *s = ctx->s;
570
0
  struct window_pane  *wp = ctx->wp;
571
0
  struct tty_ctx     ttyctx;
572
0
  struct grid   *gd = src->grid;
573
0
  struct grid_cell   gc;
574
0
  u_int      xx, yy, cx = s->cx, cy = s->cy;
575
576
0
  if (nx == 0 || ny == 0)
577
0
    return;
578
579
0
  for (yy = py; yy < py + ny; yy++) {
580
0
    if (yy >= gd->hsize + gd->sy)
581
0
      break;
582
0
    s->cx = cx;
583
0
    if (wp != NULL)
584
0
      screen_write_initctx(ctx, &ttyctx, 0);
585
0
    for (xx = px; xx < px + nx; xx++) {
586
0
      if (xx >= grid_get_line(gd, yy)->cellsize &&
587
0
          s->cx >= grid_get_line(ctx->s->grid, s->cy)->cellsize)
588
0
        break;
589
0
      grid_get_cell(gd, xx, yy, &gc);
590
0
      if (xx + gc.data.width > px + nx)
591
0
        break;
592
0
      grid_view_set_cell(ctx->s->grid, s->cx, s->cy, &gc);
593
0
      if (wp != NULL) {
594
0
        ttyctx.cell = &gc;
595
0
        tty_write(tty_cmd_cell, &ttyctx);
596
0
        ttyctx.ocx++;
597
0
      }
598
0
      s->cx++;
599
0
    }
600
0
    s->cy++;
601
0
  }
602
603
0
  s->cx = cx;
604
0
  s->cy = cy;
605
0
}
606
607
/* Select character set for drawing border lines. */
608
static void
609
screen_write_box_border_set(enum box_lines lines, int cell_type,
610
    struct grid_cell *gc)
611
0
{
612
0
  switch (lines) {
613
0
        case BOX_LINES_NONE:
614
0
    break;
615
0
        case BOX_LINES_DOUBLE:
616
0
                gc->attr &= ~GRID_ATTR_CHARSET;
617
0
                utf8_copy(&gc->data, tty_acs_double_borders(cell_type));
618
0
    break;
619
0
        case BOX_LINES_HEAVY:
620
0
                gc->attr &= ~GRID_ATTR_CHARSET;
621
0
                utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
622
0
    break;
623
0
        case BOX_LINES_ROUNDED:
624
0
                gc->attr &= ~GRID_ATTR_CHARSET;
625
0
                utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type));
626
0
    break;
627
0
        case BOX_LINES_SIMPLE:
628
0
                gc->attr &= ~GRID_ATTR_CHARSET;
629
0
                utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
630
0
                break;
631
0
        case BOX_LINES_PADDED:
632
0
                gc->attr &= ~GRID_ATTR_CHARSET;
633
0
                utf8_set(&gc->data, PADDED_BORDERS[cell_type]);
634
0
                break;
635
0
  case BOX_LINES_SINGLE:
636
0
  case BOX_LINES_DEFAULT:
637
0
    gc->attr |= GRID_ATTR_CHARSET;
638
0
    utf8_set(&gc->data, CELL_BORDERS[cell_type]);
639
0
    break;
640
0
  }
641
0
}
642
643
/* Draw a horizontal line on screen. */
644
void
645
screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right,
646
   enum box_lines lines, const struct grid_cell *border_gc)
647
0
{
648
0
  struct screen   *s = ctx->s;
649
0
  struct grid_cell   gc;
650
0
  u_int      cx, cy, i;
651
652
0
  cx = s->cx;
653
0
  cy = s->cy;
654
655
0
  if (border_gc != NULL)
656
0
    memcpy(&gc, border_gc, sizeof gc);
657
0
  else
658
0
    memcpy(&gc, &grid_default_cell, sizeof gc);
659
0
  gc.attr |= GRID_ATTR_CHARSET;
660
661
0
  if (left)
662
0
    screen_write_box_border_set(lines, CELL_LEFTJOIN, &gc);
663
0
  else
664
0
    screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
665
0
  screen_write_cell(ctx, &gc);
666
667
0
  screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
668
0
  for (i = 1; i < nx - 1; i++)
669
0
    screen_write_cell(ctx, &gc);
670
671
0
  if (right)
672
0
    screen_write_box_border_set(lines, CELL_RIGHTJOIN, &gc);
673
0
  else
674
0
    screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
675
0
  screen_write_cell(ctx, &gc);
676
677
0
  screen_write_set_cursor(ctx, cx, cy);
678
0
}
679
680
/* Draw a vertical line on screen. */
681
void
682
screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
683
0
{
684
0
  struct screen   *s = ctx->s;
685
0
  struct grid_cell   gc;
686
0
  u_int      cx, cy, i;
687
688
0
  cx = s->cx;
689
0
  cy = s->cy;
690
691
0
  memcpy(&gc, &grid_default_cell, sizeof gc);
692
0
  gc.attr |= GRID_ATTR_CHARSET;
693
694
0
  screen_write_putc(ctx, &gc, top ? 'w' : 'x');
695
0
  for (i = 1; i < ny - 1; i++) {
696
0
    screen_write_set_cursor(ctx, cx, cy + i);
697
0
    screen_write_putc(ctx, &gc, 'x');
698
0
  }
699
0
  screen_write_set_cursor(ctx, cx, cy + ny - 1);
700
0
  screen_write_putc(ctx, &gc, bottom ? 'v' : 'x');
701
702
0
  screen_write_set_cursor(ctx, cx, cy);
703
0
}
704
705
/* Draw a menu on screen. */
706
void
707
screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice,
708
    enum box_lines lines, const struct grid_cell *menu_gc,
709
    const struct grid_cell *border_gc, const struct grid_cell *choice_gc)
710
0
{
711
0
  struct screen   *s = ctx->s;
712
0
  struct grid_cell   default_gc;
713
0
  const struct grid_cell  *gc = &default_gc;
714
0
  u_int      cx, cy, i, j, width = menu->width;
715
0
  const char    *name;
716
717
0
  cx = s->cx;
718
0
  cy = s->cy;
719
720
0
  memcpy(&default_gc, menu_gc, sizeof default_gc);
721
722
0
  screen_write_box(ctx, menu->width + 4, menu->count + 2, lines,
723
0
      border_gc, menu->title);
724
725
0
  for (i = 0; i < menu->count; i++) {
726
0
    name = menu->items[i].name;
727
0
    if (name == NULL) {
728
0
      screen_write_cursormove(ctx, cx, cy + 1 + i, 0);
729
0
      screen_write_hline(ctx, width + 4, 1, 1, lines,
730
0
          border_gc);
731
0
      continue;
732
0
    }
733
734
0
    if (choice >= 0 && i == (u_int)choice && *name != '-')
735
0
      gc = choice_gc;
736
737
0
    screen_write_cursormove(ctx, cx + 1, cy + 1 + i, 0);
738
0
    for (j = 0; j < width + 2; j++)
739
0
      screen_write_putc(ctx, gc, ' ');
740
741
0
    screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
742
0
    if (*name == '-') {
743
0
      default_gc.attr |= GRID_ATTR_DIM;
744
0
      format_draw(ctx, gc, width, name + 1, NULL, 0);
745
0
      default_gc.attr &= ~GRID_ATTR_DIM;
746
0
      continue;
747
0
    }
748
749
0
    format_draw(ctx, gc, width, name, NULL, 0);
750
0
    gc = &default_gc;
751
0
  }
752
753
0
  screen_write_set_cursor(ctx, cx, cy);
754
0
}
755
756
/* Draw a box on screen. */
757
void
758
screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny,
759
    enum box_lines lines, const struct grid_cell *gcp, const char *title)
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 (gcp != NULL)
769
0
    memcpy(&gc, gcp, sizeof gc);
770
0
  else
771
0
    memcpy(&gc, &grid_default_cell, sizeof gc);
772
773
0
  gc.attr |= GRID_ATTR_CHARSET;
774
0
  gc.flags |= GRID_FLAG_NOPALETTE;
775
776
  /* Draw top border */
777
0
  screen_write_box_border_set(lines, CELL_TOPLEFT, &gc);
778
0
  screen_write_cell(ctx, &gc);
779
0
  screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
780
0
  for (i = 1; i < nx - 1; i++)
781
0
    screen_write_cell(ctx, &gc);
782
0
  screen_write_box_border_set(lines, CELL_TOPRIGHT, &gc);
783
0
  screen_write_cell(ctx, &gc);
784
785
  /* Draw bottom border */
786
0
  screen_write_set_cursor(ctx, cx, cy + ny - 1);
787
0
  screen_write_box_border_set(lines, CELL_BOTTOMLEFT, &gc);
788
0
  screen_write_cell(ctx, &gc);
789
0
  screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
790
0
  for (i = 1; i < nx - 1; i++)
791
0
    screen_write_cell(ctx, &gc);
792
0
  screen_write_box_border_set(lines, CELL_BOTTOMRIGHT, &gc);
793
0
  screen_write_cell(ctx, &gc);
794
795
  /* Draw sides */
796
0
  screen_write_box_border_set(lines, CELL_TOPBOTTOM, &gc);
797
0
  for (i = 1; i < ny - 1; i++) {
798
    /* left side */
799
0
    screen_write_set_cursor(ctx, cx, cy + i);
800
0
    screen_write_cell(ctx, &gc);
801
    /* right side */
802
0
    screen_write_set_cursor(ctx, cx + nx - 1, cy + i);
803
0
    screen_write_cell(ctx, &gc);
804
0
  }
805
806
0
  if (title != NULL) {
807
0
    gc.attr &= ~GRID_ATTR_CHARSET;
808
0
    screen_write_cursormove(ctx, cx + 2, cy, 0);
809
0
    format_draw(ctx, &gc, nx - 4, title, NULL, 0);
810
0
  }
811
812
0
  screen_write_set_cursor(ctx, cx, cy);
813
0
}
814
815
/*
816
 * Write a preview version of a window. Assumes target area is big enough and
817
 * already cleared.
818
 */
819
void
820
screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
821
    u_int ny)
822
0
{
823
0
  struct screen   *s = ctx->s;
824
0
  struct grid_cell   gc;
825
0
  u_int      cx, cy, px, py;
826
827
0
  cx = s->cx;
828
0
  cy = s->cy;
829
830
  /*
831
   * If the cursor is on, pick the area around the cursor, otherwise use
832
   * the top left.
833
   */
834
0
  if (src->mode & MODE_CURSOR) {
835
0
    px = src->cx;
836
0
    if (px < nx / 3)
837
0
      px = 0;
838
0
    else
839
0
      px = px - nx / 3;
840
0
    if (px + nx > screen_size_x(src)) {
841
0
      if (nx > screen_size_x(src))
842
0
        px = 0;
843
0
      else
844
0
        px = screen_size_x(src) - nx;
845
0
    }
846
0
    py = src->cy;
847
0
    if (py < ny / 3)
848
0
      py = 0;
849
0
    else
850
0
      py = py - ny / 3;
851
0
    if (py + ny > screen_size_y(src)) {
852
0
      if (ny > screen_size_y(src))
853
0
        py = 0;
854
0
      else
855
0
        py = screen_size_y(src) - ny;
856
0
    }
857
0
  } else {
858
0
    px = 0;
859
0
    py = 0;
860
0
  }
861
862
0
  screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny);
863
864
0
  if (src->mode & MODE_CURSOR) {
865
0
    grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
866
0
    gc.attr |= GRID_ATTR_REVERSE;
867
0
    screen_write_set_cursor(ctx, cx + (src->cx - px),
868
0
        cy + (src->cy - py));
869
0
    screen_write_cell(ctx, &gc);
870
0
  }
871
0
}
872
873
/* Set a mode. */
874
void
875
screen_write_mode_set(struct screen_write_ctx *ctx, int mode)
876
3.23k
{
877
3.23k
  struct screen *s = ctx->s;
878
879
3.23k
  s->mode |= mode;
880
881
3.23k
  if (log_get_level() != 0)
882
0
    log_debug("%s: %s", __func__, screen_mode_to_string(mode));
883
3.23k
}
884
885
/* Clear a mode. */
886
void
887
screen_write_mode_clear(struct screen_write_ctx *ctx, int mode)
888
3.36k
{
889
3.36k
  struct screen *s = ctx->s;
890
891
3.36k
  s->mode &= ~mode;
892
893
3.36k
  if (log_get_level() != 0)
894
0
    log_debug("%s: %s", __func__, screen_mode_to_string(mode));
895
3.36k
}
896
897
/* Cursor up by ny. */
898
void
899
screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
900
1.62k
{
901
1.62k
  struct screen *s = ctx->s;
902
1.62k
  u_int    cx = s->cx, cy = s->cy;
903
904
1.62k
  if (ny == 0)
905
0
    ny = 1;
906
907
1.62k
  if (cy < s->rupper) {
908
    /* Above region. */
909
437
    if (ny > cy)
910
243
      ny = cy;
911
1.18k
  } else {
912
    /* Below region. */
913
1.18k
    if (ny > cy - s->rupper)
914
987
      ny = cy - s->rupper;
915
1.18k
  }
916
1.62k
  if (cx == screen_size_x(s))
917
66
    cx--;
918
919
1.62k
  cy -= ny;
920
921
1.62k
  screen_write_set_cursor(ctx, cx, cy);
922
1.62k
}
923
924
/* Cursor down by ny. */
925
void
926
screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
927
1.71k
{
928
1.71k
  struct screen *s = ctx->s;
929
1.71k
  u_int    cx = s->cx, cy = s->cy;
930
931
1.71k
  if (ny == 0)
932
0
    ny = 1;
933
934
1.71k
  if (cy > s->rlower) {
935
    /* Below region. */
936
307
    if (ny > screen_size_y(s) - 1 - cy)
937
238
      ny = screen_size_y(s) - 1 - cy;
938
1.40k
  } else {
939
    /* Above region. */
940
1.40k
    if (ny > s->rlower - cy)
941
668
      ny = s->rlower - cy;
942
1.40k
  }
943
1.71k
  if (cx == screen_size_x(s))
944
66
      cx--;
945
1.64k
  else if (ny == 0)
946
733
    return;
947
948
979
  cy += ny;
949
950
979
  screen_write_set_cursor(ctx, cx, cy);
951
979
}
952
953
/* Cursor right by nx. */
954
void
955
screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
956
889
{
957
889
  struct screen *s = ctx->s;
958
889
  u_int    cx = s->cx, cy = s->cy;
959
960
889
  if (nx == 0)
961
0
    nx = 1;
962
963
889
  if (nx > screen_size_x(s) - 1 - cx)
964
374
    nx = screen_size_x(s) - 1 - cx;
965
889
  if (nx == 0)
966
223
    return;
967
968
666
  cx += nx;
969
970
666
  screen_write_set_cursor(ctx, cx, cy);
971
666
}
972
973
/* Cursor left by nx. */
974
void
975
screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx)
976
774
{
977
774
  struct screen *s = ctx->s;
978
774
  u_int    cx = s->cx, cy = s->cy;
979
980
774
  if (nx == 0)
981
0
    nx = 1;
982
983
774
  if (nx > cx)
984
574
    nx = cx;
985
774
  if (nx == 0)
986
567
    return;
987
988
207
  cx -= nx;
989
990
207
  screen_write_set_cursor(ctx, cx, cy);
991
207
}
992
993
/* Backspace; cursor left unless at start of wrapped line when can move up. */
994
void
995
screen_write_backspace(struct screen_write_ctx *ctx)
996
3.92k
{
997
3.92k
  struct screen   *s = ctx->s;
998
3.92k
  struct grid_line  *gl;
999
3.92k
  u_int      cx = s->cx, cy = s->cy;
1000
1001
3.92k
  if (cx == 0) {
1002
473
    if (cy == 0)
1003
210
      return;
1004
263
    gl = grid_get_line(s->grid, s->grid->hsize + cy - 1);
1005
263
    if (gl->flags & GRID_LINE_WRAPPED) {
1006
67
      cy--;
1007
67
      cx = screen_size_x(s) - 1;
1008
67
    }
1009
263
  } else
1010
3.45k
    cx--;
1011
1012
3.71k
  screen_write_set_cursor(ctx, cx, cy);
1013
3.71k
}
1014
1015
/* VT100 alignment test. */
1016
void
1017
screen_write_alignmenttest(struct screen_write_ctx *ctx)
1018
1.15k
{
1019
1.15k
  struct screen   *s = ctx->s;
1020
1.15k
  struct tty_ctx     ttyctx;
1021
1.15k
  struct grid_cell         gc;
1022
1.15k
  u_int      xx, yy;
1023
1024
1.15k
  memcpy(&gc, &grid_default_cell, sizeof gc);
1025
1.15k
  utf8_set(&gc.data, 'E');
1026
1027
#ifdef ENABLE_SIXEL
1028
  if (image_free_all(s) && ctx->wp != NULL)
1029
    ctx->wp->flags |= PANE_REDRAW;
1030
#endif
1031
1032
30.0k
  for (yy = 0; yy < screen_size_y(s); yy++) {
1033
2.34M
    for (xx = 0; xx < screen_size_x(s); xx++)
1034
2.31M
      grid_view_set_cell(s->grid, xx, yy, &gc);
1035
28.9k
  }
1036
1037
1.15k
  screen_write_set_cursor(ctx, 0, 0);
1038
1039
1.15k
  s->rupper = 0;
1040
1.15k
  s->rlower = screen_size_y(s) - 1;
1041
1042
1.15k
  screen_write_initctx(ctx, &ttyctx, 1);
1043
1044
1.15k
  screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1);
1045
1.15k
  tty_write(tty_cmd_alignmenttest, &ttyctx);
1046
1.15k
}
1047
1048
/* Insert nx characters. */
1049
void
1050
screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1051
1.36k
{
1052
1.36k
  struct screen *s = ctx->s;
1053
1.36k
  struct tty_ctx   ttyctx;
1054
1055
1.36k
  if (nx == 0)
1056
0
    nx = 1;
1057
1058
1.36k
  if (nx > screen_size_x(s) - s->cx)
1059
253
    nx = screen_size_x(s) - s->cx;
1060
1.36k
  if (nx == 0)
1061
195
    return;
1062
1063
1.17k
  if (s->cx > screen_size_x(s) - 1)
1064
0
    return;
1065
1066
#ifdef ENABLE_SIXEL
1067
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1068
    ctx->wp->flags |= PANE_REDRAW;
1069
#endif
1070
1071
1.17k
  screen_write_initctx(ctx, &ttyctx, 0);
1072
1.17k
  ttyctx.bg = bg;
1073
1074
1.17k
  grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg);
1075
1076
1.17k
  screen_write_collect_flush(ctx, 0, __func__);
1077
1.17k
  ttyctx.num = nx;
1078
1.17k
  tty_write(tty_cmd_insertcharacter, &ttyctx);
1079
1.17k
}
1080
1081
/* Delete nx characters. */
1082
void
1083
screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1084
972
{
1085
972
  struct screen *s = ctx->s;
1086
972
  struct tty_ctx   ttyctx;
1087
1088
972
  if (nx == 0)
1089
0
    nx = 1;
1090
1091
972
  if (nx > screen_size_x(s) - s->cx)
1092
248
    nx = screen_size_x(s) - s->cx;
1093
972
  if (nx == 0)
1094
195
    return;
1095
1096
777
  if (s->cx > screen_size_x(s) - 1)
1097
0
    return;
1098
1099
#ifdef ENABLE_SIXEL
1100
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1101
    ctx->wp->flags |= PANE_REDRAW;
1102
#endif
1103
1104
777
  screen_write_initctx(ctx, &ttyctx, 0);
1105
777
  ttyctx.bg = bg;
1106
1107
777
  grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg);
1108
1109
777
  screen_write_collect_flush(ctx, 0, __func__);
1110
777
  ttyctx.num = nx;
1111
777
  tty_write(tty_cmd_deletecharacter, &ttyctx);
1112
777
}
1113
1114
/* Clear nx characters. */
1115
void
1116
screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1117
1.01k
{
1118
1.01k
  struct screen *s = ctx->s;
1119
1.01k
  struct tty_ctx   ttyctx;
1120
1121
1.01k
  if (nx == 0)
1122
0
    nx = 1;
1123
1124
1.01k
  if (nx > screen_size_x(s) - s->cx)
1125
249
    nx = screen_size_x(s) - s->cx;
1126
1.01k
  if (nx == 0)
1127
194
    return;
1128
1129
816
  if (s->cx > screen_size_x(s) - 1)
1130
0
    return;
1131
1132
#ifdef ENABLE_SIXEL
1133
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1134
    ctx->wp->flags |= PANE_REDRAW;
1135
#endif
1136
1137
816
  screen_write_initctx(ctx, &ttyctx, 0);
1138
816
  ttyctx.bg = bg;
1139
1140
816
  grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg);
1141
1142
816
  screen_write_collect_flush(ctx, 0, __func__);
1143
816
  ttyctx.num = nx;
1144
816
  tty_write(tty_cmd_clearcharacter, &ttyctx);
1145
816
}
1146
1147
/* Insert ny lines. */
1148
void
1149
screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1150
849
{
1151
849
  struct screen *s = ctx->s;
1152
849
  struct grid *gd = s->grid;
1153
849
  struct tty_ctx   ttyctx;
1154
1155
#ifdef ENABLE_SIXEL
1156
  u_int    sy = screen_size_y(s);
1157
#endif
1158
1159
849
  if (ny == 0)
1160
0
    ny = 1;
1161
1162
#ifdef ENABLE_SIXEL
1163
  if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
1164
    ctx->wp->flags |= PANE_REDRAW;
1165
#endif
1166
1167
849
  if (s->cy < s->rupper || s->cy > s->rlower) {
1168
323
    if (ny > screen_size_y(s) - s->cy)
1169
108
      ny = screen_size_y(s) - s->cy;
1170
323
    if (ny == 0)
1171
0
      return;
1172
1173
323
    screen_write_initctx(ctx, &ttyctx, 1);
1174
323
    ttyctx.bg = bg;
1175
1176
323
    grid_view_insert_lines(gd, s->cy, ny, bg);
1177
1178
323
    screen_write_collect_flush(ctx, 0, __func__);
1179
323
    ttyctx.num = ny;
1180
323
    tty_write(tty_cmd_insertline, &ttyctx);
1181
323
    return;
1182
323
  }
1183
1184
526
  if (ny > s->rlower + 1 - s->cy)
1185
128
    ny = s->rlower + 1 - s->cy;
1186
526
  if (ny == 0)
1187
0
    return;
1188
1189
526
  screen_write_initctx(ctx, &ttyctx, 1);
1190
526
  ttyctx.bg = bg;
1191
1192
526
  if (s->cy < s->rupper || s->cy > s->rlower)
1193
0
    grid_view_insert_lines(gd, s->cy, ny, bg);
1194
526
  else
1195
526
    grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg);
1196
1197
526
  screen_write_collect_flush(ctx, 0, __func__);
1198
1199
526
  ttyctx.num = ny;
1200
526
  tty_write(tty_cmd_insertline, &ttyctx);
1201
526
}
1202
1203
/* Delete ny lines. */
1204
void
1205
screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1206
1.33k
{
1207
1.33k
  struct screen *s = ctx->s;
1208
1.33k
  struct grid *gd = s->grid;
1209
1.33k
  struct tty_ctx   ttyctx;
1210
1.33k
  u_int    sy = screen_size_y(s);
1211
1212
1.33k
  if (ny == 0)
1213
0
    ny = 1;
1214
1215
#ifdef ENABLE_SIXEL
1216
  if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
1217
    ctx->wp->flags |= PANE_REDRAW;
1218
#endif
1219
1220
1.33k
  if (s->cy < s->rupper || s->cy > s->rlower) {
1221
506
    if (ny > sy - s->cy)
1222
110
      ny = sy - s->cy;
1223
506
    if (ny == 0)
1224
0
      return;
1225
1226
506
    screen_write_initctx(ctx, &ttyctx, 1);
1227
506
    ttyctx.bg = bg;
1228
1229
506
    grid_view_delete_lines(gd, s->cy, ny, bg);
1230
1231
506
    screen_write_collect_flush(ctx, 0, __func__);
1232
506
    ttyctx.num = ny;
1233
506
    tty_write(tty_cmd_deleteline, &ttyctx);
1234
506
    return;
1235
506
  }
1236
1237
827
  if (ny > s->rlower + 1 - s->cy)
1238
123
    ny = s->rlower + 1 - s->cy;
1239
827
  if (ny == 0)
1240
0
    return;
1241
1242
827
  screen_write_initctx(ctx, &ttyctx, 1);
1243
827
  ttyctx.bg = bg;
1244
1245
827
  if (s->cy < s->rupper || s->cy > s->rlower)
1246
0
    grid_view_delete_lines(gd, s->cy, ny, bg);
1247
827
  else
1248
827
    grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg);
1249
1250
827
  screen_write_collect_flush(ctx, 0, __func__);
1251
827
  ttyctx.num = ny;
1252
827
  tty_write(tty_cmd_deleteline, &ttyctx);
1253
827
}
1254
1255
/* Clear line at cursor. */
1256
void
1257
screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
1258
851
{
1259
851
  struct screen     *s = ctx->s;
1260
851
  struct grid_line    *gl;
1261
851
  u_int        sx = screen_size_x(s);
1262
851
  struct screen_write_citem *ci = ctx->item;
1263
1264
851
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1265
851
  if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
1266
399
    return;
1267
1268
#ifdef ENABLE_SIXEL
1269
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1270
    ctx->wp->flags |= PANE_REDRAW;
1271
#endif
1272
1273
452
  grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1274
1275
452
  screen_write_collect_clear(ctx, s->cy, 1);
1276
452
  ci->x = 0;
1277
452
  ci->used = sx;
1278
452
  ci->type = CLEAR;
1279
452
  ci->bg = bg;
1280
452
  TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1281
452
  ctx->item = screen_write_get_citem();
1282
452
}
1283
1284
/* Clear to end of line from cursor. */
1285
void
1286
screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
1287
2.12k
{
1288
2.12k
  struct screen     *s = ctx->s;
1289
2.12k
  struct grid_line    *gl;
1290
2.12k
  u_int        sx = screen_size_x(s);
1291
2.12k
  struct screen_write_citem *ci = ctx->item, *before;
1292
1293
2.12k
  if (s->cx == 0) {
1294
706
    screen_write_clearline(ctx, bg);
1295
706
    return;
1296
706
  }
1297
1298
1.41k
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1299
1.41k
  if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
1300
402
    return;
1301
1302
#ifdef ENABLE_SIXEL
1303
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1304
    ctx->wp->flags |= PANE_REDRAW;
1305
#endif
1306
1307
1.01k
  grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
1308
1309
1.01k
  before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL);
1310
1.01k
  ci->x = s->cx;
1311
1.01k
  ci->used = sx - s->cx;
1312
1.01k
  ci->type = CLEAR;
1313
1.01k
  ci->bg = bg;
1314
1.01k
  if (before == NULL)
1315
1.01k
    TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1316
0
  else
1317
0
    TAILQ_INSERT_BEFORE(before, ci, entry);
1318
1.01k
  ctx->item = screen_write_get_citem();
1319
1.01k
}
1320
1321
/* Clear to start of line from cursor. */
1322
void
1323
screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
1324
283
{
1325
283
  struct screen      *s = ctx->s;
1326
283
  u_int        sx = screen_size_x(s);
1327
283
  struct screen_write_citem *ci = ctx->item, *before;
1328
1329
283
  if (s->cx >= sx - 1) {
1330
67
    screen_write_clearline(ctx, bg);
1331
67
    return;
1332
67
  }
1333
1334
#ifdef ENABLE_SIXEL
1335
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1336
    ctx->wp->flags |= PANE_REDRAW;
1337
#endif
1338
1339
216
  if (s->cx > sx - 1)
1340
0
    grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1341
216
  else
1342
216
    grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1343
1344
216
  before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL);
1345
216
  ci->x = 0;
1346
216
  ci->used = s->cx + 1;
1347
216
  ci->type = CLEAR;
1348
216
  ci->bg = bg;
1349
216
  if (before == NULL)
1350
137
    TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1351
79
  else
1352
79
    TAILQ_INSERT_BEFORE(before, ci, entry);
1353
216
  ctx->item = screen_write_get_citem();
1354
216
}
1355
1356
/* Move cursor to px,py. */
1357
void
1358
screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
1359
    int origin)
1360
4.33k
{
1361
4.33k
  struct screen *s = ctx->s;
1362
1363
4.33k
  if (origin && py != -1 && (s->mode & MODE_ORIGIN)) {
1364
381
    if ((u_int)py > s->rlower - s->rupper)
1365
113
      py = s->rlower;
1366
268
    else
1367
268
      py += s->rupper;
1368
381
  }
1369
1370
4.33k
  if (px != -1 && (u_int)px > screen_size_x(s) - 1)
1371
328
    px = screen_size_x(s) - 1;
1372
4.33k
  if (py != -1 && (u_int)py > screen_size_y(s) - 1)
1373
217
    py = screen_size_y(s) - 1;
1374
1375
4.33k
  log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py);
1376
4.33k
  screen_write_set_cursor(ctx, px, py);
1377
4.33k
}
1378
1379
/* Reverse index (up with scroll). */
1380
void
1381
screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
1382
633
{
1383
633
  struct screen *s = ctx->s;
1384
633
  struct tty_ctx   ttyctx;
1385
1386
633
  if (s->cy == s->rupper) {
1387
#ifdef ENABLE_SIXEL
1388
    if (image_free_all(s) && ctx->wp != NULL)
1389
      ctx->wp->flags |= PANE_REDRAW;
1390
#endif
1391
1392
222
    grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
1393
222
    screen_write_collect_flush(ctx, 0, __func__);
1394
1395
222
    screen_write_initctx(ctx, &ttyctx, 1);
1396
222
    ttyctx.bg = bg;
1397
1398
222
    tty_write(tty_cmd_reverseindex, &ttyctx);
1399
411
  } else if (s->cy > 0)
1400
214
    screen_write_set_cursor(ctx, -1, s->cy - 1);
1401
1402
633
}
1403
1404
/* Set scroll region. */
1405
void
1406
screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper,
1407
    u_int rlower)
1408
2.00k
{
1409
2.00k
  struct screen *s = ctx->s;
1410
1411
2.00k
  if (rupper > screen_size_y(s) - 1)
1412
129
    rupper = screen_size_y(s) - 1;
1413
2.00k
  if (rlower > screen_size_y(s) - 1)
1414
130
    rlower = screen_size_y(s) - 1;
1415
2.00k
  if (rupper >= rlower)  /* cannot be one line */
1416
202
    return;
1417
1418
1.80k
  screen_write_collect_flush(ctx, 0, __func__);
1419
1420
  /* Cursor moves to top-left. */
1421
1.80k
  screen_write_set_cursor(ctx, 0, 0);
1422
1423
1.80k
  s->rupper = rupper;
1424
1.80k
  s->rlower = rlower;
1425
1.80k
}
1426
1427
/* Line feed. */
1428
void
1429
screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
1430
7.52k
{
1431
7.52k
  struct screen   *s = ctx->s;
1432
7.52k
  struct grid   *gd = s->grid;
1433
7.52k
  struct grid_line  *gl;
1434
#ifdef ENABLE_SIXEL
1435
  int      redraw = 0;
1436
#endif
1437
7.52k
  u_int      rupper = s->rupper, rlower = s->rlower;
1438
1439
7.52k
  gl = grid_get_line(gd, gd->hsize + s->cy);
1440
7.52k
  if (wrapped)
1441
1.33k
    gl->flags |= GRID_LINE_WRAPPED;
1442
1443
7.52k
  log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1444
7.52k
      rupper, rlower);
1445
1446
7.52k
  if (bg != ctx->bg) {
1447
526
    screen_write_collect_flush(ctx, 1, __func__);
1448
526
    ctx->bg = bg;
1449
526
  }
1450
1451
7.52k
  if (s->cy == s->rlower) {
1452
#ifdef ENABLE_SIXEL
1453
    if (rlower == screen_size_y(s) - 1)
1454
      redraw = image_scroll_up(s, 1);
1455
    else
1456
      redraw = image_check_line(s, rupper, rlower - rupper);
1457
    if (redraw && ctx->wp != NULL)
1458
      ctx->wp->flags |= PANE_REDRAW;
1459
#endif
1460
3.47k
    grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1461
3.47k
    screen_write_collect_scroll(ctx, bg);
1462
3.47k
    ctx->scrolled++;
1463
4.05k
  } else if (s->cy < screen_size_y(s) - 1)
1464
3.83k
    screen_write_set_cursor(ctx, -1, s->cy + 1);
1465
7.52k
}
1466
1467
/* Scroll up. */
1468
void
1469
screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1470
1.25k
{
1471
1.25k
  struct screen *s = ctx->s;
1472
1.25k
  struct grid *gd = s->grid;
1473
1.25k
  u_int    i;
1474
1475
1.25k
  if (lines == 0)
1476
0
    lines = 1;
1477
1.25k
  else if (lines > s->rlower - s->rupper + 1)
1478
509
    lines = s->rlower - s->rupper + 1;
1479
1480
1.25k
  if (bg != ctx->bg) {
1481
118
    screen_write_collect_flush(ctx, 1, __func__);
1482
118
    ctx->bg = bg;
1483
118
  }
1484
1485
#ifdef ENABLE_SIXEL
1486
  if (image_scroll_up(s, lines) && ctx->wp != NULL)
1487
    ctx->wp->flags |= PANE_REDRAW;
1488
#endif
1489
1490
14.4k
  for (i = 0; i < lines; i++) {
1491
13.2k
    grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1492
13.2k
    screen_write_collect_scroll(ctx, bg);
1493
13.2k
  }
1494
1.25k
  ctx->scrolled += lines;
1495
1.25k
}
1496
1497
/* Scroll down. */
1498
void
1499
screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1500
516
{
1501
516
  struct screen *s = ctx->s;
1502
516
  struct grid *gd = s->grid;
1503
516
  struct tty_ctx   ttyctx;
1504
516
  u_int    i;
1505
1506
516
  screen_write_initctx(ctx, &ttyctx, 1);
1507
516
  ttyctx.bg = bg;
1508
1509
516
  if (lines == 0)
1510
0
    lines = 1;
1511
516
  else if (lines > s->rlower - s->rupper + 1)
1512
131
    lines = s->rlower - s->rupper + 1;
1513
1514
#ifdef ENABLE_SIXEL
1515
  if (image_free_all(s) && ctx->wp != NULL)
1516
    ctx->wp->flags |= PANE_REDRAW;
1517
#endif
1518
1519
3.33k
  for (i = 0; i < lines; i++)
1520
2.81k
    grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg);
1521
1522
516
  screen_write_collect_flush(ctx, 0, __func__);
1523
516
  ttyctx.num = lines;
1524
516
  tty_write(tty_cmd_scrolldown, &ttyctx);
1525
516
}
1526
1527
/* Carriage return (cursor to start of line). */
1528
void
1529
screen_write_carriagereturn(struct screen_write_ctx *ctx)
1530
4.09k
{
1531
4.09k
  screen_write_set_cursor(ctx, 0, -1);
1532
4.09k
}
1533
1534
/* Clear to end of screen from cursor. */
1535
void
1536
screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
1537
1.84k
{
1538
1.84k
  struct screen *s = ctx->s;
1539
1.84k
  struct grid *gd = s->grid;
1540
1.84k
  struct tty_ctx   ttyctx;
1541
1.84k
  u_int    sx = screen_size_x(s), sy = screen_size_y(s);
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.84k
  screen_write_initctx(ctx, &ttyctx, 1);
1549
1.84k
  ttyctx.bg = bg;
1550
1551
  /* Scroll into history if it is enabled and clearing entire screen. */
1552
1.84k
  if (s->cx == 0 &&
1553
1.18k
      s->cy == 0 &&
1554
758
      (gd->flags & GRID_HISTORY) &&
1555
0
      ctx->wp != NULL &&
1556
0
      options_get_number(ctx->wp->options, "scroll-on-clear"))
1557
0
    grid_view_clear_history(gd, bg);
1558
1.84k
  else {
1559
1.84k
    if (s->cx <= sx - 1)
1560
1.65k
      grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg);
1561
1.84k
    grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg);
1562
1.84k
  }
1563
1564
1.84k
  screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1));
1565
1.84k
  screen_write_collect_flush(ctx, 0, __func__);
1566
1.84k
  tty_write(tty_cmd_clearendofscreen, &ttyctx);
1567
1.84k
}
1568
1569
/* Clear to start of screen. */
1570
void
1571
screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
1572
227
{
1573
227
  struct screen *s = ctx->s;
1574
227
  struct tty_ctx   ttyctx;
1575
227
  u_int    sx = screen_size_x(s);
1576
1577
#ifdef ENABLE_SIXEL
1578
  if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL)
1579
    ctx->wp->flags |= PANE_REDRAW;
1580
#endif
1581
1582
227
  screen_write_initctx(ctx, &ttyctx, 1);
1583
227
  ttyctx.bg = bg;
1584
1585
227
  if (s->cy > 0)
1586
126
    grid_view_clear(s->grid, 0, 0, sx, s->cy, bg);
1587
227
  if (s->cx > sx - 1)
1588
66
    grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1589
161
  else
1590
161
    grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1591
1592
227
  screen_write_collect_clear(ctx, 0, s->cy);
1593
227
  screen_write_collect_flush(ctx, 0, __func__);
1594
227
  tty_write(tty_cmd_clearstartofscreen, &ttyctx);
1595
227
}
1596
1597
/* Clear entire screen. */
1598
void
1599
screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
1600
1.16k
{
1601
1.16k
  struct screen *s = ctx->s;
1602
1.16k
  struct tty_ctx   ttyctx;
1603
1.16k
  u_int    sx = screen_size_x(s), sy = screen_size_y(s);
1604
1605
#ifdef ENABLE_SIXEL
1606
  if (image_free_all(s) && ctx->wp != NULL)
1607
    ctx->wp->flags |= PANE_REDRAW;
1608
#endif
1609
1610
1.16k
  screen_write_initctx(ctx, &ttyctx, 1);
1611
1.16k
  ttyctx.bg = bg;
1612
1613
  /* Scroll into history if it is enabled. */
1614
1.16k
  if ((s->grid->flags & GRID_HISTORY) &&
1615
0
      ctx->wp != NULL &&
1616
0
      options_get_number(ctx->wp->options, "scroll-on-clear"))
1617
0
    grid_view_clear_history(s->grid, bg);
1618
1.16k
  else
1619
1.16k
    grid_view_clear(s->grid, 0, 0, sx, sy, bg);
1620
1621
1.16k
  screen_write_collect_clear(ctx, 0, sy);
1622
1.16k
  tty_write(tty_cmd_clearscreen, &ttyctx);
1623
1.16k
}
1624
1625
/* Clear entire history. */
1626
void
1627
screen_write_clearhistory(struct screen_write_ctx *ctx)
1628
263
{
1629
263
  grid_clear_history(ctx->s->grid);
1630
263
}
1631
1632
/* Force a full redraw. */
1633
void
1634
screen_write_fullredraw(struct screen_write_ctx *ctx)
1635
869
{
1636
869
  struct tty_ctx   ttyctx;
1637
1638
869
  screen_write_collect_flush(ctx, 0, __func__);
1639
1640
869
  screen_write_initctx(ctx, &ttyctx, 1);
1641
869
  if (ttyctx.redraw_cb != NULL)
1642
869
    ttyctx.redraw_cb(&ttyctx);
1643
869
}
1644
1645
/* Trim collected items. */
1646
static struct screen_write_citem *
1647
screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x,
1648
    u_int used, int *wrapped)
1649
10.2k
{
1650
10.2k
  struct screen_write_cline *cl = &ctx->s->write_list[y];
1651
10.2k
  struct screen_write_citem *ci, *ci2, *tmp, *before = NULL;
1652
10.2k
  u_int        sx = x, ex = x + used - 1;
1653
10.2k
  u_int        csx, cex;
1654
1655
10.2k
  if (TAILQ_EMPTY(&cl->items))
1656
4.19k
    return (NULL);
1657
25.6k
  TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
1658
25.6k
    csx = ci->x;
1659
25.6k
    cex = ci->x + ci->used - 1;
1660
1661
    /* Item is entirely before. */
1662
25.6k
    if (cex < sx) {
1663
20.2k
      log_debug("%s: %p %u-%u before %u-%u", __func__, ci,
1664
20.2k
          csx, cex, sx, ex);
1665
20.2k
      continue;
1666
20.2k
    }
1667
1668
    /* Item is entirely after. */
1669
5.42k
    if (csx > ex) {
1670
449
      log_debug("%s: %p %u-%u after %u-%u", __func__, ci,
1671
449
          csx, cex, sx, ex);
1672
449
      before = ci;
1673
449
      break;
1674
449
    }
1675
1676
    /* Item is entirely inside. */
1677
4.97k
    if (csx >= sx && cex <= ex) {
1678
2.41k
      log_debug("%s: %p %u-%u inside %u-%u", __func__, ci,
1679
2.41k
          csx, cex, sx, ex);
1680
2.41k
      TAILQ_REMOVE(&cl->items, ci, entry);
1681
2.41k
      screen_write_free_citem(ci);
1682
2.41k
      if (csx == 0 && ci->wrapped && wrapped != NULL)
1683
198
        *wrapped = 1;
1684
2.41k
      continue;
1685
2.41k
    }
1686
1687
    /* Item under the start. */
1688
2.56k
    if (csx < sx && cex >= sx && cex <= ex) {
1689
455
      log_debug("%s: %p %u-%u start %u-%u", __func__, ci,
1690
455
          csx, cex, sx, ex);
1691
455
      ci->used = sx - csx;
1692
455
      log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
1693
455
          ci->x + ci->used + 1);
1694
455
      continue;
1695
455
    }
1696
1697
    /* Item covers the end. */
1698
2.10k
    if (cex > ex && csx >= sx && csx <= ex) {
1699
596
      log_debug("%s: %p %u-%u end %u-%u", __func__, ci,
1700
596
          csx, cex, sx, ex);
1701
596
      ci->x = ex + 1;
1702
596
      ci->used = cex - ex;
1703
596
      log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
1704
596
          ci->x + ci->used + 1);
1705
596
      before = ci;
1706
596
      break;
1707
596
    }
1708
1709
    /* Item must cover both sides. */
1710
1.51k
    log_debug("%s: %p %u-%u under %u-%u", __func__, ci,
1711
1.51k
        csx, cex, sx, ex);
1712
1.51k
    ci2 = screen_write_get_citem();
1713
1.51k
    ci2->type = ci->type;
1714
1.51k
    ci2->bg = ci->bg;
1715
1.51k
    memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc);
1716
1.51k
    TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry);
1717
1718
1.51k
    ci->used = sx - csx;
1719
1.51k
    ci2->x = ex + 1;
1720
1.51k
    ci2->used = cex - ex;
1721
1722
1.51k
    log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci,
1723
1.51k
        ci->x, ci->x + ci->used - 1, ci, ci2->x,
1724
1.51k
        ci2->x + ci2->used - 1, ci2);
1725
1.51k
    before = ci2;
1726
1.51k
    break;
1727
2.10k
  }
1728
6.06k
  return (before);
1729
10.2k
}
1730
1731
/* Clear collected lines. */
1732
static void
1733
screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
1734
21.5k
{
1735
21.5k
  struct screen_write_cline *cl;
1736
21.5k
  u_int        i;
1737
1738
134k
  for (i = y; i < y + n; i++) {
1739
113k
    cl = &ctx->s->write_list[i];
1740
113k
    TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry);
1741
113k
  }
1742
21.5k
}
1743
1744
/* Scroll collected lines up. */
1745
static void
1746
screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg)
1747
16.6k
{
1748
16.6k
  struct screen     *s = ctx->s;
1749
16.6k
  struct screen_write_cline *cl;
1750
16.6k
  u_int        y;
1751
16.6k
  char        *saved;
1752
16.6k
  struct screen_write_citem *ci;
1753
1754
16.6k
  log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1755
16.6k
      s->rupper, s->rlower);
1756
1757
16.6k
  screen_write_collect_clear(ctx, s->rupper, 1);
1758
16.6k
  saved = ctx->s->write_list[s->rupper].data;
1759
405k
  for (y = s->rupper; y < s->rlower; y++) {
1760
388k
    cl = &ctx->s->write_list[y + 1];
1761
388k
    TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry);
1762
388k
    ctx->s->write_list[y].data = cl->data;
1763
388k
  }
1764
16.6k
  ctx->s->write_list[s->rlower].data = saved;
1765
1766
16.6k
  ci = screen_write_get_citem();
1767
16.6k
  ci->x = 0;
1768
16.6k
  ci->used = screen_size_x(s);
1769
16.6k
  ci->type = CLEAR;
1770
16.6k
  ci->bg = bg;
1771
16.6k
  TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry);
1772
16.6k
}
1773
1774
/* Flush collected lines. */
1775
static void
1776
screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
1777
    const char *from)
1778
68.8k
{
1779
68.8k
  struct screen     *s = ctx->s;
1780
68.8k
  struct screen_write_citem *ci, *tmp;
1781
68.8k
  struct screen_write_cline *cl;
1782
68.8k
  u_int        y, cx, cy, last, items = 0;
1783
68.8k
  struct tty_ctx       ttyctx;
1784
1785
68.8k
  if (ctx->scrolled != 0) {
1786
1.57k
    log_debug("%s: scrolled %u (region %u-%u)", __func__,
1787
1.57k
        ctx->scrolled, s->rupper, s->rlower);
1788
1.57k
    if (ctx->scrolled > s->rlower - s->rupper + 1)
1789
137
      ctx->scrolled = s->rlower - s->rupper + 1;
1790
1791
1.57k
    screen_write_initctx(ctx, &ttyctx, 1);
1792
1.57k
    ttyctx.num = ctx->scrolled;
1793
1.57k
    ttyctx.bg = ctx->bg;
1794
1.57k
    tty_write(tty_cmd_scrollup, &ttyctx);
1795
1796
1.57k
    if (ctx->wp != NULL)
1797
1.57k
      ctx->wp->flags |= PANE_REDRAWSCROLLBAR;
1798
1.57k
  }
1799
68.8k
  ctx->scrolled = 0;
1800
68.8k
  ctx->bg = 8;
1801
1802
68.8k
  if (scroll_only)
1803
21.3k
    return;
1804
1805
47.5k
  cx = s->cx; cy = s->cy;
1806
1.23M
  for (y = 0; y < screen_size_y(s); y++) {
1807
1.18M
    cl = &ctx->s->write_list[y];
1808
1.18M
    last = UINT_MAX;
1809
1.18M
    TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
1810
14.2k
      if (last != UINT_MAX && ci->x <= last) {
1811
0
        fatalx("collect list not in order: %u <= %u",
1812
0
            ci->x, last);
1813
0
      }
1814
14.2k
      screen_write_set_cursor(ctx, ci->x, y);
1815
14.2k
      if (ci->type == CLEAR) {
1816
7.43k
        screen_write_initctx(ctx, &ttyctx, 1);
1817
7.43k
        ttyctx.bg = ci->bg;
1818
7.43k
        ttyctx.num = ci->used;
1819
7.43k
        tty_write(tty_cmd_clearcharacter, &ttyctx);
1820
7.43k
      } else {
1821
6.81k
        screen_write_initctx(ctx, &ttyctx, 0);
1822
6.81k
        ttyctx.cell = &ci->gc;
1823
6.81k
        ttyctx.wrapped = ci->wrapped;
1824
6.81k
        ttyctx.ptr = cl->data + ci->x;
1825
6.81k
        ttyctx.num = ci->used;
1826
6.81k
        tty_write(tty_cmd_cells, &ttyctx);
1827
6.81k
      }
1828
14.2k
      items++;
1829
1830
14.2k
      TAILQ_REMOVE(&cl->items, ci, entry);
1831
14.2k
      screen_write_free_citem(ci);
1832
14.2k
      last = ci->x;
1833
14.2k
    }
1834
1.18M
  }
1835
47.5k
  s->cx = cx; s->cy = cy;
1836
1837
47.5k
  log_debug("%s: flushed %u items (%s)", __func__, items, from);
1838
47.5k
}
1839
1840
/* Finish and store collected cells. */
1841
void
1842
screen_write_collect_end(struct screen_write_ctx *ctx)
1843
523k
{
1844
523k
  struct screen     *s = ctx->s;
1845
523k
  struct screen_write_citem *ci = ctx->item, *before;
1846
523k
  struct screen_write_cline *cl = &s->write_list[s->cy];
1847
523k
  struct grid_cell     gc;
1848
523k
  u_int        xx;
1849
523k
  int        wrapped = ci->wrapped;
1850
1851
523k
  if (ci->used == 0)
1852
514k
    return;
1853
1854
9.02k
  before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used,
1855
9.02k
      &wrapped);
1856
9.02k
  ci->x = s->cx;
1857
9.02k
  ci->wrapped = wrapped;
1858
9.02k
  if (before == NULL)
1859
6.54k
    TAILQ_INSERT_TAIL(&cl->items, ci, entry);
1860
2.47k
  else
1861
2.47k
    TAILQ_INSERT_BEFORE(before, ci, entry);
1862
9.02k
  ctx->item = screen_write_get_citem();
1863
1864
9.02k
  log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used,
1865
9.02k
      (int)ci->used, cl->data + ci->x, s->cx, s->cy);
1866
1867
9.02k
  if (s->cx != 0) {
1868
9.62k
    for (xx = s->cx; xx > 0; xx--) {
1869
9.53k
      grid_view_get_cell(s->grid, xx, s->cy, &gc);
1870
9.53k
      if (~gc.flags & GRID_FLAG_PADDING)
1871
6.35k
        break;
1872
3.17k
      grid_view_set_cell(s->grid, xx, s->cy,
1873
3.17k
          &grid_default_cell);
1874
3.17k
    }
1875
6.45k
    if (xx != s->cx) {
1876
510
      if (xx == 0)
1877
94
        grid_view_get_cell(s->grid, 0, s->cy, &gc);
1878
510
      if (gc.data.width > 1) {
1879
444
        grid_view_set_cell(s->grid, xx, s->cy,
1880
444
            &grid_default_cell);
1881
444
      }
1882
510
    }
1883
6.45k
  }
1884
1885
#ifdef ENABLE_SIXEL
1886
  if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL)
1887
    ctx->wp->flags |= PANE_REDRAW;
1888
#endif
1889
1890
9.02k
  grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
1891
9.02k
      ci->used);
1892
9.02k
  screen_write_set_cursor(ctx, s->cx + ci->used, -1);
1893
1894
9.67k
  for (xx = s->cx; xx < screen_size_x(s); xx++) {
1895
8.44k
    grid_view_get_cell(s->grid, xx, s->cy, &gc);
1896
8.44k
    if (~gc.flags & GRID_FLAG_PADDING)
1897
7.78k
      break;
1898
653
    grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
1899
653
  }
1900
9.02k
}
1901
1902
/* Write cell data, collecting if necessary. */
1903
void
1904
screen_write_collect_add(struct screen_write_ctx *ctx,
1905
    const struct grid_cell *gc)
1906
37.4k
{
1907
37.4k
  struct screen     *s = ctx->s;
1908
37.4k
  struct screen_write_citem *ci;
1909
37.4k
  u_int        sx = screen_size_x(s);
1910
37.4k
  int        collect;
1911
1912
  /*
1913
   * Don't need to check that the attributes and whatnot are still the
1914
   * same - input_parse will end the collection when anything that isn't
1915
   * a plain character is encountered.
1916
   */
1917
1918
37.4k
  collect = 1;
1919
37.4k
  if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f)
1920
9.75k
    collect = 0;
1921
27.7k
  else if (gc->flags & GRID_FLAG_TAB)
1922
464
    collect = 0;
1923
27.2k
  else if (gc->attr & GRID_ATTR_CHARSET)
1924
6.80k
    collect = 0;
1925
20.4k
  else if (~s->mode & MODE_WRAP)
1926
2.92k
    collect = 0;
1927
17.5k
  else if (s->mode & MODE_INSERT)
1928
764
    collect = 0;
1929
16.7k
  else if (s->sel != NULL)
1930
0
    collect = 0;
1931
37.4k
  if (!collect) {
1932
20.7k
    screen_write_collect_end(ctx);
1933
20.7k
    screen_write_collect_flush(ctx, 0, __func__);
1934
20.7k
    screen_write_cell(ctx, gc);
1935
20.7k
    return;
1936
20.7k
  }
1937
1938
16.7k
  if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx)
1939
605
    screen_write_collect_end(ctx);
1940
16.7k
  ci = ctx->item; /* may have changed */
1941
1942
16.7k
  if (s->cx > sx - 1) {
1943
605
    log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1944
605
    ci->wrapped = 1;
1945
605
    screen_write_linefeed(ctx, 1, 8);
1946
605
    screen_write_set_cursor(ctx, 0, -1);
1947
605
  }
1948
1949
16.7k
  if (ci->used == 0)
1950
9.02k
    memcpy(&ci->gc, gc, sizeof ci->gc);
1951
16.7k
  if (ctx->s->write_list[s->cy].data == NULL)
1952
3.19k
    ctx->s->write_list[s->cy].data = xmalloc(screen_size_x(ctx->s));
1953
16.7k
  ctx->s->write_list[s->cy].data[s->cx + ci->used++] = gc->data.data[0];
1954
16.7k
}
1955
1956
/* Write cell data. */
1957
void
1958
screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
1959
20.7k
{
1960
20.7k
  struct screen   *s = ctx->s;
1961
20.7k
  struct grid   *gd = s->grid;
1962
20.7k
  const struct utf8_data  *ud = &gc->data;
1963
20.7k
  struct grid_line  *gl;
1964
20.7k
  struct grid_cell_entry  *gce;
1965
20.7k
  struct grid_cell   tmp_gc, now_gc;
1966
20.7k
  struct tty_ctx     ttyctx;
1967
20.7k
  u_int      sx = screen_size_x(s), sy = screen_size_y(s);
1968
20.7k
  u_int      width = ud->width, xx, not_wrap;
1969
20.7k
  int      selected, skip = 1;
1970
1971
  /* Ignore padding cells. */
1972
20.7k
  if (gc->flags & GRID_FLAG_PADDING)
1973
0
    return;
1974
1975
  /* Get the previous cell to check for combining. */
1976
20.7k
  if (screen_write_combine(ctx, gc) != 0)
1977
0
    return;
1978
1979
  /* Flush any existing scrolling. */
1980
20.7k
  screen_write_collect_flush(ctx, 1, __func__);
1981
1982
  /* If this character doesn't fit, ignore it. */
1983
20.7k
  if ((~s->mode & MODE_WRAP) &&
1984
4.09k
      width > 1 &&
1985
338
      (width > sx || (s->cx != sx && s->cx > sx - width)))
1986
0
    return;
1987
1988
  /* If in insert mode, make space for the cells. */
1989
20.7k
  if (s->mode & MODE_INSERT) {
1990
1.53k
    grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8);
1991
1.53k
    skip = 0;
1992
1.53k
  }
1993
1994
  /* Check this will fit on the current line and wrap if not. */
1995
20.7k
  if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
1996
728
    log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1997
728
    screen_write_linefeed(ctx, 1, 8);
1998
728
    screen_write_set_cursor(ctx, 0, -1);
1999
728
    screen_write_collect_flush(ctx, 0, __func__);
2000
728
  }
2001
2002
  /* Sanity check cursor position. */
2003
20.7k
  if (s->cx > sx - width || s->cy > sy - 1)
2004
790
    return;
2005
19.9k
  screen_write_initctx(ctx, &ttyctx, 0);
2006
2007
  /* Handle overwriting of UTF-8 characters. */
2008
19.9k
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
2009
19.9k
  if (gl->flags & GRID_LINE_EXTENDED) {
2010
12.7k
    grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
2011
12.7k
    if (screen_write_overwrite(ctx, &now_gc, width))
2012
903
      skip = 0;
2013
12.7k
  }
2014
2015
  /*
2016
   * If the new character is UTF-8 wide, fill in padding cells. Have
2017
   * already ensured there is enough room.
2018
   */
2019
45.3k
  for (xx = s->cx + 1; xx < s->cx + width; xx++) {
2020
25.3k
    log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
2021
25.3k
    grid_view_set_padding(gd, xx, s->cy);
2022
25.3k
    skip = 0;
2023
25.3k
  }
2024
2025
  /* If no change, do not draw. */
2026
19.9k
  if (skip) {
2027
14.2k
    if (s->cx >= gl->cellsize)
2028
3.90k
      skip = grid_cells_equal(gc, &grid_default_cell);
2029
10.3k
    else {
2030
10.3k
      gce = &gl->celldata[s->cx];
2031
10.3k
      if (gce->flags & GRID_FLAG_EXTENDED)
2032
2.88k
        skip = 0;
2033
7.42k
      else if (gc->flags != gce->flags)
2034
5.76k
        skip = 0;
2035
1.66k
      else if (gc->attr != gce->data.attr)
2036
281
        skip = 0;
2037
1.38k
      else if (gc->fg != gce->data.fg)
2038
271
        skip = 0;
2039
1.11k
      else if (gc->bg != gce->data.bg)
2040
290
        skip = 0;
2041
821
      else if (gc->data.width != 1)
2042
0
        skip = 0;
2043
821
      else if (gc->data.size != 1)
2044
228
        skip = 0;
2045
593
      else if (gce->data.data != gc->data.data[0])
2046
338
        skip = 0;
2047
10.3k
    }
2048
14.2k
  }
2049
2050
  /* Update the selected flag and set the cell. */
2051
19.9k
  selected = screen_check_selection(s, s->cx, s->cy);
2052
19.9k
  if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
2053
0
    memcpy(&tmp_gc, gc, sizeof tmp_gc);
2054
0
    tmp_gc.flags |= GRID_FLAG_SELECTED;
2055
0
    grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
2056
19.9k
  } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) {
2057
0
    memcpy(&tmp_gc, gc, sizeof tmp_gc);
2058
0
    tmp_gc.flags &= ~GRID_FLAG_SELECTED;
2059
0
    grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
2060
19.9k
  } else if (!skip)
2061
18.6k
    grid_view_set_cell(gd, s->cx, s->cy, gc);
2062
19.9k
  if (selected)
2063
0
    skip = 0;
2064
2065
  /*
2066
   * Move the cursor. If not wrapping, stick at the last character and
2067
   * replace it.
2068
   */
2069
19.9k
  not_wrap = !(s->mode & MODE_WRAP);
2070
19.9k
  if (s->cx <= sx - not_wrap - width)
2071
19.0k
    screen_write_set_cursor(ctx, s->cx + width, -1);
2072
873
  else
2073
873
    screen_write_set_cursor(ctx,  sx - not_wrap, -1);
2074
2075
  /* Create space for character in insert mode. */
2076
19.9k
  if (s->mode & MODE_INSERT) {
2077
1.00k
    screen_write_collect_flush(ctx, 0, __func__);
2078
1.00k
    ttyctx.num = width;
2079
1.00k
    tty_write(tty_cmd_insertcharacter, &ttyctx);
2080
1.00k
  }
2081
2082
  /* Write to the screen. */
2083
19.9k
  if (!skip) {
2084
18.6k
    if (selected) {
2085
0
      screen_select_cell(s, &tmp_gc, gc);
2086
0
      ttyctx.cell = &tmp_gc;
2087
0
    } else
2088
18.6k
      ttyctx.cell = gc;
2089
18.6k
    tty_write(tty_cmd_cell, &ttyctx);
2090
18.6k
  }
2091
19.9k
}
2092
2093
/* Combine a UTF-8 zero-width character onto the previous if necessary. */
2094
static int
2095
screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
2096
20.7k
{
2097
20.7k
  struct screen   *s = ctx->s;
2098
20.7k
  struct grid   *gd = s->grid;
2099
20.7k
  const struct utf8_data  *ud = &gc->data;
2100
20.7k
  u_int      n, cx = s->cx, cy = s->cy;
2101
20.7k
  struct grid_cell   last;
2102
20.7k
  struct tty_ctx     ttyctx;
2103
20.7k
  int      force_wide = 0, zero_width = 0;
2104
2105
  /* Ignore U+3164 HANGUL_FILLER entirely. */
2106
20.7k
  if (utf8_is_hangul_filler(ud))
2107
0
    return (1);
2108
2109
  /*
2110
   * Is this character which makes no sense without being combined? If
2111
   * this is true then flag it here and discard the character (return 1)
2112
   * if we cannot combine it.
2113
   */
2114
20.7k
  if (utf8_is_zwj(ud))
2115
0
    zero_width = 1;
2116
20.7k
  else if (utf8_is_vs(ud)) {
2117
0
    zero_width = 1;
2118
0
    if (options_get_number(global_options, "variation-selector-always-wide"))
2119
0
      force_wide = 1;
2120
20.7k
  } else if (ud->width == 0)
2121
0
    zero_width = 1;
2122
2123
  /* Cannot combine empty character or at left. */
2124
20.7k
  if (ud->size < 2 || cx == 0)
2125
12.2k
    return (zero_width);
2126
8.50k
  log_debug("%s: character %.*s at %u,%u (width %u)", __func__,
2127
8.50k
      (int)ud->size, ud->data, cx, cy, ud->width);
2128
2129
  /* Find the cell to combine with. */
2130
8.50k
  n = 1;
2131
8.50k
  grid_view_get_cell(gd, cx - n, cy, &last);
2132
8.50k
  if (cx != 1 && (last.flags & GRID_FLAG_PADDING)) {
2133
1.94k
    n = 2;
2134
1.94k
    grid_view_get_cell(gd, cx - n, cy, &last);
2135
1.94k
  }
2136
8.50k
  if (n != last.data.width || (last.flags & GRID_FLAG_PADDING))
2137
2.14k
    return (zero_width);
2138
2139
  /*
2140
   * Check if we need to combine characters. This could be a Korean
2141
   * Hangul Jamo character, zero width (set above), a modifier character
2142
   * (with an existing Unicode character) or a previous ZWJ.
2143
   */
2144
6.35k
  if (!zero_width) {
2145
6.35k
    switch (hanguljamo_check_state(&last.data, ud)) {
2146
0
    case HANGULJAMO_STATE_NOT_COMPOSABLE:
2147
0
      return (1);
2148
0
    case HANGULJAMO_STATE_CHOSEONG:
2149
0
      return (0);
2150
0
    case HANGULJAMO_STATE_COMPOSABLE:
2151
0
      break;
2152
6.35k
    case HANGULJAMO_STATE_NOT_HANGULJAMO:
2153
6.35k
      if (utf8_should_combine(&last.data, ud))
2154
0
        force_wide = 1;
2155
6.35k
      else if (!utf8_has_zwj(&last.data))
2156
6.35k
        return (0);
2157
0
      break;
2158
6.35k
    }
2159
6.35k
  }
2160
2161
  /* Check if this combined character would be too long. */
2162
0
  if (last.data.size + ud->size > sizeof last.data.data)
2163
0
    return (0);
2164
2165
  /* Combining; flush any pending output. */
2166
0
  screen_write_collect_flush(ctx, 0, __func__);
2167
2168
0
  log_debug("%s: %.*s -> %.*s at %u,%u (offset %u, width %u)", __func__,
2169
0
      (int)ud->size, ud->data, (int)last.data.size, last.data.data,
2170
0
      cx - n, cy, n, last.data.width);
2171
2172
  /* Append the data. */
2173
0
  memcpy(last.data.data + last.data.size, ud->data, ud->size);
2174
0
  last.data.size += ud->size;
2175
2176
  /* Force the width to 2 for modifiers and variation selector. */
2177
0
  if (last.data.width == 1 && force_wide) {
2178
0
    last.data.width = 2;
2179
0
    n = 2;
2180
0
    cx++;
2181
0
  } else
2182
0
    force_wide = 0;
2183
2184
  /* Set the new cell. */
2185
0
  grid_view_set_cell(gd, cx - n, cy, &last);
2186
0
  if (force_wide)
2187
0
    grid_view_set_padding(gd, cx - 1, cy);
2188
2189
  /*
2190
   * Redraw the combined cell. If forcing the cell to width 2, reset the
2191
   * cached cursor position in the tty, since we don't really know
2192
   * whether the terminal thought the character was width 1 or width 2
2193
   * and what it is going to do now.
2194
   */
2195
0
  screen_write_set_cursor(ctx, cx - n, cy);
2196
0
  screen_write_initctx(ctx, &ttyctx, 0);
2197
0
  ttyctx.cell = &last;
2198
0
  ttyctx.num = force_wide; /* reset cached cursor position */
2199
0
  tty_write(tty_cmd_cell, &ttyctx);
2200
0
  screen_write_set_cursor(ctx, cx, cy);
2201
2202
0
  return (1);
2203
0
}
2204
2205
/*
2206
 * UTF-8 wide characters are a bit of an annoyance. They take up more than one
2207
 * cell on the screen, so following cells must not be drawn by marking them as
2208
 * padding.
2209
 *
2210
 * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
2211
 * character, it is necessary to also overwrite any other cells which covered
2212
 * by the same character.
2213
 */
2214
static int
2215
screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
2216
    u_int width)
2217
12.7k
{
2218
12.7k
  struct screen   *s = ctx->s;
2219
12.7k
  struct grid   *gd = s->grid;
2220
12.7k
  struct grid_cell   tmp_gc;
2221
12.7k
  u_int      xx;
2222
12.7k
  int      done = 0;
2223
2224
12.7k
  if (gc->flags & GRID_FLAG_PADDING) {
2225
    /*
2226
     * A padding cell, so clear any following and leading padding
2227
     * cells back to the character. Don't overwrite the current
2228
     * cell as that happens later anyway.
2229
     */
2230
603
    xx = s->cx + 1;
2231
3.68k
    while (--xx > 0) {
2232
3.51k
      grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
2233
3.51k
      if (~tmp_gc.flags & GRID_FLAG_PADDING)
2234
426
        break;
2235
3.08k
      log_debug("%s: padding at %u,%u", __func__, xx, s->cy);
2236
3.08k
      grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2237
3.08k
    }
2238
2239
    /* Overwrite the character at the start of this padding. */
2240
603
    log_debug("%s: character at %u,%u", __func__, xx, s->cy);
2241
603
    grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2242
603
    done = 1;
2243
603
  }
2244
2245
  /*
2246
   * Overwrite any padding cells that belong to any UTF-8 characters
2247
   * we'll be overwriting with the current character.
2248
   */
2249
12.7k
  if (width != 1 ||
2250
9.68k
      gc->data.width != 1 ||
2251
9.32k
      gc->flags & GRID_FLAG_PADDING) {
2252
4.00k
    xx = s->cx + width - 1;
2253
6.52k
    while (++xx < screen_size_x(s)) {
2254
6.38k
      grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
2255
6.38k
      if (~tmp_gc.flags & GRID_FLAG_PADDING)
2256
3.86k
        break;
2257
2.51k
      log_debug("%s: overwrite at %u,%u", __func__, xx,
2258
2.51k
          s->cy);
2259
2.51k
      if (gc->flags & GRID_FLAG_TAB) {
2260
1.86k
        memcpy(&tmp_gc, gc, sizeof tmp_gc);
2261
1.86k
        memset(tmp_gc.data.data, 0,
2262
1.86k
            sizeof tmp_gc.data.data);
2263
1.86k
        *tmp_gc.data.data = ' ';
2264
1.86k
        tmp_gc.data.width = tmp_gc.data.size =
2265
1.86k
            tmp_gc.data.have = 1;
2266
1.86k
        grid_view_set_cell(gd, xx, s->cy, &tmp_gc);
2267
1.86k
      } else
2268
654
        grid_view_set_cell(gd, xx, s->cy,
2269
654
            &grid_default_cell);
2270
2.51k
      done = 1;
2271
2.51k
    }
2272
4.00k
  }
2273
2274
12.7k
  return (done);
2275
12.7k
}
2276
2277
/* Set external clipboard. */
2278
void
2279
screen_write_setselection(struct screen_write_ctx *ctx, const char *flags,
2280
    u_char *str, u_int len)
2281
1.23k
{
2282
1.23k
  struct tty_ctx  ttyctx;
2283
2284
1.23k
  screen_write_initctx(ctx, &ttyctx, 0);
2285
1.23k
  ttyctx.ptr = str;
2286
1.23k
  ttyctx.ptr2 = (void *)flags;
2287
1.23k
  ttyctx.num = len;
2288
2289
1.23k
  tty_write(tty_cmd_setselection, &ttyctx);
2290
1.23k
}
2291
2292
/* Write unmodified string. */
2293
void
2294
screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
2295
    int allow_invisible_panes)
2296
0
{
2297
0
  struct tty_ctx  ttyctx;
2298
2299
0
  screen_write_initctx(ctx, &ttyctx, 0);
2300
0
  ttyctx.ptr = str;
2301
0
  ttyctx.num = len;
2302
0
  ttyctx.allow_invisible_panes = allow_invisible_panes;
2303
2304
0
  tty_write(tty_cmd_rawstring, &ttyctx);
2305
0
}
2306
2307
#ifdef ENABLE_SIXEL
2308
/* Write a SIXEL image. */
2309
void
2310
screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
2311
    u_int bg)
2312
{
2313
  struct screen   *s = ctx->s;
2314
  struct grid   *gd = s->grid;
2315
  struct tty_ctx     ttyctx;
2316
  u_int      x, y, sx, sy, cx = s->cx, cy = s->cy, i, lines;
2317
  struct sixel_image  *new;
2318
2319
  sixel_size_in_cells(si, &x, &y);
2320
  if (x > screen_size_x(s) || y > screen_size_y(s)) {
2321
    if (x > screen_size_x(s) - cx)
2322
      sx = screen_size_x(s) - cx;
2323
    else
2324
      sx = x;
2325
    if (y > screen_size_y(s) - 1)
2326
      sy = screen_size_y(s) - 1;
2327
    else
2328
      sy = y;
2329
    new = sixel_scale(si, 0, 0, 0, y - sy, sx, sy, 1);
2330
    sixel_free(si);
2331
    si = new;
2332
2333
    /* Bail out if the image cannot be scaled. */
2334
    if (si == NULL)
2335
      return;
2336
    sixel_size_in_cells(si, &x, &y);
2337
  }
2338
2339
  sy = screen_size_y(s) - cy;
2340
  if (sy < y) {
2341
    lines = y - sy + 1;
2342
    if (image_scroll_up(s, lines) && ctx->wp != NULL)
2343
      ctx->wp->flags |= PANE_REDRAW;
2344
    for (i = 0; i < lines; i++) {
2345
      grid_view_scroll_region_up(gd, 0, screen_size_y(s) - 1,
2346
          bg);
2347
      screen_write_collect_scroll(ctx, bg);
2348
    }
2349
    ctx->scrolled += lines;
2350
    if (lines > cy)
2351
      screen_write_cursormove(ctx, -1, 0, 0);
2352
    else
2353
      screen_write_cursormove(ctx, -1, cy - lines, 0);
2354
  }
2355
  screen_write_collect_flush(ctx, 0, __func__);
2356
2357
  screen_write_initctx(ctx, &ttyctx, 0);
2358
  ttyctx.ptr = image_store(s, si);
2359
2360
  tty_write(tty_cmd_sixelimage, &ttyctx);
2361
2362
  screen_write_cursormove(ctx, 0, cy + y, 0);
2363
}
2364
#endif
2365
2366
/* Turn alternate screen on. */
2367
void
2368
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
2369
    int cursor)
2370
998
{
2371
998
  struct tty_ctx     ttyctx;
2372
998
  struct window_pane  *wp = ctx->wp;
2373
2374
998
  if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
2375
0
    return;
2376
2377
998
  screen_write_collect_flush(ctx, 0, __func__);
2378
998
  screen_alternate_on(ctx->s, gc, cursor);
2379
2380
998
  if (wp != NULL)
2381
998
    layout_fix_panes(wp->window, NULL);
2382
2383
998
  screen_write_initctx(ctx, &ttyctx, 1);
2384
998
  if (ttyctx.redraw_cb != NULL)
2385
998
    ttyctx.redraw_cb(&ttyctx);
2386
998
}
2387
2388
/* Turn alternate screen off. */
2389
void
2390
screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
2391
    int cursor)
2392
843
{
2393
843
  struct tty_ctx     ttyctx;
2394
843
  struct window_pane  *wp = ctx->wp;
2395
2396
843
  if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
2397
0
    return;
2398
2399
843
  screen_write_collect_flush(ctx, 0, __func__);
2400
843
  screen_alternate_off(ctx->s, gc, cursor);
2401
2402
843
  if (wp != NULL)
2403
843
    layout_fix_panes(wp->window, NULL);
2404
2405
843
  screen_write_initctx(ctx, &ttyctx, 1);
2406
843
  if (ttyctx.redraw_cb != NULL)
2407
843
    ttyctx.redraw_cb(&ttyctx);
2408
843
}