1 /* $OpenBSD: screen-write.c,v 1.103 2017/01/12 00:19:32 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 log_debug("%s: dirty %u", __func__, ctx->dirty); 107 108 cx = s->cx; 109 cy = s->cy; 110 111 offset = 0; 112 for (y = 0; y < screen_size_y(s); y++) { 113 for (x = 0; x < screen_size_x(s); x++) { 114 offset++; 115 if (!bit_test(s->dirty, offset - 1)) 116 continue; 117 bit_clear(s->dirty, offset - 1); 118 119 screen_write_cursormove(ctx, x, y); 120 grid_view_get_cell(s->grid, x, y, &gc); 121 122 screen_write_initctx(ctx, &ttyctx); 123 ttyctx.cell = &gc; 124 tty_write(tty_cmd_cell, &ttyctx); 125 ctx->written++; 126 127 if (++dirty == ctx->dirty) 128 break; 129 } 130 if (dirty == ctx->dirty) 131 break; 132 } 133 ctx->dirty = 0; 134 135 s->cx = cx; 136 s->cy = cy; 137 } 138 139 /* Reset screen state. */ 140 void 141 screen_write_reset(struct screen_write_ctx *ctx) 142 { 143 struct screen *s = ctx->s; 144 145 screen_reset_tabs(s); 146 screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); 147 148 s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD|MODE_FOCUSON); 149 s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_UTF8|MODE_MOUSE_SGR); 150 151 screen_write_clearscreen(ctx, 8); 152 screen_write_cursormove(ctx, 0, 0); 153 } 154 155 /* Write character. */ 156 void 157 screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp, 158 u_char ch) 159 { 160 struct grid_cell gc; 161 162 memcpy(&gc, gcp, sizeof gc); 163 164 utf8_set(&gc.data, ch); 165 screen_write_cell(ctx, &gc); 166 } 167 168 /* Calculate string length, with embedded formatting. */ 169 size_t 170 screen_write_cstrlen(const char *fmt, ...) 171 { 172 va_list ap; 173 char *msg, *msg2, *ptr, *ptr2; 174 size_t size; 175 176 va_start(ap, fmt); 177 xvasprintf(&msg, fmt, ap); 178 va_end(ap); 179 msg2 = xmalloc(strlen(msg) + 1); 180 181 ptr = msg; 182 ptr2 = msg2; 183 while (*ptr != '\0') { 184 if (ptr[0] == '#' && ptr[1] == '[') { 185 while (*ptr != ']' && *ptr != '\0') 186 ptr++; 187 if (*ptr == ']') 188 ptr++; 189 continue; 190 } 191 *ptr2++ = *ptr++; 192 } 193 *ptr2 = '\0'; 194 195 size = screen_write_strlen("%s", msg2); 196 197 free(msg); 198 free(msg2); 199 200 return (size); 201 } 202 203 /* Calculate string length. */ 204 size_t 205 screen_write_strlen(const char *fmt, ...) 206 { 207 va_list ap; 208 char *msg; 209 struct utf8_data ud; 210 u_char *ptr; 211 size_t left, size = 0; 212 enum utf8_state more; 213 214 va_start(ap, fmt); 215 xvasprintf(&msg, fmt, ap); 216 va_end(ap); 217 218 ptr = msg; 219 while (*ptr != '\0') { 220 if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { 221 ptr++; 222 223 left = strlen(ptr); 224 if (left < (size_t)ud.size - 1) 225 break; 226 while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) 227 ptr++; 228 ptr++; 229 230 if (more == UTF8_DONE) 231 size += ud.width; 232 } else { 233 if (*ptr > 0x1f && *ptr < 0x7f) 234 size++; 235 ptr++; 236 } 237 } 238 239 free(msg); 240 return (size); 241 } 242 243 /* Write simple string (no UTF-8 or maximum length). */ 244 void 245 screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp, 246 const char *fmt, ...) 247 { 248 va_list ap; 249 250 va_start(ap, fmt); 251 screen_write_vnputs(ctx, -1, gcp, fmt, ap); 252 va_end(ap); 253 } 254 255 /* Write string with length limit (-1 for unlimited). */ 256 void 257 screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, 258 const struct grid_cell *gcp, const char *fmt, ...) 259 { 260 va_list ap; 261 262 va_start(ap, fmt); 263 screen_write_vnputs(ctx, maxlen, gcp, fmt, ap); 264 va_end(ap); 265 } 266 267 void 268 screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, 269 const struct grid_cell *gcp, const char *fmt, va_list ap) 270 { 271 struct grid_cell gc; 272 struct utf8_data *ud = &gc.data; 273 char *msg; 274 u_char *ptr; 275 size_t left, size = 0; 276 enum utf8_state more; 277 278 memcpy(&gc, gcp, sizeof gc); 279 xvasprintf(&msg, fmt, ap); 280 281 ptr = msg; 282 while (*ptr != '\0') { 283 if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { 284 ptr++; 285 286 left = strlen(ptr); 287 if (left < (size_t)ud->size - 1) 288 break; 289 while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) 290 ptr++; 291 ptr++; 292 293 if (more != UTF8_DONE) 294 continue; 295 if (maxlen > 0 && size + ud->width > (size_t)maxlen) { 296 while (size < (size_t)maxlen) { 297 screen_write_putc(ctx, &gc, ' '); 298 size++; 299 } 300 break; 301 } 302 size += ud->width; 303 screen_write_cell(ctx, &gc); 304 } else { 305 if (maxlen > 0 && size + 1 > (size_t)maxlen) 306 break; 307 308 if (*ptr == '\001') 309 gc.attr ^= GRID_ATTR_CHARSET; 310 else if (*ptr > 0x1f && *ptr < 0x7f) { 311 size++; 312 screen_write_putc(ctx, &gc, *ptr); 313 } 314 ptr++; 315 } 316 } 317 318 free(msg); 319 } 320 321 /* Write string, similar to nputs, but with embedded formatting (#[]). */ 322 void 323 screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, 324 const struct grid_cell *gcp, const char *fmt, ...) 325 { 326 struct grid_cell gc; 327 struct utf8_data *ud = &gc.data; 328 va_list ap; 329 char *msg; 330 u_char *ptr, *last; 331 size_t left, size = 0; 332 enum utf8_state more; 333 334 memcpy(&gc, gcp, sizeof gc); 335 336 va_start(ap, fmt); 337 xvasprintf(&msg, fmt, ap); 338 va_end(ap); 339 340 ptr = msg; 341 while (*ptr != '\0') { 342 if (ptr[0] == '#' && ptr[1] == '[') { 343 ptr += 2; 344 last = ptr + strcspn(ptr, "]"); 345 if (*last == '\0') { 346 /* No ]. Not much point in doing anything. */ 347 break; 348 } 349 *last = '\0'; 350 351 style_parse(gcp, &gc, ptr); 352 ptr = last + 1; 353 continue; 354 } 355 356 if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { 357 ptr++; 358 359 left = strlen(ptr); 360 if (left < (size_t)ud->size - 1) 361 break; 362 while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) 363 ptr++; 364 ptr++; 365 366 if (more != UTF8_DONE) 367 continue; 368 if (maxlen > 0 && size + ud->width > (size_t)maxlen) { 369 while (size < (size_t)maxlen) { 370 screen_write_putc(ctx, &gc, ' '); 371 size++; 372 } 373 break; 374 } 375 size += ud->width; 376 screen_write_cell(ctx, &gc); 377 } else { 378 if (maxlen > 0 && size + 1 > (size_t)maxlen) 379 break; 380 381 if (*ptr > 0x1f && *ptr < 0x7f) { 382 size++; 383 screen_write_putc(ctx, &gc, *ptr); 384 } 385 ptr++; 386 } 387 } 388 389 free(msg); 390 } 391 392 /* Copy from another screen. */ 393 void 394 screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px, 395 u_int py, u_int nx, u_int ny, bitstr_t *markbs, 396 const struct grid_cell *markgc) 397 { 398 struct screen *s = ctx->s; 399 struct grid *gd = src->grid; 400 struct grid_cell gc; 401 u_int xx, yy, cx, cy, b; 402 403 cx = s->cx; 404 cy = s->cy; 405 406 for (yy = py; yy < py + ny; yy++) { 407 for (xx = px; xx < px + nx; xx++) { 408 grid_get_cell(gd, xx, yy, &gc); 409 if (markbs != NULL) { 410 b = (yy * screen_size_x(src)) + xx; 411 if (bit_test(markbs, b)) { 412 gc.attr = markgc->attr; 413 gc.fg = markgc->fg; 414 gc.bg = markgc->bg; 415 } 416 } 417 screen_write_cell(ctx, &gc); 418 } 419 420 cy++; 421 screen_write_cursormove(ctx, cx, cy); 422 } 423 } 424 425 /* Set up context for TTY command. */ 426 static void 427 screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) 428 { 429 struct screen *s = ctx->s; 430 431 ttyctx->wp = ctx->wp; 432 433 ttyctx->ocx = s->cx; 434 ttyctx->ocy = s->cy; 435 436 ttyctx->orlower = s->rlower; 437 ttyctx->orupper = s->rupper; 438 } 439 440 /* Save last cell on screen. */ 441 static void 442 screen_write_save_last(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) 443 { 444 struct screen *s = ctx->s; 445 struct grid *gd = s->grid; 446 struct grid_cell gc; 447 u_int xx; 448 449 memcpy(&gc, &grid_default_cell, sizeof gc); 450 for (xx = 1; xx <= screen_size_x(s); xx++) { 451 grid_view_get_cell(gd, screen_size_x(s) - xx, s->cy, &gc); 452 if (~gc.flags & GRID_FLAG_PADDING) 453 break; 454 } 455 memcpy(&ttyctx->last_cell, &gc, sizeof ttyctx->last_cell); 456 } 457 458 /* Set a mode. */ 459 void 460 screen_write_mode_set(struct screen_write_ctx *ctx, int mode) 461 { 462 struct screen *s = ctx->s; 463 464 s->mode |= mode; 465 } 466 467 /* Clear a mode. */ 468 void 469 screen_write_mode_clear(struct screen_write_ctx *ctx, int mode) 470 { 471 struct screen *s = ctx->s; 472 473 s->mode &= ~mode; 474 } 475 476 /* Cursor up by ny. */ 477 void 478 screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) 479 { 480 struct screen *s = ctx->s; 481 482 if (ny == 0) 483 ny = 1; 484 485 if (s->cy < s->rupper) { 486 /* Above region. */ 487 if (ny > s->cy) 488 ny = s->cy; 489 } else { 490 /* Below region. */ 491 if (ny > s->cy - s->rupper) 492 ny = s->cy - s->rupper; 493 } 494 if (s->cx == screen_size_x(s)) 495 s->cx--; 496 if (ny == 0) 497 return; 498 499 s->cy -= ny; 500 } 501 502 /* Cursor down by ny. */ 503 void 504 screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) 505 { 506 struct screen *s = ctx->s; 507 508 if (ny == 0) 509 ny = 1; 510 511 if (s->cy > s->rlower) { 512 /* Below region. */ 513 if (ny > screen_size_y(s) - 1 - s->cy) 514 ny = screen_size_y(s) - 1 - s->cy; 515 } else { 516 /* Above region. */ 517 if (ny > s->rlower - s->cy) 518 ny = s->rlower - s->cy; 519 } 520 if (s->cx == screen_size_x(s)) 521 s->cx--; 522 if (ny == 0) 523 return; 524 525 s->cy += ny; 526 } 527 528 /* Cursor right by nx. */ 529 void 530 screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx) 531 { 532 struct screen *s = ctx->s; 533 534 if (nx == 0) 535 nx = 1; 536 537 if (nx > screen_size_x(s) - 1 - s->cx) 538 nx = screen_size_x(s) - 1 - s->cx; 539 if (nx == 0) 540 return; 541 542 s->cx += nx; 543 } 544 545 /* Cursor left by nx. */ 546 void 547 screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx) 548 { 549 struct screen *s = ctx->s; 550 551 if (nx == 0) 552 nx = 1; 553 554 if (nx > s->cx) 555 nx = s->cx; 556 if (nx == 0) 557 return; 558 559 s->cx -= nx; 560 } 561 562 /* Backspace; cursor left unless at start of wrapped line when can move up. */ 563 void 564 screen_write_backspace(struct screen_write_ctx *ctx) 565 { 566 struct screen *s = ctx->s; 567 struct grid_line *gl; 568 569 if (s->cx == 0) { 570 if (s->cy == 0) 571 return; 572 gl = &s->grid->linedata[s->grid->hsize + s->cy - 1]; 573 if (gl->flags & GRID_LINE_WRAPPED) { 574 s->cy--; 575 s->cx = screen_size_x(s) - 1; 576 } 577 } else 578 s->cx--; 579 } 580 581 /* VT100 alignment test. */ 582 void 583 screen_write_alignmenttest(struct screen_write_ctx *ctx) 584 { 585 struct screen *s = ctx->s; 586 struct tty_ctx ttyctx; 587 struct grid_cell gc; 588 u_int xx, yy; 589 u_int sx = screen_size_x(s), sy = screen_size_y(s); 590 591 screen_write_initctx(ctx, &ttyctx); 592 593 screen_dirty_clear(s, 0, 0, sx - 1, sy - 1); 594 595 memcpy(&gc, &grid_default_cell, sizeof gc); 596 utf8_set(&gc.data, 'E'); 597 598 for (yy = 0; yy < screen_size_y(s); yy++) { 599 for (xx = 0; xx < screen_size_x(s); xx++) 600 grid_view_set_cell(s->grid, xx, yy, &gc); 601 } 602 603 s->cx = 0; 604 s->cy = 0; 605 606 s->rupper = 0; 607 s->rlower = screen_size_y(s) - 1; 608 609 tty_write(tty_cmd_alignmenttest, &ttyctx); 610 } 611 612 /* Insert nx characters. */ 613 void 614 screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) 615 { 616 struct screen *s = ctx->s; 617 struct tty_ctx ttyctx; 618 619 if (nx == 0) 620 nx = 1; 621 622 if (nx > screen_size_x(s) - s->cx) 623 nx = screen_size_x(s) - s->cx; 624 if (nx == 0) 625 return; 626 627 screen_write_flush(ctx); 628 screen_write_initctx(ctx, &ttyctx); 629 630 if (s->cx <= screen_size_x(s) - 1) 631 grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); 632 633 ttyctx.num = nx; 634 ttyctx.bg = bg; 635 tty_write(tty_cmd_insertcharacter, &ttyctx); 636 } 637 638 /* Delete nx characters. */ 639 void 640 screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) 641 { 642 struct screen *s = ctx->s; 643 struct tty_ctx ttyctx; 644 645 if (nx == 0) 646 nx = 1; 647 648 if (nx > screen_size_x(s) - s->cx) 649 nx = screen_size_x(s) - s->cx; 650 if (nx == 0) 651 return; 652 653 screen_write_flush(ctx); 654 screen_write_initctx(ctx, &ttyctx); 655 656 if (s->cx <= screen_size_x(s) - 1) 657 grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); 658 659 ttyctx.num = nx; 660 ttyctx.bg = bg; 661 tty_write(tty_cmd_deletecharacter, &ttyctx); 662 } 663 664 /* Clear nx characters. */ 665 void 666 screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx) 667 { 668 struct screen *s = ctx->s; 669 struct tty_ctx ttyctx; 670 671 if (nx == 0) 672 nx = 1; 673 674 if (nx > screen_size_x(s) - s->cx) 675 nx = screen_size_x(s) - s->cx; 676 if (nx == 0) 677 return; 678 679 screen_write_initctx(ctx, &ttyctx); 680 681 if (s->cx <= screen_size_x(s) - 1) { 682 screen_dirty_clear(s, s->cx, s->cy, s->cx + nx - 1, s->cy); 683 grid_view_clear(s->grid, s->cx, s->cy, nx, 1, 8); 684 } else 685 return; 686 687 ttyctx.num = nx; 688 tty_write(tty_cmd_clearcharacter, &ttyctx); 689 } 690 691 /* Insert ny lines. */ 692 void 693 screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) 694 { 695 struct screen *s = ctx->s; 696 struct tty_ctx ttyctx; 697 698 if (ny == 0) 699 ny = 1; 700 701 if (s->cy < s->rupper || s->cy > s->rlower) { 702 if (ny > screen_size_y(s) - s->cy) 703 ny = screen_size_y(s) - s->cy; 704 if (ny == 0) 705 return; 706 707 screen_write_flush(ctx); 708 screen_write_initctx(ctx, &ttyctx); 709 710 grid_view_insert_lines(s->grid, s->cy, ny, bg); 711 712 ttyctx.num = ny; 713 ttyctx.bg = bg; 714 tty_write(tty_cmd_insertline, &ttyctx); 715 return; 716 } 717 718 if (ny > s->rlower + 1 - s->cy) 719 ny = s->rlower + 1 - s->cy; 720 if (ny == 0) 721 return; 722 723 screen_write_flush(ctx); 724 screen_write_initctx(ctx, &ttyctx); 725 726 if (s->cy < s->rupper || s->cy > s->rlower) 727 grid_view_insert_lines(s->grid, s->cy, ny, bg); 728 else { 729 grid_view_insert_lines_region(s->grid, s->rlower, s->cy, ny, 730 bg); 731 } 732 733 ttyctx.num = ny; 734 ttyctx.bg = bg; 735 tty_write(tty_cmd_insertline, &ttyctx); 736 } 737 738 /* Delete ny lines. */ 739 void 740 screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) 741 { 742 struct screen *s = ctx->s; 743 struct tty_ctx ttyctx; 744 745 if (ny == 0) 746 ny = 1; 747 748 if (s->cy < s->rupper || s->cy > s->rlower) { 749 if (ny > screen_size_y(s) - s->cy) 750 ny = screen_size_y(s) - s->cy; 751 if (ny == 0) 752 return; 753 754 screen_write_flush(ctx); 755 screen_write_initctx(ctx, &ttyctx); 756 757 grid_view_delete_lines(s->grid, s->cy, ny, bg); 758 759 ttyctx.num = ny; 760 ttyctx.bg = bg; 761 tty_write(tty_cmd_deleteline, &ttyctx); 762 return; 763 } 764 765 if (ny > s->rlower + 1 - s->cy) 766 ny = s->rlower + 1 - s->cy; 767 if (ny == 0) 768 return; 769 770 screen_write_flush(ctx); 771 screen_write_initctx(ctx, &ttyctx); 772 773 if (s->cy < s->rupper || s->cy > s->rlower) 774 grid_view_delete_lines(s->grid, s->cy, ny, bg); 775 else { 776 grid_view_delete_lines_region(s->grid, s->rlower, s->cy, ny, 777 bg); 778 } 779 780 ttyctx.num = ny; 781 ttyctx.bg = bg; 782 tty_write(tty_cmd_deleteline, &ttyctx); 783 } 784 785 /* Clear line at cursor. */ 786 void 787 screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) 788 { 789 struct screen *s = ctx->s; 790 struct grid_line *gl; 791 struct tty_ctx ttyctx; 792 u_int sx = screen_size_x(s); 793 794 screen_write_initctx(ctx, &ttyctx); 795 ttyctx.bg = bg; 796 797 gl = &s->grid->linedata[s->grid->hsize + s->cy]; 798 if (gl->cellsize == 0 && bg == 8) 799 return; 800 801 screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy); 802 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); 803 804 tty_write(tty_cmd_clearline, &ttyctx); 805 } 806 807 /* Clear to end of line from cursor. */ 808 void 809 screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) 810 { 811 struct screen *s = ctx->s; 812 struct grid_line *gl; 813 struct tty_ctx ttyctx; 814 u_int sx = screen_size_x(s); 815 816 screen_write_initctx(ctx, &ttyctx); 817 ttyctx.bg = bg; 818 819 gl = &s->grid->linedata[s->grid->hsize + s->cy]; 820 if (s->cx > sx - 1 || (s->cx >= gl->cellsize && bg == 8)) 821 return; 822 823 screen_dirty_clear(s, s->cx, s->cy, sx - 1, s->cy); 824 grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); 825 826 tty_write(tty_cmd_clearendofline, &ttyctx); 827 } 828 829 /* Clear to start of line from cursor. */ 830 void 831 screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) 832 { 833 struct screen *s = ctx->s; 834 struct tty_ctx ttyctx; 835 u_int sx = screen_size_x(s); 836 837 screen_write_initctx(ctx, &ttyctx); 838 ttyctx.bg = bg; 839 840 if (s->cx > sx - 1) { 841 screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy); 842 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); 843 } else { 844 screen_dirty_clear(s, 0, s->cy, s->cx, s->cy); 845 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); 846 } 847 848 tty_write(tty_cmd_clearstartofline, &ttyctx); 849 } 850 851 /* Move cursor to px,py. */ 852 void 853 screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py) 854 { 855 struct screen *s = ctx->s; 856 857 if (px > screen_size_x(s) - 1) 858 px = screen_size_x(s) - 1; 859 if (py > screen_size_y(s) - 1) 860 py = screen_size_y(s) - 1; 861 862 s->cx = px; 863 s->cy = py; 864 } 865 866 /* Reverse index (up with scroll). */ 867 void 868 screen_write_reverseindex(struct screen_write_ctx *ctx) 869 { 870 struct screen *s = ctx->s; 871 struct tty_ctx ttyctx; 872 873 screen_write_initctx(ctx, &ttyctx); 874 875 if (s->cy == s->rupper) { 876 screen_write_flush(ctx); 877 grid_view_scroll_region_down(s->grid, s->rupper, s->rlower); 878 } else if (s->cy > 0) 879 s->cy--; 880 881 tty_write(tty_cmd_reverseindex, &ttyctx); 882 } 883 884 /* Set scroll region. */ 885 void 886 screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, 887 u_int rlower) 888 { 889 struct screen *s = ctx->s; 890 891 if (rupper > screen_size_y(s) - 1) 892 rupper = screen_size_y(s) - 1; 893 if (rlower > screen_size_y(s) - 1) 894 rlower = screen_size_y(s) - 1; 895 if (rupper >= rlower) /* cannot be one line */ 896 return; 897 898 /* Cursor moves to top-left. */ 899 s->cx = 0; 900 s->cy = 0; 901 902 s->rupper = rupper; 903 s->rlower = rlower; 904 } 905 906 /* Line feed. */ 907 void 908 screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped) 909 { 910 struct screen *s = ctx->s; 911 struct grid_line *gl; 912 struct tty_ctx ttyctx; 913 u_int sx = screen_size_x(s), sy = screen_size_y(s); 914 915 screen_write_initctx(ctx, &ttyctx); 916 917 gl = &s->grid->linedata[s->grid->hsize + s->cy]; 918 if (wrapped) 919 gl->flags |= GRID_LINE_WRAPPED; 920 else 921 gl->flags &= ~GRID_LINE_WRAPPED; 922 923 if (s->cy == s->rlower) { 924 screen_dirty_clear(s, 0, s->rupper, sx - 1, s->rupper); 925 screen_write_flush(ctx); 926 grid_view_scroll_region_up(s->grid, s->rupper, s->rlower); 927 } else if (s->cy < sy - 1) 928 s->cy++; 929 930 ttyctx.num = wrapped; 931 tty_write(tty_cmd_linefeed, &ttyctx); 932 } 933 934 /* Carriage return (cursor to start of line). */ 935 void 936 screen_write_carriagereturn(struct screen_write_ctx *ctx) 937 { 938 struct screen *s = ctx->s; 939 940 s->cx = 0; 941 } 942 943 /* Clear to end of screen from cursor. */ 944 void 945 screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) 946 { 947 struct screen *s = ctx->s; 948 struct tty_ctx ttyctx; 949 u_int sx = screen_size_x(s), sy = screen_size_y(s); 950 951 screen_write_initctx(ctx, &ttyctx); 952 ttyctx.bg = bg; 953 954 /* Scroll into history if it is enabled and clearing entire screen. */ 955 if (s->cx == 0 && s->cy == 0 && s->grid->flags & GRID_HISTORY) { 956 screen_dirty_clear(s, 0, 0, sx - 1, sy - 1); 957 grid_view_clear_history(s->grid, bg); 958 } else { 959 if (s->cx <= sx - 1) { 960 screen_dirty_clear(s, s->cx, s->cy, sx - 1, s->cy); 961 grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, 962 bg); 963 } 964 screen_dirty_clear(s, 0, s->cy + 1, sx - 1, sy - 1); 965 grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1), 966 bg); 967 } 968 969 tty_write(tty_cmd_clearendofscreen, &ttyctx); 970 } 971 972 /* Clear to start of screen. */ 973 void 974 screen_write_clearstartofscreen(struct screen_write_ctx *ctx) 975 { 976 struct screen *s = ctx->s; 977 struct tty_ctx ttyctx; 978 u_int sx = screen_size_x(s); 979 980 screen_write_initctx(ctx, &ttyctx); 981 982 if (s->cy > 0) { 983 screen_dirty_clear(s, 0, 0, sx - 1, s->cy); 984 grid_view_clear(s->grid, 0, 0, sx, s->cy, 8); 985 } 986 if (s->cx > sx - 1) { 987 screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy); 988 grid_view_clear(s->grid, 0, s->cy, sx, 1, 8); 989 } else { 990 screen_dirty_clear(s, 0, s->cy, s->cx, s->cy); 991 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, 8); 992 } 993 994 tty_write(tty_cmd_clearstartofscreen, &ttyctx); 995 } 996 997 /* Clear entire screen. */ 998 void 999 screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) 1000 { 1001 struct screen *s = ctx->s; 1002 struct tty_ctx ttyctx; 1003 u_int sx = screen_size_x(s), sy = screen_size_y(s); 1004 1005 screen_write_initctx(ctx, &ttyctx); 1006 ttyctx.bg = bg; 1007 1008 screen_dirty_clear(s, 0, 0, sx - 1, sy - 1); 1009 1010 /* Scroll into history if it is enabled. */ 1011 if (s->grid->flags & GRID_HISTORY) 1012 grid_view_clear_history(s->grid, bg); 1013 else 1014 grid_view_clear(s->grid, 0, 0, sx, sy, bg); 1015 1016 tty_write(tty_cmd_clearscreen, &ttyctx); 1017 } 1018 1019 /* Clear entire history. */ 1020 void 1021 screen_write_clearhistory(struct screen_write_ctx *ctx) 1022 { 1023 struct screen *s = ctx->s; 1024 struct grid *gd = s->grid; 1025 1026 grid_move_lines(gd, 0, gd->hsize, gd->sy, 8); 1027 gd->hscrolled = gd->hsize = 0; 1028 } 1029 1030 /* Write cell data. */ 1031 void 1032 screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) 1033 { 1034 struct screen *s = ctx->s; 1035 struct grid *gd = s->grid; 1036 struct tty_ctx ttyctx; 1037 u_int width, xx, last; 1038 u_int sx = screen_size_x(s), sy = screen_size_y(s); 1039 struct grid_line *gl; 1040 struct grid_cell tmp_gc, now_gc; 1041 struct grid_cell_entry *gce; 1042 int insert, skip, selected, wrapped = 0; 1043 1044 ctx->cells++; 1045 1046 /* Ignore padding. */ 1047 if (gc->flags & GRID_FLAG_PADDING) 1048 return; 1049 width = gc->data.width; 1050 1051 /* 1052 * If this is a wide character and there is no room on the screen for 1053 * the entire character, don't print it. 1054 */ 1055 if (!(s->mode & MODE_WRAP) && (width > 1 && 1056 (width > sx || (s->cx != sx && s->cx > sx - width)))) 1057 return; 1058 1059 /* 1060 * If the width is zero, combine onto the previous character, if 1061 * there is space. 1062 */ 1063 if (width == 0) { 1064 if (screen_write_combine(ctx, &gc->data) == 0) { 1065 screen_write_initctx(ctx, &ttyctx); 1066 tty_write(tty_cmd_utf8character, &ttyctx); 1067 } 1068 return; 1069 } 1070 1071 /* Initialise the redraw context. */ 1072 screen_write_initctx(ctx, &ttyctx); 1073 1074 /* If in insert mode, make space for the cells. */ 1075 if (s->mode & MODE_INSERT) { 1076 if (s->cx <= sx - width) { 1077 screen_write_flush(ctx); 1078 xx = sx - s->cx - width; 1079 grid_view_insert_cells(s->grid, s->cx, s->cy, xx, 8); 1080 } 1081 insert = 1; 1082 } else 1083 insert = 0; 1084 skip = !insert; 1085 1086 /* Check this will fit on the current line and wrap if not. */ 1087 if ((s->mode & MODE_WRAP) && s->cx > sx - width) { 1088 screen_write_flush(ctx); 1089 screen_write_save_last(ctx, &ttyctx); 1090 screen_write_linefeed(ctx, 1); 1091 s->cx = 0; /* carriage return */ 1092 skip = 0; 1093 wrapped = 1; 1094 } 1095 1096 /* Sanity check cursor position. */ 1097 if (s->cx > sx - width || s->cy > sy - 1) 1098 return; 1099 1100 /* Handle overwriting of UTF-8 characters. */ 1101 gl = &s->grid->linedata[s->grid->hsize + s->cy]; 1102 if (gl->flags & GRID_LINE_EXTENDED) { 1103 grid_view_get_cell(gd, s->cx, s->cy, &now_gc); 1104 if (screen_write_overwrite(ctx, &now_gc, width)) 1105 skip = 0; 1106 } 1107 1108 /* 1109 * If the new character is UTF-8 wide, fill in padding cells. Have 1110 * already ensured there is enough room. 1111 */ 1112 for (xx = s->cx + 1; xx < s->cx + width; xx++) { 1113 grid_view_set_cell(gd, xx, s->cy, &screen_write_pad_cell); 1114 skip = 0; 1115 } 1116 1117 /* If no change, do not draw. */ 1118 if (skip) { 1119 if (s->cx >= gl->cellsize) 1120 skip = grid_cells_equal(gc, &grid_default_cell); 1121 else { 1122 gce = &gl->celldata[s->cx]; 1123 if (gce->flags & GRID_FLAG_EXTENDED) 1124 skip = 0; 1125 else if (gc->flags != gce->flags) 1126 skip = 0; 1127 else if (gc->attr != gce->data.attr) 1128 skip = 0; 1129 else if (gc->fg != gce->data.fg) 1130 skip = 0; 1131 else if (gc->bg != gce->data.bg) 1132 skip = 0; 1133 else if (gc->data.width != 1) 1134 skip = 0; 1135 else if (gce->data.data != gc->data.data[0]) 1136 skip = 0; 1137 } 1138 } 1139 1140 /* Update the selection the flag and set the cell. */ 1141 selected = screen_check_selection(s, s->cx, s->cy); 1142 if (selected && ~gc->flags & GRID_FLAG_SELECTED) { 1143 skip = 0; 1144 memcpy(&tmp_gc, gc, sizeof tmp_gc); 1145 tmp_gc.flags |= GRID_FLAG_SELECTED; 1146 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); 1147 } else if (!selected && gc->flags & GRID_FLAG_SELECTED) { 1148 skip = 0; 1149 memcpy(&tmp_gc, gc, sizeof tmp_gc); 1150 tmp_gc.flags &= ~GRID_FLAG_SELECTED; 1151 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); 1152 } else if (!skip) 1153 grid_view_set_cell(gd, s->cx, s->cy, gc); 1154 1155 /* 1156 * Move the cursor. If not wrapping, stick at the last character and 1157 * replace it. 1158 */ 1159 last = !(s->mode & MODE_WRAP); 1160 if (s->cx <= sx - last - width) 1161 s->cx += width; 1162 else 1163 s->cx = sx - last; 1164 1165 /* Create space for character in insert mode. */ 1166 if (insert) { 1167 ttyctx.num = width; 1168 tty_write(tty_cmd_insertcharacter, &ttyctx); 1169 } 1170 1171 /* Write to the screen. */ 1172 if (selected) { 1173 screen_write_flush(ctx); 1174 screen_select_cell(s, &tmp_gc, gc); 1175 ttyctx.cell = &tmp_gc; 1176 tty_write(tty_cmd_cell, &ttyctx); 1177 ctx->written++; 1178 } else if (!skip) { 1179 if (wrapped) { 1180 ttyctx.cell = gc; 1181 tty_write(tty_cmd_cell, &ttyctx); 1182 ctx->written++; 1183 } else { 1184 /* 1185 * If wp is NULL, we are not updating the terminal and 1186 * don't care about actually writing the cells 1187 * (tty_write will just return). So don't even bother 1188 * allocating the dirty array. 1189 */ 1190 if (ctx->wp != NULL && s->dirty == NULL) { 1191 log_debug("%s: allocating %u bits", __func__, 1192 s->dirtysize); 1193 s->dirty = bit_alloc(s->dirtysize); 1194 } 1195 if (s->dirty != NULL) { 1196 bit_set(s->dirty, screen_dirty_bit(s, 1197 ttyctx.ocx, ttyctx.ocy)); 1198 ctx->dirty++; 1199 } 1200 } 1201 } else 1202 ctx->skipped++; 1203 } 1204 1205 /* Combine a UTF-8 zero-width character onto the previous. */ 1206 static int 1207 screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud) 1208 { 1209 struct screen *s = ctx->s; 1210 struct grid *gd = s->grid; 1211 struct grid_cell gc; 1212 1213 /* Can't combine if at 0. */ 1214 if (s->cx == 0) 1215 return (-1); 1216 1217 /* Empty data is out. */ 1218 if (ud->size == 0) 1219 fatalx("UTF-8 data empty"); 1220 1221 /* Retrieve the previous cell. */ 1222 grid_view_get_cell(gd, s->cx - 1, s->cy, &gc); 1223 1224 /* Check there is enough space. */ 1225 if (gc.data.size + ud->size > sizeof gc.data.data) 1226 return (-1); 1227 1228 /* Append the data. */ 1229 memcpy(gc.data.data + gc.data.size, ud->data, ud->size); 1230 gc.data.size += ud->size; 1231 1232 /* Set the new cell. */ 1233 grid_view_set_cell(gd, s->cx - 1, s->cy, &gc); 1234 1235 return (0); 1236 } 1237 1238 /* 1239 * UTF-8 wide characters are a bit of an annoyance. They take up more than one 1240 * cell on the screen, so following cells must not be drawn by marking them as 1241 * padding. 1242 * 1243 * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8 1244 * character, it is necessary to also overwrite any other cells which covered 1245 * by the same character. 1246 */ 1247 static int 1248 screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc, 1249 u_int width) 1250 { 1251 struct screen *s = ctx->s; 1252 struct grid *gd = s->grid; 1253 struct grid_cell tmp_gc; 1254 u_int xx; 1255 int done = 0; 1256 1257 if (gc->flags & GRID_FLAG_PADDING) { 1258 /* 1259 * A padding cell, so clear any following and leading padding 1260 * cells back to the character. Don't overwrite the current 1261 * cell as that happens later anyway. 1262 */ 1263 xx = s->cx + 1; 1264 while (--xx > 0) { 1265 grid_view_get_cell(gd, xx, s->cy, &tmp_gc); 1266 if (~tmp_gc.flags & GRID_FLAG_PADDING) 1267 break; 1268 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1269 } 1270 1271 /* Overwrite the character at the start of this padding. */ 1272 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1273 done = 1; 1274 } 1275 1276 /* 1277 * Overwrite any padding cells that belong to any UTF-8 characters 1278 * we'll be overwriting with the current character. 1279 */ 1280 if (width != 1 || 1281 gc->data.width != 1 || 1282 gc->flags & GRID_FLAG_PADDING) { 1283 xx = s->cx + width - 1; 1284 while (++xx < screen_size_x(s)) { 1285 grid_view_get_cell(gd, xx, s->cy, &tmp_gc); 1286 if (~tmp_gc.flags & GRID_FLAG_PADDING) 1287 break; 1288 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1289 done = 1; 1290 } 1291 } 1292 1293 return (done); 1294 } 1295 1296 void 1297 screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) 1298 { 1299 struct tty_ctx ttyctx; 1300 1301 screen_write_initctx(ctx, &ttyctx); 1302 ttyctx.ptr = str; 1303 ttyctx.num = len; 1304 1305 tty_write(tty_cmd_setselection, &ttyctx); 1306 } 1307 1308 void 1309 screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) 1310 { 1311 struct tty_ctx ttyctx; 1312 1313 screen_write_initctx(ctx, &ttyctx); 1314 ttyctx.ptr = str; 1315 ttyctx.num = len; 1316 1317 tty_write(tty_cmd_rawstring, &ttyctx); 1318 } 1319