winConsole.cc 11 KB

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