Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

Ignore:
Timestamp:
Dec 25, 2009, 10:23:58 PM (15 years ago)
Author:
rgrieder
Message:

Merged presentation2 branch back to trunk.
Major new features:

  • Actual GUI with settings, etc.
  • Improved space ship steering (human interaction)
  • Rocket fire and more particle effects
  • Advanced sound framework
Location:
code/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • code/trunk

  • code/trunk/src/libraries/core/IOConsole.cc

    r6105 r6417  
    3232#include <iomanip>
    3333#include <iostream>
     34
     35#include "util/Clock.h"
     36#include "util/Math.h"
    3437#include "core/Game.h"
    3538#include "core/input/InputBuffer.h"
     
    4245    IOConsole* IOConsole::singletonPtr_s = NULL;
    4346
    44     int IOConsole::extractLogLevel(std::string* text)
    45     {
    46         // Handle line colouring by inspecting the first letter
    47         char level = 0;
    48         if (!text->empty())
    49         {
    50             level = (*text)[0];
    51             if (level == -1 || level >= 1 && level <= 6)
    52             {
    53                 *text = text->substr(1);
    54                 if (level != -1)
    55                     return level;
    56             }
    57         }
    58         return 0;
    59     }
    60 
    61     inline bool IOConsole::willPrintStatusLines()
    62     {
    63         return !this->statusLineWidths_.empty()
    64              && this->terminalWidth_  >= this->statusLineMaxWidth_
    65              && this->terminalHeight_ >= (this->minOutputLines_ + this->statusLineWidths_.size());
    66     }
    67 
    6847    // ###############################
    6948    // ###  ShellListener methods  ###
     
    7857    }
    7958
    80     //! Called if the text in the input-line has changed
    81     void IOConsole::inputChanged()
    82     {
    83         this->printInputLine();
    84         this->cout_.flush();
    85     }
    86 
    87     //! Called if the position of the cursor in the input-line has changed
    88     void IOConsole::cursorChanged()
    89     {
    90         this->printInputLine();
    91         this->cout_.flush();
    92     }
    93 
    9459    //! Called if a command is about to be executed
    9560    void IOConsole::executed()
    9661    {
    97         this->shell_->addOutputLine(this->promptString_ + this->shell_->getInput());
     62        this->shell_->addOutput(this->promptString_ + this->shell_->getInput() + '\n', Shell::Command);
    9863    }
    9964
     
    12691
    12792    IOConsole::IOConsole()
    128         : shell_(new Shell("IOConsole", false, true))
     93        : shell_(new Shell("IOConsole", false))
    12994        , buffer_(shell_->getInputBuffer())
    13095        , cout_(std::cout.rdbuf())
     96        , promptString_("orxonox # ")
    13197        , bStatusPrinted_(false)
    132         , promptString_("orxonox # ")
    133         , originalTerminalSettings_(new termios())
     98        , originalTerminalSettings_(0)
    13499    {
    135100        this->setTerminalMode();
     
    151116
    152117        // Make sure we make way for the status lines
    153         this->update(Game::getInstance().getGameClock());
     118        this->preUpdate(Game::getInstance().getGameClock());
    154119    }
    155120
    156121    IOConsole::~IOConsole()
    157122    {
    158         // Empty all buffers
    159         this->update(Game::getInstance().getGameClock());
     123        // Process output written to std::cout in the meantime
     124        std::cout.flush();
     125        if (!this->origCout_.str().empty())
     126            this->shell_->addOutput(this->origCout_.str(), Shell::None);
    160127        // Erase input and status lines
    161128        this->cout_ << "\033[1G\033[J";
     
    166133
    167134        resetTerminalMode();
    168         delete this->originalTerminalSettings_;
    169135        this->shell_->destroy();
    170136
     
    175141    }
    176142
    177     void IOConsole::update(const Clock& time)
     143    void IOConsole::preUpdate(const Clock& time)
    178144    {
    179145        unsigned char c;
     
    284250        }
    285251
    286         // We always assume that the cursor is on the inputline.
     252        // We always assume that the cursor is on the input line.
    287253        // But we cannot always be sure about that, esp. if we scroll the console
    288254        this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B';
     
    296262
    297263        // Process output written to std::cout
     264        std::cout.flush();
    298265        if (!this->origCout_.str().empty())
    299266        {
    300             this->shell_->addOutputLine(this->origCout_.str());
     267            this->shell_->addOutput(this->origCout_.str(), Shell::None);
    301268            this->origCout_.str("");
    302269        }
    303270    }
    304271
    305     void IOConsole::printLogText(const std::string& text)
    306     {
    307         std::string output = text;
    308         /*int level =*/ this->extractLogLevel(&output);
    309 
     272    void IOConsole::printOutputLine(const std::string& text, Shell::LineType type)
     273    {
    310274/*
    311275        // Colour line
    312         switch (level)
    313         {
    314         case -1: this->cout_ << "\033[37m"; break;
    315         case  1: this->cout_ << "\033[91m"; break;
    316         case  2: this->cout_ << "\033[31m"; break;
    317         case  3: this->cout_ << "\033[34m"; break;
    318         case  4: this->cout_ << "\033[36m"; break;
    319         case  5: this->cout_ << "\033[35m"; break;
    320         case  6: this->cout_ << "\033[37m"; break;
     276        switch (type)
     277        {
     278        case Shell::None:    this->cout_ << "\033[37m"; break;
     279        case Shell::Error:  this->cout_ << "\033[91m"; break;
     280        case Shell::Warning: this->cout_ << "\033[31m"; break;
     281        case Shell::Info:    this->cout_ << "\033[34m"; break;
     282        case Shell::Debug:  this->cout_ << "\033[36m"; break;
     283        case Shell::Verbose: this->cout_ << "\033[35m"; break;
     284        case Shell::Ultra:  this->cout_ << "\033[37m"; break;
    321285        default: break;
    322286        }
     
    324288
    325289        // Print output line
    326         this->cout_ << output;
     290        this->cout_ << text;
    327291
    328292        // Reset colour to white
     
    343307        this->cout_ << "\033[u";
    344308        if (this->buffer_->getCursorPosition() > 0)
    345             this->cout_ << "\033[" << this->buffer_->getCursorPosition() << "C";
     309            this->cout_ << "\033[" << this->buffer_->getCursorPosition() << 'C';
    346310    }
    347311
     
    367331    {
    368332        termios new_settings;
     333        this->originalTerminalSettings_ = new termios();
    369334
    370335        tcgetattr(0, this->originalTerminalSettings_);
     
    375340        new_settings.c_cc[VMIN]  = 0;
    376341        tcsetattr(0, TCSANOW, &new_settings);
    377     }
    378 
    379     void IOConsole::resetTerminalMode()
    380     {
    381         tcsetattr(0, TCSANOW, IOConsole::originalTerminalSettings_);
     342        atexit(&IOConsole::resetTerminalMode);
     343    }
     344
     345    /*static*/ void IOConsole::resetTerminalMode()
     346    {
     347        if(IOConsole::singletonPtr_s && IOConsole::singletonPtr_s->originalTerminalSettings_)
     348        {
     349            tcsetattr(0, TCSANOW, IOConsole::singletonPtr_s->originalTerminalSettings_);
     350            delete IOConsole::singletonPtr_s->originalTerminalSettings_;
     351            IOConsole::singletonPtr_s->originalTerminalSettings_ = 0;
     352        }
    382353    }
    383354
     
    411382    }
    412383
     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
    413391    // ###############################
    414392    // ###  ShellListener methods  ###
     
    423401        this->cout_ << "\033[K";
    424402        // Reprint the last output line
    425         this->printLogText(*(this->shell_->getNewestLineIterator()));
     403        this->printOutputLine(this->shell_->getNewestLineIterator()->first, this->shell_->getNewestLineIterator()->second);
    426404        // Restore cursor
    427405        this->cout_ << "\033[u";
     
    432410    void IOConsole::lineAdded()
    433411    {
    434         int newLines = this->shell_->getNewestLineIterator()->size() / this->terminalWidth_ + 1;
     412        int newLines = this->shell_->getNewestLineIterator()->first.size() / this->terminalWidth_ + 1;
    435413        // Create new lines by scrolling the screen
    436414        this->cout_ << "\033[" << newLines << 'S';
     
    441419        // Print the new output lines
    442420        for (int i = 0; i < newLines; ++i)
    443             this->printLogText(this->shell_->getNewestLineIterator()->substr(i*this->terminalWidth_, this->terminalWidth_));
     421        {
     422            Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator();
     423            this->printOutputLine(it->first.substr(i*this->terminalWidth_, this->terminalWidth_), it->second);
     424        }
    444425        // Move cursor down
    445426        this->cout_ << "\033[1B\033[1G";
     
    449430        this->cout_.flush();
    450431    }
     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    }
    451446}
    452447
     
    456451// ##################################
    457452
     453#include <windows.h>
     454
    458455namespace orxonox
    459456{
     457    //! Redirects std::cout, creates the corresponding Shell and changes the terminal mode
    460458    IOConsole::IOConsole()
    461         : shell_(new Shell("IOConsole", false, true))
     459        : shell_(new Shell("IOConsole", false))
    462460        , buffer_(shell_->getInputBuffer())
    463461        , cout_(std::cout.rdbuf())
    464         , bStatusPrinted_(false)
    465462        , promptString_("orxonox # ")
    466     {
     463        , inputLineHeight_(1)
     464        , statusLines_(1)
     465        , lastOutputLineHeight_(0)
     466    {
     467        // Disable standard this->cout_ logging
     468        OutputHandler::getInstance().disableCout();
     469        // Redirect std::cout to an ostringstream
     470        // (Other part is in the initialiser list)
     471        std::cout.rdbuf(this->origCout_.rdbuf());
     472
     473        this->setTerminalMode();
     474        CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
     475        GetConsoleScreenBufferInfo(this->stdOutHandle_, &screenBufferInfo);
     476        this->terminalWidth_  = screenBufferInfo.dwSize.X;
     477        this->terminalHeight_ = screenBufferInfo.dwSize.Y;
     478        // Determines where we are in respect to output already written with std::cout
     479        this->inputLineRow_ = screenBufferInfo.dwCursorPosition.Y;
    467480/*
    468         this->setTerminalMode();
     481        this->lastTerminalWidth_  = this->terminalWidth_;
     482        this->lastTerminalHeight_ = this->terminalHeight_;
     483*/
     484
     485        // Cursor already at the end of the screen buffer?
     486        // (assuming the current input line height is 1)
     487        if (this->inputLineRow_ >= this->terminalHeight_ - this->statusLines_)
     488            SetConsoleCursorPosition(this->stdOutHandle_, makeCOORD(0, this->terminalHeight_ - this->statusLines_));
     489
     490        // Prevent input line from overflowing
     491        int maxInputLength = (this->terminalHeight_ - this->statusLines_) * this->terminalWidth_ - 1 - this->promptString_.size();
     492        // Consider that the echo of a command might include the command plus some other characters (assumed max 80)
     493        // Also put a minimum so the config file parser is not overwhelmed with the command history
     494        this->buffer_->setMaxLength(std::min(8192, (maxInputLength - 80) / 2));
     495
     496        // Print input and status line and position cursor
     497        this->inputChanged();
     498        this->cursorChanged();
     499        this->lastRefreshTime_ = Game::getInstance().getGameClock().getRealMicroseconds();
     500        this->preUpdate(Game::getInstance().getGameClock());
     501
    469502        this->shell_->registerListener(this);
    470 
    471         // Manually set the widths of the individual status lines
    472         this->statusLineWidths_.push_back(29);
    473         this->statusLineMaxWidth_ = 29;
    474 
    475         this->getTerminalSize();
     503    }
     504
     505    //! Resets std::cout redirection and restores the terminal mode
     506    IOConsole::~IOConsole()
     507    {
     508        // Process output written to std::cout in the meantime
     509        std::cout.flush();
     510        if (!this->origCout_.str().empty())
     511            this->shell_->addOutput(this->origCout_.str(), Shell::None);
     512
     513        this->shell_->unregisterListener(this);
     514
     515        // Erase input and status lines
     516        COORD pos = {0, this->inputLineRow_};
     517        this->writeText(std::string((this->inputLineHeight_ + this->statusLines_) * this->terminalWidth_, ' '), pos);
     518        // Move cursor to the beginning of the line
     519        SetConsoleCursorPosition(stdOutHandle_, pos);
     520
     521        // Restore this->cout_ redirection
     522        std::cout.rdbuf(this->cout_.rdbuf());
     523        // Enable standard this->cout_ logging again
     524        OutputHandler::getInstance().enableCout();
     525
     526        resetTerminalMode();
     527        this->shell_->destroy();
     528    }
     529
     530    //! Processes the pending input key strokes, refreshes the status lines and handles std::cout (redirected)
     531    void IOConsole::preUpdate(const Clock& time)
     532    {
     533        // Process input
     534        while (true)
     535        {
     536            DWORD count;
     537            INPUT_RECORD inrec;
     538            PeekConsoleInput(this->stdInHandle_, &inrec, 1, &count);
     539            if (count == 0)
     540                break;
     541            ReadConsoleInput(this->stdInHandle_, &inrec, 1, &count);
     542            if (inrec.EventType == KEY_EVENT && inrec.Event.KeyEvent.bKeyDown)
     543            {
     544                // Process keyboard modifiers (Ctrl, Alt and Shift)
     545                DWORD modifiersIn = inrec.Event.KeyEvent.dwControlKeyState;
     546                int modifiersOut = 0;
     547                if ((modifiersIn & (LEFT_ALT_PRESSED  | RIGHT_ALT_PRESSED))  != 0)
     548                    modifiersOut |= KeyboardModifier::Alt;
     549                if ((modifiersIn & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0)
     550                    modifiersOut |= KeyboardModifier::Ctrl;
     551                if ((modifiersIn & SHIFT_PRESSED) != 0)
     552                    modifiersOut |= KeyboardModifier::Shift;
     553
     554                // ASCII character (0 for special keys)
     555                char asciiChar = inrec.Event.KeyEvent.uChar.AsciiChar;
     556
     557                // Process special keys and if not found, use Key::A as dummy (InputBuffer uses the ASCII text anyway)
     558                switch (inrec.Event.KeyEvent.wVirtualKeyCode)
     559                {
     560                case VK_BACK:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Back,     asciiChar, modifiersOut)); break;
     561                case VK_TAB:    this->buffer_->buttonPressed(KeyEvent(KeyCode::Tab,      asciiChar, modifiersOut)); break;
     562                case VK_RETURN: this->buffer_->buttonPressed(KeyEvent(KeyCode::Return,   asciiChar, modifiersOut)); break;
     563                case VK_PAUSE:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Pause,    asciiChar, modifiersOut)); break;
     564                case VK_ESCAPE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape,   asciiChar, modifiersOut)); break;
     565                case VK_SPACE:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Space,    asciiChar, modifiersOut)); break;
     566                case VK_PRIOR:  this->buffer_->buttonPressed(KeyEvent(KeyCode::PageUp,   asciiChar, modifiersOut)); break;
     567                case VK_NEXT:   this->buffer_->buttonPressed(KeyEvent(KeyCode::PageDown, asciiChar, modifiersOut)); break;
     568                case VK_END:    this->buffer_->buttonPressed(KeyEvent(KeyCode::End,      asciiChar, modifiersOut)); break;
     569                case VK_HOME:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Home,     asciiChar, modifiersOut)); break;
     570                case VK_LEFT:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Left,     asciiChar, modifiersOut)); break;
     571                case VK_UP:     this->buffer_->buttonPressed(KeyEvent(KeyCode::Up,       asciiChar, modifiersOut)); break;
     572                case VK_RIGHT:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Right,    asciiChar, modifiersOut)); break;
     573                case VK_DOWN:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Down,     asciiChar, modifiersOut)); break;
     574                case VK_INSERT: this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert,   asciiChar, modifiersOut)); break;
     575                case VK_DELETE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete,   asciiChar, modifiersOut)); break;
     576                default:        this->buffer_->buttonPressed(KeyEvent(KeyCode::A,        asciiChar, modifiersOut));
     577                }
     578            }
     579        }
     580
     581        // TODO: Respect screen buffer size changes
     582/*
     583        // The user can manually adjust the screen buffer size on Windows
     584        // And we don't want to screw the console because of that
    476585        this->lastTerminalWidth_ = this->terminalWidth_;
    477586        this->lastTerminalHeight_ = this->terminalHeight_;
    478 
    479         // Disable standard this->cout_ logging
    480         OutputHandler::getInstance().disableCout();
     587        this->getTerminalSize(); // Also sets this->inputLineRow_ according to the cursor position
     588        // Is there still enough space below the cursor for the status line(s)?
     589        if (this->inputLineRow_ >= this->terminalHeight_ - this->statusLines_)
     590            this->moveCursor(0, -this->inputLineRow_ + this->terminalHeight_ - this->statusLines_ - 1);
    481591*/
    482     }
    483 
    484     IOConsole::~IOConsole()
    485     {
    486 /*
    487         resetTerminalMode();
    488         this->shell_->destroy();
    489 
    490         // Enable standard this->cout_ logging again
    491         OutputHandler::getInstance().enableCout();
    492 */
    493     }
    494 
    495     void IOConsole::update(const Clock& time)
    496     {
    497 /*
    498         unsigned char c = 0;
    499         while (std::cin.good())
    500         {
    501             c = std::cin.get();
    502             if (std::cin.bad())
    503                 break;
    504         }
    505         // Reset error flags in std::cin
    506         std::cin.clear();
    507 
    508         // Determine terminal width and height
    509         this->lastTerminalWidth_ = this->terminalWidth_;
    510         this->lastTerminalHeight_ = this->terminalHeight_;
    511         this->getTerminalSize();
    512 */
    513     }
    514 
    515     void IOConsole::printLogText(const std::string& text)
    516     {
    517     }
    518 
    519     void IOConsole::printInputLine()
    520     {
    521     }
    522 
     592
     593        // Refresh status line 5 times per second
     594        if (time.getMicroseconds() > this->lastRefreshTime_ + 1000000)
     595        {
     596            this->printStatusLines();
     597            this->lastRefreshTime_ = time.getMicroseconds();
     598        }
     599
     600        // Process output written to std::cout
     601        std::cout.flush();
     602        if (!this->origCout_.str().empty())
     603        {
     604            this->shell_->addOutput(this->origCout_.str(), Shell::None);
     605            this->origCout_.str("");
     606        }
     607    }
     608
     609    //! Prints output text. Similar to writeText, but sets the colour according to the output level
     610    void IOConsole::printOutputLine(const std::string& text, Shell::LineType type, const COORD& pos)
     611    {
     612        // Colour line
     613        WORD colour = 0;
     614        switch (type)
     615        {
     616        case Shell::Error:   colour = FOREGROUND_INTENSITY                    | FOREGROUND_RED; break;
     617        case Shell::Warning: colour = FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED; break;
     618        case Shell::Info:
     619        case Shell::Debug:
     620        case Shell::Verbose:
     621        case Shell::Ultra:   colour = FOREGROUND_INTENSITY                                     ; break;
     622        case Shell::Command: colour =                        FOREGROUND_GREEN                  | FOREGROUND_BLUE; break;
     623        case Shell::Hint:    colour =                        FOREGROUND_GREEN | FOREGROUND_RED                  ; break;
     624        default:             colour =                        FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE; break;
     625        }
     626
     627        // Print output line
     628        this->writeText(text, pos, colour);
     629    }
     630
     631    //! Prints all status lines with current content
    523632    void IOConsole::printStatusLines()
    524633    {
    525 /*
    526         if (this->willPrintStatusLines())
    527         {
    528             this->bStatusPrinted_ = true;
    529         }
    530         else
    531             this->bStatusPrinted_ = false;
    532 */
    533     }
    534 
     634        // Prepare text to be written
     635        std::ostringstream oss;
     636        oss << std::fixed << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgFPS() << " fps, ";
     637        oss <<               std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgTickTime() << " ms tick time";
     638        // Clear rest of the line by inserting spaces
     639        oss << std::string(this->terminalWidth_ - oss.str().size(), ' ');
     640        this->writeText(oss.str(), makeCOORD(0, this->inputLineRow_ + this->inputLineHeight_), FOREGROUND_GREEN);
     641    }
     642
     643    //! Changes the console parameters for unbuffered input
    535644    void IOConsole::setTerminalMode()
    536645    {
    537     }
    538 
     646        // Set the console mode to no-echo, raw input, and no window or mouse events
     647        this->stdOutHandle_ = GetStdHandle(STD_OUTPUT_HANDLE);
     648        this->stdInHandle_  = GetStdHandle(STD_INPUT_HANDLE);
     649        if (this->stdInHandle_ == INVALID_HANDLE_VALUE
     650            || !GetConsoleMode(this->stdInHandle_, &this->originalTerminalSettings_)
     651            || !SetConsoleMode(this->stdInHandle_, 0))
     652        {
     653            COUT(1) << "Error: Could not set Windows console settings" << std::endl;
     654            return;
     655        }
     656        FlushConsoleInputBuffer(this->stdInHandle_);
     657    }
     658
     659    //! Restores the console parameters
    539660    void IOConsole::resetTerminalMode()
    540661    {
    541     }
    542 
     662        SetConsoleMode(this->stdInHandle_, this->originalTerminalSettings_);
     663    }
     664
     665    //! Sets this->terminalWidth_ and this->terminalHeight_
    543666    void IOConsole::getTerminalSize()
    544667    {
     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        }
    545716    }
    546717
     
    549720    // ###############################
    550721
     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
    551772    //! Called if only the last output-line has changed
    552773    void IOConsole::onlyLastLineChanged()
    553774    {
    554     }
    555 
    556     //! Called if a new output-line was added
     775        int newLineHeight = 1 + this->shell_->getNewestLineIterator()->first.size() / this->terminalWidth_;
     776        // Compute the number of new lines needed
     777        int newLines = newLineHeight - this->lastOutputLineHeight_;
     778        this->lastOutputLineHeight_ = newLineHeight;
     779        // Scroll console if necessary
     780        if (newLines > 0) // newLines < 0 is assumed impossible
     781            this->createNewOutputLines(newLines);
     782        Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator();
     783        this->printOutputLine(it->first, it->second, makeCOORD(0, this->inputLineRow_ - newLineHeight));
     784    }
     785
     786    //! Called if a new output line was added
    557787    void IOConsole::lineAdded()
    558788    {
     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);
    559796    }
    560797}
Note: See TracChangeset for help on using the changeset viewer.