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