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