Coverage Report

Created: 2025-11-11 06:40

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
38.2k
{
60
38.2k
    struct screen_write_citem *ci;
61
62
38.2k
    ci = TAILQ_FIRST(&screen_write_citem_freelist);
63
38.2k
    if (ci != NULL) {
64
37.8k
        TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry);
65
37.8k
        memset(ci, 0, sizeof *ci);
66
37.8k
        return (ci);
67
37.8k
    }
68
412
    return (xcalloc(1, sizeof *ci));
69
38.2k
}
70
71
static void
72
screen_write_free_citem(struct screen_write_citem *ci)
73
29.6k
{
74
29.6k
    TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry);
75
29.6k
}
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
69.3k
{
89
69.3k
  struct window_pane  *wp = ctx->wp;
90
69.3k
  struct window   *w;
91
69.3k
  struct screen   *s = ctx->s;
92
69.3k
  struct timeval     tv = { .tv_usec = 10000 };
93
94
69.3k
  if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy)
95
8.05k
    return;
96
97
61.2k
  if (cx != -1) {
98
55.8k
    if ((u_int)cx > screen_size_x(s)) /* allow last column */
99
119
      cx = screen_size_x(s) - 1;
100
55.8k
    s->cx = cx;
101
55.8k
  }
102
61.2k
  if (cy != -1) {
103
25.2k
    if ((u_int)cy > screen_size_y(s) - 1)
104
0
      cy = screen_size_y(s) - 1;
105
25.2k
    s->cy = cy;
106
25.2k
  }
107
108
61.2k
  if (wp == NULL)
109
0
    return;
110
61.2k
  w = wp->window;
111
112
61.2k
  if (!event_initialized(&w->offset_timer))
113
61.2k
    evtimer_set(&w->offset_timer, screen_write_offset_timer, w);
114
61.2k
  if (!evtimer_pending(&w->offset_timer, NULL))
115
61.2k
    evtimer_add(&w->offset_timer, &tv);
116
61.2k
}
117
118
/* Do a full redraw. */
119
static void
120
screen_write_redraw_cb(const struct tty_ctx *ttyctx)
121
2.77k
{
122
2.77k
  struct window_pane  *wp = ttyctx->arg;
123
124
2.77k
  if (wp != NULL)
125
2.77k
    wp->flags |= PANE_REDRAW;
126
2.77k
}
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
50.8k
{
175
50.8k
  struct screen *s = ctx->s;
176
177
50.8k
  memset(ttyctx, 0, sizeof *ttyctx);
178
179
50.8k
  ttyctx->s = s;
180
50.8k
  ttyctx->sx = screen_size_x(s);
181
50.8k
  ttyctx->sy = screen_size_y(s);
182
183
50.8k
  ttyctx->ocx = s->cx;
184
50.8k
  ttyctx->ocy = s->cy;
185
50.8k
  ttyctx->orlower = s->rlower;
186
50.8k
  ttyctx->orupper = s->rupper;
187
188
50.8k
  memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults);
189
50.8k
  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
50.8k
  } else {
198
50.8k
    ttyctx->redraw_cb = screen_write_redraw_cb;
199
50.8k
    if (ctx->wp != NULL) {
200
50.8k
      tty_default_colours(&ttyctx->defaults, ctx->wp);
201
50.8k
      ttyctx->palette = &ctx->wp->palette;
202
50.8k
      ttyctx->set_client_cb = screen_write_set_client_cb;
203
50.8k
      ttyctx->arg = ctx->wp;
204
50.8k
    }
205
50.8k
  }
206
207
50.8k
  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
4.89k
    if (ctx->wp != NULL) {
215
4.89k
      if (ctx->wp != ctx->wp->window->active)
216
4.89k
        ttyctx->num = 1;
217
0
      else
218
0
        ttyctx->num = sync;
219
4.89k
    } else
220
0
      ttyctx->num = 0x10|sync;
221
4.89k
    tty_write(tty_cmd_syncstart, ttyctx);
222
4.89k
    ctx->flags |= SCREEN_WRITE_SYNC;
223
4.89k
  }
224
50.8k
}
225
226
/* Make write list. */
227
void
228
screen_write_make_list(struct screen *s)
229
12.5k
{
230
12.5k
  u_int y;
231
232
12.5k
  s->write_list = xcalloc(screen_size_y(s), sizeof *s->write_list);
233
327k
  for (y = 0; y < screen_size_y(s); y++)
234
314k
    TAILQ_INIT(&s->write_list[y].items);
235
12.5k
}
236
237
/* Free write list. */
238
void
239
screen_write_free_list(struct screen *s)
240
12.5k
{
241
12.5k
  u_int y;
242
243
327k
  for (y = 0; y < screen_size_y(s); y++)
244
314k
    free(s->write_list[y].data);
245
12.5k
  free(s->write_list);
246
12.5k
}
247
248
/* Set up for writing. */
249
static void
250
screen_write_init(struct screen_write_ctx *ctx, struct screen *s)
251
13.0k
{
252
13.0k
  memset(ctx, 0, sizeof *ctx);
253
254
13.0k
  ctx->s = s;
255
256
13.0k
  if (ctx->s->write_list == NULL)
257
11.9k
    screen_write_make_list(ctx->s);
258
13.0k
  ctx->item = screen_write_get_citem();
259
260
13.0k
  ctx->scrolled = 0;
261
13.0k
  ctx->bg = 8;
262
13.0k
}
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
13.0k
{
269
13.0k
  if (s == NULL)
270
1.09k
    s = wp->screen;
271
13.0k
  screen_write_init(ctx, s);
272
13.0k
  ctx->wp = wp;
273
274
13.0k
  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
13.0k
}
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
13.0k
{
313
13.0k
  screen_write_collect_end(ctx);
314
13.0k
  screen_write_collect_flush(ctx, 0, __func__);
315
316
13.0k
  screen_write_free_citem(ctx->item);
317
13.0k
}
318
319
/* Reset screen state. */
320
void
321
screen_write_reset(struct screen_write_ctx *ctx)
322
797
{
323
797
  struct screen *s = ctx->s;
324
325
797
  screen_reset_tabs(s);
326
797
  screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1);
327
328
797
  s->mode = MODE_CURSOR|MODE_WRAP;
329
330
797
  if (options_get_number(global_options, "extended-keys") == 2)
331
0
    s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED;
332
333
797
  screen_write_clearscreen(ctx, 8);
334
797
  screen_write_set_cursor(ctx, 0, 0);
335
797
}
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
        break;
588
0
      grid_get_cell(gd, xx, yy, &gc);
589
0
      if (xx + gc.data.width > px + nx)
590
0
        break;
591
0
      grid_view_set_cell(ctx->s->grid, s->cx, s->cy, &gc);
592
0
      if (wp != NULL) {
593
0
        ttyctx.cell = &gc;
594
0
        tty_write(tty_cmd_cell, &ttyctx);
595
0
        ttyctx.ocx++;
596
0
      }
597
0
      s->cx++;
598
0
    }
599
0
    s->cy++;
600
0
  }
601
602
0
  s->cx = cx;
603
0
  s->cy = cy;
604
0
}
605
606
/* Select character set for drawing border lines. */
607
static void
608
screen_write_box_border_set(enum box_lines lines, int cell_type,
609
    struct grid_cell *gc)
610
0
{
611
0
  switch (lines) {
612
0
        case BOX_LINES_NONE:
613
0
    break;
614
0
        case BOX_LINES_DOUBLE:
615
0
                gc->attr &= ~GRID_ATTR_CHARSET;
616
0
                utf8_copy(&gc->data, tty_acs_double_borders(cell_type));
617
0
    break;
618
0
        case BOX_LINES_HEAVY:
619
0
                gc->attr &= ~GRID_ATTR_CHARSET;
620
0
                utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
621
0
    break;
622
0
        case BOX_LINES_ROUNDED:
623
0
                gc->attr &= ~GRID_ATTR_CHARSET;
624
0
                utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type));
625
0
    break;
626
0
        case BOX_LINES_SIMPLE:
627
0
                gc->attr &= ~GRID_ATTR_CHARSET;
628
0
                utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
629
0
                break;
630
0
        case BOX_LINES_PADDED:
631
0
                gc->attr &= ~GRID_ATTR_CHARSET;
632
0
                utf8_set(&gc->data, PADDED_BORDERS[cell_type]);
633
0
                break;
634
0
  case BOX_LINES_SINGLE:
635
0
  case BOX_LINES_DEFAULT:
636
0
    gc->attr |= GRID_ATTR_CHARSET;
637
0
    utf8_set(&gc->data, CELL_BORDERS[cell_type]);
638
0
    break;
639
0
  }
640
0
}
641
642
/* Draw a horizontal line on screen. */
643
void
644
screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right,
645
   enum box_lines lines, const struct grid_cell *border_gc)
646
0
{
647
0
  struct screen   *s = ctx->s;
648
0
  struct grid_cell   gc;
649
0
  u_int      cx, cy, i;
650
651
0
  cx = s->cx;
652
0
  cy = s->cy;
653
654
0
  if (border_gc != NULL)
655
0
    memcpy(&gc, border_gc, sizeof gc);
656
0
  else
657
0
    memcpy(&gc, &grid_default_cell, sizeof gc);
658
0
  gc.attr |= GRID_ATTR_CHARSET;
659
660
0
  if (left)
661
0
    screen_write_box_border_set(lines, CELL_LEFTJOIN, &gc);
662
0
  else
663
0
    screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
664
0
  screen_write_cell(ctx, &gc);
665
666
0
  screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
667
0
  for (i = 1; i < nx - 1; i++)
668
0
    screen_write_cell(ctx, &gc);
669
670
0
  if (right)
671
0
    screen_write_box_border_set(lines, CELL_RIGHTJOIN, &gc);
672
0
  else
673
0
    screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
674
0
  screen_write_cell(ctx, &gc);
675
676
0
  screen_write_set_cursor(ctx, cx, cy);
677
0
}
678
679
/* Draw a vertical line on screen. */
680
void
681
screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
682
0
{
683
0
  struct screen   *s = ctx->s;
684
0
  struct grid_cell   gc;
685
0
  u_int      cx, cy, i;
686
687
0
  cx = s->cx;
688
0
  cy = s->cy;
689
690
0
  memcpy(&gc, &grid_default_cell, sizeof gc);
691
0
  gc.attr |= GRID_ATTR_CHARSET;
692
693
0
  screen_write_putc(ctx, &gc, top ? 'w' : 'x');
694
0
  for (i = 1; i < ny - 1; i++) {
695
0
    screen_write_set_cursor(ctx, cx, cy + i);
696
0
    screen_write_putc(ctx, &gc, 'x');
697
0
  }
698
0
  screen_write_set_cursor(ctx, cx, cy + ny - 1);
699
0
  screen_write_putc(ctx, &gc, bottom ? 'v' : 'x');
700
701
0
  screen_write_set_cursor(ctx, cx, cy);
702
0
}
703
704
/* Draw a menu on screen. */
705
void
706
screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice,
707
    enum box_lines lines, const struct grid_cell *menu_gc,
