Coverage Report

Created: 2026-08-13 06:06

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.286 2026/08/03 12:58:53 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
  struct osc133_data     od;
1610
0
  u_int        flags;
1611
1612
0
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1613
0
  if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
1614
0
    return;
1615
1616
#ifdef ENABLE_SIXEL
1617
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1618
    ctx->wp->flags |= PANE_REDRAW;
1619
#endif
1620
1621
0
  flags = gl->flags & GRID_LINE_OSC133_FLAGS;
1622
0
  memcpy(&od, &gl->osc133_data, sizeof od);
1623
0
  grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1624
0
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1625
0
  gl->flags |= flags;
1626
0
  memcpy(&gl->osc133_data, &od, sizeof gl->osc133_data);
1627
1628
0
  screen_write_collect_clear(ctx, s->cy, 1);
1629
0
  ci->x = 0;
1630
0
  ci->used = sx;
1631
0
  ci->type = CLEAR;
1632
0
  ci->bg = bg;
1633
0
  TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1634
0
  ctx->item = screen_write_get_citem();
1635
0
}
1636
1637
/* Clear to end of line from cursor. */
1638
void
1639
screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
1640
0
{
1641
0
  struct screen     *s = ctx->s;
1642
0
  struct grid_line    *gl;
1643
0
  u_int        sx = screen_size_x(s);
1644
0
  struct screen_write_citem *ci = ctx->item;
1645
1646
0
  if (s->cx == 0) {
1647
0
    screen_write_clearline(ctx, bg);
1648
0
    return;
1649
0
  }
1650
1651
0
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1652
0
  if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
1653
0
    return;
1654
1655
#ifdef ENABLE_SIXEL
1656
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1657
    ctx->wp->flags |= PANE_REDRAW;
1658
#endif
1659
1660
0
  grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
1661
1662
0
  ci->x = s->cx;
1663
0
  ci->used = sx - s->cx;
1664
0
  ci->type = CLEAR;
1665
0
  ci->bg = bg;
1666
0
  screen_write_collect_insert(ctx, ci);
1667
0
}
1668
1669
/* Clear to start of line from cursor. */
1670
void
1671
screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
1672
0
{
1673
0
  struct screen      *s = ctx->s;
1674
0
  u_int        sx = screen_size_x(s);
1675
0
  struct screen_write_citem *ci = ctx->item;
1676
1677
0
  if (s->cx >= sx - 1) {
1678
0
    screen_write_clearline(ctx, bg);
1679
0
    return;
1680
0
  }
1681
1682
#ifdef ENABLE_SIXEL
1683
  if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1684
    ctx->wp->flags |= PANE_REDRAW;
1685
#endif
1686
1687
0
  if (s->cx > sx - 1)
1688
0
    grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1689
0
  else
1690
0
    grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1691
1692
0
  ci->x = 0;
1693
0
  ci->used = s->cx + 1;
1694
0
  ci->type = CLEAR;
1695
0
  ci->bg = bg;
1696
0
  screen_write_collect_insert(ctx, ci);
1697
0
}
1698
1699
/* Move cursor to px,py. */
1700
void
1701
screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
1702
    int origin)
1703
0
{
1704
0
  struct screen *s = ctx->s;
1705
1706
0
  if (origin && py != -1 && (s->mode & MODE_ORIGIN)) {
1707
0
    if ((u_int)py > s->rlower - s->rupper)
1708
0
      py = s->rlower;
1709
0
    else
1710
0
      py += s->rupper;
1711
0
  }
1712
1713
0
  if (px != -1 && (u_int)px > screen_size_x(s) - 1)
1714
0
    px = screen_size_x(s) - 1;
1715
0
  if (py != -1 && (u_int)py > screen_size_y(s) - 1)
1716
0
    py = screen_size_y(s) - 1;
1717
1718
0
  log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py);
1719
0
  screen_write_set_cursor(ctx, px, py);
1720
0
}
1721
1722
/* Reverse index (up with scroll). */
1723
void
1724
screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
1725
0
{
1726
0
  struct screen *s = ctx->s;
1727
0
  struct tty_ctx   ttyctx;
1728
0
  u_int    ry;
1729
1730
0
  if (s->cy != s->rupper) {
1731
0
    if (s->cy > 0)
1732
0
      screen_write_set_cursor(ctx, -1, s->cy - 1);
1733
0
    return;
1734
0
  }
1735
1736
#ifdef ENABLE_SIXEL
1737
  if (image_free_all(s) && ctx->wp != NULL)
1738
    ctx->wp->flags |= PANE_REDRAW;
1739
#endif
1740
1741
0
  grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
1742
0
  screen_write_collect_flush(ctx, 0, __func__);
1743
1744
0
  screen_write_initctx(ctx, &ttyctx, 1, 1);
1745
0
  ttyctx.bg = bg;
1746
1747
0
  ry = s->rlower + 1 - s->rupper;
1748
0
  if (!screen_write_should_draw_lines(ctx, s->rupper, ry))
1749
0
    return;
1750
0
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
1751
0
    tty_write(tty_cmd_reverseindex, &ttyctx);
1752
0
    return;
1753
0
  }
1754
1755
0
  screen_write_redraw_pane(ctx, &ttyctx);
1756
0
}
1757
1758
/* Set scroll region. */
1759
void
1760
screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper,
1761
    u_int rlower)
1762
0
{
1763
0
  struct screen *s = ctx->s;
1764
1765
0
  if (rupper > screen_size_y(s) - 1)
1766
0
    rupper = screen_size_y(s) - 1;
1767
0
  if (rlower > screen_size_y(s) - 1)
1768
0
    rlower = screen_size_y(s) - 1;
1769
0
  if (rupper >= rlower) /* cannot be one line */
1770
0
    return;
1771
1772
0
  screen_write_collect_flush(ctx, 0, __func__);
1773
1774
  /* Cursor moves to top-left. */
1775
0
  screen_write_set_cursor(ctx, 0, 0);
1776
1777
0
  s->rupper = rupper;
1778
0
  s->rlower = rlower;
1779
0
}
1780
1781
/* Line feed. */
1782
void
1783
screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
1784
0
{
1785
0
  struct screen   *s = ctx->s;
1786
0
  struct grid   *gd = s->grid;
1787
0
  struct grid_line  *gl;
1788
#ifdef ENABLE_SIXEL
1789
  int      redraw = 0;
1790
#endif
1791
0
  u_int      rupper = s->rupper, rlower = s->rlower;
1792
1793
0
  gl = grid_get_line(gd, gd->hsize + s->cy);
1794
0
  if (wrapped)
1795
0
    gl->flags |= GRID_LINE_WRAPPED;
1796
1797
0
  log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1798
0
      rupper, rlower);
1799
1800
0
  if (bg != ctx->bg) {
1801
0
    screen_write_collect_flush(ctx, 1, __func__);
1802
0
    ctx->bg = bg;
1803
0
  }
1804
1805
0
  if (s->cy != s->rlower) {
1806
0
    if (s->cy < screen_size_y(s) - 1)
1807
0
      screen_write_set_cursor(ctx, -1, s->cy + 1);
1808
0
    return;
1809
0
  }
1810
1811
#ifdef ENABLE_SIXEL
1812
  if (rlower == screen_size_y(s) - 1)
1813
    redraw = image_scroll_up(s, 1);
1814
  else
1815
    redraw = image_check_line(s, rupper, rlower - rupper);
1816
  if (redraw && ctx->wp != NULL)
1817
    ctx->wp->flags |= PANE_REDRAW;
1818
#endif
1819
1820
0
  grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1821
0
  screen_write_collect_scroll(ctx, bg);
1822
0
  ctx->scrolled++;
