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