Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentation2/src/libraries/core/IOConsole.cc @ 6171

Last change on this file since 6171 was 6171, checked in by scheusso, 14 years ago

fixed problem with ioconsole not restoring terminal settings when a segfault/sigabrt/etc occured

  • Property svn:eol-style set to native
File size: 27.3 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Oliver Scheuss
24 *      Reto Grieder
25 *   Co-authors:
26 *      ...
27 *
28 */
29
30#include "IOConsole.h"
31
32#include <iomanip>
33#include <iostream>
34
35#include "util/Math.h"
36#include "core/Game.h"
37#include "core/input/InputBuffer.h"
38
39// ##########################
40// ###   Mutual methods   ###
41// ##########################
42namespace orxonox
43{
44    IOConsole* IOConsole::singletonPtr_s = NULL;
45
46    int IOConsole::extractLogLevel(std::string* text)
47    {
48        // Handle line colouring by inspecting the first letter
49        char level = 0;
50        if (!text->empty())
51        {
52            level = (*text)[0];
53            if (level == -1 || (level >= 1 && level <= 6))
54            {
55                *text = text->substr(1);
56                if (level != -1)
57                    return level;
58            }
59        }
60        return 0;
61    }
62
63    inline bool IOConsole::willPrintStatusLines()
64    {
65        return !this->statusLineWidths_.empty()
66             && this->terminalWidth_  >= this->statusLineMaxWidth_
67             && this->terminalHeight_ >= (this->minOutputLines_ + this->statusLineWidths_.size());
68    }
69
70    // ###############################
71    // ###  ShellListener methods  ###
72    // ###############################
73
74    //! Called if all output-lines have to be reprinted
75    void IOConsole::linesChanged()
76    {
77        // Method only gets called upon start to draw all the lines
78        // or when scrolling. But scrolling is disabled and the output
79        // is already in std::cout when we start the IOConsole
80    }
81
82    //! Called if the text in the input-line has changed
83    void IOConsole::inputChanged()
84    {
85        this->printInputLine();
86        this->cout_.flush();
87    }
88
89    //! Called if the position of the cursor in the input-line has changed
90    void IOConsole::cursorChanged()
91    {
92        this->printInputLine();
93        this->cout_.flush();
94    }
95
96    //! Called if a command is about to be executed
97    void IOConsole::executed()
98    {
99        this->shell_->addOutputLine(this->promptString_ + this->shell_->getInput());
100    }
101
102    //! Called if the console gets closed
103    void IOConsole::exit()
104    {
105        // Exit is not an option, just do nothing (Shell doesn't really exit too)
106    }
107}
108
109#ifdef ORXONOX_PLATFORM_UNIX
110// ###############################
111// ###   Unix Implementation   ###
112// ###############################
113
114#include <termios.h>
115#include <sys/ioctl.h>
116
117namespace orxonox
118{
119    termios* IOConsole::originalTerminalSettings_s = 0;
120   
121    namespace EscapeMode
122    {
123        enum Value
124        {
125            None,
126            First,
127            Second
128        };
129    }
130
131    IOConsole::IOConsole()
132        : shell_(new Shell("IOConsole", false, true))
133        , buffer_(shell_->getInputBuffer())
134        , cout_(std::cout.rdbuf())
135        , bStatusPrinted_(false)
136        , promptString_("orxonox # ")
137    {
138        this->setTerminalMode();
139        this->shell_->registerListener(this);
140
141        // Manually set the widths of the individual status lines
142        this->statusLineWidths_.push_back(29);
143        this->statusLineMaxWidth_ = 29;
144
145        this->getTerminalSize();
146        this->lastTerminalWidth_ = this->terminalWidth_;
147        this->lastTerminalHeight_ = this->terminalHeight_;
148
149        // Disable standard std::cout logging
150        OutputHandler::getInstance().disableCout();
151        // Redirect std::cout to an ostringstream
152        // (Other part is in the initialiser list)
153        std::cout.rdbuf(this->origCout_.rdbuf());
154
155        // Make sure we make way for the status lines
156        this->update(Game::getInstance().getGameClock());
157    }
158
159    IOConsole::~IOConsole()
160    {
161        // Empty all buffers
162        this->update(Game::getInstance().getGameClock());
163        // Erase input and status lines
164        this->cout_ << "\033[1G\033[J";
165        // Move cursor to the bottom
166        this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B';
167        // Scroll terminal to compensate for erased lines
168        this->cout_ << "\033[" << this->statusLineWidths_.size() << 'T';
169
170        resetTerminalMode();
171        this->shell_->destroy();
172
173        // Restore this->cout_ redirection
174        std::cout.rdbuf(this->cout_.rdbuf());
175        // Enable standard std::cout logging again
176        OutputHandler::getInstance().enableCout();
177    }
178
179    void IOConsole::update(const Clock& time)
180    {
181        unsigned char c;
182        std::string escapeSequence;
183        EscapeMode::Value escapeMode = EscapeMode::None;
184        while (std::cin.good())
185        {
186            c = std::cin.get();
187            if (!std::cin.good())
188                break;
189
190            if (escapeMode == EscapeMode::First && (c == '[' || c=='O') )
191                escapeMode = EscapeMode::Second;
192            // Get Alt+Tab combination when switching applications
193            else if (escapeMode == EscapeMode::First && c == '\t')
194            {
195                this->buffer_->buttonPressed(KeyEvent(KeyCode::Tab, '\t', KeyboardModifier::Alt));
196                escapeMode = EscapeMode::None;
197            }
198            else if (escapeMode == EscapeMode::Second)
199            {
200                escapeSequence += c;
201                escapeMode = EscapeMode::None;
202                if      (escapeSequence == "A")
203                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Up,       0, 0));
204                else if (escapeSequence == "B")
205                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Down,     0, 0));
206                else if (escapeSequence == "C")
207                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Right,    0, 0));
208                else if (escapeSequence == "D")
209                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Left,     0, 0));
210                else if (escapeSequence == "1~" || escapeSequence == "H")
211                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Home,     0, 0));
212                else if (escapeSequence == "2~")
213                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert,   0, 0));
214                else if (escapeSequence == "3~")
215                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete,   0, 0));
216                else if (escapeSequence == "4~" || escapeSequence == "F")
217                    this->buffer_->buttonPressed(KeyEvent(KeyCode::End,      0, 0));
218                else if (escapeSequence == "5~")
219                    this->buffer_->buttonPressed(KeyEvent(KeyCode::PageUp,   0, 0));
220                else if (escapeSequence == "6~")
221                    this->buffer_->buttonPressed(KeyEvent(KeyCode::PageDown, 0, 0));
222                else
223                    // Waiting for sequence to complete
224                    // If the user presses ESC and then '[' or 'O' while the loop is not
225                    // running (for instance while loading), the whole sequence gets dropped
226                    escapeMode = EscapeMode::Second;
227            }
228            else // not in an escape sequence OR user might have pressed just ESC
229            {
230                if (escapeMode == EscapeMode::First)
231                {
232                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, c, 0));
233                    escapeMode = EscapeMode::None;
234                }
235                if (c == '\033')
236                {
237                    escapeMode = EscapeMode::First;
238                    escapeSequence.clear();
239                }
240                else
241                {
242                    KeyCode::ByEnum code;
243                    switch (c)
244                    {
245                    case '\n'  : case '\r': code = KeyCode::Return; break;
246                    case '\177': case '\b': code = KeyCode::Back;   break;
247                    case '\t'             : code = KeyCode::Tab;    break;
248                    default:
249                        // We don't encode the key code (would be a very large switch)
250                        // because the InputBuffer will only insert the text anyway
251                        // Replacement character is simply KeyCode::A
252                        code = KeyCode::A;
253                    }
254                    this->buffer_->buttonPressed(KeyEvent(code, c, 0));
255                }
256            }
257        }
258        // Reset error flags in std::cin
259        std::cin.clear();
260
261        // If there is still an escape key pending (escape key ONLY), then
262        // it sure isn't an escape sequence anymore
263        if (escapeMode == EscapeMode::First)
264            this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, '\033', 0));
265
266        // Determine terminal width and height
267        this->lastTerminalWidth_ = this->terminalWidth_;
268        this->lastTerminalHeight_ = this->terminalHeight_;
269        this->getTerminalSize();
270
271        int heightDiff = this->terminalHeight_ - this->lastTerminalHeight_;
272        if (this->bStatusPrinted_ && heightDiff < 0)
273        {
274            // Terminal width has shrunk. The cursor will still be on the input line,
275            // but that line might very well be the last
276            int newLines = std::min((int)this->statusLineWidths_.size(), -heightDiff);
277            // Scroll terminal to create new lines
278            this->cout_ << "\033[" << newLines << 'S';
279        }
280
281        if (!this->bStatusPrinted_ && this->willPrintStatusLines())
282        {
283            // Scroll console to make way for status lines
284            this->cout_ << "\033[" << this->statusLineWidths_.size() << 'S';
285            this->bStatusPrinted_ = true;
286        }
287
288        // We always assume that the cursor is on the inputline.
289        // But we cannot always be sure about that, esp. if we scroll the console
290        this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B';
291        this->cout_ << "\033[" << this->statusLineWidths_.size() << 'A';
292
293        // Erase status and input lines
294        this->cout_ << "\033[1G\033[J";
295        this->printInputLine();
296        this->printStatusLines();
297        this->cout_.flush();
298
299        // Process output written to std::cout
300        if (!this->origCout_.str().empty())
301        {
302            this->shell_->addOutputLine(this->origCout_.str());
303            this->origCout_.str("");
304        }
305    }
306
307    void IOConsole::printLogText(const std::string& text)
308    {
309        std::string output = text;
310        /*int level =*/ this->extractLogLevel(&output);
311
312/*
313        // Colour line
314        switch (level)
315        {
316        case -1: this->cout_ << "\033[37m"; break;
317        case  1: this->cout_ << "\033[91m"; break;
318        case  2: this->cout_ << "\033[31m"; break;
319        case  3: this->cout_ << "\033[34m"; break;
320        case  4: this->cout_ << "\033[36m"; break;
321        case  5: this->cout_ << "\033[35m"; break;
322        case  6: this->cout_ << "\033[37m"; break;
323        default: break;
324        }
325*/
326
327        // Print output line
328        this->cout_ << output;
329
330        // Reset colour to white
331//        this->cout_ << "\033[37m";
332    }
333
334    void IOConsole::printInputLine()
335    {
336        // Set cursor to the beginning of the line and erase the line
337        this->cout_ << "\033[1G\033[K";
338        // Indicate a command prompt
339        this->cout_ << this->promptString_;
340        // Save cursor position
341        this->cout_ << "\033[s";
342        // Print command line buffer
343        this->cout_ << this->shell_->getInput();
344        // Restore cursor position and move it to the right
345        this->cout_ << "\033[u";
346        if (this->buffer_->getCursorPosition() > 0)
347            this->cout_ << "\033[" << this->buffer_->getCursorPosition() << "C";
348    }
349
350    void IOConsole::printStatusLines()
351    {
352        if (this->willPrintStatusLines())
353        {
354            // Save cursor position
355            this->cout_ << "\033[s";
356            // Move cursor down (don't create a new line here because the buffer might flush then!)
357            this->cout_ << "\033[1B\033[1G";
358            this->cout_ << std::fixed << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgFPS() << " fps, ";
359            this->cout_ <<               std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgTickTime() << " ms tick time";
360            // Restore cursor position
361            this->cout_ << "\033[u";
362            this->bStatusPrinted_ = true;
363        }
364        else
365            this->bStatusPrinted_ = false;
366    }
367
368    void IOConsole::setTerminalMode()
369    {
370        termios new_settings;
371        IOConsole::originalTerminalSettings_s = new termios();
372
373        tcgetattr(0, this->originalTerminalSettings_s);
374        new_settings = *this->originalTerminalSettings_s;
375        new_settings.c_lflag &= ~(ICANON | ECHO);
376        //new_settings.c_lflag |= (ISIG | IEXTEN);
377        new_settings.c_cc[VTIME] = 0;
378        new_settings.c_cc[VMIN]  = 0;
379        tcsetattr(0, TCSANOW, &new_settings);
380        atexit(&IOConsole::resetTerminalMode);
381    }
382
383    /*static*/ void IOConsole::resetTerminalMode()
384    {
385        if(IOConsole::originalTerminalSettings_s)
386        {
387            tcsetattr(0, TCSANOW, IOConsole::originalTerminalSettings_s);
388            delete IOConsole::originalTerminalSettings_s;
389            IOConsole::originalTerminalSettings_s = 0;
390        }
391    }
392
393    void IOConsole::getTerminalSize()
394    {
395#ifdef TIOCGSIZE
396        struct ttysize win;
397        if (!ioctl(STDIN_FILENO, TIOCGSIZE, &win))
398        {
399            this->terminalWidth_  = win.ts_cols;
400            this->terminalHeight_ = win.ts_lines;
401            return;
402        }
403#elif defined TIOCGWINSZ
404        struct winsize win;
405        if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &win))
406        {
407            this->terminalWidth_  = win.ws_col;
408            this->terminalHeight_ = win.ws_row;
409            return;
410        }
411#else
412        const char* s = getenv("COLUMNS");
413        this->terminalWidth_  = s ? strtol(s, NULL, 10) : 80;
414        s = getenv("LINES");
415        this->terminalHeight_ = s ? strtol(s, NULL, 10) : 24;
416        return;
417#endif
418        this->terminalWidth_  = 80;
419        this->terminalHeight_ = 24;
420    }
421
422    // ###############################
423    // ###  ShellListener methods  ###
424    // ###############################
425
426    //! Called if only the last output-line has changed
427    void IOConsole::onlyLastLineChanged()
428    {
429        // Save cursor position and move it to the beginning of the first output line
430        this->cout_ << "\033[s\033[1A\033[1G";
431        // Erase the line
432        this->cout_ << "\033[K";
433        // Reprint the last output line
434        this->printLogText(*(this->shell_->getNewestLineIterator()));
435        // Restore cursor
436        this->cout_ << "\033[u";
437        this->cout_.flush();
438    }
439
440    //! Called if a new output-line was added
441    void IOConsole::lineAdded()
442    {
443        int newLines = this->shell_->getNewestLineIterator()->size() / this->terminalWidth_ + 1;
444        // Create new lines by scrolling the screen
445        this->cout_ << "\033[" << newLines << 'S';
446        // Move cursor to the beginning of the new (last) output line
447        this->cout_ << "\033[" << newLines << "A\033[1G";
448        // Erase screen from here
449        this->cout_ << "\033[J";
450        // Print the new output lines
451        for (int i = 0; i < newLines; ++i)
452            this->printLogText(this->shell_->getNewestLineIterator()->substr(i*this->terminalWidth_, this->terminalWidth_));
453        // Move cursor down
454        this->cout_ << "\033[1B\033[1G";
455        // Print status and input lines
456        this->printInputLine();
457        this->printStatusLines();
458        this->cout_.flush();
459    }
460}
461
462#elif defined(ORXONOX_PLATFORM_WINDOWS)
463// ##################################
464// ###   Windows Implementation   ###
465// ##################################
466
467#include <windows.h>
468
469namespace orxonox
470{
471    IOConsole::IOConsole()
472        : shell_(new Shell("IOConsole", false, true))
473        , buffer_(shell_->getInputBuffer())
474        , cout_(std::cout.rdbuf())
475        , bStatusPrinted_(false)
476        , promptString_("orxonox # ")
477    {
478        this->setTerminalMode();
479        this->shell_->registerListener(this);
480
481        // Manually set the widths of the individual status lines
482        this->statusLineWidths_.push_back(29);
483        this->statusLineMaxWidth_ = 29;
484
485        this->getTerminalSize();
486        this->lastTerminalWidth_ = this->terminalWidth_;
487        this->lastTerminalHeight_ = this->terminalHeight_;
488
489        // Disable standard this->cout_ logging
490        OutputHandler::getInstance().disableCout();
491        // Redirect std::cout to an ostringstream
492        // (Other part is in the initialiser list)
493        std::cout.rdbuf(this->origCout_.rdbuf());
494
495        // Make sure we make way for the status lines
496        this->update(Game::getInstance().getGameClock());
497    }
498
499    IOConsole::~IOConsole()
500    {
501        // Empty all buffers
502        this->update(Game::getInstance().getGameClock());
503
504        resetTerminalMode();
505        this->shell_->destroy();
506
507        // Restore this->cout_ redirection
508        std::cout.rdbuf(this->cout_.rdbuf());
509        // Enable standard this->cout_ logging again
510        OutputHandler::getInstance().enableCout();
511    }
512
513    void IOConsole::update(const Clock& time)
514    {
515        while (true)
516        {
517            DWORD count;
518            INPUT_RECORD inrec;
519            PeekConsoleInput(this->stdInHandle_, &inrec, 1, &count);
520            if (count == 0)
521                break;
522            ReadConsoleInput(this->stdInHandle_, &inrec, 1, &count);
523            if (inrec.EventType == KEY_EVENT && inrec.Event.KeyEvent.bKeyDown)
524            {
525                // Process keyboard modifiers (Ctrl, Alt and Shift)
526                DWORD modifiersIn = inrec.Event.KeyEvent.dwControlKeyState;
527                int modifiersOut = 0;
528                if ((modifiersIn & (LEFT_ALT_PRESSED  | RIGHT_ALT_PRESSED))  != 0)
529                    modifiersOut |= KeyboardModifier::Alt;
530                if ((modifiersIn & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0)
531                    modifiersOut |= KeyboardModifier::Ctrl;
532                if ((modifiersIn & SHIFT_PRESSED) != 0)
533                    modifiersOut |= KeyboardModifier::Shift;
534
535                // ASCII character (0 for special keys)
536                char asciiChar = inrec.Event.KeyEvent.uChar.AsciiChar;
537
538                // Process special keys and if not found, use Key::A as dummy (InputBuffer uses the ASCII text anyway)
539                switch (inrec.Event.KeyEvent.wVirtualKeyCode)
540                {
541                case VK_BACK:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Back,     asciiChar, modifiersOut)); break;
542                case VK_TAB:    this->buffer_->buttonPressed(KeyEvent(KeyCode::Back,     asciiChar, modifiersOut)); break;
543                case VK_RETURN: this->buffer_->buttonPressed(KeyEvent(KeyCode::Back,     asciiChar, modifiersOut)); break;
544                case VK_PAUSE:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Pause,    asciiChar, modifiersOut)); break;
545                case VK_ESCAPE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape,   asciiChar, modifiersOut)); break;
546                case VK_SPACE:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Space,    asciiChar, modifiersOut)); break;
547                case VK_PRIOR:  this->buffer_->buttonPressed(KeyEvent(KeyCode::PageUp,   asciiChar, modifiersOut)); break;
548                case VK_NEXT:   this->buffer_->buttonPressed(KeyEvent(KeyCode::PageDown, asciiChar, modifiersOut)); break;
549                case VK_END:    this->buffer_->buttonPressed(KeyEvent(KeyCode::End,      asciiChar, modifiersOut)); break;
550                case VK_HOME:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Home,     asciiChar, modifiersOut)); break;
551                case VK_LEFT:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Left,     asciiChar, modifiersOut)); break;
552                case VK_UP:     this->buffer_->buttonPressed(KeyEvent(KeyCode::Up,       asciiChar, modifiersOut)); break;
553                case VK_RIGHT:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Right,    asciiChar, modifiersOut)); break;
554                case VK_DOWN:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Down,     asciiChar, modifiersOut)); break;
555                case VK_INSERT: this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert,   asciiChar, modifiersOut)); break;
556                case VK_DELETE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete,   asciiChar, modifiersOut)); break;
557                default:        this->buffer_->buttonPressed(KeyEvent(KeyCode::A,        asciiChar, modifiersOut));
558                }
559            }
560        }
561
562        // Get info about cursor and terminal size
563        this->getTerminalSize();
564
565        // Refresh status line
566        this->printStatusLines();
567
568        // Process output written to std::cout
569        if (!this->origCout_.str().empty())
570        {
571            this->shell_->addOutputLine(this->origCout_.str());
572            this->origCout_.str("");
573        }
574        this->cout_.flush();
575    }
576
577    void IOConsole::printLogText(const std::string& text)
578    {
579        std::string output = text;
580        int level = this->extractLogLevel(&output);
581
582        // Colour line
583        switch (level)
584        {
585        case  1: SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_RED | FOREGROUND_INTENSITY); break;
586        case  2: SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY); break;
587        case  3: SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_INTENSITY); break;
588        case  4: SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_INTENSITY); break;
589        default: break;
590        }
591
592        // Print output line
593        this->cout_ << output;
594
595        // Reset colour to white
596        SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN);
597    }
598
599    void IOConsole::printInputLine()
600    {
601        SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
602        this->moveCursorYAndHome(0);
603        this->clearCurrentLine();
604        this->cout_ << this->promptString_ << this->shell_->getInput();
605        this->moveCursorYAndHome(0);
606        this->moveCursor(this->promptString_.size() + this->buffer_->getCursorPosition(), 0);
607        SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN);
608    }
609
610    void IOConsole::printStatusLines()
611    {
612        if (this->willPrintStatusLines())
613        {
614            SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_GREEN);
615            this->bStatusPrinted_ = true;
616            // Put cursor on home position, one line down the input line
617            this->moveCursorYAndHome(1);
618            this->cout_ << std::fixed << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgFPS() << " fps, ";
619            this->cout_ <<               std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgTickTime() << " ms tick time";
620            // Clear rest of the line
621            CONSOLE_SCREEN_BUFFER_INFO info;
622            GetConsoleScreenBufferInfo(this->stdOutHandle_, &info);
623            this->cout_ << std::string(info.dwSize.X - info.dwCursorPosition.X - 1, ' ');
624            // Restore cursor position
625            this->moveCursorYAndHome(-1);
626            this->moveCursor(this->promptString_.size() + this->buffer_->getCursorPosition(), 0);
627            SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN);
628        }
629        else
630            this->bStatusPrinted_ = false;
631    }
632
633    void IOConsole::setTerminalMode()
634    {
635        // Set the console mode to no-echo, raw input, and no window or mouse events
636        this->stdOutHandle_ = GetStdHandle(STD_OUTPUT_HANDLE);
637        this->stdInHandle_  = GetStdHandle(STD_INPUT_HANDLE);
638        if (this->stdInHandle_ == INVALID_HANDLE_VALUE
639            || !GetConsoleMode(this->stdInHandle_, &this->originalTerminalSettings_)
640            || !SetConsoleMode(this->stdInHandle_, 0))
641        {
642            COUT(1) << "Error: Could not set Windows console settings" << std::endl;
643            return;
644        }
645        FlushConsoleInputBuffer(this->stdInHandle_);
646    }
647
648    void IOConsole::resetTerminalMode()
649    {
650        SetConsoleMode(this->stdInHandle_, this->originalTerminalSettings_);
651    }
652
653    //! Moves the console cursor around and inserts new lines when reaching the end.
654    //! Moving out on the right is just clamped though.
655    void IOConsole::moveCursor(int dx, int dy)
656    {
657        CONSOLE_SCREEN_BUFFER_INFO info;
658        GetConsoleScreenBufferInfo(this->stdOutHandle_, &info);
659        SHORT& x = info.dwCursorPosition.X;
660        x = clamp(x + dx, 0, info.dwSize.X - 1);
661        SHORT& y = info.dwCursorPosition.Y;
662        if (y + dy >= info.dwSize.Y)
663        {
664            // Insert new lines
665            this->cout_ << std::string(y + dy - info.dwSize.Y + 1, 'n');
666            y = info.dwSize.Y - 1;
667        }
668        else if (y < 0)
669            y = 0;
670        else
671            y += dy;
672        SetConsoleCursorPosition(this->stdOutHandle_, info.dwCursorPosition);
673    }
674
675    void IOConsole::moveCursorYAndHome(int dy)
676    {
677        CONSOLE_SCREEN_BUFFER_INFO info;
678        GetConsoleScreenBufferInfo(this->stdOutHandle_, &info);
679        this->moveCursor(-info.dwCursorPosition.X, dy);
680    }
681
682    void IOConsole::clearCurrentLine()
683    {
684        CONSOLE_SCREEN_BUFFER_INFO info;
685        GetConsoleScreenBufferInfo(this->stdOutHandle_, &info);
686        info.dwCursorPosition.X = 0;
687        DWORD count;
688        FillConsoleOutputCharacter(this->stdOutHandle_, ' ', info.dwSize.X, info.dwCursorPosition, &count);;
689    }
690
691    void IOConsole::getTerminalSize()
692    {
693        CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
694        GetConsoleScreenBufferInfo(this->stdOutHandle_, &screenBufferInfo);
695        // dwSize is the maximum size. If you resize you will simply see scroll bars.
696        // And if you want to write outside these boundaries, you can't.
697        this->terminalWidth_  = screenBufferInfo.dwSize.X;
698        this->terminalHeight_ = screenBufferInfo.dwSize.Y;
699    }
700
701    // ###############################
702    // ###  ShellListener methods  ###
703    // ###############################
704
705    //! Called if only the last output-line has changed
706    void IOConsole::onlyLastLineChanged()
707    {
708        this->moveCursorYAndHome(-1);
709        this->clearCurrentLine();
710        this->printLogText(*(this->shell_->getNewestLineIterator()));
711        this->moveCursorYAndHome(1);
712        this->moveCursor(this->promptString_.size() + this->shell_->getInput().size(), 0);
713        this->cout_.flush();
714    }
715
716    //! Called if a new output-line was added
717    void IOConsole::lineAdded()
718    {
719        // Move cursor to the beginning of the new (last) output line
720        this->moveCursorYAndHome(0);
721        // Print the new output lines
722        this->printLogText(*(this->shell_->getNewestLineIterator()));
723        // Move cursor down
724        this->moveCursorYAndHome(1);
725        // Print status and input lines
726        this->printInputLine();
727        this->printStatusLines();
728        this->cout_.flush();
729    }
730}
731
732#endif /* ORXONOX_PLATFORM_UNIX */
Note: See TracBrowser for help on using the repository browser.