This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions
+419
View File
@@ -0,0 +1,419 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
// CTextConsoleUnix.cpp: Unix implementation of the TextConsole class.
//
//////////////////////////////////////////////////////////////////////
#ifndef _WIN32
#include <sys/ioctl.h>
#include "TextConsoleUnix.h"
#include "tier0/icommandline.h"
#include "tier1/utllinkedlist.h"
#include "filesystem.h"
#include "../thirdparty/libedit-3.1/src/histedit.h"
#include "tier0/vprof.h"
#define CONSOLE_LOG_FILE "console.log"
static pthread_mutex_t g_lock;
static pthread_t g_threadid = (pthread_t)-1;
CUtlLinkedList< CUtlString > g_Commands;
static volatile int g_ProcessingCommands = false;
#if defined( LINUX )
// Dynamically load the libtinfo stuff. On servers without a tty attached,
// we get to skip all of these dependencies on servers without ttys.
#define TINFO_SYM(rc, fn, params, args, ret) \
typedef rc (*DYNTINFOFN_##fn) params; \
static DYNTINFOFN_##fn g_TINFO_##fn; \
extern "C" rc fn params \
{ \
ret g_TINFO_##fn args; \
}
TINFO_SYM(char *, tgoto, (char *string, int x, int y), (string, x, y), return);
TINFO_SYM(int, tputs, (const char *str, int affcnt, int (*putc)(int)), (str, affcnt, putc), return);
TINFO_SYM(int, tgetflag, (char *id), (id), return);
TINFO_SYM(int, tgetnum, (char *id), (id), return);
TINFO_SYM(int, tgetent, (char *bufp, const char *name), (bufp, name), return);
TINFO_SYM(char *, tgetstr, (char *id, char **area), (id, area), return);
#endif // LINUX
//----------------------------------------------------------------------------------------------------------------------
// init_tinfo_functions
//----------------------------------------------------------------------------------------------------------------------
static bool init_tinfo_functions()
{
#if !defined( LINUX )
return true;
#else
static void *s_ncurses_handle = NULL;
if ( !s_ncurses_handle )
{
// Long time ago, ncurses was two libraries. So if libtinfo fails, try libncurses.
static const char *names[] = { "libtinfo.so.5", "libncurses.so.5" };
for ( int i = 0; !s_ncurses_handle && ( i < ARRAYSIZE( names ) ); i++ )
{
bool bFailed = true;
s_ncurses_handle = dlopen( names[i], RTLD_NOW );
if ( s_ncurses_handle )
{
bFailed = false;
#define LOADTINFOFUNC(_handle, _func, _failed) \
do { \
g_TINFO_##_func = ( DYNTINFOFN_##_func )dlsym(_handle, #_func); \
if ( !g_TINFO_##_func) \
_failed = true; \
} while (0)
LOADTINFOFUNC( s_ncurses_handle, tgoto, bFailed );
LOADTINFOFUNC( s_ncurses_handle, tputs, bFailed );
LOADTINFOFUNC( s_ncurses_handle, tgetflag, bFailed );
LOADTINFOFUNC( s_ncurses_handle, tgetnum, bFailed );
LOADTINFOFUNC( s_ncurses_handle, tgetent, bFailed );
LOADTINFOFUNC( s_ncurses_handle, tgetstr, bFailed );
#undef LOADTINFOFUNC
}
if ( bFailed )
s_ncurses_handle = NULL;
}
if ( !s_ncurses_handle )
{
fprintf( stderr, "\nWARNING: Failed to load 32-bit libtinfo.so.5 or libncurses.so.5.\n"
" Please install (lib32tinfo5 / ncurses-libs.i686 / equivalent) to enable readline.\n\n");
}
}
return !!s_ncurses_handle;
#endif // LINUX
}
static unsigned char editline_complete( EditLine *el, int ch __attribute__((__unused__)) )
{
static const char *s_cmds[] =
{
"cvarlist ",
"find ",
"help ",
"maps ",
"nextlevel",
"quit",
"status",
"sv_cheats ",
"tf_bot_quota ",
"toggle ",
"sv_dump_edicts",
#ifdef STAGING_ONLY
"tf_bot_use_items ",
#endif
};
const LineInfo *lf = el_line(el);
const char *cmd = lf->buffer;
size_t len = lf->cursor - cmd;
if ( len > 0 )
{
for (int i = 0; i < ARRAYSIZE(s_cmds); i++)
{
if ( len > strlen( s_cmds[i] ) )
continue;
if ( !Q_strncmp( cmd, s_cmds[i], len ) )
{
if ( el_insertstr( el, s_cmds[i] + len ) == -1 )
return CC_ERROR;
else
return CC_REFRESH;
}
}
}
return CC_ERROR;
}
static const char *editline_prompt( EditLine *e )
{
// Something like: "\1\033[7m\1Srcds$\1\033[0m\1 "
static const char *szPrompt = getenv( "SRCDS_PROMPT" );
return szPrompt ? szPrompt : "";
}
static void editline_cleanup_handler( void *arg )
{
if ( arg )
{
EditLine *el = (EditLine *)arg;
el_end( el );
}
}
static bool add_command( const char *cmd, int cmd_len )
{
if ( cmd )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
// Trim trailing whitespace.
while ( ( cmd_len > 0 ) && isspace( cmd[ cmd_len - 1 ] ) )
cmd_len--;
if ( cmd_len > 0 )
{
pthread_mutex_lock( &g_lock );
if ( g_Commands.Count() < 32 )
{
CUtlString szCommand( cmd, cmd_len );
g_Commands.AddToTail( szCommand );
g_ProcessingCommands = true;
}
pthread_mutex_unlock( &g_lock );
// Wait a bit until we've processed the command we added.
for ( int i = 0; i < 6; i++ )
{
while ( g_ProcessingCommands )
usleep( 500 );
}
return true;
}
}
return false;
}
static void *editline_threadproc( void *arg )
{
HistEvent ev;
EditLine *el;
History *myhistory;
FILE *tty = (FILE *)arg;
ThreadSetDebugName( "libedit" );
// Set up state
el = el_init( "srcds_linux", stdin, tty, stderr );
el_set( el, EL_PROMPT, &editline_prompt );
el_set( el, EL_EDITOR, "emacs" ); // or "vi"
// Hitting Ctrl+R will reset prompt.
el_set( el, EL_BIND, "^R", "ed-redisplay", NULL );
/* Add a user-defined function */
el_set( el, EL_ADDFN, "ed-complete", "Complete argument", editline_complete );
/* Bind tab to it */
el_set( el, EL_BIND, "^I", "ed-complete", NULL );
// Init history.
myhistory = history_init();
if (myhistory == 0)
{
fprintf( stderr, "history could not be initialized\n" );
g_threadid = (pthread_t)-1;
return (void *)-1;
}
// History size.
history( myhistory, &ev, H_SETSIZE, 800 );
// History callback.
el_set( el, EL_HIST, history, myhistory );
// Source user's defaults.
el_source( el, NULL );
pthread_cleanup_push( editline_cleanup_handler, el );
while ( g_threadid != (pthread_t)-1 )
{
// count is the number of characters read.
// line is a const char* of our command line with the tailing \n
int count;
const char *line = el_gets( el, &count );
if ( add_command( line, count ) )
{
// Add command to history.
history( myhistory, &ev, H_ENTER, line );
}
}
pthread_cleanup_pop( 0 );
// Clean up...
history_end( myhistory );
el_end( el );
return NULL;
}
static void *fgets_threadproc( void *arg )
{
pthread_cleanup_push( editline_cleanup_handler, NULL );
while ( g_threadid != (pthread_t)-1 )
{
char cmd[ 512 ];
if ( fgets( cmd, sizeof( cmd ), stdin ) )
{
cmd[ sizeof(cmd) - 1 ] = 0;
add_command( cmd, strlen( cmd ) );
}
}
pthread_cleanup_pop( 0 );
return NULL;
}
bool CTextConsoleUnix::Init()
{
if( g_threadid != (pthread_t)-1 )
{
Assert( !"CTextConsoleUnix can only handle a single thread!" );
return false;
}
pthread_mutex_init( &g_lock, NULL );
// This code is for echo-ing key presses to the connected tty
// (which is != STDOUT)
if ( isatty( STDIN_FILENO ) )
{
const char *termid_str = ctermid( NULL );
m_tty = fopen( termid_str, "w+" );
if ( !m_tty )
{
fprintf( stderr, "WARNING: Unable to open tty(%s) for output.\n", termid_str );
m_tty = stdout;
}
void *(*terminal_threadproc) (void *) = editline_threadproc;
if ( !init_tinfo_functions() )
terminal_threadproc = fgets_threadproc;
if ( pthread_create( &g_threadid, NULL, terminal_threadproc, (void *)m_tty ) != 0 )
{
g_threadid = (pthread_t)-1;
fprintf( stderr, "WARNING: pthread_create failed: %s.\n", strerror(errno) );
}
}
else
{
m_tty = fopen( "/dev/null", "w+" );
if ( !m_tty )
m_tty = stdout;
}
m_bConDebug = CommandLine()->FindParm( "-condebug" ) != 0;
if ( m_bConDebug && CommandLine()->FindParm( "-conclearlog" ) )
g_pFullFileSystem->RemoveFile( CONSOLE_LOG_FILE, "GAME" );
return CTextConsole::Init();
}
void CTextConsoleUnix::ShutDown()
{
if ( g_threadid != (pthread_t)-1 )
{
void *status = NULL;
pthread_t tid = g_threadid;
g_threadid = (pthread_t)-1;
pthread_cancel( tid );
pthread_join( tid, &status );
}
pthread_mutex_destroy( &g_lock );
}
void CTextConsoleUnix::Print( char * pszMsg )
{
int nChars = strlen( pszMsg );
if ( nChars > 0 )
{
if ( m_bConDebug )
{
FileHandle_t fh = g_pFullFileSystem->Open( CONSOLE_LOG_FILE, "a" );
if ( fh != FILESYSTEM_INVALID_HANDLE )
{
g_pFullFileSystem->Write( pszMsg, nChars, fh );
g_pFullFileSystem->Close( fh );
}
}
fwrite( pszMsg, 1, nChars, m_tty );
}
}
void CTextConsoleUnix::SetTitle( char *pszTitle )
{
}
void CTextConsoleUnix::SetStatusLine( char *pszStatus )
{
}
void CTextConsoleUnix::UpdateStatus()
{
}
char *CTextConsoleUnix::GetLine( int index, char *buf, int buflen )
{
if ( g_threadid != (pthread_t)-1 )
{
if ( g_Commands.Count() > 0 )
{
pthread_mutex_lock( &g_lock );
const CUtlString& psCommand = g_Commands[ g_Commands.Head() ];
V_strncpy( buf, psCommand.Get(), buflen );
g_Commands.Remove( g_Commands.Head() );
pthread_mutex_unlock( &g_lock );
return buf;
}
else if ( index == 0 )
{
// We're being asked for the first command. Must be a new frame.
// Reset the processed commands global.
g_ProcessingCommands = false;
}
}
return NULL;
}
int CTextConsoleUnix::GetWidth()
{
int nWidth = 0;
struct winsize ws;
if ( ioctl( STDOUT_FILENO, TIOCGWINSZ, &ws ) == 0 )
nWidth = (int)ws.ws_col;
if ( nWidth <= 1 )
nWidth = 80;
return nWidth;
}
#endif // !_WIN32
+44
View File
@@ -0,0 +1,44 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
// TextConsoleUnix.h: Unix interface for the TextConsole class.
//
//////////////////////////////////////////////////////////////////////
#if !defined TEXTCONSOLE_UNIX_H
#define TEXTCONSOLE_UNIX_H
#ifndef _WIN32
#include <stdio.h>
#include "textconsole.h"
class CTextConsoleUnix : public CTextConsole
{
public:
virtual ~CTextConsoleUnix() { }
// CTextConsole
bool Init();
void ShutDown();
void Print( char * pszMsg );
void SetTitle( char *pszTitle );
void SetStatusLine( char *pszStatus );
void UpdateStatus();
char * GetLine( int index, char *buf, int buflen );
int GetWidth();
private:
bool m_bConDebug;
FILE *m_tty;
};
#endif // _ndef WIN32
#endif // !defined TEXTCONSOLE_UNIX_H
+644
View File
@@ -0,0 +1,644 @@
//========= 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
+82
View File
@@ -0,0 +1,82 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
// TextConsoleWin32.h: Win32 interface for the TextConsole class.
//
//////////////////////////////////////////////////////////////////////
#if !defined TEXTCONSOLE_WIN32_H
#define TEXTCONSOLE_WIN32_H
#pragma once
#ifdef _WIN32
#include <windows.h>
#include "TextConsole.h"
#define MAX_CONSOLE_TEXTLEN 256
#define MAX_BUFFER_LINES 30
class CTextConsoleWin32 : public CTextConsole
{
public:
CTextConsoleWin32();
virtual ~CTextConsoleWin32() { };
// CTextConsole
bool Init();
void ShutDown( void );
void Print( char *pszMsg );
void SetTitle( char * pszTitle );
void SetStatusLine( char * pszStatus );
void UpdateStatus( void );
char * GetLine( int index, char *buf, int buflen );
int GetWidth( void );
void SetVisible( bool visible );
protected:
// CTextConsoleWin32
void SetColor( WORD );
void PrintRaw( const char * pszMsg, int nChars = -1 );
private:
char m_szConsoleText[ MAX_CONSOLE_TEXTLEN ]; // console text buffer
int m_nConsoleTextLen; // console textbuffer length
int m_nCursorPosition; // position in the current input line
// Saved input data when scrolling back through command history
char m_szSavedConsoleText[ MAX_CONSOLE_TEXTLEN ]; // console text buffer
int m_nSavedConsoleTextLen; // console textbuffer length
char m_aszLineBuffer[ MAX_BUFFER_LINES ][ MAX_CONSOLE_TEXTLEN ]; // command buffer last MAX_BUFFER_LINES commands
int m_nInputLine; // Current line being entered
int m_nBrowseLine; // current buffer line for up/down arrow
int m_nTotalLines; // # of nonempty lines in the buffer
int ReceiveNewline( void );
void ReceiveBackspace( void );
void ReceiveTab( void );
void ReceiveStandardChar( const char ch );
void ReceiveUpArrow( void );
void ReceiveDownArrow( void );
void ReceiveLeftArrow( void );
void ReceiveRightArrow( void );
private:
HANDLE hinput; // standard input handle
HANDLE houtput; // standard output handle
WORD Attrib; // attrib colours for status bar
char statusline[81]; // first line in console is status line
};
#endif // _WIN32
#endif // !defined TEXTCONSOLE_WIN32_H
+428
View File
@@ -0,0 +1,428 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#ifdef _WIN32
// conproc.c -- support for qhost
#include <stdio.h>
#include <process.h>
#include <windows.h>
#include "conproc.h"
#include "isys.h"
#include "tier0/icommandline.h"
#include "tier1/strtools.h"
static HANDLE heventDone;
static HANDLE hfileBuffer;
static HANDLE heventChildSend;
static HANDLE heventParentSend;
static HANDLE hStdout;
static HANDLE hStdin;
/*
==============
SetConsoleCXCY
==============
*/
BOOL SetConsoleCXCY(HANDLE hStdout, int cx, int cy)
{
CONSOLE_SCREEN_BUFFER_INFO info;
COORD coordMax;
coordMax = GetLargestConsoleWindowSize(hStdout);
if (cy > coordMax.Y)
cy = coordMax.Y;
if (cx > coordMax.X)
cx = coordMax.X;
if (!GetConsoleScreenBufferInfo(hStdout, &info))
return FALSE;
// height
info.srWindow.Left = 0;
info.srWindow.Right = info.dwSize.X - 1;
info.srWindow.Top = 0;
info.srWindow.Bottom = cy - 1;
if (cy < info.dwSize.Y)
{
if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow))
return FALSE;
info.dwSize.Y = cy;
if (!SetConsoleScreenBufferSize(hStdout, info.dwSize))
return FALSE;
}
else if (cy > info.dwSize.Y)
{
info.dwSize.Y = cy;
if (!SetConsoleScreenBufferSize(hStdout, info.dwSize))
return FALSE;
if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow))
return FALSE;
}
if (!GetConsoleScreenBufferInfo(hStdout, &info))
return FALSE;
// width
info.srWindow.Left = 0;
info.srWindow.Right = cx - 1;
info.srWindow.Top = 0;
info.srWindow.Bottom = info.dwSize.Y - 1;
if (cx < info.dwSize.X)
{
if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow))
return FALSE;
info.dwSize.X = cx;
if (!SetConsoleScreenBufferSize(hStdout, info.dwSize))
return FALSE;
}
else if (cx > info.dwSize.X)
{
info.dwSize.X = cx;
if (!SetConsoleScreenBufferSize(hStdout, info.dwSize))
return FALSE;
if (!SetConsoleWindowInfo(hStdout, TRUE, &info.srWindow))
return FALSE;
}
return TRUE;
}
/*
==============
GetMappedBuffer
==============
*/
LPVOID GetMappedBuffer (HANDLE hfileBuffer)
{
LPVOID pBuffer;
pBuffer = MapViewOfFile (hfileBuffer,
FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
return pBuffer;
}
/*
==============
ReleaseMappedBuffer
==============
*/
void ReleaseMappedBuffer (LPVOID pBuffer)
{
UnmapViewOfFile (pBuffer);
}
/*
==============
GetScreenBufferLines
==============
*/
BOOL GetScreenBufferLines (int *piLines)
{
CONSOLE_SCREEN_BUFFER_INFO info;
BOOL bRet;
bRet = GetConsoleScreenBufferInfo (hStdout, &info);
if (bRet)
*piLines = info.dwSize.Y;
return bRet;
}
/*
==============
SetScreenBufferLines
==============
*/
BOOL SetScreenBufferLines (int iLines)
{
return SetConsoleCXCY (hStdout, 80, iLines);
}
/*
==============
ReadText
==============
*/
BOOL ReadText (LPTSTR pszText, int iBeginLine, int iEndLine)
{
COORD coord;
DWORD dwRead;
BOOL bRet;
coord.X = 0;
coord.Y = iBeginLine;
bRet = ReadConsoleOutputCharacter(
hStdout,
pszText,
80 * (iEndLine - iBeginLine + 1),
coord,
&dwRead);
// Make sure it's null terminated.
if (bRet)
pszText[dwRead] = '\0';
return bRet;
}
/*
==============
CharToCode
==============
*/
int CharToCode (char c)
{
char upper;
upper = toupper(c);
switch (c)
{
case 13:
return 28;
default:
break;
}
if (V_isalpha(c))
return (30 + upper - 65);
if (V_isdigit(c))
return (1 + upper - 47);
return c;
}
/*
==============
WriteText
==============
*/
BOOL WriteText (LPCTSTR szText)
{
DWORD dwWritten;
INPUT_RECORD rec;
char upper, *sz;
sz = (LPTSTR) szText;
while (*sz)
{
// 13 is the code for a carriage return (\n) instead of 10.
if (*sz == 10)
*sz = 13;
upper = toupper(*sz);
rec.EventType = KEY_EVENT;
rec.Event.KeyEvent.bKeyDown = TRUE;
rec.Event.KeyEvent.wRepeatCount = 1;
rec.Event.KeyEvent.wVirtualKeyCode = upper;
rec.Event.KeyEvent.wVirtualScanCode = CharToCode (*sz);
rec.Event.KeyEvent.uChar.AsciiChar = *sz;
rec.Event.KeyEvent.uChar.UnicodeChar = *sz;
rec.Event.KeyEvent.dwControlKeyState = V_isupper(*sz) ? 0x80 : 0x0;
WriteConsoleInput(
hStdin,
&rec,
1,
&dwWritten);
rec.Event.KeyEvent.bKeyDown = FALSE;
WriteConsoleInput(
hStdin,
&rec,
1,
&dwWritten);
sz++;
}
return TRUE;
}
/*
==============
RequestProc
==============
*/
unsigned _stdcall RequestProc (void *arg)
{
int *pBuffer;
DWORD dwRet;
HANDLE heventWait[2];
int iBeginLine, iEndLine;
heventWait[0] = heventParentSend;
heventWait[1] = heventDone;
while (1)
{
dwRet = WaitForMultipleObjects (2, heventWait, FALSE, INFINITE);
// heventDone fired, so we're exiting.
if (dwRet == WAIT_OBJECT_0 + 1)
break;
pBuffer = (int *) GetMappedBuffer (hfileBuffer);
// hfileBuffer is invalid. Just leave.
if (!pBuffer)
{
sys->Printf ("Request Proc: Invalid -HFILE handle\n");
break;
}
switch (pBuffer[0])
{
case CCOM_WRITE_TEXT:
// Param1 : Text
pBuffer[0] = WriteText ((LPCTSTR) (pBuffer + 1));
break;
case CCOM_GET_TEXT:
// Param1 : Begin line
// Param2 : End line
iBeginLine = pBuffer[1];
iEndLine = pBuffer[2];
pBuffer[0] = ReadText ((LPTSTR) (pBuffer + 1), iBeginLine,
iEndLine);
break;
case CCOM_GET_SCR_LINES:
// No params
pBuffer[0] = GetScreenBufferLines (&pBuffer[1]);
break;
case CCOM_SET_SCR_LINES:
// Param1 : Number of lines
pBuffer[0] = SetScreenBufferLines (pBuffer[1]);
break;
}
ReleaseMappedBuffer (pBuffer);
SetEvent (heventChildSend);
}
_endthreadex (0);
return 0;
}
/*
==============
DeinitConProc
==============
*/
void DeinitConProc (void)
{
if ( heventDone )
{
SetEvent ( heventDone );
}
}
/*
==============
InitConProc
==============
*/
void InitConProc ( void )
{
unsigned threadAddr;
HANDLE hFile = (HANDLE)0;
HANDLE heventParent = (HANDLE)0;
HANDLE heventChild = (HANDLE)0;
int WantHeight = 50;
const char *p;
// give external front ends a chance to hook into the console
if ( CommandLine()->CheckParm ( "-HFILE", &p ) && p )
{
hFile = (HANDLE)atoi ( p );
}
if ( CommandLine()->CheckParm ( "-HPARENT", &p ) && p )
{
heventParent = (HANDLE)atoi ( p );
}
if ( CommandLine()->CheckParm ( "-HCHILD", &p ) && p )
{
heventChild = (HANDLE)atoi ( p );
}
// ignore if we don't have all the events.
if ( !hFile || !heventParent || !heventChild )
{
//sys->Printf ("\n\nNo external front end present.\n" );
return;
}
sys->Printf( "\n\nInitConProc: Setting up external control.\n" );
hfileBuffer = hFile;
heventParentSend = heventParent;
heventChildSend = heventChild;
// So we'll know when to go away.
heventDone = CreateEvent (NULL, FALSE, FALSE, NULL);
if (!heventDone)
{
sys->Printf ("InitConProc: Couldn't create heventDone\n");
return;
}
if (!_beginthreadex (NULL, 0, RequestProc, NULL, 0, &threadAddr))
{
CloseHandle (heventDone);
sys->Printf ("InitConProc: Couldn't create third party thread\n");
return;
}
// save off the input/output handles.
hStdout = GetStdHandle (STD_OUTPUT_HANDLE);
hStdin = GetStdHandle (STD_INPUT_HANDLE);
if ( CommandLine()->CheckParm( "-conheight", &p ) && p )
{
WantHeight = atoi( p );
}
// Force 80 character width, at least 25 character height
SetConsoleCXCY( hStdout, 80, WantHeight );
}
#endif // _WIN32
+30
View File
@@ -0,0 +1,30 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
// conproc.h -- support for external server monitoring programs
#ifndef INC_CONPROCH
#define INC_CONPROCH
#define CCOM_WRITE_TEXT 0x2
// Param1 : Text
#define CCOM_GET_TEXT 0x3
// Param1 : Begin line
// Param2 : End line
#define CCOM_GET_SCR_LINES 0x4
// No params
#define CCOM_SET_SCR_LINES 0x5
// Param1 : Number of lines
void InitConProc ( void );
void DeinitConProc ( void );
void WriteStatusText( char *psz );
#endif // !INC_CONPROCH
+22
View File
@@ -0,0 +1,22 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
// TextConsole.cpp: implementation of the TextConsole class.
//
//////////////////////////////////////////////////////////////////////
#include "textconsole.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
bool CTextConsole::Init()
{
m_ConsoleVisible = true;
return true;
}
+38
View File
@@ -0,0 +1,38 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#if !defined TEXTCONSOLE_H
#define TEXTCONSOLE_H
#pragma once
class CTextConsole
{
public:
CTextConsole() : m_ConsoleVisible( true ) {}
virtual ~CTextConsole() {};
virtual bool Init ();
virtual void ShutDown() = 0;
virtual void Print( char * pszMsg ) = 0;
virtual void SetTitle( char * pszTitle ) = 0;
virtual void SetStatusLine( char * pszStatus ) = 0;
virtual void UpdateStatus() = 0;
// Must be provided by children
virtual char * GetLine( int index, char *buf, int buflen ) = 0;
virtual int GetWidth() = 0;
virtual void SetVisible( bool visible ) { m_ConsoleVisible = visible; }
virtual bool IsVisible() { return m_ConsoleVisible; }
protected:
bool m_ConsoleVisible;
};
#endif // !defined TEXTCONSOLE_H