winConsole.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 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 "platformWin32/platformWin32.h"
  23. #include "platformWin32/winConsole.h"
  24. #include "platform/event.h"
  25. #include "game/gameInterface.h"
  26. #include "string/unicode.h"
  27. WinConsole *WindowsConsole = NULL;
  28. ConsoleFunction(enableWinConsole, void, 2, 2, "( enable ) Use the enableWinConsole function to tell TGB to create an external console window, either as a separate DOS window or as a new window under OSX/Linux/*NIX.\n"
  29. "Subsequent calls to this function do nothing. Only one external console is allowed\n"
  30. "@param enable A boolean. If this value is set to true, a new console window will be created.\n"
  31. "@return No return value.")
  32. {
  33. argc;
  34. WindowsConsole->enable(dAtob(argv[1]));
  35. }
  36. void WinConsole::create()
  37. {
  38. if( !WindowsConsole )
  39. WindowsConsole = new WinConsole();
  40. }
  41. void WinConsole::destroy()
  42. {
  43. if( WindowsConsole )
  44. delete WindowsConsole;
  45. WindowsConsole = NULL;
  46. }
  47. void WinConsole::enable(bool enabled)
  48. {
  49. winConsoleEnabled = enabled;
  50. if(winConsoleEnabled)
  51. {
  52. AllocConsole();
  53. const char *title = Con::getVariable("Con::WindowTitle");
  54. if (title && *title)
  55. {
  56. #ifdef UNICODE
  57. UTF16 buf[512];
  58. convertUTF8toUTF16((UTF8 *)title, buf, sizeof(buf));
  59. SetConsoleTitle(buf);
  60. #else
  61. SetConsoleTitle(title);
  62. #endif
  63. }
  64. stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
  65. stdIn = GetStdHandle(STD_INPUT_HANDLE);
  66. stdErr = GetStdHandle(STD_ERROR_HANDLE);
  67. printf("%s", Con::getVariable("Con::Prompt"));
  68. }
  69. }
  70. bool WinConsole::isEnabled()
  71. {
  72. if ( WindowsConsole )
  73. return WindowsConsole->winConsoleEnabled;
  74. return false;
  75. }
  76. static void winConsoleConsumer(ConsoleLogEntry::Level, const char *line)
  77. {
  78. if (WindowsConsole)
  79. WindowsConsole->processConsoleLine(line);
  80. }
  81. WinConsole::WinConsole()
  82. {
  83. for (S32 iIndex = 0; iIndex < MAX_CMDS; iIndex ++)
  84. rgCmds[iIndex][0] = '\0';
  85. iCmdIndex = 0;
  86. winConsoleEnabled = false;
  87. Con::addConsumer(winConsoleConsumer);
  88. inpos = 0;
  89. lineOutput = false;
  90. }
  91. WinConsole::~WinConsole()
  92. {
  93. Con::removeConsumer(winConsoleConsumer);
  94. }
  95. void WinConsole::printf(const char *s, ...)
  96. {
  97. // Get the line into a buffer.
  98. static const int BufSize = 4096;
  99. static char buffer[4096];
  100. DWORD bytes;
  101. va_list args;
  102. va_start(args, s);
  103. vsnprintf(buffer, BufSize, s, args);
  104. // Replace tabs with carats, like the "real" console does.
  105. char *pos = buffer;
  106. while (*pos) {
  107. if (*pos == '\t') {
  108. *pos = '^';
  109. }
  110. pos++;
  111. }
  112. // Axe the color characters.
  113. Con::stripColorChars(buffer);
  114. // Print it.
  115. WriteFile(stdOut, buffer, strlen(buffer), &bytes, NULL);
  116. FlushFileBuffers( stdOut );
  117. }
  118. void WinConsole::processConsoleLine(const char *consoleLine)
  119. {
  120. if(winConsoleEnabled)
  121. {
  122. inbuf[inpos] = 0;
  123. if(lineOutput)
  124. printf("%s\n", consoleLine);
  125. else
  126. printf("%c%s\n%s%s", '\r', consoleLine, Con::getVariable("Con::Prompt"), inbuf);
  127. }
  128. }
  129. void WinConsole::process()
  130. {
  131. if(winConsoleEnabled)
  132. {
  133. DWORD numEvents;
  134. GetNumberOfConsoleInputEvents(stdIn, &numEvents);
  135. if(numEvents)
  136. {
  137. INPUT_RECORD rec[20];
  138. char outbuf[512];
  139. S32 outpos = 0;
  140. ReadConsoleInput(stdIn, rec, 20, &numEvents);
  141. DWORD i;
  142. for(i = 0; i < numEvents; i++)
  143. {
  144. if(rec[i].EventType == KEY_EVENT)
  145. {
  146. KEY_EVENT_RECORD *ke = &(rec[i].Event.KeyEvent);
  147. if(ke->bKeyDown)
  148. {
  149. switch (ke->uChar.AsciiChar)
  150. {
  151. // If no ASCII char, check if it's a handled virtual key
  152. case 0:
  153. switch (ke->wVirtualKeyCode)
  154. {
  155. // UP ARROW
  156. case 0x26 :
  157. // Go to the previous command in the cyclic array
  158. if ((-- iCmdIndex) < 0)
  159. iCmdIndex = MAX_CMDS - 1;
  160. // If this command isn't empty ...
  161. if (rgCmds[iCmdIndex][0] != '\0')
  162. {
  163. // Obliterate current displayed text
  164. for (S32 i = outpos = 0; i < inpos; i ++)
  165. {
  166. outbuf[outpos ++] = '\b';
  167. outbuf[outpos ++] = ' ';
  168. outbuf[outpos ++] = '\b';
  169. }
  170. // Copy command into command and display buffers
  171. for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++)
  172. {
  173. outbuf[outpos] = rgCmds[iCmdIndex][inpos];
  174. inbuf [inpos ] = rgCmds[iCmdIndex][inpos];
  175. }
  176. }
  177. // If previous is empty, stay on current command
  178. else if ((++ iCmdIndex) >= MAX_CMDS)
  179. {
  180. iCmdIndex = 0;
  181. }
  182. break;
  183. // DOWN ARROW
  184. case 0x28 : {
  185. // Go to the next command in the command array, if
  186. // it isn't empty
  187. if (rgCmds[iCmdIndex][0] != '\0' && (++ iCmdIndex) >= MAX_CMDS)
  188. iCmdIndex = 0;
  189. // Obliterate current displayed text
  190. for (S32 i = outpos = 0; i < inpos; i ++)
  191. {
  192. outbuf[outpos ++] = '\b';
  193. outbuf[outpos ++] = ' ';
  194. outbuf[outpos ++] = '\b';
  195. }
  196. // Copy command into command and display buffers
  197. for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++)
  198. {
  199. outbuf[outpos] = rgCmds[iCmdIndex][inpos];
  200. inbuf [inpos ] = rgCmds[iCmdIndex][inpos];
  201. }
  202. }
  203. break;
  204. // LEFT ARROW
  205. case 0x25 :
  206. break;
  207. // RIGHT ARROW
  208. case 0x27 :
  209. break;
  210. default :
  211. break;
  212. }
  213. break;
  214. case '\b':
  215. if(inpos > 0)
  216. {
  217. outbuf[outpos++] = '\b';
  218. outbuf[outpos++] = ' ';
  219. outbuf[outpos++] = '\b';
  220. inpos--;
  221. }
  222. break;
  223. case '\t':
  224. // In the output buffer, we're going to have to erase the current line (in case
  225. // we're cycling through various completions) and write out the whole input
  226. // buffer, so (inpos * 3) + complen <= 512. Should be OK. The input buffer is
  227. // also 512 chars long so that constraint will also be fine for the input buffer.
  228. {
  229. // Erase the current line.
  230. U32 i;
  231. for (i = 0; i < (U32)inpos; i++) {
  232. outbuf[outpos++] = '\b';
  233. outbuf[outpos++] = ' ';
  234. outbuf[outpos++] = '\b';
  235. }
  236. // Modify the input buffer with the completion.
  237. U32 maxlen = 512 - (inpos * 3);
  238. if (ke->dwControlKeyState & SHIFT_PRESSED) {
  239. inpos = Con::tabComplete(inbuf, inpos, maxlen, false);
  240. }
  241. else {
  242. inpos = Con::tabComplete(inbuf, inpos, maxlen, true);
  243. }
  244. // Copy the input buffer to the output.
  245. for (i = 0; i < (U32)inpos; i++) {
  246. outbuf[outpos++] = inbuf[i];
  247. }
  248. }
  249. break;
  250. case '\n':
  251. case '\r':
  252. outbuf[outpos++] = '\r';
  253. outbuf[outpos++] = '\n';
  254. inbuf[inpos] = 0;
  255. outbuf[outpos] = 0;
  256. printf("%s", outbuf);
  257. S32 eventSize;
  258. eventSize = ConsoleEventHeaderSize;
  259. dStrcpy(postEvent.data, inbuf);
  260. postEvent.size = eventSize + dStrlen(inbuf) + 1;
  261. Game->postEvent(postEvent);
  262. // If we've gone off the end of our array, wrap
  263. // back to the beginning
  264. if (iCmdIndex >= MAX_CMDS)
  265. iCmdIndex %= MAX_CMDS;
  266. // Put the new command into the array
  267. strcpy(rgCmds[iCmdIndex ++], inbuf);
  268. printf("%s", Con::getVariable("Con::Prompt"));
  269. inpos = outpos = 0;
  270. break;
  271. default:
  272. inbuf[inpos++] = ke->uChar.AsciiChar;
  273. outbuf[outpos++] = ke->uChar.AsciiChar;
  274. break;
  275. }
  276. }
  277. }
  278. }
  279. if(outpos)
  280. {
  281. outbuf[outpos] = 0;
  282. printf("%s", outbuf);
  283. }
  284. }
  285. }
  286. }