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