1/* $OpenBSD$ */
2
3/*
4 * Copyright (c) 2007 Nicholas Marriott <[email protected]>
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
26static void screen_write_initctx(struct screen_write_ctx *,
27 struct tty_ctx *);
28static void screen_write_collect_clear(struct screen_write_ctx *, u_int,
29 u_int);
30static void screen_write_collect_scroll(struct screen_write_ctx *);
31static void screen_write_collect_flush(struct screen_write_ctx *, int);
32
33static int screen_write_overwrite(struct screen_write_ctx *,
34 struct grid_cell *, u_int);
35static const struct grid_cell *screen_write_combine(struct screen_write_ctx *,
36 const struct utf8_data *, u_int *);
37
38static const struct grid_cell screen_write_pad_cell = {
39 { { 0 }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 0, 8, 8
40};
41
42struct screen_write_collect_item {
43 u_int x;
44 int wrapped;
45
46 u_int used;
47 char data[256];
48
49 struct grid_cell gc;
50
51 TAILQ_ENTRY(screen_write_collect_item) entry;
52};
53struct screen_write_collect_line {
54 TAILQ_HEAD(, screen_write_collect_item) items;
55};
56
57static void
58screen_write_offset_timer(__unused int fd, __unused short events, void *data)
59{
60 struct window *w = data;
61
62 tty_update_window_offset(w);
63}
64
65/* Set cursor position. */
66static void
67screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy)
68{
69 struct window_pane *wp = ctx->wp;
70 struct window *w;
71 struct screen *s = ctx->s;
72 struct timeval tv = { .tv_usec = 10000 };
73
74 if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy)
75 return;
76
77 if (cx != -1) {
78 if ((u_int)cx > screen_size_x(s)) /* allow last column */
79 cx = screen_size_x(s) - 1;
80 s->cx = cx;
81 }
82 if (cy != -1) {
83 if ((u_int)cy > screen_size_y(s) - 1)
84 cy = screen_size_y(s) - 1;
85 s->cy = cy;
86 }
87
88 if (wp == NULL)
89 return;
90 w = wp->window;
91
92 if (!event_initialized(&w->offset_timer))
93 evtimer_set(&w->offset_timer, screen_write_offset_timer, w);
94 if (!evtimer_pending(&w->offset_timer, NULL))
95 evtimer_add(&w->offset_timer, &tv);
96}
97
98/* Initialize writing with a window. */
99void
100screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp,
101 struct screen *s)
102{
103 u_int y;
104
105 memset(ctx, 0, sizeof *ctx);
106
107 ctx->wp = wp;
108 if (wp != NULL && s == NULL)
109 ctx->s = wp->screen;
110 else
111 ctx->s = s;
112
113 ctx->list = xcalloc(screen_size_y(ctx->s), sizeof *ctx->list);
114 for (y = 0; y < screen_size_y(ctx->s); y++)
115 TAILQ_INIT(&ctx->list[y].items);
116 ctx->item = xcalloc(1, sizeof *ctx->item);
117
118 ctx->scrolled = 0;
119 ctx->bg = 8;
120
121 if (log_get_level() != 0) {
122 if (wp != NULL) {
123 log_debug("%s: size %ux%u, pane %%%u (at %u,%u)",
124 __func__, screen_size_x(ctx->s),
125 screen_size_y(ctx->s), wp->id, wp->xoff, wp->yoff);
126 } else {
127 log_debug("%s: size %ux%u, no pane",
128 __func__, screen_size_x(ctx->s),
129 screen_size_y(ctx->s));
130 }
131 }
132}
133
134/* Finish writing. */
135void
136screen_write_stop(struct screen_write_ctx *ctx)
137{
138 screen_write_collect_end(ctx);
139 screen_write_collect_flush(ctx, 0);
140
141 log_debug("%s: %u cells (%u written, %u skipped)", __func__,
142 ctx->cells, ctx->written, ctx->skipped);
143
144 free(ctx->item);
145 free(ctx->list); /* flush will have emptied */
146}
147
148/* Reset screen state. */
149void
150screen_write_reset(struct screen_write_ctx *ctx)
151{
152 struct screen *s = ctx->s;
153
154 screen_reset_tabs(s);
155 screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1);
156
157 s->mode = MODE_CURSOR | MODE_WRAP;
158
159 screen_write_clearscreen(ctx, 8);
160 screen_write_set_cursor(ctx, 0, 0);
161}
162
163/* Write character. */
164void
165screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
166 u_char ch)
167{
168 struct grid_cell gc;
169
170 memcpy(&gc, gcp, sizeof gc);
171
172 utf8_set(&gc.data, ch);
173 screen_write_cell(ctx, &gc);
174}
175
176/* Calculate string length. */
177size_t
178screen_write_strlen(const char *fmt, ...)
179{
180 va_list ap;
181 char *msg;
182 struct utf8_data ud;
183 u_char *ptr;
184 size_t left, size = 0;
185 enum utf8_state more;
186
187 va_start(ap, fmt);
188 xvasprintf(&msg, fmt, ap);
189 va_end(ap);
190
191 ptr = msg;
192 while (*ptr != '\0') {
193 if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) {
194 ptr++;
195
196 left = strlen(ptr);
197 if (left < (size_t)ud.size - 1)
198 break;
199 while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE)
200 ptr++;
201 ptr++;
202
203 if (more == UTF8_DONE)
204 size += ud.width;
205 } else {
206 if (*ptr > 0x1f && *ptr < 0x7f)
207 size++;
208 ptr++;
209 }
210 }
211
212 free(msg);
213 return (size);
214}
215
216/* Write simple string (no UTF-8 or maximum length). */
217void
218screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
219 const char *fmt, ...)
220{
221 va_list ap;
222
223 va_start(ap, fmt);
224 screen_write_vnputs(ctx, -1, gcp, fmt, ap);
225 va_end(ap);
226}
227
228/* Write string with length limit (-1 for unlimited). */
229void
230screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen,
231 const struct grid_cell *gcp, const char *fmt, ...)
232{
233 va_list ap;
234
235 va_start(ap, fmt);
236 screen_write_vnputs(ctx, maxlen, gcp, fmt, ap);
237 va_end(ap);
238}
239
240void
241screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
242 const struct grid_cell *gcp, const char *fmt, va_list ap)
243{
244 struct grid_cell gc;
245 struct utf8_data *ud = &gc.data;
246 char *msg;
247 u_char *ptr;
248 size_t left, size = 0;
249 enum utf8_state more;
250
251 memcpy(&gc, gcp, sizeof gc);
252 xvasprintf(&msg, fmt, ap);
253
254 ptr = msg;
255 while (*ptr != '\0') {
256 if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) {
257 ptr++;
258
259 left = strlen(ptr);
260 if (left < (size_t)ud->size - 1)
261 break;
262 while ((more = utf8_append(ud, *ptr)) == UTF8_MORE)
263 ptr++;
264 ptr++;
265
266 if (more != UTF8_DONE)
267 continue;
268 if (maxlen > 0 && size + ud->width > (size_t)maxlen) {
269 while (size < (size_t)maxlen) {
270 screen_write_putc(ctx, &gc, ' ');
271 size++;
272 }
273 break;
274 }
275 size += ud->width;
276 screen_write_cell(ctx, &gc);
277 } else {
278 if (maxlen > 0 && size + 1 > (size_t)maxlen)
279 break;
280
281 if (*ptr == '\001')
282 gc.attr ^= GRID_ATTR_CHARSET;
283 else if (*ptr > 0x1f && *ptr < 0x7f) {
284 size++;
285 screen_write_putc(ctx, &gc, *ptr);
286 }
287 ptr++;
288 }
289 }
290
291 free(msg);
292}
293
294/* Copy from another screen. Assumes target region is big enough. */
295void
296screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px,
297 u_int py, u_int nx, u_int ny, bitstr_t *mbs, const struct grid_cell *mgc)
298{
299 struct screen *s = ctx->s;
300 struct grid *gd = src->grid;
301 struct grid_cell gc;
302 u_int xx, yy, cx, cy, b;
303
304 if (nx == 0 || ny == 0)
305 return;
306
307 cx = s->cx;
308 cy = s->cy;
309
310 for (yy = py; yy < py + ny; yy++) {
311 for (xx = px; xx < px + nx; xx++) {
312 grid_get_cell(gd, xx, yy, &gc);
313 if (mbs != NULL) {
314 b = (yy * screen_size_x(src)) + xx;
315 if (bit_test(mbs, b)) {
316 gc.attr = mgc->attr;
317 gc.fg = mgc->fg;
318 gc.bg = mgc->bg;
319 }
320 }
321 if (xx + gc.data.width <= px + nx)
322 screen_write_cell(ctx, &gc);
323 }
324 cy++;
325 screen_write_set_cursor(ctx, cx, cy);
326 }
327}
328
329/*
330 * Copy from another screen but without the selection stuff. Also assumes the
331 * target region is already big enough.
332 */
333void
334screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
335 u_int px, u_int py, u_int nx, u_int ny)
336{
337 struct screen *s = ctx->s;
338 struct grid *gd = src->grid;
339 struct grid_cell gc;
340 u_int xx, yy, cx, cy;
341
342 if (nx == 0 || ny == 0)
343 return;
344
345 cy = s->cy;
346 for (yy = py; yy < py + ny; yy++) {
347 if (yy >= gd->hsize + gd->sy)
348 break;
349 cx = s->cx;
350 for (xx = px; xx < px + nx; xx++) {
351 if (xx >= grid_get_line(gd, yy)->cellsize)
352 break;
353 grid_get_cell(gd, xx, yy, &gc);
354 if (xx + gc.data.width > px + nx)
355 break;
356 grid_view_set_cell(ctx->s->grid, cx, cy, &gc);
357 cx++;
358 }
359 cy++;
360 }
361}
362
363/* Draw a horizontal line on screen. */
364void
365screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right)
366{
367 struct screen *s = ctx->s;
368 struct grid_cell gc;
369 u_int cx, cy, i;
370
371 cx = s->cx;
372 cy = s->cy;
373
374 memcpy(&gc, &grid_default_cell, sizeof gc);
375 gc.attr |= GRID_ATTR_CHARSET;
376
377 screen_write_putc(ctx, &gc, left ? 't' : 'q');
378 for (i = 1; i < nx - 1; i++)
379 screen_write_putc(ctx, &gc, 'q');
380 screen_write_putc(ctx, &gc, right ? 'u' : 'q');
381
382 screen_write_set_cursor(ctx, cx, cy);
383}
384
385/* Draw a horizontal line on screen. */
386void
387screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
388{
389 struct screen *s = ctx->s;
390 struct grid_cell gc;
391 u_int cx, cy, i;
392
393 cx = s->cx;
394 cy = s->cy;
395
396 memcpy(&gc, &grid_default_cell, sizeof gc);
397 gc.attr |= GRID_ATTR_CHARSET;
398
399 screen_write_putc(ctx, &gc, top ? 'w' : 'x');
400 for (i = 1; i < ny - 1; i++) {
401 screen_write_set_cursor(ctx, cx, cy + i);
402 screen_write_putc(ctx, &gc, 'x');
403 }
404 screen_write_set_cursor(ctx, cx, cy + ny - 1);
405 screen_write_putc(ctx, &gc, bottom ? 'v' : 'x');
406
407 screen_write_set_cursor(ctx, cx, cy);
408}
409
410/* Draw a menu on screen. */
411void
412screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice)
413{
414 struct screen *s = ctx->s;
415 struct grid_cell gc;
416 u_int cx, cy, i, j;
417 const char *name;
418
419 cx = s->cx;
420 cy = s->cy;
421
422 memcpy(&gc, &grid_default_cell, sizeof gc);
423
424 screen_write_box(ctx, menu->width + 4, menu->count + 2);
425 screen_write_cursormove(ctx, cx + 2, cy, 0);
426 format_draw(ctx, &gc, menu->width, menu->title, NULL);
427
428 for (i = 0; i < menu->count; i++) {
429 name = menu->items[i].name;
430 if (name == NULL) {
431 screen_write_cursormove(ctx, cx, cy + 1 + i, 0);
432 screen_write_hline(ctx, menu->width + 4, 1, 1);
433 } else {
434 if (choice >= 0 && i == (u_int)choice && *name != '-')
435 gc.attr |= GRID_ATTR_REVERSE;
436 screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
437 for (j = 0; j < menu->width; j++)
438 screen_write_putc(ctx, &gc, ' ');
439 screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
440 if (*name == '-') {
441 name++;
442 gc.attr |= GRID_ATTR_DIM;
443 format_draw(ctx, &gc, menu->width, name, NULL);
444 gc.attr &= ~GRID_ATTR_DIM;
445 } else
446 format_draw(ctx, &gc, menu->width, name, NULL);
447 if (choice >= 0 && i == (u_int)choice)
448 gc.attr &= ~GRID_ATTR_REVERSE;
449 }
450 }
451
452 screen_write_set_cursor(ctx, cx, cy);
453}
454
455/* Draw a box on screen. */
456void
457screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny)
458{
459 struct screen *s = ctx->s;
460 struct grid_cell gc;
461 u_int cx, cy, i;
462
463 cx = s->cx;
464 cy = s->cy;
465
466 memcpy(&gc, &grid_default_cell, sizeof gc);
467 gc.attr |= GRID_ATTR_CHARSET;
468
469 screen_write_putc(ctx, &gc, 'l');
470 for (i = 1; i < nx - 1; i++)
471 screen_write_putc(ctx, &gc, 'q');
472 screen_write_putc(ctx, &gc, 'k');
473
474 screen_write_set_cursor(ctx, cx, cy + ny - 1);
475 screen_write_putc(ctx, &gc, 'm');
476 for (i = 1; i < nx - 1; i++)
477 screen_write_putc(ctx, &gc, 'q');
478 screen_write_putc(ctx, &gc, 'j');
479
480 for (i = 1; i < ny - 1; i++) {
481 screen_write_set_cursor(ctx, cx, cy + i);
482 screen_write_putc(ctx, &gc, 'x');
483 }
484 for (i = 1; i < ny - 1; i++) {
485 screen_write_set_cursor(ctx, cx + nx - 1, cy + i);
486 screen_write_putc(ctx, &gc, 'x');
487 }
488
489 screen_write_set_cursor(ctx, cx, cy);
490}
491
492/*
493 * Write a preview version of a window. Assumes target area is big enough and
494 * already cleared.
495 */
496void
497screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
498 u_int ny)
499{
500 struct screen *s = ctx->s;
501 struct grid_cell gc;
502 u_int cx, cy, px, py;
503
504 cx = s->cx;
505 cy = s->cy;
506
507 /*
508 * If the cursor is on, pick the area around the cursor, otherwise use
509 * the top left.
510 */
511 if (src->mode & MODE_CURSOR) {
512 px = src->cx;
513 if (px < nx / 3)
514 px = 0;
515 else
516 px = px - nx / 3;
517 if (px + nx > screen_size_x(src)) {
518 if (nx > screen_size_x(src))
519 px = 0;
520 else
521 px = screen_size_x(src) - nx;
522 }
523 py = src->cy;
524 if (py < ny / 3)
525 py = 0;
526 else
527 py = py - ny / 3;
528 if (py + ny > screen_size_y(src)) {
529 if (ny > screen_size_y(src))
530 py = 0;
531 else
532 py = screen_size_y(src) - ny;
533 }
534 } else {
535 px = 0;
536 py = 0;
537 }
538
539 screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny);
540
541 if (src->mode & MODE_CURSOR) {
542 grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
543 gc.attr |= GRID_ATTR_REVERSE;
544 screen_write_set_cursor(ctx, cx + (src->cx - px),
545 cy + (src->cy - py));
546 screen_write_cell(ctx, &gc);
547 }
548}
549
550/* Set up context for TTY command. */
551static void
552screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
553{
554 struct screen *s = ctx->s;
555
556 memset(ttyctx, 0, sizeof *ttyctx);
557
558 ttyctx->wp = ctx->wp;
559
560 ttyctx->ocx = s->cx;
561 ttyctx->ocy = s->cy;
562
563 ttyctx->orlower = s->rlower;
564 ttyctx->orupper = s->rupper;
565}
566
567/* Set a mode. */
568void
569screen_write_mode_set(struct screen_write_ctx *ctx, int mode)
570{
571 struct screen *s = ctx->s;
572
573 s->mode |= mode;
574}
575
576/* Clear a mode. */
577void
578screen_write_mode_clear(struct screen_write_ctx *ctx, int mode)
579{
580 struct screen *s = ctx->s;
581
582 s->mode &= ~mode;
583}
584
585/* Cursor up by ny. */
586void
587screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
588{
589 struct screen *s = ctx->s;
590 u_int cx = s->cx, cy = s->cy;
591
592 if (ny == 0)
593 ny = 1;
594
595 if (cy < s->rupper) {
596 /* Above region. */
597 if (ny > cy)
598 ny = cy;
599 } else {
600 /* Below region. */
601 if (ny > cy - s->rupper)
602 ny = cy - s->rupper;
603 }
604 if (cx == screen_size_x(s))
605 cx--;
606
607 cy -= ny;
608
609 screen_write_set_cursor(ctx, cx, cy);
610}
611
612/* Cursor down by ny. */
613void
614screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
615{
616 struct screen *s = ctx->s;
617 u_int cx = s->cx, cy = s->cy;
618
619 if (ny == 0)
620 ny = 1;
621
622 if (cy > s->rlower) {
623 /* Below region. */
624 if (ny > screen_size_y(s) - 1 - cy)
625 ny = screen_size_y(s) - 1 - cy;
626 } else {
627 /* Above region. */
628 if (ny > s->rlower - cy)
629 ny = s->rlower - cy;
630 }
631 if (cx == screen_size_x(s))
632 cx--;
633 else if (ny == 0)
634 return;
635
636 cy += ny;
637
638 screen_write_set_cursor(ctx, cx, cy);
639}
640
641/* Cursor right by nx. */
642void
643screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
644{
645 struct screen *s = ctx->s;
646 u_int cx = s->cx, cy = s->cy;
647
648 if (nx == 0)
649 nx = 1;
650
651 if (nx > screen_size_x(s) - 1 - cx)
652 nx = screen_size_x(s) - 1 - cx;
653 if (nx == 0)
654 return;
655
656 cx += nx;
657
658 screen_write_set_cursor(ctx, cx, cy);
659}
660
661/* Cursor left by nx. */
662void
663screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx)
664{
665 struct screen *s = ctx->s;
666 u_int cx = s->cx, cy = s->cy;
667
668 if (nx == 0)
669 nx = 1;
670
671 if (nx > cx)
672 nx = cx;
673 if (nx == 0)
674 return;
675
676 cx -= nx;
677
678 screen_write_set_cursor(ctx, cx, cy);
679}
680
681/* Backspace; cursor left unless at start of wrapped line when can move up. */
682void
683screen_write_backspace(struct screen_write_ctx *ctx)
684{
685 struct screen *s = ctx->s;
686 struct grid_line *gl;
687 u_int cx = s->cx, cy = s->cy;
688
689 if (cx == 0) {
690 if (cy == 0)
691 return;
692 gl = grid_get_line(s->grid, s->grid->hsize + cy - 1);
693 if (gl->flags & GRID_LINE_WRAPPED) {
694 cy--;
695 cx = screen_size_x(s) - 1;
696 }
697 } else
698 cx--;
699
700 screen_write_set_cursor(ctx, cx, cy);
701}
702
703/* VT100 alignment test. */
704void
705screen_write_alignmenttest(struct screen_write_ctx *ctx)
706{
707 struct screen *s = ctx->s;
708 struct tty_ctx ttyctx;
709 struct grid_cell gc;
710 u_int xx, yy;
711
712 memcpy(&gc, &grid_default_cell, sizeof gc);
713 utf8_set(&gc.data, 'E');
714
715 for (yy = 0; yy < screen_size_y(s); yy++) {
716 for (xx = 0; xx < screen_size_x(s); xx++)
717 grid_view_set_cell(s->grid, xx, yy, &gc);
718 }
719
720 screen_write_set_cursor(ctx, 0, 0);
721
722 s->rupper = 0;
723 s->rlower = screen_size_y(s) - 1;
724
725 screen_write_initctx(ctx, &ttyctx);
726
727 screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1);
728 tty_write(tty_cmd_alignmenttest, &ttyctx);
729}
730
731/* Insert nx characters. */
732void
733screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
734{
735 struct screen *s = ctx->s;
736 struct tty_ctx ttyctx;
737
738 if (nx == 0)
739 nx = 1;
740
741 if (nx > screen_size_x(s) - s->cx)
742 nx = screen_size_x(s) - s->cx;
743 if (nx == 0)
744 return;
745
746 if (s->cx > screen_size_x(s) - 1)
747 return;
748
749 screen_write_initctx(ctx, &ttyctx);
750 ttyctx.bg = bg;
751
752 grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg);
753
754 screen_write_collect_flush(ctx, 0);
755 ttyctx.num = nx;
756 tty_write(tty_cmd_insertcharacter, &ttyctx);
757}
758
759/* Delete nx characters. */
760void
761screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
762{
763 struct screen *s = ctx->s;
764 struct tty_ctx ttyctx;
765
766 if (nx == 0)
767 nx = 1;
768
769 if (nx > screen_size_x(s) - s->cx)
770 nx = screen_size_x(s) - s->cx;
771 if (nx == 0)
772 return;
773
774 if (s->cx > screen_size_x(s) - 1)
775 return;
776
777 screen_write_initctx(ctx, &ttyctx);
778 ttyctx.bg = bg;
779
780 grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg);
781
782 screen_write_collect_flush(ctx, 0);
783 ttyctx.num = nx;
784 tty_write(tty_cmd_deletecharacter, &ttyctx);
785}
786
787/* Clear nx characters. */
788void
789screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
790{
791 struct screen *s = ctx->s;
792 struct tty_ctx ttyctx;
793
794 if (nx == 0)
795 nx = 1;
796
797 if (nx > screen_size_x(s) - s->cx)
798 nx = screen_size_x(s) - s->cx;
799 if (nx == 0)
800 return;
801
802 if (s->cx > screen_size_x(s) - 1)
803 return;
804
805 screen_write_initctx(ctx, &ttyctx);
806 ttyctx.bg = bg;
807
808 grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg);
809
810 screen_write_collect_flush(ctx, 0);
811 ttyctx.num = nx;
812 tty_write(tty_cmd_clearcharacter, &ttyctx);
813}
814
815/* Insert ny lines. */
816void
817screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
818{
819 struct screen *s = ctx->s;
820 struct grid *gd = s->grid;
821 struct tty_ctx ttyctx;
822
823 if (ny == 0)
824 ny = 1;
825
826 if (s->cy < s->rupper || s->cy > s->rlower) {
827 if (ny > screen_size_y(s) - s->cy)
828 ny = screen_size_y(s) - s->cy;
829 if (ny == 0)
830 return;
831
832 screen_write_initctx(ctx, &ttyctx);
833 ttyctx.bg = bg;
834
835 grid_view_insert_lines(gd, s->cy, ny, bg);
836
837 screen_write_collect_flush(ctx, 0);
838 ttyctx.num = ny;
839 tty_write(tty_cmd_insertline, &ttyctx);
840 return;
841 }
842
843 if (ny > s->rlower + 1 - s->cy)
844 ny = s->rlower + 1 - s->cy;
845 if (ny == 0)
846 return;
847
848 screen_write_initctx(ctx, &ttyctx);
849 ttyctx.bg = bg;
850
851 if (s->cy < s->rupper || s->cy > s->rlower)
852 grid_view_insert_lines(gd, s->cy, ny, bg);
853 else
854 grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg);
855
856 screen_write_collect_flush(ctx, 0);
857 ttyctx.num = ny;
858 tty_write(tty_cmd_insertline, &ttyctx);
859}
860
861/* Delete ny lines. */
862void
863screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
864{
865 struct screen *s = ctx->s;
866 struct grid *gd = s->grid;
867 struct tty_ctx ttyctx;
868
869 if (ny == 0)
870 ny = 1;
871
872 if (s->cy < s->rupper || s->cy > s->rlower) {
873 if (ny > screen_size_y(s) - s->cy)
874 ny = screen_size_y(s) - s->cy;
875 if (ny == 0)
876 return;
877
878 screen_write_initctx(ctx, &ttyctx);
879 ttyctx.bg = bg;
880
881 grid_view_delete_lines(gd, s->cy, ny, bg);
882
883 screen_write_collect_flush(ctx, 0);
884 ttyctx.num = ny;
885 tty_write(tty_cmd_deleteline, &ttyctx);
886 return;
887 }
888
889 if (ny > s->rlower + 1 - s->cy)
890 ny = s->rlower + 1 - s->cy;
891 if (ny == 0)
892 return;
893
894 screen_write_initctx(ctx, &ttyctx);
895 ttyctx.bg = bg;
896
897 if (s->cy < s->rupper || s->cy > s->rlower)
898 grid_view_delete_lines(gd, s->cy, ny, bg);
899 else
900 grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg);
901
902 screen_write_collect_flush(ctx, 0);
903 ttyctx.num = ny;
904 tty_write(tty_cmd_deleteline, &ttyctx);
905}
906
907/* Clear line at cursor. */
908void
909screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
910{
911 struct screen *s = ctx->s;
912 struct grid_line *gl;
913 struct tty_ctx ttyctx;
914 u_int sx = screen_size_x(s);
915
916 gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
917 if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
918 return;
919
920 screen_write_initctx(ctx, &ttyctx);
921 ttyctx.bg = bg;
922
923 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
924
925 screen_write_collect_clear(ctx, s->cy, 1);
926 screen_write_collect_flush(ctx, 0);
927 tty_write(tty_cmd_clearline, &ttyctx);
928}
929
930/* Clear to end of line from cursor. */
931void
932screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
933{
934 struct screen *s = ctx->s;
935 struct grid_line *gl;
936 struct tty_ctx ttyctx;
937 u_int sx = screen_size_x(s);
938
939 gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
940 if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
941 return;
942
943 screen_write_initctx(ctx, &ttyctx);
944 ttyctx.bg = bg;
945
946 grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
947
948 if (s->cx == 0)
949 screen_write_collect_clear(ctx, s->cy, 1);
950 screen_write_collect_flush(ctx, 0);
951 tty_write(tty_cmd_clearendofline, &ttyctx);
952}
953
954/* Clear to start of line from cursor. */
955void
956screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
957{
958 struct screen *s = ctx->s;
959 struct tty_ctx ttyctx;
960 u_int sx = screen_size_x(s);
961
962 screen_write_initctx(ctx, &ttyctx);
963 ttyctx.bg = bg;
964
965 if (s->cx > sx - 1)
966 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
967 else
968 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
969
970 if (s->cx > sx - 1)
971 screen_write_collect_clear(ctx, s->cy, 1);
972 screen_write_collect_flush(ctx, 0);
973 tty_write(tty_cmd_clearstartofline, &ttyctx);
974}
975
976/* Move cursor to px,py. */
977void
978screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
979 int origin)
980{
981 struct screen *s = ctx->s;
982
983 if (origin && py != -1 && (s->mode & MODE_ORIGIN)) {
984 if ((u_int)py > s->rlower - s->rupper)
985 py = s->rlower;
986 else
987 py += s->rupper;
988 }
989
990 if (px != -1 && (u_int)px > screen_size_x(s) - 1)
991 px = screen_size_x(s) - 1;
992 if (py != -1 && (u_int)py > screen_size_y(s) - 1)
993 py = screen_size_y(s) - 1;
994
995 screen_write_set_cursor(ctx, px, py);
996}
997
998/* Reverse index (up with scroll). */
999void
1000screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
1001{
1002 struct screen *s = ctx->s;
1003 struct tty_ctx ttyctx;
1004
1005 screen_write_initctx(ctx, &ttyctx);
1006 ttyctx.bg = bg;
1007
1008 if (s->cy == s->rupper)
1009 grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
1010 else if (s->cy > 0)
1011 screen_write_set_cursor(ctx, -1, s->cy - 1);
1012
1013 screen_write_collect_flush(ctx, 0);
1014 tty_write(tty_cmd_reverseindex, &ttyctx);
1015}
1016
1017/* Set scroll region. */
1018void
1019screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper,
1020 u_int rlower)
1021{
1022 struct screen *s = ctx->s;
1023
1024 if (rupper > screen_size_y(s) - 1)
1025 rupper = screen_size_y(s) - 1;
1026 if (rlower > screen_size_y(s) - 1)
1027 rlower = screen_size_y(s) - 1;
1028 if (rupper >= rlower) /* cannot be one line */
1029 return;
1030
1031 screen_write_collect_flush(ctx, 0);
1032
1033 /* Cursor moves to top-left. */
1034 screen_write_set_cursor(ctx, 0, 0);
1035
1036 s->rupper = rupper;
1037 s->rlower = rlower;
1038}
1039
1040/* Line feed. */
1041void
1042screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
1043{
1044 struct screen *s = ctx->s;
1045 struct grid *gd = s->grid;
1046 struct grid_line *gl;
1047
1048 gl = grid_get_line(gd, gd->hsize + s->cy);
1049 if (wrapped)
1050 gl->flags |= GRID_LINE_WRAPPED;
1051 else
1052 gl->flags &= ~GRID_LINE_WRAPPED;
1053
1054 log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1055 s->rupper, s->rlower);
1056
1057 if (bg != ctx->bg) {
1058 screen_write_collect_flush(ctx, 1);
1059 ctx->bg = bg;
1060 }
1061
1062 if (s->cy == s->rlower) {
1063 grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1064 screen_write_collect_scroll(ctx);
1065 ctx->scrolled++;
1066 } else if (s->cy < screen_size_y(s) - 1)
1067 screen_write_set_cursor(ctx, -1, s->cy + 1);
1068}
1069
1070/* Scroll up. */
1071void
1072screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1073{
1074 struct screen *s = ctx->s;
1075 struct grid *gd = s->grid;
1076 u_int i;
1077
1078 if (lines == 0)
1079 lines = 1;
1080 else if (lines > s->rlower - s->rupper + 1)
1081 lines = s->rlower - s->rupper + 1;
1082
1083 if (bg != ctx->bg) {
1084 screen_write_collect_flush(ctx, 1);
1085 ctx->bg = bg;
1086 }
1087
1088 for (i = 0; i < lines; i++) {
1089 grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1090 screen_write_collect_scroll(ctx);
1091 }
1092 ctx->scrolled += lines;
1093}
1094
1095/* Scroll down. */
1096void
1097screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1098{
1099 struct screen *s = ctx->s;
1100 struct grid *gd = s->grid;
1101 struct tty_ctx ttyctx;
1102 u_int i;
1103
1104 screen_write_initctx(ctx, &ttyctx);
1105 ttyctx.bg = bg;
1106
1107 if (lines == 0)
1108 lines = 1;
1109 else if (lines > s->rlower - s->rupper + 1)
1110 lines = s->rlower - s->rupper + 1;
1111
1112 for (i = 0; i < lines; i++)
1113 grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg);
1114
1115 screen_write_collect_flush(ctx, 0);
1116 ttyctx.num = lines;
1117 tty_write(tty_cmd_scrolldown, &ttyctx);
1118}
1119
1120/* Carriage return (cursor to start of line). */
1121void
1122screen_write_carriagereturn(struct screen_write_ctx *ctx)
1123{
1124 screen_write_set_cursor(ctx, 0, -1);
1125}
1126
1127/* Clear to end of screen from cursor. */
1128void
1129screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
1130{
1131 struct screen *s = ctx->s;
1132 struct grid *gd = s->grid;
1133 struct tty_ctx ttyctx;
1134 u_int sx = screen_size_x(s), sy = screen_size_y(s);
1135
1136 screen_write_initctx(ctx, &ttyctx);
1137 ttyctx.bg = bg;
1138
1139 /* Scroll into history if it is enabled and clearing entire screen. */
1140 if (s->cx == 0 && s->cy == 0 && (gd->flags & GRID_HISTORY))
1141 grid_view_clear_history(gd, bg);
1142 else {
1143 if (s->cx <= sx - 1)
1144 grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg);
1145 grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg);
1146 }
1147
1148 screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1));
1149 screen_write_collect_flush(ctx, 0);
1150 tty_write(tty_cmd_clearendofscreen, &ttyctx);
1151}
1152
1153/* Clear to start of screen. */
1154void
1155screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
1156{
1157 struct screen *s = ctx->s;
1158 struct tty_ctx ttyctx;
1159 u_int sx = screen_size_x(s);
1160
1161 screen_write_initctx(ctx, &ttyctx);
1162 ttyctx.bg = bg;
1163
1164 if (s->cy > 0)
1165 grid_view_clear(s->grid, 0, 0, sx, s->cy, bg);
1166 if (s->cx > sx - 1)
1167 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1168 else
1169 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1170
1171 screen_write_collect_clear(ctx, 0, s->cy);
1172 screen_write_collect_flush(ctx, 0);
1173 tty_write(tty_cmd_clearstartofscreen, &ttyctx);
1174}
1175
1176/* Clear entire screen. */
1177void
1178screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
1179{
1180 struct screen *s = ctx->s;
1181 struct tty_ctx ttyctx;
1182 u_int sx = screen_size_x(s), sy = screen_size_y(s);
1183
1184 screen_write_initctx(ctx, &ttyctx);
1185 ttyctx.bg = bg;
1186
1187 /* Scroll into history if it is enabled. */
1188 if (s->grid->flags & GRID_HISTORY)
1189 grid_view_clear_history(s->grid, bg);
1190 else
1191 grid_view_clear(s->grid, 0, 0, sx, sy, bg);
1192
1193 screen_write_collect_clear(ctx, 0, sy);
1194 tty_write(tty_cmd_clearscreen, &ttyctx);
1195}
1196
1197/* Clear entire history. */
1198void
1199screen_write_clearhistory(struct screen_write_ctx *ctx)
1200{
1201 grid_clear_history(ctx->s->grid);
1202}
1203
1204/* Clear a collected line. */
1205static void
1206screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
1207{
1208 struct screen_write_collect_item *ci, *tmp;
1209 u_int i;
1210 size_t size;
1211
1212 for (i = y; i < y + n; i++) {
1213 if (TAILQ_EMPTY(&ctx->list[i].items))
1214 continue;
1215 size = 0;
1216 TAILQ_FOREACH_SAFE(ci, &ctx->list[i].items, entry, tmp) {
1217 size += ci->used;
1218 TAILQ_REMOVE(&ctx->list[i].items, ci, entry);
1219 free(ci);
1220 }
1221 ctx->skipped += size;
1222 log_debug("%s: dropped %zu bytes (line %u)", __func__, size, i);
1223 }
1224}
1225
1226/* Scroll collected lines up. */
1227static void
1228screen_write_collect_scroll(struct screen_write_ctx *ctx)
1229{
1230 struct screen *s = ctx->s;
1231 struct screen_write_collect_line *cl;
1232 u_int y;
1233
1234 log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1235 s->rupper, s->rlower);
1236
1237 screen_write_collect_clear(ctx, s->rupper, 1);
1238 for (y = s->rupper; y < s->rlower; y++) {
1239 cl = &ctx->list[y + 1];
1240 TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry);
1241 }
1242}
1243
1244/* Flush collected lines. */
1245static void
1246screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only)
1247{
1248 struct screen *s = ctx->s;
1249 struct screen_write_collect_item *ci, *tmp;
1250 u_int y, cx, cy, items = 0;
1251 struct tty_ctx ttyctx;
1252 size_t written = 0;
1253
1254 if (ctx->scrolled != 0) {
1255 log_debug("%s: scrolled %u (region %u-%u)", __func__,
1256 ctx->scrolled, s->rupper, s->rlower);
1257 if (ctx->scrolled > s->rlower - s->rupper + 1)
1258 ctx->scrolled = s->rlower - s->rupper + 1;
1259
1260 screen_write_initctx(ctx, &ttyctx);
1261 ttyctx.num = ctx->scrolled;
1262 ttyctx.bg = ctx->bg;
1263 tty_write(tty_cmd_scrollup, &ttyctx);
1264 }
1265 ctx->scrolled = 0;
1266 ctx->bg = 8;
1267
1268 if (scroll_only)
1269 return;
1270
1271 cx = s->cx; cy = s->cy;
1272 for (y = 0; y < screen_size_y(s); y++) {
1273 TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) {
1274 screen_write_set_cursor(ctx, ci->x, y);
1275 screen_write_initctx(ctx, &ttyctx);
1276 ttyctx.cell = &ci->gc;
1277 ttyctx.wrapped = ci->wrapped;
1278 ttyctx.ptr = ci->data;
1279 ttyctx.num = ci->used;
1280 tty_write(tty_cmd_cells, &ttyctx);
1281
1282 items++;
1283 written += ci->used;
1284
1285 TAILQ_REMOVE(&ctx->list[y].items, ci, entry);
1286 free(ci);
1287 }
1288 }
1289 s->cx = cx; s->cy = cy;
1290
1291 log_debug("%s: flushed %u items (%zu bytes)", __func__, items, written);
1292 ctx->written += written;
1293}
1294
1295/* Finish and store collected cells. */
1296void
1297screen_write_collect_end(struct screen_write_ctx *ctx)
1298{
1299 struct screen *s = ctx->s;
1300 struct screen_write_collect_item *ci = ctx->item;
1301 struct grid_cell gc;
1302 u_int xx;
1303
1304 if (ci->used == 0)
1305 return;
1306 ci->data[ci->used] = '\0';
1307
1308 ci->x = s->cx;
1309 TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry);
1310 ctx->item = xcalloc(1, sizeof *ctx->item);
1311
1312 log_debug("%s: %u %s (at %u,%u)", __func__, ci->used, ci->data, s->cx,
1313 s->cy);
1314
1315 if (s->cx != 0) {
1316 for (xx = s->cx; xx > 0; xx--) {
1317 grid_view_get_cell(s->grid, xx, s->cy, &gc);
1318 if (~gc.flags & GRID_FLAG_PADDING)
1319 break;
1320 grid_view_set_cell(s->grid, xx, s->cy,
1321 &grid_default_cell);
1322 }
1323 if (gc.data.width > 1) {
1324 grid_view_set_cell(s->grid, xx, s->cy,
1325 &grid_default_cell);
1326 }
1327 }
1328
1329 grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, ci->data, ci->used);
1330 screen_write_set_cursor(ctx, s->cx + ci->used, -1);
1331
1332 for (xx = s->cx; xx < screen_size_x(s); xx++) {
1333 grid_view_get_cell(s->grid, xx, s->cy, &gc);
1334 if (~gc.flags & GRID_FLAG_PADDING)
1335 break;
1336 grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
1337 }
1338}
1339
1340/* Write cell data, collecting if necessary. */
1341void
1342screen_write_collect_add(struct screen_write_ctx *ctx,
1343 const struct grid_cell *gc)
1344{
1345 struct screen *s = ctx->s;
1346 struct screen_write_collect_item *ci;
1347 u_int sx = screen_size_x(s);
1348 int collect;
1349
1350 /*
1351 * Don't need to check that the attributes and whatnot are still the
1352 * same - input_parse will end the collection when anything that isn't
1353 * a plain character is encountered.
1354 */
1355
1356 collect = 1;
1357 if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f)
1358 collect = 0;
1359 else if (gc->attr & GRID_ATTR_CHARSET)
1360 collect = 0;
1361 else if (~s->mode & MODE_WRAP)
1362 collect = 0;
1363 else if (s->mode & MODE_INSERT)
1364 collect = 0;
1365 else if (s->sel != NULL)
1366 collect = 0;
1367 if (!collect) {
1368 screen_write_collect_end(ctx);
1369 screen_write_collect_flush(ctx, 0);
1370 screen_write_cell(ctx, gc);
1371 return;
1372 }
1373 ctx->cells++;
1374
1375 if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx)
1376 screen_write_collect_end(ctx);
1377 ci = ctx->item; /* may have changed */
1378
1379 if (s->cx > sx - 1) {
1380 log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1381 ci->wrapped = 1;
1382 screen_write_linefeed(ctx, 1, 8);
1383 screen_write_set_cursor(ctx, 0, -1);
1384 }
1385
1386 if (ci->used == 0)
1387 memcpy(&ci->gc, gc, sizeof ci->gc);
1388 ci->data[ci->used++] = gc->data.data[0];
1389 if (ci->used == (sizeof ci->data) - 1)
1390 screen_write_collect_end(ctx);
1391}
1392
1393/* Write cell data. */
1394void
1395screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
1396{
1397 struct screen *s = ctx->s;
1398 struct grid *gd = s->grid;
1399 struct grid_line *gl;
1400 struct grid_cell_entry *gce;
1401 struct grid_cell tmp_gc, now_gc;
1402 struct tty_ctx ttyctx;
1403 u_int sx = screen_size_x(s), sy = screen_size_y(s);
1404 u_int width = gc->data.width, xx, last, cx, cy;
1405 int selected, skip = 1;
1406
1407 /* Ignore padding cells. */
1408 if (gc->flags & GRID_FLAG_PADDING)
1409 return;
1410 ctx->cells++;
1411
1412 /* If the width is zero, combine onto the previous character. */
1413 if (width == 0) {
1414 screen_write_collect_flush(ctx, 0);
1415 if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) {
1416 cx = s->cx; cy = s->cy;
1417 screen_write_set_cursor(ctx, xx, s->cy);
1418 screen_write_initctx(ctx, &ttyctx);
1419 ttyctx.cell = gc;
1420 tty_write(tty_cmd_cell, &ttyctx);
1421 s->cx = cx; s->cy = cy;
1422 }
1423 return;
1424 }
1425
1426 /* Flush any existing scrolling. */
1427 screen_write_collect_flush(ctx, 1);
1428
1429 /* If this character doesn't fit, ignore it. */
1430 if ((~s->mode & MODE_WRAP) &&
1431 width > 1 &&
1432 (width > sx || (s->cx != sx && s->cx > sx - width)))
1433 return;
1434
1435 /* If in insert mode, make space for the cells. */
1436 if (s->mode & MODE_INSERT) {
1437 grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8);
1438 skip = 0;
1439 }
1440
1441 /* Check this will fit on the current line and wrap if not. */
1442 if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
1443 log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1444 screen_write_linefeed(ctx, 1, 8);
1445 screen_write_set_cursor(ctx, 0, -1);
1446 screen_write_collect_flush(ctx, 1);
1447 }
1448
1449 /* Sanity check cursor position. */
1450 if (s->cx > sx - width || s->cy > sy - 1)
1451 return;
1452 screen_write_initctx(ctx, &ttyctx);
1453
1454 /* Handle overwriting of UTF-8 characters. */
1455 gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1456 if (gl->flags & GRID_LINE_EXTENDED) {
1457 grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
1458 if (screen_write_overwrite(ctx, &now_gc, width))
1459 skip = 0;
1460 }
1461
1462 /*
1463 * If the new character is UTF-8 wide, fill in padding cells. Have
1464 * already ensured there is enough room.
1465 */
1466 for (xx = s->cx + 1; xx < s->cx + width; xx++) {
1467 log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
1468 grid_view_set_cell(gd, xx, s->cy, &screen_write_pad_cell);
1469 skip = 0;
1470 }
1471
1472 /* If no change, do not draw. */
1473 if (skip) {
1474 if (s->cx >= gl->cellsize)
1475 skip = grid_cells_equal(gc, &grid_default_cell);
1476 else {
1477 gce = &gl->celldata[s->cx];
1478 if (gce->flags & GRID_FLAG_EXTENDED)
1479 skip = 0;
1480 else if (gc->flags != gce->flags)
1481 skip = 0;
1482 else if (gc->attr != gce->data.attr)
1483 skip = 0;
1484 else if (gc->fg != gce->data.fg)
1485 skip = 0;
1486 else if (gc->bg != gce->data.bg)
1487 skip = 0;
1488 else if (gc->data.width != 1)
1489 skip = 0;
1490 else if (gc->data.size != 1)
1491 skip = 0;
1492 else if (gce->data.data != gc->data.data[0])
1493 skip = 0;
1494 }
1495 }
1496
1497 /* Update the selected flag and set the cell. */
1498 selected = screen_check_selection(s, s->cx, s->cy);
1499 if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
1500 memcpy(&tmp_gc, gc, sizeof tmp_gc);
1501 tmp_gc.flags |= GRID_FLAG_SELECTED;
1502 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
1503 } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) {
1504 memcpy(&tmp_gc, gc, sizeof tmp_gc);
1505 tmp_gc.flags &= ~GRID_FLAG_SELECTED;
1506 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
1507 } else if (!skip)
1508 grid_view_set_cell(gd, s->cx, s->cy, gc);
1509 if (selected)
1510 skip = 0;
1511
1512 /*
1513 * Move the cursor. If not wrapping, stick at the last character and
1514 * replace it.
1515 */
1516 last = !(s->mode & MODE_WRAP);
1517 if (s->cx <= sx - last - width)
1518 screen_write_set_cursor(ctx, s->cx + width, -1);
1519 else
1520 screen_write_set_cursor(ctx, sx - last, -1);
1521
1522 /* Create space for character in insert mode. */
1523 if (s->mode & MODE_INSERT) {
1524 screen_write_collect_flush(ctx, 0);
1525 ttyctx.num = width;
1526 tty_write(tty_cmd_insertcharacter, &ttyctx);
1527 }
1528
1529 /* Write to the screen. */
1530 if (!skip) {
1531 if (selected) {
1532 screen_select_cell(s, &tmp_gc, gc);
1533 ttyctx.cell = &tmp_gc;
1534 } else
1535 ttyctx.cell = gc;
1536 tty_write(tty_cmd_cell, &ttyctx);
1537 ctx->written++;
1538 } else
1539 ctx->skipped++;
1540}
1541
1542/* Combine a UTF-8 zero-width character onto the previous. */
1543static const struct grid_cell *
1544screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud,
1545 u_int *xx)
1546{
1547 struct screen *s = ctx->s;
1548 struct grid *gd = s->grid;
1549 static struct grid_cell gc;
1550 u_int n;
1551
1552 /* Can't combine if at 0. */
1553 if (s->cx == 0)
1554 return (NULL);
1555
1556 /* Empty data is out. */
1557 if (ud->size == 0)
1558 fatalx("UTF-8 data empty");
1559
1560 /* Retrieve the previous cell. */
1561 for (n = 1; n <= s->cx; n++) {
1562 grid_view_get_cell(gd, s->cx - n, s->cy, &gc);
1563 if (~gc.flags & GRID_FLAG_PADDING)
1564 break;
1565 }
1566 if (n > s->cx)
1567 return (NULL);
1568 *xx = s->cx - n;
1569
1570 /* Check there is enough space. */
1571 if (gc.data.size + ud->size > sizeof gc.data.data)
1572 return (NULL);
1573
1574 log_debug("%s: %.*s onto %.*s at %u,%u", __func__, (int)ud->size,
1575 ud->data, (int)gc.data.size, gc.data.data, *xx, s->cy);
1576
1577 /* Append the data. */
1578 memcpy(gc.data.data + gc.data.size, ud->data, ud->size);
1579 gc.data.size += ud->size;
1580
1581 /* Set the new cell. */
1582 grid_view_set_cell(gd, *xx, s->cy, &gc);
1583
1584 return (&gc);
1585}
1586
1587/*
1588 * UTF-8 wide characters are a bit of an annoyance. They take up more than one
1589 * cell on the screen, so following cells must not be drawn by marking them as
1590 * padding.
1591 *
1592 * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
1593 * character, it is necessary to also overwrite any other cells which covered
1594 * by the same character.
1595 */
1596static int
1597screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
1598 u_int width)
1599{
1600 struct screen *s = ctx->s;
1601 struct grid *gd = s->grid;
1602 struct grid_cell tmp_gc;
1603 u_int xx;
1604 int done = 0;
1605
1606 if (gc->flags & GRID_FLAG_PADDING) {
1607 /*
1608 * A padding cell, so clear any following and leading padding
1609 * cells back to the character. Don't overwrite the current
1610 * cell as that happens later anyway.
1611 */
1612 xx = s->cx + 1;
1613 while (--xx > 0) {
1614 grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
1615 if (~tmp_gc.flags & GRID_FLAG_PADDING)
1616 break;
1617 log_debug("%s: padding at %u,%u", __func__, xx, s->cy);
1618 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1619 }
1620
1621 /* Overwrite the character at the start of this padding. */
1622 log_debug("%s: character at %u,%u", __func__, xx, s->cy);
1623 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1624 done = 1;
1625 }
1626
1627 /*
1628 * Overwrite any padding cells that belong to any UTF-8 characters
1629 * we'll be overwriting with the current character.
1630 */
1631 if (width != 1 ||
1632 gc->data.width != 1 ||
1633 gc->flags & GRID_FLAG_PADDING) {
1634 xx = s->cx + width - 1;
1635 while (++xx < screen_size_x(s)) {
1636 grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
1637 if (~tmp_gc.flags & GRID_FLAG_PADDING)
1638 break;
1639 log_debug("%s: overwrite at %u,%u", __func__, xx,
1640 s->cy);
1641 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1642 done = 1;
1643 }
1644 }
1645
1646 return (done);
1647}
1648
1649/* Set external clipboard. */
1650void
1651screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len)
1652{
1653 struct tty_ctx ttyctx;
1654
1655 screen_write_initctx(ctx, &ttyctx);
1656 ttyctx.ptr = str;
1657 ttyctx.num = len;
1658
1659 tty_write(tty_cmd_setselection, &ttyctx);
1660}
1661
1662/* Write unmodified string. */
1663void
1664screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len)
1665{
1666 struct tty_ctx ttyctx;
1667
1668 screen_write_initctx(ctx, &ttyctx);
1669 ttyctx.ptr = str;
1670 ttyctx.num = len;
1671
1672 tty_write(tty_cmd_rawstring, &ttyctx);
1673}
1674