1 /* 2 * Copyright 2001-2008, Haiku, Inc. 3 * Copyright 2003-2004 Kian Duffy, myob@users.sourceforge.net 4 * Parts Copyright 1998-1999 Kazuho Okui and Takashi Murai. 5 * All rights reserved. Distributed under the terms of the MIT license. 6 * 7 * Authors: 8 * Stefano Ceccherini <stefano.ceccherini@gmail.com> 9 * Kian Duffy, myob@users.sourceforge.net 10 * Y.Hayakawa, hida@sawada.riec.tohoku.ac.jp 11 */ 12 13 14 #include "TermView.h" 15 16 #include "CodeConv.h" 17 #include "Shell.h" 18 #include "TermBuffer.h" 19 #include "TermConst.h" 20 #include "VTkeymap.h" 21 22 #include <Alert.h> 23 #include <Beep.h> 24 #include <Clipboard.h> 25 #include <Debug.h> 26 #include <Input.h> 27 #include <Message.h> 28 #include <MessageRunner.h> 29 #include <Path.h> 30 #include <PopUpMenu.h> 31 #include <PropertyInfo.h> 32 #include <Roster.h> 33 #include <ScrollBar.h> 34 #include <String.h> 35 #include <Window.h> 36 37 #include <ctype.h> 38 #include <signal.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <termios.h> 42 43 #include <new> 44 #include <algorithm> 45 46 // defined VTKeyTbl.c 47 extern int function_keycode_table[]; 48 extern char *function_key_char_table[]; 49 50 const static rgb_color kTermColorTable[8] = { 51 { 40, 40, 40, 0}, // black 52 {204, 0, 0, 0}, // red 53 { 78, 154, 6, 0}, // green 54 {218, 168, 0, 0}, // yellow 55 { 51, 102, 152, 0}, // blue 56 {115, 68, 123, 0}, // magenta 57 { 6, 152, 154, 0}, // cyan 58 {245, 245, 245, 0}, // white 59 }; 60 61 62 #define ROWS_DEFAULT 25 63 #define COLUMNS_DEFAULT 80 64 65 66 static property_info sPropList[] = { 67 { "encoding", 68 {B_GET_PROPERTY, 0}, 69 {B_DIRECT_SPECIFIER, 0}, 70 "get terminal encoding"}, 71 { "encoding", 72 {B_SET_PROPERTY, 0}, 73 {B_DIRECT_SPECIFIER, 0}, 74 "set terminal encoding"}, 75 { "tty", 76 {B_GET_PROPERTY, 0}, 77 {B_DIRECT_SPECIFIER, 0}, 78 "get tty name."}, 79 { 0 } 80 }; 81 82 83 const static uint32 kUpdateSigWinch = 'Rwin'; 84 85 const static rgb_color kBlackColor = { 0, 0, 0, 255 }; 86 const static rgb_color kWhiteColor = { 255, 255, 255, 255 }; 87 88 89 90 TermView::TermView(BRect frame, int32 argc, const char **argv, int32 historySize) 91 : BView(frame, "termview", B_FOLLOW_ALL, 92 B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE| B_PULSE_NEEDED), 93 fShell(NULL), 94 fWinchRunner(NULL), 95 fFontWidth(0), 96 fFontHeight(0), 97 fFontAscent(0), 98 fUpdateFlag(false), 99 fInsertModeFlag(MODE_OVER), 100 fScrollUpCount(0), 101 fScrollBarRange(0), 102 fFrameResized(false), 103 fLastCursorTime(0), 104 fCursorDrawFlag(true), 105 fCursorStatus(true), 106 fCursorBlinkingFlag(true), 107 fCursorRedrawFlag(true), 108 fCursorHeight(0), 109 fCurPos(0, 0), 110 fCurStack(0, 0), 111 fBufferStartPos(-1), 112 fTermRows(ROWS_DEFAULT), 113 fTermColumns(COLUMNS_DEFAULT), 114 fEncoding(M_UTF8), 115 fTop(0), 116 fTextBuffer(NULL), 117 fScrollBar(NULL), 118 fTextForeColor(kBlackColor), 119 fTextBackColor(kWhiteColor), 120 fCursorForeColor(kWhiteColor), 121 fCursorBackColor(kBlackColor), 122 fSelectForeColor(kWhiteColor), 123 fSelectBackColor(kBlackColor), 124 fScrTop(0), 125 fScrBot(fTermRows - 1), 126 fScrBufSize(historySize), 127 fScrRegionSet(0), 128 fClickPoint(0, 0), 129 fSelStart(-1, -1), 130 fSelEnd(-1, -1), 131 fMouseTracking(false), 132 fIMflag(false) 133 { 134 _InitObject(argc, argv); 135 } 136 137 138 TermView::TermView(int rows, int columns, int32 argc, const char **argv, int32 historySize) 139 : BView(BRect(0, 0, 0, 0), "termview", B_FOLLOW_ALL, 140 B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE| B_PULSE_NEEDED), 141 fShell(NULL), 142 fWinchRunner(NULL), 143 fFontWidth(0), 144 fFontHeight(0), 145 fFontAscent(0), 146 fUpdateFlag(false), 147 fInsertModeFlag(MODE_OVER), 148 fScrollUpCount(0), 149 fScrollBarRange(0), 150 fFrameResized(false), 151 fLastCursorTime(0), 152 fCursorDrawFlag(true), 153 fCursorStatus(true), 154 fCursorBlinkingFlag(true), 155 fCursorRedrawFlag(true), 156 fCursorHeight(0), 157 fCurPos(0, 0), 158 fCurStack(0, 0), 159 fBufferStartPos(-1), 160 fTermRows(rows), 161 fTermColumns(columns), 162 fEncoding(M_UTF8), 163 fTop(0), 164 fTextBuffer(NULL), 165 fScrollBar(NULL), 166 fTextForeColor(kBlackColor), 167 fTextBackColor(kWhiteColor), 168 fCursorForeColor(kWhiteColor), 169 fCursorBackColor(kBlackColor), 170 fSelectForeColor(kWhiteColor), 171 fSelectBackColor(kBlackColor), 172 fScrTop(0), 173 fScrBot(fTermRows - 1), 174 fScrBufSize(historySize), 175 fScrRegionSet(0), 176 fClickPoint(0, 0), 177 fSelStart(-1, -1), 178 fSelEnd(-1, -1), 179 fMouseTracking(false), 180 fIMflag(false) 181 { 182 _InitObject(argc, argv); 183 SetTermSize(fTermRows, fTermColumns, true); 184 } 185 186 187 TermView::TermView(BMessage *archive) 188 : 189 BView(archive), 190 fShell(NULL), 191 fWinchRunner(NULL), 192 fFontWidth(0), 193 fFontHeight(0), 194 fFontAscent(0), 195 fUpdateFlag(false), 196 fInsertModeFlag(MODE_OVER), 197 fScrollUpCount(0), 198 fScrollBarRange(0), 199 fFrameResized(false), 200 fLastCursorTime(0), 201 fCursorDrawFlag(true), 202 fCursorStatus(true), 203 fCursorBlinkingFlag(true), 204 fCursorRedrawFlag(true), 205 fCursorHeight(0), 206 fCurPos(0, 0), 207 fCurStack(0, 0), 208 fBufferStartPos(-1), 209 fTermRows(ROWS_DEFAULT), 210 fTermColumns(COLUMNS_DEFAULT), 211 fEncoding(M_UTF8), 212 fTop(0), 213 fTextBuffer(NULL), 214 fScrollBar(NULL), 215 fTextForeColor(kBlackColor), 216 fTextBackColor(kWhiteColor), 217 fCursorForeColor(kWhiteColor), 218 fCursorBackColor(kBlackColor), 219 fSelectForeColor(kWhiteColor), 220 fSelectBackColor(kBlackColor), 221 fScrTop(0), 222 fScrBot(fTermRows - 1), 223 fScrBufSize(1000), 224 fScrRegionSet(0), 225 fClickPoint(0, 0), 226 fSelStart(-1, -1), 227 fSelEnd(-1, -1), 228 fMouseTracking(false), 229 fIMflag(false) 230 { 231 // We need this 232 SetFlags(Flags() | B_WILL_DRAW | B_PULSE_NEEDED); 233 234 if (archive->FindInt32("encoding", (int32 *)&fEncoding) < B_OK) 235 fEncoding = M_UTF8; 236 if (archive->FindInt32("columns", (int32 *)&fTermColumns) < B_OK) 237 fTermColumns = COLUMNS_DEFAULT; 238 if (archive->FindInt32("rows", (int32 *)&fTermRows) < B_OK) 239 fTermRows = ROWS_DEFAULT; 240 241 int32 argc = 0; 242 if (archive->HasInt32("argc")) 243 archive->FindInt32("argc", &argc); 244 245 const char **argv = new const char*[argc]; 246 for (int32 i = 0; i < argc; i++) { 247 archive->FindString("argv", i, (const char **)&argv[i]); 248 } 249 250 // TODO: Retrieve colors, history size, etc. from archive 251 _InitObject(argc, argv); 252 253 delete[] argv; 254 } 255 256 257 status_t 258 TermView::_InitObject(int32 argc, const char **argv) 259 { 260 fTextBuffer = new (std::nothrow) TermBuffer(fTermRows, fTermColumns, fScrBufSize); 261 if (fTextBuffer == NULL) 262 return B_NO_MEMORY; 263 264 fShell = new (std::nothrow) Shell(); 265 if (fShell == NULL) 266 return B_NO_MEMORY; 267 268 SetTermFont(be_fixed_font); 269 SetTermSize(fTermRows, fTermColumns, false); 270 //SetIMAware(false); 271 272 status_t status = fShell->Open(fTermRows, fTermColumns, 273 EncodingAsShortString(fEncoding), 274 argc, argv); 275 276 if (status < B_OK) 277 return status; 278 279 status = _AttachShell(fShell); 280 if (status < B_OK) 281 return status; 282 283 284 SetLowColor(fTextBackColor); 285 SetViewColor(fTextBackColor); 286 287 return B_OK; 288 } 289 290 291 TermView::~TermView() 292 { 293 Shell *shell = fShell; 294 // _DetachShell sets fShell to NULL 295 296 _DetachShell(); 297 298 delete fTextBuffer; 299 delete shell; 300 } 301 302 303 /* static */ 304 BArchivable * 305 TermView::Instantiate(BMessage* data) 306 { 307 if (validate_instantiation(data, "TermView")) 308 return new (std::nothrow) TermView(data); 309 310 return NULL; 311 } 312 313 314 status_t 315 TermView::Archive(BMessage* data, bool deep) const 316 { 317 status_t status = BView::Archive(data, deep); 318 if (status == B_OK) 319 status = data->AddString("add_on", TERM_SIGNATURE); 320 if (status == B_OK) 321 status = data->AddInt32("encoding", (int32)fEncoding); 322 if (status == B_OK) 323 status = data->AddInt32("columns", (int32)fTermColumns); 324 if (status == B_OK) 325 status = data->AddInt32("rows", (int32)fTermRows); 326 327 return status; 328 } 329 330 331 void 332 TermView::GetPreferredSize(float *width, float *height) 333 { 334 if (width) 335 *width = fTermColumns * fFontWidth; 336 if (height) 337 *height = fTermRows * fFontHeight; 338 } 339 340 341 const char * 342 TermView::TerminalName() const 343 { 344 if (fShell == NULL) 345 return NULL; 346 347 return fShell->TTYName(); 348 } 349 350 351 //! Get width and height for terminal font 352 void 353 TermView::GetFontSize(int* _width, int* _height) 354 { 355 *_width = fFontWidth; 356 *_height = fFontHeight; 357 } 358 359 360 //! Set number of rows and columns in terminal 361 BRect 362 TermView::SetTermSize(int rows, int cols, bool resize) 363 { 364 if (rows > 0) 365 fTermRows = rows; 366 if (cols > 0) 367 fTermColumns = cols; 368 369 fTextBuffer->ResizeTo(fTermRows, fTermColumns, 0); 370 371 fScrTop = 0; 372 fScrBot = fTermRows - 1; 373 374 BRect rect(0, 0, fTermColumns * fFontWidth, fTermRows * fFontHeight); 375 376 if (resize) 377 ResizeTo(rect.Width(), rect.Height()); 378 379 return rect; 380 } 381 382 383 void 384 TermView::SetTextColor(rgb_color fore, rgb_color back) 385 { 386 fTextForeColor = fore; 387 fTextBackColor = back; 388 389 SetLowColor(fTextBackColor); 390 SetViewColor(fTextBackColor); 391 } 392 393 394 void 395 TermView::SetSelectColor(rgb_color fore, rgb_color back) 396 { 397 fSelectForeColor = fore; 398 fSelectBackColor = back; 399 } 400 401 402 void 403 TermView::SetCursorColor(rgb_color fore, rgb_color back) 404 { 405 fCursorForeColor = fore; 406 fCursorBackColor = back; 407 } 408 409 410 int 411 TermView::Encoding() const 412 { 413 return fEncoding; 414 } 415 416 417 void 418 TermView::SetEncoding(int encoding) 419 { 420 // TODO: Shell::_Spawn() sets the "TTYPE" environment variable using 421 // the string value of encoding. But when this function is called and 422 // the encoding changes, the new value is never passed to Shell. 423 fEncoding = encoding; 424 } 425 426 427 void 428 TermView::GetTermFont(BFont *font) const 429 { 430 if (font != NULL) 431 *font = fHalfFont; 432 } 433 434 435 //! Sets font for terminal 436 void 437 TermView::SetTermFont(const BFont *font) 438 { 439 int halfWidth = 0; 440 441 fHalfFont = font; 442 443 _FixFontAttributes(fHalfFont); 444 445 // calculate half font's max width 446 // Not Bounding, check only A-Z(For case of fHalfFont is KanjiFont. ) 447 for (int c = 0x20 ; c <= 0x7e; c++){ 448 char buf[4]; 449 sprintf(buf, "%c", c); 450 int tmpWidth = (int)fHalfFont.StringWidth(buf); 451 if (tmpWidth > halfWidth) 452 halfWidth = tmpWidth; 453 } 454 455 fFontWidth = halfWidth; 456 457 font_height hh; 458 fHalfFont.GetHeight(&hh); 459 460 int font_ascent = (int)hh.ascent; 461 int font_descent =(int)hh.descent; 462 int font_leading =(int)hh.leading; 463 464 if (font_leading == 0) 465 font_leading = 1; 466 467 if (fTop) 468 fTop = fTop / fFontHeight; 469 470 fFontAscent = font_ascent; 471 fFontHeight = font_ascent + font_descent + font_leading + 1; 472 473 fTop = fTop * fFontHeight; 474 475 fCursorHeight = fFontHeight; 476 477 if (fScrollBar != NULL) { 478 fScrollBar->SetSteps(fFontHeight, fFontHeight * fTermRows); 479 } 480 } 481 482 483 void 484 TermView::SetScrollBar(BScrollBar *scrollBar) 485 { 486 fScrollBar = scrollBar; 487 if (fScrollBar != NULL) { 488 fScrollBar->SetSteps(fFontHeight, fFontHeight * fTermRows); 489 } 490 } 491 492 493 void 494 TermView::SetTitle(const char *title) 495 { 496 // TODO: Do something different in case we're a replicant, 497 // or in case we are inside a BTabView ? 498 if (Window()) 499 Window()->SetTitle(title); 500 } 501 502 503 void 504 TermView::Copy(BClipboard *clipboard) 505 { 506 if (!_HasSelection()) 507 return; 508 509 BString copyStr; 510 fTextBuffer->GetStringFromRegion(copyStr, fSelStart, fSelEnd); 511 512 if (clipboard->Lock()) { 513 BMessage *clipMsg = NULL; 514 clipboard->Clear(); 515 516 if ((clipMsg = clipboard->Data()) != NULL) { 517 clipMsg->AddData("text/plain", B_MIME_TYPE, copyStr.String(), 518 copyStr.Length()); 519 clipboard->Commit(); 520 } 521 clipboard->Unlock(); 522 } 523 524 // Deselecting the current selection is not the behavior that 525 // R5's Terminal app displays. We want to mimic the behavior, so we will 526 // no longer do the deselection 527 // if (!fMouseTracking) 528 // _DeSelect(); 529 } 530 531 532 void 533 TermView::Paste(BClipboard *clipboard) 534 { 535 if (clipboard->Lock()) { 536 BMessage *clipMsg = clipboard->Data(); 537 char *text; 538 ssize_t numBytes; 539 if (clipMsg->FindData("text/plain", B_MIME_TYPE, 540 (const void **)&text, &numBytes) == B_OK ) { 541 // Clipboard text doesn't attached EOF? 542 text[numBytes] = '\0'; 543 _WritePTY((uchar *)text, numBytes); 544 } 545 546 clipboard->Unlock(); 547 } 548 } 549 550 551 void 552 TermView::SelectAll() 553 { 554 int screen_top = fTop / fFontHeight; 555 int viewheight = fTermRows; 556 557 int start_pos = screen_top -(fScrBufSize - viewheight * 2); 558 559 CurPos start, end; 560 start.x = 0; 561 end.x = fTermColumns -1; 562 563 if (start_pos > 0) 564 start.y = start_pos; 565 else 566 start.y = 0; 567 568 end.y = fCurPos.y + screen_top; 569 570 _Select(start, end); 571 } 572 573 574 void 575 TermView::Clear() 576 { 577 _DeSelect(); 578 fTextBuffer->Clear(); 579 580 fTop = 0; 581 ScrollTo(0, 0); 582 583 if (LockLooper()) { 584 SetHighColor(fTextBackColor); 585 FillRect(Bounds()); 586 SetHighColor(fTextForeColor); 587 UnlockLooper(); 588 } 589 590 // reset cursor pos 591 SetCurPos(0, 0); 592 593 if (fScrollBar) { 594 fScrollBar->SetRange(0, 0); 595 fScrollBar->SetProportion(1); 596 } 597 } 598 599 600 //! Print one character 601 void 602 TermView::Insert(uchar *string, ushort attr) 603 { 604 int width = CodeConv::UTF8GetFontWidth((char*)string); 605 if (width == FULL_WIDTH) 606 attr |= A_WIDTH; 607 608 // check column over flow. 609 if (fCurPos.x + width > fTermColumns) { 610 UpdateLine(); 611 fCurPos.x = 0; 612 613 if (fCurPos.y == fTermRows -1) 614 ScrollScreen(); 615 else 616 fCurPos.y++; 617 } 618 619 if (fInsertModeFlag == MODE_INSERT) 620 fTextBuffer->InsertSpace(fCurPos, width); 621 622 fTextBuffer->WriteChar(fCurPos, string, attr); 623 624 if (!fUpdateFlag) 625 fBufferStartPos = fCurPos.x; 626 627 fCurPos.x += width; 628 fUpdateFlag = true; 629 } 630 631 632 //! Print a CR and move the cursor 633 void 634 TermView::InsertCR() 635 { 636 UpdateLine(); 637 fTextBuffer->WriteCR(fCurPos); 638 fCurPos.x = 0; 639 } 640 641 642 //! Print a LF and move the cursor 643 void 644 TermView::InsertLF() 645 { 646 UpdateLine(); 647 648 if (fScrRegionSet) { 649 if (fCurPos.y == fScrBot) { 650 ScrollRegion(-1, -1, SCRUP, 1); 651 return; 652 } 653 } 654 655 if (fCurPos.x != fTermColumns){ 656 if (fCurPos.y == fTermRows -1) 657 ScrollScreen(); 658 else 659 fCurPos.y++; 660 } 661 } 662 663 664 //! Print a NL and move the cursor 665 void 666 TermView::InsertNewLine(int num) 667 { 668 ScrollRegion(fCurPos.y, -1, SCRDOWN, num); 669 } 670 671 672 //! Print a space 673 void 674 TermView::InsertSpace(int num) 675 { 676 UpdateLine(); 677 678 fTextBuffer->InsertSpace(fCurPos, num); 679 _TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y)); 680 } 681 682 683 //! Set or reset Insert mode 684 void 685 TermView::SetInsertMode(int flag) 686 { 687 UpdateLine(); 688 fInsertModeFlag = flag; 689 } 690 691 692 //! Draw region 693 inline int 694 TermView::_TermDraw(const CurPos &start, const CurPos &end) 695 { 696 int x1 = start.x; 697 int y1 = start.y; 698 int x2 = end.x; 699 int y2 = end.y; 700 701 _Redraw(x1, y1 + fTop / fFontHeight, 702 x2, y2 + fTop / fFontHeight); 703 704 return 0; 705 } 706 707 708 //! Draw region 709 int 710 TermView::_TermDrawSelectedRegion(CurPos start, CurPos end) 711 { 712 CurPos inPos; 713 714 if (end < start) { 715 inPos = start; 716 start = end; 717 end = inPos; 718 } 719 720 if (start.y == end.y) { 721 _Redraw(start.x, start.y, end.x, end.y); 722 } else { 723 _Redraw(start.x, start.y, fTermColumns, start.y); 724 725 if (end.y - start.y > 0) 726 _Redraw(0, start.y + 1, fTermColumns, end.y - 1); 727 728 _Redraw(0, end.y, end.x, end.y); 729 } 730 731 return 0; 732 } 733 734 735 //! Draw region 736 int 737 TermView::_TermDrawRegion(CurPos start, CurPos end) 738 { 739 CurPos inPos; 740 int top = fTop / fFontHeight; 741 742 if (end < start) { 743 inPos = start; 744 start = end; 745 end = inPos; 746 } 747 748 start.y += top; 749 end.y += top; 750 751 if (start.y == end.y) { 752 _Redraw(start.x, start.y, end.x, end.y); 753 } else { 754 _Redraw(start.x, start.y, fTermColumns - 1, start.y); 755 756 if (end.y - start.y > 0) { 757 _Redraw(0, start.y + 1, fTermColumns - 1, end.y - 1); 758 } 759 _Redraw(0, end.y, end.x, end.y); 760 } 761 762 return 0; 763 } 764 765 766 //! Erase below cursor below. 767 void 768 TermView::EraseBelow() 769 { 770 UpdateLine(); 771 772 fTextBuffer->EraseBelow(fCurPos); 773 _TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y)); 774 if (fCurPos.y != fTermRows - 1) 775 _TermDraw(CurPos(0, fCurPos.y + 1), CurPos(fTermColumns - 1, fTermRows - 1)); 776 } 777 778 779 //! Delete num characters from current position. 780 void 781 TermView::DeleteChar(int num) 782 { 783 UpdateLine(); 784 785 fTextBuffer->DeleteChar(fCurPos, num); 786 _TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y)); 787 } 788 789 790 //! Delete cursor right characters. 791 void 792 TermView::DeleteColumns() 793 { 794 UpdateLine(); 795 796 fTextBuffer->DeleteChar(fCurPos, fTermColumns - fCurPos.x); 797 _TermDraw(fCurPos, CurPos(fTermColumns - 1, fCurPos.y)); 798 } 799 800 801 //! Delete 'num' lines from current position with scrolling. 802 void 803 TermView::DeleteLine(int num) 804 { 805 ScrollRegion(fCurPos.y, -1, SCRUP, num); 806 } 807 808 809 //! Sets cursor position 810 void 811 TermView::SetCurPos(int x, int y) 812 { 813 UpdateLine(); 814 815 if (x >= 0 && x < fTermColumns) 816 fCurPos.x = x; 817 if (y >= 0 && y < fTermRows) 818 fCurPos.y = y; 819 } 820 821 822 //! Sets cursor x position 823 void 824 TermView::SetCurX(int x) 825 { 826 if (x >= 0 && x < fTermRows) { 827 UpdateLine(); 828 fCurPos.x = x; 829 } 830 } 831 832 833 //! Sets cursor y position 834 void 835 TermView::SetCurY(int y) 836 { 837 if (y >= 0 && y < fTermColumns) { 838 UpdateLine(); 839 fCurPos.y = y; 840 } 841 } 842 843 844 //! Gets cursor position 845 void 846 TermView::GetCurPos(CurPos *inCurPos) 847 { 848 inCurPos->x = fCurPos.x; 849 inCurPos->y = fCurPos.y; 850 } 851 852 853 //! Gets cursor x position 854 int 855 TermView::GetCurX() 856 { 857 return fCurPos.x; 858 } 859 860 861 //! Gets cursor y position 862 int 863 TermView::GetCurY() 864 { 865 return fCurPos.y; 866 } 867 868 869 //! Saves cursor position 870 void 871 TermView::SaveCursor() 872 { 873 fCurStack = fCurPos; 874 } 875 876 877 //! Restores cursor position 878 void 879 TermView::RestoreCursor() 880 { 881 UpdateLine(); 882 fCurPos = fCurStack; 883 } 884 885 886 //! Move cursor right by 'num' steps. 887 void 888 TermView::MoveCurRight(int num) 889 { 890 UpdateLine(); 891 892 if (fCurPos.x + num >= fTermColumns) { 893 // Wrap around 894 fCurPos.x = 0; 895 InsertCR(); 896 InsertLF(); 897 } else 898 fCurPos.x += num; 899 } 900 901 902 //! Move cursor left by 'num' steps. 903 void 904 TermView::MoveCurLeft(int num) 905 { 906 UpdateLine(); 907 908 fCurPos.x -= num; 909 if (fCurPos.x < 0) 910 fCurPos.x = 0; 911 } 912 913 914 //! Move cursor up by 'num' steps. 915 void 916 TermView::MoveCurUp(int num) 917 { 918 UpdateLine(); 919 920 fCurPos.y -= num; 921 922 if (fCurPos.y < 0) 923 fCurPos.y = 0; 924 } 925 926 927 //! Move cursor down by 'num' steps. 928 void 929 TermView::MoveCurDown(int num) 930 { 931 UpdateLine(); 932 933 fCurPos.y += num; 934 935 if (fCurPos.y >= fTermRows) 936 fCurPos.y = fTermRows - 1; 937 } 938 939 940 // TODO: Cleanup the next 3 functions!!! 941 void 942 TermView::DrawCursor() 943 { 944 BRect rect(fFontWidth * fCurPos.x, fFontHeight * fCurPos.y + fTop, 945 fFontWidth * (fCurPos.x + 1) - 1, fFontHeight * fCurPos.y + fTop + fCursorHeight - 1); 946 947 uchar buf[4]; 948 ushort attr; 949 950 int top = fTop / fFontHeight; 951 bool m_flag = _CheckSelectedRegion(CurPos(fCurPos.x, fCurPos.y + fTop / fFontHeight)); 952 if (fTextBuffer->GetChar(fCurPos.y + top, fCurPos.x, buf, &attr) == A_CHAR) { 953 int width; 954 if (IS_WIDTH(attr)) 955 width = 2; 956 else 957 width = 1; 958 959 _DrawLines(fCurPos.x * fFontWidth, 960 fCurPos.y * fFontHeight + fTop, 961 attr, buf, width, m_flag, true, this); 962 } else { 963 if (m_flag) 964 SetHighColor(fSelectBackColor); 965 else 966 SetHighColor(fCursorBackColor); 967 968 FillRect(rect); 969 } 970 971 Sync(); 972 } 973 974 975 void 976 TermView::BlinkCursor() 977 { 978 if (fCursorDrawFlag 979 && fCursorBlinkingFlag 980 && Window()->IsActive()) { 981 if (fCursorStatus) 982 _TermDraw(fCurPos, fCurPos); 983 else 984 DrawCursor(); 985 986 fCursorStatus = !fCursorStatus; 987 fLastCursorTime = system_time(); 988 } 989 } 990 991 992 //! Draw / Clear cursor. 993 void 994 TermView::SetCurDraw(bool flag) 995 { 996 if (!flag) { 997 if (fCursorStatus) 998 _TermDraw(fCurPos, fCurPos); 999 1000 fCursorStatus = false; 1001 fCursorDrawFlag = false; 1002 } else { 1003 if (!fCursorDrawFlag) { 1004 fCursorDrawFlag = true; 1005 fCursorStatus = true; 1006 1007 if (LockLooper()) { 1008 DrawCursor(); 1009 UnlockLooper(); 1010 } 1011 } 1012 } 1013 } 1014 1015 1016 //! Sets cursor Blinking flag. 1017 void 1018 TermView::SetCurBlinking(bool flag) 1019 { 1020 fCursorBlinkingFlag = flag; 1021 } 1022 1023 1024 //! Scroll terminal dir directory by 'num' steps. 1025 void 1026 TermView::ScrollRegion(int top, int bot, int dir, int num) 1027 { 1028 UpdateLine(); 1029 1030 if (top == -1) 1031 top = fScrTop; 1032 1033 if (bot == -1) 1034 bot = fScrBot; 1035 1036 if (top < fScrTop) 1037 top = fScrTop; 1038 1039 if (bot > fScrBot) 1040 bot = fScrBot; 1041 1042 fTextBuffer->ScrollRegion(top, bot , dir ,num); 1043 _TermDraw(CurPos(0, top), CurPos(fTermColumns - 1, bot)); 1044 } 1045 1046 1047 //! Sets terminal scroll region. 1048 void 1049 TermView::SetScrollRegion(int top, int bot) 1050 { 1051 if (top >= 0 && top < fTermRows) { 1052 if (bot >= 0 && bot < fTermRows) { 1053 if (top > bot) { 1054 fScrTop = bot; 1055 fScrBot = top; 1056 } else if (top < bot ) { 1057 fScrTop = top; 1058 fScrBot = bot; 1059 } 1060 } 1061 } 1062 1063 if (fScrTop != 0 || fScrBot != fTermRows -1 ) 1064 fScrRegionSet = 1; 1065 else 1066 fScrRegionSet = 0; 1067 } 1068 1069 1070 //! Scroll to cursor position. 1071 void 1072 TermView::ScrollAtCursor() 1073 { 1074 if (LockLooper()) { 1075 _ResizeScrBarRange(); 1076 fScrollUpCount = 0; 1077 ScrollTo(0, fTop); 1078 UnlockLooper(); 1079 } 1080 } 1081 1082 1083 status_t 1084 TermView::_AttachShell(Shell *shell) 1085 { 1086 if (shell == NULL) 1087 return B_BAD_VALUE; 1088 1089 fShell = shell; 1090 fShell->ViewAttached(this); 1091 1092 return B_OK; 1093 } 1094 1095 1096 void 1097 TermView::_DetachShell() 1098 { 1099 fShell->ViewDetached(); 1100 fShell = NULL; 1101 } 1102 1103 1104 //! Draw character on offscreen bitmap. 1105 void 1106 TermView::_DrawLines(int x1, int y1, ushort attr, uchar *buf, 1107 int width, int mouse, int cursor, BView *inView) 1108 { 1109 rgb_color rgb_fore = fTextForeColor, rgb_back = fTextBackColor; 1110 1111 inView->SetFont(&fHalfFont); 1112 1113 // Set pen point 1114 int x2 = x1 + fFontWidth * width; 1115 int y2 = y1 + fFontHeight; 1116 1117 // color attribute 1118 int forecolor = IS_FORECOLOR(attr); 1119 int backcolor = IS_BACKCOLOR(attr); 1120 1121 if (IS_FORESET(attr)) 1122 rgb_fore = kTermColorTable[forecolor]; 1123 1124 if (IS_BACKSET(attr)) 1125 rgb_back = kTermColorTable[backcolor]; 1126 1127 // Selection check. 1128 if (cursor) { 1129 rgb_fore = fCursorForeColor; 1130 rgb_back = fCursorBackColor; 1131 } else if (mouse) { 1132 rgb_fore = fSelectForeColor; 1133 rgb_back = fSelectBackColor; 1134 } else { 1135 // Reverse attribute(If selected area, don't reverse color). 1136 if (IS_INVERSE(attr)) { 1137 rgb_color rgb_tmp = rgb_fore; 1138 rgb_fore = rgb_back; 1139 rgb_back = rgb_tmp; 1140 } 1141 } 1142 1143 // Fill color at Background color and set low color. 1144 inView->SetHighColor(rgb_back); 1145 inView->FillRect(BRect(x1, y1, x2 - 1, y2 - 1)); 1146 inView->SetLowColor(rgb_back); 1147 1148 inView->SetHighColor(rgb_fore); 1149 1150 // Draw character. 1151 inView->MovePenTo(x1, y1 + fFontAscent); 1152 inView->DrawString((char *) buf); 1153 1154 // bold attribute. 1155 if (IS_BOLD(attr)) { 1156 inView->MovePenTo(x1 + 1, y1 + fFontAscent); 1157 1158 inView->SetDrawingMode(B_OP_OVER); 1159 inView->DrawString((char *)buf); 1160 inView->SetDrawingMode(B_OP_COPY); 1161 } 1162 1163 // underline attribute 1164 if (IS_UNDER(attr)) { 1165 inView->MovePenTo(x1, y1 + fFontAscent); 1166 inView->StrokeLine(BPoint(x1 , y1 + fFontAscent), 1167 BPoint(x2 , y1 + fFontAscent)); 1168 } 1169 } 1170 1171 1172 //! Resize scroll bar range and knob size. 1173 void 1174 TermView::_ResizeScrBarRange() 1175 { 1176 if (fScrollBar == NULL) 1177 return; 1178 1179 float viewheight = fTermRows * fFontHeight; 1180 float start_pos = fTop -(fScrBufSize - fTermRows * 2) * fFontHeight; 1181 1182 if (start_pos > 0) { 1183 fScrollBar->SetRange(start_pos, viewheight + fTop - fFontHeight); 1184 } else { 1185 fScrollBar->SetRange(0, viewheight + fTop - fFontHeight); 1186 fScrollBar->SetProportion( viewheight /(viewheight + fTop)); 1187 } 1188 } 1189 1190 1191 //! Scrolls screen. 1192 void 1193 TermView::ScrollScreen() 1194 { 1195 fTop += fFontHeight; 1196 fScrollUpCount++; 1197 fTextBuffer->ScrollLine(); 1198 1199 if (fScrollUpCount > fTermRows ) { 1200 if (LockLooper()) { 1201 _ResizeScrBarRange(); 1202 fScrollBarRange += fScrollUpCount; 1203 fScrollUpCount = 0; 1204 ScrollTo(0, fTop); 1205 UnlockLooper(); 1206 } 1207 } 1208 } 1209 1210 1211 //! Scrolls screen. 1212 void 1213 TermView::ScrollScreenDraw() 1214 { 1215 if (fScrollUpCount){ 1216 if (LockLooper()) { 1217 _ResizeScrBarRange(); 1218 1219 fScrollBarRange += fScrollUpCount; 1220 fScrollUpCount = 0; 1221 ScrollTo(0, fTop); 1222 UnlockLooper(); 1223 } 1224 } 1225 } 1226 1227 1228 //! Handler for SIGWINCH 1229 void 1230 TermView::_UpdateSIGWINCH() 1231 { 1232 if (fFrameResized) { 1233 if (_HasSelection()) 1234 _TermDrawSelectedRegion(fSelStart, fSelEnd); 1235 ScrollTo(0, fTop); 1236 _ResizeScrBarRange(); 1237 1238 fShell->UpdateWindowSize(fTermRows, fTermColumns); 1239 1240 fFrameResized = false; 1241 if (fScrRegionSet == 0) 1242 fScrBot = fTermRows - 1; 1243 } 1244 } 1245 1246 1247 //! Device Status. 1248 void 1249 TermView::DeviceStatusReport(int n) 1250 { 1251 char sbuf[16] ; 1252 int len; 1253 1254 switch (n) { 1255 case 5: 1256 len = sprintf(sbuf,"\033[0n") ; 1257 fShell->Write(sbuf, len); 1258 break ; 1259 case 6: 1260 len = sprintf(sbuf,"\033[%d;%dR", fTermRows, fTermColumns) ; 1261 fShell->Write(sbuf, len); 1262 break ; 1263 default: 1264 return; 1265 } 1266 } 1267 1268 1269 //! Update line buffer. 1270 void 1271 TermView::UpdateLine() 1272 { 1273 if (fUpdateFlag == true) { 1274 if (fInsertModeFlag == MODE_INSERT) { 1275 _TermDraw(CurPos(fBufferStartPos, fCurPos.y), 1276 CurPos(fTermColumns - 1, fCurPos.y)); 1277 } else { 1278 _TermDraw(CurPos(fBufferStartPos, fCurPos.y), 1279 CurPos(fCurPos.x - 1, fCurPos.y)); 1280 } 1281 fUpdateFlag = false; 1282 } 1283 } 1284 1285 1286 void 1287 TermView::AttachedToWindow() 1288 { 1289 MakeFocus(true); 1290 if (fScrollBar) 1291 fScrollBar->SetSteps(fFontHeight, fFontHeight * fTermRows); 1292 1293 BMessage message(kUpdateSigWinch); 1294 fWinchRunner = new (std::nothrow) BMessageRunner(BMessenger(this), &message, 500000); 1295 1296 // TODO: Since we can also be a replicant, messing 1297 // with the window, which is not ours, is not nice: 1298 // Switch to using a BMessageRunner for the 1299 // blinking caret too. 1300 Window()->SetPulseRate(1000000); 1301 } 1302 1303 1304 void 1305 TermView::DetachedFromWindow() 1306 { 1307 delete fWinchRunner; 1308 fWinchRunner = NULL; 1309 } 1310 1311 1312 void 1313 TermView::Pulse() 1314 { 1315 //if (system_time() > fLastCursorTime + 1000000) 1316 BlinkCursor(); 1317 } 1318 1319 1320 void 1321 TermView::Draw(BRect updateRect) 1322 { 1323 if (IsPrinting()) { 1324 _DoPrint(updateRect); 1325 return; 1326 } 1327 1328 int x1 =(int)updateRect.left / fFontWidth; 1329 int x2 =(int)updateRect.right / fFontWidth; 1330 1331 int y1 =(int)updateRect.top / fFontHeight; 1332 int y2 =(int)updateRect.bottom / fFontHeight; 1333 1334 Window()->BeginViewTransaction(); 1335 1336 for (int j = y1; j <= y2; j++) { 1337 // If(x1, y1) Buffer is in string full width character, 1338 // alignment start position. 1339 1340 int k = x1; 1341 uchar buf[256]; 1342 1343 ushort attr; 1344 if (fTextBuffer->GetChar(j, k, buf, &attr) == IN_STRING) 1345 k--; 1346 1347 if (k < 0) 1348 k = 0; 1349 1350 for (int i = k; i <= x2;) { 1351 int count = fTextBuffer->GetString(j, i, x2, buf, &attr); 1352 bool insideSelection = _CheckSelectedRegion(CurPos(i, j)); 1353 1354 if (count < 0) { 1355 if (insideSelection) { 1356 BRect eraseRect; 1357 eraseRect.Set(fFontWidth * i, 1358 fFontHeight * j, 1359 fFontWidth * (i - count) -1, 1360 fFontHeight * (j + 1) -1); 1361 1362 SetHighColor(fSelectBackColor); 1363 FillRect(eraseRect); 1364 } 1365 i += abs(count); 1366 continue; 1367 } 1368 1369 _DrawLines(fFontWidth * i, fFontHeight * j, 1370 attr, buf, count, insideSelection, false, this); 1371 i += count; 1372 if (i >= fTermColumns) 1373 break; 1374 } 1375 } 1376 1377 if (fCursorStatus) 1378 DrawCursor(); 1379 1380 Window()->EndViewTransaction(); 1381 } 1382 1383 1384 void 1385 TermView::_DoPrint(BRect updateRect) 1386 { 1387 ushort attr; 1388 uchar buf[256]; 1389 1390 const int numLines =(int)((updateRect.Height()) / fFontHeight); 1391 1392 int y1 =(int)updateRect.top / fFontHeight; 1393 y1 = y1 -(fScrBufSize - numLines * 2); 1394 if (y1 < 0) 1395 y1 = 0; 1396 1397 const int y2 = y1 + numLines -1; 1398 1399 const int x1 =(int)updateRect.left / fFontWidth; 1400 const int x2 =(int)updateRect.right / fFontWidth; 1401 1402 for (int j = y1; j <= y2; j++) { 1403 // If(x1, y1) Buffer is in string full width character, 1404 // alignment start position. 1405 1406 int k = x1; 1407 if (fTextBuffer->GetChar(j, k, buf, &attr) == IN_STRING) 1408 k--; 1409 1410 if (k < 0) 1411 k = 0; 1412 1413 for (int i = k; i <= x2;) { 1414 int count = fTextBuffer->GetString(j, i, x2, buf, &attr); 1415 if (count < 0) { 1416 i += abs(count); 1417 continue; 1418 } 1419 1420 _DrawLines(fFontWidth * i, fFontHeight * j, 1421 attr, buf, count, false, false, this); 1422 i += count; 1423 } 1424 } 1425 } 1426 1427 1428 void 1429 TermView::WindowActivated(bool active) 1430 { 1431 BView::WindowActivated(active); 1432 if (active == false) { 1433 // DoIMConfirm(); 1434 } 1435 } 1436 1437 1438 void 1439 TermView::KeyDown(const char *bytes, int32 numBytes) 1440 { 1441 if (fIMflag) 1442 return; 1443 1444 int32 key, mod, rawChar; 1445 BMessage *currentMessage = Looper()->CurrentMessage(); 1446 if (currentMessage == NULL) 1447 return; 1448 1449 currentMessage->FindInt32("modifiers", &mod); 1450 currentMessage->FindInt32("key", &key); 1451 currentMessage->FindInt32("raw_char", &rawChar); 1452 1453 // If bytes[0] equal intr character, 1454 // send signal to shell process group. 1455 struct termios tio; 1456 fShell->GetAttr(tio); 1457 if (*bytes == tio.c_cc[VINTR]) { 1458 if (tio.c_lflag & ISIG) 1459 fShell->Signal(SIGINT); 1460 } 1461 1462 // Terminal filters RET, ENTER, F1...F12, and ARROW key code. 1463 // TODO: Cleanup 1464 if (numBytes == 1) { 1465 switch (*bytes) { 1466 case B_RETURN: 1467 if (rawChar == B_RETURN) { 1468 char c = 0x0d; 1469 fShell->Write(&c, 1); 1470 return; 1471 } 1472 break; 1473 1474 case B_LEFT_ARROW: 1475 if (rawChar == B_LEFT_ARROW) { 1476 fShell->Write(LEFT_ARROW_KEY_CODE, sizeof(LEFT_ARROW_KEY_CODE) - 1); 1477 return; 1478 } 1479 break; 1480 1481 case B_RIGHT_ARROW: 1482 if (rawChar == B_RIGHT_ARROW) { 1483 fShell->Write(RIGHT_ARROW_KEY_CODE, sizeof(RIGHT_ARROW_KEY_CODE) - 1); 1484 return; 1485 } 1486 break; 1487 1488 case B_UP_ARROW: 1489 if (mod & B_SHIFT_KEY) { 1490 if (Bounds().top > 0) { 1491 ScrollBy(0, -fFontHeight); 1492 Window()->UpdateIfNeeded(); 1493 } 1494 return; 1495 } 1496 if (rawChar == B_UP_ARROW) { 1497 fShell->Write(UP_ARROW_KEY_CODE, sizeof(UP_ARROW_KEY_CODE) - 1); 1498 return; 1499 } 1500 break; 1501 1502 case B_DOWN_ARROW: 1503 if (mod & B_SHIFT_KEY) { 1504 ScrollBy(0, fFontHeight); 1505 Window()->UpdateIfNeeded(); 1506 return; 1507 } 1508 1509 if (rawChar == B_DOWN_ARROW) { 1510 fShell->Write(DOWN_ARROW_KEY_CODE, sizeof(DOWN_ARROW_KEY_CODE) - 1); 1511 return; 1512 } 1513 break; 1514 1515 case B_INSERT: 1516 if (rawChar == B_INSERT) { 1517 fShell->Write(INSERT_KEY_CODE, sizeof(INSERT_KEY_CODE) - 1); 1518 return; 1519 } 1520 break; 1521 1522 case B_HOME: 1523 if (rawChar == B_HOME) { 1524 fShell->Write(HOME_KEY_CODE, sizeof(HOME_KEY_CODE) - 1); 1525 return; 1526 } 1527 break; 1528 1529 case B_END: 1530 if (rawChar == B_END) { 1531 fShell->Write(END_KEY_CODE, sizeof(END_KEY_CODE) - 1); 1532 return; 1533 } 1534 break; 1535 1536 case B_PAGE_UP: 1537 if (mod & B_SHIFT_KEY) { 1538 if (Bounds().top > 0) { 1539 ScrollBy(0, -fFontHeight * fTermRows ); 1540 Window()->UpdateIfNeeded(); 1541 } 1542 return; 1543 } 1544 if (rawChar == B_PAGE_UP) { 1545 fShell->Write(PAGE_UP_KEY_CODE, sizeof(PAGE_UP_KEY_CODE) - 1); 1546 return; 1547 } 1548 break; 1549 1550 case B_PAGE_DOWN: 1551 if (mod & B_SHIFT_KEY) { 1552 ScrollBy(0, fFontHeight * fTermRows); 1553 Window()->UpdateIfNeeded(); 1554 return; 1555 } 1556 1557 if (rawChar == B_PAGE_DOWN) { 1558 fShell->Write(PAGE_DOWN_KEY_CODE, sizeof(PAGE_DOWN_KEY_CODE) - 1); 1559 return; 1560 } 1561 break; 1562 1563 case B_FUNCTION_KEY: 1564 // TODO: Why not just fShell->Write(key) ? 1565 for (int32 i = 0; i < 12; i++) { 1566 if (key == function_keycode_table[i]) { 1567 fShell->Write(function_key_char_table[i], 5); 1568 return; 1569 } 1570 } 1571 break; 1572 default: 1573 break; 1574 } 1575 } else { 1576 // input multibyte character 1577 if (fEncoding != M_UTF8) { 1578 char destBuffer[16]; 1579 int cnum = CodeConv::ConvertFromInternal(bytes, numBytes, 1580 (char *)destBuffer, fEncoding); 1581 fShell->Write(destBuffer, cnum); 1582 return; 1583 } 1584 } 1585 1586 fShell->Write(bytes, numBytes); 1587 } 1588 1589 1590 void 1591 TermView::FrameResized(float width, float height) 1592 { 1593 const int cols = ((int)width + 1) / fFontWidth; 1594 const int rows = ((int)height + 1) / fFontHeight; 1595 1596 int offset = 0; 1597 1598 if (rows < fCurPos.y + 1) { 1599 fTop += (fCurPos.y + 1 - rows) * fFontHeight; 1600 offset = fCurPos.y + 1 - rows; 1601 fCurPos.y = rows - 1; 1602 } 1603 fTextBuffer->ResizeTo(rows, cols, offset); 1604 fTermRows = rows; 1605 fTermColumns = cols; 1606 1607 fFrameResized = true; 1608 1609 if (fScrollBar != NULL) 1610 fScrollBar->SetSteps(fFontHeight, fFontHeight * fTermRows); 1611 } 1612 1613 1614 void 1615 TermView::MessageReceived(BMessage *msg) 1616 { 1617 entry_ref ref; 1618 char *ctrl_l = ""; 1619 1620 switch (msg->what){ 1621 case B_ABOUT_REQUESTED: 1622 // (replicant) about box requested 1623 _AboutRequested(); 1624 break; 1625 1626 case B_SIMPLE_DATA: 1627 { 1628 int32 i = 0; 1629 if (msg->FindRef("refs", i++, &ref) == B_OK) { 1630 _DoFileDrop(ref); 1631 1632 while (msg->FindRef("refs", i++, &ref) == B_OK) { 1633 _WritePTY((const uchar*)" ", 1); 1634 _DoFileDrop(ref); 1635 } 1636 } else 1637 BView::MessageReceived(msg); 1638 break; 1639 } 1640 1641 case B_MIME_DATA: 1642 { 1643 char *text; 1644 int32 numBytes; 1645 status_t sts; 1646 1647 if (msg->WasDropped()) { 1648 sts = msg->FindData("text/plain", 1649 B_MIME_TYPE, (const void **)&text, &numBytes); 1650 if (sts != B_OK) 1651 break; 1652 1653 _WritePTY((uchar *)text, numBytes); 1654 } 1655 break; 1656 } 1657 1658 case B_COPY: 1659 Copy(be_clipboard); 1660 break; 1661 1662 case B_PASTE: 1663 { 1664 int32 code; 1665 if (msg->FindInt32("index", &code) == B_OK) 1666 Paste(be_clipboard); 1667 break; 1668 } 1669 1670 case B_SELECT_ALL: 1671 SelectAll(); 1672 break; 1673 1674 case B_SET_PROPERTY: 1675 { 1676 int32 i; 1677 int32 encodingID; 1678 BMessage specifier; 1679 msg->GetCurrentSpecifier(&i, &specifier); 1680 if (!strcmp("encoding", specifier.FindString("property", i))){ 1681 msg->FindInt32 ("data", &encodingID); 1682 SetEncoding(encodingID); 1683 msg->SendReply(B_REPLY); 1684 } else { 1685 BView::MessageReceived(msg); 1686 } 1687 break; 1688 } 1689 1690 case B_GET_PROPERTY: 1691 { 1692 int32 i; 1693 BMessage specifier; 1694 msg->GetCurrentSpecifier(&i, &specifier); 1695 if (!strcmp("encoding", specifier.FindString("property", i))){ 1696 BMessage reply(B_REPLY); 1697 reply.AddInt32("result", Encoding()); 1698 msg->SendReply(&reply); 1699 } else if (!strcmp("tty", specifier.FindString("property", i))) { 1700 BMessage reply(B_REPLY); 1701 reply.AddString("result", TerminalName()); 1702 msg->SendReply(&reply); 1703 } else { 1704 BView::MessageReceived(msg); 1705 } 1706 break; 1707 } 1708 1709 case MENU_CLEAR_ALL: 1710 Clear(); 1711 fShell->Write(ctrl_l, 1); 1712 break; 1713 1714 1715 // case B_INPUT_METHOD_EVENT: 1716 // { 1717 // int32 op; 1718 // msg->FindInt32("be:opcode", &op); 1719 // switch (op){ 1720 // case B_INPUT_METHOD_STARTED: 1721 //DoIMStart(msg); 1722 // break; 1723 1724 // case B_INPUT_METHOD_STOPPED: 1725 // DoIMStop(msg); 1726 // break; 1727 1728 // case B_INPUT_METHOD_CHANGED: 1729 // DoIMChange(msg); 1730 // break; 1731 1732 // case B_INPUT_METHOD_LOCATION_REQUEST: 1733 // DoIMLocation(msg); 1734 // break; 1735 // } 1736 // } 1737 case kUpdateSigWinch: 1738 _UpdateSIGWINCH(); 1739 break; 1740 default: 1741 BView::MessageReceived(msg); 1742 break; 1743 } 1744 } 1745 1746 1747 status_t 1748 TermView::GetSupportedSuites(BMessage *message) 1749 { 1750 BPropertyInfo propInfo(sPropList); 1751 message->AddString("suites", "suite/vnd.naan-termview"); 1752 message->AddFlat("messages", &propInfo); 1753 return BView::GetSupportedSuites(message); 1754 } 1755 1756 1757 BHandler* 1758 TermView::ResolveSpecifier(BMessage *message, int32 index, BMessage *specifier, 1759 int32 what, const char *property) 1760 { 1761 BHandler *target = this; 1762 BPropertyInfo propInfo(sPropList); 1763 if (propInfo.FindMatch(message, index, specifier, what, property) < B_OK) 1764 target = BView::ResolveSpecifier(message, index, specifier, what, property); 1765 1766 return target; 1767 } 1768 1769 1770 //! Gets dropped file full path and display it at cursor position. 1771 void 1772 TermView::_DoFileDrop(entry_ref &ref) 1773 { 1774 BEntry ent(&ref); 1775 BPath path(&ent); 1776 BString string(path.Path()); 1777 1778 string.CharacterEscape(" ~`#$&*()\\|[]{};'\"<>?!",'\\'); 1779 _WritePTY((const uchar *)string.String(), string.Length()); 1780 } 1781 1782 1783 /*! Write strings to PTY device. If encoding system isn't UTF8, change 1784 encoding to UTF8 before writing PTY. 1785 */ 1786 void 1787 TermView::_WritePTY(const uchar *text, int numBytes) 1788 { 1789 if (fEncoding != M_UTF8) { 1790 uchar *destBuffer = (uchar *)malloc(numBytes * 3); 1791 numBytes = CodeConv::ConvertFromInternal((char*)text, numBytes, 1792 (char*)destBuffer, fEncoding); 1793 fShell->Write(destBuffer, numBytes); 1794 free(destBuffer); 1795 } else { 1796 fShell->Write(text, numBytes); 1797 } 1798 } 1799 1800 1801 void 1802 TermView::MouseDown(BPoint where) 1803 { 1804 if (!IsFocus()) 1805 MakeFocus(); 1806 1807 int32 buttons; 1808 Window()->CurrentMessage()->FindInt32("buttons", &buttons); 1809 1810 // paste button 1811 if ((buttons & (B_SECONDARY_MOUSE_BUTTON | B_TERTIARY_MOUSE_BUTTON)) != 0) { 1812 if (_HasSelection()) { 1813 // copy text from region 1814 BString copy; 1815 fTextBuffer->GetStringFromRegion(copy, fSelStart, fSelEnd); 1816 _WritePTY((uchar *)copy.String(), copy.Length()); 1817 } else { 1818 // copy text from clipboard. 1819 Paste(be_clipboard); 1820 } 1821 return; 1822 } 1823 1824 // Select Region 1825 if (buttons == B_PRIMARY_MOUSE_BUTTON) { 1826 int32 mod, clicks; 1827 Window()->CurrentMessage()->FindInt32("modifiers", &mod); 1828 Window()->CurrentMessage()->FindInt32("clicks", &clicks); 1829 1830 if (_HasSelection()) { 1831 CurPos inPos = _ConvertToTerminal(where); 1832 if (_CheckSelectedRegion(inPos)) { 1833 if (mod & B_CONTROL_KEY) { 1834 BPoint p; 1835 uint32 bt; 1836 do { 1837 GetMouse(&p, &bt); 1838 1839 if (bt == 0) { 1840 _DeSelect(); 1841 return; 1842 } 1843 1844 snooze(40000); 1845 1846 } while (abs((int)(where.x - p.x)) < 4 1847 && abs((int)(where.y - p.y)) < 4); 1848 1849 InitiateDrag(); 1850 return; 1851 } 1852 } 1853 } 1854 1855 // If mouse has a lot of movement, disable double/triple click. 1856 /*BPoint inPoint = fClickPoint - where; 1857 if (abs((int)inPoint.x) > 16 || abs((int)inPoint.y) > 16) 1858 clicks = 1; 1859 */ 1860 1861 SetMouseEventMask(B_POINTER_EVENTS | B_KEYBOARD_EVENTS, 1862 B_NO_POINTER_HISTORY | B_LOCK_WINDOW_FOCUS); 1863 1864 fClickPoint = where; 1865 1866 if (mod & B_SHIFT_KEY) 1867 _AddSelectRegion(_ConvertToTerminal(where)); 1868 else 1869 _DeSelect(); 1870 1871 1872 // If clicks larger than 3, reset mouse click counter. 1873 clicks = clicks % 3; 1874 if (clicks == 0) 1875 clicks = 3; 1876 1877 switch (clicks) { 1878 case 1: 1879 fMouseTracking = true; 1880 break; 1881 1882 case 2: 1883 _SelectWord(where, mod); 1884 break; 1885 1886 case 3: 1887 _SelectLine(where, mod); 1888 break; 1889 } 1890 return; 1891 } 1892 1893 BView::MouseDown(where); 1894 } 1895 1896 1897 void 1898 TermView::MouseMoved(BPoint where, uint32 transit, const BMessage *message) 1899 { 1900 BView::MouseMoved(where, transit, message); 1901 if (!fMouseTracking) 1902 return; 1903 1904 CurPos startPos = _ConvertToTerminal(fClickPoint); 1905 CurPos endPos = _ConvertToTerminal(where); 1906 if (endPos.y < 0) 1907 return; 1908 1909 _DeSelect(); 1910 _Select(startPos, endPos); 1911 1912 // Scroll check 1913 if (fScrollBar != NULL) { 1914 // Get now scroll point 1915 float scrollStart, scrollEnd; 1916 fScrollBar->GetRange(&scrollStart, &scrollEnd); 1917 float scrollPos = fScrollBar->Value(); 1918 1919 if (where.y < Bounds().LeftTop().y ) { 1920 // mouse point left of window 1921 if (scrollPos != scrollStart) 1922 ScrollTo(0, where.y); 1923 } 1924 1925 if (where.y > Bounds().LeftBottom().y) { 1926 // mouse point left of window 1927 if (scrollPos != scrollEnd) 1928 ScrollTo(0, where.y); 1929 } 1930 } 1931 } 1932 1933 1934 void 1935 TermView::MouseUp(BPoint where) 1936 { 1937 BView::MouseUp(where); 1938 fMouseTracking = false; 1939 } 1940 1941 1942 // Select a range of text 1943 void 1944 TermView::_Select(CurPos start, CurPos end) 1945 { 1946 if (end < start) 1947 std::swap(start, end); 1948 1949 if (start.x < 0) 1950 start.x = 0; 1951 if (end.x >= fTermColumns) 1952 end.x = fTermColumns - 1; 1953 1954 uchar buf[4]; 1955 ushort attr; 1956 if (fTextBuffer->GetChar(start.y, start.x, buf, &attr) == IN_STRING) { 1957 start.x--; 1958 if (start.x < 0) 1959 start.x = 0; 1960 } 1961 1962 if (fTextBuffer->GetChar(end.y, end.x, buf, &attr) == IN_STRING) { 1963 end.x++; 1964 if (end.x >= fTermColumns) 1965 end.x = fTermColumns; 1966 } 1967 1968 fSelStart = start; 1969 fSelEnd = end; 1970 1971 fTextBuffer->Select(fSelStart, fSelEnd); 1972 _TermDrawSelectedRegion(fSelStart, fSelEnd); 1973 } 1974 1975 1976 // Add select region(shift + mouse click) 1977 void 1978 TermView::_AddSelectRegion(CurPos pos) 1979 { 1980 if (!_HasSelection()) 1981 return; 1982 1983 // error check, and if mouse point to a plase full width character, 1984 // select point decliment. 1985 if (pos.x >= fTermColumns) 1986 pos.x = fTermColumns - 1; 1987 else if (pos.x < 0) 1988 pos.x = 0; 1989 1990 if (pos.y < 0) 1991 pos.y = 0; 1992 1993 uchar buf[4]; 1994 ushort attr; 1995 if (fTextBuffer->GetChar(pos.y, pos.x, buf, &attr) == IN_STRING) { 1996 pos.x++; 1997 if (pos.x >= fTermColumns) 1998 pos.x = fTermColumns - 1; 1999 } 2000 2001 CurPos start = fSelStart; 2002 CurPos end = fSelEnd; 2003 CurPos inPos; 2004 2005 // Mouse point is same as selected line. 2006 if (pos.y == fSelStart.y && pos.y == fSelEnd.y) { 2007 2008 if (abs(pos.x - start.x) > abs(pos.x - end.x)) { 2009 2010 fSelStart = start; 2011 fSelEnd = pos; 2012 inPos = end; 2013 2014 } else { 2015 2016 fSelStart = end; 2017 fSelEnd = pos; 2018 inPos = start; 2019 } 2020 // else, End point set to near the start or end point. 2021 } else if (abs(pos.y - start.y) > abs(pos.y - end.y)) { 2022 2023 fSelStart = start; 2024 fSelEnd = pos; 2025 inPos = end; 2026 } else if (abs(pos.y - start.y) > abs(pos.y - end.y)) { 2027 fSelStart = end; 2028 fSelEnd = pos; 2029 inPos = start; 2030 2031 } else { 2032 if (start > end) { 2033 inPos = start; 2034 start = end; 2035 end = inPos; 2036 } 2037 2038 if (pos.y < start.y) { 2039 fSelStart = end; 2040 fSelEnd = pos; 2041 inPos = start; 2042 } else { 2043 fSelStart = start; 2044 fSelEnd = pos; 2045 inPos = end; 2046 } 2047 } 2048 2049 fTextBuffer->Select(fSelStart, fSelEnd); 2050 _TermDrawSelectedRegion(inPos, fSelEnd); 2051 } 2052 2053 2054 // Resize select region (mouse drag) 2055 void 2056 TermView::_ResizeSelectRegion(CurPos pos) 2057 { 2058 //TODO: Broken. Selecting from right to left doesn't work. 2059 2060 CurPos inPos = fSelEnd; 2061 2062 // error check, and if mouse point to a plase full width character, 2063 // select point decliment. 2064 if (pos.x >= fTermColumns) 2065 pos.x = fTermColumns - 1; 2066 else if (pos.x < 0) 2067 pos.x = 0; 2068 2069 if (pos.y < 0) 2070 pos.y = 0; 2071 2072 uchar buf[4]; 2073 ushort attr; 2074 if (fTextBuffer->GetChar(pos.y, pos.x, buf, &attr) == IN_STRING) { 2075 pos.x++; 2076 2077 if (pos == inPos) 2078 return; 2079 2080 if (pos.x >= fTermColumns) 2081 pos.x = fTermColumns - 1; 2082 } 2083 2084 fSelEnd = pos; 2085 2086 fTextBuffer->Select(fSelStart, fSelEnd); 2087 _TermDrawSelectedRegion(inPos, pos); 2088 } 2089 2090 2091 // DeSelect a range of text 2092 void 2093 TermView::_DeSelect(void) 2094 { 2095 if (!_HasSelection()) 2096 return; 2097 2098 fTextBuffer->DeSelect(); 2099 2100 CurPos start = fSelStart; 2101 CurPos end = fSelEnd; 2102 2103 fSelStart.Set(-1, -1); 2104 fSelEnd.Set(-1, -1); 2105 2106 _TermDrawSelectedRegion(start, end); 2107 } 2108 2109 2110 bool 2111 TermView::_HasSelection() const 2112 { 2113 return fSelStart != fSelEnd; 2114 } 2115 2116 2117 void 2118 TermView::_SelectWord(BPoint where, int mod) 2119 { 2120 CurPos start, end, pos; 2121 bool flag; 2122 2123 pos = _ConvertToTerminal(where); 2124 flag = fTextBuffer->FindWord(pos, &start, &end); 2125 fTextBuffer->Select(start, end); 2126 2127 if (mod & B_SHIFT_KEY) { 2128 if (flag) { 2129 if (start < fSelStart) 2130 _AddSelectRegion(start); 2131 else if (end > fSelEnd) 2132 _AddSelectRegion(end); 2133 } else 2134 _AddSelectRegion(pos); 2135 } else { 2136 _DeSelect(); 2137 if (flag) 2138 _Select(start, end); 2139 } 2140 } 2141 2142 2143 void 2144 TermView::_SelectLine(BPoint where, int mod) 2145 { 2146 CurPos pos = _ConvertToTerminal(where); 2147 2148 if (mod & B_SHIFT_KEY) { 2149 2150 CurPos start = CurPos(0, pos.y); 2151 CurPos end = CurPos(fTermColumns - 1, pos.y); 2152 2153 if (start < fSelStart) 2154 _AddSelectRegion(start); 2155 else if (end > fSelEnd) 2156 _AddSelectRegion(end); 2157 2158 } else { 2159 _DeSelect(); 2160 _Select(CurPos(0, pos.y), CurPos(fTermColumns - 1, pos.y)); 2161 } 2162 } 2163 2164 2165 // Convert View visible area corrdination to cursor position. 2166 CurPos 2167 TermView::_ConvertToTerminal(const BPoint &p) 2168 { 2169 return CurPos(p.x / fFontWidth, p.y / fFontHeight); 2170 } 2171 2172 2173 // Convert cursor position to view coordination. 2174 BPoint 2175 TermView::_ConvertFromTerminal(const CurPos &pos) 2176 { 2177 return BPoint(fFontWidth * pos.x, pos.y * fFontHeight + fTop); 2178 } 2179 2180 2181 bool 2182 TermView::_CheckSelectedRegion(const CurPos &pos) 2183 { 2184 CurPos start, end; 2185 2186 if (fSelStart > fSelEnd) { 2187 start = fSelEnd; 2188 end = fSelStart; 2189 } else { 2190 start = fSelStart; 2191 end = fSelEnd; 2192 } 2193 2194 if (pos >= start && pos <= end) 2195 return true; 2196 2197 return false; 2198 2199 } 2200 2201 void 2202 TermView::GetFrameSize(float *width, float *height) 2203 { 2204 if (width != NULL) 2205 *width = fTermColumns * fFontWidth; 2206 2207 if (height == NULL) 2208 return; 2209 2210 if (!fTop) { 2211 *height = fTermRows * fFontHeight; 2212 return; 2213 } 2214 2215 if (fTop - fTermRows * fFontHeight > fScrBufSize * fFontHeight) { 2216 2217 *height = fScrBufSize * fFontHeight; 2218 return; 2219 } 2220 2221 *height = fTop + fTermRows * fFontHeight; 2222 } 2223 2224 2225 // Find a string, and select it if found 2226 bool 2227 TermView::Find(const BString &str, bool forwardSearch, bool matchCase, bool matchWord) 2228 { 2229 //Get the buffer contents 2230 BString buffer; 2231 fTextBuffer->ToString(buffer); 2232 2233 CurPos selectionstart = fSelStart; 2234 CurPos selectionend = fSelEnd; 2235 2236 int offset = 0; 2237 if (selectionstart.x >= 0 || selectionstart.y >= 0) { 2238 if (forwardSearch) 2239 //Set the offset to the end of the selection 2240 offset = (selectionend.y) * fTermColumns + selectionend.x; 2241 else 2242 offset = (selectionstart.y) * fTermColumns + selectionstart.x; 2243 } 2244 2245 int initialresult = -1; 2246 int result = B_ERROR; 2247 2248 for (;;) { 2249 //Actual search 2250 if (forwardSearch) { 2251 if (matchCase) 2252 result = buffer.FindFirst(str, offset); 2253 else 2254 result = buffer.IFindFirst(str, offset); 2255 } else { 2256 if (matchCase) 2257 result = buffer.FindLast(str, offset); 2258 else 2259 result = buffer.IFindLast(str, offset); 2260 } 2261 2262 if (result == B_ERROR) { //Wrap search like Be's Terminal 2263 if (forwardSearch) { 2264 if (matchCase) 2265 result = buffer.FindFirst(str, 0); 2266 else 2267 result = buffer.IFindFirst(str, 0); 2268 } else { 2269 if (matchCase) 2270 result = buffer.FindLast(str, buffer.Length()); 2271 else 2272 result = buffer.IFindLast(str, buffer.Length()); 2273 } 2274 } 2275 2276 if (result < 0) 2277 return false; 2278 2279 if (matchWord) { 2280 if (isalnum(buffer.ByteAt(result - 1)) || isalnum(buffer.ByteAt(result + str.Length()))) { 2281 if (initialresult == -1) //Set the initial offset to the first result to aid word matching 2282 initialresult = result; 2283 else if (initialresult == result) //We went round the buffer, nothing found 2284 return false; 2285 if (forwardSearch) 2286 offset = result + str.Length(); 2287 else 2288 offset = result; 2289 continue; 2290 } 2291 else 2292 break; 2293 } 2294 else 2295 break; 2296 } 2297 2298 //Select the found text 2299 selectionstart.y = result / fTermColumns; 2300 selectionstart.x = result % fTermColumns; 2301 //Length -1 since it seems to count the \0 as well 2302 selectionend.y = (result + str.Length() - 1) / fTermColumns; 2303 selectionend.x = (result + str.Length() - 1) % fTermColumns; 2304 //Update the contents of the view 2305 _DeSelect(); 2306 _Select(selectionstart, selectionend); 2307 2308 return true; 2309 } 2310 2311 2312 //! Get the selected text and copy to str 2313 void 2314 TermView::GetSelection(BString &str) 2315 { 2316 str.SetTo(""); 2317 fTextBuffer->GetStringFromRegion(str, fSelStart, fSelEnd); 2318 } 2319 2320 2321 void 2322 TermView::NotifyQuit(int32 reason) 2323 { 2324 // implemented in subclasses 2325 } 2326 2327 2328 void 2329 TermView::CheckShellGone() 2330 { 2331 if (!fShell) 2332 return; 2333 2334 // check, if the shell does still live 2335 pid_t pid = fShell->ProcessID(); 2336 team_info info; 2337 if (get_team_info(pid, &info) == B_BAD_TEAM_ID) { 2338 // the shell is gone 2339 NotifyQuit(0); 2340 } 2341 } 2342 2343 2344 void 2345 TermView::InitiateDrag() 2346 { 2347 BString copyStr(""); 2348 fTextBuffer->GetStringFromRegion(copyStr, fSelStart, fSelEnd); 2349 2350 BMessage message(B_MIME_DATA); 2351 message.AddData("text/plain", B_MIME_TYPE, copyStr.String(), copyStr.Length()); 2352 2353 BPoint start = _ConvertFromTerminal(fSelStart); 2354 BPoint end = _ConvertFromTerminal(fSelEnd); 2355 2356 BRect rect; 2357 if (fSelStart.y == fSelEnd.y) { 2358 rect.Set(start.x, start.y - fTop, end.x + fFontWidth, 2359 end.y + fFontHeight - fTop); 2360 2361 } else { 2362 2363 rect.Set(0, start.y - fTop, fTermColumns * fFontWidth, 2364 end.y + fFontHeight - fTop); 2365 } 2366 2367 rect = rect & Bounds(); 2368 2369 DragMessage(&message, rect); 2370 } 2371 2372 2373 inline void 2374 TermView::_Redraw(int x1, int y1, int x2, int y2) 2375 { 2376 BRect rect(x1 * fFontWidth, y1 * fFontHeight, 2377 (x2 + 1) * fFontWidth -1, (y2 + 1) * fFontHeight -1); 2378 2379 if (LockLooper()) { 2380 Invalidate(rect); 2381 UnlockLooper(); 2382 } 2383 } 2384 2385 2386 /* static */ 2387 void 2388 TermView::_FixFontAttributes(BFont &font) 2389 { 2390 font.SetSpacing(B_FIXED_SPACING); 2391 } 2392 2393 2394 void 2395 TermView::_AboutRequested() 2396 { 2397 BAlert *alert = new (std::nothrow) BAlert("about", 2398 "Terminal\n" 2399 "\twritten by Kazuho Okui and Takashi Murai\n" 2400 "\tupdated by Kian Duffy and others\n\n" 2401 "\tCopyright " B_UTF8_COPYRIGHT "2003-2007, Haiku.\n", "Ok"); 2402 if (alert != NULL) 2403 alert->Go(); 2404 } 2405 2406 2407