Coverage Report

Created: 2026-06-10 06:30

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