1 /* $OpenBSD: screen-write.c,v 1.143 2018/11/13 11:36:37 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 21 #include <stdlib.h> 22 #include <string.h> 23 24 #include "tmux.h" 25 26 static void screen_write_initctx(struct screen_write_ctx *, 27 struct tty_ctx *); 28 static void screen_write_collect_clear(struct screen_write_ctx *, u_int, 29 u_int); 30 static void screen_write_collect_scroll(struct screen_write_ctx *); 31 static void screen_write_collect_flush(struct screen_write_ctx *, int); 32 33 static int screen_write_overwrite(struct screen_write_ctx *, 34 struct grid_cell *, u_int); 35 static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, 36 const struct utf8_data *, u_int *); 37 38 static const struct grid_cell screen_write_pad_cell = { 39 GRID_FLAG_PADDING, 0, 8, 8, { { 0 }, 0, 0, 0 } 40 }; 41 42 struct screen_write_collect_item { 43 u_int x; 44 int wrapped; 45 46 u_int used; 47 char data[256]; 48 49 struct grid_cell gc; 50 51 TAILQ_ENTRY(screen_write_collect_item) entry; 52 }; 53 struct screen_write_collect_line { 54 TAILQ_HEAD(, screen_write_collect_item) items; 55 }; 56 57 static void 58 screen_write_offset_timer(__unused int fd, __unused short events, void *data) 59 { 60 struct window *w = data; 61 62 tty_update_window_offset(w); 63 } 64 65 /* Set cursor position. */ 66 static void 67 screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy) 68 { 69 struct window_pane *wp = ctx->wp; 70 struct window *w; 71 struct screen *s = ctx->s; 72 struct timeval tv = { .tv_usec = 10000 }; 73 74 if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy) 75 return; 76 77 if (cx != -1) 78 s->cx = cx; 79 if (cy != -1) 80 s->cy = cy; 81 82 if (wp == NULL) 83 return; 84 w = wp->window; 85 86 if (!event_initialized(&w->offset_timer)) 87 evtimer_set(&w->offset_timer, screen_write_offset_timer, w); 88 if (!evtimer_pending(&w->offset_timer, NULL)) 89 evtimer_add(&w->offset_timer, &tv); 90 } 91 92 /* Initialize writing with a window. */ 93 void 94 screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, 95 struct screen *s) 96 { 97 char tmp[32]; 98 u_int y; 99 100 memset(ctx, 0, sizeof *ctx); 101 102 ctx->wp = wp; 103 if (wp != NULL && s == NULL) 104 ctx->s = wp->screen; 105 else 106 ctx->s = s; 107 108 ctx->list = xcalloc(screen_size_y(ctx->s), sizeof *ctx->list); 109 for (y = 0; y < screen_size_y(ctx->s); y++) 110 TAILQ_INIT(&ctx->list[y].items); 111 ctx->item = xcalloc(1, sizeof *ctx->item); 112 113 ctx->scrolled = 0; 114 ctx->bg = 8; 115 116 if (wp != NULL) { 117 snprintf(tmp, sizeof tmp, "pane %%%u (at %u,%u)", wp->id, 118 wp->xoff, wp->yoff); 119 } 120 log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s), 121 screen_size_y(ctx->s), wp == NULL ? "no pane" : tmp); 122 } 123 124 /* Finish writing. */ 125 void 126 screen_write_stop(struct screen_write_ctx *ctx) 127 { 128 screen_write_collect_end(ctx); 129 screen_write_collect_flush(ctx, 0); 130 131 log_debug("%s: %u cells (%u written, %u skipped)", __func__, 132 ctx->cells, ctx->written, ctx->skipped); 133 134 free(ctx->item); 135 free(ctx->list); /* flush will have emptied */ 136 } 137 138 /* Reset screen state. */ 139 void 140 screen_write_reset(struct screen_write_ctx *ctx) 141 { 142 struct screen *s = ctx->s; 143 144 screen_reset_tabs(s); 145 screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); 146 147 s->mode = MODE_CURSOR | MODE_WRAP; 148 149 screen_write_clearscreen(ctx, 8); 150 screen_write_cursormove(ctx, 0, 0); 151 } 152 153 /* Write character. */ 154 void 155 screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp, 156 u_char ch) 157 { 158 struct grid_cell gc; 159 160 memcpy(&gc, gcp, sizeof gc); 161 162 utf8_set(&gc.data, ch); 163 screen_write_cell(ctx, &gc); 164 } 165 166 /* Calculate string length, with embedded formatting. */ 167 size_t 168 screen_write_cstrlen(const char *fmt, ...) 169 { 170 va_list ap; 171 char *msg, *msg2, *ptr, *ptr2; 172 size_t size; 173 174 va_start(ap, fmt); 175 xvasprintf(&msg, fmt, ap); 176 va_end(ap); 177 msg2 = xmalloc(strlen(msg) + 1); 178 179 ptr = msg; 180 ptr2 = msg2; 181 while (*ptr != '\0') { 182 if (ptr[0] == '#' && ptr[1] == '[') { 183 while (*ptr != ']' && *ptr != '\0') 184 ptr++; 185 if (*ptr == ']') 186 ptr++; 187 continue; 188 } 189 *ptr2++ = *ptr++; 190 } 191 *ptr2 = '\0'; 192 193 size = screen_write_strlen("%s", msg2); 194 195 free(msg); 196 free(msg2); 197 198 return (size); 199 } 200 201 /* Calculate string length. */ 202 size_t 203 screen_write_strlen(const char *fmt, ...) 204 { 205 va_list ap; 206 char *msg; 207 struct utf8_data ud; 208 u_char *ptr; 209 size_t left, size = 0; 210 enum utf8_state more; 211 212 va_start(ap, fmt); 213 xvasprintf(&msg, fmt, ap); 214 va_end(ap); 215 216 ptr = msg; 217 while (*ptr != '\0') { 218 if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { 219 ptr++; 220 221 left = strlen(ptr); 222 if (left < (size_t)ud.size - 1) 223 break; 224 while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) 225 ptr++; 226 ptr++; 227 228 if (more == UTF8_DONE) 229 size += ud.width; 230 } else { 231 if (*ptr > 0x1f && *ptr < 0x7f) 232 size++; 233 ptr++; 234 } 235 } 236 237 free(msg); 238 return (size); 239 } 240 241 /* Write simple string (no UTF-8 or maximum length). */ 242 void 243 screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp, 244 const char *fmt, ...) 245 { 246 va_list ap; 247 248 va_start(ap, fmt); 249 screen_write_vnputs(ctx, -1, gcp, fmt, ap); 250 va_end(ap); 251 } 252 253 /* Write string with length limit (-1 for unlimited). */ 254 void 255 screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, 256 const struct grid_cell *gcp, const char *fmt, ...) 257 { 258 va_list ap; 259 260 va_start(ap, fmt); 261 screen_write_vnputs(ctx, maxlen, gcp, fmt, ap); 262 va_end(ap); 263 } 264 265 void 266 screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, 267 const struct grid_cell *gcp, const char *fmt, va_list ap) 268 { 269 struct grid_cell gc; 270 struct utf8_data *ud = &gc.data; 271 char *msg; 272 u_char *ptr; 273 size_t left, size = 0; 274 enum utf8_state more; 275 276 memcpy(&gc, gcp, sizeof gc); 277 xvasprintf(&msg, fmt, ap); 278 279 ptr = msg; 280 while (*ptr != '\0') { 281 if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { 282 ptr++; 283 284 left = strlen(ptr); 285 if (left < (size_t)ud->size - 1) 286 break; 287 while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) 288 ptr++; 289 ptr++; 290 291 if (more != UTF8_DONE) 292 continue; 293 if (maxlen > 0 && size + ud->width > (size_t)maxlen) { 294 while (size < (size_t)maxlen) { 295 screen_write_putc(ctx, &gc, ' '); 296 size++; 297 } 298 break; 299 } 300 size += ud->width; 301 screen_write_cell(ctx, &gc); 302 } else { 303 if (maxlen > 0 && size + 1 > (size_t)maxlen) 304 break; 305 306 if (*ptr == '\001') 307 gc.attr ^= GRID_ATTR_CHARSET; 308 else if (*ptr > 0x1f && *ptr < 0x7f) { 309 size++; 310 screen_write_putc(ctx, &gc, *ptr); 311 } 312 ptr++; 313 } 314 } 315 316 free(msg); 317 } 318 319 /* Write string, similar to nputs, but with embedded formatting (#[]). */ 320 void 321 screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, 322 const struct grid_cell *gcp, const char *fmt, ...) 323 { 324 struct grid_cell gc; 325 struct utf8_data *ud = &gc.data; 326 va_list ap; 327 char *msg; 328 u_char *ptr, *last; 329 size_t left, size = 0; 330 enum utf8_state more; 331 332 memcpy(&gc, gcp, sizeof gc); 333 334 va_start(ap, fmt); 335 xvasprintf(&msg, fmt, ap); 336 va_end(ap); 337 338 ptr = msg; 339 while (*ptr != '\0') { 340 if (ptr[0] == '#' && ptr[1] == '[') { 341 ptr += 2; 342 last = ptr + strcspn(ptr, "]"); 343 if (*last == '\0') { 344 /* No ]. Not much point in doing anything. */ 345 break; 346 } 347 *last = '\0'; 348 349 style_parse(gcp, &gc, ptr); 350 ptr = last + 1; 351 continue; 352 } 353 if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { 354 ptr++; 355 356 left = strlen(ptr); 357 if (left < (size_t)ud->size - 1) 358 break; 359 while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) 360 ptr++; 361 ptr++; 362 363 if (more != UTF8_DONE) 364 continue; 365 if (maxlen > 0 && size + ud->width > (size_t)maxlen) { 366 while (size < (size_t)maxlen) { 367 screen_write_putc(ctx, &gc, ' '); 368 size++; 369 } 370 break; 371 } 372 size += ud->width; 373 screen_write_cell(ctx, &gc); 374 } else { 375 if (maxlen > 0 && size + 1 > (size_t)maxlen) 376 break; 377 378 if (*ptr == '\001') 379 gc.attr ^= GRID_ATTR_CHARSET; 380 else if (*ptr > 0x1f && *ptr < 0x7f) { 381 size++; 382 screen_write_putc(ctx, &gc, *ptr); 383 } 384 ptr++; 385 } 386 } 387 388 free(msg); 389 } 390 391 /* Copy from another screen. Assumes target region is big enough. */ 392 void 393 screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px, 394 u_int py, u_int nx, u_int ny, bitstr_t *mbs, const struct grid_cell *mgc) 395 { 396 struct screen *s = ctx->s; 397 struct grid *gd = src->grid; 398 struct grid_cell gc; 399 u_int xx, yy, cx, cy, b; 400 401 if (nx == 0 || ny == 0) 402 return; 403 404 cx = s->cx; 405 cy = s->cy; 406 407 for (yy = py; yy < py + ny; yy++) { 408 for (xx = px; xx < px + nx; xx++) { 409 grid_get_cell(gd, xx, yy, &gc); 410 if (mbs != NULL) { 411 b = (yy * screen_size_x(src)) + xx; 412 if (bit_test(mbs, b)) { 413 gc.attr = mgc->attr; 414 gc.fg = mgc->fg; 415 gc.bg = mgc->bg; 416 } 417 } 418 if (xx + gc.data.width <= px + nx) 419 screen_write_cell(ctx, &gc); 420 } 421 cy++; 422 screen_write_cursormove(ctx, cx, cy); 423 } 424 } 425 426 /* 427 * Copy from another screen but without the selection stuff. Also assumes the 428 * target region is already big enough and already cleared. 429 */ 430 void 431 screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src, 432 u_int px, u_int py, u_int nx, u_int ny) 433 { 434 struct screen *s = ctx->s; 435 struct grid *gd = src->grid; 436 struct grid_cell gc; 437 u_int xx, yy, cx, cy; 438 439 if (nx == 0 || ny == 0) 440 return; 441 442 cy = s->cy; 443 for (yy = py; yy < py + ny; yy++) { 444 if (yy >= gd->hsize + gd->sy) 445 break; 446 cx = s->cx; 447 for (xx = px; xx < px + nx; xx++) { 448 if (xx >= grid_get_line(gd, yy)->cellsize) 449 break; 450 grid_get_cell(gd, xx, yy, &gc); 451 if (xx + gc.data.width > px + nx) 452 break; 453 if (!grid_cells_equal(&gc, &grid_default_cell)) 454 grid_view_set_cell(ctx->s->grid, cx, cy, &gc); 455 cx++; 456 } 457 cy++; 458 } 459 } 460 461 /* Draw a horizontal line on screen. */ 462 void 463 screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right) 464 { 465 struct screen *s = ctx->s; 466 struct grid_cell gc; 467 u_int cx, cy, i; 468 469 cx = s->cx; 470 cy = s->cy; 471 472 memcpy(&gc, &grid_default_cell, sizeof gc); 473 gc.attr |= GRID_ATTR_CHARSET; 474 475 screen_write_putc(ctx, &gc, left ? 't' : 'q'); 476 for (i = 1; i < nx - 1; i++) 477 screen_write_putc(ctx, &gc, 'q'); 478 screen_write_putc(ctx, &gc, right ? 'u' : 'q'); 479 480 screen_write_cursormove(ctx, cx, cy); 481 } 482 483 /* Draw a horizontal line on screen. */ 484 void 485 screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom) 486 { 487 struct screen *s = ctx->s; 488 struct grid_cell gc; 489 u_int cx, cy, i; 490 491 cx = s->cx; 492 cy = s->cy; 493 494 memcpy(&gc, &grid_default_cell, sizeof gc); 495 gc.attr |= GRID_ATTR_CHARSET; 496 497 screen_write_putc(ctx, &gc, top ? 'w' : 'x'); 498 for (i = 1; i < ny - 1; i++) { 499 screen_write_cursormove(ctx, cx, cy + i); 500 screen_write_putc(ctx, &gc, 'x'); 501 } 502 screen_write_cursormove(ctx, cx, cy + ny - 1); 503 screen_write_putc(ctx, &gc, bottom ? 'v' : 'x'); 504 505 screen_write_cursormove(ctx, cx, cy); 506 } 507 508 /* Draw a box on screen. */ 509 void 510 screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny) 511 { 512 struct screen *s = ctx->s; 513 struct grid_cell gc; 514 u_int cx, cy, i; 515 516 cx = s->cx; 517 cy = s->cy; 518 519 memcpy(&gc, &grid_default_cell, sizeof gc); 520 gc.attr |= GRID_ATTR_CHARSET; 521 522 screen_write_putc(ctx, &gc, 'l'); 523 for (i = 1; i < nx - 1; i++) 524 screen_write_putc(ctx, &gc, 'q'); 525 screen_write_putc(ctx, &gc, 'k'); 526 527 screen_write_cursormove(ctx, cx, cy + ny - 1); 528 screen_write_putc(ctx, &gc, 'm'); 529 for (i = 1; i < nx - 1; i++) 530 screen_write_putc(ctx, &gc, 'q'); 531 screen_write_putc(ctx, &gc, 'j'); 532 533 for (i = 1; i < ny - 1; i++) { 534 screen_write_cursormove(ctx, cx, cy + i); 535 screen_write_putc(ctx, &gc, 'x'); 536 } 537 for (i = 1; i < ny - 1; i++) { 538 screen_write_cursormove(ctx, cx + nx - 1, cy + i); 539 screen_write_putc(ctx, &gc, 'x'); 540 } 541 542 screen_write_cursormove(ctx, cx, cy); 543 } 544 545 /* 546 * Write a preview version of a window. Assumes target area is big enough and 547 * already cleared. 548 */ 549 void 550 screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx, 551 u_int ny) 552 { 553 struct screen *s = ctx->s; 554 struct grid_cell gc; 555 u_int cx, cy, px, py; 556 557 cx = s->cx; 558 cy = s->cy; 559 560 /* 561 * If the cursor is on, pick the area around the cursor, otherwise use 562 * the top left. 563 */ 564 if (src->mode & MODE_CURSOR) { 565 px = src->cx; 566 if (px < nx / 3) 567 px = 0; 568 else 569 px = px - nx / 3; 570 if (px + nx > screen_size_x(src)) { 571 if (nx > screen_size_x(src)) 572 px = 0; 573 else 574 px = screen_size_x(src) - nx; 575 } 576 py = src->cy; 577 if (py < ny / 3) 578 py = 0; 579 else 580 py = py - ny / 3; 581 if (py + ny > screen_size_y(src)) { 582 if (ny > screen_size_y(src)) 583 py = 0; 584 else 585 py = screen_size_y(src) - ny; 586 } 587 } else { 588 px = 0; 589 py = 0; 590 } 591 592 screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny); 593 594 if (src->mode & MODE_CURSOR) { 595 grid_view_get_cell(src->grid, src->cx, src->cy, &gc); 596 gc.attr |= GRID_ATTR_REVERSE; 597 screen_write_cursormove(ctx, cx + (src->cx - px), 598 cy + (src->cy - py)); 599 screen_write_cell(ctx, &gc); 600 } 601 } 602 603 /* Set up context for TTY command. */ 604 static void 605 screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) 606 { 607 struct screen *s = ctx->s; 608 609 memset(ttyctx, 0, sizeof *ttyctx); 610 611 ttyctx->wp = ctx->wp; 612 613 ttyctx->ocx = s->cx; 614 ttyctx->ocy = s->cy; 615 616 ttyctx->orlower = s->rlower; 617 ttyctx->orupper = s->rupper; 618 } 619 620 /* Set a mode. */ 621 void 622 screen_write_mode_set(struct screen_write_ctx *ctx, int mode) 623 { 624 struct screen *s = ctx->s; 625 626 s->mode |= mode; 627 } 628 629 /* Clear a mode. */ 630 void 631 screen_write_mode_clear(struct screen_write_ctx *ctx, int mode) 632 { 633 struct screen *s = ctx->s; 634 635 s->mode &= ~mode; 636 } 637 638 /* Cursor up by ny. */ 639 void 640 screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) 641 { 642 struct screen *s = ctx->s; 643 u_int cx = s->cx, cy = s->cy; 644 645 if (ny == 0) 646 ny = 1; 647 648 if (cy < s->rupper) { 649 /* Above region. */ 650 if (ny > cy) 651 ny = cy; 652 } else { 653 /* Below region. */ 654 if (ny > cy - s->rupper) 655 ny = cy - s->rupper; 656 } 657 if (cx == screen_size_x(s)) 658 cx--; 659 660 cy -= ny; 661 662 screen_write_set_cursor(ctx, cx, cy); 663 } 664 665 /* Cursor down by ny. */ 666 void 667 screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) 668 { 669 struct screen *s = ctx->s; 670 u_int cx = s->cx, cy = s->cy; 671 672 if (ny == 0) 673 ny = 1; 674 675 if (cy > s->rlower) { 676 /* Below region. */ 677 if (ny > screen_size_y(s) - 1 - cy) 678 ny = screen_size_y(s) - 1 - cy; 679 } else { 680 /* Above region. */ 681 if (ny > s->rlower - cy) 682 ny = s->rlower - cy; 683 } 684 if (cx == screen_size_x(s)) 685 cx--; 686 else if (ny == 0) 687 return; 688 689 cy += ny; 690 691 screen_write_set_cursor(ctx, cx, cy); 692 } 693 694 /* Cursor right by nx. */ 695 void 696 screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx) 697 { 698 struct screen *s = ctx->s; 699 u_int cx = s->cx, cy = s->cy; 700 701 if (nx == 0) 702 nx = 1; 703 704 if (nx > screen_size_x(s) - 1 - cx) 705 nx = screen_size_x(s) - 1 - cx; 706 if (nx == 0) 707 return; 708 709 cx += nx; 710 711 screen_write_set_cursor(ctx, cx, cy); 712 } 713 714 /* Cursor left by nx. */ 715 void 716 screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx) 717 { 718 struct screen *s = ctx->s; 719 u_int cx = s->cx, cy = s->cy; 720 721 if (nx == 0) 722 nx = 1; 723 724 if (nx > cx) 725 nx = cx; 726 if (nx == 0) 727 return; 728 729 cx -= nx; 730 731 screen_write_set_cursor(ctx, cx, cy); 732 } 733 734 /* Backspace; cursor left unless at start of wrapped line when can move up. */ 735 void 736 screen_write_backspace(struct screen_write_ctx *ctx) 737 { 738 struct screen *s = ctx->s; 739 struct grid_line *gl; 740 u_int cx = s->cx, cy = s->cy; 741 742 if (cx == 0) { 743 if (cy == 0) 744 return; 745 gl = grid_get_line(s->grid, s->grid->hsize + cy - 1); 746 if (gl->flags & GRID_LINE_WRAPPED) { 747 cy--; 748 cx = screen_size_x(s) - 1; 749 } 750 } else 751 cx--; 752 753 screen_write_set_cursor(ctx, cx, cy); 754 } 755 756 /* VT100 alignment test. */ 757 void 758 screen_write_alignmenttest(struct screen_write_ctx *ctx) 759 { 760 struct screen *s = ctx->s; 761 struct tty_ctx ttyctx; 762 struct grid_cell gc; 763 u_int xx, yy; 764 765 memcpy(&gc, &grid_default_cell, sizeof gc); 766 utf8_set(&gc.data, 'E'); 767 768 for (yy = 0; yy < screen_size_y(s); yy++) { 769 for (xx = 0; xx < screen_size_x(s); xx++) 770 grid_view_set_cell(s->grid, xx, yy, &gc); 771 } 772 773 screen_write_set_cursor(ctx, 0, 0); 774 775 s->rupper = 0; 776 s->rlower = screen_size_y(s) - 1; 777 778 screen_write_initctx(ctx, &ttyctx); 779 780 screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1); 781 tty_write(tty_cmd_alignmenttest, &ttyctx); 782 } 783 784 /* Insert nx characters. */ 785 void 786 screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) 787 { 788 struct screen *s = ctx->s; 789 struct tty_ctx ttyctx; 790 791 if (nx == 0) 792 nx = 1; 793 794 if (nx > screen_size_x(s) - s->cx) 795 nx = screen_size_x(s) - s->cx; 796 if (nx == 0) 797 return; 798 799 if (s->cx > screen_size_x(s) - 1) 800 return; 801 802 screen_write_initctx(ctx, &ttyctx); 803 ttyctx.bg = bg; 804 805 grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); 806 807 screen_write_collect_flush(ctx, 0); 808 ttyctx.num = nx; 809 tty_write(tty_cmd_insertcharacter, &ttyctx); 810 } 811 812 /* Delete nx characters. */ 813 void 814 screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) 815 { 816 struct screen *s = ctx->s; 817 struct tty_ctx ttyctx; 818 819 if (nx == 0) 820 nx = 1; 821 822 if (nx > screen_size_x(s) - s->cx) 823 nx = screen_size_x(s) - s->cx; 824 if (nx == 0) 825 return; 826 827 if (s->cx > screen_size_x(s) - 1) 828 return; 829 830 screen_write_initctx(ctx, &ttyctx); 831 ttyctx.bg = bg; 832 833 grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); 834 835 screen_write_collect_flush(ctx, 0); 836 ttyctx.num = nx; 837 tty_write(tty_cmd_deletecharacter, &ttyctx); 838 } 839 840 /* Clear nx characters. */ 841 void 842 screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) 843 { 844 struct screen *s = ctx->s; 845 struct tty_ctx ttyctx; 846 847 if (nx == 0) 848 nx = 1; 849 850 if (nx > screen_size_x(s) - s->cx) 851 nx = screen_size_x(s) - s->cx; 852 if (nx == 0) 853 return; 854 855 if (s->cx > screen_size_x(s) - 1) 856 return; 857 858 screen_write_initctx(ctx, &ttyctx); 859 ttyctx.bg = bg; 860 861 grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg); 862 863 screen_write_collect_flush(ctx, 0); 864 ttyctx.num = nx; 865 tty_write(tty_cmd_clearcharacter, &ttyctx); 866 } 867 868 /* Insert ny lines. */ 869 void 870 screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) 871 { 872 struct screen *s = ctx->s; 873 struct grid *gd = s->grid; 874 struct tty_ctx ttyctx; 875 876 if (ny == 0) 877 ny = 1; 878 879 if (s->cy < s->rupper || s->cy > s->rlower) { 880 if (ny > screen_size_y(s) - s->cy) 881 ny = screen_size_y(s) - s->cy; 882 if (ny == 0) 883 return; 884 885 screen_write_initctx(ctx, &ttyctx); 886 ttyctx.bg = bg; 887 888 grid_view_insert_lines(gd, s->cy, ny, bg); 889 890 screen_write_collect_flush(ctx, 0); 891 ttyctx.num = ny; 892 tty_write(tty_cmd_insertline, &ttyctx); 893 return; 894 } 895 896 if (ny > s->rlower + 1 - s->cy) 897 ny = s->rlower + 1 - s->cy; 898 if (ny == 0) 899 return; 900 901 screen_write_initctx(ctx, &ttyctx); 902 ttyctx.bg = bg; 903 904 if (s->cy < s->rupper || s->cy > s->rlower) 905 grid_view_insert_lines(gd, s->cy, ny, bg); 906 else 907 grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg); 908 909 screen_write_collect_flush(ctx, 0); 910 ttyctx.num = ny; 911 tty_write(tty_cmd_insertline, &ttyctx); 912 } 913 914 /* Delete ny lines. */ 915 void 916 screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) 917 { 918 struct screen *s = ctx->s; 919 struct grid *gd = s->grid; 920 struct tty_ctx ttyctx; 921 922 if (ny == 0) 923 ny = 1; 924 925 if (s->cy < s->rupper || s->cy > s->rlower) { 926 if (ny > screen_size_y(s) - s->cy) 927 ny = screen_size_y(s) - s->cy; 928 if (ny == 0) 929 return; 930 931 screen_write_initctx(ctx, &ttyctx); 932 ttyctx.bg = bg; 933 934 grid_view_delete_lines(gd, s->cy, ny, bg); 935 936 screen_write_collect_flush(ctx, 0); 937 ttyctx.num = ny; 938 tty_write(tty_cmd_deleteline, &ttyctx); 939 return; 940 } 941 942 if (ny > s->rlower + 1 - s->cy) 943 ny = s->rlower + 1 - s->cy; 944 if (ny == 0) 945 return; 946 947 screen_write_initctx(ctx, &ttyctx); 948 ttyctx.bg = bg; 949 950 if (s->cy < s->rupper || s->cy > s->rlower) 951 grid_view_delete_lines(gd, s->cy, ny, bg); 952 else 953 grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg); 954 955 screen_write_collect_flush(ctx, 0); 956 ttyctx.num = ny; 957 tty_write(tty_cmd_deleteline, &ttyctx); 958 } 959 960 /* Clear line at cursor. */ 961 void 962 screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) 963 { 964 struct screen *s = ctx->s; 965 struct grid_line *gl; 966 struct tty_ctx ttyctx; 967 u_int sx = screen_size_x(s); 968 969 gl = grid_get_line(s->grid, s->grid->hsize + s->cy); 970 if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) 971 return; 972 973 screen_write_initctx(ctx, &ttyctx); 974 ttyctx.bg = bg; 975 976 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); 977 978 screen_write_collect_clear(ctx, s->cy, 1); 979 screen_write_collect_flush(ctx, 0); 980 tty_write(tty_cmd_clearline, &ttyctx); 981 } 982 983 /* Clear to end of line from cursor. */ 984 void 985 screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) 986 { 987 struct screen *s = ctx->s; 988 struct grid_line *gl; 989 struct tty_ctx ttyctx; 990 u_int sx = screen_size_x(s); 991 992 gl = grid_get_line(s->grid, s->grid->hsize + s->cy); 993 if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg))) 994 return; 995 996 screen_write_initctx(ctx, &ttyctx); 997 ttyctx.bg = bg; 998 999 grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); 1000 1001 if (s->cx == 0) 1002 screen_write_collect_clear(ctx, s->cy, 1); 1003 screen_write_collect_flush(ctx, 0); 1004 tty_write(tty_cmd_clearendofline, &ttyctx); 1005 } 1006 1007 /* Clear to start of line from cursor. */ 1008 void 1009 screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) 1010 { 1011 struct screen *s = ctx->s; 1012 struct tty_ctx ttyctx; 1013 u_int sx = screen_size_x(s); 1014 1015 screen_write_initctx(ctx, &ttyctx); 1016 ttyctx.bg = bg; 1017 1018 if (s->cx > sx - 1) 1019 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); 1020 else 1021 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); 1022 1023 if (s->cx > sx - 1) 1024 screen_write_collect_clear(ctx, s->cy, 1); 1025 screen_write_collect_flush(ctx, 0); 1026 tty_write(tty_cmd_clearstartofline, &ttyctx); 1027 } 1028 1029 /* Move cursor to px,py. */ 1030 void 1031 screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py) 1032 { 1033 struct screen *s = ctx->s; 1034 1035 if (px > screen_size_x(s) - 1) 1036 px = screen_size_x(s) - 1; 1037 if (py > screen_size_y(s) - 1) 1038 py = screen_size_y(s) - 1; 1039 1040 screen_write_set_cursor(ctx, px, py); 1041 } 1042 1043 /* Reverse index (up with scroll). */ 1044 void 1045 screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg) 1046 { 1047 struct screen *s = ctx->s; 1048 struct tty_ctx ttyctx; 1049 1050 screen_write_initctx(ctx, &ttyctx); 1051 ttyctx.bg = bg; 1052 1053 if (s->cy == s->rupper) 1054 grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg); 1055 else if (s->cy > 0) 1056 screen_write_set_cursor(ctx, -1, s->cy - 1); 1057 1058 screen_write_collect_flush(ctx, 0); 1059 tty_write(tty_cmd_reverseindex, &ttyctx); 1060 } 1061 1062 /* Set scroll region. */ 1063 void 1064 screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, 1065 u_int rlower) 1066 { 1067 struct screen *s = ctx->s; 1068 1069 if (rupper > screen_size_y(s) - 1) 1070 rupper = screen_size_y(s) - 1; 1071 if (rlower > screen_size_y(s) - 1) 1072 rlower = screen_size_y(s) - 1; 1073 if (rupper >= rlower) /* cannot be one line */ 1074 return; 1075 1076 screen_write_collect_flush(ctx, 0); 1077 1078 /* Cursor moves to top-left. */ 1079 screen_write_set_cursor(ctx, 0, 0); 1080 1081 s->rupper = rupper; 1082 s->rlower = rlower; 1083 } 1084 1085 /* Line feed. */ 1086 void 1087 screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) 1088 { 1089 struct screen *s = ctx->s; 1090 struct grid *gd = s->grid; 1091 struct grid_line *gl; 1092 1093 gl = grid_get_line(gd, gd->hsize + s->cy); 1094 if (wrapped) 1095 gl->flags |= GRID_LINE_WRAPPED; 1096 else 1097 gl->flags &= ~GRID_LINE_WRAPPED; 1098 1099 log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, 1100 s->rupper, s->rlower); 1101 1102 if (bg != ctx->bg) { 1103 screen_write_collect_flush(ctx, 1); 1104 ctx->bg = bg; 1105 } 1106 1107 if (s->cy == s->rlower) { 1108 grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); 1109 screen_write_collect_scroll(ctx); 1110 ctx->scrolled++; 1111 } else if (s->cy < screen_size_y(s) - 1) 1112 screen_write_set_cursor(ctx, -1, s->cy + 1); 1113 } 1114 1115 /* Scroll up. */ 1116 void 1117 screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg) 1118 { 1119 struct screen *s = ctx->s; 1120 struct grid *gd = s->grid; 1121 u_int i; 1122 1123 if (lines == 0) 1124 lines = 1; 1125 else if (lines > s->rlower - s->rupper + 1) 1126 lines = s->rlower - s->rupper + 1; 1127 1128 if (bg != ctx->bg) { 1129 screen_write_collect_flush(ctx, 1); 1130 ctx->bg = bg; 1131 } 1132 1133 for (i = 0; i < lines; i++) { 1134 grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); 1135 screen_write_collect_scroll(ctx); 1136 } 1137 ctx->scrolled += lines; 1138 } 1139 1140 /* Carriage return (cursor to start of line). */ 1141 void 1142 screen_write_carriagereturn(struct screen_write_ctx *ctx) 1143 { 1144 screen_write_set_cursor(ctx, 0, -1); 1145 } 1146 1147 /* Clear to end of screen from cursor. */ 1148 void 1149 screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) 1150 { 1151 struct screen *s = ctx->s; 1152 struct grid *gd = s->grid; 1153 struct tty_ctx ttyctx; 1154 u_int sx = screen_size_x(s), sy = screen_size_y(s); 1155 1156 screen_write_initctx(ctx, &ttyctx); 1157 ttyctx.bg = bg; 1158 1159 /* Scroll into history if it is enabled and clearing entire screen. */ 1160 if (s->cx == 0 && s->cy == 0 && (gd->flags & GRID_HISTORY)) 1161 grid_view_clear_history(gd, bg); 1162 else { 1163 if (s->cx <= sx - 1) 1164 grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg); 1165 grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg); 1166 } 1167 1168 screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1)); 1169 screen_write_collect_flush(ctx, 0); 1170 tty_write(tty_cmd_clearendofscreen, &ttyctx); 1171 } 1172 1173 /* Clear to start of screen. */ 1174 void 1175 screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) 1176 { 1177 struct screen *s = ctx->s; 1178 struct tty_ctx ttyctx; 1179 u_int sx = screen_size_x(s); 1180 1181 screen_write_initctx(ctx, &ttyctx); 1182 ttyctx.bg = bg; 1183 1184 if (s->cy > 0) 1185 grid_view_clear(s->grid, 0, 0, sx, s->cy, bg); 1186 if (s->cx > sx - 1) 1187 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); 1188 else 1189 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); 1190 1191 screen_write_collect_clear(ctx, 0, s->cy); 1192 screen_write_collect_flush(ctx, 0); 1193 tty_write(tty_cmd_clearstartofscreen, &ttyctx); 1194 } 1195 1196 /* Clear entire screen. */ 1197 void 1198 screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) 1199 { 1200 struct screen *s = ctx->s; 1201 struct tty_ctx ttyctx; 1202 u_int sx = screen_size_x(s), sy = screen_size_y(s); 1203 1204 screen_write_initctx(ctx, &ttyctx); 1205 ttyctx.bg = bg; 1206 1207 /* Scroll into history if it is enabled. */ 1208 if (s->grid->flags & GRID_HISTORY) 1209 grid_view_clear_history(s->grid, bg); 1210 else 1211 grid_view_clear(s->grid, 0, 0, sx, sy, bg); 1212 1213 screen_write_collect_clear(ctx, 0, sy); 1214 tty_write(tty_cmd_clearscreen, &ttyctx); 1215 } 1216 1217 /* Clear entire history. */ 1218 void 1219 screen_write_clearhistory(struct screen_write_ctx *ctx) 1220 { 1221 struct screen *s = ctx->s; 1222 struct grid *gd = s->grid; 1223 1224 grid_move_lines(gd, 0, gd->hsize, gd->sy, 8); 1225 gd->hscrolled = gd->hsize = 0; 1226 } 1227 1228 /* Clear a collected line. */ 1229 static void 1230 screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) 1231 { 1232 struct screen_write_collect_item *ci, *tmp; 1233 u_int i; 1234 size_t size; 1235 1236 for (i = y ; i < y + n; i++) { 1237 if (TAILQ_EMPTY(&ctx->list[i].items)) 1238 continue; 1239 size = 0; 1240 TAILQ_FOREACH_SAFE(ci, &ctx->list[i].items, entry, tmp) { 1241 size += ci->used; 1242 TAILQ_REMOVE(&ctx->list[i].items, ci, entry); 1243 free(ci); 1244 } 1245 ctx->skipped += size; 1246 log_debug("%s: dropped %zu bytes (line %u)", __func__, size, i); 1247 } 1248 } 1249 1250 /* Scroll collected lines up. */ 1251 static void 1252 screen_write_collect_scroll(struct screen_write_ctx *ctx) 1253 { 1254 struct screen *s = ctx->s; 1255 struct screen_write_collect_line *cl; 1256 u_int y; 1257 1258 log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, 1259 s->rupper, s->rlower); 1260 1261 screen_write_collect_clear(ctx, s->rupper, 1); 1262 for (y = s->rupper; y < s->rlower; y++) { 1263 cl = &ctx->list[y + 1]; 1264 TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry); 1265 TAILQ_INIT(&cl->items); 1266 } 1267 } 1268 1269 /* Flush collected lines. */ 1270 static void 1271 screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only) 1272 { 1273 struct screen *s = ctx->s; 1274 struct screen_write_collect_item *ci, *tmp; 1275 u_int y, cx, cy, items = 0; 1276 struct tty_ctx ttyctx; 1277 size_t written = 0; 1278 1279 if (ctx->scrolled != 0) { 1280 log_debug("%s: scrolled %u (region %u-%u)", __func__, 1281 ctx->scrolled, s->rupper, s->rlower); 1282 if (ctx->scrolled > s->rlower - s->rupper + 1) 1283 ctx->scrolled = s->rlower - s->rupper + 1; 1284 1285 screen_write_initctx(ctx, &ttyctx); 1286 ttyctx.num = ctx->scrolled; 1287 ttyctx.bg = ctx->bg; 1288 tty_write(tty_cmd_scrollup, &ttyctx); 1289 } 1290 ctx->scrolled = 0; 1291 ctx->bg = 8; 1292 1293 if (scroll_only) 1294 return; 1295 1296 cx = s->cx; cy = s->cy; 1297 for (y = 0; y < screen_size_y(s); y++) { 1298 TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { 1299 screen_write_cursormove(ctx, ci->x, y); 1300 screen_write_initctx(ctx, &ttyctx); 1301 ttyctx.cell = &ci->gc; 1302 ttyctx.wrapped = ci->wrapped; 1303 ttyctx.ptr = ci->data; 1304 ttyctx.num = ci->used; 1305 tty_write(tty_cmd_cells, &ttyctx); 1306 1307 items++; 1308 written += ci->used; 1309 1310 TAILQ_REMOVE(&ctx->list[y].items, ci, entry); 1311 free(ci); 1312 } 1313 } 1314 s->cx = cx; s->cy = cy; 1315 1316 log_debug("%s: flushed %u items (%zu bytes)", __func__, items, written); 1317 ctx->written += written; 1318 } 1319 1320 /* Finish and store collected cells. */ 1321 void 1322 screen_write_collect_end(struct screen_write_ctx *ctx) 1323 { 1324 struct screen *s = ctx->s; 1325 struct screen_write_collect_item *ci = ctx->item; 1326 struct grid_cell gc; 1327 u_int xx; 1328 1329 if (ci->used == 0) 1330 return; 1331 ci->data[ci->used] = '\0'; 1332 1333 ci->x = s->cx; 1334 TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); 1335 ctx->item = xcalloc(1, sizeof *ctx->item); 1336 1337 log_debug("%s: %u %s (at %u,%u)", __func__, ci->used, ci->data, s->cx, 1338 s->cy); 1339 1340 if (s->cx != 0) { 1341 for (xx = s->cx; xx > 0; xx--) { 1342 grid_view_get_cell(s->grid, xx, s->cy, &gc); 1343 if (~gc.flags & GRID_FLAG_PADDING) 1344 break; 1345 grid_view_set_cell(s->grid, xx, s->cy, 1346 &grid_default_cell); 1347 } 1348 if (gc.data.width > 1) { 1349 grid_view_set_cell(s->grid, xx, s->cy, 1350 &grid_default_cell); 1351 } 1352 } 1353 1354 memcpy(&gc, &ci->gc, sizeof gc); 1355 grid_view_set_cells(s->grid, s->cx, s->cy, &gc, ci->data, ci->used); 1356 screen_write_set_cursor(ctx, s->cx + ci->used, -1); 1357 1358 for (xx = s->cx; xx < screen_size_x(s); xx++) { 1359 grid_view_get_cell(s->grid, xx, s->cy, &gc); 1360 if (~gc.flags & GRID_FLAG_PADDING) 1361 break; 1362 grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell); 1363 } 1364 } 1365 1366 /* Write cell data, collecting if necessary. */ 1367 void 1368 screen_write_collect_add(struct screen_write_ctx *ctx, 1369 const struct grid_cell *gc) 1370 { 1371 struct screen *s = ctx->s; 1372 struct screen_write_collect_item *ci; 1373 u_int sx = screen_size_x(s); 1374 int collect; 1375 1376 /* 1377 * Don't need to check that the attributes and whatnot are still the 1378 * same - input_parse will end the collection when anything that isn't 1379 * a plain character is encountered. Also nothing should make it here 1380 * that isn't a single ASCII character. 1381 */ 1382 1383 collect = 1; 1384 if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f) 1385 collect = 0; 1386 else if (gc->attr & GRID_ATTR_CHARSET) 1387 collect = 0; 1388 else if (~s->mode & MODE_WRAP) 1389 collect = 0; 1390 else if (s->mode & MODE_INSERT) 1391 collect = 0; 1392 else if (s->sel != NULL) 1393 collect = 0; 1394 if (!collect) { 1395 screen_write_collect_end(ctx); 1396 screen_write_collect_flush(ctx, 0); 1397 screen_write_cell(ctx, gc); 1398 return; 1399 } 1400 ctx->cells++; 1401 1402 if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx) 1403 screen_write_collect_end(ctx); 1404 ci = ctx->item; /* may have changed */ 1405 1406 if (s->cx > sx - 1) { 1407 log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); 1408 ci->wrapped = 1; 1409 screen_write_linefeed(ctx, 1, 8); 1410 screen_write_set_cursor(ctx, 0, -1); 1411 } 1412 1413 if (ci->used == 0) 1414 memcpy(&ci->gc, gc, sizeof ci->gc); 1415 ci->data[ci->used++] = gc->data.data[0]; 1416 if (ci->used == (sizeof ci->data) - 1) 1417 screen_write_collect_end(ctx); 1418 } 1419 1420 /* Write cell data. */ 1421 void 1422 screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) 1423 { 1424 struct screen *s = ctx->s; 1425 struct grid *gd = s->grid; 1426 struct grid_line *gl; 1427 struct grid_cell_entry *gce; 1428 struct grid_cell tmp_gc, now_gc; 1429 struct tty_ctx ttyctx; 1430 u_int sx = screen_size_x(s), sy = screen_size_y(s); 1431 u_int width = gc->data.width, xx, last, cx, cy; 1432 int selected, skip = 1; 1433 1434 /* Ignore padding cells. */ 1435 if (gc->flags & GRID_FLAG_PADDING) 1436 return; 1437 ctx->cells++; 1438 1439 /* If the width is zero, combine onto the previous character. */ 1440 if (width == 0) { 1441 screen_write_collect_flush(ctx, 0); 1442 if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) { 1443 cx = s->cx; cy = s->cy; 1444 screen_write_cursormove(ctx, xx, s->cy); 1445 screen_write_initctx(ctx, &ttyctx); 1446 ttyctx.cell = gc; 1447 tty_write(tty_cmd_cell, &ttyctx); 1448 s->cx = cx; s->cy = cy; 1449 } 1450 return; 1451 } 1452 1453 /* Flush any existing scrolling. */ 1454 screen_write_collect_flush(ctx, 1); 1455 1456 /* If this character doesn't fit, ignore it. */ 1457 if ((~s->mode & MODE_WRAP) && 1458 width > 1 && 1459 (width > sx || (s->cx != sx && s->cx > sx - width))) 1460 return; 1461 1462 /* If in insert mode, make space for the cells. */ 1463 if (s->mode & MODE_INSERT) { 1464 grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8); 1465 skip = 0; 1466 } 1467 1468 /* Check this will fit on the current line and wrap if not. */ 1469 if ((s->mode & MODE_WRAP) && s->cx > sx - width) { 1470 log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); 1471 screen_write_linefeed(ctx, 1, 8); 1472 screen_write_set_cursor(ctx, 0, -1); 1473 screen_write_collect_flush(ctx, 1); 1474 } 1475 1476 /* Sanity check cursor position. */ 1477 if (s->cx > sx - width || s->cy > sy - 1) 1478 return; 1479 screen_write_initctx(ctx, &ttyctx); 1480 1481 /* Handle overwriting of UTF-8 characters. */ 1482 gl = grid_get_line(s->grid, s->grid->hsize + s->cy); 1483 if (gl->flags & GRID_LINE_EXTENDED) { 1484 grid_view_get_cell(gd, s->cx, s->cy, &now_gc); 1485 if (screen_write_overwrite(ctx, &now_gc, width)) 1486 skip = 0; 1487 } 1488 1489 /* 1490 * If the new character is UTF-8 wide, fill in padding cells. Have 1491 * already ensured there is enough room. 1492 */ 1493 for (xx = s->cx + 1; xx < s->cx + width; xx++) { 1494 log_debug("%s: new padding at %u,%u", __func__, xx, s->cy); 1495 grid_view_set_cell(gd, xx, s->cy, &screen_write_pad_cell); 1496 skip = 0; 1497 } 1498 1499 /* If no change, do not draw. */ 1500 if (skip) { 1501 if (s->cx >= gl->cellsize) 1502 skip = grid_cells_equal(gc, &grid_default_cell); 1503 else { 1504 gce = &gl->celldata[s->cx]; 1505 if (gce->flags & GRID_FLAG_EXTENDED) 1506 skip = 0; 1507 else if (gc->flags != gce->flags) 1508 skip = 0; 1509 else if (gc->attr != gce->data.attr) 1510 skip = 0; 1511 else if (gc->fg != gce->data.fg) 1512 skip = 0; 1513 else if (gc->bg != gce->data.bg) 1514 skip = 0; 1515 else if (gc->data.width != 1) 1516 skip = 0; 1517 else if (gc->data.size != 1) 1518 skip = 0; 1519 else if (gce->data.data != gc->data.data[0]) 1520 skip = 0; 1521 } 1522 } 1523 1524 /* Update the selected flag and set the cell. */ 1525 selected = screen_check_selection(s, s->cx, s->cy); 1526 if (selected && (~gc->flags & GRID_FLAG_SELECTED)) { 1527 memcpy(&tmp_gc, gc, sizeof tmp_gc); 1528 tmp_gc.flags |= GRID_FLAG_SELECTED; 1529 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); 1530 } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) { 1531 memcpy(&tmp_gc, gc, sizeof tmp_gc); 1532 tmp_gc.flags &= ~GRID_FLAG_SELECTED; 1533 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); 1534 } else if (!skip) 1535 grid_view_set_cell(gd, s->cx, s->cy, gc); 1536 if (selected) 1537 skip = 0; 1538 1539 /* 1540 * Move the cursor. If not wrapping, stick at the last character and 1541 * replace it. 1542 */ 1543 last = !(s->mode & MODE_WRAP); 1544 if (s->cx <= sx - last - width) 1545 screen_write_set_cursor(ctx, s->cx + width, -1); 1546 else 1547 screen_write_set_cursor(ctx, sx - last, -1); 1548 1549 /* Create space for character in insert mode. */ 1550 if (s->mode & MODE_INSERT) { 1551 screen_write_collect_flush(ctx, 0); 1552 ttyctx.num = width; 1553 tty_write(tty_cmd_insertcharacter, &ttyctx); 1554 } 1555 1556 /* Write to the screen. */ 1557 if (!skip) { 1558 if (selected) { 1559 screen_select_cell(s, &tmp_gc, gc); 1560 ttyctx.cell = &tmp_gc; 1561 } else 1562 ttyctx.cell = gc; 1563 tty_write(tty_cmd_cell, &ttyctx); 1564 ctx->written++; 1565 } else 1566 ctx->skipped++; 1567 } 1568 1569 /* Combine a UTF-8 zero-width character onto the previous. */ 1570 static const struct grid_cell * 1571 screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud, 1572 u_int *xx) 1573 { 1574 struct screen *s = ctx->s; 1575 struct grid *gd = s->grid; 1576 static struct grid_cell gc; 1577 u_int n; 1578 1579 /* Can't combine if at 0. */ 1580 if (s->cx == 0) 1581 return (NULL); 1582 1583 /* Empty data is out. */ 1584 if (ud->size == 0) 1585 fatalx("UTF-8 data empty"); 1586 1587 /* Retrieve the previous cell. */ 1588 for (n = 1; n <= s->cx; n++) { 1589 grid_view_get_cell(gd, s->cx - n, s->cy, &gc); 1590 if (~gc.flags & GRID_FLAG_PADDING) 1591 break; 1592 } 1593 if (n > s->cx) 1594 return (NULL); 1595 *xx = s->cx - n; 1596 1597 /* Check there is enough space. */ 1598 if (gc.data.size + ud->size > sizeof gc.data.data) 1599 return (NULL); 1600 1601 log_debug("%s: %.*s onto %.*s at %u,%u", __func__, (int)ud->size, 1602 ud->data, (int)gc.data.size, gc.data.data, *xx, s->cy); 1603 1604 /* Append the data. */ 1605 memcpy(gc.data.data + gc.data.size, ud->data, ud->size); 1606 gc.data.size += ud->size; 1607 1608 /* Set the new cell. */ 1609 grid_view_set_cell(gd, *xx, s->cy, &gc); 1610 1611 return (&gc); 1612 } 1613 1614 /* 1615 * UTF-8 wide characters are a bit of an annoyance. They take up more than one 1616 * cell on the screen, so following cells must not be drawn by marking them as 1617 * padding. 1618 * 1619 * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8 1620 * character, it is necessary to also overwrite any other cells which covered 1621 * by the same character. 1622 */ 1623 static int 1624 screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc, 1625 u_int width) 1626 { 1627 struct screen *s = ctx->s; 1628 struct grid *gd = s->grid; 1629 struct grid_cell tmp_gc; 1630 u_int xx; 1631 int done = 0; 1632 1633 if (gc->flags & GRID_FLAG_PADDING) { 1634 /* 1635 * A padding cell, so clear any following and leading padding 1636 * cells back to the character. Don't overwrite the current 1637 * cell as that happens later anyway. 1638 */ 1639 xx = s->cx + 1; 1640 while (--xx > 0) { 1641 grid_view_get_cell(gd, xx, s->cy, &tmp_gc); 1642 if (~tmp_gc.flags & GRID_FLAG_PADDING) 1643 break; 1644 log_debug("%s: padding at %u,%u", __func__, xx, s->cy); 1645 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1646 } 1647 1648 /* Overwrite the character at the start of this padding. */ 1649 log_debug("%s: character at %u,%u", __func__, xx, s->cy); 1650 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1651 done = 1; 1652 } 1653 1654 /* 1655 * Overwrite any padding cells that belong to any UTF-8 characters 1656 * we'll be overwriting with the current character. 1657 */ 1658 if (width != 1 || 1659 gc->data.width != 1 || 1660 gc->flags & GRID_FLAG_PADDING) { 1661 xx = s->cx + width - 1; 1662 while (++xx < screen_size_x(s)) { 1663 grid_view_get_cell(gd, xx, s->cy, &tmp_gc); 1664 if (~tmp_gc.flags & GRID_FLAG_PADDING) 1665 break; 1666 log_debug("%s: overwrite at %u,%u", __func__, xx, s->cy); 1667 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1668 done = 1; 1669 } 1670 } 1671 1672 return (done); 1673 } 1674 1675 /* Set external clipboard. */ 1676 void 1677 screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) 1678 { 1679 struct tty_ctx ttyctx; 1680 1681 screen_write_initctx(ctx, &ttyctx); 1682 ttyctx.ptr = str; 1683 ttyctx.num = len; 1684 1685 tty_write(tty_cmd_setselection, &ttyctx); 1686 } 1687 1688 /* Write unmodified string. */ 1689 void 1690 screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) 1691 { 1692 struct tty_ctx ttyctx; 1693 1694 screen_write_initctx(ctx, &ttyctx); 1695 ttyctx.ptr = str; 1696 ttyctx.num = len; 1697 1698 tty_write(tty_cmd_rawstring, &ttyctx); 1699 } 1700