x86UNIXConsole.cpp
Engine/source/platformX86UNIX/x86UNIXConsole.cpp
Public Variables
Public Functions
DefineEngineFunction(enableWinConsole , void , (bool _enable) , "enableWinConsole(bool);" )
signalHandler(int sigtype)
stdConsoleConsumer(U32 , const char * line)
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