Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Split IOConsole in two separate files for better browsing.

File:
1 copied

Legend:

Unmodified
Added
Removed
  • code/trunk/src/libraries/core/command/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 */
Note: See TracChangeset for help on using the changeset viewer.