[Orxonox-commit 1422] r6140 - code/branches/presentation2/src/libraries/core

rgrieder at orxonox.net rgrieder at orxonox.net
Wed Nov 25 00:09:02 CET 2009


Author: rgrieder
Date: 2009-11-25 00:09:02 +0100 (Wed, 25 Nov 2009)
New Revision: 6140

Modified:
   code/branches/presentation2/src/libraries/core/IOConsole.cc
   code/branches/presentation2/src/libraries/core/IOConsole.h
Log:
Extended IOConsole to a Windows implementation. It doesn't yet work entirely as I wish, but it's not too bad either.
If you don't like the colors, I agree ^^

Modified: code/branches/presentation2/src/libraries/core/IOConsole.cc
===================================================================
--- code/branches/presentation2/src/libraries/core/IOConsole.cc	2009-11-24 23:06:15 UTC (rev 6139)
+++ code/branches/presentation2/src/libraries/core/IOConsole.cc	2009-11-24 23:09:02 UTC (rev 6140)
@@ -31,6 +31,8 @@
 
 #include <iomanip>
 #include <iostream>
+
+#include "util/Math.h"
 #include "core/Game.h"
 #include "core/input/InputBuffer.h"
 
@@ -455,6 +457,8 @@
 // ###   Windows Implementation   ###
 // ##################################
 
+#include <windows.h>
+
 namespace orxonox
 {
     IOConsole::IOConsole()
@@ -464,7 +468,6 @@
         , bStatusPrinted_(false)
         , promptString_("orxonox # ")
     {
-/*
         this->setTerminalMode();
         this->shell_->registerListener(this);
 
@@ -478,70 +481,210 @@
 
         // 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());
+
+        // Make sure we make way for the status lines
+        this->update(Game::getInstance().getGameClock());
     }
 
     IOConsole::~IOConsole()
     {
-//        resetTerminalMode();
+        // Empty all buffers
+        this->update(Game::getInstance().getGameClock());
+
+        resetTerminalMode();
         this->shell_->destroy();
 
-/*
+        // Restore this->cout_ redirection
+        std::cout.rdbuf(this->cout_.rdbuf());
         // Enable standard this->cout_ logging again
         OutputHandler::getInstance().enableCout();
-*/
     }
 
     void IOConsole::update(const Clock& time)
     {
-/*
-        unsigned char c = 0;
-        while (std::cin.good())
+        while (true)
         {
-            c = std::cin.get();
-            if (std::cin.bad())
+            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::Back,     asciiChar, modifiersOut)); break;
+                case VK_RETURN: this->buffer_->buttonPressed(KeyEvent(KeyCode::Back,     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));
+                }
+            }
         }
-        // Reset error flags in std::cin
-        std::cin.clear();
 
-        // Determine terminal width and height
-        this->lastTerminalWidth_ = this->terminalWidth_;
-        this->lastTerminalHeight_ = this->terminalHeight_;
+        // Get info about cursor and terminal size
         this->getTerminalSize();
-*/
+
+        // Refresh status line
+        this->printStatusLines();
+
+        // Process output written to std::cout
+        if (!this->origCout_.str().empty())
+        {
+            this->shell_->addOutputLine(this->origCout_.str());
+            this->origCout_.str("");
+        }
+        this->cout_.flush();
     }
 
     void IOConsole::printLogText(const std::string& text)
     {
+        std::string output = text;
+        int level = this->extractLogLevel(&output);
+
+        // Colour line
+        switch (level)
+        {
+        case  1: SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_RED | FOREGROUND_INTENSITY); break;
+        case  2: SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY); break;
+        case  3: SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_BLUE | FOREGROUND_INTENSITY); break;
+        case  4: SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_GREEN); break;
+        default: break;
+        }
+
+        // Print output line
+        this->cout_ << output;
+
+        // Reset colour to white
+        SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN);
     }
 
     void IOConsole::printInputLine()
     {
+        this->moveCursorYAndHome(0);
+        this->clearCurrentLine();
+        this->cout_ << this->promptString_ << this->shell_->getInput();
+        this->moveCursorYAndHome(0);
+        this->moveCursor(this->promptString_.size() + this->buffer_->getCursorPosition(), 0);
     }
 
     void IOConsole::printStatusLines()
     {
-/*
         if (this->willPrintStatusLines())
         {
             this->bStatusPrinted_ = true;
+            // Put cursor on home position, one line down the input line
+            this->moveCursorYAndHome(1);
+            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";
+            // Clear rest of the line
+            CONSOLE_SCREEN_BUFFER_INFO info;
+            GetConsoleScreenBufferInfo(this->stdOutHandle_, &info);
+            this->cout_ << std::string(info.dwSize.X - info.dwCursorPosition.X - 1, ' ');
+            // Restore cursor position
+            this->moveCursorYAndHome(-1);
+            this->moveCursor(this->promptString_.size() + this->buffer_->getCursorPosition(), 0);
         }
         else
             this->bStatusPrinted_ = false;
-*/
     }
 
     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_);
     }
 
     void IOConsole::resetTerminalMode()
     {
+        SetConsoleMode(this->stdInHandle_, this->originalTerminalSettings_);
     }
 
