You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
644 lines
13 KiB
644 lines
13 KiB
//========= 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
|
|
|