summaryrefslogtreecommitdiff
path: root/engine/console.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/console.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'engine/console.cpp')
-rw-r--r--engine/console.cpp1330
1 files changed, 1330 insertions, 0 deletions
diff --git a/engine/console.cpp b/engine/console.cpp
new file mode 100644
index 0000000..f2a2ba7
--- /dev/null
+++ b/engine/console.cpp
@@ -0,0 +1,1330 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=====================================================================================//
+
+#include "client_pch.h"
+#include <time.h>
+#include "console.h"
+#include "ivideomode.h"
+#include "zone.h"
+#include "sv_main.h"
+#include "server.h"
+#include "MapReslistGenerator.h"
+#include "tier0/vcrmode.h"
+#if defined( _X360 )
+#include "xbox/xbox_console.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#if !defined( _X360 )
+#define MAXPRINTMSG 4096
+#else
+#define MAXPRINTMSG 1024
+#endif
+
+bool con_debuglog = false;
+bool con_initialized = false;
+bool con_debuglogmapprefixed = false;
+
+CThreadFastMutex g_AsyncNotifyTextMutex;
+
+static ConVar con_timestamp( "con_timestamp", "0", 0, "Prefix console.log entries with timestamps" );
+
+// In order to avoid excessive opening and closing of the console log file
+// we wrap it in an object and keep the handle open. This is necessary
+// because of the sometimes considerable cost of opening and closing files
+// on Windows. Opening and closing files on Windows is always moderately
+// expensive, but profiling may dramatically underestimate the true cost
+// because some anti-virus software can make closing a file handle take
+// 20-90 ms!
+class ConsoleLogManager
+{
+public:
+ ConsoleLogManager();
+ ~ConsoleLogManager();
+
+ void RemoveConsoleLogFile();
+ bool ReadConsoleLogFile( CUtlBuffer& buf );
+ FileHandle_t GetConsoleLogFileHandleForAppend();
+ void CloseFileIfOpen();
+
+private:
+ FileHandle_t m_fh;
+
+ const char *GetConsoleLogFilename() const;
+};
+
+// Wrap the ConsoleLogManager in a function to ensure that the object is always
+// constructed before it is used.
+ConsoleLogManager& GetConsoleLogManager()
+{
+ static ConsoleLogManager object;
+ return object;
+}
+
+void ConsoleLogFileCallback( IConVar *var, const char *pOldValue, float flOldValue )
+{
+ ConVarRef con_logfile( var->GetName() );
+ const char *logFile = con_logfile.GetString();
+ // close any existing file, because we have changed the name
+ GetConsoleLogManager().CloseFileIfOpen();
+
+ // validate the path and the .log/.txt extensions
+ if ( !COM_IsValidPath( logFile ) || !COM_IsValidLogFilename( logFile ) )
+ {
+ ConMsg( "invalid log filename\n" );
+ con_logfile.SetValue( "console.log" );
+ return;
+ }
+ else
+ {
+ const char *extension = Q_GetFileExtension( logFile );
+ if ( !extension || ( Q_strcasecmp( extension, "log" ) && Q_strcasecmp( extension, "txt" ) ) )
+ {
+ char szTemp[MAX_PATH];
+ V_sprintf_safe( szTemp, "%s.log", logFile );
+ con_logfile.SetValue( szTemp );
+ return;
+ }
+ }
+
+ if ( !COM_IsValidPath( logFile ) )
+ {
+ con_debuglog = CommandLine()->FindParm( "-condebug" ) != 0;
+ }
+ else
+ {
+ con_debuglog = true;
+ }
+}
+
+ConVar con_logfile( "con_logfile", "", 0, "Console output gets written to this file", false, 0.0f, false, 0.0f, ConsoleLogFileCallback );
+
+static const char *GetTimestampString( void )
+{
+ static char string[128];
+ tm today;
+ VCRHook_LocalTime( &today );
+ Q_snprintf( string, sizeof( string ), "%02i/%02i/%04i - %02i:%02i:%02i",
+ today.tm_mon+1, today.tm_mday, 1900 + today.tm_year,
+ today.tm_hour, today.tm_min, today.tm_sec );
+ return string;
+}
+
+#ifndef SWDS
+
+static ConVar con_trace( "con_trace", "0", FCVAR_MATERIAL_SYSTEM_THREAD, "Print console text to low level printout." );
+static ConVar con_notifytime( "con_notifytime","8", FCVAR_MATERIAL_SYSTEM_THREAD, "How long to display recent console text to the upper part of the game window" );
+static ConVar con_times("contimes", "8", FCVAR_MATERIAL_SYSTEM_THREAD, "Number of console lines to overlay for debugging." );
+static ConVar con_drawnotify( "con_drawnotify", "1", 0, "Disables drawing of notification area (for taking screenshots)." );
+static ConVar con_enable("con_enable", "0", FCVAR_ARCHIVE, "Allows the console to be activated.");
+static ConVar con_filter_enable ( "con_filter_enable","0", FCVAR_MATERIAL_SYSTEM_THREAD, "Filters console output based on the setting of con_filter_text. 1 filters completely, 2 displays filtered text brighter than other text." );
+static ConVar con_filter_text ( "con_filter_text","", FCVAR_MATERIAL_SYSTEM_THREAD, "Text with which to filter console spew. Set con_filter_enable 1 or 2 to activate." );
+static ConVar con_filter_text_out ( "con_filter_text_out","", FCVAR_MATERIAL_SYSTEM_THREAD, "Text with which to filter OUT of console spew. Set con_filter_enable 1 or 2 to activate." );
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Implements the console using VGUI
+//-----------------------------------------------------------------------------
+class CConPanel : public CBasePanel
+{
+ typedef CBasePanel BaseClass;
+
+public:
+ enum
+ {
+ MAX_NOTIFY_TEXT_LINE = 256
+ };
+
+ CConPanel( vgui::Panel *parent );
+ virtual ~CConPanel( void );
+
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+ // Draws the text
+ virtual void Paint();
+ // Draws the background image
+ virtual void PaintBackground();
+
+ // Draw notify area
+ virtual void DrawNotify( void );
+ // Draws debug ( Con_NXPrintf ) areas
+ virtual void DrawDebugAreas( void );
+
+ int ProcessNotifyLines( int &left, int &top, int &right, int &bottom, bool bDraw );
+
+ // Draw helpers
+ virtual int DrawText( vgui::HFont font, int x, int y, wchar_t *data );
+
+ virtual bool ShouldDraw( void );
+
+ void Con_NPrintf( int idx, const char *msg );
+ void Con_NXPrintf( const struct con_nprint_s *info, const char *msg );
+
+ void AddToNotify( const Color& clr, char const *msg );
+ void ClearNotify();
+
+private:
+ // Console font
+ vgui::HFont m_hFont;
+ vgui::HFont m_hFontFixed;
+
+ struct CNotifyText
+ {
+ Color clr;
+ float liferemaining;
+ wchar_t text[MAX_NOTIFY_TEXT_LINE];
+ };
+
+ CCopyableUtlVector< CNotifyText > m_NotifyText;
+
+ enum
+ {
+ MAX_DBG_NOTIFY = 128,
+ DBG_NOTIFY_TIMEOUT = 4,
+ };
+
+ float da_default_color[3];
+
+ typedef struct
+ {
+ wchar_t szNotify[MAX_NOTIFY_TEXT_LINE];
+ float expire;
+ float color[3];
+ bool fixed_width_font;
+ } da_notify_t;
+
+ da_notify_t da_notify[MAX_DBG_NOTIFY];
+ bool m_bDrawDebugAreas;
+};
+
+static CConPanel *g_pConPanel = NULL;
+
+/*
+================
+Con_HideConsole_f
+
+================
+*/
+void Con_HideConsole_f( void )
+{
+ if ( IsX360() )
+ return;
+
+ if ( EngineVGui()->IsConsoleVisible() )
+ {
+ // hide the console
+ EngineVGui()->HideConsole();
+ }
+}
+
+/*
+================
+Con_ShowConsole_f
+================
+*/
+void Con_ShowConsole_f( void )
+{
+ if ( IsX360() )
+ return;
+
+ if ( vgui::input()->GetAppModalSurface() )
+ {
+ // If a dialog has modal, it probably has grabbed keyboard focus, so showing
+ // the console would be a bad idea.
+ return;
+ }
+
+ if ( !g_ClientDLL->ShouldAllowConsole() )
+ return;
+
+ // make sure we're allowed to see the console
+ if ( con_enable.GetBool() || developer.GetInt() || CommandLine()->CheckParm("-console") || CommandLine()->CheckParm("-rpt") )
+ {
+ // show the console
+ EngineVGui()->ShowConsole();
+
+ // remove any loading screen
+ SCR_EndLoadingPlaque();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: toggles the console
+//-----------------------------------------------------------------------------
+void Con_ToggleConsole_f( void )
+{
+ if ( IsX360() )
+ return;
+
+ if (EngineVGui()->IsConsoleVisible())
+ {
+ Con_HideConsole_f();
+
+ // If we hide the console, we also hide the game UI
+ EngineVGui()->HideGameUI();
+ }
+ else
+ {
+ Con_ShowConsole_f();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Clears the console
+//-----------------------------------------------------------------------------
+void Con_Clear_f( void )
+{
+ if ( IsX360() )
+ return;
+
+ EngineVGui()->ClearConsole();
+ Con_ClearNotify();
+}
+
+/*
+================
+Con_ClearNotify
+================
+*/
+void Con_ClearNotify (void)
+{
+ if ( g_pConPanel )
+ {
+ g_pConPanel->ClearNotify();
+ }
+}
+
+#endif // SWDS
+
+
+ConsoleLogManager::ConsoleLogManager()
+{
+ m_fh = FILESYSTEM_INVALID_HANDLE;
+}
+
+ConsoleLogManager::~ConsoleLogManager()
+{
+ // This fails because of destructor order problems. The file
+ // system has already been shut down by the time this runs.
+ // We'll have to count on the OS to close the file for us.
+ //CloseFileIfOpen();
+}
+
+void ConsoleLogManager::RemoveConsoleLogFile()
+{
+ // Make sure the log file is closed before we try deleting it.
+ CloseFileIfOpen();
+ g_pFileSystem->RemoveFile( GetConsoleLogFilename(), "GAME" );
+}
+
+bool ConsoleLogManager::ReadConsoleLogFile( CUtlBuffer& buf )
+{
+ // Make sure the log file is closed before we try reading it.
+ CloseFileIfOpen();
+ const char *pLogFile = GetConsoleLogFilename();
+ if ( g_pFullFileSystem->ReadFile( pLogFile, "GAME", buf ) )
+ return true;
+
+ return false;
+}
+
+FileHandle_t ConsoleLogManager::GetConsoleLogFileHandleForAppend()
+{
+ if ( m_fh == FILESYSTEM_INVALID_HANDLE )
+ {
+ const char* file = GetConsoleLogFilename();
+ m_fh = g_pFileSystem->Open( file, "a" );
+ }
+
+ return m_fh;
+}
+
+void ConsoleLogManager::CloseFileIfOpen()
+{
+ if ( m_fh != FILESYSTEM_INVALID_HANDLE )
+ {
+ g_pFileSystem->Close( m_fh );
+ m_fh = FILESYSTEM_INVALID_HANDLE;
+ }
+}
+
+const char *ConsoleLogManager::GetConsoleLogFilename() const
+{
+ const char *logFile = con_logfile.GetString();
+ if ( !COM_IsValidPath( logFile ) || !COM_IsValidLogFilename( logFile ) )
+ {
+ return "console.log";
+ }
+ return logFile;
+}
+
+
+/*
+================
+Con_Init
+================
+*/
+void Con_Init (void)
+{
+#ifdef DEDICATED
+ con_debuglog = false; // the dedicated server's console will handle this
+ con_debuglogmapprefixed = false;
+
+ // Check -consolelog arg and set con_logfile if it's present. This gets some messages logged
+ // that we would otherwise miss due to con_logfile being set in the .cfg file.
+ const char *filename = NULL;
+ if ( CommandLine()->CheckParm( "-consolelog", &filename ) && filename && filename[ 0 ] )
+ {
+ con_logfile.SetValue( filename );
+ }
+#else
+ bool bRPTClient = ( CommandLine()->FindParm( "-rpt" ) != 0 );
+ con_debuglog = bRPTClient || ( CommandLine()->FindParm( "-condebug" ) != 0 );
+ con_debuglogmapprefixed = CommandLine()->FindParm( "-makereslists" ) != 0;
+ if ( con_debuglog )
+ {
+ con_logfile.SetValue( "console.log" );
+ if ( bRPTClient || ( CommandLine()->FindParm( "-conclearlog" ) ) )
+ {
+ GetConsoleLogManager().RemoveConsoleLogFile();
+ }
+ }
+#endif // !DEDICATED
+
+ con_initialized = true;
+}
+
+/*
+================
+Con_Shutdown
+================
+*/
+void Con_Shutdown (void)
+{
+ con_initialized = false;
+}
+
+/*
+================
+Read the console log from disk and return it in 'buf'. Buf should come
+in as an empty TEXT_BUFFER CUtlBuffer.
+Returns true if the log file is successfully read.
+================
+*/
+bool GetConsoleLogFileData( CUtlBuffer& buf )
+{
+ return GetConsoleLogManager().ReadConsoleLogFile( buf );
+}
+
+/*
+================
+Con_DebugLog
+================
+*/
+void Con_DebugLog( const char *fmt, ...)
+{
+ va_list argptr;
+ char data[MAXPRINTMSG];
+
+ va_start(argptr, fmt);
+ Q_vsnprintf(data, sizeof(data), fmt, argptr);
+ va_end(argptr);
+
+ FileHandle_t fh = GetConsoleLogManager().GetConsoleLogFileHandleForAppend();
+ if (fh != FILESYSTEM_INVALID_HANDLE )
+ {
+ if ( con_debuglogmapprefixed )
+ {
+ char const *prefix = MapReslistGenerator().LogPrefix();
+ if ( prefix )
+ {
+ g_pFileSystem->Write( prefix, strlen(prefix), fh );
+ }
+ }
+
+ if ( con_timestamp.GetBool() )
+ {
+ static bool needTimestamp = true; // Start the first line with a timestamp
+ if ( needTimestamp )
+ {
+ const char *timestamp = GetTimestampString();
+ g_pFileSystem->Write( timestamp, strlen( timestamp ), fh );
+ g_pFileSystem->Write( ": ", 2, fh );
+ }
+ needTimestamp = V_stristr( data, "\n" ) != 0;
+ }
+
+ g_pFileSystem->Write( data, strlen(data), fh );
+ // Now that we don't close the file we need to flush it in order
+ // to make sure that the data makes it to the file system.
+ g_pFileSystem->Flush( fh );
+ }
+}
+
+static bool g_fIsDebugPrint = false;
+
+#ifndef SWDS
+/*
+================
+Con_Printf
+
+Handles cursor positioning, line wrapping, etc
+================
+*/
+static bool g_fColorPrintf = false;
+static bool g_bInColorPrint = false;
+extern CThreadLocalInt<> g_bInSpew;
+
+void Con_Printf( const char *fmt, ... );
+
+extern ConVar spew_consolelog_to_debugstring;
+
+void Con_ColorPrint( const Color& clr, char const *msg )
+{
+ if ( IsPC() )
+ {
+ if ( g_bInColorPrint )
+ return;
+
+ int nCon_Filter_Enable = con_filter_enable.GetInt();
+ if ( nCon_Filter_Enable > 0 )
+ {
+ const char *pszText = con_filter_text.GetString();
+ const char *pszIgnoreText = con_filter_text_out.GetString();
+
+ switch( nCon_Filter_Enable )
+ {
+ case 1:
+ // if line does not contain keyword do not print the line
+ if ( pszText && ( *pszText != '\0' ) && ( Q_stristr( msg, pszText ) == NULL ))
+ return;
+ if ( pszIgnoreText && *pszIgnoreText && ( Q_stristr( msg, pszIgnoreText ) != NULL ) )
+ return;
+ break;
+
+ case 2:
+ if ( pszIgnoreText && *pszIgnoreText && ( Q_stristr( msg, pszIgnoreText ) != NULL ) )
+ return;
+ // if line does not contain keyword print it in a darker color
+ if ( pszText && ( *pszText != '\0' ) && ( Q_stristr( msg, pszText ) == NULL ))
+ {
+ Color mycolor(200, 200, 200, 150 );
+ g_pCVar->ConsoleColorPrintf( mycolor, "%s", msg );
+ return;
+ }
+ break;
+
+ default:
+ // by default do no filtering
+ break;
+ }
+ }
+
+ g_bInColorPrint = true;
+
+ // also echo to debugging console
+ if ( Plat_IsInDebugSession() && !con_trace.GetInt() && !spew_consolelog_to_debugstring.GetBool() )
+ {
+ Sys_OutputDebugString(msg);
+ }
+
+ if ( sv.IsDedicated() )
+ {
+ g_bInColorPrint = false;
+ return; // no graphics mode
+ }
+
+ bool convisible = Con_IsVisible();
+ bool indeveloper = ( developer.GetInt() > 0 );
+ bool debugprint = g_fIsDebugPrint;
+
+ if ( g_fColorPrintf )
+ {
+ g_pCVar->ConsoleColorPrintf( clr, "%s", msg );
+ }
+ else
+ {
+ // write it out to the vgui console no matter what
+ if ( g_fIsDebugPrint )
+ {
+ // Don't spew debug stuff to actual console once in game, unless console isn't up
+ if ( !cl.IsActive() || !convisible )
+ {
+ g_pCVar->ConsoleDPrintf( "%s", msg );
+ }
+ }
+ else
+ {
+ g_pCVar->ConsolePrintf( "%s", msg );
+ }
+ }
+
+ // Make sure we "spew" if this wan't generated from the spew system
+ if ( !g_bInSpew )
+ {
+ Msg( "%s", msg );
+ }
+
+ // Only write to notify if it's non-debug or we are running with developer set > 0
+ // Buf it it's debug then make sure we don't have the console down
+ if ( ( !debugprint || indeveloper ) && !( debugprint && convisible ) )
+ {
+ if ( g_pConPanel )
+ {
+ g_pConPanel->AddToNotify( clr, msg );
+ }
+ }
+ g_bInColorPrint = false;
+ }
+
+#if defined( _X360 )
+ int r,g,b,a;
+ char buffer[MAXPRINTMSG];
+ const char *pFrom;
+ char *pTo;
+
+ clr.GetColor(r, g, b, a);
+
+ // fixup percent printers
+ pFrom = msg;
+ pTo = buffer;
+ while ( *pFrom && pTo < buffer+sizeof(buffer)-1 )
+ {
+ *pTo = *pFrom++;
+ if ( *pTo++ == '%' )
+ *pTo++ = '%';
+ }
+ *pTo = '\0';
+
+ XBX_DebugString( XMAKECOLOR(r,g,b), buffer );
+#endif
+}
+#endif
+
+// returns false if the print function shouldn't continue
+bool HandleRedirectAndDebugLog( const char *msg )
+{
+ // Add to redirected message
+ if ( SV_RedirectActive() )
+ {
+ SV_RedirectAddText( msg );
+ return false;
+ }
+
+ // log all messages to file
+ if ( con_debuglog )
+ Con_DebugLog( "%s", msg );
+
+ if (!con_initialized)
+ {
+ return false;
+ }
+ return true;
+}
+
+void Con_Print( const char *msg )
+{
+ if ( !msg || !msg[0] )
+ return;
+
+ if ( !HandleRedirectAndDebugLog( msg ) )
+ {
+ return;
+ }
+
+#ifdef SWDS
+ Msg( "%s", msg );
+#else
+ if ( sv.IsDedicated() )
+ {
+ Msg( "%s", msg );
+ }
+ else
+ {
+#if !defined( _X360 )
+ Color clr( 255, 255, 255, 255 );
+#else
+ Color clr( 0, 0, 0, 255 );
+#endif
+ Con_ColorPrint( clr, msg );
+ }
+#endif
+}
+
+void Con_Printf( const char *fmt, ... )
+{
+ va_list argptr;
+ char msg[MAXPRINTMSG];
+ static bool inupdate;
+
+ va_start( argptr, fmt );
+ Q_vsnprintf( msg, sizeof( msg ), fmt, argptr );
+ va_end( argptr );
+
+#ifndef NO_VCR
+ // Normally, we shouldn't need to write this data to the file, but it can help catch
+ // out-of-sync errors earlier.
+ if ( vcr_verbose.GetInt() )
+ {
+ VCRGenericString( "Con_Printf", msg );
+ }
+#endif
+
+ if ( !HandleRedirectAndDebugLog( msg ) )
+ {
+ return;
+ }
+
+#ifdef SWDS
+ Msg( "%s", msg );
+#else
+ if ( sv.IsDedicated() )
+ {
+ Msg( "%s", msg );
+ }
+ else
+ {
+#if !defined( _X360 )
+ Color clr( 255, 255, 255, 255 );
+#else
+ Color clr( 0, 0, 0, 255 );
+#endif
+ Con_ColorPrint( clr, msg );
+ }
+#endif
+}
+
+#ifndef SWDS
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : clr -
+// *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+void Con_ColorPrintf( const Color& clr, const char *fmt, ... )
+{
+ va_list argptr;
+ char msg[MAXPRINTMSG];
+
+ va_start (argptr,fmt);
+ Q_vsnprintf (msg,sizeof( msg ), fmt,argptr);
+ va_end (argptr);
+
+ AUTO_LOCK( g_AsyncNotifyTextMutex );
+ if ( !HandleRedirectAndDebugLog( msg ) )
+ {
+ return;
+ }
+
+ g_fColorPrintf = true;
+ Con_ColorPrint( clr, msg );
+ g_fColorPrintf = false;
+}
+#endif
+
+/*
+================
+Con_DPrintf
+
+A Con_Printf that only shows up if the "developer" cvar is set
+================
+*/
+void Con_DPrintf (const char *fmt, ...)
+{
+ va_list argptr;
+ char msg[MAXPRINTMSG];
+
+ va_start (argptr,fmt);
+ Q_vsnprintf(msg,sizeof( msg ), fmt,argptr);
+ va_end (argptr);
+
+ g_fIsDebugPrint = true;
+
+#ifdef SWDS
+ DevMsg( "%s", msg );
+#else
+ if ( sv.IsDedicated() )
+ {
+ DevMsg( "%s", msg );
+ }
+ else
+ {
+ Color clr( 196, 181, 80, 255 );
+ Con_ColorPrint ( clr, msg );
+ }
+#endif
+
+ g_fIsDebugPrint = false;
+}
+
+
+/*
+==================
+Con_SafePrintf
+
+Okay to call even when the screen can't be updated
+==================
+*/
+void Con_SafePrintf (const char *fmt, ...)
+{
+ va_list argptr;
+ char msg[MAXPRINTMSG];
+
+ va_start (argptr,fmt);
+ Q_vsnprintf(msg,sizeof( msg ), fmt,argptr);
+ va_end (argptr);
+
+#ifndef SWDS
+ bool temp;
+ temp = scr_disabled_for_loading;
+ scr_disabled_for_loading = true;
+#endif
+ g_fIsDebugPrint = true;
+ Con_Printf ("%s", msg);
+ g_fIsDebugPrint = false;
+#ifndef SWDS
+ scr_disabled_for_loading = temp;
+#endif
+}
+
+#ifndef SWDS
+bool Con_IsVisible()
+{
+ return (EngineVGui()->IsConsoleVisible());
+}
+
+void Con_NPrintf( int idx, const char *fmt, ... )
+{
+ va_list argptr;
+ char outtext[MAXPRINTMSG];
+
+ va_start(argptr, fmt);
+ Q_vsnprintf( outtext, sizeof( outtext ), fmt, argptr);
+ va_end(argptr);
+
+ if ( IsPC() )
+ {
+ g_pConPanel->Con_NPrintf( idx, outtext );
+ }
+ else
+ {
+ Con_Printf( outtext );
+ }
+}
+
+void Con_NXPrintf( const struct con_nprint_s *info, const char *fmt, ... )
+{
+ va_list argptr;
+ char outtext[MAXPRINTMSG];
+
+ va_start(argptr, fmt);
+ Q_vsnprintf( outtext, sizeof( outtext ), fmt, argptr);
+ va_end(argptr);
+
+ if ( IsPC() )
+ {
+ g_pConPanel->Con_NXPrintf( info, outtext );
+ }
+ else
+ {
+ Con_Printf( outtext );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates the console panel
+// Input : *parent -
+//-----------------------------------------------------------------------------
+CConPanel::CConPanel( vgui::Panel *parent ) : CBasePanel( parent, "CConPanel" )
+{
+ // Full screen assumed
+ SetSize( videomode->GetModeStereoWidth(), videomode->GetModeStereoHeight() );
+ SetPos( 0, 0 );
+ SetVisible( true );
+ SetMouseInputEnabled( false );
+ SetKeyBoardInputEnabled( false );
+
+ da_default_color[0] = 1.0;
+ da_default_color[1] = 1.0;
+ da_default_color[2] = 1.0;
+
+ m_bDrawDebugAreas = false;
+
+ g_pConPanel = this;
+ memset( da_notify, 0, sizeof(da_notify) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CConPanel::~CConPanel( void )
+{
+}
+
+void CConPanel::Con_NPrintf( int idx, const char *msg )
+{
+ if ( idx < 0 || idx >= MAX_DBG_NOTIFY )
+ return;
+
+#ifdef WIN32
+ _snwprintf( da_notify[idx].szNotify, sizeof( da_notify[idx].szNotify ) / sizeof( wchar_t ) - 1, L"%S", msg );
+#else
+ _snwprintf( da_notify[idx].szNotify, sizeof( da_notify[idx].szNotify ) / sizeof( wchar_t ) - 1, L"%s", msg );
+#endif
+ da_notify[idx].szNotify[ sizeof( da_notify[idx].szNotify ) / sizeof( wchar_t ) - 1 ] = L'\0';
+
+ // Reset values
+ da_notify[idx].expire = realtime + DBG_NOTIFY_TIMEOUT;
+ VectorCopy( da_default_color, da_notify[idx].color );
+ da_notify[idx].fixed_width_font = false;
+ m_bDrawDebugAreas = true;
+}
+
+void CConPanel::Con_NXPrintf( const struct con_nprint_s *info, const char *msg )
+{
+ if ( !info )
+ return;
+
+ if ( info->index < 0 || info->index >= MAX_DBG_NOTIFY )
+ return;
+
+#ifdef WIN32
+ _snwprintf( da_notify[info->index].szNotify, sizeof( da_notify[info->index].szNotify ) / sizeof( wchar_t ) - 1, L"%S", msg );
+#else
+ _snwprintf( da_notify[info->index].szNotify, sizeof( da_notify[info->index].szNotify ) / sizeof( wchar_t ) - 1, L"%s", msg );
+#endif
+ da_notify[info->index].szNotify[ sizeof( da_notify[info->index].szNotify ) / sizeof( wchar_t ) - 1 ] = L'\0';
+
+ // Reset values
+ if ( info->time_to_live == -1 )
+ da_notify[ info->index ].expire = -1; // special marker means to just draw it once
+ else
+ da_notify[ info->index ].expire = realtime + info->time_to_live;
+ VectorCopy( info->color, da_notify[ info->index ].color );
+ da_notify[ info->index ].fixed_width_font = info->fixed_width_font;
+ m_bDrawDebugAreas = true;
+}
+
+static void safestrncat( wchar_t *text, int maxCharactersWithNullTerminator, wchar_t const *add, int addchars )
+{
+ int maxCharactersWithoutTerminator = maxCharactersWithNullTerminator - 1;
+
+ int curlen = wcslen( text );
+ if ( curlen >= maxCharactersWithoutTerminator )
+ return;
+
+ wchar_t *p = text + curlen;
+ while ( curlen++ < maxCharactersWithoutTerminator &&
+ --addchars >= 0 )
+ {
+ *p++ = *add++;
+ }
+ *p = 0;
+}
+
+void CConPanel::AddToNotify( const Color& clr, char const *msg )
+{
+ if ( !host_initialized )
+ return;
+
+ // notify area only ever draws in developer mode - it should never be used for game messages
+ if ( !developer.GetBool() )
+ return;
+
+ // skip any special characters
+ if ( msg[0] == 1 ||
+ msg[0] == 2 )
+ {
+ msg++;
+ }
+
+ // Nothing left
+ if ( !msg[0] )
+ return;
+
+ // Protect against background modifications to m_NotifyText.
+ AUTO_LOCK( g_AsyncNotifyTextMutex );
+
+ CNotifyText *current = NULL;
+
+ int slot = m_NotifyText.Count() - 1;
+ if ( slot < 0 )
+ {
+ slot = m_NotifyText.AddToTail();
+ current = &m_NotifyText[ slot ];
+ current->clr = clr;
+ current->text[ 0 ] = 0;
+ current->liferemaining = con_notifytime.GetFloat();;
+ }
+ else
+ {
+ current = &m_NotifyText[ slot ];
+ current->clr = clr;
+ }
+
+ Assert( current );
+
+ wchar_t unicode[ 1024 ];
+ g_pVGuiLocalize->ConvertANSIToUnicode( msg, unicode, sizeof( unicode ) );
+
+ wchar_t const *p = unicode;
+ while ( *p )
+ {
+ const wchar_t *nextreturn = wcsstr( p, L"\n" );
+ if ( nextreturn != NULL )
+ {
+ int copysize = nextreturn - p + 1;
+ safestrncat( current->text, MAX_NOTIFY_TEXT_LINE, p, copysize );
+
+ // Add a new notify, but don't add a new one if the previous one was empty...
+ if ( current->text[0] && current->text[0] != L'\n' )
+ {
+ slot = m_NotifyText.AddToTail();
+ current = &m_NotifyText[ slot ];
+ }
+ // Clear it
+ current->clr = clr;
+ current->text[ 0 ] = 0;
+ current->liferemaining = con_notifytime.GetFloat();
+ // Skip return character
+ p += copysize;
+ continue;
+ }
+
+ // Append it
+ safestrncat( current->text, MAX_NOTIFY_TEXT_LINE, p, wcslen( p ) );
+ current->clr = clr;
+ current->liferemaining = con_notifytime.GetFloat();
+ break;
+ }
+
+ while ( m_NotifyText.Count() > 0 &&
+ ( m_NotifyText.Count() >= con_times.GetInt() ) )
+ {
+ m_NotifyText.Remove( 0 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CConPanel::ClearNotify()
+{
+ // Protect against background modifications to m_NotifyText.
+ AUTO_LOCK( g_AsyncNotifyTextMutex );
+
+ m_NotifyText.RemoveAll();
+}
+
+void CConPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ // Console font
+ m_hFont = pScheme->GetFont( "DefaultSmallDropShadow", false );
+ m_hFontFixed = pScheme->GetFont( "DefaultFixedDropShadow", false );
+}
+
+int CConPanel::DrawText( vgui::HFont font, int x, int y, wchar_t *data )
+{
+ int len = DrawColoredText( font,
+ x,
+ y,
+ 255,
+ 255,
+ 255,
+ 255,
+ data );
+
+ return len;
+}
+
+
+//-----------------------------------------------------------------------------
+// called when we're ticked...
+//-----------------------------------------------------------------------------
+bool CConPanel::ShouldDraw()
+{
+ bool bVisible = false;
+
+ if ( m_bDrawDebugAreas )
+ {
+ bVisible = true;
+ }
+
+ // Should be invisible if there's no notifys and the console is up.
+ // and if the launcher isn't active
+ if ( !Con_IsVisible() )
+ {
+ // Protect against background modifications to m_NotifyText.
+ AUTO_LOCK( g_AsyncNotifyTextMutex );
+
+ int i;
+ int c = m_NotifyText.Count();
+ for ( i = c - 1; i >= 0; i-- )
+ {
+ CNotifyText *notify = &m_NotifyText[ i ];
+
+ notify->liferemaining -= host_frametime;
+
+ if ( notify->liferemaining <= 0.0f )
+ {
+ m_NotifyText.Remove( i );
+ continue;
+ }
+
+ bVisible = true;
+ }
+ }
+ else
+ {
+ bVisible = true;
+ }
+
+ return bVisible;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CConPanel::DrawNotify( void )
+{
+ int x = 8;
+ int y = 5;
+
+ if ( !m_hFontFixed )
+ return;
+
+ // notify area only draws in developer mode
+ if ( !developer.GetBool() )
+ return;
+
+ // don't render notify area into movies, either
+ if ( cl_movieinfo.IsRecording( ) )
+ {
+ return;
+ }
+
+ vgui::surface()->DrawSetTextFont( m_hFontFixed );
+
+ int fontTall = vgui::surface()->GetFontTall( m_hFontFixed ) + 1;
+
+ // Protect against background modifications to m_NotifyText.
+ // DEADLOCK WARNING: Cannot call DrawColoredText while holding g_AsyncNotifyTextMutex or
+ // deadlock can occur while MatQueue0 holds material system lock and attempts to add text
+ // to m_NotifyText.
+ CUtlVector< CNotifyText > textToDraw;
+ {
+ AUTO_LOCK( g_AsyncNotifyTextMutex );
+ textToDraw = m_NotifyText;
+ }
+
+ int c = textToDraw.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ CNotifyText *notify = &textToDraw[ i ];
+
+ float timeleft = notify->liferemaining;
+
+ Color clr = notify->clr;
+
+ if ( timeleft < .5f )
+ {
+ float f = clamp( timeleft, 0.0f, .5f ) / .5f;
+
+ clr[3] = (int)( f * 255.0f );
+
+ if ( i == 0 && f < 0.2f )
+ {
+ y -= fontTall * ( 1.0f - f / 0.2f );
+ }
+ }
+ else
+ {
+ clr[3] = 255;
+ }
+
+ DrawColoredText( m_hFontFixed, x, y, clr[0], clr[1], clr[2], clr[3], notify->text );
+
+ y += fontTall;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ConVar con_nprint_bgalpha( "con_nprint_bgalpha", "50", 0, "Con_NPrint background alpha." );
+ConVar con_nprint_bgborder( "con_nprint_bgborder", "5", 0, "Con_NPrint border size." );
+
+void CConPanel::DrawDebugAreas( void )
+{
+ if ( !m_bDrawDebugAreas )
+ return;
+
+ // Find the top and bottom of all the nprint text so we can draw a box behind it.
+ int left=99999, top=99999, right=-99999, bottom=-99999;
+ if ( con_nprint_bgalpha.GetInt() )
+ {
+ // First, figure out the bounds of all the con_nprint text.
+ if ( ProcessNotifyLines( left, top, right, bottom, false ) )
+ {
+ int b = con_nprint_bgborder.GetInt();
+
+ // Now draw a box behind it.
+ vgui::surface()->DrawSetColor( 0, 0, 0, con_nprint_bgalpha.GetInt() );
+ vgui::surface()->DrawFilledRect( left-b, top-b, right+b, bottom+b );
+ }
+ }
+
+ // Now draw the text.
+ if ( ProcessNotifyLines( left, top, right, bottom, true ) == 0 )
+ {
+ // Have all notifies expired?
+ m_bDrawDebugAreas = false;
+ }
+}
+
+int CConPanel::ProcessNotifyLines( int &left, int &top, int &right, int &bottom, bool bDraw )
+{
+ int count = 0;
+ int y = 20;
+
+ for ( int i = 0; i < MAX_DBG_NOTIFY; i++ )
+ {
+ if ( realtime < da_notify[i].expire || da_notify[i].expire == -1 )
+ {
+ // If it's marked this way, only draw it once.
+ if ( da_notify[i].expire == -1 && bDraw )
+ {
+ da_notify[i].expire = realtime - 1;
+ }
+
+ int len;
+ int x;
+
+ vgui::HFont font = da_notify[i].fixed_width_font ? m_hFontFixed : m_hFont ;
+
+ int fontTall = vgui::surface()->GetFontTall( m_hFontFixed ) + 1;
+
+ len = DrawTextLen( font, da_notify[i].szNotify );
+ x = videomode->GetModeStereoWidth() - 10 - len;
+
+ if ( y + fontTall > videomode->GetModeStereoHeight() - 20 )
+ return count;
+
+ count++;
+ y = 20 + 10 * i;
+
+ if ( bDraw )
+ {
+ DrawColoredText( font, x, y,
+ da_notify[i].color[0] * 255,
+ da_notify[i].color[1] * 255,
+ da_notify[i].color[2] * 255,
+ 255,
+ da_notify[i].szNotify );
+ }
+
+ if ( da_notify[i].szNotify[0] )
+ {
+ // Extend the bounds.
+ left = min( left, x );
+ top = min( top, y );
+ right = max( right, x+len );
+ bottom = max( bottom, y+fontTall );
+ }
+
+ y += fontTall;
+ }
+ }
+
+ return count;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CConPanel::Paint()
+{
+ VPROF( "CConPanel::Paint" );
+
+#if !defined( SWDS ) && !defined( DEDICATED )
+ if ( IsPC() && !g_ClientDLL->ShouldDrawDropdownConsole() )
+ return;
+#endif
+
+ DrawDebugAreas();
+
+ DrawNotify(); // only draw notify in game
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CConPanel::PaintBackground()
+{
+ if ( !Con_IsVisible() )
+ return;
+
+ int wide = GetWide();
+ char ver[ 100 ];
+ Q_snprintf(ver, sizeof( ver ), "Source Engine %i (build %d)", PROTOCOL_VERSION, build_number() );
+ wchar_t unicode[ 200 ];
+ g_pVGuiLocalize->ConvertANSIToUnicode( ver, unicode, sizeof( unicode ) );
+
+ vgui::surface()->DrawSetTextColor( Color( 255, 255, 255, 255 ) );
+ int x = wide - DrawTextLen( m_hFont, unicode ) - 2;
+ DrawText( m_hFont, x, 0, unicode );
+
+ if ( cl.IsActive() )
+ {
+ if ( cl.m_NetChannel->IsLoopback() )
+ {
+ Q_snprintf(ver, sizeof( ver ), "Map '%s'", cl.m_szLevelBaseName );
+ }
+ else
+ {
+ Q_snprintf(ver, sizeof( ver ), "Server '%s' Map '%s'", cl.m_NetChannel->GetRemoteAddress().ToString(), cl.m_szLevelBaseName );
+ }
+ wchar_t wUnicode[ 200 ];
+ g_pVGuiLocalize->ConvertANSIToUnicode( ver, wUnicode, sizeof( wUnicode ) );
+
+ int tall = vgui::surface()->GetFontTall( m_hFont );
+
+ x = wide - DrawTextLen( m_hFont, wUnicode ) - 2;
+ DrawText( m_hFont, x, tall + 1, wUnicode );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates the Console VGUI object
+//-----------------------------------------------------------------------------
+static CConPanel *conPanel = NULL;
+
+void Con_CreateConsolePanel( vgui::Panel *parent )
+{
+ conPanel = new CConPanel( parent );
+ if (conPanel)
+ {
+ conPanel->SetVisible(false);
+ }
+}
+
+vgui::Panel* Con_GetConsolePanel()
+{
+ return conPanel;
+}
+
+static ConCommand toggleconsole("toggleconsole", Con_ToggleConsole_f, "Show/hide the console.", FCVAR_DONTRECORD );
+static ConCommand hideconsole("hideconsole", Con_HideConsole_f, "Hide the console.", FCVAR_DONTRECORD );
+static ConCommand showconsole("showconsole", Con_ShowConsole_f, "Show the console.", FCVAR_DONTRECORD );
+static ConCommand clear("clear", Con_Clear_f, "Clear all console output.", FCVAR_DONTRECORD );
+
+#endif // SWDS