708
    const struct grid_cell *border_gc, const struct grid_cell *choice_gc)
709
0
{
710
0
  struct screen   *s = ctx->s;
711
0
  struct grid_cell   default_gc;
712
0
  const struct grid_cell  *gc = &default_gc;
713
0
  u_int      cx, cy, i, j, width = menu->width;
714
0
  const char    *name;
715
716
0
  cx = s->cx;
717
0
  cy = s->cy;
718
719
0
  memcpy(&default_gc, menu_gc, sizeof default_gc);
720
721
0
  screen_write_box(ctx, menu->width + 4, menu->count + 2, lines,
722
0
      border_gc, menu->title);
723
724
0
  for (i = 0; i < menu->count; i++) {
725
0
    name = menu->items[i].name;
726
0
    if (name == NULL) {
727
0
      screen_write_cursormove(ctx, cx, cy + 1 + i, 0);
728
0
      screen_write_hline(ctx, width + 4, 1, 1, lines,
729
0
          border_gc);
730
0
      continue;
731
0
    }
732
733
0
    if (choice >= 0 && i == (u_int)choice && *name != '-')
734
0
      gc = choice_gc;
735
736
0
    screen_write_cursormove(ctx, cx + 1, cy + 1 + i, 0);
737
0
    for (j = 0; j < width + 2; j++)
738
0
      screen_write_putc(ctx, gc, ' ');
739
740
0
    screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
741
0
    if (*name == '-') {
742
0
      default_gc.attr |= GRID_ATTR_DIM;
743
0
      format_draw(ctx, gc, width, name + 1, NULL, 0);
744
0
      default_gc.attr &= ~GRID_ATTR_DIM;
745
0
      continue;
746
0
    }
747
748
0
    format_draw(ctx, gc, width, name, NULL, 0);
749
0
    gc = &default_gc;
750
0
  }
751
752
0
  screen_write_set_cursor(ctx, cx, cy);
753
0
}
754
755
/* Draw a box on screen. */
756
void
757
screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny,
758
    enum box_lines lines, const struct grid_cell *gcp, const char *title)
759
0
{
760
0
  struct screen   *s = ctx->s;
761
0
  struct grid_cell         gc;
762
0
  u_int      cx, cy, i;
763
764
0
  cx = s->cx;
765
0
  cy = s->cy;
766
767
0
  if (gcp != NULL)
768
0
    memcpy(&gc, gcp, sizeof gc);
769
0
  else
770
0
    memcpy(&gc, &grid_default_cell, sizeof gc);
771
772
0
  gc.attr |= GRID_ATTR_CHARSET;
773
0
  gc.flags |= GRID_FLAG_NOPALETTE;
774
775
  /* Draw top border */
776
0
  screen_write_box_border_set(lines, CELL_TOPLEFT, &gc);
777
0
  screen_write_cell(ctx, &gc);
778
0
  screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
779
0
  for (i = 1; i < nx - 1; i++)
780
0
    screen_write_cell(ctx, &gc);
781
0
  screen_write_box_border_set(lines, CELL_TOPRIGHT, &gc);
782
0
  screen_write_cell(ctx, &gc);
783
784
  /* Draw bottom border */
785
0
  screen_write_set_cursor(ctx, cx, cy + ny - 1);
786
0
  screen_write_box_border_set(lines, CELL_BOTTOMLEFT, &gc);
787
0
  screen_write_cell(ctx, &gc);
788
0
  screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
789
0
  for (i = 1; i < nx - 1; i++)
790
0
    screen_write_cell(ctx, &gc);
791
0
  screen_write_box_border_set(lines, CELL_BOTTOMRIGHT, &gc);
792
0
  screen_write_cell(ctx, &gc);
793
794
  /* Draw sides */
795
0
  screen_write_box_border_set(lines, CELL_TOPBOTTOM, &gc);
796
0
  for (i = 1; i < ny - 1; i++) {
797
    /* left side */
798
0
    screen_write_set_cursor(ctx, cx, cy + i);
799
0
    screen_write_cell(ctx, &gc);
800
    /* right side */
801
0
    screen_write_set_cursor(ctx, cx + nx - 1, cy + i);
802
0
    screen_write_cell(ctx, &gc);
803
0
  }
804
805
0
  if (title != NULL) {
806
0
    gc.attr &= ~GRID_ATTR_CHARSET;
807
0
    screen_write_cursormove(ctx, cx + 2, cy, 0);
808
0
    format_draw(ctx, &gc, nx - 4, title, NULL, 0);
809
0
  }
810
811
0
  screen_write_set_cursor(ctx, cx, cy);
812
0
}
813
814
/*
815
 * Write a preview version of a window. Assumes target area is big enough and
816
 * already cleared.
817
 */
818
void
819
screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
820
    u_int ny)
821
0
{
822
0
  struct screen   *s = ctx->s;
823
0
  struct grid_cell   gc;
824
0
  u_int      cx, cy, px, py;
825
826
0
  cx = s->cx;
827
0
  cy = s->cy;
828
829
  /*
830
   * If the cursor is on, pick the area around the cursor, otherwise use
831
   * the top left.
832
   */
833
0
  if (src->mode & MODE_CURSOR) {
834
0
    px = src->cx;
835
0
    if (px < nx / 3)
836
0
      px = 0;
837
0
    else
838
0
      px = px - nx / 3;
839
0
    if (px + nx > screen_size_x(src)) {
840
0
      if (nx > screen_size_x(src))
841
0
        px = 0;
842
0
      else
843
0
        px = screen_size_x(src) - nx;
844
0
    }
845
0
    py = src->cy;
846
0
    if (py < ny / 3)
847
0
      py = 0;
848
0
    else
849
0
      py = py - ny / 3;
850
0
    if (py + ny > screen_size_y(src)) {
851
0
      if (ny > screen_size_y(src))
852
0
        py = 0;
853
0
      else
854
0
        py = screen_size_y(src) - ny;
855
0
    }
856
0
  } else {
857
0
    px = 0;
858
0
    py = 0;
859
0
  }
860
861
0
  screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny);
862
863
0
  if (src->mode & MODE_CURSOR) {
864
0
    grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
865
0
    gc.attr |= GRID_ATTR_REVERSE;
866
0
    screen_write_set_cursor(ctx, cx + (src->cx - px),
867
0
        cy + (src->cy - py));
868
0
    screen_write_cell(ctx, &gc);
869
0
  }
