dhewm3/neo/framework/Console.cpp
dhewg 736ec20d4d Untangle the epic precompiled.h mess
Don't include the lazy precompiled.h everywhere, only what's
required for the compilation unit.
platform.h needs to be included instead to provide all essential
defines and types.
All includes use the relative path to the neo or the game
specific root.
Move all idlib related includes from idlib/Lib.h to precompiled.h.
precompiled.h still exists for the MFC stuff in tools/.
Add some missing header guards.
2011-12-19 23:21:47 +01:00

1197 lines
28 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
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.
===========================================================================
*/
#include "sys/platform.h"
#include "idlib/math/Vector.h"
#include "framework/async/AsyncNetwork.h"
#include "framework/BuildVersion.h"
#include "framework/CVarSystem.h"
#include "framework/Session.h"
#include "framework/EditField.h"
#include "framework/KeyInput.h"
#include "framework/EventLoop.h"
#include "renderer/RenderSystem.h"
#include "sound/sound.h"
#include "framework/Console.h"
void SCR_DrawTextLeftAlign( float &y, const char *text, ... ) id_attribute((format(printf,2,3)));
void SCR_DrawTextRightAlign( float &y, const char *text, ... ) id_attribute((format(printf,2,3)));
#define LINE_WIDTH 78
#define NUM_CON_TIMES 4
#define CON_TEXTSIZE 0x30000
#define TOTAL_LINES (CON_TEXTSIZE / LINE_WIDTH)
#define CONSOLE_FIRSTREPEAT 200
#define CONSOLE_REPEAT 100
#define COMMAND_HISTORY 64
// the console will query the cvar and command systems for
// command completion information
class idConsoleLocal : public idConsole {
public:
virtual void Init( void );
virtual void Shutdown( void );
virtual void LoadGraphics( void );
virtual bool ProcessEvent( const sysEvent_t *event, bool forceAccept );
virtual bool Active( void );
virtual void ClearNotifyLines( void );
virtual void Close( void );
virtual void Print( const char *text );
virtual void Draw( bool forceFullScreen );
void Dump( const char *toFile );
void Clear();
//============================
const idMaterial * charSetShader;
private:
void KeyDownEvent( int key );
void Linefeed();
void PageUp();
void PageDown();
void Top();
void Bottom();
void DrawInput();
void DrawNotify();
void DrawSolidConsole( float frac );
void Scroll();
void SetDisplayFraction( float frac );
void UpdateDisplayFraction( void );
//============================
bool keyCatching;
short text[CON_TEXTSIZE];
int current; // line where next message will be printed
int x; // offset in current line for next print
int display; // bottom of console displays this line
int lastKeyEvent; // time of last key event for scroll delay
int nextKeyEvent; // keyboard repeat rate
float displayFrac; // approaches finalFrac at scr_conspeed
float finalFrac; // 0.0 to 1.0 lines of console to display
int fracTime; // time of last displayFrac update
int vislines; // in scanlines
int times[NUM_CON_TIMES]; // cls.realtime time the line was generated
// for transparent notify lines
idVec4 color;
idEditField historyEditLines[COMMAND_HISTORY];
int nextHistoryLine;// the last line in the history buffer, not masked
int historyLine; // the line being displayed from history buffer
// will be <= nextHistoryLine
idEditField consoleField;
static idCVar con_speed;
static idCVar con_notifyTime;
static idCVar con_noPrint;
const idMaterial * whiteShader;
const idMaterial * consoleShader;
};
static idConsoleLocal localConsole;
idConsole *console = &localConsole;
idCVar idConsoleLocal::con_speed( "con_speed", "3", CVAR_SYSTEM, "speed at which the console moves up and down" );
idCVar idConsoleLocal::con_notifyTime( "con_notifyTime", "3", CVAR_SYSTEM, "time messages are displayed onscreen when console is pulled up" );
#ifdef DEBUG
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" );
#else
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" );
#endif
/*
=============================================================================
Misc stats
=============================================================================
*/
/*
==================
SCR_DrawTextLeftAlign
==================
*/
void SCR_DrawTextLeftAlign( float &y, const char *text, ... ) {
char string[MAX_STRING_CHARS];
va_list argptr;
va_start( argptr, text );
idStr::vsnPrintf( string, sizeof( string ), text, argptr );
va_end( argptr );
renderSystem->DrawSmallStringExt( 0, y + 2, string, colorWhite, true, localConsole.charSetShader );
y += SMALLCHAR_HEIGHT + 4;
}
/*
==================
SCR_DrawTextRightAlign
==================
*/
void SCR_DrawTextRightAlign( float &y, const char *text, ... ) {
char string[MAX_STRING_CHARS];
va_list argptr;
va_start( argptr, text );
int i = idStr::vsnPrintf( string, sizeof( string ), text, argptr );
va_end( argptr );
renderSystem->DrawSmallStringExt( 635 - i * SMALLCHAR_WIDTH, y + 2, string, colorWhite, true, localConsole.charSetShader );
y += SMALLCHAR_HEIGHT + 4;
}
/*
==================
SCR_DrawFPS
==================
*/
#define FPS_FRAMES 4
float SCR_DrawFPS( float y ) {
char *s;
int w;
static int previousTimes[FPS_FRAMES];
static int index;
int i, total;
int fps;
static int previous;
int t, frameTime;
// don't use serverTime, because that will be drifting to
// correct for internet lag changes, timescales, timedemos, etc
t = Sys_Milliseconds();
frameTime = t - previous;
previous = t;
previousTimes[index % FPS_FRAMES] = frameTime;
index++;
if ( index > FPS_FRAMES ) {
// average multiple frames together to smooth changes out a bit
total = 0;
for ( i = 0 ; i < FPS_FRAMES ; i++ ) {
total += previousTimes[i];
}
if ( !total ) {
total = 1;
}
fps = 10000 * FPS_FRAMES / total;
fps = (fps + 5)/10;
s = va( "%ifps", fps );
w = strlen( s ) * BIGCHAR_WIDTH;
renderSystem->DrawBigStringExt( 635 - w, idMath::FtoiFast( y ) + 2, s, colorWhite, true, localConsole.charSetShader);
}
return y + BIGCHAR_HEIGHT + 4;
}
/*
==================
SCR_DrawMemoryUsage
==================
*/
float SCR_DrawMemoryUsage( float y ) {
memoryStats_t allocs, frees;
Mem_GetStats( allocs );
SCR_DrawTextRightAlign( y, "total allocated memory: %4d, %4dkB", allocs.num, allocs.totalSize>>10 );
Mem_GetFrameStats( allocs, frees );
SCR_DrawTextRightAlign( y, "frame alloc: %4d, %4dkB frame free: %4d, %4dkB", allocs.num, allocs.totalSize>>10, frees.num, frees.totalSize>>10 );
Mem_ClearFrameStats();
return y;
}
/*
==================
SCR_DrawAsyncStats
==================
*/
float SCR_DrawAsyncStats( float y ) {
int i, outgoingRate, incomingRate;
float outgoingCompression, incomingCompression;
if ( idAsyncNetwork::server.IsActive() ) {
SCR_DrawTextRightAlign( y, "server delay = %d msec", idAsyncNetwork::server.GetDelay() );
SCR_DrawTextRightAlign( y, "total outgoing rate = %d KB/s", idAsyncNetwork::server.GetOutgoingRate() >> 10 );
SCR_DrawTextRightAlign( y, "total incoming rate = %d KB/s", idAsyncNetwork::server.GetIncomingRate() >> 10 );
for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
outgoingRate = idAsyncNetwork::server.GetClientOutgoingRate( i );
incomingRate = idAsyncNetwork::server.GetClientIncomingRate( i );
outgoingCompression = idAsyncNetwork::server.GetClientOutgoingCompression( i );
incomingCompression = idAsyncNetwork::server.GetClientIncomingCompression( i );
if ( outgoingRate != -1 && incomingRate != -1 ) {
SCR_DrawTextRightAlign( y, "client %d: out rate = %d B/s (% -2.1f%%), in rate = %d B/s (% -2.1f%%)",
i, outgoingRate, outgoingCompression, incomingRate, incomingCompression );
}
}
idStr msg;
idAsyncNetwork::server.GetAsyncStatsAvgMsg( msg );
SCR_DrawTextRightAlign( y, msg.c_str() );
} else if ( idAsyncNetwork::client.IsActive() ) {
outgoingRate = idAsyncNetwork::client.GetOutgoingRate();
incomingRate = idAsyncNetwork::client.GetIncomingRate();
outgoingCompression = idAsyncNetwork::client.GetOutgoingCompression();
incomingCompression = idAsyncNetwork::client.GetIncomingCompression();
if ( outgoingRate != -1 && incomingRate != -1 ) {
SCR_DrawTextRightAlign( y, "out rate = %d B/s (% -2.1f%%), in rate = %d B/s (% -2.1f%%)",
outgoingRate, outgoingCompression, incomingRate, incomingCompression );
}
SCR_DrawTextRightAlign( y, "packet loss = %d%%, client prediction = %d",
(int)idAsyncNetwork::client.GetIncomingPacketLoss(), idAsyncNetwork::client.GetPrediction() );
SCR_DrawTextRightAlign( y, "predicted frames: %d", idAsyncNetwork::client.GetPredictedFrames() );
}
return y;
}
/*
==================
SCR_DrawSoundDecoders
==================
*/
float SCR_DrawSoundDecoders( float y ) {
int index, numActiveDecoders;
soundDecoderInfo_t decoderInfo;
index = -1;
numActiveDecoders = 0;
while( ( index = soundSystem->GetSoundDecoderInfo( index, decoderInfo ) ) != -1 ) {
int localTime = decoderInfo.current44kHzTime - decoderInfo.start44kHzTime;
int sampleTime = decoderInfo.num44kHzSamples / decoderInfo.numChannels;
int percent;
if ( localTime > sampleTime ) {
if ( decoderInfo.looping ) {
percent = ( localTime % sampleTime ) * 100 / sampleTime;
} else {
percent = 100;
}
} else {
percent = localTime * 100 / sampleTime;
}
SCR_DrawTextLeftAlign( y, "%3d: %3d%% (%1.2f) %s: %s (%dkB)", numActiveDecoders, percent, decoderInfo.lastVolume, decoderInfo.format.c_str(), decoderInfo.name.c_str(), decoderInfo.numBytes >> 10 );
numActiveDecoders++;
}
return y;
}
//=========================================================================
/*
==============
Con_Clear_f
==============
*/
static void Con_Clear_f( const idCmdArgs &args ) {
localConsole.Clear();
}
/*
==============
Con_Dump_f
==============
*/
static void Con_Dump_f( const idCmdArgs &args ) {
if ( args.Argc() != 2 ) {
common->Printf( "usage: conDump <filename>\n" );
return;
}
idStr fileName = args.Argv(1);
fileName.DefaultFileExtension(".txt");
common->Printf( "Dumped console text to %s.\n", fileName.c_str() );
localConsole.Dump( fileName.c_str() );
}
/*
==============
idConsoleLocal::Init
==============
*/
void idConsoleLocal::Init( void ) {
int i;
keyCatching = false;
lastKeyEvent = -1;
nextKeyEvent = CONSOLE_FIRSTREPEAT;
consoleField.Clear();
consoleField.SetWidthInChars( LINE_WIDTH );
for ( i = 0 ; i < COMMAND_HISTORY ; i++ ) {
historyEditLines[i].Clear();
historyEditLines[i].SetWidthInChars( LINE_WIDTH );
}
cmdSystem->AddCommand( "clear", Con_Clear_f, CMD_FL_SYSTEM, "clears the console" );
cmdSystem->AddCommand( "conDump", Con_Dump_f, CMD_FL_SYSTEM, "dumps the console text to a file" );
}
/*
==============
idConsoleLocal::Shutdown
==============
*/
void idConsoleLocal::Shutdown( void ) {
cmdSystem->RemoveCommand( "clear" );
cmdSystem->RemoveCommand( "conDump" );
}
/*
==============
LoadGraphics
Can't be combined with init, because init happens before
the renderSystem is initialized
==============
*/
void idConsoleLocal::LoadGraphics() {
charSetShader = declManager->FindMaterial( "textures/bigchars" );
whiteShader = declManager->FindMaterial( "_white" );
consoleShader = declManager->FindMaterial( "console" );
}
/*
================
idConsoleLocal::Active
================
*/
bool idConsoleLocal::Active( void ) {
return keyCatching;
}
/*
================
idConsoleLocal::ClearNotifyLines
================
*/
void idConsoleLocal::ClearNotifyLines() {
int i;
for ( i = 0 ; i < NUM_CON_TIMES ; i++ ) {
times[i] = 0;
}
}
/*
================
idConsoleLocal::Close
================
*/
void idConsoleLocal::Close() {
keyCatching = false;
SetDisplayFraction( 0 );
displayFrac = 0; // don't scroll to that point, go immediately
ClearNotifyLines();
}
/*
================
idConsoleLocal::Clear
================
*/
void idConsoleLocal::Clear() {
int i;
for ( i = 0 ; i < CON_TEXTSIZE ; i++ ) {
text[i] = (idStr::ColorIndex(C_COLOR_CYAN)<<8) | ' ';
}
Bottom(); // go to end
}
/*
================
idConsoleLocal::Dump
Save the console contents out to a file
================
*/
void idConsoleLocal::Dump( const char *fileName ) {
int l, x, i;
short * line;
idFile *f;
char buffer[LINE_WIDTH + 3];
f = fileSystem->OpenFileWrite( fileName );
if ( !f ) {
common->Warning( "couldn't open %s", fileName );
return;
}
// skip empty lines
l = current - TOTAL_LINES + 1;
if ( l < 0 ) {
l = 0;
}
for ( ; l <= current ; l++ )
{
line = text + ( l % TOTAL_LINES ) * LINE_WIDTH;
for ( x = 0; x < LINE_WIDTH; x++ )
if ( ( line[x] & 0xff ) > ' ' )
break;
if ( x != LINE_WIDTH )
break;
}
// write the remaining lines
for ( ; l <= current; l++ ) {
line = text + ( l % TOTAL_LINES ) * LINE_WIDTH;
for( i = 0; i < LINE_WIDTH; i++ ) {
buffer[i] = line[i] & 0xff;
}
for ( x = LINE_WIDTH-1; x >= 0; x-- ) {
if ( buffer[x] <= ' ' ) {
buffer[x] = 0;
} else {
break;
}
}
buffer[x+1] = '\r';
buffer[x+2] = '\n';
buffer[x+3] = 0;
f->Write( buffer, strlen( buffer ) );
}
fileSystem->CloseFile( f );
}
/*
================
idConsoleLocal::PageUp
================
*/
void idConsoleLocal::PageUp( void ) {
display -= 2;
if ( current - display >= TOTAL_LINES ) {
display = current - TOTAL_LINES + 1;
}
}
/*
================
idConsoleLocal::PageDown
================
*/
void idConsoleLocal::PageDown( void ) {
display += 2;
if ( display > current ) {
display = current;
}
}
/*
================
idConsoleLocal::Top
================
*/
void idConsoleLocal::Top( void ) {
display = 0;
}
/*
================
idConsoleLocal::Bottom
================
*/
void idConsoleLocal::Bottom( void ) {
display = current;
}
/*
=============================================================================
CONSOLE LINE EDITING
==============================================================================
*/
/*
====================
KeyDownEvent
Handles history and console scrollback
====================
*/
void idConsoleLocal::KeyDownEvent( int key ) {
// Execute F key bindings
if ( key >= K_F1 && key <= K_F12 ) {
idKeyInput::ExecKeyBinding( key );
return;
}
// ctrl-L clears screen
if ( key == 'l' && idKeyInput::IsDown( K_CTRL ) ) {
Clear();
return;
}
// enter finishes the line
if ( key == K_ENTER || key == K_KP_ENTER ) {
common->Printf ( "]%s\n", consoleField.GetBuffer() );
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, consoleField.GetBuffer() ); // valid command
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "\n" );
// copy line to history buffer
historyEditLines[nextHistoryLine % COMMAND_HISTORY] = consoleField;
nextHistoryLine++;
historyLine = nextHistoryLine;
consoleField.Clear();
consoleField.SetWidthInChars( LINE_WIDTH );
session->UpdateScreen();// force an update, because the command
// may take some time
return;
}
// command completion
if ( key == K_TAB ) {
consoleField.AutoComplete();
return;
}
// command history (ctrl-p ctrl-n for unix style)
if ( ( key == K_UPARROW ) ||
( ( tolower(key) == 'p' ) && idKeyInput::IsDown( K_CTRL ) ) ) {
if ( nextHistoryLine - historyLine < COMMAND_HISTORY && historyLine > 0 ) {
historyLine--;
}
consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ];
return;
}
if ( ( key == K_DOWNARROW ) ||
( ( tolower( key ) == 'n' ) && idKeyInput::IsDown( K_CTRL ) ) ) {
if ( historyLine == nextHistoryLine ) {
return;
}
historyLine++;
consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ];
return;
}
// console scrolling
if ( key == K_PGUP ) {
PageUp();
lastKeyEvent = eventLoop->Milliseconds();
nextKeyEvent = CONSOLE_FIRSTREPEAT;
return;
}
if ( key == K_PGDN ) {
PageDown();
lastKeyEvent = eventLoop->Milliseconds();
nextKeyEvent = CONSOLE_FIRSTREPEAT;
return;
}
if ( key == K_MWHEELUP ) {
PageUp();
return;
}
if ( key == K_MWHEELDOWN ) {
PageDown();
return;
}
// ctrl-home = top of console
if ( key == K_HOME && idKeyInput::IsDown( K_CTRL ) ) {
Top();
return;
}
// ctrl-end = bottom of console
if ( key == K_END && idKeyInput::IsDown( K_CTRL ) ) {
Bottom();
return;
}
// pass to the normal editline routine
consoleField.KeyDownEvent( key );
}
/*
==============
Scroll
deals with scrolling text because we don't have key repeat
==============
*/
void idConsoleLocal::Scroll( ) {
if (lastKeyEvent == -1 || (lastKeyEvent+200) > eventLoop->Milliseconds()) {
return;
}
// console scrolling
if ( idKeyInput::IsDown( K_PGUP ) ) {
PageUp();
nextKeyEvent = CONSOLE_REPEAT;
return;
}
if ( idKeyInput::IsDown( K_PGDN ) ) {
PageDown();
nextKeyEvent = CONSOLE_REPEAT;
return;
}
}
/*
==============
SetDisplayFraction
Causes the console to start opening the desired amount.
==============
*/
void idConsoleLocal::SetDisplayFraction( float frac ) {
finalFrac = frac;
fracTime = com_frameTime;
}
/*
==============
UpdateDisplayFraction
Scrolls the console up or down based on conspeed
==============
*/
void idConsoleLocal::UpdateDisplayFraction( void ) {
if ( con_speed.GetFloat() <= 0.1f ) {
fracTime = com_frameTime;
displayFrac = finalFrac;
return;
}
// scroll towards the destination height
if ( finalFrac < displayFrac ) {
displayFrac -= con_speed.GetFloat() * ( com_frameTime - fracTime ) * 0.001f;
if ( finalFrac > displayFrac ) {
displayFrac = finalFrac;
}
fracTime = com_frameTime;
} else if ( finalFrac > displayFrac ) {
displayFrac += con_speed.GetFloat() * ( com_frameTime - fracTime ) * 0.001f;
if ( finalFrac < displayFrac ) {
displayFrac = finalFrac;
}
fracTime = com_frameTime;
}
}
/*
==============
ProcessEvent
==============
*/
bool idConsoleLocal::ProcessEvent( const sysEvent_t *event, bool forceAccept ) {
bool consoleKey;
consoleKey = event->evType == SE_KEY && ( event->evValue == Sys_GetConsoleKey( false ) || event->evValue == Sys_GetConsoleKey( true ) );
#if ID_CONSOLE_LOCK
// If the console's not already down, and we have it turned off, check for ctrl+alt
if ( !keyCatching && !com_allowConsole.GetBool() ) {
if ( !idKeyInput::IsDown( K_CTRL ) || !idKeyInput::IsDown( K_ALT ) ) {
consoleKey = false;
}
}
#endif
// we always catch the console key event
if ( !forceAccept && consoleKey ) {
// ignore up events
if ( event->evValue2 == 0 ) {
return true;
}
consoleField.ClearAutoComplete();
// a down event will toggle the destination lines
if ( keyCatching ) {
Close();
Sys_GrabMouseCursor( true );
cvarSystem->SetCVarBool( "ui_chat", false );
} else {
consoleField.Clear();
keyCatching = true;
if ( idKeyInput::IsDown( K_SHIFT ) ) {
// if the shift key is down, don't open the console as much
SetDisplayFraction( 0.2f );
} else {
SetDisplayFraction( 0.5f );
}
cvarSystem->SetCVarBool( "ui_chat", true );
}
return true;
}
// if we aren't key catching, dump all the other events
if ( !forceAccept && !keyCatching ) {
return false;
}
// handle key and character events
if ( event->evType == SE_CHAR ) {
// never send the console key as a character
if ( event->evValue != Sys_GetConsoleKey( false ) && event->evValue != Sys_GetConsoleKey( true ) ) {
consoleField.CharEvent( event->evValue );
}
return true;
}
if ( event->evType == SE_KEY ) {
// ignore up key events
if ( event->evValue2 == 0 ) {
return true;
}
KeyDownEvent( event->evValue );
return true;
}
// we don't handle things like mouse, joystick, and network packets
return false;
}
/*
==============================================================================
PRINTING
==============================================================================
*/
/*
===============
Linefeed
===============
*/
void idConsoleLocal::Linefeed() {
int i;
// mark time for transparent overlay
if ( current >= 0 ) {
times[current % NUM_CON_TIMES] = com_frameTime;
}
x = 0;
if ( display == current ) {
display++;
}
current++;
for ( i = 0; i < LINE_WIDTH; i++ ) {
text[(current%TOTAL_LINES)*LINE_WIDTH+i] = (idStr::ColorIndex(C_COLOR_CYAN)<<8) | ' ';
}
}
/*
================
Print
Handles cursor positioning, line wrapping, etc
================
*/
void idConsoleLocal::Print( const char *txt ) {
int y;
int c, l;
int color;
#ifdef ID_ALLOW_TOOLS
RadiantPrint( txt );
if( com_editors & EDITOR_MATERIAL ) {
MaterialEditorPrintConsole(txt);
}
#endif
color = idStr::ColorIndex( C_COLOR_CYAN );
while ( (c = *(const unsigned char*)txt) != 0 ) {
if ( idStr::IsColor( txt ) ) {
if ( *(txt+1) == C_COLOR_DEFAULT ) {
color = idStr::ColorIndex( C_COLOR_CYAN );
} else {
color = idStr::ColorIndex( *(txt+1) );
}
txt += 2;
continue;
}
y = current % TOTAL_LINES;
// if we are about to print a new word, check to see
// if we should wrap to the new line
if ( c > ' ' && ( x == 0 || text[y*LINE_WIDTH+x-1] <= ' ' ) ) {
// count word length
for (l=0 ; l< LINE_WIDTH ; l++) {
if ( txt[l] <= ' ') {
break;
}
}
// word wrap
if (l != LINE_WIDTH && (x + l >= LINE_WIDTH) ) {
Linefeed();
}
}
txt++;
switch( c ) {
case '\n':
Linefeed ();
break;
case '\t':
do {
text[y*LINE_WIDTH+x] = (color << 8) | ' ';
x++;
if ( x >= LINE_WIDTH ) {
Linefeed();
x = 0;
}
} while ( x & 3 );
break;
case '\r':
x = 0;
break;
default: // display character and advance
text[y*LINE_WIDTH+x] = (color << 8) | c;
x++;
if ( x >= LINE_WIDTH ) {
Linefeed();
x = 0;
}
break;
}
}
// mark time for transparent overlay
if ( current >= 0 ) {
times[current % NUM_CON_TIMES] = com_frameTime;
}
}
/*
==============================================================================
DRAWING
==============================================================================
*/
/*
================
DrawInput
Draw the editline after a ] prompt
================
*/
void idConsoleLocal::DrawInput() {
int y, autoCompleteLength;
y = vislines - ( SMALLCHAR_HEIGHT * 2 );
if ( consoleField.GetAutoCompleteLength() != 0 ) {
autoCompleteLength = strlen( consoleField.GetBuffer() ) - consoleField.GetAutoCompleteLength();
if ( autoCompleteLength > 0 ) {
renderSystem->SetColor4( .8f, .2f, .2f, .45f );
renderSystem->DrawStretchPic( 2 * SMALLCHAR_WIDTH + consoleField.GetAutoCompleteLength() * SMALLCHAR_WIDTH,
y + 2, autoCompleteLength * SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT - 2, 0, 0, 0, 0, whiteShader );
}
}
renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) );
renderSystem->DrawSmallChar( 1 * SMALLCHAR_WIDTH, y, ']', localConsole.charSetShader );
consoleField.Draw(2 * SMALLCHAR_WIDTH, y, SCREEN_WIDTH - 3 * SMALLCHAR_WIDTH, true, charSetShader );
}
/*
================
DrawNotify
Draws the last few lines of output transparently over the game top
================
*/
void idConsoleLocal::DrawNotify() {
int x, v;
short *text_p;
int i;
int time;
int currentColor;
if ( con_noPrint.GetBool() ) {
return;
}
currentColor = idStr::ColorIndex( C_COLOR_WHITE );
renderSystem->SetColor( idStr::ColorForIndex( currentColor ) );
v = 0;
for ( i = current-NUM_CON_TIMES+1; i <= current; i++ ) {
if ( i < 0 ) {
continue;
}
time = times[i % NUM_CON_TIMES];
if ( time == 0 ) {
continue;
}
time = com_frameTime - time;
if ( time > con_notifyTime.GetFloat() * 1000 ) {
continue;
}
text_p = text + (i % TOTAL_LINES)*LINE_WIDTH;
for ( x = 0; x < LINE_WIDTH; x++ ) {
if ( ( text_p[x] & 0xff ) == ' ' ) {
continue;
}
if ( idStr::ColorIndex(text_p[x]>>8) != currentColor ) {
currentColor = idStr::ColorIndex(text_p[x]>>8);
renderSystem->SetColor( idStr::ColorForIndex( currentColor ) );
}
renderSystem->DrawSmallChar( (x+1)*SMALLCHAR_WIDTH, v, text_p[x] & 0xff, localConsole.charSetShader );
}
v += SMALLCHAR_HEIGHT;
}
renderSystem->SetColor( colorCyan );
}
/*
================
DrawSolidConsole
Draws the console with the solid background
================
*/
void idConsoleLocal::DrawSolidConsole( float frac ) {
int i, x;
float y;
int rows;
short *text_p;
int row;
int lines;
int currentColor;
lines = idMath::FtoiFast( SCREEN_HEIGHT * frac );
if ( lines <= 0 ) {
return;
}
if ( lines > SCREEN_HEIGHT ) {
lines = SCREEN_HEIGHT;
}
// draw the background
y = frac * SCREEN_HEIGHT - 2;
if ( y < 1.0f ) {
y = 0.0f;
} else {
renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH, y, 0, 1.0f - displayFrac, 1, 1, consoleShader );
}
renderSystem->SetColor( colorCyan );
renderSystem->DrawStretchPic( 0, y, SCREEN_WIDTH, 2, 0, 0, 0, 0, whiteShader );
renderSystem->SetColor( colorWhite );
// draw the version number
renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) );
idStr version = va("%s.%i", ENGINE_VERSION, BUILD_NUMBER);
i = version.Length();
for ( x = 0; x < i; x++ ) {
renderSystem->DrawSmallChar( SCREEN_WIDTH - ( i - x ) * SMALLCHAR_WIDTH,
(lines-(SMALLCHAR_HEIGHT+SMALLCHAR_HEIGHT/2)), version[x], localConsole.charSetShader );
}
// draw the text
vislines = lines;
rows = (lines-SMALLCHAR_WIDTH)/SMALLCHAR_WIDTH; // rows of text to draw
y = lines - (SMALLCHAR_HEIGHT*3);
// draw from the bottom up
if ( display != current ) {
// draw arrows to show the buffer is backscrolled
renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) );
for ( x = 0; x < LINE_WIDTH; x += 4 ) {
renderSystem->DrawSmallChar( (x+1)*SMALLCHAR_WIDTH, idMath::FtoiFast( y ), '^', localConsole.charSetShader );
}
y -= SMALLCHAR_HEIGHT;
rows--;
}
row = display;
if ( x == 0 ) {
row--;
}
currentColor = idStr::ColorIndex( C_COLOR_WHITE );
renderSystem->SetColor( idStr::ColorForIndex( currentColor ) );
for ( i = 0; i < rows; i++, y -= SMALLCHAR_HEIGHT, row-- ) {
if ( row < 0 ) {
break;
}
if ( current - row >= TOTAL_LINES ) {
// past scrollback wrap point
continue;
}
text_p = text + (row % TOTAL_LINES)*LINE_WIDTH;
for ( x = 0; x < LINE_WIDTH; x++ ) {
if ( ( text_p[x] & 0xff ) == ' ' ) {
continue;
}
if ( idStr::ColorIndex(text_p[x]>>8) != currentColor ) {
currentColor = idStr::ColorIndex(text_p[x]>>8);
renderSystem->SetColor( idStr::ColorForIndex( currentColor ) );
}
renderSystem->DrawSmallChar( (x+1)*SMALLCHAR_WIDTH, idMath::FtoiFast( y ), text_p[x] & 0xff, localConsole.charSetShader );
}
}
// draw the input prompt, user text, and cursor if desired
DrawInput();
renderSystem->SetColor( colorCyan );
}
/*
==============
Draw
ForceFullScreen is used by the editor
==============
*/
void idConsoleLocal::Draw( bool forceFullScreen ) {
float y = 0.0f;
if ( !charSetShader ) {
return;
}
if ( forceFullScreen ) {
// if we are forced full screen because of a disconnect,
// we want the console closed when we go back to a session state
Close();
// we are however catching keyboard input
keyCatching = true;
}
Scroll();
UpdateDisplayFraction();
if ( forceFullScreen ) {
DrawSolidConsole( 1.0f );
} else if ( displayFrac ) {
DrawSolidConsole( displayFrac );
} else {
// only draw the notify lines if the developer cvar is set,
// or we are a debug build
if ( !con_noPrint.GetBool() ) {
DrawNotify();
}
}
if ( com_showFPS.GetBool() ) {
y = SCR_DrawFPS( 0 );
}
if ( com_showMemoryUsage.GetBool() ) {
y = SCR_DrawMemoryUsage( y );
}
if ( com_showAsyncStats.GetBool() ) {
y = SCR_DrawAsyncStats( y );
}
if ( com_showSoundDecoders.GetBool() ) {
y = SCR_DrawSoundDecoders( y );
}
}