1 /* $OpenBSD$ */ 2 3 /* 4 * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott (at) 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, 1, 1 }, 0, 0, 8, 8, 8, 0 41 }; 42 43 /* 44 * Padding grid cell data. Padding cells are the only zero width cell that 45 * appears in the grid - because of this, they are always extended cells. 46 */ 47 static const struct grid_cell grid_padding_cell = { 48 { { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0 49 }; 50 51 /* Cleared grid cell data. */ 52 static const struct grid_cell grid_cleared_cell = { 53 { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0 54 }; 55 static const struct grid_cell_entry grid_cleared_entry = { 56 { .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED 57 }; 58 59 /* Store cell in entry. */ 60 static void 61 grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc, 62 u_char c) 63 { 64 gce->flags = (gc->flags & ~GRID_FLAG_CLEARED); 65 66 gce->data.fg = gc->fg & 0xff; 67 if (gc->fg & COLOUR_FLAG_256) 68 gce->flags |= GRID_FLAG_FG256; 69 70 gce->data.bg = gc->bg & 0xff; 71 if (gc->bg & COLOUR_FLAG_256) 72 gce->flags |= GRID_FLAG_BG256; 73 74 gce->data.attr = gc->attr; 75 gce->data.data = c; 76 } 77 78 /* Check if a cell should be an extended cell. */ 79 static int 80 grid_need_extended_cell(const struct grid_cell_entry *gce, 81 const struct grid_cell *gc) 82 { 83 if (gce->flags & GRID_FLAG_EXTENDED) 84 return (1); 85 if (gc->attr > 0xff) 86 return (1); 87 if (gc->data.size > 1 || gc->data.width > 1) 88 return (1); 89 if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB)) 90 return (1); 91 if (gc->us != 8) /* only supports 256 or RGB */ 92 return (1); 93 if (gc->link != 0) 94 return (1); 95 if (gc->flags & GRID_FLAG_TAB) 96 return (1); 97 return (0); 98 } 99 100 /* Get an extended cell. */ 101 static void 102 grid_get_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, 103 int flags) 104 { 105 u_int at = gl->extdsize + 1; 106 107 gl->extddata = xreallocarray(gl->extddata, at, sizeof *gl->extddata); 108 gl->extdsize = at; 109 110 gce->offset = at - 1; 111 gce->flags = (flags | GRID_FLAG_EXTENDED); 112 } 113 114 /* Set cell as extended. */ 115 static struct grid_extd_entry * 116 grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, 117 const struct grid_cell *gc) 118 { 119 struct grid_extd_entry *gee; 120 int flags = (gc->flags & ~GRID_FLAG_CLEARED); 121 utf8_char uc; 122 123 if (~gce->flags & GRID_FLAG_EXTENDED) 124 grid_get_extended_cell(gl, gce, flags); 125 else if (gce->offset >= gl->extdsize) 126 fatalx("offset too big"); 127 gl->flags |= GRID_LINE_EXTENDED; 128 129 if (gc->flags & GRID_FLAG_TAB) 130 uc = gc->data.width; 131 else 132 utf8_from_data(&gc->data, &uc); 133 134 gee = &gl->extddata[gce->offset]; 135 gee->data = uc; 136 gee->attr = gc->attr; 137 gee->flags = flags; 138 gee->fg = gc->fg; 139 gee->bg = gc->bg; 140 gee->us = gc->us; 141 gee->link = gc->link; 142 return (gee); 143 } 144 145 /* Free up unused extended cells. */ 146 static void 147 grid_compact_line(struct grid_line *gl) 148 { 149 int new_extdsize = 0; 150 struct grid_extd_entry *new_extddata; 151 struct grid_cell_entry *gce; 152 struct grid_extd_entry *gee; 153 u_int px, idx; 154 155 if (gl->extdsize == 0) 156 return; 157 158 for (px = 0; px < gl->cellsize; px++) { 159 gce = &gl->celldata[px]; 160 if (gce->flags & GRID_FLAG_EXTENDED) 161 new_extdsize++; 162 } 163 164 if (new_extdsize == 0) { 165 free(gl->extddata); 166 gl->extddata = NULL; 167 gl->extdsize = 0; 168 return; 169 } 170 new_extddata = xreallocarray(NULL, new_extdsize, sizeof *gl->extddata); 171 172 idx = 0; 173 for (px = 0; px < gl->cellsize; px++) { 174 gce = &gl->celldata[px]; 175 if (gce->flags & GRID_FLAG_EXTENDED) { 176 gee = &gl->extddata[gce->offset]; 177 memcpy(&new_extddata[idx], gee, sizeof *gee); 178 gce->offset = idx++; 179 } 180 } 181 182 free(gl->extddata); 183 gl->extddata = new_extddata; 184 gl->extdsize = new_extdsize; 185 } 186 187 /* Get line data. */ 188 struct grid_line * 189 grid_get_line(struct grid *gd, u_int line) 190 { 191 return (&gd->linedata[line]); 192 } 193 194 /* Adjust number of lines. */ 195 void 196 grid_adjust_lines(struct grid *gd, u_int lines) 197 { 198 gd->linedata = xreallocarray(gd->linedata, lines, sizeof *gd->linedata); 199 } 200 201 /* Copy default into a cell. */ 202 static void 203 grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg) 204 { 205 struct grid_line *gl = &gd->linedata[py]; 206 struct grid_cell_entry *gce = &gl->celldata[px]; 207 struct grid_extd_entry *gee; 208 209 memcpy(gce, &grid_cleared_entry, sizeof *gce); 210 if (bg != 8) { 211 if (bg & COLOUR_FLAG_RGB) { 212 grid_get_extended_cell(gl, gce, gce->flags); 213 gee = grid_extended_cell(gl, gce, &grid_cleared_cell); 214 gee->bg = bg; 215 } else { 216 if (bg & COLOUR_FLAG_256) 217 gce->flags |= GRID_FLAG_BG256; 218 gce->data.bg = bg; 219 } 220 } 221 } 222 223 /* Check grid y position. */ 224 static int 225 grid_check_y(struct grid *gd, const char *from, u_int py) 226 { 227 if (py >= gd->hsize + gd->sy) { 228 log_debug("%s: y out of range: %u", from, py); 229 return (-1); 230 } 231 return (0); 232 } 233 234 /* Check if two styles are (visibly) the same. */ 235 int 236 grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) 237 { 238 int flags1 = gc1->flags, flags2 = gc2->flags;; 239 240 if (gc1->fg != gc2->fg || gc1->bg != gc2->bg) 241 return (0); 242 if (gc1->attr != gc2->attr) 243 return (0); 244 if ((flags1 & ~GRID_FLAG_CLEARED) != (flags2 & ~GRID_FLAG_CLEARED)) 245 return (0); 246 if (gc1->link != gc2->link) 247 return (0); 248 return (1); 249 } 250 251 /* Compare grid cells. Return 1 if equal, 0 if not. */ 252 int 253 grid_cells_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) 254 { 255 if (!grid_cells_look_equal(gc1, gc2)) 256 return (0); 257 if (gc1->data.width != gc2->data.width) 258 return (0); 259 if (gc1->data.size != gc2->data.size) 260 return (0); 261 return (memcmp(gc1->data.data, gc2->data.data, gc1->data.size) == 0); 262 } 263 264 /* Set grid cell to a tab. */ 265 void 266 grid_set_tab(struct grid_cell *gc, u_int width) 267 { 268 memset(gc->data.data, 0, sizeof gc->data.data); 269 gc->flags |= GRID_FLAG_TAB; 270 gc->flags &= ~GRID_FLAG_PADDING; 271 gc->data.width = gc->data.size = gc->data.have = width; 272 memset(gc->data.data, ' ', gc->data.size); 273 } 274 275 /* Free one line. */ 276 static void 277 grid_free_line(struct grid *gd, u_int py) 278 { 279 free(gd->linedata[py].celldata); 280 gd->linedata[py].celldata = NULL; 281 free(gd->linedata[py].extddata); 282 gd->linedata[py].extddata = NULL; 283 } 284 285 /* Free several lines. */ 286 static void 287 grid_free_lines(struct grid *gd, u_int py, u_int ny) 288 { 289 u_int yy; 290 291 for (yy = py; yy < py + ny; yy++) 292 grid_free_line(gd, yy); 293 } 294 295 /* Create a new grid. */ 296 struct grid * 297 grid_create(u_int sx, u_int sy, u_int hlimit) 298 { 299 struct grid *gd; 300 301 gd = xmalloc(sizeof *gd); 302 gd->sx = sx; 303 gd->sy = sy; 304 305 if (hlimit != 0) 306 gd->flags = GRID_HISTORY; 307 else 308 gd->flags = 0; 309 310 gd->hscrolled = 0; 311 gd->hsize = 0; 312 gd->hlimit = hlimit; 313 314 if (gd->sy != 0) 315 gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); 316 else 317 gd->linedata = NULL; 318 319 return (gd); 320 } 321 322 /* Destroy grid. */ 323 void 324 grid_destroy(struct grid *gd) 325 { 326 grid_free_lines(gd, 0, gd->hsize + gd->sy); 327 328 free(gd->linedata); 329 330 free(gd); 331 } 332 333 /* Compare grids. */ 334 int 335 grid_compare(struct grid *ga, struct grid *gb) 336 { 337 struct grid_line *gla, *glb; 338 struct grid_cell gca, gcb; 339 u_int xx, yy; 340 341 if (ga->sx != gb->sx || ga->sy != gb->sy) 342 return (1); 343 344 for (yy = 0; yy < ga->sy; yy++) { 345 gla = &ga->linedata[yy]; 346 glb = &gb->linedata[yy]; 347 if (gla->cellsize != glb->cellsize) 348 return (1); 349 for (xx = 0; xx < gla->cellsize; xx++) { 350 grid_get_cell(ga, xx, yy, &gca); 351 grid_get_cell(gb, xx, yy, &gcb); 352 if (!grid_cells_equal(&gca, &gcb)) 353 return (1); 354 } 355 } 356 357 return (0); 358 } 359 360 /* Trim lines from the history. */ 361 static void 362 grid_trim_history(struct grid *gd, u_int ny) 363 { 364 grid_free_lines(gd, 0, ny); 365 memmove(&gd->linedata[0], &gd->linedata[ny], 366 (gd->hsize + gd->sy - ny) * (sizeof *gd->linedata)); 367 } 368 369 /* 370 * Collect lines from the history if at the limit. Free the top (oldest) 10% 371 * and shift up. 372 */ 373 void 374 grid_collect_history(struct grid *gd) 375 { 376 u_int ny; 377 378 if (gd->hsize == 0 || gd->hsize < gd->hlimit) 379 return; 380 381 ny = gd->hlimit / 10; 382 if (ny < 1) 383 ny = 1; 384 if (ny > gd->hsize) 385 ny = gd->hsize; 386 387 /* 388 * Free the lines from 0 to ny then move the remaining lines over 389 * them. 390 */ 391 grid_trim_history(gd, ny); 392 393 gd->hsize -= ny; 394 if (gd->hscrolled > gd->hsize) 395 gd->hscrolled = gd->hsize; 396 } 397 398 /* Remove lines from the bottom of the history. */ 399 void 400 grid_remove_history(struct grid *gd, u_int ny) 401 { 402 u_int yy; 403 404 if (ny > gd->hsize) 405 return; 406 for (yy = 0; yy < ny; yy++) 407 grid_free_line(gd, gd->hsize + gd->sy - 1 - yy); 408 gd->hsize -= ny; 409 } 410 411 /* 412 * Scroll the entire visible screen, moving one line into the history. Just 413 * allocate a new line at the bottom and move the history size indicator. 414 */ 415 void 416 grid_scroll_history(struct grid *gd, u_int bg) 417 { 418 u_int yy; 419 420 yy = gd->hsize + gd->sy; 421 gd->linedata = xreallocarray(gd->linedata, yy + 1, 422 sizeof *gd->linedata); 423 grid_empty_line(gd, yy, bg); 424 425 gd->hscrolled++; 426 grid_compact_line(&gd->linedata[gd->hsize]); 427 gd->linedata[gd->hsize].time = current_time; 428 gd->hsize++; 429 } 430 431 /* Clear the history. */ 432 void 433 grid_clear_history(struct grid *gd) 434 { 435 grid_trim_history(gd, gd->hsize); 436 437 gd->hscrolled = 0; 438 gd->hsize = 0; 439 440 gd->linedata = xreallocarray(gd->linedata, gd->sy, 441 sizeof *gd->linedata); 442 } 443 444 /* Scroll a region up, moving the top line into the history. */ 445 void 446 grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg) 447 { 448 struct grid_line *gl_history, *gl_upper; 449 u_int yy; 450 451 /* Create a space for a new line. */ 452 yy = gd->hsize + gd->sy; 453 gd->linedata = xreallocarray(gd->linedata, yy + 1, 454 sizeof *gd->linedata); 455 456 /* Move the entire screen down to free a space for this line. */ 457 gl_history = &gd->linedata[gd->hsize]; 458 memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history); 459 460 /* Adjust the region and find its start and end. */ 461 upper++; 462 gl_upper = &gd->linedata[upper]; 463 lower++; 464 465 /* Move the line into the history. */ 466 memcpy(gl_history, gl_upper, sizeof *gl_history); 467 gl_history->time = current_time; 468 469 /* Then move the region up and clear the bottom line. */ 470 memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper); 471 grid_empty_line(gd, lower, bg); 472 473 /* Move the history offset down over the line. */ 474 gd->hscrolled++; 475 gd->hsize++; 476 } 477 478 /* Expand line to fit to cell. */ 479 static void 480 grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg) 481 { 482 struct grid_line *gl; 483 u_int xx; 484 485 gl = &gd->linedata[py]; 486 if (sx <= gl->cellsize) 487 return; 488 489 if (sx < gd->sx / 4) 490 sx = gd->sx / 4; 491 else if (sx < gd->sx / 2) 492 sx = gd->sx / 2; 493 else if (gd->sx > sx) 494 sx = gd->sx; 495 496 gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); 497 for (xx = gl->cellsize; xx < sx; xx++) 498 grid_clear_cell(gd, xx, py, bg); 499 gl->cellsize = sx; 500 } 501 502 /* Empty a line and set background colour if needed. */ 503 void 504 grid_empty_line(struct grid *gd, u_int py, u_int bg) 505 { 506 memset(&gd->linedata[py], 0, sizeof gd->linedata[py]); 507 if (!COLOUR_DEFAULT(bg)) 508 grid_expand_line(gd, py, gd->sx, bg); 509 } 510 511 /* Peek at grid line. */ 512 const struct grid_line * 513 grid_peek_line(struct grid *gd, u_int py) 514 { 515 if (grid_check_y(gd, __func__, py) != 0) 516 return (NULL); 517 return (&gd->linedata[py]); 518 } 519 520 /* Get cell from line. */ 521 static void 522 grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) 523 { 524 struct grid_cell_entry *gce = &gl->celldata[px]; 525 struct grid_extd_entry *gee; 526 527 if (gce->flags & GRID_FLAG_EXTENDED) { 528 if (gce->offset >= gl->extdsize) 529 memcpy(gc, &grid_default_cell, sizeof *gc); 530 else { 531 gee = &gl->extddata[gce->offset]; 532 gc->flags = gee->flags; 533 gc->attr = gee->attr; 534 gc->fg = gee->fg; 535 gc->bg = gee->bg; 536 gc->us = gee->us; 537 gc->link = gee->link; 538 539 if (gc->flags & GRID_FLAG_TAB) 540 grid_set_tab(gc, gee->data); 541 else 542 utf8_to_data(gee->data, &gc->data); 543 } 544 return; 545 } 546 547 gc->flags = gce->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); 548 gc->attr = gce->data.attr; 549 gc->fg = gce->data.fg; 550 if (gce->flags & GRID_FLAG_FG256) 551 gc->fg |= COLOUR_FLAG_256; 552 gc->bg = gce->data.bg; 553 if (gce->flags & GRID_FLAG_BG256) 554 gc->bg |= COLOUR_FLAG_256; 555 gc->us = 8; 556 utf8_set(&gc->data, gce->data.data); 557 gc->link = 0; 558 } 559 560 /* Get cell for reading. */ 561 void 562 grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) 563 { 564 if (grid_check_y(gd, __func__, py) != 0 || 565 px >= gd->linedata[py].cellsize) 566 memcpy(gc, &grid_default_cell, sizeof *gc); 567 else 568 grid_get_cell1(&gd->linedata[py], px, gc); 569 } 570 571 /* Set cell at position. */ 572 void 573 grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) 574 { 575 struct grid_line *gl; 576 struct grid_cell_entry *gce; 577 578 if (grid_check_y(gd, __func__, py) != 0) 579 return; 580 581 grid_expand_line(gd, py, px + 1, 8); 582 583 gl = &gd->linedata[py]; 584 if (px + 1 > gl->cellused) 585 gl->cellused = px + 1; 586 587 gce = &gl->celldata[px]; 588 if (grid_need_extended_cell(gce, gc)) 589 grid_extended_cell(gl, gce, gc); 590 else 591 grid_store_cell(gce, gc, gc->data.data[0]); 592 } 593 594 /* Set padding at position. */ 595 void 596 grid_set_padding(struct grid *gd, u_int px, u_int py) 597 { 598 grid_set_cell(gd, px, py, &grid_padding_cell); 599 } 600 601 /* Set cells at position. */ 602 void 603 grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc, 604 const char *s, size_t slen) 605 { 606 struct grid_line *gl; 607 struct grid_cell_entry *gce; 608 struct grid_extd_entry *gee; 609 u_int i; 610 611 if (grid_check_y(gd, __func__, py) != 0) 612 return; 613 614 grid_expand_line(gd, py, px + slen, 8); 615 616 gl = &gd->linedata[py]; 617 if (px + slen > gl->cellused) 618 gl->cellused = px + slen; 619 620 for (i = 0; i < slen; i++) { 621 gce = &gl->celldata[px + i]; 622 if (grid_need_extended_cell(gce, gc)) { 623 gee = grid_extended_cell(gl, gce, gc); 624 gee->data = utf8_build_one(s[i]); 625 } else 626 grid_store_cell(gce, gc, s[i]); 627 } 628 } 629 630 /* Clear area. */ 631 void 632 grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) 633 { 634 struct grid_line *gl; 635 u_int xx, yy, ox, sx; 636 637 if (nx == 0 || ny == 0) 638 return; 639 640 if (px == 0 && nx == gd->sx) { 641 grid_clear_lines(gd, py, ny, bg); 642 return; 643 } 644 645 if (grid_check_y(gd, __func__, py) != 0) 646 return; 647 if (grid_check_y(gd, __func__, py + ny - 1) != 0) 648 return; 649 650 for (yy = py; yy < py + ny; yy++) { 651 gl = &gd->linedata[yy]; 652 653 sx = gd->sx; 654 if (sx > gl->cellsize) 655 sx = gl->cellsize; 656 ox = nx; 657 if (COLOUR_DEFAULT(bg)) { 658 if (px > sx) 659 continue; 660 if (px + nx > sx) 661 ox = sx - px; 662 } 663 664 grid_expand_line(gd, yy, px + ox, 8); /* default bg first */ 665 for (xx = px; xx < px + ox; xx++) 666 grid_clear_cell(gd, xx, yy, bg); 667 } 668 } 669 670 /* Clear lines. This just frees and truncates the lines. */ 671 void 672 grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg) 673 { 674 u_int yy; 675 676 if (ny == 0) 677 return; 678 679 if (grid_check_y(gd, __func__, py) != 0) 680 return; 681 if (grid_check_y(gd, __func__, py + ny - 1) != 0) 682 return; 683 684 for (yy = py; yy < py + ny; yy++) { 685 grid_free_line(gd, yy); 686 grid_empty_line(gd, yy, bg); 687 } 688 if (py != 0) 689 gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED; 690 } 691 692 /* Move a group of lines. */ 693 void 694 grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg) 695 { 696 u_int yy; 697 698 if (ny == 0 || py == dy) 699 return; 700 701 if (grid_check_y(gd, __func__, py) != 0) 702 return; 703 if (grid_check_y(gd, __func__, py + ny - 1) != 0) 704 return; 705 if (grid_check_y(gd, __func__, dy) != 0) 706 return; 707 if (grid_check_y(gd, __func__, dy + ny - 1) != 0) 708 return; 709 710 /* Free any lines which are being replaced. */ 711 for (yy = dy; yy < dy + ny; yy++) { 712 if (yy >= py && yy < py + ny) 713 continue; 714 grid_free_line(gd, yy); 715 } 716 if (dy != 0) 717 gd->linedata[dy - 1].flags &= ~GRID_LINE_WRAPPED; 718 719 memmove(&gd->linedata[dy], &gd->linedata[py], 720 ny * (sizeof *gd->linedata)); 721 722 /* 723 * Wipe any lines that have been moved (without freeing them - they are 724 * still present). 725 */ 726 for (yy = py; yy < py + ny; yy++) { 727 if (yy < dy || yy >= dy + ny) 728 grid_empty_line(gd, yy, bg); 729 } 730 if (py != 0 && (py < dy || py >= dy + ny)) 731 gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED; 732 } 733 734 /* Move a group of cells. */ 735 void 736 grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx, 737 u_int bg) 738 { 739 struct grid_line *gl; 740 u_int xx; 741 742 if (nx == 0 || px == dx) 743 return; 744 745 if (grid_check_y(gd, __func__, py) != 0) 746 return; 747 gl = &gd->linedata[py]; 748 749 grid_expand_line(gd, py, px + nx, 8); 750 grid_expand_line(gd, py, dx + nx, 8); 751 memmove(&gl->celldata[dx], &gl->celldata[px], 752 nx * sizeof *gl->celldata); 753 if (dx + nx > gl->cellused) 754 gl->cellused = dx + nx; 755 756 /* Wipe any cells that have been moved. */ 757 for (xx = px; xx < px + nx; xx++) { 758 if (xx >= dx && xx < dx + nx) 759 continue; 760 grid_clear_cell(gd, xx, py, bg); 761 } 762 } 763 764 /* Get ANSI foreground sequence. */ 765 static size_t 766 grid_string_cells_fg(const struct grid_cell *gc, int *values) 767 { 768 size_t n; 769 u_char r, g, b; 770 771 n = 0; 772 if (gc->fg & COLOUR_FLAG_256) { 773 values[n++] = 38; 774 values[n++] = 5; 775 values[n++] = gc->fg & 0xff; 776 } else if (gc->fg & COLOUR_FLAG_RGB) { 777 values[n++] = 38; 778 values[n++] = 2; 779 colour_split_rgb(gc->fg, &r, &g, &b); 780 values[n++] = r; 781 values[n++] = g; 782 values[n++] = b; 783 } else { 784 switch (gc->fg) { 785 case 0: 786 case 1: 787 case 2: 788 case 3: 789 case 4: 790 case 5: 791 case 6: 792 case 7: 793 values[n++] = gc->fg + 30; 794 break; 795 case 8: 796 values[n++] = 39; 797 break; 798 case 90: 799 case 91: 800 case 92: 801 case 93: 802 case 94: 803 case 95: 804 case 96: 805 case 97: 806 values[n++] = gc->fg; 807 break; 808 } 809 } 810 return (n); 811 } 812 813 /* Get ANSI background sequence. */ 814 static size_t 815 grid_string_cells_bg(const struct grid_cell *gc, int *values) 816 { 817 size_t n; 818 u_char r, g, b; 819 820 n = 0; 821 if (gc->bg & COLOUR_FLAG_256) { 822 values[n++] = 48; 823 values[n++] = 5; 824 values[n++] = gc->bg & 0xff; 825 } else if (gc->bg & COLOUR_FLAG_RGB) { 826 values[n++] = 48; 827 values[n++] = 2; 828 colour_split_rgb(gc->bg, &r, &g, &b); 829 values[n++] = r; 830 values[n++] = g; 831 values[n++] = b; 832 } else { 833 switch (gc->bg) { 834 case 0: 835 case 1: 836 case 2: 837 case 3: 838 case 4: 839 case 5: 840 case 6: 841 case 7: 842 values[n++] = gc->bg + 40; 843 break; 844 case 8: 845 values[n++] = 49; 846 break; 847 case 90: 848 case 91: 849 case 92: 850 case 93: 851 case 94: 852 case 95: 853 case 96: 854 case 97: 855 values[n++] = gc->bg + 10; 856 break; 857 } 858 } 859 return (n); 860 } 861 862 /* Get underscore colour sequence. */ 863 static size_t 864 grid_string_cells_us(const struct grid_cell *gc, int *values) 865 { 866 size_t n; 867 u_char r, g, b; 868 869 n = 0; 870 if (gc->us & COLOUR_FLAG_256) { 871 values[n++] = 58; 872 values[n++] = 5; 873 values[n++] = gc->us & 0xff; 874 } else if (gc->us & COLOUR_FLAG_RGB) { 875 values[n++] = 58; 876 values[n++] = 2; 877 colour_split_rgb(gc->us, &r, &g, &b); 878 values[n++] = r; 879 values[n++] = g; 880 values[n++] = b; 881 } 882 return (n); 883 } 884 885 /* Add on SGR code. */ 886 static void 887 grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc, 888 int *oldc, size_t nnewc, size_t noldc, int flags) 889 { 890 u_int i; 891 char tmp[64]; 892 int reset = (n != 0 && s[0] == 0); 893 894 if (nnewc == 0) 895 return; /* no code to add */ 896 if (!reset && 897 nnewc == noldc && 898 memcmp(newc, oldc, nnewc * sizeof newc[0]) == 0) 899 return; /* no reset and colour unchanged */ 900 if (reset && (newc[0] == 49 || newc[0] == 39)) 901 return; /* reset and colour default */ 902 903 if (flags & GRID_STRING_ESCAPE_SEQUENCES) 904 strlcat(buf, "\\033[", len); 905 else 906 strlcat(buf, "\033[", len); 907 for (i = 0; i < nnewc; i++) { 908 if (i + 1 < nnewc) 909 xsnprintf(tmp, sizeof tmp, "%d;", newc[i]); 910 else 911 xsnprintf(tmp, sizeof tmp, "%d", newc[i]); 912 strlcat(buf, tmp, len); 913 } 914 strlcat(buf, "m", len); 915 } 916 917 static int 918 grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id, 919 const char *uri, int flags) 920 { 921 char *tmp; 922 923 if (strlen(uri) + strlen(id) + 17 >= len) 924 return (0); 925 926 if (flags & GRID_STRING_ESCAPE_SEQUENCES) 927 strlcat(buf, "\\033]8;", len); 928 else 929 strlcat(buf, "\033]8;", len); 930 if (*id != '\0') { 931 xasprintf(&tmp, "id=%s;", id); 932 strlcat(buf, tmp, len); 933 free(tmp); 934 } else 935 strlcat(buf, ";", len); 936 strlcat(buf, uri, len); 937 if (flags & GRID_STRING_ESCAPE_SEQUENCES) 938 strlcat(buf, "\\033\\\\", len); 939 else 940 strlcat(buf, "\033\\", len); 941 return (1); 942 } 943 944 /* 945 * Returns ANSI code to set particular attributes (colour, bold and so on) 946 * given a current state. 947 */ 948 static void 949 grid_string_cells_code(const struct grid_cell *lastgc, 950 const struct grid_cell *gc, char *buf, size_t len, int flags, 951 struct screen *sc, int *has_link) 952 { 953 int oldc[64], newc[64], s[128]; 954 size_t noldc, nnewc, n, i; 955 u_int attr = gc->attr, lastattr = lastgc->attr; 956 char tmp[64]; 957 const char *uri, *id; 958 959 static const struct { 960 u_int mask; 961 u_int code; 962 } attrs[] = { 963 { GRID_ATTR_BRIGHT, 1 }, 964 { GRID_ATTR_DIM, 2 }, 965 { GRID_ATTR_ITALICS, 3 }, 966 { GRID_ATTR_UNDERSCORE, 4 }, 967 { GRID_ATTR_BLINK, 5 }, 968 { GRID_ATTR_REVERSE, 7 }, 969 { GRID_ATTR_HIDDEN, 8 }, 970 { GRID_ATTR_STRIKETHROUGH, 9 }, 971 { GRID_ATTR_UNDERSCORE_2, 42 }, 972 { GRID_ATTR_UNDERSCORE_3, 43 }, 973 { GRID_ATTR_UNDERSCORE_4, 44 }, 974 { GRID_ATTR_UNDERSCORE_5, 45 }, 975 { GRID_ATTR_OVERLINE, 53 }, 976 }; 977 n = 0; 978 979 /* If any attribute is removed, begin with 0. */ 980 for (i = 0; i < nitems(attrs); i++) { 981 if (((~attr & attrs[i].mask) && 982 (lastattr & attrs[i].mask)) || 983 (lastgc->us != 8 && gc->us == 8)) { 984 s[n++] = 0; 985 lastattr &= GRID_ATTR_CHARSET; 986 break; 987 } 988 } 989 /* For each attribute that is newly set, add its code. */ 990 for (i = 0; i < nitems(attrs); i++) { 991 if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask)) 992 s[n++] = attrs[i].code; 993 } 994 995 /* Write the attributes. */ 996 *buf = '\0'; 997 if (n > 0) { 998 if (flags & GRID_STRING_ESCAPE_SEQUENCES) 999 strlcat(buf, "\\033[", len); 1000 else 1001 strlcat(buf, "\033[", len); 1002 for (i = 0; i < n; i++) { 1003 if (s[i] < 10) 1004 xsnprintf(tmp, sizeof tmp, "%d", s[i]); 1005 else { 1006 xsnprintf(tmp, sizeof tmp, "%d:%d", s[i] / 10, 1007 s[i] % 10); 1008 } 1009 strlcat(buf, tmp, len); 1010 if (i + 1 < n) 1011 strlcat(buf, ";", len); 1012 } 1013 strlcat(buf, "m", len); 1014 } 1015 1016 /* If the foreground colour changed, write its parameters. */ 1017 nnewc = grid_string_cells_fg(gc, newc); 1018 noldc = grid_string_cells_fg(lastgc, oldc); 1019 grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, 1020 flags); 1021 1022 /* If the background colour changed, append its parameters. */ 1023 nnewc = grid_string_cells_bg(gc, newc); 1024 noldc = grid_string_cells_bg(lastgc, oldc); 1025 grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, 1026 flags); 1027 1028 /* If the underscore colour changed, append its parameters. */ 1029 nnewc = grid_string_cells_us(gc, newc); 1030 noldc = grid_string_cells_us(lastgc, oldc); 1031 grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, 1032 flags); 1033 1034 /* Append shift in/shift out if needed. */ 1035 if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) { 1036 if (flags & GRID_STRING_ESCAPE_SEQUENCES) 1037 strlcat(buf, "\\016", len); /* SO */ 1038 else 1039 strlcat(buf, "\016", len); /* SO */ 1040 } 1041 if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) { 1042 if (flags & GRID_STRING_ESCAPE_SEQUENCES) 1043 strlcat(buf, "\\017", len); /* SI */ 1044 else 1045 strlcat(buf, "\017", len); /* SI */ 1046 } 1047 1048 /* Add hyperlink if changed. */ 1049 if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) { 1050 if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) { 1051 *has_link = grid_string_cells_add_hyperlink(buf, len, 1052 id, uri, flags); 1053 } else if (*has_link) { 1054 grid_string_cells_add_hyperlink(buf, len, "", "", 1055 flags); 1056 *has_link = 0; 1057 } 1058 } 1059 } 1060 1061 /* Convert cells into a string. */ 1062 char * 1063 grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, 1064 struct grid_cell **lastgc, int flags, struct screen *s) 1065 { 1066 struct grid_cell gc; 1067 static struct grid_cell lastgc1; 1068 const char *data; 1069 char *buf, code[8192]; 1070 size_t len, off, size, codelen; 1071 u_int xx, end; 1072 int has_link = 0; 1073 const struct grid_line *gl; 1074 1075 if (lastgc != NULL && *lastgc == NULL) { 1076 memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); 1077 *lastgc = &lastgc1; 1078 } 1079 1080 len = 128; 1081 buf = xmalloc(len); 1082 off = 0; 1083 1084 gl = grid_peek_line(gd, py); 1085 if (flags & GRID_STRING_EMPTY_CELLS) 1086 end = gl->cellsize; 1087 else 1088 end = gl->cellused; 1089 for (xx = px; xx < px + nx; xx++) { 1090 if (gl == NULL || xx >= end) 1091 break; 1092 grid_get_cell(gd, xx, py, &gc); 1093 if (gc.flags & GRID_FLAG_PADDING) 1094 continue; 1095 1096 if (flags & GRID_STRING_WITH_SEQUENCES) { 1097 grid_string_cells_code(*lastgc, &gc, code, sizeof code, 1098 flags, s, &has_link); 1099 codelen = strlen(code); 1100 memcpy(*lastgc, &gc, sizeof **lastgc); 1101 } else 1102 codelen = 0; 1103 1104 if (gc.flags & GRID_FLAG_TAB) { 1105 data = "\t"; 1106 size = 1; 1107 } else { 1108 data = (void *)gc.data.data; 1109 size = gc.data.size; 1110 if ((flags & GRID_STRING_ESCAPE_SEQUENCES) && 1111 size == 1 && 1112 *data == '\\') { 1113 data = "\\\\"; 1114 size = 2; 1115 } 1116 } 1117 1118 while (len < off + size + codelen + 1) { 1119 buf = xreallocarray(buf, 2, len); 1120 len *= 2; 1121 } 1122 1123 if (codelen != 0) { 1124 memcpy(buf + off, code, codelen); 1125 off += codelen; 1126 } 1127 memcpy(buf + off, data, size); 1128 off += size; 1129 } 1130 1131 if (has_link) { 1132 grid_string_cells_add_hyperlink(code, sizeof code, "", "", 1133 flags); 1134 codelen = strlen(code); 1135 while (len < off + size + codelen + 1) { 1136 buf = xreallocarray(buf, 2, len); 1137 len *= 2; 1138 } 1139 memcpy(buf + off, code, codelen); 1140 off += codelen; 1141 } 1142 1143 if (flags & GRID_STRING_TRIM_SPACES) { 1144 while (off > 0 && buf[off - 1] == ' ') 1145 off--; 1146 } 1147 buf[off] = '\0'; 1148 1149 return (buf); 1150 } 1151 1152 /* 1153 * Duplicate a set of lines between two grids. Both source and destination 1154 * should be big enough. 1155 */ 1156 void 1157 grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, 1158 u_int ny) 1159 { 1160 struct grid_line *dstl, *srcl; 1161 u_int yy; 1162 1163 if (dy + ny > dst->hsize + dst->sy) 1164 ny = dst->hsize + dst->sy - dy; 1165 if (sy + ny > src->hsize + src->sy) 1166 ny = src->hsize + src->sy - sy; 1167 grid_free_lines(dst, dy, ny); 1168 1169 for (yy = 0; yy < ny; yy++) { 1170 srcl = &src->linedata[sy]; 1171 dstl = &dst->linedata[dy]; 1172 1173 memcpy(dstl, srcl, sizeof *dstl); 1174 if (srcl->cellsize != 0) { 1175 dstl->celldata = xreallocarray(NULL, 1176 srcl->cellsize, sizeof *dstl->celldata); 1177 memcpy(dstl->celldata, srcl->celldata, 1178 srcl->cellsize * sizeof *dstl->celldata); 1179 } else 1180 dstl->celldata = NULL; 1181 if (srcl->extdsize != 0) { 1182 dstl->extdsize = srcl->extdsize; 1183 dstl->extddata = xreallocarray(NULL, dstl->extdsize, 1184 sizeof *dstl->extddata); 1185 memcpy(dstl->extddata, srcl->extddata, dstl->extdsize * 1186 sizeof *dstl->extddata); 1187 } else 1188 dstl->extddata = NULL; 1189 1190 sy++; 1191 dy++; 1192 } 1193 } 1194 1195 /* Mark line as dead. */ 1196 static void 1197 grid_reflow_dead(struct grid_line *gl) 1198 { 1199 memset(gl, 0, sizeof *gl); 1200 gl->flags = GRID_LINE_DEAD; 1201 } 1202 1203 /* Add lines, return the first new one. */ 1204 static struct grid_line * 1205 grid_reflow_add(struct grid *gd, u_int n) 1206 { 1207 struct grid_line *gl; 1208 u_int sy = gd->sy + n; 1209 1210 gd->linedata = xreallocarray(gd->linedata, sy, sizeof *gd->linedata); 1211 gl = &gd->linedata[gd->sy]; 1212 memset(gl, 0, n * (sizeof *gl)); 1213 gd->sy = sy; 1214 return (gl); 1215 } 1216 1217 /* Move a line across. */ 1218 static struct grid_line * 1219 grid_reflow_move(struct grid *gd, struct grid_line *from) 1220 { 1221 struct grid_line *to; 1222 1223 to = grid_reflow_add(gd, 1); 1224 memcpy(to, from, sizeof *to); 1225 grid_reflow_dead(from); 1226 return (to); 1227 } 1228 1229 /* Join line below onto this one. */ 1230 static void 1231 grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy, 1232 u_int width, int already) 1233 { 1234 struct grid_line *gl, *from = NULL; 1235 struct grid_cell gc; 1236 u_int lines, left, i, to, line, want = 0; 1237 u_int at; 1238 int wrapped = 1; 1239 1240 /* 1241 * Add a new target line. 1242 */ 1243 if (!already) { 1244 to = target->sy; 1245 gl = grid_reflow_move(target, &gd->linedata[yy]); 1246 } else { 1247 to = target->sy - 1; 1248 gl = &target->linedata[to]; 1249 } 1250 at = gl->cellused; 1251 1252 /* 1253 * Loop until no more to consume or the target line is full. 1254 */ 1255 lines = 0; 1256 for (;;) { 1257 /* 1258 * If this is now the last line, there is nothing more to be 1259 * done. 1260 */ 1261 if (yy + 1 + lines == gd->hsize + gd->sy) 1262 break; 1263 line = yy + 1 + lines; 1264 1265 /* If the next line is empty, skip it. */ 1266 if (~gd->linedata[line].flags & GRID_LINE_WRAPPED) 1267 wrapped = 0; 1268 if (gd->linedata[line].cellused == 0) { 1269 if (!wrapped) 1270 break; 1271 lines++; 1272 continue; 1273 } 1274 1275 /* 1276 * Is the destination line now full? Copy the first character 1277 * separately because we need to leave "from" set to the last 1278 * line if this line is full. 1279 */ 1280 grid_get_cell1(&gd->linedata[line], 0, &gc); 1281 if (width + gc.data.width > sx) 1282 break; 1283 width += gc.data.width; 1284 grid_set_cell(target, at, to, &gc); 1285 at++; 1286 1287 /* Join as much more as possible onto the current line. */ 1288 from = &gd->linedata[line]; 1289 for (want = 1; want < from->cellused; want++) { 1290 grid_get_cell1(from, want, &gc); 1291 if (width + gc.data.width > sx) 1292 break; 1293 width += gc.data.width; 1294 1295 grid_set_cell(target, at, to, &gc); 1296 at++; 1297 } 1298 lines++; 1299 1300 /* 1301 * If this line wasn't wrapped or we didn't consume the entire 1302 * line, don't try to join any further lines. 1303 */ 1304 if (!wrapped || want != from->cellused || width == sx) 1305 break; 1306 } 1307 if (lines == 0 || from == NULL) 1308 return; 1309 1310 /* 1311 * If we didn't consume the entire final line, then remove what we did 1312 * consume. If we consumed the entire line and it wasn't wrapped, 1313 * remove the wrap flag from this line. 1314 */ 1315 left = from->cellused - want; 1316 if (left != 0) { 1317 grid_move_cells(gd, 0, want, yy + lines, left, 8); 1318 from->cellsize = from->cellused = left; 1319 lines--; 1320 } else if (!wrapped) 1321 gl->flags &= ~GRID_LINE_WRAPPED; 1322 1323 /* Remove the lines that were completely consumed. */ 1324 for (i = yy + 1; i < yy + 1 + lines; i++) { 1325 free(gd->linedata[i].celldata); 1326 free(gd->linedata[i].extddata); 1327 grid_reflow_dead(&gd->linedata[i]); 1328 } 1329 1330 /* Adjust scroll position. */ 1331 if (gd->hscrolled > to + lines) 1332 gd->hscrolled -= lines; 1333 else if (gd->hscrolled > to) 1334 gd->hscrolled = to; 1335 } 1336 1337 /* Split this line into several new ones */ 1338 static void 1339 grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy, 1340 u_int at) 1341 { 1342 struct grid_line *gl = &gd->linedata[yy], *first; 1343 struct grid_cell gc; 1344 u_int line, lines, width, i, xx; 1345 u_int used = gl->cellused; 1346 int flags = gl->flags; 1347 1348 /* How many lines do we need to insert? We know we need at least two. */ 1349 if (~gl->flags & GRID_LINE_EXTENDED) 1350 lines = 1 + (gl->cellused - 1) / sx; 1351 else { 1352 lines = 2; 1353 width = 0; 1354 for (i = at; i < used; i++) { 1355 grid_get_cell1(gl, i, &gc); 1356 if (width + gc.data.width > sx) { 1357 lines++; 1358 width = 0; 1359 } 1360 width += gc.data.width; 1361 } 1362 } 1363 1364 /* Insert new lines. */ 1365 line = target->sy + 1; 1366 first = grid_reflow_add(target, lines); 1367 1368 /* Copy sections from the original line. */ 1369 width = 0; 1370 xx = 0; 1371 for (i = at; i < used; i++) { 1372 grid_get_cell1(gl, i, &gc); 1373 if (width + gc.data.width > sx) { 1374 target->linedata[line].flags |= GRID_LINE_WRAPPED; 1375 1376 line++; 1377 width = 0; 1378 xx = 0; 1379 } 1380 width += gc.data.width; 1381 grid_set_cell(target, xx, line, &gc); 1382 xx++; 1383 } 1384 if (flags & GRID_LINE_WRAPPED) 1385 target->linedata[line].flags |= GRID_LINE_WRAPPED; 1386 1387 /* Move the remainder of the original line. */ 1388 gl->cellsize = gl->cellused = at; 1389 gl->flags |= GRID_LINE_WRAPPED; 1390 memcpy(first, gl, sizeof *first); 1391 grid_reflow_dead(gl); 1392 1393 /* Adjust the scroll position. */ 1394 if (yy <= gd->hscrolled) 1395 gd->hscrolled += lines - 1; 1396 1397 /* 1398 * If the original line had the wrapped flag and there is still space 1399 * in the last new line, try to join with the next lines. 1400 */ 1401 if (width < sx && (flags & GRID_LINE_WRAPPED)) 1402 grid_reflow_join(target, gd, sx, yy, width, 1); 1403 } 1404 1405 /* Reflow lines on grid to new width. */ 1406 void 1407 grid_reflow(struct grid *gd, u_int sx) 1408 { 1409 struct grid *target; 1410 struct grid_line *gl; 1411 struct grid_cell gc; 1412 u_int yy, width, i, at; 1413 1414 /* 1415 * Create a destination grid. This is just used as a container for the 1416 * line data and may not be fully valid. 1417 */ 1418 target = grid_create(gd->sx, 0, 0); 1419 1420 /* 1421 * Loop over each source line. 1422 */ 1423 for (yy = 0; yy < gd->hsize + gd->sy; yy++) { 1424 gl = &gd->linedata[yy]; 1425 if (gl->flags & GRID_LINE_DEAD) 1426 continue; 1427 1428 /* 1429 * Work out the width of this line. at is the point at which 1430 * the available width is hit, and width is the full line 1431 * width. 1432 */ 1433 at = width = 0; 1434 if (~gl->flags & GRID_LINE_EXTENDED) { 1435 width = gl->cellused; 1436 if (width > sx) 1437 at = sx; 1438 else 1439 at = width; 1440 } else { 1441 for (i = 0; i < gl->cellused; i++) { 1442 grid_get_cell1(gl, i, &gc); 1443 if (at == 0 && width + gc.data.width > sx) 1444 at = i; 1445 width += gc.data.width; 1446 } 1447 } 1448 1449 /* 1450 * If the line is exactly right, just move it across 1451 * unchanged. 1452 */ 1453 if (width == sx) { 1454 grid_reflow_move(target, gl); 1455 continue; 1456 } 1457 1458 /* 1459 * If the line is too big, it needs to be split, whether or not 1460 * it was previously wrapped. 1461 */ 1462 if (width > sx) { 1463 grid_reflow_split(target, gd, sx, yy, at); 1464 continue; 1465 } 1466 1467 /* 1468 * If the line was previously wrapped, join as much as possible 1469 * of the next line. 1470 */ 1471 if (gl->flags & GRID_LINE_WRAPPED) 1472 grid_reflow_join(target, gd, sx, yy, width, 0); 1473 else 1474 grid_reflow_move(target, gl); 1475 } 1476 1477 /* 1478 * Replace the old grid with the new. 1479 */ 1480 if (target->sy < gd->sy) 1481 grid_reflow_add(target, gd->sy - target->sy); 1482 gd->hsize = target->sy - gd->sy; 1483 if (gd->hscrolled > gd->hsize) 1484 gd->hscrolled = gd->hsize; 1485 free(gd->linedata); 1486 gd->linedata = target->linedata; 1487 free(target); 1488 } 1489 1490 /* Convert to position based on wrapped lines. */ 1491 void 1492 grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy) 1493 { 1494 u_int ax = 0, ay = 0, yy; 1495 1496 for (yy = 0; yy < py; yy++) { 1497 if (gd->linedata[yy].flags & GRID_LINE_WRAPPED) 1498 ax += gd->linedata[yy].cellused; 1499 else { 1500 ax = 0; 1501 ay++; 1502 } 1503 } 1504 if (px >= gd->linedata[yy].cellused) 1505 ax = UINT_MAX; 1506 else 1507 ax += px; 1508 *wx = ax; 1509 *wy = ay; 1510 } 1511 1512 /* Convert position based on wrapped lines back. */ 1513 void 1514 grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy) 1515 { 1516 u_int yy, ay = 0; 1517 1518 for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) { 1519 if (ay == wy) 1520 break; 1521 if (~gd->linedata[yy].flags & GRID_LINE_WRAPPED) 1522 ay++; 1523 } 1524 1525 /* 1526 * yy is now 0 on the unwrapped line which contains wx. Walk forwards 1527 * until we find the end or the line now containing wx. 1528 */ 1529 if (wx == UINT_MAX) { 1530 while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) 1531 yy++; 1532 wx = gd->linedata[yy].cellused; 1533 } else { 1534 while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) { 1535 if (wx < gd->linedata[yy].cellused) 1536 break; 1537 wx -= gd->linedata[yy].cellused; 1538 yy++; 1539 } 1540 } 1541 *px = wx; 1542 *py = yy; 1543 } 1544 1545 /* Get length of line. */ 1546 u_int 1547 grid_line_length(struct grid *gd, u_int py) 1548 { 1549 struct grid_cell gc; 1550 u_int px; 1551 1552 px = grid_get_line(gd, py)->cellsize; 1553 if (px > gd->sx) 1554 px = gd->sx; 1555 while (px > 0) { 1556 grid_get_cell(gd, px - 1, py, &gc); 1557 if ((gc.flags & GRID_FLAG_PADDING) || 1558 gc.data.size != 1 || 1559 *gc.data.data != ' ') 1560 break; 1561 px--; 1562 } 1563 return (px); 1564 } 1565 1566 /* Check if character is in set. */ 1567 int 1568 grid_in_set(struct grid *gd, u_int px, u_int py, const char *set) 1569 { 1570 struct grid_cell gc, tmp_gc; 1571 u_int pxx; 1572 1573 grid_get_cell(gd, px, py, &gc); 1574 if (strchr(set, '\t')) { 1575 if (gc.flags & GRID_FLAG_PADDING) { 1576 pxx = px; 1577 do 1578 grid_get_cell(gd, --pxx, py, &tmp_gc); 1579 while (pxx > 0 && tmp_gc.flags & GRID_FLAG_PADDING); 1580 if (tmp_gc.flags & GRID_FLAG_TAB) 1581 return (tmp_gc.data.width - (px - pxx)); 1582 } else if (gc.flags & GRID_FLAG_TAB) 1583 return (gc.data.width); 1584 } 1585 if (gc.flags & GRID_FLAG_PADDING) 1586 return (0); 1587 return (utf8_cstrhas(set, &gc.data)); 1588 } 1589