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