1823
0
}
1824
1825
/* Scroll up. */
1826
void
1827
screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1828
0
{
1829
0
  struct screen *s = ctx->s;
1830
0
  struct grid *gd = s->grid;
1831
0
  u_int    i;
1832
1833
0
  if (lines == 0)
1834
0
    lines = 1;
1835
0
  else if (lines > s->rlower - s->rupper + 1)
1836
0
    lines = s->rlower - s->rupper + 1;
1837
1838
0
  if (bg != ctx->bg) {
1839
0
    screen_write_collect_flush(ctx, 1, __func__);
1840
0
    ctx->bg = bg;
1841
0
  }
1842
1843
#ifdef ENABLE_SIXEL
1844
  if (image_scroll_up(s, lines) && ctx->wp != NULL)
1845
    ctx->wp->flags |= PANE_REDRAW;
1846
#endif
1847
1848
0
  for (i = 0; i < lines; i++) {
1849
0
    grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1850
0
    screen_write_collect_scroll(ctx, bg);
1851
0
  }
1852
0
  ctx->scrolled += lines;
1853
0
}
1854
1855
/* Scroll down. */
1856
void
1857
screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1858
0
{
1859
0
  struct screen *s = ctx->s;
1860
0
  struct grid *gd = s->grid;
1861
0
  struct tty_ctx   ttyctx;
1862
0
  u_int    i, ry;
1863
1864
0
  screen_write_initctx(ctx, &ttyctx, 1, 1);
1865
0
  ttyctx.bg = bg;
1866
1867
0
  if (lines == 0)
1868
0
    lines = 1;
1869
0
  else if (lines > s->rlower - s->rupper + 1)
1870
0
    lines = s->rlower - s->rupper + 1;
1871
1872
#ifdef ENABLE_SIXEL
1873
  if (image_free_all(s) && ctx->wp != NULL)
1874
    ctx->wp->flags |= PANE_REDRAW;
1875
#endif
1876
1877
0
  for (i = 0; i < lines; i++)
1878
0
    grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg);
1879
1880
0
  screen_write_collect_flush(ctx, 0, __func__);
1881
0
  ttyctx.n = lines;
1882
1883
0
  ry = s->rlower + 1 - s->rupper;
1884
0
  if (!screen_write_should_draw_lines(ctx, s->rupper, ry))
1885
0
    return;
1886
0
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED || ctx->wp == NULL) {
1887
0
    tty_write(tty_cmd_scrolldown, &ttyctx);
1888
0
    return;
1889
0
  }
1890
1891
0
  screen_write_redraw_pane(ctx, &ttyctx);
1892
0
}
1893
1894
/* Carriage return (cursor to start of line). */
1895
void
1896
screen_write_carriagereturn(struct screen_write_ctx *ctx)
1897
0
{
1898
0
  screen_write_set_cursor(ctx, 0, -1);
1899
0
}
1900
1901
/* Clear to end of screen from cursor. */
1902
void
1903
screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
1904
0
{
1905
0
  struct screen   *s = ctx->s;
1906
0
  struct grid   *gd = s->grid;
1907
0
  struct tty_ctx     ttyctx;
1908
0
  u_int      sx = screen_size_x(s), sy = screen_size_y(s);
1909
0
  u_int      y, i, xoff, yoff, ocx, ocy;
1910
0
  struct visible_ranges *r;
1911
0
  struct visible_range  *ri;
1912
1913
#ifdef ENABLE_SIXEL
1914
  if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
1915
    ctx->wp->flags |= PANE_REDRAW;
1916
#endif
1917
1918
0
  screen_write_initctx(ctx, &ttyctx, 1, 1);
1919
0
  ttyctx.bg = bg;
1920
1921
  /* Scroll into history if it is enabled and clearing entire screen. */
1922
0
  if (s->cx == 0 &&
1923
0
      s->cy == 0 &&
1924
0
      (gd->flags & GRID_HISTORY) &&
1925
0
      ctx->wp != NULL &&
1926
0
      options_get_number(ctx->wp->options, "scroll-on-clear"))
1927
0
    grid_view_clear_history(gd, bg);
1928
0
  else {
1929
0
    if (s->cx <= sx - 1)
1930
0
      grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg);
1931
0
    grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg);
1932
0
  }
1933
1934
0
  screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1));
1935
0
  screen_write_collect_flush(ctx, 0, __func__);
1936
1937
0
  if (!screen_write_should_draw_lines(ctx, s->cy, sy - s->cy))
1938
0
    return;
1939
0
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) {
1940
0
    tty_write(tty_cmd_clearendofscreen, &ttyctx);
1941
0
    return;
1942
0
  }
1943
1944
  /* Can't just clear screen, must avoid floating windows. */
1945
0
  ocx = s->cx;
1946
0
  ocy = s->cy;
1947
0
  if (ctx->wp != NULL) {
1948
0
    xoff = ctx->wp->xoff;
1949
0
    yoff = ctx->wp->yoff;
1950
0
  } else {
1951
0
    xoff = 0;
1952
0
    yoff = 0;
1953
0
  }
1954
1955
  /* First line (containing the cursor). */
1956
0
  if (s->cx <= sx - 1) {
1957
0
    r = window_visible_ranges(ctx->wp, xoff + s->cx, yoff + s->cy,
1958
0
        sx - s->cx, NULL);
1959
0
    for (i = 0; i < r->used; i++) {
1960
0
      ri = &r->ranges[i];
1961
0
      if (ri->nx == 0)
1962
0
        continue;
1963
0
      screen_write_collect_insert_clear(ctx, ri->px - xoff,
1964
0
          ri->nx, bg);
1965
0
    }
1966
0
  }
1967
1968
  /* Below cursor to bottom. */
1969
0
  for (y = s->cy + 1; y < sy; y++) {
1970
0
    screen_write_set_cursor(ctx, 0, y);
1971
0
    r = window_visible_ranges(ctx->wp, xoff, yoff + y, sx, NULL);
1972
0
    for (i = 0; i < r->used; i++) {
1973
0
      ri = &r->ranges[i];
1974
0
      if (ri->nx == 0)
1975
0
        continue;
1976
0
      screen_write_collect_insert_clear(ctx, ri->px - xoff,
1977
0
          ri->nx, bg);
1978
0
    }
1979
0
  }
1980
0
  screen_write_set_cursor(ctx, ocx, ocy);
1981
0
}
1982
1983
/* Clear to start of screen. */
1984
void
1985
screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
1986
0
{
1987
0
  struct screen   *s = ctx->s;
1988
0
  struct tty_ctx     ttyctx;
1989
0
  u_int      sx = screen_size_x(s);
1990
0
  u_int      y, i, xoff, yoff, ocx, ocy;
1991
0
  struct visible_ranges *r;
1992
0
  struct visible_range  *ri;
1993
1994
#ifdef ENABLE_SIXEL
1995
  if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL)
1996
    ctx->wp->flags |= PANE_REDRAW;
1997
#endif
1998
1999
0
  screen_write_initctx(ctx, &ttyctx, 1, 1);
2000
0
  ttyctx.bg = bg;
2001
2002
0
  if (s->cy > 0)
2003
0
    grid_view_clear(s->grid, 0, 0, sx, s->cy, bg);
2004
0
  if (s->cx > sx - 1)
2005
0
    grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
2006
0
  else
2007
0
    grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
2008
2009
0
  screen_write_collect_clear(ctx, 0, s->cy);
2010
0
  screen_write_collect_flush(ctx, 0, __func__);
2011
2012
0
  if (!screen_write_should_draw_lines(ctx, 0, s->cy + 1))
2013
0
    return;
2014
0
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) {
2015
0
    tty_write(tty_cmd_clearstartofscreen, &ttyctx);
2016
0
    return;
2017
0
  }
2018
2019
  /* Can't just clear screen, must avoid floating windows. */
2020
0
  ocx = s->cx;
2021
0
  ocy = s->cy;
2022
0
  if (ctx->wp != NULL) {
2023
0
    xoff = ctx->wp->xoff;
2024
0
    yoff = ctx->wp->yoff;
2025
0
  } else {
2026
0
    xoff = 0;
2027
0
    yoff = 0;
2028
0
  }
2029
2030
  /* Top to above the cursor. */
2031
0
  for (y = 0; y < s->cy; y++) {
2032
0
    screen_write_set_cursor(ctx, 0, y);
2033
0
    r = window_visible_ranges(ctx->wp, xoff, yoff + y, sx, NULL);
2034
0
    for (i = 0; i < r->used; i++) {
2035
0
      ri = &r->ranges[i];
2036
0
      if (ri->nx == 0)
2037
0
        continue;
2038
0
      screen_write_collect_insert_clear(ctx, ri->px - xoff,
2039
0
          ri->nx, bg);
2040
0
    }
2041
0
  }
