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