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