winConsole.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "core/util/rawData.h"
  23. #include "core/strings/stringFunctions.h"
  24. #include "core/strings/unicode.h"
  25. #include "platformWin32/platformWin32.h"
  26. #include "platformWin32/winConsole.h"
  27. #include "console/consoleTypes.h"
  28. #include "console/engineAPI.h"
  29. #include "core/util/journal/process.h"
  30. WinConsole *WindowsConsole = NULL;
  31. namespace Con
  32. {
  33. extern bool alwaysUseDebugOutput;
  34. }
  35. DefineEngineFunction( enableWinConsole, void, (bool flag), , "enableWinConsole(bool);")
  36. {
  37. WindowsConsole->enable(flag);
  38. }
  39. void WinConsole::create()
  40. {
  41. if( !WindowsConsole )
  42. WindowsConsole = new WinConsole();
  43. }
  44. void WinConsole::destroy()
  45. {
  46. if( WindowsConsole )
  47. delete WindowsConsole;
  48. WindowsConsole = NULL;
  49. }
  50. void WinConsole::enable(bool enabled)
  51. {
  52. winConsoleEnabled = enabled;
  53. if(winConsoleEnabled)
  54. {
  55. AllocConsole();
  56. const char *title = Con::getVariable("Con::WindowTitle");
  57. if (title && *title)
  58. {
  59. #ifdef UNICODE
  60. UTF16 buf[512];
  61. convertUTF8toUTF16((UTF8 *)title, buf);
  62. SetConsoleTitle(buf);
  63. #else
  64. SetConsoleTitle(title);
  65. #endif
  66. }
  67. stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
  68. stdIn = GetStdHandle(STD_INPUT_HANDLE);
  69. stdErr = GetStdHandle(STD_ERROR_HANDLE);
  70. printf("%s", Con::getVariable("Con::Prompt"));
  71. }
  72. }
  73. bool WinConsole::isEnabled()
  74. {
  75. if ( WindowsConsole )
  76. return WindowsConsole->winConsoleEnabled;
  77. return false;
  78. }
  79. static void winConsoleConsumer(U32 level, const char *line)
  80. {
  81. if (WindowsConsole)
  82. {
  83. WindowsConsole->processConsoleLine(line);
  84. #ifndef TORQUE_SHIPPING
  85. // see console.cpp for a description of Con::alwaysUseDebugOutput
  86. if( level == ConsoleLogEntry::Error || Con::alwaysUseDebugOutput)
  87. {
  88. // [rene, 04/05/2008] This is incorrect. Should do conversion from UTF8 here.
  89. // Skipping for the sake of speed. Not meant to be seen by user anyway.
  90. OutputDebugStringA( line );
  91. OutputDebugStringA( "\n" );
  92. }
  93. #endif
  94. }
  95. }
  96. WinConsole::WinConsole()
  97. {
  98. for (S32 iIndex = 0; iIndex < MAX_CMDS; iIndex ++)
  99. rgCmds[iIndex][0] = '\0';
  100. iCmdIndex = 0;
  101. winConsoleEnabled = false;
  102. Con::addConsumer(winConsoleConsumer);
  103. inpos = 0;
  104. lineOutput = false;
  105. Process::notify(this, &WinConsole::process, PROCESS_LAST_ORDER);
  106. }
  107. WinConsole::~WinConsole()
  108. {
  109. Process::remove(this, &WinConsole::process);
  110. Con::removeConsumer(winConsoleConsumer);
  111. }
  112. void WinConsole::printf(const char *s, ...)
  113. {
  114. // Get the line into a buffer.
  115. static const S32 BufSize = 4096;
  116. static char buffer[4096];
  117. DWORD bytes;
  118. va_list args;
  119. va_start(args, s);
  120. _vsnprintf(buffer, BufSize, s, args);
  121. // Replace tabs with carats, like the "real" console does.
  122. char *pos = buffer;
  123. while (*pos) {
  124. if (*pos == '\t') {
  125. *pos = '^';
  126. }
  127. pos++;
  128. }
  129. // Axe the color characters.
  130. Con::stripColorChars(buffer);
  131. // Print it.
  132. WriteFile(stdOut, buffer, strlen(buffer), &bytes, NULL);
  133. FlushFileBuffers( stdOut );
  134. }
  135. void WinConsole::processConsoleLine(const char *consoleLine)
  136. {
  137. if(winConsoleEnabled)
  138. {
  139. inbuf[inpos] = 0;
  140. if(lineOutput)
  141. printf("%s\n", consoleLine);
  142. else
  143. printf("%c%s\n%s%s", '\r', consoleLine, Con::getVariable("Con::Prompt"), inbuf);
  144. }
  145. }
  146. void WinConsole::process()
  147. {
  148. if(winConsoleEnabled)
  149. {
  150. DWORD numEvents;
  151. GetNumberOfConsoleInputEvents(stdIn, &numEvents);
  152. if(numEvents)
  153. {
  154. INPUT_RECORD rec[20];
  155. char outbuf[512];
  156. S32 outpos = 0;
  157. ReadConsoleInput(stdIn, rec, 20, &numEvents);
  158. DWORD evt;
  159. for(evt = 0; evt < numEvents; evt++)
  160. {
  161. if(rec[evt].EventType == KEY_EVENT)
  162. {
  163. KEY_EVENT_RECORD *ke = &(rec[evt].Event.KeyEvent);
  164. if(ke->bKeyDown)
  165. {
  166. switch (ke->uChar.AsciiChar)
  167. {
  168. // If no ASCII char, check if it's a handled virtual key
  169. case 0:
  170. switch (ke->wVirtualKeyCode)
  171. {
  172. // UP ARROW
  173. case 0x26 :
  174. // Go to the previous command in the cyclic array
  175. if ((-- iCmdIndex) < 0)
  176. iCmdIndex = MAX_CMDS - 1;
  177. // If this command isn't empty ...
  178. if (rgCmds[iCmdIndex][0] != '\0')
  179. {
  180. // Obliterate current displayed text
  181. for (S32 i = outpos = 0; i < inpos; i ++)
  182. {
  183. outbuf[outpos ++] = '\b';
  184. outbuf[outpos ++] = ' ';
  185. outbuf[outpos ++] = '\b';
  186. }
  187. // Copy command into command and display buffers
  188. for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++)
  189. {
  190. outbuf[outpos] = rgCmds[iCmdIndex][inpos];
  191. inbuf [inpos ] = rgCmds[iCmdIndex][inpos];
  192. }
  193. }
  194. // If previous is empty, stay on current command
  195. else if ((++ iCmdIndex) >= MAX_CMDS)
  196. {
  197. iCmdIndex = 0;
  198. }
  199. break;
  200. // DOWN ARROW
  201. case 0x28 : {
  202. // Go to the next command in the command array, if
  203. // it isn't empty
  204. if (rgCmds[iCmdIndex][0] != '\0' && (++ iCmdIndex) >= MAX_CMDS)
  205. iCmdIndex = 0;
  206. // Obliterate current displayed text
  207. for (S32 i = outpos = 0; i < inpos; i ++)
  208. {
  209. outbuf[outpos ++] = '\b';
  210. outbuf[outpos ++] = ' ';
  211. outbuf[outpos ++] = '\b';
  212. }
  213. // Copy command into command and display buffers
  214. for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++)
  215. {
  216. outbuf[outpos] = rgCmds[iCmdIndex][inpos];
  217. inbuf [inpos ] = rgCmds[iCmdIndex][inpos];
  218. }
  219. }
  220. break;
  221. // LEFT ARROW
  222. case 0x25 :
  223. break;
  224. // RIGHT ARROW
  225. case 0x27 :
  226. break;
  227. default :
  228. break;
  229. }
  230. break;
  231. case '\b':
  232. if(inpos > 0)
  233. {
  234. outbuf[outpos++] = '\b';
  235. outbuf[outpos++] = ' ';
  236. outbuf[outpos++] = '\b';
  237. inpos--;
  238. }
  239. break;
  240. case '\t':
  241. // In the output buffer, we're going to have to erase the current line (in case
  242. // we're cycling through various completions) and write out the whole input
  243. // buffer, so (inpos * 3) + complen <= 512. Should be OK. The input buffer is
  244. // also 512 chars long so that constraint will also be fine for the input buffer.
  245. {
  246. // Erase the current line.
  247. U32 i;
  248. for (i = 0; i < inpos; i++) {
  249. outbuf[outpos++] = '\b';
  250. outbuf[outpos++] = ' ';
  251. outbuf[outpos++] = '\b';
  252. }
  253. // Modify the input buffer with the completion.
  254. U32 maxlen = 512 - (inpos * 3);
  255. if (ke->dwControlKeyState & SHIFT_PRESSED) {
  256. inpos = Con::tabComplete(inbuf, inpos, maxlen, false);
  257. }
  258. else {
  259. inpos = Con::tabComplete(inbuf, inpos, maxlen, true);
  260. }
  261. // Copy the input buffer to the output.
  262. for (i = 0; i < inpos; i++) {
  263. outbuf[outpos++] = inbuf[i];
  264. }
  265. }
  266. break;
  267. case '\n':
  268. case '\r':
  269. outbuf[outpos++] = '\r';
  270. outbuf[outpos++] = '\n';
  271. inbuf[inpos] = 0;
  272. outbuf[outpos] = 0;
  273. printf("%s", outbuf);
  274. // Pass the line along to the console for execution.
  275. {
  276. RawData rd;
  277. rd.size = inpos + 1;
  278. rd.data = ( S8* ) inbuf;
  279. Con::smConsoleInput.trigger(rd);
  280. }
  281. // If we've gone off the end of our array, wrap
  282. // back to the beginning
  283. if (iCmdIndex >= MAX_CMDS)
  284. iCmdIndex %= MAX_CMDS;
  285. // Put the new command into the array
  286. strcpy(rgCmds[iCmdIndex ++], inbuf);
  287. printf("%s", Con::getVariable("Con::Prompt"));
  288. inpos = outpos = 0;
  289. break;
  290. default:
  291. inbuf[inpos++] = ke->uChar.AsciiChar;
  292. outbuf[outpos++] = ke->uChar.AsciiChar;
  293. break;
  294. }
  295. }
  296. }
  297. }
  298. if(outpos)
  299. {
  300. outbuf[outpos] = 0;
  301. printf("%s", outbuf);
  302. }
  303. }
  304. }
  305. }