Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

that's probably more what you intended …

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