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