870
0
}
871
872
/* Set a mode. */
873
void
874
screen_write_mode_set(struct screen_write_ctx *ctx, int mode)
875
2.73k
{
876
2.73k
  struct screen *s = ctx->s;
877
878
2.73k
  s->mode |= mode;
879
880
2.73k
  if (log_get_level() != 0)
881
0
    log_debug("%s: %s", __func__, screen_mode_to_string(mode));
882
2.73k
}
883
884
/* Clear a mode. */
885
void
886
screen_write_mode_clear(struct screen_write_ctx *ctx, int mode)
887
3.06k
{
888
3.06k
  struct screen *s = ctx->s;
889
890
3.06k
  s->mode &= ~mode;
891
892
3.06k
  if (log_get_level() != 0)
893
0
    log_debug("%s: %s", __func__, screen_mode_to_string(mode));
894
3.06k
}
895
896
/* Cursor up by ny. */
897
void
898
screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
899
1.56k
{
900
1.56k
  struct screen *s = ctx->s;
901
1.56k
  u_int    cx = s->cx, cy = s->cy;
902
903
1.56k
  if (ny == 0)
904
0
    ny = 1;
905
906
1.56k
  if (cy < s->rupper) {
907
    /* Above region. */
908
399
    if (ny > cy)
909
205
      ny = cy;
910
1.16k
  } else {
911
    /* Below region. */
912
1.16k
    if (ny > cy - s->rupper)
913
871
      ny = cy - s->rupper;
914
1.16k
  }
915
1.56k
  if (cx == screen_size_x(s))
916
66
    cx--;
917
918
1.56k
  cy -= ny;
919
920
1.56k
  screen_write_set_cursor(ctx, cx, cy);
921
1.56k
}
922
923
/* Cursor down by ny. */
924
void
925
screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
926
1.86k
{
927
1.86k
  struct screen *s = ctx->s;
928
1.86k
  u_int    cx = s->cx, cy = s->cy;
929
930
1.86k
  if (ny == 0)
931
0
    ny = 1;
932
933
1.86k
  if (cy > s->rlower) {
934
    /* Below region. */
935
443
    if (ny > screen_size_y(s) - 1 - cy)
936
246
      ny = screen_size_y(s) - 1 - cy;
937
1.42k
  } else {
938
    /* Above region. */
939
1.42k
    if (ny > s->rlower - cy)
940
699
      ny = s->rlower - cy;
941
1.42k
  }
942
1.86k
  if (cx == screen_size_x(s))
943
66
      cx--;
944
1.80k
  else if (ny == 0)
945
743
    return;
946
947
1.12k
  cy += ny;
948
949
1.12k
  screen_write_set_cursor(ctx, cx, cy);
950
1.12k
}
951
952
/* Cursor right by nx. */
953
void
954
screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
955
925
{
956
925
  struct screen *s = ctx->s;
957
925
  u_int    cx = s->cx, cy = s->cy;
958
959
925
  if (nx == 0)
960
0
    nx = 1;
961
962
925
  if (nx > screen_size_x(s) - 1 - cx)
963
386
    nx = screen_size_x(s) - 1 - cx;
964
925
  if (nx == 0)
965
222
    return;
966
967
703
  cx += nx;
968
969
703
  screen_write_set_cursor(ctx, cx, cy);
970
703
}
971
972
/* Cursor left by nx. */
973
void
974
screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx)
975
775
{
976
775
  struct screen *s = ctx->s;
977
775
  u_int    cx = s->cx, cy = s->cy;
978
979
775
  if (nx == 0)
980
0
    nx = 1;
981
982
775
  if (nx > cx)
983
573
    nx = cx;
984
775
  if (nx == 0)
985
568
    return;
986
987
207
  cx -= nx;
988
989
207
  screen_write_set_cursor(ctx, cx, cy);
990
207
}
991
992
/* Backspace; cursor left unless at start of wrapped line when can move up. */
993
void
994
screen_write_backspace(struct screen_write_ctx *ctx)
995
3.43k
{
996
3.43k
  struct screen   *s = ctx->s;
997
3.43k
  struct grid_line  *gl;
998
3.43k
  u_int      cx = s->cx, cy = s->cy;
999
1000
3.43k
  if (cx == 0) {
1001
602
    if (cy == 0)
1002
210
      return;
1003
392
    gl = grid_get_line(s->grid, s->grid->hsize + cy - 1);
1004
392
    if (gl->flags & GRID_LINE_WRAPPED) {
1005
197
      cy--;
1006
197
      cx = screen_size_x(s) - 1;
1007
197
    }
1008
392
  } else
1009
2.83k
    cx--;
1010
1011
3.22k
  screen_write_set_cursor(ctx, cx, cy);
1012
3.22k
}
1013
1014
/* VT100 alignment test. */
1015
void
1016
screen_write_alignmenttest(struct screen_write_ctx *ctx)
1017
1.15k
{
1018
1.15k
  struct screen   *s = ctx->s;
1019
1.15k
  struct tty_ctx     ttyctx;
1020
1.15k
  struct grid_cell         gc;
1021
1.15k
  u_int      xx, yy;
1022
1023
1.15k
  memcpy(&gc, &grid_default_cell, sizeof gc);
1024
1.15k
  utf8_set(&gc.data, 'E');
1025
1026
#ifdef ENABLE_SIXEL
1027
  if (image_free_all(s) && ctx->wp != NULL)
1028
    ctx->wp->flags |= PANE_REDRAW;
1029
#endif
1030
1031
30.0k
  for (yy = 0; yy < screen_size_y(s); yy++) {
1032
2.33M
    for (xx = 0; xx < screen_size_x(s); xx++)
1033
2.31M
      grid_view_set_cell(s->grid, xx, yy, &gc);
1034
28.8k
  }
1035
1036
1.15k
  screen_write_set_cursor(ctx, 0, 0);
1037
1038
1.15k
  s->rupper = 0;
1039
1.15k
  s->rlower = screen_size_y(s) - 1;
1040
1041
1.15k
  screen_write_initctx(ctx, &ttyctx, 1);
1042
1043
1.15k
  screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1);
1044
1.15k
  tty_write(tty_cmd_alignmenttest, &ttyctx);
1045
1.15k
}
1046
1047
/* Insert nx characters. */
1048
void
1049
screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1050
1.30k
{
1051
1.30k
  struct screen *s = ctx->s;
1052
1.30k
  struct tty_ctx   ttyctx;
1053
1054
1.30k
  if (nx == 0)
1055
0
    nx = 1;
1056
1057
1.30k
  if (nx > screen_size_x(s) - s->cx)
1058
258
    nx = screen_size_x(s) - s->cx;
1059
1.30k
  if (nx == 0)
1060
194
    return;
1061
1062
1.11k
  if (s->cx > screen_size_x(s) - 1)
1063
0
    return;
1064
1065
#ifdef ENABLE_SIXEL
1066
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1067
    ctx->wp->flags |= PANE_REDRAW;
1068
#endif
1069
1070
1.11k
  screen_write_initctx(ctx, &ttyctx, 0);
1071
1.11k
  ttyctx.bg = bg;
1072
1073
1.11k
  grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg);
1074
1075
1.11k
  screen_write_collect_flush(ctx, 0, __func__);
1076
1.11k
  ttyctx.num = nx;
1077
1.11k
  tty_write(tty_cmd_insertcharacter, &ttyctx);
1078
1.11k
}
1079
1080
/* Delete nx characters. */
1081
void
1082
screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1083
1.11k
{
1084
1.11k
  struct screen *s = ctx->s;
1085
1.11k
  struct tty_ctx   ttyctx;
1086
1087
1.11k
  if (nx == 0)
1088
0
    nx = 1;
1089
1090
1.11k
  if (nx > screen_size_x(s) - s->cx)
1091
256
    nx = screen_size_x(s) - s->cx;
1092
1.11k
  if (nx == 0)
1093
194
    return;
1094
1095
924
  if (s->cx > screen_size_x(s) - 1)
1096
0
    return;
1097
1098
#ifdef ENABLE_SIXEL
1099
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1100
    ctx->wp->flags |= PANE_REDRAW;
1101
#endif
1102
1103
924
  screen_write_initctx(ctx, &ttyctx, 0);
1104
924
  ttyctx.bg = bg;
1105
1106
924
  grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg);
1107
1108
924
  screen_write_collect_flush(ctx, 0, __func__);
1109
924
  ttyctx.num = nx;
1110
924
  tty_write(tty_cmd_deletecharacter, &ttyctx);
1111
924
}
1112
1113
/* Clear nx characters. */
1114
void
1115
screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1116
1.05k
{
1117
1.05k
  struct screen *s = ctx->s;
1118
1.05k
  struct tty_ctx   ttyctx;
1119
1120
1.05k
  if (nx == 0)
1121
0
    nx = 1;
1122
1123
1.05k
  if (nx > screen_size_x(s) - s->cx)
1124
252
    nx = screen_size_x(s) - s->cx;
1125
1.05k
  if (nx == 0)
1126
195
    return;
1127
1128
863
  if (s->cx > screen_size_x(s) - 1)
1129
0
    return;
1130
1131
#ifdef ENABLE_SIXEL
1132
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1133
    ctx->wp->flags |= PANE_REDRAW;
1134
#endif
1135
1136
863
  screen_write_initctx(ctx, &ttyctx, 0);
1137
863
  ttyctx.bg = bg;
1138
1139
863
  grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg);
1140
1141
863
  screen_write_collect_flush(ctx, 0, __func__);
1142
863
  ttyctx.num = nx;
1143
863
  tty_write(tty_cmd_clearcharacter, &ttyctx);
1144
863
}
1145
1146
/* Insert ny lines. */
1147
void
1148
screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1149
995
{
1150
995
  struct screen *s = ctx->s;
1151
995
  struct grid *gd = s->grid;
1152
995
  struct tty_ctx   ttyctx;
1153
1154
#ifdef ENABLE_SIXEL
1155
  u_int    sy = screen_size_y(s);
1156
#endif
1157
1158
995
  if (ny == 0)
1159
0
    ny = 1;
1160
1161
#ifdef ENABLE_SIXEL
1162
  if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
1163
    ctx->wp->flags |= PANE_REDRAW;
1164
#endif
1165
1166
995
  if (s->cy < s->rupper || s->cy > s->rlower) {
1167
487
    if (ny > screen_size_y(s) - s->cy)
1168
95
      ny = screen_size_y(s) - s->cy;
1169
487
    if (ny == 0)
1170
0
      return;
1171
1172
487
    screen_write_initctx(ctx, &ttyctx, 1);
1173
487
    ttyctx.bg = bg;
1174
1175
487
    grid_view_insert_lines(gd, s->cy, ny, bg);
1176
1177
487
    screen_write_collect_flush(ctx, 0, __func__);
1178
487
    ttyctx.num = ny;
1179
487
    tty_write(tty_cmd_insertline, &ttyctx);
1180
487
    return;
1181
487
  }
1182
1183
508
  if (ny > s->rlower + 1 - s->cy)
1184
120
    ny = s->rlower + 1 - s->cy;
1185
508
  if (ny == 0)
1186
0
    return;
1187
1188
508
  screen_write_initctx(ctx, &ttyctx, 1);
1189
508
  ttyctx.bg = bg;
1190
1191
508
  if (s->cy < s->rupper || s->cy > s->rlower)
1192
0
    grid_view_insert_lines(gd, s->cy, ny, bg);
1193
508
  else
1194
508
    grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg);
1195
1196
508
  screen_write_collect_flush(ctx, 0, __func__);
1197
1198
508
  ttyctx.num = ny;
1199
508
  tty_write(tty_cmd_insertline, &ttyctx);
1200
508
}
1201
1202
/* Delete ny lines. */
1203
void
1204
screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1205
1.24k
{
1206
1.24k
  struct screen *s = ctx->s;
1207
1.24k
  struct grid *gd = s->grid;
1208
1.24k
  struct tty_ctx   ttyctx;
1209
1.24k
  u_int    sy = screen_size_y(s);
1210
1211
1.24k
  if (ny == 0)
1212
0
    ny = 1;
1213
1214
#ifdef ENABLE_SIXEL
1215
  if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
1216
    ctx->wp->flags |= PANE_REDRAW;
1217
#endif
1218
1219
1.24k
  if (s->cy < s->rupper || s->cy > s->rlower) {
1220
518
    if (ny > sy - s->cy)
1221
102
      ny = sy - s->cy;
1222
518
    if (ny == 0)
1223
0
      return;
1224
1225
518
    screen_write_initctx(ctx, &ttyctx, 1);
1226
518
    ttyctx.bg = bg;
1227
1228
518
    grid_view_delete_lines(gd, s->cy, ny, bg);
1229
1230
518
    screen_write_collect_flush(ctx, 0, __func__);
1231
518
    ttyctx.num = ny;
1232
518
    tty_write(tty_cmd_deleteline, &ttyctx);
1233
518
    return;
1234
518
  }
1235
1236
724
  if (ny > s->rlower + 1 - s->cy)
1237
135
    ny = s->rlower + 1 - s->cy;
1238
724
  if (ny == 0)
1239
0
    return;
1240
1241
724
  screen_write_initctx(ctx, &ttyctx, 1);
1242
724
  ttyctx.bg = bg;
1243
1244
724
  if (s->cy < s->rupper || s->cy > s->rlower)
1245
0
    grid_view_delete_lines(gd, s->cy, ny, bg);
1246
724
  else
1247
724
    grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg);
