winConsole.cpp
Engine/source/platformWin32/winConsole.cpp
Namespaces:
namespace
This namespace contains the core of the console functionality.
Public Variables
Public Functions
DefineEngineFunction(enableWinConsole , void , (bool flag) , "enableWinConsole(bool);" )
winConsoleConsumer(U32 level, const char * line)
Detailed Description
Public Variables
WinConsole * WindowsConsole
Public Functions
DefineEngineFunction(enableWinConsole , void , (bool flag) , "enableWinConsole(bool);" )
winConsoleConsumer(U32 level, 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 "core/util/rawData.h" 25#include "core/strings/stringFunctions.h" 26#include "core/strings/unicode.h" 27 28#include "platformWin32/platformWin32.h" 29#include "platformWin32/winConsole.h" 30#include "console/consoleTypes.h" 31#include "console/engineAPI.h" 32#include "core/util/journal/process.h" 33 34 35WinConsole *WindowsConsole = NULL; 36 37namespace Con 38{ 39 extern bool alwaysUseDebugOutput; 40} 41 42DefineEngineFunction( enableWinConsole, void, (bool flag), , "enableWinConsole(bool);") 43{ 44 WindowsConsole->enable(flag); 45} 46 47void WinConsole::create() 48{ 49 if( !WindowsConsole ) 50 WindowsConsole = new WinConsole(); 51} 52 53void WinConsole::destroy() 54{ 55 if( WindowsConsole ) 56 delete WindowsConsole; 57 WindowsConsole = NULL; 58} 59 60void WinConsole::enable(bool enabled) 61{ 62 winConsoleEnabled = enabled; 63 if(winConsoleEnabled) 64 { 65 AllocConsole(); 66 const char *title = Con::getVariable("Con::WindowTitle"); 67 if (title && *title) 68 { 69#ifdef UNICODE 70 UTF16 buf[512]; 71 convertUTF8toUTF16((UTF8 *)title, buf); 72 SetConsoleTitle(buf); 73#else 74 SetConsoleTitle(title); 75#endif 76 } 77 stdOut = GetStdHandle(STD_OUTPUT_HANDLE); 78 stdIn = GetStdHandle(STD_INPUT_HANDLE); 79 stdErr = GetStdHandle(STD_ERROR_HANDLE); 80 81 printf("%s", Con::getVariable("Con::Prompt")); 82 } 83} 84 85bool WinConsole::isEnabled() 86{ 87 if ( WindowsConsole ) 88 return WindowsConsole->winConsoleEnabled; 89 90 return false; 91} 92 93static void winConsoleConsumer(U32 level, const char *line) 94{ 95 if (WindowsConsole) 96 { 97 WindowsConsole->processConsoleLine(line); 98#ifndef TORQUE_SHIPPING 99 // see console.cpp for a description of Con::alwaysUseDebugOutput 100 if( level == ConsoleLogEntry::Error || Con::alwaysUseDebugOutput) 101 { 102 // [rene, 04/05/2008] This is incorrect. Should do conversion from UTF8 here. 103 // Skipping for the sake of speed. Not meant to be seen by user anyway. 104 OutputDebugStringA( line ); 105 OutputDebugStringA( "\n" ); 106 } 107#endif 108 } 109} 110 111WinConsole::WinConsole() 112{ 113 for (S32 iIndex = 0; iIndex < MAX_CMDS; iIndex ++) 114 rgCmds[iIndex][0] = '\0'; 115 116 iCmdIndex = 0; 117 winConsoleEnabled = false; 118 Con::addConsumer(winConsoleConsumer); 119 inpos = 0; 120 lineOutput = false; 121 122 Process::notify(this, &WinConsole::process, PROCESS_LAST_ORDER); 123} 124 125WinConsole::~WinConsole() 126{ 127 Process::remove(this, &WinConsole::process); 128 Con::removeConsumer(winConsoleConsumer); 129} 130 131void WinConsole::printf(const char *s, ...) 132{ 133 // Get the line into a buffer. 134 static const S32 BufSize = 4096; 135 static char buffer[4096]; 136 DWORD bytes; 137 va_list args; 138 va_start(args, s); 139 _vsnprintf(buffer, BufSize, s, args); 140 // Replace tabs with carats, like the "real" console does. 141 char *pos = buffer; 142 while (*pos) { 143 if (*pos == '\t') { 144 *pos = '^'; 145 } 146 pos++; 147 } 148 // Axe the color characters. 149 Con::stripColorChars(buffer); 150 // Print it. 151 WriteFile(stdOut, buffer, strlen(buffer), &bytes, NULL); 152 FlushFileBuffers( stdOut ); 153} 154 155void WinConsole::processConsoleLine(const char *consoleLine) 156{ 157 if(winConsoleEnabled) 158 { 159 inbuf[inpos] = 0; 160 if(lineOutput) 161 printf("%s\n", consoleLine); 162 else 163 printf("%c%s\n%s%s", '\r', consoleLine, Con::getVariable("Con::Prompt"), inbuf); 164 } 165} 166 167void WinConsole::process() 168{ 169 if(winConsoleEnabled) 170 { 171 DWORD numEvents; 172 GetNumberOfConsoleInputEvents(stdIn, &numEvents); 173 if(numEvents) 174 { 175 INPUT_RECORD rec[20]; 176 char outbuf[512]; 177 S32 outpos = 0; 178 179 ReadConsoleInput(stdIn, rec, 20, &numEvents); 180 DWORD evt; 181 for(evt = 0; evt < numEvents; evt++) 182 { 183 if(rec[evt].EventType == KEY_EVENT) 184 { 185 KEY_EVENT_RECORD *ke = &(rec[evt].Event.KeyEvent); 186 if(ke->bKeyDown) 187 { 188 switch (ke->uChar.AsciiChar) 189 { 190 // If no ASCII char, check if it's a handled virtual key 191 case 0: 192 switch (ke->wVirtualKeyCode) 193 { 194 // UP ARROW 195 case 0x26 : 196 // Go to the previous command in the cyclic array 197 if ((-- iCmdIndex) < 0) 198 iCmdIndex = MAX_CMDS - 1; 199 200 // If this command isn't empty ... 201 if (rgCmds[iCmdIndex][0] != '\0') 202 { 203 // Obliterate current displayed text 204 for (S32 i = outpos = 0; i < inpos; i ++) 205 { 206 outbuf[outpos ++] = '\b'; 207 outbuf[outpos ++] = ' '; 208 outbuf[outpos ++] = '\b'; 209 } 210 211 // Copy command into command and display buffers 212 for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++) 213 { 214 outbuf[outpos] = rgCmds[iCmdIndex][inpos]; 215 inbuf [inpos ] = rgCmds[iCmdIndex][inpos]; 216 } 217 } 218 // If previous is empty, stay on current command 219 else if ((++ iCmdIndex) >= MAX_CMDS) 220 { 221 iCmdIndex = 0; 222 } 223 224 break; 225 226 // DOWN ARROW 227 case 0x28 : { 228 // Go to the next command in the command array, if 229 // it isn't empty 230 if (rgCmds[iCmdIndex][0] != '\0' && (++ iCmdIndex) >= MAX_CMDS) 231 iCmdIndex = 0; 232 233 // Obliterate current displayed text 234 for (S32 i = outpos = 0; i < inpos; i ++) 235 { 236 outbuf[outpos ++] = '\b'; 237 outbuf[outpos ++] = ' '; 238 outbuf[outpos ++] = '\b'; 239 } 240 241 // Copy command into command and display buffers 242 for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++) 243 { 244 outbuf[outpos] = rgCmds[iCmdIndex][inpos]; 245 inbuf [inpos ] = rgCmds[iCmdIndex][inpos]; 246 } 247 } 248 break; 249 250 // LEFT ARROW 251 case 0x25 : 252 break; 253 254 // RIGHT ARROW 255 case 0x27 : 256 break; 257 258 default : 259 break; 260 } 261 break; 262 case '\b': 263 if(inpos > 0) 264 { 265 outbuf[outpos++] = '\b'; 266 outbuf[outpos++] = ' '; 267 outbuf[outpos++] = '\b'; 268 inpos--; 269 } 270 break; 271 case '\t': 272 // In the output buffer, we're going to have to erase the current line (in case 273 // we're cycling through various completions) and write out the whole input 274 // buffer, so (inpos * 3) + complen <= 512. Should be OK. The input buffer is 275 // also 512 chars long so that constraint will also be fine for the input buffer. 276 { 277 // Erase the current line. 278 U32 i; 279 for (i = 0; i < inpos; i++) { 280 outbuf[outpos++] = '\b'; 281 outbuf[outpos++] = ' '; 282 outbuf[outpos++] = '\b'; 283 } 284 // Modify the input buffer with the completion. 285 U32 maxlen = 512 - (inpos * 3); 286 if (ke->dwControlKeyState & SHIFT_PRESSED) { 287 inpos = Con::tabComplete(inbuf, inpos, maxlen, false); 288 } 289 else { 290 inpos = Con::tabComplete(inbuf, inpos, maxlen, true); 291 } 292 // Copy the input buffer to the output. 293 for (i = 0; i < inpos; i++) { 294 outbuf[outpos++] = inbuf[i]; 295 } 296 } 297 break; 298 case '\n': 299 case '\r': 300 outbuf[outpos++] = '\r'; 301 outbuf[outpos++] = '\n'; 302 303 inbuf[inpos] = 0; 304 outbuf[outpos] = 0; 305 printf("%s", outbuf); 306 307 // Pass the line along to the console for execution. 308 { 309 RawData rd; 310 rd.size = inpos + 1; 311 rd.data = ( S8* ) inbuf; 312 313 Con::smConsoleInput.trigger(rd); 314 } 315 316 // If we've gone off the end of our array, wrap 317 // back to the beginning 318 if (iCmdIndex >= MAX_CMDS) 319 iCmdIndex %= MAX_CMDS; 320 321 // Put the new command into the array 322 strcpy(rgCmds[iCmdIndex ++], inbuf); 323 324 printf("%s", Con::getVariable("Con::Prompt")); 325 inpos = outpos = 0; 326 break; 327 default: 328 inbuf[inpos++] = ke->uChar.AsciiChar; 329 outbuf[outpos++] = ke->uChar.AsciiChar; 330 break; 331 } 332 } 333 } 334 } 335 if(outpos) 336 { 337 outbuf[outpos] = 0; 338 printf("%s", outbuf); 339 } 340 } 341 } 342} 343