Index: /code/trunk/src/libraries/core/command/IOConsole.cc
===================================================================
--- /code/trunk/src/libraries/core/command/IOConsole.cc	(revision 7286)
+++ /code/trunk/src/libraries/core/command/IOConsole.cc	(revision 7287)
@@ -21,5 +21,4 @@
  *
  *   Author:
- *      Oliver Scheuss
  *      Reto Grieder
  *   Co-authors:
@@ -30,770 +29,7 @@
 #include "IOConsole.h"
 
-#include <iomanip>
-#include <iostream>
-
-#include "util/Clock.h"
-#include "util/Math.h"
-#include "core/Game.h"
-#include "core/input/InputBuffer.h"
-
-// ##########################
-// ###   Mutual methods   ###
-// ##########################
-namespace orxonox
-{
-    IOConsole* IOConsole::singletonPtr_s = NULL;
-
-    // ###############################
-    // ###  ShellListener methods  ###
-    // ###############################
-
-    //! Called if all output-lines have to be reprinted
-    void IOConsole::linesChanged()
-    {
-        // Method only gets called upon start to draw all the lines
-        // or when scrolling. But scrolling is disabled and the output
-        // is already in std::cout when we start the IOConsole
-    }
-
-    //! Called if a command is about to be executed
-    void IOConsole::executed()
-    {
-        this->shell_->addOutput(this->promptString_ + this->shell_->getInput() + '\n', Shell::Command);
-    }
-
-    //! Called if the console gets closed
-    void IOConsole::exit()
-    {
-        // Exit is not an option, just do nothing (Shell doesn't really exit too)
-    }
-}
-
-#ifdef ORXONOX_PLATFORM_UNIX
-// ###############################
-// ###   Unix Implementation   ###
-// ###############################
-
-#include <termios.h>
-#include <sys/ioctl.h>
-
-namespace orxonox
-{
-    namespace EscapeMode
-    {
-        enum Value
-        {
-            None,
-            First,
-            Second
-        };
-    }
-
-    IOConsole::IOConsole()
-        : shell_(new Shell("IOConsole", false))
-        , buffer_(shell_->getInputBuffer())
-        , cout_(std::cout.rdbuf())
-        , promptString_("orxonox # ")
-        , bStatusPrinted_(false)
-        , originalTerminalSettings_(0)
-    {
-        this->setTerminalMode();
-        this->shell_->registerListener(this);
-
-        // Manually set the widths of the individual status lines
-        this->statusLineWidths_.push_back(29);
-        this->statusLineMaxWidth_ = 29;
-
-        this->getTerminalSize();
-        this->lastTerminalWidth_ = this->terminalWidth_;
-        this->lastTerminalHeight_ = this->terminalHeight_;
-
-        // Disable standard std::cout logging
-        OutputHandler::getInstance().disableCout();
-        // Redirect std::cout to an ostringstream
-        // (Other part is in the initialiser list)
-        std::cout.rdbuf(this->origCout_.rdbuf());
-
-        // Make sure we make way for the status lines
-        this->preUpdate(Game::getInstance().getGameClock());
-    }
-
-    IOConsole::~IOConsole()
-    {
-        // Process output written to std::cout in the meantime
-        std::cout.flush();
-        if (!this->origCout_.str().empty())
-            this->shell_->addOutput(this->origCout_.str(), Shell::None);
-        // Erase input and status lines
-        this->cout_ << "\033[1G\033[J";
-        // Move cursor to the bottom
-        this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B';
-        // Scroll terminal to compensate for erased lines
-        this->cout_ << "\033[" << this->statusLineWidths_.size() << 'T';
-
-        resetTerminalMode();
-        this->shell_->destroy();
-
-        // Restore this->cout_ redirection
-        std::cout.rdbuf(this->cout_.rdbuf());
-        // Enable standard std::cout logging again
-        OutputHandler::getInstance().enableCout();
-    }
-
-    void IOConsole::preUpdate(const Clock& time)
-    {
-        unsigned char c;
-        std::string escapeSequence;
-        EscapeMode::Value escapeMode = EscapeMode::None;
-        while (std::cin.good())
-        {
-            c = std::cin.get();
-            if (!std::cin.good())
-                break;
-
-            if (escapeMode == EscapeMode::First && (c == '[' || c=='O') )
-                escapeMode = EscapeMode::Second;
-            // Get Alt+Tab combination when switching applications
-            else if (escapeMode == EscapeMode::First && c == '\t')
-            {
-                this->buffer_->buttonPressed(KeyEvent(KeyCode::Tab, '\t', KeyboardModifier::Alt));
-                escapeMode = EscapeMode::None;
-            }
-            else if (escapeMode == EscapeMode::Second)
-            {
-                escapeSequence += c;
-                escapeMode = EscapeMode::None;
-                if      (escapeSequence == "A")
-                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Up,       0, 0));
-                else if (escapeSequence == "B")
-                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Down,     0, 0));
-                else if (escapeSequence == "C")
-                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Right,    0, 0));
-                else if (escapeSequence == "D")
-                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Left,     0, 0));
-                else if (escapeSequence == "1~" || escapeSequence == "H")
-                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Home,     0, 0));
-                else if (escapeSequence == "2~")
-                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert,   0, 0));
-                else if (escapeSequence == "3~")
-                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete,   0, 0));
-                else if (escapeSequence == "4~" || escapeSequence == "F")
-                    this->buffer_->buttonPressed(KeyEvent(KeyCode::End,      0, 0));
-                else if (escapeSequence == "5~")
-                    this->buffer_->buttonPressed(KeyEvent(KeyCode::PageUp,   0, 0));
-                else if (escapeSequence == "6~")
-                    this->buffer_->buttonPressed(KeyEvent(KeyCode::PageDown, 0, 0));
-                else
-                    // Waiting for sequence to complete
-                    // If the user presses ESC and then '[' or 'O' while the loop is not
-                    // running (for instance while loading), the whole sequence gets dropped
-                    escapeMode = EscapeMode::Second;
-            }
-            else // not in an escape sequence OR user might have pressed just ESC
-            {
-                if (escapeMode == EscapeMode::First)
-                {
-                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, c, 0));
-                    escapeMode = EscapeMode::None;
-                }
-                if (c == '\033')
-                {
-                    escapeMode = EscapeMode::First;
-                    escapeSequence.clear();
-                }
-                else
-                {
-                    KeyCode::ByEnum code;
-                    switch (c)
-                    {
-                    case '\n'  : case '\r': code = KeyCode::Return; break;
-                    case '\177': case '\b': code = KeyCode::Back;   break;
-                    case '\t'             : code = KeyCode::Tab;    break;
-                    default:
-                        // We don't encode the key code (would be a very large switch)
-                        // because the InputBuffer will only insert the text anyway
-                        // Replacement character is simply KeyCode::A
-                        code = KeyCode::A;
-                    }
-                    this->buffer_->buttonPressed(KeyEvent(code, c, 0));
-                }
-            }
-        }
-        // Reset error flags in std::cin
-        std::cin.clear();
-
-        // If there is still an escape key pending (escape key ONLY), then
-        // it sure isn't an escape sequence anymore
-        if (escapeMode == EscapeMode::First)
-            this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, '\033', 0));
-
-        // Determine terminal width and height
-        this->lastTerminalWidth_ = this->terminalWidth_;
-        this->lastTerminalHeight_ = this->terminalHeight_;
-        this->getTerminalSize();
-
-        int heightDiff = this->terminalHeight_ - this->lastTerminalHeight_;
-        if (this->bStatusPrinted_ && heightDiff < 0)
-        {
-            // Terminal width has shrunk. The cursor will still be on the input line,
-            // but that line might very well be the last
-            int newLines = std::min((int)this->statusLineWidths_.size(), -heightDiff);
-            // Scroll terminal to create new lines
-            this->cout_ << "\033[" << newLines << 'S';
-        }
-
-        if (!this->bStatusPrinted_ && this->willPrintStatusLines())
-        {
-            // Scroll console to make way for status lines
-            this->cout_ << "\033[" << this->statusLineWidths_.size() << 'S';
-            this->bStatusPrinted_ = true;
-        }
-
-        // We always assume that the cursor is on the input line.
-        // But we cannot always be sure about that, esp. if we scroll the console
-        this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B';
-        this->cout_ << "\033[" << this->statusLineWidths_.size() << 'A';
-
-        // Erase status and input lines
-        this->cout_ << "\033[1G\033[J";
-        this->printInputLine();
-        this->printStatusLines();
-        this->cout_.flush();
-
-        // Process output written to std::cout
-        std::cout.flush();
-        if (!this->origCout_.str().empty())
-        {
-            this->shell_->addOutput(this->origCout_.str(), Shell::None);
-            this->origCout_.str("");
-        }
-    }
-
-    void IOConsole::printOutputLine(const std::string& text, Shell::LineType type)
-    {
-/*
-        // Colour line
-        switch (type)
-        {
-        case Shell::None:    this->cout_ << "\033[37m"; break;
-        case Shell::Error:   this->cout_ << "\033[91m"; break;
-        case Shell::Warning: this->cout_ << "\033[31m"; break;
-        case Shell::Info:    this->cout_ << "\033[34m"; break;
-        case Shell::Debug:   this->cout_ << "\033[36m"; break;
-        case Shell::Verbose: this->cout_ << "\033[35m"; break;
-        case Shell::Ultra:   this->cout_ << "\033[37m"; break;
-        default: break;
-        }
-*/
-
-        // Print output line
-        this->cout_ << text;
-
-        // Reset colour to white
-//        this->cout_ << "\033[37m";
-    }
-
-    void IOConsole::printInputLine()
-    {
-        // Set cursor to the beginning of the line and erase the line
-        this->cout_ << "\033[1G\033[K";
-        // Indicate a command prompt
-        this->cout_ << this->promptString_;
-        // Save cursor position
-        this->cout_ << "\033[s";
-        // Print command line buffer
-        this->cout_ << this->shell_->getInput();
-        // Restore cursor position and move it to the right
-        this->cout_ << "\033[u";
-        if (this->buffer_->getCursorPosition() > 0)
-            this->cout_ << "\033[" << this->buffer_->getCursorPosition() << 'C';
-    }
-
-    void IOConsole::printStatusLines()
-    {
-        if (this->willPrintStatusLines())
-        {
-            // Save cursor position
-            this->cout_ << "\033[s";
-            // Move cursor down (don't create a new line here because the buffer might flush then!)
-            this->cout_ << "\033[1B\033[1G";
-            this->cout_ << std::fixed << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgFPS() << " fps, ";
-            this->cout_ <<               std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgTickTime() << " ms tick time";
-            // Restore cursor position
-            this->cout_ << "\033[u";
-            this->bStatusPrinted_ = true;
-        }
-        else
-            this->bStatusPrinted_ = false;
-    }
-
-    void IOConsole::setTerminalMode()
-    {
-        termios new_settings;
-        this->originalTerminalSettings_ = new termios();
-
-        tcgetattr(0, this->originalTerminalSettings_);
-        new_settings = *this->originalTerminalSettings_;
-        new_settings.c_lflag &= ~(ICANON | ECHO);
-        //new_settings.c_lflag |= (ISIG | IEXTEN);
-        new_settings.c_cc[VTIME] = 0;
-        new_settings.c_cc[VMIN]  = 0;
-        tcsetattr(0, TCSANOW, &new_settings);
-        atexit(&IOConsole::resetTerminalMode);
-    }
-
-    /*static*/ void IOConsole::resetTerminalMode()
-    {
-        if (IOConsole::singletonPtr_s && IOConsole::singletonPtr_s->originalTerminalSettings_)
-        {
-            tcsetattr(0, TCSANOW, IOConsole::singletonPtr_s->originalTerminalSettings_);
-            delete IOConsole::singletonPtr_s->originalTerminalSettings_;
-            IOConsole::singletonPtr_s->originalTerminalSettings_ = 0;
-        }
-    }
-
-    void IOConsole::getTerminalSize()
-    {
-#ifdef TIOCGSIZE
-        struct ttysize win;
-        if (!ioctl(STDIN_FILENO, TIOCGSIZE, &win))
-        {
-            this->terminalWidth_  = win.ts_cols;
-            this->terminalHeight_ = win.ts_lines;
-            return;
-        }
-#elif defined TIOCGWINSZ
-        struct winsize win;
-        if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &win))
-        {
-            this->terminalWidth_  = win.ws_col;
-            this->terminalHeight_ = win.ws_row;
-            return;
-        }
-#else
-        const char* s = getenv("COLUMNS");
-        this->terminalWidth_  = s ? strtol(s, NULL, 10) : 80;
-        s = getenv("LINES");
-        this->terminalHeight_ = s ? strtol(s, NULL, 10) : 24;
-        return;
+#if defined(ORXONOX_PLATFORM_UNIX)
+  #include "IOConsolePOSIX.cc"
+#elif defined(ORXONOX_PLATFORM_WINDOWS)
+  #include "IOConsoleWindows.cc"
 #endif
