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