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