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