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