Torque3D Documentation / _generateds / x86UNIXConsole.cpp

x86UNIXConsole.cpp

Engine/source/platformX86UNIX/x86UNIXConsole.cpp

More...

Public Variables

Public Functions

DefineEngineFunction(enableWinConsole , void , (bool _enable) , "enableWinConsole(bool);" )
signalHandler(int sigtype)

Detailed Description

Public Variables

StdConsole * stdConsole 

Public Functions

DefineEngineFunction(enableWinConsole , void , (bool _enable) , "enableWinConsole(bool);" )

signalHandler(int sigtype)

stdConsoleConsumer(U32 , const char * line)

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "platformX86UNIX/platformX86UNIX.h"
 25#include "platformX86UNIX/x86UNIXStdConsole.h"
 26#include "platformX86UNIX/x86UNIXUtils.h"
 27#include "platform/input/event.h"
 28#include "platform/platform.h"
 29#include "core/util/rawData.h"
 30#include "core/strings/stringFunctions.h"
 31#include "core/util/journal/process.h"
 32
 33#include <signal.h>
 34#include <unistd.h>
 35#include <stdarg.h>
 36#include <fcntl.h>
 37
 38#include "console/engineAPI.h"
 39
 40StdConsole *stdConsole = NULL;
 41
 42DefineEngineFunction(enableWinConsole, void, (bool _enable),, "enableWinConsole(bool);")
 43{
 44   if (stdConsole)
 45      stdConsole->enable(_enable);
 46}
 47
 48void StdConsole::create()
 49{
 50   if (stdConsole == NULL)
 51      stdConsole = new StdConsole();
 52}
 53
 54void StdConsole::destroy()
 55{
 56   if (stdConsole && stdConsole->isEnabled())
 57      stdConsole->enable(false);
 58
 59   delete stdConsole;
 60   stdConsole = NULL;
 61}
 62
 63static void signalHandler(int sigtype)
 64{
 65   if (sigtype == SIGCONT && stdConsole != NULL)
 66      stdConsole->resetTerminal();
 67}
 68
 69void StdConsole::resetTerminal()
 70{
 71   if (stdConsoleEnabled)
 72   {
 73      /* setup the proper terminal modes */
 74      struct termios termModes;
 75      tcgetattr(stdIn, &termModes);
 76      termModes.c_lflag &= ~ICANON; // enable non-canonical mode
 77      termModes.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHOKE);
 78      termModes.c_cc[VMIN] = 0;
 79      termModes.c_cc[VTIME] = 5;
 80      tcsetattr(stdIn, TCSAFLUSH, &termModes);
 81   }
 82}
 83
 84void StdConsole::enable(bool enabled)
 85{
 86   if (enabled && !stdConsoleEnabled)
 87   {
 88      stdConsoleEnabled = true;
 89
 90      // install signal handler for sigcont
 91      signal(SIGCONT, &signalHandler);
 92      
 93      // save the terminal state
 94      if (originalTermState == NULL)
 95         originalTermState = new termios;
 96
 97      tcgetattr(stdIn, originalTermState);
 98      
 99      // put the terminal into our preferred mode
100      resetTerminal();
101      
102      printf("%s", Con::getVariable("Con::Prompt"));
103     
104   }
105   else if (!enabled && stdConsoleEnabled)
106   {
107      stdConsoleEnabled = false;
108
109      // uninstall signal handler
110      signal(SIGCONT, SIG_DFL);
111
112      // reset the original terminal state
113      if (originalTermState != NULL)
114         tcsetattr(stdIn, TCSANOW, originalTermState);
115   }
116}
117
118void StdConsole::enableInput( bool enabled )
119{
120    stdConsoleInputEnabled = enabled;
121}
122
123bool StdConsole::isEnabled()
124{
125   if ( stdConsole )
126      return stdConsole->stdConsoleEnabled;
127
128   return false;
129}
130
131static void stdConsoleConsumer(/*ConsoleLogEntry::Level*/ U32, const char *line)
132{
133   stdConsole->processConsoleLine(line);
134}
135
136StdConsole::StdConsole()
137{
138   for (S32 iIndex = 0; iIndex < MAX_CMDS; iIndex ++)
139      rgCmds[iIndex][0] = '\0';
140
141   stdOut = dup(1);
142   stdIn  = dup(0);
143   stdErr = dup(2);
144
145   iCmdIndex = 0;
146   stdConsoleEnabled = false;
147   stdConsoleInputEnabled = false;
148   Con::addConsumer(stdConsoleConsumer);
149   inpos = 0;
150   lineOutput = false;
151   inBackground = false;
152   originalTermState = NULL;
153   
154   Process::notify(this, &StdConsole::process, PROCESS_LAST_ORDER);
155}
156
157StdConsole::~StdConsole()
158{
159   Con::removeConsumer(stdConsoleConsumer);
160
161   if (stdConsoleEnabled)
162      enable(false);
163
164   if (originalTermState != NULL)
165   {
166      delete originalTermState;
167      originalTermState = NULL;
168   }
169}
170
171void StdConsole::printf(const char *s, ...)
172{
173   // Get the line into a buffer.
174   static const int BufSize = 4096;
175   static char buffer[BufSize];
176   va_list args;
177   va_start(args, s);
178   vsnprintf(buffer, BufSize, s, args);
179   va_end(args);
180   // Replace tabs with carats, like the "real" console does.
181   char *pos = buffer;
182   while (*pos) {
183      if (*pos == '\t') {
184         *pos = '^';
185      }
186      pos++;
187   }
188   // Axe the color characters.
189   Con::stripColorChars(buffer);
190   // Print it.
191   write(stdOut, buffer, strlen(buffer));
192   fflush(stdout);
193}
194
195void StdConsole::processConsoleLine(const char *consoleLine)
196{
197   if(stdConsoleEnabled)
198   {
199      inbuf[inpos] = 0;
200      if(lineOutput)
201         printf("%s\n", consoleLine);
202      else
203         printf("%c%s\n%s%s", '\r', consoleLine, Con::getVariable("Con::Prompt"), inbuf);
204   }
205}
206
207void StdConsole::process()
208{
209   if(stdConsoleEnabled)
210   {
211      //U16 key;
212      char typedData[64];  // damn, if you can type this fast... :-D
213
214      if (UUtils->inBackground())
215         // we don't have the terminal
216         inBackground = true;
217      else
218      {
219         // if we were in the background, reset the terminal
220         if (inBackground)
221            resetTerminal();
222         inBackground = false;
223      }   
224
225      // see if stdIn has any input waiting
226      // mojo for select call
227      fd_set rfds;
228      struct timeval tv;
229
230      FD_ZERO(&rfds);
231      FD_SET(stdIn, &rfds);
232      // don't wait at all in select
233      tv.tv_sec = 0;
234      tv.tv_usec = 0;
235
236      int numEvents = select(stdIn+1, &rfds, NULL, NULL, &tv);
237      if (numEvents <= 0)
238         // no data available
239         return;
240
241      numEvents = read(stdIn, typedData, 64);     
242      if (numEvents == -1) 
243         return;
244
245      // TODO LINUX, when debug in qtCreator some times we get false console inputs.
246      if( !stdConsoleInputEnabled )
247         return;
248
249      typedData[numEvents] = '\0';
250      if (numEvents > 0)
251      {
252        char outbuf[512];
253        S32 outpos = 0;
254
255        for (int i = 0; i < numEvents; i++)
256        {
257         switch(typedData[i])
258         {
259            case 8:
260            case 127:
261            /* backspace */
262               if (inpos > 0)
263               {
264                  outbuf[outpos++] = '\b';
265                  outbuf[outpos++] = ' ';
266                  outbuf[outpos++] = '\b';
267                  inpos--;
268               }
269               break;
270
271            // XXX Don't know if we can detect shift-TAB.  So, only handling
272            // TAB for now.
273
274            case '\t':
275                        // In the output buffer, we're going to have to erase the current line (in case
276                        // we're cycling through various completions) and write out the whole input
277                        // buffer, so (inpos * 3) + complen <= 512.  Should be OK.  The input buffer is
278                        // also 512 chars long so that constraint will also be fine for the input buffer.
279                        {
280                           // Erase the current line.
281                           U32 i;
282                           for (i = 0; i < inpos; i++) {
283                              outbuf[outpos++] = '\b';
284                              outbuf[outpos++] = ' ';
285                              outbuf[outpos++] = '\b';
286                           }
287                           // Modify the input buffer with the completion.
288                           U32 maxlen = 512 - (inpos * 3);
289                           inpos = Con::tabComplete(inbuf, inpos, maxlen, true);
290                           // Copy the input buffer to the output.
291                           for (i = 0; i < inpos; i++) {
292                              outbuf[outpos++] = inbuf[i];
293                           }
294                        }
295                        break;
296
297            case '\n':
298            case '\r':
299            /* new line */
300                  outbuf[outpos++] = '\n';
301
302                  inbuf[inpos] = 0;
303                  outbuf[outpos] = 0;
304                  printf("%s", outbuf);
305
306                  S32 eventSize;
307                  eventSize = 1;
308
309                  {
310                     RawData rd;
311                     rd.size = inpos + 1;
312                     rd.data = (S8*) inbuf;
313                     
314                     Con::smConsoleInput.trigger(rd);
315                  }
316                  
317                  // If we've gone off the end of our array, wrap
318                  // back to the beginning
319                  if (iCmdIndex >= MAX_CMDS)
320                      iCmdIndex %= MAX_CMDS;
321
322                  // Put the new command into the array
323                  strcpy(rgCmds[iCmdIndex ++], inbuf);
324
325                  printf("%s", Con::getVariable("Con::Prompt"));
326                  inpos = outpos = 0;
327                  break;
328            case 27:
329               // JMQTODO: are these magic numbers keyboard map specific?
330               if (typedData[i+1] == 91 || typedData[i+1] == 79)
331               {
332                  i += 2;
333                  // an arrow key was pressed.
334                  switch(typedData[i])
335                  {
336                     case 'A':
337                        /* up arrow */
338                        // Go to the previous command in the cyclic array
339                        if ((-- iCmdIndex) < 0)
340                           iCmdIndex = MAX_CMDS - 1;
341
342                        // If this command isn't empty ...
343                        if (rgCmds[iCmdIndex][0] != '\0')
344                        {
345                           // Obliterate current displayed text
346                           for (S32 i = outpos = 0; i < inpos; i ++)
347                           {
348                              outbuf[outpos++] = '\b';
349                              outbuf[outpos++] = ' ';
350                              outbuf[outpos++] = '\b';
351                           }
352
353                           // Copy command into command and display buffers
354                           for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos++, outpos++)
355                           {
356                              outbuf[outpos] = rgCmds[iCmdIndex][inpos];
357                              inbuf [inpos ] = rgCmds[iCmdIndex][inpos];
358                           }
359                        }
360                        // If previous is empty, stay on current command
361                        else if ((++ iCmdIndex) >= MAX_CMDS)
362                        {
363                           iCmdIndex = 0;
364                        }
365                        break;
366                     case 'B':
367                        /* down arrow */
368                        // Go to the next command in the command array, if
369                        // it isn't empty
370                        if (rgCmds[iCmdIndex][0] != '\0' && (++ iCmdIndex) >= MAX_CMDS)
371                           iCmdIndex = 0;
372
373                        // Obliterate current displayed text
374                        for (S32 i = outpos = 0; i < inpos; i ++)
375                        {
376                           outbuf[outpos++] = '\b';
377                           outbuf[outpos++] = ' ';
378                           outbuf[outpos++] = '\b';
379                        }
380
381                        // Copy command into command and display buffers
382                        for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos++, outpos++)
383                        {
384                           outbuf[outpos] = rgCmds[iCmdIndex][inpos];
385                           inbuf [inpos ] = rgCmds[iCmdIndex][inpos];
386                        }
387                        break;
388                     case 'C':
389                        /* right arrow */
390                        break;
391                     case 'D':
392                        /* left arrow */
393                        break;
394                  }
395                  // read again to get rid of a bad char.
396                  //read(stdIn, &key, sizeof(char));
397                  break;
398               } else {
399                  inbuf[inpos++] = typedData[i];
400                  outbuf[outpos++] = typedData[i];
401                  break;
402               }
403               break;
404            default:
405               inbuf[inpos++] = typedData[i];
406               outbuf[outpos++] = typedData[i];
407               break;
408         }
409        }
410        if (outpos)
411        {
412           outbuf[outpos] = 0;
413           printf("%s", outbuf);
414        }
415      }
416   }
417}
418