Changeset 6417 for code/trunk/src/libraries/core/IOConsole.cc
- Timestamp:
- Dec 25, 2009, 10:23:58 PM (15 years ago)
- Location:
- code/trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
code/trunk
- Property svn:mergeinfo changed
-
code/trunk/src/libraries/core/IOConsole.cc
r6105 r6417 32 32 #include <iomanip> 33 33 #include <iostream> 34 35 #include "util/Clock.h" 36 #include "util/Math.h" 34 37 #include "core/Game.h" 35 38 #include "core/input/InputBuffer.h" … … 42 45 IOConsole* IOConsole::singletonPtr_s = NULL; 43 46 44 int IOConsole::extractLogLevel(std::string* text)45 {46 // Handle line colouring by inspecting the first letter47 char level = 0;48 if (!text->empty())49 {50 level = (*text)[0];51 if (level == -1 || level >= 1 && level <= 6)52 {53 *text = text->substr(1);54 if (level != -1)55 return level;56 }57 }58 return 0;59 }60 61 inline bool IOConsole::willPrintStatusLines()62 {63 return !this->statusLineWidths_.empty()64 && this->terminalWidth_ >= this->statusLineMaxWidth_65 && this->terminalHeight_ >= (this->minOutputLines_ + this->statusLineWidths_.size());66 }67 68 47 // ############################### 69 48 // ### ShellListener methods ### … … 78 57 } 79 58 80 //! Called if the text in the input-line has changed81 void IOConsole::inputChanged()82 {83 this->printInputLine();84 this->cout_.flush();85 }86 87 //! Called if the position of the cursor in the input-line has changed88 void IOConsole::cursorChanged()89 {90 this->printInputLine();91 this->cout_.flush();92 }93 94 59 //! Called if a command is about to be executed 95 60 void IOConsole::executed() 96 61 { 97 this->shell_->addOutput Line(this->promptString_ + this->shell_->getInput());62 this->shell_->addOutput(this->promptString_ + this->shell_->getInput() + '\n', Shell::Command); 98 63 } 99 64 … … 126 91 127 92 IOConsole::IOConsole() 128 : shell_(new Shell("IOConsole", false , true))93 : shell_(new Shell("IOConsole", false)) 129 94 , buffer_(shell_->getInputBuffer()) 130 95 , cout_(std::cout.rdbuf()) 96 , promptString_("orxonox # ") 131 97 , bStatusPrinted_(false) 132 , promptString_("orxonox # ") 133 , originalTerminalSettings_(new termios()) 98 , originalTerminalSettings_(0) 134 99 { 135 100 this->setTerminalMode(); … … 151 116 152 117 // Make sure we make way for the status lines 153 this-> update(Game::getInstance().getGameClock());118 this->preUpdate(Game::getInstance().getGameClock()); 154 119 } 155 120 156 121 IOConsole::~IOConsole() 157 122 { 158 // Empty all buffers 159 this->update(Game::getInstance().getGameClock()); 123 // Process output written to std::cout in the meantime 124 std::cout.flush(); 125 if (!this->origCout_.str().empty()) 126 this->shell_->addOutput(this->origCout_.str(), Shell::None); 160 127 // Erase input and status lines 161 128 this->cout_ << "\033[1G\033[J"; … … 166 133 167 134 resetTerminalMode(); 168 delete this->originalTerminalSettings_;169 135 this->shell_->destroy(); 170 136 … … 175 141 } 176 142 177 void IOConsole:: update(const Clock& time)143 void IOConsole::preUpdate(const Clock& time) 178 144 { 179 145 unsigned char c; … … 284 250 } 285 251 286 // We always assume that the cursor is on the input line.252 // We always assume that the cursor is on the input line. 287 253 // But we cannot always be sure about that, esp. if we scroll the console 288 254 this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B'; … … 296 262 297 263 // Process output written to std::cout 264 std::cout.flush(); 298 265 if (!this->origCout_.str().empty()) 299 266 { 300 this->shell_->addOutput Line(this->origCout_.str());267 this->shell_->addOutput(this->origCout_.str(), Shell::None); 301 268 this->origCout_.str(""); 302 269 } 303 270 } 304 271 305 void IOConsole::printLogText(const std::string& text) 306 { 307 std::string output = text; 308 /*int level =*/ this->extractLogLevel(&output); 309 272 void IOConsole::printOutputLine(const std::string& text, Shell::LineType type) 273 { 310 274 /* 311 275 // Colour line 312 switch ( level)313 { 314 case -1:this->cout_ << "\033[37m"; break;315 case 1:this->cout_ << "\033[91m"; break;316 case 2: this->cout_ << "\033[31m"; break;317 case 3:this->cout_ << "\033[34m"; break;318 case 4:this->cout_ << "\033[36m"; break;319 case 5: this->cout_ << "\033[35m"; break;320 case 6:this->cout_ << "\033[37m"; break;276 switch (type) 277 { 278 case Shell::None: this->cout_ << "\033[37m"; break; 279 case Shell::Error: this->cout_ << "\033[91m"; break; 280 case Shell::Warning: this->cout_ << "\033[31m"; break; 281 case Shell::Info: this->cout_ << "\033[34m"; break; 282 case Shell::Debug: this->cout_ << "\033[36m"; break; 283 case Shell::Verbose: this->cout_ << "\033[35m"; break; 284 case Shell::Ultra: this->cout_ << "\033[37m"; break; 321 285 default: break; 322 286 } … … 324 288 325 289 // Print output line 326 this->cout_ << output;290 this->cout_ << text; 327 291 328 292 // Reset colour to white … … 343 307 this->cout_ << "\033[u"; 344 308 if (this->buffer_->getCursorPosition() > 0) 345 this->cout_ << "\033[" << this->buffer_->getCursorPosition() << "C";309 this->cout_ << "\033[" << this->buffer_->getCursorPosition() << 'C'; 346 310 } 347 311 … … 367 331 { 368 332 termios new_settings; 333 this->originalTerminalSettings_ = new termios(); 369 334 370 335 tcgetattr(0, this->originalTerminalSettings_); … … 375 340 new_settings.c_cc[VMIN] = 0; 376 341 tcsetattr(0, TCSANOW, &new_settings); 377 } 378 379 void IOConsole::resetTerminalMode() 380 { 381 tcsetattr(0, TCSANOW, IOConsole::originalTerminalSettings_); 342 atexit(&IOConsole::resetTerminalMode); 343 } 344 345 /*static*/ void IOConsole::resetTerminalMode() 346 { 347 if(IOConsole::singletonPtr_s && IOConsole::singletonPtr_s->originalTerminalSettings_) 348 { 349 tcsetattr(0, TCSANOW, IOConsole::singletonPtr_s->originalTerminalSettings_); 350 delete IOConsole::singletonPtr_s->originalTerminalSettings_; 351 IOConsole::singletonPtr_s->originalTerminalSettings_ = 0; 352 } 382 353 } 383 354 … … 411 382 } 412 383 384 inline bool IOConsole::willPrintStatusLines() 385 { 386 return !this->statusLineWidths_.empty() 387 && this->terminalWidth_ >= this->statusLineMaxWidth_ 388 && this->terminalHeight_ >= this->minOutputLines_ + (int)this->statusLineWidths_.size(); 389 } 390 413 391 // ############################### 414 392 // ### ShellListener methods ### … … 423 401 this->cout_ << "\033[K"; 424 402 // Reprint the last output line 425 this->print LogText(*(this->shell_->getNewestLineIterator()));403 this->printOutputLine(this->shell_->getNewestLineIterator()->first, this->shell_->getNewestLineIterator()->second); 426 404 // Restore cursor 427 405 this->cout_ << "\033[u"; … … 432 410 void IOConsole::lineAdded() 433 411 { 434 int newLines = this->shell_->getNewestLineIterator()-> size() / this->terminalWidth_ + 1;412 int newLines = this->shell_->getNewestLineIterator()->first.size() / this->terminalWidth_ + 1; 435 413 // Create new lines by scrolling the screen 436 414 this->cout_ << "\033[" << newLines << 'S'; … … 441 419 // Print the new output lines 442 420 for (int i = 0; i < newLines; ++i) 443 this->printLogText(this->shell_->getNewestLineIterator()->substr(i*this->terminalWidth_, this->terminalWidth_)); 421 { 422 Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator(); 423 this->printOutputLine(it->first.substr(i*this->terminalWidth_, this->terminalWidth_), it->second); 424 } 444 425 // Move cursor down 445 426 this->cout_ << "\033[1B\033[1G"; … … 449 430 this->cout_.flush(); 450 431 } 432 433 //! Called if the text in the input-line has changed 434 void IOConsole::inputChanged() 435 { 436 this->printInputLine(); 437 this->cout_.flush(); 438 } 439 440 //! Called if the position of the cursor in the input-line has changed 441 void IOConsole::cursorChanged() 442 { 443 this->printInputLine(); 444 this->cout_.flush(); 445 } 451 446 } 452 447 … … 456 451 // ################################## 457 452 453 #include <windows.h> 454 458 455 namespace orxonox 459 456 { 457 //! Redirects std::cout, creates the corresponding Shell and changes the terminal mode 460 458 IOConsole::IOConsole() 461 : shell_(new Shell("IOConsole", false , true))459 : shell_(new Shell("IOConsole", false)) 462 460 , buffer_(shell_->getInputBuffer()) 463 461 , cout_(std::cout.rdbuf()) 464 , bStatusPrinted_(false)465 462 , promptString_("orxonox # ") 466 { 463 , inputLineHeight_(1) 464 , statusLines_(1) 465 , lastOutputLineHeight_(0) 466 { 467 // Disable standard this->cout_ logging 468 OutputHandler::getInstance().disableCout(); 469 // Redirect std::cout to an ostringstream 470 // (Other part is in the initialiser list) 471 std::cout.rdbuf(this->origCout_.rdbuf()); 472 473 this->setTerminalMode(); 474 CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo; 475 GetConsoleScreenBufferInfo(this->stdOutHandle_, &screenBufferInfo); 476 this->terminalWidth_ = screenBufferInfo.dwSize.X; 477 this->terminalHeight_ = screenBufferInfo.dwSize.Y; 478 // Determines where we are in respect to output already written with std::cout 479 this->inputLineRow_ = screenBufferInfo.dwCursorPosition.Y; 467 480 /* 468 this->setTerminalMode(); 481 this->lastTerminalWidth_ = this->terminalWidth_; 482 this->lastTerminalHeight_ = this->terminalHeight_; 483 */ 484 485 // Cursor already at the end of the screen buffer? 486 // (assuming the current input line height is 1) 487 if (this->inputLineRow_ >= this->terminalHeight_ - this->statusLines_) 488 SetConsoleCursorPosition(this->stdOutHandle_, makeCOORD(0, this->terminalHeight_ - this->statusLines_)); 489 490 // Prevent input line from overflowing 491 int maxInputLength = (this->terminalHeight_ - this->statusLines_) * this->terminalWidth_ - 1 - this->promptString_.size(); 492 // Consider that the echo of a command might include the command plus some other characters (assumed max 80) 493 // Also put a minimum so the config file parser is not overwhelmed with the command history 494 this->buffer_->setMaxLength(std::min(8192, (maxInputLength - 80) / 2)); 495 496 // Print input and status line and position cursor 497 this->inputChanged(); 498 this->cursorChanged(); 499 this->lastRefreshTime_ = Game::getInstance().getGameClock().getRealMicroseconds(); 500 this->preUpdate(Game::getInstance().getGameClock()); 501 469 502 this->shell_->registerListener(this); 470 471 // Manually set the widths of the individual status lines 472 this->statusLineWidths_.push_back(29); 473 this->statusLineMaxWidth_ = 29; 474 475 this->getTerminalSize(); 503 } 504 505 //! Resets std::cout redirection and restores the terminal mode 506 IOConsole::~IOConsole() 507 { 508 // Process output written to std::cout in the meantime 509 std::cout.flush(); 510 if (!this->origCout_.str().empty()) 511 this->shell_->addOutput(this->origCout_.str(), Shell::None); 512 513 this->shell_->unregisterListener(this); 514 515 // Erase input and status lines 516 COORD pos = {0, this->inputLineRow_}; 517 this->writeText(std::string((this->inputLineHeight_ + this->statusLines_) * this->terminalWidth_, ' '), pos); 518 // Move cursor to the beginning of the line 519 SetConsoleCursorPosition(stdOutHandle_, pos); 520 521 // Restore this->cout_ redirection 522 std::cout.rdbuf(this->cout_.rdbuf()); 523 // Enable standard this->cout_ logging again 524 OutputHandler::getInstance().enableCout(); 525 526 resetTerminalMode(); 527 this->shell_->destroy(); 528 } 529 530 //! Processes the pending input key strokes, refreshes the status lines and handles std::cout (redirected) 531 void IOConsole::preUpdate(const Clock& time) 532 { 533 // Process input 534 while (true) 535 { 536 DWORD count; 537 INPUT_RECORD inrec; 538 PeekConsoleInput(this->stdInHandle_, &inrec, 1, &count); 539 if (count == 0) 540 break; 541 ReadConsoleInput(this->stdInHandle_, &inrec, 1, &count); 542 if (inrec.EventType == KEY_EVENT && inrec.Event.KeyEvent.bKeyDown) 543 { 544 // Process keyboard modifiers (Ctrl, Alt and Shift) 545 DWORD modifiersIn = inrec.Event.KeyEvent.dwControlKeyState; 546 int modifiersOut = 0; 547 if ((modifiersIn & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) != 0) 548 modifiersOut |= KeyboardModifier::Alt; 549 if ((modifiersIn & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0) 550 modifiersOut |= KeyboardModifier::Ctrl; 551 if ((modifiersIn & SHIFT_PRESSED) != 0) 552 modifiersOut |= KeyboardModifier::Shift; 553 554 // ASCII character (0 for special keys) 555 char asciiChar = inrec.Event.KeyEvent.uChar.AsciiChar; 556 557 // Process special keys and if not found, use Key::A as dummy (InputBuffer uses the ASCII text anyway) 558 switch (inrec.Event.KeyEvent.wVirtualKeyCode) 559 { 560 case VK_BACK: this->buffer_->buttonPressed(KeyEvent(KeyCode::Back, asciiChar, modifiersOut)); break; 561 case VK_TAB: this->buffer_->buttonPressed(KeyEvent(KeyCode::Tab, asciiChar, modifiersOut)); break; 562 case VK_RETURN: this->buffer_->buttonPressed(KeyEvent(KeyCode::Return, asciiChar, modifiersOut)); break; 563 case VK_PAUSE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Pause, asciiChar, modifiersOut)); break; 564 case VK_ESCAPE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, asciiChar, modifiersOut)); break; 565 case VK_SPACE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Space, asciiChar, modifiersOut)); break; 566 case VK_PRIOR: this->buffer_->buttonPressed(KeyEvent(KeyCode::PageUp, asciiChar, modifiersOut)); break; 567 case VK_NEXT: this->buffer_->buttonPressed(KeyEvent(KeyCode::PageDown, asciiChar, modifiersOut)); break; 568 case VK_END: this->buffer_->buttonPressed(KeyEvent(KeyCode::End, asciiChar, modifiersOut)); break; 569 case VK_HOME: this->buffer_->buttonPressed(KeyEvent(KeyCode::Home, asciiChar, modifiersOut)); break; 570 case VK_LEFT: this->buffer_->buttonPressed(KeyEvent(KeyCode::Left, asciiChar, modifiersOut)); break; 571 case VK_UP: this->buffer_->buttonPressed(KeyEvent(KeyCode::Up, asciiChar, modifiersOut)); break; 572 case VK_RIGHT: this->buffer_->buttonPressed(KeyEvent(KeyCode::Right, asciiChar, modifiersOut)); break; 573 case VK_DOWN: this->buffer_->buttonPressed(KeyEvent(KeyCode::Down, asciiChar, modifiersOut)); break; 574 case VK_INSERT: this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert, asciiChar, modifiersOut)); break; 575 case VK_DELETE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete, asciiChar, modifiersOut)); break; 576 default: this->buffer_->buttonPressed(KeyEvent(KeyCode::A, asciiChar, modifiersOut)); 577 } 578 } 579 } 580 581 // TODO: Respect screen buffer size changes 582 /* 583 // The user can manually adjust the screen buffer size on Windows 584 // And we don't want to screw the console because of that 476 585 this->lastTerminalWidth_ = this->terminalWidth_; 477 586 this->lastTerminalHeight_ = this->terminalHeight_; 478 479 // Disable standard this->cout_ logging 480 OutputHandler::getInstance().disableCout(); 587 this->getTerminalSize(); // Also sets this->inputLineRow_ according to the cursor position 588 // Is there still enough space below the cursor for the status line(s)? 589 if (this->inputLineRow_ >= this->terminalHeight_ - this->statusLines_) 590 this->moveCursor(0, -this->inputLineRow_ + this->terminalHeight_ - this->statusLines_ - 1); 481 591 */ 482 } 483 484 IOConsole::~IOConsole() 485 { 486 /* 487 resetTerminalMode(); 488 this->shell_->destroy(); 489 490 // Enable standard this->cout_ logging again 491 OutputHandler::getInstance().enableCout(); 492 */ 493 } 494 495 void IOConsole::update(const Clock& time) 496 { 497 /* 498 unsigned char c = 0; 499 while (std::cin.good()) 500 { 501 c = std::cin.get(); 502 if (std::cin.bad()) 503 break; 504 } 505 // Reset error flags in std::cin 506 std::cin.clear(); 507 508 // Determine terminal width and height 509 this->lastTerminalWidth_ = this->terminalWidth_; 510 this->lastTerminalHeight_ = this->terminalHeight_; 511 this->getTerminalSize(); 512 */ 513 } 514 515 void IOConsole::printLogText(const std::string& text) 516 { 517 } 518 519 void IOConsole::printInputLine() 520 { 521 } 522 592 593 // Refresh status line 5 times per second 594 if (time.getMicroseconds() > this->lastRefreshTime_ + 1000000) 595 { 596 this->printStatusLines(); 597 this->lastRefreshTime_ = time.getMicroseconds(); 598 } 599 600 // Process output written to std::cout 601 std::cout.flush(); 602 if (!this->origCout_.str().empty()) 603 { 604 this->shell_->addOutput(this->origCout_.str(), Shell::None); 605 this->origCout_.str(""); 606 } 607 } 608 609 //! Prints output text. Similar to writeText, but sets the colour according to the output level 610 void IOConsole::printOutputLine(const std::string& text, Shell::LineType type, const COORD& pos) 611 { 612 // Colour line 613 WORD colour = 0; 614 switch (type) 615 { 616 case Shell::Error: colour = FOREGROUND_INTENSITY | FOREGROUND_RED; break; 617 case Shell::Warning: colour = FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED; break; 618 case Shell::Info: 619 case Shell::Debug: 620 case Shell::Verbose: 621 case Shell::Ultra: colour = FOREGROUND_INTENSITY ; break; 622 case Shell::Command: colour = FOREGROUND_GREEN | FOREGROUND_BLUE; break; 623 case Shell::Hint: colour = FOREGROUND_GREEN | FOREGROUND_RED ; break; 624 default: colour = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE; break; 625 } 626 627 // Print output line 628 this->writeText(text, pos, colour); 629 } 630 631 //! Prints all status lines with current content 523 632 void IOConsole::printStatusLines() 524 633 { 525 /* 526 if (this->willPrintStatusLines())527 {528 this->bStatusPrinted_ = true;529 }530 else531 this->bStatusPrinted_ = false;532 */ 533 } 534 634 // Prepare text to be written 635 std::ostringstream oss; 636 oss << std::fixed << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgFPS() << " fps, "; 637 oss << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgTickTime() << " ms tick time"; 638 // Clear rest of the line by inserting spaces 639 oss << std::string(this->terminalWidth_ - oss.str().size(), ' '); 640 this->writeText(oss.str(), makeCOORD(0, this->inputLineRow_ + this->inputLineHeight_), FOREGROUND_GREEN); 641 } 642 643 //! Changes the console parameters for unbuffered input 535 644 void IOConsole::setTerminalMode() 536 645 { 537 } 538 646 // Set the console mode to no-echo, raw input, and no window or mouse events 647 this->stdOutHandle_ = GetStdHandle(STD_OUTPUT_HANDLE); 648 this->stdInHandle_ = GetStdHandle(STD_INPUT_HANDLE); 649 if (this->stdInHandle_ == INVALID_HANDLE_VALUE 650 || !GetConsoleMode(this->stdInHandle_, &this->originalTerminalSettings_) 651 || !SetConsoleMode(this->stdInHandle_, 0)) 652 { 653 COUT(1) << "Error: Could not set Windows console settings" << std::endl; 654 return; 655 } 656 FlushConsoleInputBuffer(this->stdInHandle_); 657 } 658 659 //! Restores the console parameters 539 660 void IOConsole::resetTerminalMode() 540 661 { 541 } 542 662 SetConsoleMode(this->stdInHandle_, this->originalTerminalSettings_); 663 } 664 665 //! Sets this->terminalWidth_ and this->terminalHeight_ 543 666 void IOConsole::getTerminalSize() 544 667 { 668 CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo; 669 GetConsoleScreenBufferInfo(this->stdOutHandle_, &screenBufferInfo); 670 this->terminalWidth_ = screenBufferInfo.dwSize.X; 671 this->terminalHeight_ = screenBufferInfo.dwSize.Y; 672 } 673 674 //! Writes arbitrary text to the console with a certain colour and screen buffer position 675 void IOConsole::writeText(const std::string& text, const COORD& coord, WORD attributes) 676 { 677 DWORD count; 678 WriteConsoleOutputCharacter(stdOutHandle_, text.c_str(), text.size(), coord, &count); 679 FillConsoleOutputAttribute(stdOutHandle_, attributes, text.size(), coord, &count); 680 } 681 682 /** Scrolls the console screen buffer to create empty lines above the input line. 683 @details 684 If the input and status lines are already at the bottom of the screen buffer 685 the whole output gets scrolled up. In the other case the input and status 686 lines get scrolled down. 687 In any case the status and input lines get scrolled down as far as possible. 688 @param lines 689 Number of lines to be inserted. Behavior for negative values is undefined. 690 */ 691 void IOConsole::createNewOutputLines(int lines) 692 { 693 CHAR_INFO fillChar = {{' '}, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED}; 694 // Lines to scroll input/status down (if possible) 695 int linesDown = clamp(terminalHeight_ - inputLineRow_ - inputLineHeight_ - statusLines_, 0, lines); 696 if (linesDown > 0) 697 { 698 // Scroll input and status lines down 699 SMALL_RECT oldRect = {0, this->inputLineRow_, 700 this->terminalWidth_ - 1, this->inputLineRow_ + this->inputLineHeight_ + this->statusLines_ - 1}; 701 this->inputLineRow_ += linesDown; 702 ScrollConsoleScreenBuffer(stdOutHandle_, &oldRect, NULL, makeCOORD(0, this->inputLineRow_), &fillChar); 703 // Move cursor down to the new bottom so the user can see the status lines 704 COORD pos = {0, this->inputLineRow_ + this->inputLineHeight_ - 1 + this->statusLines_}; 705 SetConsoleCursorPosition(stdOutHandle_, pos); 706 // Get cursor back to the right position 707 this->cursorChanged(); 708 } 709 // Check how many lines we still have to scroll up the output 710 if (lines - linesDown > 0) 711 { 712 // Scroll output up 713 SMALL_RECT oldRect = {0, lines - linesDown, this->terminalWidth_ - 1, this->inputLineRow_ - 1}; 714 ScrollConsoleScreenBuffer(stdOutHandle_, &oldRect, NULL, makeCOORD(0, 0), &fillChar); 715 } 545 716 } 546 717 … … 549 720 // ############################### 550 721 722 //! Called if the text in the input line has changed 723 void IOConsole::inputChanged() 724 { 725 int newInputLineLength = this->promptString_.size() + this->shell_->getInput().size(); 726 int newInputLineHeight = 1 + newInputLineLength / this->terminalWidth_; 727 int newLines = newInputLineHeight - this->inputLineHeight_; 728 if (newLines > 0) 729 { 730 // Abuse this function to scroll the console 731 this->createNewOutputLines(newLines); 732 // Either Compensate for side effects (input/status lines scrolled down) 733 // or we have to do this anyway (output scrolled up) 734 this->inputLineRow_ -= newLines; 735 } 736 else if (newLines < 0) 737 { 738 // Scroll status lines up 739 int statusLineRow = this->inputLineRow_ + this->inputLineHeight_; 740 SMALL_RECT oldRect = {0, statusLineRow, this->terminalWidth_ - 1, statusLineRow + this->statusLines_}; 741 CHAR_INFO fillChar = {{' '}, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED}; 742 ScrollConsoleScreenBuffer(stdOutHandle_, &oldRect, NULL, makeCOORD(0, statusLineRow + newLines), &fillChar); 743 // Clear potential leftovers 744 if (-newLines - this->statusLines_ > 0) 745 { 746 COORD pos = {0, this->inputLineRow_ + newInputLineHeight + this->statusLines_}; 747 this->writeText(std::string((-newLines - this->statusLines_) * this->terminalWidth_, ' '), pos); 748 } 749 } 750 this->inputLineHeight_ = newInputLineHeight; 751 752 // Print the whole line, including spaces that erase leftovers 753 std::string inputLine = this->promptString_ + this->shell_->getInput(); 754 inputLine += std::string(this->terminalWidth_ - newInputLineLength % this->terminalWidth_, ' '); 755 this->writeText(inputLine, makeCOORD(0, this->inputLineRow_), FOREGROUND_GREEN | FOREGROUND_INTENSITY); 756 // If necessary, move cursor 757 if (newLines != 0) 758 this->cursorChanged(); 759 } 760 761 //! Called if the position of the cursor in the input-line has changed 762 void IOConsole::cursorChanged() 763 { 764 int rawCursorPos = this->promptString_.size() + this->buffer_->getCursorPosition(); 765 // Compensate for cursor further to the right than the terminal width 766 COORD pos; 767 pos.X = rawCursorPos % this->terminalWidth_; 768 pos.Y = this->inputLineRow_ + rawCursorPos / this->terminalWidth_; 769 SetConsoleCursorPosition(stdOutHandle_, pos); 770 } 771 551 772 //! Called if only the last output-line has changed 552 773 void IOConsole::onlyLastLineChanged() 553 774 { 554 } 555 556 //! Called if a new output-line was added 775 int newLineHeight = 1 + this->shell_->getNewestLineIterator()->first.size() / this->terminalWidth_; 776 // Compute the number of new lines needed 777 int newLines = newLineHeight - this->lastOutputLineHeight_; 778 this->lastOutputLineHeight_ = newLineHeight; 779 // Scroll console if necessary 780 if (newLines > 0) // newLines < 0 is assumed impossible 781 this->createNewOutputLines(newLines); 782 Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator(); 783 this->printOutputLine(it->first, it->second, makeCOORD(0, this->inputLineRow_ - newLineHeight)); 784 } 785 786 //! Called if a new output line was added 557 787 void IOConsole::lineAdded() 558 788 { 789 Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator(); 790 // Scroll console 791 this->lastOutputLineHeight_ = 1 + it->first.size() / this->terminalWidth_; 792 this->createNewOutputLines(this->lastOutputLineHeight_); 793 // Write the text 794 COORD pos = {0, this->inputLineRow_ - this->lastOutputLineHeight_}; 795 this->printOutputLine(it->first, it->second, pos); 559 796 } 560 797 }
Note: See TracChangeset
for help on using the changeset viewer.