1248
1249
724
  screen_write_collect_flush(ctx, 0, __func__);
1250
724
  ttyctx.num = ny;
1251
724
  tty_write(tty_cmd_deleteline, &ttyctx);
1252
724
}
1253
1254
/* Clear line at cursor. */
1255
void
1256
screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
1257
888
{
1258
888
  struct screen     *s = ctx->s;
1259
888
  struct grid_line    *gl;
1260
888
  u_int        sx = screen_size_x(s);
1261
888
  struct screen_write_citem *ci = ctx->item;
1262
1263
888
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1264
888
  if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
1265
398
    return;
1266
1267
#ifdef ENABLE_SIXEL
1268
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1269
    ctx->wp->flags |= PANE_REDRAW;
1270
#endif
1271
1272
490
  grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1273
1274
490
  screen_write_collect_clear(ctx, s->cy, 1);
1275
490
  ci->x = 0;
1276
490
  ci->used = sx;
1277
490
  ci->type = CLEAR;
1278
490
  ci->bg = bg;
1279
490
  TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1280
490
  ctx->item = screen_write_get_citem();
1281
490
}
1282
1283
/* Clear to end of line from cursor. */
1284
void
1285
screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
1286
1.99k
{
1287
1.99k
  struct screen     *s = ctx->s;
1288
1.99k
  struct grid_line    *gl;
1289
1.99k
  u_int        sx = screen_size_x(s);
1290
1.99k
  struct screen_write_citem *ci = ctx->item, *before;
1291
1292
1.99k
  if (s->cx == 0) {
1293
742
    screen_write_clearline(ctx, bg);
1294
742
    return;
1295
742
  }
1296
1297
1.25k
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1298
1.25k
  if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
1299
395
    return;
1300
1301
#ifdef ENABLE_SIXEL
1302
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1303
    ctx->wp->flags |= PANE_REDRAW;
1304
#endif
1305
1306
857
  grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
1307
1308
857
  before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL);
1309
857
  ci->x = s->cx;
1310
857
  ci->used = sx - s->cx;
1311
857
  ci->type = CLEAR;
1312
857
  ci->bg = bg;
1313
857
  if (before == NULL)
1314
857
    TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1315
0
  else
1316
0
    TAILQ_INSERT_BEFORE(before, ci, entry);
1317
857
  ctx->item = screen_write_get_citem();
1318
857
}
1319
1320
/* Clear to start of line from cursor. */
1321
void
1322
screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
1323
303
{
1324
303
  struct screen      *s = ctx->s;
1325
303
  u_int        sx = screen_size_x(s);
1326
303
  struct screen_write_citem *ci = ctx->item, *before;
1327
1328
303
  if (s->cx >= sx - 1) {
1329
66
    screen_write_clearline(ctx, bg);
1330
66
    return;
1331
66
  }
1332
1333
#ifdef ENABLE_SIXEL
1334
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1335
    ctx->wp->flags |= PANE_REDRAW;
1336
#endif
1337
1338
237
  if (s->cx > sx - 1)
1339
0
    grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1340
237
  else
1341
237
    grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1342
1343
237
  before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL);
1344
237
  ci->x = 0;
1345
237
  ci->used = s->cx + 1;
1346
237
  ci->type = CLEAR;
1347
237
  ci->bg = bg;
1348
237
  if (before == NULL)
1349
163
    TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1350
74
  else
1351
74
    TAILQ_INSERT_BEFORE(before, ci, entry);
1352
237
  ctx->item = screen_write_get_citem();
1353
237
}
1354
1355
/* Move cursor to px,py. */
1356
void
1357
screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
1358
    int origin)
1359
4.46k
{
1360
4.46k
  struct screen *s = ctx->s;
1361
1362
4.46k
  if (origin && py != -1 && (s->mode & MODE_ORIGIN)) {
1363
541
    if ((u_int)py > s->rlower - s->rupper)
1364
66
      py = s->rlower;
1365
475
    else
1366
475
      py += s->rupper;
1367
541
  }
1368
1369
4.46k
  if (px != -1 && (u_int)px > screen_size_x(s) - 1)
1370
347
    px = screen_size_x(s) - 1;
1371
4.46k
  if (py != -1 && (u_int)py > screen_size_y(s) - 1)
1372
218
    py = screen_size_y(s) - 1;
1373
1374
4.46k
  log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py);
1375
4.46k
  screen_write_set_cursor(ctx, px, py);
1376
4.46k
}
1377
1378
/* Reverse index (up with scroll). */
1379
void
1380
screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
1381
615
{
1382
615
  struct screen *s = ctx->s;
1383
615
  struct tty_ctx   ttyctx;
1384
1385
615
  if (s->cy == s->rupper) {
1386
#ifdef ENABLE_SIXEL
1387
    if (image_free_all(s) && ctx->wp != NULL)
1388
      ctx->wp->flags |= PANE_REDRAW;
1389
#endif
1390
1391
220
    grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
1392
220
    screen_write_collect_flush(ctx, 0, __func__);
1393
1394
220
    screen_write_initctx(ctx, &ttyctx, 1);
1395
220
    ttyctx.bg = bg;
1396
1397
220
    tty_write(tty_cmd_reverseindex, &ttyctx);
1398
395
  } else if (s->cy > 0)
1399
200
    screen_write_set_cursor(ctx, -1, s->cy - 1);
1400
1401
615
}
1402
1403
/* Set scroll region. */
1404
void
1405
screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper,
1406
    u_int rlower)
1407
2.00k
{
1408
2.00k
  struct screen *s = ctx->s;
1409
1410
2.00k
  if (rupper > screen_size_y(s) - 1)
1411
128
    rupper = screen_size_y(s) - 1;
1412
2.00k
  if (rlower > screen_size_y(s) - 1)
1413
126
    rlower = screen_size_y(s) - 1;
1414
2.00k
  if (rupper >= rlower)  /* cannot be one line */
1415
203
    return;
1416
1417
1.79k
  screen_write_collect_flush(ctx, 0, __func__);
1418
1419
  /* Cursor moves to top-left. */
1420
1.79k
  screen_write_set_cursor(ctx, 0, 0);
1421
1422
1.79k
  s->rupper = rupper;
1423
1.79k
  s->rlower = rlower;
1424
1.79k
}
1425
1426
/* Line feed. */
1427
void
1428
screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
1429
8.36k
{
1430
8.36k
  struct screen   *s = ctx->s;
1431
8.36k
  struct grid   *gd = s->grid;
1432
8.36k
  struct grid_line  *gl;
1433
#ifdef ENABLE_SIXEL
1434
  int      redraw = 0;
1435
#endif
1436
8.36k
  u_int      rupper = s->rupper, rlower = s->rlower;
1437
1438
8.36k
  gl = grid_get_line(gd, gd->hsize + s->cy);
1439
8.36k
  if (wrapped)
1440
1.59k
    gl->flags |= GRID_LINE_WRAPPED;
1441
1442
8.36k
  log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1443
8.36k
      rupper, rlower);
1444
1445
8.36k
  if (bg != ctx->bg) {
1446
532
    screen_write_collect_flush(ctx, 1, __func__);
1447
532
    ctx->bg = bg;
1448
532
  }
1449
1450
8.36k
  if (s->cy == s->rlower) {
1451
#ifdef ENABLE_SIXEL
1452
    if (rlower == screen_size_y(s) - 1)
1453
      redraw = image_scroll_up(s, 1);
1454
    else
1455
      redraw = image_check_line(s, rupper, rlower - rupper);
1456
    if (redraw && ctx->wp != NULL)
1457
      ctx->wp->flags |= PANE_REDRAW;
1458
#endif
1459
3.68k
    grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1460
3.68k
    screen_write_collect_scroll(ctx, bg);
1461
3.68k
    ctx->scrolled++;
1462
4.67k
  } else if (s->cy < screen_size_y(s) - 1)
1463
4.35k
    screen_write_set_cursor(ctx, -1, s->cy + 1);
1464
8.36k
}
1465
1466
/* Scroll up. */
1467
void
1468
screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1469
1.04k
{
1470
1.04k
  struct screen *s = ctx->s;
1471
1.04k
  struct grid *gd = s->grid;
1472
1.04k
  u_int    i;
1473
1474
1.04k
  if (lines == 0)
1475
0
    lines = 1;
1476
1.04k
  else if (lines > s->rlower - s->rupper + 1)
1477
349
    lines = s->rlower - s->rupper + 1;
1478
1479
1.04k
  if (bg != ctx->bg) {
1480
118
    screen_write_collect_flush(ctx, 1, __func__);
1481
118
    ctx->bg = bg;
1482
118
  }
1483
1484
#ifdef ENABLE_SIXEL
1485
  if (image_scroll_up(s, lines) && ctx->wp != NULL)
1486
    ctx->wp->flags |= PANE_REDRAW;
1487
#endif
1488
1489
10.0k
  for (i = 0; i < lines; i++) {
1490
9.03k
    grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1491
9.03k
    screen_write_collect_scroll(ctx, bg);
1492
9.03k
  }
1493
1.04k
  ctx->scrolled += lines;
1494
1.04k
}
1495
1496
/* Scroll down. */
1497
void
1498
screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1499
555
{
1500
555
  struct screen *s = ctx->s;
1501
555
  struct grid *gd = s->grid;
1502
555
  struct tty_ctx   ttyctx;
1503
555
  u_int    i;
1504
1505
555
  screen_write_initctx(ctx, &ttyctx, 1);
1506
555
  ttyctx.bg = bg;
1507
1508
555
  if (lines == 0)
1509
0
    lines = 1;
1510
555
  else if (lines > s->rlower - s->rupper + 1)
1511
147
    lines = s->rlower - s->rupper + 1;
1512
1513
#ifdef ENABLE_SIXEL
1514
  if (image_free_all(s) && ctx->wp != NULL)
1515
    ctx->wp->flags |= PANE_REDRAW;
1516
#endif
1517
1518
3.83k
  for (i = 0; i < lines; i++)
1519
3.27k
    grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg);
