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