Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1330 lines
31 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=====================================================================================//
  6. #include "client_pch.h"
  7. #include <time.h>
  8. #include "console.h"
  9. #include "ivideomode.h"
  10. #include "zone.h"
  11. #include "sv_main.h"
  12. #include "server.h"
  13. #include "MapReslistGenerator.h"
  14. #include "tier0/vcrmode.h"
  15. #if defined( _X360 )
  16. #include "xbox/xbox_console.h"
  17. #endif
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. #if !defined( _X360 )
  21. #define MAXPRINTMSG 4096
  22. #else
  23. #define MAXPRINTMSG 1024
  24. #endif
  25. bool con_debuglog = false;
  26. bool con_initialized = false;
  27. bool con_debuglogmapprefixed = false;
  28. CThreadFastMutex g_AsyncNotifyTextMutex;
  29. static ConVar con_timestamp( "con_timestamp", "0", 0, "Prefix console.log entries with timestamps" );
  30. // In order to avoid excessive opening and closing of the console log file
  31. // we wrap it in an object and keep the handle open. This is necessary
  32. // because of the sometimes considerable cost of opening and closing files
  33. // on Windows. Opening and closing files on Windows is always moderately
  34. // expensive, but profiling may dramatically underestimate the true cost
  35. // because some anti-virus software can make closing a file handle take
  36. // 20-90 ms!
  37. class ConsoleLogManager
  38. {
  39. public:
  40. ConsoleLogManager();
  41. ~ConsoleLogManager();
  42. void RemoveConsoleLogFile();
  43. bool ReadConsoleLogFile( CUtlBuffer& buf );
  44. FileHandle_t GetConsoleLogFileHandleForAppend();
  45. void CloseFileIfOpen();
  46. private:
  47. FileHandle_t m_fh;
  48. const char *GetConsoleLogFilename() const;
  49. };
  50. // Wrap the ConsoleLogManager in a function to ensure that the object is always
  51. // constructed before it is used.
  52. ConsoleLogManager& GetConsoleLogManager()
  53. {
  54. static ConsoleLogManager object;
  55. return object;
  56. }
  57. void ConsoleLogFileCallback( IConVar *var, const char *pOldValue, float flOldValue )
  58. {
  59. ConVarRef con_logfile( var->GetName() );
  60. const char *logFile = con_logfile.GetString();
  61. // close any existing file, because we have changed the name
  62. GetConsoleLogManager().CloseFileIfOpen();
  63. // validate the path and the .log/.txt extensions
  64. if ( !COM_IsValidPath( logFile ) || !COM_IsValidLogFilename( logFile ) )
  65. {
  66. ConMsg( "invalid log filename\n" );
  67. con_logfile.SetValue( "console.log" );
  68. return;
  69. }
  70. else
  71. {
  72. const char *extension = Q_GetFileExtension( logFile );
  73. if ( !extension || ( Q_strcasecmp( extension, "log" ) && Q_strcasecmp( extension, "txt" ) ) )
  74. {
  75. char szTemp[MAX_PATH];
  76. V_sprintf_safe( szTemp, "%s.log", logFile );
  77. con_logfile.SetValue( szTemp );
  78. return;
  79. }
  80. }
  81. if ( !COM_IsValidPath( logFile ) )
  82. {
  83. con_debuglog = CommandLine()->FindParm( "-condebug" ) != 0;
  84. }
  85. else
  86. {
  87. con_debuglog = true;
  88. }
  89. }
  90. ConVar con_logfile( "con_logfile", "", 0, "Console output gets written to this file", false, 0.0f, false, 0.0f, ConsoleLogFileCallback );
  91. static const char *GetTimestampString( void )
  92. {
  93. static char string[128];
  94. tm today;
  95. VCRHook_LocalTime( &today );
  96. Q_snprintf( string, sizeof( string ), "%02i/%02i/%04i - %02i:%02i:%02i",
  97. today.tm_mon+1, today.tm_mday, 1900 + today.tm_year,
  98. today.tm_hour, today.tm_min, today.tm_sec );
  99. return string;
  100. }
  101. #ifndef SWDS
  102. static ConVar con_trace( "con_trace", "0", FCVAR_MATERIAL_SYSTEM_THREAD, "Print console text to low level printout." );
  103. 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" );
  104. static ConVar con_times("contimes", "8", FCVAR_MATERIAL_SYSTEM_THREAD, "Number of console lines to overlay for debugging." );
  105. static ConVar con_drawnotify( "con_drawnotify", "1", 0, "Disables drawing of notification area (for taking screenshots)." );
  106. static ConVar con_enable("con_enable", "0", FCVAR_ARCHIVE, "Allows the console to be activated.");
  107. 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." );
  108. 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." );
  109. 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." );
  110. //-----------------------------------------------------------------------------
  111. // Purpose: Implements the console using VGUI
  112. //-----------------------------------------------------------------------------
  113. class CConPanel : public CBasePanel
  114. {
  115. typedef CBasePanel BaseClass;
  116. public:
  117. enum
  118. {
  119. MAX_NOTIFY_TEXT_LINE = 256
  120. };
  121. CConPanel( vgui::Panel *parent );
  122. virtual ~CConPanel( void );
  123. virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
  124. // Draws the text
  125. virtual void Paint();
  126. // Draws the background image
  127. virtual void PaintBackground();
  128. // Draw notify area
  129. virtual void DrawNotify( void );
  130. // Draws debug ( Con_NXPrintf ) areas
  131. virtual void DrawDebugAreas( void );
  132. int ProcessNotifyLines( int &left, int &top, int &right, int &bottom, bool bDraw );
  133. // Draw helpers
  134. virtual int DrawText( vgui::HFont font, int x, int y, wchar_t *data );
  135. virtual bool ShouldDraw( void );
  136. void Con_NPrintf( int idx, const char *msg );
  137. void Con_NXPrintf( const struct con_nprint_s *info, const char *msg );
  138. void AddToNotify( const Color& clr, char const *msg );
  139. void ClearNotify();
  140. private:
  141. // Console font
  142. vgui::HFont m_hFont;
  143. vgui::HFont m_hFontFixed;
  144. struct CNotifyText
  145. {
  146. Color clr;
  147. float liferemaining;
  148. wchar_t text[MAX_NOTIFY_TEXT_LINE];
  149. };
  150. CCopyableUtlVector< CNotifyText > m_NotifyText;
  151. enum
  152. {
  153. MAX_DBG_NOTIFY = 128,
  154. DBG_NOTIFY_TIMEOUT = 4,
  155. };
  156. float da_default_color[3];
  157. typedef struct
  158. {
  159. wchar_t szNotify[MAX_NOTIFY_TEXT_LINE];
  160. float expire;
  161. float color[3];
  162. bool fixed_width_font;
  163. } da_notify_t;
  164. da_notify_t da_notify[MAX_DBG_NOTIFY];
  165. bool m_bDrawDebugAreas;
  166. };
  167. static CConPanel *g_pConPanel = NULL;
  168. /*
  169. ================
  170. Con_HideConsole_f
  171. ================
  172. */
  173. void Con_HideConsole_f( void )
  174. {
  175. if ( IsX360() )
  176. return;
  177. if ( EngineVGui()->IsConsoleVisible() )
  178. {
  179. // hide the console
  180. EngineVGui()->HideConsole();
  181. }
  182. }
  183. /*
  184. ================
  185. Con_ShowConsole_f
  186. ================
  187. */
  188. void Con_ShowConsole_f( void )
  189. {
  190. if ( IsX360() )
  191. return;
  192. if ( vgui::input()->GetAppModalSurface() )
  193. {
  194. // If a dialog has modal, it probably has grabbed keyboard focus, so showing
  195. // the console would be a bad idea.
  196. return;
  197. }
  198. if ( !g_ClientDLL->ShouldAllowConsole() )
  199. return;
  200. // make sure we're allowed to see the console
  201. if ( con_enable.GetBool() || developer.GetInt() || CommandLine()->CheckParm("-console") || CommandLine()->CheckParm("-rpt") )
  202. {
  203. // show the console
  204. EngineVGui()->ShowConsole();
  205. // remove any loading screen
  206. SCR_EndLoadingPlaque();
  207. }
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Purpose: toggles the console
  211. //-----------------------------------------------------------------------------
  212. void Con_ToggleConsole_f( void )
  213. {
  214. if ( IsX360() )
  215. return;
  216. if (EngineVGui()->IsConsoleVisible())
  217. {
  218. Con_HideConsole_f();
  219. // If we hide the console, we also hide the game UI
  220. EngineVGui()->HideGameUI();
  221. }
  222. else
  223. {
  224. Con_ShowConsole_f();
  225. }
  226. }
  227. //-----------------------------------------------------------------------------
  228. // Purpose: Clears the console
  229. //-----------------------------------------------------------------------------
  230. void Con_Clear_f( void )
  231. {
  232. if ( IsX360() )
  233. return;
  234. EngineVGui()->ClearConsole();
  235. Con_ClearNotify();
  236. }
  237. /*
  238. ================
  239. Con_ClearNotify
  240. ================
  241. */
  242. void Con_ClearNotify (void)
  243. {
  244. if ( g_pConPanel )
  245. {
  246. g_pConPanel->ClearNotify();
  247. }
  248. }
  249. #endif // SWDS
  250. ConsoleLogManager::ConsoleLogManager()
  251. {
  252. m_fh = FILESYSTEM_INVALID_HANDLE;
  253. }
  254. ConsoleLogManager::~ConsoleLogManager()
  255. {
  256. // This fails because of destructor order problems. The file
  257. // system has already been shut down by the time this runs.
  258. // We'll have to count on the OS to close the file for us.
  259. //CloseFileIfOpen();
  260. }
  261. void ConsoleLogManager::RemoveConsoleLogFile()
  262. {
  263. // Make sure the log file is closed before we try deleting it.
  264. CloseFileIfOpen();
  265. g_pFileSystem->RemoveFile( GetConsoleLogFilename(), "GAME" );
  266. }
  267. bool ConsoleLogManager::ReadConsoleLogFile( CUtlBuffer& buf )
  268. {
  269. // Make sure the log file is closed before we try reading it.
  270. CloseFileIfOpen();
  271. const char *pLogFile = GetConsoleLogFilename();
  272. if ( g_pFullFileSystem->ReadFile( pLogFile, "GAME", buf ) )
  273. return true;
  274. return false;
  275. }
  276. FileHandle_t ConsoleLogManager::GetConsoleLogFileHandleForAppend()
  277. {
  278. if ( m_fh == FILESYSTEM_INVALID_HANDLE )
  279. {
  280. const char* file = GetConsoleLogFilename();
  281. m_fh = g_pFileSystem->Open( file, "a" );
  282. }
  283. return m_fh;
  284. }
  285. void ConsoleLogManager::CloseFileIfOpen()
  286. {
  287. if ( m_fh != FILESYSTEM_INVALID_HANDLE )
  288. {
  289. g_pFileSystem->Close( m_fh );
  290. m_fh = FILESYSTEM_INVALID_HANDLE;
  291. }
  292. }
  293. const char *ConsoleLogManager::GetConsoleLogFilename() const
  294. {
  295. const char *logFile = con_logfile.GetString();
  296. if ( !COM_IsValidPath( logFile ) || !COM_IsValidLogFilename( logFile ) )
  297. {
  298. return "console.log";
  299. }
  300. return logFile;
  301. }
  302. /*
  303. ================
  304. Con_Init
  305. ================
  306. */
  307. void Con_Init (void)
  308. {
  309. #ifdef DEDICATED
  310. con_debuglog = false; // the dedicated server's console will handle this
  311. con_debuglogmapprefixed = false;
  312. // Check -consolelog arg and set con_logfile if it's present. This gets some messages logged
  313. // that we would otherwise miss due to con_logfile being set in the .cfg file.
  314. const char *filename = NULL;
  315. if ( CommandLine()->CheckParm( "-consolelog", &filename ) && filename && filename[ 0 ] )
  316. {
  317. con_logfile.SetValue( filename );
  318. }
  319. #else
  320. bool bRPTClient = ( CommandLine()->FindParm( "-rpt" ) != 0 );
  321. con_debuglog = bRPTClient || ( CommandLine()->FindParm( "-condebug" ) != 0 );
  322. con_debuglogmapprefixed = CommandLine()->FindParm( "-makereslists" ) != 0;
  323. if ( con_debuglog )
  324. {
  325. con_logfile.SetValue( "console.log" );
  326. if ( bRPTClient || ( CommandLine()->FindParm( "-conclearlog" ) ) )
  327. {
  328. GetConsoleLogManager().RemoveConsoleLogFile();
  329. }
  330. }
  331. #endif // !DEDICATED
  332. con_initialized = true;
  333. }
  334. /*
  335. ================
  336. Con_Shutdown
  337. ================
  338. */
  339. void Con_Shutdown (void)
  340. {
  341. con_initialized = false;
  342. }
  343. /*
  344. ================
  345. Read the console log from disk and return it in 'buf'. Buf should come
  346. in as an empty TEXT_BUFFER CUtlBuffer.
  347. Returns true if the log file is successfully read.
  348. ================
  349. */
  350. bool GetConsoleLogFileData( CUtlBuffer& buf )
  351. {
  352. return GetConsoleLogManager().ReadConsoleLogFile( buf );
  353. }
  354. /*
  355. ================
  356. Con_DebugLog
  357. ================
  358. */
  359. void Con_DebugLog( const char *fmt, ...)
  360. {
  361. va_list argptr;
  362. char data[MAXPRINTMSG];
  363. va_start(argptr, fmt);
  364. Q_vsnprintf(data, sizeof(data), fmt, argptr);
  365. va_end(argptr);
  366. FileHandle_t fh = GetConsoleLogManager().GetConsoleLogFileHandleForAppend();
  367. if (fh != FILESYSTEM_INVALID_HANDLE )
  368. {
  369. if ( con_debuglogmapprefixed )
  370. {
  371. char const *prefix = MapReslistGenerator().LogPrefix();
  372. if ( prefix )
  373. {
  374. g_pFileSystem->Write( prefix, strlen(prefix), fh );
  375. }
  376. }
  377. if ( con_timestamp.GetBool() )
  378. {
  379. static bool needTimestamp = true; // Start the first line with a timestamp
  380. if ( needTimestamp )
  381. {
  382. const char *timestamp = GetTimestampString();
  383. g_pFileSystem->Write( timestamp, strlen( timestamp ), fh );
  384. g_pFileSystem->Write( ": ", 2, fh );
  385. }
  386. needTimestamp = V_stristr( data, "\n" ) != 0;
  387. }
  388. g_pFileSystem->Write( data, strlen(data), fh );
  389. // Now that we don't close the file we need to flush it in order
  390. // to make sure that the data makes it to the file system.
  391. g_pFileSystem->Flush( fh );
  392. }
  393. }
  394. static bool g_fIsDebugPrint = false;
  395. #ifndef SWDS
  396. /*
  397. ================
  398. Con_Printf
  399. Handles cursor positioning, line wrapping, etc
  400. ================
  401. */
  402. static bool g_fColorPrintf = false;
  403. static bool g_bInColorPrint = false;
  404. extern CThreadLocalInt<> g_bInSpew;
  405. void Con_Printf( const char *fmt, ... );
  406. extern ConVar spew_consolelog_to_debugstring;
  407. void Con_ColorPrint( const Color& clr, char const *msg )
  408. {
  409. if ( IsPC() )
  410. {
  411. if ( g_bInColorPrint )
  412. return;
  413. int nCon_Filter_Enable = con_filter_enable.GetInt();
  414. if ( nCon_Filter_Enable > 0 )
  415. {
  416. const char *pszText = con_filter_text.GetString();
  417. const char *pszIgnoreText = con_filter_text_out.GetString();
  418. switch( nCon_Filter_Enable )
  419. {
  420. case 1:
  421. // if line does not contain keyword do not print the line
  422. if ( pszText && ( *pszText != '\0' ) && ( Q_stristr( msg, pszText ) == NULL ))
  423. return;
  424. if ( pszIgnoreText && *pszIgnoreText && ( Q_stristr( msg, pszIgnoreText ) != NULL ) )
  425. return;
  426. break;
  427. case 2:
  428. if ( pszIgnoreText && *pszIgnoreText && ( Q_stristr( msg, pszIgnoreText ) != NULL ) )
  429. return;
  430. // if line does not contain keyword print it in a darker color
  431. if ( pszText && ( *pszText != '\0' ) && ( Q_stristr( msg, pszText ) == NULL ))
  432. {
  433. Color mycolor(200, 200, 200, 150 );
  434. g_pCVar->ConsoleColorPrintf( mycolor, "%s", msg );
  435. return;
  436. }
  437. break;
  438. default:
  439. // by default do no filtering
  440. break;
  441. }
  442. }
  443. g_bInColorPrint = true;
  444. // also echo to debugging console
  445. if ( Plat_IsInDebugSession() && !con_trace.GetInt() && !spew_consolelog_to_debugstring.GetBool() )
  446. {
  447. Sys_OutputDebugString(msg);
  448. }
  449. if ( sv.IsDedicated() )
  450. {
  451. g_bInColorPrint = false;
  452. return; // no graphics mode
  453. }
  454. bool convisible = Con_IsVisible();
  455. bool indeveloper = ( developer.GetInt() > 0 );
  456. bool debugprint = g_fIsDebugPrint;
  457. if ( g_fColorPrintf )
  458. {
  459. g_pCVar->ConsoleColorPrintf( clr, "%s", msg );
  460. }
  461. else
  462. {
  463. // write it out to the vgui console no matter what
  464. if ( g_fIsDebugPrint )
  465. {
  466. // Don't spew debug stuff to actual console once in game, unless console isn't up
  467. if ( !cl.IsActive() || !convisible )
  468. {
  469. g_pCVar->ConsoleDPrintf( "%s", msg );
  470. }
  471. }
  472. else
  473. {
  474. g_pCVar->ConsolePrintf( "%s", msg );
  475. }
  476. }
  477. // Make sure we "spew" if this wan't generated from the spew system
  478. if ( !g_bInSpew )
  479. {
  480. Msg( "%s", msg );
  481. }
  482. // Only write to notify if it's non-debug or we are running with developer set > 0
  483. // Buf it it's debug then make sure we don't have the console down
  484. if ( ( !debugprint || indeveloper ) && !( debugprint && convisible ) )
  485. {
  486. if ( g_pConPanel )
  487. {
  488. g_pConPanel->AddToNotify( clr, msg );
  489. }
  490. }
  491. g_bInColorPrint = false;
  492. }
  493. #if defined( _X360 )
  494. int r,g,b,a;
  495. char buffer[MAXPRINTMSG];
  496. const char *pFrom;
  497. char *pTo;
  498. clr.GetColor(r, g, b, a);
  499. // fixup percent printers
  500. pFrom = msg;
  501. pTo = buffer;
  502. while ( *pFrom && pTo < buffer+sizeof(buffer)-1 )
  503. {
  504. *pTo = *pFrom++;
  505. if ( *pTo++ == '%' )
  506. *pTo++ = '%';
  507. }
  508. *pTo = '\0';
  509. XBX_DebugString( XMAKECOLOR(r,g,b), buffer );
  510. #endif
  511. }
  512. #endif
  513. // returns false if the print function shouldn't continue
  514. bool HandleRedirectAndDebugLog( const char *msg )
  515. {
  516. // Add to redirected message
  517. if ( SV_RedirectActive() )
  518. {
  519. SV_RedirectAddText( msg );
  520. return false;
  521. }
  522. // log all messages to file
  523. if ( con_debuglog )
  524. Con_DebugLog( "%s", msg );
  525. if (!con_initialized)
  526. {
  527. return false;
  528. }
  529. return true;
  530. }
  531. void Con_Print( const char *msg )
  532. {
  533. if ( !msg || !msg[0] )
  534. return;
  535. if ( !HandleRedirectAndDebugLog( msg ) )
  536. {
  537. return;
  538. }
  539. #ifdef SWDS
  540. Msg( "%s", msg );
  541. #else
  542. if ( sv.IsDedicated() )
  543. {
  544. Msg( "%s", msg );
  545. }
  546. else
  547. {
  548. #if !defined( _X360 )
  549. Color clr( 255, 255, 255, 255 );
  550. #else
  551. Color clr( 0, 0, 0, 255 );
  552. #endif
  553. Con_ColorPrint( clr, msg );
  554. }
  555. #endif
  556. }
  557. void Con_Printf( const char *fmt, ... )
  558. {
  559. va_list argptr;
  560. char msg[MAXPRINTMSG];
  561. static bool inupdate;
  562. va_start( argptr, fmt );
  563. Q_vsnprintf( msg, sizeof( msg ), fmt, argptr );
  564. va_end( argptr );
  565. #ifndef NO_VCR
  566. // Normally, we shouldn't need to write this data to the file, but it can help catch
  567. // out-of-sync errors earlier.
  568. if ( vcr_verbose.GetInt() )
  569. {
  570. VCRGenericString( "Con_Printf", msg );
  571. }
  572. #endif
  573. if ( !HandleRedirectAndDebugLog( msg ) )
  574. {
  575. return;
  576. }
  577. #ifdef SWDS
  578. Msg( "%s", msg );
  579. #else
  580. if ( sv.IsDedicated() )
  581. {
  582. Msg( "%s", msg );
  583. }
  584. else
  585. {
  586. #if !defined( _X360 )
  587. Color clr( 255, 255, 255, 255 );
  588. #else
  589. Color clr( 0, 0, 0, 255 );
  590. #endif
  591. Con_ColorPrint( clr, msg );
  592. }
  593. #endif
  594. }
  595. #ifndef SWDS
  596. //-----------------------------------------------------------------------------
  597. // Purpose:
  598. // Input : clr -
  599. // *fmt -
  600. // ... -
  601. //-----------------------------------------------------------------------------
  602. void Con_ColorPrintf( const Color& clr, const char *fmt, ... )
  603. {
  604. va_list argptr;
  605. char msg[MAXPRINTMSG];
  606. va_start (argptr,fmt);
  607. Q_vsnprintf (msg,sizeof( msg ), fmt,argptr);
  608. va_end (argptr);
  609. AUTO_LOCK( g_AsyncNotifyTextMutex );
  610. if ( !HandleRedirectAndDebugLog( msg ) )
  611. {
  612. return;
  613. }
  614. g_fColorPrintf = true;
  615. Con_ColorPrint( clr, msg );
  616. g_fColorPrintf = false;
  617. }
  618. #endif
  619. /*
  620. ================
  621. Con_DPrintf
  622. A Con_Printf that only shows up if the "developer" cvar is set
  623. ================
  624. */
  625. void Con_DPrintf (const char *fmt, ...)
  626. {
  627. va_list argptr;
  628. char msg[MAXPRINTMSG];
  629. va_start (argptr,fmt);
  630. Q_vsnprintf(msg,sizeof( msg ), fmt,argptr);
  631. va_end (argptr);
  632. g_fIsDebugPrint = true;
  633. #ifdef SWDS
  634. DevMsg( "%s", msg );
  635. #else
  636. if ( sv.IsDedicated() )
  637. {
  638. DevMsg( "%s", msg );
  639. }
  640. else
  641. {
  642. Color clr( 196, 181, 80, 255 );
  643. Con_ColorPrint ( clr, msg );
  644. }
  645. #endif
  646. g_fIsDebugPrint = false;
  647. }
  648. /*
  649. ==================
  650. Con_SafePrintf
  651. Okay to call even when the screen can't be updated
  652. ==================
  653. */
  654. void Con_SafePrintf (const char *fmt, ...)
  655. {
  656. va_list argptr;
  657. char msg[MAXPRINTMSG];
  658. va_start (argptr,fmt);
  659. Q_vsnprintf(msg,sizeof( msg ), fmt,argptr);
  660. va_end (argptr);
  661. #ifndef SWDS
  662. bool temp;
  663. temp = scr_disabled_for_loading;
  664. scr_disabled_for_loading = true;
  665. #endif
  666. g_fIsDebugPrint = true;
  667. Con_Printf ("%s", msg);
  668. g_fIsDebugPrint = false;
  669. #ifndef SWDS
  670. scr_disabled_for_loading = temp;
  671. #endif
  672. }
  673. #ifndef SWDS
  674. bool Con_IsVisible()
  675. {
  676. return (EngineVGui()->IsConsoleVisible());
  677. }
  678. void Con_NPrintf( int idx, const char *fmt, ... )
  679. {
  680. va_list argptr;
  681. char outtext[MAXPRINTMSG];
  682. va_start(argptr, fmt);
  683. Q_vsnprintf( outtext, sizeof( outtext ), fmt, argptr);
  684. va_end(argptr);
  685. if ( IsPC() )
  686. {
  687. g_pConPanel->Con_NPrintf( idx, outtext );
  688. }
  689. else
  690. {
  691. Con_Printf( outtext );
  692. }
  693. }
  694. void Con_NXPrintf( const struct con_nprint_s *info, const char *fmt, ... )
  695. {
  696. va_list argptr;
  697. char outtext[MAXPRINTMSG];
  698. va_start(argptr, fmt);
  699. Q_vsnprintf( outtext, sizeof( outtext ), fmt, argptr);
  700. va_end(argptr);
  701. if ( IsPC() )
  702. {
  703. g_pConPanel->Con_NXPrintf( info, outtext );
  704. }
  705. else
  706. {
  707. Con_Printf( outtext );
  708. }
  709. }
  710. //-----------------------------------------------------------------------------
  711. // Purpose: Creates the console panel
  712. // Input : *parent -
  713. //-----------------------------------------------------------------------------
  714. CConPanel::CConPanel( vgui::Panel *parent ) : CBasePanel( parent, "CConPanel" )
  715. {
  716. // Full screen assumed
  717. SetSize( videomode->GetModeStereoWidth(), videomode->GetModeStereoHeight() );
  718. SetPos( 0, 0 );
  719. SetVisible( true );
  720. SetMouseInputEnabled( false );
  721. SetKeyBoardInputEnabled( false );
  722. da_default_color[0] = 1.0;
  723. da_default_color[1] = 1.0;
  724. da_default_color[2] = 1.0;
  725. m_bDrawDebugAreas = false;
  726. g_pConPanel = this;
  727. memset( da_notify, 0, sizeof(da_notify) );
  728. }
  729. //-----------------------------------------------------------------------------
  730. // Purpose:
  731. //-----------------------------------------------------------------------------
  732. CConPanel::~CConPanel( void )
  733. {
  734. }
  735. void CConPanel::Con_NPrintf( int idx, const char *msg )
  736. {
  737. if ( idx < 0 || idx >= MAX_DBG_NOTIFY )
  738. return;
  739. #ifdef WIN32
  740. _snwprintf( da_notify[idx].szNotify, sizeof( da_notify[idx].szNotify ) / sizeof( wchar_t ) - 1, L"%S", msg );
  741. #else
  742. _snwprintf( da_notify[idx].szNotify, sizeof( da_notify[idx].szNotify ) / sizeof( wchar_t ) - 1, L"%s", msg );
  743. #endif
  744. da_notify[idx].szNotify[ sizeof( da_notify[idx].szNotify ) / sizeof( wchar_t ) - 1 ] = L'\0';
  745. // Reset values
  746. da_notify[idx].expire = realtime + DBG_NOTIFY_TIMEOUT;
  747. VectorCopy( da_default_color, da_notify[idx].color );
  748. da_notify[idx].fixed_width_font = false;
  749. m_bDrawDebugAreas = true;
  750. }
  751. void CConPanel::Con_NXPrintf( const struct con_nprint_s *info, const char *msg )
  752. {
  753. if ( !info )
  754. return;
  755. if ( info->index < 0 || info->index >= MAX_DBG_NOTIFY )
  756. return;
  757. #ifdef WIN32
  758. _snwprintf( da_notify[info->index].szNotify, sizeof( da_notify[info->index].szNotify ) / sizeof( wchar_t ) - 1, L"%S", msg );
  759. #else
  760. _snwprintf( da_notify[info->index].szNotify, sizeof( da_notify[info->index].szNotify ) / sizeof( wchar_t ) - 1, L"%s", msg );
  761. #endif
  762. da_notify[info->index].szNotify[ sizeof( da_notify[info->index].szNotify ) / sizeof( wchar_t ) - 1 ] = L'\0';
  763. // Reset values
  764. if ( info->time_to_live == -1 )
  765. da_notify[ info->index ].expire = -1; // special marker means to just draw it once
  766. else
  767. da_notify[ info->index ].expire = realtime + info->time_to_live;
  768. VectorCopy( info->color, da_notify[ info->index ].color );
  769. da_notify[ info->index ].fixed_width_font = info->fixed_width_font;
  770. m_bDrawDebugAreas = true;
  771. }
  772. static void safestrncat( wchar_t *text, int maxCharactersWithNullTerminator, wchar_t const *add, int addchars )
  773. {
  774. int maxCharactersWithoutTerminator = maxCharactersWithNullTerminator - 1;
  775. int curlen = wcslen( text );
  776. if ( curlen >= maxCharactersWithoutTerminator )
  777. return;
  778. wchar_t *p = text + curlen;
  779. while ( curlen++ < maxCharactersWithoutTerminator &&
  780. --addchars >= 0 )
  781. {
  782. *p++ = *add++;
  783. }
  784. *p = 0;
  785. }
  786. void CConPanel::AddToNotify( const Color& clr, char const *msg )
  787. {
  788. if ( !host_initialized )
  789. return;
  790. // notify area only ever draws in developer mode - it should never be used for game messages
  791. if ( !developer.GetBool() )
  792. return;
  793. // skip any special characters
  794. if ( msg[0] == 1 ||
  795. msg[0] == 2 )
  796. {
  797. msg++;
  798. }
  799. // Nothing left
  800. if ( !msg[0] )
  801. return;
  802. // Protect against background modifications to m_NotifyText.
  803. AUTO_LOCK( g_AsyncNotifyTextMutex );
  804. CNotifyText *current = NULL;
  805. int slot = m_NotifyText.Count() - 1;
  806. if ( slot < 0 )
  807. {
  808. slot = m_NotifyText.AddToTail();
  809. current = &m_NotifyText[ slot ];
  810. current->clr = clr;
  811. current->text[ 0 ] = 0;
  812. current->liferemaining = con_notifytime.GetFloat();;
  813. }
  814. else
  815. {
  816. current = &m_NotifyText[ slot ];
  817. current->clr = clr;
  818. }
  819. Assert( current );
  820. wchar_t unicode[ 1024 ];
  821. g_pVGuiLocalize->ConvertANSIToUnicode( msg, unicode, sizeof( unicode ) );
  822. wchar_t const *p = unicode;
  823. while ( *p )
  824. {
  825. const wchar_t *nextreturn = wcsstr( p, L"\n" );
  826. if ( nextreturn != NULL )
  827. {
  828. int copysize = nextreturn - p + 1;
  829. safestrncat( current->text, MAX_NOTIFY_TEXT_LINE, p, copysize );
  830. // Add a new notify, but don't add a new one if the previous one was empty...
  831. if ( current->text[0] && current->text[0] != L'\n' )
  832. {
  833. slot = m_NotifyText.AddToTail();
  834. current = &m_NotifyText[ slot ];
  835. }
  836. // Clear it
  837. current->clr = clr;
  838. current->text[ 0 ] = 0;
  839. current->liferemaining = con_notifytime.GetFloat();
  840. // Skip return character
  841. p += copysize;
  842. continue;
  843. }
  844. // Append it
  845. safestrncat( current->text, MAX_NOTIFY_TEXT_LINE, p, wcslen( p ) );
  846. current->clr = clr;
  847. current->liferemaining = con_notifytime.GetFloat();
  848. break;
  849. }
  850. while ( m_NotifyText.Count() > 0 &&
  851. ( m_NotifyText.Count() >= con_times.GetInt() ) )
  852. {
  853. m_NotifyText.Remove( 0 );
  854. }
  855. }
  856. //-----------------------------------------------------------------------------
  857. // Purpose:
  858. //-----------------------------------------------------------------------------
  859. void CConPanel::ClearNotify()
  860. {
  861. // Protect against background modifications to m_NotifyText.
  862. AUTO_LOCK( g_AsyncNotifyTextMutex );
  863. m_NotifyText.RemoveAll();
  864. }
  865. void CConPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  866. {
  867. BaseClass::ApplySchemeSettings( pScheme );
  868. // Console font
  869. m_hFont = pScheme->GetFont( "DefaultSmallDropShadow", false );
  870. m_hFontFixed = pScheme->GetFont( "DefaultFixedDropShadow", false );
  871. }
  872. int CConPanel::DrawText( vgui::HFont font, int x, int y, wchar_t *data )
  873. {
  874. int len = DrawColoredText( font,
  875. x,
  876. y,
  877. 255,
  878. 255,
  879. 255,
  880. 255,
  881. data );
  882. return len;
  883. }
  884. //-----------------------------------------------------------------------------
  885. // called when we're ticked...
  886. //-----------------------------------------------------------------------------
  887. bool CConPanel::ShouldDraw()
  888. {
  889. bool bVisible = false;
  890. if ( m_bDrawDebugAreas )
  891. {
  892. bVisible = true;
  893. }
  894. // Should be invisible if there's no notifys and the console is up.
  895. // and if the launcher isn't active
  896. if ( !Con_IsVisible() )
  897. {
  898. // Protect against background modifications to m_NotifyText.
  899. AUTO_LOCK( g_AsyncNotifyTextMutex );
  900. int i;
  901. int c = m_NotifyText.Count();
  902. for ( i = c - 1; i >= 0; i-- )
  903. {
  904. CNotifyText *notify = &m_NotifyText[ i ];
  905. notify->liferemaining -= host_frametime;
  906. if ( notify->liferemaining <= 0.0f )
  907. {
  908. m_NotifyText.Remove( i );
  909. continue;
  910. }
  911. bVisible = true;
  912. }
  913. }
  914. else
  915. {
  916. bVisible = true;
  917. }
  918. return bVisible;
  919. }
  920. //-----------------------------------------------------------------------------
  921. // Purpose:
  922. //-----------------------------------------------------------------------------
  923. void CConPanel::DrawNotify( void )
  924. {
  925. int x = 8;
  926. int y = 5;
  927. if ( !m_hFontFixed )
  928. return;
  929. // notify area only draws in developer mode
  930. if ( !developer.GetBool() )
  931. return;
  932. // don't render notify area into movies, either
  933. if ( cl_movieinfo.IsRecording( ) )
  934. {
  935. return;
  936. }
  937. vgui::surface()->DrawSetTextFont( m_hFontFixed );
  938. int fontTall = vgui::surface()->GetFontTall( m_hFontFixed ) + 1;
  939. // Protect against background modifications to m_NotifyText.
  940. // DEADLOCK WARNING: Cannot call DrawColoredText while holding g_AsyncNotifyTextMutex or
  941. // deadlock can occur while MatQueue0 holds material system lock and attempts to add text
  942. // to m_NotifyText.
  943. CUtlVector< CNotifyText > textToDraw;
  944. {
  945. AUTO_LOCK( g_AsyncNotifyTextMutex );
  946. textToDraw = m_NotifyText;
  947. }
  948. int c = textToDraw.Count();
  949. for ( int i = 0; i < c; i++ )
  950. {
  951. CNotifyText *notify = &textToDraw[ i ];
  952. float timeleft = notify->liferemaining;
  953. Color clr = notify->clr;
  954. if ( timeleft < .5f )
  955. {
  956. float f = clamp( timeleft, 0.0f, .5f ) / .5f;
  957. clr[3] = (int)( f * 255.0f );
  958. if ( i == 0 && f < 0.2f )
  959. {
  960. y -= fontTall * ( 1.0f - f / 0.2f );
  961. }
  962. }
  963. else
  964. {
  965. clr[3] = 255;
  966. }
  967. DrawColoredText( m_hFontFixed, x, y, clr[0], clr[1], clr[2], clr[3], notify->text );
  968. y += fontTall;
  969. }
  970. }
  971. //-----------------------------------------------------------------------------
  972. // Purpose:
  973. //-----------------------------------------------------------------------------
  974. ConVar con_nprint_bgalpha( "con_nprint_bgalpha", "50", 0, "Con_NPrint background alpha." );
  975. ConVar con_nprint_bgborder( "con_nprint_bgborder", "5", 0, "Con_NPrint border size." );
  976. void CConPanel::DrawDebugAreas( void )
  977. {
  978. if ( !m_bDrawDebugAreas )
  979. return;
  980. // Find the top and bottom of all the nprint text so we can draw a box behind it.
  981. int left=99999, top=99999, right=-99999, bottom=-99999;
  982. if ( con_nprint_bgalpha.GetInt() )
  983. {
  984. // First, figure out the bounds of all the con_nprint text.
  985. if ( ProcessNotifyLines( left, top, right, bottom, false ) )
  986. {
  987. int b = con_nprint_bgborder.GetInt();
  988. // Now draw a box behind it.
  989. vgui::surface()->DrawSetColor( 0, 0, 0, con_nprint_bgalpha.GetInt() );
  990. vgui::surface()->DrawFilledRect( left-b, top-b, right+b, bottom+b );
  991. }
  992. }
  993. // Now draw the text.
  994. if ( ProcessNotifyLines( left, top, right, bottom, true ) == 0 )
  995. {
  996. // Have all notifies expired?
  997. m_bDrawDebugAreas = false;
  998. }
  999. }
  1000. int CConPanel::ProcessNotifyLines( int &left, int &top, int &right, int &bottom, bool bDraw )
  1001. {
  1002. int count = 0;
  1003. int y = 20;
  1004. for ( int i = 0; i < MAX_DBG_NOTIFY; i++ )
  1005. {
  1006. if ( realtime < da_notify[i].expire || da_notify[i].expire == -1 )
  1007. {
  1008. // If it's marked this way, only draw it once.
  1009. if ( da_notify[i].expire == -1 && bDraw )
  1010. {
  1011. da_notify[i].expire = realtime - 1;
  1012. }
  1013. int len;
  1014. int x;
  1015. vgui::HFont font = da_notify[i].fixed_width_font ? m_hFontFixed : m_hFont ;
  1016. int fontTall = vgui::surface()->GetFontTall( m_hFontFixed ) + 1;
  1017. len = DrawTextLen( font, da_notify[i].szNotify );
  1018. x = videomode->GetModeStereoWidth() - 10 - len;
  1019. if ( y + fontTall > videomode->GetModeStereoHeight() - 20 )
  1020. return count;
  1021. count++;
  1022. y = 20 + 10 * i;
  1023. if ( bDraw )
  1024. {
  1025. DrawColoredText( font, x, y,
  1026. da_notify[i].color[0] * 255,
  1027. da_notify[i].color[1] * 255,
  1028. da_notify[i].color[2] * 255,
  1029. 255,
  1030. da_notify[i].szNotify );
  1031. }
  1032. if ( da_notify[i].szNotify[0] )
  1033. {
  1034. // Extend the bounds.
  1035. left = min( left, x );
  1036. top = min( top, y );
  1037. right = max( right, x+len );
  1038. bottom = max( bottom, y+fontTall );
  1039. }
  1040. y += fontTall;
  1041. }
  1042. }
  1043. return count;
  1044. }
  1045. //-----------------------------------------------------------------------------
  1046. // Purpose:
  1047. //-----------------------------------------------------------------------------
  1048. void CConPanel::Paint()
  1049. {
  1050. VPROF( "CConPanel::Paint" );
  1051. #if !defined( SWDS ) && !defined( DEDICATED )
  1052. if ( IsPC() && !g_ClientDLL->ShouldDrawDropdownConsole() )
  1053. return;
  1054. #endif
  1055. DrawDebugAreas();
  1056. DrawNotify(); // only draw notify in game
  1057. }
  1058. //-----------------------------------------------------------------------------
  1059. // Purpose:
  1060. //-----------------------------------------------------------------------------
  1061. void CConPanel::PaintBackground()
  1062. {
  1063. if ( !Con_IsVisible() )
  1064. return;
  1065. int wide = GetWide();
  1066. char ver[ 100 ];
  1067. Q_snprintf(ver, sizeof( ver ), "Source Engine %i (build %d)", PROTOCOL_VERSION, build_number() );
  1068. wchar_t unicode[ 200 ];
  1069. g_pVGuiLocalize->ConvertANSIToUnicode( ver, unicode, sizeof( unicode ) );
  1070. vgui::surface()->DrawSetTextColor( Color( 255, 255, 255, 255 ) );
  1071. int x = wide - DrawTextLen( m_hFont, unicode ) - 2;
  1072. DrawText( m_hFont, x, 0, unicode );
  1073. if ( cl.IsActive() )
  1074. {
  1075. if ( cl.m_NetChannel->IsLoopback() )
  1076. {
  1077. Q_snprintf(ver, sizeof( ver ), "Map '%s'", cl.m_szLevelBaseName );
  1078. }
  1079. else
  1080. {
  1081. Q_snprintf(ver, sizeof( ver ), "Server '%s' Map '%s'", cl.m_NetChannel->GetRemoteAddress().ToString(), cl.m_szLevelBaseName );
  1082. }
  1083. wchar_t wUnicode[ 200 ];
  1084. g_pVGuiLocalize->ConvertANSIToUnicode( ver, wUnicode, sizeof( wUnicode ) );
  1085. int tall = vgui::surface()->GetFontTall( m_hFont );
  1086. x = wide - DrawTextLen( m_hFont, wUnicode ) - 2;
  1087. DrawText( m_hFont, x, tall + 1, wUnicode );
  1088. }
  1089. }
  1090. //-----------------------------------------------------------------------------
  1091. // Purpose: Creates the Console VGUI object
  1092. //-----------------------------------------------------------------------------
  1093. static CConPanel *conPanel = NULL;
  1094. void Con_CreateConsolePanel( vgui::Panel *parent )
  1095. {
  1096. conPanel = new CConPanel( parent );
  1097. if (conPanel)
  1098. {
  1099. conPanel->SetVisible(false);
  1100. }
  1101. }
  1102. vgui::Panel* Con_GetConsolePanel()
  1103. {
  1104. return conPanel;
  1105. }
  1106. static ConCommand toggleconsole("toggleconsole", Con_ToggleConsole_f, "Show/hide the console.", FCVAR_DONTRECORD );
  1107. static ConCommand hideconsole("hideconsole", Con_HideConsole_f, "Hide the console.", FCVAR_DONTRECORD );
  1108. static ConCommand showconsole("showconsole", Con_ShowConsole_f, "Show the console.", FCVAR_DONTRECORD );
  1109. static ConCommand clear("clear", Con_Clear_f, "Clear all console output.", FCVAR_DONTRECORD );
  1110. #endif // SWDS