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