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