Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/bugger/src/core/Shell.cc @ 2560

Last change on this file since 2560 was 2343, checked in by rgrieder, 16 years ago

Fixed a segfault bug than occurred when producing debug output after the Shell has been destroyed. The reason was that the Shell provides an OutputBuffer itself, so when the Shell gets destroyed, it has to set the fallback buffer again to the OutputHandler.

  • Property svn:eol-style set to native
File size: 11.6 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 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "Shell.h"
30#include "CommandExecutor.h"
31#include "CoreIncludes.h"
32#include "ConfigValueIncludes.h"
33#include "Core.h"
34#include "ConsoleCommand.h"
35#include "util/OutputHandler.h"
36
37#define SHELL_UPDATE_LISTENERS(function) \
38    for (std::list<ShellListener*>::iterator it = this->listeners_.begin(); it != this->listeners_.end(); ) \
39        (*(it++))->function()
40
41namespace orxonox
42{
43    SetConsoleCommand(Shell, clearShell, true);
44    SetConsoleCommand(Shell, history, true);
45
46    SetConsoleCommandShortcut(OutputHandler, log);
47    SetConsoleCommandShortcut(OutputHandler, error);
48    SetConsoleCommandShortcut(OutputHandler, warning);
49    SetConsoleCommandShortcut(OutputHandler, info);
50    SetConsoleCommandShortcut(OutputHandler, debug);
51
52    Shell* Shell::singletonRef_s = 0;
53
54    Shell::Shell()
55    {
56        assert(singletonRef_s == 0);
57        singletonRef_s = this;
58
59        int level = Core::getSoftDebugLevel(OutputHandler::LD_Shell);
60        Core::setSoftDebugLevel(OutputHandler::LD_Shell, -1);
61
62        RegisterRootObject(Shell);
63
64        this->scrollPosition_ = 0;
65        this->maxHistoryLength_ = 100;
66        this->historyPosition_ = 0;
67        this->historyOffset_ = 0;
68        this->finishedLastLine_ = true;
69        this->bAddOutputLevel_ = false;
70
71        this->clearLines();
72
73        this->inputBuffer_ = new InputBuffer();
74        this->configureInputBuffer();
75
76        this->outputBuffer_.registerListener(this);
77        OutputHandler::getOutStream().setOutputBuffer(&this->outputBuffer_);
78
79        this->setConfigValues();
80
81        Core::setSoftDebugLevel(OutputHandler::LD_Shell, level);
82    }
83
84    Shell::~Shell()
85    {
86        OutputHandler::getOutStream().setOutputBuffer(0);
87        if (this->inputBuffer_)
88            delete this->inputBuffer_;
89        singletonRef_s = 0;
90    }
91
92    void Shell::setConfigValues()
93    {
94        SetConfigValue(maxHistoryLength_, 100).callback(this, &Shell::commandHistoryLengthChanged);
95        SetConfigValue(historyOffset_, 0).callback(this, &Shell::commandHistoryOffsetChanged);
96        SetConfigValueVector(commandHistory_, std::vector<std::string>());
97    }
98
99    void Shell::commandHistoryOffsetChanged()
100    {
101        if (this->historyOffset_ >= this->maxHistoryLength_)
102            this->historyOffset_ = 0;
103    }
104
105    void Shell::commandHistoryLengthChanged()
106    {
107        this->commandHistoryOffsetChanged();
108
109        while (this->commandHistory_.size() > this->maxHistoryLength_)
110        {
111            unsigned int index = this->commandHistory_.size() - 1;
112            this->commandHistory_.erase(this->commandHistory_.begin() + index);
113            ModifyConfigValue(commandHistory_, remove, index);
114        }
115    }
116
117    void Shell::configureInputBuffer()
118    {
119        this->inputBuffer_->registerListener(this, &Shell::inputChanged, true);
120        this->inputBuffer_->registerListener(this, &Shell::execute, '\r', false);
121        this->inputBuffer_->registerListener(this, &Shell::hintandcomplete, '\t', true);
122        this->inputBuffer_->registerListener(this, &Shell::backspace, '\b', true);
123        this->inputBuffer_->registerListener(this, &Shell::deletechar, KeyCode::Delete);
124        this->inputBuffer_->registerListener(this, &Shell::exit, (char)27, true);
125        this->inputBuffer_->registerListener(this, &Shell::cursor_right, KeyCode::Right);
126        this->inputBuffer_->registerListener(this, &Shell::cursor_left, KeyCode::Left);
127        this->inputBuffer_->registerListener(this, &Shell::cursor_end, KeyCode::End);
128        this->inputBuffer_->registerListener(this, &Shell::cursor_home, KeyCode::Home);
129        this->inputBuffer_->registerListener(this, &Shell::history_up, KeyCode::Up);
130        this->inputBuffer_->registerListener(this, &Shell::history_down, KeyCode::Down);
131        this->inputBuffer_->registerListener(this, &Shell::scroll_up, KeyCode::PageUp);
132        this->inputBuffer_->registerListener(this, &Shell::scroll_down, KeyCode::PageDown);
133    }
134
135    void Shell::clearShell()
136    {
137        Shell::getInstance().clearLines();
138    }
139
140    void Shell::history()
141    {
142        Shell& instance = Shell::getInstance();
143
144        for (int i = instance.historyOffset_; i < (int)instance.commandHistory_.size(); ++i)
145            instance.addLine(instance.commandHistory_[i], -1);
146        for (int i =  0; i < (int)instance.historyOffset_; ++i)
147            instance.addLine(instance.commandHistory_[i], -1);
148    }
149
150    void Shell::registerListener(ShellListener* listener)
151    {
152        this->listeners_.insert(this->listeners_.end(), listener);
153    }
154
155    void Shell::unregisterListener(ShellListener* listener)
156    {
157        for (std::list<ShellListener*>::iterator it = this->listeners_.begin(); it != this->listeners_.end(); )
158        {
159            if ((*it) == listener)
160                this->listeners_.erase(it++);
161            else
162                ++it;
163        }
164    }
165
166    void Shell::setCursorPosition(unsigned int cursor)
167    {
168        this->inputBuffer_->setCursorPosition(cursor);
169        SHELL_UPDATE_LISTENERS(cursorChanged);
170    }
171
172    void Shell::setInput(const std::string& input)
173    {
174        this->inputBuffer_->set(input);
175        this->inputChanged();
176    }
177
178    void Shell::addLine(const std::string& line, int level)
179    {
180        int original_level = OutputHandler::getOutStream().getOutputLevel();
181        OutputHandler::getOutStream().setOutputLevel(level);
182
183        if (!this->finishedLastLine_)
184            this->outputBuffer_ << std::endl;
185
186        this->outputBuffer_ << line << std::endl;
187        OutputHandler::getOutStream().setOutputLevel(original_level);
188    }
189
190    void Shell::clearLines()
191    {
192        this->lines_.clear();
193        this->scrollIterator_ = this->lines_.begin();
194
195        this->scrollPosition_ = 0;
196        this->finishedLastLine_ = true;
197
198        SHELL_UPDATE_LISTENERS(linesChanged);
199    }
200
201    std::list<std::string>::const_iterator Shell::getNewestLineIterator() const
202    {
203        if (this->scrollPosition_)
204            return this->scrollIterator_;
205        else
206            return this->lines_.begin();
207    }
208
209    std::list<std::string>::const_iterator Shell::getEndIterator() const
210    {
211        return this->lines_.end();
212    }
213
214    void Shell::addToHistory(const std::string& command)
215    {
216        ModifyConfigValue(commandHistory_, set, this->historyOffset_, command);
217        this->historyPosition_ = 0;
218        ModifyConfigValue(historyOffset_, set, (this->historyOffset_ + 1) % this->maxHistoryLength_);
219    }
220
221    std::string Shell::getFromHistory() const
222    {
223        unsigned int index = mod(((int)this->historyOffset_) - ((int)this->historyPosition_), this->maxHistoryLength_);
224        if (index < this->commandHistory_.size() && this->historyPosition_ != 0)
225            return this->commandHistory_[index];
226        else
227            return "";
228    }
229
230    void Shell::outputChanged()
231    {
232        std::string output;
233        bool newline;
234        do
235        {
236            newline = this->outputBuffer_.getLine(&output);
237
238            if (!newline && output == "")
239                break;
240
241            if (this->finishedLastLine_)
242            {
243                if (this->bAddOutputLevel_)
244                    output.insert(0, 1, (char)OutputHandler::getOutStream().getOutputLevel());
245
246                this->lines_.insert(this->lines_.begin(), output);
247
248                if (this->scrollPosition_)
249                    this->scrollPosition_++;
250                else
251                    this->scrollIterator_ = this->lines_.begin();
252
253                this->finishedLastLine_ = newline;
254
255                if (!this->scrollPosition_)
256                {
257                    SHELL_UPDATE_LISTENERS(lineAdded);
258                }
259            }
260            else
261            {
262                (*this->lines_.begin()) += output;
263                this->finishedLastLine_ = newline;
264                SHELL_UPDATE_LISTENERS(onlyLastLineChanged);
265            }
266
267        } while (newline);
268    }
269
270    void Shell::inputChanged()
271    {
272        SHELL_UPDATE_LISTENERS(inputChanged);
273        SHELL_UPDATE_LISTENERS(cursorChanged);
274    }
275
276    void Shell::execute()
277    {
278        this->addToHistory(this->inputBuffer_->get());
279        this->addLine(this->inputBuffer_->get(), 0);
280
281        if (!CommandExecutor::execute(this->inputBuffer_->get()))
282            this->addLine("Error: Can't execute \"" + this->inputBuffer_->get() + "\".", 1);
283
284        this->clear();
285    }
286
287    void Shell::hintandcomplete()
288    {
289        this->inputBuffer_->set(CommandExecutor::complete(this->inputBuffer_->get()));
290        this->addLine(CommandExecutor::hint(this->inputBuffer_->get()), -1);
291
292        this->inputChanged();
293    }
294
295    void Shell::backspace()
296    {
297        this->inputBuffer_->removeBehindCursor();
298        SHELL_UPDATE_LISTENERS(inputChanged);
299        SHELL_UPDATE_LISTENERS(cursorChanged);
300    }
301
302    void Shell::deletechar()
303    {
304        this->inputBuffer_->removeAtCursor();
305        SHELL_UPDATE_LISTENERS(inputChanged);
306    }
307
308    void Shell::clear()
309    {
310        this->inputBuffer_->clear();
311        this->historyPosition_ = 0;
312        SHELL_UPDATE_LISTENERS(inputChanged);
313        SHELL_UPDATE_LISTENERS(cursorChanged);
314    }
315
316    void Shell::cursor_right()
317    {
318        this->inputBuffer_->increaseCursor();
319        SHELL_UPDATE_LISTENERS(cursorChanged);
320    }
321
322    void Shell::cursor_left()
323    {
324        this->inputBuffer_->decreaseCursor();
325        SHELL_UPDATE_LISTENERS(cursorChanged);
326    }
327
328    void Shell::cursor_end()
329    {
330        this->inputBuffer_->setCursorToEnd();
331        SHELL_UPDATE_LISTENERS(cursorChanged);
332    }
333
334    void Shell::cursor_home()
335    {
336        this->inputBuffer_->setCursorToBegin();
337        SHELL_UPDATE_LISTENERS(cursorChanged);
338    }
339
340    void Shell::history_up()
341    {
342        if (this->historyPosition_ < this->commandHistory_.size())
343        {
344            this->historyPosition_++;
345            this->inputBuffer_->set(this->getFromHistory());
346        }
347    }
348
349    void Shell::history_down()
350    {
351        if (this->historyPosition_ > 0)
352        {
353            this->historyPosition_--;
354            this->inputBuffer_->set(this->getFromHistory());
355        }
356    }
357
358    void Shell::scroll_up()
359    {
360        if (this->scrollIterator_ != this->lines_.end())
361        {
362            ++this->scrollIterator_;
363            ++this->scrollPosition_;
364
365            SHELL_UPDATE_LISTENERS(linesChanged);
366        }
367    }
368
369    void Shell::scroll_down()
370    {
371        if (this->scrollIterator_ != this->lines_.begin())
372        {
373            --this->scrollIterator_;
374            --this->scrollPosition_;
375
376            SHELL_UPDATE_LISTENERS(linesChanged);
377        }
378    }
379
380    void Shell::exit()
381    {
382        if (this->inputBuffer_->getSize() > 0)
383        {
384            this->clear();
385            return;
386        }
387
388        this->clear();
389        this->scrollPosition_ = 0;
390        this->scrollIterator_ = this->lines_.begin();
391
392        SHELL_UPDATE_LISTENERS(exit);
393    }
394}
Note: See TracBrowser for help on using the repository browser.