1520
1521
555
  screen_write_collect_flush(ctx, 0, __func__);
1522
555
  ttyctx.num = lines;
1523
555
  tty_write(tty_cmd_scrolldown, &ttyctx);
1524
555
}
1525
1526
/* Carriage return (cursor to start of line). */
1527
void
1528
screen_write_carriagereturn(struct screen_write_ctx *ctx)
1529
4.39k
{
1530
4.39k
  screen_write_set_cursor(ctx, 0, -1);
1531
4.39k
}
1532
1533
/* Clear to end of screen from cursor. */
1534
void
1535
screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
1536
2.10k
{
1537
2.10k
  struct screen *s = ctx->s;
1538
2.10k
  struct grid *gd = s->grid;
1539
2.10k
  struct tty_ctx   ttyctx;
1540
2.10k
  u_int    sx = screen_size_x(s), sy = screen_size_y(s);
1541
1542
#ifdef ENABLE_SIXEL
1543
  if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
1544
    ctx->wp->flags |= PANE_REDRAW;
1545
#endif
1546
1547
2.10k
  screen_write_initctx(ctx, &ttyctx, 1);
1548
2.10k
  ttyctx.bg = bg;
1549
1550
  /* Scroll into history if it is enabled and clearing entire screen. */
1551
2.10k
  if (s->cx == 0 &&
1552
1.31k
      s->cy == 0 &&
1553
916
      (gd->flags & GRID_HISTORY) &&
1554
0
      ctx->wp != NULL &&
1555
0
      options_get_number(ctx->wp->options, "scroll-on-clear"))
1556
0
    grid_view_clear_history(gd, bg);
1557
2.10k
  else {
1558
2.10k
    if (s->cx <= sx - 1)
1559
1.91k
      grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg);
1560
2.10k
    grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg);
1561
2.10k
  }
1562
1563
2.10k
  screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1));
1564
2.10k
  screen_write_collect_flush(ctx, 0, __func__);
1565
2.10k
  tty_write(tty_cmd_clearendofscreen, &ttyctx);
1566
2.10k
}
1567
1568
/* Clear to start of screen. */
1569
void
1570
screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
1571
231
{
1572
231
  struct screen *s = ctx->s;
1573
231
  struct tty_ctx   ttyctx;
1574
231
  u_int    sx = screen_size_x(s);
1575
1576
#ifdef ENABLE_SIXEL
1577
  if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL)
1578
    ctx->wp->flags |= PANE_REDRAW;
1579
#endif
1580
1581
231
  screen_write_initctx(ctx, &ttyctx, 1);
1582
231
  ttyctx.bg = bg;
1583
1584
231
  if (s->cy > 0)
1585
96
    grid_view_clear(s->grid, 0, 0, sx, s->cy, bg);
1586
231
  if (s->cx > sx - 1)
1587
66
    grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1588
165
  else
1589
165
    grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1590
1591
231
  screen_write_collect_clear(ctx, 0, s->cy);
1592
231
  screen_write_collect_flush(ctx, 0, __func__);
1593
231
  tty_write(tty_cmd_clearstartofscreen, &ttyctx);
1594
231
}
1595
1596
/* Clear entire screen. */
1597
void
1598
screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
1599
1.41k
{
1600
1.41k
  struct screen *s = ctx->s;
1601
1.41k
  struct tty_ctx   ttyctx;
1602
1.41k
  u_int    sx = screen_size_x(s), sy = screen_size_y(s);
1603
1604
#ifdef ENABLE_SIXEL
1605
  if (image_free_all(s) && ctx->wp != NULL)
1606
    ctx->wp->flags |= PANE_REDRAW;
1607
#endif
1608
1609
1.41k
  screen_write_initctx(ctx, &ttyctx, 1);
1610
1.41k
  ttyctx.bg = bg;
1611
1612
  /* Scroll into history if it is enabled. */
1613
1.41k
  if ((s->grid->flags & GRID_HISTORY) &&
1614
0
      ctx->wp != NULL &&
1615
0
      options_get_number(ctx->wp->options, "scroll-on-clear"))
1616
0
    grid_view_clear_history(s->grid, bg);
1617
1.41k
  else
1618
1.41k
    grid_view_clear(s->grid, 0, 0, sx, sy, bg);
1619
1620
1.41k
  screen_write_collect_clear(ctx, 0, sy);
1621
1.41k
  tty_write(tty_cmd_clearscreen, &ttyctx);
1622
1.41k
}
1623
1624
/* Clear entire history. */
1625
void
1626
screen_write_clearhistory(struct screen_write_ctx *ctx)
1627
263
{
1628
263
  grid_clear_history(ctx->s->grid);
1629
263
}
1630
1631
/* Force a full redraw. */
1632
void
1633
screen_write_fullredraw(struct screen_write_ctx *ctx)
1634
863
{
1635
863
  struct tty_ctx   ttyctx;
1636
1637
863
  screen_write_collect_flush(ctx, 0, __func__);
1638
1639
863
  screen_write_initctx(ctx, &ttyctx, 1);
1640
863
  if (ttyctx.redraw_cb != NULL)
1641
863
    ttyctx.redraw_cb(&ttyctx);
1642
863
}
1643
1644
/* Trim collected items. */
1645
static struct screen_write_citem *
1646
screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x,
1647
    u_int used, int *wrapped)
1648
10.3k
{
1649
10.3k
  struct screen_write_cline *cl = &ctx->s->write_list[y];
1650
10.3k
  struct screen_write_citem *ci, *ci2, *tmp, *before = NULL;
1651
10.3k
  u_int        sx = x, ex = x + used - 1;
1652
10.3k
  u_int        csx, cex;
1653
1654
10.3k
  if (TAILQ_EMPTY(&cl->items))
1655
4.05k
    return (NULL);
1656
23.3k
  TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
1657
23.3k
    csx = ci->x;
1658
23.3k
    cex = ci->x + ci->used - 1;
1659
1660
    /* Item is entirely before. */
1661
23.3k
    if (cex < sx) {
1662
17.8k
      log_debug("%s: %p %u-%u before %u-%u", __func__, ci,
1663
17.8k
          csx, cex, sx, ex);
1664
17.8k
      continue;
1665
17.8k
    }
1666
1667
    /* Item is entirely after. */
1668
5.53k
    if (csx > ex) {
1669
482
      log_debug("%s: %p %u-%u after %u-%u", __func__, ci,
1670
482
          csx, cex, sx, ex);
1671
482
      before = ci;
1672
482
      break;
1673
482
    }
1674
1675
    /* Item is entirely inside. */
1676
5.04k
    if (csx >= sx && cex <= ex) {
1677
2.07k
      log_debug("%s: %p %u-%u inside %u-%u", __func__, ci,
1678
2.07k
          csx, cex, sx, ex);
1679
2.07k
      TAILQ_REMOVE(&cl->items, ci, entry);
1680
2.07k
      screen_write_free_citem(ci);
1681
2.07k
      if (csx == 0 && ci->wrapped && wrapped != NULL)
1682
226
        *wrapped = 1;
1683
2.07k
      continue;
1684
2.07k
    }
1685
1686
    /* Item under the start. */
1687
2.96k
    if (csx < sx && cex >= sx && cex <= ex) {
1688
426
      log_debug("%s: %p %u-%u start %u-%u", __func__, ci,
1689
426
          csx, cex, sx, ex);
1690
426
      ci->used = sx - csx;
1691
426
      log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
1692
426
          ci->x + ci->used + 1);
1693
426
      continue;
1694
426
    }
1695
1696
    /* Item covers the end. */
1697
2.54k
    if (cex > ex && csx >= sx && csx <= ex) {
1698
819
      log_debug("%s: %p %u-%u end %u-%u", __func__, ci,
1699
819
          csx, cex, sx, ex);
1700
819
      ci->x = ex + 1;
1701
819
      ci->used = cex - ex;
1702
819
      log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
1703
819
          ci->x + ci->used + 1);
1704
819
      before = ci;
1705
819
      break;
1706
819
    }
1707
1708
    /* Item must cover both sides. */
1709
1.72k
    log_debug("%s: %p %u-%u under %u-%u", __func__, ci,
1710
1.72k
        csx, cex, sx, ex);
1711
1.72k
    ci2 = screen_write_get_citem();
1712
1.72k
    ci2->type = ci->type;
1713
1.72k
    ci2->bg = ci->bg;
1714
1.72k
    memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc);
1715
1.72k
    TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry);
1716
1717
1.72k
    ci->used = sx - csx;
1718
1.72k
    ci2->x = ex + 1;
1719
1.72k
    ci2->used = cex - ex;
1720
1721
1.72k
    log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci,
1722
1.72k
        ci->x, ci->x + ci->used - 1, ci, ci2->x,
1723
1.72k
        ci2->x + ci2->used - 1, ci2);
1724
1.72k
    before = ci2;
1725
1.72k
    break;
1726
2.54k
  }
1727
6.25k
  return (before);
1728
10.3k
}
1729
1730
/* Clear collected lines. */
1731
static void
1732
screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
1733
18.1k
{
1734
18.1k
  struct screen_write_cline *cl;
1735
18.1k
  u_int        i;
1736
1737
140k
  for (i = y; i < y + n; i++) {
1738
122k
    cl = &ctx->s->write_list[i];
1739
122k
    TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry);
1740
122k
  }
