Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Build fixes for IOConsole (can't test that on Windows…)

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