1 /* $OpenBSD: tty.c,v 1.46 2009/10/12 17:19:47 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/ioctl.h> 21 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <termios.h> 27 #include <unistd.h> 28 29 #include "tmux.h" 30 31 void tty_fill_acs(struct tty *); 32 33 int tty_try_256(struct tty *, u_char, const char *); 34 int tty_try_88(struct tty *, u_char, const char *); 35 36 void tty_attributes_fg(struct tty *, const struct grid_cell *); 37 void tty_attributes_bg(struct tty *, const struct grid_cell *); 38 39 void tty_redraw_region(struct tty *, const struct tty_ctx *); 40 void tty_emulate_repeat( 41 struct tty *, enum tty_code_code, enum tty_code_code, u_int); 42 void tty_cell(struct tty *, 43 const struct grid_cell *, const struct grid_utf8 *); 44 45 void 46 tty_init(struct tty *tty, int fd, char *term) 47 { 48 char *path; 49 50 memset(tty, 0, sizeof *tty); 51 tty->log_fd = -1; 52 53 if (term == NULL || *term == '\0') 54 tty->termname = xstrdup("unknown"); 55 else 56 tty->termname = xstrdup(term); 57 58 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 59 fatal("fcntl failed"); 60 tty->fd = fd; 61 62 if ((path = ttyname(fd)) == NULL) 63 fatalx("ttyname failed"); 64 tty->path = xstrdup(path); 65 66 tty->flags = 0; 67 tty->term_flags = 0; 68 } 69 70 void 71 tty_resize(struct tty *tty) 72 { 73 struct winsize ws; 74 75 if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) { 76 tty->sx = ws.ws_col; 77 tty->sy = ws.ws_row; 78 } 79 if (tty->sx == 0) 80 tty->sx = 80; 81 if (tty->sy == 0) 82 tty->sy = 24; 83 84 tty->cx = UINT_MAX; 85 tty->cy = UINT_MAX; 86 87 tty->rupper = UINT_MAX; 88 tty->rlower = UINT_MAX; 89 } 90 91 int 92 tty_open(struct tty *tty, const char *overrides, char **cause) 93 { 94 int fd; 95 96 if (debug_level > 3) { 97 fd = open("tmux.out", O_WRONLY|O_CREAT|O_TRUNC, 0644); 98 if (fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 99 fatal("fcntl failed"); 100 tty->log_fd = fd; 101 } 102 103 tty->term = tty_term_find(tty->termname, tty->fd, overrides, cause); 104 if (tty->term == NULL) { 105 tty_close(tty); 106 return (-1); 107 } 108 tty->flags |= TTY_OPENED; 109 110 tty->in = buffer_create(BUFSIZ); 111 tty->out = buffer_create(BUFSIZ); 112 113 tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_ESCAPE); 114 115 tty_start_tty(tty); 116 117 tty_keys_init(tty); 118 119 tty_fill_acs(tty); 120 121 return (0); 122 } 123 124 void 125 tty_start_tty(struct tty *tty) 126 { 127 struct termios tio; 128 int what, mode; 129 130 if (tty->fd == -1) 131 return; 132 133 if ((mode = fcntl(tty->fd, F_GETFL)) == -1) 134 fatal("fcntl failed"); 135 if (fcntl(tty->fd, F_SETFL, mode|O_NONBLOCK) == -1) 136 fatal("fcntl failed"); 137 138 if (tcgetattr(tty->fd, &tty->tio) != 0) 139 fatal("tcgetattr failed"); 140 memcpy(&tio, &tty->tio, sizeof tio); 141 tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP); 142 tio.c_iflag |= IGNBRK; 143 tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET); 144 tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL| 145 ECHOPRT|ECHOKE|ECHOCTL|ISIG); 146 tio.c_cc[VMIN] = 1; 147 tio.c_cc[VTIME] = 0; 148 if (tcsetattr(tty->fd, TCSANOW, &tio) != 0) 149 fatal("tcsetattr failed"); 150 151 what = 0; 152 if (ioctl(tty->fd, TIOCFLUSH, &what) != 0) 153 fatal("ioctl(TIOCFLUSH)"); 154 155 tty_putcode(tty, TTYC_SMCUP); 156 157 tty_putcode(tty, TTYC_SGR0); 158 memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell); 159 160 tty_putcode(tty, TTYC_SMKX); 161 tty_putcode(tty, TTYC_ENACS); 162 tty_putcode(tty, TTYC_CLEAR); 163 164 tty_putcode(tty, TTYC_CNORM); 165 if (tty_term_has(tty->term, TTYC_KMOUS)) 166 tty_puts(tty, "\033[?1000l"); 167 168 tty->cx = UINT_MAX; 169 tty->cy = UINT_MAX; 170 171 tty->rlower = UINT_MAX; 172 tty->rupper = UINT_MAX; 173 174 tty->mode = MODE_CURSOR; 175 176 tty->flags |= TTY_STARTED; 177 } 178 179 void 180 tty_stop_tty(struct tty *tty) 181 { 182 struct winsize ws; 183 int mode; 184 185 if (!(tty->flags & TTY_STARTED)) 186 return; 187 tty->flags &= ~TTY_STARTED; 188 189 /* 190 * Be flexible about error handling and try not kill the server just 191 * because the fd is invalid. Things like ssh -t can easily leave us 192 * with a dead tty. 193 */ 194 if ((mode = fcntl(tty->fd, F_GETFL)) == -1) 195 return; 196 if (fcntl(tty->fd, F_SETFL, mode & ~O_NONBLOCK) == -1) 197 return; 198 if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1) 199 return; 200 if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1) 201 return; 202 203 tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); 204 tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS)); 205 tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); 206 tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); 207 tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR)); 208 209 tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); 210 if (tty_term_has(tty->term, TTYC_KMOUS)) 211 tty_raw(tty, "\033[?1000l"); 212 213 tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); 214 } 215 216 void 217 tty_fill_acs(struct tty *tty) 218 { 219 const char *ptr; 220 221 memset(tty->acs, 0, sizeof tty->acs); 222 if (!tty_term_has(tty->term, TTYC_ACSC)) 223 return; 224 225 ptr = tty_term_string(tty->term, TTYC_ACSC); 226 if (strlen(ptr) % 2 != 0) 227 return; 228 for (; *ptr != '\0'; ptr += 2) 229 tty->acs[(u_char) ptr[0]] = ptr[1]; 230 } 231 232 u_char 233 tty_get_acs(struct tty *tty, u_char ch) 234 { 235 if (tty->acs[ch] != '\0') 236 return (tty->acs[ch]); 237 return (ch); 238 } 239 240 void 241 tty_close(struct tty *tty) 242 { 243 if (tty->log_fd != -1) { 244 close(tty->log_fd); 245 tty->log_fd = -1; 246 } 247 248 tty_stop_tty(tty); 249 250 if (tty->flags & TTY_OPENED) { 251 tty_term_free(tty->term); 252 tty_keys_free(tty); 253 254 buffer_destroy(tty->in); 255 buffer_destroy(tty->out); 256 257 tty->flags &= ~TTY_OPENED; 258 } 259 260 if (tty->fd != -1) { 261 close(tty->fd); 262 tty->fd = -1; 263 } 264 } 265 266 void 267 tty_free(struct tty *tty) 268 { 269 tty_close(tty); 270 271 if (tty->path != NULL) 272 xfree(tty->path); 273 if (tty->termname != NULL) 274 xfree(tty->termname); 275 } 276 277 void 278 tty_raw(struct tty *tty, const char *s) 279 { 280 write(tty->fd, s, strlen(s)); 281 } 282 283 void 284 tty_putcode(struct tty *tty, enum tty_code_code code) 285 { 286 tty_puts(tty, tty_term_string(tty->term, code)); 287 } 288 289 void 290 tty_putcode1(struct tty *tty, enum tty_code_code code, int a) 291 { 292 if (a < 0) 293 return; 294 tty_puts(tty, tty_term_string1(tty->term, code, a)); 295 } 296 297 void 298 tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b) 299 { 300 if (a < 0 || b < 0) 301 return; 302 tty_puts(tty, tty_term_string2(tty->term, code, a, b)); 303 } 304 305 void 306 tty_puts(struct tty *tty, const char *s) 307 { 308 if (*s == '\0') 309 return; 310 buffer_write(tty->out, s, strlen(s)); 311 312 if (tty->log_fd != -1) 313 write(tty->log_fd, s, strlen(s)); 314 } 315 316 void 317 tty_putc(struct tty *tty, u_char ch) 318 { 319 u_int sx; 320 321 if (tty->cell.attr & GRID_ATTR_CHARSET) 322 ch = tty_get_acs(tty, ch); 323 buffer_write8(tty->out, ch); 324 325 if (ch >= 0x20 && ch != 0x7f) { 326 sx = tty->sx; 327 if (tty->term->flags & TERM_EARLYWRAP) 328 sx--; 329 330 if (tty->cx >= sx) { 331 tty->cx = 1; 332 if (tty->cy != tty->rlower) 333 tty->cy++; 334 } else 335 tty->cx++; 336 } 337 338 if (tty->log_fd != -1) 339 write(tty->log_fd, &ch, 1); 340 } 341 342 void 343 tty_pututf8(struct tty *tty, const struct grid_utf8 *gu) 344 { 345 u_int i, width; 346 347 for (i = 0; i < UTF8_SIZE; i++) { 348 if (gu->data[i] == 0xff) 349 break; 350 buffer_write8(tty->out, gu->data[i]); 351 if (tty->log_fd != -1) 352 write(tty->log_fd, &gu->data[i], 1); 353 } 354 355 width = utf8_width(gu->data); 356 tty->cx += width; 357 } 358 359 void 360 tty_set_title(struct tty *tty, const char *title) 361 { 362 if (strstr(tty->termname, "xterm") == NULL && 363 strstr(tty->termname, "rxvt") == NULL && 364 strcmp(tty->termname, "screen") != 0) 365 return; 366 367 tty_puts(tty, "\033]0;"); 368 tty_puts(tty, title); 369 tty_putc(tty, '\007'); 370 } 371 372 void 373 tty_update_mode(struct tty *tty, int mode) 374 { 375 int changed; 376 377 if (tty->flags & TTY_NOCURSOR) 378 mode &= ~MODE_CURSOR; 379 380 changed = mode ^ tty->mode; 381 if (changed & MODE_CURSOR) { 382 if (mode & MODE_CURSOR) 383 tty_putcode(tty, TTYC_CNORM); 384 else 385 tty_putcode(tty, TTYC_CIVIS); 386 } 387 if (changed & MODE_MOUSE) { 388 if (mode & MODE_MOUSE) 389 tty_puts(tty, "\033[?1000h"); 390 else 391 tty_puts(tty, "\033[?1000l"); 392 } 393 tty->mode = mode; 394 } 395 396 void 397 tty_emulate_repeat( 398 struct tty *tty, enum tty_code_code code, enum tty_code_code code1, u_int n) 399 { 400 if (tty_term_has(tty->term, code)) 401 tty_putcode1(tty, code, n); 402 else { 403 while (n-- > 0) 404 tty_putcode(tty, code1); 405 } 406 } 407 408 /* 409 * Redraw scroll region using data from screen (already updated). Used when 410 * CSR not supported, or window is a pane that doesn't take up the full 411 * width of the terminal. 412 */ 413 void 414 tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) 415 { 416 struct window_pane *wp = ctx->wp; 417 struct screen *s = wp->screen; 418 u_int i; 419 420 /* 421 * If region is >= 50% of the screen, just schedule a window redraw. In 422 * most cases, this is likely to be followed by some more scrolling - 423 * without this, the entire pane ends up being redrawn many times which 424 * can be much more data. 425 */ 426 if (ctx->orupper - ctx->orlower >= screen_size_y(s) / 2) { 427 wp->flags |= PANE_REDRAW; 428 return; 429 } 430 431 if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) { 432 for (i = ctx->ocy; i < screen_size_y(s); i++) 433 tty_draw_line(tty, s, i, wp->xoff, wp->yoff); 434 } else { 435 for (i = ctx->orupper; i <= ctx->orlower; i++) 436 tty_draw_line(tty, s, i, wp->xoff, wp->yoff); 437 } 438 } 439 440 void 441 tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy) 442 { 443 const struct grid_cell *gc; 444 struct grid_line *gl; 445 struct grid_cell tmpgc; 446 const struct grid_utf8 *gu; 447 u_int i, sx; 448 449 tty_update_mode(tty, tty->mode & ~MODE_CURSOR); 450 451 sx = screen_size_x(s); 452 if (sx > s->grid->linedata[s->grid->hsize + py].cellsize) 453 sx = s->grid->linedata[s->grid->hsize + py].cellsize; 454 if (sx > tty->sx) 455 sx = tty->sx; 456 457 /* 458 * Don't move the cursor to the start permission if it will wrap there 459 * itself; much the same as the conditions in tty_cmd_cell. 460 */ 461 gl = NULL; 462 if (py != 0) 463 gl = &s->grid->linedata[s->grid->hsize + py - 1]; 464 if (ox != 0 || (gl != NULL && !(gl->flags & GRID_LINE_WRAPPED)) || 465 tty->cy != oy + py - 1 || tty->cx < tty->sx) 466 tty_cursor(tty, ox, oy + py); 467 468 for (i = 0; i < sx; i++) { 469 gc = grid_view_peek_cell(s->grid, i, py); 470 471 gu = NULL; 472 if (gc->flags & GRID_FLAG_UTF8) 473 gu = grid_view_peek_utf8(s->grid, i, py); 474 475 if (screen_check_selection(s, i, py)) { 476 memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc); 477 tmpgc.data = gc->data; 478 tmpgc.flags = gc->flags & 479 ~(GRID_FLAG_FG256|GRID_FLAG_BG256); 480 tmpgc.flags |= s->sel.cell.flags & 481 (GRID_FLAG_FG256|GRID_FLAG_BG256); 482 tty_cell(tty, &tmpgc, gu); 483 } else 484 tty_cell(tty, gc, gu); 485 } 486 487 if (sx >= tty->sx) { 488 tty_update_mode(tty, tty->mode); 489 return; 490 } 491 tty_reset(tty); 492 493 tty_cursor(tty, ox + sx, oy + py); 494 if (screen_size_x(s) >= tty->sx && tty_term_has(tty->term, TTYC_EL)) 495 tty_putcode(tty, TTYC_EL); 496 else { 497 for (i = sx; i < screen_size_x(s); i++) 498 tty_putc(tty, ' '); 499 } 500 tty_update_mode(tty, tty->mode); 501 } 502 503 void 504 tty_write(void (*cmdfn)( 505 struct tty *, const struct tty_ctx *), const struct tty_ctx *ctx) 506 { 507 struct window_pane *wp = ctx->wp; 508 struct client *c; 509 u_int i; 510 511 if (wp == NULL) 512 return; 513 514 if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW) 515 return; 516 if (wp->window->flags & WINDOW_HIDDEN || !window_pane_visible(wp)) 517 return; 518 519 for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 520 c = ARRAY_ITEM(&clients, i); 521 if (c == NULL || c->session == NULL) 522 continue; 523 if (c->flags & CLIENT_SUSPENDED) 524 continue; 525 526 if (c->session->curw->window == wp->window) { 527 if (c->tty.flags & TTY_FREEZE || c->tty.term == NULL) 528 continue; 529 cmdfn(&c->tty, ctx); 530 } 531 } 532 } 533 534 void 535 tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) 536 { 537 struct window_pane *wp = ctx->wp; 538 struct screen *s = wp->screen; 539 u_int i; 540 541 if (wp->xoff != 0 || screen_size_x(s) < tty->sx) { 542 tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff); 543 return; 544 } 545 546 tty_reset(tty); 547 548 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 549 550 if (tty_term_has(tty->term, TTYC_ICH) || 551 tty_term_has(tty->term, TTYC_ICH1)) 552 tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num); 553 else { 554 tty_putcode(tty, TTYC_SMIR); 555 for (i = 0; i < ctx->num; i++) 556 tty_putc(tty, ' '); 557 tty_putcode(tty, TTYC_RMIR); 558 } 559 } 560 561 void 562 tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) 563 { 564 struct window_pane *wp = ctx->wp; 565 struct screen *s = wp->screen; 566 567 if (wp->xoff != 0 || screen_size_x(s) < tty->sx || 568 (!tty_term_has(tty->term, TTYC_DCH) && 569 !tty_term_has(tty->term, TTYC_DCH1))) { 570 tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff); 571 return; 572 } 573 574 tty_reset(tty); 575 576 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 577 578 if (tty_term_has(tty->term, TTYC_DCH) || 579 tty_term_has(tty->term, TTYC_DCH1)) 580 tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num); 581 } 582 583 void 584 tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) 585 { 586 struct window_pane *wp = ctx->wp; 587 struct screen *s = wp->screen; 588 589 if (wp->xoff != 0 || screen_size_x(s) < tty->sx || 590 !tty_term_has(tty->term, TTYC_CSR)) { 591 tty_redraw_region(tty, ctx); 592 return; 593 } 594 595 tty_reset(tty); 596 597 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 598 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 599 600 tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num); 601 } 602 603 void 604 tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) 605 { 606 struct window_pane *wp = ctx->wp; 607 struct screen *s = wp->screen; 608 609 if (wp->xoff != 0 || screen_size_x(s) < tty->sx || 610 !tty_term_has(tty->term, TTYC_CSR)) { 611 tty_redraw_region(tty, ctx); 612 return; 613 } 614 615 tty_reset(tty); 616 617 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 618 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 619 620 tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num); 621 } 622 623 void 624 tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) 625 { 626 struct window_pane *wp = ctx->wp; 627 struct screen *s = wp->screen; 628 u_int i; 629 630 tty_reset(tty); 631 632 tty_cursor_pane(tty, ctx, 0, ctx->ocy); 633 634 if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && 635 tty_term_has(tty->term, TTYC_EL)) { 636 tty_putcode(tty, TTYC_EL); 637 } else { 638 for (i = 0; i < screen_size_x(s); i++) 639 tty_putc(tty, ' '); 640 } 641 } 642 643 void 644 tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) 645 { 646 struct window_pane *wp = ctx->wp; 647 struct screen *s = wp->screen; 648 u_int i; 649 650 tty_reset(tty); 651 652 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 653 654 if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && 655 tty_term_has(tty->term, TTYC_EL)) 656 tty_putcode(tty, TTYC_EL); 657 else { 658 for (i = ctx->ocx; i < screen_size_x(s); i++) 659 tty_putc(tty, ' '); 660 } 661 } 662 663 void 664 tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) 665 { 666 struct window_pane *wp = ctx->wp; 667 u_int i; 668 669 tty_reset(tty); 670 671 if (wp->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) { 672 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 673 tty_putcode(tty, TTYC_EL1); 674 } else { 675 tty_cursor_pane(tty, ctx, 0, ctx->ocy); 676 for (i = 0; i < ctx->ocx + 1; i++) 677 tty_putc(tty, ' '); 678 } 679 } 680 681 void 682 tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) 683 { 684 struct window_pane *wp = ctx->wp; 685 struct screen *s = wp->screen; 686 687 if (wp->xoff != 0 || screen_size_x(s) < tty->sx || 688 !tty_term_has(tty->term, TTYC_CSR)) { 689 tty_redraw_region(tty, ctx); 690 return; 691 } 692 693 if (ctx->ocy == ctx->orupper) { 694 tty_reset(tty); 695 696 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 697 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); 698 699 tty_putcode(tty, TTYC_RI); 700 } 701 } 702 703 void 704 tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) 705 { 706 struct window_pane *wp = ctx->wp; 707 struct screen *s = wp->screen; 708 709 if (wp->xoff != 0 || screen_size_x(s) < tty->sx || 710 !tty_term_has(tty->term, TTYC_CSR)) { 711 tty_redraw_region(tty, ctx); 712 return; 713 } 714 715 if (ctx->ocy == ctx->orlower) { 716 tty_reset(tty); 717 718 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 719 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 720 721 tty_putc(tty, '\n'); 722 } 723 } 724 725 void 726 tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) 727 { 728 struct window_pane *wp = ctx->wp; 729 struct screen *s = wp->screen; 730 u_int i, j; 731 732 tty_reset(tty); 733 734 tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); 735 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 736 737 if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && 738 tty_term_has(tty->term, TTYC_EL)) { 739 tty_putcode(tty, TTYC_EL); 740 if (ctx->ocy != screen_size_y(s) - 1) { 741 tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1); 742 for (i = ctx->ocy + 1; i < screen_size_y(s); i++) { 743 tty_putcode(tty, TTYC_EL); 744 if (i == screen_size_y(s) - 1) 745 continue; 746 tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); 747 tty->cy++; 748 } 749 } 750 } else { 751 for (i = ctx->ocx; i < screen_size_x(s); i++) 752 tty_putc(tty, ' '); 753 for (j = ctx->ocy; j < screen_size_y(s); j++) { 754 tty_cursor_pane(tty, ctx, 0, j); 755 for (i = 0; i < screen_size_x(s); i++) 756 tty_putc(tty, ' '); 757 } 758 } 759 } 760 761 void 762 tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) 763 { 764 struct window_pane *wp = ctx->wp; 765 struct screen *s = wp->screen; 766 u_int i, j; 767 768 tty_reset(tty); 769 770 tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); 771 tty_cursor_pane(tty, ctx, 0, 0); 772 773 if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && 774 tty_term_has(tty->term, TTYC_EL)) { 775 for (i = 0; i < ctx->ocy; i++) { 776 tty_putcode(tty, TTYC_EL); 777 tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); 778 tty->cy++; 779 } 780 } else { 781 for (j = 0; j < ctx->ocy; j++) { 782 tty_cursor_pane(tty, ctx, 0, j); 783 for (i = 0; i < screen_size_x(s); i++) 784 tty_putc(tty, ' '); 785 } 786 } 787 for (i = 0; i <= ctx->ocx; i++) 788 tty_putc(tty, ' '); 789 } 790 791 void 792 tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) 793 { 794 struct window_pane *wp = ctx->wp; 795 struct screen *s = wp->screen; 796 u_int i, j; 797 798 tty_reset(tty); 799 800 tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); 801 tty_cursor_pane(tty, ctx, 0, 0); 802 803 if (wp->xoff == 0 && screen_size_x(s) >= tty->sx && 804 tty_term_has(tty->term, TTYC_EL)) { 805 for (i = 0; i < screen_size_y(s); i++) { 806 tty_putcode(tty, TTYC_EL); 807 if (i != screen_size_y(s) - 1) { 808 tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1); 809 tty->cy++; 810 } 811 } 812 } else { 813 for (j = 0; j < screen_size_y(s); j++) { 814 tty_cursor_pane(tty, ctx, 0, j); 815 for (i = 0; i < screen_size_x(s); i++) 816 tty_putc(tty, ' '); 817 } 818 } 819 } 820 821 void 822 tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) 823 { 824 struct window_pane *wp = ctx->wp; 825 struct screen *s = wp->screen; 826 u_int i, j; 827 828 tty_reset(tty); 829 830 tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1); 831 832 for (j = 0; j < screen_size_y(s); j++) { 833 tty_cursor_pane(tty, ctx, 0, j); 834 for (i = 0; i < screen_size_x(s); i++) 835 tty_putc(tty, 'E'); 836 } 837 } 838 839 void 840 tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) 841 { 842 struct window_pane *wp = ctx->wp; 843 struct screen *s = wp->screen; 844 struct grid_line *gl; 845 u_int wx, wy; 846 847 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 848 849 wx = ctx->ocx + wp->xoff; 850 wy = ctx->ocy + wp->yoff; 851 852 /* 853 * If: 854 * 855 * - the line was wrapped: 856 * - the cursor is beyond the edge of the screen, 857 * - the desired position is at the left, 858 * - and either a) the desired next line is the one below the current 859 * or b) the current line is the bottom of the scroll region, 860 * 861 * Then just printing the next character will be enough to scroll into 862 * place, so don't do an explicit cursor move. 863 */ 864 gl = NULL; 865 if (ctx->ocy != 0) 866 gl = &s->grid->linedata[s->grid->hsize + ctx->ocy - 1]; 867 if (wy == 0 || (gl != NULL && !(gl->flags & GRID_LINE_WRAPPED)) || 868 tty->cx < tty->sx || /* not at edge of screen */ 869 wx != 0 || /* don't want 0 next */ 870 (wy != tty->cy + 1 && tty->cy != ctx->orlower + wp->yoff)) 871 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 872 873 tty_cell(tty, ctx->cell, ctx->utf8); 874 } 875 876 void 877 tty_cmd_utf8character(struct tty *tty, const struct tty_ctx *ctx) 878 { 879 u_char *ptr = ctx->ptr; 880 size_t i; 881 882 for (i = 0; i < UTF8_SIZE; i++) { 883 if (ptr[i] == 0xff) 884 break; 885 tty_putc(tty, ptr[i]); 886 } 887 } 888 889 void 890 tty_cell( 891 struct tty *tty, const struct grid_cell *gc, const struct grid_utf8 *gu) 892 { 893 u_int i; 894 895 /* Skip last character if terminal is stupid. */ 896 if (tty->term->flags & TERM_EARLYWRAP && 897 tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1) 898 return; 899 900 /* If this is a padding character, do nothing. */ 901 if (gc->flags & GRID_FLAG_PADDING) 902 return; 903 904 /* Set the attributes. */ 905 tty_attributes(tty, gc); 906 907 /* If not UTF-8, write directly. */ 908 if (!(gc->flags & GRID_FLAG_UTF8)) { 909 if (gc->data < 0x20 || gc->data == 0x7f) 910 return; 911 tty_putc(tty, gc->data); 912 return; 913 } 914 915 /* If the terminal doesn't support UTF-8, write underscores. */ 916 if (!(tty->flags & TTY_UTF8)) { 917 for (i = 0; i < gu->width; i++) 918 tty_putc(tty, '_'); 919 return; 920 } 921 922 /* Otherwise, write UTF-8. */ 923 tty_pututf8(tty, gu); 924 } 925 926 void 927 tty_reset(struct tty *tty) 928 { 929 struct grid_cell *gc = &tty->cell; 930 931 if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0) 932 return; 933 934 if (tty_term_has(tty->term, TTYC_RMACS) && gc->attr & GRID_ATTR_CHARSET) 935 tty_putcode(tty, TTYC_RMACS); 936 tty_putcode(tty, TTYC_SGR0); 937 memcpy(gc, &grid_default_cell, sizeof *gc); 938 } 939 940 /* Set region inside pane. */ 941 void 942 tty_region_pane( 943 struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower) 944 { 945 struct window_pane *wp = ctx->wp; 946 947 tty_region(tty, wp->yoff + rupper, wp->yoff + rlower); 948 } 949 950 /* Set region at absolute position. */ 951 void 952 tty_region(struct tty *tty, u_int rupper, u_int rlower) 953 { 954 if (tty->rlower == rlower && tty->rupper == rupper) 955 return; 956 if (!tty_term_has(tty->term, TTYC_CSR)) 957 return; 958 959 tty->rupper = rupper; 960 tty->rlower = rlower; 961 962 tty->cx = 0; 963 tty->cy = 0; 964 965 tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower); 966 } 967 968 /* Move cursor inside pane. */ 969 void 970 tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy) 971 { 972 struct window_pane *wp = ctx->wp; 973 974 tty_cursor(tty, wp->xoff + cx, wp->yoff + cy); 975 } 976 977 /* Move cursor to absolute position. */ 978 void 979 tty_cursor(struct tty *tty, u_int cx, u_int cy) 980 { 981 struct tty_term *term = tty->term; 982 u_int thisx, thisy; 983 int change; 984 985 if (cx > tty->sx - 1) 986 cx = tty->sx - 1; 987 988 thisx = tty->cx; 989 thisy = tty->cy; 990 991 /* No change. */ 992 if (cx == thisx && cy == thisy) 993 return; 994 995 /* Very end of the line, just use absolute movement. */ 996 if (thisx > tty->sx - 1) 997 goto absolute; 998 999 /* Move to home position (0, 0). */ 1000 if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) { 1001 tty_putcode(tty, TTYC_HOME); 1002 goto out; 1003 } 1004 1005 /* Zero on the next line. */ 1006 if (cx == 0 && cy == thisy + 1) { 1007 tty_putc(tty, '\r'); 1008 tty_putc(tty, '\n'); 1009 goto out; 1010 } 1011 1012 /* Moving column or row. */ 1013 if (cy == thisy) { 1014 /* 1015 * Moving column only, row staying the same. 1016 */ 1017 1018 /* To left edge. */ 1019 if (cx == 0) { 1020 tty_putc(tty, '\r'); 1021 goto out; 1022 } 1023 1024 /* One to the left. */ 1025 if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) { 1026 tty_putcode(tty, TTYC_CUB1); 1027 goto out; 1028 } 1029 1030 /* One to the right. */ 1031 if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) { 1032 tty_putcode(tty, TTYC_CUF1); 1033 goto out; 1034 } 1035 1036 /* Calculate difference. */ 1037 change = thisx - cx; /* +ve left, -ve right */ 1038 1039 /* 1040 * Use HPA if change is larger than absolute, otherwise move 1041 * the cursor with CUB/CUF. 1042 */ 1043 if (abs(change) > cx && tty_term_has(term, TTYC_HPA)) { 1044 tty_putcode1(tty, TTYC_HPA, cx); 1045 goto out; 1046 } else if (change > 0 && tty_term_has(term, TTYC_CUB)) { 1047 tty_putcode1(tty, TTYC_CUB, change); 1048 goto out; 1049 } else if (change < 0 && tty_term_has(term, TTYC_CUF)) { 1050 tty_putcode1(tty, TTYC_CUF, -change); 1051 goto out; 1052 } 1053 } else if (cx == thisx) { 1054 /* 1055 * Moving row only, column staying the same. 1056 */ 1057 1058 /* One above. */ 1059 if (cy != tty->rupper && 1060 cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) { 1061 tty_putcode(tty, TTYC_CUU1); 1062 goto out; 1063 } 1064 1065 /* One below. */ 1066 if (cy != tty->rlower && 1067 cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) { 1068 tty_putcode(tty, TTYC_CUD1); 1069 goto out; 1070 } 1071 1072 /* Calculate difference. */ 1073 change = thisy - cy; /* +ve up, -ve down */ 1074 1075 /* 1076 * Try to use VPA if change is larger than absolute or if this change 1077 * would cross the scroll region, otherwise use CUU/CUD. 1078 */ 1079 if (abs(change) > cy || 1080 (change < 0 && cy - change > tty->rlower) || 1081 (change > 0 && cy - change < tty->rupper)) { 1082 if (tty_term_has(term, TTYC_VPA)) { 1083 tty_putcode1(tty, TTYC_VPA, cy); 1084 goto out; 1085 } 1086 } else if (change > 0 && tty_term_has(term, TTYC_CUU)) { 1087 tty_putcode1(tty, TTYC_CUU, change); 1088 goto out; 1089 } else if (change < 0 && tty_term_has(term, TTYC_CUD)) { 1090 tty_putcode1(tty, TTYC_CUD, -change); 1091 goto out; 1092 } 1093 } 1094 1095 absolute: 1096 /* Absolute movement. */ 1097 tty_putcode2(tty, TTYC_CUP, cy, cx); 1098 1099 out: 1100 tty->cx = cx; 1101 tty->cy = cy; 1102 } 1103 1104 void 1105 tty_attributes(struct tty *tty, const struct grid_cell *gc) 1106 { 1107 struct grid_cell *tc = &tty->cell; 1108 u_char changed; 1109 u_int fg, bg, attr; 1110 1111 /* 1112 * If no setab, try to use the reverse attribute as a best-effort for a 1113 * non-default background. This is a bit of a hack but it doesn't do 1114 * any serious harm and makes a couple of applications happier. 1115 */ 1116 fg = gc->fg; bg = gc->bg; attr = gc->attr; 1117 if (!tty_term_has(tty->term, TTYC_SETAB)) { 1118 if (attr & GRID_ATTR_REVERSE) { 1119 if (fg != 7 && fg != 8) 1120 attr &= ~GRID_ATTR_REVERSE; 1121 } else { 1122 if (bg != 0 && bg != 8) 1123 attr |= GRID_ATTR_REVERSE; 1124 } 1125 } 1126 1127 /* If any bits are being cleared, reset everything. */ 1128 if (tc->attr & ~attr) 1129 tty_reset(tty); 1130 1131 /* Filter out attribute bits already set. */ 1132 changed = attr & ~tc->attr; 1133 tc->attr = attr; 1134 1135 /* Set the attributes. */ 1136 if (changed & GRID_ATTR_BRIGHT) 1137 tty_putcode(tty, TTYC_BOLD); 1138 if (changed & GRID_ATTR_DIM) 1139 tty_putcode(tty, TTYC_DIM); 1140 if (changed & GRID_ATTR_ITALICS) 1141 tty_putcode(tty, TTYC_SMSO); 1142 if (changed & GRID_ATTR_UNDERSCORE) 1143 tty_putcode(tty, TTYC_SMUL); 1144 if (changed & GRID_ATTR_BLINK) 1145 tty_putcode(tty, TTYC_BLINK); 1146 if (changed & GRID_ATTR_REVERSE) { 1147 if (tty_term_has(tty->term, TTYC_REV)) 1148 tty_putcode(tty, TTYC_REV); 1149 else if (tty_term_has(tty->term, TTYC_SMSO)) 1150 tty_putcode(tty, TTYC_SMSO); 1151 } 1152 if (changed & GRID_ATTR_HIDDEN) 1153 tty_putcode(tty, TTYC_INVIS); 1154 if (changed & GRID_ATTR_CHARSET) 1155 tty_putcode(tty, TTYC_SMACS); 1156 1157 /* Set foreground colour. */ 1158 if (fg != tc->fg || 1159 (gc->flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256)) { 1160 tty_attributes_fg(tty, gc); 1161 tc->fg = fg; 1162 tc->flags &= ~GRID_FLAG_FG256; 1163 tc->flags |= gc->flags & GRID_FLAG_FG256; 1164 } 1165 1166 /* Set background colour. */ 1167 if (bg != tc->bg || 1168 (gc->flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256)) { 1169 tty_attributes_bg(tty, gc); 1170 tc->bg = bg; 1171 tc->flags &= ~GRID_FLAG_BG256; 1172 tc->flags |= gc->flags & GRID_FLAG_BG256; 1173 } 1174 } 1175 1176 int 1177 tty_try_256(struct tty *tty, u_char colour, const char *type) 1178 { 1179 char s[32]; 1180 1181 if (!(tty->term->flags & TERM_256COLOURS) && 1182 !(tty->term_flags & TERM_256COLOURS)) 1183 return (-1); 1184 1185 xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); 1186 tty_puts(tty, s); 1187 return (0); 1188 } 1189 1190 int 1191 tty_try_88(struct tty *tty, u_char colour, const char *type) 1192 { 1193 char s[32]; 1194 1195 if (!(tty->term->flags & TERM_88COLOURS) && 1196 !(tty->term_flags & TERM_88COLOURS)) 1197 return (-1); 1198 colour = colour_256to88(colour); 1199 1200 xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour); 1201 tty_puts(tty, s); 1202 return (0); 1203 } 1204 1205 void 1206 tty_attributes_fg(struct tty *tty, const struct grid_cell *gc) 1207 { 1208 u_char fg; 1209 1210 fg = gc->fg; 1211 if (gc->flags & GRID_FLAG_FG256) { 1212 if (tty_try_256(tty, fg, "38") == 0) 1213 return; 1214 if (tty_try_88(tty, fg, "38") == 0) 1215 return; 1216 fg = colour_256to16(fg); 1217 if (fg & 8) { 1218 fg &= 7; 1219 tty_putcode(tty, TTYC_BOLD); 1220 tty->cell.attr |= GRID_ATTR_BRIGHT; 1221 } else if (tty->cell.attr & GRID_ATTR_BRIGHT) 1222 tty_reset(tty); 1223 } 1224 1225 if (fg == 8 && 1226 !(tty->term->flags & TERM_HASDEFAULTS) && 1227 !(tty->term_flags & TERM_HASDEFAULTS)) 1228 fg = 7; 1229 if (fg == 8) 1230 tty_puts(tty, "\033[39m"); 1231 else 1232 tty_putcode1(tty, TTYC_SETAF, fg); 1233 } 1234 1235 void 1236 tty_attributes_bg(struct tty *tty, const struct grid_cell *gc) 1237 { 1238 u_char bg; 1239 1240 bg = gc->bg; 1241 if (gc->flags & GRID_FLAG_BG256) { 1242 if (tty_try_256(tty, bg, "48") == 0) 1243 return; 1244 if (tty_try_88(tty, bg, "48") == 0) 1245 return; 1246 bg = colour_256to16(bg); 1247 if (bg & 8) 1248 bg &= 7; 1249 } 1250 1251 if (bg == 8 && 1252 !(tty->term->flags & TERM_HASDEFAULTS) && 1253 !(tty->term_flags & TERM_HASDEFAULTS)) 1254 bg = 0; 1255 if (bg == 8) 1256 tty_puts(tty, "\033[49m"); 1257 else 1258 tty_putcode1(tty, TTYC_SETAB, bg); 1259 } 1260