1 /* $OpenBSD$ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <stdlib.h> 22 #include <string.h> 23 24 #include "tmux.h" 25 26 static void screen_write_initctx(struct screen_write_ctx *, 27 struct tty_ctx *); 28 static void screen_write_collect_clear(struct screen_write_ctx *, u_int, 29 u_int); 30 static void screen_write_collect_scroll(struct screen_write_ctx *); 31 static void screen_write_collect_flush(struct screen_write_ctx *, int); 32 33 static int screen_write_overwrite(struct screen_write_ctx *, 34 struct grid_cell *, u_int); 35 static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, 36 const struct utf8_data *, u_int *); 37 38 static const struct grid_cell screen_write_pad_cell = { 39 GRID_FLAG_PADDING, 0, 8, 8, { { 0 }, 0, 0, 0 } 40 }; 41 42 struct screen_write_collect_item { 43 u_int x; 44 45 u_int used; 46 char data[256]; 47 48 struct grid_cell gc; 49 50 TAILQ_ENTRY (screen_write_collect_item) entry; 51 }; 52 struct screen_write_collect_line { 53 TAILQ_HEAD(, screen_write_collect_item) items; 54 }; 55 56 /* Initialize writing with a window. */ 57 void 58 screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, 59 struct screen *s) 60 { 61 char tmp[16]; 62 u_int y; 63 64 memset(ctx, 0, sizeof *ctx); 65 66 ctx->wp = wp; 67 if (wp != NULL && s == NULL) 68 ctx->s = wp->screen; 69 else 70 ctx->s = s; 71 72 ctx->list = xcalloc(screen_size_y(ctx->s), sizeof *ctx->list); 73 for (y = 0; y < screen_size_y(ctx->s); y++) 74 TAILQ_INIT(&ctx->list[y].items); 75 ctx->item = xcalloc(1, sizeof *ctx->item); 76 77 if (wp != NULL) 78 snprintf(tmp, sizeof tmp, "pane %%%u", wp->id); 79 log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s), 80 screen_size_y(ctx->s), wp == NULL ? "no pane" : tmp); 81 } 82 83 /* Finish writing. */ 84 void 85 screen_write_stop(struct screen_write_ctx *ctx) 86 { 87 screen_write_collect_end(ctx); 88 screen_write_collect_flush(ctx, 0); 89 90 log_debug("%s: %u cells (%u written, %u skipped)", __func__, 91 ctx->cells, ctx->written, ctx->skipped); 92 93 free(ctx->item); 94 free(ctx->list); /* flush will have emptied */ 95 } 96 97 /* Reset screen state. */ 98 void 99 screen_write_reset(struct screen_write_ctx *ctx) 100 { 101 struct screen *s = ctx->s; 102 103 screen_reset_tabs(s); 104 screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); 105 106 s->mode &= ~(MODE_INSERT|MODE_KCURSOR|MODE_KKEYPAD|MODE_FOCUSON); 107 s->mode &= ~(ALL_MOUSE_MODES|MODE_MOUSE_UTF8|MODE_MOUSE_SGR); 108 109 screen_write_clearscreen(ctx, 8); 110 screen_write_cursormove(ctx, 0, 0); 111 } 112 113 /* Write character. */ 114 void 115 screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp, 116 u_char ch) 117 { 118 struct grid_cell gc; 119 120 memcpy(&gc, gcp, sizeof gc); 121 122 utf8_set(&gc.data, ch); 123 screen_write_cell(ctx, &gc); 124 } 125 126 /* Calculate string length, with embedded formatting. */ 127 size_t 128 screen_write_cstrlen(const char *fmt, ...) 129 { 130 va_list ap; 131 char *msg, *msg2, *ptr, *ptr2; 132 size_t size; 133 134 va_start(ap, fmt); 135 xvasprintf(&msg, fmt, ap); 136 va_end(ap); 137 msg2 = xmalloc(strlen(msg) + 1); 138 139 ptr = msg; 140 ptr2 = msg2; 141 while (*ptr != '\0') { 142 if (ptr[0] == '#' && ptr[1] == '[') { 143 while (*ptr != ']' && *ptr != '\0') 144 ptr++; 145 if (*ptr == ']') 146 ptr++; 147 continue; 148 } 149 *ptr2++ = *ptr++; 150 } 151 *ptr2 = '\0'; 152 153 size = screen_write_strlen("%s", msg2); 154 155 free(msg); 156 free(msg2); 157 158 return (size); 159 } 160 161 /* Calculate string length. */ 162 size_t 163 screen_write_strlen(const char *fmt, ...) 164 { 165 va_list ap; 166 char *msg; 167 struct utf8_data ud; 168 u_char *ptr; 169 size_t left, size = 0; 170 enum utf8_state more; 171 172 va_start(ap, fmt); 173 xvasprintf(&msg, fmt, ap); 174 va_end(ap); 175 176 ptr = (u_char *)msg; 177 while (*ptr != '\0') { 178 if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { 179 ptr++; 180 181 left = strlen((char *)ptr); 182 if (left < (size_t)ud.size - 1) 183 break; 184 while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) 185 ptr++; 186 ptr++; 187 188 if (more == UTF8_DONE) 189 size += ud.width; 190 } else { 191 if (*ptr > 0x1f && *ptr < 0x7f) 192 size++; 193 ptr++; 194 } 195 } 196 197 free(msg); 198 return (size); 199 } 200 201 /* Write simple string (no UTF-8 or maximum length). */ 202 void 203 screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp, 204 const char *fmt, ...) 205 { 206 va_list ap; 207 208 va_start(ap, fmt); 209 screen_write_vnputs(ctx, -1, gcp, fmt, ap); 210 va_end(ap); 211 } 212 213 /* Write string with length limit (-1 for unlimited). */ 214 void 215 screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, 216 const struct grid_cell *gcp, const char *fmt, ...) 217 { 218 va_list ap; 219 220 va_start(ap, fmt); 221 screen_write_vnputs(ctx, maxlen, gcp, fmt, ap); 222 va_end(ap); 223 } 224 225 void 226 screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, 227 const struct grid_cell *gcp, const char *fmt, va_list ap) 228 { 229 struct grid_cell gc; 230 struct utf8_data *ud = &gc.data; 231 char *msg; 232 u_char *ptr; 233 size_t left, size = 0; 234 enum utf8_state more; 235 236 memcpy(&gc, gcp, sizeof gc); 237 xvasprintf(&msg, fmt, ap); 238 239 ptr = (u_char *)msg; 240 while (*ptr != '\0') { 241 if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { 242 ptr++; 243 244 left = strlen((char *)ptr); 245 if (left < (size_t)ud->size - 1) 246 break; 247 while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) 248 ptr++; 249 ptr++; 250 251 if (more != UTF8_DONE) 252 continue; 253 if (maxlen > 0 && size + ud->width > (size_t)maxlen) { 254 while (size < (size_t)maxlen) { 255 screen_write_putc(ctx, &gc, ' '); 256 size++; 257 } 258 break; 259 } 260 size += ud->width; 261 screen_write_cell(ctx, &gc); 262 } else { 263 if (maxlen > 0 && size + 1 > (size_t)maxlen) 264 break; 265 266 if (*ptr == '\001') 267 gc.attr ^= GRID_ATTR_CHARSET; 268 else if (*ptr > 0x1f && *ptr < 0x7f) { 269 size++; 270 screen_write_putc(ctx, &gc, *ptr); 271 } 272 ptr++; 273 } 274 } 275 276 free(msg); 277 } 278 279 /* Write string, similar to nputs, but with embedded formatting (#[]). */ 280 void 281 screen_write_cnputs(struct screen_write_ctx *ctx, ssize_t maxlen, 282 const struct grid_cell *gcp, const char *fmt, ...) 283 { 284 struct grid_cell gc; 285 struct utf8_data *ud = &gc.data; 286 va_list ap; 287 char *msg; 288 u_char *ptr, *last; 289 size_t left, size = 0; 290 enum utf8_state more; 291 292 memcpy(&gc, gcp, sizeof gc); 293 294 va_start(ap, fmt); 295 xvasprintf(&msg, fmt, ap); 296 va_end(ap); 297 298 ptr = (u_char *)msg; 299 while (*ptr != '\0') { 300 if (ptr[0] == '#' && ptr[1] == '[') { 301 ptr += 2; 302 last = ptr + strcspn((char *)ptr, "]"); 303 if (*last == '\0') { 304 /* No ]. Not much point in doing anything. */ 305 break; 306 } 307 *last = '\0'; 308 309 style_parse(gcp, &gc, (char *)ptr); 310 ptr = last + 1; 311 continue; 312 } 313 314 if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { 315 ptr++; 316 317 left = strlen((char *)ptr); 318 if (left < (size_t)ud->size - 1) 319 break; 320 while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) 321 ptr++; 322 ptr++; 323 324 if (more != UTF8_DONE) 325 continue; 326 if (maxlen > 0 && size + ud->width > (size_t)maxlen) { 327 while (size < (size_t)maxlen) { 328 screen_write_putc(ctx, &gc, ' '); 329 size++; 330 } 331 break; 332 } 333 size += ud->width; 334 screen_write_cell(ctx, &gc); 335 } else { 336 if (maxlen > 0 && size + 1 > (size_t)maxlen) 337 break; 338 339 if (*ptr > 0x1f && *ptr < 0x7f) { 340 size++; 341 screen_write_putc(ctx, &gc, *ptr); 342 } 343 ptr++; 344 } 345 } 346 347 free(msg); 348 } 349 350 /* Copy from another screen. */ 351 void 352 screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px, 353 u_int py, u_int nx, u_int ny, bitstr_t *markbs, 354 const struct grid_cell *markgc) 355 { 356 struct screen *s = ctx->s; 357 struct grid *gd = src->grid; 358 struct grid_cell gc; 359 u_int xx, yy, cx, cy, b; 360 361 cx = s->cx; 362 cy = s->cy; 363 364 for (yy = py; yy < py + ny; yy++) { 365 for (xx = px; xx < px + nx; xx++) { 366 grid_get_cell(gd, xx, yy, &gc); 367 if (markbs != NULL) { 368 b = (yy * screen_size_x(src)) + xx; 369 if (bit_test(markbs, b)) { 370 gc.attr = markgc->attr; 371 gc.fg = markgc->fg; 372 gc.bg = markgc->bg; 373 } 374 } 375 screen_write_cell(ctx, &gc); 376 } 377 378 cy++; 379 screen_write_cursormove(ctx, cx, cy); 380 } 381 } 382 383 /* Set up context for TTY command. */ 384 static void 385 screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) 386 { 387 struct screen *s = ctx->s; 388 389 ttyctx->wp = ctx->wp; 390 391 ttyctx->ocx = s->cx; 392 ttyctx->ocy = s->cy; 393 394 ttyctx->orlower = s->rlower; 395 ttyctx->orupper = s->rupper; 396 } 397 398 /* Set a mode. */ 399 void 400 screen_write_mode_set(struct screen_write_ctx *ctx, int mode) 401 { 402 struct screen *s = ctx->s; 403 404 s->mode |= mode; 405 } 406 407 /* Clear a mode. */ 408 void 409 screen_write_mode_clear(struct screen_write_ctx *ctx, int mode) 410 { 411 struct screen *s = ctx->s; 412 413 s->mode &= ~mode; 414 } 415 416 /* Cursor up by ny. */ 417 void 418 screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) 419 { 420 struct screen *s = ctx->s; 421 422 if (ny == 0) 423 ny = 1; 424 425 if (s->cy < s->rupper) { 426 /* Above region. */ 427 if (ny > s->cy) 428 ny = s->cy; 429 } else { 430 /* Below region. */ 431 if (ny > s->cy - s->rupper) 432 ny = s->cy - s->rupper; 433 } 434 if (s->cx == screen_size_x(s)) 435 s->cx--; 436 if (ny == 0) 437 return; 438 439 s->cy -= ny; 440 } 441 442 /* Cursor down by ny. */ 443 void 444 screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) 445 { 446 struct screen *s = ctx->s; 447 448 if (ny == 0) 449 ny = 1; 450 451 if (s->cy > s->rlower) { 452 /* Below region. */ 453 if (ny > screen_size_y(s) - 1 - s->cy) 454 ny = screen_size_y(s) - 1 - s->cy; 455 } else { 456 /* Above region. */ 457 if (ny > s->rlower - s->cy) 458 ny = s->rlower - s->cy; 459 } 460 if (s->cx == screen_size_x(s)) 461 s->cx--; 462 if (ny == 0) 463 return; 464 465 s->cy += ny; 466 } 467 468 /* Cursor right by nx. */ 469 void 470 screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx) 471 { 472 struct screen *s = ctx->s; 473 474 if (nx == 0) 475 nx = 1; 476 477 if (nx > screen_size_x(s) - 1 - s->cx) 478 nx = screen_size_x(s) - 1 - s->cx; 479 if (nx == 0) 480 return; 481 482 s->cx += nx; 483 } 484 485 /* Cursor left by nx. */ 486 void 487 screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx) 488 { 489 struct screen *s = ctx->s; 490 491 if (nx == 0) 492 nx = 1; 493 494 if (nx > s->cx) 495 nx = s->cx; 496 if (nx == 0) 497 return; 498 499 s->cx -= nx; 500 } 501 502 /* Backspace; cursor left unless at start of wrapped line when can move up. */ 503 void 504 screen_write_backspace(struct screen_write_ctx *ctx) 505 { 506 struct screen *s = ctx->s; 507 struct grid_line *gl; 508 509 if (s->cx == 0) { 510 if (s->cy == 0) 511 return; 512 gl = &s->grid->linedata[s->grid->hsize + s->cy - 1]; 513 if (gl->flags & GRID_LINE_WRAPPED) { 514 s->cy--; 515 s->cx = screen_size_x(s) - 1; 516 } 517 } else 518 s->cx--; 519 } 520 521 /* VT100 alignment test. */ 522 void 523 screen_write_alignmenttest(struct screen_write_ctx *ctx) 524 { 525 struct screen *s = ctx->s; 526 struct tty_ctx ttyctx; 527 struct grid_cell gc; 528 u_int xx, yy; 529 530 screen_write_initctx(ctx, &ttyctx); 531 532 memcpy(&gc, &grid_default_cell, sizeof gc); 533 utf8_set(&gc.data, 'E'); 534 535 for (yy = 0; yy < screen_size_y(s); yy++) { 536 for (xx = 0; xx < screen_size_x(s); xx++) 537 grid_view_set_cell(s->grid, xx, yy, &gc); 538 } 539 540 s->cx = 0; 541 s->cy = 0; 542 543 s->rupper = 0; 544 s->rlower = screen_size_y(s) - 1; 545 546 screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1); 547 tty_write(tty_cmd_alignmenttest, &ttyctx); 548 } 549 550 /* Insert nx characters. */ 551 void 552 screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) 553 { 554 struct screen *s = ctx->s; 555 struct tty_ctx ttyctx; 556 557 if (nx == 0) 558 nx = 1; 559 560 if (nx > screen_size_x(s) - s->cx) 561 nx = screen_size_x(s) - s->cx; 562 if (nx == 0) 563 return; 564 565 if (s->cx > screen_size_x(s) - 1) 566 return; 567 568 screen_write_initctx(ctx, &ttyctx); 569 ttyctx.bg = bg; 570 571 grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); 572 573 screen_write_collect_flush(ctx, 0); 574 ttyctx.num = nx; 575 tty_write(tty_cmd_insertcharacter, &ttyctx); 576 } 577 578 /* Delete nx characters. */ 579 void 580 screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) 581 { 582 struct screen *s = ctx->s; 583 struct tty_ctx ttyctx; 584 585 if (nx == 0) 586 nx = 1; 587 588 if (nx > screen_size_x(s) - s->cx) 589 nx = screen_size_x(s) - s->cx; 590 if (nx == 0) 591 return; 592 593 if (s->cx > screen_size_x(s) - 1) 594 return; 595 596 screen_write_initctx(ctx, &ttyctx); 597 ttyctx.bg = bg; 598 599 grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); 600 601 screen_write_collect_flush(ctx, 0); 602 ttyctx.num = nx; 603 tty_write(tty_cmd_deletecharacter, &ttyctx); 604 } 605 606 /* Clear nx characters. */ 607 void 608 screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx) 609 { 610 struct screen *s = ctx->s; 611 struct tty_ctx ttyctx; 612 613 if (nx == 0) 614 nx = 1; 615 616 if (nx > screen_size_x(s) - s->cx) 617 nx = screen_size_x(s) - s->cx; 618 if (nx == 0) 619 return; 620 621 if (s->cx > screen_size_x(s) - 1) 622 return; 623 624 screen_write_initctx(ctx, &ttyctx); 625 626 grid_view_clear(s->grid, s->cx, s->cy, nx, 1, 8); 627 628 screen_write_collect_flush(ctx, 0); 629 ttyctx.num = nx; 630 tty_write(tty_cmd_clearcharacter, &ttyctx); 631 } 632 633 /* Insert ny lines. */ 634 void 635 screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) 636 { 637 struct screen *s = ctx->s; 638 struct grid *gd = s->grid; 639 struct tty_ctx ttyctx; 640 641 if (ny == 0) 642 ny = 1; 643 644 if (s->cy < s->rupper || s->cy > s->rlower) { 645 if (ny > screen_size_y(s) - s->cy) 646 ny = screen_size_y(s) - s->cy; 647 if (ny == 0) 648 return; 649 650 screen_write_initctx(ctx, &ttyctx); 651 ttyctx.bg = bg; 652 653 grid_view_insert_lines(gd, s->cy, ny, bg); 654 655 screen_write_collect_flush(ctx, 0); 656 ttyctx.num = ny; 657 tty_write(tty_cmd_insertline, &ttyctx); 658 return; 659 } 660 661 if (ny > s->rlower + 1 - s->cy) 662 ny = s->rlower + 1 - s->cy; 663 if (ny == 0) 664 return; 665 666 screen_write_initctx(ctx, &ttyctx); 667 ttyctx.bg = bg; 668 669 if (s->cy < s->rupper || s->cy > s->rlower) 670 grid_view_insert_lines(gd, s->cy, ny, bg); 671 else 672 grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg); 673 674 screen_write_collect_flush(ctx, 0); 675 ttyctx.num = ny; 676 tty_write(tty_cmd_insertline, &ttyctx); 677 } 678 679 /* Delete ny lines. */ 680 void 681 screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) 682 { 683 struct screen *s = ctx->s; 684 struct grid *gd = s->grid; 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); 697 ttyctx.bg = bg; 698 699 grid_view_delete_lines(gd, s->cy, ny, bg); 700 701 screen_write_collect_flush(ctx, 0); 702 ttyctx.num = ny; 703 tty_write(tty_cmd_deleteline, &ttyctx); 704 return; 705 } 706 707 if (ny > s->rlower + 1 - s->cy) 708 ny = s->rlower + 1 - s->cy; 709 if (ny == 0) 710 return; 711 712 screen_write_initctx(ctx, &ttyctx); 713 ttyctx.bg = bg; 714 715 if (s->cy < s->rupper || s->cy > s->rlower) 716 grid_view_delete_lines(gd, s->cy, ny, bg); 717 else 718 grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg); 719 720 screen_write_collect_flush(ctx, 0); 721 ttyctx.num = ny; 722 tty_write(tty_cmd_deleteline, &ttyctx); 723 } 724 725 /* Clear line at cursor. */ 726 void 727 screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) 728 { 729 struct screen *s = ctx->s; 730 struct grid_line *gl; 731 struct tty_ctx ttyctx; 732 u_int sx = screen_size_x(s); 733 734 gl = &s->grid->linedata[s->grid->hsize + s->cy]; 735 if (gl->cellsize == 0 && bg == 8) 736 return; 737 738 screen_write_initctx(ctx, &ttyctx); 739 ttyctx.bg = bg; 740 741 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); 742 743 screen_write_collect_clear(ctx, s->cy, 1); 744 screen_write_collect_flush(ctx, 0); 745 tty_write(tty_cmd_clearline, &ttyctx); 746 } 747 748 /* Clear to end of line from cursor. */ 749 void 750 screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) 751 { 752 struct screen *s = ctx->s; 753 struct grid_line *gl; 754 struct tty_ctx ttyctx; 755 u_int sx = screen_size_x(s); 756 757 gl = &s->grid->linedata[s->grid->hsize + s->cy]; 758 if (s->cx > sx - 1 || (s->cx >= gl->cellsize && bg == 8)) 759 return; 760 761 screen_write_initctx(ctx, &ttyctx); 762 ttyctx.bg = bg; 763 764 grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); 765 766 if (s->cx == 0) 767 screen_write_collect_clear(ctx, s->cy, 1); 768 screen_write_collect_flush(ctx, 0); 769 tty_write(tty_cmd_clearendofline, &ttyctx); 770 } 771 772 /* Clear to start of line from cursor. */ 773 void 774 screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) 775 { 776 struct screen *s = ctx->s; 777 struct tty_ctx ttyctx; 778 u_int sx = screen_size_x(s); 779 780 screen_write_initctx(ctx, &ttyctx); 781 ttyctx.bg = bg; 782 783 if (s->cx > sx - 1) 784 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); 785 else 786 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); 787 788 if (s->cx > sx - 1) 789 screen_write_collect_clear(ctx, s->cy, 1); 790 screen_write_collect_flush(ctx, 0); 791 tty_write(tty_cmd_clearstartofline, &ttyctx); 792 } 793 794 /* Move cursor to px,py. */ 795 void 796 screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py) 797 { 798 struct screen *s = ctx->s; 799 800 if (px > screen_size_x(s) - 1) 801 px = screen_size_x(s) - 1; 802 if (py > screen_size_y(s) - 1) 803 py = screen_size_y(s) - 1; 804 805 s->cx = px; 806 s->cy = py; 807 } 808 809 /* Reverse index (up with scroll). */ 810 void 811 screen_write_reverseindex(struct screen_write_ctx *ctx) 812 { 813 struct screen *s = ctx->s; 814 struct tty_ctx ttyctx; 815 816 screen_write_initctx(ctx, &ttyctx); 817 818 if (s->cy == s->rupper) 819 grid_view_scroll_region_down(s->grid, s->rupper, s->rlower); 820 else if (s->cy > 0) 821 s->cy--; 822 823 screen_write_collect_flush(ctx, 0); 824 tty_write(tty_cmd_reverseindex, &ttyctx); 825 } 826 827 /* Set scroll region. */ 828 void 829 screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, 830 u_int rlower) 831 { 832 struct screen *s = ctx->s; 833 834 if (rupper > screen_size_y(s) - 1) 835 rupper = screen_size_y(s) - 1; 836 if (rlower > screen_size_y(s) - 1) 837 rlower = screen_size_y(s) - 1; 838 if (rupper >= rlower) /* cannot be one line */ 839 return; 840 841 screen_write_collect_flush(ctx, 0); 842 843 /* Cursor moves to top-left. */ 844 s->cx = 0; 845 s->cy = 0; 846 847 s->rupper = rupper; 848 s->rlower = rlower; 849 } 850 851 /* Line feed. */ 852 void 853 screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped) 854 { 855 struct screen *s = ctx->s; 856 struct grid *gd = s->grid; 857 struct grid_line *gl; 858 859 gl = &gd->linedata[gd->hsize + s->cy]; 860 if (wrapped) 861 gl->flags |= GRID_LINE_WRAPPED; 862 else 863 gl->flags &= ~GRID_LINE_WRAPPED; 864 865 log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, 866 s->rupper, s->rlower); 867 868 if (s->cy == s->rlower) { 869 grid_view_scroll_region_up(gd, s->rupper, s->rlower); 870 screen_write_collect_scroll(ctx); 871 ctx->scrolled++; 872 } else if (s->cy < screen_size_y(s) - 1) 873 s->cy++; 874 } 875 876 /* Scroll up. */ 877 void 878 screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines) 879 { 880 struct screen *s = ctx->s; 881 struct grid *gd = s->grid; 882 u_int i; 883 884 if (lines == 0) 885 lines = 1; 886 else if (lines > s->rlower - s->rupper + 1) 887 lines = s->rlower - s->rupper + 1; 888 889 for (i = 0; i < lines; i++) { 890 grid_view_scroll_region_up(gd, s->rupper, s->rlower); 891 screen_write_collect_scroll(ctx); 892 } 893 ctx->scrolled += lines; 894 } 895 896 /* Carriage return (cursor to start of line). */ 897 void 898 screen_write_carriagereturn(struct screen_write_ctx *ctx) 899 { 900 struct screen *s = ctx->s; 901 902 s->cx = 0; 903 } 904 905 /* Clear to end of screen from cursor. */ 906 void 907 screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) 908 { 909 struct screen *s = ctx->s; 910 struct grid *gd = s->grid; 911 struct tty_ctx ttyctx; 912 u_int sx = screen_size_x(s), sy = screen_size_y(s); 913 914 screen_write_initctx(ctx, &ttyctx); 915 ttyctx.bg = bg; 916 917 /* Scroll into history if it is enabled and clearing entire screen. */ 918 if (s->cx == 0 && s->cy == 0 && (gd->flags & GRID_HISTORY)) 919 grid_view_clear_history(gd, bg); 920 else { 921 if (s->cx <= sx - 1) 922 grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg); 923 grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg); 924 } 925 926 screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1)); 927 screen_write_collect_flush(ctx, 0); 928 tty_write(tty_cmd_clearendofscreen, &ttyctx); 929 } 930 931 /* Clear to start of screen. */ 932 void 933 screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) 934 { 935 struct screen *s = ctx->s; 936 struct tty_ctx ttyctx; 937 u_int sx = screen_size_x(s); 938 939 screen_write_initctx(ctx, &ttyctx); 940 ttyctx.bg = bg; 941 942 if (s->cy > 0) 943 grid_view_clear(s->grid, 0, 0, sx, s->cy, bg); 944 if (s->cx > sx - 1) 945 grid_view_clear(s->grid, 0, s->cy, sx, 1, 8); 946 else 947 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, 8); 948 949 screen_write_collect_clear(ctx, 0, s->cy); 950 screen_write_collect_flush(ctx, 0); 951 tty_write(tty_cmd_clearstartofscreen, &ttyctx); 952 } 953 954 /* Clear entire screen. */ 955 void 956 screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) 957 { 958 struct screen *s = ctx->s; 959 struct tty_ctx ttyctx; 960 u_int sx = screen_size_x(s), sy = screen_size_y(s); 961 962 screen_write_initctx(ctx, &ttyctx); 963 ttyctx.bg = bg; 964 965 /* Scroll into history if it is enabled. */ 966 if (s->grid->flags & GRID_HISTORY) 967 grid_view_clear_history(s->grid, bg); 968 else 969 grid_view_clear(s->grid, 0, 0, sx, sy, bg); 970 971 screen_write_collect_clear(ctx, 0, sy); 972 tty_write(tty_cmd_clearscreen, &ttyctx); 973 } 974 975 /* Clear entire history. */ 976 void 977 screen_write_clearhistory(struct screen_write_ctx *ctx) 978 { 979 struct screen *s = ctx->s; 980 struct grid *gd = s->grid; 981 982 grid_move_lines(gd, 0, gd->hsize, gd->sy, 8); 983 gd->hscrolled = gd->hsize = 0; 984 } 985 986 /* Clear a collected line. */ 987 static void 988 screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) 989 { 990 struct screen_write_collect_item *ci, *tmp; 991 u_int i; 992 size_t size; 993 994 for (i = y ; i < y + n; i++) { 995 if (TAILQ_EMPTY(&ctx->list[i].items)) 996 continue; 997 size = 0; 998 TAILQ_FOREACH_SAFE(ci, &ctx->list[i].items, entry, tmp) { 999 size += ci->used; 1000 TAILQ_REMOVE(&ctx->list[i].items, ci, entry); 1001 free(ci); 1002 } 1003 ctx->skipped += size; 1004 log_debug("%s: dropped %zu bytes (line %u)", __func__, size, i); 1005 } 1006 } 1007 1008 /* Scroll collected lines up. */ 1009 static void 1010 screen_write_collect_scroll(struct screen_write_ctx *ctx) 1011 { 1012 struct screen *s = ctx->s; 1013 struct screen_write_collect_line *cl; 1014 u_int y; 1015 1016 log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, 1017 s->rupper, s->rlower); 1018 1019 screen_write_collect_clear(ctx, s->rupper, 1); 1020 for (y = s->rupper; y < s->rlower; y++) { 1021 cl = &ctx->list[y + 1]; 1022 TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry); 1023 TAILQ_INIT(&cl->items); 1024 } 1025 } 1026 1027 /* Flush collected lines. */ 1028 static void 1029 screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only) 1030 { 1031 struct screen *s = ctx->s; 1032 struct screen_write_collect_item *ci, *tmp; 1033 u_int y, cx, cy, items = 0; 1034 struct tty_ctx ttyctx; 1035 size_t written = 0; 1036 1037 if (ctx->scrolled != 0) { 1038 log_debug("%s: scrolled %u (region %u-%u)", __func__, 1039 ctx->scrolled, s->rupper, s->rlower); 1040 if (ctx->scrolled > s->rlower - s->rupper + 1) 1041 ctx->scrolled = s->rlower - s->rupper + 1; 1042 1043 screen_write_initctx(ctx, &ttyctx); 1044 ttyctx.num = ctx->scrolled; 1045 tty_write(tty_cmd_scrollup, &ttyctx); 1046 } 1047 ctx->scrolled = 0; 1048 if (scroll_only) 1049 return; 1050 1051 cx = s->cx; cy = s->cy; 1052 for (y = 0; y < screen_size_y(s); y++) { 1053 TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { 1054 screen_write_cursormove(ctx, ci->x, y); 1055 screen_write_initctx(ctx, &ttyctx); 1056 ttyctx.cell = &ci->gc; 1057 ttyctx.ptr = ci->data; 1058 ttyctx.num = ci->used; 1059 tty_write(tty_cmd_cells, &ttyctx); 1060 1061 items++; 1062 written += ci->used; 1063 1064 TAILQ_REMOVE(&ctx->list[y].items, ci, entry); 1065 free(ci); 1066 } 1067 } 1068 s->cx = cx; s->cy = cy; 1069 1070 log_debug("%s: flushed %u items (%zu bytes)", __func__, items, written); 1071 ctx->written += written; 1072 } 1073 1074 /* Finish and store collected cells. */ 1075 void 1076 screen_write_collect_end(struct screen_write_ctx *ctx) 1077 { 1078 struct screen *s = ctx->s; 1079 struct screen_write_collect_item *ci = ctx->item; 1080 struct grid_cell gc; 1081 1082 if (ci->used == 0) 1083 return; 1084 ci->data[ci->used] = '\0'; 1085 1086 ci->x = s->cx; 1087 TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); 1088 ctx->item = xcalloc(1, sizeof *ctx->item); 1089 1090 log_debug("%s: %u %s (at %u,%u)", __func__, ci->used, ci->data, s->cx, 1091 s->cy); 1092 1093 memcpy(&gc, &ci->gc, sizeof gc); 1094 grid_view_set_cells(s->grid, s->cx, s->cy, &gc, ci->data, ci->used); 1095 s->cx += ci->used; 1096 } 1097 1098 /* Write cell data, collecting if necessary. */ 1099 void 1100 screen_write_collect_add(struct screen_write_ctx *ctx, 1101 const struct grid_cell *gc) 1102 { 1103 struct screen *s = ctx->s; 1104 struct screen_write_collect_item *ci; 1105 u_int sx = screen_size_x(s); 1106 int collect; 1107 1108 /* 1109 * Don't need to check that the attributes and whatnot are still the 1110 * same - input_parse will end the collection when anything that isn't 1111 * a plain character is encountered. Also nothing should make it here 1112 * that isn't a single ASCII character. 1113 */ 1114 1115 collect = 1; 1116 if (gc->data.width != 1) 1117 collect = 0; 1118 else if (gc->attr & GRID_ATTR_CHARSET) 1119 collect = 0; 1120 else if (~s->mode & MODE_WRAP) 1121 collect = 0; 1122 else if (s->mode & MODE_INSERT) 1123 collect = 0; 1124 else if (s->sel.flag) 1125 collect = 0; 1126 if (!collect) { 1127 screen_write_collect_end(ctx); 1128 screen_write_collect_flush(ctx, 0); 1129 screen_write_cell(ctx, gc); 1130 return; 1131 } 1132 ctx->cells++; 1133 1134 if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx) 1135 screen_write_collect_end(ctx); 1136 if (s->cx > sx - 1) { 1137 log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); 1138 screen_write_linefeed(ctx, 1); 1139 s->cx = 0; 1140 } 1141 1142 ci = ctx->item; /* may have changed */ 1143 if (ci->used == 0) 1144 memcpy(&ci->gc, gc, sizeof ci->gc); 1145 ci->data[ci->used++] = gc->data.data[0]; 1146 if (ci->used == (sizeof ci->data) - 1) 1147 screen_write_collect_end(ctx); 1148 } 1149 1150 /* Write cell data. */ 1151 void 1152 screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) 1153 { 1154 struct screen *s = ctx->s; 1155 struct grid *gd = s->grid; 1156 struct grid_line *gl; 1157 struct grid_cell_entry *gce; 1158 struct grid_cell tmp_gc, now_gc; 1159 struct tty_ctx ttyctx; 1160 u_int sx = screen_size_x(s), sy = screen_size_y(s); 1161 u_int width = gc->data.width, xx, last, cx, cy; 1162 int selected, skip = 1; 1163 1164 /* Ignore padding cells. */ 1165 if (gc->flags & GRID_FLAG_PADDING) 1166 return; 1167 ctx->cells++; 1168 1169 /* If the width is zero, combine onto the previous character. */ 1170 if (width == 0) { 1171 screen_write_collect_flush(ctx, 0); 1172 if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) { 1173 cx = s->cx; cy = s->cy; 1174 screen_write_cursormove(ctx, xx, s->cy); 1175 screen_write_initctx(ctx, &ttyctx); 1176 ttyctx.cell = gc; 1177 tty_write(tty_cmd_cell, &ttyctx); 1178 s->cx = cx; s->cy = cy; 1179 } 1180 return; 1181 } 1182 1183 /* Flush any existing scrolling. */ 1184 screen_write_collect_flush(ctx, 1); 1185 1186 /* If this character doesn't fit, ignore it. */ 1187 if ((~s->mode & MODE_WRAP) && 1188 width > 1 && 1189 (width > sx || (s->cx != sx && s->cx > sx - width))) 1190 return; 1191 1192 /* If in insert mode, make space for the cells. */ 1193 if (s->mode & MODE_INSERT) { 1194 grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8); 1195 skip = 0; 1196 } 1197 1198 /* Check this will fit on the current line and wrap if not. */ 1199 if ((s->mode & MODE_WRAP) && s->cx > sx - width) { 1200 screen_write_linefeed(ctx, 1); 1201 s->cx = 0; 1202 } 1203 1204 /* Sanity check cursor position. */ 1205 if (s->cx > sx - width || s->cy > sy - 1) 1206 return; 1207 screen_write_initctx(ctx, &ttyctx); 1208 1209 /* Handle overwriting of UTF-8 characters. */ 1210 gl = &s->grid->linedata[s->grid->hsize + s->cy]; 1211 if (gl->flags & GRID_LINE_EXTENDED) { 1212 grid_view_get_cell(gd, s->cx, s->cy, &now_gc); 1213 if (screen_write_overwrite(ctx, &now_gc, width)) 1214 skip = 0; 1215 } 1216 1217 /* 1218 * If the new character is UTF-8 wide, fill in padding cells. Have 1219 * already ensured there is enough room. 1220 */ 1221 for (xx = s->cx + 1; xx < s->cx + width; xx++) { 1222 grid_view_set_cell(gd, xx, s->cy, &screen_write_pad_cell); 1223 skip = 0; 1224 } 1225 1226 /* If no change, do not draw. */ 1227 if (skip) { 1228 if (s->cx >= gl->cellsize) 1229 skip = grid_cells_equal(gc, &grid_default_cell); 1230 else { 1231 gce = &gl->celldata[s->cx]; 1232 if (gce->flags & GRID_FLAG_EXTENDED) 1233 skip = 0; 1234 else if (gc->flags != gce->flags) 1235 skip = 0; 1236 else if (gc->attr != gce->data.attr) 1237 skip = 0; 1238 else if (gc->fg != gce->data.fg) 1239 skip = 0; 1240 else if (gc->bg != gce->data.bg) 1241 skip = 0; 1242 else if (gc->data.width != 1) 1243 skip = 0; 1244 else if (gc->data.size != 1) 1245 skip = 0; 1246 else if (gce->data.data != gc->data.data[0]) 1247 skip = 0; 1248 } 1249 } 1250 1251 /* Update the selection the flag and set the cell. */ 1252 selected = screen_check_selection(s, s->cx, s->cy); 1253 if (selected && (~gc->flags & GRID_FLAG_SELECTED)) { 1254 memcpy(&tmp_gc, gc, sizeof tmp_gc); 1255 tmp_gc.flags |= GRID_FLAG_SELECTED; 1256 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); 1257 } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) { 1258 memcpy(&tmp_gc, gc, sizeof tmp_gc); 1259 tmp_gc.flags &= ~GRID_FLAG_SELECTED; 1260 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); 1261 } else if (!skip) 1262 grid_view_set_cell(gd, s->cx, s->cy, gc); 1263 if (selected) 1264 skip = 0; 1265 1266 /* 1267 * Move the cursor. If not wrapping, stick at the last character and 1268 * replace it. 1269 */ 1270 last = !(s->mode & MODE_WRAP); 1271 if (s->cx <= sx - last - width) 1272 s->cx += width; 1273 else 1274 s->cx = sx - last; 1275 1276 /* Create space for character in insert mode. */ 1277 if (s->mode & MODE_INSERT) { 1278 screen_write_collect_flush(ctx, 0); 1279 ttyctx.num = width; 1280 tty_write(tty_cmd_insertcharacter, &ttyctx); 1281 } 1282 1283 /* Write to the screen. */ 1284 if (!skip) { 1285 if (selected) { 1286 screen_select_cell(s, &tmp_gc, gc); 1287 ttyctx.cell = &tmp_gc; 1288 } else 1289 ttyctx.cell = gc; 1290 tty_write(tty_cmd_cell, &ttyctx); 1291 ctx->written++; 1292 } else 1293 ctx->skipped++; 1294 } 1295 1296 /* Combine a UTF-8 zero-width character onto the previous. */ 1297 static const struct grid_cell * 1298 screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud, 1299 u_int *xx) 1300 { 1301 struct screen *s = ctx->s; 1302 struct grid *gd = s->grid; 1303 static struct grid_cell gc; 1304 u_int n; 1305 1306 /* Can't combine if at 0. */ 1307 if (s->cx == 0) 1308 return (NULL); 1309 1310 /* Empty data is out. */ 1311 if (ud->size == 0) 1312 fatalx("UTF-8 data empty"); 1313 1314 /* Retrieve the previous cell. */ 1315 for (n = 1; n < s->cx; n++) { 1316 grid_view_get_cell(gd, s->cx - n, s->cy, &gc); 1317 if (~gc.flags & GRID_FLAG_PADDING) 1318 break; 1319 } 1320 if (n == s->cx) 1321 return (NULL); 1322 *xx = s->cx - n; 1323 1324 /* Check there is enough space. */ 1325 if (gc.data.size + ud->size > sizeof gc.data.data) 1326 return (NULL); 1327 1328 log_debug("%s: %.*s onto %.*s at %u,%u", __func__, (int)ud->size, 1329 ud->data, (int)gc.data.size, gc.data.data, *xx, s->cy); 1330 1331 /* Append the data. */ 1332 memcpy(gc.data.data + gc.data.size, ud->data, ud->size); 1333 gc.data.size += ud->size; 1334 1335 /* Set the new cell. */ 1336 grid_view_set_cell(gd, *xx, s->cy, &gc); 1337 1338 return (&gc); 1339 } 1340 1341 /* 1342 * UTF-8 wide characters are a bit of an annoyance. They take up more than one 1343 * cell on the screen, so following cells must not be drawn by marking them as 1344 * padding. 1345 * 1346 * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8 1347 * character, it is necessary to also overwrite any other cells which covered 1348 * by the same character. 1349 */ 1350 static int 1351 screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc, 1352 u_int width) 1353 { 1354 struct screen *s = ctx->s; 1355 struct grid *gd = s->grid; 1356 struct grid_cell tmp_gc; 1357 u_int xx; 1358 int done = 0; 1359 1360 if (gc->flags & GRID_FLAG_PADDING) { 1361 /* 1362 * A padding cell, so clear any following and leading padding 1363 * cells back to the character. Don't overwrite the current 1364 * cell as that happens later anyway. 1365 */ 1366 xx = s->cx + 1; 1367 while (--xx > 0) { 1368 grid_view_get_cell(gd, xx, s->cy, &tmp_gc); 1369 if (~tmp_gc.flags & GRID_FLAG_PADDING) 1370 break; 1371 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1372 } 1373 1374 /* Overwrite the character at the start of this padding. */ 1375 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1376 done = 1; 1377 } 1378 1379 /* 1380 * Overwrite any padding cells that belong to any UTF-8 characters 1381 * we'll be overwriting with the current character. 1382 */ 1383 if (width != 1 || 1384 gc->data.width != 1 || 1385 gc->flags & GRID_FLAG_PADDING) { 1386 xx = s->cx + width - 1; 1387 while (++xx < screen_size_x(s)) { 1388 grid_view_get_cell(gd, xx, s->cy, &tmp_gc); 1389 if (~tmp_gc.flags & GRID_FLAG_PADDING) 1390 break; 1391 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1392 done = 1; 1393 } 1394 } 1395 1396 return (done); 1397 } 1398 1399 /* Set external clipboard. */ 1400 void 1401 screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) 1402 { 1403 struct tty_ctx ttyctx; 1404 1405 screen_write_initctx(ctx, &ttyctx); 1406 ttyctx.ptr = str; 1407 ttyctx.num = len; 1408 1409 tty_write(tty_cmd_setselection, &ttyctx); 1410 } 1411 1412 /* Write unmodified string. */ 1413 void 1414 screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) 1415 { 1416 struct tty_ctx ttyctx; 1417 1418 screen_write_initctx(ctx, &ttyctx); 1419 ttyctx.ptr = str; 1420 ttyctx.num = len; 1421 1422 tty_write(tty_cmd_rawstring, &ttyctx); 1423 } 1424