Coverage Report

Created: 2026-07-16 06:55

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