1741
18.1k
}
1742
1743
/* Scroll collected lines up. */
1744
static void
1745
screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg)
1746
12.7k
{
1747
12.7k
  struct screen     *s = ctx->s;
1748
12.7k
  struct screen_write_cline *cl;
1749
12.7k
  u_int        y;
1750
12.7k
  char        *saved;
1751
12.7k
  struct screen_write_citem *ci;
1752
1753
12.7k
  log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1754
12.7k
      s->rupper, s->rlower);
1755
1756
12.7k
  screen_write_collect_clear(ctx, s->rupper, 1);
1757
12.7k
  saved = ctx->s->write_list[s->rupper].data;
1758
302k
  for (y = s->rupper; y < s->rlower; y++) {
1759
289k
    cl = &ctx->s->write_list[y + 1];
1760
289k
    TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry);
1761
289k
    ctx->s->write_list[y].data = cl->data;
1762
289k
  }
1763
12.7k
  ctx->s->write_list[s->rlower].data = saved;
1764
1765
12.7k
  ci = screen_write_get_citem();
1766
12.7k
  ci->x = 0;
1767
12.7k
  ci->used = screen_size_x(s);
1768
12.7k
  ci->type = CLEAR;
1769
12.7k
  ci->bg = bg;
1770
12.7k
  TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry);
1771
12.7k
}
1772
1773
/* Flush collected lines. */
1774
static void
1775
screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
1776
    const char *from)
1777
69.7k
{
1778
69.7k
  struct screen     *s = ctx->s;
1779
69.7k
  struct screen_write_citem *ci, *tmp;
1780
69.7k
  struct screen_write_cline *cl;
1781
69.7k
  u_int        y, cx, cy, last, items = 0;
1782
69.7k
  struct tty_ctx       ttyctx;
1783
1784
69.7k
  if (ctx->scrolled != 0) {
1785
1.64k
    log_debug("%s: scrolled %u (region %u-%u)", __func__,
1786
1.64k
        ctx->scrolled, s->rupper, s->rlower);
1787
1.64k
    if (ctx->scrolled > s->rlower - s->rupper + 1)
1788
126
      ctx->scrolled = s->rlower - s->rupper + 1;
1789
1790
1.64k
    screen_write_initctx(ctx, &ttyctx, 1);
1791
1.64k
    ttyctx.num = ctx->scrolled;
1792
1.64k
    ttyctx.bg = ctx->bg;
1793
1.64k
    tty_write(tty_cmd_scrollup, &ttyctx);
1794
1795
1.64k
    if (ctx->wp != NULL)
1796
1.64k
      ctx->wp->flags |= PANE_REDRAWSCROLLBAR;
1797
1.64k
  }
1798
69.7k
  ctx->scrolled = 0;
1799
69.7k
  ctx->bg = 8;
1800
1801
69.7k
  if (scroll_only)
1802
21.3k
    return;
1803
1804
48.3k
  cx = s->cx; cy = s->cy;
1805
1.25M
  for (y = 0; y < screen_size_y(s); y++) {
1806
1.20M
    cl = &ctx->s->write_list[y];
1807
1.20M
    last = UINT_MAX;
1808
1.20M
    TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
1809
14.5k
      if (last != UINT_MAX && ci->x <= last) {
1810
0
        fatalx("collect list not in order: %u <= %u",
1811
0
            ci->x, last);
1812
0
      }
1813
14.5k
      screen_write_set_cursor(ctx, ci->x, y);
1814
14.5k
      if (ci->type == CLEAR) {
1815
7.50k
        screen_write_initctx(ctx, &ttyctx, 1);
1816
7.50k
        ttyctx.bg = ci->bg;
1817
7.50k
        ttyctx.num = ci->used;
1818
7.50k
        tty_write(tty_cmd_clearcharacter, &ttyctx);
1819
7.50k
      } else {
1820
7.02k
        screen_write_initctx(ctx, &ttyctx, 0);
1821
7.02k
        ttyctx.cell = &ci->gc;
1822
7.02k
        ttyctx.wrapped = ci->wrapped;
1823
7.02k
        ttyctx.ptr = cl->data + ci->x;
1824
7.02k
        ttyctx.num = ci->used;
1825
7.02k
        tty_write(tty_cmd_cells, &ttyctx);
1826
7.02k
      }
1827
14.5k
      items++;
1828
1829
14.5k
      TAILQ_REMOVE(&cl->items, ci, entry);
1830
14.5k
      screen_write_free_citem(ci);
1831
14.5k
      last = ci->x;
1832
14.5k
    }
1833
1.20M
  }
1834
48.3k
  s->cx = cx; s->cy = cy;
1835
1836
48.3k
  log_debug("%s: flushed %u items (%s)", __func__, items, from);
1837
48.3k
}
1838
1839
/* Finish and store collected cells. */
1840
void
1841
screen_write_collect_end(struct screen_write_ctx *ctx)
1842
522k
{
1843
522k
  struct screen     *s = ctx->s;
1844
522k
  struct screen_write_citem *ci = ctx->item, *before;
1845
522k
  struct screen_write_cline *cl = &s->write_list[s->cy];
1846
522k
  struct grid_cell     gc;
1847
522k
  u_int        xx;
1848
522k
  int        wrapped = ci->wrapped;
1849
1850
522k
  if (ci->used == 0)
1851
513k
    return;
1852
1853
9.21k
  before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used,
1854
9.21k
      &wrapped);
1855
9.21k
  ci->x = s->cx;
1856
9.21k
  ci->wrapped = wrapped;
1857
9.21k
  if (before == NULL)
1858
6.26k
    TAILQ_INSERT_TAIL(&cl->items, ci, entry);
1859
2.95k
  else
1860
2.95k
    TAILQ_INSERT_BEFORE(before, ci, entry);
1861
9.21k
  ctx->item = screen_write_get_citem();
1862
1863
9.21k
  log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used,
1864
9.21k
      (int)ci->used, cl->data + ci->x, s->cx, s->cy);
1865
1866
9.21k
  if (s->cx != 0) {
1867
9.72k
    for (xx = s->cx; xx > 0; xx--) {
1868
9.63k
      grid_view_get_cell(s->grid, xx, s->cy, &gc);
1869
9.63k
      if (~gc.flags & GRID_FLAG_PADDING)
1870
6.35k
        break;
1871
3.27k
      grid_view_set_cell(s->grid, xx, s->cy,
1872
3.27k
          &grid_default_cell);
1873
3.27k
    }
1874
6.45k
    if (xx != s->cx) {
1875
523
      if (xx == 0)
1876
98
        grid_view_get_cell(s->grid, 0, s->cy, &gc);
1877
523
      if (gc.data.width > 1) {
1878
455
        grid_view_set_cell(s->grid, xx, s->cy,
1879
455
            &grid_default_cell);
1880
455
      }
1881
523
    }
1882
6.45k
  }
1883
1884
#ifdef ENABLE_SIXEL
1885
  if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL)
1886
    ctx->wp->flags |= PANE_REDRAW;
1887
#endif
1888
1889
9.21k
  grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
1890
9.21k
      ci->used);
1891
9.21k
  screen_write_set_cursor(ctx, s->cx + ci->used, -1);
1892
1893
9.84k
  for (xx = s->cx; xx < screen_size_x(s); xx++) {
1894
8.44k
    grid_view_get_cell(s->grid, xx, s->cy, &gc);
1895
8.44k
    if (~gc.flags & GRID_FLAG_PADDING)
1896
7.81k
      break;
1897
630
    grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
1898
630
  }
1899
9.21k
}
1900
1901
/* Write cell data, collecting if necessary. */
1902
void
1903
screen_write_collect_add(struct screen_write_ctx *ctx,
1904
    const struct grid_cell *gc)
1905
36.4k
{
1906
36.4k
  struct screen     *s = ctx->s;
1907
36.4k
  struct screen_write_citem *ci;
1908
36.4k
  u_int        sx = screen_size_x(s);
1909
36.4k
  int        collect;
1910
1911
  /*
1912
   * Don't need to check that the attributes and whatnot are still the
1913
   * same - input_parse will end the collection when anything that isn't
1914
   * a plain character is encountered.
1915
   */
1916
1917
36.4k
  collect = 1;
1918
36.4k
  if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f)
1919
9.08k
    collect = 0;
1920
27.4k
  else if (gc->flags & GRID_FLAG_TAB)
1921
487
    collect = 0;
1922
26.9k
  else if (gc->attr & GRID_ATTR_CHARSET)
1923
7.52k
    collect = 0;
1924
19.3k
  else if (~s->mode & MODE_WRAP)
1925
2.88k
    collect = 0;
1926
16.5k
  else if (s->mode & MODE_INSERT)
1927
764
    collect = 0;
1928
15.7k
  else if (s->sel != NULL)
1929
0
    collect = 0;
1930
36.4k
  if (!collect) {
1931
20.7k
    screen_write_collect_end(ctx);
1932
20.7k
    screen_write_collect_flush(ctx, 0, __func__);
1933
20.7k
    screen_write_cell(ctx, gc);
1934
20.7k
    return;
1935
20.7k
  }
1936
1937
15.7k
  if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx)
1938
863
    screen_write_collect_end(ctx);
1939
15.7k
  ci = ctx->item; /* may have changed */
1940
1941
15.7k
  if (s->cx > sx - 1) {
1942
863
    log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1943
863
    ci->wrapped = 1;
1944
863
    screen_write_linefeed(ctx, 1, 8);
1945
863
    screen_write_set_cursor(ctx, 0, -1);
1946
863
  }
1947
1948
15.7k
  if (ci->used == 0)
1949
9.21k
    memcpy(&ci->gc, gc, sizeof ci->gc);
1950
15.7k
  if (ctx->s->write_list[s->cy].data == NULL)
1951
3.11k
    ctx->s->write_list[s->cy].data = xmalloc(screen_size_x(ctx->s));
1952
15.7k
  ctx->s->write_list[s->cy].data[s->cx + ci->used++] = gc->data.data[0];
