Use the tsl and fsl terminfo(5) capabilities to update terminal title
[tmux-openbsd.git] / tty.c
blobb423f6d86afd32a41fcd5afc6154e8f8d265c2ae
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <termios.h>
27 #include <unistd.h>
29 #include "tmux.h"
31 void tty_read_callback(struct bufferevent *, void *);
32 void tty_error_callback(struct bufferevent *, short, void *);
34 int tty_try_256(struct tty *, u_char, const char *);
35 int tty_try_88(struct tty *, u_char, const char *);
37 void tty_colours(struct tty *, const struct grid_cell *);
38 void tty_check_fg(struct tty *, struct grid_cell *);
39 void tty_check_bg(struct tty *, struct grid_cell *);
40 void tty_colours_fg(struct tty *, const struct grid_cell *);
41 void tty_colours_bg(struct tty *, const struct grid_cell *);
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 *);
49 #define tty_use_acs(tty) \
50 (tty_term_has(tty->term, TTYC_ACSC) && !((tty)->flags & TTY_UTF8))
52 void
53 tty_init(struct tty *tty, int fd, char *term)
55 char *path;
57 memset(tty, 0, sizeof *tty);
58 tty->log_fd = -1;
60 if (term == NULL || *term == '\0')
61 tty->termname = xstrdup("unknown");
62 else
63 tty->termname = xstrdup(term);
64 tty->fd = fd;
66 if ((path = ttyname(fd)) == NULL)
67 fatalx("ttyname failed");
68 tty->path = xstrdup(path);
70 tty->flags = 0;
71 tty->term_flags = 0;
74 int
75 tty_resize(struct tty *tty)
77 struct winsize ws;
78 u_int sx, sy;
80 if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) {
81 sx = ws.ws_col;
82 if (sx == 0)
83 sx = 80;
84 sy = ws.ws_row;
85 if (sy == 0)
86 sy = 24;
87 } else {
88 sx = 80;
89 sy = 24;
91 if (sx == tty->sx && sy == tty->sy)
92 return (0);
93 tty->sx = sx;
94 tty->sy = sy;
96 tty->cx = UINT_MAX;
97 tty->cy = UINT_MAX;
99 tty->rupper = UINT_MAX;
100 tty->rlower = UINT_MAX;
103 * If the terminal has been started, reset the actual scroll region and
104 * cursor position, as this may not have happened.
106 if (tty->flags & TTY_STARTED) {
107 tty_cursor(tty, 0, 0);
108 tty_region(tty, 0, tty->sy - 1);
111 return (1);
115 tty_open(struct tty *tty, const char *overrides, char **cause)
117 char out[64];
118 int fd;
120 if (debug_level > 3) {
121 xsnprintf(out, sizeof out, "tmux-out-%ld.log", (long) getpid());
122 fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644);
123 if (fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
124 fatal("fcntl failed");
125 tty->log_fd = fd;
128 tty->term = tty_term_find(tty->termname, tty->fd, overrides, cause);
129 if (tty->term == NULL) {
130 tty_close(tty);
131 return (-1);
133 tty->flags |= TTY_OPENED;
135 tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_ESCAPE);
137 tty->event = bufferevent_new(
138 tty->fd, tty_read_callback, NULL, tty_error_callback, tty);
140 tty_start_tty(tty);
142 tty_keys_init(tty);
144 return (0);
147 /* ARGSUSED */
148 void
149 tty_read_callback(unused struct bufferevent *bufev, void *data)
151 struct tty *tty = data;
153 while (tty_keys_next(tty))
157 /* ARGSUSED */
158 void
159 tty_error_callback(
160 unused struct bufferevent *bufev, unused short what, unused void *data)
164 void
165 tty_start_tty(struct tty *tty)
167 struct termios tio;
169 if (tty->fd == -1 || tcgetattr(tty->fd, &tty->tio) != 0)
170 return;
172 setblocking(tty->fd, 0);
174 bufferevent_enable(tty->event, EV_READ|EV_WRITE);
176 memcpy(&tio, &tty->tio, sizeof tio);
177 tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP);
178 tio.c_iflag |= IGNBRK;
179 tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET);
180 tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL|
181 ECHOPRT|ECHOKE|ECHOCTL|ISIG);
182 tio.c_cc[VMIN] = 1;
183 tio.c_cc[VTIME] = 0;
184 if (tcsetattr(tty->fd, TCSANOW, &tio) == 0)
185 tcflush(tty->fd, TCIOFLUSH);
187 tty_putcode(tty, TTYC_SMCUP);
189 tty_putcode(tty, TTYC_SGR0);
190 memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell);
192 tty_putcode(tty, TTYC_RMKX);
193 if (tty_use_acs(tty))
194 tty_putcode(tty, TTYC_ENACS);
195 tty_putcode(tty, TTYC_CLEAR);
197 tty_putcode(tty, TTYC_CNORM);
198 if (tty_term_has(tty->term, TTYC_KMOUS))
199 tty_puts(tty, "\033[?1000l");
201 tty->cx = UINT_MAX;
202 tty->cy = UINT_MAX;
204 tty->rlower = UINT_MAX;
205 tty->rupper = UINT_MAX;
207 tty->mode = MODE_CURSOR;
209 tty->flags |= TTY_STARTED;
212 void
213 tty_stop_tty(struct tty *tty)
215 struct winsize ws;
217 if (!(tty->flags & TTY_STARTED))
218 return;
219 tty->flags &= ~TTY_STARTED;
221 bufferevent_disable(tty->event, EV_READ|EV_WRITE);
224 * Be flexible about error handling and try not kill the server just
225 * because the fd is invalid. Things like ssh -t can easily leave us
226 * with a dead tty.
228 if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1)
229 return;
230 if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1)
231 return;
233 setblocking(tty->fd, 1);
235 tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
236 if (tty_use_acs(tty))
237 tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
238 tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
239 tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
240 tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
242 tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
243 if (tty_term_has(tty->term, TTYC_KMOUS))
244 tty_raw(tty, "\033[?1000l");
246 tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
249 void
250 tty_close(struct tty *tty)
252 if (tty->log_fd != -1) {
253 close(tty->log_fd);
254 tty->log_fd = -1;
257 evtimer_del(&tty->key_timer);
258 tty_stop_tty(tty);
260 if (tty->flags & TTY_OPENED) {
261 bufferevent_free(tty->event);
263 tty_term_free(tty->term);
264 tty_keys_free(tty);
266 tty->flags &= ~TTY_OPENED;
269 if (tty->fd != -1) {
270 close(tty->fd);
271 tty->fd = -1;
275 void
276 tty_free(struct tty *tty)
278 tty_close(tty);
280 if (tty->path != NULL)
281 xfree(tty->path);
282 if (tty->termname != NULL)
283 xfree(tty->termname);
286 void
287 tty_raw(struct tty *tty, const char *s)
289 write(tty->fd, s, strlen(s));
292 void
293 tty_putcode(struct tty *tty, enum tty_code_code code)
295 tty_puts(tty, tty_term_string(tty->term, code));
298 void
299 tty_putcode1(struct tty *tty, enum tty_code_code code, int a)
301 if (a < 0)
302 return;
303 tty_puts(tty, tty_term_string1(tty->term, code, a));
306 void
307 tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b)
309 if (a < 0 || b < 0)
310 return;
311 tty_puts(tty, tty_term_string2(tty->term, code, a, b));
314 void
315 tty_puts(struct tty *tty, const char *s)
317 if (*s == '\0')
318 return;
319 bufferevent_write(tty->event, s, strlen(s));
321 if (tty->log_fd != -1)
322 write(tty->log_fd, s, strlen(s));
325 void
326 tty_putc(struct tty *tty, u_char ch)
328 const char *acs;
329 u_int sx;
331 if (tty->cell.attr & GRID_ATTR_CHARSET) {
332 acs = tty_acs_get(tty, ch);
333 if (acs != NULL)
334 bufferevent_write(tty->event, acs, strlen(acs));
335 else
336 bufferevent_write(tty->event, &ch, 1);
337 } else
338 bufferevent_write(tty->event, &ch, 1);
340 if (ch >= 0x20 && ch != 0x7f) {
341 sx = tty->sx;
342 if (tty->term->flags & TERM_EARLYWRAP)
343 sx--;
345 if (tty->cx >= sx) {
346 tty->cx = 1;
347 if (tty->cy != tty->rlower)
348 tty->cy++;
349 } else
350 tty->cx++;
353 if (tty->log_fd != -1)
354 write(tty->log_fd, &ch, 1);
357 void
358 tty_pututf8(struct tty *tty, const struct grid_utf8 *gu)
360 size_t size;
362 size = grid_utf8_size(gu);
363 bufferevent_write(tty->event, gu->data, size);
364 if (tty->log_fd != -1)
365 write(tty->log_fd, gu->data, size);
366 tty->cx += gu->width;
369 void
370 tty_set_title(struct tty *tty, const char *title)
372 if (!tty_term_has(tty->term, TTYC_TSL) ||
373 !tty_term_has(tty->term, TTYC_FSL))
374 return;
376 tty_putcode(tty, TTYC_TSL);
377 tty_puts(tty, title);
378 tty_putcode(tty, TTYC_FSL);
381 void
382 tty_update_mode(struct tty *tty, int mode)
384 int changed;
386 if (tty->flags & TTY_NOCURSOR)
387 mode &= ~MODE_CURSOR;
389 changed = mode ^ tty->mode;
390 if (changed & MODE_CURSOR) {
391 if (mode & MODE_CURSOR)
392 tty_putcode(tty, TTYC_CNORM);
393 else
394 tty_putcode(tty, TTYC_CIVIS);
396 if (changed & ALL_MOUSE_MODES) {
397 if (mode & ALL_MOUSE_MODES) {
398 if (mode & MODE_MOUSE_UTF8)
399 tty_puts(tty, "\033[?1005h");
400 if (mode & MODE_MOUSE_ANY)
401 tty_puts(tty, "\033[?1003h");
402 else if (mode & MODE_MOUSE_BUTTON)
403 tty_puts(tty, "\033[?1002h");
404 else if (mode & MODE_MOUSE_STANDARD)
405 tty_puts(tty, "\033[?1000h");
406 } else {
407 if (tty->mode & MODE_MOUSE_ANY)
408 tty_puts(tty, "\033[?1003l");
409 else if (tty->mode & MODE_MOUSE_BUTTON)
410 tty_puts(tty, "\033[?1002l");
411 else if (tty->mode & MODE_MOUSE_STANDARD)
412 tty_puts(tty, "\033[?1000l");
413 if (tty->mode & MODE_MOUSE_UTF8)
414 tty_puts(tty, "\033[?1005l");
417 if (changed & MODE_KKEYPAD) {
418 if (mode & MODE_KKEYPAD)
419 tty_putcode(tty, TTYC_SMKX);
420 else
421 tty_putcode(tty, TTYC_RMKX);
423 tty->mode = mode;
426 void
427 tty_emulate_repeat(
428 struct tty *tty, enum tty_code_code code, enum tty_code_code code1, u_int n)
430 if (tty_term_has(tty->term, code))
431 tty_putcode1(tty, code, n);
432 else {
433 while (n-- > 0)
434 tty_putcode(tty, code1);
439 * Redraw scroll region using data from screen (already updated). Used when
440 * CSR not supported, or window is a pane that doesn't take up the full
441 * width of the terminal.
443 void
444 tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx)
446 struct window_pane *wp = ctx->wp;
447 struct screen *s = wp->screen;
448 u_int i;
451 * If region is >= 50% of the screen, just schedule a window redraw. In
452 * most cases, this is likely to be followed by some more scrolling -
453 * without this, the entire pane ends up being redrawn many times which
454 * can be much more data.
456 if (ctx->orlower - ctx->orupper >= screen_size_y(s) / 2) {
457 wp->flags |= PANE_REDRAW;
458 return;
461 if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) {
462 for (i = ctx->ocy; i < screen_size_y(s); i++)
463 tty_draw_line(tty, s, i, wp->xoff, wp->yoff);
464 } else {
465 for (i = ctx->orupper; i <= ctx->orlower; i++)
466 tty_draw_line(tty, s, i, wp->xoff, wp->yoff);
470 void
471 tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy)
473 const struct grid_cell *gc;
474 struct grid_line *gl;
475 struct grid_cell tmpgc;
476 const struct grid_utf8 *gu;
477 u_int i, sx;
479 tty_update_mode(tty, tty->mode & ~MODE_CURSOR);
481 sx = screen_size_x(s);
482 if (sx > s->grid->linedata[s->grid->hsize + py].cellsize)
483 sx = s->grid->linedata[s->grid->hsize + py].cellsize;
484 if (sx > tty->sx)
485 sx = tty->sx;
488 * Don't move the cursor to the start permission if it will wrap there
489 * itself.
491 gl = NULL;
492 if (py != 0)
493 gl = &s->grid->linedata[s->grid->hsize + py - 1];
494 if (oy + py == 0 || gl == NULL || !(gl->flags & GRID_LINE_WRAPPED) ||
495 tty->cx < tty->sx || ox != 0 ||
496 (oy + py != tty->cy + 1 && tty->cy != s->rlower + oy))
497 tty_cursor(tty, ox, oy + py);
499 for (i = 0; i < sx; i++) {
500 gc = grid_view_peek_cell(s->grid, i, py);
502 gu = NULL;
503 if (gc->flags & GRID_FLAG_UTF8)
504 gu = grid_view_peek_utf8(s->grid, i, py);
506 if (screen_check_selection(s, i, py)) {
507 memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc);
508 tmpgc.data = gc->data;
509 tmpgc.flags = gc->flags &
510 ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
511 tmpgc.flags |= s->sel.cell.flags &
512 (GRID_FLAG_FG256|GRID_FLAG_BG256);
513 tty_cell(tty, &tmpgc, gu);
514 } else
515 tty_cell(tty, gc, gu);
518 if (sx >= tty->sx) {
519 tty_update_mode(tty, tty->mode);
520 return;
522 tty_reset(tty);
524 tty_cursor(tty, ox + sx, oy + py);
525 if (screen_size_x(s) >= tty->sx && tty_term_has(tty->term, TTYC_EL))
526 tty_putcode(tty, TTYC_EL);
527 else {
528 for (i = sx; i < screen_size_x(s); i++)
529 tty_putc(tty, ' ');
531 tty_update_mode(tty, tty->mode);
534 void
535 tty_write(void (*cmdfn)(
536 struct tty *, const struct tty_ctx *), const struct tty_ctx *ctx)
538 struct window_pane *wp = ctx->wp;
539 struct client *c;
540 u_int i;
542 /* wp can be NULL if updating the screen but not the terminal. */
543 if (wp == NULL)
544 return;
546 if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW)
547 return;
548 if (!window_pane_visible(wp))
549 return;
551 for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
552 c = ARRAY_ITEM(&clients, i);
553 if (c == NULL || c->session == NULL)
554 continue;
555 if (c->flags & CLIENT_SUSPENDED)
556 continue;
558 if (c->session->curw->window == wp->window) {
559 if (c->tty.term == NULL)
560 continue;
561 if (c->tty.flags & (TTY_FREEZE|TTY_BACKOFF))
562 continue;
563 cmdfn(&c->tty, ctx);
568 void
569 tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
571 struct window_pane *wp = ctx->wp;
572 struct screen *s = wp->screen;
573 u_int i;
575 if (wp->xoff != 0 || screen_size_x(s) < tty->sx) {
576 tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
577 return;
580 tty_reset(tty);
582 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
584 if (tty_term_has(tty->term, TTYC_ICH) ||
585 tty_term_has(tty->term, TTYC_ICH1))
586 tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num);
587 else if (tty_term_has(tty->term, TTYC_SMIR) &&
588 tty_term_has(tty->term, TTYC_RMIR)) {
589 tty_putcode(tty, TTYC_SMIR);
590 for (i = 0; i < ctx->num; i++)
591 tty_putc(tty, ' ');
592 tty_putcode(tty, TTYC_RMIR);
593 } else
594 tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
597 void
598 tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
600 struct window_pane *wp = ctx->wp;
601 struct screen *s = wp->screen;
603 if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
604 (!tty_term_has(tty->term, TTYC_DCH) &&
605 !tty_term_has(tty->term, TTYC_DCH1))) {
606 tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
607 return;
610 tty_reset(tty);
612 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
614 if (tty_term_has(tty->term, TTYC_DCH) ||
615 tty_term_has(tty->term, TTYC_DCH1))
616 tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num);
619 void
620 tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
622 struct window_pane *wp = ctx->wp;
623 struct screen *s = wp->screen;
625 if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
626 !tty_term_has(tty->term, TTYC_CSR) ||
627 !tty_term_has(tty->term, TTYC_IL1)) {
628 tty_redraw_region(tty, ctx);
629 return;
632 tty_reset(tty);
634 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
635 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
637 tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num);
640 void
641 tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
643 struct window_pane *wp = ctx->wp;
644 struct screen *s = wp->screen;
646 if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
647 !tty_term_has(tty->term, TTYC_CSR) ||
648 !tty_term_has(tty->term, TTYC_DL1)) {
649 tty_redraw_region(tty, ctx);
650 return;
653 tty_reset(tty);
655 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
656 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
658 tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num);
661 void
662 tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
664 struct window_pane *wp = ctx->wp;
665 struct screen *s = wp->screen;
666 u_int i;
668 tty_reset(tty);
670 tty_cursor_pane(tty, ctx, 0, ctx->ocy);
672 if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
673 tty_term_has(tty->term, TTYC_EL)) {
674 tty_putcode(tty, TTYC_EL);
675 } else {
676 for (i = 0; i < screen_size_x(s); i++)
677 tty_putc(tty, ' ');
681 void
682 tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
684 struct window_pane *wp = ctx->wp;
685 struct screen *s = wp->screen;
686 u_int i;
688 tty_reset(tty);
690 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
692 if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
693 tty_term_has(tty->term, TTYC_EL))
694 tty_putcode(tty, TTYC_EL);
695 else {
696 for (i = ctx->ocx; i < screen_size_x(s); i++)
697 tty_putc(tty, ' ');
701 void
702 tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
704 struct window_pane *wp = ctx->wp;
705 u_int i;
707 tty_reset(tty);
709 if (wp->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) {
710 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
711 tty_putcode(tty, TTYC_EL1);
712 } else {
713 tty_cursor_pane(tty, ctx, 0, ctx->ocy);
714 for (i = 0; i < ctx->ocx + 1; i++)
715 tty_putc(tty, ' ');
719 void
720 tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
722 struct window_pane *wp = ctx->wp;
723 struct screen *s = wp->screen;
725 if (ctx->ocy != ctx->orupper)
726 return;
728 if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
729 !tty_term_has(tty->term, TTYC_CSR) ||
730 !tty_term_has(tty->term, TTYC_RI)) {
731 tty_redraw_region(tty, ctx);
732 return;
735 tty_reset(tty);
737 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
738 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
740 tty_putcode(tty, TTYC_RI);
743 void
744 tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
746 struct window_pane *wp = ctx->wp;
747 struct screen *s = wp->screen;
749 if (ctx->ocy != ctx->orlower)
750 return;
752 if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
753 !tty_term_has(tty->term, TTYC_CSR)) {
754 tty_redraw_region(tty, ctx);
755 return;
759 * If this line wrapped naturally (ctx->num is nonzero), don't do
760 * anything - the cursor can just be moved to the last cell and wrap
761 * naturally.
763 if (ctx->num && !(tty->term->flags & TERM_EARLYWRAP))
764 return;
766 tty_reset(tty);
768 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
769 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
771 tty_putc(tty, '\n');
774 void
775 tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
777 struct window_pane *wp = ctx->wp;
778 struct screen *s = wp->screen;
779 u_int i, j;
781 tty_reset(tty);
783 tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
784 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
786 if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
787 tty_term_has(tty->term, TTYC_EL)) {
788 tty_putcode(tty, TTYC_EL);
789 if (ctx->ocy != screen_size_y(s) - 1) {
790 tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
791 for (i = ctx->ocy + 1; i < screen_size_y(s); i++) {
792 tty_putcode(tty, TTYC_EL);
793 if (i == screen_size_y(s) - 1)
794 continue;
795 tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
796 tty->cy++;
799 } else {
800 for (i = ctx->ocx; i < screen_size_x(s); i++)
801 tty_putc(tty, ' ');
802 for (j = ctx->ocy + 1; j < screen_size_y(s); j++) {
803 tty_cursor_pane(tty, ctx, 0, j);
804 for (i = 0; i < screen_size_x(s); i++)
805 tty_putc(tty, ' ');
810 void
811 tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
813 struct window_pane *wp = ctx->wp;
814 struct screen *s = wp->screen;
815 u_int i, j;
817 tty_reset(tty);
819 tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
820 tty_cursor_pane(tty, ctx, 0, 0);
822 if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
823 tty_term_has(tty->term, TTYC_EL)) {
824 for (i = 0; i < ctx->ocy; i++) {
825 tty_putcode(tty, TTYC_EL);
826 tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
827 tty->cy++;
829 } else {
830 for (j = 0; j < ctx->ocy; j++) {
831 tty_cursor_pane(tty, ctx, 0, j);
832 for (i = 0; i < screen_size_x(s); i++)
833 tty_putc(tty, ' ');
836 for (i = 0; i <= ctx->ocx; i++)
837 tty_putc(tty, ' ');
840 void
841 tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
843 struct window_pane *wp = ctx->wp;
844 struct screen *s = wp->screen;
845 u_int i, j;
847 tty_reset(tty);
849 tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
850 tty_cursor_pane(tty, ctx, 0, 0);
852 if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
853 tty_term_has(tty->term, TTYC_EL)) {
854 for (i = 0; i < screen_size_y(s); i++) {
855 tty_putcode(tty, TTYC_EL);
856 if (i != screen_size_y(s) - 1) {
857 tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
858 tty->cy++;
861 } else {
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, ' ');
870 void
871 tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
873 struct window_pane *wp = ctx->wp;
874 struct screen *s = wp->screen;
875 u_int i, j;
877 tty_reset(tty);
879 tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
881 for (j = 0; j < screen_size_y(s); j++) {
882 tty_cursor_pane(tty, ctx, 0, j);
883 for (i = 0; i < screen_size_x(s); i++)
884 tty_putc(tty, 'E');
888 void
889 tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
891 struct window_pane *wp = ctx->wp;
892 struct screen *s = wp->screen;
893 u_int cx;
894 u_int width;
895 const struct grid_cell *gc = ctx->cell;
896 const struct grid_utf8 *gu = ctx->utf8;
898 if (gc->flags & GRID_FLAG_UTF8)
899 width = gu->width;
900 else
901 width = 1;
903 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
905 /* Is the cursor in the very last position? */
906 if (ctx->ocx > wp->sx - width) {
907 if (wp->xoff != 0 || wp->sx != tty->sx) {
909 * The pane doesn't fill the entire line, the linefeed
910 * will already have happened, so just move the cursor.
912 tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
913 } else if (tty->cx < tty->sx) {
915 * The cursor isn't in the last position already, so
916 * move as far left as possible and redraw the last
917 * cell to move into the last position.
919 cx = screen_size_x(s) - width;
920 tty_cursor_pane(tty, ctx, cx, ctx->ocy);
921 tty_cell(tty, &ctx->last_cell, &ctx->last_utf8);
923 } else
924 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
926 tty_cell(tty, ctx->cell, ctx->utf8);
929 void
930 tty_cmd_utf8character(struct tty *tty, const struct tty_ctx *ctx)
932 struct window_pane *wp = ctx->wp;
935 * Cannot rely on not being a partial character, so just redraw the
936 * whole line.
938 tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
941 void
942 tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
944 u_int i;
945 u_char *str = ctx->ptr;
947 for (i = 0; i < ctx->num; i++)
948 tty_putc(tty, str[i]);
951 void
952 tty_cell(
953 struct tty *tty, const struct grid_cell *gc, const struct grid_utf8 *gu)
955 u_int i;
957 /* Skip last character if terminal is stupid. */
958 if (tty->term->flags & TERM_EARLYWRAP &&
959 tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1)
960 return;
962 /* If this is a padding character, do nothing. */
963 if (gc->flags & GRID_FLAG_PADDING)
964 return;
966 /* Set the attributes. */
967 tty_attributes(tty, gc);
969 /* If not UTF-8, write directly. */
970 if (!(gc->flags & GRID_FLAG_UTF8)) {
971 if (gc->data < 0x20 || gc->data == 0x7f)
972 return;
973 tty_putc(tty, gc->data);
974 return;
977 /* If the terminal doesn't support UTF-8, write underscores. */
978 if (!(tty->flags & TTY_UTF8)) {
979 for (i = 0; i < gu->width; i++)
980 tty_putc(tty, '_');
981 return;
984 /* Otherwise, write UTF-8. */
985 tty_pututf8(tty, gu);
988 void
989 tty_reset(struct tty *tty)
991 struct grid_cell *gc = &tty->cell;
993 if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0)
994 return;
996 if ((gc->attr & GRID_ATTR_CHARSET) && tty_use_acs(tty))
997 tty_putcode(tty, TTYC_RMACS);
998 tty_putcode(tty, TTYC_SGR0);
999 memcpy(gc, &grid_default_cell, sizeof *gc);
1002 /* Set region inside pane. */
1003 void
1004 tty_region_pane(
1005 struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower)
1007 struct window_pane *wp = ctx->wp;
1009 tty_region(tty, wp->yoff + rupper, wp->yoff + rlower);
1012 /* Set region at absolute position. */
1013 void
1014 tty_region(struct tty *tty, u_int rupper, u_int rlower)
1016 if (tty->rlower == rlower && tty->rupper == rupper)
1017 return;
1018 if (!tty_term_has(tty->term, TTYC_CSR))
1019 return;
1021 tty->rupper = rupper;
1022 tty->rlower = rlower;
1025 * Some terminals (such as PuTTY) do not correctly reset the cursor to
1026 * 0,0 if it is beyond the last column (they do not reset their wrap
1027 * flag so further output causes a line feed). As a workaround, do an
1028 * explicit move to 0 first.
1030 if (tty->cx >= tty->sx)
1031 tty_cursor(tty, 0, tty->cy);
1033 tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower);
1034 tty_cursor(tty, 0, 0);
1037 /* Move cursor inside pane. */
1038 void
1039 tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy)
1041 struct window_pane *wp = ctx->wp;
1043 tty_cursor(tty, wp->xoff + cx, wp->yoff + cy);
1046 /* Move cursor to absolute position. */
1047 void
1048 tty_cursor(struct tty *tty, u_int cx, u_int cy)
1050 struct tty_term *term = tty->term;
1051 u_int thisx, thisy;
1052 int change;
1054 if (cx > tty->sx - 1)
1055 cx = tty->sx - 1;
1057 thisx = tty->cx;
1058 thisy = tty->cy;
1060 /* No change. */
1061 if (cx == thisx && cy == thisy)
1062 return;
1064 /* Very end of the line, just use absolute movement. */
1065 if (thisx > tty->sx - 1)
1066 goto absolute;
1068 /* Move to home position (0, 0). */
1069 if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) {
1070 tty_putcode(tty, TTYC_HOME);
1071 goto out;
1074 /* Zero on the next line. */
1075 if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower) {
1076 tty_putc(tty, '\r');
1077 tty_putc(tty, '\n');
1078 goto out;
1081 /* Moving column or row. */
1082 if (cy == thisy) {
1084 * Moving column only, row staying the same.
1087 /* To left edge. */
1088 if (cx == 0) {
1089 tty_putc(tty, '\r');
1090 goto out;
1093 /* One to the left. */
1094 if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) {
1095 tty_putcode(tty, TTYC_CUB1);
1096 goto out;
1099 /* One to the right. */
1100 if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) {
1101 tty_putcode(tty, TTYC_CUF1);
1102 goto out;
1105 /* Calculate difference. */
1106 change = thisx - cx; /* +ve left, -ve right */
1109 * Use HPA if change is larger than absolute, otherwise move
1110 * the cursor with CUB/CUF.
1112 if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) {
1113 tty_putcode1(tty, TTYC_HPA, cx);
1114 goto out;
1115 } else if (change > 0 && tty_term_has(term, TTYC_CUB)) {
1116 tty_putcode1(tty, TTYC_CUB, change);
1117 goto out;
1118 } else if (change < 0 && tty_term_has(term, TTYC_CUF)) {
1119 tty_putcode1(tty, TTYC_CUF, -change);
1120 goto out;
1122 } else if (cx == thisx) {
1124 * Moving row only, column staying the same.
1127 /* One above. */
1128 if (thisy != tty->rupper &&
1129 cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) {
1130 tty_putcode(tty, TTYC_CUU1);
1131 goto out;
1134 /* One below. */
1135 if (thisy != tty->rlower &&
1136 cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) {
1137 tty_putcode(tty, TTYC_CUD1);
1138 goto out;
1141 /* Calculate difference. */
1142 change = thisy - cy; /* +ve up, -ve down */
1145 * Try to use VPA if change is larger than absolute or if this
1146 * change would cross the scroll region, otherwise use CUU/CUD.
1148 if ((u_int) abs(change) > cy ||
1149 (change < 0 && cy - change > tty->rlower) ||
1150 (change > 0 && cy - change < tty->rupper)) {
1151 if (tty_term_has(term, TTYC_VPA)) {
1152 tty_putcode1(tty, TTYC_VPA, cy);
1153 goto out;
1155 } else if (change > 0 && tty_term_has(term, TTYC_CUU)) {
1156 tty_putcode1(tty, TTYC_CUU, change);
1157 goto out;
1158 } else if (change < 0 && tty_term_has(term, TTYC_CUD)) {
1159 tty_putcode1(tty, TTYC_CUD, -change);
1160 goto out;
1164 absolute:
1165 /* Absolute movement. */
1166 tty_putcode2(tty, TTYC_CUP, cy, cx);
1168 out:
1169 tty->cx = cx;
1170 tty->cy = cy;
1173 void
1174 tty_attributes(struct tty *tty, const struct grid_cell *gc)
1176 struct grid_cell *tc = &tty->cell, gc2;
1177 u_char changed;
1179 memcpy(&gc2, gc, sizeof gc2);
1182 * If no setab, try to use the reverse attribute as a best-effort for a
1183 * non-default background. This is a bit of a hack but it doesn't do
1184 * any serious harm and makes a couple of applications happier.
1186 if (!tty_term_has(tty->term, TTYC_SETAB)) {
1187 if (gc2.attr & GRID_ATTR_REVERSE) {
1188 if (gc2.fg != 7 && gc2.fg != 8)
1189 gc2.attr &= ~GRID_ATTR_REVERSE;
1190 } else {
1191 if (gc2.bg != 0 && gc2.bg != 8)
1192 gc2.attr |= GRID_ATTR_REVERSE;
1196 /* Fix up the colours if necessary. */
1197 tty_check_fg(tty, &gc2);
1198 tty_check_bg(tty, &gc2);
1200 /* If any bits are being cleared, reset everything. */
1201 if (tc->attr & ~gc2.attr)
1202 tty_reset(tty);
1205 * Set the colours. This may call tty_reset() (so it comes next) and
1206 * may add to (NOT remove) the desired attributes by changing new_attr.
1208 tty_colours(tty, &gc2);
1210 /* Filter out attribute bits already set. */
1211 changed = gc2.attr & ~tc->attr;
1212 tc->attr = gc2.attr;
1214 /* Set the attributes. */
1215 if (changed & GRID_ATTR_BRIGHT)
1216 tty_putcode(tty, TTYC_BOLD);
1217 if (changed & GRID_ATTR_DIM)
1218 tty_putcode(tty, TTYC_DIM);
1219 if (changed & GRID_ATTR_ITALICS)
1221 if (tty_term_has(tty->term, TTYC_SITM))
1222 tty_putcode(tty, TTYC_SITM);
1223 else
1224 tty_putcode(tty, TTYC_SMSO);
1226 if (changed & GRID_ATTR_UNDERSCORE)
1227 tty_putcode(tty, TTYC_SMUL);
1228 if (changed & GRID_ATTR_BLINK)
1229 tty_putcode(tty, TTYC_BLINK);
1230 if (changed & GRID_ATTR_REVERSE) {
1231 if (tty_term_has(tty->term, TTYC_REV))
1232 tty_putcode(tty, TTYC_REV);
1233 else if (tty_term_has(tty->term, TTYC_SMSO))
1234 tty_putcode(tty, TTYC_SMSO);
1236 if (changed & GRID_ATTR_HIDDEN)
1237 tty_putcode(tty, TTYC_INVIS);
1238 if ((changed & GRID_ATTR_CHARSET) && tty_use_acs(tty))
1239 tty_putcode(tty, TTYC_SMACS);
1242 void
1243 tty_colours(struct tty *tty, const struct grid_cell *gc)
1245 struct grid_cell *tc = &tty->cell;
1246 u_char fg = gc->fg, bg = gc->bg, flags = gc->flags;
1247 int have_ax, fg_default, bg_default;
1249 /* No changes? Nothing is necessary. */
1250 if (fg == tc->fg && bg == tc->bg &&
1251 ((flags ^ tc->flags) & (GRID_FLAG_FG256|GRID_FLAG_BG256)) == 0)
1252 return;
1255 * Is either the default colour? This is handled specially because the
1256 * best solution might be to reset both colours to default, in which
1257 * case if only one is default need to fall onward to set the other
1258 * colour.
1260 fg_default = (fg == 8 && !(flags & GRID_FLAG_FG256));
1261 bg_default = (bg == 8 && !(flags & GRID_FLAG_BG256));
1262 if (fg_default || bg_default) {
1264 * If don't have AX but do have op, send sgr0 (op can't
1265 * actually be used because it is sometimes the same as sgr0
1266 * and sometimes isn't). This resets both colours to default.
1268 * Otherwise, try to set the default colour only as needed.
1270 have_ax = tty_term_has(tty->term, TTYC_AX);
1271 if (!have_ax && tty_term_has(tty->term, TTYC_OP))
1272 tty_reset(tty);
1273 else {
1274 if (fg_default &&
1275 (tc->fg != 8 || tc->flags & GRID_FLAG_FG256)) {
1276 if (have_ax)
1277 tty_puts(tty, "\033[39m");
1278 else if (tc->fg != 7 ||
1279 tc->flags & GRID_FLAG_FG256)
1280 tty_putcode1(tty, TTYC_SETAF, 7);
1281 tc->fg = 8;
1282 tc->flags &= ~GRID_FLAG_FG256;
1284 if (bg_default &&
1285 (tc->bg != 8 || tc->flags & GRID_FLAG_BG256)) {
1286 if (have_ax)
1287 tty_puts(tty, "\033[49m");
1288 else if (tc->bg != 0 ||
1289 tc->flags & GRID_FLAG_BG256)
1290 tty_putcode1(tty, TTYC_SETAB, 0);
1291 tc->bg = 8;
1292 tc->flags &= ~GRID_FLAG_BG256;
1297 /* Set the foreground colour. */
1298 if (!fg_default && (fg != tc->fg ||
1299 ((flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256))))
1300 tty_colours_fg(tty, gc);
1303 * Set the background colour. This must come after the foreground as
1304 * tty_colour_fg() can call tty_reset().
1306 if (!bg_default && (bg != tc->bg ||
1307 ((flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256))))
1308 tty_colours_bg(tty, gc);
1311 void
1312 tty_check_fg(struct tty *tty, struct grid_cell *gc)
1314 u_int colours;
1316 /* Is this a 256-colour colour? */
1317 if (gc->flags & GRID_FLAG_FG256) {
1318 /* And not a 256 colour mode? */
1319 if (!(tty->term->flags & TERM_88COLOURS) &&
1320 !(tty->term_flags & TERM_88COLOURS) &&
1321 !(tty->term->flags & TERM_256COLOURS) &&
1322 !(tty->term_flags & TERM_256COLOURS)) {
1323 gc->fg = colour_256to16(gc->fg);
1324 if (gc->fg & 8) {
1325 gc->fg &= 7;
1326 gc->attr |= GRID_ATTR_BRIGHT;
1327 } else
1328 gc->attr &= ~GRID_ATTR_BRIGHT;
1329 gc->flags &= ~GRID_FLAG_FG256;
1331 return;
1334 /* Is this an aixterm colour? */
1335 colours = tty_term_number(tty->term, TTYC_COLORS);
1336 if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) {
1337 gc->fg -= 90;
1338 gc->attr |= GRID_ATTR_BRIGHT;
1342 void
1343 tty_check_bg(struct tty *tty, struct grid_cell *gc)
1345 u_int colours;
1347 /* Is this a 256-colour colour? */
1348 if (gc->flags & GRID_FLAG_BG256) {
1350 * And not a 256 colour mode? Translate to 16-colour
1351 * palette. Bold background doesn't exist portably, so just
1352 * discard the bold bit if set.
1354 if (!(tty->term->flags & TERM_88COLOURS) &&
1355 !(tty->term_flags & TERM_88COLOURS) &&
1356 !(tty->term->flags & TERM_256COLOURS) &&
1357 !(tty->term_flags & TERM_256COLOURS)) {
1358 gc->bg = colour_256to16(gc->bg);
1359 if (gc->bg & 8)
1360 gc->bg &= 7;
1361 gc->attr &= ~GRID_ATTR_BRIGHT;
1362 gc->flags &= ~GRID_FLAG_BG256;
1364 return;
1367 /* Is this an aixterm colour? */
1368 colours = tty_term_number(tty->term, TTYC_COLORS);
1369 if (gc->bg >= 100 && gc->bg <= 107 && colours < 16) {
1370 gc->bg -= 90;
1371 gc->attr |= GRID_ATTR_BRIGHT;
1375 void
1376 tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
1378 struct grid_cell *tc = &tty->cell;
1379 u_char fg = gc->fg;
1380 char s[32];
1382 /* Is this a 256-colour colour? */
1383 if (gc->flags & GRID_FLAG_FG256) {
1384 /* Try as 256 colours or translating to 88. */
1385 if (tty_try_256(tty, fg, "38") == 0)
1386 goto save_fg;
1387 if (tty_try_88(tty, fg, "38") == 0)
1388 goto save_fg;
1389 /* Else already handled by tty_check_fg. */
1390 return;
1393 /* Is this an aixterm bright colour? */
1394 if (fg >= 90 && fg <= 97) {
1395 xsnprintf(s, sizeof s, "\033[%dm", fg);
1396 tty_puts(tty, s);
1397 goto save_fg;
1400 /* Otherwise set the foreground colour. */
1401 tty_putcode1(tty, TTYC_SETAF, fg);
1403 save_fg:
1404 /* Save the new values in the terminal current cell. */
1405 tc->fg = fg;
1406 tc->flags &= ~GRID_FLAG_FG256;
1407 tc->flags |= gc->flags & GRID_FLAG_FG256;
1410 void
1411 tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
1413 struct grid_cell *tc = &tty->cell;
1414 u_char bg = gc->bg;
1415 char s[32];
1417 /* Is this a 256-colour colour? */
1418 if (gc->flags & GRID_FLAG_BG256) {
1419 /* Try as 256 colours or translating to 88. */
1420 if (tty_try_256(tty, bg, "48") == 0)
1421 goto save_bg;
1422 if (tty_try_88(tty, bg, "48") == 0)
1423 goto save_bg;
1424 /* Else already handled by tty_check_bg. */
1425 return;
1428 /* Is this an aixterm bright colour? */
1429 if (bg >= 100 && bg <= 107) {
1430 /* 16 colour terminals or above only. */
1431 if (tty_term_number(tty->term, TTYC_COLORS) >= 16) {
1432 xsnprintf(s, sizeof s, "\033[%dm", bg);
1433 tty_puts(tty, s);
1434 goto save_bg;
1436 bg -= 100;
1437 /* no such thing as a bold background */
1440 /* Otherwise set the background colour. */
1441 tty_putcode1(tty, TTYC_SETAB, bg);
1443 save_bg:
1444 /* Save the new values in the terminal current cell. */
1445 tc->bg = bg;
1446 tc->flags &= ~GRID_FLAG_BG256;
1447 tc->flags |= gc->flags & GRID_FLAG_BG256;
1451 tty_try_256(struct tty *tty, u_char colour, const char *type)
1453 char s[32];
1455 if (!(tty->term->flags & TERM_256COLOURS) &&
1456 !(tty->term_flags & TERM_256COLOURS))
1457 return (-1);
1459 xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1460 tty_puts(tty, s);
1461 return (0);
1465 tty_try_88(struct tty *tty, u_char colour, const char *type)
1467 char s[32];
1469 if (!(tty->term->flags & TERM_88COLOURS) &&
1470 !(tty->term_flags & TERM_88COLOURS))
1471 return (-1);
1472 colour = colour_256to88(colour);
1474 xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1475 tty_puts(tty, s);
1476 return (0);