Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Fixed IOConsole partly and fixed two problems in PathConfig and OutputHandler

  • Property svn:eol-style set to native
File size: 12.8 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    const std::string promptString_g = "orxonox>";
53
54#ifdef ORXONOX_PLATFORM_UNIX
55
56    namespace EscapeMode
57    {
58        enum Value
59        {
60            None,
61            First,
62            Second
63        };
64    }
65
66    IOConsole::IOConsole()
67        : shell_(new Shell("IOConsole", false))
68        , buffer_(shell_->getInputBuffer())
69        , originalTerminalSettings_(new termios())
70        , bStatusPrinted_(false)
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::AltPageUp,   0, 0));
146                else if (escapeSequence == "6~")
147                    this->buffer_->buttonPressed(KeyEvent(KeyCode::AltPageDown, 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    }
195
196    void IOConsole::printLogText(const std::string& text)
197    {
198        std::string output;
199
200        // Handle line colouring by inspecting the first letter
201        char level = 0;
202        if (!text.empty())
203            level = text[0];
204        if (level >= -1 && level <= 6)
205            output = text.substr(1);
206        else
207            output = text;
208
209        // Colour line
210/*
211        switch (level)
212        {
213        case -1: std::cout << "\033[37m"; break;
214        case  1: std::cout << "\033[91m"; break;
215        case  2: std::cout << "\033[31m"; break;
216        case  3: std::cout << "\033[34m"; break;
217        case  4: std::cout << "\033[36m"; break;
218        case  5: std::cout << "\033[35m"; break;
219        case  6: std::cout << "\033[37m"; break;
220        default: break;
221        }
222*/
223
224        // Print output line
225        std::cout << output;
226
227        // Reset colour to white
228//        std::cout << "\033[37m";
229        std::cout.flush();
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        std::cout.flush();
247    }
248
249    void IOConsole::printStatusLines()
250    {
251        if (!this->statusLineWidths_.empty())
252        {
253            // Check terminal size
254            int x, y;
255            if (this->getTerminalSize(&x, &y) && (x < (int)this->statusLineMaxWidth_ || y < (int)(this->minOutputLines_ + this->statusLineWidths_.size())))
256            {
257                this->bStatusPrinted_ = false;
258                return;
259            }
260            std::cout << "Status" << std::endl;
261            std::cout.flush();
262            this->bStatusPrinted_ = true;
263        }
264    }
265
266    int IOConsole::getTerminalSize(int* x, int* y)
267    {
268#ifdef TIOCGSIZE
269        struct ttysize win;
270#elif defined(TIOCGWINSZ)
271        struct winsize win;
272#endif
273
274#ifdef TIOCGSIZE
275        if (ioctl(STDIN_FILENO, TIOCGSIZE, &win))
276            return 0;
277        *y = win.ts_lines;
278        *x = win.ts_cols;
279#elif defined TIOCGWINSZ
280        if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win))
281            return 0;
282        *y = win.ws_row;
283        *x = win.ws_col;
284#else
285        {
286            const char* s = getenv("LINES");
287            if (s)
288                *y = strtol(s, NULL, 10);
289            else
290                *y = 25;
291            s = getenv("COLUMNS");
292            if (s)
293                *x = strtol(s, NULL, 10);
294            else
295                *x = 80;
296        }
297#endif
298        return 1;
299    }
300
301#elif defined(ORXONOX_PLATFORM_WINDOWS)
302
303    IOConsole::IOConsole()
304        : shell_(new Shell("IOConsole", false))
305        , buffer_(shell_->getInputBuffer())
306    {
307        this->setTerminalMode();
308    }
309
310    IOConsole::~IOConsole()
311    {
312    }
313
314    void IOConsole::setTerminalMode()
315    {
316    }
317
318    void IOConsole::resetTerminalMode()
319    {
320    }
321
322    void IOConsole::update(const Clock& time)
323    {
324    }
325
326    void IOConsole::printLogText(const std::string& text)
327    {
328    }
329
330    void IOConsole::printInputLine()
331    {
332    }
333
334#endif /* ORXONOX_PLATFORM_UNIX */
335
336    // ###############################
337    // ###  ShellListener methods  ###
338    // ###############################
339
340    /**
341    @brief
342        Called if all output-lines have to be redrawn.
343    */
344    void IOConsole::linesChanged()
345    {
346        // Method only gets called upon start to draw all the lines
347        // But we are using std::cout anyway, so do nothing here
348    }
349
350    /**
351    @brief
352        Called if only the last output-line has changed.
353    */
354    void IOConsole::onlyLastLineChanged()
355    {
356        // Save cursor position and move it to the beginning of the first output line
357        std::cout << "\033[s\033[" << (1 + this->statusLineWidths_.size()) << "F";
358        // Erase the line
359        std::cout << "\033[K";
360        // Reprint the last output line
361        this->printLogText(*(this->shell_->getNewestLineIterator()));
362        // Restore cursor
363        std::cout << "\033[u";
364        std::cout.flush();
365    }
366
367    /**
368    @brief
369        Called if a new output-line was added.
370    */
371    void IOConsole::lineAdded()
372    {
373        // Move cursor to the beginning of the first status line and erase screen from there
374        std::cout << "\033[" << this->statusLineWidths_.size() << "F\033[J";
375        // Print the new output line
376        this->printLogText(*(this->shell_->getNewestLineIterator()));
377        std::cout << std::endl;
378        this->printStatusLines();
379        this->printInputLine();
380    }
381
382    /**
383    @brief
384        Called if the text in the input-line has changed.
385    */
386    void IOConsole::inputChanged()
387    {
388        this->printInputLine();
389    }
390
391    /**
392    @brief
393        Called if the position of the cursor in the input-line has changed.
394    */
395    void IOConsole::cursorChanged()
396    {
397        this->printInputLine();
398    }
399
400    /**
401    @brief
402        Called if a command is about to be executed
403    */
404    void IOConsole::executed()
405    {
406        // Move cursor the beginning of the line
407        std::cout << "\033[1G";
408        // Print command so the user knows what he has typed
409        std::cout << promptString_g << this->shell_->getInput() << std::endl;
410        this->printInputLine();
411    }
412
413
414    /**
415    @brief
416        Called if the console gets closed.
417    */
418    void IOConsole::exit()
419    {
420        // Exit is not an option, IOConsole always exists
421    }
422
423}
Note: See TracBrowser for help on using the repository browser.