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 { { 0 }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 0, 8, 8 40 }; 41 42 struct screen_write_collect_item { 43 u_int x; 44 int wrapped; 45 46 u_int used; 47 char data[256]; 48 49 struct grid_cell gc; 50 51 TAILQ_ENTRY(screen_write_collect_item) entry; 52 }; 53 struct screen_write_collect_line { 54 TAILQ_HEAD(, screen_write_collect_item) items; 55 }; 56 57 static void 58 screen_write_offset_timer(__unused int fd, __unused short events, void *data) 59 { 60 struct window *w = data; 61 62 tty_update_window_offset(w); 63 } 64 65 /* Set cursor position. */ 66 static void 67 screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy) 68 { 69 struct window_pane *wp = ctx->wp; 70 struct window *w; 71 struct screen *s = ctx->s; 72 struct timeval tv = { .tv_usec = 10000 }; 73 74 if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy) 75 return; 76 77 if (cx != -1) { 78 if ((u_int)cx > screen_size_x(s)) /* allow last column */ 79 cx = screen_size_x(s) - 1; 80 s->cx = cx; 81 } 82 if (cy != -1) { 83 if ((u_int)cy > screen_size_y(s) - 1) 84 cy = screen_size_y(s) - 1; 85 s->cy = cy; 86 } 87 88 if (wp == NULL) 89 return; 90 w = wp->window; 91 92 if (!event_initialized(&w->offset_timer)) 93 evtimer_set(&w->offset_timer, screen_write_offset_timer, w); 94 if (!evtimer_pending(&w->offset_timer, NULL)) 95 evtimer_add(&w->offset_timer, &tv); 96 } 97 98 /* Initialize writing with a window. */ 99 void 100 screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, 101 struct screen *s) 102 { 103 char tmp[32]; 104 u_int y; 105 106 memset(ctx, 0, sizeof *ctx); 107 108 ctx->wp = wp; 109 if (wp != NULL && s == NULL) 110 ctx->s = wp->screen; 111 else 112 ctx->s = s; 113 114 ctx->list = xcalloc(screen_size_y(ctx->s), sizeof *ctx->list); 115 for (y = 0; y < screen_size_y(ctx->s); y++) 116 TAILQ_INIT(&ctx->list[y].items); 117 ctx->item = xcalloc(1, sizeof *ctx->item); 118 119 ctx->scrolled = 0; 120 ctx->bg = 8; 121 122 if (wp != NULL) { 123 snprintf(tmp, sizeof tmp, "pane %%%u (at %u,%u)", wp->id, 124 wp->xoff, wp->yoff); 125 } 126 log_debug("%s: size %ux%u, %s", __func__, screen_size_x(ctx->s), 127 screen_size_y(ctx->s), wp == NULL ? "no pane" : tmp); 128 } 129 130 /* Finish writing. */ 131 void 132 screen_write_stop(struct screen_write_ctx *ctx) 133 { 134 screen_write_collect_end(ctx); 135 screen_write_collect_flush(ctx, 0); 136 137 log_debug("%s: %u cells (%u written, %u skipped)", __func__, 138 ctx->cells, ctx->written, ctx->skipped); 139 140 free(ctx->item); 141 free(ctx->list); /* flush will have emptied */ 142 } 143 144 /* Reset screen state. */ 145 void 146 screen_write_reset(struct screen_write_ctx *ctx) 147 { 148 struct screen *s = ctx->s; 149 150 screen_reset_tabs(s); 151 screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); 152 153 s->mode = MODE_CURSOR | MODE_WRAP; 154 155 screen_write_clearscreen(ctx, 8); 156 screen_write_set_cursor(ctx, 0, 0); 157 } 158 159 /* Write character. */ 160 void 161 screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp, 162 u_char ch) 163 { 164 struct grid_cell gc; 165 166 memcpy(&gc, gcp, sizeof gc); 167 168 utf8_set(&gc.data, ch); 169 screen_write_cell(ctx, &gc); 170 } 171 172 /* Calculate string length. */ 173 size_t 174 screen_write_strlen(const char *fmt, ...) 175 { 176 va_list ap; 177 char *msg; 178 struct utf8_data ud; 179 u_char *ptr; 180 size_t left, size = 0; 181 enum utf8_state more; 182 183 va_start(ap, fmt); 184 xvasprintf(&msg, fmt, ap); 185 va_end(ap); 186 187 ptr = (u_char *)msg; 188 while (*ptr != '\0') { 189 if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { 190 ptr++; 191 192 left = strlen((char *)ptr); 193 if (left < (size_t)ud.size - 1) 194 break; 195 while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) 196 ptr++; 197 ptr++; 198 199 if (more == UTF8_DONE) 200 size += ud.width; 201 } else { 202 if (*ptr > 0x1f && *ptr < 0x7f) 203 size++; 204 ptr++; 205 } 206 } 207 208 free(msg); 209 return (size); 210 } 211 212 /* Write simple string (no UTF-8 or maximum length). */ 213 void 214 screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp, 215 const char *fmt, ...) 216 { 217 va_list ap; 218 219 va_start(ap, fmt); 220 screen_write_vnputs(ctx, -1, gcp, fmt, ap); 221 va_end(ap); 222 } 223 224 /* Write string with length limit (-1 for unlimited). */ 225 void 226 screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, 227 const struct grid_cell *gcp, const char *fmt, ...) 228 { 229 va_list ap; 230 231 va_start(ap, fmt); 232 screen_write_vnputs(ctx, maxlen, gcp, fmt, ap); 233 va_end(ap); 234 } 235 236 void 237 screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, 238 const struct grid_cell *gcp, const char *fmt, va_list ap) 239 { 240 struct grid_cell gc; 241 struct utf8_data *ud = &gc.data; 242 char *msg; 243 u_char *ptr; 244 size_t left, size = 0; 245 enum utf8_state more; 246 247 memcpy(&gc, gcp, sizeof gc); 248 xvasprintf(&msg, fmt, ap); 249 250 ptr = (u_char *)msg; 251 while (*ptr != '\0') { 252 if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { 253 ptr++; 254 255 left = strlen((char *)ptr); 256 if (left < (size_t)ud->size - 1) 257 break; 258 while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) 259 ptr++; 260 ptr++; 261 262 if (more != UTF8_DONE) 263 continue; 264 if (maxlen > 0 && size + ud->width > (size_t)maxlen) { 265 while (size < (size_t)maxlen) { 266 screen_write_putc(ctx, &gc, ' '); 267 size++; 268 } 269 break; 270 } 271 size += ud->width; 272 screen_write_cell(ctx, &gc); 273 } else { 274 if (maxlen > 0 && size + 1 > (size_t)maxlen) 275 break; 276 277 if (*ptr == '\001') 278 gc.attr ^= GRID_ATTR_CHARSET; 279 else if (*ptr > 0x1f && *ptr < 0x7f) { 280 size++; 281 screen_write_putc(ctx, &gc, *ptr); 282 } 283 ptr++; 284 } 285 } 286 287 free(msg); 288 } 289 290 /* Copy from another screen. Assumes target region is big enough. */ 291 void 292 screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px, 293 u_int py, u_int nx, u_int ny, bitstr_t *mbs, const struct grid_cell *mgc) 294 { 295 struct screen *s = ctx->s; 296 struct grid *gd = src->grid; 297 struct grid_cell gc; 298 u_int xx, yy, cx, cy, b; 299 300 if (nx == 0 || ny == 0) 301 return; 302 303 cx = s->cx; 304 cy = s->cy; 305 306 for (yy = py; yy < py + ny; yy++) { 307 for (xx = px; xx < px + nx; xx++) { 308 grid_get_cell(gd, xx, yy, &gc); 309 if (mbs != NULL) { 310 b = (yy * screen_size_x(src)) + xx; 311 if (bit_test(mbs, b)) { 312 gc.attr = mgc->attr; 313 gc.fg = mgc->fg; 314 gc.bg = mgc->bg; 315 } 316 } 317 if (xx + gc.data.width <= px + nx) 318 screen_write_cell(ctx, &gc); 319 } 320 cy++; 321 screen_write_set_cursor(ctx, cx, cy); 322 } 323 } 324 325 /* 326 * Copy from another screen but without the selection stuff. Also assumes the 327 * target region is already big enough. 328 */ 329 void 330 screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src, 331 u_int px, u_int py, u_int nx, u_int ny) 332 { 333 struct screen *s = ctx->s; 334 struct grid *gd = src->grid; 335 struct grid_cell gc; 336 u_int xx, yy, cx, cy; 337 338 if (nx == 0 || ny == 0) 339 return; 340 341 cy = s->cy; 342 for (yy = py; yy < py + ny; yy++) { 343 if (yy >= gd->hsize + gd->sy) 344 break; 345 cx = s->cx; 346 for (xx = px; xx < px + nx; xx++) { 347 if (xx >= grid_get_line(gd, yy)->cellsize) 348 break; 349 grid_get_cell(gd, xx, yy, &gc); 350 if (xx + gc.data.width > px + nx) 351 break; 352 grid_view_set_cell(ctx->s->grid, cx, cy, &gc); 353 cx++; 354 } 355 cy++; 356 } 357 } 358 359 /* Draw a horizontal line on screen. */ 360 void 361 screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right) 362 { 363 struct screen *s = ctx->s; 364 struct grid_cell gc; 365 u_int cx, cy, i; 366 367 cx = s->cx; 368 cy = s->cy; 369 370 memcpy(&gc, &grid_default_cell, sizeof gc); 371 gc.attr |= GRID_ATTR_CHARSET; 372 373 screen_write_putc(ctx, &gc, left ? 't' : 'q'); 374 for (i = 1; i < nx - 1; i++) 375 screen_write_putc(ctx, &gc, 'q'); 376 screen_write_putc(ctx, &gc, right ? 'u' : 'q'); 377 378 screen_write_set_cursor(ctx, cx, cy); 379 } 380 381 /* Draw a horizontal line on screen. */ 382 void 383 screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom) 384 { 385 struct screen *s = ctx->s; 386 struct grid_cell gc; 387 u_int cx, cy, i; 388 389 cx = s->cx; 390 cy = s->cy; 391 392 memcpy(&gc, &grid_default_cell, sizeof gc); 393 gc.attr |= GRID_ATTR_CHARSET; 394 395 screen_write_putc(ctx, &gc, top ? 'w' : 'x'); 396 for (i = 1; i < ny - 1; i++) { 397 screen_write_set_cursor(ctx, cx, cy + i); 398 screen_write_putc(ctx, &gc, 'x'); 399 } 400 screen_write_set_cursor(ctx, cx, cy + ny - 1); 401 screen_write_putc(ctx, &gc, bottom ? 'v' : 'x'); 402 403 screen_write_set_cursor(ctx, cx, cy); 404 } 405 406 /* Draw a menu on screen. */ 407 void 408 screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice) 409 { 410 struct screen *s = ctx->s; 411 struct grid_cell gc; 412 u_int cx, cy, i, j; 413 const char *name; 414 415 cx = s->cx; 416 cy = s->cy; 417 418 memcpy(&gc, &grid_default_cell, sizeof gc); 419 420 screen_write_box(ctx, menu->width + 4, menu->count + 2); 421 screen_write_cursormove(ctx, cx + 2, cy, 0); 422 format_draw(ctx, &gc, menu->width, menu->title, NULL); 423 424 for (i = 0; i < menu->count; i++) { 425 name = menu->items[i].name; 426 if (name == NULL) { 427 screen_write_cursormove(ctx, cx, cy + 1 + i, 0); 428 screen_write_hline(ctx, menu->width + 4, 1, 1); 429 } else { 430 if (choice >= 0 && i == (u_int)choice && *name != '-') 431 gc.attr |= GRID_ATTR_REVERSE; 432 screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0); 433 for (j = 0; j < menu->width; j++) 434 screen_write_putc(ctx, &gc, ' '); 435 screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0); 436 if (*name == '-') { 437 name++; 438 gc.attr |= GRID_ATTR_DIM; 439 format_draw(ctx, &gc, menu->width, name, NULL); 440 gc.attr &= ~GRID_ATTR_DIM; 441 } else 442 format_draw(ctx, &gc, menu->width, name, NULL); 443 if (choice >= 0 && i == (u_int)choice) 444 gc.attr &= ~GRID_ATTR_REVERSE; 445 } 446 } 447 448 screen_write_set_cursor(ctx, cx, cy); 449 } 450 451 /* Draw a box on screen. */ 452 void 453 screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny) 454 { 455 struct screen *s = ctx->s; 456 struct grid_cell gc; 457 u_int cx, cy, i; 458 459 cx = s->cx; 460 cy = s->cy; 461 462 memcpy(&gc, &grid_default_cell, sizeof gc); 463 gc.attr |= GRID_ATTR_CHARSET; 464 465 screen_write_putc(ctx, &gc, 'l'); 466 for (i = 1; i < nx - 1; i++) 467 screen_write_putc(ctx, &gc, 'q'); 468 screen_write_putc(ctx, &gc, 'k'); 469 470 screen_write_set_cursor(ctx, cx, cy + ny - 1); 471 screen_write_putc(ctx, &gc, 'm'); 472 for (i = 1; i < nx - 1; i++) 473 screen_write_putc(ctx, &gc, 'q'); 474 screen_write_putc(ctx, &gc, 'j'); 475 476 for (i = 1; i < ny - 1; i++) { 477 screen_write_set_cursor(ctx, cx, cy + i); 478 screen_write_putc(ctx, &gc, 'x'); 479 } 480 for (i = 1; i < ny - 1; i++) { 481 screen_write_set_cursor(ctx, cx + nx - 1, cy + i); 482 screen_write_putc(ctx, &gc, 'x'); 483 } 484 485 screen_write_set_cursor(ctx, cx, cy); 486 } 487 488 /* 489 * Write a preview version of a window. Assumes target area is big enough and 490 * already cleared. 491 */ 492 void 493 screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx, 494 u_int ny) 495 { 496 struct screen *s = ctx->s; 497 struct grid_cell gc; 498 u_int cx, cy, px, py; 499 500 cx = s->cx; 501 cy = s->cy; 502 503 /* 504 * If the cursor is on, pick the area around the cursor, otherwise use 505 * the top left. 506 */ 507 if (src->mode & MODE_CURSOR) { 508 px = src->cx; 509 if (px < nx / 3) 510 px = 0; 511 else 512 px = px - nx / 3; 513 if (px + nx > screen_size_x(src)) { 514 if (nx > screen_size_x(src)) 515 px = 0; 516 else 517 px = screen_size_x(src) - nx; 518 } 519 py = src->cy; 520 if (py < ny / 3) 521 py = 0; 522 else 523 py = py - ny / 3; 524 if (py + ny > screen_size_y(src)) { 525 if (ny > screen_size_y(src)) 526 py = 0; 527 else 528 py = screen_size_y(src) - ny; 529 } 530 } else { 531 px = 0; 532 py = 0; 533 } 534 535 screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny); 536 537 if (src->mode & MODE_CURSOR) { 538 grid_view_get_cell(src->grid, src->cx, src->cy, &gc); 539 gc.attr |= GRID_ATTR_REVERSE; 540 screen_write_set_cursor(ctx, cx + (src->cx - px), 541 cy + (src->cy - py)); 542 screen_write_cell(ctx, &gc); 543 } 544 } 545 546 /* Set up context for TTY command. */ 547 static void 548 screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) 549 { 550 struct screen *s = ctx->s; 551 552 memset(ttyctx, 0, sizeof *ttyctx); 553 554 ttyctx->wp = ctx->wp; 555 556 ttyctx->ocx = s->cx; 557 ttyctx->ocy = s->cy; 558 559 ttyctx->orlower = s->rlower; 560 ttyctx->orupper = s->rupper; 561 } 562 563 /* Set a mode. */ 564 void 565 screen_write_mode_set(struct screen_write_ctx *ctx, int mode) 566 { 567 struct screen *s = ctx->s; 568 569 s->mode |= mode; 570 } 571 572 /* Clear a mode. */ 573 void 574 screen_write_mode_clear(struct screen_write_ctx *ctx, int mode) 575 { 576 struct screen *s = ctx->s; 577 578 s->mode &= ~mode; 579 } 580 581 /* Cursor up by ny. */ 582 void 583 screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) 584 { 585 struct screen *s = ctx->s; 586 u_int cx = s->cx, cy = s->cy; 587 588 if (ny == 0) 589 ny = 1; 590 591 if (cy < s->rupper) { 592 /* Above region. */ 593 if (ny > cy) 594 ny = cy; 595 } else { 596 /* Below region. */ 597 if (ny > cy - s->rupper) 598 ny = cy - s->rupper; 599 } 600 if (cx == screen_size_x(s)) 601 cx--; 602 603 cy -= ny; 604 605 screen_write_set_cursor(ctx, cx, cy); 606 } 607 608 /* Cursor down by ny. */ 609 void 610 screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) 611 { 612 struct screen *s = ctx->s; 613 u_int cx = s->cx, cy = s->cy; 614 615 if (ny == 0) 616 ny = 1; 617 618 if (cy > s->rlower) { 619 /* Below region. */ 620 if (ny > screen_size_y(s) - 1 - cy) 621 ny = screen_size_y(s) - 1 - cy; 622 } else { 623 /* Above region. */ 624 if (ny > s->rlower - cy) 625 ny = s->rlower - cy; 626 } 627 if (cx == screen_size_x(s)) 628 cx--; 629 else if (ny == 0) 630 return; 631 632 cy += ny; 633 634 screen_write_set_cursor(ctx, cx, cy); 635 } 636 637 /* Cursor right by nx. */ 638 void 639 screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx) 640 { 641 struct screen *s = ctx->s; 642 u_int cx = s->cx, cy = s->cy; 643 644 if (nx == 0) 645 nx = 1; 646 647 if (nx > screen_size_x(s) - 1 - cx) 648 nx = screen_size_x(s) - 1 - cx; 649 if (nx == 0) 650 return; 651 652 cx += nx; 653 654 screen_write_set_cursor(ctx, cx, cy); 655 } 656 657 /* Cursor left by nx. */ 658 void 659 screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx) 660 { 661 struct screen *s = ctx->s; 662 u_int cx = s->cx, cy = s->cy; 663 664 if (nx == 0) 665 nx = 1; 666 667 if (nx > cx) 668 nx = cx; 669 if (nx == 0) 670 return; 671 672 cx -= nx; 673 674 screen_write_set_cursor(ctx, cx, cy); 675 } 676 677 /* Backspace; cursor left unless at start of wrapped line when can move up. */ 678 void 679 screen_write_backspace(struct screen_write_ctx *ctx) 680 { 681 struct screen *s = ctx->s; 682 struct grid_line *gl; 683 u_int cx = s->cx, cy = s->cy; 684 685 if (cx == 0) { 686 if (cy == 0) 687 return; 688 gl = grid_get_line(s->grid, s->grid->hsize + cy - 1); 689 if (gl->flags & GRID_LINE_WRAPPED) { 690 cy--; 691 cx = screen_size_x(s) - 1; 692 } 693 } else 694 cx--; 695 696 screen_write_set_cursor(ctx, cx, cy); 697 } 698 699 /* VT100 alignment test. */ 700 void 701 screen_write_alignmenttest(struct screen_write_ctx *ctx) 702 { 703 struct screen *s = ctx->s; 704 struct tty_ctx ttyctx; 705 struct grid_cell gc; 706 u_int xx, yy; 707 708 memcpy(&gc, &grid_default_cell, sizeof gc); 709 utf8_set(&gc.data, 'E'); 710 711 for (yy = 0; yy < screen_size_y(s); yy++) { 712 for (xx = 0; xx < screen_size_x(s); xx++) 713 grid_view_set_cell(s->grid, xx, yy, &gc); 714 } 715 716 screen_write_set_cursor(ctx, 0, 0); 717 718 s->rupper = 0; 719 s->rlower = screen_size_y(s) - 1; 720 721 screen_write_initctx(ctx, &ttyctx); 722 723 screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1); 724 tty_write(tty_cmd_alignmenttest, &ttyctx); 725 } 726 727 /* Insert nx characters. */ 728 void 729 screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) 730 { 731 struct screen *s = ctx->s; 732 struct tty_ctx ttyctx; 733 734 if (nx == 0) 735 nx = 1; 736 737 if (nx > screen_size_x(s) - s->cx) 738 nx = screen_size_x(s) - s->cx; 739 if (nx == 0) 740 return; 741 742 if (s->cx > screen_size_x(s) - 1) 743 return; 744 745 screen_write_initctx(ctx, &ttyctx); 746 ttyctx.bg = bg; 747 748 grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); 749 750 screen_write_collect_flush(ctx, 0); 751 ttyctx.num = nx; 752 tty_write(tty_cmd_insertcharacter, &ttyctx); 753 } 754 755 /* Delete nx characters. */ 756 void 757 screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) 758 { 759 struct screen *s = ctx->s; 760 struct tty_ctx ttyctx; 761 762 if (nx == 0) 763 nx = 1; 764 765 if (nx > screen_size_x(s) - s->cx) 766 nx = screen_size_x(s) - s->cx; 767 if (nx == 0) 768 return; 769 770 if (s->cx > screen_size_x(s) - 1) 771 return; 772 773 screen_write_initctx(ctx, &ttyctx); 774 ttyctx.bg = bg; 775 776 grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); 777 778 screen_write_collect_flush(ctx, 0); 779 ttyctx.num = nx; 780 tty_write(tty_cmd_deletecharacter, &ttyctx); 781 } 782 783 /* Clear nx characters. */ 784 void 785 screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) 786 { 787 struct screen *s = ctx->s; 788 struct tty_ctx ttyctx; 789 790 if (nx == 0) 791 nx = 1; 792 793 if (nx > screen_size_x(s) - s->cx) 794 nx = screen_size_x(s) - s->cx; 795 if (nx == 0) 796 return; 797 798 if (s->cx > screen_size_x(s) - 1) 799 return; 800 801 screen_write_initctx(ctx, &ttyctx); 802 ttyctx.bg = bg; 803 804 grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg); 805 806 screen_write_collect_flush(ctx, 0); 807 ttyctx.num = nx; 808 tty_write(tty_cmd_clearcharacter, &ttyctx); 809 } 810 811 /* Insert ny lines. */ 812 void 813 screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) 814 { 815 struct screen *s = ctx->s; 816 struct grid *gd = s->grid; 817 struct tty_ctx ttyctx; 818 819 if (ny == 0) 820 ny = 1; 821 822 if (s->cy < s->rupper || s->cy > s->rlower) { 823 if (ny > screen_size_y(s) - s->cy) 824 ny = screen_size_y(s) - s->cy; 825 if (ny == 0) 826 return; 827 828 screen_write_initctx(ctx, &ttyctx); 829 ttyctx.bg = bg; 830 831 grid_view_insert_lines(gd, s->cy, ny, bg); 832 833 screen_write_collect_flush(ctx, 0); 834 ttyctx.num = ny; 835 tty_write(tty_cmd_insertline, &ttyctx); 836 return; 837 } 838 839 if (ny > s->rlower + 1 - s->cy) 840 ny = s->rlower + 1 - s->cy; 841 if (ny == 0) 842 return; 843 844 screen_write_initctx(ctx, &ttyctx); 845 ttyctx.bg = bg; 846 847 if (s->cy < s->rupper || s->cy > s->rlower) 848 grid_view_insert_lines(gd, s->cy, ny, bg); 849 else 850 grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg); 851 852 screen_write_collect_flush(ctx, 0); 853 ttyctx.num = ny; 854 tty_write(tty_cmd_insertline, &ttyctx); 855 } 856 857 /* Delete ny lines. */ 858 void 859 screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) 860 { 861 struct screen *s = ctx->s; 862 struct grid *gd = s->grid; 863 struct tty_ctx ttyctx; 864 865 if (ny == 0) 866 ny = 1; 867 868 if (s->cy < s->rupper || s->cy > s->rlower) { 869 if (ny > screen_size_y(s) - s->cy) 870 ny = screen_size_y(s) - s->cy; 871 if (ny == 0) 872 return; 873 874 screen_write_initctx(ctx, &ttyctx); 875 ttyctx.bg = bg; 876 877 grid_view_delete_lines(gd, s->cy, ny, bg); 878 879 screen_write_collect_flush(ctx, 0); 880 ttyctx.num = ny; 881 tty_write(tty_cmd_deleteline, &ttyctx); 882 return; 883 } 884 885 if (ny > s->rlower + 1 - s->cy) 886 ny = s->rlower + 1 - s->cy; 887 if (ny == 0) 888 return; 889 890 screen_write_initctx(ctx, &ttyctx); 891 ttyctx.bg = bg; 892 893 if (s->cy < s->rupper || s->cy > s->rlower) 894 grid_view_delete_lines(gd, s->cy, ny, bg); 895 else 896 grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg); 897 898 screen_write_collect_flush(ctx, 0); 899 ttyctx.num = ny; 900 tty_write(tty_cmd_deleteline, &ttyctx); 901 } 902 903 /* Clear line at cursor. */ 904 void 905 screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) 906 { 907 struct screen *s = ctx->s; 908 struct grid_line *gl; 909 struct tty_ctx ttyctx; 910 u_int sx = screen_size_x(s); 911 912 gl = grid_get_line(s->grid, s->grid->hsize + s->cy); 913 if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) 914 return; 915 916 screen_write_initctx(ctx, &ttyctx); 917 ttyctx.bg = bg; 918 919 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); 920 921 screen_write_collect_clear(ctx, s->cy, 1); 922 screen_write_collect_flush(ctx, 0); 923 tty_write(tty_cmd_clearline, &ttyctx); 924 } 925 926 /* Clear to end of line from cursor. */ 927 void 928 screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) 929 { 930 struct screen *s = ctx->s; 931 struct grid_line *gl; 932 struct tty_ctx ttyctx; 933 u_int sx = screen_size_x(s); 934 935 gl = grid_get_line(s->grid, s->grid->hsize + s->cy); 936 if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg))) 937 return; 938 939 screen_write_initctx(ctx, &ttyctx); 940 ttyctx.bg = bg; 941 942 grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); 943 944 if (s->cx == 0) 945 screen_write_collect_clear(ctx, s->cy, 1); 946 screen_write_collect_flush(ctx, 0); 947 tty_write(tty_cmd_clearendofline, &ttyctx); 948 } 949 950 /* Clear to start of line from cursor. */ 951 void 952 screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) 953 { 954 struct screen *s = ctx->s; 955 struct tty_ctx ttyctx; 956 u_int sx = screen_size_x(s); 957 958 screen_write_initctx(ctx, &ttyctx); 959 ttyctx.bg = bg; 960 961 if (s->cx > sx - 1) 962 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); 963 else 964 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); 965 966 if (s->cx > sx - 1) 967 screen_write_collect_clear(ctx, s->cy, 1); 968 screen_write_collect_flush(ctx, 0); 969 tty_write(tty_cmd_clearstartofline, &ttyctx); 970 } 971 972 /* Move cursor to px,py. */ 973 void 974 screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py, 975 int origin) 976 { 977 struct screen *s = ctx->s; 978 979 if (origin && py != -1 && (s->mode & MODE_ORIGIN)) { 980 if ((u_int)py > s->rlower - s->rupper) 981 py = s->rlower; 982 else 983 py += s->rupper; 984 } 985 986 if (px != -1 && (u_int)px > screen_size_x(s) - 1) 987 px = screen_size_x(s) - 1; 988 if (py != -1 && (u_int)py > screen_size_y(s) - 1) 989 py = screen_size_y(s) - 1; 990 991 screen_write_set_cursor(ctx, px, py); 992 } 993 994 /* Reverse index (up with scroll). */ 995 void 996 screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg) 997 { 998 struct screen *s = ctx->s; 999 struct tty_ctx ttyctx; 1000 1001 screen_write_initctx(ctx, &ttyctx); 1002 ttyctx.bg = bg; 1003 1004 if (s->cy == s->rupper) 1005 grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg); 1006 else if (s->cy > 0) 1007 screen_write_set_cursor(ctx, -1, s->cy - 1); 1008 1009 screen_write_collect_flush(ctx, 0); 1010 tty_write(tty_cmd_reverseindex, &ttyctx); 1011 } 1012 1013 /* Set scroll region. */ 1014 void 1015 screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, 1016 u_int rlower) 1017 { 1018 struct screen *s = ctx->s; 1019 1020 if (rupper > screen_size_y(s) - 1) 1021 rupper = screen_size_y(s) - 1; 1022 if (rlower > screen_size_y(s) - 1) 1023 rlower = screen_size_y(s) - 1; 1024 if (rupper >= rlower) /* cannot be one line */ 1025 return; 1026 1027 screen_write_collect_flush(ctx, 0); 1028 1029 /* Cursor moves to top-left. */ 1030 screen_write_set_cursor(ctx, 0, 0); 1031 1032 s->rupper = rupper; 1033 s->rlower = rlower; 1034 } 1035 1036 /* Line feed. */ 1037 void 1038 screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) 1039 { 1040 struct screen *s = ctx->s; 1041 struct grid *gd = s->grid; 1042 struct grid_line *gl; 1043 1044 gl = grid_get_line(gd, gd->hsize + s->cy); 1045 if (wrapped) 1046 gl->flags |= GRID_LINE_WRAPPED; 1047 else 1048 gl->flags &= ~GRID_LINE_WRAPPED; 1049 1050 log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, 1051 s->rupper, s->rlower); 1052 1053 if (bg != ctx->bg) { 1054 screen_write_collect_flush(ctx, 1); 1055 ctx->bg = bg; 1056 } 1057 1058 if (s->cy == s->rlower) { 1059 grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); 1060 screen_write_collect_scroll(ctx); 1061 ctx->scrolled++; 1062 } else if (s->cy < screen_size_y(s) - 1) 1063 screen_write_set_cursor(ctx, -1, s->cy + 1); 1064 } 1065 1066 /* Scroll up. */ 1067 void 1068 screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg) 1069 { 1070 struct screen *s = ctx->s; 1071 struct grid *gd = s->grid; 1072 u_int i; 1073 1074 if (lines == 0) 1075 lines = 1; 1076 else if (lines > s->rlower - s->rupper + 1) 1077 lines = s->rlower - s->rupper + 1; 1078 1079 if (bg != ctx->bg) { 1080 screen_write_collect_flush(ctx, 1); 1081 ctx->bg = bg; 1082 } 1083 1084 for (i = 0; i < lines; i++) { 1085 grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); 1086 screen_write_collect_scroll(ctx); 1087 } 1088 ctx->scrolled += lines; 1089 } 1090 1091 /* Scroll down. */ 1092 void 1093 screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg) 1094 { 1095 struct screen *s = ctx->s; 1096 struct grid *gd = s->grid; 1097 struct tty_ctx ttyctx; 1098 u_int i; 1099 1100 screen_write_initctx(ctx, &ttyctx); 1101 ttyctx.bg = bg; 1102 1103 if (lines == 0) 1104 lines = 1; 1105 else if (lines > s->rlower - s->rupper + 1) 1106 lines = s->rlower - s->rupper + 1; 1107 1108 for (i = 0; i < lines; i++) 1109 grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg); 1110 1111 screen_write_collect_flush(ctx, 0); 1112 ttyctx.num = lines; 1113 tty_write(tty_cmd_scrolldown, &ttyctx); 1114 } 1115 1116 /* Carriage return (cursor to start of line). */ 1117 void 1118 screen_write_carriagereturn(struct screen_write_ctx *ctx) 1119 { 1120 screen_write_set_cursor(ctx, 0, -1); 1121 } 1122 1123 /* Clear to end of screen from cursor. */ 1124 void 1125 screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) 1126 { 1127 struct screen *s = ctx->s; 1128 struct grid *gd = s->grid; 1129 struct tty_ctx ttyctx; 1130 u_int sx = screen_size_x(s), sy = screen_size_y(s); 1131 1132 screen_write_initctx(ctx, &ttyctx); 1133 ttyctx.bg = bg; 1134 1135 /* Scroll into history if it is enabled and clearing entire screen. */ 1136 if (s->cx == 0 && s->cy == 0 && (gd->flags & GRID_HISTORY)) 1137 grid_view_clear_history(gd, bg); 1138 else { 1139 if (s->cx <= sx - 1) 1140 grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg); 1141 grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg); 1142 } 1143 1144 screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1)); 1145 screen_write_collect_flush(ctx, 0); 1146 tty_write(tty_cmd_clearendofscreen, &ttyctx); 1147 } 1148 1149 /* Clear to start of screen. */ 1150 void 1151 screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) 1152 { 1153 struct screen *s = ctx->s; 1154 struct tty_ctx ttyctx; 1155 u_int sx = screen_size_x(s); 1156 1157 screen_write_initctx(ctx, &ttyctx); 1158 ttyctx.bg = bg; 1159 1160 if (s->cy > 0) 1161 grid_view_clear(s->grid, 0, 0, sx, s->cy, bg); 1162 if (s->cx > sx - 1) 1163 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); 1164 else 1165 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); 1166 1167 screen_write_collect_clear(ctx, 0, s->cy); 1168 screen_write_collect_flush(ctx, 0); 1169 tty_write(tty_cmd_clearstartofscreen, &ttyctx); 1170 } 1171 1172 /* Clear entire screen. */ 1173 void 1174 screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) 1175 { 1176 struct screen *s = ctx->s; 1177 struct tty_ctx ttyctx; 1178 u_int sx = screen_size_x(s), sy = screen_size_y(s); 1179 1180 screen_write_initctx(ctx, &ttyctx); 1181 ttyctx.bg = bg; 1182 1183 /* Scroll into history if it is enabled. */ 1184 if (s->grid->flags & GRID_HISTORY) 1185 grid_view_clear_history(s->grid, bg); 1186 else 1187 grid_view_clear(s->grid, 0, 0, sx, sy, bg); 1188 1189 screen_write_collect_clear(ctx, 0, sy); 1190 tty_write(tty_cmd_clearscreen, &ttyctx); 1191 } 1192 1193 /* Clear entire history. */ 1194 void 1195 screen_write_clearhistory(struct screen_write_ctx *ctx) 1196 { 1197 grid_clear_history(ctx->s->grid); 1198 } 1199 1200 /* Clear a collected line. */ 1201 static void 1202 screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) 1203 { 1204 struct screen_write_collect_item *ci, *tmp; 1205 u_int i; 1206 size_t size; 1207 1208 for (i = y; i < y + n; i++) { 1209 if (TAILQ_EMPTY(&ctx->list[i].items)) 1210 continue; 1211 size = 0; 1212 TAILQ_FOREACH_SAFE(ci, &ctx->list[i].items, entry, tmp) { 1213 size += ci->used; 1214 TAILQ_REMOVE(&ctx->list[i].items, ci, entry); 1215 free(ci); 1216 } 1217 ctx->skipped += size; 1218 log_debug("%s: dropped %zu bytes (line %u)", __func__, size, i); 1219 } 1220 } 1221 1222 /* Scroll collected lines up. */ 1223 static void 1224 screen_write_collect_scroll(struct screen_write_ctx *ctx) 1225 { 1226 struct screen *s = ctx->s; 1227 struct screen_write_collect_line *cl; 1228 u_int y; 1229 1230 log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, 1231 s->rupper, s->rlower); 1232 1233 screen_write_collect_clear(ctx, s->rupper, 1); 1234 for (y = s->rupper; y < s->rlower; y++) { 1235 cl = &ctx->list[y + 1]; 1236 TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry); 1237 TAILQ_INIT(&cl->items); 1238 } 1239 } 1240 1241 /* Flush collected lines. */ 1242 static void 1243 screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only) 1244 { 1245 struct screen *s = ctx->s; 1246 struct screen_write_collect_item *ci, *tmp; 1247 u_int y, cx, cy, items = 0; 1248 struct tty_ctx ttyctx; 1249 size_t written = 0; 1250 1251 if (ctx->scrolled != 0) { 1252 log_debug("%s: scrolled %u (region %u-%u)", __func__, 1253 ctx->scrolled, s->rupper, s->rlower); 1254 if (ctx->scrolled > s->rlower - s->rupper + 1) 1255 ctx->scrolled = s->rlower - s->rupper + 1; 1256 1257 screen_write_initctx(ctx, &ttyctx); 1258 ttyctx.num = ctx->scrolled; 1259 ttyctx.bg = ctx->bg; 1260 tty_write(tty_cmd_scrollup, &ttyctx); 1261 } 1262 ctx->scrolled = 0; 1263 ctx->bg = 8; 1264 1265 if (scroll_only) 1266 return; 1267 1268 cx = s->cx; cy = s->cy; 1269 for (y = 0; y < screen_size_y(s); y++) { 1270 TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { 1271 screen_write_set_cursor(ctx, ci->x, y); 1272 screen_write_initctx(ctx, &ttyctx); 1273 ttyctx.cell = &ci->gc; 1274 ttyctx.wrapped = ci->wrapped; 1275 ttyctx.ptr = ci->data; 1276 ttyctx.num = ci->used; 1277 tty_write(tty_cmd_cells, &ttyctx); 1278 1279 items++; 1280 written += ci->used; 1281 1282 TAILQ_REMOVE(&ctx->list[y].items, ci, entry); 1283 free(ci); 1284 } 1285 } 1286 s->cx = cx; s->cy = cy; 1287 1288 log_debug("%s: flushed %u items (%zu bytes)", __func__, items, written); 1289 ctx->written += written; 1290 } 1291 1292 /* Finish and store collected cells. */ 1293 void 1294 screen_write_collect_end(struct screen_write_ctx *ctx) 1295 { 1296 struct screen *s = ctx->s; 1297 struct screen_write_collect_item *ci = ctx->item; 1298 struct grid_cell gc; 1299 u_int xx; 1300 1301 if (ci->used == 0) 1302 return; 1303 ci->data[ci->used] = '\0'; 1304 1305 ci->x = s->cx; 1306 TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); 1307 ctx->item = xcalloc(1, sizeof *ctx->item); 1308 1309 log_debug("%s: %u %s (at %u,%u)", __func__, ci->used, ci->data, s->cx, 1310 s->cy); 1311 1312 if (s->cx != 0) { 1313 for (xx = s->cx; xx > 0; xx--) { 1314 grid_view_get_cell(s->grid, xx, s->cy, &gc); 1315 if (~gc.flags & GRID_FLAG_PADDING) 1316 break; 1317 grid_view_set_cell(s->grid, xx, s->cy, 1318 &grid_default_cell); 1319 } 1320 if (gc.data.width > 1) { 1321 grid_view_set_cell(s->grid, xx, s->cy, 1322 &grid_default_cell); 1323 } 1324 } 1325 1326 memcpy(&gc, &ci->gc, sizeof gc); 1327 grid_view_set_cells(s->grid, s->cx, s->cy, &gc, ci->data, ci->used); 1328 screen_write_set_cursor(ctx, s->cx + ci->used, -1); 1329 1330 for (xx = s->cx; xx < screen_size_x(s); xx++) { 1331 grid_view_get_cell(s->grid, xx, s->cy, &gc); 1332 if (~gc.flags & GRID_FLAG_PADDING) 1333 break; 1334 grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell); 1335 } 1336 } 1337 1338 /* Write cell data, collecting if necessary. */ 1339 void 1340 screen_write_collect_add(struct screen_write_ctx *ctx, 1341 const struct grid_cell *gc) 1342 { 1343 struct screen *s = ctx->s; 1344 struct screen_write_collect_item *ci; 1345 u_int sx = screen_size_x(s); 1346 int collect; 1347 1348 /* 1349 * Don't need to check that the attributes and whatnot are still the 1350 * same - input_parse will end the collection when anything that isn't 1351 * a plain character is encountered. Also nothing should make it here 1352 * that isn't a single ASCII character. 1353 */ 1354 1355 collect = 1; 1356 if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f) 1357 collect = 0; 1358 else if (gc->attr & GRID_ATTR_CHARSET) 1359 collect = 0; 1360 else if (~s->mode & MODE_WRAP) 1361 collect = 0; 1362 else if (s->mode & MODE_INSERT) 1363 collect = 0; 1364 else if (s->sel != NULL) 1365 collect = 0; 1366 if (!collect) { 1367 screen_write_collect_end(ctx); 1368 screen_write_collect_flush(ctx, 0); 1369 screen_write_cell(ctx, gc); 1370 return; 1371 } 1372 ctx->cells++; 1373 1374 if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx) 1375 screen_write_collect_end(ctx); 1376 ci = ctx->item; /* may have changed */ 1377 1378 if (s->cx > sx - 1) { 1379 log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); 1380 ci->wrapped = 1; 1381 screen_write_linefeed(ctx, 1, 8); 1382 screen_write_set_cursor(ctx, 0, -1); 1383 } 1384 1385 if (ci->used == 0) 1386 memcpy(&ci->gc, gc, sizeof ci->gc); 1387 ci->data[ci->used++] = gc->data.data[0]; 1388 if (ci->used == (sizeof ci->data) - 1) 1389 screen_write_collect_end(ctx); 1390 } 1391 1392 /* Write cell data. */ 1393 void 1394 screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) 1395 { 1396 struct screen *s = ctx->s; 1397 struct grid *gd = s->grid; 1398 struct grid_line *gl; 1399 struct grid_cell_entry *gce; 1400 struct grid_cell tmp_gc, now_gc; 1401 struct tty_ctx ttyctx; 1402 u_int sx = screen_size_x(s), sy = screen_size_y(s); 1403 u_int width = gc->data.width, xx, last, cx, cy; 1404 int selected, skip = 1; 1405 1406 /* Ignore padding cells. */ 1407 if (gc->flags & GRID_FLAG_PADDING) 1408 return; 1409 ctx->cells++; 1410 1411 /* If the width is zero, combine onto the previous character. */ 1412 if (width == 0) { 1413 screen_write_collect_flush(ctx, 0); 1414 if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) { 1415 cx = s->cx; cy = s->cy; 1416 screen_write_set_cursor(ctx, xx, s->cy); 1417 screen_write_initctx(ctx, &ttyctx); 1418 ttyctx.cell = gc; 1419 tty_write(tty_cmd_cell, &ttyctx); 1420 s->cx = cx; s->cy = cy; 1421 } 1422 return; 1423 } 1424 1425 /* Flush any existing scrolling. */ 1426 screen_write_collect_flush(ctx, 1); 1427 1428 /* If this character doesn't fit, ignore it. */ 1429 if ((~s->mode & MODE_WRAP) && 1430 width > 1 && 1431 (width > sx || (s->cx != sx && s->cx > sx - width))) 1432 return; 1433 1434 /* If in insert mode, make space for the cells. */ 1435 if (s->mode & MODE_INSERT) { 1436 grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8); 1437 skip = 0; 1438 } 1439 1440 /* Check this will fit on the current line and wrap if not. */ 1441 if ((s->mode & MODE_WRAP) && s->cx > sx - width) { 1442 log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); 1443 screen_write_linefeed(ctx, 1, 8); 1444 screen_write_set_cursor(ctx, 0, -1); 1445 screen_write_collect_flush(ctx, 1); 1446 } 1447 1448 /* Sanity check cursor position. */ 1449 if (s->cx > sx - width || s->cy > sy - 1) 1450 return; 1451 screen_write_initctx(ctx, &ttyctx); 1452 1453 /* Handle overwriting of UTF-8 characters. */ 1454 gl = grid_get_line(s->grid, s->grid->hsize + s->cy); 1455 if (gl->flags & GRID_LINE_EXTENDED) { 1456 grid_view_get_cell(gd, s->cx, s->cy, &now_gc); 1457 if (screen_write_overwrite(ctx, &now_gc, width)) 1458 skip = 0; 1459 } 1460 1461 /* 1462 * If the new character is UTF-8 wide, fill in padding cells. Have 1463 * already ensured there is enough room. 1464 */ 1465 for (xx = s->cx + 1; xx < s->cx + width; xx++) { 1466 log_debug("%s: new padding at %u,%u", __func__, xx, s->cy); 1467 grid_view_set_cell(gd, xx, s->cy, &screen_write_pad_cell); 1468 skip = 0; 1469 } 1470 1471 /* If no change, do not draw. */ 1472 if (skip) { 1473 if (s->cx >= gl->cellsize) 1474 skip = grid_cells_equal(gc, &grid_default_cell); 1475 else { 1476 gce = &gl->celldata[s->cx]; 1477 if (gce->flags & GRID_FLAG_EXTENDED) 1478 skip = 0; 1479 else if (gc->flags != gce->flags) 1480 skip = 0; 1481 else if (gc->attr != gce->data.attr) 1482 skip = 0; 1483 else if (gc->fg != gce->data.fg) 1484 skip = 0; 1485 else if (gc->bg != gce->data.bg) 1486 skip = 0; 1487 else if (gc->data.width != 1) 1488 skip = 0; 1489 else if (gc->data.size != 1) 1490 skip = 0; 1491 else if (gce->data.data != gc->data.data[0]) 1492 skip = 0; 1493 } 1494 } 1495 1496 /* Update the selected flag and set the cell. */ 1497 selected = screen_check_selection(s, s->cx, s->cy); 1498 if (selected && (~gc->flags & GRID_FLAG_SELECTED)) { 1499 memcpy(&tmp_gc, gc, sizeof tmp_gc); 1500 tmp_gc.flags |= GRID_FLAG_SELECTED; 1501 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); 1502 } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) { 1503 memcpy(&tmp_gc, gc, sizeof tmp_gc); 1504 tmp_gc.flags &= ~GRID_FLAG_SELECTED; 1505 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); 1506 } else if (!skip) 1507 grid_view_set_cell(gd, s->cx, s->cy, gc); 1508 if (selected) 1509 skip = 0; 1510 1511 /* 1512 * Move the cursor. If not wrapping, stick at the last character and 1513 * replace it. 1514 */ 1515 last = !(s->mode & MODE_WRAP); 1516 if (s->cx <= sx - last - width) 1517 screen_write_set_cursor(ctx, s->cx + width, -1); 1518 else 1519 screen_write_set_cursor(ctx, sx - last, -1); 1520 1521 /* Create space for character in insert mode. */ 1522 if (s->mode & MODE_INSERT) { 1523 screen_write_collect_flush(ctx, 0); 1524 ttyctx.num = width; 1525 tty_write(tty_cmd_insertcharacter, &ttyctx); 1526 } 1527 1528 /* Write to the screen. */ 1529 if (!skip) { 1530 if (selected) { 1531 screen_select_cell(s, &tmp_gc, gc); 1532 ttyctx.cell = &tmp_gc; 1533 } else 1534 ttyctx.cell = gc; 1535 tty_write(tty_cmd_cell, &ttyctx); 1536 ctx->written++; 1537 } else 1538 ctx->skipped++; 1539 } 1540 1541 /* Combine a UTF-8 zero-width character onto the previous. */ 1542 static const struct grid_cell * 1543 screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud, 1544 u_int *xx) 1545 { 1546 struct screen *s = ctx->s; 1547 struct grid *gd = s->grid; 1548 static struct grid_cell gc; 1549 u_int n; 1550 1551 /* Can't combine if at 0. */ 1552 if (s->cx == 0) 1553 return (NULL); 1554 1555 /* Empty data is out. */ 1556 if (ud->size == 0) 1557 fatalx("UTF-8 data empty"); 1558 1559 /* Retrieve the previous cell. */ 1560 for (n = 1; n <= s->cx; n++) { 1561 grid_view_get_cell(gd, s->cx - n, s->cy, &gc); 1562 if (~gc.flags & GRID_FLAG_PADDING) 1563 break; 1564 } 1565 if (n > s->cx) 1566 return (NULL); 1567 *xx = s->cx - n; 1568 1569 /* Check there is enough space. */ 1570 if (gc.data.size + ud->size > sizeof gc.data.data) 1571 return (NULL); 1572 1573 log_debug("%s: %.*s onto %.*s at %u,%u", __func__, (int)ud->size, 1574 ud->data, (int)gc.data.size, gc.data.data, *xx, s->cy); 1575 1576 /* Append the data. */ 1577 memcpy(gc.data.data + gc.data.size, ud->data, ud->size); 1578 gc.data.size += ud->size; 1579 1580 /* Set the new cell. */ 1581 grid_view_set_cell(gd, *xx, s->cy, &gc); 1582 1583 return (&gc); 1584 } 1585 1586 /* 1587 * UTF-8 wide characters are a bit of an annoyance. They take up more than one 1588 * cell on the screen, so following cells must not be drawn by marking them as 1589 * padding. 1590 * 1591 * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8 1592 * character, it is necessary to also overwrite any other cells which covered 1593 * by the same character. 1594 */ 1595 static int 1596 screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc, 1597 u_int width) 1598 { 1599 struct screen *s = ctx->s; 1600 struct grid *gd = s->grid; 1601 struct grid_cell tmp_gc; 1602 u_int xx; 1603 int done = 0; 1604 1605 if (gc->flags & GRID_FLAG_PADDING) { 1606 /* 1607 * A padding cell, so clear any following and leading padding 1608 * cells back to the character. Don't overwrite the current 1609 * cell as that happens later anyway. 1610 */ 1611 xx = s->cx + 1; 1612 while (--xx > 0) { 1613 grid_view_get_cell(gd, xx, s->cy, &tmp_gc); 1614 if (~tmp_gc.flags & GRID_FLAG_PADDING) 1615 break; 1616 log_debug("%s: padding at %u,%u", __func__, xx, s->cy); 1617 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1618 } 1619 1620 /* Overwrite the character at the start of this padding. */ 1621 log_debug("%s: character at %u,%u", __func__, xx, s->cy); 1622 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1623 done = 1; 1624 } 1625 1626 /* 1627 * Overwrite any padding cells that belong to any UTF-8 characters 1628 * we'll be overwriting with the current character. 1629 */ 1630 if (width != 1 || 1631 gc->data.width != 1 || 1632 gc->flags & GRID_FLAG_PADDING) { 1633 xx = s->cx + width - 1; 1634 while (++xx < screen_size_x(s)) { 1635 grid_view_get_cell(gd, xx, s->cy, &tmp_gc); 1636 if (~tmp_gc.flags & GRID_FLAG_PADDING) 1637 break; 1638 log_debug("%s: overwrite at %u,%u", __func__, xx, s->cy); 1639 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 1640 done = 1; 1641 } 1642 } 1643 1644 return (done); 1645 } 1646 1647 /* Set external clipboard. */ 1648 void 1649 screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) 1650 { 1651 struct tty_ctx ttyctx; 1652 1653 screen_write_initctx(ctx, &ttyctx); 1654 ttyctx.ptr = str; 1655 ttyctx.num = len; 1656 1657 tty_write(tty_cmd_setselection, &ttyctx); 1658 } 1659 1660 /* Write unmodified string. */ 1661 void 1662 screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) 1663 { 1664 struct tty_ctx ttyctx; 1665 1666 screen_write_initctx(ctx, &ttyctx); 1667 ttyctx.ptr = str; 1668 ttyctx.num = len; 1669 1670 tty_write(tty_cmd_rawstring, &ttyctx); 1671 } 1672