mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-30 16:11:11 +00:00
736ec20d4d
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.
1197 lines
28 KiB
C++
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 );
|
|
}
|
|
}
|