1 /* $OpenBSD: screen-write.c,v 1.93 2016/09/02 20:57:20 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_save_last(struct screen_write_ctx *, 29 struct tty_ctx *); 30 static void screen_write_flush(struct screen_write_ctx *); 31 32 static int screen_write_overwrite(struct screen_write_ctx *, 33 struct grid_cell *, u_int); 34 static int screen_write_combine(struct screen_write_ctx *, 35 const struct utf8_data *); 36 37 static const struct grid_cell screen_write_pad_cell = { 38 GRID_FLAG_PADDING, 0, 8, 8, { { 0 }, 0, 0, 0 } 39 }; 40 41 #define screen_dirty_bit(s, x, y) (((y) * screen_size_x(s)) + (x)) 42 #define screen_dirty_clear(s, sx, sy, ex, ey) \ 43 do { \ 44 if (s->dirty != NULL) { \ 45 bit_nclear(s->dirty, \ 46 screen_dirty_bit(s, sx, sy), \ 47 screen_dirty_bit(s, ex, ey)); \ 48 } \ 49 } while (0) 50 51 /* Initialize writing with a window. */ 52 void 53 screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, 54 struct screen *s) 55 { 56 u_int size; 57 char tmp[16]; 58 const char *cp = tmp; 59 60 ctx->wp = wp; 61 if (wp != NULL && s == NULL) 62 ctx->s = wp->screen; 63 else 64 ctx->s = s; 65 66 size = screen_size_x(ctx->s) * screen_size_y(ctx->s); 67 if (ctx->s->dirtysize != size) { 68 free(ctx->s->dirty); 69 ctx->s->dirty = NULL; 70 ctx->s->dirtysize = size; 71 } 72 ctx->dirty = 0; 73 74 ctx->cells = ctx->written = ctx->skipped = 0; 75 76 if (wp == NULL) 77 cp = "no pane"; 78 else 79 snprintf(tmp, sizeof tmp, "pane %%%u", wp->id); 80 log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s), 81 screen_size_y(ctx->s), cp); 82 } 83 84 /* Finish writing. */ 85 void 86 screen_write_stop(struct screen_write_ctx *ctx) 87 { 88 screen_write_flush(ctx); 89 90 log_debug("%s: %u of %u written (dirty %u, skipped %u)", __func__, 91 ctx->written, ctx->cells, ctx->cells - ctx->written, ctx->skipped); 92 } 93 94 /* Flush outstanding cell writes. */ 95 static void 96 screen_write_flush(struct screen_write_ctx *ctx) 97 { 98 struct screen *s = ctx->s; 99 struct tty_ctx ttyctx; 100 u_int x, y, offset, cx, cy, dirty; 101 struct grid_cell gc; 102 103 if (ctx->dirty == 0) 104 return; 105 dirty = 0; 106 107 cx = s->cx; 108 cy = s->cy; 109 110 offset = 0; 111 for (y = 0; y < screen_size_y(s); y++) { 112 for (x = 0; x < screen_size_x(s); x++) { 113 offset++; 114 if (!bit_test(s->dirty, offset - 1)) 115 continue; 116 bit_clear(s->dirty, offset - 1); 117 118 screen_write_cursormove(ctx, x, y); 119 grid_view_get_cell(s->grid, x, y, &gc); 120 121 screen_write_initctx(ctx, &ttyctx); 122 ttyctx.cell = &gc; 123 tty_write(tty_cmd_cell, &ttyctx); 124 ctx->written++; 125 126 if (++dirty == ctx->dirty) 127 break; 128 } 129 if (dirty == ctx->dirty) 130 break; 131 } 132 133 s->cx = cx; 134 s->cy = cy; 135 } 136 137 /* Reset screen state. */ 138 void 139 screen_write_reset(struct screen_write_ctx *ctx) 140 { 141 struct screen *s = ctx->s; 142 143 screen_reset_tabs(s); 144 screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); 145 146 s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD|MODE_FOCUSON); 147 s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_UTF8|MODE_MOUSE_SGR); 148 149 screen_write_clearscreen(ctx); 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 354 if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { 355 ptr++; 356 357 left = strlen(ptr); 358 if (left < (size_t)ud->size - 1) 359 break; 360 while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) 361 ptr++; 362 ptr++; 363 364 if (more != UTF8_DONE) 365 continue; 366 if (maxlen > 0 && size + ud->width > (size_t)maxlen) { 367 while (size < (size_t)maxlen) { 368 screen_write_putc(ctx, &gc, ' '); 369 size++; 370 } 371 break; 372 } 373 size += ud->width; 374 screen_write_cell(ctx, &gc); 375 } else { 376 if (maxlen > 0 && size + 1 > (size_t)maxlen) 377 break; 378 379 if (*ptr > 0x1f && *ptr < 0x7f) { 380 size++; 381 screen_write_putc(ctx, &gc, *ptr); 382 } 383 ptr++; 384 } 385 } 386 387 free(msg); 388 } 389 390 /* Copy from another screen. */ 391 void 392 screen_write_copy(struct screen_write_ctx *ctx, 393 struct screen *src, u_int px, u_int py, u_int nx, u_int ny) 394 { 395 struct screen *s = ctx->s; 396 struct grid *gd = src->grid; 397 struct grid_line *gl; 398 struct grid_cell gc; 399 u_int xx, yy, cx, cy, ax, bx; 400 401 cx = s->cx; 402 cy = s->cy; 403 for (yy = py; yy < py + ny; yy++) { 404 gl = &gd->linedata[yy]; 405 if (yy < gd->hsize + gd->sy) { 406 /* 407 * Find start and end position and copy between 408 * them. Limit to the real end of the line then use a 409 * clear EOL only if copying to the end, otherwise 410 * could overwrite whatever is there already. 411 */ 412 if (px > gl->cellsize) 413 ax = gl->cellsize; 414 else 415 ax = px; 416 if (px + nx == gd->sx && px + nx > gl->cellsize) 417 bx = gl->cellsize; 418 else 419 bx = px + nx; 420 421 for (xx = ax; xx < bx; xx++) { 422 grid_get_cell(gd, xx, yy, &gc); 423 screen_write_cell(ctx, &gc); 424 } 425 if (px + nx == gd->sx && px + nx > gl->cellsize) 426 screen_write_clearendofline(ctx); 427 } else 428 screen_write_clearline(ctx); 429 cy++; 430 screen_write_cursormove(ctx, cx, cy); 431 } 432 } 433 434 /* Set up context for TTY command. */ 435 static void 436 screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) 437 { 438 struct screen *s = ctx->s; 439 440 ttyctx->wp = ctx->wp; 441 442 ttyctx->ocx = s->cx; 443 ttyctx->ocy = s->cy; 444 445 ttyctx->orlower = s->rlower; 446 ttyctx->orupper = s->rupper; 447 } 448 449 /* Save last cell on screen. */ 450 static void 451 screen_write_save_last(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) 452 { 453 struct screen *s = ctx->s; 454 struct grid *gd = s->grid; 455 struct grid_cell gc; 456 u_int xx; 457 458 memcpy(&gc, &grid_default_cell, sizeof gc); 459 for (xx = 1; xx <= screen_size_x(s); xx++) { 460 grid_view_get_cell(gd, screen_size_x(s) - xx, s->cy, &gc); 461 if (~gc.flags & GRID_FLAG_PADDING) 462 break; 463 } 464 memcpy(&ttyctx->last_cell, &gc, sizeof ttyctx->last_cell); 465 } 466 467 /* Set a mode. */ 468 void 469 screen_write_mode_set(struct screen_write_ctx *ctx, int mode) 470 { 471 struct screen *s = ctx->s; 472 473 s->mode |= mode; 474 } 475 476 /* Clear a mode. */ 477 void 478 screen_write_mode_clear(struct screen_write_ctx *ctx, int mode) 479 { 480 struct screen *s = ctx->s; 481 482 s->mode &= ~mode; 483 } 484 485 /* Cursor up by ny. */ 486 void 487 screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) 488 { 489 struct screen *s = ctx->s; 490 491 if (ny == 0) 492 ny = 1; 493 494 if (s->cy < s->rupper) { 495 /* Above region. */ 496 if (ny > s->cy) 497 ny = s->cy; 498 } else { 499 /* Below region. */ 500 if (ny > s->cy - s->rupper) 501 ny = s->cy - s->rupper; 502 } 503 if (s->cx == screen_size_x(s)) 504 s->cx--; 505 if (ny == 0) 506 return; 507 508 s->cy -= ny; 509 } 510 511 /* Cursor down by ny. */ 512 void 513 screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) 514 { 515 struct screen *s = ctx->s; 516 517 if (ny == 0) 518 ny = 1; 519 520 if (s->cy > s->rlower) { 521 /* Below region. */ 522 if (ny > screen_size_y(s) - 1 - s->cy) 523 ny = screen_size_y(s) - 1 - s->cy; 524 } else { 525 /* Above region. */ 526 if (ny > s->rlower - s->cy) 527 ny = s->rlower - s->cy; 528 } 529 if (s->cx == screen_size_x(s)) 530 s->cx--; 531 if (ny == 0) 532 return; 533 534 s->cy += ny; 535 } 536 537 /* Cursor right by nx. */ 538 void 539 screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx) 540 { 541 struct screen *s = ctx->s; 542 543 if (nx == 0) 544 nx = 1; 545 546 if (nx > screen_size_x(s) - 1 - s->cx) 547 nx = screen_size_x(s) - 1 - s->cx; 548 if (nx == 0) 549 return; 550 551 s->cx += nx; 552 } 553 554 /* Cursor left by nx. */ 555 void 556 screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx) 557 { 558 struct screen *s = ctx->s; 559 560 if (nx == 0) 561 nx = 1; 562 563 if (nx > s->cx) 564 nx = s->cx; 565 if (nx == 0) 566 return; 567 568 s->cx -= nx; 569 } 570 571 /* Backspace; cursor left unless at start of wrapped line when can move up. */ 572 void 573 screen_write_backspace(struct screen_write_ctx *ctx) 574 { 575 struct screen *s = ctx->s; 576 struct grid_line *gl; 577 578 if (s->cx == 0) { 579 if (s->cy == 0) 580 return; 581 gl = &s->grid->linedata[s->grid->hsize + s->cy - 1]; 582 if (gl->flags & GRID_LINE_WRAPPED) { 583 s->cy--; 584 s->cx = screen_size_x(s) - 1; 585 } 586 } else 587 s->cx--; 588 } 589 590 /* VT100 alignment test. */ 591 void 592 screen_write_alignmenttest(struct screen_write_ctx *ctx) 593 { 594 struct screen *s = ctx->s; 595 struct tty_ctx ttyctx; 596 struct grid_cell gc; 597 u_int xx, yy; 598 u_int sx = screen_size_x(s), sy = screen_size_y(s); 599 600 screen_write_initctx(ctx, &ttyctx); 601 602 screen_dirty_clear(s, 0, 0, sx - 1, sy - 1); 603 604 memcpy(&gc, &grid_default_cell, sizeof gc); 605 utf8_set(&gc.data, 'E'); 606 607 for (yy = 0; yy < screen_size_y(s); yy++) { 608 for (xx = 0; xx < screen_size_x(s); xx++) 609 grid_view_set_cell(s->grid, xx, yy, &gc); 610 } 611 612 s->cx = 0; 613 s->cy = 0; 614 615 s->rupper = 0; 616 s->rlower = screen_size_y(s) - 1; 617 618 tty_write(tty_cmd_alignmenttest, &ttyctx); 619 } 620 621 /* Insert nx characters. */ 622 void 623 screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx) 624 { 625 struct screen *s = ctx->s; 626 struct tty_ctx ttyctx; 627 628 if (nx == 0) 629 nx = 1; 630 631 if (nx > screen_size_x(s) - s->cx) 632 nx = screen_size_x(s) - s->cx; 633 if (nx == 0) 634 return; 635 636 screen_write_flush(ctx); 637 screen_write_initctx(ctx, &ttyctx); 638 639 if (s->cx <= screen_size_x(s) - 1) 640 grid_view_insert_cells(s->grid, s->cx, s->cy, nx); 641 642 ttyctx.num = nx; 643 tty_write(tty_cmd_insertcharacter, &ttyctx); 644 } 645 646 /* Delete nx characters. */ 647 void 648 screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx) 649 { 650 struct screen *s = ctx->s; 651 struct tty_ctx ttyctx; 652 653 if (nx == 0) 654 nx = 1; 655 656 if (nx > screen_size_x(s) - s->cx) 657 nx = screen_size_x(s) - s->cx; 658 if (nx == 0) 659 return; 660 661 screen_write_flush(ctx); 662 screen_write_initctx(ctx, &ttyctx); 663 664 if (s->cx <= screen_size_x(s) - 1) 665 grid_view_delete_cells(s->grid, s->cx, s->cy, nx); 666 667 ttyctx.num = nx; 668 tty_write(tty_cmd_deletecharacter, &ttyctx); 669 } 670 671 /* Clear nx characters. */ 672 void 673 screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx) 674 { 675 struct screen *s = ctx->s; 676 struct tty_ctx ttyctx; 677 678 if (nx == 0) 679 nx = 1; 680 681 if (nx > screen_size_x(s) - s->cx) 682 nx = screen_size_x(s) - s->cx; 683 if (nx == 0) 684 return; 685 686 screen_write_initctx(ctx, &ttyctx); 687 688 if (s->cx <= screen_size_x(s) - 1) { 689 screen_dirty_clear(s, s->cx, s->cy, s->cx + nx - 1, s->cy); 690 grid_view_clear(s->grid, s->cx, s->cy, nx, 1); 691 } else 692 return; 693 694 ttyctx.num = nx; 695 tty_write(tty_cmd_clearcharacter, &ttyctx); 696 } 697 698 /* Insert ny lines. */ 699 void 700 screen_write_insertline(struct screen_write_ctx *ctx, u_int ny) 701 { 702 struct screen *s = ctx->s; 703 struct tty_ctx ttyctx; 704 705 if (ny == 0) 706 ny = 1; 707 708 if (s->cy < s->rupper || s->cy > s->rlower) { 709 if (ny > screen_size_y(s) - s->cy) 710 ny = screen_size_y(s) - s->cy; 711 if (ny == 0) 712 return; 713 714 screen_write_flush(ctx); 715 screen_write_initctx(ctx, &ttyctx); 716 717 grid_view_insert_lines(s->grid, s->cy, ny); 718 719 ttyctx.num = ny; 720 tty_write(tty_cmd_insertline, &ttyctx); 721 return; 722 } 723 724 if (ny > s->rlower + 1 - s->cy) 725 ny = s->rlower + 1 - s->cy; 726 if (ny == 0) 727 return; 728 729 screen_write_flush(ctx); 730 screen_write_initctx(ctx, &ttyctx); 731 732 if (s->cy < s->rupper || s->cy > s->rlower) 733 grid_view_insert_lines(s->grid, s->cy, ny); 734 else 735 grid_view_insert_lines_region(s->grid, s->rlower, s->cy, ny); 736 737 ttyctx.num = ny; 738 tty_write(tty_cmd_insertline, &ttyctx); 739 } 740 741 /* Delete ny lines. */ 742 void 743 screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny) 744 { 745 struct screen *s = ctx->s; 746 struct tty_ctx ttyctx; 747 748 if (ny == 0) 749 ny = 1; 750 751 if (s->cy < s->rupper || s->cy > s->rlower) { 752 if (ny > screen_size_y(s) - s->cy) 753 ny = screen_size_y(s) - s->cy; 754 if (ny == 0) 755 return; 756 757 screen_write_flush(ctx); 758 screen_write_initctx(ctx, &ttyctx); 759 760 grid_view_delete_lines(s->grid, s->cy, ny); 761 762 ttyctx.num = ny; 763 tty_write(tty_cmd_deleteline, &ttyctx); 764 return; 765 } 766 767 if (ny > s->rlower + 1 - s->cy) 768 ny = s->rlower + 1 - s->cy; 769 if (ny == 0) 770 return; 771 772 screen_write_flush(ctx); 773 screen_write_initctx(ctx, &ttyctx); 774 775 if (s->cy < s->rupper || s->cy > s->rlower) 776 grid_view_delete_lines(s->grid, s->cy, ny); 777 else 778 grid_view_delete_lines_region(s->grid, s->rlower, s->cy, ny); 779 780 ttyctx.num = ny; 781 tty_write(tty_cmd_deleteline, &ttyctx); 782 } 783 784 /* Clear line at cursor. */ 785 void 786 screen_write_clearline(struct screen_write_ctx *ctx) 787 { 788 struct screen *s = ctx->s; 789 struct grid_line *gl; 790 struct tty_ctx ttyctx; 791 u_int sx = screen_size_x(s); 792 793 screen_write_initctx(ctx, &ttyctx); 794 795 gl = &s->grid->linedata[s->grid->hsize + s->cy]; 796 if (gl->cellsize != 0) { 797 screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy); 798 grid_view_clear(s->grid, 0, s->cy, sx, 1); 799 } else 800 return; 801 802 tty_write(tty_cmd_clearline, &ttyctx); 803 } 804 805 /* Clear to end of line from cursor. */ 806 void 807 screen_write_clearendofline(struct screen_write_ctx *ctx) 808 { 809 struct screen *s = ctx->s; 810 struct grid_line *gl; 811 struct tty_ctx ttyctx; 812 u_int sx = screen_size_x(s); 813 814 screen_write_initctx(ctx, &ttyctx); 815 816 gl = &s->grid->linedata[s->grid->hsize + s->cy]; 817 if (s->cx <= sx - 1 && s->cx < gl->cellsize) { 818 screen_dirty_clear(s, s->cx, s->cy, sx - 1, s->cy); 819 grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1); 820 } else 821 return; 822 823 tty_write(tty_cmd_clearendofline, &ttyctx); 824 } 825 826 /* Clear to start of line from cursor. */ 827 void 828 screen_write_clearstartofline(struct screen_write_ctx *ctx) 829 { 830 struct screen *s = ctx->s; 831 struct tty_ctx ttyctx; 832 u_int sx = screen_size_x(s); 833 834 screen_write_initctx(ctx, &ttyctx); 835 836 if (s->cx > sx - 1) { 837 screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy); 838 grid_view_clear(s->grid, 0, s->cy, sx, 1); 839 } else { 840 screen_dirty_clear(s, 0, s->cy, s->cx, s->cy); 841 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1); 842 } 843 844 tty_write(tty_cmd_clearstartofline, &ttyctx); 845 } 846 847 /* Move cursor to px,py. */ 848 void 849 screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py) 850 { 851 struct screen *s = ctx->s; 852 853 if (px > screen_size_x(s) - 1) 854 px = screen_size_x(s) - 1; 855 if (py > screen_size_y(s) - 1) 856 py = screen_size_y(s) - 1; 857 858 s->cx = px; 859 s->cy = py; 860 } 861 862 /* Reverse index (up with scroll). */ 863 void 864 screen_write_reverseindex(struct screen_write_ctx *ctx) 865 { 866 struct screen *s = ctx->s; 867 struct tty_ctx ttyctx; 868 869 screen_write_initctx(ctx, &ttyctx); 870 871 if (s->cy == s->rupper) { 872 screen_write_flush(ctx); 873 grid_view_scroll_region_down(s->grid, s->rupper, s->rlower); 874 } else if (s->cy > 0) 875 s->cy--; 876 877 tty_write(tty_cmd_reverseindex, &ttyctx); 878 } 879 880 /* Set scroll region. */ 881 void 882 screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, 883 u_int rlower) 884 { 885 struct screen *s = ctx->s; 886 887 if (rupper > screen_size_y(s) - 1) 888 rupper = screen_size_y(s) - 1; 889 if (rlower > screen_size_y(s) - 1) 890 rlower = screen_size_y(s) - 1; 891 if (rupper >= rlower) /* cannot be one line */ 892 return; 893 894 /* Cursor moves to top-left. */ 895 s->cx = 0; 896 s->cy = 0; 897 898 s->rupper = rupper; 899 s->rlower = rlower; 900 } 901 902 /* Line feed. */ 903 void 904 screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped) 905 { 906 struct screen *s = ctx->s; 907 struct grid_line *gl; 908 struct tty_ctx ttyctx; 909 u_int sx = screen_size_x(s), sy = screen_size_y(s); 910 911 screen_write_initctx(ctx, &ttyctx); 912 913 gl = &s->grid->linedata[s->grid->hsize + s->cy]; 914 if (wrapped) 915 gl->flags |= GRID_LINE_WRAPPED; 916 else 917 gl->flags &= ~GRID_LINE_WRAPPED; 918 919 if (s->cy == s->rlower) { 920 screen_dirty_clear(s, 0, s->rupper, sx - 1, s->rupper); 921 screen_write_flush(ctx); 922 grid_view_scroll_region_up(s->grid, s->rupper, s->rlower); 923 } else if (s->cy < sy - 1) 924 s->cy++; 925 926 ttyctx.num = wrapped; 927 tty_write(tty_cmd_linefeed, &ttyctx); 928 } 929 930 /* Carriage return (cursor to start of line). */ 931 void 932 screen_write_carriagereturn(struct screen_write_ctx *ctx) 933 { 934 struct screen *s = ctx->s; 935 936 s->cx = 0; 937 } 938 939 /* Clear to end of screen from cursor. */ 940 void 941 screen_write_clearendofscreen(struct screen_write_ctx *ctx) 942 { 943 struct screen *s = ctx->s; 944 struct tty_ctx ttyctx; 945 u_int sx = screen_size_x(s), sy = screen_size_y(s); 946 947 screen_write_initctx(ctx, &ttyctx); 948 949 /* Scroll into history if it is enabled and clearing entire screen. */ 950 if (s->cy == 0 && s->grid->flags & GRID_HISTORY) { 951 screen_dirty_clear(s, 0, 0, sx - 1, sy - 1); 952 grid_view_clear_history(s->grid); 953 } else { 954 if (s->cx <= sx - 1) { 955 screen_dirty_clear(s, s->cx, s->cy, sx - 1, s->cy); 956 grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1); 957 } 958 screen_dirty_clear(s, 0, s->cy + 1, sx - 1, sy - 1); 959 grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1)); 960 } 961 962 tty_write(tty_cmd_clearendofscreen, &ttyctx); 963 } 964 965 /* Clear to start of screen. */ 966 void 967 screen_write_clearstartofscreen(struct screen_write_ctx *ctx) 968 { 969 struct screen *s = ctx->s; 970 struct tty_ctx ttyctx; 971 u_int sx = screen_size_x(s); 972 973 screen_write_initctx(ctx, &ttyctx); 974 975 if (s->cy > 0) { 976 screen_dirty_clear(s, 0, 0, sx - 1, s->cy); 977 grid_view_clear(s->grid, 0, 0, sx, s->cy); 978 } 979 if (s->cx > sx - 1) { 980 screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy); 981 grid_view_clear(s->grid, 0, s->cy, sx, 1); 982 } else { 983 screen_dirty_clear(s, 0, s->cy, s->cx, s->cy); 984 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1); 985 } 986 987 tty_write(tty_cmd_clearstartofscreen, &ttyctx); 988 } 989 990 /* Clear entire screen. */ 991 void 992 screen_write_clearscreen(struct screen_write_ctx *ctx) 993 { 994 struct screen *s = ctx->s; 995 struct tty_ctx ttyctx; 996 u_int sx = screen_size_x(s), sy = screen_size_y(s); 997 998 screen_write_initctx(ctx, &ttyctx); 999 1000 screen_dirty_clear(s, 0, 0, sx - 1, sy - 1); 1001 1002 /* Scroll into history if it is enabled. */ 1003 if (s->grid->flags & GRID_HISTORY) 1004 grid_view_clear_history(s->grid); 1005 else 1006 grid_view_clear(s->grid, 0, 0, sx, sy); 1007 1008 tty_write(tty_cmd_clearscreen, &ttyctx); 1009 } 1010 1011 /* Clear entire history. */ 1012 void 1013 screen_write_clearhistory(struct screen_write_ctx *ctx) 1014 { 1015 struct screen *s = ctx->s; 1016 struct grid *gd = s->grid; 1017 1018 grid_move_lines(gd, 0, gd->hsize, gd->sy); 1019 gd->hscrolled = gd->hsize = 0; 1020 } 1021 1022 /* Write cell data. */ 1023 void 1024 screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) 1025 { 1026 struct screen *s = ctx->s; 1027 struct grid *gd = s->grid; 1028 struct tty_ctx ttyctx; 1029 u_int width, xx, last; 1030 u_int sx = screen_size_x(s), sy = screen_size_y(s); 1031 struct grid_line *gl; 1032 struct grid_cell tmp_gc, now_gc; 1033 struct grid_cell_entry *gce; 1034 int insert, skip, selected, wrapped = 0; 1035 1036 ctx->cells++; 1037 1038 /* Ignore padding. */ 1039 if (gc->flags & GRID_FLAG_PADDING) 1040 return; 1041 width = gc->data.width; 1042 1043 /* 1044 * If this is a wide character and there is no room on the screen, for 1045 * the entire character, don't print it. 1046 */ 1047 if (!(s->mode & MODE_WRAP) && (width > 1 && 1048 (width > sx || (s->cx != sx && s->cx > sx - width)))) 1049 return; 1050 1051 /* 1052 * If the width is zero, combine onto the previous character, if 1053 * there is space. 1054 */ 1055 if (width == 0) { 1056 if (screen_write_combine(ctx, &gc->data) == 0) { 1057 screen_write_initctx(ctx, &ttyctx); 1058 tty_write(tty_cmd_utf8character, &ttyctx); 1059 } 1060 return; 1061 } 1062 1063 /* Initialise the redraw context. */ 1064 screen_write_initctx(ctx, &ttyctx); 1065 1066 /* If in insert mode, make space for the cells. */ 1067 if ((s->mode & MODE_INSERT) && s->cx <= sx - width) { 1068 xx = sx - s->cx - width; 1069 grid_move_cells(s->grid, s->cx + width, s->cx, s->cy, xx); 1070 insert = 1; 1071 } else 1072 insert = 0; 1073 skip = !insert; 1074 1075 /* Check this will fit on the current line and wrap if not. */ 1076 if ((s->mode & MODE_WRAP) && s->cx > sx - width) { 1077 screen_write_flush(ctx); 1078 screen_write_save_last(ctx, &ttyctx); 1079 screen_write_linefeed(ctx, 1); 1080 s->cx = 0; /* carriage return */ 1081 skip = 0; 1082 wrapped = 1; 1083 } 1084 1085 /* Sanity check cursor position. */ 1086 if (s->cx > sx - width || s->cy > sy - 1) 1087 return; 1088 1089 /* Handle overwriting of UTF-8 characters. */ 1090 gl = &s->grid->linedata[s->grid->hsize + s->cy]; 1091 if (gl->flags & GRID_LINE_EXTENDED) { 1092 grid_view_get_cell(gd, s->cx, s->cy, &now_gc); 1093 if (screen_write_overwrite(ctx, &now_gc, width)) 1094 skip = 0; 1095 } 1096 1097 /* 1098 * If the new character is UTF-8 wide, fill in padding cells. Have 1099 * already ensured there is enough room. 1100 */ 1101 for (xx = s->cx + 1; xx < s->cx + width; xx++) { 1102 grid_view_set_cell(gd, xx, s->cy, &screen_write_pad_cell); 1103 skip = 0; 1104 } 1105 1106 /* If no change, do not draw. */ 1107 if (skip) { 1108 if (s->cx >= gl->cellsize) 1109 skip = grid_cells_equal(gc, &grid_default_cell); 1110 else { 1111 gce = &gl->celldata[s->cx]; 1112 if (gce->flags & GRID_FLAG_EXTENDED) 1113 skip = 0; 1114 else if (gc->flags != (gce->flags & ~GRID_FLAG_EXTENDED)) 1115 skip = 0; 1116 else if (gc->attr != gce->data.attr) 1117 skip = 0; 1118 else if (gc->fg != gce->data.fg) 1119 skip = 0; 1120 else if (gc->bg != gce->data.bg) 1121 skip = 0; 1122 else if (gc->data.width != 1 || gce->data.data != gc->data.data[0]) 1123 skip = 0; 1124 } 1125 } 1126 1127 /* Update the selection the flag and set the cell. */ 1128 selected = screen_check_selection(s, s->cx, s->cy); 1129 if (selected && ~gc->flags & GRID_FLAG_SELECTED) { 1130 skip = 0; 1131 memcpy(&tmp_gc, gc, sizeof tmp_gc); 1132 tmp_gc.flags |= GRID_FLAG_SELECTED; 1133 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); 1134 } else if (!selected && gc->flags & GRID_FLAG_SELECTED) { 1135 skip = 0; 1136 memcpy(&tmp_gc, gc, sizeof tmp_gc); 1137 tmp_gc.flags &= ~GRID_FLAG_SELECTED; 1138 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); 1139 } else if (!skip) 1140 grid_view_set_cell(gd, s->cx, s->cy, gc); 1141 1142 /* 1143 * Move the cursor. If not wrapping, stick at the last character and 1144 * replace it. 1145 */ 1146 last = !(s->mode & MODE_WRAP); 1147 if (s->cx <= sx - last - width) 1148 s->cx += width; 1149 else 1150 s->cx = sx - last; 1151 1152 /* Create space for character in insert mode. */ 1153 if (insert) { 1154 if (!wrapped) 1155 screen_write_flush(ctx); 1156 ttyctx.num = width; 1157 tty_write(tty_cmd_insertcharacter, &ttyctx); 1158 } 1159 1160 /* Write to the screen. */ 1161 if (selected) { 1162 memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc); 1163 utf8_copy(&tmp_gc.data, &gc->data); 1164 tmp_gc.attr = tmp_gc.attr & ~GRID_ATTR_CHARSET; 1165 tmp_gc.attr |= gc->attr & GRID_ATTR_CHARSET; 1166 tmp_gc.flags = gc->flags; 1167 screen_write_flush(ctx); 1168 ttyctx.cell = &tmp_gc; 1169 tty_write(tty_cmd_cell, &ttyctx); 1170 ctx->written++; 1171 } else if (!skip) { 1172 if (wrapped) { 1173 ttyctx.cell = gc; 1174 tty_write(tty_cmd_cell, &ttyctx); 1175 ctx->written++; 1176 } else { 1177 /* 1178 * If wp is NULL, we are not updating the terminal and 1179 * don't care about actually writing the cells 1180 * (tty_write will just return). So don't even bother 1181 * allocating the dirty array. 1182 */ 1183 if (ctx->wp != NULL && s->dirty == NULL) { 1184 log_debug("%s: allocating %u bits", __func__, 1185 s->dirtysize); 1186 s->dirty = bit_alloc(s->dirtysize); 1187 } 1188 if (s->dirty != NULL) { 1189 bit_set(s->dirty, screen_dirty_bit(s, 1190 ttyctx.ocx, ttyctx.ocy)); 1191 ctx->dirty++; 1192 } 1193 } 1194 } else 1195 ctx->skipped++; 1196 } 1197 1198 /* Combine a UTF-8 zero-width character onto the previous. */ 1199 static int 1200 screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud) 1201 { 1202 struct screen *s = ctx->s; 1203 struct grid *gd = s->grid; 1204 struct grid_cell gc; 1205 1206 /* Can't combine if at 0. */ 1207 if (s->cx == 0) 1208 return (-1); 1209 1210 /* Empty data is out. */ 1211 if (ud->size == 0) 1212 fatalx("UTF-8 data empty"); 1213 1214 /* Retrieve the previous cell. */ 1215 grid_view_get_cell(gd, s->cx - 1, s->cy, &gc); 1216 1217 /* Check there is enough space. */ 1218 if (gc.data.size + ud->size > sizeof gc.data.data) 1219 return (-1); 1220 1221 /* Append the data. */ 1222 memcpy(gc.data.data + gc.data.size, ud->data, ud->size); 1223 gc.data.size += ud->size; 1224 1225 /* Set the new cell. */ 1226 grid_view_set_cell(gd, s->cx - 1, s->cy, &gc); 1227 1228 return (0); 1229 } 1230 1231 /* 1232 * UTF-8 wide characters are a bit of an annoyance. They take up more than one 1233 * cell on the screen, so following cells must not be drawn by marking them as 1234 * padding. 1235 * 1236 * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8 1237 * character, it is necessary to also overwrite any other cells which covered 1238 * by the same character. 1239 */ 1240 static int 1241 screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc, 1242 u_int width) 1243 { 1244 struct screen *s = ctx->s; 1245 struct grid *gd = s->grid; 1246 struct grid_cell tmp_gc; 1247 u_int xx; 1248 int done = 0; 1249 1250 if (gc->flags & GRID_FLAG_PADDING) { 1251 /* 1252 * A padding cell, so clear any following and leading padding 1253 * cells back to the character. Don't overwrite the current 1254 * cell as that happens later anyway. 1255 */ 1256 xx = s->cx + 1; 1257 while (--xx > 0) { 1258 grid_view_get_cell(gd, xx, s->cy, &tmp_gc); 1259 if (~tmp_gc.flags & GRID_FLAG_PADDING) 1260 break; 1261 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1262 } 1263 1264 /* Overwrite the character at the start of this padding. */ 1265 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1266 done = 1; 1267 } 1268 1269 /* 1270 * Overwrite any padding cells that belong to a UTF-8 character 1271 * we'll be overwriting with the current character. 1272 */ 1273 if (gc->data.width != 1 || gc->flags & GRID_FLAG_PADDING) { 1274 xx = s->cx + width - 1; 1275 while (++xx < screen_size_x(s)) { 1276 grid_view_get_cell(gd, xx, s->cy, &tmp_gc); 1277 if (~tmp_gc.flags & GRID_FLAG_PADDING) 1278 break; 1279 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1280 done = 1; 1281 } 1282 } 1283 1284 return (done); 1285 } 1286 1287 void 1288 screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) 1289 { 1290 struct tty_ctx ttyctx; 1291 1292 screen_write_initctx(ctx, &ttyctx); 1293 ttyctx.ptr = str; 1294 ttyctx.num = len; 1295 1296 tty_write(tty_cmd_setselection, &ttyctx); 1297 } 1298 1299 void 1300 screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) 1301 { 1302 struct tty_ctx ttyctx; 1303 1304 screen_write_initctx(ctx, &ttyctx); 1305 ttyctx.ptr = str; 1306 ttyctx.num = len; 1307 1308 tty_write(tty_cmd_rawstring, &ttyctx); 1309 } 1310