1 /* $OpenBSD: grid.c,v 1.41 2014/10/08 17:35:58 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net> 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 /* 27 * Grid data. This is the basic data structure that represents what is shown on 28 * screen. 29 * 30 * A grid is a grid of cells (struct grid_cell). Lines are not allocated until 31 * cells in that line are written to. The grid is split into history and 32 * viewable data with the history starting at row (line) 0 and extending to 33 * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All 34 * functions in this file work on absolute coordinates, grid-view.c has 35 * functions which work on the screen data. 36 */ 37 38 /* Default grid cell data. */ 39 const struct grid_cell grid_default_cell = { 0, 0, 8, 8, (1 << 4) | 1, " " }; 40 41 #define grid_put_cell(gd, px, py, gc) do { \ 42 memcpy(&gd->linedata[py].celldata[px], \ 43 gc, sizeof gd->linedata[py].celldata[px]); \ 44 } while (0) 45 #define grid_put_utf8(gd, px, py, gc) do { \ 46 memcpy(&gd->linedata[py].utf8data[px], \ 47 gc, sizeof gd->linedata[py].utf8data[px]); \ 48 } while (0) 49 50 int grid_check_y(struct grid *, u_int); 51 52 #ifdef DEBUG 53 int 54 grid_check_y(struct grid *gd, u_int py) 55 { 56 if ((py) >= (gd)->hsize + (gd)->sy) 57 log_fatalx("y out of range: %u", py); 58 return (0); 59 } 60 #else 61 int 62 grid_check_y(struct grid *gd, u_int py) 63 { 64 if ((py) >= (gd)->hsize + (gd)->sy) { 65 log_debug("y out of range: %u", py); 66 return (-1); 67 } 68 return (0); 69 } 70 #endif 71 72 void grid_reflow_join(struct grid *, u_int *, struct grid_line *, u_int); 73 void grid_reflow_split(struct grid *, u_int *, struct grid_line *, u_int, 74 u_int); 75 void grid_reflow_move(struct grid *, u_int *, struct grid_line *); 76 size_t grid_string_cells_fg(const struct grid_cell *, int *); 77 size_t grid_string_cells_bg(const struct grid_cell *, int *); 78 void grid_string_cells_code(const struct grid_cell *, 79 const struct grid_cell *, char *, size_t, int); 80 81 /* Create a new grid. */ 82 struct grid * 83 grid_create(u_int sx, u_int sy, u_int hlimit) 84 { 85 struct grid *gd; 86 87 gd = xmalloc(sizeof *gd); 88 gd->sx = sx; 89 gd->sy = sy; 90 91 gd->flags = GRID_HISTORY; 92 93 gd->hsize = 0; 94 gd->hlimit = hlimit; 95 96 gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); 97 98 return (gd); 99 } 100 101 /* Destroy grid. */ 102 void 103 grid_destroy(struct grid *gd) 104 { 105 struct grid_line *gl; 106 u_int yy; 107 108 for (yy = 0; yy < gd->hsize + gd->sy; yy++) { 109 gl = &gd->linedata[yy]; 110 free(gl->celldata); 111 } 112 113 free(gd->linedata); 114 115 free(gd); 116 } 117 118 /* Compare grids. */ 119 int 120 grid_compare(struct grid *ga, struct grid *gb) 121 { 122 struct grid_line *gla, *glb; 123 struct grid_cell *gca, *gcb; 124 u_int xx, yy; 125 126 if (ga->sx != gb->sx || ga->sy != gb->sy) 127 return (1); 128 129 for (yy = 0; yy < ga->sy; yy++) { 130 gla = &ga->linedata[yy]; 131 glb = &gb->linedata[yy]; 132 if (gla->cellsize != glb->cellsize) 133 return (1); 134 for (xx = 0; xx < ga->sx; xx++) { 135 gca = &gla->celldata[xx]; 136 gcb = &glb->celldata[xx]; 137 if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0) 138 return (1); 139 } 140 } 141 142 return (0); 143 } 144 145 /* 146 * Collect lines from the history if at the limit. Free the top (oldest) 10% 147 * and shift up. 148 */ 149 void 150 grid_collect_history(struct grid *gd) 151 { 152 u_int yy; 153 154 if (gd->hsize < gd->hlimit) 155 return; 156 157 yy = gd->hlimit / 10; 158 if (yy < 1) 159 yy = 1; 160 161 grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy); 162 gd->hsize -= yy; 163 } 164 165 /* 166 * Scroll the entire visible screen, moving one line into the history. Just 167 * allocate a new line at the bottom and move the history size indicator. 168 */ 169 void 170 grid_scroll_history(struct grid *gd) 171 { 172 u_int yy; 173 174 yy = gd->hsize + gd->sy; 175 gd->linedata = xreallocarray(gd->linedata, yy + 1, 176 sizeof *gd->linedata); 177 memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]); 178 179 gd->hsize++; 180 } 181 182 /* Scroll a region up, moving the top line into the history. */ 183 void 184 grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower) 185 { 186 struct grid_line *gl_history, *gl_upper, *gl_lower; 187 u_int yy; 188 189 /* Create a space for a new line. */ 190 yy = gd->hsize + gd->sy; 191 gd->linedata = xreallocarray(gd->linedata, yy + 1, 192 sizeof *gd->linedata); 193 194 /* Move the entire screen down to free a space for this line. */ 195 gl_history = &gd->linedata[gd->hsize]; 196 memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history); 197 198 /* Adjust the region and find its start and end. */ 199 upper++; 200 gl_upper = &gd->linedata[upper]; 201 lower++; 202 gl_lower = &gd->linedata[lower]; 203 204 /* Move the line into the history. */ 205 memcpy(gl_history, gl_upper, sizeof *gl_history); 206 207 /* Then move the region up and clear the bottom line. */ 208 memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper); 209 memset(gl_lower, 0, sizeof *gl_lower); 210 211 /* Move the history offset down over the line. */ 212 gd->hsize++; 213 } 214 215 /* Expand line to fit to cell. */ 216 void 217 grid_expand_line(struct grid *gd, u_int py, u_int sx) 218 { 219 struct grid_line *gl; 220 u_int xx; 221 222 gl = &gd->linedata[py]; 223 if (sx <= gl->cellsize) 224 return; 225 226 gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); 227 for (xx = gl->cellsize; xx < sx; xx++) 228 grid_put_cell(gd, xx, py, &grid_default_cell); 229 gl->cellsize = sx; 230 } 231 232 /* Peek at grid line. */ 233 const struct grid_line * 234 grid_peek_line(struct grid *gd, u_int py) 235 { 236 if (grid_check_y(gd, py) != 0) 237 return (NULL); 238 return (&gd->linedata[py]); 239 } 240 241 /* Get cell for reading. */ 242 const struct grid_cell * 243 grid_peek_cell(struct grid *gd, u_int px, u_int py) 244 { 245 if (grid_check_y(gd, py) != 0) 246 return (&grid_default_cell); 247 248 if (px >= gd->linedata[py].cellsize) 249 return (&grid_default_cell); 250 return (&gd->linedata[py].celldata[px]); 251 } 252 253 /* Get cell at relative position (for writing). */ 254 struct grid_cell * 255 grid_get_cell(struct grid *gd, u_int px, u_int py) 256 { 257 if (grid_check_y(gd, py) != 0) 258 return (NULL); 259 260 grid_expand_line(gd, py, px + 1); 261 return (&gd->linedata[py].celldata[px]); 262 } 263 264 /* Set cell at relative position. */ 265 void 266 grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) 267 { 268 if (grid_check_y(gd, py) != 0) 269 return; 270 271 grid_expand_line(gd, py, px + 1); 272 grid_put_cell(gd, px, py, gc); 273 } 274 275 /* Clear area. */ 276 void 277 grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) 278 { 279 u_int xx, yy; 280 281 if (nx == 0 || ny == 0) 282 return; 283 284 if (px == 0 && nx == gd->sx) { 285 grid_clear_lines(gd, py, ny); 286 return; 287 } 288 289 if (grid_check_y(gd, py) != 0) 290 return; 291 if (grid_check_y(gd, py + ny - 1) != 0) 292 return; 293 294 for (yy = py; yy < py + ny; yy++) { 295 if (px >= gd->linedata[yy].cellsize) 296 continue; 297 if (px + nx >= gd->linedata[yy].cellsize) { 298 gd->linedata[yy].cellsize = px; 299 continue; 300 } 301 for (xx = px; xx < px + nx; xx++) { 302 if (xx >= gd->linedata[yy].cellsize) 303 break; 304 grid_put_cell(gd, xx, yy, &grid_default_cell); 305 } 306 } 307 } 308 309 /* Clear lines. This just frees and truncates the lines. */ 310 void 311 grid_clear_lines(struct grid *gd, u_int py, u_int ny) 312 { 313 struct grid_line *gl; 314 u_int yy; 315 316 if (ny == 0) 317 return; 318 319 if (grid_check_y(gd, py) != 0) 320 return; 321 if (grid_check_y(gd, py + ny - 1) != 0) 322 return; 323 324 for (yy = py; yy < py + ny; yy++) { 325 gl = &gd->linedata[yy]; 326 free(gl->celldata); 327 memset(gl, 0, sizeof *gl); 328 } 329 } 330 331 /* Move a group of lines. */ 332 void 333 grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny) 334 { 335 u_int yy; 336 337 if (ny == 0 || py == dy) 338 return; 339 340 if (grid_check_y(gd, py) != 0) 341 return; 342 if (grid_check_y(gd, py + ny - 1) != 0) 343 return; 344 if (grid_check_y(gd, dy) != 0) 345 return; 346 if (grid_check_y(gd, dy + ny - 1) != 0) 347 return; 348 349 /* Free any lines which are being replaced. */ 350 for (yy = dy; yy < dy + ny; yy++) { 351 if (yy >= py && yy < py + ny) 352 continue; 353 grid_clear_lines(gd, yy, 1); 354 } 355 356 memmove( 357 &gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata)); 358 359 /* Wipe any lines that have been moved (without freeing them). */ 360 for (yy = py; yy < py + ny; yy++) { 361 if (yy >= dy && yy < dy + ny) 362 continue; 363 memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]); 364 } 365 } 366 367 /* Move a group of cells. */ 368 void 369 grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) 370 { 371 struct grid_line *gl; 372 u_int xx; 373 374 if (nx == 0 || px == dx) 375 return; 376 377 if (grid_check_y(gd, py) != 0) 378 return; 379 gl = &gd->linedata[py]; 380 381 grid_expand_line(gd, py, px + nx); 382 grid_expand_line(gd, py, dx + nx); 383 memmove( 384 &gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata); 385 386 /* Wipe any cells that have been moved. */ 387 for (xx = px; xx < px + nx; xx++) { 388 if (xx >= dx && xx < dx + nx) 389 continue; 390 grid_put_cell(gd, xx, py, &grid_default_cell); 391 } 392 } 393 394 /* Get ANSI foreground sequence. */ 395 size_t 396 grid_string_cells_fg(const struct grid_cell *gc, int *values) 397 { 398 size_t n; 399 400 n = 0; 401 if (gc->flags & GRID_FLAG_FG256) { 402 values[n++] = 38; 403 values[n++] = 5; 404 values[n++] = gc->fg; 405 } else { 406 switch (gc->fg) { 407 case 0: 408 case 1: 409 case 2: 410 case 3: 411 case 4: 412 case 5: 413 case 6: 414 case 7: 415 values[n++] = gc->fg + 30; 416 break; 417 case 8: 418 values[n++] = 39; 419 break; 420 case 90: 421 case 91: 422 case 92: 423 case 93: 424 case 94: 425 case 95: 426 case 96: 427 case 97: 428 values[n++] = gc->fg; 429 break; 430 } 431 } 432 return (n); 433 } 434 435 /* Get ANSI background sequence. */ 436 size_t 437 grid_string_cells_bg(const struct grid_cell *gc, int *values) 438 { 439 size_t n; 440 441 n = 0; 442 if (gc->flags & GRID_FLAG_BG256) { 443 values[n++] = 48; 444 values[n++] = 5; 445 values[n++] = gc->bg; 446 } else { 447 switch (gc->bg) { 448 case 0: 449 case 1: 450 case 2: 451 case 3: 452 case 4: 453 case 5: 454 case 6: 455 case 7: 456 values[n++] = gc->bg + 40; 457 break; 458 case 8: 459 values[n++] = 49; 460 break; 461 case 100: 462 case 101: 463 case 102: 464 case 103: 465 case 104: 466 case 105: 467 case 106: 468 case 107: 469 values[n++] = gc->bg - 10; 470 break; 471 } 472 } 473 return (n); 474 } 475 476 /* 477 * Returns ANSI code to set particular attributes (colour, bold and so on) 478 * given a current state. The output buffer must be able to hold at least 57 479 * bytes. 480 */ 481 void 482 grid_string_cells_code(const struct grid_cell *lastgc, 483 const struct grid_cell *gc, char *buf, size_t len, int escape_c0) 484 { 485 int oldc[16], newc[16], s[32]; 486 size_t noldc, nnewc, n, i; 487 u_int attr = gc->attr; 488 u_int lastattr = lastgc->attr; 489 char tmp[64]; 490 491 struct { 492 u_int mask; 493 u_int code; 494 } attrs[] = { 495 { GRID_ATTR_BRIGHT, 1 }, 496 { GRID_ATTR_DIM, 2 }, 497 { GRID_ATTR_ITALICS, 3 }, 498 { GRID_ATTR_UNDERSCORE, 4 }, 499 { GRID_ATTR_BLINK, 5 }, 500 { GRID_ATTR_REVERSE, 7 }, 501 { GRID_ATTR_HIDDEN, 8 } 502 }; 503 n = 0; 504 505 /* If any attribute is removed, begin with 0. */ 506 for (i = 0; i < nitems(attrs); i++) { 507 if (!(attr & attrs[i].mask) && (lastattr & attrs[i].mask)) { 508 s[n++] = 0; 509 lastattr &= GRID_ATTR_CHARSET; 510 break; 511 } 512 } 513 /* For each attribute that is newly set, add its code. */ 514 for (i = 0; i < nitems(attrs); i++) { 515 if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask)) 516 s[n++] = attrs[i].code; 517 } 518 519 /* If the foreground colour changed, append its parameters. */ 520 nnewc = grid_string_cells_fg(gc, newc); 521 noldc = grid_string_cells_fg(lastgc, oldc); 522 if (nnewc != noldc || memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0) { 523 for (i = 0; i < nnewc; i++) 524 s[n++] = newc[i]; 525 } 526 527 /* If the background colour changed, append its parameters. */ 528 nnewc = grid_string_cells_bg(gc, newc); 529 noldc = grid_string_cells_bg(lastgc, oldc); 530 if (nnewc != noldc || memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0) { 531 for (i = 0; i < nnewc; i++) 532 s[n++] = newc[i]; 533 } 534 535 /* If there are any parameters, append an SGR code. */ 536 *buf = '\0'; 537 if (n > 0) { 538 if (escape_c0) 539 strlcat(buf, "\\033[", len); 540 else 541 strlcat(buf, "\033[", len); 542 for (i = 0; i < n; i++) { 543 if (i + 1 < n) 544 xsnprintf(tmp, sizeof tmp, "%d;", s[i]); 545 else 546 xsnprintf(tmp, sizeof tmp, "%d", s[i]); 547 strlcat(buf, tmp, len); 548 } 549 strlcat(buf, "m", len); 550 } 551 552 /* Append shift in/shift out if needed. */ 553 if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) { 554 if (escape_c0) 555 strlcat(buf, "\\016", len); /* SO */ 556 else 557 strlcat(buf, "\016", len); /* SO */ 558 } 559 if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) { 560 if (escape_c0) 561 strlcat(buf, "\\017", len); /* SI */ 562 else 563 strlcat(buf, "\017", len); /* SI */ 564 } 565 } 566 567 /* Convert cells into a string. */ 568 char * 569 grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, 570 struct grid_cell **lastgc, int with_codes, int escape_c0, int trim) 571 { 572 const struct grid_cell *gc; 573 static struct grid_cell lastgc1; 574 struct utf8_data ud; 575 const char *data; 576 char *buf, code[128]; 577 size_t len, off, size, codelen; 578 u_int xx; 579 const struct grid_line *gl; 580 581 if (lastgc != NULL && *lastgc == NULL) { 582 memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); 583 *lastgc = &lastgc1; 584 } 585 586 len = 128; 587 buf = xmalloc(len); 588 off = 0; 589 590 gl = grid_peek_line(gd, py); 591 for (xx = px; xx < px + nx; xx++) { 592 if (gl == NULL || xx >= gl->cellsize) 593 break; 594 gc = &gl->celldata[xx]; 595 if (gc->flags & GRID_FLAG_PADDING) 596 continue; 597 grid_cell_get(gc, &ud); 598 599 if (with_codes) { 600 grid_string_cells_code(*lastgc, gc, code, sizeof code, 601 escape_c0); 602 codelen = strlen(code); 603 memcpy(*lastgc, gc, sizeof *gc); 604 } else 605 codelen = 0; 606 607 data = ud.data; 608 size = ud.size; 609 if (escape_c0 && size == 1 && *data == '\\') { 610 data = "\\\\"; 611 size = 2; 612 } 613 614 while (len < off + size + codelen + 1) { 615 buf = xreallocarray(buf, 2, len); 616 len *= 2; 617 } 618 619 if (codelen != 0) { 620 memcpy(buf + off, code, codelen); 621 off += codelen; 622 } 623 memcpy(buf + off, data, size); 624 off += size; 625 } 626 627 if (trim) { 628 while (off > 0 && buf[off - 1] == ' ') 629 off--; 630 } 631 buf[off] = '\0'; 632 633 return (buf); 634 } 635 636 /* 637 * Duplicate a set of lines between two grids. If there aren't enough lines in 638 * either source or destination, the number of lines is limited to the number 639 * available. 640 */ 641 void 642 grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, 643 u_int ny) 644 { 645 struct grid_line *dstl, *srcl; 646 u_int yy; 647 648 if (dy + ny > dst->hsize + dst->sy) 649 ny = dst->hsize + dst->sy - dy; 650 if (sy + ny > src->hsize + src->sy) 651 ny = src->hsize + src->sy - sy; 652 grid_clear_lines(dst, dy, ny); 653 654 for (yy = 0; yy < ny; yy++) { 655 srcl = &src->linedata[sy]; 656 dstl = &dst->linedata[dy]; 657 658 memcpy(dstl, srcl, sizeof *dstl); 659 if (srcl->cellsize != 0) { 660 dstl->celldata = xcalloc( 661 srcl->cellsize, sizeof *dstl->celldata); 662 memcpy(dstl->celldata, srcl->celldata, 663 srcl->cellsize * sizeof *dstl->celldata); 664 } 665 666 sy++; 667 dy++; 668 } 669 } 670 671 /* Join line data. */ 672 void 673 grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl, 674 u_int new_x) 675 { 676 struct grid_line *dst_gl = &dst->linedata[(*py) - 1]; 677 u_int left, to_copy, ox, nx; 678 679 /* How much is left on the old line? */ 680 left = new_x - dst_gl->cellsize; 681 682 /* Work out how much to append. */ 683 to_copy = src_gl->cellsize; 684 if (to_copy > left) 685 to_copy = left; 686 ox = dst_gl->cellsize; 687 nx = ox + to_copy; 688 689 /* Resize the destination line. */ 690 dst_gl->celldata = xreallocarray(dst_gl->celldata, nx, 691 sizeof *dst_gl->celldata); 692 dst_gl->cellsize = nx; 693 694 /* Append as much as possible. */ 695 memcpy(&dst_gl->celldata[ox], &src_gl->celldata[0], 696 to_copy * sizeof src_gl->celldata[0]); 697 698 /* If there is any left in the source, split it. */ 699 if (src_gl->cellsize > to_copy) { 700 dst_gl->flags |= GRID_LINE_WRAPPED; 701 702 src_gl->cellsize -= to_copy; 703 grid_reflow_split(dst, py, src_gl, new_x, to_copy); 704 } 705 } 706 707 /* Split line data. */ 708 void 709 grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl, 710 u_int new_x, u_int offset) 711 { 712 struct grid_line *dst_gl = NULL; 713 u_int to_copy; 714 715 /* Loop and copy sections of the source line. */ 716 while (src_gl->cellsize > 0) { 717 /* Create new line. */ 718 if (*py >= dst->hsize + dst->sy) 719 grid_scroll_history(dst); 720 dst_gl = &dst->linedata[*py]; 721 (*py)++; 722 723 /* How much should we copy? */ 724 to_copy = new_x; 725 if (to_copy > src_gl->cellsize) 726 to_copy = src_gl->cellsize; 727 728 /* Expand destination line. */ 729 dst_gl->celldata = xreallocarray(NULL, to_copy, 730 sizeof *dst_gl->celldata); 731 dst_gl->cellsize = to_copy; 732 dst_gl->flags |= GRID_LINE_WRAPPED; 733 734 /* Copy the data. */ 735 memcpy(&dst_gl->celldata[0], &src_gl->celldata[offset], 736 to_copy * sizeof dst_gl->celldata[0]); 737 738 /* Move offset and reduce old line size. */ 739 offset += to_copy; 740 src_gl->cellsize -= to_copy; 741 } 742 743 /* Last line is not wrapped. */ 744 if (dst_gl != NULL) 745 dst_gl->flags &= ~GRID_LINE_WRAPPED; 746 } 747 748 /* Move line data. */ 749 void 750 grid_reflow_move(struct grid *dst, u_int *py, struct grid_line *src_gl) 751 { 752 struct grid_line *dst_gl; 753 754 /* Create new line. */ 755 if (*py >= dst->hsize + dst->sy) 756 grid_scroll_history(dst); 757 dst_gl = &dst->linedata[*py]; 758 (*py)++; 759 760 /* Copy the old line. */ 761 memcpy(dst_gl, src_gl, sizeof *dst_gl); 762 dst_gl->flags &= ~GRID_LINE_WRAPPED; 763 764 /* Clear old line. */ 765 src_gl->celldata = NULL; 766 } 767 768 /* 769 * Reflow lines from src grid into dst grid of width new_x. Returns number of 770 * lines fewer in the visible area. The source grid is destroyed. 771 */ 772 u_int 773 grid_reflow(struct grid *dst, struct grid *src, u_int new_x) 774 { 775 u_int py, sy, line; 776 int previous_wrapped; 777 struct grid_line *src_gl; 778 779 py = 0; 780 sy = src->sy; 781 782 previous_wrapped = 0; 783 for (line = 0; line < sy + src->hsize; line++) { 784 src_gl = src->linedata + line; 785 if (!previous_wrapped) { 786 /* Wasn't wrapped. If smaller, move to destination. */ 787 if (src_gl->cellsize <= new_x) 788 grid_reflow_move(dst, &py, src_gl); 789 else 790 grid_reflow_split(dst, &py, src_gl, new_x, 0); 791 } else { 792 /* Previous was wrapped. Try to join. */ 793 grid_reflow_join(dst, &py, src_gl, new_x); 794 } 795 previous_wrapped = src_gl->flags & GRID_LINE_WRAPPED; 796 } 797 798 grid_destroy(src); 799 800 if (py > sy) 801 return (0); 802 return (sy - py); 803 } 804