/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: ... */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_SHELL #include "shell.h" #include "shell_command.h" #include "shell_buffer.h" #include "shell_input.h" #include "multi_line_text.h" #include "graphics_engine.h" #include "material.h" #include "event_handler.h" namespace OrxShell { SHELL_COMMAND(clear, Shell, clear) ->describe("Clears the shell from unwanted lines (empties all buffers)") ->setAlias("clear"); SHELL_COMMAND(deactivate, Shell, deactivate) ->describe("Deactivates the Shell. (moves it into background)") ->setAlias("hide"); SHELL_COMMAND(textsize, Shell, setTextSize) ->describe("Sets the size of the Text size, linespacing") ->defaultValues(15, 0); SHELL_COMMAND(textcolor, Shell, setTextColor) ->describe("Sets the Color of the Shells Text (red, green, blue, alpha)") ->defaultValues(SHELL_DEFAULT_TEXT_COLOR); SHELL_COMMAND(backgroundcolor, Shell, setBackgroundColor) ->describe("Sets the Color of the Shells Background (red, green, blue, alpha)") ->defaultValues(SHELL_DEFAULT_BACKGROUND_COLOR); SHELL_COMMAND(backgroundimage, Shell, setBackgroundImage) ->describe("sets the background image to load for the Shell") ->completionPlugin(0, OrxShell::CompletorFileSystem()); SHELL_COMMAND(font, Shell, setFont) ->describe("Sets the font of the Shell") ->defaultValues(SHELL_DEFAULT_FONT) ->completionPlugin(0, OrxShell::CompletorFileSystem(".ttf", "fonts/")); /** * standard constructor */ Shell::Shell () { this->setClassID(CL_SHELL, "Shell"); this->setName("Shell"); this->shellBuffer = ShellBuffer::getInstance(); // EVENT-Handler subscription of '`' to all States. this->subscribeEvent(ES_ALL, SDLK_BACKQUOTE); this->subscribeEvent(ES_ALL, SDLK_F12); this->subscribeEvent(ES_SHELL, SDLK_PAGEUP); this->subscribeEvent(ES_SHELL, SDLK_PAGEDOWN); this->subscribeEvent(ES_SHELL, EV_VIDEO_RESIZE); EventHandler::getInstance()->withUNICODE(ES_SHELL, true); // BUFFER this->bufferIterator = this->shellBuffer->getBuffer().begin(); // INPUT LINE this->setLayer(E2D_LAYER_ABOVE_ALL); this->shellInput.setLayer(E2D_LAYER_ABOVE_ALL); // Element2D and generals this->setSizeX2D(GraphicsEngine::getInstance()->getResolutionX()); this->setAbsCoor2D(3, -400); this->textSize = 15; this->lineSpacing = 0; this->bActive = false; this->fontFile = SHELL_DEFAULT_FONT; this->setBufferDisplaySize(10); this->setTextColor(SHELL_DEFAULT_TEXT_COLOR); this->setBackgroundColor(SHELL_DEFAULT_BACKGROUND_COLOR); this->deactivate(); } /** * @brief standard deconstructor */ Shell::~Shell () { // delete the displayable Buffers while (!this->bufferText.empty()) { delete this->bufferText.front(); this->bufferText.pop_front(); } } /** * @brief activates the shell * * This also feeds the Last few lines from the main buffers into the displayBuffer */ void Shell::activate() { if (this->bActive == true) PRINTF(3)("The shell is already active\n"); this->bActive = true; this->activate2D(); EventHandler::getInstance()->pushState(ES_SHELL); this->setRelCoorSoft2D(0, 0, 5); this->linesProcessed = this->shellBuffer->getLineCount(); std::list::const_iterator textLine = this->bufferIterator = this->shellBuffer->getBuffer().begin(); for (std::list::iterator text = this->bufferText.begin(); text != this->bufferText.end(); ++text) { (*text)->setVisibility(true); if (textLine != this->shellBuffer->getBuffer().end()) { (*text)->setText((*textLine)); textLine++; } else (*text)->setText(""); } this->updateResolution( GraphicsEngine::getInstance()->getResolutionX()); this->repositionText(); } /** * @brief deactiveates the Shell. */ void Shell::deactivate() { if (this->bActive == false) PRINTF(3)("The shell is already inactive\n"); this->bActive = false; this->deactivate2D(); EventHandler::getInstance()->popState(); this->setRelCoorSoft2D(0, -(int)this->shellHeight, 5); for (std::list::iterator text = this->bufferText.begin(); text != this->bufferText.end(); ++text) (*text)->setVisibility(false); // Go to the End again. this->bufferIterator = this->shellBuffer->getBuffer().begin(); } /** * @brief sets the File to load the fonts from * @param fontFile the file to load the font from * * it is quite important, that the font pointed too really exists! * (be aware that within orxonox fontFile is relative to the Data-Dir) */ void Shell::setFont(const std::string& fontFile) { this->fontFile = fontFile; this->applySettings(); } /** * @brief sets the size of the text and spacing * @param textSize the size of the Text in Pixels * @param lineSpacing the size of the Spacing between two lines in pixels * * this also rebuilds the entire Text, inputLine and displayBuffer, * to be accurate again. */ void Shell::setTextSize(unsigned int textSize, unsigned int lineSpacing) { this->textSize = textSize; this->lineSpacing = lineSpacing; this->applySettings(); } /** * @brief sets the color of the Font. * @param r: red * @param g: green * @param b: blue * @param a: alpha-value. */ void Shell::setTextColor(float r, float g, float b, float a) { this->textColor[0] = r; this->textColor[1] = g; this->textColor[2] = b; this->textColor[3] = a; this->applySettings(); } /** * @brief sets the color of the Backgrond. * @param r: red * @param g: green * @param b: blue * @param a: alpha-value. */ void Shell::setBackgroundColor(float r, float g, float b, float a) { this->backgroundMaterial.setDiffuse(r, g, b); this->backgroundMaterial.setTransparency(a); } /** * @brief sets a nice background image to the Shell's background * @param fileName the filename of the Image to load */ void Shell::setBackgroundImage(const std::string& fileName) { this->backgroundMaterial.setDiffuseMap(fileName); } /** * @brief updates the Shell's Width * @param width the new Width. */ void Shell::updateResolution(unsigned int width) { if (width == this->getSizeX2D()) return; this->setSizeX2D(width); for (std::list::iterator textIt = this->bufferText.begin(); textIt != this->bufferText.end(); ++textIt) { (*textIt)->setLineWidth(width); } } /** * @brief repositiones all the Texts to their position. */ void Shell::repositionText() { int linePos = -1; std::list::iterator textIt; for (textIt = this->bufferText.begin() ; textIt != this->bufferText.end(); ++textIt ) { linePos += (*textIt)->getLineCount(); (*textIt)->setRelCoorSoft2D(this->calculateLinePosition(linePos), 8); } } /** * @brief applies the Shells Settings to a single Text of the Shell. * @param text the Text to apply the settings to. */ void Shell::applyTextSettings(Text* text) { text->setSize(this->textSize); text->setFont(this->fontFile, this->textSize); text->setColor(this->textColor[0], this->textColor[1], this->textColor[2]); text->setBlending(this->textColor[3]); text->setLayer(this->getLayer()); if (text->getParent2D() != this) text->setParent2D(this); } /** * @brief resets the Values of all visible shell's commandos to the Shell's stored values * * this functions synchronizes the stored Data with the visible one. */ void Shell::applySettings() { this->applyTextSettings(&this->shellInput); this->shellInput.setRelCoor2D(15, (this->textSize + this->lineSpacing)*(this->bufferDisplaySize)); /* Here we create a Copy of the Buffer, so that we do not f**k up the List when some * output is routed from Some other Thread or by Changing any Values. */ std::list bufferTextCopy = this->bufferText; for (std::list::iterator textIt = bufferTextCopy.begin(); textIt != bufferTextCopy.end(); ++textIt) { this->applyTextSettings(*textIt); (*textIt)->setLineSpacing(this->lineSpacing); (*textIt)->setLineWidth(this->getSizeX2D() * GraphicsEngine::getInstance()->getResolutionX()); } this->repositionText(); this->shellHeight = (this->textSize + this->lineSpacing) * (bufferDisplaySize+1); } /** * @brief sets The count of Lines to display in the buffer. * @param bufferDisplaySize the count of lines to display in the Shell-Buffer. */ void Shell::setBufferDisplaySize(unsigned int bufferDisplaySize) { unsigned int oldSize = this->bufferText.size(); if (oldSize > bufferDisplaySize) { for (unsigned int i = bufferDisplaySize; i <= oldSize; i++) { delete this->bufferText.back(); this->bufferText.pop_back(); } } else if (oldSize < bufferDisplaySize) { for (unsigned int i = oldSize; i <= bufferDisplaySize; i++) { this->bufferText.push_back(new MultiLineText); } } this->bufferDisplaySize = bufferDisplaySize; this->applySettings(); } /** * @brief deletes all the Buffers */ void Shell::flush() { for (std::list::iterator textIt = this->bufferText.begin(); textIt != this->bufferText.end(); ++textIt) { (*textIt)->setText(""); // remove all chars from the BufferTexts. } this->shellBuffer->flush(); // BUFFER FLUSHING } /** * @brief prints out some text to the input-buffers * @param text the text to output. */ void Shell::printToDisplayBuffer(const std::string& text) { // Remove Last Entry and prepend it to the front. this->bufferText.push_front(this->bufferText.back()); this->bufferText.pop_back(); // Set the new Text. this->bufferText.front()->setText(text); this->bufferIterator = this->shellBuffer->getBuffer().begin(); // The LineCount will be started here. // The First Line gets a special Animation this->bufferText.front()->setRelCoor2D(this->calculateLinePosition(0)- Vector2D(-1000,0)); // Move all lines one Entry up. this->repositionText(); } /** * @brief moves the Display buffer (up + or down - ) * @param lineCount the count by which to shift the InputBuffer. * * @todo make this work */ void Shell::moveDisplayBuffer(int lineCount) { // moving the iterator to the right position (counting the steps) int moves = 0; while (moves != lineCount) { if (moves < lineCount) { if(this->bufferIterator == --this->shellBuffer->getBuffer().end()) break; ++moves; ++this->bufferIterator; } else { if (this->bufferIterator == this->shellBuffer->getBuffer().begin()) break; --moves; --this->bufferIterator; } } // redisplay the buffers std::list::iterator textIt; // Give a Graphical Representation that no move is possible. if (moves == 0) { int linePos = -1; for (textIt = this->bufferText.begin(); textIt != this->bufferText.end(); ++textIt) { linePos += (*textIt)->getLineCount(); (*textIt)->setRelCoor2D(this->calculateLinePosition(linePos)+ Vector2D(20,0)); (*textIt)->setRelCoorSoft2D(this->calculateLinePosition(linePos), 10); } return; } // Move all the Lines. int linePos = moves; std::list::const_iterator it = this->bufferIterator; for (textIt = this->bufferText.begin(); textIt != this->bufferText.end(); ++textIt, ++it) { if (it == this->shellBuffer->getBuffer().end()) { PRINTF(1)("LAST LINE REACHED\n"); break; } (*textIt)->setRelCoor2D(calculateLinePosition( (linePos++ > 0) ? linePos : 0)); (*textIt)->setText((*it)); } while (textIt != this->bufferText.end()) { (*textIt)->setText(""); textIt++; } this->repositionText(); } /** * @brief clears the Shell (empties all buffers) */ void Shell::clear() { this->flush(); ShellBuffer::addBufferLineStatic("orxonox - shell\n ==================== \n", NULL); } /** * @brief listens for some event * @param event the Event happened */ void Shell::process(const Event &event) { if (event.bPressed) { if (event.type == SDLK_BACKQUOTE || event.type == SDLK_F12) { if (this->bActive == false) this->activate(); else this->deactivate(); } else if (event.type == SDLK_PAGEUP) { this->moveDisplayBuffer(+this->bufferDisplaySize-1); } else if (event.type == SDLK_PAGEDOWN) { this->moveDisplayBuffer(-this->bufferDisplaySize+1); } else if (event.type == EV_VIDEO_RESIZE) { this->updateResolution(event.resize.w); this->repositionText(); } } } void Shell::tick(float dt) { if (this->linesProcessed < this->shellBuffer->getLineCount()) { unsigned int pollLines = this->shellBuffer->getLineCount() - this->linesProcessed; if (this->bufferText.size() < pollLines) pollLines = this->bufferText.size(); if (unlikely(this->shellBuffer->getBuffer().size() < pollLines)) pollLines = this->shellBuffer->getBuffer().size(); std::list::const_iterator line = this->shellBuffer->getBuffer().begin(); unsigned int i; for(i = 0; i < pollLines; i++) line ++; for (i = 0; i < pollLines; i++) { this->printToDisplayBuffer((*--line)); } } this->linesProcessed = this->shellBuffer->getLineCount(); } /** * displays the Shell */ void Shell::draw() const { // transform for alignment. // setting the Blending effects this->backgroundMaterial.select(); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(0, 0); glVertex2f(this->getAbsCoor2D().x, this->getAbsCoor2D().y ); glTexCoord2f(1, 0); glVertex2f(GraphicsEngine::getInstance()->getResolutionX() - this->getAbsCoor2D().x, this->getAbsCoor2D().y ); glTexCoord2f(0, 1); glVertex2f(this->getAbsCoor2D().x, this->getAbsCoor2D().y + this->shellHeight); glTexCoord2f(1, 1); glVertex2f(GraphicsEngine::getInstance()->getResolutionX() - this->getAbsCoor2D().x, this->getAbsCoor2D().y + this->shellHeight); glEnd(); } /////////////////////// // HELPER FUNCTIONS // /////////////////////// /** * @brief calculates the position of a Buffer-Display Line * @param lineNumber the lineNumber from the bottom to calculate the position from * @returns the Position of the Line. */ Vector2D Shell::calculateLinePosition(unsigned int lineNumber) { return Vector2D(5.0f, (float)(this->textSize + this->lineSpacing)*(float)((int)this->bufferDisplaySize - (int)lineNumber - (int)1)); } /** * @brief displays some nice output from the Shell */ void Shell::debug() const { PRINT(3)("Debugging output to console (not this shell)\n"); // if (this->pressedKey != SDLK_FIRST) // printf("%s::%f %f\n", SDLKToKeyname(this->pressedKey), this->delayed, this->repeatDelay); this->shellBuffer->debug(); } /** * @brief a Handy Function, to Test the behaviour of the Shell. */ void Shell::testShell() const { for (unsigned int i = 0; i < 100; i++) PRINT(0)("%d\n", i); } SHELL_COMMAND(test, Shell, testShell); }