Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/util/shell.cc @ 5125

Last change on this file since 5125 was 5125, checked in by bensch, 19 years ago

orxonox/trunk: a first command can be executed 'clear'
also improved the seg-fault-protection of the TextEngine

File size: 20.1 KB
Line 
1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11   ### File Specific:
12   main-programmer: Benjamin Grauer
13   co-programmer: ...
14*/
15
16//#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_
17
18#include "shell.h"
19
20#include "text_engine.h"
21#include "list.h"
22#include "graphics_engine.h"
23#include "event_handler.h"
24
25#include "load_param.h"
26#include "class_list.h"
27
28#include "key_names.h"
29#include "debug.h"
30#include <stdarg.h>
31#include <stdio.h>
32
33using namespace std;
34
35
36/**
37 * standard constructor
38 */
39Shell::Shell ()
40{
41  this->setClassID(CL_SHELL, "Shell");
42  this->setName("Shell");
43
44  this->shellHeight = 400;
45  this->bActive = false;
46  this->buffer = new tList<char>;
47  this->bufferIterator = this->buffer->getIterator();
48
49  this->textSize = 15;
50  this->lineSpacing = 5;
51
52  //this->bufferSize = 0;
53  this->bufferText = NULL;
54  this->setBufferSize(100);
55  this->bufferDisplaySize = 10;
56  this->setAbsCoor2D(3, -400);
57  this->delayed = 0;
58  this->setRepeatDelay(.3, .05);
59  this->pressedKey = SDLK_FIRST;
60
61  this->inputLineText = NULL;
62  this->inputLine = new char[1];
63  this->inputLine[0] = '\0';
64
65  this->rebuildText();
66  this->completionList = NULL;
67
68  // EVENT-Handler subscription of '`' to all States, and all other keyboard commands to ES_SEHLL
69  EventHandler* evh = EventHandler::getInstance();
70  evh->subscribe(this, ES_ALL, SDLK_BACKQUOTE);
71  for (int i = 1; i < SDLK_LAST; i++)
72    evh->subscribe(this, ES_SHELL, i);
73}
74
75Shell* Shell::singletonRef = NULL;
76
77/**
78 * standard deconstructor
79 */
80Shell::~Shell ()
81{
82  // delete the displayable Buffers
83  for (int i = 0; i < this->bufferDisplaySize; i++)
84    delete this->bufferText[i];
85  delete[] this->bufferText;
86
87  // delete the inputLine
88  delete this->inputLineText;
89  delete this->inputLine;
90
91  // delete all the Chars in the Buffers
92  char* charElem = this->bufferIterator->firstElement();
93  while (charElem != NULL)
94  {
95    delete charElem;
96    charElem = this->bufferIterator->nextElement();
97  }
98  delete this->bufferIterator;
99
100//  if (this->completionList != NULL)
101    //delete this->completionList;
102
103  Shell::singletonRef = NULL;
104}
105
106
107/**
108 * activates the shell
109 *
110 * This also feeds the Last few lines from the main buffers into the displayBuffer
111 */
112void Shell::activate()
113{
114  if (this->bActive == true)
115    PRINTF(3)("The shell is already active\n");
116  this->bActive = true;
117
118  EventHandler::getInstance()->setState(ES_SHELL);
119  this->setRelCoorSoft2D(0, 0, 1, 5);
120
121  this->bufferIterator->lastElement();
122  for (int i = 0; i < this->bufferDisplaySize; i++)
123    this->bufferText[i]->setText(this->bufferIterator->prevElement());
124}
125
126/**
127 * deactiveates the Shell.
128 */
129void Shell::deactivate()
130{
131  if (this->bActive == false)
132    PRINTF(3)("The shell is already inactive\n");
133  this->bActive = false;
134
135  EventHandler::getInstance()->setState(ES_GAME);
136  this->setRelCoorSoft2D(0, -400, 1, 5);
137
138}
139
140/**
141 * sets the size of the text and spacing
142 * @param textSize the size of the Text in Pixels
143 * @param lineSpacing the size of the Spacing between two lines in pixels
144 *
145 * this also rebuilds the entire Text, inputLine and displayBuffer,
146 * to be accurate again.
147 */
148void Shell::setTextSize(unsigned int textSize, unsigned int lineSpacing)
149{
150  this->textSize = textSize;
151  this->lineSpacing = lineSpacing;
152
153  this->rebuildText();
154}
155
156void Shell::rebuildText()
157{
158  if (this->inputLineText == NULL)
159    delete this->inputLineText;
160  this->inputLineText = TextEngine::getInstance()->createText("fonts/Aniron_Bold.ttf", this->textSize, TEXT_RENDER_DYNAMIC);
161  this->inputLineText->setColor(1, 0, 0);
162  this->inputLineText->setAlignment(TEXT_ALIGN_LEFT);
163  this->inputLineText->setText(NULL);
164  this->inputLineText->setParent2D(this);
165  this->inputLineText->setRelCoor2D(5, (this->textSize + this->lineSpacing)*this->bufferDisplaySize + this->textSize);
166
167  this->setBufferDisplaySize(this->bufferDisplaySize);
168}
169
170/**
171 * sets The count of Lines to display in the buffer.
172 * @param bufferDisplaySize the count of lines to display in the Shell-Buffer.
173 */
174void Shell::setBufferDisplaySize(unsigned int bufferDisplaySize)
175{
176  if (this->bufferText != NULL)
177  {
178    for (unsigned int i = 0; i < this->bufferDisplaySize; i++)
179      delete this->bufferText[i];
180    delete[] this->bufferText;
181  }
182
183  this->bufferText = new Text*[bufferDisplaySize];
184  for (unsigned int i = 0; i < bufferDisplaySize; i++)
185  {
186    this->bufferText[i] = TextEngine::getInstance()->createText("fonts/Aniron_Bold.ttf", this->textSize, TEXT_RENDER_DYNAMIC);
187    this->bufferText[i]->setColor(1, 0, 0);
188    this->bufferText[i]->setAlignment(TEXT_ALIGN_LEFT);
189    this->bufferText[i]->setRelCoor2D(calculateLinePosition(i));
190    this->bufferText[i]->setText(NULL);
191    this->bufferText[i]->setParent2D(this);
192  }
193  this->bufferDisplaySize = bufferDisplaySize;
194
195  this->shellHeight = (this->textSize + this->lineSpacing) * (bufferDisplaySize+1);
196}
197
198/**
199 * deletes all the Buffers
200 */
201void Shell::flushBuffers()
202{
203  // remove all chars from the BufferTexts.
204  if (this->bufferText)
205    for (int i = 0; i < this->bufferDisplaySize; i++)
206    {
207      this->bufferText[i]->setText(NULL, true);
208    }
209
210  // delete all the Chars in the Buffers
211  tIterator<char>* charIterator = this->buffer->getIterator();
212  char* charElem = charIterator->firstElement();
213  while (charElem != NULL)
214  {
215    delete charElem;
216
217    charElem = charIterator->nextElement();
218  }
219  delete charIterator;
220  delete this->buffer;
221  this->buffer = new tList<char>;
222}
223
224/**
225 * adds a new Line to the List of Buffers
226 * @param line the Line as in the first argument in printf
227 * @param args the arguments as a va_list
228 */
229bool Shell::addBufferLineStatic(const char* line, ...)
230{
231  va_list arguments;
232  va_start(arguments, line);
233
234#if DEBUG < 3
235  if (Shell::singletonRef == NULL)
236#endif
237
238  vprintf(line, arguments);
239#if DEBUG < 3
240  else
241#else
242  if (Shell::singletonRef != NULL)
243#endif
244    Shell::singletonRef->addBufferLine(line, arguments);
245  return true;
246}
247
248/**
249 * add a Line to the List of Buffers
250 * @param line
251 * @param arguments
252 *
253 * This function Adds one line to the buffer.
254 * and displays the line as the First Line of the display-buffer
255 */
256void Shell::addBufferLine(const char* line, va_list arguments)
257{
258   vsprintf(this->bufferArray, line, arguments);
259
260   char* newLine = new char[strlen(this->bufferArray)+1];
261   strcpy(newLine, this->bufferArray);
262
263   this->buffer->add(newLine);
264
265   if (this->buffer->getSize() > this->bufferSize)
266   {
267     delete this->buffer->firstElement();
268     this->buffer->remove(this->buffer->firstElement());
269   }
270
271   if (this->bActive)
272   {
273     this->printToDisplayBuffer(newLine);
274   }
275}
276
277/**
278 * prints out some text to the input-buffers
279 * @param text the text to output.
280 */
281void Shell::printToDisplayBuffer(const char* text)
282{
283  if(likely(bufferText != NULL))
284  {
285    Text* lastText = this->bufferText[this->bufferDisplaySize-1];
286
287    Text* swapText;
288    Text* moveText = this->bufferText[0];
289    this->bufferText[0]->setRelCoorSoft2D(this->calculateLinePosition(1),10);
290    for (unsigned int i = 1; i < this->bufferDisplaySize; i++)
291    {
292      if ( i < this->bufferDisplaySize-1)
293        this->bufferText[i]->setRelCoorSoft2D(this->calculateLinePosition(i+1),5);
294      swapText = this->bufferText[i];
295      this  ->bufferText[i] = moveText;
296      moveText = swapText;
297    }
298    lastText->setRelCoor2D(this->calculateLinePosition(0));
299    this->bufferText[0] = lastText;
300
301    this->bufferText[0]->setText(text, true);
302  }
303}
304
305/**
306 * moves the buffer around lineCount lines upwards (negative values move down)
307 * @param lineCount the Count of lines to move upwards
308 *
309 * @todo
310 */
311void Shell::moveBuffer(int lineCount)
312{
313}
314
315/**
316 * @param lineNumber the n-th line from the bottom
317 * @returns the Buffer at Line lineNumber
318 */
319const char* Shell::getBufferLine(unsigned int lineNumber)
320{
321  tIterator<char>* charIterator = this->buffer->getIterator();
322  char* charElem = charIterator->firstElement();
323
324  int i = 1;
325  while (charElem != NULL)
326  {
327    if (i++ < lineNumber)
328    {
329      delete charIterator;
330      return charElem;
331    }
332
333    charElem = charIterator->nextElement();
334  }
335  delete charIterator;
336}
337
338/**
339 * deletes the InputLine
340 */
341void Shell::flushInputLine()
342{
343  if (likely(this->inputLine != NULL))
344  {
345    delete[] this->inputLine;
346  }
347  this->inputLine = new char[1];
348  *this->inputLine = '\0';
349  this->inputLineText->setText(this->inputLine, true);
350}
351
352/**
353 * adds one character to the inputLine
354 * @param character the character to add to the inputLine
355 */
356void Shell::addCharacter(char character)
357{
358  char* addCharLine = new char[strlen(inputLine)+2];
359
360  sprintf(addCharLine, "%s%c", this->inputLine, character);
361  delete this->inputLine;
362  this->inputLine = addCharLine;
363  this->inputLineText->setText(inputLine, true);
364}
365
366/**
367 * adds multiple Characters to thr inputLine
368 * @param characters a '\0' terminated char-array to add to the InputLine
369 */
370void Shell::addCharacters(const char* characters)
371{
372  char* addCharLine = new char[strlen(inputLine)+strlen(characters)+1];
373
374  sprintf(addCharLine, "%s%s", this->inputLine, characters);
375  delete this->inputLine;
376  this->inputLine = addCharLine;
377  this->inputLineText->setText(inputLine);
378}
379
380/**
381 * removes characterCount characters from the InputLine
382 * @param characterCount the count of Characters to remove from the input Line
383 */
384void Shell::removeCharacters(unsigned int characterCount)
385{
386  if (strlen(this->inputLine) == 0)
387    return;
388
389  if (characterCount > strlen(this->inputLine))
390    characterCount = strlen(this->inputLine);
391
392  char* removeCharLine = new char[strlen(inputLine)-characterCount+1];
393
394  strncpy(removeCharLine, this->inputLine, strlen(inputLine)-characterCount);
395  removeCharLine[strlen(inputLine)-characterCount] = '\0';
396  delete this->inputLine;
397  this->inputLine = removeCharLine;
398  this->inputLineText->setText(inputLine);
399}
400
401/**
402 * executes the command stored in the inputLine
403 * @return true if the command was commited successfully, false otherwise
404 */
405bool Shell::executeCommand()
406{
407  this->addBufferLineStatic("Execute Command: %s\n", this->inputLine);
408
409  if (!strcmp(this->inputLine, "clear"))
410  {
411    this->flushBuffers();
412  }
413
414  this->flushInputLine();
415
416  return false;
417}
418
419/**
420 * sets the Repeate-delay and rate
421 * @param repeatDelay the Delay it takes, to repeate a key
422 * @param repeatRate the rate to repeate a pressed key
423 */
424void Shell::setRepeatDelay(float repeatDelay, float repeatRate)
425{
426  this->repeatDelay = repeatDelay;
427  this->repeatRate = repeatRate;
428
429}
430
431/**
432 * listens for some event
433 * @param event the Event happened
434 */
435void Shell::process(const Event &event)
436{
437  if (event.bPressed)
438  {
439    PRINTF(4)("Shell received command %s\n", SDLKToKeyname(event.type));
440    if (event.type == SDLK_BACKQUOTE)
441    {
442      if (EventHandler::getInstance()->getState() == ES_GAME)
443        this->activate();
444      else
445        this->deactivate();
446    }
447    else if (event.type == SDLK_F1)
448      this->help();
449    else if (event.type == SDLK_F2)
450      this->debug();
451    else if (event.type == SDLK_TAB)
452      this->autoComplete();
453    else if (event.type == SDLK_BACKSPACE)
454    {
455      this->delayed = this->repeatDelay;
456      this->pressedKey = SDLK_BACKSPACE;
457      this->removeCharacters(1);
458    }
459    else if (event.type == SDLK_RETURN)
460      this->executeCommand();
461    else if (likely(event.type < 127))
462    {
463      this->delayed = this->repeatDelay;
464      this->pressedKey = event.type;
465      this->addCharacter(event.type);
466    }
467  }
468  else // if(!event.bPressed)
469  {
470    if (this->pressedKey == event.type)
471    {
472      this->pressedKey = SDLK_FIRST;
473      this->delayed = 0.0;
474    }
475  }
476}
477
478/**
479 * ticks the Shell for dt Seconds
480 * @param dt the elapsed time since the last tick();
481 */
482void Shell::tick(float dt)
483{
484  if (this->delayed > 0.0)
485    this->delayed -= dt;
486  else if (this->pressedKey != SDLK_FIRST )
487  {
488    this->delayed = this->repeatRate;
489    if (this->pressedKey == SDLK_BACKSPACE)
490      this->removeCharacters(1);
491    else if (pressedKey < 127)
492      this->addCharacter(this->pressedKey);
493  }
494}
495
496/**
497 * displays the Shell
498 */
499void Shell::draw() const
500{
501  glPushMatrix();
502  // transform for alignment.
503  // setting the Blending effects
504
505  glColor4f(0.0f, 0.0f, 0.8f, .4);
506  glEnable(GL_BLEND);
507  glDisable(GL_TEXTURE_2D);
508  glBlendFunc(GL_SRC_ALPHA, GL_ONE);
509
510//  glBindTexture(GL_TEXTURE_2D, this->texture);
511  glBegin(GL_QUADS);
512
513//  glTexCoord2f(this->texCoord.minU, this->texCoord.minV);
514  glVertex2f(this->getAbsCoor2D().x,   this->getAbsCoor2D().);
515
516//  glTexCoord2f(this->texCoord.maxU, this->texCoord.minV);
517  glVertex2f(GraphicsEngine::getInstance()->getResolutionX() - this->getAbsCoor2D().x, this->getAbsCoor2D().);
518
519//  glTexCoord2f(this->texCoord.maxU, this->texCoord.maxV);
520  glVertex2f(GraphicsEngine::getInstance()->getResolutionX() - this->getAbsCoor2D().x, this->getAbsCoor2D().y + this->shellHeight);
521
522//  glTexCoord2f(this->texCoord.minU, this->texCoord.maxV);
523  glVertex2f(this->getAbsCoor2D().x, this->getAbsCoor2D().y + this->shellHeight);
524
525  glEnd();
526}
527
528
529/**
530 * autocompletes the Shell's inputLine
531 * @returns true, if a result was found, false otherwise
532 *
533 * @todo implement it!!
534 */
535bool Shell::autoComplete()
536{
537  //PRINTF(3)("AutoCompletion not implemented yet\n");
538
539  char* completionLine = new char[strlen(inputLine)+1];
540  strcpy(completionLine, this->inputLine);
541
542  char* commandBegin = strrchr(completionLine, ' ');
543  if (commandBegin == NULL)
544    commandBegin = completionLine;
545  else
546  {
547    if(commandBegin >= completionLine + strlen(completionLine))
548      commandBegin = completionLine + strlen(completionLine);
549    else
550      commandBegin++;
551  }
552
553  char* objectStart;
554  if (objectStart = strstr(commandBegin, "::"))
555  {
556    char* classIdentity = new char[objectStart - commandBegin +1];
557    strncpy(classIdentity, commandBegin, objectStart - commandBegin);
558    classIdentity[objectStart - commandBegin] = '\0';
559    this->objectComplete(objectStart+2, ClassList::StringToID(classIdentity));
560    delete[] classIdentity;
561  }
562  else
563    this->classComplete(commandBegin);
564
565  delete[] completionLine;
566}
567
568/**
569 * autocompletes a className
570 * @param classBegin the Beginning of a String to autoComplete
571 * @return true on success, false otherwise
572 */
573bool Shell::classComplete(const char* classBegin)
574{
575  if (unlikely(classBegin == NULL))
576    return false;
577  const tList<const char>* clList = ClassList::getClassList();
578  if (clList != NULL)
579  {
580    const tList<const char>* classList = this->createCompleteList(clList, classBegin);
581    if (classList != NULL)
582      this->generalComplete(classList, classBegin, "%s::", "::");
583    else
584      return false;
585  }
586  else
587    return false;
588  return true;
589}
590
591/**
592 * autocompletes an ObjectName
593 * @param objectBegin the beginning string of a Object
594 * @param classID the ID of the Class to search for.
595 * @return true on success, false otherwise
596 */
597bool Shell::objectComplete(const char* objectBegin, long classID)
598{
599  printf("%s\n", objectBegin);
600
601  if (unlikely(objectBegin == NULL))
602    return false;
603  tList<BaseObject>* boList = ClassList::getList(classID);
604  if (boList != NULL)
605  {
606    printf("\n", boList->firstElement()->getName());
607    const tList<const char>* objectList = this->createCompleteList(boList, objectBegin);
608    if (objectList != NULL)
609      this->generalComplete(objectList, objectBegin, "%s");
610    else
611      return false;
612  }
613  else
614    return false;
615  return true;
616}
617
618bool Shell::functionComplete(const char* functionBegin)
619{
620}
621
622/**
623 * completes the inputline on grounds of an inputList
624 * @param stringList the List to parse through
625 * @param begin the String to search in the inputList, and to extend with it.
626 * @param displayAs how to display the found value to the user, printf-style, !!with only one %s!! ex.: "::%s::"
627 * @param addBack what should be added at the end of the completion
628 * @param addFront what should be added to the front of one finished completion
629 * @return true if ok, false otherwise
630 */
631bool Shell::generalComplete(const tList<const char>* stringList, const char* begin, const char* displayAs, const char* addBack, const char* addFront)
632{
633  if (stringList->getSize() == 0)
634    return false;
635
636  const char* addString = stringList->firstElement();
637  unsigned int addLength = 0;
638  unsigned int inputLenght = strlen(begin);
639
640  if (addString != NULL)
641    addLength = strlen(addString);
642  tIterator<const char>* charIterator = stringList->getIterator();
643  const char* charElem = charIterator->firstElement();
644  while (charElem != NULL)
645  {
646    PRINTF(0)(displayAs, charElem);
647    for (unsigned int i = inputLenght; i < addLength; i++)
648      if (addString[i] != charElem[i])
649    {
650      addLength = i;
651      break;
652    }
653    charElem = charIterator->nextElement();
654  }
655  delete charIterator;
656
657  if (addLength >= inputLenght)
658  {
659    char* adder = new char[addLength+1];
660    strncpy(adder, addString, addLength);
661    adder[addLength] = '\0';
662    this->removeCharacters(inputLenght);
663    this->addCharacters(adder);
664    if (addBack != NULL && stringList->getSize() == 1)
665      this->addCharacters("::");
666    delete[] adder;
667  }
668  return true;
669}
670
671/**
672 * searches for classes, which beginn with classNameBegin
673 * @param inputList the List to parse through
674 * @param classNameBegin the beginning string
675 * @return a NEW char-array with ClassNames. The LIST should be deleted afterwards,
676 * !! The strings MUST NOT be deleted !!
677 */
678const tList<const char>* Shell::createCompleteList(const tList<const char>* inputList, const char* classNameBegin)
679{
680  if (inputList == NULL || classNameBegin == NULL)
681    return NULL;
682  unsigned int searchLength = strlen(classNameBegin);
683  if (this->completionList != NULL)
684    delete this->completionList;
685  this->completionList = new tList<const char>;
686
687//  tList<const char>* classList = ClassList::getClassList();
688
689  tIterator<const char>* iterator = inputList->getIterator();
690  const char* enumString = iterator->firstElement();
691  while (enumString != NULL)
692  {
693    if (strlen(enumString)>searchLength+1 &&
694        !strncasecmp(enumString, classNameBegin, searchLength))
695    {
696      this->completionList->add(enumString);
697    }
698    enumString = iterator->nextElement();
699  }
700  delete iterator;
701
702  return this->completionList;
703}
704
705/**
706 * searches for classes, which beginn with classNameBegin
707 * @param inputList the List to parse through
708 * @param classNameBegin the beginning string
709 * @return a NEW char-array with ClassNames. The LIST should be deleted afterwards,
710 * !! The strings MUST NOT be deleted !!
711 */
712const tList<const char>* Shell::createCompleteList(const tList<BaseObject>* inputList, const char* classNameBegin)
713{
714  if (inputList == NULL || classNameBegin == NULL)
715    return NULL;
716  unsigned int searchLength = strlen(classNameBegin);
717  if (this->completionList != NULL)
718    delete this->completionList;
719  this->completionList = new tList<const char>;
720
721  tIterator<BaseObject>* iterator = inputList->getIterator();
722  BaseObject* enumBO = iterator->firstElement();
723  while (enumBO != NULL)
724  {
725    if (enumBO->getName() != NULL &&
726        strlen(enumBO->getName())>searchLength+1 &&
727        !strncasecmp(enumBO->getName(), classNameBegin, searchLength))
728    {
729      this->completionList->add(enumBO->getName());
730    }
731    enumBO = iterator->nextElement();
732  }
733  delete iterator;
734
735  return this->completionList;
736}
737
738void Shell::help() const
739{
740  PRINT(0)("Help for the most important Shell-commands\n");
741  PRINT(0)("F1 - HELP; F2 - DEBUG; ` - open/close shell\n");
742  PRINT(0)("input order:\n");
743  PRINT(0)("ClassName::objectName function [parameter1, [parameter2 ...]]  or\n");
744  PRINT(0)("Command [parameter]\n");
745}
746
747
748///////////////////////
749// HELPER FUNCTIONS  //
750///////////////////////
751Vector Shell::calculateLinePosition(unsigned int lineNumber)
752{
753  return Vector(5, (this->textSize + this->lineSpacing)*(this->bufferDisplaySize - lineNumber -1) + this->textSize, 0);
754}
755
756
757
758/**
759 * displays some nice output from the Shell
760 */
761void Shell::debug() const
762{
763  PRINT(3)("Debugging output to console (not this shell)\n");
764
765  if (this->pressedKey != SDLK_FIRST)
766    printf("%s::%f %f\n", SDLKToKeyname(this->pressedKey), this->delayed, this->repeatDelay);
767
768
769  char* tmpChar = this->bufferIterator->firstElement();
770  while(tmpChar != NULL)
771  {
772    printf(tmpChar);
773    tmpChar = this->bufferIterator->nextElement();
774  }
775}
Note: See TracBrowser for help on using the repository browser.