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