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