1 /* 2 * Copyright 2001-2009, Haiku, Inc. 3 * Copyright (c) 2003-4 Kian Duffy <myob@users.sourceforge.net> 4 * Parts Copyright (C) 1998,99 Kazuho Okui and Takashi Murai. 5 * Distributed under the terms of the MIT license. 6 */ 7 8 //! Escape sequence parse and character encoding. 9 10 #include "TermParse.h" 11 12 #include <ctype.h> 13 #include <errno.h> 14 #include <stdio.h> 15 #include <signal.h> 16 #include <string.h> 17 #include <unistd.h> 18 19 #include <Autolock.h> 20 #include <Beep.h> 21 #include <Message.h> 22 23 #include "CodeConv.h" 24 #include "TermConst.h" 25 #include "TerminalBuffer.h" 26 #include "VTparse.h" 27 28 29 extern int gUTF8GroundTable[]; /* UTF8 Ground table */ 30 extern int gCS96GroundTable[]; /* CS96 Ground table */ 31 extern int gISO8859GroundTable[]; /* ISO8859 & EUC Ground table */ 32 extern int gSJISGroundTable[]; /* Shift-JIS Ground table */ 33 34 extern int gEscTable[]; /* ESC */ 35 extern int gCsiTable[]; /* ESC [ */ 36 extern int gDecTable[]; /* ESC [ ? */ 37 extern int gScrTable[]; /* ESC # */ 38 extern int gIgnoreTable[]; /* ignore table */ 39 extern int gIesTable[]; /* ignore ESC table */ 40 extern int gEscIgnoreTable[]; /* ESC ignore table */ 41 extern int gMbcsTable[]; /* ESC $ */ 42 43 44 45 #define DEFAULT -1 46 #define NPARAM 10 // Max parameters 47 48 49 //! Get char from pty reader buffer. 50 inline status_t 51 TermParse::_NextParseChar(uchar &c) 52 { 53 if (fParserBufferOffset >= fParserBufferSize) { 54 // parser buffer empty 55 status_t error = _ReadParserBuffer(); 56 if (error != B_OK) 57 return error; 58 } 59 60 c = fParserBuffer[fParserBufferOffset++]; 61 62 return B_OK; 63 } 64 65 66 TermParse::TermParse(int fd) 67 : 68 fFd(fd), 69 fParseThread(-1), 70 fReaderThread(-1), 71 fReaderSem(-1), 72 fReaderLocker(-1), 73 fBufferPosition(0), 74 fReadBufferSize(0), 75 fParserBufferSize(0), 76 fParserBufferOffset(0), 77 fBuffer(NULL), 78 fQuitting(true) 79 { 80 } 81 82 83 TermParse::~TermParse() 84 { 85 StopThreads(); 86 } 87 88 89 status_t 90 TermParse::StartThreads(TerminalBuffer *buffer) 91 { 92 if (fBuffer != NULL) 93 return B_ERROR; 94 95 fQuitting = false; 96 fBuffer = buffer; 97 98 status_t status = InitPtyReader(); 99 if (status < B_OK) { 100 fBuffer = NULL; 101 return status; 102 } 103 104 status = InitTermParse(); 105 if (status < B_OK) { 106 StopPtyReader(); 107 fBuffer = NULL; 108 return status; 109 } 110 111 return B_OK; 112 } 113 114 115 status_t 116 TermParse::StopThreads() 117 { 118 if (fBuffer == NULL) 119 return B_ERROR; 120 121 fQuitting = true; 122 123 StopPtyReader(); 124 StopTermParse(); 125 126 fBuffer = NULL; 127 128 return B_OK; 129 } 130 131 132 //! Initialize and spawn EscParse thread. 133 status_t 134 TermParse::InitTermParse() 135 { 136 if (fParseThread >= 0) 137 return B_ERROR; // we might want to return B_OK instead ? 138 139 fParseThread = spawn_thread(_escparse_thread, "EscParse", 140 B_DISPLAY_PRIORITY, this); 141 142 resume_thread(fParseThread); 143 144 return B_OK; 145 } 146 147 148 //! Initialize and spawn PtyReader thread. 149 status_t 150 TermParse::InitPtyReader() 151 { 152 if (fReaderThread >= 0) 153 return B_ERROR; // same as above 154 155 fReaderSem = create_sem(0, "pty_reader_sem"); 156 if (fReaderSem < 0) 157 return fReaderSem; 158 159 fReaderLocker = create_sem(0, "pty_locker_sem"); 160 if (fReaderLocker < 0) { 161 delete_sem(fReaderSem); 162 fReaderSem = -1; 163 return fReaderLocker; 164 } 165 166 fReaderThread = spawn_thread(_ptyreader_thread, "PtyReader", 167 B_NORMAL_PRIORITY, this); 168 if (fReaderThread < 0) { 169 delete_sem(fReaderSem); 170 fReaderSem = -1; 171 delete_sem(fReaderLocker); 172 fReaderLocker = -1; 173 return fReaderThread; 174 } 175 176 resume_thread(fReaderThread); 177 178 return B_OK; 179 } 180 181 182 void 183 TermParse::StopTermParse() 184 { 185 if (fParseThread >= 0) { 186 status_t dummy; 187 wait_for_thread(fParseThread, &dummy); 188 fParseThread = -1; 189 } 190 } 191 192 193 void 194 TermParse::StopPtyReader() 195 { 196 if (fReaderSem >= 0) { 197 delete_sem(fReaderSem); 198 fReaderSem = -1; 199 } 200 if (fReaderLocker >= 0) { 201 delete_sem(fReaderLocker); 202 fReaderLocker = -1; 203 } 204 205 if (fReaderThread >= 0) { 206 suspend_thread(fReaderThread); 207 208 status_t status; 209 wait_for_thread(fReaderThread, &status); 210 211 fReaderThread = -1; 212 } 213 } 214 215 216 //! Get data from pty device. 217 int32 218 TermParse::PtyReader() 219 { 220 int32 bufferSize = 0; 221 int32 readPos = 0; 222 while (!fQuitting) { 223 // If Pty Buffer nearly full, snooze this thread, and continue. 224 while (READ_BUF_SIZE - bufferSize < MIN_PTY_BUFFER_SPACE) { 225 status_t status; 226 do { 227 status = acquire_sem(fReaderLocker); 228 } while (status == B_INTERRUPTED); 229 if (status < B_OK) 230 return status; 231 232 bufferSize = fReadBufferSize; 233 } 234 235 // Read PTY 236 uchar buf[READ_BUF_SIZE]; 237 ssize_t nread = read(fFd, buf, READ_BUF_SIZE - bufferSize); 238 if (nread <= 0) { 239 fBuffer->NotifyQuit(errno); 240 return B_OK; 241 } 242 243 // Copy read string to PtyBuffer. 244 245 int32 left = READ_BUF_SIZE - readPos; 246 247 if (nread >= left) { 248 memcpy(fReadBuffer + readPos, buf, left); 249 memcpy(fReadBuffer, buf + left, nread - left); 250 } else 251 memcpy(fReadBuffer + readPos, buf, nread); 252 253 bufferSize = atomic_add(&fReadBufferSize, nread); 254 if (bufferSize == 0) 255 release_sem(fReaderSem); 256 257 bufferSize += nread; 258 readPos = (readPos + nread) % READ_BUF_SIZE; 259 } 260 261 return B_OK; 262 } 263 264 265 void 266 TermParse::DumpState(int *groundtable, int *parsestate, uchar c) 267 { 268 static const struct { 269 int *p; 270 const char *name; 271 } tables[] = { 272 #define T(t) \ 273 { t, #t } 274 T(gUTF8GroundTable), 275 T(gCS96GroundTable), 276 T(gISO8859GroundTable), 277 T(gSJISGroundTable), 278 T(gEscTable), 279 T(gCsiTable), 280 T(gDecTable), 281 T(gScrTable), 282 T(gIgnoreTable), 283 T(gIesTable), 284 T(gEscIgnoreTable), 285 T(gMbcsTable), 286 { NULL, NULL } 287 }; 288 int i; 289 fprintf(stderr, "groundtable: "); 290 for (i = 0; tables[i].p; i++) 291 if (tables[i].p == groundtable) 292 fprintf(stderr, "%s\t", tables[i].name); 293 fprintf(stderr, "parsestate: "); 294 for (i = 0; tables[i].p; i++) 295 if (tables[i].p == parsestate) 296 fprintf(stderr, "%s\t", tables[i].name); 297 fprintf(stderr, "char: 0x%02x (%d)\n", c, c); 298 } 299 300 301 int32 302 TermParse::EscParse() 303 { 304 int tmp; 305 int top, bot; 306 int cs96 = 0; 307 uchar curess = 0; 308 309 char cbuf[4], dstbuf[4]; 310 char *ptr; 311 312 int now_coding = -1; 313 314 ushort attr = BACKCOLOR; 315 316 int param[NPARAM]; 317 int nparam = 1; 318 319 int row, col; 320 321 /* default coding system is UTF8 */ 322 int *groundtable = gUTF8GroundTable; 323 int *parsestate = groundtable; 324 325 int width = 1; 326 BAutolock locker(fBuffer); 327 328 while (!fQuitting) { 329 uchar c; 330 if (_NextParseChar(c) < B_OK) 331 break; 332 333 //DumpState(groundtable, parsestate, c); 334 335 if (now_coding != fBuffer->Encoding()) { 336 /* 337 * Change coding, change parse table. 338 */ 339 switch (fBuffer->Encoding()) { 340 case B_ISO1_CONVERSION: 341 case B_ISO2_CONVERSION: 342 case B_ISO3_CONVERSION: 343 case B_ISO4_CONVERSION: 344 case B_ISO5_CONVERSION: 345 case B_ISO6_CONVERSION: 346 case B_ISO7_CONVERSION: 347 case B_ISO8_CONVERSION: 348 case B_ISO9_CONVERSION: 349 case B_ISO10_CONVERSION: 350 groundtable = gISO8859GroundTable; 351 break; 352 case B_SJIS_CONVERSION: 353 groundtable = gSJISGroundTable; 354 break; 355 case B_EUC_CONVERSION: 356 case B_EUC_KR_CONVERSION: 357 case B_JIS_CONVERSION: 358 case B_GBK_CONVERSION: 359 case B_BIG5_CONVERSION: 360 groundtable = gISO8859GroundTable; 361 break; 362 case M_UTF8: 363 default: 364 groundtable = gUTF8GroundTable; 365 break; 366 } 367 parsestate = groundtable; 368 now_coding = fBuffer->Encoding(); 369 } 370 371 //debug_printf("TermParse: char: '%c' (%d), parse state: %d\n", c, c, parsestate[c]); 372 switch (parsestate[c]) { 373 case CASE_PRINT: 374 fBuffer->InsertChar((char)c, attr); 375 break; 376 377 case CASE_PRINT_GR: 378 /* case iso8859 gr character, or euc */ 379 ptr = cbuf; 380 if (now_coding == B_EUC_CONVERSION 381 || now_coding == B_EUC_KR_CONVERSION 382 || now_coding == B_JIS_CONVERSION 383 || now_coding == B_GBK_CONVERSION 384 || now_coding == B_BIG5_CONVERSION) { 385 switch (parsestate[curess]) { 386 case CASE_SS2: /* JIS X 0201 */ 387 width = 1; 388 *ptr++ = curess; 389 *ptr++ = c; 390 *ptr = 0; 391 curess = 0; 392 break; 393 394 case CASE_SS3: /* JIS X 0212 */ 395 width = 1; 396 *ptr++ = curess; 397 *ptr++ = c; 398 _NextParseChar(c); 399 *ptr++ = c; 400 *ptr = 0; 401 curess = 0; 402 break; 403 404 default: /* JIS X 0208 */ 405 width = 2; 406 *ptr++ = c; 407 _NextParseChar(c); 408 *ptr++ = c; 409 *ptr = 0; 410 break; 411 } 412 } else { 413 /* ISO-8859-1...10 and MacRoman */ 414 *ptr++ = c; 415 *ptr = 0; 416 } 417 418 if (now_coding != B_JIS_CONVERSION) { 419 CodeConv::ConvertToInternal(cbuf, -1, dstbuf, now_coding); 420 } else { 421 CodeConv::ConvertToInternal(cbuf, -1, dstbuf, 422 B_EUC_CONVERSION); 423 } 424 425 fBuffer->InsertChar(dstbuf, 4, width, attr); 426 break; 427 428 case CASE_PRINT_CS96: 429 cbuf[0] = c | 0x80; 430 _NextParseChar(c); 431 cbuf[1] = c | 0x80; 432 cbuf[2] = 0; 433 CodeConv::ConvertToInternal(cbuf, 2, dstbuf, B_EUC_CONVERSION); 434 fBuffer->InsertChar(dstbuf, 4, attr); 435 break; 436 437 case CASE_LF: 438 fBuffer->InsertLF(); 439 break; 440 441 case CASE_CR: 442 fBuffer->InsertCR(); 443 break; 444 445 case CASE_SJIS_KANA: 446 cbuf[0] = c; 447 cbuf[1] = '\0'; 448 CodeConv::ConvertToInternal(cbuf, 1, dstbuf, now_coding); 449 fBuffer->InsertChar(dstbuf, 4, attr); 450 break; 451 452 case CASE_SJIS_INSTRING: 453 cbuf[0] = c; 454 _NextParseChar(c); 455 cbuf[1] = c; 456 cbuf[2] = '\0'; 457 CodeConv::ConvertToInternal(cbuf, 2, dstbuf, now_coding); 458 fBuffer->InsertChar(dstbuf, 4, attr); 459 break; 460 461 case CASE_UTF8_2BYTE: 462 cbuf[0] = c; 463 _NextParseChar(c); 464 if (groundtable[c] != CASE_UTF8_INSTRING) 465 break; 466 cbuf[1] = c; 467 cbuf[2] = '\0'; 468 469 fBuffer->InsertChar(cbuf, 2, attr); 470 break; 471 472 case CASE_UTF8_3BYTE: 473 cbuf[0] = c; 474 _NextParseChar(c); 475 if (groundtable[c] != CASE_UTF8_INSTRING) 476 break; 477 cbuf[1] = c; 478 479 _NextParseChar(c); 480 if (groundtable[c] != CASE_UTF8_INSTRING) 481 break; 482 cbuf[2] = c; 483 cbuf[3] = '\0'; 484 fBuffer->InsertChar(cbuf, 3, attr); 485 break; 486 487 case CASE_MBCS: 488 /* ESC $ */ 489 parsestate = gMbcsTable; 490 break; 491 492 case CASE_GSETS: 493 /* ESC $ ? */ 494 parsestate = gCS96GroundTable; 495 cs96 = 1; 496 break; 497 498 case CASE_SCS_STATE: 499 { 500 cs96 = 0; 501 uchar dummy; 502 _NextParseChar(dummy); 503 parsestate = groundtable; 504 break; 505 } 506 case CASE_GROUND_STATE: 507 /* exit ignore mode */ 508 parsestate = groundtable; 509 break; 510 511 case CASE_BELL: 512 beep(); 513 break; 514 515 case CASE_BS: 516 fBuffer->MoveCursorLeft(1); 517 break; 518 519 case CASE_TAB: 520 tmp = fBuffer->Cursor().x; 521 tmp %= 8; 522 fBuffer->MoveCursorRight(8 - tmp); 523 break; 524 525 case CASE_ESC: 526 /* escape */ 527 parsestate = gEscTable; 528 break; 529 530 case CASE_IGNORE_STATE: 531 /* Ies: ignore anything else */ 532 parsestate = gIgnoreTable; 533 break; 534 535 case CASE_IGNORE_ESC: 536 /* Ign: escape */ 537 parsestate = gIesTable; 538 break; 539 540 case CASE_IGNORE: 541 /* Ignore character */ 542 break; 543 544 case CASE_SI: 545 break; 546 547 case CASE_SO: 548 break; 549 550 case CASE_SCR_STATE: // ESC # 551 /* enter scr state */ 552 parsestate = gScrTable; 553 break; 554 555 case CASE_ESC_IGNORE: 556 /* unknown escape sequence */ 557 parsestate = gEscIgnoreTable; 558 break; 559 560 case CASE_ESC_DIGIT: // ESC [ number 561 /* digit in csi or dec mode */ 562 if ((row = param[nparam - 1]) == DEFAULT) 563 row = 0; 564 param[nparam - 1] = 10 * row + (c - '0'); 565 break; 566 567 case CASE_ESC_SEMI: // ESC ; 568 /* semicolon in csi or dec mode */ 569 if (nparam < NPARAM) 570 param[nparam++] = DEFAULT; 571 break; 572 573 case CASE_DEC_STATE: 574 /* enter dec mode */ 575 parsestate = gDecTable; 576 break; 577 578 case CASE_ICH: // ESC [@ insert charactor 579 /* ICH */ 580 if ((row = param[0]) < 1) 581 row = 1; 582 fBuffer->InsertSpace(row); 583 parsestate = groundtable; 584 break; 585 586 case CASE_CUU: // ESC [A cursor up, up arrow key. 587 /* CUU */ 588 if ((row = param[0]) < 1) 589 row = 1; 590 fBuffer->MoveCursorUp(row); 591 parsestate = groundtable; 592 break; 593 594 case CASE_CUD: // ESC [B cursor down, down arrow key. 595 /* CUD */ 596 if ((row = param[0]) < 1) 597 row = 1; 598 fBuffer->MoveCursorDown(row); 599 parsestate = groundtable; 600 break; 601 602 case CASE_CUF: // ESC [C cursor forword 603 /* CUF */ 604 if ((row = param[0]) < 1) 605 row = 1; 606 fBuffer->MoveCursorRight(row); 607 parsestate = groundtable; 608 break; 609 610 case CASE_CUB: // ESC [D cursor backword 611 /* CUB */ 612 if ((row = param[0]) < 1) 613 row = 1; 614 fBuffer->MoveCursorLeft(row); 615 parsestate = groundtable; 616 break; 617 618 case CASE_CUP: // ESC [...H move cursor 619 /* CUP | HVP */ 620 if ((row = param[0]) < 1) 621 row = 1; 622 if (nparam < 2 || (col = param[1]) < 1) 623 col = 1; 624 625 fBuffer->SetCursor(col - 1, row - 1 ); 626 parsestate = groundtable; 627 break; 628 629 case CASE_ED: // ESC [ ...J clear screen 630 /* ED */ 631 switch (param[0]) { 632 case DEFAULT: 633 case 0: 634 fBuffer->EraseBelow(); 635 break; 636 637 case 1: 638 fBuffer->EraseAbove(); 639 break; 640 641 case 2: 642 fBuffer->EraseAll(); 643 break; 644 } 645 parsestate = groundtable; 646 break; 647 648 case CASE_EL: // delete line 649 /* EL */ 650 fBuffer->DeleteColumns(); 651 parsestate = groundtable; 652 break; 653 654 case CASE_IL: 655 /* IL */ 656 if ((row = param[0]) < 1) 657 row = 1; 658 fBuffer->InsertLines(row); 659 parsestate = groundtable; 660 break; 661 662 case CASE_DL: 663 /* DL */ 664 if ((row = param[0]) < 1) 665 row = 1; 666 fBuffer->DeleteLines(row); 667 parsestate = groundtable; 668 break; 669 670 case CASE_DCH: 671 /* DCH */ 672 if ((row = param[0]) < 1) 673 row = 1; 674 fBuffer->DeleteChars(row); 675 parsestate = groundtable; 676 break; 677 678 case CASE_SET: 679 /* SET */ 680 if (param[0] == 4) 681 fBuffer->SetInsertMode(MODE_INSERT); 682 parsestate = groundtable; 683 break; 684 685 case CASE_RST: 686 /* RST */ 687 if (param[0] == 4) 688 fBuffer->SetInsertMode(MODE_OVER); 689 parsestate = groundtable; 690 break; 691 692 case CASE_SGR: 693 /* SGR */ 694 for (row = 0; row < nparam; ++row) { 695 switch (param[row]) { 696 case DEFAULT: 697 case 0: /* Reset attribute */ 698 attr = 0; 699 break; 700 701 case 1: 702 case 5: /* Bold */ 703 attr |= BOLD; 704 break; 705 706 case 4: /* Underline */ 707 attr |= UNDERLINE; 708 break; 709 710 case 7: /* Inverse */ 711 attr |= INVERSE; 712 break; 713 714 case 22: /* Not Bold */ 715 attr &= ~BOLD; 716 break; 717 718 case 24: /* Not Underline */ 719 attr &= ~UNDERLINE; 720 break; 721 722 case 27: /* Not Inverse */ 723 attr &= ~INVERSE; 724 break; 725 726 case 30: 727 case 31: 728 case 32: 729 case 33: 730 case 34: 731 case 35: 732 case 36: 733 case 37: 734 attr &= ~FORECOLOR; 735 attr |= FORECOLORED(param[row] - 30); 736 attr |= FORESET; 737 break; 738 739 case 39: 740 attr &= ~FORESET; 741 break; 742 743 case 40: 744 case 41: 745 case 42: 746 case 43: 747 case 44: 748 case 45: 749 case 46: 750 case 47: 751 attr &= ~BACKCOLOR; 752 attr |= BACKCOLORED(param[row] - 40); 753 attr |= BACKSET; 754 break; 755 756 case 49: 757 attr &= ~BACKSET; 758 break; 759 } 760 } 761 parsestate = groundtable; 762 break; 763 764 case CASE_CPR: 765 // Q & D hack by Y.Hayakawa (hida@sawada.riec.tohoku.ac.jp) 766 // 21-JUL-99 767 _DeviceStatusReport(param[0]); 768 parsestate = groundtable; 769 break; 770 771 case CASE_DECSTBM: 772 /* DECSTBM - set scrolling region */ 773 774 if ((top = param[0]) < 1) 775 top = 1; 776 777 if (nparam < 2) 778 bot = fBuffer->Height(); 779 else 780 bot = param[1]; 781 782 top--; 783 bot--; 784 785 if (bot > top) 786 fBuffer->SetScrollRegion(top, bot); 787 788 parsestate = groundtable; 789 break; 790 791 case CASE_DECREQTPARM: 792 parsestate = groundtable; 793 break; 794 795 case CASE_DECSET: 796 /* DECSET */ 797 for (int i = 0; i < nparam; i++) 798 _DecPrivateModeSet(param[i]); 799 parsestate = groundtable; 800 break; 801 802 case CASE_DECRST: 803 /* DECRST */ 804 for (int i = 0; i < nparam; i++) 805 _DecPrivateModeReset(param[i]); 806 parsestate = groundtable; 807 break; 808 809 case CASE_DECALN: 810 /* DECALN */ 811 // if(screen->cursor_state) 812 // HideCursor(); 813 // ScrnRefresh(screen, 0, 0, screen->max_row + 1, 814 // screen->max_col + 1, False); 815 parsestate = groundtable; 816 break; 817 818 // case CASE_GSETS: 819 // screen->gsets[scstype] = GSET(c) | cs96; 820 // parsestate = groundtable; 821 // break; 822 823 case CASE_DECSC: 824 /* DECSC */ 825 fBuffer->SaveCursor(); 826 parsestate = groundtable; 827 break; 828 829 case CASE_DECRC: 830 /* DECRC */ 831 fBuffer->RestoreCursor(); 832 parsestate = groundtable; 833 break; 834 835 case CASE_HTS: 836 /* HTS */ 837 // TabSet(term->tabs, screen->cur_col); 838 parsestate = groundtable; 839 break; 840 841 case CASE_RI: 842 /* RI */ 843 if (fBuffer->Cursor().y == 0) 844 fBuffer->ScrollBy(-1); 845 else 846 fBuffer->MoveCursorUp(1); 847 parsestate = groundtable; 848 break; 849 850 case CASE_SS2: 851 /* SS2 */ 852 curess = c; 853 parsestate = groundtable; 854 break; 855 856 case CASE_SS3: 857 /* SS3 */ 858 curess = c; 859 parsestate = groundtable; 860 break; 861 862 case CASE_CSI_STATE: 863 /* enter csi state */ 864 nparam = 1; 865 param[0] = DEFAULT; 866 parsestate = gCsiTable; 867 break; 868 869 case CASE_OSC: 870 { 871 /* Operating System Command: ESC ] */ 872 char string[512]; 873 uint32 len = 0; 874 uchar mode_char; 875 _NextParseChar(mode_char); 876 if (mode_char != '0' 877 && mode_char != '1' 878 && mode_char != '2') { 879 parsestate = groundtable; 880 break; 881 } 882 uchar current_char; 883 _NextParseChar(current_char); 884 while (_NextParseChar(current_char) == B_OK 885 && current_char != 0x7) { 886 if (!isprint(current_char & 0x7f) 887 || len+2 >= sizeof(string)) 888 break; 889 string[len++] = current_char; 890 } 891 if (current_char == 0x7) { 892 string[len] = '\0'; 893 switch (mode_char) { 894 case '0': 895 case '2': 896 fBuffer->SetTitle(string); 897 break; 898 case '1': 899 break; 900 } 901 } 902 parsestate = groundtable; 903 break; 904 } 905 906 case CASE_RIS: // ESC c ... Reset terminal. 907 break; 908 909 case CASE_LS2: 910 /* LS2 */ 911 // screen->curgl = 2; 912 parsestate = groundtable; 913 break; 914 915 case CASE_LS3: 916 /* LS3 */ 917 // screen->curgl = 3; 918 parsestate = groundtable; 919 break; 920 921 case CASE_LS3R: 922 /* LS3R */ 923 // screen->curgr = 3; 924 parsestate = groundtable; 925 break; 926 927 case CASE_LS2R: 928 /* LS2R */ 929 // screen->curgr = 2; 930 parsestate = groundtable; 931 break; 932 933 case CASE_LS1R: 934 /* LS1R */ 935 // screen->curgr = 1; 936 parsestate = groundtable; 937 break; 938 939 case CASE_VPA: // ESC [...d move cursor absolute vertical 940 /* VPA (CV) */ 941 if ((row = param[0]) < 1) 942 row = 1; 943 944 // note beterm wants it 1-based unlike usual terminals 945 fBuffer->SetCursorY(row - 1); 946 parsestate = groundtable; 947 break; 948 949 case CASE_HPA: // ESC [...G move cursor absolute horizontal 950 /* HPA (CH) */ 951 if ((col = param[0]) < 1) 952 col = 1; 953 954 // note beterm wants it 1-based unlike usual terminals 955 fBuffer->SetCursorX(col - 1); 956 parsestate = groundtable; 957 break; 958 959 case CASE_SU: // scroll screen up 960 if ((row = param[0]) < 1) 961 row = 1; 962 fBuffer->ScrollBy(row); 963 parsestate = groundtable; 964 break; 965 966 case CASE_SD: // scroll screen down 967 if ((row = param[0]) < 1) 968 row = 1; 969 fBuffer->ScrollBy(-row); 970 parsestate = groundtable; 971 break; 972 973 974 case CASE_ECH: // erase characters 975 if ((col = param[0]) < 1) 976 col = 1; 977 fBuffer->EraseChars(col); 978 parsestate = groundtable; 979 break; 980 981 default: 982 break; 983 } 984 } 985 986 return B_OK; 987 } 988 989 990 /*static*/ int32 991 TermParse::_ptyreader_thread(void *data) 992 { 993 return reinterpret_cast<TermParse *>(data)->PtyReader(); 994 } 995 996 997 /*static*/ int32 998 TermParse::_escparse_thread(void *data) 999 { 1000 return reinterpret_cast<TermParse *>(data)->EscParse(); 1001 } 1002 1003 1004 status_t 1005 TermParse::_ReadParserBuffer() 1006 { 1007 // We have to unlock the terminal buffer while waiting for data from the 1008 // PTY. We don't have to unlock when we don't need to wait, but we do it 1009 // anyway, so that TermView won't be starved when trying to synchronize. 1010 fBuffer->Unlock(); 1011 1012 // wait for new input from pty 1013 if (fReadBufferSize == 0) { 1014 status_t status = B_OK; 1015 while (fReadBufferSize == 0 && status == B_OK) { 1016 do { 1017 status = acquire_sem(fReaderSem); 1018 } while (status == B_INTERRUPTED); 1019 1020 // eat any sems that were released unconditionally 1021 int32 semCount; 1022 if (get_sem_count(fReaderSem, &semCount) == B_OK && semCount > 0) 1023 acquire_sem_etc(fReaderSem, semCount, B_RELATIVE_TIMEOUT, 0); 1024 } 1025 1026 if (status < B_OK) { 1027 fBuffer->Lock(); 1028 return status; 1029 } 1030 } 1031 1032 int32 toRead = fReadBufferSize; 1033 if (toRead > ESC_PARSER_BUFFER_SIZE) 1034 toRead = ESC_PARSER_BUFFER_SIZE; 1035 1036 for (int32 i = 0; i < toRead; i++) { 1037 // TODO: This could be optimized using memcpy instead and 1038 // calculating space left as in the PtyReader(). 1039 fParserBuffer[i] = fReadBuffer[fBufferPosition]; 1040 fBufferPosition = (fBufferPosition + 1) % READ_BUF_SIZE; 1041 } 1042 1043 int32 bufferSize = atomic_add(&fReadBufferSize, -toRead); 1044 1045 // If the pty reader thread waits and we have made enough space in the 1046 // buffer now, let it run again. 1047 if (bufferSize > READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE 1048 && bufferSize - toRead <= READ_BUF_SIZE - MIN_PTY_BUFFER_SPACE) { 1049 release_sem(fReaderLocker); 1050 } 1051 1052 fParserBufferSize = toRead; 1053 fParserBufferOffset = 0; 1054 1055 fBuffer->Lock(); 1056 return B_OK; 1057 } 1058 1059 1060 void 1061 TermParse::_DeviceStatusReport(int n) 1062 { 1063 char sbuf[16] ; 1064 int len; 1065 1066 switch (n) { 1067 case 5: 1068 { 1069 const char* toWrite = "\033[0n"; 1070 write(fFd, toWrite, strlen(toWrite)); 1071 break ; 1072 } 1073 case 6: 1074 len = sprintf(sbuf, "\033[%ld;%ldR", fBuffer->Height(), 1075 fBuffer->Width()) ; 1076 write(fFd, sbuf, len); 1077 break ; 1078 default: 1079 return; 1080 } 1081 } 1082 1083 1084 void 1085 TermParse::_DecPrivateModeSet(int value) 1086 { 1087 switch (value) { 1088 case 1: 1089 // Application Cursor Keys (whatever that means). 1090 // Not supported yet. 1091 break; 1092 case 5: 1093 // Reverse Video (inverses colors for the complete screen 1094 // -- when followed by normal video, that's shortly flashes the 1095 // screen). 1096 // Not supported yet. 1097 break; 1098 case 9: 1099 // Set Mouse X and Y on button press. 1100 fBuffer->ReportX10MouseEvent(true); 1101 break; 1102 case 12: 1103 // Start Blinking Cursor. 1104 // Not supported yet. 1105 break; 1106 case 25: 1107 // Show Cursor. 1108 // Not supported yet. 1109 break; 1110 case 47: 1111 // Use Alternate Screen Buffer. 1112 fBuffer->UseAlternateScreenBuffer(false); 1113 break; 1114 case 1000: 1115 // Send Mouse X & Y on button press and release. 1116 fBuffer->ReportNormalMouseEvent(true); 1117 break; 1118 case 1002: 1119 // Send Mouse X and Y on button press and release, and on motion 1120 // when the mouse enter a new cell 1121 fBuffer->ReportButtonMouseEvent(true); 1122 break; 1123 case 1003: 1124 // Use All Motion Mouse Tracking 1125 fBuffer->ReportAnyMouseEvent(true); 1126 break; 1127 case 1034: 1128 // TODO: Interprete "meta" key, sets eighth bit. 1129 // Not supported yet. 1130 break; 1131 case 1036: 1132 // TODO: Send ESC when Meta modifies a key 1133 // Not supported yet. 1134 break; 1135 case 1039: 1136 // TODO: Send ESC when Alt modifies a key 1137 // Not supported yet. 1138 break; 1139 case 1049: 1140 // Save cursor as in DECSC and use Alternate Screen Buffer, clearing 1141 // it first. 1142 fBuffer->SaveCursor(); 1143 fBuffer->UseAlternateScreenBuffer(true); 1144 break; 1145 } 1146 } 1147 1148 1149 void 1150 TermParse::_DecPrivateModeReset(int value) 1151 { 1152 switch (value) { 1153 case 1: 1154 // Normal Cursor Keys (whatever that means). 1155 // Not supported yet. 1156 break; 1157 case 3: 1158 // 80 Column Mode. 1159 // Not supported yet. 1160 break; 1161 case 4: 1162 // Jump (Fast) Scroll. 1163 // Not supported yet. 1164 break; 1165 case 5: 1166 // Normal Video (Leaves Reverse Video, cf. there). 1167 // Not supported yet. 1168 break; 1169 case 9: 1170 // Disable Mouse X and Y on button press. 1171 fBuffer->ReportX10MouseEvent(false); 1172 break; 1173 case 12: 1174 // Stop Blinking Cursor. 1175 // Not supported yet. 1176 break; 1177 case 25: 1178 // Hide Cursor 1179 // Not supported yet. 1180 break; 1181 case 47: 1182 // Use Normal Screen Buffer. 1183 fBuffer->UseNormalScreenBuffer(); 1184 break; 1185 case 1000: 1186 // Don't send Mouse X & Y on button press and release. 1187 fBuffer->ReportNormalMouseEvent(false); 1188 break; 1189 case 1002: 1190 // Don't send Mouse X and Y on button press and release, and on motion 1191 // when the mouse enter a new cell 1192 fBuffer->ReportButtonMouseEvent(false); 1193 break; 1194 case 1003: 1195 // Disable All Motion Mouse Tracking. 1196 fBuffer->ReportAnyMouseEvent(false); 1197 break; 1198 case 1034: 1199 // Don't interprete "meta" key. 1200 // Not supported yet. 1201 break; 1202 case 1036: 1203 // TODO: Don't send ESC when Meta modifies a key 1204 // Not supported yet. 1205 break; 1206 case 1039: 1207 // TODO: Don't send ESC when Alt modifies a key 1208 // Not supported yet. 1209 break; 1210 case 1049: 1211 // Use Normal Screen Buffer and restore cursor as in DECRC. 1212 fBuffer->UseNormalScreenBuffer(); 1213 fBuffer->RestoreCursor(); 1214 break; 1215 } 1216 } 1217