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