dhewm3/neo/framework/Console.cpp
Daniel Gibson ae63021d00 Add absolute mouse mode and refactor mouse grabbing code
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.
2022-01-10 00:46:32 +01:00

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 );
}
}