1 /* $Id: screen-write.c,v 1.3 2011/08/17 18:48:36 jmmv 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 UTF-8 mouse mode. */ 834 void 835 screen_write_utf8mousemode(struct screen_write_ctx *ctx, int state) 836 { 837 struct screen *s = ctx->s; 838 839 if (state) 840 s->mode |= MODE_MOUSE_UTF8; 841 else 842 s->mode &= ~MODE_MOUSE_UTF8; 843 } 844 845 /* Set mouse mode off. */ 846 void 847 screen_write_mousemode_off(struct screen_write_ctx *ctx) 848 { 849 struct screen *s = ctx->s; 850 851 s->mode &= ~ALL_MOUSE_MODES; 852 } 853 854 /* Set mouse mode on. */ 855 void 856 screen_write_mousemode_on(struct screen_write_ctx *ctx, int mode) 857 { 858 struct screen *s = ctx->s; 859 860 s->mode &= ~ALL_MOUSE_MODES; 861 s->mode |= mode; 862 } 863 864 /* Line feed. */ 865 void 866 screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped) 867 { 868 struct screen *s = ctx->s; 869 struct grid_line *gl; 870 struct tty_ctx ttyctx; 871 872 screen_write_initctx(ctx, &ttyctx, 0); 873 874 gl = &s->grid->linedata[s->grid->hsize + s->cy]; 875 if (wrapped) 876 gl->flags |= GRID_LINE_WRAPPED; 877 else 878 gl->flags &= ~GRID_LINE_WRAPPED; 879 880 if (s->cy == s->rlower) 881 grid_view_scroll_region_up(s->grid, s->rupper, s->rlower); 882 else if (s->cy < screen_size_y(s) - 1) 883 s->cy++; 884 885 ttyctx.num = wrapped; 886 tty_write(tty_cmd_linefeed, &ttyctx); 887 } 888 889 /* Carriage return (cursor to start of line). */ 890 void 891 screen_write_carriagereturn(struct screen_write_ctx *ctx) 892 { 893 struct screen *s = ctx->s; 894 895 s->cx = 0; 896 } 897 898 /* Set keypad cursor keys mode. */ 899 void 900 screen_write_kcursormode(struct screen_write_ctx *ctx, int state) 901 { 902 struct screen *s = ctx->s; 903 904 if (state) 905 s->mode |= MODE_KCURSOR; 906 else 907 s->mode &= ~MODE_KCURSOR; 908 } 909 910 /* Set keypad number keys mode. */ 911 void 912 screen_write_kkeypadmode(struct screen_write_ctx *ctx, int state) 913 { 914 struct screen *s = ctx->s; 915 916 if (state) 917 s->mode |= MODE_KKEYPAD; 918 else 919 s->mode &= ~MODE_KKEYPAD; 920 } 921 922 /* Clear to end of screen from cursor. */ 923 void 924 screen_write_clearendofscreen(struct screen_write_ctx *ctx) 925 { 926 struct screen *s = ctx->s; 927 struct tty_ctx ttyctx; 928 u_int sx, sy; 929 930 screen_write_initctx(ctx, &ttyctx, 0); 931 932 sx = screen_size_x(s); 933 sy = screen_size_y(s); 934 935 /* Scroll into history if it is enabled and clearing entire screen. */ 936 if (s->cy == 0 && s->grid->flags & GRID_HISTORY) 937 grid_view_clear_history(s->grid); 938 else { 939 if (s->cx <= sx - 1) 940 grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1); 941 grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1)); 942 } 943 944 tty_write(tty_cmd_clearendofscreen, &ttyctx); 945 } 946 947 /* Clear to start of screen. */ 948 void 949 screen_write_clearstartofscreen(struct screen_write_ctx *ctx) 950 { 951 struct screen *s = ctx->s; 952 struct tty_ctx ttyctx; 953 u_int sx; 954 955 screen_write_initctx(ctx, &ttyctx, 0); 956 957 sx = screen_size_x(s); 958 959 if (s->cy > 0) 960 grid_view_clear(s->grid, 0, 0, sx, s->cy); 961 if (s->cx > sx - 1) 962 grid_view_clear(s->grid, 0, s->cy, sx, 1); 963 else 964 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1); 965 966 tty_write(tty_cmd_clearstartofscreen, &ttyctx); 967 } 968 969 /* Clear entire screen. */ 970 void 971 screen_write_clearscreen(struct screen_write_ctx *ctx) 972 { 973 struct screen *s = ctx->s; 974 struct tty_ctx ttyctx; 975 976 screen_write_initctx(ctx, &ttyctx, 0); 977 978 /* Scroll into history if it is enabled. */ 979 if (s->grid->flags & GRID_HISTORY) 980 grid_view_clear_history(s->grid); 981 else { 982 grid_view_clear( 983 s->grid, 0, 0, screen_size_x(s), screen_size_y(s)); 984 } 985 986 tty_write(tty_cmd_clearscreen, &ttyctx); 987 } 988 989 /* Write cell data. */ 990 void 991 screen_write_cell(struct screen_write_ctx *ctx, 992 const struct grid_cell *gc, const struct utf8_data *utf8data) 993 { 994 struct screen *s = ctx->s; 995 struct grid *gd = s->grid; 996 struct tty_ctx ttyctx; 997 struct grid_utf8 gu; 998 u_int width, xx; 999 struct grid_cell tmp_gc, *tmp_gcp; 1000 int insert = 0; 1001 1002 /* Ignore padding. */ 1003 if (gc->flags & GRID_FLAG_PADDING) 1004 return; 1005 1006 /* Find character width. */ 1007 if (gc->flags & GRID_FLAG_UTF8) 1008 width = utf8data->width; 1009 else 1010 width = 1; 1011 1012 /* 1013 * If this is a wide character and there is no room on the screen, for 1014 * the entire character, don't print it. 1015 */ 1016 if (!(s->mode & MODE_WRAP) 1017 && (width > 1 && (width > screen_size_x(s) || 1018 (s->cx != screen_size_x(s) 1019 && s->cx > screen_size_x(s) - width)))) 1020 return; 1021 1022 /* 1023 * If the width is zero, combine onto the previous character, if 1024 * there is space. 1025 */ 1026 if (width == 0) { 1027 if (screen_write_combine(ctx, utf8data) == 0) { 1028 screen_write_initctx(ctx, &ttyctx, 0); 1029 tty_write(tty_cmd_utf8character, &ttyctx); 1030 } 1031 return; 1032 } 1033 1034 /* Initialise the redraw context, saving the last cell. */ 1035 screen_write_initctx(ctx, &ttyctx, 1); 1036 1037 /* If in insert mode, make space for the cells. */ 1038 if (s->mode & MODE_INSERT && s->cx <= screen_size_x(s) - width) { 1039 xx = screen_size_x(s) - s->cx - width; 1040 grid_move_cells(s->grid, s->cx + width, s->cx, s->cy, xx); 1041 insert = 1; 1042 } 1043 1044 /* Check this will fit on the current line and wrap if not. */ 1045 if ((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) { 1046 screen_write_linefeed(ctx, 1); 1047 s->cx = 0; /* carriage return */ 1048 } 1049 1050 /* Sanity checks. */ 1051 if (((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) 1052 || s->cy > screen_size_y(s) - 1) 1053 return; 1054 1055 /* Handle overwriting of UTF-8 characters. */ 1056 screen_write_overwrite(ctx, width); 1057 1058 /* 1059 * If the new character is UTF-8 wide, fill in padding cells. Have 1060 * already ensured there is enough room. 1061 */ 1062 for (xx = s->cx + 1; xx < s->cx + width; xx++) { 1063 tmp_gcp = grid_view_get_cell(gd, xx, s->cy); 1064 if (tmp_gcp != NULL) 1065 tmp_gcp->flags |= GRID_FLAG_PADDING; 1066 } 1067 1068 /* Set the cell. */ 1069 grid_view_set_cell(gd, s->cx, s->cy, gc); 1070 if (gc->flags & GRID_FLAG_UTF8) { 1071 /* Construct UTF-8 and write it. */ 1072 grid_utf8_set(&gu, utf8data); 1073 grid_view_set_utf8(gd, s->cx, s->cy, &gu); 1074 } 1075 1076 /* Move the cursor. */ 1077 s->cx += width; 1078 1079 /* Draw to the screen if necessary. */ 1080 if (insert) { 1081 ttyctx.num = width; 1082 tty_write(tty_cmd_insertcharacter, &ttyctx); 1083 } 1084 ttyctx.utf8 = &gu; 1085 if (screen_check_selection(s, s->cx - width, s->cy)) { 1086 memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc); 1087 tmp_gc.data = gc->data; 1088 tmp_gc.flags = gc->flags & 1089 ~(GRID_FLAG_FG256|GRID_FLAG_BG256); 1090 tmp_gc.flags |= s->sel.cell.flags & 1091 (GRID_FLAG_FG256|GRID_FLAG_BG256); 1092 ttyctx.cell = &tmp_gc; 1093 tty_write(tty_cmd_cell, &ttyctx); 1094 } else { 1095 ttyctx.cell = gc; 1096 tty_write(tty_cmd_cell, &ttyctx); 1097 } 1098 } 1099 1100 /* Combine a UTF-8 zero-width character onto the previous. */ 1101 int 1102 screen_write_combine( 1103 struct screen_write_ctx *ctx, const struct utf8_data *utf8data) 1104 { 1105 struct screen *s = ctx->s; 1106 struct grid *gd = s->grid; 1107 struct grid_cell *gc; 1108 struct grid_utf8 *gu, tmp_gu; 1109 u_int i; 1110 1111 /* Can't combine if at 0. */ 1112 if (s->cx == 0) 1113 return (-1); 1114 1115 /* Empty utf8data is out. */ 1116 if (utf8data->size == 0) 1117 fatalx("UTF-8 data empty"); 1118 1119 /* Retrieve the previous cell and convert to UTF-8 if not already. */ 1120 gc = grid_view_get_cell(gd, s->cx - 1, s->cy); 1121 if (!(gc->flags & GRID_FLAG_UTF8)) { 1122 tmp_gu.data[0] = gc->data; 1123 tmp_gu.data[1] = 0xff; 1124 tmp_gu.width = 1; 1125 1126 grid_view_set_utf8(gd, s->cx - 1, s->cy, &tmp_gu); 1127 gc->flags |= GRID_FLAG_UTF8; 1128 } 1129 1130 /* Append the current cell. */ 1131 gu = grid_view_get_utf8(gd, s->cx - 1, s->cy); 1132 if (grid_utf8_append(gu, utf8data) != 0) { 1133 /* Failed: scrap this character and replace with underscores. */ 1134 if (gu->width == 1) { 1135 gc->data = '_'; 1136 gc->flags &= ~GRID_FLAG_UTF8; 1137 } else { 1138 for (i = 0; i < gu->width && i != sizeof gu->data; i++) 1139 gu->data[i] = '_'; 1140 if (i != sizeof gu->data) 1141 gu->data[i] = 0xff; 1142 gu->width = i; 1143 } 1144 } 1145 1146 return (0); 1147 } 1148 1149 /* 1150 * UTF-8 wide characters are a bit of an annoyance. They take up more than one 1151 * cell on the screen, so following cells must not be drawn by marking them as 1152 * padding. 1153 * 1154 * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8 1155 * character, it is necessary to also overwrite any other cells which covered 1156 * by the same character. 1157 */ 1158 void 1159 screen_write_overwrite(struct screen_write_ctx *ctx, u_int width) 1160 { 1161 struct screen *s = ctx->s; 1162 struct grid *gd = s->grid; 1163 const struct grid_cell *gc; 1164 u_int xx; 1165 1166 gc = grid_view_peek_cell(gd, s->cx, s->cy); 1167 if (gc->flags & GRID_FLAG_PADDING) { 1168 /* 1169 * A padding cell, so clear any following and leading padding 1170 * cells back to the character. Don't overwrite the current 1171 * cell as that happens later anyway. 1172 */ 1173 xx = s->cx + 1; 1174 while (--xx > 0) { 1175 gc = grid_view_peek_cell(gd, xx, s->cy); 1176 if (!(gc->flags & GRID_FLAG_PADDING)) 1177 break; 1178 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1179 } 1180 1181 /* Overwrite the character at the start of this padding. */ 1182 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1183 } 1184 1185 /* 1186 * Overwrite any padding cells that belong to a UTF-8 character 1187 * we'll be overwriting with the current character. 1188 */ 1189 xx = s->cx + width - 1; 1190 while (++xx < screen_size_x(s)) { 1191 gc = grid_view_peek_cell(gd, xx, s->cy); 1192 if (!(gc->flags & GRID_FLAG_PADDING)) 1193 break; 1194 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1195 } 1196 } 1197 1198 void 1199 screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) 1200 { 1201 struct tty_ctx ttyctx; 1202 1203 screen_write_initctx(ctx, &ttyctx, 0); 1204 ttyctx.ptr = str; 1205 ttyctx.num = len; 1206 1207 tty_write(tty_cmd_setselection, &ttyctx); 1208 } 1209 1210 void 1211 screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) 1212 { 1213 struct tty_ctx ttyctx; 1214 1215 screen_write_initctx(ctx, &ttyctx, 0); 1216 ttyctx.ptr = str; 1217 ttyctx.num = len; 1218 1219 tty_write(tty_cmd_rawstring, &ttyctx); 1220 } 1221