Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Changeset 7287


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

Split IOConsole in two separate files for better browsing.

Location:
code/trunk/src/libraries/core/command
Files:
2 edited
4 copied

Legend:

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

    r7284 r7287  
    2121 *
    2222 *   Author:
    23  *      Oliver Scheuss
    2423 *      Reto Grieder
    2524 *   Co-authors:
     
    3029#include "IOConsole.h"
    3130
    32 #include <iomanip>
    33 #include <iostream>
    34 
    35 #include "util/Clock.h"
    36 #include "util/Math.h"
    37 #include "core/Game.h"
    38 #include "core/input/InputBuffer.h"
    39 
    40 // ##########################
    41 // ###   Mutual methods   ###
    42 // ##########################
    43 namespace orxonox
    44 {
    45     IOConsole* IOConsole::singletonPtr_s = NULL;
    46 
    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 {
    82     namespace EscapeMode
    83     {
    84         enum Value
    85         {
    86             None,
    87             First,
    88             Second
    89         };
    90     }
    91 
    92     IOConsole::IOConsole()
    93         : shell_(new Shell("IOConsole", false))
    94         , buffer_(shell_->getInputBuffer())
    95         , cout_(std::cout.rdbuf())
    96         , promptString_("orxonox # ")
    97         , bStatusPrinted_(false)
    98         , originalTerminalSettings_(0)
    99     {
    100         this->setTerminalMode();
    101         this->shell_->registerListener(this);
    102 
    103         // Manually set the widths of the individual status lines
    104         this->statusLineWidths_.push_back(29);
    105         this->statusLineMaxWidth_ = 29;
    106 
    107         this->getTerminalSize();
    108         this->lastTerminalWidth_ = this->terminalWidth_;
    109         this->lastTerminalHeight_ = this->terminalHeight_;
    110 
    111         // Disable standard std::cout logging
    112         OutputHandler::getInstance().disableCout();
    113         // Redirect std::cout to an ostringstream
    114         // (Other part is in the initialiser list)
    115         std::cout.rdbuf(this->origCout_.rdbuf());
    116 
    117         // Make sure we make way for the status lines
    118         this->preUpdate(Game::getInstance().getGameClock());
    119     }
    120 
    121     IOConsole::~IOConsole()
    122     {
    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);
    127         // Erase input and status lines
    128         this->cout_ << "\033[1G\033[J";
    129         // Move cursor to the bottom
    130         this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B';
    131         // Scroll terminal to compensate for erased lines
    132         this->cout_ << "\033[" << this->statusLineWidths_.size() << 'T';
    133 
    134         resetTerminalMode();
    135         this->shell_->destroy();
    136 
    137         // Restore this->cout_ redirection
    138         std::cout.rdbuf(this->cout_.rdbuf());
    139         // Enable standard std::cout logging again
    140         OutputHandler::getInstance().enableCout();
    141     }
    142 
    143     void IOConsole::preUpdate(const Clock& time)
    144     {
    145         unsigned char c;
    146         std::string escapeSequence;
    147         EscapeMode::Value escapeMode = EscapeMode::None;
    148         while (std::cin.good())
    149         {
    150             c = std::cin.get();
    151             if (!std::cin.good())
    152                 break;
    153 
    154             if (escapeMode == EscapeMode::First && (c == '[' || c=='O') )
    155                 escapeMode = EscapeMode::Second;
    156             // Get Alt+Tab combination when switching applications
    157             else if (escapeMode == EscapeMode::First && c == '\t')
    158             {
    159                 this->buffer_->buttonPressed(KeyEvent(KeyCode::Tab, '\t', KeyboardModifier::Alt));
    160                 escapeMode = EscapeMode::None;
    161             }
    162             else if (escapeMode == EscapeMode::Second)
    163             {
    164                 escapeSequence += c;
    165                 escapeMode = EscapeMode::None;
    166                 if      (escapeSequence == "A")
    167                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Up,       0, 0));
    168                 else if (escapeSequence == "B")
    169                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Down,     0, 0));
    170                 else if (escapeSequence == "C")
    171                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Right,    0, 0));
    172                 else if (escapeSequence == "D")
    173                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Left,     0, 0));
    174                 else if (escapeSequence == "1~" || escapeSequence == "H")
    175                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Home,     0, 0));
    176                 else if (escapeSequence == "2~")
    177                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert,   0, 0));
    178                 else if (escapeSequence == "3~")
    179                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete,   0, 0));
    180                 else if (escapeSequence == "4~" || escapeSequence == "F")
    181                     this->buffer_->buttonPressed(KeyEvent(KeyCode::End,      0, 0));
    182                 else if (escapeSequence == "5~")
    183                     this->buffer_->buttonPressed(KeyEvent(KeyCode::PageUp,   0, 0));
    184                 else if (escapeSequence == "6~")
    185                     this->buffer_->buttonPressed(KeyEvent(KeyCode::PageDown, 0, 0));
    186                 else
    187                     // Waiting for sequence to complete
    188                     // If the user presses ESC and then '[' or 'O' while the loop is not
    189                     // running (for instance while loading), the whole sequence gets dropped
    190                     escapeMode = EscapeMode::Second;
    191             }
    192             else // not in an escape sequence OR user might have pressed just ESC
    193             {
    194                 if (escapeMode == EscapeMode::First)
    195                 {
    196                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, c, 0));
    197                     escapeMode = EscapeMode::None;
    198                 }
    199                 if (c == '\033')
    200                 {
    201                     escapeMode = EscapeMode::First;
    202                     escapeSequence.clear();
    203                 }
    204                 else
    205                 {
    206                     KeyCode::ByEnum code;
    207                     switch (c)
    208                     {
    209                     case '\n'  : case '\r': code = KeyCode::Return; break;
    210                     case '\177': case '\b': code = KeyCode::Back;   break;
    211                     case '\t'             : code = KeyCode::Tab;    break;
    212                     default:
    213                         // We don't encode the key code (would be a very large switch)
    214                         // because the InputBuffer will only insert the text anyway
    215                         // Replacement character is simply KeyCode::A
    216                         code = KeyCode::A;
    217                     }
    218                     this->buffer_->buttonPressed(KeyEvent(code, c, 0));
    219                 }
    220             }
    221         }
    222         // Reset error flags in std::cin
    223         std::cin.clear();
    224 
    225         // If there is still an escape key pending (escape key ONLY), then
    226         // it sure isn't an escape sequence anymore
    227         if (escapeMode == EscapeMode::First)
    228             this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, '\033', 0));
    229 
    230         // Determine terminal width and height
    231         this->lastTerminalWidth_ = this->terminalWidth_;
    232         this->lastTerminalHeight_ = this->terminalHeight_;
    233         this->getTerminalSize();
    234 
    235         int heightDiff = this->terminalHeight_ - this->lastTerminalHeight_;
    236         if (this->bStatusPrinted_ && heightDiff < 0)
    237         {
    238             // Terminal width has shrunk. The cursor will still be on the input line,
    239             // but that line might very well be the last
    240             int newLines = std::min((int)this->statusLineWidths_.size(), -heightDiff);
    241             // Scroll terminal to create new lines
    242             this->cout_ << "\033[" << newLines << 'S';
    243         }
    244 
    245         if (!this->bStatusPrinted_ && this->willPrintStatusLines())
    246         {
    247             // Scroll console to make way for status lines
    248             this->cout_ << "\033[" << this->statusLineWidths_.size() << 'S';
    249             this->bStatusPrinted_ = true;
    250         }
    251 
    252         // We always assume that the cursor is on the input line.
    253         // But we cannot always be sure about that, esp. if we scroll the console
    254         this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B';
    255         this->cout_ << "\033[" << this->statusLineWidths_.size() << 'A';
    256 
    257         // Erase status and input lines
    258         this->cout_ << "\033[1G\033[J";
    259         this->printInputLine();
    260         this->printStatusLines();
    261         this->cout_.flush();
    262 
    263         // Process output written to std::cout
    264         std::cout.flush();
    265         if (!this->origCout_.str().empty())
    266         {
    267             this->shell_->addOutput(this->origCout_.str(), Shell::None);
    268             this->origCout_.str("");
    269         }
    270     }
    271 
    272     void IOConsole::printOutputLine(const std::string& text, Shell::LineType type)
    273     {
    274 /*
    275         // Colour line
    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;
    285         default: break;
    286         }
    287 */
    288 
    289         // Print output line
    290         this->cout_ << text;
    291 
    292         // Reset colour to white
    293 //        this->cout_ << "\033[37m";
    294     }
    295 
    296     void IOConsole::printInputLine()
    297     {
    298         // Set cursor to the beginning of the line and erase the line
    299         this->cout_ << "\033[1G\033[K";
    300         // Indicate a command prompt
    301         this->cout_ << this->promptString_;
    302         // Save cursor position
    303         this->cout_ << "\033[s";
    304         // Print command line buffer
    305         this->cout_ << this->shell_->getInput();
    306         // Restore cursor position and move it to the right
    307         this->cout_ << "\033[u";
    308         if (this->buffer_->getCursorPosition() > 0)
    309             this->cout_ << "\033[" << this->buffer_->getCursorPosition() << 'C';
    310     }
    311 
    312     void IOConsole::printStatusLines()
    313     {
    314         if (this->willPrintStatusLines())
    315         {
    316             // Save cursor position
    317             this->cout_ << "\033[s";
    318             // Move cursor down (don't create a new line here because the buffer might flush then!)
    319             this->cout_ << "\033[1B\033[1G";
    320             this->cout_ << std::fixed << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgFPS() << " fps, ";
    321             this->cout_ <<               std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgTickTime() << " ms tick time";
    322             // Restore cursor position
    323             this->cout_ << "\033[u";
    324             this->bStatusPrinted_ = true;
    325         }
    326         else
    327             this->bStatusPrinted_ = false;
    328     }
    329 
    330     void IOConsole::setTerminalMode()
    331     {
    332         termios new_settings;
    333         this->originalTerminalSettings_ = new termios();
    334 
    335         tcgetattr(0, this->originalTerminalSettings_);
    336         new_settings = *this->originalTerminalSettings_;
    337         new_settings.c_lflag &= ~(ICANON | ECHO);
    338         //new_settings.c_lflag |= (ISIG | IEXTEN);
    339         new_settings.c_cc[VTIME] = 0;
    340         new_settings.c_cc[VMIN]  = 0;
    341         tcsetattr(0, TCSANOW, &new_settings);
    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         }
    353     }
    354 
    355     void IOConsole::getTerminalSize()
    356     {
    357 #ifdef TIOCGSIZE
    358         struct ttysize win;
    359         if (!ioctl(STDIN_FILENO, TIOCGSIZE, &win))
    360         {
    361             this->terminalWidth_  = win.ts_cols;
    362             this->terminalHeight_ = win.ts_lines;
    363             return;
    364         }
    365 #elif defined TIOCGWINSZ
    366         struct winsize win;
    367         if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &win))
    368         {
    369             this->terminalWidth_  = win.ws_col;
    370             this->terminalHeight_ = win.ws_row;
    371             return;
    372         }
    373 #else
    374         const char* s = getenv("COLUMNS");
    375         this->terminalWidth_  = s ? strtol(s, NULL, 10) : 80;
    376         s = getenv("LINES");
    377         this->terminalHeight_ = s ? strtol(s, NULL, 10) : 24;
    378         return;
     31#if defined(ORXONOX_PLATFORM_UNIX)
     32  #include "IOConsolePOSIX.cc"
     33#elif defined(ORXONOX_PLATFORM_WINDOWS)
     34  #include "IOConsoleWindows.cc"
    37935#endif
    380         this->terminalWidth_  = 80;
    381         this->terminalHeight_ = 24;
    382     }
    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 
    391     // ###############################
    392     // ###  ShellListener methods  ###
    393     // ###############################
    394 
    395     //! Called if only the last output-line has changed
    396     void IOConsole::onlyLastLineChanged()
    397     {
    398         // Save cursor position and move it to the beginning of the first output line
    399         this->cout_ << "\033[s\033[1A\033[1G";
    400         // Erase the line
    401         this->cout_ << "\033[K";
    402         // Reprint the last output line
    403         this->printOutputLine(this->shell_->getNewestLineIterator()->first, this->shell_->getNewestLineIterator()->second);
    404         // Restore cursor
    405         this->cout_ << "\033[u";
    406         this->cout_.flush();
    407     }
    408 
    409     //! Called if a new output-line was added
    410     void IOConsole::lineAdded()
    411     {
    412         int newLines = this->shell_->getNewestLineIterator()->first.size() / this->terminalWidth_ + 1;
    413         // Create new lines by scrolling the screen
    414         this->cout_ << "\033[" << newLines << 'S';
    415         // Move cursor to the beginning of the new (last) output line
    416         this->cout_ << "\033[" << newLines << "A\033[1G";
    417         // Erase screen from here
    418         this->cout_ << "\033[J";
    419         // Print the new output lines
    420         for (int i = 0; i < newLines; ++i)
    421         {
    422             Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator();
    423             this->printOutputLine(it->first.substr(i*this->terminalWidth_, this->terminalWidth_), it->second);
    424         }
    425         // Move cursor down
    426         this->cout_ << "\033[1B\033[1G";
    427         // Print status and input lines
    428         this->printInputLine();
    429         this->printStatusLines();
    430         this->cout_.flush();
    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     }
    446 }
    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 */
  • code/trunk/src/libraries/core/command/IOConsole.h

    r7284 r7287  
    2121 *
    2222 *   Author:
    23  *      Oliver Scheuss
    2423 *      Reto Grieder
    2524 *   Co-authors:
     
    2827 */
    2928
    30 #ifndef _IOConsole_H__
    31 #define _IOConsole_H__
     29#include "OrxonoxConfig.h"
    3230
    33 #include "core/CorePrereqs.h"
    34 
    35 #include <sstream>
    36 #include <string>
    37 #include <vector>
    38 #include "util/Singleton.h"
    39 #include "Shell.h"
    40 
    41 #ifdef ORXONOX_PLATFORM_UNIX
    42 struct termios;
     31#if defined(ORXONOX_PLATFORM_UNIX)
     32  #include "IOConsolePOSIX.h"
    4333#elif defined(ORXONOX_PLATFORM_WINDOWS)
    44 #define WIN32_LEAN_AND_MEAN
    45 #ifndef NOMINMAX
    46 #define NOMINMAX
     34  #include "IOConsoleWindows.h"
    4735#endif
    48 #include <windows.h>
    49 #endif
    50 
    51 namespace orxonox
    52 {
    53     class _CoreExport IOConsole : public Singleton<IOConsole>, public ShellListener
    54     {
    55         friend class Singleton<IOConsole>;
    56 
    57     public:
    58         IOConsole();
    59         ~IOConsole();
    60 
    61         void preUpdate(const Clock& time);
    62 
    63     private:
    64         void setTerminalMode();
    65         void getTerminalSize();
    66         void printStatusLines();
    67         static int extractLogLevel(std::string* text);
    68 
    69         // Methods from ShellListener
    70         void linesChanged();
    71         void onlyLastLineChanged();
    72         void lineAdded();
    73         void inputChanged();
    74         void cursorChanged();
    75         void executed();
    76         void exit();
    77 
    78         Shell*                  shell_;
    79         InputBuffer*            buffer_;
    80         std::ostream            cout_;
    81         std::ostringstream      origCout_;
    82         int                     terminalWidth_;
    83         int                     terminalHeight_;
    84         int                     lastTerminalWidth_;
    85         int                     lastTerminalHeight_;
    86         const std::string       promptString_;
    87 
    88 #ifdef ORXONOX_PLATFORM_UNIX
    89         bool willPrintStatusLines();
    90         void printInputLine();
    91         void printOutputLine(const std::string& line, Shell::LineType type);
    92         static void resetTerminalMode();
    93 
    94         bool                    bPrintStatusLine_;
    95         bool                    bStatusPrinted_;
    96         std::vector<int>        statusLineWidths_;
    97         int                     statusLineMaxWidth_;
    98         static const int        minOutputLines_ = 3;
    99         termios*                originalTerminalSettings_;
    100 
    101 #elif defined(ORXONOX_PLATFORM_WINDOWS)
    102         void resetTerminalMode();
    103         void moveCursor(int dx, int dy);
    104         void writeText(const std::string& text, const COORD& pos, WORD attributes = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
    105         void createNewOutputLines(int lines);
    106         void printOutputLine(const std::string& line, Shell::LineType type, const COORD& pos);
    107 
    108         static inline COORD makeCOORD(int x, int y)
    109         {
    110             COORD val = {x, y};
    111             return val;
    112         }
    113 
    114         DWORD                   originalTerminalSettings_;
    115         HANDLE                  stdInHandle_;
    116         HANDLE                  stdOutHandle_;
    117         int                     inputLineRow_;
    118         int                     inputLineHeight_;
    119         const int               statusLines_;
    120         int                     lastOutputLineHeight_;
    121         uint64_t                lastRefreshTime_;
    122 #endif
    123 
    124         static IOConsole* singletonPtr_s;
    125     };
    126 }
    127 
    128 #endif /* _IOConsole_H__ */
  • 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 */
  • code/trunk/src/libraries/core/command/IOConsolePOSIX.h

    r7265 r7287  
    3131#define _IOConsole_H__
    3232
    33 #include "CorePrereqs.h"
     33#include "core/CorePrereqs.h"
    3434
    3535#include <sstream>
     
    3939#include "Shell.h"
    4040
    41 #ifdef ORXONOX_PLATFORM_UNIX
    4241struct termios;
    43 #elif defined(ORXONOX_PLATFORM_WINDOWS)
    44 #define WIN32_LEAN_AND_MEAN
    45 #ifndef NOMINMAX
    46 #define NOMINMAX
    47 #endif
    48 #include <windows.h>
    49 #endif
    5042
    5143namespace orxonox
     
    7668        void exit();
    7769
     70        bool willPrintStatusLines();
     71        void printInputLine();
     72        void printOutputLine(const std::string& line, Shell::LineType type);
     73        static void resetTerminalMode();
     74
    7875        Shell*                  shell_;
    7976        InputBuffer*            buffer_;
     
    8683        const std::string       promptString_;
    8784
    88 #ifdef ORXONOX_PLATFORM_UNIX
    89         bool willPrintStatusLines();
    90         void printInputLine();
    91         void printOutputLine(const std::string& line, Shell::LineType type);
    92         static void resetTerminalMode();
    93 
    9485        bool                    bPrintStatusLine_;
    9586        bool                    bStatusPrinted_;
     
    9990        termios*                originalTerminalSettings_;
    10091
    101 #elif defined(ORXONOX_PLATFORM_WINDOWS)
    102         void resetTerminalMode();
    103         void moveCursor(int dx, int dy);
    104         void writeText(const std::string& text, const COORD& pos, WORD attributes = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
    105         void createNewOutputLines(int lines);
    106         void printOutputLine(const std::string& line, Shell::LineType type, const COORD& pos);
    107 
    108         static inline COORD makeCOORD(int x, int y)
    109         {
    110             COORD val = {x, y};
    111             return val;
    112         }
    113 
    114         DWORD                   originalTerminalSettings_;
    115         HANDLE                  stdInHandle_;
    116         HANDLE                  stdOutHandle_;
    117         int                     inputLineRow_;
    118         int                     inputLineHeight_;
    119         const int               statusLines_;
    120         int                     lastOutputLineHeight_;
    121         uint64_t                lastRefreshTime_;
    122 #endif
    123 
    12492        static IOConsole* singletonPtr_s;
    12593    };
  • code/trunk/src/libraries/core/command/IOConsoleWindows.cc

    r7265 r7287  
    2121 *
    2222 *   Author:
    23  *      Oliver Scheuss
    2423 *      Reto Grieder
    2524 *   Co-authors:
     
    3534#include "util/Clock.h"
    3635#include "util/Math.h"
    37 #include "Game.h"
    38 #include "input/InputBuffer.h"
    39 
    40 // ##########################
    41 // ###   Mutual methods   ###
    42 // ##########################
     36#include "core/Game.h"
     37#include "core/input/InputBuffer.h"
     38
    4339namespace orxonox
    4440{
    4541    IOConsole* IOConsole::singletonPtr_s = NULL;
    4642
    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 {
    82     namespace EscapeMode
    83     {
    84         enum Value
    85         {
    86             None,
    87             First,
    88             Second
    89         };
    90     }
    91 
    92     IOConsole::IOConsole()
    93         : shell_(new Shell("IOConsole", false))
    94         , buffer_(shell_->getInputBuffer())
    95         , cout_(std::cout.rdbuf())
    96         , promptString_("orxonox # ")
    97         , bStatusPrinted_(false)
    98         , originalTerminalSettings_(0)
    99     {
    100         this->setTerminalMode();
    101         this->shell_->registerListener(this);
    102 
    103         // Manually set the widths of the individual status lines
    104         this->statusLineWidths_.push_back(29);
    105         this->statusLineMaxWidth_ = 29;
    106 
    107         this->getTerminalSize();
    108         this->lastTerminalWidth_ = this->terminalWidth_;
    109         this->lastTerminalHeight_ = this->terminalHeight_;
    110 
    111         // Disable standard std::cout logging
    112         OutputHandler::getInstance().disableCout();
    113         // Redirect std::cout to an ostringstream
    114         // (Other part is in the initialiser list)
    115         std::cout.rdbuf(this->origCout_.rdbuf());
    116 
    117         // Make sure we make way for the status lines
    118         this->preUpdate(Game::getInstance().getGameClock());
    119     }
    120 
    121     IOConsole::~IOConsole()
    122     {
    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);
    127         // Erase input and status lines
    128         this->cout_ << "\033[1G\033[J";
    129         // Move cursor to the bottom
    130         this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B';
    131         // Scroll terminal to compensate for erased lines
    132         this->cout_ << "\033[" << this->statusLineWidths_.size() << 'T';
    133 
    134         resetTerminalMode();
    135         this->shell_->destroy();
    136 
    137         // Restore this->cout_ redirection
    138         std::cout.rdbuf(this->cout_.rdbuf());
    139         // Enable standard std::cout logging again
    140         OutputHandler::getInstance().enableCout();
    141     }
    142 
    143     void IOConsole::preUpdate(const Clock& time)
    144     {
    145         unsigned char c;
    146         std::string escapeSequence;
    147         EscapeMode::Value escapeMode = EscapeMode::None;
    148         while (std::cin.good())
    149         {
    150             c = std::cin.get();
    151             if (!std::cin.good())
    152                 break;
    153 
    154             if (escapeMode == EscapeMode::First && (c == '[' || c=='O') )
    155                 escapeMode = EscapeMode::Second;
    156             // Get Alt+Tab combination when switching applications
    157             else if (escapeMode == EscapeMode::First && c == '\t')
    158             {
    159                 this->buffer_->buttonPressed(KeyEvent(KeyCode::Tab, '\t', KeyboardModifier::Alt));
    160                 escapeMode = EscapeMode::None;
    161             }
    162             else if (escapeMode == EscapeMode::Second)
    163             {
    164                 escapeSequence += c;
    165                 escapeMode = EscapeMode::None;
    166                 if      (escapeSequence == "A")
    167                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Up,       0, 0));
    168                 else if (escapeSequence == "B")
    169                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Down,     0, 0));
    170                 else if (escapeSequence == "C")
    171                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Right,    0, 0));
    172                 else if (escapeSequence == "D")
    173                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Left,     0, 0));
    174                 else if (escapeSequence == "1~" || escapeSequence == "H")
    175                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Home,     0, 0));
    176                 else if (escapeSequence == "2~")
    177                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert,   0, 0));
    178                 else if (escapeSequence == "3~")
    179                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete,   0, 0));
    180                 else if (escapeSequence == "4~" || escapeSequence == "F")
    181                     this->buffer_->buttonPressed(KeyEvent(KeyCode::End,      0, 0));
    182                 else if (escapeSequence == "5~")
    183                     this->buffer_->buttonPressed(KeyEvent(KeyCode::PageUp,   0, 0));
    184                 else if (escapeSequence == "6~")
    185                     this->buffer_->buttonPressed(KeyEvent(KeyCode::PageDown, 0, 0));
    186                 else
    187                     // Waiting for sequence to complete
    188                     // If the user presses ESC and then '[' or 'O' while the loop is not
    189                     // running (for instance while loading), the whole sequence gets dropped
    190                     escapeMode = EscapeMode::Second;
    191             }
    192             else // not in an escape sequence OR user might have pressed just ESC
    193             {
    194                 if (escapeMode == EscapeMode::First)
    195                 {
    196                     this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, c, 0));
    197                     escapeMode = EscapeMode::None;
    198                 }
    199                 if (c == '\033')
    200                 {
    201                     escapeMode = EscapeMode::First;
    202                     escapeSequence.clear();
    203                 }
    204                 else
    205                 {
    206                     KeyCode::ByEnum code;
    207                     switch (c)
    208                     {
    209                     case '\n'  : case '\r': code = KeyCode::Return; break;
    210                     case '\177': case '\b': code = KeyCode::Back;   break;
    211                     case '\t'             : code = KeyCode::Tab;    break;
    212                     default:
    213                         // We don't encode the key code (would be a very large switch)
    214                         // because the InputBuffer will only insert the text anyway
    215                         // Replacement character is simply KeyCode::A
    216                         code = KeyCode::A;
    217                     }
    218                     this->buffer_->buttonPressed(KeyEvent(code, c, 0));
    219                 }
    220             }
    221         }
    222         // Reset error flags in std::cin
    223         std::cin.clear();
    224 
    225         // If there is still an escape key pending (escape key ONLY), then
    226         // it sure isn't an escape sequence anymore
    227         if (escapeMode == EscapeMode::First)
    228             this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, '\033', 0));
    229 
    230         // Determine terminal width and height
    231         this->lastTerminalWidth_ = this->terminalWidth_;
    232         this->lastTerminalHeight_ = this->terminalHeight_;
    233         this->getTerminalSize();
    234 
    235         int heightDiff = this->terminalHeight_ - this->lastTerminalHeight_;
    236         if (this->bStatusPrinted_ && heightDiff < 0)
    237         {
    238             // Terminal width has shrunk. The cursor will still be on the input line,
    239             // but that line might very well be the last
    240             int newLines = std::min((int)this->statusLineWidths_.size(), -heightDiff);
    241             // Scroll terminal to create new lines
    242             this->cout_ << "\033[" << newLines << 'S';
    243         }
    244 
    245         if (!this->bStatusPrinted_ && this->willPrintStatusLines())
    246         {
    247             // Scroll console to make way for status lines
    248             this->cout_ << "\033[" << this->statusLineWidths_.size() << 'S';
    249             this->bStatusPrinted_ = true;
    250         }
    251 
    252         // We always assume that the cursor is on the input line.
    253         // But we cannot always be sure about that, esp. if we scroll the console
    254         this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B';
    255         this->cout_ << "\033[" << this->statusLineWidths_.size() << 'A';
    256 
    257         // Erase status and input lines
    258         this->cout_ << "\033[1G\033[J";
    259         this->printInputLine();
    260         this->printStatusLines();
    261         this->cout_.flush();
    262 
    263         // Process output written to std::cout
    264         std::cout.flush();
    265         if (!this->origCout_.str().empty())
    266         {
    267             this->shell_->addOutput(this->origCout_.str(), Shell::None);
    268             this->origCout_.str("");
    269         }
    270     }
    271 
    272     void IOConsole::printOutputLine(const std::string& text, Shell::LineType type)
    273     {
    274 /*
    275         // Colour line
    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;
    285         default: break;
    286         }
    287 */
    288 
    289         // Print output line
    290         this->cout_ << text;
    291 
    292         // Reset colour to white
    293 //        this->cout_ << "\033[37m";
    294     }
    295 
    296     void IOConsole::printInputLine()
    297     {
    298         // Set cursor to the beginning of the line and erase the line
    299         this->cout_ << "\033[1G\033[K";
    300         // Indicate a command prompt
    301         this->cout_ << this->promptString_;
    302         // Save cursor position
    303         this->cout_ << "\033[s";
    304         // Print command line buffer
    305         this->cout_ << this->shell_->getInput();
    306         // Restore cursor position and move it to the right
    307         this->cout_ << "\033[u";
    308         if (this->buffer_->getCursorPosition() > 0)
    309             this->cout_ << "\033[" << this->buffer_->getCursorPosition() << 'C';
    310     }
    311 
    312     void IOConsole::printStatusLines()
    313     {
    314         if (this->willPrintStatusLines())
    315         {
    316             // Save cursor position
    317             this->cout_ << "\033[s";
    318             // Move cursor down (don't create a new line here because the buffer might flush then!)
    319             this->cout_ << "\033[1B\033[1G";
    320             this->cout_ << std::fixed << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgFPS() << " fps, ";
    321             this->cout_ <<               std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgTickTime() << " ms tick time";
    322             // Restore cursor position
    323             this->cout_ << "\033[u";
    324             this->bStatusPrinted_ = true;
    325         }
    326         else
    327             this->bStatusPrinted_ = false;
    328     }
    329 
    330     void IOConsole::setTerminalMode()
    331     {
    332         termios new_settings;
    333         this->originalTerminalSettings_ = new termios();
    334 
    335         tcgetattr(0, this->originalTerminalSettings_);
    336         new_settings = *this->originalTerminalSettings_;
    337         new_settings.c_lflag &= ~(ICANON | ECHO);
    338         //new_settings.c_lflag |= (ISIG | IEXTEN);
    339         new_settings.c_cc[VTIME] = 0;
    340         new_settings.c_cc[VMIN]  = 0;
    341         tcsetattr(0, TCSANOW, &new_settings);
    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         }
    353     }
    354 
    355     void IOConsole::getTerminalSize()
    356     {
    357 #ifdef TIOCGSIZE
    358         struct ttysize win;
    359         if (!ioctl(STDIN_FILENO, TIOCGSIZE, &win))
    360         {
    361             this->terminalWidth_  = win.ts_cols;
    362             this->terminalHeight_ = win.ts_lines;
    363             return;
    364         }
    365 #elif defined TIOCGWINSZ
    366         struct winsize win;
    367         if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &win))
    368         {
    369             this->terminalWidth_  = win.ws_col;
    370             this->terminalHeight_ = win.ws_row;
    371             return;
    372         }
    373 #else
    374         const char* s = getenv("COLUMNS");
    375         this->terminalWidth_  = s ? strtol(s, NULL, 10) : 80;
    376         s = getenv("LINES");
    377         this->terminalHeight_ = s ? strtol(s, NULL, 10) : 24;
    378         return;
    379 #endif
    380         this->terminalWidth_  = 80;
    381         this->terminalHeight_ = 24;
    382     }
    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 
    391     // ###############################
    392     // ###  ShellListener methods  ###
    393     // ###############################
    394 
    395     //! Called if only the last output-line has changed
    396     void IOConsole::onlyLastLineChanged()
    397     {
    398         // Save cursor position and move it to the beginning of the first output line
    399         this->cout_ << "\033[s\033[1A\033[1G";
    400         // Erase the line
    401         this->cout_ << "\033[K";
    402         // Reprint the last output line
    403         this->printOutputLine(this->shell_->getNewestLineIterator()->first, this->shell_->getNewestLineIterator()->second);
    404         // Restore cursor
    405         this->cout_ << "\033[u";
    406         this->cout_.flush();
    407     }
    408 
    409     //! Called if a new output-line was added
    410     void IOConsole::lineAdded()
    411     {
    412         int newLines = this->shell_->getNewestLineIterator()->first.size() / this->terminalWidth_ + 1;
    413         // Create new lines by scrolling the screen
    414         this->cout_ << "\033[" << newLines << 'S';
    415         // Move cursor to the beginning of the new (last) output line
    416         this->cout_ << "\033[" << newLines << "A\033[1G";
    417         // Erase screen from here
    418         this->cout_ << "\033[J";
    419         // Print the new output lines
    420         for (int i = 0; i < newLines; ++i)
    421         {
    422             Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator();
    423             this->printOutputLine(it->first.substr(i*this->terminalWidth_, this->terminalWidth_), it->second);
    424         }
    425         // Move cursor down
    426         this->cout_ << "\033[1B\033[1G";
    427         // Print status and input lines
    428         this->printInputLine();
    429         this->printStatusLines();
    430         this->cout_.flush();
    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     }
    446 }
    447 
    448 #elif defined(ORXONOX_PLATFORM_WINDOWS)
    449 // ##################################
    450 // ###   Windows Implementation   ###
    451 // ##################################
    452 
    453 #include <windows.h>
    454 
    455 namespace orxonox
    456 {
    45743    //! Redirects std::cout, creates the corresponding Shell and changes the terminal mode
    45844    IOConsole::IOConsole()
     
    720306    // ###############################
    721307
     308    //! Called if all output-lines have to be reprinted
     309    void IOConsole::linesChanged()
     310    {
     311        // Method only gets called upon start to draw all the lines
     312        // or when scrolling. But scrolling is disabled and the output
     313        // is already in std::cout when we start the IOConsole
     314    }
     315
     316    //! Called if a command is about to be executed
     317    void IOConsole::executed()
     318    {
     319        this->shell_->addOutput(this->promptString_ + this->shell_->getInput() + '\n', Shell::Command);
     320    }
     321
     322    //! Called if the console gets closed
     323    void IOConsole::exit()
     324    {
     325        // Exit is not an option, just do nothing (Shell doesn't really exit too)
     326    }
     327
    722328    //! Called if the text in the input line has changed
    723329    void IOConsole::inputChanged()
     
    796402    }
    797403}
    798 
    799 #endif /* ORXONOX_PLATFORM_UNIX */
  • code/trunk/src/libraries/core/command/IOConsoleWindows.h

    r7265 r7287  
    2121 *
    2222 *   Author:
    23  *      Oliver Scheuss
    2423 *      Reto Grieder
    2524 *   Co-authors:
     
    3130#define _IOConsole_H__
    3231
    33 #include "CorePrereqs.h"
     32#include "core/CorePrereqs.h"
    3433
    3534#include <sstream>
     
    3938#include "Shell.h"
    4039
    41 #ifdef ORXONOX_PLATFORM_UNIX
    42 struct termios;
    43 #elif defined(ORXONOX_PLATFORM_WINDOWS)
    4440#define WIN32_LEAN_AND_MEAN
    4541#ifndef NOMINMAX
     
    4743#endif
    4844#include <windows.h>
    49 #endif
    5045
    5146namespace orxonox
     
    7671        void exit();
    7772
    78         Shell*                  shell_;
    79         InputBuffer*            buffer_;
    80         std::ostream            cout_;
    81         std::ostringstream      origCout_;
    82         int                     terminalWidth_;
    83         int                     terminalHeight_;
    84         int                     lastTerminalWidth_;
    85         int                     lastTerminalHeight_;
    86         const std::string       promptString_;
    87 
    88 #ifdef ORXONOX_PLATFORM_UNIX
    89         bool willPrintStatusLines();
    90         void printInputLine();
    91         void printOutputLine(const std::string& line, Shell::LineType type);
    92         static void resetTerminalMode();
    93 
    94         bool                    bPrintStatusLine_;
    95         bool                    bStatusPrinted_;
    96         std::vector<int>        statusLineWidths_;
    97         int                     statusLineMaxWidth_;
    98         static const int        minOutputLines_ = 3;
    99         termios*                originalTerminalSettings_;
    100 
    101 #elif defined(ORXONOX_PLATFORM_WINDOWS)
    10273        void resetTerminalMode();
    10374        void moveCursor(int dx, int dy);
     
    11283        }
    11384
     85        Shell*                  shell_;
     86        InputBuffer*            buffer_;
     87        std::ostream            cout_;
     88        std::ostringstream      origCout_;
     89        int                     terminalWidth_;
     90        int                     terminalHeight_;
     91        int                     lastTerminalWidth_;
     92        int                     lastTerminalHeight_;
     93        const std::string       promptString_;
     94
    11495        DWORD                   originalTerminalSettings_;
    11596        HANDLE                  stdInHandle_;
     
    120101        int                     lastOutputLineHeight_;
    121102        uint64_t                lastRefreshTime_;
    122 #endif
    123103
    124104        static IOConsole* singletonPtr_s;
Note: See TracChangeset for help on using the changeset viewer.