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