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