1#include "vterm_internal.h" 2 3#include <stdio.h> 4#include <string.h> 5 6#define strneq(a,b,n) (strncmp(a,b,n)==0) 7 8#include "utf8.h" 9 10#ifdef DEBUG_LIBVTERM 11# define DEBUG_GLYPH_COMBINE 12#endif 13 14#define MOUSE_WANT_CLICK 0x01 15#define MOUSE_WANT_DRAG 0x02 16#define MOUSE_WANT_MOVE 0x04 17 18/* Some convenient wrappers to make callback functions easier */ 19 20static void putglyph(VTermState *state, const uint32_t chars[], int width, VTermPos pos) 21{ 22 VTermGlyphInfo info = { 23 .chars = chars, 24 .width = width, 25 .protected_cell = state->protected_cell, 26 }; 27 28 if(state->callbacks && state->callbacks->putglyph) 29 if((*state->callbacks->putglyph)(&info, pos, state->cbdata)) 30 return; 31 32 fprintf(stderr, "libvterm: Unhandled putglyph U+%04x at (%d,%d)\n", chars[0], pos.col, pos.row); 33} 34 35static void updatecursor(VTermState *state, VTermPos *oldpos, int cancel_phantom) 36{ 37 if(state->pos.col == oldpos->col && state->pos.row == oldpos->row) 38 return; 39 40 if(cancel_phantom) 41 state->at_phantom = 0; 42 43 if(state->callbacks && state->callbacks->movecursor) 44 if((*state->callbacks->movecursor)(state->pos, *oldpos, state->mode.cursor_visible, state->cbdata)) 45 return; 46} 47 48static void erase(VTermState *state, VTermRect rect, int selective) 49{ 50 if(state->callbacks && state->callbacks->erase) 51 if((*state->callbacks->erase)(rect, selective, state->cbdata)) 52 return; 53} 54 55static VTermState *vterm_state_new(VTerm *vt) 56{ 57 VTermState *state = vterm_allocator_malloc(vt, sizeof(VTermState)); 58 59 state->vt = vt; 60 61 state->rows = vt->rows; 62 state->cols = vt->cols; 63 64 // 90% grey so that pure white is brighter 65 state->default_fg.red = state->default_fg.green = state->default_fg.blue = 240; 66 state->default_bg.red = state->default_bg.green = state->default_bg.blue = 0; 67 68 state->bold_is_highbright = 0; 69 70 return state; 71} 72 73void vterm_state_free(VTermState *state) 74{ 75 vterm_allocator_free(state->vt, state->combine_chars); 76 vterm_allocator_free(state->vt, state); 77} 78 79static void scroll(VTermState *state, VTermRect rect, int downward, int rightward) 80{ 81 if(!downward && !rightward) 82 return; 83 84 if(state->callbacks && state->callbacks->scrollrect) 85 if((*state->callbacks->scrollrect)(rect, downward, rightward, state->cbdata)) 86 return; 87 88 if(state->callbacks) 89 vterm_scroll_rect(rect, downward, rightward, 90 state->callbacks->moverect, state->callbacks->erase, state->cbdata); 91} 92 93static void linefeed(VTermState *state) 94{ 95 if(state->pos.row == SCROLLREGION_BOTTOM(state) - 1) { 96 VTermRect rect = { 97 .start_row = state->scrollregion_top, 98 .end_row = SCROLLREGION_BOTTOM(state), 99 .start_col = SCROLLREGION_LEFT(state), 100 .end_col = SCROLLREGION_RIGHT(state), 101 }; 102 103 scroll(state, rect, 1, 0); 104 } 105 else if(state->pos.row < state->rows-1) 106 state->pos.row++; 107} 108 109static void grow_combine_buffer(VTermState *state) 110{ 111 size_t new_size = state->combine_chars_size * 2; 112 uint32_t *new_chars = vterm_allocator_malloc(state->vt, new_size * sizeof(new_chars[0])); 113 114 memcpy(new_chars, state->combine_chars, state->combine_chars_size * sizeof(new_chars[0])); 115 116 vterm_allocator_free(state->vt, state->combine_chars); 117 state->combine_chars = new_chars; 118} 119 120static void set_col_tabstop(VTermState *state, int col) 121{ 122 unsigned char mask = 1 << (col & 7); 123 state->tabstops[col >> 3] |= mask; 124} 125 126static void clear_col_tabstop(VTermState *state, int col) 127{ 128 unsigned char mask = 1 << (col & 7); 129 state->tabstops[col >> 3] &= ~mask; 130} 131 132static int is_col_tabstop(VTermState *state, int col) 133{ 134 unsigned char mask = 1 << (col & 7); 135 return state->tabstops[col >> 3] & mask; 136} 137 138static void tab(VTermState *state, int count, int direction) 139{ 140 while(count--) 141 while(state->pos.col >= 0 && state->pos.col < state->cols-1) { 142 state->pos.col += direction; 143 144 if(is_col_tabstop(state, state->pos.col)) 145 break; 146 } 147} 148 149static int on_text(const char bytes[], size_t len, void *user) 150{ 151 VTermState *state = user; 152 153 VTermPos oldpos = state->pos; 154 155 // We'll have at most len codepoints 156 uint32_t codepoints[len]; 157 int npoints = 0; 158 size_t eaten = 0; 159 160 VTermEncodingInstance *encoding = 161 !(bytes[eaten] & 0x80) ? &state->encoding[state->gl_set] : 162 state->vt->mode.utf8 ? &state->encoding_utf8 : 163 &state->encoding[state->gr_set]; 164 165 (*encoding->enc->decode)(encoding->enc, encoding->data, 166 codepoints, &npoints, len, bytes, &eaten, len); 167 168 int i = 0; 169 170 /* This is a combining char. that needs to be merged with the previous 171 * glyph output */ 172 if(vterm_unicode_is_combining(codepoints[i])) { 173 /* See if the cursor has moved since */ 174 if(state->pos.row == state->combine_pos.row && state->pos.col == state->combine_pos.col + state->combine_width) { 175#ifdef DEBUG_GLYPH_COMBINE 176 int printpos; 177 printf("DEBUG: COMBINING SPLIT GLYPH of chars {"); 178 for(printpos = 0; state->combine_chars[printpos]; printpos++) 179 printf("U+%04x ", state->combine_chars[printpos]); 180 printf("} + {"); 181#endif 182 183 /* Find where we need to append these combining chars */ 184 int saved_i = 0; 185 while(state->combine_chars[saved_i]) 186 saved_i++; 187 188 /* Add extra ones */ 189 while(i < npoints && vterm_unicode_is_combining(codepoints[i])) { 190 if(saved_i >= state->combine_chars_size) 191 grow_combine_buffer(state); 192 state->combine_chars[saved_i++] = codepoints[i++]; 193 } 194 if(saved_i >= state->combine_chars_size) 195 grow_combine_buffer(state); 196 state->combine_chars[saved_i] = 0; 197 198#ifdef DEBUG_GLYPH_COMBINE 199 for(; state->combine_chars[printpos]; printpos++) 200 printf("U+%04x ", state->combine_chars[printpos]); 201 printf("}\n"); 202#endif 203 204 /* Now render it */ 205 putglyph(state, state->combine_chars, state->combine_width, state->combine_pos); 206 } 207 else { 208 fprintf(stderr, "libvterm: TODO: Skip over split char+combining\n"); 209 } 210 } 211 212 for(; i < npoints; i++) { 213 // Try to find combining characters following this 214 int glyph_starts = i; 215 int glyph_ends; 216 for(glyph_ends = i + 1; glyph_ends < npoints; glyph_ends++) 217 if(!vterm_unicode_is_combining(codepoints[glyph_ends])) 218 break; 219 220 int width = 0; 221 222 uint32_t chars[glyph_ends - glyph_starts + 1]; 223 224 for( ; i < glyph_ends; i++) { 225 chars[i - glyph_starts] = codepoints[i]; 226 width += vterm_unicode_width(codepoints[i]); 227 } 228 229 chars[glyph_ends - glyph_starts] = 0; 230 i--; 231 232#ifdef DEBUG_GLYPH_COMBINE 233 int printpos; 234 printf("DEBUG: COMBINED GLYPH of %d chars {", glyph_ends - glyph_starts); 235 for(printpos = 0; printpos < glyph_ends - glyph_starts; printpos++) 236 printf("U+%04x ", chars[printpos]); 237 printf("}, onscreen width %d\n", width); 238#endif 239 240 if(state->at_phantom) { 241 linefeed(state); 242 state->pos.col = 0; 243 state->at_phantom = 0; 244 } 245 246 if(state->mode.insert) { 247 /* TODO: This will be a little inefficient for large bodies of text, as 248 * it'll have to 'ICH' effectively before every glyph. We should scan 249 * ahead and ICH as many times as required 250 */ 251 VTermRect rect = { 252 .start_row = state->pos.row, 253 .end_row = state->pos.row + 1, 254 .start_col = state->pos.col, 255 .end_col = state->cols, 256 }; 257 scroll(state, rect, 0, -1); 258 } 259 putglyph(state, chars, width, state->pos); 260 261 if(i == npoints - 1) { 262 /* End of the buffer. Save the chars in case we have to combine with 263 * more on the next call */ 264 int save_i; 265 for(save_i = 0; chars[save_i]; save_i++) { 266 if(save_i >= state->combine_chars_size) 267 grow_combine_buffer(state); 268 state->combine_chars[save_i] = chars[save_i]; 269 } 270 if(save_i >= state->combine_chars_size) 271 grow_combine_buffer(state); 272 state->combine_chars[save_i] = 0; 273 state->combine_width = width; 274 state->combine_pos = state->pos; 275 } 276 277 if(state->pos.col + width >= state->cols) { 278 if(state->mode.autowrap) 279 state->at_phantom = 1; 280 } 281 else { 282 state->pos.col += width; 283 } 284 } 285 286 updatecursor(state, &oldpos, 0); 287 288 return eaten; 289} 290 291static int on_control(unsigned char control, void *user) 292{ 293 VTermState *state = user; 294 295 VTermPos oldpos = state->pos; 296 297 switch(control) { 298 case 0x07: // BEL - ECMA-48 8.3.3 299 if(state->callbacks && state->callbacks->bell) 300 (*state->callbacks->bell)(state->cbdata); 301 break; 302 303 case 0x08: // BS - ECMA-48 8.3.5 304 if(state->pos.col > 0) 305 state->pos.col--; 306 break; 307 308 case 0x09: // HT - ECMA-48 8.3.60 309 tab(state, 1, +1); 310 break; 311 312 case 0x0a: // LF - ECMA-48 8.3.74 313 case 0x0b: // VT 314 case 0x0c: // FF 315 linefeed(state); 316 if(state->mode.newline) 317 state->pos.col = 0; 318 break; 319 320 case 0x0d: // CR - ECMA-48 8.3.15 321 state->pos.col = 0; 322 break; 323 324 case 0x0e: // LS1 - ECMA-48 8.3.76 325 state->gl_set = 1; 326 break; 327 328 case 0x0f: // LS0 - ECMA-48 8.3.75 329 state->gl_set = 0; 330 break; 331 332 case 0x84: // IND - DEPRECATED but implemented for completeness 333 linefeed(state); 334 break; 335 336 case 0x85: // NEL - ECMA-48 8.3.86 337 linefeed(state); 338 state->pos.col = 0; 339 break; 340 341 case 0x88: // HTS - ECMA-48 8.3.62 342 set_col_tabstop(state, state->pos.col); 343 break; 344 345 case 0x8d: // RI - ECMA-48 8.3.104 346 if(state->pos.row == state->scrollregion_top) { 347 VTermRect rect = { 348 .start_row = state->scrollregion_top, 349 .end_row = SCROLLREGION_BOTTOM(state), 350 .start_col = SCROLLREGION_LEFT(state), 351 .end_col = SCROLLREGION_RIGHT(state), 352 }; 353 354 scroll(state, rect, -1, 0); 355 } 356 else if(state->pos.row > 0) 357 state->pos.row--; 358 break; 359 360 default: 361 return 0; 362 } 363 364 updatecursor(state, &oldpos, 1); 365 366 return 1; 367} 368 369static void output_mouse(VTermState *state, int code, int pressed, int modifiers, int col, int row) 370{ 371 modifiers <<= 2; 372 373 switch(state->mouse_protocol) { 374 case MOUSE_X10: 375 if(col + 0x21 > 0xff) 376 col = 0xff - 0x21; 377 if(row + 0x21 > 0xff) 378 row = 0xff - 0x21; 379 380 if(!pressed) 381 code = 3; 382 383 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%c%c%c", 384 (code | modifiers) + 0x20, col + 0x21, row + 0x21); 385 break; 386 387 case MOUSE_UTF8: 388 { 389 char utf8[18]; size_t len = 0; 390 391 if(!pressed) 392 code = 3; 393 394 len += fill_utf8((code | modifiers) + 0x20, utf8 + len); 395 len += fill_utf8(col + 0x21, utf8 + len); 396 len += fill_utf8(row + 0x21, utf8 + len); 397 398 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%s", utf8); 399 } 400 break; 401 402 case MOUSE_SGR: 403 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "<%d;%d;%d%c", 404 code | modifiers, col + 1, row + 1, pressed ? 'M' : 'm'); 405 break; 406 407 case MOUSE_RXVT: 408 if(!pressed) 409 code = 3; 410 411 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%d;%d;%dM", 412 code | modifiers, col + 1, row + 1); 413 break; 414 } 415} 416 417static void mousefunc(int col, int row, int button, int pressed, int modifiers, void *data) 418{ 419 VTermState *state = data; 420 421 int old_col = state->mouse_col; 422 int old_row = state->mouse_row; 423 int old_buttons = state->mouse_buttons; 424 425 state->mouse_col = col; 426 state->mouse_row = row; 427 428 if(button > 0 && button <= 3) { 429 if(pressed) 430 state->mouse_buttons |= (1 << (button-1)); 431 else 432 state->mouse_buttons &= ~(1 << (button-1)); 433 } 434 435 modifiers &= 0x7; 436 437 438 /* Most of the time we don't get button releases from 4/5 */ 439 if(state->mouse_buttons != old_buttons || button >= 4) { 440 if(button < 4) { 441 output_mouse(state, button-1, pressed, modifiers, col, row); 442 } 443 else if(button < 6) { 444 output_mouse(state, button-4 + 0x40, pressed, modifiers, col, row); 445 } 446 } 447 else if(col != old_col || row != old_row) { 448 if((state->mouse_flags & MOUSE_WANT_DRAG && state->mouse_buttons) || 449 (state->mouse_flags & MOUSE_WANT_MOVE)) { 450 int button = state->mouse_buttons & 0x01 ? 1 : 451 state->mouse_buttons & 0x02 ? 2 : 452 state->mouse_buttons & 0x04 ? 3 : 4; 453 output_mouse(state, button-1 + 0x20, 1, modifiers, col, row); 454 } 455 } 456} 457 458static int settermprop_bool(VTermState *state, VTermProp prop, int v) 459{ 460 VTermValue val = { .boolean = v }; 461 return vterm_state_set_termprop(state, prop, &val); 462} 463 464static int settermprop_int(VTermState *state, VTermProp prop, int v) 465{ 466 VTermValue val = { .number = v }; 467 return vterm_state_set_termprop(state, prop, &val); 468} 469 470static int settermprop_string(VTermState *state, VTermProp prop, const char *str, size_t len) 471{ 472 char strvalue[len+1]; 473 strncpy(strvalue, str, len); 474 strvalue[len] = 0; 475 476 VTermValue val = { .string = strvalue }; 477 return vterm_state_set_termprop(state, prop, &val); 478} 479 480static void savecursor(VTermState *state, int save) 481{ 482 if(save) { 483 state->saved.pos = state->pos; 484 state->saved.mode.cursor_visible = state->mode.cursor_visible; 485 state->saved.mode.cursor_blink = state->mode.cursor_blink; 486 state->saved.mode.cursor_shape = state->mode.cursor_shape; 487 488 vterm_state_savepen(state, 1); 489 } 490 else { 491 VTermPos oldpos = state->pos; 492 493 state->pos = state->saved.pos; 494 495 settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, state->saved.mode.cursor_visible); 496 settermprop_bool(state, VTERM_PROP_CURSORBLINK, state->saved.mode.cursor_blink); 497 settermprop_int (state, VTERM_PROP_CURSORSHAPE, state->saved.mode.cursor_shape); 498 499 vterm_state_savepen(state, 0); 500 501 updatecursor(state, &oldpos, 1); 502 } 503} 504 505static int on_escape(const char *bytes, size_t len, void *user) 506{ 507 VTermState *state = user; 508 509 /* Easier to decode this from the first byte, even though the final 510 * byte terminates it 511 */ 512 switch(bytes[0]) { 513 case ' ': 514 if(len != 2) 515 return 0; 516 517 switch(bytes[1]) { 518 case 'F': // S7C1T 519 state->vt->mode.ctrl8bit = 0; 520 break; 521 522 case 'G': // S8C1T 523 state->vt->mode.ctrl8bit = 1; 524 break; 525 526 default: 527 return 0; 528 } 529 return 2; 530 531 case '#': 532 if(len != 2) 533 return 0; 534 535 switch(bytes[1]) { 536 case '8': // DECALN 537 { 538 VTermPos pos; 539 uint32_t E[] = { 'E', 0 }; 540 for(pos.row = 0; pos.row < state->rows; pos.row++) 541 for(pos.col = 0; pos.col < state->cols; pos.col++) 542 putglyph(state, E, 1, pos); 543 break; 544 } 545 546 default: 547 return 0; 548 } 549 return 2; 550 551 case '(': case ')': case '*': case '+': // SCS 552 if(len != 2) 553 return 0; 554 555 { 556 int setnum = bytes[0] - 0x28; 557 VTermEncoding *newenc = vterm_lookup_encoding(ENC_SINGLE_94, bytes[1]); 558 559 if(newenc) { 560 state->encoding[setnum].enc = newenc; 561 562 if(newenc->init) 563 (*newenc->init)(newenc, state->encoding[setnum].data); 564 } 565 } 566 567 return 2; 568 569 case '7': // DECSC 570 savecursor(state, 1); 571 return 1; 572 573 case '8': // DECRC 574 savecursor(state, 0); 575 return 1; 576 577 case '=': // DECKPAM 578 state->mode.keypad = 1; 579 return 1; 580 581 case '>': // DECKPNM 582 state->mode.keypad = 0; 583 return 1; 584 585 case 'c': // RIS - ECMA-48 8.3.105 586 { 587 VTermPos oldpos = state->pos; 588 vterm_state_reset(state, 1); 589 if(state->callbacks && state->callbacks->movecursor) 590 (*state->callbacks->movecursor)(state->pos, oldpos, state->mode.cursor_visible, state->cbdata); 591 return 1; 592 } 593 594 case 'n': // LS2 - ECMA-48 8.3.78 595 state->gl_set = 2; 596 return 1; 597 598 case 'o': // LS3 - ECMA-48 8.3.80 599 state->gl_set = 3; 600 return 1; 601 602 default: 603 return 0; 604 } 605} 606 607static void set_mode(VTermState *state, int num, int val) 608{ 609 switch(num) { 610 case 4: // IRM - ECMA-48 7.2.10 611 state->mode.insert = val; 612 break; 613 614 case 20: // LNM - ANSI X3.4-1977 615 state->mode.newline = val; 616 break; 617 618 default: 619 fprintf(stderr, "libvterm: Unknown mode %d\n", num); 620 return; 621 } 622} 623 624static void set_dec_mode(VTermState *state, int num, int val) 625{ 626 switch(num) { 627 case 1: 628 state->mode.cursor = val; 629 break; 630 631 case 5: // DECSCNM - screen mode 632 settermprop_bool(state, VTERM_PROP_REVERSE, val); 633 break; 634 635 case 6: // DECOM - origin mode 636 { 637 VTermPos oldpos = state->pos; 638 state->mode.origin = val; 639 state->pos.row = state->mode.origin ? state->scrollregion_top : 0; 640 state->pos.col = state->mode.origin ? SCROLLREGION_LEFT(state) : 0; 641 updatecursor(state, &oldpos, 1); 642 } 643 break; 644 645 case 7: 646 state->mode.autowrap = val; 647 break; 648 649 case 12: 650 settermprop_bool(state, VTERM_PROP_CURSORBLINK, val); 651 break; 652 653 case 25: 654 settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, val); 655 break; 656 657 case 69: // DECVSSM - vertical split screen mode 658 state->mode.leftrightmargin = val; 659 break; 660 661 case 1000: 662 case 1002: 663 case 1003: 664 if(val) { 665 state->mouse_col = 0; 666 state->mouse_row = 0; 667 state->mouse_buttons = 0; 668 669 state->mouse_flags = MOUSE_WANT_CLICK; 670 state->mouse_protocol = MOUSE_X10; 671 672 if(num == 1002) 673 state->mouse_flags |= MOUSE_WANT_DRAG; 674 if(num == 1003) 675 state->mouse_flags |= MOUSE_WANT_MOVE; 676 } 677 else { 678 state->mouse_flags = 0; 679 } 680 681 if(state->callbacks && state->callbacks->setmousefunc) 682 (*state->callbacks->setmousefunc)(val ? mousefunc : NULL, state, state->cbdata); 683 684 break; 685 686 case 1005: 687 state->mouse_protocol = val ? MOUSE_UTF8 : MOUSE_X10; 688 break; 689 690 case 1006: 691 state->mouse_protocol = val ? MOUSE_SGR : MOUSE_X10; 692 break; 693 694 case 1015: 695 state->mouse_protocol = val ? MOUSE_RXVT : MOUSE_X10; 696 break; 697 698 case 1047: 699 settermprop_bool(state, VTERM_PROP_ALTSCREEN, val); 700 break; 701 702 case 1048: 703 savecursor(state, val); 704 break; 705 706 case 1049: 707 settermprop_bool(state, VTERM_PROP_ALTSCREEN, val); 708 savecursor(state, val); 709 break; 710 711 default: 712 fprintf(stderr, "libvterm: Unknown DEC mode %d\n", num); 713 return; 714 } 715} 716 717static void request_dec_mode(VTermState *state, int num) 718{ 719 int reply; 720 721 switch(num) { 722 case 1: 723 reply = state->mode.cursor; 724 break; 725 726 case 5: 727 reply = state->mode.screen; 728 break; 729 730 case 6: 731 reply = state->mode.origin; 732 break; 733 734 case 7: 735 reply = state->mode.autowrap; 736 break; 737 738 case 12: 739 reply = state->mode.cursor_blink; 740 break; 741 742 case 25: 743 reply = state->mode.cursor_visible; 744 break; 745 746 case 69: 747 reply = state->mode.leftrightmargin; 748 break; 749 750 case 1000: 751 reply = state->mouse_flags == MOUSE_WANT_CLICK; 752 break; 753 754 case 1002: 755 reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_DRAG); 756 break; 757 758 case 1003: 759 reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_MOVE); 760 break; 761 762 case 1005: 763 reply = state->mouse_protocol == MOUSE_UTF8; 764 break; 765 766 case 1006: 767 reply = state->mouse_protocol == MOUSE_SGR; 768 break; 769 770 case 1015: 771 reply = state->mouse_protocol == MOUSE_RXVT; 772 break; 773 774 case 1047: 775 reply = state->mode.alt_screen; 776 break; 777 778 default: 779 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, 0); 780 return; 781 } 782 783 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, reply ? 1 : 2); 784} 785 786static int on_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user) 787{ 788 VTermState *state = user; 789 int leader_byte = 0; 790 int intermed_byte = 0; 791 792 if(leader && leader[0]) { 793 if(leader[1]) // longer than 1 char 794 return 0; 795 796 switch(leader[0]) { 797 case '?': 798 case '>': 799 leader_byte = leader[0]; 800 break; 801 default: 802 return 0; 803 } 804 } 805 806 if(intermed && intermed[0]) { 807 if(intermed[1]) // longer than 1 char 808 return 0; 809 810 switch(intermed[0]) { 811 case ' ': 812 case '"': 813 case '$': 814 case '\'': 815 intermed_byte = intermed[0]; 816 break; 817 default: 818 return 0; 819 } 820 } 821 822 VTermPos oldpos = state->pos; 823 824 // Some temporaries for later code 825 int count, val; 826 int row, col; 827 VTermRect rect; 828 int selective; 829 830#define LBOUND(v,min) if((v) < (min)) (v) = (min) 831#define UBOUND(v,max) if((v) > (max)) (v) = (max) 832 833#define LEADER(l,b) ((l << 8) | b) 834#define INTERMED(i,b) ((i << 16) | b) 835 836 switch(intermed_byte << 16 | leader_byte << 8 | command) { 837 case 0x40: // ICH - ECMA-48 8.3.64 838 count = CSI_ARG_COUNT(args[0]); 839 840 rect.start_row = state->pos.row; 841 rect.end_row = state->pos.row + 1; 842 rect.start_col = state->pos.col; 843 rect.end_col = state->cols; 844 845 scroll(state, rect, 0, -count); 846 847 break; 848 849 case 0x41: // CUU - ECMA-48 8.3.22 850 count = CSI_ARG_COUNT(args[0]); 851 state->pos.row -= count; 852 state->at_phantom = 0; 853 break; 854 855 case 0x42: // CUD - ECMA-48 8.3.19 856 count = CSI_ARG_COUNT(args[0]); 857 state->pos.row += count; 858 state->at_phantom = 0; 859 break; 860 861 case 0x43: // CUF - ECMA-48 8.3.20 862 count = CSI_ARG_COUNT(args[0]); 863 state->pos.col += count; 864 state->at_phantom = 0; 865 break; 866 867 case 0x44: // CUB - ECMA-48 8.3.18 868 count = CSI_ARG_COUNT(args[0]); 869 state->pos.col -= count; 870 state->at_phantom = 0; 871 break; 872 873 case 0x45: // CNL - ECMA-48 8.3.12 874 count = CSI_ARG_COUNT(args[0]); 875 state->pos.col = 0; 876 state->pos.row += count; 877 state->at_phantom = 0; 878 break; 879 880 case 0x46: // CPL - ECMA-48 8.3.13 881 count = CSI_ARG_COUNT(args[0]); 882 state->pos.col = 0; 883 state->pos.row -= count; 884 state->at_phantom = 0; 885 break; 886 887 case 0x47: // CHA - ECMA-48 8.3.9 888 val = CSI_ARG_OR(args[0], 1); 889 state->pos.col = val-1; 890 state->at_phantom = 0; 891 break; 892 893 case 0x48: // CUP - ECMA-48 8.3.21 894 row = CSI_ARG_OR(args[0], 1); 895 col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]); 896 // zero-based 897 state->pos.row = row-1; 898 state->pos.col = col-1; 899 if(state->mode.origin) { 900 state->pos.row += state->scrollregion_top; 901 state->pos.col += SCROLLREGION_LEFT(state); 902 } 903 state->at_phantom = 0; 904 break; 905 906 case 0x49: // CHT - ECMA-48 8.3.10 907 count = CSI_ARG_COUNT(args[0]); 908 tab(state, count, +1); 909 break; 910 911 case 0x4a: // ED - ECMA-48 8.3.39 912 case LEADER('?', 0x4a): // DECSED - Selective Erase in Display 913 selective = (leader_byte == '?'); 914 switch(CSI_ARG(args[0])) { 915 case CSI_ARG_MISSING: 916 case 0: 917 rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1; 918 rect.start_col = state->pos.col; rect.end_col = state->cols; 919 if(rect.end_col > rect.start_col) 920 erase(state, rect, selective); 921 922 rect.start_row = state->pos.row + 1; rect.end_row = state->rows; 923 rect.start_col = 0; 924 if(rect.end_row > rect.start_row) 925 erase(state, rect, selective); 926 break; 927 928 case 1: 929 rect.start_row = 0; rect.end_row = state->pos.row; 930 rect.start_col = 0; rect.end_col = state->cols; 931 if(rect.end_col > rect.start_col) 932 erase(state, rect, selective); 933 934 rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1; 935 rect.end_col = state->pos.col + 1; 936 if(rect.end_row > rect.start_row) 937 erase(state, rect, selective); 938 break; 939 940 case 2: 941 rect.start_row = 0; rect.end_row = state->rows; 942 rect.start_col = 0; rect.end_col = state->cols; 943 erase(state, rect, selective); 944 break; 945 } 946 break; 947 948 case 0x4b: // EL - ECMA-48 8.3.41 949 case LEADER('?', 0x4b): // DECSEL - Selective Erase in Line 950 selective = (leader_byte == '?'); 951 rect.start_row = state->pos.row; 952 rect.end_row = state->pos.row + 1; 953 954 switch(CSI_ARG(args[0])) { 955 case CSI_ARG_MISSING: 956 case 0: 957 rect.start_col = state->pos.col; rect.end_col = state->cols; break; 958 case 1: 959 rect.start_col = 0; rect.end_col = state->pos.col + 1; break; 960 case 2: 961 rect.start_col = 0; rect.end_col = state->cols; break; 962 default: 963 return 0; 964 } 965 966 if(rect.end_col > rect.start_col) 967 erase(state, rect, selective); 968 969 break; 970 971 case 0x4c: // IL - ECMA-48 8.3.67 972 count = CSI_ARG_COUNT(args[0]); 973 974 rect.start_row = state->pos.row; 975 rect.end_row = SCROLLREGION_BOTTOM(state); 976 rect.start_col = SCROLLREGION_LEFT(state); 977 rect.end_col = SCROLLREGION_RIGHT(state); 978 979 scroll(state, rect, -count, 0); 980 981 break; 982 983 case 0x4d: // DL - ECMA-48 8.3.32 984 count = CSI_ARG_COUNT(args[0]); 985 986 rect.start_row = state->pos.row; 987 rect.end_row = SCROLLREGION_BOTTOM(state); 988 rect.start_col = SCROLLREGION_LEFT(state); 989 rect.end_col = SCROLLREGION_RIGHT(state); 990 991 scroll(state, rect, count, 0); 992 993 break; 994 995 case 0x50: // DCH - ECMA-48 8.3.26 996 count = CSI_ARG_COUNT(args[0]); 997 998 rect.start_row = state->pos.row; 999 rect.end_row = state->pos.row + 1; 1000 rect.start_col = state->pos.col; 1001 rect.end_col = state->cols; 1002 1003 scroll(state, rect, 0, count); 1004 1005 break; 1006 1007 case 0x53: // SU - ECMA-48 8.3.147 1008 count = CSI_ARG_COUNT(args[0]); 1009 1010 rect.start_row = state->scrollregion_top; 1011 rect.end_row = SCROLLREGION_BOTTOM(state); 1012 rect.start_col = SCROLLREGION_LEFT(state); 1013 rect.end_col = SCROLLREGION_RIGHT(state); 1014 1015 scroll(state, rect, count, 0); 1016 1017 break; 1018 1019 case 0x54: // SD - ECMA-48 8.3.113 1020 count = CSI_ARG_COUNT(args[0]); 1021 1022 rect.start_row = state->scrollregion_top; 1023 rect.end_row = SCROLLREGION_BOTTOM(state); 1024 rect.start_col = SCROLLREGION_LEFT(state); 1025 rect.end_col = SCROLLREGION_RIGHT(state); 1026 1027 scroll(state, rect, -count, 0); 1028 1029 break; 1030 1031 case 0x58: // ECH - ECMA-48 8.3.38 1032 count = CSI_ARG_COUNT(args[0]); 1033 1034 rect.start_row = state->pos.row; 1035 rect.end_row = state->pos.row + 1; 1036 rect.start_col = state->pos.col; 1037 rect.end_col = state->pos.col + count; 1038 UBOUND(rect.end_col, state->cols); 1039 1040 erase(state, rect, 0); 1041 break; 1042 1043 case 0x5a: // CBT - ECMA-48 8.3.7 1044 count = CSI_ARG_COUNT(args[0]); 1045 tab(state, count, -1); 1046 break; 1047 1048 case 0x60: // HPA - ECMA-48 8.3.57 1049 col = CSI_ARG_OR(args[0], 1); 1050 state->pos.col = col-1; 1051 state->at_phantom = 0; 1052 break; 1053 1054 case 0x61: // HPR - ECMA-48 8.3.59 1055 count = CSI_ARG_COUNT(args[0]); 1056 state->pos.col += count; 1057 state->at_phantom = 0; 1058 break; 1059 1060 case 0x63: // DA - ECMA-48 8.3.24 1061 val = CSI_ARG_OR(args[0], 0); 1062 if(val == 0) 1063 // DEC VT100 response 1064 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?1;2c"); 1065 break; 1066 1067 case LEADER('>', 0x63): // DEC secondary Device Attributes 1068 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, ">%d;%d;%dc", 0, 100, 0); 1069 break; 1070 1071 case 0x64: // VPA - ECMA-48 8.3.158 1072 row = CSI_ARG_OR(args[0], 1); 1073 state->pos.row = row-1; 1074 if(state->mode.origin) 1075 state->pos.row += state->scrollregion_top; 1076 state->at_phantom = 0; 1077 break; 1078 1079 case 0x65: // VPR - ECMA-48 8.3.160 1080 count = CSI_ARG_COUNT(args[0]); 1081 state->pos.row += count; 1082 state->at_phantom = 0; 1083 break; 1084 1085 case 0x66: // HVP - ECMA-48 8.3.63 1086 row = CSI_ARG_OR(args[0], 1); 1087 col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]); 1088 // zero-based 1089 state->pos.row = row-1; 1090 state->pos.col = col-1; 1091 if(state->mode.origin) { 1092 state->pos.row += state->scrollregion_top; 1093 state->pos.col += SCROLLREGION_LEFT(state); 1094 } 1095 state->at_phantom = 0; 1096 break; 1097 1098 case 0x67: // TBC - ECMA-48 8.3.154 1099 val = CSI_ARG_OR(args[0], 0); 1100 1101 switch(val) { 1102 case 0: 1103 clear_col_tabstop(state, state->pos.col); 1104 break; 1105 case 3: 1106 case 5: 1107 for(col = 0; col < state->cols; col++) 1108 clear_col_tabstop(state, col); 1109 break; 1110 case 1: 1111 case 2: 1112 case 4: 1113 break; 1114 /* TODO: 1, 2 and 4 aren't meaningful yet without line tab stops */ 1115 default: 1116 return 0; 1117 } 1118 break; 1119 1120 case 0x68: // SM - ECMA-48 8.3.125 1121 if(!CSI_ARG_IS_MISSING(args[0])) 1122 set_mode(state, CSI_ARG(args[0]), 1); 1123 break; 1124 1125 case LEADER('?', 0x68): // DEC private mode set 1126 if(!CSI_ARG_IS_MISSING(args[0])) 1127 set_dec_mode(state, CSI_ARG(args[0]), 1); 1128 break; 1129 1130 case 0x6a: // HPB - ECMA-48 8.3.58 1131 count = CSI_ARG_COUNT(args[0]); 1132 state->pos.col -= count; 1133 state->at_phantom = 0; 1134 break; 1135 1136 case 0x6b: // VPB - ECMA-48 8.3.159 1137 count = CSI_ARG_COUNT(args[0]); 1138 state->pos.row -= count; 1139 state->at_phantom = 0; 1140 break; 1141 1142 case 0x6c: // RM - ECMA-48 8.3.106 1143 if(!CSI_ARG_IS_MISSING(args[0])) 1144 set_mode(state, CSI_ARG(args[0]), 0); 1145 break; 1146 1147 case LEADER('?', 0x6c): // DEC private mode reset 1148 if(!CSI_ARG_IS_MISSING(args[0])) 1149 set_dec_mode(state, CSI_ARG(args[0]), 0); 1150 break; 1151 1152 case 0x6d: // SGR - ECMA-48 8.3.117 1153 vterm_state_setpen(state, args, argcount); 1154 break; 1155 1156 case 0x6e: // DSR - ECMA-48 8.3.35 1157 case LEADER('?', 0x6e): // DECDSR 1158 val = CSI_ARG_OR(args[0], 0); 1159 1160 { 1161 char *qmark = (leader_byte == '?') ? "?" : ""; 1162 1163 switch(val) { 1164 case 0: case 1: case 2: case 3: case 4: 1165 // ignore - these are replies 1166 break; 1167 case 5: 1168 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s0n", qmark); 1169 break; 1170 case 6: // CPR - cursor position report 1171 vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s%d;%dR", qmark, state->pos.row + 1, state->pos.col + 1); 1172 break; 1173 } 1174 } 1175 break; 1176 1177 1178 case LEADER('!', 0x70): // DECSTR - DEC soft terminal reset 1179 vterm_state_reset(state, 0); 1180 break; 1181 1182 case LEADER('?', INTERMED('$', 0x70)): 1183 request_dec_mode(state, CSI_ARG(args[0])); 1184 break; 1185 1186 case INTERMED(' ', 0x71): // DECSCUSR - DEC set cursor shape 1187 val = CSI_ARG_OR(args[0], 1); 1188 1189 switch(val) { 1190 case 0: case 1: 1191 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1); 1192 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK); 1193 break; 1194 case 2: 1195 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0); 1196 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK); 1197 break; 1198 case 3: 1199 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1); 1200 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE); 1201 break; 1202 case 4: 1203 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0); 1204 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE); 1205 break; 1206 case 5: 1207 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1); 1208 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT); 1209 break; 1210 case 6: 1211 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0); 1212 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT); 1213 break; 1214 } 1215 1216 break; 1217 1218 case INTERMED('"', 0x71): // DECSCA - DEC select character protection attribute 1219 val = CSI_ARG_OR(args[0], 0); 1220 1221 switch(val) { 1222 case 0: case 2: 1223 state->protected_cell = 0; 1224 break; 1225 case 1: 1226 state->protected_cell = 1; 1227 break; 1228 } 1229 1230 break; 1231 1232 case 0x72: // DECSTBM - DEC custom 1233 state->scrollregion_top = CSI_ARG_OR(args[0], 1) - 1; 1234 state->scrollregion_bottom = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]); 1235 if(state->scrollregion_top == 0 && state->scrollregion_bottom == state->rows) 1236 state->scrollregion_bottom = -1; 1237 break; 1238 1239 case 0x73: // DECSLRM - DEC custom 1240 // Always allow setting these margins, just they won't take effect without DECVSSM 1241 state->scrollregion_left = CSI_ARG_OR(args[0], 1) - 1; 1242 state->scrollregion_right = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]); 1243 if(state->scrollregion_left == 0 && state->scrollregion_right == state->cols) 1244 state->scrollregion_right = -1; 1245 break; 1246 1247 case INTERMED('\'', 0x7D): // DECIC 1248 count = CSI_ARG_COUNT(args[0]); 1249 1250 rect.start_row = state->scrollregion_top; 1251 rect.end_row = SCROLLREGION_BOTTOM(state); 1252 rect.start_col = state->pos.col; 1253 rect.end_col = SCROLLREGION_RIGHT(state); 1254 1255 scroll(state, rect, 0, -count); 1256 1257 break; 1258 1259 case INTERMED('\'', 0x7E): // DECDC 1260 count = CSI_ARG_COUNT(args[0]); 1261 1262 rect.start_row = state->scrollregion_top; 1263 rect.end_row = SCROLLREGION_BOTTOM(state); 1264 rect.start_col = state->pos.col; 1265 rect.end_col = SCROLLREGION_RIGHT(state); 1266 1267 scroll(state, rect, 0, count); 1268 1269 break; 1270 1271 default: 1272 return 0; 1273 } 1274 1275 if(state->mode.origin) { 1276 LBOUND(state->pos.row, state->scrollregion_top); 1277 UBOUND(state->pos.row, state->scrollregion_bottom-1); 1278 LBOUND(state->pos.col, SCROLLREGION_LEFT(state)); 1279 UBOUND(state->pos.col, SCROLLREGION_RIGHT(state)-1); 1280 } 1281 else { 1282 LBOUND(state->pos.row, 0); 1283 UBOUND(state->pos.row, state->rows-1); 1284 LBOUND(state->pos.col, 0); 1285 UBOUND(state->pos.col, state->cols-1); 1286 } 1287 1288 updatecursor(state, &oldpos, 1); 1289 1290 return 1; 1291} 1292 1293static int on_osc(const char *command, size_t cmdlen, void *user) 1294{ 1295 VTermState *state = user; 1296 1297 if(cmdlen < 2) 1298 return 0; 1299 1300 if(strneq(command, "0;", 2)) { 1301 settermprop_string(state, VTERM_PROP_ICONNAME, command + 2, cmdlen - 2); 1302 settermprop_string(state, VTERM_PROP_TITLE, command + 2, cmdlen - 2); 1303 return 1; 1304 } 1305 else if(strneq(command, "1;", 2)) { 1306 settermprop_string(state, VTERM_PROP_ICONNAME, command + 2, cmdlen - 2); 1307 return 1; 1308 } 1309 else if(strneq(command, "2;", 2)) { 1310 settermprop_string(state, VTERM_PROP_TITLE, command + 2, cmdlen - 2); 1311 return 1; 1312 } 1313 1314 return 0; 1315} 1316 1317static void request_status_string(VTermState *state, const char *command, size_t cmdlen) 1318{ 1319 if(cmdlen == 1) 1320 switch(command[0]) { 1321 case 'r': // Query DECSTBM 1322 vterm_push_output_sprintf_ctrl(state->vt, C1_DCS, "1$r%d;%dr", state->scrollregion_top+1, SCROLLREGION_BOTTOM(state)); 1323 return; 1324 case 's': // Query DECSLRM 1325 vterm_push_output_sprintf_ctrl(state->vt, C1_DCS, "1$r%d;%ds", SCROLLREGION_LEFT(state)+1, SCROLLREGION_RIGHT(state)); 1326 return; 1327 } 1328 1329 if(cmdlen == 2) { 1330 if(strneq(command, " q", 2)) { 1331 int reply = 0; 1332 switch(state->mode.cursor_shape) { 1333 case VTERM_PROP_CURSORSHAPE_BLOCK: reply = 2; break; 1334 case VTERM_PROP_CURSORSHAPE_UNDERLINE: reply = 4; break; 1335 case VTERM_PROP_CURSORSHAPE_BAR_LEFT: reply = 6; break; 1336 } 1337 if(state->mode.cursor_blink) 1338 reply--; 1339 vterm_push_output_sprintf_ctrl(state->vt, C1_DCS, "1$r%d q", reply); 1340 return; 1341 } 1342 else if(strneq(command, "\"q", 2)) { 1343 vterm_push_output_sprintf_ctrl(state->vt, C1_DCS, "1$r%d\"q", state->protected_cell ? 1 : 2); 1344 return; 1345 } 1346 } 1347 1348 vterm_push_output_sprintf_ctrl(state->vt, C1_DCS, "0$r%.s", (int)cmdlen, command); 1349} 1350 1351static int on_dcs(const char *command, size_t cmdlen, void *user) 1352{ 1353 VTermState *state = user; 1354 1355 if(cmdlen >= 2 && strneq(command, "$q", 2)) { 1356 request_status_string(state, command+2, cmdlen-2); 1357 return 1; 1358 } 1359 1360 return 0; 1361} 1362 1363static int on_resize(int rows, int cols, void *user) 1364{ 1365 VTermState *state = user; 1366 VTermPos oldpos = state->pos; 1367 1368 if(cols != state->cols) { 1369 unsigned char *newtabstops = vterm_allocator_malloc(state->vt, (cols + 7) / 8); 1370 1371 /* TODO: This can all be done much more efficiently bytewise */ 1372 int col; 1373 for(col = 0; col < state->cols && col < cols; col++) { 1374 unsigned char mask = 1 << (col & 7); 1375 if(state->tabstops[col >> 3] & mask) 1376 newtabstops[col >> 3] |= mask; 1377 else 1378 newtabstops[col >> 3] &= ~mask; 1379 } 1380 1381 for( ; col < cols; col++) { 1382 unsigned char mask = 1 << (col & 7); 1383 if(col % 8 == 0) 1384 newtabstops[col >> 3] |= mask; 1385 else 1386 newtabstops[col >> 3] &= ~mask; 1387 } 1388 1389 vterm_allocator_free(state->vt, state->tabstops); 1390 state->tabstops = newtabstops; 1391 } 1392 1393 state->rows = rows; 1394 state->cols = cols; 1395 1396 if(state->pos.row >= rows) 1397 state->pos.row = rows - 1; 1398 if(state->pos.col >= cols) 1399 state->pos.col = cols - 1; 1400 1401 if(state->at_phantom && state->pos.col < cols-1) { 1402 state->at_phantom = 0; 1403 state->pos.col++; 1404 } 1405 1406 if(state->callbacks && state->callbacks->resize) 1407 (*state->callbacks->resize)(rows, cols, state->cbdata); 1408 1409 updatecursor(state, &oldpos, 1); 1410 1411 return 1; 1412} 1413 1414static const VTermParserCallbacks parser_callbacks = { 1415 .text = on_text, 1416 .control = on_control, 1417 .escape = on_escape, 1418 .csi = on_csi, 1419 .osc = on_osc, 1420 .dcs = on_dcs, 1421 .resize = on_resize, 1422}; 1423 1424VTermState *vterm_obtain_state(VTerm *vt) 1425{ 1426 if(vt->state) 1427 return vt->state; 1428 1429 VTermState *state = vterm_state_new(vt); 1430 vt->state = state; 1431 1432 state->combine_chars_size = 16; 1433 state->combine_chars = vterm_allocator_malloc(state->vt, state->combine_chars_size * sizeof(state->combine_chars[0])); 1434 1435 state->tabstops = vterm_allocator_malloc(state->vt, (state->cols + 7) / 8); 1436 1437 state->encoding_utf8.enc = vterm_lookup_encoding(ENC_UTF8, 'u'); 1438 if(*state->encoding_utf8.enc->init) 1439 (*state->encoding_utf8.enc->init)(state->encoding_utf8.enc, state->encoding_utf8.data); 1440 1441 vterm_set_parser_callbacks(vt, &parser_callbacks, state); 1442 1443 return state; 1444} 1445 1446void vterm_state_reset(VTermState *state, int hard) 1447{ 1448 state->scrollregion_top = 0; 1449 state->scrollregion_bottom = -1; 1450 state->scrollregion_left = 0; 1451 state->scrollregion_right = -1; 1452 1453 state->mode.keypad = 0; 1454 state->mode.cursor = 0; 1455 state->mode.autowrap = 1; 1456 state->mode.insert = 0; 1457 state->mode.newline = 0; 1458 state->mode.alt_screen = 0; 1459 state->mode.origin = 0; 1460 state->mode.leftrightmargin = 0; 1461 1462 state->vt->mode.ctrl8bit = 0; 1463 1464 for(int col = 0; col < state->cols; col++) 1465 if(col % 8 == 0) 1466 set_col_tabstop(state, col); 1467 else 1468 clear_col_tabstop(state, col); 1469 1470 if(state->callbacks && state->callbacks->initpen) 1471 (*state->callbacks->initpen)(state->cbdata); 1472 1473 vterm_state_resetpen(state); 1474 1475 VTermEncoding *default_enc = state->vt->mode.utf8 ? 1476 vterm_lookup_encoding(ENC_UTF8, 'u') : 1477 vterm_lookup_encoding(ENC_SINGLE_94, 'B'); 1478 1479 for(int i = 0; i < 4; i++) { 1480 state->encoding[i].enc = default_enc; 1481 if(default_enc->init) 1482 (*default_enc->init)(default_enc, state->encoding[i].data); 1483 } 1484 1485 state->gl_set = 0; 1486 state->gr_set = 0; 1487 1488 state->protected_cell = 0; 1489 1490 // Initialise the props 1491 settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, 1); 1492 settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1); 1493 settermprop_int (state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK); 1494 1495 if(hard) { 1496 state->pos.row = 0; 1497 state->pos.col = 0; 1498 state->at_phantom = 0; 1499 1500 VTermRect rect = { 0, state->rows, 0, state->cols }; 1501 erase(state, rect, 0); 1502 } 1503} 1504 1505void vterm_state_get_cursorpos(VTermState *state, VTermPos *cursorpos) 1506{ 1507 *cursorpos = state->pos; 1508} 1509 1510void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user) 1511{ 1512 if(callbacks) { 1513 state->callbacks = callbacks; 1514 state->cbdata = user; 1515 1516 if(state->callbacks && state->callbacks->initpen) 1517 (*state->callbacks->initpen)(state->cbdata); 1518 } 1519 else { 1520 state->callbacks = NULL; 1521 state->cbdata = NULL; 1522 } 1523} 1524 1525int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val) 1526{ 1527 /* Only store the new value of the property if usercode said it was happy. 1528 * This is especially important for altscreen switching */ 1529 if(state->callbacks && state->callbacks->settermprop) 1530 if(!(*state->callbacks->settermprop)(prop, val, state->cbdata)) 1531 return 0; 1532 1533 switch(prop) { 1534 case VTERM_PROP_TITLE: 1535 case VTERM_PROP_ICONNAME: 1536 // we don't store these, just transparently pass through 1537 return 1; 1538 case VTERM_PROP_CURSORVISIBLE: 1539 state->mode.cursor_visible = val->boolean; 1540 return 1; 1541 case VTERM_PROP_CURSORBLINK: 1542 state->mode.cursor_blink = val->boolean; 1543 return 1; 1544 case VTERM_PROP_CURSORSHAPE: 1545 state->mode.cursor_shape = val->number; 1546 return 1; 1547 case VTERM_PROP_REVERSE: 1548 state->mode.screen = val->boolean; 1549 return 1; 1550 case VTERM_PROP_ALTSCREEN: 1551 state->mode.alt_screen = val->boolean; 1552 if(state->mode.alt_screen) { 1553 VTermRect rect = { 1554 .start_row = 0, 1555 .start_col = 0, 1556 .end_row = state->rows, 1557 .end_col = state->cols, 1558 }; 1559 erase(state, rect, 0); 1560 } 1561 return 1; 1562 } 1563 1564 return 0; 1565} 1566