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