xref: /openbsd-src/usr.bin/tmux/tty.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /* $OpenBSD: tty.c,v 1.46 2009/10/12 17:19:47 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 #include <sys/ioctl.h>
21 
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <termios.h>
27 #include <unistd.h>
28 
29 #include "tmux.h"
30 
31 void	tty_fill_acs(struct tty *);
32 
33 int	tty_try_256(struct tty *, u_char, const char *);
34 int	tty_try_88(struct tty *, u_char, const char *);
35 
36 void	tty_attributes_fg(struct tty *, const struct grid_cell *);
37 void	tty_attributes_bg(struct tty *, const struct grid_cell *);
38 
39 void	tty_redraw_region(struct tty *, const struct tty_ctx *);
40 void	tty_emulate_repeat(
41 	    struct tty *, enum tty_code_code, enum tty_code_code, u_int);
42 void	tty_cell(struct tty *,
43     	    const struct grid_cell *, const struct grid_utf8 *);
44 
45 void
46 tty_init(struct tty *tty, int fd, char *term)
47 {
48 	char	*path;
49 
50 	memset(tty, 0, sizeof *tty);
51 	tty->log_fd = -1;
52 
53 	if (term == NULL || *term == '\0')
54 		tty->termname = xstrdup("unknown");
55 	else
56 		tty->termname = xstrdup(term);
57 
58 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
59 		fatal("fcntl failed");
60 	tty->fd = fd;
61 
62 	if ((path = ttyname(fd)) == NULL)
63 		fatalx("ttyname failed");
64 	tty->path = xstrdup(path);
65 
66 	tty->flags = 0;
67 	tty->term_flags = 0;
68 }
69 
70 void
71 tty_resize(struct tty *tty)
72 {
73 	struct winsize	ws;
74 
75 	if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) {
76 		tty->sx = ws.ws_col;
77 		tty->sy = ws.ws_row;
78 	}
79 	if (tty->sx == 0)
80 		tty->sx = 80;
81 	if (tty->sy == 0)
82 		tty->sy = 24;
83 
84 	tty->cx = UINT_MAX;
85 	tty->cy = UINT_MAX;
86 
87 	tty->rupper = UINT_MAX;
88 	tty->rlower = UINT_MAX;
89 }
90 
91 int
92 tty_open(struct tty *tty, const char *overrides, char **cause)
93 {
94 	int	fd;
95 
96 	if (debug_level > 3) {
97 		fd = open("tmux.out", O_WRONLY|O_CREAT|O_TRUNC, 0644);
98 		if (fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
99 			fatal("fcntl failed");
100 		tty->log_fd = fd;
101 	}
102 
103 	tty->term = tty_term_find(tty->termname, tty->fd, overrides, cause);
104 	if (tty->term == NULL) {
105 		tty_close(tty);
106 		return (-1);
107 	}
108 	tty->flags |= TTY_OPENED;
109 
110 	tty->in = buffer_create(BUFSIZ);
111 	tty->out = buffer_create(BUFSIZ);
112 
113 	tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_ESCAPE);
114 
115 	tty_start_tty(tty);
116 
117 	tty_keys_init(tty);
118 
119 	tty_fill_acs(tty);
120 
121 	return (0);
122 }
123 
124 void
125 tty_start_tty(struct tty *tty)
126 {
127 	struct termios	 tio;
128 	int		 what, mode;
129 
130 	if (tty->fd == -1)
131 		return;
132 
133 	if ((mode = fcntl(tty->fd, F_GETFL)) == -1)
134 		fatal("fcntl failed");
135 	if (fcntl(tty->fd, F_SETFL, mode|O_NONBLOCK) == -1)
136 		fatal("fcntl failed");
137 
138 	if (tcgetattr(tty->fd, &tty->tio) != 0)
139 		fatal("tcgetattr failed");
140 	memcpy(&tio, &tty->tio, sizeof tio);
141 	tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP);
142 	tio.c_iflag |= IGNBRK;
143 	tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET);
144 	tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL|
145 	    ECHOPRT|ECHOKE|ECHOCTL|ISIG);
146 	tio.c_cc[VMIN] = 1;
147         tio.c_cc[VTIME] = 0;
148 	if (tcsetattr(tty->fd, TCSANOW, &tio) != 0)
149 		fatal("tcsetattr failed");
150 
151 	what = 0;
152 	if (ioctl(tty->fd, TIOCFLUSH, &what) != 0)
153 		fatal("ioctl(TIOCFLUSH)");
154 
155 	tty_putcode(tty, TTYC_SMCUP);
156 
157 	tty_putcode(tty, TTYC_SGR0);
158 	memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell);
159 
160 	tty_putcode(tty, TTYC_SMKX);
161 	tty_putcode(tty, TTYC_ENACS);
162 	tty_putcode(tty, TTYC_CLEAR);
163 
164 	tty_putcode(tty, TTYC_CNORM);
165 	if (tty_term_has(tty->term, TTYC_KMOUS))
166 		tty_puts(tty, "\033[?1000l");
167 
168 	tty->cx = UINT_MAX;
169 	tty->cy = UINT_MAX;
170 
171 	tty->rlower = UINT_MAX;
172 	tty->rupper = UINT_MAX;
173 
174 	tty->mode = MODE_CURSOR;
175 
176 	tty->flags |= TTY_STARTED;
177 }
178 
179 void
180 tty_stop_tty(struct tty *tty)
181 {
182 	struct winsize	ws;
183 	int		mode;
184 
185 	if (!(tty->flags & TTY_STARTED))
186 		return;
187 	tty->flags &= ~TTY_STARTED;
188 
189 	/*
190 	 * Be flexible about error handling and try not kill the server just
191 	 * because the fd is invalid. Things like ssh -t can easily leave us
192 	 * with a dead tty.
193 	 */
194 	if ((mode = fcntl(tty->fd, F_GETFL)) == -1)
195 		return;
196 	if (fcntl(tty->fd, F_SETFL, mode & ~O_NONBLOCK) == -1)
197 		return;
198 	if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1)
199 		return;
200 	if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1)
201 		return;
202 
203 	tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
204 	tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
205 	tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
206 	tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
207 	tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
208 
209 	tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
210 	if (tty_term_has(tty->term, TTYC_KMOUS))
211 		tty_raw(tty, "\033[?1000l");
212 
213 	tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
214 }
215 
216 void
217 tty_fill_acs(struct tty *tty)
218 {
219 	const char *ptr;
220 
221 	memset(tty->acs, 0, sizeof tty->acs);
222 	if (!tty_term_has(tty->term, TTYC_ACSC))
223 		return;
224 
225 	ptr = tty_term_string(tty->term, TTYC_ACSC);
226 	if (strlen(ptr) % 2 != 0)
227 		return;
228 	for (; *ptr != '\0'; ptr += 2)
229 		tty->acs[(u_char) ptr[0]] = ptr[1];
230 }
231 
232 u_char
233 tty_get_acs(struct tty *tty, u_char ch)
234 {
235 	if (tty->acs[ch] != '\0')
236 		return (tty->acs[ch]);
237 	return (ch);
238 }
239 
240 void
241 tty_close(struct tty *tty)
242 {
243 	if (tty->log_fd != -1) {
244 		close(tty->log_fd);
245 		tty->log_fd = -1;
246 	}
247 
248 	tty_stop_tty(tty);
249 
250 	if (tty->flags & TTY_OPENED) {
251 		tty_term_free(tty->term);
252 		tty_keys_free(tty);
253 
254 		buffer_destroy(tty->in);
255 		buffer_destroy(tty->out);
256 
257 		tty->flags &= ~TTY_OPENED;
258 	}
259 
260 	if (tty->fd != -1) {
261 		close(tty->fd);
262 		tty->fd = -1;
263 	}
264 }
265 
266 void
267 tty_free(struct tty *tty)
268 {
269 	tty_close(tty);
270 
271 	if (tty->path != NULL)
272 		xfree(tty->path);
273 	if (tty->termname != NULL)
274 		xfree(tty->termname);
275 }
276 
277 void
278 tty_raw(struct tty *tty, const char *s)
279 {
280 	write(tty->fd, s, strlen(s));
281 }
282 
283 void
284 tty_putcode(struct tty *tty, enum tty_code_code code)
285 {
286 	tty_puts(tty, tty_term_string(tty->term, code));
287 }
288 
289 void
290 tty_putcode1(struct tty *tty, enum tty_code_code code, int a)
291 {
292 	if (a < 0)
293 		return;
294 	tty_puts(tty, tty_term_string1(tty->term, code, a));
295 }
296 
297 void
298 tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b)
299 {
300 	if (a < 0 || b < 0)
301 		return;
302 	tty_puts(tty, tty_term_string2(tty->term, code, a, b));
303 }
304 
305 void
306 tty_puts(struct tty *tty, const char *s)
307 {
308 	if (*s == '\0')
309 		return;
310 	buffer_write(tty->out, s, strlen(s));
311 
312 	if (tty->log_fd != -1)
313 		write(tty->log_fd, s, strlen(s));
314 }
315 
316 void
317 tty_putc(struct tty *tty, u_char ch)
318 {
319 	u_int	sx;
320 
321 	if (tty->cell.attr & GRID_ATTR_CHARSET)
322 		ch = tty_get_acs(tty, ch);
323 	buffer_write8(tty->out, ch);
324 
325 	if (ch >= 0x20 && ch != 0x7f) {
326 		sx = tty->sx;
327 		if (tty->term->flags & TERM_EARLYWRAP)
328 			sx--;
329 
330 		if (tty->cx >= sx) {
331 			tty->cx = 1;
332 			if (tty->cy != tty->rlower)
333 				tty->cy++;
334 		} else
335 			tty->cx++;
336 	}
337 
338 	if (tty->log_fd != -1)
339 		write(tty->log_fd, &ch, 1);
340 }
341 
342 void
343 tty_pututf8(struct tty *tty, const struct grid_utf8 *gu)
344 {
345 	u_int	i, width;
346 
347 	for (i = 0; i < UTF8_SIZE; i++) {
348 		if (gu->data[i] == 0xff)
349 			break;
350 		buffer_write8(tty->out, gu->data[i]);
351 		if (tty->log_fd != -1)
352 			write(tty->log_fd, &gu->data[i], 1);
353 	}
354 
355 	width = utf8_width(gu->data);
356 	tty->cx += width;
357 }
358 
359 void
360 tty_set_title(struct tty *tty, const char *title)
361 {
362 	if (strstr(tty->termname, "xterm") == NULL &&
363 	    strstr(tty->termname, "rxvt") == NULL &&
364 	    strcmp(tty->termname, "screen") != 0)
365 		return;
366 
367 	tty_puts(tty, "\033]0;");
368 	tty_puts(tty, title);
369 	tty_putc(tty, '\007');
370 }
371 
372 void
373 tty_update_mode(struct tty *tty, int mode)
374 {
375 	int	changed;
376 
377 	if (tty->flags & TTY_NOCURSOR)
378 		mode &= ~MODE_CURSOR;
379 
380 	changed = mode ^ tty->mode;
381 	if (changed & MODE_CURSOR) {
382 		if (mode & MODE_CURSOR)
383 			tty_putcode(tty, TTYC_CNORM);
384 		else
385 			tty_putcode(tty, TTYC_CIVIS);
386 	}
387 	if (changed & MODE_MOUSE) {
388 		if (mode & MODE_MOUSE)
389 			tty_puts(tty, "\033[?1000h");
390 		else
391 			tty_puts(tty, "\033[?1000l");
392 	}
393 	tty->mode = mode;
394 }
395 
396 void
397 tty_emulate_repeat(
398     struct tty *tty, enum tty_code_code code, enum tty_code_code code1, u_int n)
399 {
400 	if (tty_term_has(tty->term, code))
401 		tty_putcode1(tty, code, n);
402 	else {
403 		while (n-- > 0)
404 			tty_putcode(tty, code1);
405 	}
406 }
407 
408 /*
409  * Redraw scroll region using data from screen (already updated). Used when
410  * CSR not supported, or window is a pane that doesn't take up the full
411  * width of the terminal.
412  */
413 void
414 tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx)
415 {
416 	struct window_pane	*wp = ctx->wp;
417 	struct screen		*s = wp->screen;
418 	u_int		 	 i;
419 
420 	/*
421 	 * If region is >= 50% of the screen, just schedule a window redraw. In
422 	 * most cases, this is likely to be followed by some more scrolling -
423 	 * without this, the entire pane ends up being redrawn many times which
424 	 * can be much more data.
425 	 */
426 	if (ctx->orupper - ctx->orlower >= screen_size_y(s) / 2) {
427 		wp->flags |= PANE_REDRAW;
428 		return;
429 	}
430 
431 	if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) {
432 		for (i = ctx->ocy; i < screen_size_y(s); i++)
433 			tty_draw_line(tty, s, i, wp->xoff, wp->yoff);
434 	} else {
435 		for (i = ctx->orupper; i <= ctx->orlower; i++)
436 			tty_draw_line(tty, s, i, wp->xoff, wp->yoff);
437 	}
438 }
439 
440 void
441 tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy)
442 {
443 	const struct grid_cell	*gc;
444 	struct grid_line	*gl;
445 	struct grid_cell	 tmpgc;
446 	const struct grid_utf8	*gu;
447 	u_int			 i, sx;
448 
449 	tty_update_mode(tty, tty->mode & ~MODE_CURSOR);
450 
451 	sx = screen_size_x(s);
452 	if (sx > s->grid->linedata[s->grid->hsize + py].cellsize)
453 		sx = s->grid->linedata[s->grid->hsize + py].cellsize;
454 	if (sx > tty->sx)
455 		sx = tty->sx;
456 
457 	/*
458 	 * Don't move the cursor to the start permission if it will wrap there
459 	 * itself; much the same as the conditions in tty_cmd_cell.
460 	 */
461 	gl = NULL;
462 	if (py != 0)
463 		gl = &s->grid->linedata[s->grid->hsize + py - 1];
464 	if (ox != 0 || (gl != NULL && !(gl->flags & GRID_LINE_WRAPPED)) ||
465 	    tty->cy != oy + py - 1 || tty->cx < tty->sx)
466 		tty_cursor(tty, ox, oy + py);
467 
468 	for (i = 0; i < sx; i++) {
469 		gc = grid_view_peek_cell(s->grid, i, py);
470 
471 		gu = NULL;
472 		if (gc->flags & GRID_FLAG_UTF8)
473 			gu = grid_view_peek_utf8(s->grid, i, py);
474 
475 		if (screen_check_selection(s, i, py)) {
476 			memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc);
477 			tmpgc.data = gc->data;
478 			tmpgc.flags = gc->flags &
479 			    ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
480 			tmpgc.flags |= s->sel.cell.flags &
481 			    (GRID_FLAG_FG256|GRID_FLAG_BG256);
482 			tty_cell(tty, &tmpgc, gu);
483 		} else
484 			tty_cell(tty, gc, gu);
485 	}
486 
487 	if (sx >= tty->sx) {
488 		tty_update_mode(tty, tty->mode);
489 		return;
490 	}
491 	tty_reset(tty);
492 
493 	tty_cursor(tty, ox + sx, oy + py);
494 	if (screen_size_x(s) >= tty->sx && tty_term_has(tty->term, TTYC_EL))
495 		tty_putcode(tty, TTYC_EL);
496 	else {
497 		for (i = sx; i < screen_size_x(s); i++)
498 			tty_putc(tty, ' ');
499 	}
500 	tty_update_mode(tty, tty->mode);
501 }
502 
503 void
504 tty_write(void (*cmdfn)(
505     struct tty *, const struct tty_ctx *), const struct tty_ctx *ctx)
506 {
507 	struct window_pane	*wp = ctx->wp;
508 	struct client		*c;
509 	u_int		 	 i;
510 
511 	if (wp == NULL)
512 		return;
513 
514 	if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW)
515 		return;
516 	if (wp->window->flags & WINDOW_HIDDEN || !window_pane_visible(wp))
517 		return;
518 
519 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
520 		c = ARRAY_ITEM(&clients, i);
521 		if (c == NULL || c->session == NULL)
522 			continue;
523 		if (c->flags & CLIENT_SUSPENDED)
524 			continue;
525 
526 		if (c->session->curw->window == wp->window) {
527 			if (c->tty.flags & TTY_FREEZE || c->tty.term == NULL)
528 				continue;
529 			cmdfn(&c->tty, ctx);
530 		}
531 	}
532 }
533 
534 void
535 tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
536 {
537   	struct window_pane	*wp = ctx->wp;
538 	struct screen		*s = wp->screen;
539 	u_int			 i;
540 
541 	if (wp->xoff != 0 || screen_size_x(s) < tty->sx) {
542 		tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
543 		return;
544 	}
545 
546 	tty_reset(tty);
547 
548  	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
549 
550 	if (tty_term_has(tty->term, TTYC_ICH) ||
551 	    tty_term_has(tty->term, TTYC_ICH1))
552 		tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num);
553 	else {
554 		tty_putcode(tty, TTYC_SMIR);
555 		for (i = 0; i < ctx->num; i++)
556 			tty_putc(tty, ' ');
557 		tty_putcode(tty, TTYC_RMIR);
558 	}
559 }
560 
561 void
562 tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
563 {
564   	struct window_pane	*wp = ctx->wp;
565 	struct screen		*s = wp->screen;
566 
567 	if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
568 	    (!tty_term_has(tty->term, TTYC_DCH) &&
569 	    !tty_term_has(tty->term, TTYC_DCH1))) {
570 		tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
571 		return;
572 	}
573 
574 	tty_reset(tty);
575 
576  	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
577 
578 	if (tty_term_has(tty->term, TTYC_DCH) ||
579 	    tty_term_has(tty->term, TTYC_DCH1))
580 		tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num);
581 }
582 
583 void
584 tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
585 {
586   	struct window_pane	*wp = ctx->wp;
587 	struct screen		*s = wp->screen;
588 
589  	if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
590 	    !tty_term_has(tty->term, TTYC_CSR)) {
591 		tty_redraw_region(tty, ctx);
592 		return;
593 	}
594 
595 	tty_reset(tty);
596 
597  	tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
598  	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
599 
600 	tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num);
601 }
602 
603 void
604 tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
605 {
606   	struct window_pane	*wp = ctx->wp;
607 	struct screen		*s = wp->screen;
608 
609  	if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
610 	    !tty_term_has(tty->term, TTYC_CSR)) {
611 		tty_redraw_region(tty, ctx);
612 		return;
613 	}
614 
615 	tty_reset(tty);
616 
617  	tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
618  	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
619 
620 	tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num);
621 }
622 
623 void
624 tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
625 {
626   	struct window_pane	*wp = ctx->wp;
627 	struct screen		*s = wp->screen;
628 	u_int		 	 i;
629 
630 	tty_reset(tty);
631 
632  	tty_cursor_pane(tty, ctx, 0, ctx->ocy);
633 
634 	if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
635 	    tty_term_has(tty->term, TTYC_EL)) {
636 		tty_putcode(tty, TTYC_EL);
637 	} else {
638 		for (i = 0; i < screen_size_x(s); i++)
639 			tty_putc(tty, ' ');
640 	}
641 }
642 
643 void
644 tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
645 {
646   	struct window_pane	*wp = ctx->wp;
647 	struct screen		*s = wp->screen;
648 	u_int		 	 i;
649 
650 	tty_reset(tty);
651 
652 	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
653 
654 	if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
655 	    tty_term_has(tty->term, TTYC_EL))
656 		tty_putcode(tty, TTYC_EL);
657 	else {
658 		for (i = ctx->ocx; i < screen_size_x(s); i++)
659 			tty_putc(tty, ' ');
660 	}
661 }
662 
663 void
664 tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
665 {
666   	struct window_pane	*wp = ctx->wp;
667 	u_int		 	 i;
668 
669 	tty_reset(tty);
670 
671 	if (wp->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) {
672 		tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
673 		tty_putcode(tty, TTYC_EL1);
674 	} else {
675 		tty_cursor_pane(tty, ctx, 0, ctx->ocy);
676 		for (i = 0; i < ctx->ocx + 1; i++)
677 			tty_putc(tty, ' ');
678 	}
679 }
680 
681 void
682 tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
683 {
684   	struct window_pane	*wp = ctx->wp;
685 	struct screen		*s = wp->screen;
686 
687  	if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
688 	    !tty_term_has(tty->term, TTYC_CSR)) {
689 		tty_redraw_region(tty, ctx);
690 		return;
691 	}
692 
693 	if (ctx->ocy == ctx->orupper) {
694 		tty_reset(tty);
695 
696 		tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
697 		tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
698 
699 		tty_putcode(tty, TTYC_RI);
700 	}
701 }
702 
703 void
704 tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
705 {
706   	struct window_pane	*wp = ctx->wp;
707 	struct screen		*s = wp->screen;
708 
709  	if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
710 	    !tty_term_has(tty->term, TTYC_CSR)) {
711 		tty_redraw_region(tty, ctx);
712 		return;
713 	}
714 
715 	if (ctx->ocy == ctx->orlower) {
716 		tty_reset(tty);
717 
718 		tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
719 		tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
720 
721 		tty_putc(tty, '\n');
722 	}
723 }
724 
725 void
726 tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
727 {
728   	struct window_pane	*wp = ctx->wp;
729 	struct screen		*s = wp->screen;
730 	u_int		 	 i, j;
731 
732 	tty_reset(tty);
733 
734 	tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
735 	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
736 
737 	if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
738 	    tty_term_has(tty->term, TTYC_EL)) {
739 		tty_putcode(tty, TTYC_EL);
740 		if (ctx->ocy != screen_size_y(s) - 1) {
741 			tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
742 			for (i = ctx->ocy + 1; i < screen_size_y(s); i++) {
743 				tty_putcode(tty, TTYC_EL);
744 				if (i == screen_size_y(s) - 1)
745 					continue;
746 				tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
747 				tty->cy++;
748 			}
749 		}
750 	} else {
751 		for (i = ctx->ocx; i < screen_size_x(s); i++)
752 			tty_putc(tty, ' ');
753 		for (j = ctx->ocy; j < screen_size_y(s); j++) {
754 			tty_cursor_pane(tty, ctx, 0, j);
755 			for (i = 0; i < screen_size_x(s); i++)
756 				tty_putc(tty, ' ');
757 		}
758 	}
759 }
760 
761 void
762 tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
763 {
764  	struct window_pane	*wp = ctx->wp;
765 	struct screen		*s = wp->screen;
766 	u_int		 	 i, j;
767 
768 	tty_reset(tty);
769 
770 	tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
771 	tty_cursor_pane(tty, ctx, 0, 0);
772 
773 	if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
774 	    tty_term_has(tty->term, TTYC_EL)) {
775 		for (i = 0; i < ctx->ocy; i++) {
776 			tty_putcode(tty, TTYC_EL);
777 			tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
778 			tty->cy++;
779 		}
780 	} else {
781 		for (j = 0; j < ctx->ocy; j++) {
782 			tty_cursor_pane(tty, ctx, 0, j);
783 			for (i = 0; i < screen_size_x(s); i++)
784 				tty_putc(tty, ' ');
785 		}
786 	}
787 	for (i = 0; i <= ctx->ocx; i++)
788 		tty_putc(tty, ' ');
789 }
790 
791 void
792 tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
793 {
794  	struct window_pane	*wp = ctx->wp;
795 	struct screen		*s = wp->screen;
796 	u_int		 	 i, j;
797 
798 	tty_reset(tty);
799 
800 	tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
801 	tty_cursor_pane(tty, ctx, 0, 0);
802 
803 	if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
804 	    tty_term_has(tty->term, TTYC_EL)) {
805 		for (i = 0; i < screen_size_y(s); i++) {
806 			tty_putcode(tty, TTYC_EL);
807 			if (i != screen_size_y(s) - 1) {
808 				tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
809 				tty->cy++;
810 			}
811 		}
812 	} else {
813 		for (j = 0; j < screen_size_y(s); j++) {
814 			tty_cursor_pane(tty, ctx, 0, j);
815 			for (i = 0; i < screen_size_x(s); i++)
816 				tty_putc(tty, ' ');
817 		}
818 	}
819 }
820 
821 void
822 tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
823 {
824 	struct window_pane	*wp = ctx->wp;
825 	struct screen		*s = wp->screen;
826 	u_int			 i, j;
827 
828 	tty_reset(tty);
829 
830 	tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
831 
832 	for (j = 0; j < screen_size_y(s); j++) {
833 		tty_cursor_pane(tty, ctx, 0, j);
834 		for (i = 0; i < screen_size_x(s); i++)
835 			tty_putc(tty, 'E');
836 	}
837 }
838 
839 void
840 tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
841 {
842 	struct window_pane	*wp = ctx->wp;
843 	struct screen		*s = wp->screen;
844 	struct grid_line	*gl;
845 	u_int			 wx, wy;
846 
847 	tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
848 
849 	wx = ctx->ocx + wp->xoff;
850 	wy = ctx->ocy + wp->yoff;
851 
852 	/*
853 	 * If:
854 	 *
855 	 * - the line was wrapped:
856 	 * - the cursor is beyond the edge of the screen,
857 	 * - the desired position is at the left,
858 	 * - and either a) the desired next line is the one below the current
859 	 *   or b) the current line is the bottom of the scroll region,
860 	 *
861 	 * Then just printing the next character will be enough to scroll into
862 	 * place, so don't do an explicit cursor move.
863 	 */
864 	gl = NULL;
865 	if (ctx->ocy != 0)
866 		gl = &s->grid->linedata[s->grid->hsize + ctx->ocy - 1];
867 	if (wy == 0 || (gl != NULL && !(gl->flags & GRID_LINE_WRAPPED)) ||
868 	    tty->cx < tty->sx ||	/* not at edge of screen */
869 	    wx != 0 ||			/* don't want 0 next */
870 	    (wy != tty->cy + 1 && tty->cy != ctx->orlower + wp->yoff))
871 		tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
872 
873 	tty_cell(tty, ctx->cell, ctx->utf8);
874 }
875 
876 void
877 tty_cmd_utf8character(struct tty *tty, const struct tty_ctx *ctx)
878 {
879 	u_char	*ptr = ctx->ptr;
880 	size_t	 i;
881 
882 	for (i = 0; i < UTF8_SIZE; i++) {
883 		if (ptr[i] == 0xff)
884 			break;
885 		tty_putc(tty, ptr[i]);
886 	}
887 }
888 
889 void
890 tty_cell(
891     struct tty *tty, const struct grid_cell *gc, const struct grid_utf8 *gu)
892 {
893 	u_int	i;
894 
895 	/* Skip last character if terminal is stupid. */
896 	if (tty->term->flags & TERM_EARLYWRAP &&
897 	    tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1)
898 		return;
899 
900 	/* If this is a padding character, do nothing. */
901 	if (gc->flags & GRID_FLAG_PADDING)
902 		return;
903 
904 	/* Set the attributes. */
905 	tty_attributes(tty, gc);
906 
907 	/* If not UTF-8, write directly. */
908 	if (!(gc->flags & GRID_FLAG_UTF8)) {
909 		if (gc->data < 0x20 || gc->data == 0x7f)
910 			return;
911 		tty_putc(tty, gc->data);
912 		return;
913 	}
914 
915 	/* If the terminal doesn't support UTF-8, write underscores. */
916 	if (!(tty->flags & TTY_UTF8)) {
917 		for (i = 0; i < gu->width; i++)
918 			tty_putc(tty, '_');
919 		return;
920 	}
921 
922 	/* Otherwise, write UTF-8. */
923 	tty_pututf8(tty, gu);
924 }
925 
926 void
927 tty_reset(struct tty *tty)
928 {
929 	struct grid_cell	*gc = &tty->cell;
930 
931 	if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0)
932 		return;
933 
934 	if (tty_term_has(tty->term, TTYC_RMACS) && gc->attr & GRID_ATTR_CHARSET)
935 		tty_putcode(tty, TTYC_RMACS);
936 	tty_putcode(tty, TTYC_SGR0);
937 	memcpy(gc, &grid_default_cell, sizeof *gc);
938 }
939 
940 /* Set region inside pane. */
941 void
942 tty_region_pane(
943     struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower)
944 {
945   	struct window_pane	*wp = ctx->wp;
946 
947 	tty_region(tty, wp->yoff + rupper, wp->yoff + rlower);
948 }
949 
950 /* Set region at absolute position. */
951 void
952 tty_region(struct tty *tty, u_int rupper, u_int rlower)
953 {
954 	if (tty->rlower == rlower && tty->rupper == rupper)
955 		return;
956 	if (!tty_term_has(tty->term, TTYC_CSR))
957 		return;
958 
959 	tty->rupper = rupper;
960 	tty->rlower = rlower;
961 
962 	tty->cx = 0;
963 	tty->cy = 0;
964 
965 	tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower);
966 }
967 
968 /* Move cursor inside pane. */
969 void
970 tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy)
971 {
972   	struct window_pane	*wp = ctx->wp;
973 
974 	tty_cursor(tty, wp->xoff + cx, wp->yoff + cy);
975 }
976 
977 /* Move cursor to absolute position. */
978 void
979 tty_cursor(struct tty *tty, u_int cx, u_int cy)
980 {
981 	struct tty_term	*term = tty->term;
982 	u_int		 thisx, thisy;
983 	int		 change;
984 
985 	if (cx > tty->sx - 1)
986 		cx = tty->sx - 1;
987 
988 	thisx = tty->cx;
989 	thisy = tty->cy;
990 
991 	/* No change. */
992 	if (cx == thisx && cy == thisy)
993 		return;
994 
995 	/* Very end of the line, just use absolute movement. */
996 	if (thisx > tty->sx - 1)
997 		goto absolute;
998 
999 	/* Move to home position (0, 0). */
1000 	if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) {
1001 		tty_putcode(tty, TTYC_HOME);
1002 		goto out;
1003 	}
1004 
1005 	/* Zero on the next line. */
1006 	if (cx == 0 && cy == thisy + 1) {
1007 		tty_putc(tty, '\r');
1008 		tty_putc(tty, '\n');
1009 		goto out;
1010 	}
1011 
1012 	/* Moving column or row. */
1013 	if (cy == thisy) {
1014 		/*
1015 		 * Moving column only, row staying the same.
1016 		 */
1017 
1018 		/* To left edge. */
1019 		if (cx == 0)	{
1020 			tty_putc(tty, '\r');
1021 			goto out;
1022 		}
1023 
1024 		/* One to the left. */
1025 		if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) {
1026 			tty_putcode(tty, TTYC_CUB1);
1027 			goto out;
1028 		}
1029 
1030 		/* One to the right. */
1031 		if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) {
1032 			tty_putcode(tty, TTYC_CUF1);
1033 			goto out;
1034 		}
1035 
1036 		/* Calculate difference. */
1037 		change = thisx - cx;	/* +ve left, -ve right */
1038 
1039 		/*
1040 		 * Use HPA if change is larger than absolute, otherwise move
1041 		 * the cursor with CUB/CUF.
1042 		 */
1043 		if (abs(change) > cx && tty_term_has(term, TTYC_HPA)) {
1044 			tty_putcode1(tty, TTYC_HPA, cx);
1045 			goto out;
1046 		} else if (change > 0 && tty_term_has(term, TTYC_CUB)) {
1047 			tty_putcode1(tty, TTYC_CUB, change);
1048 			goto out;
1049 		} else if (change < 0 && tty_term_has(term, TTYC_CUF)) {
1050 			tty_putcode1(tty, TTYC_CUF, -change);
1051 			goto out;
1052 		}
1053 	} else if (cx == thisx) {
1054 		/*
1055 		 * Moving row only, column staying the same.
1056 		 */
1057 
1058 		/* One above. */
1059 		if (cy != tty->rupper &&
1060 		    cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) {
1061 			tty_putcode(tty, TTYC_CUU1);
1062 			goto out;
1063 		}
1064 
1065 		/* One below. */
1066 		if (cy != tty->rlower &&
1067 		    cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) {
1068 			tty_putcode(tty, TTYC_CUD1);
1069 			goto out;
1070 		}
1071 
1072 		/* Calculate difference. */
1073 		change = thisy - cy;	/* +ve up, -ve down */
1074 
1075 		/*
1076 		 * Try to use VPA if change is larger than absolute or if this change
1077 		 * would cross the scroll region, otherwise use CUU/CUD.
1078 		 */
1079 		if (abs(change) > cy ||
1080 		    (change < 0 && cy - change > tty->rlower) ||
1081 		    (change > 0 && cy - change < tty->rupper)) {
1082 			    if (tty_term_has(term, TTYC_VPA)) {
1083 				    tty_putcode1(tty, TTYC_VPA, cy);
1084 				    goto out;
1085 			    }
1086 		} else if (change > 0 && tty_term_has(term, TTYC_CUU)) {
1087 			tty_putcode1(tty, TTYC_CUU, change);
1088 			goto out;
1089 		} else if (change < 0 && tty_term_has(term, TTYC_CUD)) {
1090 			tty_putcode1(tty, TTYC_CUD, -change);
1091 			goto out;
1092 		}
1093 	}
1094 
1095 absolute:
1096 	/* Absolute movement. */
1097 	tty_putcode2(tty, TTYC_CUP, cy, cx);
1098 
1099 out:
1100 	tty->cx = cx;
1101 	tty->cy = cy;
1102 }
1103 
1104 void
1105 tty_attributes(struct tty *tty, const struct grid_cell *gc)
1106 {
1107 	struct grid_cell	*tc = &tty->cell;
1108 	u_char			 changed;
1109 	u_int			 fg, bg, attr;
1110 
1111 	/*
1112 	 * If no setab, try to use the reverse attribute as a best-effort for a
1113 	 * non-default background. This is a bit of a hack but it doesn't do
1114 	 * any serious harm and makes a couple of applications happier.
1115 	 */
1116 	fg = gc->fg; bg = gc->bg; attr = gc->attr;
1117 	if (!tty_term_has(tty->term, TTYC_SETAB)) {
1118 		if (attr & GRID_ATTR_REVERSE) {
1119 			if (fg != 7 && fg != 8)
1120 				attr &= ~GRID_ATTR_REVERSE;
1121 		} else {
1122 			if (bg != 0 && bg != 8)
1123 				attr |= GRID_ATTR_REVERSE;
1124 		}
1125 	}
1126 
1127 	/* If any bits are being cleared, reset everything. */
1128 	if (tc->attr & ~attr)
1129 		tty_reset(tty);
1130 
1131 	/* Filter out attribute bits already set. */
1132 	changed = attr & ~tc->attr;
1133 	tc->attr = attr;
1134 
1135 	/* Set the attributes. */
1136 	if (changed & GRID_ATTR_BRIGHT)
1137 		tty_putcode(tty, TTYC_BOLD);
1138 	if (changed & GRID_ATTR_DIM)
1139 		tty_putcode(tty, TTYC_DIM);
1140 	if (changed & GRID_ATTR_ITALICS)
1141 		tty_putcode(tty, TTYC_SMSO);
1142 	if (changed & GRID_ATTR_UNDERSCORE)
1143 		tty_putcode(tty, TTYC_SMUL);
1144 	if (changed & GRID_ATTR_BLINK)
1145 		tty_putcode(tty, TTYC_BLINK);
1146 	if (changed & GRID_ATTR_REVERSE) {
1147 		if (tty_term_has(tty->term, TTYC_REV))
1148 			tty_putcode(tty, TTYC_REV);
1149 		else if (tty_term_has(tty->term, TTYC_SMSO))
1150 			tty_putcode(tty, TTYC_SMSO);
1151 	}
1152 	if (changed & GRID_ATTR_HIDDEN)
1153 		tty_putcode(tty, TTYC_INVIS);
1154 	if (changed & GRID_ATTR_CHARSET)
1155 		tty_putcode(tty, TTYC_SMACS);
1156 
1157 	/* Set foreground colour. */
1158 	if (fg != tc->fg ||
1159 	    (gc->flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256)) {
1160 		tty_attributes_fg(tty, gc);
1161 		tc->fg = fg;
1162 		tc->flags &= ~GRID_FLAG_FG256;
1163 		tc->flags |= gc->flags & GRID_FLAG_FG256;
1164 	}
1165 
1166 	/* Set background colour. */
1167 	if (bg != tc->bg ||
1168 	    (gc->flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256)) {
1169 		tty_attributes_bg(tty, gc);
1170 		tc->bg = bg;
1171 		tc->flags &= ~GRID_FLAG_BG256;
1172 		tc->flags |= gc->flags & GRID_FLAG_BG256;
1173 	}
1174 }
1175 
1176 int
1177 tty_try_256(struct tty *tty, u_char colour, const char *type)
1178 {
1179 	char	s[32];
1180 
1181 	if (!(tty->term->flags & TERM_256COLOURS) &&
1182 	    !(tty->term_flags & TERM_256COLOURS))
1183 		return (-1);
1184 
1185 	xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1186 	tty_puts(tty, s);
1187 	return (0);
1188 }
1189 
1190 int
1191 tty_try_88(struct tty *tty, u_char colour, const char *type)
1192 {
1193 	char	s[32];
1194 
1195 	if (!(tty->term->flags & TERM_88COLOURS) &&
1196 	    !(tty->term_flags & TERM_88COLOURS))
1197 		return (-1);
1198 	colour = colour_256to88(colour);
1199 
1200 	xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1201 	tty_puts(tty, s);
1202 	return (0);
1203 }
1204 
1205 void
1206 tty_attributes_fg(struct tty *tty, const struct grid_cell *gc)
1207 {
1208 	u_char	fg;
1209 
1210 	fg = gc->fg;
1211 	if (gc->flags & GRID_FLAG_FG256) {
1212 		if (tty_try_256(tty, fg, "38") == 0)
1213 			return;
1214 		if (tty_try_88(tty, fg, "38") == 0)
1215 			return;
1216 		fg = colour_256to16(fg);
1217 		if (fg & 8) {
1218 			fg &= 7;
1219 			tty_putcode(tty, TTYC_BOLD);
1220 			tty->cell.attr |= GRID_ATTR_BRIGHT;
1221 		} else if (tty->cell.attr & GRID_ATTR_BRIGHT)
1222 			tty_reset(tty);
1223 	}
1224 
1225 	if (fg == 8 &&
1226 	    !(tty->term->flags & TERM_HASDEFAULTS) &&
1227 	    !(tty->term_flags & TERM_HASDEFAULTS))
1228 		fg = 7;
1229 	if (fg == 8)
1230 		tty_puts(tty, "\033[39m");
1231 	else
1232 		tty_putcode1(tty, TTYC_SETAF, fg);
1233 }
1234 
1235 void
1236 tty_attributes_bg(struct tty *tty, const struct grid_cell *gc)
1237 {
1238 	u_char	bg;
1239 
1240 	bg = gc->bg;
1241 	if (gc->flags & GRID_FLAG_BG256) {
1242 		if (tty_try_256(tty, bg, "48") == 0)
1243 			return;
1244 		if (tty_try_88(tty, bg, "48") == 0)
1245 			return;
1246 		bg = colour_256to16(bg);
1247 		if (bg & 8)
1248 			bg &= 7;
1249 	}
1250 
1251 	if (bg == 8 &&
1252 	    !(tty->term->flags & TERM_HASDEFAULTS) &&
1253 	    !(tty->term_flags & TERM_HASDEFAULTS))
1254 		bg = 0;
1255 	if (bg == 8)
1256 		tty_puts(tty, "\033[49m");
1257 	else
1258 		tty_putcode1(tty, TTYC_SETAB, bg);
1259 }
1260