123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "core/util/rawData.h"
- #include "core/strings/stringFunctions.h"
- #include "core/strings/unicode.h"
- #include "platformWin32/platformWin32.h"
- #include "platformWin32/winConsole.h"
- #include "console/consoleTypes.h"
- #include "console/engineAPI.h"
- #include "core/util/journal/process.h"
- WinConsole *WindowsConsole = NULL;
- namespace Con
- {
- extern bool alwaysUseDebugOutput;
- }
- DefineEngineFunction( enableWinConsole, void, (bool flag), , "enableWinConsole(bool);")
- {
- WindowsConsole->enable(flag);
- }
- void WinConsole::create()
- {
- if( !WindowsConsole )
- WindowsConsole = new WinConsole();
- }
- void WinConsole::destroy()
- {
- if( WindowsConsole )
- delete WindowsConsole;
- WindowsConsole = NULL;
- }
- void WinConsole::enable(bool enabled)
- {
- winConsoleEnabled = enabled;
- if(winConsoleEnabled)
- {
- AllocConsole();
- const char *title = Con::getVariable("Con::WindowTitle");
- if (title && *title)
- {
- #ifdef UNICODE
- UTF16 buf[512];
- convertUTF8toUTF16((UTF8 *)title, buf);
- SetConsoleTitle(buf);
- #else
- SetConsoleTitle(title);
- #endif
- }
- stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
- stdIn = GetStdHandle(STD_INPUT_HANDLE);
- stdErr = GetStdHandle(STD_ERROR_HANDLE);
- printf("%s", Con::getVariable("Con::Prompt"));
- }
- }
- bool WinConsole::isEnabled()
- {
- if ( WindowsConsole )
- return WindowsConsole->winConsoleEnabled;
- return false;
- }
- static void winConsoleConsumer(U32 level, const char *line)
- {
- if (WindowsConsole)
- {
- WindowsConsole->processConsoleLine(line);
- #ifndef TORQUE_SHIPPING
- // see console.cpp for a description of Con::alwaysUseDebugOutput
- if( level == ConsoleLogEntry::Error || Con::alwaysUseDebugOutput)
- {
- // [rene, 04/05/2008] This is incorrect. Should do conversion from UTF8 here.
- // Skipping for the sake of speed. Not meant to be seen by user anyway.
- OutputDebugStringA( line );
- OutputDebugStringA( "\n" );
- }
- #endif
- }
- }
- WinConsole::WinConsole()
- {
- for (S32 iIndex = 0; iIndex < MAX_CMDS; iIndex ++)
- rgCmds[iIndex][0] = '\0';
- iCmdIndex = 0;
- winConsoleEnabled = false;
- Con::addConsumer(winConsoleConsumer);
- inpos = 0;
- lineOutput = false;
- Process::notify(this, &WinConsole::process, PROCESS_LAST_ORDER);
- }
- WinConsole::~WinConsole()
- {
- Process::remove(this, &WinConsole::process);
- Con::removeConsumer(winConsoleConsumer);
- }
- void WinConsole::printf(const char *s, ...)
- {
- // Get the line into a buffer.
- static const S32 BufSize = 4096;
- static char buffer[4096];
- DWORD bytes;
- va_list args;
- va_start(args, s);
- _vsnprintf(buffer, BufSize, s, args);
- // Replace tabs with carats, like the "real" console does.
- char *pos = buffer;
- while (*pos) {
- if (*pos == '\t') {
- *pos = '^';
- }
- pos++;
- }
- // Axe the color characters.
- Con::stripColorChars(buffer);
- // Print it.
- WriteFile(stdOut, buffer, strlen(buffer), &bytes, NULL);
- FlushFileBuffers( stdOut );
- }
- void WinConsole::processConsoleLine(const char *consoleLine)
- {
- if(winConsoleEnabled)
- {
- inbuf[inpos] = 0;
- if(lineOutput)
- printf("%s\n", consoleLine);
- else
- printf("%c%s\n%s%s", '\r', consoleLine, Con::getVariable("Con::Prompt"), inbuf);
- }
- }
- void WinConsole::process()
- {
- if(winConsoleEnabled)
- {
- DWORD numEvents;
- GetNumberOfConsoleInputEvents(stdIn, &numEvents);
- if(numEvents)
- {
- INPUT_RECORD rec[20];
- char outbuf[512];
- S32 outpos = 0;
- ReadConsoleInput(stdIn, rec, 20, &numEvents);
- DWORD evt;
- for(evt = 0; evt < numEvents; evt++)
- {
- if(rec[evt].EventType == KEY_EVENT)
- {
- KEY_EVENT_RECORD *ke = &(rec[evt].Event.KeyEvent);
- if(ke->bKeyDown)
- {
- switch (ke->uChar.AsciiChar)
- {
- // If no ASCII char, check if it's a handled virtual key
- case 0:
- switch (ke->wVirtualKeyCode)
- {
- // UP ARROW
- case 0x26 :
- // Go to the previous command in the cyclic array
- if ((-- iCmdIndex) < 0)
- iCmdIndex = MAX_CMDS - 1;
- // If this command isn't empty ...
- if (rgCmds[iCmdIndex][0] != '\0')
- {
- // Obliterate current displayed text
- for (S32 i = outpos = 0; i < inpos; i ++)
- {
- outbuf[outpos ++] = '\b';
- outbuf[outpos ++] = ' ';
- outbuf[outpos ++] = '\b';
- }
- // Copy command into command and display buffers
- for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++)
- {
- outbuf[outpos] = rgCmds[iCmdIndex][inpos];
- inbuf [inpos ] = rgCmds[iCmdIndex][inpos];
- }
- }
- // If previous is empty, stay on current command
- else if ((++ iCmdIndex) >= MAX_CMDS)
- {
- iCmdIndex = 0;
- }
- break;
- // DOWN ARROW
- case 0x28 : {
- // Go to the next command in the command array, if
- // it isn't empty
- if (rgCmds[iCmdIndex][0] != '\0' && (++ iCmdIndex) >= MAX_CMDS)
- iCmdIndex = 0;
- // Obliterate current displayed text
- for (S32 i = outpos = 0; i < inpos; i ++)
- {
- outbuf[outpos ++] = '\b';
- outbuf[outpos ++] = ' ';
- outbuf[outpos ++] = '\b';
- }
- // Copy command into command and display buffers
- for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++)
- {
- outbuf[outpos] = rgCmds[iCmdIndex][inpos];
- inbuf [inpos ] = rgCmds[iCmdIndex][inpos];
- }
- }
- break;
- // LEFT ARROW
- case 0x25 :
- break;
- // RIGHT ARROW
- case 0x27 :
- break;
- default :
- break;
- }
- break;
- case '\b':
- if(inpos > 0)
- {
- outbuf[outpos++] = '\b';
- outbuf[outpos++] = ' ';
- outbuf[outpos++] = '\b';
- inpos--;
- }
- break;
- case '\t':
- // In the output buffer, we're going to have to erase the current line (in case
- // we're cycling through various completions) and write out the whole input
- // buffer, so (inpos * 3) + complen <= 512. Should be OK. The input buffer is
- // also 512 chars long so that constraint will also be fine for the input buffer.
- {
- // Erase the current line.
- U32 i;
- for (i = 0; i < inpos; i++) {
- outbuf[outpos++] = '\b';
- outbuf[outpos++] = ' ';
- outbuf[outpos++] = '\b';
- }
- // Modify the input buffer with the completion.
- U32 maxlen = 512 - (inpos * 3);
- if (ke->dwControlKeyState & SHIFT_PRESSED) {
- inpos = Con::tabComplete(inbuf, inpos, maxlen, false);
- }
- else {
- inpos = Con::tabComplete(inbuf, inpos, maxlen, true);
- }
- // Copy the input buffer to the output.
- for (i = 0; i < inpos; i++) {
- outbuf[outpos++] = inbuf[i];
- }
- }
- break;
- case '\n':
- case '\r':
- outbuf[outpos++] = '\r';
- outbuf[outpos++] = '\n';
- inbuf[inpos] = 0;
- outbuf[outpos] = 0;
- printf("%s", outbuf);
- // Pass the line along to the console for execution.
- {
- RawData rd;
- rd.size = inpos + 1;
- rd.data = ( S8* ) inbuf;
- Con::smConsoleInput.trigger(rd);
- }
- // If we've gone off the end of our array, wrap
- // back to the beginning
- if (iCmdIndex >= MAX_CMDS)
- iCmdIndex %= MAX_CMDS;
- // Put the new command into the array
- strcpy(rgCmds[iCmdIndex ++], inbuf);
- printf("%s", Con::getVariable("Con::Prompt"));
- inpos = outpos = 0;
- break;
- default:
- inbuf[inpos++] = ke->uChar.AsciiChar;
- outbuf[outpos++] = ke->uChar.AsciiChar;
- break;
- }
- }
- }
- }
- if(outpos)
- {
- outbuf[outpos] = 0;
- printf("%s", outbuf);
- }
- }
- }
- }
|