1953
15.7k
}
1954
1955
/* Write cell data. */
1956
void
1957
screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
1958
20.7k
{
1959
20.7k
  struct screen   *s = ctx->s;
1960
20.7k
  struct grid   *gd = s->grid;
1961
20.7k
  const struct utf8_data  *ud = &gc->data;
1962
20.7k
  struct grid_line  *gl;
1963
20.7k
  struct grid_cell_entry  *gce;
1964
20.7k
  struct grid_cell   tmp_gc, now_gc;
1965
20.7k
  struct tty_ctx     ttyctx;
1966
20.7k
  u_int      sx = screen_size_x(s), sy = screen_size_y(s);
1967
20.7k
  u_int      width = ud->width, xx, not_wrap;
1968
20.7k
  int      selected, skip = 1;
1969
1970
  /* Ignore padding cells. */
1971
20.7k
  if (gc->flags & GRID_FLAG_PADDING)
1972
0
    return;
1973
1974
  /* Get the previous cell to check for combining. */
1975
20.7k
  if (screen_write_combine(ctx, gc) != 0)
1976
0
    return;
1977
1978
  /* Flush any existing scrolling. */
1979
20.7k
  screen_write_collect_flush(ctx, 1, __func__);
1980
1981
  /* If this character doesn't fit, ignore it. */
1982
20.7k
  if ((~s->mode & MODE_WRAP) &&
1983
3.85k
      width > 1 &&
1984
303
      (width > sx || (s->cx != sx && s->cx > sx - width)))
1985
0
    return;
1986
1987
  /* If in insert mode, make space for the cells. */
1988
20.7k
  if (s->mode & MODE_INSERT) {
1989
1.49k
    grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8);
1990
1.49k
    skip = 0;
1991
1.49k
  }
1992
1993
  /* Check this will fit on the current line and wrap if not. */
1994
20.7k
  if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
1995
735
    log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1996
735
    screen_write_linefeed(ctx, 1, 8);
1997
735
    screen_write_set_cursor(ctx, 0, -1);
1998
735
    screen_write_collect_flush(ctx, 0, __func__);
1999
735
  }
2000
2001
  /* Sanity check cursor position. */
2002
20.7k
  if (s->cx > sx - width || s->cy > sy - 1)
2003
743
    return;
2004
20.0k
  screen_write_initctx(ctx, &ttyctx, 0);
2005
2006
  /* Handle overwriting of UTF-8 characters. */
2007
20.0k
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
2008
20.0k
  if (gl->flags & GRID_LINE_EXTENDED) {
2009
12.4k
    grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
2010
12.4k
    if (screen_write_overwrite(ctx, &now_gc, width))
2011
770
      skip = 0;
2012
12.4k
  }
2013
2014
  /*
2015
   * If the new character is UTF-8 wide, fill in padding cells. Have
2016
   * already ensured there is enough room.
2017
   */
2018
43.1k
  for (xx = s->cx + 1; xx < s->cx + width; xx++) {
2019
23.1k
    log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
2020
23.1k
    grid_view_set_padding(gd, xx, s->cy);
2021
23.1k
    skip = 0;
2022
23.1k
  }
2023
2024
  /* If no change, do not draw. */
2025
20.0k
  if (skip) {
2026
14.7k
    if (s->cx >= gl->cellsize)
2027
4.02k
      skip = grid_cells_equal(gc, &grid_default_cell);
2028
10.7k
    else {
2029
10.7k
      gce = &gl->celldata[s->cx];
2030
10.7k
      if (gce->flags & GRID_FLAG_EXTENDED)
2031
3.25k
        skip = 0;
2032
7.51k
      else if (gc->flags != gce->flags)
2033
5.80k
        skip = 0;
2034
1.71k
      else if (gc->attr != gce->data.attr)
2035
274
        skip = 0;
2036
1.43k
      else if (gc->fg != gce->data.fg)
2037
285
        skip = 0;
2038
1.15k
      else if (gc->bg != gce->data.bg)
2039
290
        skip = 0;
2040
863
      else if (gc->data.width != 1)
2041
0
        skip = 0;
2042
863
      else if (gc->data.size != 1)
2043
223
        skip = 0;
2044
640
      else if (gce->data.data != gc->data.data[0])
2045
337
        skip = 0;
2046
10.7k
    }
2047
14.7k
  }
2048
2049
  /* Update the selected flag and set the cell. */
2050
20.0k
  selected = screen_check_selection(s, s->cx, s->cy);
2051
20.0k
  if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
2052
0
    memcpy(&tmp_gc, gc, sizeof tmp_gc);
2053
0
    tmp_gc.flags |= GRID_FLAG_SELECTED;
2054
0
    grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
2055
20.0k
  } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) {
2056
0
    memcpy(&tmp_gc, gc, sizeof tmp_gc);
2057
0
    tmp_gc.flags &= ~GRID_FLAG_SELECTED;
2058
0
    grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
2059
20.0k
  } else if (!skip)
2060
18.7k
    grid_view_set_cell(gd, s->cx, s->cy, gc);
2061
20.0k
  if (selected)
2062
0
    skip = 0;
2063
2064
  /*
2065
   * Move the cursor. If not wrapping, stick at the last character and
2066
   * replace it.
2067
   */
2068
20.0k
  not_wrap = !(s->mode & MODE_WRAP);
2069
20.0k
  if (s->cx <= sx - not_wrap - width)
2070
19.0k
    screen_write_set_cursor(ctx, s->cx + width, -1);
2071
922
  else
2072
922
    screen_write_set_cursor(ctx,  sx - not_wrap, -1);
2073
2074
  /* Create space for character in insert mode. */
2075
20.0k
  if (s->mode & MODE_INSERT) {
2076
1.01k
    screen_write_collect_flush(ctx, 0, __func__);
2077
1.01k
    ttyctx.num = width;
2078
1.01k
    tty_write(tty_cmd_insertcharacter, &ttyctx);
2079
1.01k
  }
2080
2081
  /* Write to the screen. */
2082
20.0k
  if (!skip) {
2083
18.7k
    if (selected) {
2084
0
      screen_select_cell(s, &tmp_gc, gc);
2085
0
      ttyctx.cell = &tmp_gc;
2086
0
    } else
2087
18.7k
      ttyctx.cell = gc;
2088
18.7k
    tty_write(tty_cmd_cell, &ttyctx);
2089
18.7k
  }
2090
20.0k
}
2091
2092
/* Combine a UTF-8 zero-width character onto the previous if necessary. */
2093
static int
2094
screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
2095
20.7k
{
2096
20.7k
  struct screen   *s = ctx->s;
2097
20.7k
  struct grid   *gd = s->grid;
2098
20.7k
  const struct utf8_data  *ud = &gc->data;
2099
20.7k
  u_int      n, cx = s->cx, cy = s->cy;
2100
20.7k
  struct grid_cell   last;
2101
20.7k
  struct tty_ctx     ttyctx;
2102
20.7k
  int      force_wide = 0, zero_width = 0;
2103
2104
  /* Ignore U+3164 HANGUL_FILLER entirely. */
2105
20.7k
  if (utf8_is_hangul_filler(ud))
2106
0
    return (1);
2107
2108
  /*
2109
   * Is this character which makes no sense without being combined? If
2110
   * this is true then flag it here and discard the character (return 1)
2111
   * if we cannot combine it.
2112
   */
2113
20.7k
  if (utf8_is_zwj(ud))
2114
0
    zero_width = 1;
2115
20.7k
  else if (utf8_is_vs(ud)) {
2116
0
    zero_width = 1;
2117
0
    if (options_get_number(global_options, "variation-selector-always-wide"))
2118
0
      force_wide = 1;
2119
20.7k
  } else if (ud->width == 0)
2120
0
    zero_width = 1;
2121
2122
  /* Cannot combine empty character or at left. */
2123
20.7k
  if (ud->size < 2 || cx == 0)
2124
13.0k
    return (zero_width);
2125
7.71k
  log_debug("%s: character %.*s at %u,%u (width %u)", __func__,
2126
7.71k
      (int)ud->size, ud->data, cx, cy, ud->width);
2127
2128
  /* Find the cell to combine with. */
2129
7.71k
  n = 1;
2130
7.71k
  grid_view_get_cell(gd, cx - n, cy, &last);
2131
7.71k
  if (cx != 1 && (last.flags & GRID_FLAG_PADDING)) {
2132
1.85k
    n = 2;
2133
1.85k
    grid_view_get_cell(gd, cx - n, cy, &last);
2134
1.85k
  }
2135
7.71k
  if (n != last.data.width || (last.flags & GRID_FLAG_PADDING))
2136
2.04k
    return (zero_width);
2137
2138
  /*
2139
   * Check if we need to combine characters. This could be a Korean
2140
   * Hangul Jamo character, zero width (set above), a modifier character
2141
   * (with an existing Unicode character) or a previous ZWJ.
2142
   */
2143
5.66k
  if (!zero_width) {
2144
5.66k
    switch (hanguljamo_check_state(&last.data, ud)) {
2145
0
    case HANGULJAMO_STATE_NOT_COMPOSABLE:
2146
0
      return (1);
2147
0
    case HANGULJAMO_STATE_CHOSEONG:
2148
0
      return (0);
2149
0
    case HANGULJAMO_STATE_COMPOSABLE:
2150
0
      break;
2151
5.66k
    case HANGULJAMO_STATE_NOT_HANGULJAMO:
2152
5.66k
      if (utf8_should_combine(&last.data, ud))
2153
0
        force_wide = 1;
2154
5.66k
      else if (!utf8_has_zwj(&last.data))
2155
5.66k
        return (0);
2156
0
      break;
2157
5.66k
    }
2158
5.66k
  }
2159
2160
  /* Check if this combined character would be too long. */
2161
0
  if (last.data.size + ud->size > sizeof last.data.data)
2162
0
    return (0);
2163
2164
  /* Combining; flush any pending output. */
2165
0
  screen_write_collect_flush(ctx, 0, __func__);
2166
2167
0
  log_debug("%s: %.*s -> %.*s at %u,%u (offset %u, width %u)", __func__,
2168
0
      (int)ud->size, ud->data, (int)last.data.size, last.data.data,
2169
0
      cx - n, cy, n, last.data.width);
2170
2171
  /* Append the data. */
2172
0
  memcpy(last.data.data + last.data.size, ud->data, ud->size);
2173
0
  last.data.size += ud->size;
2174
2175
  /* Force the width to 2 for modifiers and variation selector. */
2176
0
  if (last.data.width == 1 && force_wide) {
2177
0
    last.data.width = 2;
2178
0
    n = 2;
2179
0
    cx++;
2180
0
  } else
2181
0
    force_wide = 0;
2182
2183
  /* Set the new cell. */
2184
0
  grid_view_set_cell(gd, cx - n, cy, &last);
2185
0
  if (force_wide)
2186
0
    grid_view_set_padding(gd, cx - 1, cy);
2187
2188
  /*
2189
   * Redraw the combined cell. If forcing the cell to width 2, reset the
2190
   * cached cursor position in the tty, since we don't really know
2191
   * whether the terminal thought the character was width 1 or width 2
2192
   * and what it is going to do now.
2193
   */
2194
0
  screen_write_set_cursor(ctx, cx - n, cy);
2195
0
  screen_write_initctx(ctx, &ttyctx, 0);
2196
0
  ttyctx.cell = &last;
2197
0
  ttyctx.num = force_wide; /* reset cached cursor position */
2198
0
  tty_write(tty_cmd_cell, &ttyctx);
2199
0
  screen_write_set_cursor(ctx, cx, cy);
2200
2201
0
  return (1);
2202
0
}
2203
2204
/*
2205
 * UTF-8 wide characters are a bit of an annoyance. They take up more than one
2206
 * cell on the screen, so following cells must not be drawn by marking them as
2207
 * padding.
2208
 *
2209
 * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
2210
 * character, it is necessary to also overwrite any other cells which covered
2211
 * by the same character.
2212
 */
