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