-        this->terminalWidth_  = 80;
-        this->terminalHeight_ = 24;
-    }
-
-    inline bool IOConsole::willPrintStatusLines()
-    {
-        return !this->statusLineWidths_.empty()
-             && this->terminalWidth_  >= this->statusLineMaxWidth_
-             && this->terminalHeight_ >= this->minOutputLines_ + (int)this->statusLineWidths_.size();
-    }
-
-    // ###############################
-    // ###  ShellListener methods  ###
-    // ###############################
-
-    //! Called if only the last output-line has changed
-    void IOConsole::onlyLastLineChanged()
-    {
-        // Save cursor position and move it to the beginning of the first output line
-        this->cout_ << "\033[s\033[1A\033[1G";
-        // Erase the line
-        this->cout_ << "\033[K";
-        // Reprint the last output line
-        this->printOutputLine(this->shell_->getNewestLineIterator()->first, this->shell_->getNewestLineIterator()->second);
-        // Restore cursor
-        this->cout_ << "\033[u";
-        this->cout_.flush();
-    }
-
-    //! Called if a new output-line was added
-    void IOConsole::lineAdded()
-    {
-        int newLines = this->shell_->getNewestLineIterator()->first.size() / this->terminalWidth_ + 1;
-        // Create new lines by scrolling the screen
-        this->cout_ << "\033[" << newLines << 'S';
-        // Move cursor to the beginning of the new (last) output line
-        this->cout_ << "\033[" << newLines << "A\033[1G";
-        // Erase screen from here
-        this->cout_ << "\033[J";
-        // Print the new output lines
-        for (int i = 0; i < newLines; ++i)
-        {
-            Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator();
-            this->printOutputLine(it->first.substr(i*this->terminalWidth_, this->terminalWidth_), it->second);
-        }
-        // Move cursor down
-        this->cout_ << "\033[1B\033[1G";
-        // Print status and input lines
-        this->printInputLine();
-        this->printStatusLines();
-        this->cout_.flush();
-    }
-
-    //! Called if the text in the input-line has changed
-    void IOConsole::inputChanged()
-    {
-        this->printInputLine();
-        this->cout_.flush();
-    }
-
-    //! Called if the position of the cursor in the input-line has changed
-    void IOConsole::cursorChanged()
-    {
-        this->printInputLine();
-        this->cout_.flush();
-    }
-}
-
-#elif defined(ORXONOX_PLATFORM_WINDOWS)
-// ##################################
-// ###   Windows Implementation   ###
-// ##################################
-
-#include <windows.h>
-
-namespace orxonox
-{
-    //! Redirects std::cout, creates the corresponding Shell and changes the terminal mode
-    IOConsole::IOConsole()
-        : shell_(new Shell("IOConsole", false))
-        , buffer_(shell_->getInputBuffer())
-        , cout_(std::cout.rdbuf())
-        , promptString_("orxonox # ")
-        , inputLineHeight_(1)
-        , statusLines_(1)
-        , lastOutputLineHeight_(0)
-    {
-        // Disable standard this->cout_ logging
-        OutputHandler::getInstance().disableCout();
-        // Redirect std::cout to an ostringstream
-        // (Other part is in the initialiser list)
-        std::cout.rdbuf(this->origCout_.rdbuf());
-
-        this->setTerminalMode();
-        CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
-        GetConsoleScreenBufferInfo(this->stdOutHandle_, &screenBufferInfo);
-        this->terminalWidth_  = screenBufferInfo.dwSize.X;
-        this->terminalHeight_ = screenBufferInfo.dwSize.Y;
-        // Determines where we are in respect to output already written with std::cout
-        this->inputLineRow_ = screenBufferInfo.dwCursorPosition.Y;
-/*
-        this->lastTerminalWidth_  = this->terminalWidth_;
-        this->lastTerminalHeight_ = this->terminalHeight_;
-*/
-
-        // Cursor already at the end of the screen buffer?
-        // (assuming the current input line height is 1)
-        if (this->inputLineRow_ >= this->terminalHeight_ - this->statusLines_)
-            SetConsoleCursorPosition(this->stdOutHandle_, makeCOORD(0, this->terminalHeight_ - this->statusLines_));
-
-        // Prevent input line from overflowing
-        int maxInputLength = (this->terminalHeight_ - this->statusLines_) * this->terminalWidth_ - 1 - this->promptString_.size();
-        // Consider that the echo of a command might include the command plus some other characters (assumed max 80)
-        // Also put a minimum so the config file parser is not overwhelmed with the command history
-        this->buffer_->setMaxLength(std::min(8192, (maxInputLength - 80) / 2));
-
-        // Print input and status line and position cursor
-        this->inputChanged();
-        this->cursorChanged();
-        this->lastRefreshTime_ = Game::getInstance().getGameClock().getRealMicroseconds();
-        this->preUpdate(Game::getInstance().getGameClock());
-
-        this->shell_->registerListener(this);
-    }
-
-    //! Resets std::cout redirection and restores the terminal mode
-    IOConsole::~IOConsole()
-    {
-        // Process output written to std::cout in the meantime
-        std::cout.flush();
-        if (!this->origCout_.str().empty())
-            this->shell_->addOutput(this->origCout_.str(), Shell::None);
-
-        this->shell_->unregisterListener(this);
-
-        // Erase input and status lines
-        COORD pos = {0, this->inputLineRow_};
-        this->writeText(std::string((this->inputLineHeight_ + this->statusLines_) * this->terminalWidth_, ' '), pos);
-        // Move cursor to the beginning of the line
-        SetConsoleCursorPosition(stdOutHandle_, pos);
-
-        // Restore this->cout_ redirection
-        std::cout.rdbuf(this->cout_.rdbuf());
-        // Enable standard this->cout_ logging again
-        OutputHandler::getInstance().enableCout();
-
-        resetTerminalMode();
-        this->shell_->destroy();
-    }
-
-    //! Processes the pending input key strokes, refreshes the status lines and handles std::cout (redirected)
-    void IOConsole::preUpdate(const Clock& time)
-    {
-        // Process input
-        while (true)
-        {
-            DWORD count;
-            INPUT_RECORD inrec;
-            PeekConsoleInput(this->stdInHandle_, &inrec, 1, &count);
-            if (count == 0)
-                break;
-            ReadConsoleInput(this->stdInHandle_, &inrec, 1, &count);
-            if (inrec.EventType == KEY_EVENT && inrec.Event.KeyEvent.bKeyDown)
-            {
-                // Process keyboard modifiers (Ctrl, Alt and Shift)
-                DWORD modifiersIn = inrec.Event.KeyEvent.dwControlKeyState;
-                int modifiersOut = 0;
-                if ((modifiersIn & (LEFT_ALT_PRESSED  | RIGHT_ALT_PRESSED))  != 0)
-                    modifiersOut |= KeyboardModifier::Alt;
-                if ((modifiersIn & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0)
-                    modifiersOut |= KeyboardModifier::Ctrl;
-                if ((modifiersIn & SHIFT_PRESSED) != 0)
-                    modifiersOut |= KeyboardModifier::Shift;
-
-                // ASCII character (0 for special keys)
-                char asciiChar = inrec.Event.KeyEvent.uChar.AsciiChar;
-
-                // Process special keys and if not found, use Key::A as dummy (InputBuffer uses the ASCII text anyway)
-                switch (inrec.Event.KeyEvent.wVirtualKeyCode)
-                {
-                case VK_BACK:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Back,     asciiChar, modifiersOut)); break;
-                case VK_TAB:    this->buffer_->buttonPressed(KeyEvent(KeyCode::Tab,      asciiChar, modifiersOut)); break;
-                case VK_RETURN: this->buffer_->buttonPressed(KeyEvent(KeyCode::Return,   asciiChar, modifiersOut)); break;
-                case VK_PAUSE:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Pause,    asciiChar, modifiersOut)); break;
-                case VK_ESCAPE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape,   asciiChar, modifiersOut)); break;
-                case VK_SPACE:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Space,    asciiChar, modifiersOut)); break;
-                case VK_PRIOR:  this->buffer_->buttonPressed(KeyEvent(KeyCode::PageUp,   asciiChar, modifiersOut)); break;
-                case VK_NEXT:   this->buffer_->buttonPressed(KeyEvent(KeyCode::PageDown, asciiChar, modifiersOut)); break;
-                case VK_END:    this->buffer_->buttonPressed(KeyEvent(KeyCode::End,      asciiChar, modifiersOut)); break;
-                case VK_HOME:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Home,     asciiChar, modifiersOut)); break;
-                case VK_LEFT:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Left,     asciiChar, modifiersOut)); break;
-                case VK_UP:     this->buffer_->buttonPressed(KeyEvent(KeyCode::Up,       asciiChar, modifiersOut)); break;
-                case VK_RIGHT:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Right,    asciiChar, modifiersOut)); break;
-                case VK_DOWN:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Down,     asciiChar, modifiersOut)); break;
-                case VK_INSERT: this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert,   asciiChar, modifiersOut)); break;
-                case VK_DELETE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete,   asciiChar, modifiersOut)); break;
-                default:        this->buffer_->buttonPressed(KeyEvent(KeyCode::A,        asciiChar, modifiersOut));
-                }
-            }
-        }
-
-        // TODO: Respect screen buffer size changes
-/*
-        // The user can manually adjust the screen buffer size on Windows
-        // And we don't want to screw the console because of that
-        this->lastTerminalWidth_ = this->terminalWidth_;
-        this->lastTerminalHeight_ = this->terminalHeight_;
-        this->getTerminalSize(); // Also sets this->inputLineRow_ according to the cursor position
-        // Is there still enough space below the cursor for the status line(s)?
-        if (this->inputLineRow_ >= this->terminalHeight_ - this->statusLines_)
-            this->moveCursor(0, -this->inputLineRow_ + this->terminalHeight_ - this->statusLines_ - 1);
-*/
-
-        // Refresh status line 5 times per second
-        if (time.getMicroseconds() > this->lastRefreshTime_ + 1000000)
-        {
-            this->printStatusLines();
-            this->lastRefreshTime_ = time.getMicroseconds();
-        }
-
-        // Process output written to std::cout
-        std::cout.flush();
-        if (!this->origCout_.str().empty())
-        {
-            this->shell_->addOutput(this->origCout_.str(), Shell::None);
-            this->origCout_.str("");
-        }
-    }
-
-    //! Prints output text. Similar to writeText, but sets the colour according to the output level
-    void IOConsole::printOutputLine(const std::string& text, Shell::LineType type, const COORD& pos)
-    {
-        // Colour line
-        WORD colour = 0;
-        switch (type)
-        {
-        case Shell::Error:   colour = FOREGROUND_INTENSITY                    | FOREGROUND_RED; break;
-        case Shell::Warning: colour = FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED; break;
-        case Shell::Info:
-        case Shell::Debug:
-        case Shell::Verbose:
-        case Shell::Ultra:   colour = FOREGROUND_INTENSITY                                     ; break;
-        case Shell::Command: colour =                        FOREGROUND_GREEN                  | FOREGROUND_BLUE; break;
-        case Shell::Hint:    colour =                        FOREGROUND_GREEN | FOREGROUND_RED                  ; break;
-        default:             colour =                        FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE; break;
-        }
-
-        // Print output line
-        this->writeText(text, pos, colour);
-    }
-
-    //! Prints all status lines with current content
-    void IOConsole::printStatusLines()
-    {
-        // Prepare text to be written
-        std::ostringstream oss;
-        oss << std::fixed << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgFPS() << " fps, ";
-        oss <<               std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgTickTime() << " ms tick time";
-        // Clear rest of the line by inserting spaces
-        oss << std::string(this->terminalWidth_ - oss.str().size(), ' ');
-        this->writeText(oss.str(), makeCOORD(0, this->inputLineRow_ + this->inputLineHeight_), FOREGROUND_GREEN);
-    }
-
-    //! Changes the console parameters for unbuffered input
-    void IOConsole::setTerminalMode()
-    {
-        // Set the console mode to no-echo, raw input, and no window or mouse events
-        this->stdOutHandle_ = GetStdHandle(STD_OUTPUT_HANDLE);
-        this->stdInHandle_  = GetStdHandle(STD_INPUT_HANDLE);
-        if (this->stdInHandle_ == INVALID_HANDLE_VALUE
-            || !GetConsoleMode(this->stdInHandle_, &this->originalTerminalSettings_)
-            || !SetConsoleMode(this->stdInHandle_, 0))
-        {
-            COUT(1) << "Error: Could not set Windows console settings" << std::endl;
-            return;
-        }
-        FlushConsoleInputBuffer(this->stdInHandle_);
-    }
-
-    //! Restores the console parameters
-    void IOConsole::resetTerminalMode()
-    {
-        SetConsoleMode(this->stdInHandle_, this->originalTerminalSettings_);
-    }
-
-    //! Sets this->terminalWidth_ and this->terminalHeight_
-    void IOConsole::getTerminalSize()
-    {
-        CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
-        GetConsoleScreenBufferInfo(this->stdOutHandle_, &screenBufferInfo);
-        this->terminalWidth_  = screenBufferInfo.dwSize.X;
-        this->terminalHeight_ = screenBufferInfo.dwSize.Y;
-    }
-
-    //! Writes arbitrary text to the console with a certain colour and screen buffer position
-    void IOConsole::writeText(const std::string& text, const COORD& coord, WORD attributes)
-    {
-        DWORD count;
-        WriteConsoleOutputCharacter(stdOutHandle_, text.c_str(), text.size(), coord, &count);
-        FillConsoleOutputAttribute(stdOutHandle_, attributes, text.size(), coord, &count);
-    }
-
-    /** Scrolls the console screen buffer to create empty lines above the input line.
-    @details
-        If the input and status lines are already at the bottom of the screen buffer
-        the whole output gets scrolled up. In the other case the input and status
-        lines get scrolled down.
-        In any case the status and input lines get scrolled down as far as possible.
-    @param lines
-        Number of lines to be inserted. Behavior for negative values is undefined.
-    */
-    void IOConsole::createNewOutputLines(int lines)
-    {
-        CHAR_INFO fillChar = {{' '}, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED};
-        // Lines to scroll input/status down (if possible)
-        int linesDown = clamp(terminalHeight_ - inputLineRow_ - inputLineHeight_ - statusLines_, 0, lines);
-        if (linesDown > 0)
-        {
-            // Scroll input and status lines down
-            SMALL_RECT oldRect = {0, this->inputLineRow_,
-                this->terminalWidth_ - 1, this->inputLineRow_ + this->inputLineHeight_ + this->statusLines_ - 1};
-            this->inputLineRow_ += linesDown;
-            ScrollConsoleScreenBuffer(stdOutHandle_, &oldRect, NULL, makeCOORD(0, this->inputLineRow_), &fillChar);
-            // Move cursor down to the new bottom so the user can see the status lines
-            COORD pos = {0, this->inputLineRow_ + this->inputLineHeight_ - 1 + this->statusLines_};
-            SetConsoleCursorPosition(stdOutHandle_, pos);
-            // Get cursor back to the right position
-            this->cursorChanged();
-        }
-        // Check how many lines we still have to scroll up the output
-        if (lines - linesDown > 0)
-        {
-            // Scroll output up
-            SMALL_RECT oldRect = {0, lines - linesDown, this->terminalWidth_ - 1, this->inputLineRow_ - 1};
-            ScrollConsoleScreenBuffer(stdOutHandle_, &oldRect, NULL, makeCOORD(0, 0), &fillChar);
-        }
-    }
-
-    // ###############################
-    // ###  ShellListener methods  ###
-    // ###############################
-
-    //! Called if the text in the input line has changed
-    void IOConsole::inputChanged()
-    {
-        int newInputLineLength = this->promptString_.size() + this->shell_->getInput().size();
-        int newInputLineHeight = 1 + newInputLineLength / this->terminalWidth_;
-        int newLines = newInputLineHeight - this->inputLineHeight_;
-        if (newLines > 0)
-        {
-            // Abuse this function to scroll the console
-            this->createNewOutputLines(newLines);
-            // Either Compensate for side effects (input/status lines scrolled down)
-            // or we have to do this anyway (output scrolled up)
-            this->inputLineRow_ -= newLines;
-        }
-        else if (newLines < 0)
-        {
-            // Scroll status lines up
-            int statusLineRow = this->inputLineRow_ + this->inputLineHeight_;
-            SMALL_RECT oldRect = {0, statusLineRow, this->terminalWidth_ - 1, statusLineRow + this->statusLines_};
-            CHAR_INFO fillChar = {{' '}, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED};
-            ScrollConsoleScreenBuffer(stdOutHandle_, &oldRect, NULL, makeCOORD(0, statusLineRow + newLines), &fillChar);
-            // Clear potential leftovers
-            if (-newLines - this->statusLines_ > 0)
-            {
-                COORD pos = {0, this->inputLineRow_ + newInputLineHeight + this->statusLines_};
-                this->writeText(std::string((-newLines - this->statusLines_) * this->terminalWidth_, ' '), pos);
-            }
-        }
-        this->inputLineHeight_ = newInputLineHeight;
-
-        // Print the whole line, including spaces that erase leftovers
-        std::string inputLine = this->promptString_ + this->shell_->getInput();
-        inputLine += std::string(this->terminalWidth_ - newInputLineLength % this->terminalWidth_, ' ');
-        this->writeText(inputLine, makeCOORD(0, this->inputLineRow_), FOREGROUND_GREEN | FOREGROUND_INTENSITY);
-        // If necessary, move cursor
-        if (newLines != 0)
-            this->cursorChanged();
-    }
-
-    //! Called if the position of the cursor in the input-line has changed
-    void IOConsole::cursorChanged()
-    {
-        int rawCursorPos = this->promptString_.size() + this->buffer_->getCursorPosition();
-        // Compensate for cursor further to the right than the terminal width
-        COORD pos;
-        pos.X = rawCursorPos % this->terminalWidth_;
-        pos.Y = this->inputLineRow_ + rawCursorPos / this->terminalWidth_;
-        SetConsoleCursorPosition(stdOutHandle_, pos);
-    }
-
-    //! Called if only the last output-line has changed
-    void IOConsole::onlyLastLineChanged()
-    {
-        int newLineHeight = 1 + this->shell_->getNewestLineIterator()->first.size() / this->terminalWidth_;
-        // Compute the number of new lines needed
-        int newLines = newLineHeight - this->lastOutputLineHeight_;
-        this->lastOutputLineHeight_ = newLineHeight;
-        // Scroll console if necessary
-        if (newLines > 0) // newLines < 0 is assumed impossible
-            this->createNewOutputLines(newLines);
-        Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator();
-        this->printOutputLine(it->first, it->second, makeCOORD(0, this->inputLineRow_ - newLineHeight));
-    }
-
-    //! Called if a new output line was added
-    void IOConsole::lineAdded()
-    {
-        Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator();
-        // Scroll console
-        this->lastOutputLineHeight_ = 1 + it->first.size() / this->terminalWidth_;
-        this->createNewOutputLines(this->lastOutputLineHeight_);
-        // Write the text
-        COORD pos = {0, this->inputLineRow_ - this->lastOutputLineHeight_};
-        this->printOutputLine(it->first, it->second, pos);
-    }
-}
-
-#endif /* ORXONOX_PLATFORM_UNIX */
Index: /code/trunk/src/libraries/core/command/IOConsole.h
===================================================================
--- /code/trunk/src/libraries/core/command/IOConsole.h	(revision 7286)
+++ /code/trunk/src/libraries/core/command/IOConsole.h	(revision 7287)
@@ -21,5 +21,4 @@
  *
  *   Author:
