[Orxonox-commit 2582] r7287 - code/trunk/src/libraries/core/command
rgrieder at orxonox.net
rgrieder at orxonox.net
Tue Aug 31 11:13:47 CEST 2010
Author: rgrieder
Date: 2010-08-31 11:13:46 +0200 (Tue, 31 Aug 2010)
New Revision: 7287
Added:
code/trunk/src/libraries/core/command/IOConsolePOSIX.cc
code/trunk/src/libraries/core/command/IOConsolePOSIX.h
code/trunk/src/libraries/core/command/IOConsoleWindows.cc
code/trunk/src/libraries/core/command/IOConsoleWindows.h
Modified:
code/trunk/src/libraries/core/command/IOConsole.cc
code/trunk/src/libraries/core/command/IOConsole.h
Log:
Split IOConsole in two separate files for better browsing.
Modified: code/trunk/src/libraries/core/command/IOConsole.cc
===================================================================
--- code/trunk/src/libraries/core/command/IOConsole.cc 2010-08-31 08:09:12 UTC (rev 7286)
+++ code/trunk/src/libraries/core/command/IOConsole.cc 2010-08-31 09:13:46 UTC (rev 7287)
@@ -20,7 +20,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Author:
- * Oliver Scheuss
* Reto Grieder
* Co-authors:
* ...
@@ -29,771 +28,8 @@
#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;
-#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();
- }
-}
-
+#if defined(ORXONOX_PLATFORM_UNIX)
+ #include "IOConsolePOSIX.cc"
#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 */
+ #include "IOConsoleWindows.cc"
+#endif
Modified: code/trunk/src/libraries/core/command/IOConsole.h
===================================================================
--- code/trunk/src/libraries/core/command/IOConsole.h 2010-08-31 08:09:12 UTC (rev 7286)
+++ code/trunk/src/libraries/core/command/IOConsole.h 2010-08-31 09:13:46 UTC (rev 7287)
@@ -20,109 +20,16 @@
* 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 "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__ */
Copied: code/trunk/src/libraries/core/command/IOConsolePOSIX.cc (from rev 7265, code/trunk/src/libraries/core/IOConsole.cc)
===================================================================
--- code/trunk/src/libraries/core/command/IOConsolePOSIX.cc (rev 0)
+++ code/trunk/src/libraries/core/command/IOConsolePOSIX.cc 2010-08-31 09:13:46 UTC (rev 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();
+ }
+}
Copied: code/trunk/src/libraries/core/command/IOConsolePOSIX.h (from rev 7265, code/trunk/src/libraries/core/IOConsole.h)
===================================================================
--- code/trunk/src/libraries/core/command/IOConsolePOSIX.h (rev 0)
+++ code/trunk/src/libraries/core/command/IOConsolePOSIX.h 2010-08-31 09:13:46 UTC (rev 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__ */
Copied: code/trunk/src/libraries/core/command/IOConsoleWindows.cc (from rev 7265, code/trunk/src/libraries/core/IOConsole.cc)
===================================================================
--- code/trunk/src/libraries/core/command/IOConsoleWindows.cc (rev 0)
+++ code/trunk/src/libraries/core/command/IOConsoleWindows.cc 2010-08-31 09:13:46 UTC (rev 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);
+ }
+}
Copied: code/trunk/src/libraries/core/command/IOConsoleWindows.h (from rev 7265, code/trunk/src/libraries/core/IOConsole.h)
===================================================================
--- code/trunk/src/libraries/core/command/IOConsoleWindows.h (rev 0)
+++ code/trunk/src/libraries/core/command/IOConsoleWindows.h 2010-08-31 09:13:46 UTC (rev 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__ */
More information about the Orxonox-commit
mailing list