Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/console/src/libraries/core/IOConsole.cc @ 6010

Last change on this file since 6010 was 6010, checked in by rgrieder, 15 years ago

Sorted out some small stuff in the Shell and hopefully fixed IOConsole problems (flickering and too much output).

  • Property svn:eol-style set to native
File size: 12.7 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 <cstring>
33#include <iomanip>
34#include <iostream>
35
36#include "util/Clock.h"
37#include "util/Debug.h"
38#include "util/Sleep.h"
39#include "core/CommandExecutor.h"
40#include "core/Game.h"
41#include "core/GameMode.h"
42#include "core/Shell.h"
43#include "core/input/InputBuffer.h"
44
45#ifdef ORXONOX_PLATFORM_UNIX
46#include <termios.h>
47#endif
48
49namespace orxonox
50{
51    IOConsole* IOConsole::singletonPtr_s = NULL;
52
53#ifdef ORXONOX_PLATFORM_UNIX
54
55    namespace EscapeMode
56    {
57        enum Value
58        {
59            None,
60            First,
61            Second
62        };
63    }
64
65    IOConsole::IOConsole()
66        : shell_(new Shell("IOConsole", false, true))
67        , buffer_(shell_->getInputBuffer())
68        , originalTerminalSettings_(new termios())
69        , bStatusPrinted_(false)
70        , promptString_("orxonox> ")
71    {
72        this->setTerminalMode();
73        this->shell_->registerListener(this);
74
75        // Manually set the widths of the individual status lines
76        this->statusLineWidths_.push_back(6);
77        this->statusLineMaxWidth_ = 6;
78    }
79
80    IOConsole::~IOConsole()
81    {
82        std::cout << "\033[0G\033[K";
83        std::cout.flush();
84        resetTerminalMode();
85        delete this->originalTerminalSettings_;
86        this->shell_->destroy();
87    }
88
89    void IOConsole::setTerminalMode()
90    {
91        termios new_settings;
92
93        tcgetattr(0, this->originalTerminalSettings_);
94        new_settings = *this->originalTerminalSettings_;
95        new_settings.c_lflag &= ~(ICANON | ECHO);
96        //         new_settings.c_lflag |= ( ISIG | IEXTEN );
97        new_settings.c_cc[VTIME] = 0;
98        new_settings.c_cc[VMIN]  = 0;
99        tcsetattr(0, TCSANOW, &new_settings);
100        COUT(0) << endl;
101        //       atexit(&IOConsole::resetTerminalMode);
102    }
103
104    void IOConsole::resetTerminalMode()
105    {
106        tcsetattr(0, TCSANOW, IOConsole::originalTerminalSettings_);
107    }
108
109    void IOConsole::update(const Clock& time)
110    {
111        unsigned char c = 0;
112        std::string escapeSequence;
113        EscapeMode::Value escapeMode = EscapeMode::None;
114        while (read(STDIN_FILENO, &c, 1) == 1)
115        {
116            if (escapeMode == EscapeMode::First && (c == '[' || c=='O') )
117                escapeMode = EscapeMode::Second;
118            // Get Alt+Tab combination when switching applications
119            else if (escapeMode == EscapeMode::First && c == '\t')
120            {
121                this->buffer_->buttonPressed(KeyEvent(KeyCode::Tab, '\t', KeyboardModifier::Alt));
122                escapeMode = EscapeMode::None;
123            }
124            else if (escapeMode == EscapeMode::Second)
125            {
126                escapeSequence += c;
127                escapeMode = EscapeMode::None;
128                if      (escapeSequence == "A")
129                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Up,       0, 0));
130                else if (escapeSequence == "B")
131                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Down,     0, 0));
132                else if (escapeSequence == "C")
133                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Right,    0, 0));
134                else if (escapeSequence == "D")
135                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Left,     0, 0));
136                else if (escapeSequence == "1~" || escapeSequence == "H")
137                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Home,     0, 0));
138                else if (escapeSequence == "2~")
139                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert,   0, 0));
140                else if (escapeSequence == "3~")
141                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete,   0, 0));
142                else if (escapeSequence == "4~" || escapeSequence == "F")
143                    this->buffer_->buttonPressed(KeyEvent(KeyCode::End,      0, 0));
144                else if (escapeSequence == "5~")
145                    this->buffer_->buttonPressed(KeyEvent(KeyCode::PageUp,   0, 0));
146                else if (escapeSequence == "6~")
147                    this->buffer_->buttonPressed(KeyEvent(KeyCode::PageDown, 0, 0));
148                else
149                    // Waiting for sequence to complete
150                    // If the user presses ESC and then '[' or 'O' while the loop is not
151                    // running (for instance while loading), the whole sequence gets dropped
152                    escapeMode = EscapeMode::Second;
153            }
154            else // not in an escape sequence OR user might have pressed just ESC
155            {
156                if (escapeMode == EscapeMode::First)
157                {
158                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, c, 0));
159                    escapeMode = EscapeMode::None;
160                }
161                if (c == '\033')
162                {
163                    escapeMode = EscapeMode::First;
164                    escapeSequence.clear();
165                }
166                else
167                {
168                    KeyCode::ByEnum code;
169                    switch (c)
170                    {
171                    case '\n'  : case '\r': code = KeyCode::Return; break;
172                    case '\177': case '\b': code = KeyCode::Back;   break;
173                    case '\t'             : code = KeyCode::Tab;    break;
174                    default:
175                        // We don't encode the key code (would be a very large switch)
176                        // because the InputBuffer will only insert the text anyway
177                        // Replacement character is simply KeyCode::A
178                        code = KeyCode::A;
179                    }
180                    this->buffer_->buttonPressed(KeyEvent(code, c, 0));
181                }
182            }
183        }
184
185        // If there is still an escape key pending (escape key ONLY), then
186        // it sure isn't an escape sequence anymore
187        if (escapeMode == EscapeMode::First)
188            this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, '\033', 0));
189
190        // Clear screen below the last output line by first moving the cursor to the beginning of the first status line
191        std::cout << "\033[" << (this->bStatusPrinted_ ? this->statusLineWidths_.size() : 0) << "F\033[J";
192        this->printStatusLines();
193        this->printInputLine();
194        std::cout.flush();
195    }
196
197    void IOConsole::printLogText(const std::string& text)
198    {
199        std::string output;
200
201        // Handle line colouring by inspecting the first letter
202        char level = 0;
203        if (!text.empty())
204            level = text[0];
205        if (level >= -1 && level <= 6)
206            output = text.substr(1);
207        else
208            output = text;
209
210        // Colour line
211/*
212        switch (level)
213        {
214        case -1: std::cout << "\033[37m"; break;
215        case  1: std::cout << "\033[91m"; break;
216        case  2: std::cout << "\033[31m"; break;
217        case  3: std::cout << "\033[34m"; break;
218        case  4: std::cout << "\033[36m"; break;
219        case  5: std::cout << "\033[35m"; break;
220        case  6: std::cout << "\033[37m"; break;
221        default: break;
222        }
223*/
224
225        // Print output line
226        std::cout << output;
227
228        // Reset colour to white
229//        std::cout << "\033[37m";
230    }
231
232    void IOConsole::printInputLine()
233    {
234        // Set cursor to the beginning of the line and erase the line
235        std::cout << "\033[1G\033[K";
236        // Indicate a command prompt
237        std::cout << promptString_g;
238        // Save cursor position
239        std::cout << "\033[s";
240        // Print command line buffer
241        std::cout << this->shell_->getInput();
242        // Restore cursor position and move it to the right
243        std::cout << "\033[u";
244        if (this->buffer_->getCursorPosition() > 0)
245            std::cout << "\033[" << this->buffer_->getCursorPosition() << "C";
246    }
247
248    void IOConsole::printStatusLines()
249    {
250        if (!this->statusLineWidths_.empty())
251        {
252            // Check terminal size
253            int x, y;
254            if (this->getTerminalSize(&x, &y) && (x < (int)this->statusLineMaxWidth_ || y < (int)(this->minOutputLines_ + this->statusLineWidths_.size())))
255            {
256                this->bStatusPrinted_ = false;
257                return;
258            }
259            std::cout << "Status" << std::endl;
260            this->bStatusPrinted_ = true;
261        }
262    }
263
264    int IOConsole::getTerminalSize(int* x, int* y)
265    {
266#ifdef TIOCGSIZE
267        struct ttysize win;
268#elif defined(TIOCGWINSZ)
269        struct winsize win;
270#endif
271
272#ifdef TIOCGSIZE
273        if (ioctl(STDIN_FILENO, TIOCGSIZE, &win))
274            return 0;
275        *y = win.ts_lines;
276        *x = win.ts_cols;
277#elif defined TIOCGWINSZ
278        if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win))
279            return 0;
280        *y = win.ws_row;
281        *x = win.ws_col;
282#else
283        {
284            const char* s = getenv("LINES");
285            if (s)
286                *y = strtol(s, NULL, 10);
287            else
288                *y = 25;
289            s = getenv("COLUMNS");
290            if (s)
291                *x = strtol(s, NULL, 10);
292            else
293                *x = 80;
294        }
295#endif
296        return 1;
297    }
298
299#elif defined(ORXONOX_PLATFORM_WINDOWS)
300
301    IOConsole::IOConsole()
302        : shell_(new Shell("IOConsole", false))
303        , buffer_(shell_->getInputBuffer())
304    {
305        this->setTerminalMode();
306    }
307
308    IOConsole::~IOConsole()
309    {
310    }
311
312    void IOConsole::setTerminalMode()
313    {
314    }
315
316    void IOConsole::resetTerminalMode()
317    {
318    }
319
320    void IOConsole::update(const Clock& time)
321    {
322    }
323
324    void IOConsole::printLogText(const std::string& text)
325    {
326    }
327
328    void IOConsole::printInputLine()
329    {
330    }
331
332    void IOConsole::printStatusLines()
333    {
334    }
335
336#endif /* ORXONOX_PLATFORM_UNIX */
337
338    // ###############################
339    // ###  ShellListener methods  ###
340    // ###############################
341
342    /**
343    @brief
344        Called if all output-lines have to be redrawn.
345    */
346    void IOConsole::linesChanged()
347    {
348        // Method only gets called upon start to draw all the lines
349        // But we are using std::cout anyway, so do nothing here
350    }
351
352    /**
353    @brief
354        Called if only the last output-line has changed.
355    */
356    void IOConsole::onlyLastLineChanged()
357    {
358        // Save cursor position and move it to the beginning of the first output line
359        std::cout << "\033[s\033[" << (1 + this->statusLineWidths_.size()) << "F";
360        // Erase the line
361        std::cout << "\033[K";
362        // Reprint the last output line
363        this->printLogText(*(this->shell_->getNewestLineIterator()));
364        // Restore cursor
365        std::cout << "\033[u";
366        std::cout.flush();
367    }
368
369    /**
370    @brief
371        Called if a new output-line was added.
372    */
373    void IOConsole::lineAdded()
374    {
375        // Move cursor to the beginning of the first status line and erase screen from there
376        std::cout << "\033[" << this->statusLineWidths_.size() << "F\033[J";
377        // Print the new output line
378        this->printLogText(*(this->shell_->getNewestLineIterator()));
379        std::cout << std::endl;
380        this->printStatusLines();
381        this->printInputLine();
382        std::cout.flush();
383    }
384
385    /**
386    @brief
387        Called if the text in the input-line has changed.
388    */
389    void IOConsole::inputChanged()
390    {
391        this->printInputLine();
392        std::cout.flush();
393    }
394
395    /**
396    @brief
397        Called if the position of the cursor in the input-line has changed.
398    */
399    void IOConsole::cursorChanged()
400    {
401        this->printInputLine();
402        std::cout.flush();
403    }
404
405    /**
406    @brief
407        Called if a command is about to be executed
408    */
409    void IOConsole::executed()
410    {
411        this->shell_->addOutputLine(this->promptString_ + this->shell_->getInput());
412    }
413
414
415    /**
416    @brief
417        Called if the console gets closed.
418    */
419    void IOConsole::exit()
420    {
421        // Exit is not an option, IOConsole always exists
422    }
423
424}
Note: See TracBrowser for help on using the repository browser.