2020-04-23 00:56:21 +08:00
//========= 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 ;
2022-06-05 06:44:42 +08:00
extern CTHREADLOCALINT g_bInSpew ;
2020-04-23 00:56:21 +08:00
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 )
{
2022-05-02 01:08:32 +08:00
g_pConPanel = NULL ;
2020-04-23 00:56:21 +08:00
}
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