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