2213
static int
2214
screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
2215
    u_int width)
2216
12.4k
{
2217
12.4k
  struct screen   *s = ctx->s;
2218
12.4k
  struct grid   *gd = s->grid;
2219
12.4k
  struct grid_cell   tmp_gc;
2220
12.4k
  u_int      xx;
2221
12.4k
  int      done = 0;
2222
2223
12.4k
  if (gc->flags & GRID_FLAG_PADDING) {
2224
    /*
2225
     * A padding cell, so clear any following and leading padding
2226
     * cells back to the character. Don't overwrite the current
2227
     * cell as that happens later anyway.
2228
     */
2229
444
    xx = s->cx + 1;
2230
2.62k
    while (--xx > 0) {
2231
2.46k
      grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
2232
2.46k
      if (~tmp_gc.flags & GRID_FLAG_PADDING)
2233
281
        break;
2234
2.17k
      log_debug("%s: padding at %u,%u", __func__, xx, s->cy);
2235
2.17k
      grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2236
2.17k
    }
2237
2238
    /* Overwrite the character at the start of this padding. */
2239
444
    log_debug("%s: character at %u,%u", __func__, xx, s->cy);
2240
444
    grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2241
444
    done = 1;
2242
444
  }
2243
2244
  /*
2245
   * Overwrite any padding cells that belong to any UTF-8 characters
2246
   * we'll be overwriting with the current character.
2247
   */
2248
12.4k
  if (width != 1 ||
2249
9.71k
      gc->data.width != 1 ||
2250
9.33k
      gc->flags & GRID_FLAG_PADDING) {
2251
3.55k
    xx = s->cx + width - 1;
2252
6.22k
    while (++xx < screen_size_x(s)) {
2253
6.09k
      grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
2254
6.09k
      if (~tmp_gc.flags & GRID_FLAG_PADDING)
2255
3.42k
        break;
2256
2.67k
      log_debug("%s: overwrite at %u,%u", __func__, xx,
2257
2.67k
          s->cy);
2258
2.67k
      if (gc->flags & GRID_FLAG_TAB) {
2259
2.02k
        memcpy(&tmp_gc, gc, sizeof tmp_gc);
2260
2.02k
        memset(tmp_gc.data.data, 0,
2261
2.02k
            sizeof tmp_gc.data.data);
2262
2.02k
        *tmp_gc.data.data = ' ';
2263
2.02k
        tmp_gc.data.width = tmp_gc.data.size =
2264
2.02k
            tmp_gc.data.have = 1;
2265
2.02k
        grid_view_set_cell(gd, xx, s->cy, &tmp_gc);
2266
2.02k
      } else
2267
647
        grid_view_set_cell(gd, xx, s->cy,
2268
647
            &grid_default_cell);
2269
2.67k
      done = 1;
2270
2.67k
    }
2271
3.55k
  }
2272
2273
12.4k
  return (done);
2274
12.4k
}
2275
2276
/* Set external clipboard. */
2277
void
2278
screen_write_setselection(struct screen_write_ctx *ctx, const char *flags,
2279
    u_char *str, u_int len)
2280
1.09k
{
2281
1.09k
  struct tty_ctx  ttyctx;
2282
2283
1.09k
  screen_write_initctx(ctx, &ttyctx, 0);
2284
1.09k
  ttyctx.ptr = str;
2285
1.09k
  ttyctx.ptr2 = (void *)flags;
2286
1.09k
  ttyctx.num = len;
2287
2288
1.09k
  tty_write(tty_cmd_setselection, &ttyctx);
2289
1.09k
}
2290
2291
/* Write unmodified string. */
2292
void
2293
screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
2294
    int allow_invisible_panes)
2295
0
{
2296
0
  struct tty_ctx  ttyctx;
2297
2298
0
  screen_write_initctx(ctx, &ttyctx, 0);
2299
0
  ttyctx.ptr = str;
2300
0
  ttyctx.num = len;
2301
0
  ttyctx.allow_invisible_panes = allow_invisible_panes;
2302
2303
0
  tty_write(tty_cmd_rawstring, &ttyctx);
2304
0
}
2305
2306
#ifdef ENABLE_SIXEL
2307
/* Write a SIXEL image. */
2308
void
2309
screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
2310
    u_int bg)
2311
{
2312
  struct screen   *s = ctx->s;
2313
  struct grid   *gd = s->grid;
2314
  struct tty_ctx     ttyctx;
2315
  u_int      x, y, sx, sy, cx = s->cx, cy = s->cy, i, lines;
2316
  struct sixel_image  *new;
2317
2318
  sixel_size_in_cells(si, &x, &y);
2319
  if (x > screen_size_x(s) || y > screen_size_y(s)) {
2320
    if (x > screen_size_x(s) - cx)
2321
      sx = screen_size_x(s) - cx;
2322
    else
2323
      sx = x;
2324
    if (y > screen_size_y(s) - 1)
2325
      sy = screen_size_y(s) - 1;
2326
    else
2327
      sy = y;
2328
    new = sixel_scale(si, 0, 0, 0, y - sy, sx, sy, 1);
2329
    sixel_free(si);
2330
    si = new;
2331
2332
    /* Bail out if the image cannot be scaled. */
2333
    if (si == NULL)
2334
      return;
2335
    sixel_size_in_cells(si, &x, &y);
2336
  }
2337
2338
  sy = screen_size_y(s) - cy;
2339
  if (sy < y) {
2340
    lines = y - sy + 1;
2341
    if (image_scroll_up(s, lines) && ctx->wp != NULL)
2342
      ctx->wp->flags |= PANE_REDRAW;
2343
    for (i = 0; i < lines; i++) {
2344
      grid_view_scroll_region_up(gd, 0, screen_size_y(s) - 1,
2345
          bg);
2346
      screen_write_collect_scroll(ctx, bg);
2347
    }
2348
    ctx->scrolled += lines;
2349
    if (lines > cy)
2350
      screen_write_cursormove(ctx, -1, 0, 0);
2351
    else
2352
      screen_write_cursormove(ctx, -1, cy - lines, 0);
2353
  }
2354
  screen_write_collect_flush(ctx, 0, __func__);
2355
2356
  screen_write_initctx(ctx, &ttyctx, 0);
2357
  ttyctx.ptr = image_store(s, si);
2358
2359
  tty_write(tty_cmd_sixelimage, &ttyctx);
2360
2361
  screen_write_cursormove(ctx, 0, cy + y, 0);
2362
}
2363
#endif
2364
2365
/* Turn alternate screen on. */
2366
void
2367
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
2368
    int cursor)
2369
1.03k
{
2370
1.03k
  struct tty_ctx     ttyctx;
2371
1.03k
  struct window_pane  *wp = ctx->wp;
2372
2373
1.03k
  if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
2374
0
    return;
2375
2376
1.03k
  screen_write_collect_flush(ctx, 0, __func__);
2377
1.03k
  screen_alternate_on(ctx->s, gc, cursor);
2378
2379
1.03k
  if (wp != NULL)
2380
1.03k
    layout_fix_panes(wp->window, NULL);
2381
2382
1.03k
  screen_write_initctx(ctx, &ttyctx, 1);
2383
1.03k
  if (ttyctx.redraw_cb != NULL)
2384
1.03k
    ttyctx.redraw_cb(&ttyctx);
2385
1.03k
}
2386
2387
/* Turn alternate screen off. */
2388
void
2389
screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
2390
    int cursor)
2391
875
{
2392
875
  struct tty_ctx     ttyctx;
2393
875
  struct window_pane  *wp = ctx->wp;
2394
2395
875
  if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
2396
0
    return;
2397
2398
875
  screen_write_collect_flush(ctx, 0, __func__);
2399
875
  screen_alternate_off(ctx->s, gc, cursor);
2400
2401
875
  if (wp != NULL)
2402
875
    layout_fix_panes(wp->window, NULL);
2403
2404
875
  screen_write_initctx(ctx, &ttyctx, 1);
2405
875
  if (ttyctx.redraw_cb != NULL)
2406
875
    ttyctx.redraw_cb(&ttyctx);
2407
875
}