1 /* $OpenBSD: grid.c,v 1.80 2018/02/16 09:51:41 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_empty_line(struct grid *, u_int, u_int); 47 48 /* Store cell in entry. */ 49 static void 50 grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc, 51 u_char c) 52 { 53 gce->flags = gc->flags; 54 55 gce->data.fg = gc->fg & 0xff; 56 if (gc->fg & COLOUR_FLAG_256) 57 gce->flags |= GRID_FLAG_FG256; 58 59 gce->data.bg = gc->bg & 0xff; 60 if (gc->bg & COLOUR_FLAG_256) 61 gce->flags |= GRID_FLAG_BG256; 62 63 gce->data.attr = gc->attr; 64 gce->data.data = c; 65 } 66 67 /* Check if a cell should be extended. */ 68 static int 69 grid_need_extended_cell(const struct grid_cell_entry *gce, 70 const struct grid_cell *gc) 71 { 72 if (gce->flags & GRID_FLAG_EXTENDED) 73 return (1); 74 if (gc->attr > 0xff) 75 return (1); 76 if (gc->data.size != 1 || gc->data.width != 1) 77 return (1); 78 if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB)) 79 return (1); 80 return (0); 81 } 82 83 /* Free up unused extended cells. */ 84 static void 85 grid_compact_line(struct grid_line *gl) 86 { 87 int new_extdsize = 0; 88 struct grid_cell *new_extddata; 89 struct grid_cell_entry *gce; 90 struct grid_cell *gc; 91 u_int px, idx; 92 93 if (gl->extdsize == 0) 94 return; 95 96 for (px = 0; px < gl->cellsize; px++) { 97 gce = &gl->celldata[px]; 98 if (gce->flags & GRID_FLAG_EXTENDED) 99 new_extdsize++; 100 } 101 102 if (new_extdsize == 0) { 103 free(gl->extddata); 104 gl->extddata = NULL; 105 gl->extdsize = 0; 106 return; 107 } 108 new_extddata = xreallocarray(NULL, new_extdsize, sizeof *gl->extddata); 109 110 idx = 0; 111 for (px = 0; px < gl->cellsize; px++) { 112 gce = &gl->celldata[px]; 113 if (gce->flags & GRID_FLAG_EXTENDED) { 114 gc = &gl->extddata[gce->offset]; 115 memcpy(&new_extddata[idx], gc, sizeof *gc); 116 gce->offset = idx++; 117 } 118 } 119 120 free(gl->extddata); 121 gl->extddata = new_extddata; 122 gl->extdsize = new_extdsize; 123 } 124 125 /* Set cell as extended. */ 126 static struct grid_cell * 127 grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, 128 const struct grid_cell *gc) 129 { 130 struct grid_cell *gcp; 131 132 gl->flags |= GRID_LINE_EXTENDED; 133 134 if (~gce->flags & GRID_FLAG_EXTENDED) { 135 gl->extddata = xreallocarray(gl->extddata, gl->extdsize + 1, 136 sizeof *gl->extddata); 137 gce->offset = gl->extdsize++; 138 gce->flags = gc->flags | GRID_FLAG_EXTENDED; 139 } 140 if (gce->offset >= gl->extdsize) 141 fatalx("offset too big"); 142 143 gcp = &gl->extddata[gce->offset]; 144 memcpy(gcp, gc, sizeof *gcp); 145 return (gcp); 146 } 147 148 /* Copy default into a cell. */ 149 static void 150 grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg) 151 { 152 struct grid_line *gl = &gd->linedata[py]; 153 struct grid_cell_entry *gce = &gl->celldata[px]; 154 struct grid_cell *gc; 155 156 memcpy(gce, &grid_default_entry, sizeof *gce); 157 if (bg & COLOUR_FLAG_RGB) { 158 gc = grid_extended_cell(gl, gce, &grid_default_cell); 159 gc->bg = bg; 160 } else { 161 if (bg & COLOUR_FLAG_256) 162 gce->flags |= GRID_FLAG_BG256; 163 gce->data.bg = bg; 164 } 165 } 166 167 /* Check grid y position. */ 168 static int 169 grid_check_y(struct grid *gd, u_int py) 170 { 171 if (py >= gd->hsize + gd->sy) { 172 log_debug("y out of range: %u", py); 173 return (-1); 174 } 175 return (0); 176 } 177 178 /* Compare grid cells. Return 1 if equal, 0 if not. */ 179 int 180 grid_cells_equal(const struct grid_cell *gca, const struct grid_cell *gcb) 181 { 182 if (gca->fg != gcb->fg || gca->bg != gcb->bg) 183 return (0); 184 if (gca->attr != gcb->attr || gca->flags != gcb->flags) 185 return (0); 186 if (gca->data.width != gcb->data.width) 187 return (0); 188 if (gca->data.size != gcb->data.size) 189 return (0); 190 return (memcmp(gca->data.data, gcb->data.data, gca->data.size) == 0); 191 } 192 193 /* Free one line. */ 194 static void 195 grid_free_line(struct grid *gd, u_int py) 196 { 197 free(gd->linedata[py].celldata); 198 gd->linedata[py].celldata = NULL; 199 free(gd->linedata[py].extddata); 200 gd->linedata[py].extddata = NULL; 201 } 202 203 /* Free several lines. */ 204 static void 205 grid_free_lines(struct grid *gd, u_int py, u_int ny) 206 { 207 u_int yy; 208 209 for (yy = py; yy < py + ny; yy++) 210 grid_free_line(gd, yy); 211 } 212 213 /* Create a new grid. */ 214 struct grid * 215 grid_create(u_int sx, u_int sy, u_int hlimit) 216 { 217 struct grid *gd; 218 219 gd = xmalloc(sizeof *gd); 220 gd->sx = sx; 221 gd->sy = sy; 222 223 gd->flags = GRID_HISTORY; 224 225 gd->hscrolled = 0; 226 gd->hsize = 0; 227 gd->hlimit = hlimit; 228 229 if (gd->sy != 0) 230 gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); 231 else 232 gd->linedata = NULL; 233 234 return (gd); 235 } 236 237 /* Destroy grid. */ 238 void 239 grid_destroy(struct grid *gd) 240 { 241 grid_free_lines(gd, 0, gd->hsize + gd->sy); 242 243 free(gd->linedata); 244 245 free(gd); 246 } 247 248 /* Compare grids. */ 249 int 250 grid_compare(struct grid *ga, struct grid *gb) 251 { 252 struct grid_line *gla, *glb; 253 struct grid_cell gca, gcb; 254 u_int xx, yy; 255 256 if (ga->sx != gb->sx || ga->sy != gb->sy) 257 return (1); 258 259 for (yy = 0; yy < ga->sy; yy++) { 260 gla = &ga->linedata[yy]; 261 glb = &gb->linedata[yy]; 262 if (gla->cellsize != glb->cellsize) 263 return (1); 264 for (xx = 0; xx < gla->cellsize; xx++) { 265 grid_get_cell(ga, xx, yy, &gca); 266 grid_get_cell(gb, xx, yy, &gcb); 267 if (!grid_cells_equal(&gca, &gcb)) 268 return (1); 269 } 270 } 271 272 return (0); 273 } 274 275 /* 276 * Collect lines from the history if at the limit. Free the top (oldest) 10% 277 * and shift up. 278 */ 279 void 280 grid_collect_history(struct grid *gd) 281 { 282 u_int ny; 283 284 if (gd->hsize == 0 || gd->hsize < gd->hlimit) 285 return; 286 287 ny = gd->hlimit / 10; 288 if (ny < 1) 289 ny = 1; 290 if (ny > gd->hsize) 291 ny = gd->hsize; 292 293 /* 294 * Free the lines from 0 to ny then move the remaining lines over 295 * them. 296 */ 297 grid_free_lines(gd, 0, ny); 298 memmove(&gd->linedata[0], &gd->linedata[ny], 299 (gd->hsize + gd->sy - ny) * (sizeof *gd->linedata)); 300 301 gd->hsize -= ny; 302 if (gd->hscrolled > gd->hsize) 303 gd->hscrolled = gd->hsize; 304 } 305 306 /* 307 * Scroll the entire visible screen, moving one line into the history. Just 308 * allocate a new line at the bottom and move the history size indicator. 309 */ 310 void 311 grid_scroll_history(struct grid *gd, u_int bg) 312 { 313 u_int yy; 314 315 yy = gd->hsize + gd->sy; 316 gd->linedata = xreallocarray(gd->linedata, yy + 1, 317 sizeof *gd->linedata); 318 grid_empty_line(gd, yy, bg); 319 320 gd->hscrolled++; 321 grid_compact_line(&gd->linedata[gd->hsize]); 322 gd->hsize++; 323 } 324 325 /* Clear the history. */ 326 void 327 grid_clear_history(struct grid *gd) 328 { 329 grid_free_lines(gd, 0, gd->hsize); 330 memmove(&gd->linedata[0], &gd->linedata[gd->hsize], 331 gd->sy * (sizeof *gd->linedata)); 332 333 gd->hscrolled = 0; 334 gd->hsize = 0; 335 336 gd->linedata = xreallocarray(gd->linedata, gd->sy, 337 sizeof *gd->linedata); 338 } 339 340 /* Scroll a region up, moving the top line into the history. */ 341 void 342 grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg) 343 { 344 struct grid_line *gl_history, *gl_upper; 345 u_int yy; 346 347 /* Create a space for a new line. */ 348 yy = gd->hsize + gd->sy; 349 gd->linedata = xreallocarray(gd->linedata, yy + 1, 350 sizeof *gd->linedata); 351 352 /* Move the entire screen down to free a space for this line. */ 353 gl_history = &gd->linedata[gd->hsize]; 354 memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history); 355 356 /* Adjust the region and find its start and end. */ 357 upper++; 358 gl_upper = &gd->linedata[upper]; 359 lower++; 360 361 /* Move the line into the history. */ 362 memcpy(gl_history, gl_upper, sizeof *gl_history); 363 364 /* Then move the region up and clear the bottom line. */ 365 memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper); 366 grid_empty_line(gd, lower, bg); 367 368 /* Move the history offset down over the line. */ 369 gd->hscrolled++; 370 gd->hsize++; 371 } 372 373 /* Expand line to fit to cell. */ 374 static void 375 grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg) 376 { 377 struct grid_line *gl; 378 u_int xx; 379 380 gl = &gd->linedata[py]; 381 if (sx <= gl->cellsize) 382 return; 383 384 if (sx < gd->sx / 4) 385 sx = gd->sx / 4; 386 else if (sx < gd->sx / 2) 387 sx = gd->sx / 2; 388 else 389 sx = gd->sx; 390 391 gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); 392 for (xx = gl->cellsize; xx < sx; xx++) 393 grid_clear_cell(gd, xx, py, bg); 394 gl->cellsize = sx; 395 } 396 397 /* Empty a line and set background colour if needed. */ 398 static void 399 grid_empty_line(struct grid *gd, u_int py, u_int bg) 400 { 401 memset(&gd->linedata[py], 0, sizeof gd->linedata[py]); 402 if (bg != 8) 403 grid_expand_line(gd, py, gd->sx, bg); 404 } 405 406 /* Peek at grid line. */ 407 const struct grid_line * 408 grid_peek_line(struct grid *gd, u_int py) 409 { 410 if (grid_check_y(gd, py) != 0) 411 return (NULL); 412 return (&gd->linedata[py]); 413 } 414 415 /* Get cell from line. */ 416 static void 417 grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) 418 { 419 struct grid_cell_entry *gce = &gl->celldata[px]; 420 421 if (gce->flags & GRID_FLAG_EXTENDED) { 422 if (gce->offset >= gl->extdsize) 423 memcpy(gc, &grid_default_cell, sizeof *gc); 424 else 425 memcpy(gc, &gl->extddata[gce->offset], sizeof *gc); 426 return; 427 } 428 429 gc->flags = gce->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); 430 gc->attr = gce->data.attr; 431 gc->fg = gce->data.fg; 432 if (gce->flags & GRID_FLAG_FG256) 433 gc->fg |= COLOUR_FLAG_256; 434 gc->bg = gce->data.bg; 435 if (gce->flags & GRID_FLAG_BG256) 436 gc->bg |= COLOUR_FLAG_256; 437 utf8_set(&gc->data, gce->data.data); 438 } 439 440 /* Get cell for reading. */ 441 void 442 grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) 443 { 444 if (grid_check_y(gd, py) != 0 || px >= gd->linedata[py].cellsize) { 445 memcpy(gc, &grid_default_cell, sizeof *gc); 446 return; 447 } 448 return (grid_get_cell1(&gd->linedata[py], px, gc)); 449 } 450 451 /* Set cell at relative position. */ 452 void 453 grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) 454 { 455 struct grid_line *gl; 456 struct grid_cell_entry *gce; 457 458 if (grid_check_y(gd, py) != 0) 459 return; 460 461 grid_expand_line(gd, py, px + 1, 8); 462 463 gl = &gd->linedata[py]; 464 if (px + 1 > gl->cellused) 465 gl->cellused = px + 1; 466 467 gce = &gl->celldata[px]; 468 if (grid_need_extended_cell(gce, gc)) 469 grid_extended_cell(gl, gce, gc); 470 else 471 grid_store_cell(gce, gc, gc->data.data[0]); 472 } 473 474 /* Set cells at relative position. */ 475 void 476 grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc, 477 const char *s, size_t slen) 478 { 479 struct grid_line *gl; 480 struct grid_cell_entry *gce; 481 struct grid_cell *gcp; 482 u_int i; 483 484 if (grid_check_y(gd, py) != 0) 485 return; 486 487 grid_expand_line(gd, py, px + slen, 8); 488 489 gl = &gd->linedata[py]; 490 if (px + slen > gl->cellused) 491 gl->cellused = px + slen; 492 493 for (i = 0; i < slen; i++) { 494 gce = &gl->celldata[px + i]; 495 if (grid_need_extended_cell(gce, gc)) { 496 gcp = grid_extended_cell(gl, gce, gc); 497 utf8_set(&gcp->data, s[i]); 498 } else 499 grid_store_cell(gce, gc, s[i]); 500 } 501 } 502 503 /* Clear area. */ 504 void 505 grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) 506 { 507 u_int xx, yy; 508 509 if (nx == 0 || ny == 0) 510 return; 511 512 if (px == 0 && nx == gd->sx) { 513 grid_clear_lines(gd, py, ny, bg); 514 return; 515 } 516 517 if (grid_check_y(gd, py) != 0) 518 return; 519 if (grid_check_y(gd, py + ny - 1) != 0) 520 return; 521 522 for (yy = py; yy < py + ny; yy++) { 523 if (px + nx >= gd->sx && px < gd->linedata[yy].cellused) 524 gd->linedata[yy].cellused = px; 525 if (px > gd->linedata[yy].cellsize && bg == 8) 526 continue; 527 if (px + nx >= gd->linedata[yy].cellsize && bg == 8) { 528 gd->linedata[yy].cellsize = px; 529 continue; 530 } 531 grid_expand_line(gd, yy, px + nx, 8); /* default bg first */ 532 for (xx = px; xx < px + nx; xx++) 533 grid_clear_cell(gd, xx, yy, bg); 534 } 535 } 536 537 /* Clear lines. This just frees and truncates the lines. */ 538 void 539 grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg) 540 { 541 u_int yy; 542 543 if (ny == 0) 544 return; 545 546 if (grid_check_y(gd, py) != 0) 547 return; 548 if (grid_check_y(gd, py + ny - 1) != 0) 549 return; 550 551 for (yy = py; yy < py + ny; yy++) { 552 grid_free_line(gd, yy); 553 grid_empty_line(gd, yy, bg); 554 } 555 } 556 557 /* Move a group of lines. */ 558 void 559 grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg) 560 { 561 u_int yy; 562 563 if (ny == 0 || py == dy) 564 return; 565 566 if (grid_check_y(gd, py) != 0) 567 return; 568 if (grid_check_y(gd, py + ny - 1) != 0) 569 return; 570 if (grid_check_y(gd, dy) != 0) 571 return; 572 if (grid_check_y(gd, dy + ny - 1) != 0) 573 return; 574 575 /* Free any lines which are being replaced. */ 576 for (yy = dy; yy < dy + ny; yy++) { 577 if (yy >= py && yy < py + ny) 578 continue; 579 grid_free_line(gd, yy); 580 } 581 582 memmove(&gd->linedata[dy], &gd->linedata[py], 583 ny * (sizeof *gd->linedata)); 584 585 /* 586 * Wipe any lines that have been moved (without freeing them - they are 587 * still present). 588 */ 589 for (yy = py; yy < py + ny; yy++) { 590 if (yy < dy || yy >= dy + ny) 591 grid_empty_line(gd, yy, bg); 592 } 593 } 594 595 /* Move a group of cells. */ 596 void 597 grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx, 598 u_int bg) 599 { 600 struct grid_line *gl; 601 u_int xx; 602 603 if (nx == 0 || px == dx) 604 return; 605 606 if (grid_check_y(gd, py) != 0) 607 return; 608 gl = &gd->linedata[py]; 609 610 grid_expand_line(gd, py, px + nx, 8); 611 grid_expand_line(gd, py, dx + nx, 8); 612 memmove(&gl->celldata[dx], &gl->celldata[px], 613 nx * sizeof *gl->celldata); 614 if (dx + nx > gl->cellused) 615 gl->cellused = dx + nx; 616 617 /* Wipe any cells that have been moved. */ 618 for (xx = px; xx < px + nx; xx++) { 619 if (xx >= dx && xx < dx + nx) 620 continue; 621 grid_clear_cell(gd, xx, py, bg); 622 } 623 } 624 625 /* Get ANSI foreground sequence. */ 626 static size_t 627 grid_string_cells_fg(const struct grid_cell *gc, int *values) 628 { 629 size_t n; 630 u_char r, g, b; 631 632 n = 0; 633 if (gc->fg & COLOUR_FLAG_256) { 634 values[n++] = 38; 635 values[n++] = 5; 636 values[n++] = gc->fg & 0xff; 637 } else if (gc->fg & COLOUR_FLAG_RGB) { 638 values[n++] = 38; 639 values[n++] = 2; 640 colour_split_rgb(gc->fg, &r, &g, &b); 641 values[n++] = r; 642 values[n++] = g; 643 values[n++] = b; 644 } else { 645 switch (gc->fg) { 646 case 0: 647 case 1: 648 case 2: 649 case 3: 650 case 4: 651 case 5: 652 case 6: 653 case 7: 654 values[n++] = gc->fg + 30; 655 break; 656 case 8: 657 values[n++] = 39; 658 break; 659 case 90: 660 case 91: 661 case 92: 662 case 93: 663 case 94: 664 case 95: 665 case 96: 666 case 97: 667 values[n++] = gc->fg; 668 break; 669 } 670 } 671 return (n); 672 } 673 674 /* Get ANSI background sequence. */ 675 static size_t 676 grid_string_cells_bg(const struct grid_cell *gc, int *values) 677 { 678 size_t n; 679 u_char r, g, b; 680 681 n = 0; 682 if (gc->bg & COLOUR_FLAG_256) { 683 values[n++] = 48; 684 values[n++] = 5; 685 values[n++] = gc->bg & 0xff; 686 } else if (gc->bg & COLOUR_FLAG_RGB) { 687 values[n++] = 48; 688 values[n++] = 2; 689 colour_split_rgb(gc->bg, &r, &g, &b); 690 values[n++] = r; 691 values[n++] = g; 692 values[n++] = b; 693 } else { 694 switch (gc->bg) { 695 case 0: 696 case 1: 697 case 2: 698 case 3: 699 case 4: 700 case 5: 701 case 6: 702 case 7: 703 values[n++] = gc->bg + 40; 704 break; 705 case 8: 706 values[n++] = 49; 707 break; 708 case 100: 709 case 101: 710 case 102: 711 case 103: 712 case 104: 713 case 105: 714 case 106: 715 case 107: 716 values[n++] = gc->bg - 10; 717 break; 718 } 719 } 720 return (n); 721 } 722 723 /* 724 * Returns ANSI code to set particular attributes (colour, bold and so on) 725 * given a current state. 726 */ 727 static void 728 grid_string_cells_code(const struct grid_cell *lastgc, 729 const struct grid_cell *gc, char *buf, size_t len, int escape_c0) 730 { 731 int oldc[64], newc[64], s[128]; 732 size_t noldc, nnewc, n, i; 733 u_int attr = gc->attr, lastattr = lastgc->attr; 734 char tmp[64]; 735 736 struct { 737 u_int mask; 738 u_int code; 739 } attrs[] = { 740 { GRID_ATTR_BRIGHT, 1 }, 741 { GRID_ATTR_DIM, 2 }, 742 { GRID_ATTR_ITALICS, 3 }, 743 { GRID_ATTR_UNDERSCORE, 4 }, 744 { GRID_ATTR_BLINK, 5 }, 745 { GRID_ATTR_REVERSE, 7 }, 746 { GRID_ATTR_HIDDEN, 8 }, 747 { GRID_ATTR_STRIKETHROUGH, 9 } 748 }; 749 n = 0; 750 751 /* If any attribute is removed, begin with 0. */ 752 for (i = 0; i < nitems(attrs); i++) { 753 if (!(attr & attrs[i].mask) && (lastattr & attrs[i].mask)) { 754 s[n++] = 0; 755 lastattr &= GRID_ATTR_CHARSET; 756 break; 757 } 758 } 759 /* For each attribute that is newly set, add its code. */ 760 for (i = 0; i < nitems(attrs); i++) { 761 if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask)) 762 s[n++] = attrs[i].code; 763 } 764 765 /* Write the attributes. */ 766 *buf = '\0'; 767 if (n > 0) { 768 if (escape_c0) 769 strlcat(buf, "\\033[", len); 770 else 771 strlcat(buf, "\033[", len); 772 for (i = 0; i < n; i++) { 773 if (i + 1 < n) 774 xsnprintf(tmp, sizeof tmp, "%d;", s[i]); 775 else 776 xsnprintf(tmp, sizeof tmp, "%d", s[i]); 777 strlcat(buf, tmp, len); 778 } 779 strlcat(buf, "m", len); 780 } 781 782 /* If the foreground colour changed, write its parameters. */ 783 nnewc = grid_string_cells_fg(gc, newc); 784 noldc = grid_string_cells_fg(lastgc, oldc); 785 if (nnewc != noldc || 786 memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 || 787 (n != 0 && s[0] == 0)) { 788 if (escape_c0) 789 strlcat(buf, "\\033[", len); 790 else 791 strlcat(buf, "\033[", len); 792 for (i = 0; i < nnewc; i++) { 793 if (i + 1 < nnewc) 794 xsnprintf(tmp, sizeof tmp, "%d;", newc[i]); 795 else 796 xsnprintf(tmp, sizeof tmp, "%d", newc[i]); 797 strlcat(buf, tmp, len); 798 } 799 strlcat(buf, "m", len); 800 } 801 802 /* If the background colour changed, append its parameters. */ 803 nnewc = grid_string_cells_bg(gc, newc); 804 noldc = grid_string_cells_bg(lastgc, oldc); 805 if (nnewc != noldc || 806 memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 || 807 (n != 0 && s[0] == 0)) { 808 if (escape_c0) 809 strlcat(buf, "\\033[", len); 810 else 811 strlcat(buf, "\033[", len); 812 for (i = 0; i < nnewc; i++) { 813 if (i + 1 < nnewc) 814 xsnprintf(tmp, sizeof tmp, "%d;", newc[i]); 815 else 816 xsnprintf(tmp, sizeof tmp, "%d", newc[i]); 817 strlcat(buf, tmp, len); 818 } 819 strlcat(buf, "m", len); 820 } 821 822 /* Append shift in/shift out if needed. */ 823 if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) { 824 if (escape_c0) 825 strlcat(buf, "\\016", len); /* SO */ 826 else 827 strlcat(buf, "\016", len); /* SO */ 828 } 829 if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) { 830 if (escape_c0) 831 strlcat(buf, "\\017", len); /* SI */ 832 else 833 strlcat(buf, "\017", len); /* SI */ 834 } 835 } 836 837 /* Convert cells into a string. */ 838 char * 839 grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, 840 struct grid_cell **lastgc, int with_codes, int escape_c0, int trim) 841 { 842 struct grid_cell gc; 843 static struct grid_cell lastgc1; 844 const char *data; 845 char *buf, code[128]; 846 size_t len, off, size, codelen; 847 u_int xx; 848 const struct grid_line *gl; 849 850 if (lastgc != NULL && *lastgc == NULL) { 851 memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); 852 *lastgc = &lastgc1; 853 } 854 855 len = 128; 856 buf = xmalloc(len); 857 off = 0; 858 859 gl = grid_peek_line(gd, py); 860 for (xx = px; xx < px + nx; xx++) { 861 if (gl == NULL || xx >= gl->cellsize) 862 break; 863 grid_get_cell(gd, xx, py, &gc); 864 if (gc.flags & GRID_FLAG_PADDING) 865 continue; 866 867 if (with_codes) { 868 grid_string_cells_code(*lastgc, &gc, code, sizeof code, 869 escape_c0); 870 codelen = strlen(code); 871 memcpy(*lastgc, &gc, sizeof **lastgc); 872 } else 873 codelen = 0; 874 875 data = gc.data.data; 876 size = gc.data.size; 877 if (escape_c0 && size == 1 && *data == '\\') { 878 data = "\\\\"; 879 size = 2; 880 } 881 882 while (len < off + size + codelen + 1) { 883 buf = xreallocarray(buf, 2, len); 884 len *= 2; 885 } 886 887 if (codelen != 0) { 888 memcpy(buf + off, code, codelen); 889 off += codelen; 890 } 891 memcpy(buf + off, data, size); 892 off += size; 893 } 894 895 if (trim) { 896 while (off > 0 && buf[off - 1] == ' ') 897 off--; 898 } 899 buf[off] = '\0'; 900 901 return (buf); 902 } 903 904 /* 905 * Duplicate a set of lines between two grids. Both source and destination 906 * should be big enough. 907 */ 908 void 909 grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, 910 u_int ny) 911 { 912 struct grid_line *dstl, *srcl; 913 u_int yy; 914 915 if (dy + ny > dst->hsize + dst->sy) 916 ny = dst->hsize + dst->sy - dy; 917 if (sy + ny > src->hsize + src->sy) 918 ny = src->hsize + src->sy - sy; 919 grid_free_lines(dst, dy, ny); 920 921 for (yy = 0; yy < ny; yy++) { 922 srcl = &src->linedata[sy]; 923 dstl = &dst->linedata[dy]; 924 925 memcpy(dstl, srcl, sizeof *dstl); 926 if (srcl->cellsize != 0) { 927 dstl->celldata = xreallocarray(NULL, 928 srcl->cellsize, sizeof *dstl->celldata); 929 memcpy(dstl->celldata, srcl->celldata, 930 srcl->cellsize * sizeof *dstl->celldata); 931 } else 932 dstl->celldata = NULL; 933 934 if (srcl->extdsize != 0) { 935 dstl->extdsize = srcl->extdsize; 936 dstl->extddata = xreallocarray(NULL, dstl->extdsize, 937 sizeof *dstl->extddata); 938 memcpy(dstl->extddata, srcl->extddata, dstl->extdsize * 939 sizeof *dstl->extddata); 940 } 941 942 sy++; 943 dy++; 944 } 945 } 946 947 /* Mark line as dead. */ 948 static void 949 grid_reflow_dead(struct grid_line *gl) 950 { 951 memset(gl, 0, sizeof *gl); 952 gl->flags = GRID_LINE_DEAD; 953 } 954 955 /* Add lines, return the first new one. */ 956 static struct grid_line * 957 grid_reflow_add(struct grid *gd, u_int n) 958 { 959 struct grid_line *gl; 960 u_int sy = gd->sy + n; 961 962 gd->linedata = xreallocarray(gd->linedata, sy, sizeof *gd->linedata); 963 gl = &gd->linedata[gd->sy]; 964 memset(gl, 0, n * (sizeof *gl)); 965 gd->sy = sy; 966 return (gl); 967 } 968 969 /* Move a line across. */ 970 static struct grid_line * 971 grid_reflow_move(struct grid *gd, struct grid_line *from) 972 { 973 struct grid_line *to; 974 975 to = grid_reflow_add(gd, 1); 976 memcpy(to, from, sizeof *to); 977 grid_reflow_dead(from); 978 return (to); 979 } 980 981 /* Join line below onto this one. */ 982 static void 983 grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy, 984 u_int width, u_int *cy, int already) 985 { 986 struct grid_line *gl, *from; 987 struct grid_cell gc; 988 u_int lines, want, left, i, to, line; 989 u_int at; 990 int wrapped = 1; 991 992 /* 993 * Add a new target line. 994 */ 995 if (!already) { 996 to = target->sy; 997 gl = grid_reflow_move(target, &gd->linedata[yy]); 998 } else { 999 to = target->sy - 1; 1000 gl = &target->linedata[to]; 1001 } 1002 at = gl->cellused; 1003 1004 /* 1005 * Loop until no more to consume or the target line is full. 1006 */ 1007 lines = 0; 1008 for (;;) { 1009 /* 1010 * If this is now the last line, there is nothing more to be 1011 * done. 1012 */ 1013 if (yy + lines == gd->hsize + gd->sy) 1014 break; 1015 line = yy + 1 + lines; 1016 1017 /* If the next line is empty, skip it. */ 1018 if (~gd->linedata[line].flags & GRID_LINE_WRAPPED) 1019 wrapped = 0; 1020 if (gd->linedata[line].cellused == 0) { 1021 if (!wrapped) 1022 break; 1023 continue; 1024 } 1025 1026 /* 1027 * Is the destination line now full? Copy the first character 1028 * separately because we need to leave "from" set to the last 1029 * line if this line is full. 1030 */ 1031 grid_get_cell1(&gd->linedata[line], 0, &gc); 1032 if (width + gc.data.width > sx) 1033 break; 1034 width += gc.data.width; 1035 grid_set_cell(target, at, to, &gc); 1036 at++; 1037 1038 /* Join as much more as possible onto the current line. */ 1039 from = &gd->linedata[line]; 1040 for (want = 1; want < from->cellused; want++) { 1041 grid_get_cell1(from, want, &gc); 1042 if (width + gc.data.width > sx) 1043 break; 1044 width += gc.data.width; 1045 1046 grid_set_cell(target, at, to, &gc); 1047 at++; 1048 } 1049 lines++; 1050 1051 /* 1052 * If this line wasn't wrapped or we didn't consume the entire 1053 * line, don't try to join any further lines. 1054 */ 1055 if (!wrapped || want != from->cellused || width == sx) 1056 break; 1057 } 1058 if (lines == 0) 1059 return; 1060 1061 /* 1062 * If we didn't consume the entire final line, then remove what we did 1063 * consume. If we consumed the entire line and it wasn't wrapped, 1064 * remove the wrap flag from this line. 1065 */ 1066 left = from->cellused - want; 1067 if (left != 0) { 1068 grid_move_cells(gd, 0, want, yy + lines, left, 8); 1069 from->cellsize = from->cellused = left; 1070 lines--; 1071 } else if (!wrapped) 1072 gl->flags &= ~GRID_LINE_WRAPPED; 1073 1074 /* Remove the lines that were completely consumed. */ 1075 for (i = yy + 1; i < yy + 1 + lines; i++) { 1076 free(gd->linedata[i].celldata); 1077 free(gd->linedata[i].extddata); 1078 grid_reflow_dead(&gd->linedata[i]); 1079 } 1080 1081 /* Adjust cursor and scroll positions. */ 1082 if (*cy > to + lines) 1083 *cy -= lines; 1084 else if (*cy > to) 1085 *cy = to; 1086 if (gd->hscrolled > to + lines) 1087 gd->hscrolled -= lines; 1088 else if (gd->hscrolled > to) 1089 gd->hscrolled = to; 1090 } 1091 1092 /* Split this line into several new ones */ 1093 static void 1094 grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy, 1095 u_int at, u_int *cy) 1096 { 1097 struct grid_line *gl = &gd->linedata[yy], *first; 1098 struct grid_cell gc; 1099 u_int line, lines, width, i, xx; 1100 u_int used = gl->cellused; 1101 int flags = gl->flags; 1102 1103 /* How many lines do we need to insert? We know we need at least two. */ 1104 if (~gl->flags & GRID_LINE_EXTENDED) 1105 lines = 1 + (gl->cellused - 1) / sx; 1106 else { 1107 lines = 2; 1108 width = 0; 1109 for (i = at; i < used; i++) { 1110 grid_get_cell1(gl, i, &gc); 1111 if (width + gc.data.width > sx) { 1112 lines++; 1113 width = 0; 1114 } 1115 width += gc.data.width; 1116 } 1117 } 1118 1119 /* Insert new lines. */ 1120 line = target->sy + 1; 1121 first = grid_reflow_add(target, lines); 1122 1123 /* Copy sections from the original line. */ 1124 width = 0; 1125 xx = 0; 1126 for (i = at; i < used; i++) { 1127 grid_get_cell1(gl, i, &gc); 1128 if (width + gc.data.width > sx) { 1129 target->linedata[line].flags |= GRID_LINE_WRAPPED; 1130 1131 line++; 1132 width = 0; 1133 xx = 0; 1134 } 1135 width += gc.data.width; 1136 grid_set_cell(target, xx, line, &gc); 1137 xx++; 1138 } 1139 if (flags & GRID_LINE_WRAPPED) 1140 target->linedata[line].flags |= GRID_LINE_WRAPPED; 1141 1142 /* Move the remainder of the original line. */ 1143 gl->cellsize = gl->cellused = at; 1144 gl->flags |= GRID_LINE_WRAPPED; 1145 memcpy(first, gl, sizeof *first); 1146 grid_reflow_dead(gl); 1147 1148 /* Adjust the cursor and scroll positions. */ 1149 if (yy <= *cy) 1150 (*cy) += lines - 1; 1151 if (yy <= gd->hscrolled) 1152 gd->hscrolled += lines - 1; 1153 1154 /* 1155 * If the original line had the wrapped flag and there is still space 1156 * in the last new line, try to join with the next lines. 1157 */ 1158 if (width < sx && (flags & GRID_LINE_WRAPPED)) 1159 grid_reflow_join(target, gd, sx, yy, width, cy, 1); 1160 } 1161 1162 /* Reflow lines on grid to new width. */ 1163 void 1164 grid_reflow(struct grid *gd, u_int sx, u_int *cursor) 1165 { 1166 struct grid *target; 1167 struct grid_line *gl; 1168 struct grid_cell gc; 1169 u_int yy, cy, width, i, at, first; 1170 struct timeval start, tv; 1171 1172 gettimeofday(&start, NULL); 1173 1174 log_debug("%s: %u lines, new width %u", __func__, gd->hsize + gd->sy, 1175 sx); 1176 cy = gd->hsize + (*cursor); 1177 1178 /* 1179 * Create a destination grid. This is just used as a container for the 1180 * line data and may not be fully valid. 1181 */ 1182 target = grid_create(gd->sx, 0, 0); 1183 1184 /* 1185 * Loop over each source line. 1186 */ 1187 for (yy = 0; yy < gd->hsize + gd->sy; yy++) { 1188 gl = &gd->linedata[yy]; 1189 if (gl->flags & GRID_LINE_DEAD) 1190 continue; 1191 1192 /* 1193 * Work out the width of this line. first is the width of the 1194 * first character, at is the point at which the available 1195 * width is hit, and width is the full line width. 1196 */ 1197 first = at = width = 0; 1198 if (~gl->flags & GRID_LINE_EXTENDED) { 1199 first = 1; 1200 width = gl->cellused; 1201 if (width > sx) 1202 at = sx; 1203 else 1204 at = width; 1205 } else { 1206 for (i = 0; i < gl->cellused; i++) { 1207 grid_get_cell1(gl, i, &gc); 1208 if (i == 0) 1209 first = gc.data.width; 1210 if (at == 0 && width + gc.data.width > sx) 1211 at = i; 1212 width += gc.data.width; 1213 } 1214 } 1215 1216 /* 1217 * If the line is exactly right or the first character is wider 1218 * than the targe width, just move it across unchanged. 1219 */ 1220 if (width == sx || first > sx) { 1221 grid_reflow_move(target, gl); 1222 continue; 1223 } 1224 1225 /* 1226 * If the line is too big, it needs to be split, whether or not 1227 * it was previously wrapped. 1228 */ 1229 if (width > sx) { 1230 grid_reflow_split(target, gd, sx, yy, at, &cy); 1231 continue; 1232 } 1233 1234 /* 1235 * If the line was previously wrapped, join as much as possible 1236 * of the next line. 1237 */ 1238 if (gl->flags & GRID_LINE_WRAPPED) 1239 grid_reflow_join(target, gd, sx, yy, width, &cy, 0); 1240 else 1241 grid_reflow_move(target, gl); 1242 } 1243 1244 /* 1245 * Replace the old grid with the new. 1246 */ 1247 if (target->sy < gd->sy) 1248 grid_reflow_add(target, gd->sy - target->sy); 1249 gd->hsize = target->sy - gd->sy; 1250 free(gd->linedata); 1251 gd->linedata = target->linedata; 1252 free(target); 1253 1254 /* 1255 * Update scrolled and cursor positions. 1256 */ 1257 if (gd->hscrolled > gd->hsize) 1258 gd->hscrolled = gd->hsize; 1259 if (cy < gd->hsize) 1260 *cursor = 0; 1261 else 1262 *cursor = cy - gd->hsize; 1263 1264 gettimeofday(&tv, NULL); 1265 timersub(&tv, &start, &tv); 1266 log_debug("%s: now %u lines (in %llu.%06u seconds)", __func__, 1267 gd->hsize + gd->sy, (unsigned long long)tv.tv_sec, 1268 (u_int)tv.tv_usec); 1269 } 1270