Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 6005 was 6005, checked in by scheusso, 15 years ago

gcc didn't like the old version ;)

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