2042
2043
  /* Last line (containing the cursor). */
2044
0
  screen_write_set_cursor(ctx, 0, s->cy);
2045
0
  r = window_visible_ranges(ctx->wp, xoff, yoff + ocy, s->cx + 1, NULL);
2046
0
  for (i = 0; i < r->used; i++) {
2047
0
    ri = &r->ranges[i];
2048
0
    if (ri->nx == 0)
2049
0
      continue;
2050
0
    screen_write_collect_insert_clear(ctx, ri->px - xoff, ri->nx,
2051
0
        bg);
2052
0
  }
2053
0
  screen_write_set_cursor(ctx, ocx, ocy);
2054
0
}
2055
2056
/* Clear entire screen. */
2057
void
2058
screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
2059
0
{
2060
0
  struct screen   *s = ctx->s;
2061
0
  struct tty_ctx     ttyctx;
2062
0
  u_int      sx = screen_size_x(s), sy = screen_size_y(s);
2063
0
  u_int      y, i, xoff, yoff, ocx, ocy;
2064
0
  struct visible_ranges *r;
2065
0
  struct visible_range  *ri;
2066
2067
#ifdef ENABLE_SIXEL
2068
  if (image_free_all(s) && ctx->wp != NULL)
2069
    ctx->wp->flags |= PANE_REDRAW;
2070
#endif
2071
2072
0
  screen_write_initctx(ctx, &ttyctx, 1, 1);
2073
0
  ttyctx.bg = bg;
2074
2075
  /* Scroll into history if it is enabled. */
2076
0
  if ((s->grid->flags & GRID_HISTORY) &&
2077
0
      ctx->wp != NULL &&
2078
0
      options_get_number(ctx->wp->options, "scroll-on-clear"))
2079
0
    grid_view_clear_history(s->grid, bg);
2080
0
  else
2081
0
    grid_view_clear(s->grid, 0, 0, sx, sy, bg);
2082
2083
0
  screen_write_collect_clear(ctx, 0, sy);
2084
2085
0
  if (!screen_write_should_draw_lines(ctx, 0, sy))
2086
0
    return;
2087
0
  if (~ttyctx.flags & TTY_CTX_PANE_OBSCURED) {
2088
0
    tty_write(tty_cmd_clearscreen, &ttyctx);
2089
0
    return;
2090
0
  }
2091
2092
  /* Can't just clear screen, must avoid floating windows. */
2093
0
  ocx = s->cx;
2094
0
  ocy = s->cy;
2095
0
  if (ctx->wp != NULL) {
2096
0
    xoff = ctx->wp->xoff;
2097
0
    yoff = ctx->wp->yoff;
2098
0
  } else {
2099
0
    xoff = 0;
2100
0
    yoff = 0;
2101
0
  }
2102
2103
  /* Clear every line. */
2104
0
  for (y = 0; y < sy; y++) {
2105
0
    screen_write_set_cursor(ctx, 0, y);
2106
0
    r = window_visible_ranges(ctx->wp, xoff, yoff + y, sx, NULL);
2107
0
    for (i = 0; i < r->used; i++) {
2108
0
      ri = &r->ranges[i];
2109
0
      if (ri->nx == 0)
2110
0
        continue;
2111
0
      screen_write_collect_insert_clear(ctx, ri->px - xoff,
2112
0
          ri->nx, bg);
2113
0
    }
2114
0
  }
2115
0
  screen_write_set_cursor(ctx, ocx, ocy);
2116
0
}
2117
2118
/* Clear entire history. */
2119
void
2120
screen_write_clearhistory(struct screen_write_ctx *ctx)
2121
0
{
2122
0
  grid_clear_history(ctx->s->grid);
2123
0
}
2124
2125
/* Force a full redraw. */
2126
void
2127
screen_write_fullredraw(struct screen_write_ctx *ctx)
2128
0
{
2129
0
  struct tty_ctx   ttyctx;
2130
2131
0
  screen_write_collect_flush(ctx, 0, __func__);
2132
2133
0
  screen_write_initctx(ctx, &ttyctx, 1, 0);
2134
0
  if (ttyctx.redraw_cb != NULL)
2135
0
    ttyctx.redraw_cb(&ttyctx);
2136
0
}
2137
2138
/* Trim collected items. */
2139
static struct screen_write_citem *
2140
screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x,
2141
    u_int used, int *wrapped)
2142
0
{
2143
0
  struct screen_write_cline *cl = &ctx->s->write_list[y];
2144
0
  struct screen_write_citem *ci, *ci2, *tmp, *before = NULL;
2145
0
  u_int        sx = x, ex = x + used - 1;
2146
0
  u_int        csx, cex;
2147
2148
0
  if (TAILQ_EMPTY(&cl->items))
2149
0
    return (NULL);
2150
0
  TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
2151
0
    csx = ci->x;
2152
0
    cex = ci->x + ci->used - 1;
2153
2154
    /* Item is entirely before. */
2155
0
    if (cex < sx) {
2156
0
      log_debug("%s: %p %u-%u before %u-%u", __func__, ci,
2157
0
          csx, cex, sx, ex);
2158
0
      continue;
2159
0
    }
2160
2161
    /* Item is entirely after. */
2162
0
    if (csx > ex) {
2163
0
      log_debug("%s: %p %u-%u after %u-%u", __func__, ci,
2164
0
          csx, cex, sx, ex);
2165
0
      before = ci;
2166
0
      break;
2167
0
    }
2168
2169
    /* Item is entirely inside. */
2170
0
    if (csx >= sx && cex <= ex) {
2171
0
      log_debug("%s: %p %u-%u inside %u-%u", __func__, ci,
2172
0
          csx, cex, sx, ex);
2173
0
      TAILQ_REMOVE(&cl->items, ci, entry);
2174
0
      screen_write_free_citem(ci);
2175
0
      if (csx == 0 && ci->wrapped && wrapped != NULL)
2176
0
        *wrapped = 1;
2177
0
      continue;
2178
0
    }
2179
2180
    /* Item under the start. */
2181
0
    if (csx < sx && cex >= sx && cex <= ex) {
2182
0
      log_debug("%s: %p %u-%u start %u-%u", __func__, ci,
2183
0
          csx, cex, sx, ex);
2184
0
      ci->used = sx - csx;
2185
0
      log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
2186
0
          ci->x + ci->used + 1);
2187
0
      continue;
2188
0
    }
2189
2190
    /* Item covers the end. */
2191
0
    if (cex > ex && csx >= sx && csx <= ex) {
2192
0
      log_debug("%s: %p %u-%u end %u-%u", __func__, ci,
2193
0
          csx, cex, sx, ex);
2194
0
      ci->x = ex + 1;
2195
0
      ci->used = cex - ex;
2196
0
      log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
2197
0
          ci->x + ci->used + 1);
2198
0
      before = ci;
2199
0
      break;
2200
0
    }
2201
2202
    /* Item must cover both sides. */
2203
0
    log_debug("%s: %p %u-%u under %u-%u", __func__, ci,
2204
0
        csx, cex, sx, ex);
2205
0
    ci2 = screen_write_get_citem();
2206
0
    ci2->type = ci->type;
2207
0
    ci2->bg = ci->bg;
2208
0
    memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc);
2209
0
    TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry);
2210
2211
0
    ci->used = sx - csx;
2212
0
    ci2->x = ex + 1;
2213
0
    ci2->used = cex - ex;
2214
2215
0
    log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci,
2216
0
        ci->x, ci->x + ci->used - 1, ci, ci2->x,
2217
0
        ci2->x + ci2->used - 1, ci2);
2218
0
    before = ci2;
2219
0
    break;
2220
0
  }
2221
0
  return (before);
2222
0
}
2223
2224
/* Clear collected lines. */
2225
static void
2226
screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
2227
0
{
2228
0
  struct screen_write_cline *cl;
2229
0
  u_int        i;
2230
2231
0
  for (i = y; i < y + n; i++) {
2232
0
    cl = &ctx->s->write_list[i];
2233
0
    TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry);
2234
0
  }
