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