1 #include "vterm_internal.h" 2 3 #include <stdio.h> 4 #include <string.h> 5 6 #include "rect.h" 7 #include "utf8.h" 8 9 #define UNICODE_SPACE 0x20 10 #define UNICODE_LINEFEED 0x0a 11 12 /* State of the pen at some moment in time, also used in a cell */ 13 typedef struct 14 { 15 /* After the bitfield */ 16 VTermColor fg, bg; 17 18 unsigned int bold : 1; 19 unsigned int underline : 2; 20 unsigned int italic : 1; 21 unsigned int blink : 1; 22 unsigned int reverse : 1; 23 unsigned int strike : 1; 24 unsigned int font : 4; /* 0 to 9 */ 25 26 /* Extra state storage that isn't strictly pen-related */ 27 unsigned int protected_cell : 1; 28 unsigned int dwl : 1; /* on a DECDWL or DECDHL line */ 29 unsigned int dhl : 2; /* on a DECDHL line (1=top 2=bottom) */ 30 } ScreenPen; 31 32 /* Internal representation of a screen cell */ 33 typedef struct 34 { 35 uint32_t chars[VTERM_MAX_CHARS_PER_CELL]; 36 ScreenPen pen; 37 } ScreenCell; 38 39 static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell); 40 41 struct VTermScreen 42 { 43 VTerm *vt; 44 VTermState *state; 45 46 const VTermScreenCallbacks *callbacks; 47 void *cbdata; 48 49 VTermDamageSize damage_merge; 50 /* start_row == -1 => no damage */ 51 VTermRect damaged; 52 VTermRect pending_scrollrect; 53 int pending_scroll_downward, pending_scroll_rightward; 54 55 int rows; 56 int cols; 57 int global_reverse; 58 59 /* Primary and Altscreen. buffers[1] is lazily allocated as needed */ 60 ScreenCell *buffers[2]; 61 62 /* buffer will == buffers[0] or buffers[1], depending on altscreen */ 63 ScreenCell *buffer; 64 65 /* buffer for a single screen row used in scrollback storage callbacks */ 66 VTermScreenCell *sb_buffer; 67 68 ScreenPen pen; 69 }; 70 71 static inline ScreenCell *getcell(const VTermScreen *screen, int row, int col) 72 { 73 if(row < 0 || row >= screen->rows) 74 return NULL; 75 if(col < 0 || col >= screen->cols) 76 return NULL; 77 return screen->buffer + (screen->cols * row) + col; 78 } 79 80 static ScreenCell *realloc_buffer(VTermScreen *screen, ScreenCell *buffer, int new_rows, int new_cols) 81 { 82 ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt, sizeof(ScreenCell) * new_rows * new_cols); 83 84 for(int row = 0; row < new_rows; row++) { 85 for(int col = 0; col < new_cols; col++) { 86 ScreenCell *new_cell = new_buffer + row*new_cols + col; 87 88 if(buffer && row < screen->rows && col < screen->cols) 89 *new_cell = buffer[row * screen->cols + col]; 90 else { 91 new_cell->chars[0] = 0; 92 new_cell->pen = screen->pen; 93 } 94 } 95 } 96 97 if(buffer) 98 vterm_allocator_free(screen->vt, buffer); 99 100 return new_buffer; 101 } 102 103 static void damagerect(VTermScreen *screen, VTermRect rect) 104 { 105 VTermRect emit; 106 107 switch(screen->damage_merge) { 108 case VTERM_DAMAGE_CELL: 109 /* Always emit damage event */ 110 emit = rect; 111 break; 112 113 case VTERM_DAMAGE_ROW: 114 /* Emit damage longer than one row. Try to merge with existing damage in 115 * the same row */ 116 if(rect.end_row > rect.start_row + 1) { 117 // Bigger than 1 line - flush existing, emit this 118 vterm_screen_flush_damage(screen); 119 emit = rect; 120 } 121 else if(screen->damaged.start_row == -1) { 122 // None stored yet 123 screen->damaged = rect; 124 return; 125 } 126 else if(rect.start_row == screen->damaged.start_row) { 127 // Merge with the stored line 128 if(screen->damaged.start_col > rect.start_col) 129 screen->damaged.start_col = rect.start_col; 130 if(screen->damaged.end_col < rect.end_col) 131 screen->damaged.end_col = rect.end_col; 132 return; 133 } 134 else { 135 // Emit the currently stored line, store a new one 136 emit = screen->damaged; 137 screen->damaged = rect; 138 } 139 break; 140 141 case VTERM_DAMAGE_SCREEN: 142 case VTERM_DAMAGE_SCROLL: 143 /* Never emit damage event */ 144 if(screen->damaged.start_row == -1) 145 screen->damaged = rect; 146 else { 147 rect_expand(&screen->damaged, &rect); 148 } 149 return; 150 151 default: 152 fprintf(stderr, "TODO: Maybe merge damage for level %d\n", screen->damage_merge); 153 return; 154 } 155 156 if(screen->callbacks && screen->callbacks->damage) 157 (*screen->callbacks->damage)(emit, screen->cbdata); 158 } 159 160 static void damagescreen(VTermScreen *screen) 161 { 162 VTermRect rect = { 163 .start_row = 0, 164 .end_row = screen->rows, 165 .start_col = 0, 166 .end_col = screen->cols, 167 }; 168 169 damagerect(screen, rect); 170 } 171 172 static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user) 173 { 174 VTermScreen *screen = user; 175 ScreenCell *cell = getcell(screen, pos.row, pos.col); 176 177 if(!cell) 178 return 0; 179 180 int i; 181 for(i = 0; i < VTERM_MAX_CHARS_PER_CELL && info->chars[i]; i++) { 182 cell->chars[i] = info->chars[i]; 183 cell->pen = screen->pen; 184 } 185 if(i < VTERM_MAX_CHARS_PER_CELL) 186 cell->chars[i] = 0; 187 188 for(int col = 1; col < info->width; col++) 189 getcell(screen, pos.row, pos.col + col)->chars[0] = (uint32_t)-1; 190 191 VTermRect rect = { 192 .start_row = pos.row, 193 .end_row = pos.row+1, 194 .start_col = pos.col, 195 .end_col = pos.col+info->width, 196 }; 197 198 cell->pen.protected_cell = info->protected_cell; 199 cell->pen.dwl = info->dwl; 200 cell->pen.dhl = info->dhl; 201 202 damagerect(screen, rect); 203 204 return 1; 205 } 206 207 static int moverect_internal(VTermRect dest, VTermRect src, void *user) 208 { 209 VTermScreen *screen = user; 210 211 if(screen->callbacks && screen->callbacks->sb_pushline && 212 dest.start_row == 0 && dest.start_col == 0 && // starts top-left corner 213 dest.end_col == screen->cols && // full width 214 screen->buffer == screen->buffers[0]) { // not altscreen 215 VTermPos pos; 216 for(pos.row = 0; pos.row < src.start_row; pos.row++) { 217 for(pos.col = 0; pos.col < screen->cols; pos.col++) 218 vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col); 219 220 (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata); 221 } 222 } 223 224 int cols = src.end_col - src.start_col; 225 int downward = src.start_row - dest.start_row; 226 227 int init_row, test_row, inc_row; 228 if(downward < 0) { 229 init_row = dest.end_row - 1; 230 test_row = dest.start_row - 1; 231 inc_row = -1; 232 } 233 else { 234 init_row = dest.start_row; 235 test_row = dest.end_row; 236 inc_row = +1; 237 } 238 239 for(int row = init_row; row != test_row; row += inc_row) 240 memmove(getcell(screen, row, dest.start_col), 241 getcell(screen, row + downward, src.start_col), 242 cols * sizeof(ScreenCell)); 243 244 return 1; 245 } 246 247 static int moverect_user(VTermRect dest, VTermRect src, void *user) 248 { 249 VTermScreen *screen = user; 250 251 if(screen->callbacks && screen->callbacks->moverect) { 252 if(screen->damage_merge != VTERM_DAMAGE_SCROLL) 253 // Avoid an infinite loop 254 vterm_screen_flush_damage(screen); 255 256 if((*screen->callbacks->moverect)(dest, src, screen->cbdata)) 257 return 1; 258 } 259 260 damagerect(screen, dest); 261 262 return 1; 263 } 264 265 static int erase_internal(VTermRect rect, int selective, void *user) 266 { 267 VTermScreen *screen = user; 268 269 for(int row = rect.start_row; row < rect.end_row; row++) { 270 const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row); 271 272 for(int col = rect.start_col; col < rect.end_col; col++) { 273 ScreenCell *cell = getcell(screen, row, col); 274 275 if(selective && cell->pen.protected_cell) 276 continue; 277 278 cell->chars[0] = 0; 279 cell->pen = screen->pen; 280 cell->pen.dwl = info->doublewidth; 281 cell->pen.dhl = info->doubleheight; 282 } 283 } 284 285 return 1; 286 } 287 288 static int erase_user(VTermRect rect, int selective, void *user) 289 { 290 VTermScreen *screen = user; 291 292 damagerect(screen, rect); 293 294 return 1; 295 } 296 297 static int erase(VTermRect rect, int selective, void *user) 298 { 299 erase_internal(rect, selective, user); 300 return erase_user(rect, 0, user); 301 } 302 303 static int scrollrect(VTermRect rect, int downward, int rightward, void *user) 304 { 305 VTermScreen *screen = user; 306 307 vterm_scroll_rect(rect, downward, rightward, 308 moverect_internal, erase_internal, screen); 309 310 if(screen->damage_merge != VTERM_DAMAGE_SCROLL) { 311 vterm_screen_flush_damage(screen); 312 313 vterm_scroll_rect(rect, downward, rightward, 314 moverect_user, erase_user, screen); 315 316 return 1; 317 } 318 319 if(screen->damaged.start_row != -1 && 320 !rect_intersects(&rect, &screen->damaged)) { 321 vterm_screen_flush_damage(screen); 322 } 323 324 if(screen->pending_scrollrect.start_row == -1) { 325 screen->pending_scrollrect = rect; 326 screen->pending_scroll_downward = downward; 327 screen->pending_scroll_rightward = rightward; 328 } 329 else if(rect_equal(&screen->pending_scrollrect, &rect) && 330 ((screen->pending_scroll_downward == 0 && downward == 0) || 331 (screen->pending_scroll_rightward == 0 && rightward == 0))) { 332 screen->pending_scroll_downward += downward; 333 screen->pending_scroll_rightward += rightward; 334 } 335 else { 336 vterm_screen_flush_damage(screen); 337 338 screen->pending_scrollrect = rect; 339 screen->pending_scroll_downward = downward; 340 screen->pending_scroll_rightward = rightward; 341 } 342 343 if(screen->damaged.start_row == -1) 344 return 1; 345 346 if(rect_contains(&rect, &screen->damaged)) { 347 vterm_rect_move(&screen->damaged, -downward, -rightward); 348 rect_clip(&screen->damaged, &rect); 349 } 350 /* There are a number of possible cases here, but lets restrict this to only 351 * the common case where we might actually gain some performance by 352 * optimising it. Namely, a vertical scroll that neatly cuts the damage 353 * region in half. 354 */ 355 else if(rect.start_col <= screen->damaged.start_col && 356 rect.end_col >= screen->damaged.end_col && 357 rightward == 0) { 358 if(screen->damaged.start_row >= rect.start_row && 359 screen->damaged.start_row < rect.end_row) { 360 screen->damaged.start_row -= downward; 361 if(screen->damaged.start_row < rect.start_row) 362 screen->damaged.start_row = rect.start_row; 363 if(screen->damaged.start_row > rect.end_row) 364 screen->damaged.start_row = rect.end_row; 365 } 366 if(screen->damaged.end_row >= rect.start_row && 367 screen->damaged.end_row < rect.end_row) { 368 screen->damaged.end_row -= downward; 369 if(screen->damaged.end_row < rect.start_row) 370 screen->damaged.end_row = rect.start_row; 371 if(screen->damaged.end_row > rect.end_row) 372 screen->damaged.end_row = rect.end_row; 373 } 374 } 375 else { 376 fprintf(stderr, "TODO: Just flush and redo damaged=" STRFrect " rect=" STRFrect "\n", 377 ARGSrect(screen->damaged), ARGSrect(rect)); 378 } 379 380 return 1; 381 } 382 383 static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user) 384 { 385 VTermScreen *screen = user; 386 387 if(screen->callbacks && screen->callbacks->movecursor) 388 return (*screen->callbacks->movecursor)(pos, oldpos, visible, screen->cbdata); 389 390 return 0; 391 } 392 393 static int setpenattr(VTermAttr attr, VTermValue *val, void *user) 394 { 395 VTermScreen *screen = user; 396 397 switch(attr) { 398 case VTERM_ATTR_BOLD: 399 screen->pen.bold = val->boolean; 400 return 1; 401 case VTERM_ATTR_UNDERLINE: 402 screen->pen.underline = val->number; 403 return 1; 404 case VTERM_ATTR_ITALIC: 405 screen->pen.italic = val->boolean; 406 return 1; 407 case VTERM_ATTR_BLINK: 408 screen->pen.blink = val->boolean; 409 return 1; 410 case VTERM_ATTR_REVERSE: 411 screen->pen.reverse = val->boolean; 412 return 1; 413 case VTERM_ATTR_STRIKE: 414 screen->pen.strike = val->boolean; 415 return 1; 416 case VTERM_ATTR_FONT: 417 screen->pen.font = val->number; 418 return 1; 419 case VTERM_ATTR_FOREGROUND: 420 screen->pen.fg = val->color; 421 return 1; 422 case VTERM_ATTR_BACKGROUND: 423 screen->pen.bg = val->color; 424 return 1; 425 } 426 427 return 0; 428 } 429 430 static int settermprop(VTermProp prop, VTermValue *val, void *user) 431 { 432 VTermScreen *screen = user; 433 434 switch(prop) { 435 case VTERM_PROP_ALTSCREEN: 436 if(val->boolean && !screen->buffers[1]) 437 return 0; 438 439 screen->buffer = val->boolean ? screen->buffers[1] : screen->buffers[0]; 440 /* only send a damage event on disable; because during enable there's an 441 * erase that sends a damage anyway 442 */ 443 if(!val->boolean) 444 damagescreen(screen); 445 break; 446 case VTERM_PROP_REVERSE: 447 screen->global_reverse = val->boolean; 448 damagescreen(screen); 449 break; 450 default: 451 ; /* ignore */ 452 } 453 454 if(screen->callbacks && screen->callbacks->settermprop) 455 return (*screen->callbacks->settermprop)(prop, val, screen->cbdata); 456 457 return 1; 458 } 459 460 static int setmousefunc(VTermMouseFunc func, void *data, void *user) 461 { 462 VTermScreen *screen = user; 463 464 if(screen->callbacks && screen->callbacks->setmousefunc) 465 return (*screen->callbacks->setmousefunc)(func, data, screen->cbdata); 466 467 return 0; 468 } 469 470 static int bell(void *user) 471 { 472 VTermScreen *screen = user; 473 474 if(screen->callbacks && screen->callbacks->bell) 475 return (*screen->callbacks->bell)(screen->cbdata); 476 477 return 0; 478 } 479 480 static int resize(int new_rows, int new_cols, VTermPos *delta, void *user) 481 { 482 VTermScreen *screen = user; 483 484 int is_altscreen = (screen->buffers[1] && screen->buffer == screen->buffers[1]); 485 486 int old_rows = screen->rows; 487 int old_cols = screen->cols; 488 489 if(!is_altscreen && new_rows < old_rows) { 490 // Fewer rows - determine if we're going to scroll at all, and if so, push 491 // those lines to scrollback 492 VTermPos pos = { 0, 0 }; 493 for(pos.row = old_rows - 1; pos.row >= new_rows; pos.row--) 494 if(!vterm_screen_is_eol(screen, pos)) 495 break; 496 497 int first_blank_row = pos.row + 1; 498 if(first_blank_row > new_rows) { 499 VTermRect rect = { 500 .start_row = 0, 501 .end_row = old_rows, 502 .start_col = 0, 503 .end_col = old_cols, 504 }; 505 scrollrect(rect, first_blank_row - new_rows, 0, user); 506 vterm_screen_flush_damage(screen); 507 508 delta->row -= first_blank_row - new_rows; 509 } 510 } 511 512 screen->buffers[0] = realloc_buffer(screen, screen->buffers[0], new_rows, new_cols); 513 if(screen->buffers[1]) 514 screen->buffers[1] = realloc_buffer(screen, screen->buffers[1], new_rows, new_cols); 515 516 screen->buffer = is_altscreen ? screen->buffers[1] : screen->buffers[0]; 517 518 screen->rows = new_rows; 519 screen->cols = new_cols; 520 521 if(screen->sb_buffer) 522 vterm_allocator_free(screen->vt, screen->sb_buffer); 523 524 screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * new_cols); 525 526 if(new_cols > old_cols) { 527 VTermRect rect = { 528 .start_row = 0, 529 .end_row = old_rows, 530 .start_col = old_cols, 531 .end_col = new_cols, 532 }; 533 damagerect(screen, rect); 534 } 535 536 if(new_rows > old_rows) { 537 if(!is_altscreen && screen->callbacks && screen->callbacks->sb_popline) { 538 int rows = new_rows - old_rows; 539 while(rows) { 540 if(!(screen->callbacks->sb_popline(screen->cols, screen->sb_buffer, screen->cbdata))) 541 break; 542 543 VTermRect rect = { 544 .start_row = 0, 545 .end_row = screen->rows, 546 .start_col = 0, 547 .end_col = screen->cols, 548 }; 549 scrollrect(rect, -1, 0, user); 550 551 VTermPos pos = { 0, 0 }; 552 for(pos.col = 0; pos.col < screen->cols; pos.col += screen->sb_buffer[pos.col].width) 553 vterm_screen_set_cell(screen, pos, screen->sb_buffer + pos.col); 554 555 rect.end_row = 1; 556 damagerect(screen, rect); 557 558 vterm_screen_flush_damage(screen); 559 560 rows--; 561 delta->row++; 562 } 563 } 564 565 VTermRect rect = { 566 .start_row = old_rows, 567 .end_row = new_rows, 568 .start_col = 0, 569 .end_col = new_cols, 570 }; 571 damagerect(screen, rect); 572 } 573 574 if(screen->callbacks && screen->callbacks->resize) 575 return (*screen->callbacks->resize)(new_rows, new_cols, screen->cbdata); 576 577 return 1; 578 } 579 580 static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user) 581 { 582 VTermScreen *screen = user; 583 584 if(newinfo->doublewidth != oldinfo->doublewidth || 585 newinfo->doubleheight != oldinfo->doubleheight) { 586 for(int col = 0; col < screen->cols; col++) { 587 ScreenCell *cell = getcell(screen, row, col); 588 cell->pen.dwl = newinfo->doublewidth; 589 cell->pen.dhl = newinfo->doubleheight; 590 } 591 592 VTermRect rect = { 593 .start_row = row, 594 .end_row = row + 1, 595 .start_col = 0, 596 .end_col = newinfo->doublewidth ? screen->cols / 2 : screen->cols, 597 }; 598 damagerect(screen, rect); 599 600 if(newinfo->doublewidth) { 601 rect.start_col = screen->cols / 2; 602 rect.end_col = screen->cols; 603 604 erase_internal(rect, 0, user); 605 } 606 } 607 608 return 1; 609 } 610 611 static VTermStateCallbacks state_cbs = { 612 .putglyph = &putglyph, 613 .movecursor = &movecursor, 614 .scrollrect = &scrollrect, 615 .erase = &erase, 616 .setpenattr = &setpenattr, 617 .settermprop = &settermprop, 618 .setmousefunc = &setmousefunc, 619 .bell = &bell, 620 .resize = &resize, 621 .setlineinfo = &setlineinfo, 622 }; 623 624 static VTermScreen *screen_new(VTerm *vt) 625 { 626 VTermState *state = vterm_obtain_state(vt); 627 if(!state) 628 return NULL; 629 630 VTermScreen *screen = vterm_allocator_malloc(vt, sizeof(VTermScreen)); 631 int rows, cols; 632 633 vterm_get_size(vt, &rows, &cols); 634 635 screen->vt = vt; 636 screen->state = state; 637 638 screen->damage_merge = VTERM_DAMAGE_CELL; 639 screen->damaged.start_row = -1; 640 screen->pending_scrollrect.start_row = -1; 641 642 screen->rows = rows; 643 screen->cols = cols; 644 645 screen->buffers[0] = realloc_buffer(screen, NULL, rows, cols); 646 647 screen->buffer = screen->buffers[0]; 648 649 screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * cols); 650 651 vterm_state_set_callbacks(screen->state, &state_cbs, screen); 652 653 return screen; 654 } 655 656 INTERNAL void vterm_screen_free(VTermScreen *screen) 657 { 658 vterm_allocator_free(screen->vt, screen->buffers[0]); 659 if(screen->buffers[1]) 660 vterm_allocator_free(screen->vt, screen->buffers[1]); 661 662 vterm_allocator_free(screen->vt, screen->sb_buffer); 663 664 vterm_allocator_free(screen->vt, screen); 665 } 666 667 void vterm_screen_reset(VTermScreen *screen, int hard) 668 { 669 screen->damaged.start_row = -1; 670 screen->pending_scrollrect.start_row = -1; 671 vterm_state_reset(screen->state, hard); 672 vterm_screen_flush_damage(screen); 673 } 674 675 static size_t _get_chars(const VTermScreen *screen, const int utf8, void *buffer, size_t len, const VTermRect rect) 676 { 677 size_t outpos = 0; 678 int padding = 0; 679 680 #define PUT(c) \ 681 if(utf8) { \ 682 size_t thislen = utf8_seqlen(c); \ 683 if(buffer && outpos + thislen <= len) \ 684 outpos += fill_utf8((c), (char *)buffer + outpos); \ 685 else \ 686 outpos += thislen; \ 687 } \ 688 else { \ 689 if(buffer && outpos + 1 <= len) \ 690 ((uint32_t*)buffer)[outpos++] = (c); \ 691 else \ 692 outpos++; \ 693 } 694 695 for(int row = rect.start_row; row < rect.end_row; row++) { 696 for(int col = rect.start_col; col < rect.end_col; col++) { 697 ScreenCell *cell = getcell(screen, row, col); 698 699 if(cell->chars[0] == 0) 700 // Erased cell, might need a space 701 padding++; 702 else if(cell->chars[0] == (uint32_t)-1) 703 // Gap behind a double-width char, do nothing 704 ; 705 else { 706 while(padding) { 707 PUT(UNICODE_SPACE); 708 padding--; 709 } 710 for(int i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell->chars[i]; i++) { 711 PUT(cell->chars[i]); 712 } 713 } 714 } 715 716 if(row < rect.end_row - 1) { 717 PUT(UNICODE_LINEFEED); 718 padding = 0; 719 } 720 } 721 722 return outpos; 723 } 724 725 size_t vterm_screen_get_chars(const VTermScreen *screen, uint32_t *chars, size_t len, const VTermRect rect) 726 { 727 return _get_chars(screen, 0, chars, len, rect); 728 } 729 730 size_t vterm_screen_get_text(const VTermScreen *screen, char *str, size_t len, const VTermRect rect) 731 { 732 return _get_chars(screen, 1, str, len, rect); 733 } 734 735 /* Copy internal to external representation of a screen cell */ 736 int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell) 737 { 738 ScreenCell *intcell = getcell(screen, pos.row, pos.col); 739 if(!intcell) 740 return 0; 741 742 for(int i = 0; ; i++) { 743 cell->chars[i] = intcell->chars[i]; 744 if(!intcell->chars[i]) 745 break; 746 } 747 748 cell->attrs.bold = intcell->pen.bold; 749 cell->attrs.underline = intcell->pen.underline; 750 cell->attrs.italic = intcell->pen.italic; 751 cell->attrs.blink = intcell->pen.blink; 752 cell->attrs.reverse = intcell->pen.reverse ^ screen->global_reverse; 753 cell->attrs.strike = intcell->pen.strike; 754 cell->attrs.font = intcell->pen.font; 755 756 cell->attrs.dwl = intcell->pen.dwl; 757 cell->attrs.dhl = intcell->pen.dhl; 758 759 cell->fg = intcell->pen.fg; 760 cell->bg = intcell->pen.bg; 761 762 if(pos.col < (screen->cols - 1) && 763 getcell(screen, pos.row, pos.col + 1)->chars[0] == (uint32_t)-1) 764 cell->width = 2; 765 else 766 cell->width = 1; 767 768 return 1; 769 } 770 771 /* Copy external to internal representation of a screen cell */ 772 /* static because it's only used internally for sb_popline during resize */ 773 static int vterm_screen_set_cell(VTermScreen *screen, VTermPos pos, const VTermScreenCell *cell) 774 { 775 ScreenCell *intcell = getcell(screen, pos.row, pos.col); 776 if(!intcell) 777 return 0; 778 779 for(int i = 0; ; i++) { 780 intcell->chars[i] = cell->chars[i]; 781 if(!cell->chars[i]) 782 break; 783 } 784 785 intcell->pen.bold = cell->attrs.bold; 786 intcell->pen.underline = cell->attrs.underline; 787 intcell->pen.italic = cell->attrs.italic; 788 intcell->pen.blink = cell->attrs.blink; 789 intcell->pen.reverse = cell->attrs.reverse ^ screen->global_reverse; 790 intcell->pen.strike = cell->attrs.strike; 791 intcell->pen.font = cell->attrs.font; 792 793 intcell->pen.fg = cell->fg; 794 intcell->pen.bg = cell->bg; 795 796 if(cell->width == 2) 797 getcell(screen, pos.row, pos.col + 1)->chars[0] = (uint32_t)-1; 798 799 return 1; 800 } 801 802 int vterm_screen_is_eol(const VTermScreen *screen, VTermPos pos) 803 { 804 /* This cell is EOL if this and every cell to the right is black */ 805 for(; pos.col < screen->cols; pos.col++) { 806 ScreenCell *cell = getcell(screen, pos.row, pos.col); 807 if(cell->chars[0] != 0) 808 return 0; 809 } 810 811 return 1; 812 } 813 814 VTermScreen *vterm_obtain_screen(VTerm *vt) 815 { 816 if(vt->screen) 817 return vt->screen; 818 819 VTermScreen *screen = screen_new(vt); 820 vt->screen = screen; 821 822 return screen; 823 } 824 825 void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen) 826 { 827 828 if(!screen->buffers[1] && altscreen) { 829 int rows, cols; 830 vterm_get_size(screen->vt, &rows, &cols); 831 832 screen->buffers[1] = realloc_buffer(screen, NULL, rows, cols); 833 } 834 } 835 836 void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user) 837 { 838 screen->callbacks = callbacks; 839 screen->cbdata = user; 840 } 841 842 void vterm_screen_flush_damage(VTermScreen *screen) 843 { 844 if(screen->pending_scrollrect.start_row != -1) { 845 vterm_scroll_rect(screen->pending_scrollrect, screen->pending_scroll_downward, screen->pending_scroll_rightward, 846 moverect_user, erase_user, screen); 847 848 screen->pending_scrollrect.start_row = -1; 849 } 850 851 if(screen->damaged.start_row != -1) { 852 if(screen->callbacks && screen->callbacks->damage) 853 (*screen->callbacks->damage)(screen->damaged, screen->cbdata); 854 855 screen->damaged.start_row = -1; 856 } 857 } 858 859 void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size) 860 { 861 vterm_screen_flush_damage(screen); 862 screen->damage_merge = size; 863 } 864 865 static int attrs_differ(VTermAttrMask attrs, ScreenCell *a, ScreenCell *b) 866 { 867 if((attrs & VTERM_ATTR_BOLD_MASK) && (a->pen.bold != b->pen.bold)) 868 return 1; 869 if((attrs & VTERM_ATTR_UNDERLINE_MASK) && (a->pen.underline != b->pen.underline)) 870 return 1; 871 if((attrs & VTERM_ATTR_ITALIC_MASK) && (a->pen.italic != b->pen.italic)) 872 return 1; 873 if((attrs & VTERM_ATTR_BLINK_MASK) && (a->pen.blink != b->pen.blink)) 874 return 1; 875 if((attrs & VTERM_ATTR_REVERSE_MASK) && (a->pen.reverse != b->pen.reverse)) 876 return 1; 877 if((attrs & VTERM_ATTR_STRIKE_MASK) && (a->pen.strike != b->pen.strike)) 878 return 1; 879 if((attrs & VTERM_ATTR_FONT_MASK) && (a->pen.font != b->pen.font)) 880 return 1; 881 if((attrs & VTERM_ATTR_FOREGROUND_MASK) && !vterm_color_equal(a->pen.fg, b->pen.fg)) 882 return 1; 883 if((attrs & VTERM_ATTR_BACKGROUND_MASK) && !vterm_color_equal(a->pen.bg, b->pen.bg)) 884 return 1; 885 886 return 0; 887 } 888 889 int vterm_screen_get_attrs_extent(const VTermScreen *screen, VTermRect *extent, VTermPos pos, VTermAttrMask attrs) 890 { 891 ScreenCell *target = getcell(screen, pos.row, pos.col); 892 893 // TODO: bounds check 894 extent->start_row = pos.row; 895 extent->end_row = pos.row + 1; 896 897 if(extent->start_col < 0) 898 extent->start_col = 0; 899 if(extent->end_col < 0) 900 extent->end_col = screen->cols; 901 902 int col; 903 904 for(col = pos.col - 1; col >= extent->start_col; col--) 905 if(attrs_differ(attrs, target, getcell(screen, pos.row, col))) 906 break; 907 extent->start_col = col + 1; 908 909 for(col = pos.col + 1; col < extent->end_col; col++) 910 if(attrs_differ(attrs, target, getcell(screen, pos.row, col))) 911 break; 912 extent->end_col = col - 1; 913 914 return 1; 915 } 916