Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Changed Output concept a little bit to allow for more general use.
Every output (log) target has to be implemented as OutputListener. There is already a LogFileWriter and a MemoryLogWriter (stores ALL the log in a vector and provides iterators).
The OutputListener has a unique and constant name, a stream pointer and a soft debug level (that can only be changed via OutputHandler::setSoftDebugLevel(name, level)).
This concept doesn't require the OutputBuffer anymore, so I deleted it.

The adjustments in the Shell are just preliminary for this commit.

  • Property svn:eol-style set to native
File size: 11.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 <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    termios* IOConsole::originalTerminalSettings_;
56
57    IOConsole::IOConsole()
58        : shell_(Shell::getInstance())
59        , escapeMode_(None)
60        , buffer_(Shell::getInstance().getInputBuffer())
61    {
62        this->originalTerminalSettings_ = new termios;
63        this->setTerminalMode();
64        this->shell_.registerListener(this);
65    }
66
67    IOConsole::~IOConsole()
68    {
69        std::cout << "\033[0G\033[K";
70        std::cout.flush();
71        resetTerminalMode();
72        delete this->originalTerminalSettings_;
73    }
74
75    void IOConsole::setTerminalMode()
76    {
77        termios new_settings;
78
79        tcgetattr(0, this->originalTerminalSettings_);
80        new_settings = *this->originalTerminalSettings_;
81        new_settings.c_lflag &= ~(ICANON | ECHO);
82        //         new_settings.c_lflag |= ( ISIG | IEXTEN );
83        new_settings.c_cc[VTIME] = 0;
84        new_settings.c_cc[VMIN]  = 0;
85        tcsetattr(0, TCSANOW, &new_settings);
86        COUT(0) << endl;
87        //       atexit(&IOConsole::resetTerminalMode);
88    }
89
90    void IOConsole::resetTerminalMode()
91    {
92        tcsetattr(0, TCSANOW, IOConsole::originalTerminalSettings_);
93    }
94
95    void IOConsole::update(const Clock& time)
96    {
97        unsigned char c = 0;
98        while (read(STDIN_FILENO, &c, 1) == 1)
99        {
100            if (this->escapeMode_ == First && (c == '[' || c=='O') )
101                this->escapeMode_ = Second;
102            // Get Alt+Tab combination when switching applications
103            else if (this->escapeMode_ == First && c == '\t')
104            {
105                this->buffer_->buttonPressed(KeyEvent(KeyCode::Tab, '\t', KeyboardModifier::Alt));
106                this->escapeMode_ = None;
107            }
108            else if (this->escapeMode_ == Second)
109            {
110                this->escapeSequence_ += c;
111                this->escapeMode_ = None;
112                if      (this->escapeSequence_ == "A")
113                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Up,       0, 0));
114                else if (this->escapeSequence_ == "B")
115                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Down,     0, 0));
116                else if (this->escapeSequence_ == "C")
117                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Right,    0, 0));
118                else if (this->escapeSequence_ == "D")
119                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Left,     0, 0));
120                else if (this->escapeSequence_ == "1~" || this->escapeSequence_ == "H")
121                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Home,     0, 0));
122                else if (this->escapeSequence_ == "2~")
123                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert,   0, 0));
124                else if (this->escapeSequence_ == "3~")
125                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete,   0, 0));
126                else if (this->escapeSequence_ == "4~" || this->escapeSequence_ == "F")
127                    this->buffer_->buttonPressed(KeyEvent(KeyCode::End,      0, 0));
128                else if (this->escapeSequence_ == "5~")
129                    this->buffer_->buttonPressed(KeyEvent(KeyCode::AltPageUp,   0, 0));
130                else if (this->escapeSequence_ == "6~")
131                    this->buffer_->buttonPressed(KeyEvent(KeyCode::AltPageDown, 0, 0));
132                else if (this->escapeSequence_.size() > 4)
133                    // User probably very quickly pressed ESC and [
134                    this->escapeMode_ = None;
135                else
136                    // Waiting for sequence to complete
137                    this->escapeMode_ = Second;
138            }
139            else // not in an escape sequence OR user might have pressed just ESC
140            {
141                if (this->escapeMode_ == First)
142                {
143                    this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, c, 0));
144                    this->escapeMode_ = None;
145                }
146                if (c == '\033')
147                {
148                    this->escapeMode_ = First;
149                    this->escapeSequence_.clear();
150                }
151                else
152                {
153                    KeyCode::ByEnum code;
154                    switch (c)
155                    {
156                    case '\n': code = KeyCode::Return; break;
157                    case '\r': code = KeyCode::Return; break;
158                    case  127: code = KeyCode::Back;   break; 
159                    case '\b': code = KeyCode::Back;   break;
160                    case '\t': code = KeyCode::Tab;    break;
161                    default:
162                        // We don't encode the key code (would be a very large switch)
163                        // because the InputBuffer will only insert the text anyway
164                        // Replacement character is simply KeyCode::A
165                        code = KeyCode::A;
166                    }
167                    this->buffer_->buttonPressed(KeyEvent(code, c, 0));
168                }
169            }
170        }
171
172        // If there is still an escape key pending (escape key ONLY), then
173        // it sure isn't an escape sequence here
174        if (this->escapeMode_ == First)
175            this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, '\033', 0));
176        // Reset in any case because escape sequences always come in one piece
177        this->escapeMode_ = None;
178
179        // Print input line
180        this->printInputLine();
181    }
182
183    void IOConsole::print(const std::string& text)
184    {
185        std::string output;
186
187        // Handle line colouring by inspecting the first letter
188        char level = 0;
189        if (!text.empty())
190            level = text[0];
191        if (level >= -1 && level <= 6)
192            output = text.substr(1);
193        else
194            output = text;
195
196        // Colour line
197        switch (level)
198        {
199        case -1: std::cout << "\033[37m"; break;
200        case  1: std::cout << "\033[91m"; break;
201        case  2: std::cout << "\033[31m"; break;
202        case  3: std::cout << "\033[34m"; break;
203        case  4: std::cout << "\033[36m"; break;
204        case  5: std::cout << "\033[35m"; break;
205        case  6: std::cout << "\033[37m"; break;
206        default: break;
207        }
208
209        // Print output line
210        std::cout << output;
211
212        // Reset colour to white
213        std::cout << "\033[37m";
214        std::cout.flush();
215    }
216
217    void IOConsole::printInputLine()
218    {
219        // set cursor to the beginning of the line and erase the line
220        std::cout << "\033[1G\033[K";
221        // print status line
222        //std::cout << std::fixed << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgFPS() << " fps, " << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgTickTime() << " ms avg ticktime # ";
223        // Show an arrow to indicate a command prompt
224        std::cout << "orxonox>";
225        // save cursor position
226        std::cout << "\033[s";
227        // print commandLine buffer
228        std::cout << this->shell_.getInput();
229        // restore cursor position and move it to the right
230        std::cout << "\033[u";
231        if (this->buffer_->getCursorPosition() > 0)
232            std::cout << "\033[" << this->buffer_->getCursorPosition() << "C";
233        std::cout.flush();
234    }
235
236#elif defined(ORXONOX_PLATFORM_WINDOWS)
237
238    IOConsole::IOConsole()
239        : shell_(Shell::getInstance())
240        , buffer_(Shell::getInstance().getInputBuffer())
241    {
242        this->setTerminalMode();
243    }
244
245    IOConsole::~IOConsole()
246    {
247    }
248
249    void IOConsole::setTerminalMode()
250    {
251    }
252
253    void IOConsole::resetTerminalMode()
254    {
255    }
256
257    void IOConsole::update(const Clock& time)
258    {
259    }
260
261    void IOConsole::print(const std::string& text)
262    {
263    }
264
265    void IOConsole::printInputLine()
266    {
267    }
268
269#endif /* ORXONOX_PLATFORM_UNIX */
270
271    // ###############################
272    // ###  ShellListener methods  ###
273    // ###############################
274
275    /**
276    @brief
277        Called if all output-lines have to be redrawn.
278    */
279    void IOConsole::linesChanged()
280    {
281        // Method only gets called upon start to draw all the lines
282        // But we are using std::cout anyway, so do nothing here
283    }
284
285    /**
286    @brief
287        Called if only the last output-line has changed.
288    */
289    void IOConsole::onlyLastLineChanged()
290    {
291        // Save cursor position and move it the beginning of the second to last line
292        std::cout << "\033[s\033[1F";
293        // Erase the second to last line
294        std::cout << "\033[K";
295        this->print(*(this->shell_.getNewestLineIterator()));
296        // Restore cursor
297        std::cout << "\033[u";
298        std::cout.flush();
299    }
300
301    /**
302    @brief
303        Called if a new output-line was added.
304    */
305    void IOConsole::lineAdded()
306    {
307        // Move curosr the beginning of the line and erase it
308        std::cout << "\033[1G\033[K";
309        this->print(*(this->shell_.getNewestLineIterator()));
310        std::cout << std::endl;
311        this->printInputLine();
312    }
313
314    /**
315    @brief
316        Called if the text in the input-line has changed.
317    */
318    void IOConsole::inputChanged()
319    {
320        this->printInputLine();
321    }
322
323    /**
324    @brief
325        Called if the position of the cursor in the input-line has changed.
326    */
327    void IOConsole::cursorChanged()
328    {
329        this->printInputLine();
330    }
331
332    /**
333    @brief
334        Called if a command is about to be executed
335    */
336    void IOConsole::executed()
337    {
338        // Move cursor the beginning of the line
339        std::cout << "\033[1G";
340        // Print command so the user knows what he has typed
341        std::cout << "orxonox>" << this->shell_.getInput() << std::endl;
342        this->printInputLine();
343    }
344
345
346    /**
347    @brief
348        Called if the console gets closed.
349    */
350    void IOConsole::exit()
351    {
352        // Exit is not an option, IOConsole always exists
353    }
354
355}
Note: See TracBrowser for help on using the repository browser.