+    //! Moves the console cursor around and inserts new lines when reaching the end.
+    //! Moving out on the right is just clamped though.
+    void IOConsole::moveCursor(int dx, int dy)
+    {
+        CONSOLE_SCREEN_BUFFER_INFO info;
+        GetConsoleScreenBufferInfo(this->stdOutHandle_, &info);
+        SHORT& x = info.dwCursorPosition.X;
+        x = clamp(x + dx, 0, info.dwSize.X - 1);
+        SHORT& y = info.dwCursorPosition.Y;
+        if (y + dy >= info.dwSize.Y)
+        {
+            // Insert new lines
+            this->cout_ << std::string(y + dy - info.dwSize.Y + 1, 'n');
+            y = info.dwSize.Y - 1;
+        }
+        else if (y < 0)
+            y = 0;
+        else
+            y += dy;
+        SetConsoleCursorPosition(this->stdOutHandle_, info.dwCursorPosition);
+    }
+
+    void IOConsole::moveCursorYAndHome(int dy)
+    {
+        CONSOLE_SCREEN_BUFFER_INFO info;
+        GetConsoleScreenBufferInfo(this->stdOutHandle_, &info);
+        this->moveCursor(-info.dwCursorPosition.X, dy);
+    }
+
+    void IOConsole::clearCurrentLine()
+    {
+        CONSOLE_SCREEN_BUFFER_INFO info;
+        GetConsoleScreenBufferInfo(this->stdOutHandle_, &info);
+        info.dwCursorPosition.X = 0;
+        DWORD count;
+        FillConsoleOutputCharacter(this->stdOutHandle_, ' ', info.dwSize.X, info.dwCursorPosition, &count);;
+    }
+
     void IOConsole::getTerminalSize()
     {
+        CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
+        GetConsoleScreenBufferInfo(this->stdOutHandle_, &screenBufferInfo);
+        // dwSize is the maximum size. If you resize you will simply see scroll bars.
+        // And if you want to write outside these boundaries, you can't.
+        this->terminalWidth_  = screenBufferInfo.dwSize.X;
+        this->terminalHeight_ = screenBufferInfo.dwSize.Y;
     }
 
     // ###############################
@@ -551,11 +694,27 @@
     //! Called if only the last output-line has changed
     void IOConsole::onlyLastLineChanged()
     {
+        this->moveCursorYAndHome(-1);
+        this->clearCurrentLine();
+        this->printLogText(*(this->shell_->getNewestLineIterator()));
+        this->moveCursorYAndHome(1);
+        this->moveCursor(this->promptString_.size() + this->shell_->getInput().size(), 0);
+        this->cout_.flush();
     }
 
     //! Called if a new output-line was added
     void IOConsole::lineAdded()
     {
+        // Move cursor to the beginning of the new (last) output line
+        this->moveCursorYAndHome(0);
+        // Print the new output lines
+        this->printLogText(*(this->shell_->getNewestLineIterator()));
+        // Move cursor down
+        this->moveCursorYAndHome(1);
+        // Print status and input lines
+        this->printInputLine();
+        this->printStatusLines();
+        this->cout_.flush();
     }
 }
 

Modified: code/branches/presentation2/src/libraries/core/IOConsole.h
===================================================================
--- code/branches/presentation2/src/libraries/core/IOConsole.h	2009-11-24 23:06:15 UTC (rev 6139)
+++ code/branches/presentation2/src/libraries/core/IOConsole.h	2009-11-24 23:09:02 UTC (rev 6140)
@@ -40,6 +40,9 @@
 
 #ifdef ORXONOX_PLATFORM_UNIX
 struct termios;
+#elif defined(ORXONOX_PLATFORM_WINDOWS)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
 #endif
 
 namespace orxonox
@@ -90,6 +93,14 @@
 
 #ifdef ORXONOX_PLATFORM_UNIX
         termios*                originalTerminalSettings_;
+#elif defined(ORXONOX_PLATFORM_WINDOWS)
+        void moveCursor(int dx, int dy);
+        void moveCursorYAndHome(int dy);
+        void clearCurrentLine();
+
+        DWORD                   originalTerminalSettings_;
+        HANDLE                  stdInHandle_;
+        HANDLE                  stdOutHandle_;
 #endif
 
         static IOConsole* singletonPtr_s;




More information about the Orxonox-commit mailing list