mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 12:44:46 +00:00
342 lines
12 KiB
C++
342 lines
12 KiB
C++
//-----------------------------------------------------------------------------
|
|
// 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, (U32)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);
|
|
}
|
|
}
|
|
}
|
|
}
|