random_racer/src/Console.cpp

Go to the documentation of this file.
00001 #include <ctype.h> // isprint()
00002 #include <stdio.h>
00003 #include <stdlib.h>
00004 
00005 #include <fstream>
00006 #include <string>
00007 
00008 #include "SDL.h"
00009 
00010 #include <vrs/camera.h>
00011 #include <vrs/font.h>
00012 #include <vrs/opengl/lightmodelgl.h>
00013 #include <vrs/opengl/shapematerialgl.h>
00014 #include <vrs/opengl/transparencytechniquegl.h>
00015 #include <vrs/polygonset.h>
00016 #include <vrs/sg/behaviorcallback.h>
00017 #include <vrs/sg/canvas.h>
00018 #include <vrs/sg/key.h>
00019 #include <vrs/sg/keyevent.h>
00020 #include <vrs/sg/resizeevent.h>
00021 #include <vrs/text.h>
00022 #include <vrs/translation.h>
00023 
00024 #include "ResourceManager.h"
00025 
00026 #include "Console.h"
00027 
00028 using namespace VRS;
00029 
00030 
00031 namespace random_racer
00032 {
00033 
00034 // init the statics
00035 SO<Console> Console::s_instance = NULL;
00036 const float Console::c_marginX = 0.3;
00037 const float Console::c_marginY = 0.6;
00038 const float Console::c_pixelsPerChar = 11.0;
00039 const unsigned int Console::c_animationDuration = 230;
00040 
00041 
00042 Console::TextBuffer::TextBuffer(
00043     int p_maxLines, const Vector& p_startOffset, double p_lineHeight)
00044     : SceneThing()
00045 {
00046     m_maxLines    = p_maxLines;
00047     m_startOffset = p_startOffset;
00048     m_lineHeight  = p_lineHeight;
00049 }
00050 
00051 void
00052 Console::TextBuffer::addLine(SO<Text> p_newLine)
00053 {
00054     // prepend the new line
00055     prepend( p_newLine );
00056 
00057     // remove the last line if needed
00058     if(objects() > m_maxLines)
00059         remove(m_maxLines);
00060 
00061     SO<Text> line;
00062     Vector   offset = m_startOffset;
00063 
00064     // recalculate positions for all texts
00065     for( int i = 0; i < objects(); i++ )
00066     {
00067         line = VRS_Cast(Text, object(i));
00068         line->setPosition(offset);
00069         offset[1] += m_lineHeight;
00070     }
00071 }
00072 
00073 void
00074 Console::TextBuffer::addLine(const std::string& p_newLine)
00075 {
00076     addLine(new Text(Console::get()->font(), p_newLine));
00077 }
00078 
00079 Console::Console()
00080 {
00081     // a funky very big value which is the upper right
00082     // point (x and y) of the consoles background
00083     const float maxConsoleSize = 2300000.42;
00084 
00085     // the array containing the points for the console
00086     // background plane trianglestrip
00087     SO<NonPersistentArray<Vector> > points = new NonPersistentArray<Vector>();
00088     points->append(Vector(-c_marginX, -c_marginY, 0.0001));
00089     points->append(Vector(maxConsoleSize, -c_marginY, 0.0001));
00090     points->append(Vector(-c_marginX, maxConsoleSize, 0.0001));
00091     points->append(Vector(maxConsoleSize, maxConsoleSize, 0.0001));
00092 
00093     m_sceneThing = new SceneThing();
00094     // m_sceneThing->setNodeState(0);
00095 
00096     // create a first camera, the good one is created after the first resize
00097     m_cam = Camera::newNormalizedDeviceCoordsCamera();
00098     m_sceneThing->append(m_cam);
00099 
00100     // no lightmodel for the console
00101     m_sceneThing->append(new LightModelGL(false));
00102 
00103     // no longer needed because we assume a TransparencyTechniqueGL
00104     // in the main scene graph.
00105     // m_sceneThing->append(new TransparencyTechniqueGL());
00106 
00107     m_translation = new Translation();
00108     m_sceneThing->append(m_translation);
00109 
00110     // a blue color
00111     m_sceneThing->append(new ShapeMaterialGL(
00112                          Color(0.0, 0.25, 0.5, 0.54), Color(1.0)));
00113     m_sceneThing->append(new PolygonSet(
00114                          PolygonSet::TriangleStrip, points->newIterator()));
00115     // white
00116     m_sceneThing->append(new ShapeMaterialGL(Color(1.0), Color(1.0)));
00117 
00118     // get our console font
00119     m_font = ResourceManager::get()->loadFont("DejaVuSansMono.ttf",
00120                                               Font::POLYGON);
00121 
00122     m_currentLine = new Text(m_font);
00123     m_sceneThing->append(m_currentLine);
00124 
00125     m_textBuffer = new TextBuffer(42, Vector(0, 1.4, 0), 1.4);
00126     m_sceneThing->append(m_textBuffer);
00127 
00128     // initialize the history
00129     m_history.push_front("");
00130     m_histIter = m_history.begin();
00131 
00132     // init our canvas and the callback
00133     m_canvas = NULL;
00134 
00135     m_eventCallback = new BehaviorCallback;
00136     m_eventCallback->setCanvasCallback(
00137         new MethodCallback<Console>(this, &Console::handleEvents));
00138 
00139     m_startNewTabCompletition = true;
00140 
00141     m_animationInProgress = false;
00142 }
00143 
00144 SO<Console>
00145 Console::get()
00146 {
00147     if(s_instance == NULL)
00148         s_instance = new Console();
00149 
00150     return s_instance;
00151 }
00152 
00153 void
00154 Console::handleKey(unsigned int p_key)
00155 {
00156     std::string text = m_currentLine->getText();
00157 
00158     // backspace
00159     if((p_key == BackSpaceKey || p_key == DeleteKey) && !text.empty())
00160         m_currentLine->setText(text.substr(0, text.size()-1));
00161 
00162     // ctrl + u (delete line)
00163     else if(p_key == DeleteLineKey)
00164         m_currentLine->setText("");
00165 
00166     // enter / return
00167     else if(p_key == EnterKey || p_key == ReturnKey)
00168     {
00169         m_textBuffer->addLine(m_currentLine);
00170         m_sceneThing->remove(m_currentLine);
00171 
00172         m_currentLine = new Text(m_font);
00173         m_sceneThing->append(m_currentLine);
00174 
00175         dispatchCommand(text);
00176     }
00177     // up
00178     else if(p_key == UpKey)
00179         historyUp();
00180 
00181     // down
00182     else if(p_key == DownKey)
00183         historyDown();
00184 
00185     // tab
00186     else if(p_key == TabKey)
00187         tabCompleteForward();
00188 
00189     // shift + tab
00190     else if(p_key == ShiftTabKey)
00191         tabCompleteBackward();
00192 
00193     // "else"
00194     else if(isprint(p_key))
00195     {
00196         text += p_key;
00197         m_currentLine->setText(text);
00198     }
00199 
00200     // check if we should start a new tab completition cycle next time
00201     m_startNewTabCompletition = (p_key == TabKey||p_key == ShiftTabKey) ?
00202                                                                   false : true;
00203 }
00204 
00205 bool
00206 Console::dispatchCommand(const std::string& p_line, bool p_batchMode)
00207 {
00208     if(!p_batchMode && !p_line.empty())
00209     {
00210         // put line into the history
00211         m_histIter = m_history.begin();
00212         m_history.insert(++m_histIter, p_line);
00213 
00214         // .. and reset the iterator
00215         m_histIter = m_history.begin();
00216     }
00217 
00218     std::string cmd, args;
00219     unsigned int state = 0, r = 0;
00220 
00221     // try to parse the command line
00222     // (strip leading spaces and spaces between the
00223     //  command name and the arguments)
00224     for(unsigned int i = 0; i < p_line.size(); i++)
00225         if(state == 0 && p_line[i] != ' ')
00226         {
00227             r = i;
00228             state = 1;
00229         }
00230         else if(state == 1 && p_line[i] == ' ')
00231         {
00232             cmd = p_line.substr(r, i-r);
00233             state = 2;
00234         }
00235         else if(state == 2 && p_line[i] != ' ')
00236         {
00237             r = i;
00238             state = 3;
00239             break;
00240         }
00241 
00242     // catch some non default cases and set the
00243     // variables according to them
00244     if(state < 2 && cmd.empty())
00245         cmd  = p_line.substr(r, p_line.size()-r);
00246     else if(state == 3)
00247         args = p_line.substr(r, p_line.size()-r);
00248 
00249 
00250     if(!cmd.empty())
00251     {
00252         // if there is not command with that name try to find one that
00253         // starts with this string and if there is only one set this as
00254         // the command to execute (like in gdb).
00255         if(!m_registry.contains(cmd))
00256         {
00257             unsigned int found = 0;
00258             std::string fullCmd;
00259             SO<Iterator<std::string> > it = m_registry.newKeyIterator();
00260 
00261             for(unsigned int i = 0; i < it->size() && found <= 1; i++)
00262                 if((it->get(i)).find(cmd) == 0)
00263                 {
00264                     fullCmd = it->get(i);
00265                     found++;
00266                 }
00267 
00268             if(found != 1)
00269             {
00270                 if(!p_batchMode)
00271                     addLine("(command not found)");
00272 
00273                 return false;
00274             }
00275 
00276             cmd = fullCmd;
00277         }
00278 
00279         // the following code tries to cast to a 2 argument
00280         // callback first and ifthis fails it tries to
00281         // cast to a callback without any arguments
00282         CallbackBase* c = (CallbackBase*)m_registry[cmd];
00283 
00284 // 2 arguments
00285 if(dynamic_cast<Callback2<const std::string&,const std::string&>*>(c) != NULL)
00286     ((Callback2<const std::string&,const std::string&>*)c)->callback(cmd, args);
00287 
00288         // 1 argument
00289         else if(dynamic_cast<Callback1<const std::string&>*>(c) != NULL)
00290             ((Callback1<const std::string&>*)c)->callback(args);
00291 
00292         // no args
00293         else if(dynamic_cast<Callback*>(c) != NULL)
00294             ((Callback*)c)->callback();
00295 
00296         else
00297         {
00298             if(!p_batchMode)
00299                 addLine("(invalid callback)");
00300 
00301             return false;
00302         }
00303     }
00304     // check if the last command is not m_history.end() or if it was empty
00305     // and if so, dispatch it in batch-mode again.
00306     else if(!p_batchMode &&
00307             ++m_histIter != m_history.end() &&
00308             !(*m_histIter).empty())
00309         return dispatchCommand(*(m_histIter--), true);
00310 
00311     else
00312     {
00313         if(!p_batchMode)
00314             addLine("(empty line)");
00315 
00316         return false;
00317     }
00318 
00319     return true;
00320 }
00321 
00322 void
00323 Console::historyUp(bool p_realyUp)
00324 {
00325     // history is empty
00326     if(m_histIter == m_history.end())
00327         return;
00328 
00329     // up pressed
00330     if(p_realyUp)
00331     {
00332         // this is the end
00333         if(++m_histIter == m_history.end())
00334             m_histIter--;
00335     }
00336     // down pressed and this is not the beginning
00337     else if(m_histIter != m_history.begin())
00338             m_histIter--;
00339 
00340     m_currentLine->setText(*m_histIter);
00341 }
00342 
00343 void
00344 Console::setCanvas(SO<Canvas> p_canvas)
00345 {
00346     if(p_canvas == m_canvas)
00347         return;
00348 
00349     if(m_canvas != NULL)
00350     {
00351         m_eventCallback->deactivate();
00352         m_canvas->remove(m_eventCallback);
00353     }
00354 
00355     m_canvas = p_canvas;
00356 
00357     if(p_canvas == NULL)
00358         return;
00359 
00360     m_canvas->append(m_eventCallback);
00361     m_eventCallback->activate();
00362 }
00363 
00364 void
00365 Console::handleEvents()
00366 {
00367     KeyEvent* event = VRS_Cast(KeyEvent, m_eventCallback->currentCanvasEvent());
00368 
00369     if(event != NULL)
00370         keyEvent(event);
00371 
00372     else
00373     {
00374         ResizeEvent* event = VRS_Cast(ResizeEvent,
00375                                       m_eventCallback->currentCanvasEvent());
00376         if(event != NULL)
00377             resizeEvent(event);
00378     }
00379 
00380     m_canvas->postForRedisplay();
00381 }
00382 
00383 void
00384 Console::resizeEvent(SO<ResizeEvent> p_event)
00385 {
00386     double x1, x2, y1, y2, ratio;
00387     ratio = double(p_event->width()) / double(p_event->height());
00388 
00389     y2 = double(p_event->height())/2.0/c_pixelsPerChar;
00390     y1 = -y2;
00391     x1 = -c_marginX;
00392     x2 = (double(p_event->height())/c_pixelsPerChar)*ratio;
00393 
00394     m_sceneThing->remove(m_cam);
00395     m_cam = Camera::newNormalizedDeviceCoordsCamera(x1, x2, y1, y2);
00396     m_sceneThing->prepend(m_cam);
00397 }
00398 
00399 void
00400 Console::keyEvent(SO<KeyEvent> p_event)
00401 {
00402     if(p_event->pressed())
00403     {
00404         if(p_event->keyCode() == '<' || p_event->keyCode() == '^')
00405             toggleVisibility();
00406 
00407         else if(isVisible())
00408             handleKey(p_event->keyCode());
00409     }
00410 }
00411 
00412 void
00413 Console::attach(SO<Canvas> p_canvas)
00414 {
00415     if(! p_canvas->contains(sceneThing()))
00416         p_canvas->append(sceneThing());
00417 
00418     setCanvas(p_canvas);
00419 }
00420 
00421 void
00422 Console::detach()
00423 {
00424     if(m_canvas->contains(sceneThing()))
00425         m_canvas->remove(sceneThing());
00426 
00427     setCanvas(NULL);
00428 }
00429 
00430 // this 2 macros are used in the next method for makeing it
00431 // easier to read and maintain
00432 #define iterateBackCyclic(BEGIN, END, ITERATOR) \
00433     { if(ITERATOR-- == BEGIN) { ITERATOR = END; ITERATOR--; } }
00434 
00435 #define iterateForwardCyclic(BEGIN, END, ITERATOR) \
00436     { if(++ITERATOR == END) ITERATOR = BEGIN; }
00437 
00438 void
00439 Console::tabCompleteForward(bool p_forward)
00440 {
00441     if(m_startNewTabCompletition)
00442     {
00443         m_currentTabWord = m_currentLine->getText();
00444 
00445         m_tabList.clear();
00446 
00447         SO<Iterator<std::string> > it = m_registry.newKeyIterator();
00448         int pos;
00449 
00450         // find all commands that start with the current word after
00451         // which someone pressed tab
00452         for(unsigned int i = 0; i < it->size(); i++)
00453         {
00454             pos = (it->get(i)).find(m_currentTabWord);
00455 
00456             if(pos == 0)
00457                 m_tabList.push_back(it->get(i));
00458         }
00459 
00460         m_tabList.sort();
00461 
00462         if(p_forward)
00463         {
00464             m_tabIter = m_tabList.begin();
00465             m_lastTabWasForward = true;
00466         }
00467         else
00468         {
00469             m_tabIter = m_tabList.end();
00470             m_lastTabWasForward = false;
00471 
00472             if(m_tabIter != m_tabList.begin())
00473                 m_tabIter--;
00474         }
00475     }
00476 
00477     // check for empty container
00478     if(m_tabIter == m_tabList.begin() && m_tabIter == m_tabList.end())
00479         return;
00480 
00481 
00482     if(p_forward)
00483     {
00484         // if the last tab was a backwards tab then compensate this by
00485         // iterating forwards twice
00486         if(!m_lastTabWasForward)
00487         {
00488             iterateForwardCyclic(m_tabList.begin(), m_tabList.end(), m_tabIter);
00489             iterateForwardCyclic(m_tabList.begin(), m_tabList.end(), m_tabIter);
00490         }
00491 
00492         m_currentLine->setText(*m_tabIter + " ");
00493 
00494         iterateForwardCyclic(m_tabList.begin(), m_tabList.end(), m_tabIter);
00495         m_lastTabWasForward = true;
00496     }
00497     else
00498     {
00499         if(m_lastTabWasForward)
00500         {
00501             iterateBackCyclic(m_tabList.begin(), m_tabList.end(), m_tabIter);
00502             iterateBackCyclic(m_tabList.begin(), m_tabList.end(), m_tabIter);
00503         }
00504 
00505         m_currentLine->setText(*m_tabIter + " ");
00506 
00507         iterateBackCyclic(m_tabList.begin(), m_tabList.end(), m_tabIter);
00508         m_lastTabWasForward = false;
00509     }
00510 }
00511 
00512 #undef iterateBackCyclic
00513 #undef iterateForwardCyclic
00514 
00515 void
00516 Console::autoexec()
00517 {
00518     std::fstream stream(ResourceManager::get()->getPathOfFile(
00519                             "autoexec.txt").c_str(), std::ios_base::in);
00520 
00521     if(!stream.is_open())
00522         return;
00523 
00524     char lineBuf[255];
00525 
00526     while(stream.good())
00527     {
00528         stream.getline(lineBuf, sizeof(lineBuf));
00529 
00530         // ignore empty lines and the ones staring with a #
00531         if(lineBuf[0] != 0 && lineBuf[0] != '#')
00532             dispatchCommand(lineBuf, true);
00533     }
00534 
00535     stream.close();
00536 }
00537 
00538 bool
00539 Console::toggleVisibility()
00540 {
00541     if(isVisible())
00542     {
00543         hide();
00544         return false;
00545     }
00546     else
00547     {
00548         show();
00549         return true;
00550     }
00551 }
00552 
00553 bool
00554 Console::isVisible()
00555 {
00556     return(!m_animationInProgress && m_sceneThing->getNodeState() != 0);
00557 }
00558 
00559 void
00560 Console::startAnimation()
00561 {
00562     m_lastTick = SDL_GetTicks();
00563 
00564     m_animationInProgress = true;
00565 
00566     startTimer(23);
00567 }
00568 
00569 void
00570 Console::hide()
00571 {
00572     if(m_animationInProgress || m_sceneThing->getNodeState() == 0)
00573         return;
00574 
00575 
00576     m_animationDistance = double(m_canvas->getHeight())/2.0/c_pixelsPerChar;
00577     m_distancePerTick = m_animationDistance / double(c_animationDuration);
00578 
00579     startAnimation();
00580 }
00581 
00582 void
00583 Console::show()
00584 {
00585     if(m_animationInProgress || m_sceneThing->getNodeState() != 0)
00586         return;
00587 
00588 
00589     m_sceneThing->setNodeState(
00590         SceneNode::EvaluationOn|SceneNode::TraversalOn);
00591 
00592     m_animationDistance = double(m_canvas->getHeight())/2.0/c_pixelsPerChar;
00593     m_translation->setTranslate(Vector(0.0, m_animationDistance, 0.0));
00594     m_distancePerTick = -m_animationDistance / double(c_animationDuration);
00595 
00596     startAnimation();
00597 }
00598 
00599 void
00600 Console::timerTick()
00601 {
00602     unsigned int currentTick = SDL_GetTicks();
00603 
00604     // std::cout << "@" << (currentTick - m_lastTick) << std::endl;
00605 
00606     // do not update anything if no time elapsed
00607     if(currentTick == m_lastTick)
00608         return;
00609 
00610     Vector tr = m_translation->getTranslate();
00611     tr[1] += (m_distancePerTick * (currentTick - m_lastTick));
00612 
00613 
00614     // catch a finished animation
00615     if(tr[1] < 0 || tr[1] > m_animationDistance)
00616     {
00617         // std::cout << "STOP " << tr[1] << std::endl;
00618 
00619         m_animationInProgress = false;
00620 
00621         // this was a show
00622         if(m_distancePerTick < 0)
00623             m_translation->setTranslate(Vector(0,0,0));
00624 
00625         // this was a hide
00626         else
00627             m_sceneThing->setNodeState(0);
00628 
00629 
00630         stopTimer();
00631     }
00632     else
00633     {
00634         m_translation->setTranslate(tr);
00635         m_lastTick = currentTick;
00636     }
00637 }
00638 
00639 } // namespace random_racer

Generated on Fri May 11 21:01:57 2007 for Random Racer by  doxygen 1.5.1