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