2235
0
}
2236
2237
/* Scroll collected lines up. */
2238
static void
2239
screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg)
2240
0
{
2241
0
  struct screen     *s = ctx->s;
2242
0
  struct screen_write_cline *cl;
2243
0
  u_int        y;
2244
0
  char        *saved;
2245
0
  struct screen_write_citem *ci;
2246
2247
0
  log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
2248
0
      s->rupper, s->rlower);
2249
2250
0
  screen_write_collect_clear(ctx, s->rupper, 1);
2251
0
  saved = ctx->s->write_list[s->rupper].data;
2252
0
  for (y = s->rupper; y < s->rlower; y++) {
2253
0
    cl = &ctx->s->write_list[y + 1];
2254
0
    TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry);
2255
0
    ctx->s->write_list[y].data = cl->data;
2256
0
  }
2257
0
  ctx->s->write_list[s->rlower].data = saved;
2258
2259
0
  ci = screen_write_get_citem();
2260
0
  ci->x = 0;
2261
0
  ci->used = screen_size_x(s);
2262
0
  ci->type = CLEAR;
2263
0
  ci->bg = bg;
2264
0
  TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry);
2265
0
}
2266
2267
/* Flush collected scrolling. */
2268
static int
2269
screen_write_collect_flush_scrolled(struct screen_write_ctx *ctx)
2270
0
{
2271
0
  struct window_pane  *wp = ctx->wp;
2272
0
  struct screen   *s = ctx->s;
2273
0
  struct tty_ctx     ttyctx;
2274
2275
0
  screen_write_initctx(ctx, &ttyctx, 1, 1);
2276
0
  if (ttyctx.flags & TTY_CTX_PANE_OBSCURED && wp != NULL) {
2277
0
    screen_write_redraw_pane(ctx, &ttyctx);
2278
0
    return 0;
2279
0
  }
2280
0
  if (wp != NULL && window_pane_scrollbar_overlay_visible(wp)) {
2281
0
    wp->flags |= PANE_REDRAW;
2282
0
    return 0;
2283
0
  }
2284
2285
0
  log_debug("%s: scrolled %u (region %u-%u)", __func__, ctx->scrolled,
2286
0
      s->rupper, s->rlower);
2287
0
  if (ctx->scrolled > s->rlower - s->rupper + 1)
2288
0
    ctx->scrolled = s->rlower - s->rupper + 1;
2289
2290
0
  if (wp != NULL && wp->yoff + wp->sy > wp->window->sy)
2291
0
    ttyctx.orlower -= (wp->yoff + wp->sy - wp->window->sy);
2292
0
  ttyctx.n = ctx->scrolled;
2293
0
  ttyctx.bg = ctx->bg;
2294
0
  tty_write(tty_cmd_scrollup, &ttyctx);
2295
2296
0
  if (wp != NULL)
2297
0
    window_pane_scrollbar_redraw(wp);
2298
0
  return 1;
2299
0
}
2300
2301
/* Flush a collected line. */
2302
static u_int
2303
screen_write_collect_flush_line(struct screen_write_ctx *ctx, u_int y)
2304
0
{
2305
0
  struct window_pane    *wp = ctx->wp;
2306
0
  struct screen     *s = ctx->s;
2307
0
  struct screen_write_citem *ci, *tmp;
2308
0
  struct screen_write_cline *cl = &s->write_list[y];
2309
0
  u_int        last = UINT_MAX, items = 0, wsx, wsy;
2310
0
  u_int        w_length, i;
2311
0
  int        w_start, w_end, xoff, yoff, written;
2312
0
  int        r_start, r_end, c_start, c_end;
2313
0
  struct tty_ctx       ttyctx;
2314
0
  struct visible_ranges   *r;
2315
0
  struct visible_range    *ri;
2316
2317
0
  if (wp != NULL) {
2318
0
    wsx = wp->window->sx;
2319
0
    wsy = wp->window->sy;
2320
0
    xoff = wp->xoff;
2321
0
    yoff = wp->yoff;
2322
0
  } else {
2323
0
    wsx = screen_size_x(s);
2324
0
    wsy = screen_size_y(s);
2325
0
    xoff = 0;
2326
0
    yoff = 0;
2327
0
  }
2328
0
  if (y + yoff >= wsy)
2329
0
    return (0);
2330
2331
0
  r = window_visible_ranges(wp, 0, y + yoff, wsx, NULL);
2332
0
  TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
2333
0
    log_debug("collect list: x=%u (last %u), y=%u, used=%u", ci->x,
2334
0
        last, y, ci->used);
2335
0
    if (last != UINT_MAX && ci->x <= last)
2336
0
      fatalx("collect list bad order: %u <= %u", ci->x, last);
2337
2338
0
    w_length = 0;
2339
0
    written = 0;
2340
0
    for (i = 0; i < r->used; i++) {
2341
0
      ri = &r->ranges[i];
2342
0
      if (ri->nx == 0)
2343
0
        continue;
2344
2345
0
      r_start = ri->px;
2346
0
      r_end = ri->px + ri->nx;
2347
0
      c_start = ci->x;
2348
0
      c_end = ci->x + ci->used;
2349
2350
0
      if (c_start + xoff >= r_end || c_end + xoff <= r_start)
2351
0
        continue;
2352
0
      if (r_start > c_start + xoff)
2353
0
        w_start = r_start - xoff;
2354
0
      else
2355
0
        w_start = c_start;
2356
0
      if (c_end + xoff > r_end)
2357
0
        w_end = r_end - xoff;
2358
0
      else
2359
0
        w_end = c_end;
2360
0
      if (w_end <= w_start)
2361
0
        continue;
2362
0
      w_length = w_end - w_start;
2363
0
      if (w_length <= 0)
2364
0
        continue;
2365
2366
0
      screen_write_set_cursor(ctx, w_start, y);
2367
0
      if (ci->type == CLEAR) {
2368
0
        screen_write_initctx(ctx, &ttyctx, 1, 0);
2369
0
        ttyctx.bg = ci->bg;
2370
0
        ttyctx.n = w_length;
2371
0
        tty_write(tty_cmd_clearcharacter, &ttyctx);
2372
0
      } else {
2373
0
        screen_write_initctx(ctx, &ttyctx, 0, 0);
2374
0
        ttyctx.cell = &ci->gc;
2375
0
        if (ci->wrapped)
2376
0
          ttyctx.flags |= TTY_CTX_WRAPPED;
2377
0
        ttyctx.data.data = cl->data + w_start;
2378
0
        ttyctx.data.size = w_length;
2379
0
        tty_write(tty_cmd_cells, &ttyctx);
2380
0
      }
2381
0
      items++;
2382
0
      written = 1;
2383
0
    }
2384
0
    if (written) {
2385
0
      last = ci->x;
2386
0
      TAILQ_REMOVE(&cl->items, ci, entry);
2387
0
      screen_write_free_citem(ci);
2388
0
    }
2389
0
  }
2390
0
  return (items);
2391
0
}
2392
2393
/* Flush collected lines. */
2394
static void
2395
screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
2396
    const char *from)
