Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 6175 was 6172, checked in by scheusso, 16 years ago

actually i think it's nicer this way ;)

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