1 /* $OpenBSD$ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott (at) 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 <curses.h> 25 #include <errno.h> 26 #include <fcntl.h> 27 #include <resolv.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <termios.h> 31 #include <time.h> 32 #include <unistd.h> 33 34 #include "tmux.h" 35 36 static int tty_log_fd = -1; 37 38 static void tty_start_timer_callback(int, short, void *); 39 static void tty_clipboard_query_callback(int, short, void *); 40 static void tty_set_italics(struct tty *); 41 static int tty_try_colour(struct tty *, int, const char *); 42 static void tty_force_cursor_colour(struct tty *, int); 43 static void tty_cursor_pane(struct tty *, const struct tty_ctx *, u_int, 44 u_int); 45 static void tty_cursor_pane_unless_wrap(struct tty *, 46 const struct tty_ctx *, u_int, u_int); 47 static void tty_colours(struct tty *, const struct grid_cell *); 48 static void tty_check_fg(struct tty *, struct colour_palette *, 49 struct grid_cell *); 50 static void tty_check_bg(struct tty *, struct colour_palette *, 51 struct grid_cell *); 52 static void tty_check_us(struct tty *, struct colour_palette *, 53 struct grid_cell *); 54 static void tty_colours_fg(struct tty *, const struct grid_cell *); 55 static void tty_colours_bg(struct tty *, const struct grid_cell *); 56 static void tty_colours_us(struct tty *, const struct grid_cell *); 57 58 static void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, 59 u_int); 60 static void tty_region(struct tty *, u_int, u_int); 61 static void tty_margin_pane(struct tty *, const struct tty_ctx *); 62 static void tty_margin(struct tty *, u_int, u_int); 63 static int tty_large_region(struct tty *, const struct tty_ctx *); 64 static int tty_fake_bce(const struct tty *, const struct grid_cell *, 65 u_int); 66 static void tty_redraw_region(struct tty *, const struct tty_ctx *); 67 static void tty_emulate_repeat(struct tty *, enum tty_code_code, 68 enum tty_code_code, u_int); 69 static void tty_repeat_space(struct tty *, u_int); 70 static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); 71 static void tty_default_attributes(struct tty *, const struct grid_cell *, 72 struct colour_palette *, u_int, struct hyperlinks *); 73 static int tty_check_overlay(struct tty *, u_int, u_int); 74 static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int, 75 struct overlay_ranges *); 76 77 #ifdef ENABLE_SIXEL 78 static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *), 79 struct client *, struct tty_ctx *); 80 #endif 81 82 #define tty_use_margin(tty) \ 83 (tty->term->flags & TERM_DECSLRM) 84 #define tty_full_width(tty, ctx) \ 85 ((ctx)->xoff == 0 && (ctx)->sx >= (tty)->sx) 86 87 #define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */) 88 #define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8) 89 #define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8) 90 91 #define TTY_QUERY_TIMEOUT 5 92 #define TTY_REQUEST_LIMIT 30 93 94 void 95 tty_create_log(void) 96 { 97 char name[64]; 98 99 xsnprintf(name, sizeof name, "tmux-out-%ld.log", (long)getpid()); 100 101 tty_log_fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644); 102 if (tty_log_fd != -1 && fcntl(tty_log_fd, F_SETFD, FD_CLOEXEC) == -1) 103 fatal("fcntl failed"); 104 } 105 106 int 107 tty_init(struct tty *tty, struct client *c) 108 { 109 if (!isatty(c->fd)) 110 return (-1); 111 112 memset(tty, 0, sizeof *tty); 113 tty->client = c; 114 115 tty->cstyle = SCREEN_CURSOR_DEFAULT; 116 tty->ccolour = -1; 117 tty->fg = tty->bg = -1; 118 119 if (tcgetattr(c->fd, &tty->tio) != 0) 120 return (-1); 121 return (0); 122 } 123 124 void 125 tty_resize(struct tty *tty) 126 { 127 struct client *c = tty->client; 128 struct winsize ws; 129 u_int sx, sy, xpixel, ypixel; 130 131 if (ioctl(c->fd, TIOCGWINSZ, &ws) != -1) { 132 sx = ws.ws_col; 133 if (sx == 0) { 134 sx = 80; 135 xpixel = 0; 136 } else 137 xpixel = ws.ws_xpixel / sx; 138 sy = ws.ws_row; 139 if (sy == 0) { 140 sy = 24; 141 ypixel = 0; 142 } else 143 ypixel = ws.ws_ypixel / sy; 144 145 if ((xpixel == 0 || ypixel == 0) && 146 tty->out != NULL && 147 !(tty->flags & TTY_WINSIZEQUERY) && 148 (tty->term->flags & TERM_VT100LIKE)) { 149 tty_puts(tty, "\033[18t\033[14t"); 150 tty->flags |= TTY_WINSIZEQUERY; 151 } 152 } else { 153 sx = 80; 154 sy = 24; 155 xpixel = 0; 156 ypixel = 0; 157 } 158 log_debug("%s: %s now %ux%u (%ux%u)", __func__, c->name, sx, sy, 159 xpixel, ypixel); 160 tty_set_size(tty, sx, sy, xpixel, ypixel); 161 tty_invalidate(tty); 162 } 163 164 void 165 tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel) 166 { 167 tty->sx = sx; 168 tty->sy = sy; 169 tty->xpixel = xpixel; 170 tty->ypixel = ypixel; 171 } 172 173 static void 174 tty_read_callback(__unused int fd, __unused short events, void *data) 175 { 176 struct tty *tty = data; 177 struct client *c = tty->client; 178 const char *name = c->name; 179 size_t size = EVBUFFER_LENGTH(tty->in); 180 int nread; 181 182 nread = evbuffer_read(tty->in, c->fd, -1); 183 if (nread == 0 || nread == -1) { 184 if (nread == 0) 185 log_debug("%s: read closed", name); 186 else 187 log_debug("%s: read error: %s", name, strerror(errno)); 188 event_del(&tty->event_in); 189 server_client_lost(tty->client); 190 return; 191 } 192 log_debug("%s: read %d bytes (already %zu)", name, nread, size); 193 194 while (tty_keys_next(tty)) 195 ; 196 } 197 198 static void 199 tty_timer_callback(__unused int fd, __unused short events, void *data) 200 { 201 struct tty *tty = data; 202 struct client *c = tty->client; 203 struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL }; 204 205 log_debug("%s: %zu discarded", c->name, tty->discarded); 206 207 c->flags |= CLIENT_ALLREDRAWFLAGS; 208 c->discarded += tty->discarded; 209 210 if (tty->discarded < TTY_BLOCK_STOP(tty)) { 211 tty->flags &= ~TTY_BLOCK; 212 tty_invalidate(tty); 213 return; 214 } 215 tty->discarded = 0; 216 evtimer_add(&tty->timer, &tv); 217 } 218 219 static int 220 tty_block_maybe(struct tty *tty) 221 { 222 struct client *c = tty->client; 223 size_t size = EVBUFFER_LENGTH(tty->out); 224 struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL }; 225 226 if (size == 0) 227 tty->flags &= ~TTY_NOBLOCK; 228 else if (tty->flags & TTY_NOBLOCK) 229 return (0); 230 231 if (size < TTY_BLOCK_START(tty)) 232 return (0); 233 234 if (tty->flags & TTY_BLOCK) 235 return (1); 236 tty->flags |= TTY_BLOCK; 237 238 log_debug("%s: can't keep up, %zu discarded", c->name, size); 239 240 evbuffer_drain(tty->out, size); 241 c->discarded += size; 242 243 tty->discarded = 0; 244 evtimer_add(&tty->timer, &tv); 245 return (1); 246 } 247 248 static void 249 tty_write_callback(__unused int fd, __unused short events, void *data) 250 { 251 struct tty *tty = data; 252 struct client *c = tty->client; 253 size_t size = EVBUFFER_LENGTH(tty->out); 254 int nwrite; 255 256 nwrite = evbuffer_write(tty->out, c->fd); 257 if (nwrite == -1) 258 return; 259 log_debug("%s: wrote %d bytes (of %zu)", c->name, nwrite, size); 260 261 if (c->redraw > 0) { 262 if ((size_t)nwrite >= c->redraw) 263 c->redraw = 0; 264 else 265 c->redraw -= nwrite; 266 log_debug("%s: waiting for redraw, %zu bytes left", c->name, 267 c->redraw); 268 } else if (tty_block_maybe(tty)) 269 return; 270 271 if (EVBUFFER_LENGTH(tty->out) != 0) 272 event_add(&tty->event_out, NULL); 273 } 274 275 int 276 tty_open(struct tty *tty, char **cause) 277 { 278 struct client *c = tty->client; 279 280 tty->term = tty_term_create(tty, c->term_name, c->term_caps, 281 c->term_ncaps, &c->term_features, cause); 282 if (tty->term == NULL) { 283 tty_close(tty); 284 return (-1); 285 } 286 tty->flags |= TTY_OPENED; 287 288 tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_BLOCK|TTY_TIMER); 289 290 event_set(&tty->event_in, c->fd, EV_PERSIST|EV_READ, 291 tty_read_callback, tty); 292 tty->in = evbuffer_new(); 293 if (tty->in == NULL) 294 fatal("out of memory"); 295 296 event_set(&tty->event_out, c->fd, EV_WRITE, tty_write_callback, tty); 297 tty->out = evbuffer_new(); 298 if (tty->out == NULL) 299 fatal("out of memory"); 300 301 evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty); 302 evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); 303 evtimer_set(&tty->timer, tty_timer_callback, tty); 304 305 tty_start_tty(tty); 306 tty_keys_build(tty); 307 308 return (0); 309 } 310 311 static void 312 tty_start_timer_callback(__unused int fd, __unused short events, void *data) 313 { 314 struct tty *tty = data; 315 struct client *c = tty->client; 316 317 log_debug("%s: start timer fired", c->name); 318 319 if ((tty->flags & (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)) == 0) 320 tty_update_features(tty); 321 tty->flags |= TTY_ALL_REQUEST_FLAGS; 322 323 tty->flags &= ~(TTY_WAITBG|TTY_WAITFG); 324 } 325 326 static void 327 tty_start_start_timer(struct tty *tty) 328 { 329 struct client *c = tty->client; 330 struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT }; 331 332 log_debug("%s: start timer started", c->name); 333 evtimer_del(&tty->start_timer); 334 evtimer_add(&tty->start_timer, &tv); 335 } 336 337 void 338 tty_start_tty(struct tty *tty) 339 { 340 struct client *c = tty->client; 341 struct termios tio; 342 343 setblocking(c->fd, 0); 344 event_add(&tty->event_in, NULL); 345 346 memcpy(&tio, &tty->tio, sizeof tio); 347 tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP); 348 tio.c_iflag |= IGNBRK; 349 tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET); 350 tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL|ECHOPRT| 351 ECHOKE|ISIG); 352 tio.c_cc[VMIN] = 1; 353 tio.c_cc[VTIME] = 0; 354 if (tcsetattr(c->fd, TCSANOW, &tio) == 0) 355 tcflush(c->fd, TCOFLUSH); 356 357 tty_putcode(tty, TTYC_SMCUP); 358 359 tty_putcode(tty, TTYC_SMKX); 360 tty_putcode(tty, TTYC_CLEAR); 361 362 if (tty_acs_needed(tty)) { 363 log_debug("%s: using capabilities for ACS", c->name); 364 tty_putcode(tty, TTYC_ENACS); 365 } else 366 log_debug("%s: using UTF-8 for ACS", c->name); 367 368 tty_putcode(tty, TTYC_CNORM); 369 if (tty_term_has(tty->term, TTYC_KMOUS)) { 370 tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l"); 371 tty_puts(tty, "\033[?1006l\033[?1005l"); 372 } 373 if (tty_term_has(tty->term, TTYC_ENBP)) 374 tty_putcode(tty, TTYC_ENBP); 375 376 if (tty->term->flags & TERM_VT100LIKE) { 377 /* Subscribe to theme changes and request theme now. */ 378 tty_puts(tty, "\033[?2031h\033[?996n"); 379 } 380 381 tty_start_start_timer(tty); 382 383 tty->flags |= TTY_STARTED; 384 tty_invalidate(tty); 385 386 if (tty->ccolour != -1) 387 tty_force_cursor_colour(tty, -1); 388 389 tty->mouse_drag_flag = 0; 390 tty->mouse_drag_update = NULL; 391 tty->mouse_drag_release = NULL; 392 } 393 394 void 395 tty_send_requests(struct tty *tty) 396 { 397 if (~tty->flags & TTY_STARTED) 398 return; 399 400 if (tty->term->flags & TERM_VT100LIKE) { 401 if (~tty->flags & TTY_HAVEDA) 402 tty_puts(tty, "\033[c"); 403 if (~tty->flags & TTY_HAVEDA2) 404 tty_puts(tty, "\033[>c"); 405 if (~tty->flags & TTY_HAVEXDA) 406 tty_puts(tty, "\033[>q"); 407 tty_puts(tty, "\033]10;?\033\\\033]11;?\033\\"); 408 tty->flags |= (TTY_WAITBG|TTY_WAITFG); 409 } else 410 tty->flags |= TTY_ALL_REQUEST_FLAGS; 411 tty->last_requests = time(NULL); 412 } 413 414 void 415 tty_repeat_requests(struct tty *tty, int force) 416 { 417 struct client *c = tty->client; 418 time_t t = time(NULL); 419 u_int n = t - tty->last_requests; 420 421 if (~tty->flags & TTY_STARTED) 422 return; 423 424 if (!force && n <= TTY_REQUEST_LIMIT) { 425 log_debug("%s: not repeating requests (%u seconds)", c->name, 426 n); 427 return; 428 } 429 log_debug("%s: %srepeating requests (%u seconds)", c->name, 430 force ? "(force) " : "" , n); 431 tty->last_requests = t; 432 433 if (tty->term->flags & TERM_VT100LIKE) { 434 tty_puts(tty, "\033]10;?\033\\\033]11;?\033\\"); 435 tty->flags |= (TTY_WAITBG|TTY_WAITFG); 436 } 437 tty_start_start_timer(tty); 438 } 439 440 void 441 tty_stop_tty(struct tty *tty) 442 { 443 struct client *c = tty->client; 444 struct winsize ws; 445 446 if (!(tty->flags & TTY_STARTED)) 447 return; 448 tty->flags &= ~TTY_STARTED; 449 450 evtimer_del(&tty->start_timer); 451 evtimer_del(&tty->clipboard_timer); 452 453 event_del(&tty->timer); 454 tty->flags &= ~TTY_BLOCK; 455 456 event_del(&tty->event_in); 457 event_del(&tty->event_out); 458 459 /* 460 * Be flexible about error handling and try not kill the server just 461 * because the fd is invalid. Things like ssh -t can easily leave us 462 * with a dead tty. 463 */ 464 if (ioctl(c->fd, TIOCGWINSZ, &ws) == -1) 465 return; 466 if (tcsetattr(c->fd, TCSANOW, &tty->tio) == -1) 467 return; 468 469 tty_raw(tty, tty_term_string_ii(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); 470 if (tty_acs_needed(tty)) 471 tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS)); 472 tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); 473 tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); 474 tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR)); 475 if (tty->cstyle != SCREEN_CURSOR_DEFAULT) { 476 if (tty_term_has(tty->term, TTYC_SE)) 477 tty_raw(tty, tty_term_string(tty->term, TTYC_SE)); 478 else if (tty_term_has(tty->term, TTYC_SS)) 479 tty_raw(tty, tty_term_string_i(tty->term, TTYC_SS, 0)); 480 } 481 if (tty->ccolour != -1) 482 tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); 483 484 tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); 485 if (tty_term_has(tty->term, TTYC_KMOUS)) { 486 tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l"); 487 tty_raw(tty, "\033[?1006l\033[?1005l"); 488 } 489 if (tty_term_has(tty->term, TTYC_DSBP)) 490 tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP)); 491 492 if (tty->term->flags & TERM_VT100LIKE) 493 tty_raw(tty, "\033[?7727l"); 494 tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); 495 tty_raw(tty, tty_term_string(tty->term, TTYC_DSEKS)); 496 497 if (tty_use_margin(tty)) 498 tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG)); 499 tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); 500 501 if (tty->term->flags & TERM_VT100LIKE) 502 tty_raw(tty, "\033[?2031l"); 503 504 setblocking(c->fd, 1); 505 } 506 507 void 508 tty_close(struct tty *tty) 509 { 510 if (event_initialized(&tty->key_timer)) 511 evtimer_del(&tty->key_timer); 512 tty_stop_tty(tty); 513 514 if (tty->flags & TTY_OPENED) { 515 evbuffer_free(tty->in); 516 event_del(&tty->event_in); 517 evbuffer_free(tty->out); 518 event_del(&tty->event_out); 519 520 tty_term_free(tty->term); 521 tty_keys_free(tty); 522 523 tty->flags &= ~TTY_OPENED; 524 } 525 } 526 527 void 528 tty_free(struct tty *tty) 529 { 530 tty_close(tty); 531 } 532 533 void 534 tty_update_features(struct tty *tty) 535 { 536 struct client *c = tty->client; 537 538 if (tty_apply_features(tty->term, c->term_features)) 539 tty_term_apply_overrides(tty->term); 540 541 if (tty_use_margin(tty)) 542 tty_putcode(tty, TTYC_ENMG); 543 if (options_get_number(global_options, "extended-keys")) 544 tty_puts(tty, tty_term_string(tty->term, TTYC_ENEKS)); 545 if (options_get_number(global_options, "focus-events")) 546 tty_puts(tty, tty_term_string(tty->term, TTYC_ENFCS)); 547 if (tty->term->flags & TERM_VT100LIKE) 548 tty_puts(tty, "\033[?7727h"); 549 550 /* 551 * Features might have changed since the first draw during attach. For 552 * example, this happens when DA responses are received. 553 */ 554 server_redraw_client(c); 555 556 tty_invalidate(tty); 557 } 558 559 void 560 tty_raw(struct tty *tty, const char *s) 561 { 562 struct client *c = tty->client; 563 ssize_t n, slen; 564 u_int i; 565 566 slen = strlen(s); 567 for (i = 0; i < 5; i++) { 568 n = write(c->fd, s, slen); 569 if (n >= 0) { 570 s += n; 571 slen -= n; 572 if (slen == 0) 573 break; 574 } else if (n == -1 && errno != EAGAIN) 575 break; 576 usleep(100); 577 } 578 } 579 580 void 581 tty_putcode(struct tty *tty, enum tty_code_code code) 582 { 583 tty_puts(tty, tty_term_string(tty->term, code)); 584 } 585 586 void 587 tty_putcode_i(struct tty *tty, enum tty_code_code code, int a) 588 { 589 if (a < 0) 590 return; 591 tty_puts(tty, tty_term_string_i(tty->term, code, a)); 592 } 593 594 void 595 tty_putcode_ii(struct tty *tty, enum tty_code_code code, int a, int b) 596 { 597 if (a < 0 || b < 0) 598 return; 599 tty_puts(tty, tty_term_string_ii(tty->term, code, a, b)); 600 } 601 602 void 603 tty_putcode_iii(struct tty *tty, enum tty_code_code code, int a, int b, int c) 604 { 605 if (a < 0 || b < 0 || c < 0) 606 return; 607 tty_puts(tty, tty_term_string_iii(tty->term, code, a, b, c)); 608 } 609 610 void 611 tty_putcode_s(struct tty *tty, enum tty_code_code code, const char *a) 612 { 613 if (a != NULL) 614 tty_puts(tty, tty_term_string_s(tty->term, code, a)); 615 } 616 617 void 618 tty_putcode_ss(struct tty *tty, enum tty_code_code code, const char *a, 619 const char *b) 620 { 621 if (a != NULL && b != NULL) 622 tty_puts(tty, tty_term_string_ss(tty->term, code, a, b)); 623 } 624 625 static void 626 tty_add(struct tty *tty, const char *buf, size_t len) 627 { 628 struct client *c = tty->client; 629 630 if (tty->flags & TTY_BLOCK) { 631 tty->discarded += len; 632 return; 633 } 634 635 evbuffer_add(tty->out, buf, len); 636 log_debug("%s: %.*s", c->name, (int)len, buf); 637 c->written += len; 638 639 if (tty_log_fd != -1) 640 write(tty_log_fd, buf, len); 641 if (tty->flags & TTY_STARTED) 642 event_add(&tty->event_out, NULL); 643 } 644 645 void 646 tty_puts(struct tty *tty, const char *s) 647 { 648 if (*s != '\0') 649 tty_add(tty, s, strlen(s)); 650 } 651 652 void 653 tty_putc(struct tty *tty, u_char ch) 654 { 655 const char *acs; 656 657 if ((tty->term->flags & TERM_NOAM) && 658 ch >= 0x20 && ch != 0x7f && 659 tty->cy == tty->sy - 1 && 660 tty->cx + 1 >= tty->sx) 661 return; 662 663 if (tty->cell.attr & GRID_ATTR_CHARSET) { 664 acs = tty_acs_get(tty, ch); 665 if (acs != NULL) 666 tty_add(tty, acs, strlen(acs)); 667 else 668 tty_add(tty, (char *)&ch, 1); 669 } else 670 tty_add(tty, (char *)&ch, 1); 671 672 if (ch >= 0x20 && ch != 0x7f) { 673 if (tty->cx >= tty->sx) { 674 tty->cx = 1; 675 if (tty->cy != tty->rlower) 676 tty->cy++; 677 678 /* 679 * On !am terminals, force the cursor position to where 680 * we think it should be after a line wrap - this means 681 * it works on sensible terminals as well. 682 */ 683 if (tty->term->flags & TERM_NOAM) 684 tty_putcode_ii(tty, TTYC_CUP, tty->cy, tty->cx); 685 } else 686 tty->cx++; 687 } 688 } 689 690 void 691 tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) 692 { 693 if ((tty->term->flags & TERM_NOAM) && 694 tty->cy == tty->sy - 1 && 695 tty->cx + len >= tty->sx) 696 len = tty->sx - tty->cx - 1; 697 698 tty_add(tty, buf, len); 699 if (tty->cx + width > tty->sx) { 700 tty->cx = (tty->cx + width) - tty->sx; 701 if (tty->cx <= tty->sx) 702 tty->cy++; 703 else 704 tty->cx = tty->cy = UINT_MAX; 705 } else 706 tty->cx += width; 707 } 708 709 static void 710 tty_set_italics(struct tty *tty) 711 { 712 const char *s; 713 714 if (tty_term_has(tty->term, TTYC_SITM)) { 715 s = options_get_string(global_options, "default-terminal"); 716 if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) { 717 tty_putcode(tty, TTYC_SITM); 718 return; 719 } 720 } 721 tty_putcode(tty, TTYC_SMSO); 722 } 723 724 void 725 tty_set_title(struct tty *tty, const char *title) 726 { 727 if (!tty_term_has(tty->term, TTYC_TSL) || 728 !tty_term_has(tty->term, TTYC_FSL)) 729 return; 730 731 tty_putcode(tty, TTYC_TSL); 732 tty_puts(tty, title); 733 tty_putcode(tty, TTYC_FSL); 734 } 735 736 void 737 tty_set_path(struct tty *tty, const char *title) 738 { 739 if (!tty_term_has(tty->term, TTYC_SWD) || 740 !tty_term_has(tty->term, TTYC_FSL)) 741 return; 742 743 tty_putcode(tty, TTYC_SWD); 744 tty_puts(tty, title); 745 tty_putcode(tty, TTYC_FSL); 746 } 747 748 static void 749 tty_force_cursor_colour(struct tty *tty, int c) 750 { 751 u_char r, g, b; 752 char s[13]; 753 754 if (c != -1) 755 c = colour_force_rgb(c); 756 if (c == tty->ccolour) 757 return; 758 if (c == -1) 759 tty_putcode(tty, TTYC_CR); 760 else { 761 colour_split_rgb(c, &r, &g, &b); 762 xsnprintf(s, sizeof s, "rgb:%02hhx/%02hhx/%02hhx", r, g, b); 763 tty_putcode_s(tty, TTYC_CS, s); 764 } 765 tty->ccolour = c; 766 } 767 768 static int 769 tty_update_cursor(struct tty *tty, int mode, struct screen *s) 770 { 771 enum screen_cursor_style cstyle; 772 int ccolour, changed, cmode = mode; 773 774 /* Set cursor colour if changed. */ 775 if (s != NULL) { 776 ccolour = s->ccolour; 777 if (s->ccolour == -1) 778 ccolour = s->default_ccolour; 779 tty_force_cursor_colour(tty, ccolour); 780 } 781 782 /* If cursor is off, set as invisible. */ 783 if (~cmode & MODE_CURSOR) { 784 if (tty->mode & MODE_CURSOR) 785 tty_putcode(tty, TTYC_CIVIS); 786 return (cmode); 787 } 788 789 /* Check if blinking or very visible flag changed or style changed. */ 790 if (s == NULL) 791 cstyle = tty->cstyle; 792 else { 793 cstyle = s->cstyle; 794 if (cstyle == SCREEN_CURSOR_DEFAULT) { 795 if (~cmode & MODE_CURSOR_BLINKING_SET) { 796 if (s->default_mode & MODE_CURSOR_BLINKING) 797 cmode |= MODE_CURSOR_BLINKING; 798 else 799 cmode &= ~MODE_CURSOR_BLINKING; 800 } 801 cstyle = s->default_cstyle; 802 } 803 } 804 805 /* If nothing changed, do nothing. */ 806 changed = cmode ^ tty->mode; 807 if ((changed & CURSOR_MODES) == 0 && cstyle == tty->cstyle) 808 return (cmode); 809 810 /* 811 * Set cursor style. If an explicit style has been set with DECSCUSR, 812 * set it if supported, otherwise send cvvis for blinking styles. 813 * 814 * If no style, has been set (SCREEN_CURSOR_DEFAULT), then send cvvis 815 * if either the blinking or very visible flags are set. 816 */ 817 tty_putcode(tty, TTYC_CNORM); 818 switch (cstyle) { 819 case SCREEN_CURSOR_DEFAULT: 820 if (tty->cstyle != SCREEN_CURSOR_DEFAULT) { 821 if (tty_term_has(tty->term, TTYC_SE)) 822 tty_putcode(tty, TTYC_SE); 823 else 824 tty_putcode_i(tty, TTYC_SS, 0); 825 } 826 if (cmode & (MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE)) 827 tty_putcode(tty, TTYC_CVVIS); 828 break; 829 case SCREEN_CURSOR_BLOCK: 830 if (tty_term_has(tty->term, TTYC_SS)) { 831 if (cmode & MODE_CURSOR_BLINKING) 832 tty_putcode_i(tty, TTYC_SS, 1); 833 else 834 tty_putcode_i(tty, TTYC_SS, 2); 835 } else if (cmode & MODE_CURSOR_BLINKING) 836 tty_putcode(tty, TTYC_CVVIS); 837 break; 838 case SCREEN_CURSOR_UNDERLINE: 839 if (tty_term_has(tty->term, TTYC_SS)) { 840 if (cmode & MODE_CURSOR_BLINKING) 841 tty_putcode_i(tty, TTYC_SS, 3); 842 else 843 tty_putcode_i(tty, TTYC_SS, 4); 844 } else if (cmode & MODE_CURSOR_BLINKING) 845 tty_putcode(tty, TTYC_CVVIS); 846 break; 847 case SCREEN_CURSOR_BAR: 848 if (tty_term_has(tty->term, TTYC_SS)) { 849 if (cmode & MODE_CURSOR_BLINKING) 850 tty_putcode_i(tty, TTYC_SS, 5); 851 else 852 tty_putcode_i(tty, TTYC_SS, 6); 853 } else if (cmode & MODE_CURSOR_BLINKING) 854 tty_putcode(tty, TTYC_CVVIS); 855 break; 856 } 857 tty->cstyle = cstyle; 858 return (cmode); 859 } 860 861 void 862 tty_update_mode(struct tty *tty, int mode, struct screen *s) 863 { 864 struct tty_term *term = tty->term; 865 struct client *c = tty->client; 866 int changed; 867 868 if (tty->flags & TTY_NOCURSOR) 869 mode &= ~MODE_CURSOR; 870 871 if (tty_update_cursor(tty, mode, s) & MODE_CURSOR_BLINKING) 872 mode |= MODE_CURSOR_BLINKING; 873 else 874 mode &= ~MODE_CURSOR_BLINKING; 875 876 changed = mode ^ tty->mode; 877 if (log_get_level() != 0 && changed != 0) { 878 log_debug("%s: current mode %s", c->name, 879 screen_mode_to_string(tty->mode)); 880 log_debug("%s: setting mode %s", c->name, 881 screen_mode_to_string(mode)); 882 } 883 884 if ((changed & ALL_MOUSE_MODES) && tty_term_has(term, TTYC_KMOUS)) { 885 /* 886 * If the mouse modes have changed, clear then all and apply 887 * again. There are differences in how terminals track the 888 * various bits. 889 */ 890 tty_puts(tty, "\033[?1006l\033[?1000l\033[?1002l\033[?1003l"); 891 if (mode & ALL_MOUSE_MODES) 892 tty_puts(tty, "\033[?1006h"); 893 if (mode & MODE_MOUSE_ALL) 894 tty_puts(tty, "\033[?1000h\033[?1002h\033[?1003h"); 895 else if (mode & MODE_MOUSE_BUTTON) 896 tty_puts(tty, "\033[?1000h\033[?1002h"); 897 else if (mode & MODE_MOUSE_STANDARD) 898 tty_puts(tty, "\033[?1000h"); 899 } 900 tty->mode = mode; 901 } 902 903 static void 904 tty_emulate_repeat(struct tty *tty, enum tty_code_code code, 905 enum tty_code_code code1, u_int n) 906 { 907 if (tty_term_has(tty->term, code)) 908 tty_putcode_i(tty, code, n); 909 else { 910 while (n-- > 0) 911 tty_putcode(tty, code1); 912 } 913 } 914 915 static void 916 tty_repeat_space(struct tty *tty, u_int n) 917 { 918 static char s[500]; 919 920 if (*s != ' ') 921 memset(s, ' ', sizeof s); 922 923 while (n > sizeof s) { 924 tty_putn(tty, s, sizeof s, sizeof s); 925 n -= sizeof s; 926 } 927 if (n != 0) 928 tty_putn(tty, s, n, n); 929 } 930 931 /* Is this window bigger than the terminal? */ 932 int 933 tty_window_bigger(struct tty *tty) 934 { 935 struct client *c = tty->client; 936 struct window *w = c->session->curw->window; 937 938 return (tty->sx < w->sx || tty->sy - status_line_size(c) < w->sy); 939 } 940 941 /* What offset should this window be drawn at? */ 942 int 943 tty_window_offset(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy) 944 { 945 *ox = tty->oox; 946 *oy = tty->ooy; 947 *sx = tty->osx; 948 *sy = tty->osy; 949 950 return (tty->oflag); 951 } 952 953 /* What offset should this window be drawn at? */ 954 static int 955 tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy) 956 { 957 struct client *c = tty->client; 958 struct window *w = c->session->curw->window; 959 struct window_pane *wp = server_client_get_pane(c); 960 u_int cx, cy, lines; 961 962 lines = status_line_size(c); 963 964 if (tty->sx >= w->sx && tty->sy - lines >= w->sy) { 965 *ox = 0; 966 *oy = 0; 967 *sx = w->sx; 968 *sy = w->sy; 969 970 c->pan_window = NULL; 971 return (0); 972 } 973 974 *sx = tty->sx; 975 *sy = tty->sy - lines; 976 977 if (c->pan_window == w) { 978 if (*sx >= w->sx) 979 c->pan_ox = 0; 980 else if (c->pan_ox + *sx > w->sx) 981 c->pan_ox = w->sx - *sx; 982 *ox = c->pan_ox; 983 if (*sy >= w->sy) 984 c->pan_oy = 0; 985 else if (c->pan_oy + *sy > w->sy) 986 c->pan_oy = w->sy - *sy; 987 *oy = c->pan_oy; 988 return (1); 989 } 990 991 if (~wp->screen->mode & MODE_CURSOR) { 992 *ox = 0; 993 *oy = 0; 994 } else { 995 cx = wp->xoff + wp->screen->cx; 996 cy = wp->yoff + wp->screen->cy; 997 998 if (cx < *sx) 999 *ox = 0; 1000 else if (cx > w->sx - *sx) 1001 *ox = w->sx - *sx; 1002 else 1003 *ox = cx - *sx / 2; 1004 1005 if (cy < *sy) 1006 *oy = 0; 1007 else if (cy > w->sy - *sy) 1008 *oy = w->sy - *sy; 1009 else 1010 *oy = cy - *sy / 2; 1011 } 1012 1013 c->pan_window = NULL; 1014 return (1); 1015 } 1016 1017 /* Update stored offsets for a window and redraw if necessary. */ 1018 void 1019 tty_update_window_offset(struct window *w) 1020 { 1021 struct client *c; 1022 1023 TAILQ_FOREACH(c, &clients, entry) { 1024 if (c->session != NULL && 1025 c->session->curw != NULL && 1026 c->session->curw->window == w) 1027 tty_update_client_offset(c); 1028 } 1029 } 1030 1031 /* Update stored offsets for a client and redraw if necessary. */ 1032 void 1033 tty_update_client_offset(struct client *c) 1034 { 1035 u_int ox, oy, sx, sy; 1036 1037 if (~c->flags & CLIENT_TERMINAL) 1038 return; 1039 1040 c->tty.oflag = tty_window_offset1(&c->tty, &ox, &oy, &sx, &sy); 1041 if (ox == c->tty.oox && 1042 oy == c->tty.ooy && 1043 sx == c->tty.osx && 1044 sy == c->tty.osy) 1045 return; 1046 1047 log_debug ("%s: %s offset has changed (%u,%u %ux%u -> %u,%u %ux%u)", 1048 __func__, c->name, c->tty.oox, c->tty.ooy, c->tty.osx, c->tty.osy, 1049 ox, oy, sx, sy); 1050 1051 c->tty.oox = ox; 1052 c->tty.ooy = oy; 1053 c->tty.osx = sx; 1054 c->tty.osy = sy; 1055 1056 c->flags |= (CLIENT_REDRAWWINDOW|CLIENT_REDRAWSTATUS); 1057 } 1058 1059 /* 1060 * Is the region large enough to be worth redrawing once later rather than 1061 * probably several times now? Currently yes if it is more than 50% of the 1062 * pane. 1063 */ 1064 static int 1065 tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx) 1066 { 1067 return (ctx->orlower - ctx->orupper >= ctx->sy / 2); 1068 } 1069 1070 /* 1071 * Return if BCE is needed but the terminal doesn't have it - it'll need to be 1072 * emulated. 1073 */ 1074 static int 1075 tty_fake_bce(const struct tty *tty, const struct grid_cell *gc, u_int bg) 1076 { 1077 if (tty_term_flag(tty->term, TTYC_BCE)) 1078 return (0); 1079 if (!COLOUR_DEFAULT(bg) || !COLOUR_DEFAULT(gc->bg)) 1080 return (1); 1081 return (0); 1082 } 1083 1084 /* 1085 * Redraw scroll region using data from screen (already updated). Used when 1086 * CSR not supported, or window is a pane that doesn't take up the full 1087 * width of the terminal. 1088 */ 1089 static void 1090 tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) 1091 { 1092 struct client *c = tty->client; 1093 u_int i; 1094 1095 /* 1096 * If region is large, schedule a redraw. In most cases this is likely 1097 * to be followed by some more scrolling. 1098 */ 1099 if (tty_large_region(tty, ctx)) { 1100 log_debug("%s: %s large redraw", __func__, c->name); 1101 ctx->redraw_cb(ctx); 1102 return; 1103 } 1104 1105 for (i = ctx->orupper; i <= ctx->orlower; i++) 1106 tty_draw_pane(tty, ctx, i); 1107 } 1108 1109 /* Is this position visible in the pane? */ 1110 static int 1111 tty_is_visible(__unused struct tty *tty, const struct tty_ctx *ctx, u_int px, 1112 u_int py, u_int nx, u_int ny) 1113 { 1114 u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py; 1115 1116 if (!ctx->bigger) 1117 return (1); 1118 1119 if (xoff + nx <= ctx->wox || xoff >= ctx->wox + ctx->wsx || 1120 yoff + ny <= ctx->woy || yoff >= ctx->woy + ctx->wsy) 1121 return (0); 1122 return (1); 1123 } 1124 1125 /* Clamp line position to visible part of pane. */ 1126 static int 1127 tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, 1128 u_int nx, u_int *i, u_int *x, u_int *rx, u_int *ry) 1129 { 1130 u_int xoff = ctx->rxoff + px; 1131 1132 if (!tty_is_visible(tty, ctx, px, py, nx, 1)) 1133 return (0); 1134 *ry = ctx->yoff + py - ctx->woy; 1135 1136 if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) { 1137 /* All visible. */ 1138 *i = 0; 1139 *x = ctx->xoff + px - ctx->wox; 1140 *rx = nx; 1141 } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) { 1142 /* Both left and right not visible. */ 1143 *i = ctx->wox; 1144 *x = 0; 1145 *rx = ctx->wsx; 1146 } else if (xoff < ctx->wox) { 1147 /* Left not visible. */ 1148 *i = ctx->wox - (ctx->xoff + px); 1149 *x = 0; 1150 *rx = nx - *i; 1151 } else { 1152 /* Right not visible. */ 1153 *i = 0; 1154 *x = (ctx->xoff + px) - ctx->wox; 1155 *rx = ctx->wsx - *x; 1156 } 1157 if (*rx > nx) 1158 fatalx("%s: x too big, %u > %u", __func__, *rx, nx); 1159 1160 return (1); 1161 } 1162 1163 /* Clear a line. */ 1164 static void 1165 tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py, 1166 u_int px, u_int nx, u_int bg) 1167 { 1168 struct client *c = tty->client; 1169 struct overlay_ranges r; 1170 u_int i; 1171 1172 log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); 1173 1174 /* Nothing to clear. */ 1175 if (nx == 0) 1176 return; 1177 1178 /* If genuine BCE is available, can try escape sequences. */ 1179 if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) { 1180 /* Off the end of the line, use EL if available. */ 1181 if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) { 1182 tty_cursor(tty, px, py); 1183 tty_putcode(tty, TTYC_EL); 1184 return; 1185 } 1186 1187 /* At the start of the line. Use EL1. */ 1188 if (px == 0 && tty_term_has(tty->term, TTYC_EL1)) { 1189 tty_cursor(tty, px + nx - 1, py); 1190 tty_putcode(tty, TTYC_EL1); 1191 return; 1192 } 1193 1194 /* Section of line. Use ECH if possible. */ 1195 if (tty_term_has(tty->term, TTYC_ECH)) { 1196 tty_cursor(tty, px, py); 1197 tty_putcode_i(tty, TTYC_ECH, nx); 1198 return; 1199 } 1200 } 1201 1202 /* 1203 * Couldn't use an escape sequence, use spaces. Clear only the visible 1204 * bit if there is an overlay. 1205 */ 1206 tty_check_overlay_range(tty, px, py, nx, &r); 1207 for (i = 0; i < OVERLAY_MAX_RANGES; i++) { 1208 if (r.nx[i] == 0) 1209 continue; 1210 tty_cursor(tty, r.px[i], py); 1211 tty_repeat_space(tty, r.nx[i]); 1212 } 1213 } 1214 1215 /* Clear a line, adjusting to visible part of pane. */ 1216 static void 1217 tty_clear_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int py, 1218 u_int px, u_int nx, u_int bg) 1219 { 1220 struct client *c = tty->client; 1221 u_int i, x, rx, ry; 1222 1223 log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); 1224 1225 if (tty_clamp_line(tty, ctx, px, py, nx, &i, &x, &rx, &ry)) 1226 tty_clear_line(tty, &ctx->defaults, ry, x, rx, bg); 1227 } 1228 1229 /* Clamp area position to visible part of pane. */ 1230 static int 1231 tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, 1232 u_int nx, u_int ny, u_int *i, u_int *j, u_int *x, u_int *y, u_int *rx, 1233 u_int *ry) 1234 { 1235 u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py; 1236 1237 if (!tty_is_visible(tty, ctx, px, py, nx, ny)) 1238 return (0); 1239 1240 if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) { 1241 /* All visible. */ 1242 *i = 0; 1243 *x = ctx->xoff + px - ctx->wox; 1244 *rx = nx; 1245 } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) { 1246 /* Both left and right not visible. */ 1247 *i = ctx->wox; 1248 *x = 0; 1249 *rx = ctx->wsx; 1250 } else if (xoff < ctx->wox) { 1251 /* Left not visible. */ 1252 *i = ctx->wox - (ctx->xoff + px); 1253 *x = 0; 1254 *rx = nx - *i; 1255 } else { 1256 /* Right not visible. */ 1257 *i = 0; 1258 *x = (ctx->xoff + px) - ctx->wox; 1259 *rx = ctx->wsx - *x; 1260 } 1261 if (*rx > nx) 1262 fatalx("%s: x too big, %u > %u", __func__, *rx, nx); 1263 1264 if (yoff >= ctx->woy && yoff + ny <= ctx->woy + ctx->wsy) { 1265 /* All visible. */ 1266 *j = 0; 1267 *y = ctx->yoff + py - ctx->woy; 1268 *ry = ny; 1269 } else if (yoff < ctx->woy && yoff + ny > ctx->woy + ctx->wsy) { 1270 /* Both top and bottom not visible. */ 1271 *j = ctx->woy; 1272 *y = 0; 1273 *ry = ctx->wsy; 1274 } else if (yoff < ctx->woy) { 1275 /* Top not visible. */ 1276 *j = ctx->woy - (ctx->yoff + py); 1277 *y = 0; 1278 *ry = ny - *j; 1279 } else { 1280 /* Bottom not visible. */ 1281 *j = 0; 1282 *y = (ctx->yoff + py) - ctx->woy; 1283 *ry = ctx->wsy - *y; 1284 } 1285 if (*ry > ny) 1286 fatalx("%s: y too big, %u > %u", __func__, *ry, ny); 1287 1288 return (1); 1289 } 1290 1291 /* Clear an area, adjusting to visible part of pane. */ 1292 static void 1293 tty_clear_area(struct tty *tty, const struct grid_cell *defaults, u_int py, 1294 u_int ny, u_int px, u_int nx, u_int bg) 1295 { 1296 struct client *c = tty->client; 1297 u_int yy; 1298 char tmp[64]; 1299 1300 log_debug("%s: %s, %u,%u at %u,%u", __func__, c->name, nx, ny, px, py); 1301 1302 /* Nothing to clear. */ 1303 if (nx == 0 || ny == 0) 1304 return; 1305 1306 /* If genuine BCE is available, can try escape sequences. */ 1307 if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) { 1308 /* Use ED if clearing off the bottom of the terminal. */ 1309 if (px == 0 && 1310 px + nx >= tty->sx && 1311 py + ny >= tty->sy && 1312 tty_term_has(tty->term, TTYC_ED)) { 1313 tty_cursor(tty, 0, py); 1314 tty_putcode(tty, TTYC_ED); 1315 return; 1316 } 1317 1318 /* 1319 * On VT420 compatible terminals we can use DECFRA if the 1320 * background colour isn't default (because it doesn't work 1321 * after SGR 0). 1322 */ 1323 if ((tty->term->flags & TERM_DECFRA) && !COLOUR_DEFAULT(bg)) { 1324 xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x", 1325 py + 1, px + 1, py + ny, px + nx); 1326 tty_puts(tty, tmp); 1327 return; 1328 } 1329 1330 /* Full lines can be scrolled away to clear them. */ 1331 if (px == 0 && 1332 px + nx >= tty->sx && 1333 ny > 2 && 1334 tty_term_has(tty->term, TTYC_CSR) && 1335 tty_term_has(tty->term, TTYC_INDN)) { 1336 tty_region(tty, py, py + ny - 1); 1337 tty_margin_off(tty); 1338 tty_putcode_i(tty, TTYC_INDN, ny); 1339 return; 1340 } 1341 1342 /* 1343 * If margins are supported, can just scroll the area off to 1344 * clear it. 1345 */ 1346 if (nx > 2 && 1347 ny > 2 && 1348 tty_term_has(tty->term, TTYC_CSR) && 1349 tty_use_margin(tty) && 1350 tty_term_has(tty->term, TTYC_INDN)) { 1351 tty_region(tty, py, py + ny - 1); 1352 tty_margin(tty, px, px + nx - 1); 1353 tty_putcode_i(tty, TTYC_INDN, ny); 1354 return; 1355 } 1356 } 1357 1358 /* Couldn't use an escape sequence, loop over the lines. */ 1359 for (yy = py; yy < py + ny; yy++) 1360 tty_clear_line(tty, defaults, yy, px, nx, bg); 1361 } 1362 1363 /* Clear an area in a pane. */ 1364 static void 1365 tty_clear_pane_area(struct tty *tty, const struct tty_ctx *ctx, u_int py, 1366 u_int ny, u_int px, u_int nx, u_int bg) 1367 { 1368 u_int i, j, x, y, rx, ry; 1369 1370 if (tty_clamp_area(tty, ctx, px, py, nx, ny, &i, &j, &x, &y, &rx, &ry)) 1371 tty_clear_area(tty, &ctx->defaults, y, ry, x, rx, bg); 1372 } 1373 1374 static void 1375 tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py) 1376 { 1377 struct screen *s = ctx->s; 1378 u_int nx = ctx->sx, i, x, rx, ry; 1379 1380 log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger); 1381 1382 if (!ctx->bigger) { 1383 tty_draw_line(tty, s, 0, py, nx, ctx->xoff, ctx->yoff + py, 1384 &ctx->defaults, ctx->palette); 1385 return; 1386 } 1387 if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) { 1388 tty_draw_line(tty, s, i, py, rx, x, ry, &ctx->defaults, 1389 ctx->palette); 1390 } 1391 } 1392 1393 static const struct grid_cell * 1394 tty_check_codeset(struct tty *tty, const struct grid_cell *gc) 1395 { 1396 static struct grid_cell new; 1397 int c; 1398 1399 /* Characters less than 0x7f are always fine, no matter what. */ 1400 if (gc->data.size == 1 && *gc->data.data < 0x7f) 1401 return (gc); 1402 if (gc->flags & GRID_FLAG_TAB) 1403 return (gc); 1404 1405 /* UTF-8 terminal and a UTF-8 character - fine. */ 1406 if (tty->client->flags & CLIENT_UTF8) 1407 return (gc); 1408 memcpy(&new, gc, sizeof new); 1409 1410 /* See if this can be mapped to an ACS character. */ 1411 c = tty_acs_reverse_get(tty, (const char *)gc->data.data, gc->data.size); 1412 if (c != -1) { 1413 utf8_set(&new.data, c); 1414 new.attr |= GRID_ATTR_CHARSET; 1415 return (&new); 1416 } 1417 1418 /* Replace by the right number of underscores. */ 1419 new.data.size = gc->data.width; 1420 if (new.data.size > UTF8_SIZE) 1421 new.data.size = UTF8_SIZE; 1422 memset(new.data.data, '_', new.data.size); 1423 return (&new); 1424 } 1425 1426 /* 1427 * Check if a single character is obstructed by the overlay and return a 1428 * boolean. 1429 */ 1430 static int 1431 tty_check_overlay(struct tty *tty, u_int px, u_int py) 1432 { 1433 struct overlay_ranges r; 1434 1435 /* 1436 * A unit width range will always return nx[2] == 0 from a check, even 1437 * with multiple overlays, so it's sufficient to check just the first 1438 * two entries. 1439 */ 1440 tty_check_overlay_range(tty, px, py, 1, &r); 1441 if (r.nx[0] + r.nx[1] == 0) 1442 return (0); 1443 return (1); 1444 } 1445 1446 /* Return parts of the input range which are visible. */ 1447 static void 1448 tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx, 1449 struct overlay_ranges *r) 1450 { 1451 struct client *c = tty->client; 1452 1453 if (c->overlay_check == NULL) { 1454 r->px[0] = px; 1455 r->nx[0] = nx; 1456 r->px[1] = 0; 1457 r->nx[1] = 0; 1458 r->px[2] = 0; 1459 r->nx[2] = 0; 1460 return; 1461 } 1462 1463 c->overlay_check(c, c->overlay_data, px, py, nx, r); 1464 } 1465 1466 void 1467 tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, 1468 u_int atx, u_int aty, const struct grid_cell *defaults, 1469 struct colour_palette *palette) 1470 { 1471 struct grid *gd = s->grid; 1472 struct grid_cell gc, last; 1473 const struct grid_cell *gcp; 1474 struct grid_line *gl; 1475 struct client *c = tty->client; 1476 struct overlay_ranges r; 1477 u_int i, j, ux, sx, width, hidden, eux, nxx; 1478 u_int cellsize; 1479 int flags, cleared = 0, wrapped = 0; 1480 char buf[512]; 1481 size_t len; 1482 1483 log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__, 1484 px, py, nx, atx, aty); 1485 log_debug("%s: defaults: fg=%d, bg=%d", __func__, defaults->fg, 1486 defaults->bg); 1487 1488 /* 1489 * py is the line in the screen to draw. 1490 * px is the start x and nx is the width to draw. 1491 * atx,aty is the line on the terminal to draw it. 1492 */ 1493 1494 flags = (tty->flags & TTY_NOCURSOR); 1495 tty->flags |= TTY_NOCURSOR; 1496 tty_update_mode(tty, tty->mode, s); 1497 1498 tty_region_off(tty); 1499 tty_margin_off(tty); 1500 1501 /* 1502 * Clamp the width to cellsize - note this is not cellused, because 1503 * there may be empty background cells after it (from BCE). 1504 */ 1505 sx = screen_size_x(s); 1506 if (nx > sx) 1507 nx = sx; 1508 cellsize = grid_get_line(gd, gd->hsize + py)->cellsize; 1509 if (sx > cellsize) 1510 sx = cellsize; 1511 if (sx > tty->sx) 1512 sx = tty->sx; 1513 if (sx > nx) 1514 sx = nx; 1515 ux = 0; 1516 1517 if (py == 0) 1518 gl = NULL; 1519 else 1520 gl = grid_get_line(gd, gd->hsize + py - 1); 1521 if (gl == NULL || 1522 (~gl->flags & GRID_LINE_WRAPPED) || 1523 atx != 0 || 1524 tty->cx < tty->sx || 1525 nx < tty->sx) { 1526 if (nx < tty->sx && 1527 atx == 0 && 1528 px + sx != nx && 1529 tty_term_has(tty->term, TTYC_EL1) && 1530 !tty_fake_bce(tty, defaults, 8) && 1531 c->overlay_check == NULL) { 1532 tty_default_attributes(tty, defaults, palette, 8, 1533 s->hyperlinks); 1534 tty_cursor(tty, nx - 1, aty); 1535 tty_putcode(tty, TTYC_EL1); 1536 cleared = 1; 1537 } 1538 } else { 1539 log_debug("%s: wrapped line %u", __func__, aty); 1540 wrapped = 1; 1541 } 1542 1543 memcpy(&last, &grid_default_cell, sizeof last); 1544 len = 0; 1545 width = 0; 1546 1547 for (i = 0; i < sx; i++) { 1548 grid_view_get_cell(gd, px + i, py, &gc); 1549 gcp = tty_check_codeset(tty, &gc); 1550 if (len != 0 && 1551 (!tty_check_overlay(tty, atx + ux + width, aty) || 1552 (gcp->attr & GRID_ATTR_CHARSET) || 1553 gcp->flags != last.flags || 1554 gcp->attr != last.attr || 1555 gcp->fg != last.fg || 1556 gcp->bg != last.bg || 1557 gcp->us != last.us || 1558 gcp->link != last.link || 1559 ux + width + gcp->data.width > nx || 1560 (sizeof buf) - len < gcp->data.size)) { 1561 tty_attributes(tty, &last, defaults, palette, 1562 s->hyperlinks); 1563 if (last.flags & GRID_FLAG_CLEARED) { 1564 log_debug("%s: %zu cleared", __func__, len); 1565 tty_clear_line(tty, defaults, aty, atx + ux, 1566 width, last.bg); 1567 } else { 1568 if (!wrapped || atx != 0 || ux != 0) 1569 tty_cursor(tty, atx + ux, aty); 1570 tty_putn(tty, buf, len, width); 1571 } 1572 ux += width; 1573 1574 len = 0; 1575 width = 0; 1576 wrapped = 0; 1577 } 1578 1579 if (gcp->flags & GRID_FLAG_SELECTED) 1580 screen_select_cell(s, &last, gcp); 1581 else 1582 memcpy(&last, gcp, sizeof last); 1583 1584 tty_check_overlay_range(tty, atx + ux, aty, gcp->data.width, 1585 &r); 1586 hidden = 0; 1587 for (j = 0; j < OVERLAY_MAX_RANGES; j++) 1588 hidden += r.nx[j]; 1589 hidden = gcp->data.width - hidden; 1590 if (hidden != 0 && hidden == gcp->data.width) { 1591 if (~gcp->flags & GRID_FLAG_PADDING) 1592 ux += gcp->data.width; 1593 } else if (hidden != 0 || ux + gcp->data.width > nx) { 1594 if (~gcp->flags & GRID_FLAG_PADDING) { 1595 tty_attributes(tty, &last, defaults, palette, 1596 s->hyperlinks); 1597 for (j = 0; j < OVERLAY_MAX_RANGES; j++) { 1598 if (r.nx[j] == 0) 1599 continue; 1600 /* Effective width drawn so far. */ 1601 eux = r.px[j] - atx; 1602 if (eux < nx) { 1603 tty_cursor(tty, r.px[j], aty); 1604 nxx = nx - eux; 1605 if (r.nx[j] > nxx) 1606 r.nx[j] = nxx; 1607 tty_repeat_space(tty, r.nx[j]); 1608 ux = eux + r.nx[j]; 1609 } 1610 } 1611 } 1612 } else if (gcp->attr & GRID_ATTR_CHARSET) { 1613 tty_attributes(tty, &last, defaults, palette, 1614 s->hyperlinks); 1615 tty_cursor(tty, atx + ux, aty); 1616 for (j = 0; j < gcp->data.size; j++) 1617 tty_putc(tty, gcp->data.data[j]); 1618 ux += gcp->data.width; 1619 } else if (~gcp->flags & GRID_FLAG_PADDING) { 1620 memcpy(buf + len, gcp->data.data, gcp->data.size); 1621 len += gcp->data.size; 1622 width += gcp->data.width; 1623 } 1624 } 1625 if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) { 1626 tty_attributes(tty, &last, defaults, palette, s->hyperlinks); 1627 if (last.flags & GRID_FLAG_CLEARED) { 1628 log_debug("%s: %zu cleared (end)", __func__, len); 1629 tty_clear_line(tty, defaults, aty, atx + ux, width, 1630 last.bg); 1631 } else { 1632 if (!wrapped || atx != 0 || ux != 0) 1633 tty_cursor(tty, atx + ux, aty); 1634 tty_putn(tty, buf, len, width); 1635 } 1636 ux += width; 1637 } 1638 1639 if (!cleared && ux < nx) { 1640 log_debug("%s: %u to end of line (%zu cleared)", __func__, 1641 nx - ux, len); 1642 tty_default_attributes(tty, defaults, palette, 8, 1643 s->hyperlinks); 1644 tty_clear_line(tty, defaults, aty, atx + ux, nx - ux, 8); 1645 } 1646 1647 tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags; 1648 tty_update_mode(tty, tty->mode, s); 1649 } 1650 1651 #ifdef ENABLE_SIXEL 1652 /* Update context for client. */ 1653 static int 1654 tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c) 1655 { 1656 struct window_pane *wp = ttyctx->arg; 1657 1658 if (c->session->curw->window != wp->window) 1659 return (0); 1660 if (wp->layout_cell == NULL) 1661 return (0); 1662 1663 /* Set the properties relevant to the current client. */ 1664 ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, 1665 &ttyctx->wsx, &ttyctx->wsy); 1666 1667 ttyctx->yoff = ttyctx->ryoff = wp->yoff; 1668 if (status_at_line(c) == 0) 1669 ttyctx->yoff += status_line_size(c); 1670 1671 return (1); 1672 } 1673 1674 void 1675 tty_draw_images(struct client *c, struct window_pane *wp, struct screen *s) 1676 { 1677 struct image *im; 1678 struct tty_ctx ttyctx; 1679 1680 TAILQ_FOREACH(im, &s->images, entry) { 1681 memset(&ttyctx, 0, sizeof ttyctx); 1682 1683 /* Set the client independent properties. */ 1684 ttyctx.ocx = im->px; 1685 ttyctx.ocy = im->py; 1686 1687 ttyctx.orlower = s->rlower; 1688 ttyctx.orupper = s->rupper; 1689 1690 ttyctx.xoff = ttyctx.rxoff = wp->xoff; 1691 ttyctx.sx = wp->sx; 1692 ttyctx.sy = wp->sy; 1693 1694 ttyctx.ptr = im; 1695 ttyctx.arg = wp; 1696 ttyctx.set_client_cb = tty_set_client_cb; 1697 ttyctx.allow_invisible_panes = 1; 1698 tty_write_one(tty_cmd_sixelimage, c, &ttyctx); 1699 } 1700 } 1701 #endif 1702 1703 void 1704 tty_sync_start(struct tty *tty) 1705 { 1706 if (tty->flags & TTY_BLOCK) 1707 return; 1708 if (tty->flags & TTY_SYNCING) 1709 return; 1710 tty->flags |= TTY_SYNCING; 1711 1712 if (tty_term_has(tty->term, TTYC_SYNC)) { 1713 log_debug("%s sync start", tty->client->name); 1714 tty_putcode_i(tty, TTYC_SYNC, 1); 1715 } 1716 } 1717 1718 void 1719 tty_sync_end(struct tty *tty) 1720 { 1721 if (tty->flags & TTY_BLOCK) 1722 return; 1723 if (~tty->flags & TTY_SYNCING) 1724 return; 1725 tty->flags &= ~TTY_SYNCING; 1726 1727 if (tty_term_has(tty->term, TTYC_SYNC)) { 1728 log_debug("%s sync end", tty->client->name); 1729 tty_putcode_i(tty, TTYC_SYNC, 2); 1730 } 1731 } 1732 1733 static int 1734 tty_client_ready(const struct tty_ctx *ctx, struct client *c) 1735 { 1736 if (c->session == NULL || c->tty.term == NULL) 1737 return (0); 1738 if (c->flags & CLIENT_SUSPENDED) 1739 return (0); 1740 1741 /* 1742 * If invisible panes are allowed (used for passthrough), don't care if 1743 * redrawing or frozen. 1744 */ 1745 if (ctx->allow_invisible_panes) 1746 return (1); 1747 1748 if (c->flags & CLIENT_REDRAWWINDOW) 1749 return (0); 1750 if (c->tty.flags & TTY_FREEZE) 1751 return (0); 1752 return (1); 1753 } 1754 1755 void 1756 tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), 1757 struct tty_ctx *ctx) 1758 { 1759 struct client *c; 1760 int state; 1761 1762 if (ctx->set_client_cb == NULL) 1763 return; 1764 TAILQ_FOREACH(c, &clients, entry) { 1765 if (tty_client_ready(ctx, c)) { 1766 state = ctx->set_client_cb(ctx, c); 1767 if (state == -1) 1768 break; 1769 if (state == 0) 1770 continue; 1771 cmdfn(&c->tty, ctx); 1772 } 1773 } 1774 } 1775 1776 #ifdef ENABLE_SIXEL 1777 /* Only write to the incoming tty instead of every client. */ 1778 static void 1779 tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *), 1780 struct client *c, struct tty_ctx *ctx) 1781 { 1782 if (ctx->set_client_cb == NULL) 1783 return; 1784 if ((ctx->set_client_cb(ctx, c)) == 1) 1785 cmdfn(&c->tty, ctx); 1786 } 1787 #endif 1788 1789 void 1790 tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) 1791 { 1792 struct client *c = tty->client; 1793 1794 if (ctx->bigger || 1795 !tty_full_width(tty, ctx) || 1796 tty_fake_bce(tty, &ctx->defaults, ctx->bg) || 1797 (!tty_term_has(tty->term, TTYC_ICH) && 1798 !tty_term_has(tty->term, TTYC_ICH1)) || 1799 c->overlay_check != NULL) { 1800 tty_draw_pane(tty, ctx, ctx->ocy); 1801 return; 1802 } 1803 1804 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1805 ctx->s->hyperlinks); 1806 1807 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 1808 1809 tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num); 1810 } 1811 1812 void 1813 tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) 1814 { 1815 struct client *c = tty->client; 1816 1817 if (ctx->bigger || 1818 !tty_full_width(tty, ctx) || 1819 tty_fake_bce(tty, &ctx->defaults, ctx->bg) || 1820 (!tty_term_has(tty->term, TTYC_DCH) && 1821 !tty_term_has(tty->term, TTYC_DCH1)) || 1822 c->overlay_check != NULL) { 1823 tty_draw_pane(tty, ctx, ctx->ocy); 1824 return; 1825 } 1826 1827 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1828 ctx->s->hyperlinks); 1829 1830 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 1831 1832 tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num); 1833 } 1834 1835 void 1836 tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) 1837 { 1838 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1839 ctx->s->hyperlinks); 1840 1841 tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->num, ctx->bg); 1842 } 1843 1844 void 1845 tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) 1846 { 1847 struct client *c = tty->client; 1848 1849 if (ctx->bigger || 1850 !tty_full_width(tty, ctx) || 1851 tty_fake_bce(tty, &ctx->defaults, ctx->bg) || 1852 !tty_term_has(tty->term, TTYC_CSR) || 1853 !tty_term_has(tty->term, TTYC_IL1) || 1854 ctx->sx == 1 || 1855 ctx->sy == 1 || 1856 c->overlay_check != NULL) { 1857 tty_redraw_region(tty, ctx); 1858 return; 1859 } 1860 1861 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1862 ctx->s->hyperlinks); 1863 1864 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 1865 tty_margin_off(tty); 1866 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 1867 1868 tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num); 1869 tty->cx = tty->cy = UINT_MAX; 1870 } 1871 1872 void 1873 tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) 1874 { 1875 struct client *c = tty->client; 1876 1877 if (ctx->bigger || 1878 !tty_full_width(tty, ctx) || 1879 tty_fake_bce(tty, &ctx->defaults, ctx->bg) || 1880 !tty_term_has(tty->term, TTYC_CSR) || 1881 !tty_term_has(tty->term, TTYC_DL1) || 1882 ctx->sx == 1 || 1883 ctx->sy == 1 || 1884 c->overlay_check != NULL) { 1885 tty_redraw_region(tty, ctx); 1886 return; 1887 } 1888 1889 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1890 ctx->s->hyperlinks); 1891 1892 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 1893 tty_margin_off(tty); 1894 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 1895 1896 tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num); 1897 tty->cx = tty->cy = UINT_MAX; 1898 } 1899 1900 void 1901 tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) 1902 { 1903 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1904 ctx->s->hyperlinks); 1905 1906 tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg); 1907 } 1908 1909 void 1910 tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) 1911 { 1912 u_int nx = ctx->sx - ctx->ocx; 1913 1914 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1915 ctx->s->hyperlinks); 1916 1917 tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg); 1918 } 1919 1920 void 1921 tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) 1922 { 1923 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1924 ctx->s->hyperlinks); 1925 1926 tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg); 1927 } 1928 1929 void 1930 tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) 1931 { 1932 struct client *c = tty->client; 1933 1934 if (ctx->ocy != ctx->orupper) 1935 return; 1936 1937 if (ctx->bigger || 1938 (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || 1939 tty_fake_bce(tty, &ctx->defaults, 8) || 1940 !tty_term_has(tty->term, TTYC_CSR) || 1941 (!tty_term_has(tty->term, TTYC_RI) && 1942 !tty_term_has(tty->term, TTYC_RIN)) || 1943 ctx->sx == 1 || 1944 ctx->sy == 1 || 1945 c->overlay_check != NULL) { 1946 tty_redraw_region(tty, ctx); 1947 return; 1948 } 1949 1950 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1951 ctx->s->hyperlinks); 1952 1953 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 1954 tty_margin_pane(tty, ctx); 1955 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); 1956 1957 if (tty_term_has(tty->term, TTYC_RI)) 1958 tty_putcode(tty, TTYC_RI); 1959 else 1960 tty_putcode_i(tty, TTYC_RIN, 1); 1961 } 1962 1963 void 1964 tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) 1965 { 1966 struct client *c = tty->client; 1967 1968 if (ctx->ocy != ctx->orlower) 1969 return; 1970 1971 if (ctx->bigger || 1972 (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || 1973 tty_fake_bce(tty, &ctx->defaults, 8) || 1974 !tty_term_has(tty->term, TTYC_CSR) || 1975 ctx->sx == 1 || 1976 ctx->sy == 1 || 1977 c->overlay_check != NULL) { 1978 tty_redraw_region(tty, ctx); 1979 return; 1980 } 1981 1982 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1983 ctx->s->hyperlinks); 1984 1985 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 1986 tty_margin_pane(tty, ctx); 1987 1988 /* 1989 * If we want to wrap a pane while using margins, the cursor needs to 1990 * be exactly on the right of the region. If the cursor is entirely off 1991 * the edge - move it back to the right. Some terminals are funny about 1992 * this and insert extra spaces, so only use the right if margins are 1993 * enabled. 1994 */ 1995 if (ctx->xoff + ctx->ocx > tty->rright) { 1996 if (!tty_use_margin(tty)) 1997 tty_cursor(tty, 0, ctx->yoff + ctx->ocy); 1998 else 1999 tty_cursor(tty, tty->rright, ctx->yoff + ctx->ocy); 2000 } else 2001 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 2002 2003 tty_putc(tty, '\n'); 2004 } 2005 2006 void 2007 tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) 2008 { 2009 struct client *c = tty->client; 2010 u_int i; 2011 2012 if (ctx->bigger || 2013 (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || 2014 tty_fake_bce(tty, &ctx->defaults, 8) || 2015 !tty_term_has(tty->term, TTYC_CSR) || 2016 ctx->sx == 1 || 2017 ctx->sy == 1 || 2018 c->overlay_check != NULL) { 2019 tty_redraw_region(tty, ctx); 2020 return; 2021 } 2022 2023 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 2024 ctx->s->hyperlinks); 2025 2026 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 2027 tty_margin_pane(tty, ctx); 2028 2029 if (ctx->num == 1 || !tty_term_has(tty->term, TTYC_INDN)) { 2030 if (!tty_use_margin(tty)) 2031 tty_cursor(tty, 0, tty->rlower); 2032 else 2033 tty_cursor(tty, tty->rright, tty->rlower); 2034 for (i = 0; i < ctx->num; i++) 2035 tty_putc(tty, '\n'); 2036 } else { 2037 if (tty->cy == UINT_MAX) 2038 tty_cursor(tty, 0, 0); 2039 else 2040 tty_cursor(tty, 0, tty->cy); 2041 tty_putcode_i(tty, TTYC_INDN, ctx->num); 2042 } 2043 } 2044 2045 void 2046 tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) 2047 { 2048 u_int i; 2049 struct client *c = tty->client; 2050 2051 if (ctx->bigger || 2052 (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || 2053 tty_fake_bce(tty, &ctx->defaults, 8) || 2054 !tty_term_has(tty->term, TTYC_CSR) || 2055 (!tty_term_has(tty->term, TTYC_RI) && 2056 !tty_term_has(tty->term, TTYC_RIN)) || 2057 ctx->sx == 1 || 2058 ctx->sy == 1 || 2059 c->overlay_check != NULL) { 2060 tty_redraw_region(tty, ctx); 2061 return; 2062 } 2063 2064 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 2065 ctx->s->hyperlinks); 2066 2067 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 2068 tty_margin_pane(tty, ctx); 2069 tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); 2070 2071 if (tty_term_has(tty->term, TTYC_RIN)) 2072 tty_putcode_i(tty, TTYC_RIN, ctx->num); 2073 else { 2074 for (i = 0; i < ctx->num; i++) 2075 tty_putcode(tty, TTYC_RI); 2076 } 2077 } 2078 2079 void 2080 tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) 2081 { 2082 u_int px, py, nx, ny; 2083 2084 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 2085 ctx->s->hyperlinks); 2086 2087 tty_region_pane(tty, ctx, 0, ctx->sy - 1); 2088 tty_margin_off(tty); 2089 2090 px = 0; 2091 nx = ctx->sx; 2092 py = ctx->ocy + 1; 2093 ny = ctx->sy - ctx->ocy - 1; 2094 2095 tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); 2096 2097 px = ctx->ocx; 2098 nx = ctx->sx - ctx->ocx; 2099 py = ctx->ocy; 2100 2101 tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg); 2102 } 2103 2104 void 2105 tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) 2106 { 2107 u_int px, py, nx, ny; 2108 2109 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 2110 ctx->s->hyperlinks); 2111 2112 tty_region_pane(tty, ctx, 0, ctx->sy - 1); 2113 tty_margin_off(tty); 2114 2115 px = 0; 2116 nx = ctx->sx; 2117 py = 0; 2118 ny = ctx->ocy; 2119 2120 tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); 2121 2122 px = 0; 2123 nx = ctx->ocx + 1; 2124 py = ctx->ocy; 2125 2126 tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg); 2127 } 2128 2129 void 2130 tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) 2131 { 2132 u_int px, py, nx, ny; 2133 2134 tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 2135 ctx->s->hyperlinks); 2136 2137 tty_region_pane(tty, ctx, 0, ctx->sy - 1); 2138 tty_margin_off(tty); 2139 2140 px = 0; 2141 nx = ctx->sx; 2142 py = 0; 2143 ny = ctx->sy; 2144 2145 tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); 2146 } 2147 2148 void 2149 tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) 2150 { 2151 u_int i, j; 2152 2153 if (ctx->bigger) { 2154 ctx->redraw_cb(ctx); 2155 return; 2156 } 2157 2158 tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette, 2159 ctx->s->hyperlinks); 2160 2161 tty_region_pane(tty, ctx, 0, ctx->sy - 1); 2162 tty_margin_off(tty); 2163 2164 for (j = 0; j < ctx->sy; j++) { 2165 tty_cursor_pane(tty, ctx, 0, j); 2166 for (i = 0; i < ctx->sx; i++) 2167 tty_putc(tty, 'E'); 2168 } 2169 } 2170 2171 void 2172 tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) 2173 { 2174 const struct grid_cell *gcp = ctx->cell; 2175 struct screen *s = ctx->s; 2176 struct overlay_ranges r; 2177 u_int px, py, i, vis = 0; 2178 2179 px = ctx->xoff + ctx->ocx - ctx->wox; 2180 py = ctx->yoff + ctx->ocy - ctx->woy; 2181 if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1) || 2182 (gcp->data.width == 1 && !tty_check_overlay(tty, px, py))) 2183 return; 2184 2185 /* Handle partially obstructed wide characters. */ 2186 if (gcp->data.width > 1) { 2187 tty_check_overlay_range(tty, px, py, gcp->data.width, &r); 2188 for (i = 0; i < OVERLAY_MAX_RANGES; i++) 2189 vis += r.nx[i]; 2190 if (vis < gcp->data.width) { 2191 tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width, 2192 px, py, &ctx->defaults, ctx->palette); 2193 return; 2194 } 2195 } 2196 2197 if (ctx->xoff + ctx->ocx - ctx->wox > tty->sx - 1 && 2198 ctx->ocy == ctx->orlower && 2199 tty_full_width(tty, ctx)) 2200 tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 2201 2202 tty_margin_off(tty); 2203 tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); 2204 2205 tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette, 2206 ctx->s->hyperlinks); 2207 2208 if (ctx->num == 1) 2209 tty_invalidate(tty); 2210 } 2211 2212 void 2213 tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) 2214 { 2215 struct overlay_ranges r; 2216 u_int i, px, py, cx; 2217 char *cp = ctx->ptr; 2218 2219 if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, ctx->num, 1)) 2220 return; 2221 2222 if (ctx->bigger && 2223 (ctx->xoff + ctx->ocx < ctx->wox || 2224 ctx->xoff + ctx->ocx + ctx->num > ctx->wox + ctx->wsx)) { 2225 if (!ctx->wrapped || 2226 !tty_full_width(tty, ctx) || 2227 (tty->term->flags & TERM_NOAM) || 2228 ctx->xoff + ctx->ocx != 0 || 2229 ctx->yoff + ctx->ocy != tty->cy + 1 || 2230 tty->cx < tty->sx || 2231 tty->cy == tty->rlower) 2232 tty_draw_pane(tty, ctx, ctx->ocy); 2233 else 2234 ctx->redraw_cb(ctx); 2235 return; 2236 } 2237 2238 tty_margin_off(tty); 2239 tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); 2240 tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette, ctx->s->hyperlinks); 2241 2242 /* Get tty position from pane position for overlay check. */ 2243 px = ctx->xoff + ctx->ocx - ctx->wox; 2244 py = ctx->yoff + ctx->ocy - ctx->woy; 2245 2246 tty_check_overlay_range(tty, px, py, ctx->num, &r); 2247 for (i = 0; i < OVERLAY_MAX_RANGES; i++) { 2248 if (r.nx[i] == 0) 2249 continue; 2250 /* Convert back to pane position for printing. */ 2251 cx = r.px[i] - ctx->xoff + ctx->wox; 2252 tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy); 2253 tty_putn(tty, cp + r.px[i] - px, r.nx[i], r.nx[i]); 2254 } 2255 } 2256 2257 void 2258 tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx) 2259 { 2260 tty_set_selection(tty, ctx->ptr2, ctx->ptr, ctx->num); 2261 } 2262 2263 void 2264 tty_set_selection(struct tty *tty, const char *flags, const char *buf, 2265 size_t len) 2266 { 2267 char *encoded; 2268 size_t size; 2269 2270 if (~tty->flags & TTY_STARTED) 2271 return; 2272 if (!tty_term_has(tty->term, TTYC_MS)) 2273 return; 2274 2275 size = 4 * ((len + 2) / 3) + 1; /* storage for base64 */ 2276 encoded = xmalloc(size); 2277 2278 b64_ntop(buf, len, encoded, size); 2279 tty->flags |= TTY_NOBLOCK; 2280 tty_putcode_ss(tty, TTYC_MS, flags, encoded); 2281 2282 free(encoded); 2283 } 2284 2285 void 2286 tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) 2287 { 2288 tty->flags |= TTY_NOBLOCK; 2289 tty_add(tty, ctx->ptr, ctx->num); 2290 tty_invalidate(tty); 2291 } 2292 2293 #ifdef ENABLE_SIXEL 2294 void 2295 tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx) 2296 { 2297 struct image *im = ctx->ptr; 2298 struct sixel_image *si = im->data; 2299 struct sixel_image *new; 2300 char *data; 2301 size_t size; 2302 u_int cx = ctx->ocx, cy = ctx->ocy, sx, sy; 2303 u_int i, j, x, y, rx, ry; 2304 int fallback = 0; 2305 2306 if ((~tty->term->flags & TERM_SIXEL) && 2307 !tty_term_has(tty->term, TTYC_SXL)) 2308 fallback = 1; 2309 if (tty->xpixel == 0 || tty->ypixel == 0) 2310 fallback = 1; 2311 2312 sixel_size_in_cells(si, &sx, &sy); 2313 log_debug("%s: image is %ux%u", __func__, sx, sy); 2314 if (!tty_clamp_area(tty, ctx, cx, cy, sx, sy, &i, &j, &x, &y, &rx, &ry)) 2315 return; 2316 log_debug("%s: clamping to %u,%u-%u,%u", __func__, i, j, rx, ry); 2317 2318 if (fallback == 1) { 2319 data = xstrdup(im->fallback); 2320 size = strlen(data); 2321 } else { 2322 new = sixel_scale(si, tty->xpixel, tty->ypixel, i, j, rx, ry, 0); 2323 if (new == NULL) 2324 return; 2325 2326 data = sixel_print(new, si, &size); 2327 } 2328 if (data != NULL) { 2329 log_debug("%s: %zu bytes: %s", __func__, size, data); 2330 tty_region_off(tty); 2331 tty_margin_off(tty); 2332 tty_cursor(tty, x, y); 2333 2334 tty->flags |= TTY_NOBLOCK; 2335 tty_add(tty, data, size); 2336 tty_invalidate(tty); 2337 free(data); 2338 } 2339 2340 if (fallback == 0) 2341 sixel_free(new); 2342 } 2343 #endif 2344 2345 void 2346 tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx) 2347 { 2348 if (ctx->num == 0x11) { 2349 /* 2350 * This is an overlay and a command that moves the cursor so 2351 * start synchronized updates. 2352 */ 2353 tty_sync_start(tty); 2354 } else if (~ctx->num & 0x10) { 2355 /* 2356 * This is a pane. If there is an overlay, always start; 2357 * otherwise, only if requested. 2358 */ 2359 if (ctx->num || tty->client->overlay_draw != NULL) 2360 tty_sync_start(tty); 2361 } 2362 } 2363 2364 void 2365 tty_cell(struct tty *tty, const struct grid_cell *gc, 2366 const struct grid_cell *defaults, struct colour_palette *palette, 2367 struct hyperlinks *hl) 2368 { 2369 const struct grid_cell *gcp; 2370 2371 /* Skip last character if terminal is stupid. */ 2372 if ((tty->term->flags & TERM_NOAM) && 2373 tty->cy == tty->sy - 1 && 2374 tty->cx == tty->sx - 1) 2375 return; 2376 2377 /* If this is a padding character, do nothing. */ 2378 if (gc->flags & GRID_FLAG_PADDING) 2379 return; 2380 2381 /* Check the output codeset and apply attributes. */ 2382 gcp = tty_check_codeset(tty, gc); 2383 tty_attributes(tty, gcp, defaults, palette, hl); 2384 2385 /* If it is a single character, write with putc to handle ACS. */ 2386 if (gcp->data.size == 1) { 2387 tty_attributes(tty, gcp, defaults, palette, hl); 2388 if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f) 2389 return; 2390 tty_putc(tty, *gcp->data.data); 2391 return; 2392 } 2393 2394 /* Write the data. */ 2395 tty_putn(tty, gcp->data.data, gcp->data.size, gcp->data.width); 2396 } 2397 2398 void 2399 tty_reset(struct tty *tty) 2400 { 2401 struct grid_cell *gc = &tty->cell; 2402 2403 if (!grid_cells_equal(gc, &grid_default_cell)) { 2404 if (gc->link != 0) 2405 tty_putcode_ss(tty, TTYC_HLS, "", ""); 2406 if ((gc->attr & GRID_ATTR_CHARSET) && tty_acs_needed(tty)) 2407 tty_putcode(tty, TTYC_RMACS); 2408 tty_putcode(tty, TTYC_SGR0); 2409 memcpy(gc, &grid_default_cell, sizeof *gc); 2410 } 2411 memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell); 2412 } 2413 2414 void 2415 tty_invalidate(struct tty *tty) 2416 { 2417 memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell); 2418 memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell); 2419 2420 tty->cx = tty->cy = UINT_MAX; 2421 tty->rupper = tty->rleft = UINT_MAX; 2422 tty->rlower = tty->rright = UINT_MAX; 2423 2424 if (tty->flags & TTY_STARTED) { 2425 if (tty_use_margin(tty)) 2426 tty_putcode(tty, TTYC_ENMG); 2427 tty_putcode(tty, TTYC_SGR0); 2428 2429 tty->mode = ALL_MODES; 2430 tty_update_mode(tty, MODE_CURSOR, NULL); 2431 2432 tty_cursor(tty, 0, 0); 2433 tty_region_off(tty); 2434 tty_margin_off(tty); 2435 } else 2436 tty->mode = MODE_CURSOR; 2437 } 2438 2439 /* Turn off margin. */ 2440 void 2441 tty_region_off(struct tty *tty) 2442 { 2443 tty_region(tty, 0, tty->sy - 1); 2444 } 2445 2446 /* Set region inside pane. */ 2447 static void 2448 tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper, 2449 u_int rlower) 2450 { 2451 tty_region(tty, ctx->yoff + rupper - ctx->woy, 2452 ctx->yoff + rlower - ctx->woy); 2453 } 2454 2455 /* Set region at absolute position. */ 2456 static void 2457 tty_region(struct tty *tty, u_int rupper, u_int rlower) 2458 { 2459 if (tty->rlower == rlower && tty->rupper == rupper) 2460 return; 2461 if (!tty_term_has(tty->term, TTYC_CSR)) 2462 return; 2463 2464 tty->rupper = rupper; 2465 tty->rlower = rlower; 2466 2467 /* 2468 * Some terminals (such as PuTTY) do not correctly reset the cursor to 2469 * 0,0 if it is beyond the last column (they do not reset their wrap 2470 * flag so further output causes a line feed). As a workaround, do an 2471 * explicit move to 0 first. 2472 */ 2473 if (tty->cx >= tty->sx) { 2474 if (tty->cy == UINT_MAX) 2475 tty_cursor(tty, 0, 0); 2476 else 2477 tty_cursor(tty, 0, tty->cy); 2478 } 2479 2480 tty_putcode_ii(tty, TTYC_CSR, tty->rupper, tty->rlower); 2481 tty->cx = tty->cy = UINT_MAX; 2482 } 2483 2484 /* Turn off margin. */ 2485 void 2486 tty_margin_off(struct tty *tty) 2487 { 2488 tty_margin(tty, 0, tty->sx - 1); 2489 } 2490 2491 /* Set margin inside pane. */ 2492 static void 2493 tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) 2494 { 2495 tty_margin(tty, ctx->xoff - ctx->wox, 2496 ctx->xoff + ctx->sx - 1 - ctx->wox); 2497 } 2498 2499 /* Set margin at absolute position. */ 2500 static void 2501 tty_margin(struct tty *tty, u_int rleft, u_int rright) 2502 { 2503 if (!tty_use_margin(tty)) 2504 return; 2505 if (tty->rleft == rleft && tty->rright == rright) 2506 return; 2507 2508 tty_putcode_ii(tty, TTYC_CSR, tty->rupper, tty->rlower); 2509 2510 tty->rleft = rleft; 2511 tty->rright = rright; 2512 2513 if (rleft == 0 && rright == tty->sx - 1) 2514 tty_putcode(tty, TTYC_CLMG); 2515 else 2516 tty_putcode_ii(tty, TTYC_CMG, rleft, rright); 2517 tty->cx = tty->cy = UINT_MAX; 2518 } 2519 2520 /* 2521 * Move the cursor, unless it would wrap itself when the next character is 2522 * printed. 2523 */ 2524 static void 2525 tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, 2526 u_int cx, u_int cy) 2527 { 2528 if (!ctx->wrapped || 2529 !tty_full_width(tty, ctx) || 2530 (tty->term->flags & TERM_NOAM) || 2531 ctx->xoff + cx != 0 || 2532 ctx->yoff + cy != tty->cy + 1 || 2533 tty->cx < tty->sx || 2534 tty->cy == tty->rlower) 2535 tty_cursor_pane(tty, ctx, cx, cy); 2536 else 2537 log_debug("%s: will wrap at %u,%u", __func__, tty->cx, tty->cy); 2538 } 2539 2540 /* Move cursor inside pane. */ 2541 static void 2542 tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy) 2543 { 2544 tty_cursor(tty, ctx->xoff + cx - ctx->wox, ctx->yoff + cy - ctx->woy); 2545 } 2546 2547 /* Move cursor to absolute position. */ 2548 void 2549 tty_cursor(struct tty *tty, u_int cx, u_int cy) 2550 { 2551 struct tty_term *term = tty->term; 2552 u_int thisx, thisy; 2553 int change; 2554 2555 if (tty->flags & TTY_BLOCK) 2556 return; 2557 2558 thisx = tty->cx; 2559 thisy = tty->cy; 2560 2561 /* 2562 * If in the automargin space, and want to be there, do not move. 2563 * Otherwise, force the cursor to be in range (and complain). 2564 */ 2565 if (cx == thisx && cy == thisy && cx == tty->sx) 2566 return; 2567 if (cx > tty->sx - 1) { 2568 log_debug("%s: x too big %u > %u", __func__, cx, tty->sx - 1); 2569 cx = tty->sx - 1; 2570 } 2571 2572 /* No change. */ 2573 if (cx == thisx && cy == thisy) 2574 return; 2575 2576 /* Currently at the very end of the line - use absolute movement. */ 2577 if (thisx > tty->sx - 1) 2578 goto absolute; 2579 2580 /* Move to home position (0, 0). */ 2581 if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) { 2582 tty_putcode(tty, TTYC_HOME); 2583 goto out; 2584 } 2585 2586 /* Zero on the next line. */ 2587 if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower && 2588 (!tty_use_margin(tty) || tty->rleft == 0)) { 2589 tty_putc(tty, '\r'); 2590 tty_putc(tty, '\n'); 2591 goto out; 2592 } 2593 2594 /* Moving column or row. */ 2595 if (cy == thisy) { 2596 /* 2597 * Moving column only, row staying the same. 2598 */ 2599 2600 /* To left edge. */ 2601 if (cx == 0 && (!tty_use_margin(tty) || tty->rleft == 0)) { 2602 tty_putc(tty, '\r'); 2603 goto out; 2604 } 2605 2606 /* One to the left. */ 2607 if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) { 2608 tty_putcode(tty, TTYC_CUB1); 2609 goto out; 2610 } 2611 2612 /* One to the right. */ 2613 if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) { 2614 tty_putcode(tty, TTYC_CUF1); 2615 goto out; 2616 } 2617 2618 /* Calculate difference. */ 2619 change = thisx - cx; /* +ve left, -ve right */ 2620 2621 /* 2622 * Use HPA if change is larger than absolute, otherwise move 2623 * the cursor with CUB/CUF. 2624 */ 2625 if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) { 2626 tty_putcode_i(tty, TTYC_HPA, cx); 2627 goto out; 2628 } else if (change > 0 && 2629 tty_term_has(term, TTYC_CUB) && 2630 !tty_use_margin(tty)) { 2631 if (change == 2 && tty_term_has(term, TTYC_CUB1)) { 2632 tty_putcode(tty, TTYC_CUB1); 2633 tty_putcode(tty, TTYC_CUB1); 2634 goto out; 2635 } 2636 tty_putcode_i(tty, TTYC_CUB, change); 2637 goto out; 2638 } else if (change < 0 && 2639 tty_term_has(term, TTYC_CUF) && 2640 !tty_use_margin(tty)) { 2641 tty_putcode_i(tty, TTYC_CUF, -change); 2642 goto out; 2643 } 2644 } else if (cx == thisx) { 2645 /* 2646 * Moving row only, column staying the same. 2647 */ 2648 2649 /* One above. */ 2650 if (thisy != tty->rupper && 2651 cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) { 2652 tty_putcode(tty, TTYC_CUU1); 2653 goto out; 2654 } 2655 2656 /* One below. */ 2657 if (thisy != tty->rlower && 2658 cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) { 2659 tty_putcode(tty, TTYC_CUD1); 2660 goto out; 2661 } 2662 2663 /* Calculate difference. */ 2664 change = thisy - cy; /* +ve up, -ve down */ 2665 2666 /* 2667 * Try to use VPA if change is larger than absolute or if this 2668 * change would cross the scroll region, otherwise use CUU/CUD. 2669 */ 2670 if ((u_int) abs(change) > cy || 2671 (change < 0 && cy - change > tty->rlower) || 2672 (change > 0 && cy - change < tty->rupper)) { 2673 if (tty_term_has(term, TTYC_VPA)) { 2674 tty_putcode_i(tty, TTYC_VPA, cy); 2675 goto out; 2676 } 2677 } else if (change > 0 && tty_term_has(term, TTYC_CUU)) { 2678 tty_putcode_i(tty, TTYC_CUU, change); 2679 goto out; 2680 } else if (change < 0 && tty_term_has(term, TTYC_CUD)) { 2681 tty_putcode_i(tty, TTYC_CUD, -change); 2682 goto out; 2683 } 2684 } 2685 2686 absolute: 2687 /* Absolute movement. */ 2688 tty_putcode_ii(tty, TTYC_CUP, cy, cx); 2689 2690 out: 2691 tty->cx = cx; 2692 tty->cy = cy; 2693 } 2694 2695 static void 2696 tty_hyperlink(struct tty *tty, const struct grid_cell *gc, 2697 struct hyperlinks *hl) 2698 { 2699 const char *uri, *id; 2700 2701 if (gc->link == tty->cell.link) 2702 return; 2703 tty->cell.link = gc->link; 2704 2705 if (hl == NULL) 2706 return; 2707 2708 if (gc->link == 0 || !hyperlinks_get(hl, gc->link, &uri, NULL, &id)) 2709 tty_putcode_ss(tty, TTYC_HLS, "", ""); 2710 else 2711 tty_putcode_ss(tty, TTYC_HLS, id, uri); 2712 } 2713 2714 void 2715 tty_attributes(struct tty *tty, const struct grid_cell *gc, 2716 const struct grid_cell *defaults, struct colour_palette *palette, 2717 struct hyperlinks *hl) 2718 { 2719 struct grid_cell *tc = &tty->cell, gc2; 2720 int changed; 2721 2722 /* Copy cell and update default colours. */ 2723 memcpy(&gc2, gc, sizeof gc2); 2724 if (~gc->flags & GRID_FLAG_NOPALETTE) { 2725 if (gc2.fg == 8) 2726 gc2.fg = defaults->fg; 2727 if (gc2.bg == 8) 2728 gc2.bg = defaults->bg; 2729 } 2730 2731 /* Ignore cell if it is the same as the last one. */ 2732 if (gc2.attr == tty->last_cell.attr && 2733 gc2.fg == tty->last_cell.fg && 2734 gc2.bg == tty->last_cell.bg && 2735 gc2.us == tty->last_cell.us && 2736 gc2.link == tty->last_cell.link) 2737 return; 2738 2739 /* 2740 * If no setab, try to use the reverse attribute as a best-effort for a 2741 * non-default background. This is a bit of a hack but it doesn't do 2742 * any serious harm and makes a couple of applications happier. 2743 */ 2744 if (!tty_term_has(tty->term, TTYC_SETAB)) { 2745 if (gc2.attr & GRID_ATTR_REVERSE) { 2746 if (gc2.fg != 7 && !COLOUR_DEFAULT(gc2.fg)) 2747 gc2.attr &= ~GRID_ATTR_REVERSE; 2748 } else { 2749 if (gc2.bg != 0 && !COLOUR_DEFAULT(gc2.bg)) 2750 gc2.attr |= GRID_ATTR_REVERSE; 2751 } 2752 } 2753 2754 /* Fix up the colours if necessary. */ 2755 tty_check_fg(tty, palette, &gc2); 2756 tty_check_bg(tty, palette, &gc2); 2757 tty_check_us(tty, palette, &gc2); 2758 2759 /* 2760 * If any bits are being cleared or the underline colour is now default, 2761 * reset everything. 2762 */ 2763 if ((tc->attr & ~gc2.attr) || (tc->us != gc2.us && gc2.us == 0)) 2764 tty_reset(tty); 2765 2766 /* 2767 * Set the colours. This may call tty_reset() (so it comes next) and 2768 * may add to (NOT remove) the desired attributes. 2769 */ 2770 tty_colours(tty, &gc2); 2771 2772 /* Filter out attribute bits already set. */ 2773 changed = gc2.attr & ~tc->attr; 2774 tc->attr = gc2.attr; 2775 2776 /* Set the attributes. */ 2777 if (changed & GRID_ATTR_BRIGHT) 2778 tty_putcode(tty, TTYC_BOLD); 2779 if (changed & GRID_ATTR_DIM) 2780 tty_putcode(tty, TTYC_DIM); 2781 if (changed & GRID_ATTR_ITALICS) 2782 tty_set_italics(tty); 2783 if (changed & GRID_ATTR_ALL_UNDERSCORE) { 2784 if (changed & GRID_ATTR_UNDERSCORE) 2785 tty_putcode(tty, TTYC_SMUL); 2786 else if (changed & GRID_ATTR_UNDERSCORE_2) 2787 tty_putcode_i(tty, TTYC_SMULX, 2); 2788 else if (changed & GRID_ATTR_UNDERSCORE_3) 2789 tty_putcode_i(tty, TTYC_SMULX, 3); 2790 else if (changed & GRID_ATTR_UNDERSCORE_4) 2791 tty_putcode_i(tty, TTYC_SMULX, 4); 2792 else if (changed & GRID_ATTR_UNDERSCORE_5) 2793 tty_putcode_i(tty, TTYC_SMULX, 5); 2794 } 2795 if (changed & GRID_ATTR_BLINK) 2796 tty_putcode(tty, TTYC_BLINK); 2797 if (changed & GRID_ATTR_REVERSE) { 2798 if (tty_term_has(tty->term, TTYC_REV)) 2799 tty_putcode(tty, TTYC_REV); 2800 else if (tty_term_has(tty->term, TTYC_SMSO)) 2801 tty_putcode(tty, TTYC_SMSO); 2802 } 2803 if (changed & GRID_ATTR_HIDDEN) 2804 tty_putcode(tty, TTYC_INVIS); 2805 if (changed & GRID_ATTR_STRIKETHROUGH) 2806 tty_putcode(tty, TTYC_SMXX); 2807 if (changed & GRID_ATTR_OVERLINE) 2808 tty_putcode(tty, TTYC_SMOL); 2809 if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty)) 2810 tty_putcode(tty, TTYC_SMACS); 2811 2812 /* Set hyperlink if any. */ 2813 tty_hyperlink(tty, gc, hl); 2814 2815 memcpy(&tty->last_cell, &gc2, sizeof tty->last_cell); 2816 } 2817 2818 static void 2819 tty_colours(struct tty *tty, const struct grid_cell *gc) 2820 { 2821 struct grid_cell *tc = &tty->cell; 2822 2823 /* No changes? Nothing is necessary. */ 2824 if (gc->fg == tc->fg && gc->bg == tc->bg && gc->us == tc->us) 2825 return; 2826 2827 /* 2828 * Is either the default colour? This is handled specially because the 2829 * best solution might be to reset both colours to default, in which 2830 * case if only one is default need to fall onward to set the other 2831 * colour. 2832 */ 2833 if (COLOUR_DEFAULT(gc->fg) || COLOUR_DEFAULT(gc->bg)) { 2834 /* 2835 * If don't have AX, send sgr0. This resets both colours to default. 2836 * Otherwise, try to set the default colour only as needed. 2837 */ 2838 if (!tty_term_flag(tty->term, TTYC_AX)) 2839 tty_reset(tty); 2840 else { 2841 if (COLOUR_DEFAULT(gc->fg) && !COLOUR_DEFAULT(tc->fg)) { 2842 tty_puts(tty, "\033[39m"); 2843 tc->fg = gc->fg; 2844 } 2845 if (COLOUR_DEFAULT(gc->bg) && !COLOUR_DEFAULT(tc->bg)) { 2846 tty_puts(tty, "\033[49m"); 2847 tc->bg = gc->bg; 2848 } 2849 } 2850 } 2851 2852 /* Set the foreground colour. */ 2853 if (!COLOUR_DEFAULT(gc->fg) && gc->fg != tc->fg) 2854 tty_colours_fg(tty, gc); 2855 2856 /* 2857 * Set the background colour. This must come after the foreground as 2858 * tty_colours_fg() can call tty_reset(). 2859 */ 2860 if (!COLOUR_DEFAULT(gc->bg) && gc->bg != tc->bg) 2861 tty_colours_bg(tty, gc); 2862 2863 /* Set the underscore colour. */ 2864 if (gc->us != tc->us) 2865 tty_colours_us(tty, gc); 2866 } 2867 2868 static void 2869 tty_check_fg(struct tty *tty, struct colour_palette *palette, 2870 struct grid_cell *gc) 2871 { 2872 u_char r, g, b; 2873 u_int colours; 2874 int c; 2875 2876 /* 2877 * Perform substitution if this pane has a palette. If the bright 2878 * attribute is set and Nobr is not present, use the bright entry in 2879 * the palette by changing to the aixterm colour 2880 */ 2881 if (~gc->flags & GRID_FLAG_NOPALETTE) { 2882 c = gc->fg; 2883 if (c < 8 && 2884 gc->attr & GRID_ATTR_BRIGHT && 2885 !tty_term_has(tty->term, TTYC_NOBR)) 2886 c += 90; 2887 if ((c = colour_palette_get(palette, c)) != -1) 2888 gc->fg = c; 2889 } 2890 2891 /* Is this a 24-bit colour? */ 2892 if (gc->fg & COLOUR_FLAG_RGB) { 2893 /* Not a 24-bit terminal? Translate to 256-colour palette. */ 2894 if (tty->term->flags & TERM_RGBCOLOURS) 2895 return; 2896 colour_split_rgb(gc->fg, &r, &g, &b); 2897 gc->fg = colour_find_rgb(r, g, b); 2898 } 2899 2900 /* How many colours does this terminal have? */ 2901 if (tty->term->flags & TERM_256COLOURS) 2902 colours = 256; 2903 else 2904 colours = tty_term_number(tty->term, TTYC_COLORS); 2905 2906 /* Is this a 256-colour colour? */ 2907 if (gc->fg & COLOUR_FLAG_256) { 2908 /* And not a 256 colour mode? */ 2909 if (colours >= 256) 2910 return; 2911 gc->fg = colour_256to16(gc->fg); 2912 if (~gc->fg & 8) 2913 return; 2914 gc->fg &= 7; 2915 if (colours >= 16) 2916 gc->fg += 90; 2917 else { 2918 /* 2919 * Mapping to black-on-black or white-on-white is not 2920 * much use, so change the foreground. 2921 */ 2922 if (gc->fg == 0 && gc->bg == 0) 2923 gc->fg = 7; 2924 else if (gc->fg == 7 && gc->bg == 7) 2925 gc->fg = 0; 2926 } 2927 return; 2928 } 2929 2930 /* Is this an aixterm colour? */ 2931 if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) { 2932 gc->fg -= 90; 2933 gc->attr |= GRID_ATTR_BRIGHT; 2934 } 2935 } 2936 2937 static void 2938 tty_check_bg(struct tty *tty, struct colour_palette *palette, 2939 struct grid_cell *gc) 2940 { 2941 u_char r, g, b; 2942 u_int colours; 2943 int c; 2944 2945 /* Perform substitution if this pane has a palette. */ 2946 if (~gc->flags & GRID_FLAG_NOPALETTE) { 2947 if ((c = colour_palette_get(palette, gc->bg)) != -1) 2948 gc->bg = c; 2949 } 2950 2951 /* Is this a 24-bit colour? */ 2952 if (gc->bg & COLOUR_FLAG_RGB) { 2953 /* Not a 24-bit terminal? Translate to 256-colour palette. */ 2954 if (tty->term->flags & TERM_RGBCOLOURS) 2955 return; 2956 colour_split_rgb(gc->bg, &r, &g, &b); 2957 gc->bg = colour_find_rgb(r, g, b); 2958 } 2959 2960 /* How many colours does this terminal have? */ 2961 if (tty->term->flags & TERM_256COLOURS) 2962 colours = 256; 2963 else 2964 colours = tty_term_number(tty->term, TTYC_COLORS); 2965 2966 /* Is this a 256-colour colour? */ 2967 if (gc->bg & COLOUR_FLAG_256) { 2968 /* 2969 * And not a 256 colour mode? Translate to 16-colour 2970 * palette. Bold background doesn't exist portably, so just 2971 * discard the bold bit if set. 2972 */ 2973 if (colours >= 256) 2974 return; 2975 gc->bg = colour_256to16(gc->bg); 2976 if (~gc->bg & 8) 2977 return; 2978 gc->bg &= 7; 2979 if (colours >= 16) 2980 gc->bg += 90; 2981 return; 2982 } 2983 2984 /* Is this an aixterm colour? */ 2985 if (gc->bg >= 90 && gc->bg <= 97 && colours < 16) 2986 gc->bg -= 90; 2987 } 2988 2989 static void 2990 tty_check_us(__unused struct tty *tty, struct colour_palette *palette, 2991 struct grid_cell *gc) 2992 { 2993 int c; 2994 2995 /* Perform substitution if this pane has a palette. */ 2996 if (~gc->flags & GRID_FLAG_NOPALETTE) { 2997 if ((c = colour_palette_get(palette, gc->us)) != -1) 2998 gc->us = c; 2999 } 3000 3001 /* Convert underscore colour if only RGB can be supported. */ 3002 if (!tty_term_has(tty->term, TTYC_SETULC1)) { 3003 if ((c = colour_force_rgb (gc->us)) == -1) 3004 gc->us = 8; 3005 else 3006 gc->us = c; 3007 } 3008 } 3009 3010 static void 3011 tty_colours_fg(struct tty *tty, const struct grid_cell *gc) 3012 { 3013 struct grid_cell *tc = &tty->cell; 3014 char s[32]; 3015 3016 /* 3017 * If the current colour is an aixterm bright colour and the new is not, 3018 * reset because some terminals do not clear bright correctly. 3019 */ 3020 if (tty->cell.fg >= 90 && 3021 tty->cell.bg <= 97 && 3022 (gc->fg < 90 || gc->fg > 97)) 3023 tty_reset(tty); 3024 3025 /* Is this a 24-bit or 256-colour colour? */ 3026 if (gc->fg & COLOUR_FLAG_RGB || gc->fg & COLOUR_FLAG_256) { 3027 if (tty_try_colour(tty, gc->fg, "38") == 0) 3028 goto save; 3029 /* Should not get here, already converted in tty_check_fg. */ 3030 return; 3031 } 3032 3033 /* Is this an aixterm bright colour? */ 3034 if (gc->fg >= 90 && gc->fg <= 97) { 3035 if (tty->term->flags & TERM_256COLOURS) { 3036 xsnprintf(s, sizeof s, "\033[%dm", gc->fg); 3037 tty_puts(tty, s); 3038 } else 3039 tty_putcode_i(tty, TTYC_SETAF, gc->fg - 90 + 8); 3040 goto save; 3041 } 3042 3043 /* Otherwise set the foreground colour. */ 3044 tty_putcode_i(tty, TTYC_SETAF, gc->fg); 3045 3046 save: 3047 /* Save the new values in the terminal current cell. */ 3048 tc->fg = gc->fg; 3049 } 3050 3051 static void 3052 tty_colours_bg(struct tty *tty, const struct grid_cell *gc) 3053 { 3054 struct grid_cell *tc = &tty->cell; 3055 char s[32]; 3056 3057 /* Is this a 24-bit or 256-colour colour? */ 3058 if (gc->bg & COLOUR_FLAG_RGB || gc->bg & COLOUR_FLAG_256) { 3059 if (tty_try_colour(tty, gc->bg, "48") == 0) 3060 goto save; 3061 /* Should not get here, already converted in tty_check_bg. */ 3062 return; 3063 } 3064 3065 /* Is this an aixterm bright colour? */ 3066 if (gc->bg >= 90 && gc->bg <= 97) { 3067 if (tty->term->flags & TERM_256COLOURS) { 3068 xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10); 3069 tty_puts(tty, s); 3070 } else 3071 tty_putcode_i(tty, TTYC_SETAB, gc->bg - 90 + 8); 3072 goto save; 3073 } 3074 3075 /* Otherwise set the background colour. */ 3076 tty_putcode_i(tty, TTYC_SETAB, gc->bg); 3077 3078 save: 3079 /* Save the new values in the terminal current cell. */ 3080 tc->bg = gc->bg; 3081 } 3082 3083 static void 3084 tty_colours_us(struct tty *tty, const struct grid_cell *gc) 3085 { 3086 struct grid_cell *tc = &tty->cell; 3087 u_int c; 3088 u_char r, g, b; 3089 3090 /* Clear underline colour. */ 3091 if (COLOUR_DEFAULT(gc->us)) { 3092 tty_putcode(tty, TTYC_OL); 3093 goto save; 3094 } 3095 3096 /* 3097 * If this is not an RGB colour, use Setulc1 if it exists, otherwise 3098 * convert. 3099 */ 3100 if (~gc->us & COLOUR_FLAG_RGB) { 3101 c = gc->us; 3102 if ((~c & COLOUR_FLAG_256) && (c >= 90 && c <= 97)) 3103 c -= 82; 3104 tty_putcode_i(tty, TTYC_SETULC1, c & ~COLOUR_FLAG_256); 3105 return; 3106 } 3107 3108 /* 3109 * Setulc and setal follows the ncurses(3) one argument "direct colour" 3110 * capability format. Calculate the colour value. 3111 */ 3112 colour_split_rgb(gc->us, &r, &g, &b); 3113 c = (65536 * r) + (256 * g) + b; 3114 3115 /* 3116 * Write the colour. Only use setal if the RGB flag is set because the 3117 * non-RGB version may be wrong. 3118 */ 3119 if (tty_term_has(tty->term, TTYC_SETULC)) 3120 tty_putcode_i(tty, TTYC_SETULC, c); 3121 else if (tty_term_has(tty->term, TTYC_SETAL) && 3122 tty_term_has(tty->term, TTYC_RGB)) 3123 tty_putcode_i(tty, TTYC_SETAL, c); 3124 3125 save: 3126 /* Save the new values in the terminal current cell. */ 3127 tc->us = gc->us; 3128 } 3129 3130 static int 3131 tty_try_colour(struct tty *tty, int colour, const char *type) 3132 { 3133 u_char r, g, b; 3134 3135 if (colour & COLOUR_FLAG_256) { 3136 if (*type == '3' && tty_term_has(tty->term, TTYC_SETAF)) 3137 tty_putcode_i(tty, TTYC_SETAF, colour & 0xff); 3138 else if (tty_term_has(tty->term, TTYC_SETAB)) 3139 tty_putcode_i(tty, TTYC_SETAB, colour & 0xff); 3140 return (0); 3141 } 3142 3143 if (colour & COLOUR_FLAG_RGB) { 3144 colour_split_rgb(colour & 0xffffff, &r, &g, &b); 3145 if (*type == '3' && tty_term_has(tty->term, TTYC_SETRGBF)) 3146 tty_putcode_iii(tty, TTYC_SETRGBF, r, g, b); 3147 else if (tty_term_has(tty->term, TTYC_SETRGBB)) 3148 tty_putcode_iii(tty, TTYC_SETRGBB, r, g, b); 3149 return (0); 3150 } 3151 3152 return (-1); 3153 } 3154 3155 static void 3156 tty_window_default_style(struct grid_cell *gc, struct window_pane *wp) 3157 { 3158 memcpy(gc, &grid_default_cell, sizeof *gc); 3159 gc->fg = wp->palette.fg; 3160 gc->bg = wp->palette.bg; 3161 } 3162 3163 void 3164 tty_default_colours(struct grid_cell *gc, struct window_pane *wp) 3165 { 3166 struct options *oo = wp->options; 3167 struct format_tree *ft; 3168 3169 memcpy(gc, &grid_default_cell, sizeof *gc); 3170 3171 if (wp->flags & PANE_STYLECHANGED) { 3172 log_debug("%%%u: style changed", wp->id); 3173 wp->flags &= ~PANE_STYLECHANGED; 3174 3175 ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, 3176 FORMAT_NOJOBS); 3177 format_defaults(ft, NULL, NULL, NULL, wp); 3178 tty_window_default_style(&wp->cached_active_gc, wp); 3179 style_add(&wp->cached_active_gc, oo, "window-active-style", ft); 3180 tty_window_default_style(&wp->cached_gc, wp); 3181 style_add(&wp->cached_gc, oo, "window-style", ft); 3182 format_free(ft); 3183 } 3184 3185 if (gc->fg == 8) { 3186 if (wp == wp->window->active && wp->cached_active_gc.fg != 8) 3187 gc->fg = wp->cached_active_gc.fg; 3188 else 3189 gc->fg = wp->cached_gc.fg; 3190 } 3191 3192 if (gc->bg == 8) { 3193 if (wp == wp->window->active && wp->cached_active_gc.bg != 8) 3194 gc->bg = wp->cached_active_gc.bg; 3195 else 3196 gc->bg = wp->cached_gc.bg; 3197 } 3198 } 3199 3200 static void 3201 tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, 3202 struct colour_palette *palette, u_int bg, struct hyperlinks *hl) 3203 { 3204 struct grid_cell gc; 3205 3206 memcpy(&gc, &grid_default_cell, sizeof gc); 3207 gc.bg = bg; 3208 tty_attributes(tty, &gc, defaults, palette, hl); 3209 } 3210 3211 static void 3212 tty_clipboard_query_callback(__unused int fd, __unused short events, void *data) 3213 { 3214 struct tty *tty = data; 3215 struct client *c = tty->client; 3216 3217 c->flags &= ~CLIENT_CLIPBOARDBUFFER; 3218 free(c->clipboard_panes); 3219 c->clipboard_panes = NULL; 3220 c->clipboard_npanes = 0; 3221 3222 tty->flags &= ~TTY_OSC52QUERY; 3223 } 3224 3225 void 3226 tty_clipboard_query(struct tty *tty) 3227 { 3228 struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT }; 3229 3230 if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY)) 3231 return; 3232 tty_putcode_ss(tty, TTYC_MS, "", "?"); 3233 3234 tty->flags |= TTY_OSC52QUERY; 3235 evtimer_add(&tty->clipboard_timer, &tv); 3236 } 3237