2397
0
{
2398
0
  struct screen     *s = ctx->s;
2399
0
  struct window_pane    *wp = ctx->wp;
2400
0
  u_int        y, cx, cy, items = 0;
2401
0
  struct screen_write_citem *ci, *tmp;
2402
0
  struct screen_write_cline *cl;
2403
2404
0
  if (wp != NULL && (wp->flags & (PANE_REDRAW|PANE_DROP)))
2405
0
    goto discard;
2406
0
  if (s->mode & MODE_SYNC) {
2407
0
    if (ctx->scrolled != 0) {
2408
0
      screen_write_should_draw_lines(ctx, s->rupper,
2409
0
          s->rlower + 1 - s->rupper);
2410
0
    }
2411
0
    for (y = 0; y < screen_size_y(s); y++) {
2412
0
      cl = &s->write_list[y];
2413
0
      if (!TAILQ_EMPTY(&cl->items))
2414
0
        screen_write_should_draw_line(ctx, y);
2415
0
    }
2416
0
    goto discard;
2417
0
  }
2418
2419
0
  if (ctx->scrolled != 0) {
2420
0
    if (!screen_write_collect_flush_scrolled(ctx))
2421
0
      goto discard;
2422
0
    ctx->scrolled = 0;
2423
0
  }
2424
0
  ctx->bg = 8;
2425
2426
0
  if (scroll_only)
2427
0
    return;
2428
2429
0
  cx = s->cx; cy = s->cy;
2430
0
  for (y = 0; y < screen_size_y(s); y++)
2431
0
    items += screen_write_collect_flush_line(ctx, y);
2432
0
  s->cx = cx; s->cy = cy;
2433
2434
0
  log_debug("%s: flushed %u items (%s)", __func__, items, from);
2435
0
  return;
2436
2437
0
discard:
2438
0
  for (y = 0; y < screen_size_y(s); y++) {
2439
0
    cl = &s->write_list[y];
2440
0
    TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
2441
0
      TAILQ_REMOVE(&cl->items, ci, entry);
2442
0
      screen_write_free_citem(ci);
2443
0
    }
2444
0
  }
2445
0
  ctx->scrolled = 0;
2446
0
  ctx->bg = 8;
2447
0
}
2448
2449
/* Insert an item on current line. */
2450
static void
2451
screen_write_collect_insert(struct screen_write_ctx *ctx,
2452
    struct screen_write_citem *ci)
2453
0
{
2454
0
  struct screen     *s = ctx->s;
2455
0
  struct screen_write_cline *cl = &s->write_list[s->cy];
2456
0
  struct screen_write_citem *before;
2457
2458
0
  before = screen_write_collect_trim(ctx, s->cy, ci->x, ci->used,
2459
0
      &ci->wrapped);
2460
0
  if (before == NULL)
2461
0
    TAILQ_INSERT_TAIL(&cl->items, ci, entry);
2462
0
  else
2463
0
    TAILQ_INSERT_BEFORE(before, ci, entry);
2464
0
  ctx->item = screen_write_get_citem();
2465
0
}
2466
2467
/* Insert a clear for part of a line. */
2468
static void
2469
screen_write_collect_insert_clear(struct screen_write_ctx *ctx, u_int px,
2470
    u_int nx, u_int bg)
2471
0
{
2472
0
  struct screen_write_citem *ci = ctx->item;
2473
2474
0
  if (nx != 0) {
2475
0
    ci->x = px;
2476
0
    ci->used = nx;
2477
0
    ci->type = CLEAR;
2478
0
    ci->bg = bg;
2479
0
    screen_write_collect_insert(ctx, ci);
2480
0
  }
2481
0
}
2482
2483
/*
2484
 * Clear a cell that is being broken up by a write over part of it, keeping the
2485
 * background so the columns it covered do not lose their colour.
2486
 */
2487
static void
2488
screen_write_clear_cell(struct grid *gd, u_int px, u_int py)
2489
0
{
2490
0
  struct grid_cell   gc;
2491
0
  int      bg;
2492
2493
0
  grid_view_get_cell(gd, px, py, &gc);
2494
0
  bg = gc.bg;
2495
2496
0
  memcpy(&gc, &grid_default_cell, sizeof gc);
2497
0
  gc.bg = bg;
2498
0
  grid_view_set_cell(gd, px, py, &gc);
2499
0
}
2500
2501
/*
2502
 * Insert clears for a range of cells already cleared in the grid. Adjacent
2503
 * cells may have come from different characters and so have different
2504
 * backgrounds, so this is done in runs of the same colour.
2505
 */
2506
static void
2507
screen_write_insert_clears(struct screen_write_ctx *ctx, u_int px, u_int nx)
2508
0
{
2509
0
  struct screen   *s = ctx->s;
2510
0
  struct grid_cell   gc;
2511
0
  u_int      xx, start = px, n;
2512
0
  int      bg = 8;
2513
2514
0
  for (xx = px; xx < px + nx; xx++) {
2515
0
    grid_view_get_cell(s->grid, xx, s->cy, &gc);
2516
0
    if (xx == start)
2517
0
      bg = gc.bg;
2518
0
    else if (gc.bg != bg) {
2519
0
      n = xx - start;
2520
0
      log_debug("%s: from %u, size %u", __func__, start, n);
2521
0
      screen_write_collect_insert_clear(ctx, start, n, bg);
2522
0
      start = xx;
2523
0
      bg = gc.bg;
2524
0
    }
2525
0
  }
2526
0
  log_debug("%s: from %u, size %u", __func__, start, xx - start);
2527
0
  screen_write_collect_insert_clear(ctx, start, xx - start, bg);
2528
0
}
2529
2530
/* Finish and store collected cells. */
2531
void
2532
screen_write_collect_end(struct screen_write_ctx *ctx)
2533
0
{
2534
0
  struct screen     *s = ctx->s;
2535
0
  struct screen_write_citem *ci = ctx->item;
2536
0
  struct screen_write_cline *cl = &s->write_list[s->cy];
2537
0
  struct grid_cell     gc;
2538
0
  u_int        xx, bx = 0, bnx = 0;
2539
2540
0
  if (ci->used == 0)
2541
0
    return;
2542
2543
0
  ci->x = s->cx;
2544
0
  screen_write_collect_insert(ctx, ci);
2545
2546
0
  log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used,
2547
0
      (int)ci->used, cl->data + ci->x, s->cx, s->cy);
2548
2549
0
  if (s->cx != 0) {
2550
0
    for (xx = s->cx; xx > 0; xx--) {
2551
0
      grid_view_get_cell(s->grid, xx, s->cy, &gc);
2552
0
      if (~gc.flags & GRID_FLAG_PADDING)
2553
0
        break;
2554
0
      screen_write_clear_cell(s->grid, xx, s->cy);
2555
0
      log_debug("%s: padding erased (before) at %u (cx %u)",
2556
0
          __func__, xx, s->cx);
2557
0
    }
2558
0
    if (xx != s->cx) {
2559
0
      if (xx == 0)
2560
0
        grid_view_get_cell(s->grid, 0, s->cy, &gc);
2561
0
      if (gc.data.width > 1 ||
2562
0
          (gc.flags & GRID_FLAG_PADDING)) {
2563
0
        screen_write_clear_cell(s->grid, xx, s->cy);
2564
0
        log_debug("%s: padding erased (before) at %u "
2565
0
            "(cx %u)", __func__, xx, s->cx);
2566
0
      }
2567
0
      bx = xx;
2568
0
      bnx = s->cx - xx;
2569
0
    }
2570
0
  }
2571
2572
#ifdef ENABLE_SIXEL
2573
  if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL)
2574
    ctx->wp->flags |= PANE_REDRAW;
2575
#endif
2576
2577
0
  grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
2578
0
      ci->used);
2579
0
  if (bnx != 0)
2580
0
    screen_write_insert_clears(ctx, bx, bnx);
2581
0
  screen_write_set_cursor(ctx, s->cx + ci->used, -1);
2582
2583
0
  for (xx = s->cx; xx < screen_size_x(s); xx++) {
2584
0
    grid_view_get_cell(s->grid, xx, s->cy, &gc);
2585
0
    if (~gc.flags & GRID_FLAG_PADDING)
2586
0
      break;
2587
0
    screen_write_clear_cell(s->grid, xx, s->cy);
2588
0
    log_debug("%s: padding erased (after) at %u (cx %u)", __func__,
2589
0
        xx, s->cx);
2590
0
  }
2591
0
  if (xx != s->cx)
2592
0
    screen_write_insert_clears(ctx, s->cx, xx - s->cx);
2593
0
}
2594
2595
/* Write cell data, collecting if necessary. */
2596
void
2597
screen_write_collect_add(struct screen_write_ctx *ctx,
2598
    const struct grid_cell *gc)
2599
0
{
2600
0
  struct screen     *s = ctx->s;
2601
0
  struct screen_write_citem *ci;
2602
0
  u_int        sx = screen_size_x(s);
2603
0
  int        collect;
2604
2605
  /*
2606
   * Don't need to check that the attributes and whatnot are still the
2607
   * same - input_parse will end the collection when anything that isn't
2608
   * a plain character is encountered.
2609
   */
2610
2611
0
  collect = 1;
2612
0
  if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f)
