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