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