Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Ignore:
Timestamp:
Aug 31, 2010, 11:13:46 AM (14 years ago)
Author:
rgrieder
Message:

Split IOConsole in two separate files for better browsing.

File:
1 copied

Legend:

Unmodified
Added
Removed
  • code/trunk/src/libraries/core/command/IOConsolePOSIX.cc

    r7265 r7287  
    3232#include <iomanip>
    3333#include <iostream>
     34#include <termios.h>
     35#include <sys/ioctl.h>
    3436
    3537#include "util/Clock.h"
    3638#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
    4342namespace orxonox
    4443{
    4544    IOConsole* IOConsole::singletonPtr_s = NULL;
    4645
    47     // ###############################
    48     // ###  ShellListener methods  ###
    49     // ###############################
    50 
    51     //! Called if all output-lines have to be reprinted
    52     void IOConsole::linesChanged()
    53     {
    54         // Method only gets called upon start to draw all the lines
    55         // or when scrolling. But scrolling is disabled and the output
    56         // is already in std::cout when we start the IOConsole
    57     }
    58 
    59     //! Called if a command is about to be executed
    60     void IOConsole::executed()
    61     {
    62         this->shell_->addOutput(this->promptString_ + this->shell_->getInput() + '\n', Shell::Command);
    63     }
    64 
    65     //! Called if the console gets closed
    66     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_UNIX
    73 // ###############################
    74 // ###   Unix Implementation   ###
    75 // ###############################
    76 
    77 #include <termios.h>
    78 #include <sys/ioctl.h>
    79 
    80 namespace orxonox
    81 {
    8246    namespace EscapeMode
    8347    {
     
    393357    // ###############################
    394358
     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
    395379    //! Called if only the last output-line has changed
    396380    void IOConsole::onlyLastLineChanged()
     
    445429    }
    446430}
    447 
    448 #elif defined(ORXONOX_PLATFORM_WINDOWS)
    449 // ##################################
    450 // ###   Windows Implementation   ###
    451 // ##################################
    452 
    453 #include <windows.h>
    454 
    455 namespace orxonox
    456 {
    457     //! Redirects std::cout, creates the corresponding Shell and changes the terminal mode
    458     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_ 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;
    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 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 
    502         this->shell_->registerListener(this);
    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
    585         this->lastTerminalWidth_ = this->terminalWidth_;
    586         this->lastTerminalHeight_ = this->terminalHeight_;
    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);
    591 */
    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
    632     void IOConsole::printStatusLines()
    633     {
    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
    644     void IOConsole::setTerminalMode()
    645     {
    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
    660     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 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         }
    716     }
    717 
    718     // ###############################
    719     // ###  ShellListener methods  ###
    720     // ###############################
    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 
    772     //! Called if only the last output-line has changed
    773     void IOConsole::onlyLastLineChanged()
    774     {
    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
    787     void IOConsole::lineAdded()
    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);
    796     }
    797 }
    798 
    799 #endif /* ORXONOX_PLATFORM_UNIX */
Note: See TracChangeset for help on using the changeset viewer.