Changeset 7287 for code/trunk/src/libraries/core/command/IOConsolePOSIX.cc
- Timestamp:
- Aug 31, 2010, 11:13:46 AM (15 years ago)
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
code/trunk/src/libraries/core/command/IOConsolePOSIX.cc
r7265 r7287 32 32 #include <iomanip> 33 33 #include <iostream> 34 #include <termios.h> 35 #include <sys/ioctl.h> 34 36 35 37 #include "util/Clock.h" 36 38 #include "util/Math.h" 37 #include "Game.h" 38 #include "input/InputBuffer.h" 39 40 // ########################## 41 // ### Mutual methods ### 42 // ########################## 39 #include "core/Game.h" 40 #include "core/input/InputBuffer.h" 41 43 42 namespace orxonox 44 43 { 45 44 IOConsole* IOConsole::singletonPtr_s = NULL; 46 45 47 // ###############################48 // ### ShellListener methods ###49 // ###############################50 51 //! Called if all output-lines have to be reprinted52 void IOConsole::linesChanged()53 {54 // Method only gets called upon start to draw all the lines55 // or when scrolling. But scrolling is disabled and the output56 // is already in std::cout when we start the IOConsole57 }58 59 //! Called if a command is about to be executed60 void IOConsole::executed()61 {62 this->shell_->addOutput(this->promptString_ + this->shell_->getInput() + '\n', Shell::Command);63 }64 65 //! Called if the console gets closed66 void IOConsole::exit()67 {68 // Exit is not an option, just do nothing (Shell doesn't really exit too)69 }70 }71 72 #ifdef ORXONOX_PLATFORM_UNIX73 // ###############################74 // ### Unix Implementation ###75 // ###############################76 77 #include <termios.h>78 #include <sys/ioctl.h>79 80 namespace orxonox81 {82 46 namespace EscapeMode 83 47 { … … 393 357 // ############################### 394 358 359 //! Called if all output-lines have to be reprinted 360 void IOConsole::linesChanged() 361 { 362 // Method only gets called upon start to draw all the lines 363 // or when scrolling. But scrolling is disabled and the output 364 // is already in std::cout when we start the IOConsole 365 } 366 367 //! Called if a command is about to be executed 368 void IOConsole::executed() 369 { 370 this->shell_->addOutput(this->promptString_ + this->shell_->getInput() + '\n', Shell::Command); 371 } 372 373 //! Called if the console gets closed 374 void IOConsole::exit() 375 { 376 // Exit is not an option, just do nothing (Shell doesn't really exit too) 377 } 378 395 379 //! Called if only the last output-line has changed 396 380 void IOConsole::onlyLastLineChanged() … … 445 429 } 446 430 } 447 448 #elif defined(ORXONOX_PLATFORM_WINDOWS)449 // ##################################450 // ### Windows Implementation ###451 // ##################################452 453 #include <windows.h>454 455 namespace orxonox456 {457 //! Redirects std::cout, creates the corresponding Shell and changes the terminal mode458 IOConsole::IOConsole()459 : shell_(new Shell("IOConsole", false))460 , buffer_(shell_->getInputBuffer())461 , cout_(std::cout.rdbuf())462 , promptString_("orxonox # ")463 , inputLineHeight_(1)464 , statusLines_(1)465 , lastOutputLineHeight_(0)466 {467 // Disable standard this->cout_ logging468 OutputHandler::getInstance().disableCout();469 // Redirect std::cout to an ostringstream470 // (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::cout479 this->inputLineRow_ = screenBufferInfo.dwCursorPosition.Y;480 /*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 overflowing491 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 history494 this->buffer_->setMaxLength(std::min(8192, (maxInputLength - 80) / 2));495 496 // Print input and status line and position cursor497 this->inputChanged();498 this->cursorChanged();499 this->lastRefreshTime_ = Game::getInstance().getGameClock().getRealMicroseconds();500 this->preUpdate(Game::getInstance().getGameClock());501 502 this->shell_->registerListener(this);503 }504 505 //! Resets std::cout redirection and restores the terminal mode506 IOConsole::~IOConsole()507 {508 // Process output written to std::cout in the meantime509 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 lines516 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 line519 SetConsoleCursorPosition(stdOutHandle_, pos);520 521 // Restore this->cout_ redirection522 std::cout.rdbuf(this->cout_.rdbuf());523 // Enable standard this->cout_ logging again524 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 input534 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 changes582 /*583 // The user can manually adjust the screen buffer size on Windows584 // And we don't want to screw the console because of that585 this->lastTerminalWidth_ = this->terminalWidth_;586 this->lastTerminalHeight_ = this->terminalHeight_;587 this->getTerminalSize(); // Also sets this->inputLineRow_ according to the cursor position588 // 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);591 */592 593 // Refresh status line 5 times per second594 if (time.getMicroseconds() > this->lastRefreshTime_ + 1000000)595 {596 this->printStatusLines();597 this->lastRefreshTime_ = time.getMicroseconds();598 }599 600 // Process output written to std::cout601 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 level610 void IOConsole::printOutputLine(const std::string& text, Shell::LineType type, const COORD& pos)611 {612 // Colour line613 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 line628 this->writeText(text, pos, colour);629 }630 631 //! Prints all status lines with current content632 void IOConsole::printStatusLines()633 {634 // Prepare text to be written635 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 spaces639 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 input644 void IOConsole::setTerminalMode()645 {646 // Set the console mode to no-echo, raw input, and no window or mouse events647 this->stdOutHandle_ = GetStdHandle(STD_OUTPUT_HANDLE);648 this->stdInHandle_ = GetStdHandle(STD_INPUT_HANDLE);649 if (this->stdInHandle_ == INVALID_HANDLE_VALUE650 || !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 parameters660 void IOConsole::resetTerminalMode()661 {662 SetConsoleMode(this->stdInHandle_, this->originalTerminalSettings_);663 }664 665 //! Sets this->terminalWidth_ and this->terminalHeight_666 void IOConsole::getTerminalSize()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 position675 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 @details684 If the input and status lines are already at the bottom of the screen buffer685 the whole output gets scrolled up. In the other case the input and status686 lines get scrolled down.687 In any case the status and input lines get scrolled down as far as possible.688 @param lines689 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 down699 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 lines704 COORD pos = {0, this->inputLineRow_ + this->inputLineHeight_ - 1 + this->statusLines_};705 SetConsoleCursorPosition(stdOutHandle_, pos);706 // Get cursor back to the right position707 this->cursorChanged();708 }709 // Check how many lines we still have to scroll up the output710 if (lines - linesDown > 0)711 {712 // Scroll output up713 SMALL_RECT oldRect = {0, lines - linesDown, this->terminalWidth_ - 1, this->inputLineRow_ - 1};714 ScrollConsoleScreenBuffer(stdOutHandle_, &oldRect, NULL, makeCOORD(0, 0), &fillChar);715 }716 }717 718 // ###############################719 // ### ShellListener methods ###720 // ###############################721 722 //! Called if the text in the input line has changed723 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 console731 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 up739 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 leftovers744 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 leftovers753 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 cursor757 if (newLines != 0)758 this->cursorChanged();759 }760 761 //! Called if the position of the cursor in the input-line has changed762 void IOConsole::cursorChanged()763 {764 int rawCursorPos = this->promptString_.size() + this->buffer_->getCursorPosition();765 // Compensate for cursor further to the right than the terminal width766 COORD pos;767 pos.X = rawCursorPos % this->terminalWidth_;768 pos.Y = this->inputLineRow_ + rawCursorPos / this->terminalWidth_;769 SetConsoleCursorPosition(stdOutHandle_, pos);770 }771 772 //! Called if only the last output-line has changed773 void IOConsole::onlyLastLineChanged()774 {775 int newLineHeight = 1 + this->shell_->getNewestLineIterator()->first.size() / this->terminalWidth_;776 // Compute the number of new lines needed777 int newLines = newLineHeight - this->lastOutputLineHeight_;778 this->lastOutputLineHeight_ = newLineHeight;779 // Scroll console if necessary780 if (newLines > 0) // newLines < 0 is assumed impossible781 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 added787 void IOConsole::lineAdded()788 {789 Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator();790 // Scroll console791 this->lastOutputLineHeight_ = 1 + it->first.size() / this->terminalWidth_;792 this->createNewOutputLines(this->lastOutputLineHeight_);793 // Write the text794 COORD pos = {0, this->inputLineRow_ - this->lastOutputLineHeight_};795 this->printOutputLine(it->first, it->second, pos);796 }797 }798 799 #endif /* ORXONOX_PLATFORM_UNIX */
Note: See TracChangeset
for help on using the changeset viewer.