2613
0
    collect = 0;
2614
0
  else if (gc->flags & GRID_FLAG_TAB)
2615
0
    collect = 0;
2616
0
  else if (gc->attr & GRID_ATTR_CHARSET)
2617
0
    collect = 0;
2618
0
  else if (~s->mode & MODE_WRAP)
2619
0
    collect = 0;
2620
0
  else if (s->mode & MODE_INSERT)
2621
0
    collect = 0;
2622
0
  else if (s->sel != NULL)
2623
0
    collect = 0;
2624
0
  if (!collect) {
2625
0
    screen_write_collect_end(ctx);
2626
0
    screen_write_collect_flush(ctx, 0, __func__);
2627
0
    screen_write_cell(ctx, gc);
2628
0
    return;
2629
0
  }
2630
2631
0
  if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx)
2632
0
    screen_write_collect_end(ctx);
2633
0
  ci = ctx->item; /* may have changed */
2634
2635
0
  if (s->cx > sx - 1) {
2636
0
    log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
2637
0
    ci->wrapped = 1;
2638
0
    screen_write_linefeed(ctx, 1, 8);
2639
0
    screen_write_set_cursor(ctx, 0, -1);
2640
0
  }
2641
2642
0
  if (ci->used == 0)
2643
0
    memcpy(&ci->gc, gc, sizeof ci->gc);
2644
0
  if (ctx->s->write_list[s->cy].data == NULL)
2645
0
    ctx->s->write_list[s->cy].data = xmalloc(screen_size_x(ctx->s));
2646
0
  ctx->s->write_list[s->cy].data[s->cx + ci->used++] = gc->data.data[0];
2647
0
}
2648
2649
/* Write cell data. */
2650
void
2651
screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
2652
0
{
2653
0
  struct screen   *s = ctx->s;
2654
0
  struct window_pane  *wp = ctx->wp;
2655
0
  struct grid   *gd = s->grid;
2656
0
  const struct utf8_data  *ud = &gc->data;
2657
0
  struct grid_line  *gl;
2658
0
  struct grid_cell_entry  *gce;
2659
0
  struct grid_cell   tmp_gc, now_gc;
2660
0
  struct tty_ctx     ttyctx;
2661
0
  u_int      sx = screen_size_x(s), sy = screen_size_y(s);
2662
0
  u_int      width = ud->width, xx, not_wrap, i, n, vis;
2663
0
  int      selected, skip = 1, redraw = 0;
2664
0
  int      yoff = 0, xoff = 0;
2665
0
  struct visible_ranges *r;
2666
0
  struct visible_range  *ri;
2667
2668
  /* Ignore padding cells. */
2669
0
  if (gc->flags & GRID_FLAG_PADDING)
2670
0
    return;
2671
2672
  /* Get the previous cell to check for combining. */
2673
0
  if (screen_write_combine(ctx, gc) != 0)
2674
0
    return;
2675
2676
  /* Flush any existing scrolling. */
2677
0
  screen_write_collect_flush(ctx, 1, __func__);
2678
2679
  /* If this character doesn't fit, ignore it. */
2680
0
  if ((~s->mode & MODE_WRAP) &&
2681
0
      width > 1 &&
2682
0
      (width > sx || (s->cx != sx && s->cx > sx - width)))
2683
0
    return;
2684
2685
  /* If in insert mode, make space for the cells. */
2686
0
  if (s->mode & MODE_INSERT) {
2687
0
    grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8);
2688
0
    skip = 0;
2689
0
  }
2690
2691
  /* Check this will fit on the current line and wrap if not. */
2692
0
  if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
2693
0
    log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
2694
0
    screen_write_linefeed(ctx, 1, 8);
2695
0
    screen_write_set_cursor(ctx, 0, -1);
2696
0
    screen_write_collect_flush(ctx, 0, __func__);
2697
0
  }
2698
2699
  /* Sanity check cursor position. */
2700
0
  if (s->cx > sx - width || s->cy > sy - 1)
2701
0
    return;
2702
0
  screen_write_initctx(ctx, &ttyctx, 0, 0);
2703
2704
  /* Handle overwriting of UTF-8 characters. */
2705
0
  gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
2706
0
  if (gl->flags & GRID_LINE_EXTENDED) {
2707
0
    grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
2708
0
    if (screen_write_overwrite(ctx, &now_gc, width)) {
2709
0
      redraw = 1;
2710
0
      skip = 0;
2711
0
    }
2712
0
  }
2713
2714
  /*
2715
   * If the new character is UTF-8 wide, fill in padding cells. Have
2716
   * already ensured there is enough room.
2717
   */
2718
0
  for (xx = s->cx + 1; xx < s->cx + width; xx++) {
2719
0
    log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
2720
0
    grid_view_set_padding(gd, xx, s->cy, gc->bg);
2721
0
    skip = 0;
2722
0
  }
2723
2724
  /* If no change, do not draw. */
2725
0
  if (skip) {
2726
0
    if (s->cx >= gl->cellsize)
2727
0
      skip = grid_cells_equal(gc, &grid_default_cell);
2728
0
    else {
2729
0
      gce = &gl->celldata[s->cx];
2730
0
      if (gce->flags & GRID_FLAG_EXTENDED)
2731
0
        skip = 0;
2732
0
      else if (gc->flags != gce->flags)
2733
0
        skip = 0;
2734
0
      else if (gc->attr != gce->data.attr)
2735
0
        skip = 0;
2736
0
      else if (gc->fg != gce->data.fg)
2737
0
        skip = 0;
2738
0
      else if (gc->bg != gce->data.bg)
2739
0
        skip = 0;
2740
0
      else if (gc->data.width != 1)
2741
0
        skip = 0;
2742
0
      else if (gc->data.size != 1)
2743
0
        skip = 0;
2744
0
      else if (gce->data.data != gc->data.data[0])
2745
0
        skip = 0;
2746
0
    }
2747
0
  }
2748
2749
  /* Update the selected flag and set the cell. */
2750
0
  selected = screen_check_selection(s, s->cx, s->cy);
2751
0
  if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
2752
0
    memcpy(&tmp_gc, gc, sizeof tmp_gc);
2753
0
    tmp_gc.flags |= GRID_FLAG_SELECTED;
2754
0
    grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
2755
0
  } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) {
2756
0
    memcpy(&tmp_gc, gc, sizeof tmp_gc);
2757
0
    tmp_gc.flags &= ~GRID_FLAG_SELECTED;
2758
0
    grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
2759
0
  } else if (!skip)
2760
0
    grid_view_set_cell(gd, s->cx, s->cy, gc);
2761
0
  if (selected)
2762
0
    skip = 0;
2763
2764
  /* Get visible ranges for the character before moving the cursor. */
2765
0
  if (wp != NULL) {
2766
0
    xoff = wp->xoff;
2767
0
    yoff = wp->yoff;
2768
0
  }
2769
0
  r = window_visible_ranges(wp, xoff + s->cx, s->cy + yoff, width, NULL);
2770
2771
  /*
2772
   * Move the cursor. If not wrapping, stick at the last character and
2773
   * replace it.
2774
   */
2775
0
  not_wrap = !(s->mode & MODE_WRAP);
2776
0
  if (s->cx <= sx - not_wrap - width)
2777
0
    screen_write_set_cursor(ctx, s->cx + width, -1);
2778
0
  else
2779
0
    screen_write_set_cursor(ctx, sx - not_wrap, -1);
2780
2781
  /* Create space for character in insert mode. */
2782
0
  if (s->mode & MODE_INSERT) {
2783
0
    screen_write_collect_flush(ctx, 0, __func__);
2784
0
    ttyctx.n = width;
2785
0
    if (screen_write_should_draw_line(ctx, s->cy))
2786
0
      tty_write(tty_cmd_insertcharacter, &ttyctx);
2787
0
  }
2788
2789
  /* If not writing, done now. */
2790
0
  if (skip || !screen_write_should_draw_line(ctx, s->cy))
2791
0
    return;
