xref: /openbsd-src/usr.bin/tmux/screen-write.c (revision ac9b4aacc1da35008afea06a5d23c2f2dea9b93e)
1 /* $OpenBSD: screen-write.c,v 1.56 2012/07/10 11:53:01 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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 void	screen_write_initctx(struct screen_write_ctx *, struct tty_ctx *, int);
27 void	screen_write_overwrite(struct screen_write_ctx *, u_int);
28 int	screen_write_combine(
29 	    struct screen_write_ctx *, const struct utf8_data *);
30 
31 /* Initialise writing with a window. */
32 void
33 screen_write_start(
34     struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s)
35 {
36 	ctx->wp = wp;
37 	if (wp != NULL && s == NULL)
38 		ctx->s = wp->screen;
39 	else
40 		ctx->s = s;
41 }
42 
43 /* Finish writing. */
44 /* ARGSUSED */
45 void
46 screen_write_stop(unused struct screen_write_ctx *ctx)
47 {
48 }
49 
50 
51 /* Reset screen state. */
52 void
53 screen_write_reset(struct screen_write_ctx *ctx)
54 {
55 	screen_reset_tabs(ctx->s);
56 
57 	screen_write_scrollregion(ctx, 0, screen_size_y(ctx->s) - 1);
58 
59 	screen_write_insertmode(ctx, 0);
60 	screen_write_kcursormode(ctx, 0);
61 	screen_write_kkeypadmode(ctx, 0);
62 	screen_write_mousemode_off(ctx);
63 
64 	screen_write_clearscreen(ctx);
65 	screen_write_cursormove(ctx, 0, 0);
66 }
67 
68 /* Write character. */
69 void
70 screen_write_putc(
71     struct screen_write_ctx *ctx, struct grid_cell *gc, u_char ch)
72 {
73 	gc->data = ch;
74 	screen_write_cell(ctx, gc, NULL);
75 }
76 
77 /* Calculate string length, with embedded formatting. */
78 size_t printflike2
79 screen_write_cstrlen(int utf8flag, const char *fmt, ...)
80 {
81 	va_list	ap;
82 	char   *msg, *msg2, *ptr, *ptr2;
83 	size_t	size;
84 
85 	va_start(ap, fmt);
86 	xvasprintf(&msg, fmt, ap);
87 	va_end(ap);
88 	msg2 = xmalloc(strlen(msg) + 1);
89 
90 	ptr = msg;
91 	ptr2 = msg2;
92 	while (*ptr != '\0') {
93 		if (ptr[0] == '#' && ptr[1] == '[') {
94 			while (*ptr != ']' && *ptr != '\0')
95 				ptr++;
96 			if (*ptr == ']')
97 				ptr++;
98 			continue;
99 		}
100 		*ptr2++ = *ptr++;
101 	}
102 	*ptr2 = '\0';
103 
104 	size = screen_write_strlen(utf8flag, "%s", msg2);
105 
106 	free(msg);
107 	free(msg2);
108 
109 	return (size);
110 }
111 
112 /* Calculate string length. */
113 size_t printflike2
114 screen_write_strlen(int utf8flag, const char *fmt, ...)
115 {
116 	va_list			ap;
117 	char   	       	       *msg;
118 	struct utf8_data	utf8data;
119 	u_char 	      	       *ptr;
120 	size_t			left, size = 0;
121 
122 	va_start(ap, fmt);
123 	xvasprintf(&msg, fmt, ap);
124 	va_end(ap);
125 
126 	ptr = msg;
127 	while (*ptr != '\0') {
128 		if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) {
129 			ptr++;
130 
131 			left = strlen(ptr);
132 			if (left < utf8data.size - 1)
133 				break;
134 			while (utf8_append(&utf8data, *ptr))
135 				ptr++;
136 			ptr++;
137 
138 			size += utf8data.width;
139 		} else {
140 			size++;
141 			ptr++;
142 		}
143 	}
144 
145 	free(msg);
146 	return (size);
147 }
148 
149 /* Write simple string (no UTF-8 or maximum length). */
150 void printflike3
151 screen_write_puts(
152     struct screen_write_ctx *ctx, struct grid_cell *gc, const char *fmt, ...)
153 {
154 	va_list	ap;
155 
156 	va_start(ap, fmt);
157 	screen_write_vnputs(ctx, -1, gc, 0, fmt, ap);
158 	va_end(ap);
159 }
160 
161 /* Write string with length limit (-1 for unlimited). */
162 void printflike5
163 screen_write_nputs(struct screen_write_ctx *ctx,
164     ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...)
165 {
166 	va_list	ap;
167 
168 	va_start(ap, fmt);
169 	screen_write_vnputs(ctx, maxlen, gc, utf8flag, fmt, ap);
170 	va_end(ap);
171 }
172 
173 void
174 screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
175     struct grid_cell *gc, int utf8flag, const char *fmt, va_list ap)
176 {
177 	char   		       *msg;
178 	struct utf8_data	utf8data;
179 	u_char 		       *ptr;
180 	size_t		 	left, size = 0;
181 
182 	xvasprintf(&msg, fmt, ap);
183 
184 	ptr = msg;
185 	while (*ptr != '\0') {
186 		if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) {
187 			ptr++;
188 
189 			left = strlen(ptr);
190 			if (left < utf8data.size - 1)
191 				break;
192 			while (utf8_append(&utf8data, *ptr))
193 				ptr++;
194 			ptr++;
195 
196 			if (maxlen > 0 &&
197 			    size + utf8data.width > (size_t) maxlen) {
198 				while (size < (size_t) maxlen) {
199 					screen_write_putc(ctx, gc, ' ');
200 					size++;
201 				}
202 				break;
203 			}
204 			size += utf8data.width;
205 
206 			gc->flags |= GRID_FLAG_UTF8;
207 			screen_write_cell(ctx, gc, &utf8data);
208 			gc->flags &= ~GRID_FLAG_UTF8;
209 		} else {
210 			if (maxlen > 0 && size + 1 > (size_t) maxlen)
211 				break;
212 
213 			size++;
214 			screen_write_putc(ctx, gc, *ptr);
215 			ptr++;
216 		}
217 	}
218 
219 	free(msg);
220 }
221 
222 /* Write string, similar to nputs, but with embedded formatting (#[]). */
223 void printflike5
224 screen_write_cnputs(struct screen_write_ctx *ctx,
225     ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...)
226 {
227 	struct grid_cell	 lgc;
228 	struct utf8_data	 utf8data;
229 	va_list			 ap;
230 	char			*msg;
231 	u_char 			*ptr, *last;
232 	size_t			 left, size = 0;
233 
234 	va_start(ap, fmt);
235 	xvasprintf(&msg, fmt, ap);
236 	va_end(ap);
237 
238 	memcpy(&lgc, gc, sizeof lgc);
239 
240 	ptr = msg;
241 	while (*ptr != '\0') {
242 		if (ptr[0] == '#' && ptr[1] == '[') {
243 			ptr += 2;
244 			last = ptr + strcspn(ptr, "]");
245 			if (*last == '\0') {
246 				/* No ]. Not much point in doing anything. */
247 				break;
248 			}
249 			*last = '\0';
250 
251 			screen_write_parsestyle(gc, &lgc, ptr);
252 			ptr = last + 1;
253 			continue;
254 		}
255 
256 		if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) {
257 			ptr++;
258 
259 			left = strlen(ptr);
260 			if (left < utf8data.size - 1)
261 				break;
262 			while (utf8_append(&utf8data, *ptr))
263 				ptr++;
264 			ptr++;
265 
266 			if (maxlen > 0 &&
267 			    size + utf8data.width > (size_t) maxlen) {
268 				while (size < (size_t) maxlen) {
269 					screen_write_putc(ctx, gc, ' ');
270 					size++;
271 				}
272 				break;
273 			}
274 			size += utf8data.width;
275 
276 			lgc.flags |= GRID_FLAG_UTF8;
277 			screen_write_cell(ctx, &lgc, &utf8data);
278 			lgc.flags &= ~GRID_FLAG_UTF8;
279 		} else {
280 			if (maxlen > 0 && size + 1 > (size_t) maxlen)
281 				break;
282 
283 			size++;
284 			screen_write_putc(ctx, &lgc, *ptr);
285 			ptr++;
286 		}
287 	}
288 
289 	free(msg);
290 }
291 
292 /* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". */
293 void
294 screen_write_parsestyle(
295     struct grid_cell *defgc, struct grid_cell *gc, const char *in)
296 {
297 	const char	delimiters[] = " ,";
298 	char		tmp[32];
299 	int		val;
300 	size_t		end;
301 	u_char		fg, bg, attr, flags;
302 
303 	if (*in == '\0')
304 		return;
305 	if (strchr(delimiters, in[strlen(in) - 1]) != NULL)
306 		return;
307 
308 	fg = gc->fg;
309 	bg = gc->bg;
310 	attr = gc->attr;
311 	flags = gc->flags;
312 	do {
313 		end = strcspn(in, delimiters);
314 		if (end > (sizeof tmp) - 1)
315 			return;
316 		memcpy(tmp, in, end);
317 		tmp[end] = '\0';
318 
319 		if (strcasecmp(tmp, "default") == 0) {
320 			fg = defgc->fg;
321 			bg = defgc->bg;
322 			attr = defgc->attr;
323 		} else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) {
324 			if ((val = colour_fromstring(tmp + 3)) == -1)
325 				return;
326 			if (*in == 'f' || *in == 'F') {
327 				if (val != 8) {
328 					if (val & 0x100) {
329 						flags |= GRID_FLAG_FG256;
330 						val &= ~0x100;
331 					} else
332 						flags &= ~GRID_FLAG_FG256;
333 					fg = val;
334 				} else
335 					fg = defgc->fg;
336 			} else if (*in == 'b' || *in == 'B') {
337 				if (val != 8) {
338 					if (val & 0x100) {
339 						flags |= GRID_FLAG_BG256;
340 						val &= ~0x100;
341 					} else
342 						flags &= ~GRID_FLAG_BG256;
343 					bg = val;
344 				} else
345 					bg = defgc->bg;
346 			} else
347 				return;
348 		} else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) {
349 			if ((val = attributes_fromstring(tmp + 2)) == -1)
350 				return;
351 			attr &= ~val;
352 		} else {
353 			if ((val = attributes_fromstring(tmp)) == -1)
354 				return;
355 			attr |= val;
356 		}
357 
358 		in += end + strspn(in + end, delimiters);
359 	} while (*in != '\0');
360 	gc->fg = fg;
361 	gc->bg = bg;
362 	gc->attr = attr;
363 	gc->flags = flags;
364 }
365 
366 /* Copy from another screen. */
367 void
368 screen_write_copy(struct screen_write_ctx *ctx,
369     struct screen *src, u_int px, u_int py, u_int nx, u_int ny)
370 {
371 	struct screen		*s = ctx->s;
372 	struct grid		*gd = src->grid;
373 	struct grid_line	*gl;
374 	const struct grid_cell	*gc;
375 	const struct grid_utf8	*gu;
376 	struct utf8_data	 utf8data;
377 	u_int		 	 xx, yy, cx, cy, ax, bx;
378 
379 	cx = s->cx;
380 	cy = s->cy;
381 	for (yy = py; yy < py + ny; yy++) {
382 		gl = &gd->linedata[yy];
383 		if (yy < gd->hsize + gd->sy) {
384 			/*
385 			 * Find start and end position and copy between
386 			 * them. Limit to the real end of the line then use a
387 			 * clear EOL only if copying to the end, otherwise
388 			 * could overwrite whatever is there already.
389 			 */
390 			if (px > gl->cellsize)
391 				ax = gl->cellsize;
392 			else
393 				ax = px;
394 			if (px + nx == gd->sx && px + nx > gl->cellsize)
395 				bx = gl->cellsize;
396 			else
397 				bx = px + nx;
398 
399 			for (xx = ax; xx < bx; xx++) {
400 				if (xx >= gl->cellsize)
401 					gc = &grid_default_cell;
402 				else
403 					gc = &gl->celldata[xx];
404 				if (!(gc->flags & GRID_FLAG_UTF8)) {
405 					screen_write_cell(ctx, gc, NULL);
406 					continue;
407 				}
408 				/* Reinject the UTF-8 sequence. */
409 				gu = &gl->utf8data[xx];
410 				utf8data.size = grid_utf8_copy(
411 				    gu, utf8data.data, sizeof utf8data.data);
412 				utf8data.width = gu->width;
413 				screen_write_cell(ctx, gc, &utf8data);
414 			}
415 			if (px + nx == gd->sx && px + nx > gl->cellsize)
416 				screen_write_clearendofline(ctx);
417 		} else
418 			screen_write_clearline(ctx);
419 		cy++;
420 		screen_write_cursormove(ctx, cx, cy);
421 	}
422 }
423 
424 /* Set up context for TTY command. */
425 void
426 screen_write_initctx(
427     struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, int save_last)
428 {
429 	struct screen		*s = ctx->s;
430 	struct grid		*gd = s->grid;
431 	const struct grid_cell	*gc;
432 	const struct grid_utf8	*gu;
433 	u_int			 xx;
434 
435 	ttyctx->wp = ctx->wp;
436 
437 	ttyctx->ocx = s->cx;
438 	ttyctx->ocy = s->cy;
439 
440 	ttyctx->orlower = s->rlower;
441 	ttyctx->orupper = s->rupper;
442 
443 	if (!save_last)
444 		return;
445 
446 	/* Save the last cell on the screen. */
447 	gc = &grid_default_cell;
448 	for (xx = 1; xx <= screen_size_x(s); xx++) {
449 		gc = grid_view_peek_cell(gd, screen_size_x(s) - xx, s->cy);
450 		if (!(gc->flags & GRID_FLAG_PADDING))
451 			break;
452 	}
453 	ttyctx->last_width = xx;
454 	memcpy(&ttyctx->last_cell, gc, sizeof ttyctx->last_cell);
455 	if (gc->flags & GRID_FLAG_UTF8) {
456 		gu = grid_view_peek_utf8(gd, screen_size_x(s) - xx, s->cy);
457 		memcpy(&ttyctx->last_utf8, gu, sizeof ttyctx->last_utf8);
458 	}
459 }
460 
461 /* Cursor up by ny. */
462 void
463 screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
464 {
465 	struct screen	*s = ctx->s;
466 
467 	if (ny == 0)
468 		ny = 1;
469 
470 	if (s->cy < s->rupper) {
471 		/* Above region. */
472 		if (ny > s->cy)
473 			ny = s->cy;
474 	} else {
475 		/* Below region. */
476 		if (ny > s->cy - s->rupper)
477 			ny = s->cy - s->rupper;
478 	}
479 	if (ny == 0)
480 		return;
481 
482 	s->cy -= ny;
483 }
484 
485 /* Cursor down by ny. */
486 void
487 screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
488 {
489 	struct screen	*s = ctx->s;
490 
491 	if (ny == 0)
492 		ny = 1;
493 
494 	if (s->cy > s->rlower) {
495 		/* Below region. */
496 		if (ny > screen_size_y(s) - 1 - s->cy)
497 			ny = screen_size_y(s) - 1 - s->cy;
498 	} else {
499 		/* Above region. */
500 		if (ny > s->rlower - s->cy)
501 			ny = s->rlower - s->cy;
502 	}
503 	if (ny == 0)
504 		return;
505 
506 	s->cy += ny;
507 }
508 
509 /* Cursor right by nx.  */
510 void
511 screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
512 {
513 	struct screen	*s = ctx->s;
514 
515 	if (nx == 0)
516 		nx = 1;
517 
518 	if (nx > screen_size_x(s) - 1 - s->cx)
519 		nx = screen_size_x(s) - 1 - s->cx;
520 	if (nx == 0)
521 		return;
522 
523 	s->cx += nx;
524 }
525 
526 /* Cursor left by nx. */
527 void
528 screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx)
529 {
530 	struct screen	*s = ctx->s;
531 
532 	if (nx == 0)
533 		nx = 1;
534 
535 	if (nx > s->cx)
536 		nx = s->cx;
537 	if (nx == 0)
538 		return;
539 
540 	s->cx -= nx;
541 }
542 
543 /* Backspace; cursor left unless at start of wrapped line when can move up. */
544 void
545 screen_write_backspace(struct screen_write_ctx *ctx)
546 {
547 	struct screen		*s = ctx->s;
548 	struct grid_line	*gl;
549 
550 	if (s->cx == 0) {
551 		if (s->cy == 0)
552 			return;
553 		gl = &s->grid->linedata[s->grid->hsize + s->cy - 1];
554 		if (gl->flags & GRID_LINE_WRAPPED) {
555 			s->cy--;
556 			s->cx = screen_size_x(s) - 1;
557 		}
558 	} else
559 		s->cx--;
560 }
561 
562 /* VT100 alignment test. */
563 void
564 screen_write_alignmenttest(struct screen_write_ctx *ctx)
565 {
566 	struct screen		*s = ctx->s;
567 	struct tty_ctx	 	 ttyctx;
568 	struct grid_cell       	 gc;
569 	u_int			 xx, yy;
570 
571 	screen_write_initctx(ctx, &ttyctx, 0);
572 
573 	memcpy(&gc, &grid_default_cell, sizeof gc);
574 	gc.data = 'E';
575 
576 	for (yy = 0; yy < screen_size_y(s); yy++) {
577 		for (xx = 0; xx < screen_size_x(s); xx++)
578 			grid_view_set_cell(s->grid, xx, yy, &gc);
579 	}
580 
581 	s->cx = 0;
582 	s->cy = 0;
583 
584 	s->rupper = 0;
585 
586 	s->rlower = screen_size_y(s) - 1;
587 
588 	tty_write(tty_cmd_alignmenttest, &ttyctx);
589 }
590 
591 /* Insert nx characters. */
592 void
593 screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx)
594 {
595 	struct screen	*s = ctx->s;
596 	struct tty_ctx	 ttyctx;
597 
598 	if (nx == 0)
599 		nx = 1;
600 
601 	if (nx > screen_size_x(s) - s->cx)
602 		nx = screen_size_x(s) - s->cx;
603 	if (nx == 0)
604 		return;
605 
606 	screen_write_initctx(ctx, &ttyctx, 0);
607 
608 	if (s->cx <= screen_size_x(s) - 1)
609 		grid_view_insert_cells(s->grid, s->cx, s->cy, nx);
610 
611 	ttyctx.num = nx;
612 	tty_write(tty_cmd_insertcharacter, &ttyctx);
613 }
614 
615 /* Delete nx characters. */
616 void
617 screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx)
618 {
619 	struct screen	*s = ctx->s;
620 	struct tty_ctx	 ttyctx;
621 
622 	if (nx == 0)
623 		nx = 1;
624 
625 	if (nx > screen_size_x(s) - s->cx)
626 		nx = screen_size_x(s) - s->cx;
627 	if (nx == 0)
628 		return;
629 
630 	screen_write_initctx(ctx, &ttyctx, 0);
631 
632 	if (s->cx <= screen_size_x(s) - 1)
633 		grid_view_delete_cells(s->grid, s->cx, s->cy, nx);
634 
635 	ttyctx.num = nx;
636 	tty_write(tty_cmd_deletecharacter, &ttyctx);
637 }
638 
639 /* Insert ny lines. */
640 void
641 screen_write_insertline(struct screen_write_ctx *ctx, u_int ny)
642 {
643 	struct screen	*s = ctx->s;
644 	struct tty_ctx	 ttyctx;
645 
646 	if (ny == 0)
647 		ny = 1;
648 
649 	if (s->cy < s->rupper || s->cy > s->rlower) {
650 		if (ny > screen_size_y(s) - s->cy)
651 			ny = screen_size_y(s) - s->cy;
652 		if (ny == 0)
653 			return;
654 
655 		screen_write_initctx(ctx, &ttyctx, 0);
656 
657 		grid_view_insert_lines(s->grid, s->cy, ny);
658 
659 		ttyctx.num = ny;
660 		tty_write(tty_cmd_insertline, &ttyctx);
661 		return;
662 	}
663 
664 	if (ny > s->rlower + 1 - s->cy)
665 		ny = s->rlower + 1 - s->cy;
666 	if (ny == 0)
667 		return;
668 
669 	screen_write_initctx(ctx, &ttyctx, 0);
670 
671 	if (s->cy < s->rupper || s->cy > s->rlower)
672 		grid_view_insert_lines(s->grid, s->cy, ny);
673 	else
674 		grid_view_insert_lines_region(s->grid, s->rlower, s->cy, ny);
675 
676 	ttyctx.num = ny;
677 	tty_write(tty_cmd_insertline, &ttyctx);
678 }
679 
680 /* Delete ny lines. */
681 void
682 screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny)
683 {
684 	struct screen	*s = ctx->s;
685 	struct tty_ctx	 ttyctx;
686 
687 	if (ny == 0)
688 		ny = 1;
689 
690 	if (s->cy < s->rupper || s->cy > s->rlower) {
691 		if (ny > screen_size_y(s) - s->cy)
692 			ny = screen_size_y(s) - s->cy;
693 		if (ny == 0)
694 			return;
695 
696 		screen_write_initctx(ctx, &ttyctx, 0);
697 
698 		grid_view_delete_lines(s->grid, s->cy, ny);
699 
700 		ttyctx.num = ny;
701 		tty_write(tty_cmd_deleteline, &ttyctx);
702 		return;
703 	}
704 
705 	if (ny > s->rlower + 1 - s->cy)
706 		ny = s->rlower + 1 - s->cy;
707 	if (ny == 0)
708 		return;
709 
710 	screen_write_initctx(ctx, &ttyctx, 0);
711 
712 	if (s->cy < s->rupper || s->cy > s->rlower)
713 		grid_view_delete_lines(s->grid, s->cy, ny);
714 	else
715 		grid_view_delete_lines_region(s->grid, s->rlower, s->cy, ny);
716 
717 	ttyctx.num = ny;
718 	tty_write(tty_cmd_deleteline, &ttyctx);
719 }
720 
721 /* Clear line at cursor. */
722 void
723 screen_write_clearline(struct screen_write_ctx *ctx)
724 {
725 	struct screen	*s = ctx->s;
726 	struct tty_ctx	 ttyctx;
727 
728 	screen_write_initctx(ctx, &ttyctx, 0);
729 
730 	grid_view_clear(s->grid, 0, s->cy, screen_size_x(s), 1);
731 
732 	tty_write(tty_cmd_clearline, &ttyctx);
733 }
734 
735 /* Clear to end of line from cursor. */
736 void
737 screen_write_clearendofline(struct screen_write_ctx *ctx)
738 {
739 	struct screen	*s = ctx->s;
740 	struct tty_ctx	 ttyctx;
741 	u_int		 sx;
742 
743 	screen_write_initctx(ctx, &ttyctx, 0);
744 
745 	sx = screen_size_x(s);
746 
747 	if (s->cx <= sx - 1)
748 		grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1);
749 
750 	tty_write(tty_cmd_clearendofline, &ttyctx);
751 }
752 
753 /* Clear to start of line from cursor. */
754 void
755 screen_write_clearstartofline(struct screen_write_ctx *ctx)
756 {
757 	struct screen	*s = ctx->s;
758 	struct tty_ctx	 ttyctx;
759 	u_int		 sx;
760 
761 	screen_write_initctx(ctx, &ttyctx, 0);
762 
763 	sx = screen_size_x(s);
764 
765 	if (s->cx > sx - 1)
766 		grid_view_clear(s->grid, 0, s->cy, sx, 1);
767 	else
768 		grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1);
769 
770 	tty_write(tty_cmd_clearstartofline, &ttyctx);
771 }
772 
773 /* Move cursor to px,py.  */
774 void
775 screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py)
776 {
777 	struct screen	*s = ctx->s;
778 
779 	if (px > screen_size_x(s) - 1)
780 		px = screen_size_x(s) - 1;
781 	if (py > screen_size_y(s) - 1)
782 		py = screen_size_y(s) - 1;
783 
784 	s->cx = px;
785 	s->cy = py;
786 }
787 
788 /* Set cursor mode. */
789 void
790 screen_write_cursormode(struct screen_write_ctx *ctx, int state)
791 {
792 	struct screen	*s = ctx->s;
793 
794 	if (state)
795 		s->mode |= MODE_CURSOR;
796 	else
797 		s->mode &= ~MODE_CURSOR;
798 }
799 
800 /* Reverse index (up with scroll).  */
801 void
802 screen_write_reverseindex(struct screen_write_ctx *ctx)
803 {
804 	struct screen	*s = ctx->s;
805 	struct tty_ctx	 ttyctx;
806 
807 	screen_write_initctx(ctx, &ttyctx, 0);
808 
809 	if (s->cy == s->rupper)
810 		grid_view_scroll_region_down(s->grid, s->rupper, s->rlower);
811 	else if (s->cy > 0)
812 		s->cy--;
813 
814 	tty_write(tty_cmd_reverseindex, &ttyctx);
815 }
816 
817 /* Set scroll region. */
818 void
819 screen_write_scrollregion(
820     struct screen_write_ctx *ctx, u_int rupper, u_int rlower)
821 {
822 	struct screen	*s = ctx->s;
823 
824 	if (rupper > screen_size_y(s) - 1)
825 		rupper = screen_size_y(s) - 1;
826 	if (rlower > screen_size_y(s) - 1)
827 		rlower = screen_size_y(s) - 1;
828 	if (rupper >= rlower)	/* cannot be one line */
829 		return;
830 
831 	/* Cursor moves to top-left. */
832 	s->cx = 0;
833 	s->cy = 0;
834 
835 	s->rupper = rupper;
836 	s->rlower = rlower;
837 }
838 
839 /* Set insert mode. */
840 void
841 screen_write_insertmode(struct screen_write_ctx *ctx, int state)
842 {
843 	struct screen	*s = ctx->s;
844 
845 	if (state)
846 		s->mode |= MODE_INSERT;
847 	else
848 		s->mode &= ~MODE_INSERT;
849 }
850 
851 /* Set UTF-8 mouse mode.  */
852 void
853 screen_write_utf8mousemode(struct screen_write_ctx *ctx, int state)
854 {
855 	struct screen	*s = ctx->s;
856 
857 	if (state)
858 		s->mode |= MODE_MOUSE_UTF8;
859 	else
860 		s->mode &= ~MODE_MOUSE_UTF8;
861 }
862 
863 /* Set mouse mode off. */
864 void
865 screen_write_mousemode_off(struct screen_write_ctx *ctx)
866 {
867 	struct screen	*s = ctx->s;
868 
869 	s->mode &= ~ALL_MOUSE_MODES;
870 }
871 
872 /* Set mouse mode on. */
873 void
874 screen_write_mousemode_on(struct screen_write_ctx *ctx, int mode)
875 {
876 	struct screen	*s = ctx->s;
877 
878 	s->mode &= ~ALL_MOUSE_MODES;
879 	s->mode |= mode;
880 }
881 
882 /* Set bracketed paste mode. */
883 void
884 screen_write_bracketpaste(struct screen_write_ctx *ctx, int state)
885 {
886 	struct screen	*s = ctx->s;
887 
888 	if (state)
889 		s->mode |= MODE_BRACKETPASTE;
890 	else
891 		s->mode &= ~MODE_BRACKETPASTE;
892 }
893 
894 /* Line feed. */
895 void
896 screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped)
897 {
898 	struct screen		*s = ctx->s;
899 	struct grid_line	*gl;
900 	struct tty_ctx	 	 ttyctx;
901 
902 	screen_write_initctx(ctx, &ttyctx, 0);
903 
904 	gl = &s->grid->linedata[s->grid->hsize + s->cy];
905 	if (wrapped)
906 		gl->flags |= GRID_LINE_WRAPPED;
907 	else
908 		gl->flags &= ~GRID_LINE_WRAPPED;
909 
910 	if (s->cy == s->rlower)
911 		grid_view_scroll_region_up(s->grid, s->rupper, s->rlower);
912 	else if (s->cy < screen_size_y(s) - 1)
913 		s->cy++;
914 
915 	ttyctx.num = wrapped;
916 	tty_write(tty_cmd_linefeed, &ttyctx);
917 }
918 
919 /* Carriage return (cursor to start of line). */
920 void
921 screen_write_carriagereturn(struct screen_write_ctx *ctx)
922 {
923 	struct screen	*s = ctx->s;
924 
925 	s->cx = 0;
926 }
927 
928 /* Set keypad cursor keys mode. */
929 void
930 screen_write_kcursormode(struct screen_write_ctx *ctx, int state)
931 {
932 	struct screen	*s = ctx->s;
933 
934 	if (state)
935 		s->mode |= MODE_KCURSOR;
936 	else
937 		s->mode &= ~MODE_KCURSOR;
938 }
939 
940 /* Set keypad number keys mode. */
941 void
942 screen_write_kkeypadmode(struct screen_write_ctx *ctx, int state)
943 {
944 	struct screen	*s = ctx->s;
945 
946 	if (state)
947 		s->mode |= MODE_KKEYPAD;
948 	else
949 		s->mode &= ~MODE_KKEYPAD;
950 }
951 
952 /* Clear to end of screen from cursor. */
953 void
954 screen_write_clearendofscreen(struct screen_write_ctx *ctx)
955 {
956 	struct screen	*s = ctx->s;
957 	struct tty_ctx	 ttyctx;
958 	u_int		 sx, sy;
959 
960 	screen_write_initctx(ctx, &ttyctx, 0);
961 
962 	sx = screen_size_x(s);
963 	sy = screen_size_y(s);
964 
965 	/* Scroll into history if it is enabled and clearing entire screen. */
966 	if (s->cy == 0 && s->grid->flags & GRID_HISTORY)
967 		grid_view_clear_history(s->grid);
968 	else {
969 		if (s->cx <= sx - 1)
970 			grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1);
971 		grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1));
972 	}
973 
974 	tty_write(tty_cmd_clearendofscreen, &ttyctx);
975 }
976 
977 /* Clear to start of screen. */
978 void
979 screen_write_clearstartofscreen(struct screen_write_ctx *ctx)
980 {
981 	struct screen	*s = ctx->s;
982 	struct tty_ctx	 ttyctx;
983 	u_int		 sx;
984 
985 	screen_write_initctx(ctx, &ttyctx, 0);
986 
987 	sx = screen_size_x(s);
988 
989 	if (s->cy > 0)
990 		grid_view_clear(s->grid, 0, 0, sx, s->cy);
991 	if (s->cx > sx - 1)
992 		grid_view_clear(s->grid, 0, s->cy, sx, 1);
993 	else
994 		grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1);
995 
996 	tty_write(tty_cmd_clearstartofscreen, &ttyctx);
997 }
998 
999 /* Clear entire screen. */
1000 void
1001 screen_write_clearscreen(struct screen_write_ctx *ctx)
1002 {
1003 	struct screen	*s = ctx->s;
1004 	struct tty_ctx	 ttyctx;
1005 
1006 	screen_write_initctx(ctx, &ttyctx, 0);
1007 
1008 	/* Scroll into history if it is enabled. */
1009 	if (s->grid->flags & GRID_HISTORY)
1010 		grid_view_clear_history(s->grid);
1011 	else {
1012 		grid_view_clear(
1013 		    s->grid, 0, 0, screen_size_x(s), screen_size_y(s));
1014 	}
1015 
1016 	tty_write(tty_cmd_clearscreen, &ttyctx);
1017 }
1018 
1019 /* Clear entire history. */
1020 void
1021 screen_write_clearhistory(struct screen_write_ctx *ctx)
1022 {
1023 	struct screen	*s = ctx->s;
1024 	struct grid	*gd = s->grid;
1025 
1026 	grid_move_lines(gd, 0, gd->hsize, gd->sy);
1027 	gd->hsize = 0;
1028 }
1029 
1030 /* Write cell data. */
1031 void
1032 screen_write_cell(struct screen_write_ctx *ctx,
1033     const struct grid_cell *gc, const struct utf8_data *utf8data)
1034 {
1035 	struct screen		*s = ctx->s;
1036 	struct grid		*gd = s->grid;
1037 	struct tty_ctx		 ttyctx;
1038 	struct grid_utf8	 gu;
1039 	u_int		 	 width, xx;
1040 	struct grid_cell 	 tmp_gc, *tmp_gcp;
1041 	int			 insert = 0;
1042 
1043 	/* Ignore padding. */
1044 	if (gc->flags & GRID_FLAG_PADDING)
1045 		return;
1046 
1047 	/* Find character width. */
1048 	if (gc->flags & GRID_FLAG_UTF8)
1049 		width = utf8data->width;
1050 	else
1051 		width = 1;
1052 
1053 	/*
1054 	 * If this is a wide character and there is no room on the screen, for
1055 	 * the entire character, don't print it.
1056 	 */
1057 	if (!(s->mode & MODE_WRAP)
1058 	    && (width > 1 && (width > screen_size_x(s) ||
1059 		(s->cx != screen_size_x(s)
1060 		 && s->cx > screen_size_x(s) - width))))
1061 		return;
1062 
1063 	/*
1064 	 * If the width is zero, combine onto the previous character, if
1065 	 * there is space.
1066 	 */
1067 	if (width == 0) {
1068 		if (screen_write_combine(ctx, utf8data) == 0) {
1069 			screen_write_initctx(ctx, &ttyctx, 0);
1070 			tty_write(tty_cmd_utf8character, &ttyctx);
1071 		}
1072 		return;
1073 	}
1074 
1075 	/* Initialise the redraw context, saving the last cell. */
1076 	screen_write_initctx(ctx, &ttyctx, 1);
1077 
1078 	/* If in insert mode, make space for the cells. */
1079 	if ((s->mode & MODE_INSERT) && s->cx <= screen_size_x(s) - width) {
1080 		xx = screen_size_x(s) - s->cx - width;
1081 		grid_move_cells(s->grid, s->cx + width, s->cx, s->cy, xx);
1082 		insert = 1;
1083 	}
1084 
1085 	/* Check this will fit on the current line and wrap if not. */
1086 	if ((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) {
1087 		screen_write_linefeed(ctx, 1);
1088 		s->cx = 0;	/* carriage return */
1089 	}
1090 
1091 	/* Sanity checks. */
1092 	if (((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width)
1093 	    || s->cy > screen_size_y(s) - 1)
1094 		return;
1095 
1096 	/* Handle overwriting of UTF-8 characters. */
1097 	screen_write_overwrite(ctx, width);
1098 
1099 	/*
1100 	 * If the new character is UTF-8 wide, fill in padding cells. Have
1101 	 * already ensured there is enough room.
1102 	 */
1103 	for (xx = s->cx + 1; xx < s->cx + width; xx++) {
1104 		tmp_gcp = grid_view_get_cell(gd, xx, s->cy);
1105 		if (tmp_gcp != NULL)
1106 			tmp_gcp->flags |= GRID_FLAG_PADDING;
1107 	}
1108 
1109 	/* Set the cell. */
1110 	grid_view_set_cell(gd, s->cx, s->cy, gc);
1111 	if (gc->flags & GRID_FLAG_UTF8) {
1112 		/* Construct UTF-8 and write it. */
1113 		grid_utf8_set(&gu, utf8data);
1114 		grid_view_set_utf8(gd, s->cx, s->cy, &gu);
1115 	}
1116 
1117 	/* Move the cursor. */
1118 	s->cx += width;
1119 
1120 	/* Draw to the screen if necessary. */
1121 	if (insert) {
1122 		ttyctx.num = width;
1123 		tty_write(tty_cmd_insertcharacter, &ttyctx);
1124 	}
1125 	ttyctx.utf8 = &gu;
1126 	if (screen_check_selection(s, s->cx - width, s->cy)) {
1127 		memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc);
1128 		tmp_gc.data = gc->data;
1129 		tmp_gc.flags = gc->flags &
1130 		    ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
1131 		tmp_gc.flags |= s->sel.cell.flags &
1132 		    (GRID_FLAG_FG256|GRID_FLAG_BG256);
1133 		ttyctx.cell = &tmp_gc;
1134 		tty_write(tty_cmd_cell, &ttyctx);
1135 	} else {
1136 		ttyctx.cell = gc;
1137 		tty_write(tty_cmd_cell, &ttyctx);
1138 	}
1139 }
1140 
1141 /* Combine a UTF-8 zero-width character onto the previous. */
1142 int
1143 screen_write_combine(
1144     struct screen_write_ctx *ctx, const struct utf8_data *utf8data)
1145 {
1146 	struct screen		*s = ctx->s;
1147 	struct grid		*gd = s->grid;
1148 	struct grid_cell	*gc;
1149 	struct grid_utf8	*gu, tmp_gu;
1150 	u_int			 i;
1151 
1152 	/* Can't combine if at 0. */
1153 	if (s->cx == 0)
1154 		return (-1);
1155 
1156 	/* Empty utf8data is out. */
1157 	if (utf8data->size == 0)
1158 		fatalx("UTF-8 data empty");
1159 
1160 	/* Retrieve the previous cell and convert to UTF-8 if not already. */
1161 	gc = grid_view_get_cell(gd, s->cx - 1, s->cy);
1162 	if (!(gc->flags & GRID_FLAG_UTF8)) {
1163 		tmp_gu.data[0] = gc->data;
1164 		tmp_gu.data[1] = 0xff;
1165 		tmp_gu.width = 1;
1166 
1167 		grid_view_set_utf8(gd, s->cx - 1, s->cy, &tmp_gu);
1168 		gc->flags |= GRID_FLAG_UTF8;
1169 	}
1170 
1171 	/* Append the current cell. */
1172 	gu = grid_view_get_utf8(gd, s->cx - 1, s->cy);
1173 	if (grid_utf8_append(gu, utf8data) != 0) {
1174 		/* Failed: scrap this character and replace with underscores. */
1175 		if (gu->width == 1) {
1176 			gc->data = '_';
1177 			gc->flags &= ~GRID_FLAG_UTF8;
1178 		} else {
1179 			for (i = 0; i < gu->width && i != sizeof gu->data; i++)
1180 				gu->data[i] = '_';
1181 			if (i != sizeof gu->data)
1182 				gu->data[i] = 0xff;
1183 			gu->width = i;
1184 		}
1185 	}
1186 
1187 	return (0);
1188 }
1189 
1190 /*
1191  * UTF-8 wide characters are a bit of an annoyance. They take up more than one
1192  * cell on the screen, so following cells must not be drawn by marking them as
1193  * padding.
1194  *
1195  * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
1196  * character, it is necessary to also overwrite any other cells which covered
1197  * by the same character.
1198  */
1199 void
1200 screen_write_overwrite(struct screen_write_ctx *ctx, u_int width)
1201 {
1202 	struct screen		*s = ctx->s;
1203 	struct grid		*gd = s->grid;
1204 	const struct grid_cell	*gc;
1205 	u_int			 xx;
1206 
1207 	gc = grid_view_peek_cell(gd, s->cx, s->cy);
1208 	if (gc->flags & GRID_FLAG_PADDING) {
1209 		/*
1210 		 * A padding cell, so clear any following and leading padding
1211 		 * cells back to the character. Don't overwrite the current
1212 		 * cell as that happens later anyway.
1213 		 */
1214 		xx = s->cx + 1;
1215 		while (--xx > 0) {
1216 			gc = grid_view_peek_cell(gd, xx, s->cy);
1217 			if (!(gc->flags & GRID_FLAG_PADDING))
1218 				break;
1219 			grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1220 		}
1221 
1222 		/* Overwrite the character at the start of this padding. */
1223 		grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1224 	}
1225 
1226 	/*
1227 	 * Overwrite any padding cells that belong to a UTF-8 character
1228 	 * we'll be overwriting with the current character.
1229 	 */
1230 	xx = s->cx + width - 1;
1231 	while (++xx < screen_size_x(s)) {
1232 		gc = grid_view_peek_cell(gd, xx, s->cy);
1233 		if (!(gc->flags & GRID_FLAG_PADDING))
1234 			break;
1235 		grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1236 	}
1237 }
1238 
1239 void
1240 screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len)
1241 {
1242 	struct tty_ctx	ttyctx;
1243 
1244 	screen_write_initctx(ctx, &ttyctx, 0);
1245 	ttyctx.ptr = str;
1246 	ttyctx.num = len;
1247 
1248 	tty_write(tty_cmd_setselection, &ttyctx);
1249 }
1250 
1251 void
1252 screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len)
1253 {
1254 	struct tty_ctx		 ttyctx;
1255 
1256 	screen_write_initctx(ctx, &ttyctx, 0);
1257 	ttyctx.ptr = str;
1258 	ttyctx.num = len;
1259 
1260 	tty_write(tty_cmd_rawstring, &ttyctx);
1261 }
1262