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