2792
2793
  /* Do a full line redraw if needed. */
2794
0
  if (redraw && wp != NULL) {
2795
0
    screen_write_redraw_line(ctx, &ttyctx, s->cy);
2796
0
    return;
2797
0
  }
2798
2799
  /* Work out the cell attributes. */
2800
0
  if (selected)
2801
0
    screen_select_cell(s, &tmp_gc, gc);
2802
0
  else
2803
0
    memcpy(&tmp_gc, gc, sizeof tmp_gc);
2804
0
  ttyctx.cell = &tmp_gc;
2805
2806
  /* If the cell is fully visible, it can be written entirely. */
2807
0
  for (i = 0, vis = 0; i < r->used; i++)
2808
0
    vis += r->ranges[i].nx;
2809
0
  if (vis >= width) {
2810
0
    if (screen_write_should_draw_line(ctx, s->cy))
2811
0
      tty_write(tty_cmd_cell, &ttyctx);
2812
0
    return;
2813
0
  }
2814
2815
  /*
2816
   * Otherwise this is a wide character or tab partly obscured. Write
2817
   * spaces in the visible regions.
2818
   */
2819
0
  utf8_set(&tmp_gc.data, ' ');
2820
0
  if (!screen_write_should_draw_line(ctx, s->cy))
2821
0
    return;
2822
0
  for (i = 0; i < r->used; i++) {
2823
0
    ri = &r->ranges[i];
2824
0
    if (ri->nx == 0)
2825
0
      continue;
2826
0
    for (n = 0; n < ri->nx; n++) {
2827
0
      ttyctx.ocx = (int)ri->px - xoff + (int)n;
2828
0
      tty_write(tty_cmd_cell, &ttyctx);
2829
0
    }
2830
0
  }
2831
0
}
2832
2833
/* Combine a UTF-8 zero-width character onto the previous if necessary. */
2834
static int
2835
screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
2836
0
{
2837
0
  struct screen   *s = ctx->s;
2838
0
  struct window_pane  *wp = ctx->wp;
2839
0
  struct grid   *gd = s->grid;
2840
0
  const struct utf8_data  *ud = &gc->data;
2841
0
  struct options    *oo = global_options;
2842
0
  u_int      i, n, cx = s->cx, cy = s->cy, vis, yoff = 0;
2843
0
  struct grid_cell   last;
2844
0
  struct tty_ctx     ttyctx;
2845
0
  int      force_wide = 0, zero_width = 0;
2846
0
  struct visible_ranges *r;
2847
2848
  /* Ignore U+3164 HANGUL_FILLER entirely. */
2849
0
  if (utf8_is_hangul_filler(ud))
2850
0
    return (1);
2851
2852
  /*
2853
   * Is this character which makes no sense without being combined? If
2854
   * this is true then flag it here and discard the character (return 1)
2855
   * if we cannot combine it.
2856
   */
2857
0
  if (utf8_is_zwj(ud))
2858
0
    zero_width = 1;
2859
0
  else if (utf8_is_vs(ud)) {
2860
0
    zero_width = 1;
2861
0
    if (options_get_number(oo, "variation-selector-always-wide"))
2862
0
      force_wide = 1;
2863
0
  } else if (ud->width == 0)
2864
0
    zero_width = 1;
2865
2866
  /* Cannot combine empty character or at left. */
2867
0
  if (ud->size < 2 || cx == 0)
2868
0
    return (zero_width);
2869
0
  log_debug("%s: character %.*s at %u,%u (width %u)", __func__,
2870
0
      (int)ud->size, ud->data, cx, cy, ud->width);
2871
2872
  /* Find the cell to combine with. */
2873
0
  n = 1;
2874
0
  grid_view_get_cell(gd, cx - n, cy, &last);
2875
0
  if (cx != 1 && (last.flags & GRID_FLAG_PADDING)) {
2876
0
    n = 2;
2877
0
    grid_view_get_cell(gd, cx - n, cy, &last);
2878
0
  }
2879
0
  if (n != last.data.width || (last.flags & GRID_FLAG_PADDING))
2880
0
    return (zero_width);
2881
2882
  /*
2883
   * Check if we need to combine characters. This could be a Korean
2884
   * Hangul Jamo character, zero width (set above), a modifier character
2885
   * (with an existing Unicode character) or a previous ZWJ.
2886
   */
2887
0
  if (!zero_width) {
2888
0
    switch (hanguljamo_check_state(&last.data, ud)) {
2889
0
    case HANGULJAMO_STATE_NOT_COMPOSABLE:
2890
0
      return (1);
2891
0
    case HANGULJAMO_STATE_CHOSEONG:
2892
0
      return (0);
2893
0
    case HANGULJAMO_STATE_COMPOSABLE:
2894
0
      break;
2895
0
    case HANGULJAMO_STATE_NOT_HANGULJAMO:
2896
0
      if (utf8_should_combine(&last.data, ud))
2897
0
        force_wide = 1;
2898
0
      else if (utf8_should_combine(ud, &last.data))
2899
0
        force_wide = 1;
2900
0
      else if (!utf8_has_zwj(&last.data))
2901
0
        return (0);
2902
0
      break;
2903
0
    }
2904
0
  }
2905
2906
  /* Check if this combined character would be too long. */
2907
0
  if (last.data.size + ud->size > sizeof last.data.data)
2908
0
    return (zero_width);
2909
2910
  /* Combining; flush any pending output. */
2911
0
  screen_write_collect_flush(ctx, 0, __func__);
2912
2913
0
  log_debug("%s: %.*s -> %.*s at %u,%u (offset %u, width %u)", __func__,
2914
0
      (int)ud->size, ud->data, (int)last.data.size, last.data.data,
2915
0
      cx - n, cy, n, last.data.width);
2916
2917
  /* Append the data. */
2918
0
  memcpy(last.data.data + last.data.size, ud->data, ud->size);
2919
0
  last.data.size += ud->size;
2920
2921
  /* Force the width to 2 for modifiers and variation selector. */
2922
0
  if (last.data.width == 1 && force_wide) {
2923
0
    last.data.width = 2;
2924
0
    n = 2;
2925
0
    cx++;
2926
0
  } else
2927
0
    force_wide = 0;
2928
2929
  /* Set the new cell. */
2930
0
  grid_view_set_cell(gd, cx - n, cy, &last);
2931
0
  if (force_wide)
2932
0
    grid_view_set_padding(gd, cx - 1, cy, last.bg);
2933
2934
  /*
2935
   * Check if all of this character is visible. No character will be
2936
   * obscured in the middle, only on left or right, but there could be an
2937
   * empty range in the visible ranges so we add them all up.
2938
   */
2939
0
  if (wp != NULL)
2940
0
    yoff = wp->yoff;
2941
0
  r = window_visible_ranges(wp, cx - n, cy + yoff, n, NULL);
2942
0
  for (i = 0, vis = 0; i < r->used; i++)
2943
0
    vis += r->ranges[i].nx;
2944
0
  if (vis < n) {
2945
    /*
2946
     * Part of this character is obscured. Return 1 and let
2947
     * screen_write_cell write a space.
2948
     */
2949
0
    return (1);
2950
0
  }
2951
2952
  /*
2953
   * Redraw the combined cell. If forcing the cell to width 2, reset the
2954
   * cached cursor position in the tty, since we don't really know
2955
   * whether the terminal thought the character was width 1 or width 2
2956
   * and what it is going to do now.
2957
   */
2958
0
  screen_write_set_cursor(ctx, cx - n, cy);
2959
0
  screen_write_initctx(ctx, &ttyctx, 0, 0);
2960
0
  ttyctx.cell = &last;
2961
0
  if (force_wide)
2962
0
    ttyctx.flags |= TTY_CTX_CELL_INVALIDATE;
2963
0
  if (screen_write_should_draw_line(ctx, cy))
2964
0
    tty_write(tty_cmd_cell, &ttyctx);
2965
0
  screen_write_set_cursor(ctx, cx, cy);
2966
2967
0
  return (1);
2968
0
}
2969
2970
/*
2971
 * UTF-8 wide characters are a bit of an annoyance. They take up more than one
2972
 * cell on the screen, so following cells must not be drawn by marking them as
2973
 * padding.
2974
 *
2975
 * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
2976
 * character, it is necessary to also overwrite any other cells which covered
2977
 * by the same character.
2978
 */
