DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Console.cpp (31914B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../idlib/precompiled.h"
     31 #include "ConsoleHistory.h"
     32 #include "../renderer/ResolutionScale.h"
     33 #include "Common_local.h"
     34 
     35 #define	CON_TEXTSIZE			0x30000
     36 #define	NUM_CON_TIMES			4
     37 #define CONSOLE_FIRSTREPEAT		200
     38 #define CONSOLE_REPEAT			100
     39 
     40 #define	COMMAND_HISTORY			64
     41 
     42 struct overlayText_t {
     43 	idStr			text;
     44 	justify_t		justify;
     45 	int				time;
     46 };
     47 
     48 // the console will query the cvar and command systems for
     49 // command completion information
     50 
     51 class idConsoleLocal : public idConsole {
     52 public:
     53 	virtual	void		Init();
     54 	virtual void		Shutdown();
     55 	virtual	bool		ProcessEvent( const sysEvent_t * event, bool forceAccept );
     56 	virtual	bool		Active();
     57 	virtual	void		ClearNotifyLines();
     58 	virtual	void		Close();
     59 	virtual	void		Print( const char *text );
     60 	virtual	void		Draw( bool forceFullScreen );
     61 
     62 	virtual void		PrintOverlay( idOverlayHandle &handle, justify_t justify, const char *text, ... );
     63 
     64 	virtual idDebugGraph *	CreateGraph( int numItems );
     65 	virtual void			DestroyGraph( idDebugGraph * graph );
     66 
     67 	void				Dump( const char *toFile );
     68 	void				Clear();
     69 
     70 private:
     71 	void				KeyDownEvent( int key );
     72 
     73 	void				Linefeed();
     74 
     75 	void				PageUp();
     76 	void				PageDown();
     77 	void				Top();
     78 	void				Bottom();
     79 
     80 	void				DrawInput();
     81 	void				DrawNotify();
     82 	void				DrawSolidConsole( float frac );
     83 
     84 	void				Scroll();
     85 	void				SetDisplayFraction( float frac );
     86 	void				UpdateDisplayFraction();
     87 
     88 	void				DrawTextLeftAlign( float x, float &y, const char *text, ... );
     89 	void				DrawTextRightAlign( float x, float &y, const char *text, ... );
     90 
     91 	float				DrawFPS( float y );
     92 	float				DrawMemoryUsage( float y );
     93 
     94 	void				DrawOverlayText( float & leftY, float & rightY, float & centerY );
     95 	void				DrawDebugGraphs();
     96 
     97 	//============================
     98 
     99 	// allow these constants to be adjusted for HMD
    100 	int					LOCALSAFE_LEFT;
    101 	int					LOCALSAFE_RIGHT;
    102 	int					LOCALSAFE_TOP;
    103 	int					LOCALSAFE_BOTTOM;
    104 	int					LOCALSAFE_WIDTH;
    105 	int					LOCALSAFE_HEIGHT;
    106 	int					LINE_WIDTH;
    107 	int					TOTAL_LINES;
    108 
    109 	bool				keyCatching;
    110 
    111 	short				text[CON_TEXTSIZE];
    112 	int					current;		// line where next message will be printed
    113 	int					x;				// offset in current line for next print
    114 	int					display;		// bottom of console displays this line
    115 	int					lastKeyEvent;	// time of last key event for scroll delay
    116 	int					nextKeyEvent;	// keyboard repeat rate
    117 
    118 	float				displayFrac;	// approaches finalFrac at con_speed
    119 	float				finalFrac;		// 0.0 to 1.0 lines of console to display
    120 	int					fracTime;		// time of last displayFrac update
    121 
    122 	int					vislines;		// in scanlines
    123 
    124 	int					times[NUM_CON_TIMES];	// cls.realtime time the line was generated
    125 									// for transparent notify lines
    126 	idVec4				color;
    127 
    128 	idEditField			historyEditLines[COMMAND_HISTORY];
    129 
    130 	int					nextHistoryLine;// the last line in the history buffer, not masked
    131 	int					historyLine;	// the line being displayed from history buffer
    132 									// will be <= nextHistoryLine
    133 
    134 	idEditField			consoleField;
    135 
    136 	idList< overlayText_t >	overlayText;
    137 	idList< idDebugGraph *> debugGraphs;
    138 
    139 	static idCVar		con_speed;
    140 	static idCVar		con_notifyTime;
    141 	static idCVar		con_noPrint;
    142 };
    143 
    144 static idConsoleLocal localConsole;
    145 idConsole * console = &localConsole;
    146 
    147 idCVar idConsoleLocal::con_speed( "con_speed", "3", CVAR_SYSTEM, "speed at which the console moves up and down" );
    148 idCVar idConsoleLocal::con_notifyTime( "con_notifyTime", "3", CVAR_SYSTEM, "time messages are displayed onscreen when console is pulled up" );
    149 #ifdef DEBUG
    150 idCVar idConsoleLocal::con_noPrint( "con_noPrint", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "print on the console but not onscreen when console is pulled up" );
    151 #else
    152 idCVar idConsoleLocal::con_noPrint( "con_noPrint", "1", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "print on the console but not onscreen when console is pulled up" );
    153 #endif
    154 
    155 /*
    156 =============================================================================
    157 
    158 	Misc stats
    159 
    160 =============================================================================
    161 */
    162 
    163 /*
    164 ==================
    165 idConsoleLocal::DrawTextLeftAlign
    166 ==================
    167 */
    168 void idConsoleLocal::DrawTextLeftAlign( float x, float &y, const char *text, ... ) {
    169 	char string[MAX_STRING_CHARS];
    170 	va_list argptr;
    171 	va_start( argptr, text );
    172 	idStr::vsnPrintf( string, sizeof( string ), text, argptr );
    173 	va_end( argptr );
    174 	renderSystem->DrawSmallStringExt( x, y + 2, string, colorWhite, true );
    175 	y += SMALLCHAR_HEIGHT + 4;
    176 }
    177 
    178 /*
    179 ==================
    180 idConsoleLocal::DrawTextRightAlign
    181 ==================
    182 */
    183 void idConsoleLocal::DrawTextRightAlign( float x, float &y, const char *text, ... ) {
    184 	char string[MAX_STRING_CHARS];
    185 	va_list argptr;
    186 	va_start( argptr, text );
    187 	int i = idStr::vsnPrintf( string, sizeof( string ), text, argptr );
    188 	va_end( argptr );
    189 	renderSystem->DrawSmallStringExt( x - i * SMALLCHAR_WIDTH, y + 2, string, colorWhite, true );
    190 	y += SMALLCHAR_HEIGHT + 4;
    191 }
    192 
    193 
    194 
    195 
    196 /*
    197 ==================
    198 idConsoleLocal::DrawFPS
    199 ==================
    200 */
    201 #define	FPS_FRAMES	6
    202 float idConsoleLocal::DrawFPS( float y ) {
    203 	static int previousTimes[FPS_FRAMES];
    204 	static int index;
    205 	static int previous;
    206 
    207 	// don't use serverTime, because that will be drifting to
    208 	// correct for internet lag changes, timescales, timedemos, etc
    209 	int t = Sys_Milliseconds();
    210 	int frameTime = t - previous;
    211 	previous = t;
    212 
    213 	previousTimes[index % FPS_FRAMES] = frameTime;
    214 	index++;
    215 	if ( index > FPS_FRAMES ) {
    216 		// average multiple frames together to smooth changes out a bit
    217 		int total = 0;
    218 		for ( int i = 0 ; i < FPS_FRAMES ; i++ ) {
    219 			total += previousTimes[i];
    220 		}
    221 		if ( !total ) {
    222 			total = 1;
    223 		}
    224 		int fps = 1000000 * FPS_FRAMES / total;
    225 		fps = ( fps + 500 ) / 1000;
    226 
    227 		const char * s = va( "%ifps", fps );
    228 		int w = strlen( s ) * BIGCHAR_WIDTH;
    229 
    230 		renderSystem->DrawBigStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, s, colorWhite, true );
    231 	}
    232 
    233 	y += BIGCHAR_HEIGHT + 4;
    234 
    235 	// print the resolution scale so we can tell when we are at reduced resolution
    236 	idStr resolutionText;
    237 	resolutionScale.GetConsoleText( resolutionText );
    238 	int w = resolutionText.Length() * BIGCHAR_WIDTH;
    239 	renderSystem->DrawBigStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, resolutionText.c_str(), colorWhite, true );
    240 
    241 	const int gameThreadTotalTime = commonLocal.GetGameThreadTotalTime();
    242 	const int gameThreadGameTime = commonLocal.GetGameThreadGameTime();
    243 	const int gameThreadRenderTime = commonLocal.GetGameThreadRenderTime();
    244 	const int rendererBackEndTime = commonLocal.GetRendererBackEndMicroseconds();
    245 	const int rendererShadowsTime = commonLocal.GetRendererShadowsMicroseconds();
    246 	const int rendererGPUIdleTime = commonLocal.GetRendererIdleMicroseconds();
    247 	const int rendererGPUTime = commonLocal.GetRendererGPUMicroseconds();
    248 	const int maxTime = 16;
    249 
    250 	y += SMALLCHAR_HEIGHT + 4;
    251 	idStr timeStr;
    252 	timeStr.Format( "%sG+RF: %4d", gameThreadTotalTime > maxTime ? S_COLOR_RED : "", gameThreadTotalTime );
    253 	w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH;
    254 	renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false );
    255 	y += SMALLCHAR_HEIGHT + 4;
    256 
    257 	timeStr.Format( "%sG: %4d", gameThreadGameTime > maxTime ? S_COLOR_RED : "", gameThreadGameTime );
    258 	w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH;
    259 	renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false );
    260 	y += SMALLCHAR_HEIGHT + 4;
    261 
    262 	timeStr.Format( "%sRF: %4d", gameThreadRenderTime > maxTime ? S_COLOR_RED : "", gameThreadRenderTime );
    263 	w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH;
    264 	renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false );
    265 	y += SMALLCHAR_HEIGHT + 4;
    266 
    267 	timeStr.Format( "%sRB: %4.1f", rendererBackEndTime > maxTime * 1000 ? S_COLOR_RED : "", rendererBackEndTime / 1000.0f );
    268 	w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH;
    269 	renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false );
    270 	y += SMALLCHAR_HEIGHT + 4;
    271 
    272 	timeStr.Format( "%sSV: %4.1f", rendererShadowsTime > maxTime * 1000 ? S_COLOR_RED : "", rendererShadowsTime / 1000.0f );
    273 	w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH;
    274 	renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false );
    275 	y += SMALLCHAR_HEIGHT + 4;
    276 
    277 	timeStr.Format( "%sIDLE: %4.1f", rendererGPUIdleTime > maxTime * 1000 ? S_COLOR_RED : "", rendererGPUIdleTime / 1000.0f );
    278 	w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH;
    279 	renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false );
    280 	y += SMALLCHAR_HEIGHT + 4;
    281 
    282 	timeStr.Format( "%sGPU: %4.1f", rendererGPUTime > maxTime * 1000 ? S_COLOR_RED : "", rendererGPUTime / 1000.0f );
    283 	w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH;
    284 	renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false );
    285 
    286 	return y + BIGCHAR_HEIGHT + 4;
    287 }
    288 
    289 /*
    290 ==================
    291 idConsoleLocal::DrawMemoryUsage
    292 ==================
    293 */
    294 float idConsoleLocal::DrawMemoryUsage( float y ) {
    295 	return y;
    296 }
    297 
    298 //=========================================================================
    299 
    300 /*
    301 ==============
    302 Con_Clear_f
    303 ==============
    304 */
    305 static void Con_Clear_f( const idCmdArgs &args ) {
    306 	localConsole.Clear();
    307 }
    308 
    309 /*
    310 ==============
    311 Con_Dump_f
    312 ==============
    313 */
    314 static void Con_Dump_f( const idCmdArgs &args ) {
    315 	if ( args.Argc() != 2 ) {
    316 		common->Printf( "usage: conDump <filename>\n" );
    317 		return;
    318 	}
    319 
    320 	idStr fileName = args.Argv(1);
    321 	fileName.DefaultFileExtension(".txt");
    322 
    323 	common->Printf( "Dumped console text to %s.\n", fileName.c_str() );
    324 
    325 	localConsole.Dump( fileName.c_str() );
    326 }
    327 
    328 /*
    329 ==============
    330 idConsoleLocal::Init
    331 ==============
    332 */
    333 void idConsoleLocal::Init() {
    334 	int		i;
    335 
    336 	keyCatching = false;
    337 
    338 	LOCALSAFE_LEFT		= 32;
    339 	LOCALSAFE_RIGHT		= 608;
    340 	LOCALSAFE_TOP		= 24;
    341 	LOCALSAFE_BOTTOM	= 456;
    342 	LOCALSAFE_WIDTH		= LOCALSAFE_RIGHT - LOCALSAFE_LEFT;
    343 	LOCALSAFE_HEIGHT	= LOCALSAFE_BOTTOM - LOCALSAFE_TOP;
    344 
    345 	LINE_WIDTH = ( ( LOCALSAFE_WIDTH / SMALLCHAR_WIDTH ) - 2 );
    346 	TOTAL_LINES = (CON_TEXTSIZE / LINE_WIDTH);
    347 
    348 	lastKeyEvent = -1;
    349 	nextKeyEvent = CONSOLE_FIRSTREPEAT;
    350 
    351 	consoleField.Clear();
    352 	consoleField.SetWidthInChars( LINE_WIDTH );
    353 
    354 	for ( i = 0 ; i < COMMAND_HISTORY ; i++ ) {
    355 		historyEditLines[i].Clear();
    356 		historyEditLines[i].SetWidthInChars( LINE_WIDTH );
    357 	}
    358 
    359 	cmdSystem->AddCommand( "clear", Con_Clear_f, CMD_FL_SYSTEM, "clears the console" );
    360 	cmdSystem->AddCommand( "conDump", Con_Dump_f, CMD_FL_SYSTEM, "dumps the console text to a file" );
    361 }
    362 
    363 /*
    364 ==============
    365 idConsoleLocal::Shutdown
    366 ==============
    367 */
    368 void idConsoleLocal::Shutdown() {
    369 	cmdSystem->RemoveCommand( "clear" );
    370 	cmdSystem->RemoveCommand( "conDump" );
    371 
    372 	debugGraphs.DeleteContents( true );
    373 }
    374 
    375 /*
    376 ================
    377 idConsoleLocal::Active
    378 ================
    379 */
    380 bool	idConsoleLocal::Active() {
    381 	return keyCatching;
    382 }
    383 
    384 /*
    385 ================
    386 idConsoleLocal::ClearNotifyLines
    387 ================
    388 */
    389 void	idConsoleLocal::ClearNotifyLines() {
    390 	int		i;
    391 
    392 	for ( i = 0 ; i < NUM_CON_TIMES ; i++ ) {
    393 		times[i] = 0;
    394 	}
    395 }
    396 
    397 /*
    398 ================
    399 idConsoleLocal::Close
    400 ================
    401 */
    402 void	idConsoleLocal::Close() {
    403 	keyCatching = false;
    404 	SetDisplayFraction( 0 );
    405 	displayFrac = 0;	// don't scroll to that point, go immediately
    406 	ClearNotifyLines();
    407 }
    408 
    409 /*
    410 ================
    411 idConsoleLocal::Clear
    412 ================
    413 */
    414 void idConsoleLocal::Clear() {
    415 	int		i;
    416 
    417 	for ( i = 0 ; i < CON_TEXTSIZE ; i++ ) {
    418 		text[i] = (idStr::ColorIndex(C_COLOR_CYAN)<<8) | ' ';
    419 	}
    420 
    421 	Bottom();		// go to end
    422 }
    423 
    424 /*
    425 ================
    426 idConsoleLocal::Dump
    427 
    428 Save the console contents out to a file
    429 ================
    430 */
    431 void idConsoleLocal::Dump( const char *fileName ) {
    432 	int		l, x, i;
    433 	short *	line;
    434 	idFile *f;
    435 	char	* buffer = (char *)alloca( LINE_WIDTH + 3 );
    436 
    437 	f = fileSystem->OpenFileWrite( fileName );
    438 	if ( !f ) {
    439 		common->Warning( "couldn't open %s", fileName );
    440 		return;
    441 	}
    442 
    443 	// skip empty lines
    444 	l = current - TOTAL_LINES + 1;
    445 	if ( l < 0 ) {
    446 		l = 0;
    447 	}
    448 	for ( ; l <= current ; l++ )
    449 	{
    450 		line = text + ( l % TOTAL_LINES ) * LINE_WIDTH;
    451 		for ( x = 0; x < LINE_WIDTH; x++ )
    452 			if ( ( line[x] & 0xff ) > ' ' )
    453 				break;
    454 		if ( x != LINE_WIDTH )
    455 			break;
    456 	}
    457 
    458 	// write the remaining lines
    459 	for ( ; l <= current; l++ ) {
    460 		line = text + ( l % TOTAL_LINES ) * LINE_WIDTH;
    461 		for( i = 0; i < LINE_WIDTH; i++ ) {
    462 			buffer[i] = line[i] & 0xff;
    463 		}
    464 		for ( x = LINE_WIDTH-1; x >= 0; x-- ) {
    465 			if ( buffer[x] <= ' ' ) {
    466 				buffer[x] = 0;
    467 			} else {
    468 				break;
    469 			}
    470 		}
    471 		buffer[x+1] = '\r';
    472 		buffer[x+2] = '\n';
    473 		buffer[x+3] = 0;
    474 		f->Write( buffer, strlen( buffer ) );
    475 	}
    476 
    477 	fileSystem->CloseFile( f );
    478 }
    479 
    480 /*
    481 ================
    482 idConsoleLocal::PageUp
    483 ================
    484 */
    485 void idConsoleLocal::PageUp() {
    486 	display -= 2;
    487 	if ( current - display >= TOTAL_LINES ) {
    488 		display = current - TOTAL_LINES + 1;
    489 	}
    490 }
    491 
    492 /*
    493 ================
    494 idConsoleLocal::PageDown
    495 ================
    496 */
    497 void idConsoleLocal::PageDown() {
    498 	display += 2;
    499 	if ( display > current ) {
    500 		display = current;
    501 	}
    502 }
    503 
    504 /*
    505 ================
    506 idConsoleLocal::Top
    507 ================
    508 */
    509 void idConsoleLocal::Top() {
    510 	display = 0;
    511 }
    512 
    513 /*
    514 ================
    515 idConsoleLocal::Bottom
    516 ================
    517 */
    518 void idConsoleLocal::Bottom() {
    519 	display = current;
    520 }
    521 
    522 
    523 /*
    524 =============================================================================
    525 
    526 CONSOLE LINE EDITING
    527 
    528 ==============================================================================
    529 */
    530 
    531 /*
    532 ====================
    533 KeyDownEvent
    534 
    535 Handles history and console scrollback
    536 ====================
    537 */
    538 void idConsoleLocal::KeyDownEvent( int key ) {
    539 	
    540 	// Execute F key bindings
    541 	if ( key >= K_F1 && key <= K_F12 ) {
    542 		idKeyInput::ExecKeyBinding( key );
    543 		return;
    544 	}
    545 
    546 	// ctrl-L clears screen
    547 	if ( key == K_L && ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) ) {
    548 		Clear();
    549 		return;
    550 	}
    551 
    552 	// enter finishes the line
    553 	if ( key == K_ENTER || key == K_KP_ENTER ) {
    554 
    555 		common->Printf ( "]%s\n", consoleField.GetBuffer() );
    556 
    557 		cmdSystem->BufferCommandText( CMD_EXEC_APPEND, consoleField.GetBuffer() );	// valid command
    558 		cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "\n" );
    559 
    560 		// copy line to history buffer
    561 
    562 		if ( consoleField.GetBuffer()[ 0 ] != '\n' && consoleField.GetBuffer()[ 0 ] != '\0' ) {
    563 			consoleHistory.AddToHistory( consoleField.GetBuffer() );
    564 		}
    565 
    566 		consoleField.Clear();
    567 		consoleField.SetWidthInChars( LINE_WIDTH );
    568 
    569 		const bool captureToImage = false;
    570 		common->UpdateScreen( captureToImage );// force an update, because the command
    571 								// may take some time
    572 		return;
    573 	}
    574 
    575 	// command completion
    576 
    577 	if ( key == K_TAB ) {
    578 		consoleField.AutoComplete();
    579 		return;
    580 	}
    581 
    582 	// command history (ctrl-p ctrl-n for unix style)
    583 
    584 	if ( ( key == K_UPARROW ) ||
    585 		 ( key == K_P && ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) ) ) {
    586 		idStr hist = consoleHistory.RetrieveFromHistory( true );
    587 		if ( !hist.IsEmpty() ) {
    588 			consoleField.SetBuffer( hist );
    589 		}
    590 		return;
    591 	}
    592 
    593 	if ( ( key == K_DOWNARROW ) ||
    594 		 ( key == K_N && ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) ) ) {
    595 		idStr hist = consoleHistory.RetrieveFromHistory( false );
    596 		if ( !hist.IsEmpty() ) {
    597 			consoleField.SetBuffer( hist );
    598 		}
    599 		return;
    600 	}
    601 
    602 	// console scrolling
    603 	if ( key == K_PGUP ) {
    604 		PageUp();
    605 		lastKeyEvent = eventLoop->Milliseconds();
    606 		nextKeyEvent = CONSOLE_FIRSTREPEAT;
    607 		return;
    608 	}
    609 
    610 	if ( key == K_PGDN ) {
    611 		PageDown();
    612 		lastKeyEvent = eventLoop->Milliseconds();
    613 		nextKeyEvent = CONSOLE_FIRSTREPEAT;
    614 		return;
    615 	}
    616 
    617 	if ( key == K_MWHEELUP ) {
    618 		PageUp();
    619 		return;
    620 	}
    621 
    622 	if ( key == K_MWHEELDOWN ) {
    623 		PageDown();
    624 		return;
    625 	}
    626 
    627 	// ctrl-home = top of console
    628 	if ( key == K_HOME && ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) ) {
    629 		Top();
    630 		return;
    631 	}
    632 
    633 	// ctrl-end = bottom of console
    634 	if ( key == K_END && ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) ) {
    635 		Bottom();
    636 		return;
    637 	}
    638 
    639 	// pass to the normal editline routine
    640 	consoleField.KeyDownEvent( key );
    641 }
    642 
    643 /*
    644 ==============
    645 Scroll
    646 deals with scrolling text because we don't have key repeat
    647 ==============
    648 */
    649 void idConsoleLocal::Scroll( ) {
    650 	if (lastKeyEvent == -1 || (lastKeyEvent+200) > eventLoop->Milliseconds()) {
    651 		return;
    652 	}
    653 	// console scrolling
    654 	if ( idKeyInput::IsDown( K_PGUP ) ) {
    655 		PageUp();
    656 		nextKeyEvent = CONSOLE_REPEAT;
    657 		return;
    658 	}
    659 
    660 	if ( idKeyInput::IsDown( K_PGDN ) ) {
    661 		PageDown();
    662 		nextKeyEvent = CONSOLE_REPEAT;
    663 		return;
    664 	}
    665 }
    666 
    667 /*
    668 ==============
    669 SetDisplayFraction
    670 
    671 Causes the console to start opening the desired amount.
    672 ==============
    673 */
    674 void idConsoleLocal::SetDisplayFraction( float frac ) {
    675 	finalFrac = frac;
    676 	fracTime = Sys_Milliseconds();
    677 }
    678 
    679 /*
    680 ==============
    681 UpdateDisplayFraction
    682 
    683 Scrolls the console up or down based on conspeed
    684 ==============
    685 */
    686 void idConsoleLocal::UpdateDisplayFraction() {
    687 	if ( con_speed.GetFloat() <= 0.1f ) {
    688 		fracTime = Sys_Milliseconds();
    689 		displayFrac = finalFrac;
    690 		return;
    691 	}
    692 
    693 	// scroll towards the destination height
    694 	if ( finalFrac < displayFrac ) {
    695 		displayFrac -= con_speed.GetFloat() * ( Sys_Milliseconds() - fracTime ) * 0.001f;
    696 		if ( finalFrac > displayFrac ) {
    697 			displayFrac = finalFrac;
    698 		}
    699 		fracTime = Sys_Milliseconds();
    700 	} else if ( finalFrac > displayFrac ) {
    701 		displayFrac += con_speed.GetFloat() * ( Sys_Milliseconds() - fracTime ) * 0.001f;
    702 		if ( finalFrac < displayFrac ) {
    703 			displayFrac = finalFrac;
    704 		}
    705 		fracTime = Sys_Milliseconds();
    706 	}
    707 }
    708 
    709 /*
    710 ==============
    711 ProcessEvent
    712 ==============
    713 */
    714 bool	idConsoleLocal::ProcessEvent( const sysEvent_t *event, bool forceAccept ) {
    715 	const bool consoleKey = event->evType == SE_KEY && event->evValue == K_GRAVE && com_allowConsole.GetBool();
    716 
    717 	// we always catch the console key event
    718 	if ( !forceAccept && consoleKey ) {
    719 		// ignore up events
    720 		if ( event->evValue2 == 0 ) {
    721 			return true;
    722 		}
    723 
    724 		consoleField.ClearAutoComplete();
    725 
    726 		// a down event will toggle the destination lines
    727 		if ( keyCatching ) {
    728 			Close();
    729 			Sys_GrabMouseCursor( true );
    730 		} else {
    731 			consoleField.Clear();
    732 			keyCatching = true;
    733 			if ( idKeyInput::IsDown( K_LSHIFT ) || idKeyInput::IsDown( K_RSHIFT ) ) {
    734 				// if the shift key is down, don't open the console as much
    735 				SetDisplayFraction( 0.2f );
    736 			} else {
    737 				SetDisplayFraction( 0.5f );
    738 			}
    739 		}
    740 		return true;
    741 	}
    742 
    743 	// if we aren't key catching, dump all the other events
    744 	if ( !forceAccept && !keyCatching ) {
    745 		return false;
    746 	}
    747 
    748 	// handle key and character events
    749 	if ( event->evType == SE_CHAR ) {
    750 		// never send the console key as a character
    751 		if ( event->evValue != '`' && event->evValue != '~' ) {
    752 			consoleField.CharEvent( event->evValue );
    753 		}
    754 		return true;
    755 	}
    756 
    757 	if ( event->evType == SE_KEY ) {
    758 		// ignore up key events
    759 		if ( event->evValue2 == 0 ) {
    760 			return true;
    761 		}
    762 
    763 		KeyDownEvent( event->evValue );
    764 		return true;
    765 	}
    766 
    767 	// we don't handle things like mouse, joystick, and network packets
    768 	return false;
    769 }
    770 
    771 /*
    772 ==============================================================================
    773 
    774 PRINTING
    775 
    776 ==============================================================================
    777 */
    778 
    779 /*
    780 ===============
    781 Linefeed
    782 ===============
    783 */
    784 void idConsoleLocal::Linefeed() {
    785 	int		i;
    786 
    787 	// mark time for transparent overlay
    788 	if ( current >= 0 ) {
    789 		times[current % NUM_CON_TIMES] = Sys_Milliseconds();
    790 	}
    791 
    792 	x = 0;
    793 	if ( display == current ) {
    794 		display++;
    795 	}
    796 	current++;
    797 	for ( i = 0; i < LINE_WIDTH; i++ ) {
    798 		int offset = ( (unsigned int)current % TOTAL_LINES ) * LINE_WIDTH + i;
    799 		text[offset] = (idStr::ColorIndex(C_COLOR_CYAN)<<8) | ' ';
    800 	}
    801 }
    802 
    803 
    804 /*
    805 ================
    806 Print
    807 
    808 Handles cursor positioning, line wrapping, etc
    809 ================
    810 */
    811 void idConsoleLocal::Print( const char *txt ) {
    812 	int		y;
    813 	int		c, l;
    814 	int		color;
    815 
    816 	if ( TOTAL_LINES == 0 ) {
    817 		// not yet initialized
    818 		return;
    819 	}
    820 
    821 	color = idStr::ColorIndex( C_COLOR_CYAN );
    822 
    823 	while ( (c = *(const unsigned char*)txt) != 0 ) {
    824 		if ( idStr::IsColor( txt ) ) {
    825 			if ( *(txt+1) == C_COLOR_DEFAULT ) {
    826 				color = idStr::ColorIndex( C_COLOR_CYAN );
    827 			} else {
    828 				color = idStr::ColorIndex( *(txt+1) );
    829 			}
    830 			txt += 2;
    831 			continue;
    832 		}
    833 
    834 		y = current % TOTAL_LINES;
    835 
    836 		// if we are about to print a new word, check to see
    837 		// if we should wrap to the new line
    838 		if ( c > ' ' && ( x == 0 || text[y*LINE_WIDTH+x-1] <= ' ' ) ) {
    839 			// count word length
    840 			for (l=0 ; l< LINE_WIDTH ; l++) {
    841 				if ( txt[l] <= ' ') {
    842 					break;
    843 				}
    844 			}
    845 
    846 			// word wrap
    847 			if (l != LINE_WIDTH && (x + l >= LINE_WIDTH) ) {
    848 				Linefeed();
    849 			}
    850 		}
    851 
    852 		txt++;
    853 
    854 		switch( c ) {
    855 			case '\n':
    856 				Linefeed ();
    857 				break;
    858 			case '\t':
    859 				do {
    860 					text[y*LINE_WIDTH+x] = (color << 8) | ' ';
    861 					x++;
    862 					if ( x >= LINE_WIDTH ) {
    863 						Linefeed();
    864 						x = 0;
    865 					}
    866 				} while ( x & 3 );
    867 				break;
    868 			case '\r':
    869 				x = 0;
    870 				break;
    871 			default:	// display character and advance
    872 				text[y*LINE_WIDTH+x] = (color << 8) | c;
    873 				x++;
    874 				if ( x >= LINE_WIDTH ) {
    875 					Linefeed();
    876 					x = 0;
    877 				}
    878 				break;
    879 		}
    880 	}
    881 
    882 
    883 	// mark time for transparent overlay
    884 	if ( current >= 0 ) {
    885 		times[current % NUM_CON_TIMES] = Sys_Milliseconds();
    886 	}
    887 }
    888 
    889 
    890 /*
    891 ==============================================================================
    892 
    893 DRAWING
    894 
    895 ==============================================================================
    896 */
    897 
    898 
    899 /*
    900 ================
    901 DrawInput
    902 
    903 Draw the editline after a ] prompt
    904 ================
    905 */
    906 void idConsoleLocal::DrawInput() {
    907 	int y, autoCompleteLength;
    908 
    909 	y = vislines - ( SMALLCHAR_HEIGHT * 2 );
    910 
    911 	if ( consoleField.GetAutoCompleteLength() != 0 ) {
    912 		autoCompleteLength = strlen( consoleField.GetBuffer() ) - consoleField.GetAutoCompleteLength();
    913 
    914 		if ( autoCompleteLength > 0 ) {
    915 			renderSystem->DrawFilled( idVec4( 0.8f, 0.2f, 0.2f, 0.45f ),
    916 				LOCALSAFE_LEFT + 2 * SMALLCHAR_WIDTH + consoleField.GetAutoCompleteLength() * SMALLCHAR_WIDTH,
    917 				y + 2, autoCompleteLength * SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT - 2 );
    918 		}
    919 	}
    920 
    921 	renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) );
    922 
    923 	renderSystem->DrawSmallChar( LOCALSAFE_LEFT + 1 * SMALLCHAR_WIDTH, y, ']' );
    924 
    925 	consoleField.Draw( LOCALSAFE_LEFT + 2 * SMALLCHAR_WIDTH, y, SCREEN_WIDTH - 3 * SMALLCHAR_WIDTH, true );
    926 }
    927 
    928 
    929 /*
    930 ================
    931 DrawNotify
    932 
    933 Draws the last few lines of output transparently over the game top
    934 ================
    935 */
    936 void idConsoleLocal::DrawNotify() {
    937 	int		x, v;
    938 	short	*text_p;
    939 	int		i;
    940 	int		time;
    941 	int		currentColor;
    942 
    943 	if ( con_noPrint.GetBool() ) {
    944 		return;
    945 	}
    946 
    947 	currentColor = idStr::ColorIndex( C_COLOR_WHITE );
    948 	renderSystem->SetColor( idStr::ColorForIndex( currentColor ) );
    949 
    950 	v = 0;
    951 	for ( i = current-NUM_CON_TIMES+1; i <= current; i++ ) {
    952 		if ( i < 0 ) {
    953 			continue;
    954 		}
    955 		time = times[i % NUM_CON_TIMES];
    956 		if ( time == 0 ) {
    957 			continue;
    958 		}
    959 		time = Sys_Milliseconds() - time;
    960 		if ( time > con_notifyTime.GetFloat() * 1000 ) {
    961 			continue;
    962 		}
    963 		text_p = text + (i % TOTAL_LINES)*LINE_WIDTH;
    964 		
    965 		for ( x = 0; x < LINE_WIDTH; x++ ) {
    966 			if ( ( text_p[x] & 0xff ) == ' ' ) {
    967 				continue;
    968 			}
    969 			if ( idStr::ColorIndex(text_p[x]>>8) != currentColor ) {
    970 				currentColor = idStr::ColorIndex(text_p[x]>>8);
    971 				renderSystem->SetColor( idStr::ColorForIndex( currentColor ) );
    972 			}
    973 			renderSystem->DrawSmallChar( LOCALSAFE_LEFT + (x+1)*SMALLCHAR_WIDTH, v, text_p[x] & 0xff );
    974 		}
    975 
    976 		v += SMALLCHAR_HEIGHT;
    977 	}
    978 
    979 	renderSystem->SetColor( colorCyan );
    980 }
    981 
    982 /*
    983 ================
    984 DrawSolidConsole
    985 
    986 Draws the console with the solid background
    987 ================
    988 */
    989 void idConsoleLocal::DrawSolidConsole( float frac ) {
    990 	int				i, x;
    991 	float			y;
    992 	int				rows;
    993 	short			*text_p;
    994 	int				row;
    995 	int				lines;
    996 	int				currentColor;
    997 
    998 	lines = idMath::Ftoi( SCREEN_HEIGHT * frac );
    999 	if ( lines <= 0 ) {
   1000 		return;
   1001 	}
   1002 
   1003 	if ( lines > SCREEN_HEIGHT ) {
   1004 		lines = SCREEN_HEIGHT;
   1005 	}
   1006 
   1007 	// draw the background
   1008 	y = frac * SCREEN_HEIGHT - 2;
   1009 	if ( y < 1.0f ) {
   1010 		y = 0.0f;
   1011 	} else {
   1012 		renderSystem->DrawFilled( idVec4( 0.0f, 0.0f, 0.0f, 0.75f ), 0, 0, SCREEN_WIDTH, y );
   1013 	}
   1014 
   1015 	renderSystem->DrawFilled( colorCyan, 0, y, SCREEN_WIDTH, 2 );
   1016 
   1017 	// draw the version number
   1018 
   1019 	renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) );
   1020 
   1021 	idStr version = va( "%s.%i.%i", ENGINE_VERSION, BUILD_NUMBER, BUILD_NUMBER_MINOR );
   1022 	i = version.Length();
   1023 
   1024 	for ( x = 0; x < i; x++ ) {
   1025 		renderSystem->DrawSmallChar( LOCALSAFE_WIDTH - ( i - x ) * SMALLCHAR_WIDTH, 
   1026 			(lines-(SMALLCHAR_HEIGHT+SMALLCHAR_HEIGHT/4)), version[x] );
   1027 
   1028 	}
   1029 
   1030 
   1031 	// draw the text
   1032 	vislines = lines;
   1033 	rows = (lines-SMALLCHAR_WIDTH)/SMALLCHAR_WIDTH;		// rows of text to draw
   1034 
   1035 	y = lines - (SMALLCHAR_HEIGHT*3);
   1036 
   1037 	// draw from the bottom up
   1038 	if ( display != current ) {
   1039 		// draw arrows to show the buffer is backscrolled
   1040 		renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) );
   1041 		for ( x = 0; x < LINE_WIDTH; x += 4 ) {
   1042 			renderSystem->DrawSmallChar( LOCALSAFE_LEFT + (x+1)*SMALLCHAR_WIDTH, idMath::Ftoi( y ), '^' );
   1043 		}
   1044 		y -= SMALLCHAR_HEIGHT;
   1045 		rows--;
   1046 	}
   1047 	
   1048 	row = display;
   1049 
   1050 	if ( x == 0 ) {
   1051 		row--;
   1052 	}
   1053 
   1054 	currentColor = idStr::ColorIndex( C_COLOR_WHITE );
   1055 	renderSystem->SetColor( idStr::ColorForIndex( currentColor ) );
   1056 
   1057 	for ( i = 0; i < rows; i++, y -= SMALLCHAR_HEIGHT, row-- ) {
   1058 		if ( row < 0 ) {
   1059 			break;
   1060 		}
   1061 		if ( current - row >= TOTAL_LINES ) {
   1062 			// past scrollback wrap point
   1063 			continue;	
   1064 		}
   1065 
   1066 		text_p = text + (row % TOTAL_LINES)*LINE_WIDTH;
   1067 
   1068 		for ( x = 0; x < LINE_WIDTH; x++ ) {
   1069 			if ( ( text_p[x] & 0xff ) == ' ' ) {
   1070 				continue;
   1071 			}
   1072 
   1073 			if ( idStr::ColorIndex(text_p[x]>>8) != currentColor ) {
   1074 				currentColor = idStr::ColorIndex(text_p[x]>>8);
   1075 				renderSystem->SetColor( idStr::ColorForIndex( currentColor ) );
   1076 			}
   1077 			renderSystem->DrawSmallChar( LOCALSAFE_LEFT + (x+1)*SMALLCHAR_WIDTH, idMath::Ftoi( y ), text_p[x] & 0xff );
   1078 		}
   1079 	}
   1080 
   1081 	// draw the input prompt, user text, and cursor if desired
   1082 	DrawInput();
   1083 
   1084 	renderSystem->SetColor( colorCyan );
   1085 }
   1086 
   1087 
   1088 /*
   1089 ==============
   1090 Draw
   1091 
   1092 ForceFullScreen is used by the editor
   1093 ==============
   1094 */
   1095 void idConsoleLocal::Draw( bool forceFullScreen ) {
   1096 	if ( forceFullScreen ) {
   1097 		// if we are forced full screen because of a disconnect, 
   1098 		// we want the console closed when we go back to a session state
   1099 		Close();
   1100 		// we are however catching keyboard input
   1101 		keyCatching = true;
   1102 	}
   1103 
   1104 	Scroll();
   1105 
   1106 	UpdateDisplayFraction();
   1107 
   1108 	if ( forceFullScreen ) {
   1109 		DrawSolidConsole( 1.0f );
   1110 	} else if ( displayFrac ) {
   1111 		DrawSolidConsole( displayFrac );
   1112 	} else {
   1113 		// only draw the notify lines if the developer cvar is set,
   1114 		// or we are a debug build
   1115 		if ( !con_noPrint.GetBool() ) {
   1116 			DrawNotify();
   1117 		}
   1118 	}
   1119 
   1120 	float lefty = LOCALSAFE_TOP;
   1121 	float righty = LOCALSAFE_TOP;
   1122 	float centery = LOCALSAFE_TOP;
   1123 	if ( com_showFPS.GetBool() ) {
   1124 		righty = DrawFPS( righty );
   1125 	}
   1126 	if ( com_showMemoryUsage.GetBool() ) {
   1127 		righty = DrawMemoryUsage( righty );
   1128 	}
   1129 	DrawOverlayText( lefty, righty, centery );
   1130 	DrawDebugGraphs();
   1131 }
   1132 
   1133 /*
   1134 ========================
   1135 idConsoleLocal::PrintOverlay 
   1136 ========================
   1137 */
   1138 void idConsoleLocal::PrintOverlay( idOverlayHandle &handle, justify_t justify, const char *text, ... ) {
   1139 	if ( handle.index >= 0 && handle.index < overlayText.Num() ) {
   1140 		if ( overlayText[handle.index].time == handle.time ) {
   1141 			return;
   1142 		}
   1143 	}
   1144 
   1145 	char string[MAX_PRINT_MSG];
   1146 	va_list argptr;
   1147 	va_start( argptr, text );
   1148 	idStr::vsnPrintf( string, sizeof( string ), text, argptr );
   1149 	va_end( argptr );
   1150 
   1151 	overlayText_t &overlay = overlayText.Alloc();
   1152 	overlay.text = string;
   1153 	overlay.justify = justify;
   1154 	overlay.time = Sys_Milliseconds();
   1155 
   1156 	handle.index = overlayText.Num() - 1;
   1157 	handle.time = overlay.time;
   1158 }
   1159 
   1160 /*
   1161 ========================
   1162 idConsoleLocal::DrawOverlayText
   1163 ========================
   1164 */
   1165 void idConsoleLocal::DrawOverlayText( float & leftY, float & rightY, float & centerY ) {
   1166 	for ( int i = 0; i < overlayText.Num(); i++ ) {
   1167 		const idStr & text = overlayText[i].text;
   1168 
   1169 		int maxWidth = 0;
   1170 		int numLines = 0;
   1171 		for ( int j = 0; j < text.Length(); j++ ) {
   1172 			int width = 1;
   1173 			for (; j < text.Length() && text[j] != '\n'; j++ ) {
   1174 				width++;
   1175 			}
   1176 			numLines++;
   1177 			if ( width > maxWidth ) {
   1178 				maxWidth = width;
   1179 			}
   1180 		}
   1181 
   1182 		idVec4 bgColor( 0.0f, 0.0f, 0.0f, 0.75f );
   1183 
   1184 		const float width = maxWidth * SMALLCHAR_WIDTH;
   1185 		const float height = numLines * ( SMALLCHAR_HEIGHT + 4 );
   1186 		const float bgAdjust = - 0.5f * SMALLCHAR_WIDTH;
   1187 		if ( overlayText[i].justify == JUSTIFY_LEFT ) {
   1188 			renderSystem->DrawFilled( bgColor, LOCALSAFE_LEFT + bgAdjust, leftY, width, height );
   1189 		} else if ( overlayText[i].justify == JUSTIFY_RIGHT ) {
   1190 			renderSystem->DrawFilled( bgColor, LOCALSAFE_RIGHT - width + bgAdjust, rightY, width, height );
   1191 		} else if ( overlayText[i].justify == JUSTIFY_CENTER_LEFT || overlayText[i].justify == JUSTIFY_CENTER_RIGHT ) {
   1192 			renderSystem->DrawFilled( bgColor, LOCALSAFE_LEFT + ( LOCALSAFE_WIDTH - width + bgAdjust ) * 0.5f, centerY, width, height );
   1193 		} else {
   1194 			assert( false );
   1195 		}
   1196 
   1197 		idStr singleLine;
   1198 		for ( int j = 0; j < text.Length(); j += singleLine.Length() + 1 ) {
   1199 			singleLine = "";
   1200 			for ( int k = j; k < text.Length() && text[k] != '\n'; k++ ) {
   1201 				singleLine.Append( text[k] );
   1202 			}
   1203 			if ( overlayText[i].justify == JUSTIFY_LEFT ) {
   1204 				DrawTextLeftAlign( LOCALSAFE_LEFT, leftY, "%s", singleLine.c_str() );
   1205 			} else if ( overlayText[i].justify == JUSTIFY_RIGHT ) {
   1206 				DrawTextRightAlign( LOCALSAFE_RIGHT, rightY, "%s", singleLine.c_str() );
   1207 			} else if ( overlayText[i].justify == JUSTIFY_CENTER_LEFT ) {
   1208 				DrawTextLeftAlign( LOCALSAFE_LEFT + ( LOCALSAFE_WIDTH - width ) * 0.5f, centerY, "%s", singleLine.c_str() );
   1209 			} else if ( overlayText[i].justify == JUSTIFY_CENTER_RIGHT ) {
   1210 				DrawTextRightAlign( LOCALSAFE_LEFT + ( LOCALSAFE_WIDTH + width ) * 0.5f, centerY, "%s", singleLine.c_str() );
   1211 			} else {
   1212 				assert( false );
   1213 			}
   1214 		}
   1215 	}
   1216 	overlayText.SetNum( 0 );
   1217 }
   1218 
   1219 /*
   1220 ========================
   1221 idConsoleLocal::CreateGraph
   1222 ========================
   1223 */
   1224 idDebugGraph * idConsoleLocal::CreateGraph( int numItems ) {
   1225 	idDebugGraph * graph = new (TAG_SYSTEM) idDebugGraph( numItems );
   1226 	debugGraphs.Append( graph );
   1227 	return graph;
   1228 }
   1229 
   1230 /*
   1231 ========================
   1232 idConsoleLocal::DestroyGraph
   1233 ========================
   1234 */
   1235 void idConsoleLocal::DestroyGraph( idDebugGraph * graph ) {
   1236 	debugGraphs.Remove( graph );
   1237 	delete graph;
   1238 }
   1239 
   1240 /*
   1241 ========================
   1242 idConsoleLocal::DrawDebugGraphs 
   1243 ========================
   1244 */
   1245 void idConsoleLocal::DrawDebugGraphs() {
   1246 	for ( int i = 0; i < debugGraphs.Num(); i++ ) {
   1247 		debugGraphs[i]->Render( renderSystem );
   1248 	}
   1249 }