Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 6141 was 6141, checked in by rgrieder, 14 years ago

Better colour scheme for the IOConsole on Windows.

  • Property svn:eol-style set to native
File size: 27.1 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_(new termios())
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        delete this->originalTerminalSettings_;
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
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    }
380
381    void IOConsole::resetTerminalMode()
382    {
383        tcsetattr(0, TCSANOW, IOConsole::originalTerminalSettings_);
384    }
385
386    void IOConsole::getTerminalSize()
387    {
388#ifdef TIOCGSIZE
389        struct ttysize win;
390        if (!ioctl(STDIN_FILENO, TIOCGSIZE, &win))
391        {
392            this->terminalWidth_  = win.ts_cols;
393            this->terminalHeight_ = win.ts_lines;
394            return;
395        }
396#elif defined TIOCGWINSZ
397        struct winsize win;
398        if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &win))
399        {
400            this->terminalWidth_  = win.ws_col;
401            this->terminalHeight_ = win.ws_row;
402            return;
403        }
404#else
405        const char* s = getenv("COLUMNS");
406        this->terminalWidth_  = s ? strtol(s, NULL, 10) : 80;
407        s = getenv("LINES");
408        this->terminalHeight_ = s ? strtol(s, NULL, 10) : 24;
409        return;
410#endif
411        this->terminalWidth_  = 80;
412        this->terminalHeight_ = 24;
413    }
414
415    // ###############################
416    // ###  ShellListener methods  ###
417    // ###############################
418
419    //! Called if only the last output-line has changed
420    void IOConsole::onlyLastLineChanged()
421    {
422        // Save cursor position and move it to the beginning of the first output line
423        this->cout_ << "\033[s\033[1A\033[1G";
424        // Erase the line
425        this->cout_ << "\033[K";
426        // Reprint the last output line
427        this->printLogText(*(this->shell_->getNewestLineIterator()));
428        // Restore cursor
429        this->cout_ << "\033[u";
430        this->cout_.flush();
431    }
432
433    //! Called if a new output-line was added
434    void IOConsole::lineAdded()
435    {
436        int newLines = this->shell_->getNewestLineIterator()->size() / this->terminalWidth_ + 1;
437        // Create new lines by scrolling the screen
438        this->cout_ << "\033[" << newLines << 'S';
439        // Move cursor to the beginning of the new (last) output line
440        this->cout_ << "\033[" << newLines << "A\033[1G";
441        // Erase screen from here
442        this->cout_ << "\033[J";
443        // Print the new output lines
444        for (int i = 0; i < newLines; ++i)
445            this->printLogText(this->shell_->getNewestLineIterator()->substr(i*this->terminalWidth_, this->terminalWidth_));
446        // Move cursor down
447        this->cout_ << "\033[1B\033[1G";
448        // Print status and input lines
449        this->printInputLine();
450        this->printStatusLines();
451        this->cout_.flush();
452    }
453}
454
455#elif defined(ORXONOX_PLATFORM_WINDOWS)
456// ##################################
457// ###   Windows Implementation   ###
458// ##################################
459
460#include <windows.h>
461
462namespace orxonox
463{
464    IOConsole::IOConsole()
465        : shell_(new Shell("IOConsole", false, true))
466        , buffer_(shell_->getInputBuffer())
467        , cout_(std::cout.rdbuf())
468        , bStatusPrinted_(false)
469        , promptString_("orxonox # ")
470    {
471        this->setTerminalMode();
472        this->shell_->registerListener(this);
473
474        // Manually set the widths of the individual status lines
475        this->statusLineWidths_.push_back(29);
476        this->statusLineMaxWidth_ = 29;
477
478        this->getTerminalSize();
479        this->lastTerminalWidth_ = this->terminalWidth_;
480        this->lastTerminalHeight_ = this->terminalHeight_;
481
482        // Disable standard this->cout_ logging
483        OutputHandler::getInstance().disableCout();
484        // Redirect std::cout to an ostringstream
485        // (Other part is in the initialiser list)
486        std::cout.rdbuf(this->origCout_.rdbuf());
487
488        // Make sure we make way for the status lines
489        this->update(Game::getInstance().getGameClock());
490    }
491
492    IOConsole::~IOConsole()
493    {
494        // Empty all buffers
495        this->update(Game::getInstance().getGameClock());
496
497        resetTerminalMode();
498        this->shell_->destroy();
499
500        // Restore this->cout_ redirection
501        std::cout.rdbuf(this->cout_.rdbuf());
502        // Enable standard this->cout_ logging again
503        OutputHandler::getInstance().enableCout();
504    }
505
506    void IOConsole::update(const Clock& time)
507    {
508        while (true)
509        {
510            DWORD count;
511            INPUT_RECORD inrec;
512            PeekConsoleInput(this->stdInHandle_, &inrec, 1, &count);
513            if (count == 0)
514                break;
515            ReadConsoleInput(this->stdInHandle_, &inrec, 1, &count);
516            if (inrec.EventType == KEY_EVENT && inrec.Event.KeyEvent.bKeyDown)
517            {
518                // Process keyboard modifiers (Ctrl, Alt and Shift)
519                DWORD modifiersIn = inrec.Event.KeyEvent.dwControlKeyState;
520                int modifiersOut = 0;
521                if ((modifiersIn & (LEFT_ALT_PRESSED  | RIGHT_ALT_PRESSED))  != 0)
522                    modifiersOut |= KeyboardModifier::Alt;
523                if ((modifiersIn & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0)
524                    modifiersOut |= KeyboardModifier::Ctrl;
525                if ((modifiersIn & SHIFT_PRESSED) != 0)
526                    modifiersOut |= KeyboardModifier::Shift;
527
528                // ASCII character (0 for special keys)
529                char asciiChar = inrec.Event.KeyEvent.uChar.AsciiChar;
530
531                // Process special keys and if not found, use Key::A as dummy (InputBuffer uses the ASCII text anyway)
532                switch (inrec.Event.KeyEvent.wVirtualKeyCode)
533                {
534                case VK_BACK:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Back,     asciiChar, modifiersOut)); break;
535                case VK_TAB:    this->buffer_->buttonPressed(KeyEvent(KeyCode::Back,     asciiChar, modifiersOut)); break;
536                case VK_RETURN: this->buffer_->buttonPressed(KeyEvent(KeyCode::Back,     asciiChar, modifiersOut)); break;
537                case VK_PAUSE:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Pause,    asciiChar, modifiersOut)); break;
538                case VK_ESCAPE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape,   asciiChar, modifiersOut)); break;
539                case VK_SPACE:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Space,    asciiChar, modifiersOut)); break;
540                case VK_PRIOR:  this->buffer_->buttonPressed(KeyEvent(KeyCode::PageUp,   asciiChar, modifiersOut)); break;
541                case VK_NEXT:   this->buffer_->buttonPressed(KeyEvent(KeyCode::PageDown, asciiChar, modifiersOut)); break;
542                case VK_END:    this->buffer_->buttonPressed(KeyEvent(KeyCode::End,      asciiChar, modifiersOut)); break;
543                case VK_HOME:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Home,     asciiChar, modifiersOut)); break;
544                case VK_LEFT:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Left,     asciiChar, modifiersOut)); break;
545                case VK_UP:     this->buffer_->buttonPressed(KeyEvent(KeyCode::Up,       asciiChar, modifiersOut)); break;
546                case VK_RIGHT:  this->buffer_->buttonPressed(KeyEvent(KeyCode::Right,    asciiChar, modifiersOut)); break;
547                case VK_DOWN:   this->buffer_->buttonPressed(KeyEvent(KeyCode::Down,     asciiChar, modifiersOut)); break;
548                case VK_INSERT: this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert,   asciiChar, modifiersOut)); break;
549                case VK_DELETE: this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete,   asciiChar, modifiersOut)); break;
550                default:        this->buffer_->buttonPressed(KeyEvent(KeyCode::A,        asciiChar, modifiersOut));
551                }
552            }
553        }
554
555        // Get info about cursor and terminal size
556        this->getTerminalSize();
557
558        // Refresh status line
559        this->printStatusLines();
560
561        // Process output written to std::cout
562        if (!this->origCout_.str().empty())
563        {
564            this->shell_->addOutputLine(this->origCout_.str());
565            this->origCout_.str("");
566        }
567        this->cout_.flush();
568    }
569
570    void IOConsole::printLogText(const std::string& text)
571    {
572        std::string output = text;
573        int level = this->extractLogLevel(&output);
574
575        // Colour line
576        switch (level)
577        {
578        case  1: SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_RED | FOREGROUND_INTENSITY); break;
579        case  2: SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY); break;
580        case  3: SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_INTENSITY); break;
581        case  4: SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_INTENSITY); break;
582        default: break;
583        }
584
585        // Print output line
586        this->cout_ << output;
587
588        // Reset colour to white
589        SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN);
590    }
591
592    void IOConsole::printInputLine()
593    {
594        SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
595        this->moveCursorYAndHome(0);
596        this->clearCurrentLine();
597        this->cout_ << this->promptString_ << this->shell_->getInput();
598        this->moveCursorYAndHome(0);
599        this->moveCursor(this->promptString_.size() + this->buffer_->getCursorPosition(), 0);
600        SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN);
601    }
602
603    void IOConsole::printStatusLines()
604    {
605        if (this->willPrintStatusLines())
606        {
607            SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_GREEN);
608            this->bStatusPrinted_ = true;
609            // Put cursor on home position, one line down the input line
610            this->moveCursorYAndHome(1);
611            this->cout_ << std::fixed << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgFPS() << " fps, ";
612            this->cout_ <<               std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgTickTime() << " ms tick time";
613            // Clear rest of the line
614            CONSOLE_SCREEN_BUFFER_INFO info;
615            GetConsoleScreenBufferInfo(this->stdOutHandle_, &info);
616            this->cout_ << std::string(info.dwSize.X - info.dwCursorPosition.X - 1, ' ');
617            // Restore cursor position
618            this->moveCursorYAndHome(-1);
619            this->moveCursor(this->promptString_.size() + this->buffer_->getCursorPosition(), 0);
620            SetConsoleTextAttribute(stdOutHandle_, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN);
621        }
622        else
623            this->bStatusPrinted_ = false;
624    }
625
626    void IOConsole::setTerminalMode()
627    {
628        // Set the console mode to no-echo, raw input, and no window or mouse events
629        this->stdOutHandle_ = GetStdHandle(STD_OUTPUT_HANDLE);
630        this->stdInHandle_  = GetStdHandle(STD_INPUT_HANDLE);
631        if (this->stdInHandle_ == INVALID_HANDLE_VALUE
632            || !GetConsoleMode(this->stdInHandle_, &this->originalTerminalSettings_)
633            || !SetConsoleMode(this->stdInHandle_, 0))
634        {
635            COUT(1) << "Error: Could not set Windows console settings" << std::endl;
636            return;
637        }
638        FlushConsoleInputBuffer(this->stdInHandle_);
639    }
640
641    void IOConsole::resetTerminalMode()
642    {
643        SetConsoleMode(this->stdInHandle_, this->originalTerminalSettings_);
644    }
645
646    //! Moves the console cursor around and inserts new lines when reaching the end.
647    //! Moving out on the right is just clamped though.
648    void IOConsole::moveCursor(int dx, int dy)
649    {
650        CONSOLE_SCREEN_BUFFER_INFO info;
651        GetConsoleScreenBufferInfo(this->stdOutHandle_, &info);
652        SHORT& x = info.dwCursorPosition.X;
653        x = clamp(x + dx, 0, info.dwSize.X - 1);
654        SHORT& y = info.dwCursorPosition.Y;
655        if (y + dy >= info.dwSize.Y)
656        {
657            // Insert new lines
658            this->cout_ << std::string(y + dy - info.dwSize.Y + 1, 'n');
659            y = info.dwSize.Y - 1;
660        }
661        else if (y < 0)
662            y = 0;
663        else
664            y += dy;
665        SetConsoleCursorPosition(this->stdOutHandle_, info.dwCursorPosition);
666    }
667
668    void IOConsole::moveCursorYAndHome(int dy)
669    {
670        CONSOLE_SCREEN_BUFFER_INFO info;
671        GetConsoleScreenBufferInfo(this->stdOutHandle_, &info);
672        this->moveCursor(-info.dwCursorPosition.X, dy);
673    }
674
675    void IOConsole::clearCurrentLine()
676    {
677        CONSOLE_SCREEN_BUFFER_INFO info;
678        GetConsoleScreenBufferInfo(this->stdOutHandle_, &info);
679        info.dwCursorPosition.X = 0;
680        DWORD count;
681        FillConsoleOutputCharacter(this->stdOutHandle_, ' ', info.dwSize.X, info.dwCursorPosition, &count);;
682    }
683
684    void IOConsole::getTerminalSize()
685    {
686        CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
687        GetConsoleScreenBufferInfo(this->stdOutHandle_, &screenBufferInfo);
688        // dwSize is the maximum size. If you resize you will simply see scroll bars.
689        // And if you want to write outside these boundaries, you can't.
690        this->terminalWidth_  = screenBufferInfo.dwSize.X;
691        this->terminalHeight_ = screenBufferInfo.dwSize.Y;
692    }
693
694    // ###############################
695    // ###  ShellListener methods  ###
696    // ###############################
697
698    //! Called if only the last output-line has changed
699    void IOConsole::onlyLastLineChanged()
700    {
701        this->moveCursorYAndHome(-1);
702        this->clearCurrentLine();
703        this->printLogText(*(this->shell_->getNewestLineIterator()));
704        this->moveCursorYAndHome(1);
705        this->moveCursor(this->promptString_.size() + this->shell_->getInput().size(), 0);
706        this->cout_.flush();
707    }
708
709    //! Called if a new output-line was added
710    void IOConsole::lineAdded()
711    {
712        // Move cursor to the beginning of the new (last) output line
713        this->moveCursorYAndHome(0);
714        // Print the new output lines
715        this->printLogText(*(this->shell_->getNewestLineIterator()));
716        // Move cursor down
717        this->moveCursorYAndHome(1);
718        // Print status and input lines
719        this->printInputLine();
720        this->printStatusLines();
721        this->cout_.flush();
722    }
723}
724
725#endif /* ORXONOX_PLATFORM_UNIX */
Note: See TracBrowser for help on using the repository browser.