mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-12-11 13:30:59 +00:00
ae63021d00
There were lots of places in the code that called Sys_GrabInput(), some of them each frame. Most of this is unified in events.cpp now, in handleMouseGrab() which is called once per frame by Sys_GenerateEvents() - this makes reasoning about when the mouse is grabbed and when not a lot easier. Sys_GrabInput(false) still is called in a few places, before operations that tend to take long (like loading a map or vid_restart), but (hopefully) not regularly anymore. The other big change is that the game now uses SDLs absolute mouse mode for fullscreen menus (except the PDA which is an ugly hack), so the ingame cursor is at the same position as the system cursor, which especially helps when debugging with `in_nograb 1` and should also help if someone wants to integrate an additional GUI toolkit like Dear ImGui.
1249 lines
30 KiB
C++
1249 lines
30 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"
|
|
|
|
#include "tools/edit_public.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();
|
|
|
|
virtual void SaveHistory();
|
|
virtual void LoadHistory();
|
|
|
|
//============================
|
|
|
|
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 );
|
|
}
|
|
|
|
void idConsoleLocal::SaveHistory() {
|
|
idFile *f = fileSystem->OpenFileWrite( "consolehistory.dat" );
|
|
for ( int i=0; i < COMMAND_HISTORY; ++i ) {
|
|
// make sure the history is in the right order
|
|
int line = (nextHistoryLine + i) % COMMAND_HISTORY;
|
|
const char *s = historyEditLines[line].GetBuffer();
|
|
if ( s && s[0] ) {
|
|
f->WriteString(s);
|
|
}
|
|
}
|
|
fileSystem->CloseFile(f);
|
|
}
|
|
|
|
void idConsoleLocal::LoadHistory() {
|
|
idFile *f = fileSystem->OpenFileRead( "consolehistory.dat" );
|
|
if ( f == NULL ) // file doesn't exist
|
|
return;
|
|
|
|
historyLine = 0;
|
|
idStr tmp;
|
|
for ( int i=0; i < COMMAND_HISTORY; ++i ) {
|
|
if ( f->Tell() >= f->Length() ) {
|
|
break; // EOF is reached
|
|
}
|
|
f->ReadString(tmp);
|
|
historyEditLines[i].SetBuffer(tmp.c_str());
|
|
++historyLine;
|
|
}
|
|
nextHistoryLine = historyLine;
|
|
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, if it isn't the same as the last command
|
|
if ( idStr::Cmp( consoleField.GetBuffer(),
|
|
historyEditLines[(nextHistoryLine + COMMAND_HISTORY - 1) % COMMAND_HISTORY].GetBuffer()) != 0 )
|
|
{
|
|
historyEditLines[nextHistoryLine % COMMAND_HISTORY] = consoleField;
|
|
nextHistoryLine++;
|
|
}
|
|
|
|
historyLine = nextHistoryLine;
|
|
// clear the next line from old garbage, else the oldest history entry turns up when pressing DOWN
|
|
historyEditLines[nextHistoryLine % COMMAND_HISTORY].Clear();
|
|
|
|
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 = false;
|
|
if(event->evType == SE_KEY)
|
|
{
|
|
bool shiftPressed = idKeyInput::IsDown( K_SHIFT );
|
|
if( event->evValue == K_CONSOLE || event->evValue == Sys_GetConsoleKey( shiftPressed )
|
|
|| (event->evValue == K_ESCAPE && shiftPressed) ) // shift+esc should also open console
|
|
{
|
|
consoleKey = 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();
|
|
cvarSystem->SetCVarBool( "ui_chat", false );
|
|
} else {
|
|
consoleField.Clear();
|
|
keyCatching = true;
|
|
if ( idKeyInput::IsDown( K_SHIFT ) && event->evValue != K_ESCAPE ) {
|
|
// if the shift key is down, don't open the console as much
|
|
// except we used shift+esc.
|
|
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( idKeyInput::IsDown( K_SHIFT ) ) ) {
|
|
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 );
|
|
}
|
|
}
|