- *      Oliver Scheuss
  *      Reto Grieder
  *   Co-authors:
@@ -28,101 +27,9 @@
  */
 
-#ifndef _IOConsole_H__
-#define _IOConsole_H__
+#include "OrxonoxConfig.h"
 
-#include "core/CorePrereqs.h"
-
-#include <sstream>
-#include <string>
-#include <vector>
-#include "util/Singleton.h"
-#include "Shell.h"
-
-#ifdef ORXONOX_PLATFORM_UNIX
-struct termios;
+#if defined(ORXONOX_PLATFORM_UNIX)
+  #include "IOConsolePOSIX.h"
 #elif defined(ORXONOX_PLATFORM_WINDOWS)
-#define WIN32_LEAN_AND_MEAN
-#ifndef NOMINMAX
-#define NOMINMAX
+  #include "IOConsoleWindows.h"
 #endif
-#include <windows.h>
-#endif
-
-namespace orxonox
-{
-    class _CoreExport IOConsole : public Singleton<IOConsole>, public ShellListener
-    {
-        friend class Singleton<IOConsole>;
-
-    public:
-        IOConsole();
-        ~IOConsole();
-
-        void preUpdate(const Clock& time);
-
-    private:
-        void setTerminalMode();
-        void getTerminalSize();
-        void printStatusLines();
-        static int extractLogLevel(std::string* text);
-
-        // Methods from ShellListener
-        void linesChanged();
-        void onlyLastLineChanged();
-        void lineAdded();
-        void inputChanged();
-        void cursorChanged();
-        void executed();
-        void exit();
-
-        Shell*                  shell_;
-        InputBuffer*            buffer_;
-        std::ostream            cout_;
-        std::ostringstream      origCout_;
-        int                     terminalWidth_;
-        int                     terminalHeight_;
-        int                     lastTerminalWidth_;
-        int                     lastTerminalHeight_;
-        const std::string       promptString_;
-
-#ifdef ORXONOX_PLATFORM_UNIX
-        bool willPrintStatusLines();
-        void printInputLine();
-        void printOutputLine(const std::string& line, Shell::LineType type);
-        static void resetTerminalMode();
-
-        bool                    bPrintStatusLine_;
-        bool                    bStatusPrinted_;
-        std::vector<int>        statusLineWidths_;
-        int                     statusLineMaxWidth_;
-        static const int        minOutputLines_ = 3;
-        termios*                originalTerminalSettings_;
-
-#elif defined(ORXONOX_PLATFORM_WINDOWS)
-        void resetTerminalMode();
-        void moveCursor(int dx, int dy);
-        void writeText(const std::string& text, const COORD& pos, WORD attributes = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
-        void createNewOutputLines(int lines);
-        void printOutputLine(const std::string& line, Shell::LineType type, const COORD& pos);
-
-        static inline COORD makeCOORD(int x, int y)
-        {
-            COORD val = {x, y};
-            return val;
-        }
-
-        DWORD                   originalTerminalSettings_;
-        HANDLE                  stdInHandle_;
-        HANDLE                  stdOutHandle_;
-        int                     inputLineRow_;
-        int                     inputLineHeight_;
-        const int               statusLines_;
-        int                     lastOutputLineHeight_;
-        uint64_t                lastRefreshTime_;
-#endif
-
-        static IOConsole* singletonPtr_s;
-    };
-}
-
-#endif /* _IOConsole_H__ */
Index: /code/trunk/src/libraries/core/command/IOConsolePOSIX.cc
===================================================================
--- /code/trunk/src/libraries/core/command/IOConsolePOSIX.cc	(revision 7287)
+++ /code/trunk/src/libraries/core/command/IOConsolePOSIX.cc	(revision 7287)
@@ -0,0 +1,430 @@
+/*
+ *   ORXONOX - the hottest 3D action shooter ever to exist
+ *                    > www.orxonox.net <
+ *
+ *
+ *   License notice:
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation; either version 2
+ *   of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ *   Author:
+ *      Oliver Scheuss
+ *      Reto Grieder
+ *   Co-authors:
+ *      ...
+ *
+ */
+
+#include "IOConsole.h"
+
+#include <iomanip>
+#include <iostream>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+#include "util/Clock.h"
+#include "util/Math.h"
+#include "core/Game.h"
+#include "core/input/InputBuffer.h"
+
+namespace orxonox
+{
+    IOConsole* IOConsole::singletonPtr_s = NULL;
+
+    namespace EscapeMode
+    {
+        enum Value
+        {
+            None,
+            First,
+            Second
+        };
+    }
+
+    IOConsole::IOConsole()
+        : shell_(new Shell("IOConsole", false))
+        , buffer_(shell_->getInputBuffer())
+        , cout_(std::cout.rdbuf())
+        , promptString_("orxonox # ")
+        , bStatusPrinted_(false)
+        , originalTerminalSettings_(0)
+    {
+        this->setTerminalMode();
+        this->shell_->registerListener(this);
+
+        // Manually set the widths of the individual status lines
+        this->statusLineWidths_.push_back(29);
+        this->statusLineMaxWidth_ = 29;
+
+        this->getTerminalSize();
+        this->lastTerminalWidth_ = this->terminalWidth_;
+        this->lastTerminalHeight_ = this->terminalHeight_;
+
+        // Disable standard std::cout logging
+        OutputHandler::getInstance().disableCout();
+        // Redirect std::cout to an ostringstream
+        // (Other part is in the initialiser list)
+        std::cout.rdbuf(this->origCout_.rdbuf());
+
+        // Make sure we make way for the status lines
+        this->preUpdate(Game::getInstance().getGameClock());
+    }
+
+    IOConsole::~IOConsole()
+    {
+        // Process output written to std::cout in the meantime
+        std::cout.flush();
+        if (!this->origCout_.str().empty())
+            this->shell_->addOutput(this->origCout_.str(), Shell::None);
+        // Erase input and status lines
+        this->cout_ << "\033[1G\033[J";
+        // Move cursor to the bottom
+        this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B';
+        // Scroll terminal to compensate for erased lines
+        this->cout_ << "\033[" << this->statusLineWidths_.size() << 'T';
+
+        resetTerminalMode();
+        this->shell_->destroy();
+
+        // Restore this->cout_ redirection
+        std::cout.rdbuf(this->cout_.rdbuf());
+        // Enable standard std::cout logging again
+        OutputHandler::getInstance().enableCout();
+    }
+
+    void IOConsole::preUpdate(const Clock& time)
+    {
+        unsigned char c;
+        std::string escapeSequence;
+        EscapeMode::Value escapeMode = EscapeMode::None;
+        while (std::cin.good())
+        {
+            c = std::cin.get();
+            if (!std::cin.good())
+                break;
+
+            if (escapeMode == EscapeMode::First && (c == '[' || c=='O') )
+                escapeMode = EscapeMode::Second;
+            // Get Alt+Tab combination when switching applications
+            else if (escapeMode == EscapeMode::First && c == '\t')
+            {
+                this->buffer_->buttonPressed(KeyEvent(KeyCode::Tab, '\t', KeyboardModifier::Alt));
+                escapeMode = EscapeMode::None;
+            }
+            else if (escapeMode == EscapeMode::Second)
+            {
+                escapeSequence += c;
+                escapeMode = EscapeMode::None;
+                if      (escapeSequence == "A")
+                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Up,       0, 0));
+                else if (escapeSequence == "B")
+                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Down,     0, 0));
+                else if (escapeSequence == "C")
+                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Right,    0, 0));
+                else if (escapeSequence == "D")
+                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Left,     0, 0));
+                else if (escapeSequence == "1~" || escapeSequence == "H")
+                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Home,     0, 0));
+                else if (escapeSequence == "2~")
+                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert,   0, 0));
+                else if (escapeSequence == "3~")
+                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete,   0, 0));
+                else if (escapeSequence == "4~" || escapeSequence == "F")
+                    this->buffer_->buttonPressed(KeyEvent(KeyCode::End,      0, 0));
+                else if (escapeSequence == "5~")
+                    this->buffer_->buttonPressed(KeyEvent(KeyCode::PageUp,   0, 0));
+                else if (escapeSequence == "6~")
+                    this->buffer_->buttonPressed(KeyEvent(KeyCode::PageDown, 0, 0));
+                else
+                    // Waiting for sequence to complete
+                    // If the user presses ESC and then '[' or 'O' while the loop is not
+                    // running (for instance while loading), the whole sequence gets dropped
+                    escapeMode = EscapeMode::Second;
+            }
+            else // not in an escape sequence OR user might have pressed just ESC
+            {
+                if (escapeMode == EscapeMode::First)
+                {
+                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, c, 0));
+                    escapeMode = EscapeMode::None;
+                }
+                if (c == '\033')
+                {
+                    escapeMode = EscapeMode::First;
+                    escapeSequence.clear();
+                }
+                else
+                {
+                    KeyCode::ByEnum code;
+                    switch (c)
+                    {
+                    case '\n'  : case '\r': code = KeyCode::Return; break;
+                    case '\177': case '\b': code = KeyCode::Back;   break;
+                    case '\t'             : code = KeyCode::Tab;    break;
+                    default:
+                        // We don't encode the key code (would be a very large switch)
+                        // because the InputBuffer will only insert the text anyway
+                        // Replacement character is simply KeyCode::A
+                        code = KeyCode::A;
+                    }
+                    this->buffer_->buttonPressed(KeyEvent(code, c, 0));
+                }
+            }
+        }
+        // Reset error flags in std::cin
+        std::cin.clear();
+
+        // If there is still an escape key pending (escape key ONLY), then
+        // it sure isn't an escape sequence anymore
+        if (escapeMode == EscapeMode::First)
+            this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, '\033', 0));
+
+        // Determine terminal width and height
+        this->lastTerminalWidth_ = this->terminalWidth_;
+        this->lastTerminalHeight_ = this->terminalHeight_;
+        this->getTerminalSize();
+
+        int heightDiff = this->terminalHeight_ - this->lastTerminalHeight_;
+        if (this->bStatusPrinted_ && heightDiff < 0)
+        {
+            // Terminal width has shrunk. The cursor will still be on the input line,
+            // but that line might very well be the last
+            int newLines = std::min((int)this->statusLineWidths_.size(), -heightDiff);
+            // Scroll terminal to create new lines
+            this->cout_ << "\033[" << newLines << 'S';
+        }
+
+        if (!this->bStatusPrinted_ && this->willPrintStatusLines())
+        {
+            // Scroll console to make way for status lines
+            this->cout_ << "\033[" << this->statusLineWidths_.size() << 'S';
+            this->bStatusPrinted_ = true;
+        }
+
+        // We always assume that the cursor is on the input line.
+        // But we cannot always be sure about that, esp. if we scroll the console
+        this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B';
+        this->cout_ << "\033[" << this->statusLineWidths_.size() << 'A';
+
+        // Erase status and input lines
+        this->cout_ << "\033[1G\033[J";
+        this->printInputLine();
+        this->printStatusLines();
+        this->cout_.flush();
+
+        // Process output written to std::cout
+        std::cout.flush();
+        if (!this->origCout_.str().empty())
+        {
+            this->shell_->addOutput(this->origCout_.str(), Shell::None);
+            this->origCout_.str("");
+        }
+    }
+
+    void IOConsole::printOutputLine(const std::string& text, Shell::LineType type)
+    {
+/*
+        // Colour line
+        switch (type)
+        {
+        case Shell::None:    this->cout_ << "\033[37m"; break;
+        case Shell::Error:   this->cout_ << "\033[91m"; break;
+        case Shell::Warning: this->cout_ << "\033[31m"; break;
+        case Shell::Info:    this->cout_ << "\033[34m"; break;
+        case Shell::Debug:   this->cout_ << "\033[36m"; break;
+        case Shell::Verbose: this->cout_ << "\033[35m"; break;
+        case Shell::Ultra:   this->cout_ << "\033[37m"; break;
+        default: break;
+        }
+*/
+
+        // Print output line
+        this->cout_ << text;
+
+        // Reset colour to white
+//        this->cout_ << "\033[37m";
+    }
+
+    void IOConsole::printInputLine()
+    {
+        // Set cursor to the beginning of the line and erase the line
+        this->cout_ << "\033[1G\033[K";
+        // Indicate a command prompt
+        this->cout_ << this->promptString_;
+        // Save cursor position
+        this->cout_ << "\033[s";
+        // Print command line buffer
+        this->cout_ << this->shell_->getInput();
+        // Restore cursor position and move it to the right
+        this->cout_ << "\033[u";
+        if (this->buffer_->getCursorPosition() > 0)
+            this->cout_ << "\033[" << this->buffer_->getCursorPosition() << 'C';
+    }
+
+    void IOConsole::printStatusLines()
+    {
+        if (this->willPrintStatusLines())
+        {
+            // Save cursor position
+            this->cout_ << "\033[s";
+            // Move cursor down (don't create a new line here because the buffer might flush then!)
+            this->cout_ << "\033[1B\033[1G";
+            this->cout_ << std::fixed << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgFPS() << " fps, ";
+            this->cout_ <<               std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgTickTime() << " ms tick time";
+            // Restore cursor position
+            this->cout_ << "\033[u";
+            this->bStatusPrinted_ = true;
+        }
+        else
+            this->bStatusPrinted_ = false;
+    }
+
+    void IOConsole::setTerminalMode()
+    {
+        termios new_settings;
+        this->originalTerminalSettings_ = new termios();
+
+        tcgetattr(0, this->originalTerminalSettings_);
+        new_settings = *this->originalTerminalSettings_;
+        new_settings.c_lflag &= ~(ICANON | ECHO);
+        //new_settings.c_lflag |= (ISIG | IEXTEN);
+        new_settings.c_cc[VTIME] = 0;
+        new_settings.c_cc[VMIN]  = 0;
+        tcsetattr(0, TCSANOW, &new_settings);
+        atexit(&IOConsole::resetTerminalMode);
+    }
+
+    /*static*/ void IOConsole::resetTerminalMode()
+    {
+        if (IOConsole::singletonPtr_s && IOConsole::singletonPtr_s->originalTerminalSettings_)
+        {
+            tcsetattr(0, TCSANOW, IOConsole::singletonPtr_s->originalTerminalSettings_);
+            delete IOConsole::singletonPtr_s->originalTerminalSettings_;
+            IOConsole::singletonPtr_s->originalTerminalSettings_ = 0;
+        }
+    }
+
+    void IOConsole::getTerminalSize()
+    {
+#ifdef TIOCGSIZE
+        struct ttysize win;
+        if (!ioctl(STDIN_FILENO, TIOCGSIZE, &win))
+        {
+            this->terminalWidth_  = win.ts_cols;
+            this->terminalHeight_ = win.ts_lines;
+            return;
+        }
+#elif defined TIOCGWINSZ
+        struct winsize win;
+        if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &win))
+        {
+            this->terminalWidth_  = win.ws_col;
+            this->terminalHeight_ = win.ws_row;
+            return;
+        }
+#else
+        const char* s = getenv("COLUMNS");
+        this->terminalWidth_  = s ? strtol(s, NULL, 10) : 80;
+        s = getenv("LINES");
+        this->terminalHeight_ = s ? strtol(s, NULL, 10) : 24;
+        return;
+#endif
+        this->terminalWidth_  = 80;
+        this->terminalHeight_ = 24;
+    }
+
+    inline bool IOConsole::willPrintStatusLines()
+    {
+        return !this->statusLineWidths_.empty()
+             && this->terminalWidth_  >= this->statusLineMaxWidth_
+             && this->terminalHeight_ >= this->minOutputLines_ + (int)this->statusLineWidths_.size();
+    }
+
+    // ###############################
+    // ###  ShellListener methods  ###
+    // ###############################
+
+    //! Called if all output-lines have to be reprinted
+    void IOConsole::linesChanged()
+    {
+        // Method only gets called upon start to draw all the lines
+        // or when scrolling. But scrolling is disabled and the output
+        // is already in std::cout when we start the IOConsole
+    }
+
+    //! Called if a command is about to be executed
+    void IOConsole::executed()
+    {
+        this->shell_->addOutput(this->promptString_ + this->shell_->getInput() + '\n', Shell::Command);
+    }
+
+    //! Called if the console gets closed
+    void IOConsole::exit()
+    {
+        // Exit is not an option, just do nothing (Shell doesn't really exit too)
+    }
+
+    //! Called if only the last output-line has changed
+    void IOConsole::onlyLastLineChanged()
+    {
+        // Save cursor position and move it to the beginning of the first output line
+        this->cout_ << "\033[s\033[1A\033[1G";
+        // Erase the line
+        this->cout_ << "\033[K";
+        // Reprint the last output line
+        this->printOutputLine(this->shell_->getNewestLineIterator()->first, this->shell_->getNewestLineIterator()->second);
+        // Restore cursor
+        this->cout_ << "\033[u";
+        this->cout_.flush();
+    }
+
+    //! Called if a new output-line was added
+    void IOConsole::lineAdded()
+    {
+        int newLines = this->shell_->getNewestLineIterator()->first.size() / this->terminalWidth_ + 1;
+        // Create new lines by scrolling the screen
+        this->cout_ << "\033[" << newLines << 'S';
+        // Move cursor to the beginning of the new (last) output line
+        this->cout_ << "\033[" << newLines << "A\033[1G";
+        // Erase screen from here
+        this->cout_ << "\033[J";
+        // Print the new output lines
+        for (int i = 0; i < newLines; ++i)
+        {
+            Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator();
+            this->printOutputLine(it->first.substr(i*this->terminalWidth_, this->terminalWidth_), it->second);
+        }
+        // Move cursor down
+        this->cout_ << "\033[1B\033[1G";
+        // Print status and input lines
+        this->printInputLine();
+        this->printStatusLines();
+        this->cout_.flush();
+    }
+
+    //! Called if the text in the input-line has changed
+    void IOConsole::inputChanged()
+    {
+        this->printInputLine();
+        this->cout_.flush();
+    }
+
+    //! Called if the position of the cursor in the input-line has changed
+    void IOConsole::cursorChanged()
+    {
+        this->printInputLine();
+        this->cout_.flush();
+    }
+}
Index: /code/trunk/src/libraries/core/command/IOConsolePOSIX.h
===================================================================
--- /code/trunk/src/libraries/core/command/IOConsolePOSIX.h	(revision 7287)
+++ /code/trunk/src/libraries/core/command/IOConsolePOSIX.h	(revision 7287)
@@ -0,0 +1,96 @@
+/*
+ *   ORXONOX - the hottest 3D action shooter ever to exist
+ *                    > www.orxonox.net <
+ *
+ *
+ *   License notice:
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation; either version 2
+ *   of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ *   Author:
+ *      Oliver Scheuss
+ *      Reto Grieder
+ *   Co-authors:
+ *      ...
+ *
+ */
+
+#ifndef _IOConsole_H__
+#define _IOConsole_H__
+
+#include "core/CorePrereqs.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include "util/Singleton.h"
+#include "Shell.h"
+
+struct termios;
+
+namespace orxonox
+{
+    class _CoreExport IOConsole : public Singleton<IOConsole>, public ShellListener
+    {
+        friend class Singleton<IOConsole>;
+
+    public:
+        IOConsole();
+        ~IOConsole();
+
+        void preUpdate(const Clock& time);
+
+    private:
+        void setTerminalMode();
+        void getTerminalSize();
+        void printStatusLines();
+        static int extractLogLevel(std::string* text);
+
+        // Methods from ShellListener
+        void linesChanged();
+        void onlyLastLineChanged();
+        void lineAdded();
+        void inputChanged();
+        void cursorChanged();
+        void executed();
+        void exit();
+
+        bool willPrintStatusLines();
+        void printInputLine();
+        void printOutputLine(const std::string& line, Shell::LineType type);
+        static void resetTerminalMode();
+
+        Shell*                  shell_;
+        InputBuffer*            buffer_;
+        std::ostream            cout_;
+        std::ostringstream      origCout_;
+        int                     terminalWidth_;
+        int                     terminalHeight_;
+        int                     lastTerminalWidth_;
+        int                     lastTerminalHeight_;
+        const std::string       promptString_;
+
+        bool                    bPrintStatusLine_;
+        bool                    bStatusPrinted_;
+        std::vector<int>        statusLineWidths_;
+        int                     statusLineMaxWidth_;
+        static const int        minOutputLines_ = 3;
+        termios*                originalTerminalSettings_;
+
+        static IOConsole* singletonPtr_s;
+    };
+}
+
+#endif /* _IOConsole_H__ */
Index: /code/trunk/src/libraries/core/command/IOConsoleWindows.cc
===================================================================
--- /code/trunk/src/libraries/core/command/IOConsoleWindows.cc	(revision 7287)
+++ /code/trunk/src/libraries/core/command/IOConsoleWindows.cc	(revision 7287)
@@ -0,0 +1,403 @@
+/*
+ *   ORXONOX - the hottest 3D action shooter ever to exist
+ *                    > www.orxonox.net <
+ *
+ *
+ *   License notice:
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation; either version 2
+ *   of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ *   Author:
+ *      Reto Grieder
+ *   Co-authors:
+ *      ...
+ *
+ */
+
+#include "IOConsole.h"
+
+#include <iomanip>
+#include <iostream>
+
+#include "util/Clock.h"
+#include "util/Math.h"
+#include "core/Game.h"
+#include "core/input/InputBuffer.h"
+
+namespace orxonox
+{
+    IOConsole* IOConsole::singletonPtr_s = NULL;
+
+    //! Redirects std::cout, creates the corresponding Shell and changes the terminal mode
+    IOConsole::IOConsole()
+        : shell_(new Shell("IOConsole", false))
+        , buffer_(shell_->getInputBuffer())
+        , cout_(std::cout.rdbuf())
+        , promptString_("orxonox # ")
+        , inputLineHeight_(1)
+        , statusLines_(1)
+        , lastOutputLineHeight_(0)
+    {
+        // Disable standard this->cout_ logging
+        OutputHandler::getInstance().disableCout();
+        // Redirect std::cout to an ostringstream
+        // (Other part is in the initialiser list)
+        std::cout.rdbuf(this->origCout_.rdbuf());
+
+        this->setTerminalMode();
+        CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
+        GetConsoleScreenBufferInfo(this->stdOutHandle_, &screenBufferInfo);
+        this->terminalWidth_  = screenBufferInfo.dwSize.X;
+        this->terminalHeight_ = screenBufferInfo.dwSize.Y;
+        // Determines where we are in respect to output already written with std::cout
+        this->inputLineRow_ = screenBufferInfo.dwCursorPosition.Y;
+/*
+        this->lastTerminalWidth_  = this->terminalWidth_;
+        this->lastTerminalHeight_ = this->terminalHeight_;
+*/
+
+        // Cursor already at the end of the screen buffer?
+        // (assuming the current input line height is 1)
+        if (this->inputLineRow_ >= this->terminalHeight_ - this->statusLines_)
+            SetConsoleCursorPosition(this->stdOutHandle_, makeCOORD(0, this->terminalHeight_ - this->statusLines_));
+
+        // Prevent input line from overflowing
+        int maxInputLength = (this->terminalHeight_ - this->statusLines_) * this->terminalWidth_ - 1 - this->promptString_.size();
+        // Consider that the echo of a command might include the command plus some other characters (assumed max 80)
+        // Also put a minimum so the config file parser is not overwhelmed with the command history
+        this->buffer_->setMaxLength(std::min(8192, (maxInputLength - 80) / 2));
+
+        // Print input and status line and position cursor
+        this->inputChanged();
+        this->cursorChanged();
+        this->lastRefreshTime_ = Game::getInstance().getGameClock().getRealMicroseconds();
+        this->preUpdate(Game::getInstance().getGameClock());
+
+        this->shell_->registerListener(this);
+    }
+
+    //! Resets std::cout redirection and restores the terminal mode
+    IOConsole::~IOConsole()
+    {
+        // Process output written to std::cout in the meantime
+        std::cout.flush();
+        if (!this->origCout_.str().empty())
+            this->shell_->addOutput(this->origCout_.str(), Shell::None);
+
+        this->shell_->unregisterListener(this);
+
+        // Erase input and status lines
+        COORD pos = {0, this->inputLineRow_};
+        this->writeText(std::string((this->inputLineHeight_ + this->statusLines_) * this->terminalWidth_, ' '), pos);
+        // Move cursor to the beginning of the line
+        SetConsoleCursorPosition(stdOutHandle_, pos);
+
+        // Restore this->cout_ redirection
+        std::cout.rdbuf(this->cout_.rdbuf());
+        // Enable standard this->cout_ logging again
+        OutputHandler::getInstance().enableCout();
+
+        resetTerminalMode();
+        this->shell_->destroy();
+    }
+
+    //! Processes the pending input key strokes, refreshes the status lines and handles std::cout (redirected)
+    void IOConsole::preUpdate(const Clock& time)
+    {
+        // Process input
+        while (true)
+        {
+            DWORD count;
+            INPUT_RECORD inrec;
+            PeekConsoleInput(this->stdInHandle_, &inrec, 1, &count);
+            if (count == 0)
+                break;
+            ReadConsoleInput(this->stdInHandle_, &inrec, 1, &count);
+            if (inrec.EventType == KEY_EVENT && inrec.Event.KeyEvent.bKeyDown)
+            {
+                // Process keyboard modifiers (Ctrl, Alt and Shift)
+                DWORD modifiersIn = inrec.Event.KeyEvent.dwControlKeyState;
+                int modifiersOut = 0;
+                if ((modifiersIn & (LEFT_ALT_PRESSED  | RIGHT_ALT_PRESSED))  != 0)
+                    modifiersOut |= KeyboardModifier::Alt;
+                if ((modifiersIn & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0)
+                    modifiersOut |= KeyboardModifier::Ctrl;
+                if ((modifiersIn & SHIFT_PRESSED) != 0)
+                    modifiersOut |= KeyboardModifier::Shift;
+
+                // ASCII character (0 for special keys)
+                char asciiChar = inrec.Event.KeyEvent.uChar.AsciiChar;
+
+                // Process special keys and if not found, use Key::A as dummy (InputBuffer uses the ASCII text anyway)
+                switch (inrec.Event.KeyEvent.wVirtualKeyCode)
+                {
+                case VK_BACK:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Back,     asciiChar, modifiersOut)); break;
+                case VK_TAB:    this->buffer_->buttonPressed(KeyEvent(KeyCode::Tab,      asciiChar, modifiersOut)); break;
+                case VK_RETURN: this->buffer_->buttonPressed(KeyEvent(KeyCode::Return,   asciiChar, modifiersOut)); break;
+                case VK_PAUSE:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Pause,    asciiChar, modifiersOut)); break;
+                case VK_ESCAPE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape,   asciiChar, modifiersOut)); break;
+                case VK_SPACE:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Space,    asciiChar, modifiersOut)); break;
+                case VK_PRIOR:  this->buffer_->buttonPressed(KeyEvent(KeyCode::PageUp,   asciiChar, modifiersOut)); break;
+                case VK_NEXT:   this->buffer_->buttonPressed(KeyEvent(KeyCode::PageDown, asciiChar, modifiersOut)); break;
+                case VK_END:    this->buffer_->buttonPressed(KeyEvent(KeyCode::End,      asciiChar, modifiersOut)); break;
+                case VK_HOME:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Home,     asciiChar, modifiersOut)); break;
+                case VK_LEFT:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Left,     asciiChar, modifiersOut)); break;
+                case VK_UP:     this->buffer_->buttonPressed(KeyEvent(KeyCode::Up,       asciiChar, modifiersOut)); break;
+                case VK_RIGHT:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Right,    asciiChar, modifiersOut)); break;
+                case VK_DOWN:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Down,     asciiChar, modifiersOut)); break;
+                case VK_INSERT: this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert,   asciiChar, modifiersOut)); break;
+                case VK_DELETE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete,   asciiChar, modifiersOut)); break;
+                default:        this->buffer_->buttonPressed(KeyEvent(KeyCode::A,        asciiChar, modifiersOut));
+                }
+            }
+        }
+
+        // TODO: Respect screen buffer size changes
+/*
+        // The user can manually adjust the screen buffer size on Windows
+        // And we don't want to screw the console because of that
+        this->lastTerminalWidth_ = this->terminalWidth_;
+        this->lastTerminalHeight_ = this->terminalHeight_;
+        this->getTerminalSize(); // Also sets this->inputLineRow_ according to the cursor position
+        // Is there still enough space below the cursor for the status line(s)?
+        if (this->inputLineRow_ >= this->terminalHeight_ - this->statusLines_)
+            this->moveCursor(0, -this->inputLineRow_ + this->terminalHeight_ - this->statusLines_ - 1);
+*/
+
+        // Refresh status line 5 times per second
+        if (time.getMicroseconds() > this->lastRefreshTime_ + 1000000)
+        {
+            this->printStatusLines();
+            this->lastRefreshTime_ = time.getMicroseconds();
+        }
+
+        // Process output written to std::cout
+        std::cout.flush();
+        if (!this->origCout_.str().empty())
+        {
+            this->shell_->addOutput(this->origCout_.str(), Shell::None);
+            this->origCout_.str("");
+        }
+    }
+
+    //! Prints output text. Similar to writeText, but sets the colour according to the output level
+    void IOConsole::printOutputLine(const std::string& text, Shell::LineType type, const COORD& pos)
+    {
+        // Colour line
+        WORD colour = 0;
+        switch (type)
+        {
+        case Shell::Error:   colour = FOREGROUND_INTENSITY                    | FOREGROUND_RED; break;
+        case Shell::Warning: colour = FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED; break;
+        case Shell::Info:
+        case Shell::Debug:
+        case Shell::Verbose:
+        case Shell::Ultra:   colour = FOREGROUND_INTENSITY                                     ; break;
+        case Shell::Command: colour =                        FOREGROUND_GREEN                  | FOREGROUND_BLUE; break;
+        case Shell::Hint:    colour =                        FOREGROUND_GREEN | FOREGROUND_RED                  ; break;
+        default:             colour =                        FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE; break;
+        }
+
+        // Print output line
+        this->writeText(text, pos, colour);
+    }
+
+    //! Prints all status lines with current content
+    void IOConsole::printStatusLines()
+    {
+        // Prepare text to be written
+        std::ostringstream oss;
+        oss << std::fixed << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgFPS() << " fps, ";
+        oss <<               std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgTickTime() << " ms tick time";
+        // Clear rest of the line by inserting spaces
+        oss << std::string(this->terminalWidth_ - oss.str().size(), ' ');
+        this->writeText(oss.str(), makeCOORD(0, this->inputLineRow_ + this->inputLineHeight_), FOREGROUND_GREEN);
+    }
+
+    //! Changes the console parameters for unbuffered input
+    void IOConsole::setTerminalMode()
+    {
+        // Set the console mode to no-echo, raw input, and no window or mouse events
+        this->stdOutHandle_ = GetStdHandle(STD_OUTPUT_HANDLE);
+        this->stdInHandle_  = GetStdHandle(STD_INPUT_HANDLE);
+        if (this->stdInHandle_ == INVALID_HANDLE_VALUE
+            || !GetConsoleMode(this->stdInHandle_, &this->originalTerminalSettings_)
+            || !SetConsoleMode(this->stdInHandle_, 0))
+        {
+            COUT(1) << "Error: Could not set Windows console settings" << std::endl;
+            return;
+        }
+        FlushConsoleInputBuffer(this->stdInHandle_);
+    }
+
+    //! Restores the console parameters
+    void IOConsole::resetTerminalMode()
+    {
+        SetConsoleMode(this->stdInHandle_, this->originalTerminalSettings_);
+    }
+
+    //! Sets this->terminalWidth_ and this->terminalHeight_
+    void IOConsole::getTerminalSize()
+    {
+        CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
+        GetConsoleScreenBufferInfo(this->stdOutHandle_, &screenBufferInfo);
+        this->terminalWidth_  = screenBufferInfo.dwSize.X;
+        this->terminalHeight_ = screenBufferInfo.dwSize.Y;
+    }
+
+    //! Writes arbitrary text to the console with a certain colour and screen buffer position
+    void IOConsole::writeText(const std::string& text, const COORD& coord, WORD attributes)
+    {
+        DWORD count;
+        WriteConsoleOutputCharacter(stdOutHandle_, text.c_str(), text.size(), coord, &count);
+        FillConsoleOutputAttribute(stdOutHandle_, attributes, text.size(), coord, &count);
+    }
+
+    /** Scrolls the console screen buffer to create empty lines above the input line.
+    @details
+        If the input and status lines are already at the bottom of the screen buffer
+        the whole output gets scrolled up. In the other case the input and status
+        lines get scrolled down.
+        In any case the status and input lines get scrolled down as far as possible.
+    @param lines
+        Number of lines to be inserted. Behavior for negative values is undefined.
+    */
+    void IOConsole::createNewOutputLines(int lines)
+    {
+        CHAR_INFO fillChar = {{' '}, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED};
+        // Lines to scroll input/status down (if possible)
+        int linesDown = clamp(terminalHeight_ - inputLineRow_ - inputLineHeight_ - statusLines_, 0, lines);
+        if (linesDown > 0)
+        {
+            // Scroll input and status lines down
+            SMALL_RECT oldRect = {0, this->inputLineRow_,
+                this->terminalWidth_ - 1, this->inputLineRow_ + this->inputLineHeight_ + this->statusLines_ - 1};
+            this->inputLineRow_ += linesDown;
+            ScrollConsoleScreenBuffer(stdOutHandle_, &oldRect, NULL, makeCOORD(0, this->inputLineRow_), &fillChar);
+            // Move cursor down to the new bottom so the user can see the status lines
+            COORD pos = {0, this->inputLineRow_ + this->inputLineHeight_ - 1 + this->statusLines_};
+            SetConsoleCursorPosition(stdOutHandle_, pos);
+            // Get cursor back to the right position
+            this->cursorChanged();
+        }
+        // Check how many lines we still have to scroll up the output
+        if (lines - linesDown > 0)
+        {
+            // Scroll output up
+            SMALL_RECT oldRect = {0, lines - linesDown, this->terminalWidth_ - 1, this->inputLineRow_ - 1};
+            ScrollConsoleScreenBuffer(stdOutHandle_, &oldRect, NULL, makeCOORD(0, 0), &fillChar);
+        }
+    }
+
+    // ###############################
+    // ###  ShellListener methods  ###
+    // ###############################
+
+    //! Called if all output-lines have to be reprinted
+    void IOConsole::linesChanged()
+    {
+        // Method only gets called upon start to draw all the lines
+        // or when scrolling. But scrolling is disabled and the output
+        // is already in std::cout when we start the IOConsole
+    }
+
+    //! Called if a command is about to be executed
+    void IOConsole::executed()
+    {
+        this->shell_->addOutput(this->promptString_ + this->shell_->getInput() + '\n', Shell::Command);
+    }
+
+    //! Called if the console gets closed
+    void IOConsole::exit()
+    {
+        // Exit is not an option, just do nothing (Shell doesn't really exit too)
+    }
+
+    //! Called if the text in the input line has changed
+    void IOConsole::inputChanged()
+    {
+        int newInputLineLength = this->promptString_.size() + this->shell_->getInput().size();
+        int newInputLineHeight = 1 + newInputLineLength / this->terminalWidth_;
+        int newLines = newInputLineHeight - this->inputLineHeight_;
+        if (newLines > 0)
+        {
+            // Abuse this function to scroll the console
+            this->createNewOutputLines(newLines);
+            // Either Compensate for side effects (input/status lines scrolled down)
+            // or we have to do this anyway (output scrolled up)
+            this->inputLineRow_ -= newLines;
+        }
+        else if (newLines < 0)
+        {
+            // Scroll status lines up
+            int statusLineRow = this->inputLineRow_ + this->inputLineHeight_;
+            SMALL_RECT oldRect = {0, statusLineRow, this->terminalWidth_ - 1, statusLineRow + this->statusLines_};
+            CHAR_INFO fillChar = {{' '}, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED};
+            ScrollConsoleScreenBuffer(stdOutHandle_, &oldRect, NULL, makeCOORD(0, statusLineRow + newLines), &fillChar);
+            // Clear potential leftovers
+            if (-newLines - this->statusLines_ > 0)
+            {
+                COORD pos = {0, this->inputLineRow_ + newInputLineHeight + this->statusLines_};
+                this->writeText(std::string((-newLines - this->statusLines_) * this->terminalWidth_, ' '), pos);
+            }
+        }
+        this->inputLineHeight_ = newInputLineHeight;
+
+        // Print the whole line, including spaces that erase leftovers
+        std::string inputLine = this->promptString_ + this->shell_->getInput();
+        inputLine += std::string(this->terminalWidth_ - newInputLineLength % this->terminalWidth_, ' ');
+        this->writeText(inputLine, makeCOORD(0, this->inputLineRow_), FOREGROUND_GREEN | FOREGROUND_INTENSITY);
+        // If necessary, move cursor
+        if (newLines != 0)
+            this->cursorChanged();
+    }
+
+    //! Called if the position of the cursor in the input-line has changed
+    void IOConsole::cursorChanged()
+    {
+        int rawCursorPos = this->promptString_.size() + this->buffer_->getCursorPosition();
+        // Compensate for cursor further to the right than the terminal width
+        COORD pos;
+        pos.X = rawCursorPos % this->terminalWidth_;
+        pos.Y = this->inputLineRow_ + rawCursorPos / this->terminalWidth_;
+        SetConsoleCursorPosition(stdOutHandle_, pos);
+    }
+
+    //! Called if only the last output-line has changed
+    void IOConsole::onlyLastLineChanged()
+    {
+        int newLineHeight = 1 + this->shell_->getNewestLineIterator()->first.size() / this->terminalWidth_;
+        // Compute the number of new lines needed
+        int newLines = newLineHeight - this->lastOutputLineHeight_;
+        this->lastOutputLineHeight_ = newLineHeight;
+        // Scroll console if necessary
+        if (newLines > 0) // newLines < 0 is assumed impossible
+            this->createNewOutputLines(newLines);
+        Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator();
+        this->printOutputLine(it->first, it->second, makeCOORD(0, this->inputLineRow_ - newLineHeight));
+    }
+
+    //! Called if a new output line was added
+    void IOConsole::lineAdded()
+    {
+        Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator();
+        // Scroll console
+        this->lastOutputLineHeight_ = 1 + it->first.size() / this->terminalWidth_;
+        this->createNewOutputLines(this->lastOutputLineHeight_);
+        // Write the text
+        COORD pos = {0, this->inputLineRow_ - this->lastOutputLineHeight_};
+        this->printOutputLine(it->first, it->second, pos);
+    }
+}
Index: /code/trunk/src/libraries/core/command/IOConsoleWindows.h
===================================================================
--- /code/trunk/src/libraries/core/command/IOConsoleWindows.h	(revision 7287)
+++ /code/trunk/src/libraries/core/command/IOConsoleWindows.h	(revision 7287)
@@ -0,0 +1,108 @@
+/*
+ *   ORXONOX - the hottest 3D action shooter ever to exist
+ *                    > www.orxonox.net <
+ *
+ *
+ *   License notice:
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation; either version 2
+ *   of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ *   Author:
+ *      Reto Grieder
+ *   Co-authors:
+ *      ...
+ *
+ */
+
+#ifndef _IOConsole_H__
+#define _IOConsole_H__
+
+#include "core/CorePrereqs.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include "util/Singleton.h"
+#include "Shell.h"
+
+#define WIN32_LEAN_AND_MEAN
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#include <windows.h>
+
+namespace orxonox
+{
+    class _CoreExport IOConsole : public Singleton<IOConsole>, public ShellListener
+    {
+        friend class Singleton<IOConsole>;
+
+    public:
+        IOConsole();
+        ~IOConsole();
+
+        void preUpdate(const Clock& time);
+
+    private:
+        void setTerminalMode();
+        void getTerminalSize();
+        void printStatusLines();
+        static int extractLogLevel(std::string* text);
+
+        // Methods from ShellListener
+        void linesChanged();
+        void onlyLastLineChanged();
+        void lineAdded();
+        void inputChanged();
+        void cursorChanged();
+        void executed();
+        void exit();
+
+        void resetTerminalMode();
+        void moveCursor(int dx, int dy);
+        void writeText(const std::string& text, const COORD& pos, WORD attributes = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
+        void createNewOutputLines(int lines);
+        void printOutputLine(const std::string& line, Shell::LineType type, const COORD& pos);
+
+        static inline COORD makeCOORD(int x, int y)
+        {
+            COORD val = {x, y};
+            return val;
+        }
+
+        Shell*                  shell_;
+        InputBuffer*            buffer_;
+        std::ostream            cout_;
+        std::ostringstream      origCout_;
+        int                     terminalWidth_;
+        int                     terminalHeight_;
+        int                     lastTerminalWidth_;
+        int                     lastTerminalHeight_;
+        const std::string       promptString_;
+
+        DWORD                   originalTerminalSettings_;
+        HANDLE                  stdInHandle_;
+        HANDLE                  stdOutHandle_;
+        int                     inputLineRow_;
+        int                     inputLineHeight_;
+        const int               statusLines_;
+        int                     lastOutputLineHeight_;
+        uint64_t                lastRefreshTime_;
+
+        static IOConsole* singletonPtr_s;
+    };
+}
+
+#endif /* _IOConsole_H__ */
