reflow.c (20722B)
1 void 2 tloaddefscreen(int clear, int loadcursor) 3 { 4 int col, row, alt = IS_SET(MODE_ALTSCREEN); 5 6 if (alt) { 7 if (clear) { 8 tclearregion(0, 0, term.col-1, term.row-1, 1); 9 #if SIXEL_PATCH 10 tdeleteimages(); 11 #endif // SIXEL_PATCH 12 } 13 col = term.col, row = term.row; 14 tswapscreen(); 15 } 16 if (loadcursor) 17 tcursor(CURSOR_LOAD); 18 if (alt) 19 tresizedef(col, row); 20 } 21 22 void 23 tloadaltscreen(int clear, int savecursor) 24 { 25 int col, row, def = !IS_SET(MODE_ALTSCREEN); 26 27 if (savecursor) 28 tcursor(CURSOR_SAVE); 29 if (def) { 30 col = term.col, row = term.row; 31 kscrolldown(&((Arg){ .i = term.scr })); 32 tswapscreen(); 33 tresizealt(col, row); 34 } 35 if (clear) { 36 tclearregion(0, 0, term.col-1, term.row-1, 1); 37 #if SIXEL_PATCH 38 tdeleteimages(); 39 #endif // SIXEL_PATCH 40 } 41 } 42 43 void 44 selmove(int n) 45 { 46 sel.ob.y += n, sel.nb.y += n; 47 sel.oe.y += n, sel.ne.y += n; 48 } 49 50 void 51 tclearglyph(Glyph *gp, int usecurattr) 52 { 53 if (usecurattr) { 54 gp->fg = term.c.attr.fg; 55 gp->bg = term.c.attr.bg; 56 } else { 57 gp->fg = defaultfg; 58 gp->bg = defaultbg; 59 } 60 gp->mode = ATTR_NULL; 61 gp->u = ' '; 62 } 63 64 #if SIXEL_PATCH 65 void 66 treflow_moveimages(int oldy, int newy) 67 { 68 ImageList *im; 69 70 for (im = term.images; im; im = im->next) { 71 if (im->y == oldy) 72 im->reflow_y = newy; 73 } 74 } 75 #endif // SIXEL_PATCH 76 77 void 78 treflow(int col, int row) 79 { 80 int i, j; 81 int oce, nce, bot, scr; 82 int ox = 0, oy = -term.histf, nx = 0, ny = -1, len; 83 int cy = -1; /* proxy for new y coordinate of cursor */ 84 int buflen, nlines; 85 Line *buf, bufline, line; 86 #if SIXEL_PATCH 87 ImageList *im, *next; 88 89 for (im = term.images; im; im = im->next) 90 im->reflow_y = INT_MIN; /* unset reflow_y */ 91 #endif // SIXEL_PATCH 92 93 /* y coordinate of cursor line end */ 94 for (oce = term.c.y; oce < term.row - 1 && 95 tiswrapped(term.line[oce]); oce++); 96 97 nlines = HISTSIZE + row; 98 buf = xmalloc(nlines * sizeof(Line)); 99 do { 100 if (!nx && ++ny < nlines) 101 buf[ny] = xmalloc(col * sizeof(Glyph)); 102 if (!ox) { 103 line = TLINEABS(oy); 104 len = tlinelen(line); 105 } 106 if (oy == term.c.y) { 107 if (!ox) 108 len = MAX(len, term.c.x + 1); 109 /* update cursor */ 110 if (cy < 0 && term.c.x - ox < col - nx) { 111 term.c.x = nx + term.c.x - ox, cy = ny; 112 UPDATEWRAPNEXT(0, col); 113 } 114 } 115 /* get reflowed lines in buf */ 116 bufline = buf[ny % nlines]; 117 if (col - nx > len - ox) { 118 memcpy(&bufline[nx], &line[ox], (len-ox) * sizeof(Glyph)); 119 nx += len - ox; 120 if (len == 0 || !(line[len - 1].mode & ATTR_WRAP)) { 121 for (j = nx; j < col; j++) 122 tclearglyph(&bufline[j], 0); 123 #if SIXEL_PATCH 124 treflow_moveimages(oy+term.scr, ny); 125 #endif // SIXEL_PATCH 126 nx = 0; 127 } else if (nx > 0) { 128 bufline[nx - 1].mode &= ~ATTR_WRAP; 129 } 130 ox = 0, oy++; 131 } else if (col - nx == len - ox) { 132 memcpy(&bufline[nx], &line[ox], (col-nx) * sizeof(Glyph)); 133 #if SIXEL_PATCH 134 treflow_moveimages(oy+term.scr, ny); 135 #endif // SIXEL_PATCH 136 ox = 0, oy++, nx = 0; 137 } else/* if (col - nx < len - ox) */ { 138 memcpy(&bufline[nx], &line[ox], (col-nx) * sizeof(Glyph)); 139 if (bufline[col - 1].mode & ATTR_WIDE) { 140 bufline[col - 2].mode |= ATTR_WRAP; 141 tclearglyph(&bufline[col - 1], 0); 142 ox--; 143 } else { 144 bufline[col - 1].mode |= ATTR_WRAP; 145 } 146 #if SIXEL_PATCH 147 treflow_moveimages(oy+term.scr, ny); 148 #endif // SIXEL_PATCH 149 ox += col - nx; 150 nx = 0; 151 } 152 } while (oy <= oce); 153 if (nx) 154 for (j = nx; j < col; j++) 155 tclearglyph(&bufline[j], 0); 156 157 /* free extra lines */ 158 for (i = row; i < term.row; i++) 159 free(term.line[i]); 160 /* resize to new height */ 161 term.line = xrealloc(term.line, row * sizeof(Line)); 162 163 buflen = MIN(ny + 1, nlines); 164 bot = MIN(ny, row - 1); 165 scr = MAX(row - term.row, 0); 166 /* update y coordinate of cursor line end */ 167 nce = MIN(oce + scr, bot); 168 /* update cursor y coordinate */ 169 term.c.y = nce - (ny - cy); 170 if (term.c.y < 0) { 171 j = nce, nce = MIN(nce + -term.c.y, bot); 172 term.c.y += nce - j; 173 while (term.c.y < 0) { 174 free(buf[ny-- % nlines]); 175 buflen--; 176 term.c.y++; 177 } 178 } 179 /* allocate new rows */ 180 for (i = row - 1; i > nce; i--) { 181 if (i >= term.row) 182 term.line[i] = xmalloc(col * sizeof(Glyph)); 183 else 184 term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); 185 for (j = 0; j < col; j++) 186 tclearglyph(&term.line[i][j], 0); 187 } 188 /* fill visible area */ 189 for (/*i = nce */; i >= term.row; i--, ny--, buflen--) 190 term.line[i] = buf[ny % nlines]; 191 for (/*i = term.row - 1 */; i >= 0; i--, ny--, buflen--) { 192 free(term.line[i]); 193 term.line[i] = buf[ny % nlines]; 194 } 195 /* fill lines in history buffer and update term.histf */ 196 for (/*i = -1 */; buflen > 0 && i >= -HISTSIZE; i--, ny--, buflen--) { 197 j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE; 198 free(term.hist[j]); 199 term.hist[j] = buf[ny % nlines]; 200 } 201 term.histf = -i - 1; 202 term.scr = MIN(term.scr, term.histf); 203 /* resize rest of the history lines */ 204 for (/*i = -term.histf - 1 */; i >= -HISTSIZE; i--) { 205 j = (term.histi + i + 1 + HISTSIZE) % HISTSIZE; 206 term.hist[j] = xrealloc(term.hist[j], col * sizeof(Glyph)); 207 } 208 209 #if SIXEL_PATCH 210 /* move images to the final position */ 211 for (im = term.images; im; im = next) { 212 next = im->next; 213 if (im->reflow_y == INT_MIN) { 214 delete_image(im); 215 } else { 216 im->y = im->reflow_y - term.histf + term.scr - (ny + 1); 217 if (im->y - term.scr < -HISTSIZE || im->y - term.scr >= row) 218 delete_image(im); 219 } 220 } 221 222 /* expand images into new text cells */ 223 for (im = term.images; im; im = im->next) { 224 j = MIN(im->x + im->cols, col); 225 line = TLINE(im->y); 226 for (i = im->x; i < j; i++) { 227 if (!(line[i].mode & ATTR_SET)) 228 line[i].mode |= ATTR_SIXEL; 229 } 230 } 231 #endif // SIXEL_PATCH 232 233 for (; buflen > 0; ny--, buflen--) 234 free(buf[ny % nlines]); 235 free(buf); 236 } 237 238 void 239 rscrolldown(int n) 240 { 241 int i; 242 Line temp; 243 244 /* can never be true as of now 245 if (IS_SET(MODE_ALTSCREEN)) 246 return; */ 247 248 if ((n = MIN(n, term.histf)) <= 0) 249 return; 250 251 for (i = term.c.y + n; i >= n; i--) { 252 temp = term.line[i]; 253 term.line[i] = term.line[i-n]; 254 term.line[i-n] = temp; 255 } 256 for (/*i = n - 1 */; i >= 0; i--) { 257 temp = term.line[i]; 258 term.line[i] = term.hist[term.histi]; 259 term.hist[term.histi] = temp; 260 term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; 261 } 262 term.c.y += n; 263 term.histf -= n; 264 if ((i = term.scr - n) >= 0) { 265 term.scr = i; 266 } else { 267 #if SIXEL_PATCH 268 scroll_images(n - term.scr); 269 #endif // SIXEL_PATCH 270 term.scr = 0; 271 if (sel.ob.x != -1 && !sel.alt) 272 selmove(-i); 273 } 274 } 275 276 void 277 tresizedef(int col, int row) 278 { 279 int i, j; 280 281 /* return if dimensions haven't changed */ 282 if (term.col == col && term.row == row) { 283 tfulldirt(); 284 return; 285 } 286 if (col != term.col) { 287 if (!sel.alt) 288 selremove(); 289 treflow(col, row); 290 } else { 291 /* slide screen up if otherwise cursor would get out of the screen */ 292 if (term.c.y >= row) { 293 tscrollup(0, term.row - 1, term.c.y - row + 1, SCROLL_RESIZE); 294 term.c.y = row - 1; 295 } 296 for (i = row; i < term.row; i++) 297 free(term.line[i]); 298 299 /* resize to new height */ 300 term.line = xrealloc(term.line, row * sizeof(Line)); 301 /* allocate any new rows */ 302 for (i = term.row; i < row; i++) { 303 term.line[i] = xmalloc(col * sizeof(Glyph)); 304 for (j = 0; j < col; j++) 305 tclearglyph(&term.line[i][j], 0); 306 } 307 /* scroll down as much as height has increased */ 308 rscrolldown(row - term.row); 309 } 310 /* update terminal size */ 311 term.col = col, term.row = row; 312 /* reset scrolling region */ 313 term.top = 0, term.bot = row - 1; 314 /* dirty all lines */ 315 tfulldirt(); 316 } 317 318 void 319 tresizealt(int col, int row) 320 { 321 int i, j; 322 #if SIXEL_PATCH 323 ImageList *im, *next; 324 #endif // SIXEL_PATCH 325 326 /* return if dimensions haven't changed */ 327 if (term.col == col && term.row == row) { 328 tfulldirt(); 329 return; 330 } 331 if (sel.alt) 332 selremove(); 333 /* slide screen up if otherwise cursor would get out of the screen */ 334 for (i = 0; i <= term.c.y - row; i++) 335 free(term.line[i]); 336 if (i > 0) { 337 /* ensure that both src and dst are not NULL */ 338 memmove(term.line, term.line + i, row * sizeof(Line)); 339 #if SIXEL_PATCH 340 scroll_images(-i); 341 #endif // SIXEL_PATCH 342 term.c.y = row - 1; 343 } 344 for (i += row; i < term.row; i++) 345 free(term.line[i]); 346 /* resize to new height */ 347 term.line = xrealloc(term.line, row * sizeof(Line)); 348 /* resize to new width */ 349 for (i = 0; i < MIN(row, term.row); i++) { 350 term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); 351 for (j = term.col; j < col; j++) 352 tclearglyph(&term.line[i][j], 0); 353 } 354 /* allocate any new rows */ 355 for (/*i = MIN(row, term.row) */; i < row; i++) { 356 term.line[i] = xmalloc(col * sizeof(Glyph)); 357 for (j = 0; j < col; j++) 358 tclearglyph(&term.line[i][j], 0); 359 } 360 /* update cursor */ 361 if (term.c.x >= col) { 362 term.c.state &= ~CURSOR_WRAPNEXT; 363 term.c.x = col - 1; 364 } else { 365 UPDATEWRAPNEXT(1, col); 366 } 367 /* update terminal size */ 368 term.col = col, term.row = row; 369 /* reset scrolling region */ 370 term.top = 0, term.bot = row - 1; 371 372 #if SIXEL_PATCH 373 /* delete or clip images if they are not inside the screen */ 374 for (im = term.images; im; im = next) { 375 next = im->next; 376 if (im->x >= term.col || im->y >= term.row || im->y < 0) { 377 delete_image(im); 378 } else { 379 if ((im->cols = MIN(im->x + im->cols, term.col) - im->x) <= 0) 380 delete_image(im); 381 } 382 } 383 #endif // SIXEL_PATCH 384 385 /* dirty all lines */ 386 tfulldirt(); 387 } 388 389 void 390 kscrolldown(const Arg* a) 391 { 392 int n = a->i; 393 394 if (!term.scr || IS_SET(MODE_ALTSCREEN)) 395 return; 396 397 if (n < 0) 398 n = MAX(term.row / -n, 1); 399 400 if (n <= term.scr) { 401 term.scr -= n; 402 } else { 403 n = term.scr; 404 term.scr = 0; 405 } 406 407 if (sel.ob.x != -1 && !sel.alt) 408 selmove(-n); /* negate change in term.scr */ 409 tfulldirt(); 410 411 #if SIXEL_PATCH 412 scroll_images(-1*n); 413 #endif // SIXEL_PATCH 414 415 #if OPENURLONCLICK_PATCH 416 if (n > 0) 417 restoremousecursor(); 418 #endif // OPENURLONCLICK_PATCH 419 } 420 421 void 422 kscrollup(const Arg* a) 423 { 424 int n = a->i; 425 426 if (!term.histf || IS_SET(MODE_ALTSCREEN)) 427 return; 428 429 if (n < 0) 430 n = MAX(term.row / -n, 1); 431 432 if (term.scr + n <= term.histf) { 433 term.scr += n; 434 } else { 435 n = term.histf - term.scr; 436 term.scr = term.histf; 437 } 438 439 if (sel.ob.x != -1 && !sel.alt) 440 selmove(n); /* negate change in term.scr */ 441 tfulldirt(); 442 443 #if SIXEL_PATCH 444 scroll_images(n); 445 #endif // SIXEL_PATCH 446 447 #if OPENURLONCLICK_PATCH 448 if (n > 0) 449 restoremousecursor(); 450 #endif // OPENURLONCLICK_PATCH 451 } 452 453 void 454 tscrollup(int top, int bot, int n, int mode) 455 { 456 #if OPENURLONCLICK_PATCH 457 restoremousecursor(); 458 #endif //OPENURLONCLICK_PATCH 459 460 int i, j, s; 461 Line temp; 462 int alt = IS_SET(MODE_ALTSCREEN); 463 int savehist = !alt && top == 0 && mode != SCROLL_NOSAVEHIST; 464 int scr = alt ? 0 : term.scr; 465 #if SIXEL_PATCH 466 int itop = top + scr, ibot = bot + scr; 467 ImageList *im, *next; 468 #endif // SIXEL_PATCH 469 470 if (n <= 0) 471 return; 472 n = MIN(n, bot-top+1); 473 474 if (savehist) { 475 for (i = 0; i < n; i++) { 476 term.histi = (term.histi + 1) % HISTSIZE; 477 temp = term.hist[term.histi]; 478 for (j = 0; j < term.col; j++) 479 tclearglyph(&temp[j], 1); 480 term.hist[term.histi] = term.line[i]; 481 term.line[i] = temp; 482 } 483 term.histf = MIN(term.histf + n, HISTSIZE); 484 s = n; 485 if (term.scr) { 486 j = term.scr; 487 term.scr = MIN(j + n, HISTSIZE); 488 s = j + n - term.scr; 489 } 490 if (mode != SCROLL_RESIZE) 491 tfulldirt(); 492 } else { 493 tclearregion(0, top, term.col-1, top+n-1, 1); 494 tsetdirt(top + scr, bot + scr); 495 } 496 497 for (i = top; i <= bot-n; i++) { 498 temp = term.line[i]; 499 term.line[i] = term.line[i+n]; 500 term.line[i+n] = temp; 501 } 502 503 #if SIXEL_PATCH 504 if (alt || !savehist) { 505 /* move images, if they are inside the scrolling region */ 506 for (im = term.images; im; im = next) { 507 next = im->next; 508 if (im->y >= itop && im->y <= ibot) { 509 im->y -= n; 510 if (im->y < itop) 511 delete_image(im); 512 } 513 } 514 } else { 515 /* move images, if they are inside the scrolling region or scrollback */ 516 for (im = term.images; im; im = next) { 517 next = im->next; 518 im->y -= scr; 519 if (im->y < 0) { 520 im->y -= n; 521 } else if (im->y >= top && im->y <= bot) { 522 im->y -= n; 523 if (im->y < top) 524 im->y -= top; // move to scrollback 525 } 526 if (im->y < -HISTSIZE) 527 delete_image(im); 528 else 529 im->y += term.scr; 530 } 531 } 532 #endif // SIXEL_PATCH 533 534 if (sel.ob.x != -1 && sel.alt == alt) { 535 if (!savehist) { 536 selscroll(top, bot, -n); 537 } else if (s > 0) { 538 selmove(-s); 539 if (-term.scr + sel.nb.y < -term.histf) 540 selremove(); 541 } 542 } 543 } 544 545 void 546 tscrolldown(int top, int n) 547 { 548 #if OPENURLONCLICK_PATCH 549 restoremousecursor(); 550 #endif //OPENURLONCLICK_PATCH 551 552 int i, bot = term.bot; 553 int scr = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr; 554 int itop = top + scr, ibot = bot + scr; 555 Line temp; 556 #if SIXEL_PATCH 557 ImageList *im, *next; 558 #endif // SIXEL_PATCH 559 560 if (n <= 0) 561 return; 562 n = MIN(n, bot-top+1); 563 564 tsetdirt(top + scr, bot + scr); 565 tclearregion(0, bot-n+1, term.col-1, bot, 1); 566 567 for (i = bot; i >= top+n; i--) { 568 temp = term.line[i]; 569 term.line[i] = term.line[i-n]; 570 term.line[i-n] = temp; 571 } 572 573 #if SIXEL_PATCH 574 /* move images, if they are inside the scrolling region */ 575 for (im = term.images; im; im = next) { 576 next = im->next; 577 if (im->y >= itop && im->y <= ibot) { 578 im->y += n; 579 if (im->y > ibot) 580 delete_image(im); 581 } 582 } 583 #endif // SIXEL_PATCH 584 585 if (sel.ob.x != -1 && sel.alt == IS_SET(MODE_ALTSCREEN)) 586 selscroll(top, bot, n); 587 } 588 589 void 590 tresize(int col, int row) 591 { 592 int *bp; 593 594 #if KEYBOARDSELECT_PATCH 595 if (row != term.row || col != term.col) 596 win.mode ^= kbds_keyboardhandler(XK_Escape, NULL, 0, 1); 597 #endif // KEYBOARDSELECT_PATCH 598 599 term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); 600 term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); 601 if (col > term.col) { 602 bp = term.tabs + term.col; 603 memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); 604 while (--bp > term.tabs && !*bp) 605 /* nothing */ ; 606 for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) 607 *bp = 1; 608 } 609 610 if (IS_SET(MODE_ALTSCREEN)) 611 tresizealt(col, row); 612 else 613 tresizedef(col, row); 614 } 615 616 void 617 tclearregion(int x1, int y1, int x2, int y2, int usecurattr) 618 { 619 int x, y; 620 621 /* regionselected() takes relative coordinates */ 622 if (regionselected(x1+term.scr, y1+term.scr, x2+term.scr, y2+term.scr)) 623 selremove(); 624 625 for (y = y1; y <= y2; y++) { 626 term.dirty[y] = 1; 627 for (x = x1; x <= x2; x++) 628 tclearglyph(&term.line[y][x], usecurattr); 629 } 630 } 631 632 void 633 tnew(int col, int row) 634 { 635 int i, j; 636 for (i = 0; i < 2; i++) { 637 term.line = xmalloc(row * sizeof(Line)); 638 for (j = 0; j < row; j++) 639 term.line[j] = xmalloc(col * sizeof(Glyph)); 640 term.col = col, term.row = row; 641 tswapscreen(); 642 } 643 term.dirty = xmalloc(row * sizeof(*term.dirty)); 644 term.tabs = xmalloc(col * sizeof(*term.tabs)); 645 for (i = 0; i < HISTSIZE; i++) 646 term.hist[i] = xmalloc(col * sizeof(Glyph)); 647 treset(); 648 } 649 650 void 651 tdeletechar(int n) 652 { 653 int src, dst, size; 654 Line line; 655 656 if (n <= 0) 657 return; 658 dst = term.c.x; 659 src = MIN(term.c.x + n, term.col); 660 size = term.col - src; 661 if (size > 0) { /* otherwise src would point beyond the array 662 https://stackoverflow.com/questions/29844298 */ 663 line = term.line[term.c.y]; 664 memmove(&line[dst], &line[src], size * sizeof(Glyph)); 665 } 666 tclearregion(dst + size, term.c.y, term.col - 1, term.c.y, 1); 667 } 668 669 void 670 tinsertblank(int n) 671 { 672 int src, dst, size; 673 Line line; 674 675 if (n <= 0) 676 return; 677 dst = MIN(term.c.x + n, term.col); 678 src = term.c.x; 679 size = term.col - dst; 680 681 if (size > 0) { /* otherwise dst would point beyond the array */ 682 line = term.line[term.c.y]; 683 memmove(&line[dst], &line[src], size * sizeof(Glyph)); 684 } 685 tclearregion(src, term.c.y, dst - 1, term.c.y, 1); 686 } 687 688 int 689 tlinelen(Line line) 690 { 691 int i = term.col - 1; 692 693 /* We are using a different algorithm on the alt screen because an 694 * application might use spaces to clear the screen and in that case it is 695 * impossible to find the end of the line when every cell has the ATTR_SET 696 * attribute. The second algorithm is more accurate on the main screen and 697 * and we can use it there. */ 698 if (IS_SET(MODE_ALTSCREEN)) 699 for (; i >= 0 && !(line[i].mode & ATTR_WRAP) && line[i].u == ' '; i--); 700 else 701 for (; i >= 0 && !(line[i].mode & (ATTR_SET | ATTR_WRAP)); i--); 702 703 return i + 1; 704 } 705 706 int 707 tiswrapped(Line line) 708 { 709 int len = tlinelen(line); 710 711 return len > 0 && (line[len - 1].mode & ATTR_WRAP); 712 } 713 714 char * 715 tgetglyphs(char *buf, const Glyph *gp, const Glyph *lgp) 716 { 717 while (gp <= lgp) 718 if (gp->mode & ATTR_WDUMMY) { 719 gp++; 720 } else { 721 buf += utf8encode((gp++)->u, buf); 722 } 723 return buf; 724 } 725 726 size_t 727 tgetline(char *buf, const Glyph *fgp) 728 { 729 char *ptr; 730 const Glyph *lgp = &fgp[term.col - 1]; 731 732 while (lgp > fgp && !(lgp->mode & (ATTR_SET | ATTR_WRAP))) 733 lgp--; 734 ptr = tgetglyphs(buf, fgp, lgp); 735 if (!(lgp->mode & ATTR_WRAP)) 736 *(ptr++) = '\n'; 737 return ptr - buf; 738 } 739 740 int 741 regionselected(int x1, int y1, int x2, int y2) 742 { 743 if (sel.ob.x == -1 || sel.mode == SEL_EMPTY || 744 sel.alt != IS_SET(MODE_ALTSCREEN) || sel.nb.y > y2 || sel.ne.y < y1) 745 return 0; 746 747 return (sel.type == SEL_RECTANGULAR) ? sel.nb.x <= x2 && sel.ne.x >= x1 748 : (sel.nb.y != y2 || sel.nb.x <= x2) && 749 (sel.ne.y != y1 || sel.ne.x >= x1); 750 } 751 752 int 753 selected(int x, int y) 754 { 755 return regionselected(x, y, x, y); 756 } 757 758 void 759 selsnap(int *x, int *y, int direction) 760 { 761 int newx, newy; 762 int rtop = 0, rbot = term.row - 1; 763 int delim, prevdelim, maxlen; 764 const Glyph *gp, *prevgp; 765 766 if (!IS_SET(MODE_ALTSCREEN)) 767 rtop += -term.histf + term.scr, rbot += term.scr; 768 769 switch (sel.snap) { 770 case SNAP_WORD: 771 /* 772 * Snap around if the word wraps around at the end or 773 * beginning of a line. 774 */ 775 maxlen = (TLINE(*y)[term.col-2].mode & ATTR_WRAP) ? term.col-1 : term.col; 776 LIMIT(*x, 0, maxlen - 1); 777 prevgp = &TLINE(*y)[*x]; 778 prevdelim = ISDELIM(prevgp->u); 779 for (;;) { 780 newx = *x + direction; 781 newy = *y; 782 if (!BETWEEN(newx, 0, maxlen - 1)) { 783 newy += direction; 784 if (!BETWEEN(newy, rtop, rbot)) 785 break; 786 787 if (!tiswrapped(TLINE(direction > 0 ? *y : newy))) 788 break; 789 790 maxlen = (TLINE(newy)[term.col-2].mode & ATTR_WRAP) ? term.col-1 : term.col; 791 newx = direction > 0 ? 0 : maxlen - 1; 792 } 793 794 gp = &TLINE(newy)[newx]; 795 delim = ISDELIM(gp->u); 796 if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim 797 || (delim && gp->u != prevgp->u))) 798 break; 799 800 *x = newx; 801 *y = newy; 802 if (!(gp->mode & ATTR_WDUMMY)) { 803 prevgp = gp; 804 prevdelim = delim; 805 } 806 } 807 break; 808 case SNAP_LINE: 809 /* 810 * Snap around if the the previous line or the current one 811 * has set ATTR_WRAP at its end. Then the whole next or 812 * previous line will be selected. 813 */ 814 *x = (direction < 0) ? 0 : term.col - 1; 815 if (direction < 0) { 816 for (; *y > rtop; *y -= 1) { 817 if (!tiswrapped(TLINE(*y-1))) 818 break; 819 } 820 } else if (direction > 0) { 821 for (; *y < rbot; *y += 1) { 822 if (!tiswrapped(TLINE(*y))) 823 break; 824 } 825 } 826 break; 827 } 828 } 829 830 void 831 selscroll(int top, int bot, int n) 832 { 833 /* turn absolute coordinates into relative */ 834 top += term.scr, bot += term.scr; 835 836 if (BETWEEN(sel.nb.y, top, bot) != BETWEEN(sel.ne.y, top, bot)) { 837 selclear(); 838 } else if (BETWEEN(sel.nb.y, top, bot)) { 839 selmove(n); 840 if (sel.nb.y < top || sel.ne.y > bot) 841 selclear(); 842 } 843 } 844 845 void 846 tswapscreen(void) 847 { 848 static Line *altline; 849 static int altcol, altrow; 850 Line *tmpline = term.line; 851 int tmpcol = term.col, tmprow = term.row; 852 #if SIXEL_PATCH 853 ImageList *im = term.images; 854 #endif // SIXEL_PATCH 855 856 term.line = altline; 857 term.col = altcol, term.row = altrow; 858 altline = tmpline; 859 altcol = tmpcol, altrow = tmprow; 860 term.mode ^= MODE_ALTSCREEN; 861 862 #if SIXEL_PATCH 863 term.images = term.images_alt; 864 term.images_alt = im; 865 #endif // SIXEL_PATCH 866 } 867 868 char * 869 getsel(void) 870 { 871 char *str, *ptr; 872 int y, lastx, linelen; 873 const Glyph *gp, *lgp; 874 875 if (sel.ob.x == -1 || sel.alt != IS_SET(MODE_ALTSCREEN)) 876 return NULL; 877 878 str = xmalloc((term.col + 1) * (sel.ne.y - sel.nb.y + 1) * UTF_SIZ); 879 ptr = str; 880 881 /* append every set & selected glyph to the selection */ 882 for (y = sel.nb.y; y <= sel.ne.y; y++) { 883 Line line = TLINE(y); 884 885 if ((linelen = tlinelen(line)) == 0) { 886 *ptr++ = '\n'; 887 continue; 888 } 889 890 if (sel.type == SEL_RECTANGULAR) { 891 gp = &line[sel.nb.x]; 892 lastx = sel.ne.x; 893 } else { 894 gp = &line[sel.nb.y == y ? sel.nb.x : 0]; 895 lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; 896 } 897 lgp = &line[MIN(lastx, linelen-1)]; 898 899 ptr = tgetglyphs(ptr, gp, lgp); 900 /* 901 * Copy and pasting of line endings is inconsistent 902 * in the inconsistent terminal and GUI world. 903 * The best solution seems like to produce '\n' when 904 * something is copied from st and convert '\n' to 905 * '\r', when something to be pasted is received by 906 * st. 907 * FIXME: Fix the computer world. 908 */ 909 if ((y < sel.ne.y || lastx >= linelen) && 910 (!(lgp->mode & ATTR_WRAP) || sel.type == SEL_RECTANGULAR)) 911 *ptr++ = '\n'; 912 } 913 *ptr = '\0'; 914 return str; 915 } 916 917 void 918 tdumpline(int n) 919 { 920 char str[(term.col + 1) * UTF_SIZ]; 921 922 tprinter(str, tgetline(str, &term.line[n][0])); 923 }