mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-22 22:27:05 +00:00
645 lines
13 KiB
C++
645 lines
13 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//=============================================================================//
|
|
// CTextConsoleWin32.cpp: Win32 implementation of the TextConsole class.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#include "TextConsoleWin32.h"
|
|
#include "tier0/dbg.h"
|
|
#include "utlvector.h"
|
|
|
|
// Could possibly switch all this code over to using readline. This:
|
|
// http://mingweditline.sourceforge.net/?Description
|
|
// readline() / add_history(char *)
|
|
|
|
#ifdef _WIN32
|
|
|
|
BOOL WINAPI ConsoleHandlerRoutine( DWORD CtrlType )
|
|
{
|
|
NOTE_UNUSED( CtrlType );
|
|
/* TODO ?
|
|
if ( CtrlType != CTRL_C_EVENT && CtrlType != CTRL_BREAK_EVENT )
|
|
m_System->Stop(); // don't quit on break or ctrl+c
|
|
*/
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// GetConsoleHwnd() helper function from MSDN Knowledge Base Article Q124103
|
|
// needed, because HWND GetConsoleWindow(VOID) is not avaliable under Win95/98/ME
|
|
|
|
HWND GetConsoleHwnd(void)
|
|
{
|
|
typedef HWND (WINAPI *PFNGETCONSOLEWINDOW)( VOID );
|
|
static PFNGETCONSOLEWINDOW s_pfnGetConsoleWindow = (PFNGETCONSOLEWINDOW) GetProcAddress( GetModuleHandle("kernel32"), "GetConsoleWindow" );
|
|
if ( s_pfnGetConsoleWindow )
|
|
return s_pfnGetConsoleWindow();
|
|
|
|
HWND hwndFound; // This is what is returned to the caller.
|
|
char pszNewWindowTitle[1024]; // Contains fabricated WindowTitle
|
|
char pszOldWindowTitle[1024]; // Contains original WindowTitle
|
|
|
|
// Fetch current window title.
|
|
GetConsoleTitle( pszOldWindowTitle, 1024 );
|
|
|
|
// Format a "unique" NewWindowTitle.
|
|
wsprintf( pszNewWindowTitle,"%d/%d", GetTickCount(), GetCurrentProcessId() );
|
|
|
|
// Change current window title.
|
|
SetConsoleTitle(pszNewWindowTitle);
|
|
|
|
// Ensure window title has been updated.
|
|
Sleep(40);
|
|
|
|
// Look for NewWindowTitle.
|
|
hwndFound = FindWindow( NULL, pszNewWindowTitle );
|
|
|
|
// Restore original window title.
|
|
|
|
SetConsoleTitle( pszOldWindowTitle );
|
|
|
|
return hwndFound;
|
|
}
|
|
|
|
CTextConsoleWin32::CTextConsoleWin32()
|
|
{
|
|
hinput = NULL;
|
|
houtput = NULL;
|
|
Attrib = 0;
|
|
statusline[0] = '\0';
|
|
}
|
|
|
|
bool CTextConsoleWin32::Init()
|
|
{
|
|
(void) AllocConsole();
|
|
SetTitle( "SOURCE DEDICATED SERVER" );
|
|
|
|
hinput = GetStdHandle ( STD_INPUT_HANDLE );
|
|
houtput = GetStdHandle ( STD_OUTPUT_HANDLE );
|
|
|
|
if ( !SetConsoleCtrlHandler( &ConsoleHandlerRoutine, TRUE) )
|
|
{
|
|
Print( "WARNING! TextConsole::Init: Could not attach console hook.\n" );
|
|
}
|
|
|
|
Attrib = FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY ;
|
|
|
|
SetWindowPos( GetConsoleHwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW );
|
|
|
|
memset( m_szConsoleText, 0, sizeof( m_szConsoleText ) );
|
|
m_nConsoleTextLen = 0;
|
|
m_nCursorPosition = 0;
|
|
|
|
memset( m_szSavedConsoleText, 0, sizeof( m_szSavedConsoleText ) );
|
|
m_nSavedConsoleTextLen = 0;
|
|
|
|
memset( m_aszLineBuffer, 0, sizeof( m_aszLineBuffer ) );
|
|
m_nTotalLines = 0;
|
|
m_nInputLine = 0;
|
|
m_nBrowseLine = 0;
|
|
|
|
// these are log messages, not related to console
|
|
Msg( "\n" );
|
|
Msg( "Console initialized.\n" );
|
|
|
|
return CTextConsole::Init();
|
|
}
|
|
|
|
void CTextConsoleWin32::ShutDown( void )
|
|
{
|
|
FreeConsole();
|
|
}
|
|
|
|
void CTextConsoleWin32::SetVisible( bool visible )
|
|
{
|
|
ShowWindow ( GetConsoleHwnd(), visible ? SW_SHOW : SW_HIDE );
|
|
m_ConsoleVisible = visible;
|
|
}
|
|
|
|
char * CTextConsoleWin32::GetLine( int index, char *buf, int buflen )
|
|
{
|
|
while ( 1 )
|
|
{
|
|
INPUT_RECORD recs[ 1024 ];
|
|
unsigned long numread;
|
|
unsigned long numevents;
|
|
|
|
if ( !GetNumberOfConsoleInputEvents( hinput, &numevents ) )
|
|
{
|
|
Error("CTextConsoleWin32::GetLine: !GetNumberOfConsoleInputEvents");
|
|
return NULL;
|
|
}
|
|
|
|
if ( numevents <= 0 )
|
|
break;
|
|
|
|
if ( !ReadConsoleInput( hinput, recs, ARRAYSIZE( recs ), &numread ) )
|
|
{
|
|
Error("CTextConsoleWin32::GetLine: !ReadConsoleInput");
|
|
return NULL;
|
|
}
|
|
|
|
if ( numread == 0 )
|
|
return NULL;
|
|
|
|
for ( int i=0; i < (int)numread; i++ )
|
|
{
|
|
INPUT_RECORD *pRec = &recs[i];
|
|
if ( pRec->EventType != KEY_EVENT )
|
|
continue;
|
|
|
|
if ( pRec->Event.KeyEvent.bKeyDown )
|
|
{
|
|
// check for cursor keys
|
|
if ( pRec->Event.KeyEvent.wVirtualKeyCode == VK_UP )
|
|
{
|
|
ReceiveUpArrow();
|
|
}
|
|
else if ( pRec->Event.KeyEvent.wVirtualKeyCode == VK_DOWN )
|
|
{
|
|
ReceiveDownArrow();
|
|
}
|
|
else if ( pRec->Event.KeyEvent.wVirtualKeyCode == VK_LEFT )
|
|
{
|
|
ReceiveLeftArrow();
|
|
}
|
|
else if ( pRec->Event.KeyEvent.wVirtualKeyCode == VK_RIGHT )
|
|
{
|
|
ReceiveRightArrow();
|
|
}
|
|
else
|
|
{
|
|
char ch;
|
|
int nLen;
|
|
|
|
ch = pRec->Event.KeyEvent.uChar.AsciiChar;
|
|
switch ( ch )
|
|
{
|
|
case '\r': // Enter
|
|
nLen = ReceiveNewline();
|
|
if ( nLen )
|
|
{
|
|
strncpy( buf, m_szConsoleText, buflen );
|
|
buf[ buflen - 1 ] = 0;
|
|
return buf;
|
|
}
|
|
break;
|
|
|
|
case '\b': // Backspace
|
|
ReceiveBackspace();
|
|
break;
|
|
|
|
case '\t': // TAB
|
|
ReceiveTab();
|
|
break;
|
|
|
|
default:
|
|
if ( ( ch >= ' ') && ( ch <= '~' ) ) // dont' accept nonprintable chars
|
|
{
|
|
ReceiveStandardChar( ch );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CTextConsoleWin32::Print( char * pszMsg )
|
|
{
|
|
if ( m_nConsoleTextLen )
|
|
{
|
|
int nLen;
|
|
|
|
nLen = m_nConsoleTextLen;
|
|
|
|
while ( nLen-- )
|
|
{
|
|
PrintRaw( "\b \b" );
|
|
}
|
|
}
|
|
|
|
PrintRaw( pszMsg );
|
|
|
|
if ( m_nConsoleTextLen )
|
|
{
|
|
PrintRaw( m_szConsoleText, m_nConsoleTextLen );
|
|
}
|
|
|
|
UpdateStatus();
|
|
}
|
|
|
|
void CTextConsoleWin32::PrintRaw( const char * pszMsg, int nChars )
|
|
{
|
|
unsigned long dummy;
|
|
|
|
if ( houtput == NULL )
|
|
{
|
|
houtput = GetStdHandle ( STD_OUTPUT_HANDLE );
|
|
if ( houtput == NULL )
|
|
return;
|
|
}
|
|
|
|
if ( nChars <= 0 )
|
|
{
|
|
nChars = strlen( pszMsg );
|
|
if ( nChars <= 0 )
|
|
return;
|
|
}
|
|
|
|
// filter out ASCII BEL characters because windows actually plays a
|
|
// bell sound, which can be used to lag the server in a DOS attack.
|
|
char * pTempBuf = NULL;
|
|
for ( int i = 0; i < nChars; ++i )
|
|
{
|
|
if ( pszMsg[i] == 0x07 /*BEL*/ )
|
|
{
|
|
if ( !pTempBuf )
|
|
{
|
|
pTempBuf = ( char * ) malloc( nChars );
|
|
memcpy( pTempBuf, pszMsg, nChars );
|
|
}
|
|
pTempBuf[i] = '.';
|
|
}
|
|
}
|
|
|
|
WriteFile( houtput, pTempBuf ? pTempBuf : pszMsg, nChars, &dummy, NULL );
|
|
|
|
free( pTempBuf ); // usually NULL
|
|
}
|
|
|
|
int CTextConsoleWin32::GetWidth( void )
|
|
{
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
int nWidth;
|
|
|
|
nWidth = 0;
|
|
|
|
if ( GetConsoleScreenBufferInfo( houtput, &csbi ) )
|
|
{
|
|
nWidth = csbi.dwSize.X;
|
|
}
|
|
|
|
if ( nWidth <= 1 )
|
|
nWidth = 80;
|
|
|
|
return nWidth;
|
|
}
|
|
|
|
void CTextConsoleWin32::SetStatusLine( char * pszStatus )
|
|
{
|
|
strncpy( statusline, pszStatus, 80 );
|
|
statusline[ 79 ] = '\0';
|
|
UpdateStatus();
|
|
}
|
|
|
|
void CTextConsoleWin32::UpdateStatus( void )
|
|
{
|
|
COORD coord;
|
|
DWORD dwWritten = 0;
|
|
WORD wAttrib[ 80 ];
|
|
|
|
for ( int i = 0; i < 80; i++ )
|
|
{
|
|
wAttrib[i] = Attrib; //FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY ;
|
|
}
|
|
|
|
coord.X = coord.Y = 0;
|
|
|
|
WriteConsoleOutputAttribute( houtput, wAttrib, 80, coord, &dwWritten );
|
|
WriteConsoleOutputCharacter( houtput, statusline, 80, coord, &dwWritten );
|
|
}
|
|
|
|
|
|
void CTextConsoleWin32::SetTitle( char * pszTitle )
|
|
{
|
|
SetConsoleTitle( pszTitle );
|
|
}
|
|
|
|
void CTextConsoleWin32::SetColor(WORD attrib)
|
|
{
|
|
Attrib = attrib;
|
|
}
|
|
|
|
int CTextConsoleWin32::ReceiveNewline( void )
|
|
{
|
|
int nLen = 0;
|
|
|
|
PrintRaw( "\n" );
|
|
|
|
if ( m_nConsoleTextLen )
|
|
{
|
|
nLen = m_nConsoleTextLen;
|
|
|
|
m_szConsoleText[ m_nConsoleTextLen ] = 0;
|
|
m_nConsoleTextLen = 0;
|
|
m_nCursorPosition = 0;
|
|
|
|
// cache line in buffer, but only if it's not a duplicate of the previous line
|
|
if ( ( m_nInputLine == 0 ) || ( strcmp( m_aszLineBuffer[ m_nInputLine - 1 ], m_szConsoleText ) ) )
|
|
{
|
|
strncpy( m_aszLineBuffer[ m_nInputLine ], m_szConsoleText, MAX_CONSOLE_TEXTLEN );
|
|
|
|
m_nInputLine++;
|
|
|
|
if ( m_nInputLine > m_nTotalLines )
|
|
m_nTotalLines = m_nInputLine;
|
|
|
|
if ( m_nInputLine >= MAX_BUFFER_LINES )
|
|
m_nInputLine = 0;
|
|
|
|
}
|
|
|
|
m_nBrowseLine = m_nInputLine;
|
|
}
|
|
|
|
return nLen;
|
|
}
|
|
|
|
|
|
void CTextConsoleWin32::ReceiveBackspace( void )
|
|
{
|
|
int nCount;
|
|
|
|
if ( m_nCursorPosition == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_nConsoleTextLen--;
|
|
m_nCursorPosition--;
|
|
|
|
PrintRaw( "\b" );
|
|
|
|
for ( nCount = m_nCursorPosition; nCount < m_nConsoleTextLen; nCount++ )
|
|
{
|
|
m_szConsoleText[ nCount ] = m_szConsoleText[ nCount + 1 ];
|
|
PrintRaw( m_szConsoleText + nCount, 1 );
|
|
}
|
|
|
|
PrintRaw( " " );
|
|
|
|
nCount = m_nConsoleTextLen;
|
|
while ( nCount >= m_nCursorPosition )
|
|
{
|
|
PrintRaw( "\b" );
|
|
nCount--;
|
|
}
|
|
|
|
m_nBrowseLine = m_nInputLine;
|
|
}
|
|
|
|
|
|
void CTextConsoleWin32::ReceiveTab( void )
|
|
{
|
|
CUtlVector<char *> matches;
|
|
|
|
m_szConsoleText[ m_nConsoleTextLen ] = 0;
|
|
|
|
if ( matches.Count() == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( matches.Count() == 1 )
|
|
{
|
|
char * pszCmdName;
|
|
char * pszRest;
|
|
|
|
pszCmdName = matches[0];
|
|
pszRest = pszCmdName + strlen( m_szConsoleText );
|
|
|
|
if ( pszRest )
|
|
{
|
|
PrintRaw( pszRest );
|
|
strcat( m_szConsoleText, pszRest );
|
|
m_nConsoleTextLen += strlen( pszRest );
|
|
|
|
PrintRaw( " " );
|
|
strcat( m_szConsoleText, " " );
|
|
m_nConsoleTextLen++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int nLongestCmd;
|
|
int nTotalColumns;
|
|
int nCurrentColumn;
|
|
char * pszCurrentCmd;
|
|
int i = 0;
|
|
|
|
nLongestCmd = 0;
|
|
|
|
pszCurrentCmd = matches[0];
|
|
while ( pszCurrentCmd )
|
|
{
|
|
if ( (int)strlen( pszCurrentCmd) > nLongestCmd )
|
|
{
|
|
nLongestCmd = strlen( pszCurrentCmd);
|
|
}
|
|
|
|
i++;
|
|
pszCurrentCmd = (char *)matches[i];
|
|
}
|
|
|
|
nTotalColumns = ( GetWidth() - 1 ) / ( nLongestCmd + 1 );
|
|
nCurrentColumn = 0;
|
|
|
|
PrintRaw( "\n" );
|
|
|
|
// Would be nice if these were sorted, but not that big a deal
|
|
pszCurrentCmd = matches[0];
|
|
i = 0;
|
|
while ( pszCurrentCmd )
|
|
{
|
|
char szFormatCmd[ 256 ];
|
|
|
|
nCurrentColumn++;
|
|
|
|
if ( nCurrentColumn > nTotalColumns )
|
|
{
|
|
PrintRaw( "\n" );
|
|
nCurrentColumn = 1;
|
|
}
|
|
|
|
Q_snprintf( szFormatCmd, sizeof(szFormatCmd), "%-*s ", nLongestCmd, pszCurrentCmd );
|
|
PrintRaw( szFormatCmd );
|
|
|
|
i++;
|
|
pszCurrentCmd = matches[i];
|
|
}
|
|
|
|
PrintRaw( "\n" );
|
|
PrintRaw( m_szConsoleText );
|
|
// TODO: Tack on 'common' chars in all the matches, i.e. if I typed 'con' and all the
|
|
// matches begin with 'connect_' then print the matches but also complete the
|
|
// command up to that point at least.
|
|
}
|
|
|
|
m_nCursorPosition = m_nConsoleTextLen;
|
|
m_nBrowseLine = m_nInputLine;
|
|
}
|
|
|
|
|
|
|
|
void CTextConsoleWin32::ReceiveStandardChar( const char ch )
|
|
{
|
|
int nCount;
|
|
|
|
// If the line buffer is maxed out, ignore this char
|
|
if ( m_nConsoleTextLen >= ( sizeof( m_szConsoleText ) - 2 ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
nCount = m_nConsoleTextLen;
|
|
while ( nCount > m_nCursorPosition )
|
|
{
|
|
m_szConsoleText[ nCount ] = m_szConsoleText[ nCount - 1 ];
|
|
nCount--;
|
|
}
|
|
|
|
m_szConsoleText[ m_nCursorPosition ] = ch;
|
|
|
|
PrintRaw( m_szConsoleText + m_nCursorPosition, m_nConsoleTextLen - m_nCursorPosition + 1 );
|
|
|
|
m_nConsoleTextLen++;
|
|
m_nCursorPosition++;
|
|
|
|
nCount = m_nConsoleTextLen;
|
|
while ( nCount > m_nCursorPosition )
|
|
{
|
|
PrintRaw( "\b" );
|
|
nCount--;
|
|
}
|
|
|
|
m_nBrowseLine = m_nInputLine;
|
|
}
|
|
|
|
|
|
void CTextConsoleWin32::ReceiveUpArrow( void )
|
|
{
|
|
int nLastCommandInHistory;
|
|
|
|
nLastCommandInHistory = m_nInputLine + 1;
|
|
if ( nLastCommandInHistory > m_nTotalLines )
|
|
{
|
|
nLastCommandInHistory = 0;
|
|
}
|
|
|
|
if ( m_nBrowseLine == nLastCommandInHistory )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( m_nBrowseLine == m_nInputLine )
|
|
{
|
|
if ( m_nConsoleTextLen > 0 )
|
|
{
|
|
// Save off current text
|
|
strncpy( m_szSavedConsoleText, m_szConsoleText, m_nConsoleTextLen );
|
|
// No terminator, it's a raw buffer we always know the length of
|
|
}
|
|
|
|
m_nSavedConsoleTextLen = m_nConsoleTextLen;
|
|
}
|
|
|
|
m_nBrowseLine--;
|
|
if ( m_nBrowseLine < 0 )
|
|
{
|
|
m_nBrowseLine = m_nTotalLines - 1;
|
|
}
|
|
|
|
while ( m_nConsoleTextLen-- ) // delete old line
|
|
{
|
|
PrintRaw( "\b \b" );
|
|
}
|
|
|
|
// copy buffered line
|
|
PrintRaw( m_aszLineBuffer[ m_nBrowseLine ] );
|
|
|
|
strncpy( m_szConsoleText, m_aszLineBuffer[ m_nBrowseLine ], MAX_CONSOLE_TEXTLEN );
|
|
m_nConsoleTextLen = strlen( m_aszLineBuffer[ m_nBrowseLine ] );
|
|
|
|
m_nCursorPosition = m_nConsoleTextLen;
|
|
}
|
|
|
|
|
|
void CTextConsoleWin32::ReceiveDownArrow( void )
|
|
{
|
|
if ( m_nBrowseLine == m_nInputLine )
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_nBrowseLine++;
|
|
if ( m_nBrowseLine > m_nTotalLines )
|
|
{
|
|
m_nBrowseLine = 0;
|
|
}
|
|
|
|
while ( m_nConsoleTextLen-- ) // delete old line
|
|
{
|
|
PrintRaw( "\b \b" );
|
|
}
|
|
|
|
if ( m_nBrowseLine == m_nInputLine )
|
|
{
|
|
if ( m_nSavedConsoleTextLen > 0 )
|
|
{
|
|
// Restore current text
|
|
strncpy( m_szConsoleText, m_szSavedConsoleText, m_nSavedConsoleTextLen );
|
|
// No terminator, it's a raw buffer we always know the length of
|
|
|
|
PrintRaw( m_szConsoleText, m_nSavedConsoleTextLen );
|
|
}
|
|
|
|
m_nConsoleTextLen = m_nSavedConsoleTextLen;
|
|
}
|
|
else
|
|
{
|
|
// copy buffered line
|
|
PrintRaw( m_aszLineBuffer[ m_nBrowseLine ] );
|
|
|
|
strncpy( m_szConsoleText, m_aszLineBuffer[ m_nBrowseLine ], MAX_CONSOLE_TEXTLEN );
|
|
|
|
m_nConsoleTextLen = strlen( m_aszLineBuffer[ m_nBrowseLine ] );
|
|
}
|
|
|
|
m_nCursorPosition = m_nConsoleTextLen;
|
|
}
|
|
|
|
|
|
void CTextConsoleWin32::ReceiveLeftArrow( void )
|
|
{
|
|
if ( m_nCursorPosition == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
PrintRaw( "\b" );
|
|
m_nCursorPosition--;
|
|
}
|
|
|
|
|
|
void CTextConsoleWin32::ReceiveRightArrow( void )
|
|
{
|
|
if ( m_nCursorPosition == m_nConsoleTextLen )
|
|
{
|
|
return;
|
|
}
|
|
|
|
PrintRaw( m_szConsoleText + m_nCursorPosition, 1 );
|
|
m_nCursorPosition++;
|
|
}
|
|
|
|
#endif // _WIN32
|