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