2979
static int
2980
screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
2981
    u_int width)
2982
0
{
2983
0
  struct screen   *s = ctx->s;
2984
0
  struct grid   *gd = s->grid;
2985
0
  struct grid_cell   tmp_gc;
2986
0
  u_int      xx;
2987
0
  int      done = 0;
2988
2989
0
  if (gc->flags & GRID_FLAG_PADDING) {
2990
    /*
2991
     * A padding cell, so clear any following and leading padding
2992
     * cells back to the character. Don't overwrite the current
2993
     * cell as that happens later anyway.
2994
     */
2995
0
    xx = s->cx + 1;
2996
0
    while (--xx > 0) {
2997
0
      grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
2998
0
      if (~tmp_gc.flags & GRID_FLAG_PADDING)
2999
0
        break;
3000
0
      log_debug("%s: padding at %u,%u", __func__, xx, s->cy);
3001
0
      screen_write_clear_cell(gd, xx, s->cy);
3002
0
    }
3003
3004
    /* Overwrite the character at the start of this padding. */
3005
0
    log_debug("%s: character at %u,%u", __func__, xx, s->cy);
3006
0
    screen_write_clear_cell(gd, xx, s->cy);
3007
0
    done = 1;
3008
0
  }
3009
3010
  /*
3011
   * Overwrite any padding cells that belong to any UTF-8 characters
3012
   * we'll be overwriting with the current character.
3013
   */
3014
0
  if (width != 1 ||
3015
0
      gc->data.width != 1 ||
3016
0
      gc->flags & GRID_FLAG_PADDING) {
3017
0
    xx = s->cx + width - 1;
3018
0
    while (++xx < screen_size_x(s)) {
3019
0
      grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
3020
0
      if (~tmp_gc.flags & GRID_FLAG_PADDING)
3021
0
        break;
3022
0
      log_debug("%s: overwrite at %u,%u", __func__, xx,
3023
0
          s->cy);
3024
0
      screen_write_clear_cell(gd, xx, s->cy);
3025
0
      done = 1;
3026
0
    }
3027
0
  }
3028
3029
0
  return (done);
3030
0
}
3031
3032
/* Set external clipboard. */
3033
void
3034
screen_write_setselection(struct screen_write_ctx *ctx, const char *clip,
3035
    u_char *str, u_int len)
3036
0
{
3037
0
  struct tty_ctx  ttyctx;
3038
3039
0
  screen_write_initctx(ctx, &ttyctx, 0, 0);
3040
0
  ttyctx.sel.clip = clip;
3041
0
  ttyctx.sel.data = str;
3042
0
  ttyctx.sel.size = len;
3043
3044
0
  tty_write(tty_cmd_setselection, &ttyctx);
3045
0
}
3046
3047
/* Write unmodified string. */
3048
void
3049
screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
3050
    int allow_invisible_panes)
3051
0
{
3052
0
  struct tty_ctx  ttyctx;
3053
3054
0
  screen_write_initctx(ctx, &ttyctx, 0, 0);
3055
0
  if (allow_invisible_panes)
3056
0
    ttyctx.flags |= TTY_CTX_INVISIBLE_PANES;
3057
0
  ttyctx.data.data = str;
3058
0
  ttyctx.data.size = len;
3059
3060
0
  tty_write(tty_cmd_rawstring, &ttyctx);
3061
0
}
3062
3063
#ifdef ENABLE_SIXEL
3064
/* Write a SIXEL image. */
3065
void
3066
screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
3067
    u_int bg)
3068
{
3069
  struct screen   *s = ctx->s;
3070
  struct grid   *gd = s->grid;
3071
  struct tty_ctx     ttyctx;
3072
  u_int      x, y, sx, sy, cx = s->cx, cy = s->cy, i, lines;
3073
  struct sixel_image  *new;
3074
3075
  if (screen_size_y(s) == 1)
3076
    return;
3077
3078
  sixel_size_in_cells(si, &x, &y);
3079
  if (x > screen_size_x(s) || y > screen_size_y(s) - 1) {
3080
    if (x > screen_size_x(s) - cx)
3081
      sx = screen_size_x(s) - cx;
3082
    else
3083
      sx = x;
3084
    if (y > screen_size_y(s) - 1)
3085
      sy = screen_size_y(s) - 1;
3086
    else
3087
      sy = y;
3088
    new = sixel_scale(si, 0, 0, 0, y - sy, sx, sy, 1);
3089
    sixel_free(si);
3090
    if (new == NULL)
3091
      return;
3092
    sixel_size_in_cells(new, &x, &y);
3093
    si = new;
3094
  }
3095
3096
  sy = screen_size_y(s) - cy;
3097
  if (sy <= y) {
3098
    lines = y - sy + 1;
3099
    if (image_scroll_up(s, lines) && ctx->wp != NULL)
3100
      ctx->wp->flags |= PANE_REDRAW;
3101
    for (i = 0; i < lines; i++) {
3102
      grid_view_scroll_region_up(gd, 0, screen_size_y(s) - 1,
3103
          bg);
3104
      screen_write_collect_scroll(ctx, bg);
3105
    }
3106
    ctx->scrolled += lines;
3107
    if (lines > cy)
3108
      screen_write_cursormove(ctx, -1, 0, 0);
3109
    else
3110
      screen_write_cursormove(ctx, -1, cy - lines, 0);
3111
  }
3112
  screen_write_collect_flush(ctx, 0, __func__);
3113
3114
  screen_write_initctx(ctx, &ttyctx, 0, 0);
3115
  ttyctx.image = image_store(s, si);
3116
3117
  tty_write(tty_cmd_sixelimage, &ttyctx);
3118
3119
  screen_write_cursormove(ctx, 0, cy + y, 0);
3120
}
3121
#endif
3122
3123
/* Turn alternate screen on. */
3124
void
3125
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
3126
    int cursor)
3127
0
{
3128
0
  struct tty_ctx       ttyctx;
3129
0
  struct window_pane    *wp = ctx->wp;
3130
3131
0
  if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
3132
0
    return;
3133
3134
0
  screen_write_collect_flush(ctx, 0, __func__);
3135
0
  if (!screen_alternate_on(ctx->s, gc, cursor))
3136
0
    return;
3137
3138
0
  if (wp != NULL) {
3139
0
    window_pane_clear_resizes(wp, NULL);
3140
0
    if (event_initialized(&wp->resize_timer))
3141
0
      evtimer_del(&wp->resize_timer);
3142
0
    layout_fix_panes(wp->window, NULL);
3143
0
    if (!TAILQ_EMPTY(&wp->resize_queue)) {
3144
0
      window_pane_send_resize(wp, wp->sx, wp->sy);
3145
0
      window_pane_clear_resizes(wp, NULL);
3146
0
    }
3147
0
    server_redraw_window_borders(wp->window);
3148
0
  }
3149
3150
0
  screen_write_initctx(ctx, &ttyctx, 1, 0);
3151
0
  if (ttyctx.redraw_cb != NULL)
3152
0
    ttyctx.redraw_cb(&ttyctx);
3153
0
}
3154
3155
/* Turn alternate screen off. */
3156
void
3157
screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
3158
    int cursor)
3159
0
{
3160
0
  struct tty_ctx     ttyctx;
3161
0
  struct window_pane  *wp = ctx->wp;
3162
3163
0
  if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
3164
0
    return;
3165
3166
0
  screen_write_collect_flush(ctx, 0, __func__);
3167
0
  if (!screen_alternate_off(ctx->s, gc, cursor))
3168
0
    return;
3169
3170
0
  if (wp != NULL) {
3171
0
    layout_fix_panes(wp->window, NULL);
3172
0
    server_redraw_window_borders(wp->window);
3173
0
  }
3174
3175
0
  screen_write_initctx(ctx, &ttyctx, 1, 0);
3176
0
  if (ttyctx.redraw_cb != NULL)
3177
0
    ttyctx.redraw_cb(&ttyctx);
3178
0
}