This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/common/console.c

1699 lines
38 KiB
C
Raw Normal View History

2010-07-19 22:00:00 +02:00
//=======================================================================
// Copyright XashXT Group 2007 <20>
// con_main.c - client console
//=======================================================================
#include "common.h"
#include "client.h"
#include "keydefs.h"
2010-08-04 22:00:00 +02:00
#include "protocol.h" // get the protocol version
2010-08-20 22:00:00 +02:00
#include "con_nprint.h"
2010-12-02 22:00:00 +01:00
#include "gl_local.h"
2010-09-10 22:00:00 +02:00
#include "qfont.h"
2010-07-19 22:00:00 +02:00
2010-10-22 22:00:00 +02:00
convar_t *con_notifytime;
convar_t *scr_conspeed;
convar_t *con_fontsize;
2010-07-19 22:00:00 +02:00
2010-08-04 22:00:00 +02:00
#define CON_TIMES 5 // need for 4 lines
2010-07-20 22:00:00 +02:00
#define COLOR_DEFAULT '7'
2010-07-19 22:00:00 +02:00
#define CON_HISTORY 32
2011-03-07 22:00:00 +01:00
#define MAX_DBG_NOTIFY 128
2010-09-09 22:00:00 +02:00
#define ColorIndex( c ) ((( c ) - '0' ) & 7 )
2010-07-19 22:00:00 +02:00
2010-07-26 22:00:00 +02:00
#define CON_TEXTSIZE 131072 // 128 kb buffer
2010-07-19 22:00:00 +02:00
// console color typeing
rgba_t g_color_table[8] =
{
2010-07-20 22:00:00 +02:00
{ 0, 0, 0, 255}, // black
{255, 0, 0, 255}, // red
{ 0, 255, 0, 255}, // green
{255, 255, 0, 255}, // yellow
{ 0, 0, 255, 255}, // blue
{ 0, 255, 255, 255}, // cyan
{255, 0, 255, 255}, // magenta
{240, 180, 24, 255}, // default color (can be changed by user)
2010-07-19 22:00:00 +02:00
};
typedef struct
{
2010-07-20 22:00:00 +02:00
string buffer;
int cursor;
int scroll;
int widthInChars;
2010-07-19 22:00:00 +02:00
} field_t;
2011-03-07 22:00:00 +01:00
typedef struct
{
string szNotify;
float expire;
rgba_t color;
} notify_t;
2010-07-19 22:00:00 +02:00
typedef struct
{
2010-10-26 22:00:00 +02:00
qboolean initialized;
2010-07-19 22:00:00 +02:00
2010-07-20 22:00:00 +02:00
short text[CON_TEXTSIZE];
int current; // line where next message will be printed
int display; // bottom of console displays this line
int x; // offset in current line for next print
2010-07-19 22:00:00 +02:00
2010-07-20 22:00:00 +02:00
int linewidth; // characters across screen
int totallines; // total lines in console scrollback
2010-07-19 22:00:00 +02:00
2010-07-20 22:00:00 +02:00
float displayFrac; // aproaches finalFrac at scr_conspeed
float finalFrac; // 0.0 to 1.0 lines of console to display
2010-07-19 22:00:00 +02:00
2010-07-20 22:00:00 +02:00
int vislines; // in scanlines
2010-10-09 22:00:00 +02:00
double times[CON_TIMES]; // host.realtime the line was generated for transparent notify lines
2010-07-20 22:00:00 +02:00
rgba_t color;
2010-07-19 22:00:00 +02:00
2010-07-20 22:00:00 +02:00
// console images
2011-02-11 22:00:00 +01:00
int background; // console background
2010-07-19 22:00:00 +02:00
2010-07-20 22:00:00 +02:00
// conchars
cl_font_t chars; // fonts.wad/font1.fnt
int charHeight;
byte charWidths[256];
2010-07-19 22:00:00 +02:00
// console input
2010-07-20 22:00:00 +02:00
field_t input;
2010-07-19 22:00:00 +02:00
// console history
2010-07-20 22:00:00 +02:00
field_t historyLines[CON_HISTORY];
int historyLine; // the line being displayed from history buffer will be <= nextHistoryLine
int nextHistoryLine; // the last line in the history buffer, not masked
2010-07-19 22:00:00 +02:00
2011-03-07 22:00:00 +01:00
notify_t notify[MAX_DBG_NOTIFY]; // for Con_NXPrintf
qboolean draw_notify; // true if we have NXPrint message
2010-07-19 22:00:00 +02:00
// console auto-complete
2010-07-20 22:00:00 +02:00
field_t *completionField;
char *completionString;
string shortestMatch;
int matchCount;
2010-07-19 22:00:00 +02:00
} console_t;
2010-07-20 22:00:00 +02:00
int g_console_field_width = 78;
2010-07-19 22:00:00 +02:00
static console_t con;
void Field_CharEvent( field_t *edit, int ch );
/*
================
Con_Clear_f
================
*/
void Con_Clear_f( void )
{
int i;
for( i = 0; i < CON_TEXTSIZE; i++ )
2010-07-20 22:00:00 +02:00
con.text[i] = ( ColorIndex( COLOR_DEFAULT ) << 8 ) | ' ';
con.display = con.current; // go to end
2010-07-19 22:00:00 +02:00
}
2010-10-23 22:00:00 +02:00
/*
================
Con_SetColor_f
================
*/
void Con_SetColor_f( void )
{
vec3_t color;
switch( Cmd_Argc() )
{
case 1:
Msg( "\"con_color\" is %i %i %i\n", g_color_table[7][0], g_color_table[7][1], g_color_table[7][2] );
break;
case 2:
VectorSet( color, g_color_table[7][0], g_color_table[7][1], g_color_table[7][2] );
2011-03-09 22:00:00 +01:00
Q_atov( color, Cmd_Argv( 1 ), 3 );
2010-10-23 22:00:00 +02:00
Con_DefaultColor( color[0], color[1], color[2] );
break;
default:
Msg( "Usage: con_color \"r g b\"\n" );
break;
}
}
2010-07-19 22:00:00 +02:00
/*
================
Con_ClearNotify
================
*/
void Con_ClearNotify( void )
{
int i;
for( i = 0; i < CON_TIMES; i++ )
2010-07-23 22:00:00 +02:00
con.times[i] = 0;
2010-07-19 22:00:00 +02:00
}
/*
================
Con_ClearField
================
*/
void Con_ClearField( field_t *edit )
{
2011-03-10 22:00:00 +01:00
Q_memset( edit->buffer, 0, MAX_STRING );
2010-07-19 22:00:00 +02:00
edit->cursor = 0;
edit->scroll = 0;
}
/*
================
Con_ClearTyping
================
*/
void Con_ClearTyping( void )
{
Con_ClearField( &con.input );
con.input.widthInChars = g_console_field_width;
}
2011-03-09 22:00:00 +01:00
/*
============
Con_StringLength
skipped color prefixes
============
*/
int Con_StringLength( const char *string )
{
int len;
const char *p;
if( !string ) return 0;
len = 0;
p = string;
while( *p )
{
if( IsColorString( p ))
{
p += 2;
continue;
}
p++;
len++;
}
return len;
}
2010-07-19 22:00:00 +02:00
/*
================
Con_ToggleConsole_f
================
*/
void Con_ToggleConsole_f( void )
{
if( !host.developer ) return; // disabled
// show console only in game or by special call from menu
if( cls.state != ca_active || cls.key_dest == key_menu )
return;
Con_ClearTyping();
Con_ClearNotify();
if( cls.key_dest == key_console )
{
2011-02-26 22:00:00 +01:00
if( Cvar_VariableInteger( "sv_background" ))
UI_SetActiveMenu( true );
else UI_SetActiveMenu( false );
2010-07-19 22:00:00 +02:00
}
else
{
UI_SetActiveMenu( false );
2010-12-27 22:00:00 +01:00
Key_SetKeyDest( key_console );
2010-07-19 22:00:00 +02:00
}
}
/*
================
Con_CheckResize
If the line width has changed, reformat the buffer.
================
*/
void Con_CheckResize( void )
{
int i, j, width, oldwidth, oldtotallines, numlines, numchars;
short tbuf[CON_TEXTSIZE];
2010-07-20 22:00:00 +02:00
width = ( scr_width->integer / 8 ) - 2;
2010-07-19 22:00:00 +02:00
if( width == con.linewidth )
return;
2010-12-02 22:00:00 +01:00
if( !glw_state.initialized )
2010-07-19 22:00:00 +02:00
{
2010-07-20 22:00:00 +02:00
// video hasn't been initialized yet
width = g_console_field_width;
2010-07-19 22:00:00 +02:00
con.linewidth = width;
con.totallines = CON_TEXTSIZE / con.linewidth;
2010-07-20 22:00:00 +02:00
for( i = 0; i < CON_TEXTSIZE; i++ )
con.text[i] = ( ColorIndex( COLOR_DEFAULT ) << 8 ) | ' ';
2010-07-19 22:00:00 +02:00
}
else
{
oldwidth = con.linewidth;
con.linewidth = g_console_field_width;
oldtotallines = con.totallines;
con.totallines = CON_TEXTSIZE / con.linewidth;
numlines = oldtotallines;
if( con.totallines < numlines )
numlines = con.totallines;
numchars = oldwidth;
if( con.linewidth < numchars )
numchars = con.linewidth;
2011-03-10 22:00:00 +01:00
Q_memcpy( tbuf, con.text, CON_TEXTSIZE * sizeof( short ));
2010-07-19 22:00:00 +02:00
for( i = 0; i < CON_TEXTSIZE; i++ )
2010-07-20 22:00:00 +02:00
con.text[i] = ( ColorIndex( COLOR_DEFAULT ) << 8 ) | ' ';
2010-07-19 22:00:00 +02:00
2010-07-20 22:00:00 +02:00
for( i = 0; i < numlines; i++ )
2010-07-19 22:00:00 +02:00
{
2010-07-20 22:00:00 +02:00
for( j = 0; j < numchars; j++ )
2010-07-19 22:00:00 +02:00
{
2010-07-20 22:00:00 +02:00
con.text[(con.totallines - 1 - i) * con.linewidth + j] = tbuf[((con.current - i + oldtotallines) % oldtotallines) * oldwidth + j];
2010-07-19 22:00:00 +02:00
}
}
Con_ClearNotify ();
}
con.current = con.totallines - 1;
con.display = con.current;
}
2010-07-20 22:00:00 +02:00
/*
================
Con_PageUp
================
*/
void Con_PageUp( void )
{
con.display -= 2;
if( con.current - con.display >= con.totallines )
con.display = con.current - con.totallines + 1;
}
/*
================
Con_PageDown
================
*/
void Con_PageDown( void )
{
con.display += 2;
2010-08-12 22:00:00 +02:00
if( con.display > con.current )
2010-07-20 22:00:00 +02:00
con.display = con.current;
}
/*
================
Con_Top
================
*/
void Con_Top( void )
{
con.display = con.totallines;
if( con.current - con.display >= con.totallines )
con.display = con.current - con.totallines + 1;
}
/*
================
Con_Bottom
================
*/
void Con_Bottom( void )
{
con.display = con.current;
}
/*
================
Con_Visible
================
*/
2010-10-26 22:00:00 +02:00
qboolean Con_Visible( void )
2010-07-20 22:00:00 +02:00
{
return (con.finalFrac != 0.0f);
}
/*
================
Con_LoadConchars
================
*/
static void Con_LoadConchars( void )
{
int fontWidth, fontHeight;
// make sure fontnum in-range
2010-10-31 22:00:00 +01:00
if( con_fontsize->integer < 0 ) Cvar_SetFloat( "con_fontsize", 0 );
if( con_fontsize->integer > 2 ) Cvar_SetFloat( "con_fontsize", 2 );
2010-07-20 22:00:00 +02:00
2010-09-02 22:00:00 +02:00
// select properly fontsize
2010-10-31 22:00:00 +01:00
if( scr_width->integer < 640 ) Cvar_SetFloat( "con_fontsize", 0 );
else if( scr_width->integer >= 1280 ) Cvar_SetFloat( "con_fontsize", 2 );
else Cvar_SetFloat( "con_fontsize", 1 );
2010-09-02 22:00:00 +02:00
2010-07-20 22:00:00 +02:00
// loading conchars
2010-12-02 22:00:00 +01:00
con.chars.hFontTexture = GL_LoadTexture( va( "fonts/font%i", con_fontsize->integer ), NULL, 0, TF_FONT );
2010-07-20 22:00:00 +02:00
if( !con_fontsize->modified ) return; // font not changed
2010-12-02 22:00:00 +01:00
R_GetTextureParms( &fontWidth, &fontHeight, con.chars.hFontTexture );
2010-07-20 22:00:00 +02:00
// setup creditsfont
2011-03-08 22:00:00 +01:00
if( FS_FileExists( va( "fonts/font%i.fnt", con_fontsize->integer ), false ))
2010-07-20 22:00:00 +02:00
{
byte *buffer;
size_t length;
qfont_t *src;
// half-life font with variable chars witdh
2011-03-08 22:00:00 +01:00
buffer = FS_LoadFile( va( "fonts/font%i", con_fontsize->integer ), &length, false );
2010-07-20 22:00:00 +02:00
if( buffer && length >= sizeof( qfont_t ))
{
int i;
src = (qfont_t *)buffer;
2010-11-21 22:00:00 +01:00
con.charHeight = src->rowheight;
2010-07-20 22:00:00 +02:00
// build rectangles
for( i = 0; i < 256; i++ )
{
2010-11-21 22:00:00 +01:00
con.chars.fontRc[i].left = (word)src->fontinfo[i].startoffset % fontWidth;
con.chars.fontRc[i].right = con.chars.fontRc[i].left + src->fontinfo[i].charwidth;
con.chars.fontRc[i].top = (word)src->fontinfo[i].startoffset / fontWidth;
con.chars.fontRc[i].bottom = con.chars.fontRc[i].top + src->rowheight;
con.charWidths[i] = src->fontinfo[i].charwidth;
2010-07-20 22:00:00 +02:00
}
con.chars.valid = true;
}
if( buffer ) Mem_Free( buffer );
}
}
static int Con_DrawGenericChar( int x, int y, int number, rgba_t color )
{
int width, height;
float s1, t1, s2, t2;
wrect_t *rc;
number &= 255;
if( !con.chars.valid )
return 0;
if( number < 32 ) return 0;
if( y < -con.charHeight )
return 0;
rc = &con.chars.fontRc[number];
2010-12-14 22:00:00 +01:00
pglColor4ubv( color );
2010-12-02 22:00:00 +01:00
R_GetTextureParms( &width, &height, con.chars.hFontTexture );
2010-07-20 22:00:00 +02:00
// calc rectangle
s1 = (float)rc->left / width;
t1 = (float)rc->top / height;
s2 = (float)rc->right / width;
t2 = (float)rc->bottom / height;
width = rc->right - rc->left;
height = rc->bottom - rc->top;
2010-12-02 22:00:00 +01:00
R_DrawStretchPic( x, y, width, height, s1, t1, s2, t2, con.chars.hFontTexture );
2010-12-14 22:00:00 +01:00
pglColor4ub( 255, 255, 255, 255 ); // don't forget reset color
2010-07-20 22:00:00 +02:00
return con.charWidths[number];
}
static int Con_DrawCharacter( int x, int y, int number, rgba_t color )
{
2011-02-26 22:00:00 +01:00
GL_SetRenderMode( kRenderTransTexture );
2010-07-20 22:00:00 +02:00
return Con_DrawGenericChar( x, y, number, color );
}
void Con_DrawStringLen( const char *pText, int *length, int *height )
{
2010-08-04 22:00:00 +02:00
int curLength = 0;
2010-07-20 22:00:00 +02:00
if( height ) *height = con.charHeight;
if( !length ) return;
*length = 0;
2010-08-04 22:00:00 +02:00
while( *pText )
2010-07-20 22:00:00 +02:00
{
byte c = *pText;
2010-08-04 22:00:00 +02:00
if( *pText == '\n' )
{
pText++;
curLength = 0;
}
2010-07-20 22:00:00 +02:00
// skip color strings they are not drawing
if( IsColorString( pText ))
{
pText += 2;
continue;
}
2010-08-04 22:00:00 +02:00
curLength += con.charWidths[c];
2010-07-20 22:00:00 +02:00
pText++;
2010-08-04 22:00:00 +02:00
if( curLength > *length )
*length = curLength;
2010-07-20 22:00:00 +02:00
}
}
/*
==================
Con_DrawString
Draws a multi-colored string, optionally forcing
to a fixed color.
==================
*/
2010-10-26 22:00:00 +02:00
int Con_DrawGenericString( int x, int y, const char *string, rgba_t setColor, qboolean forceColor, int hideChar )
2010-07-20 22:00:00 +02:00
{
rgba_t color;
int drawLen = 0;
int numDraws = 0;
const char *s;
// draw the colored text
s = string;
*(uint *)color = *(uint *)setColor;
while ( *s )
{
2010-08-04 22:00:00 +02:00
if( *s == '\n' )
{
s++;
drawLen = 0; // begin new row
y += con.charHeight;
}
2010-07-20 22:00:00 +02:00
if( IsColorString( s ))
{
if( !forceColor )
{
2011-03-10 22:00:00 +01:00
Q_memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ));
2010-07-20 22:00:00 +02:00
color[3] = setColor[3];
}
s += 2;
numDraws++;
continue;
}
// hide char for overstrike mode
if( hideChar == numDraws )
drawLen += con.charWidths[*s];
else drawLen += Con_DrawCharacter( x + drawLen, y, *s, color );
numDraws++;
s++;
}
2010-12-14 22:00:00 +01:00
pglColor4ub( 255, 255, 255, 255 );
2010-07-20 22:00:00 +02:00
return drawLen;
}
int Con_DrawString( int x, int y, const char *string, rgba_t setColor )
{
return Con_DrawGenericString( x, y, string, setColor, false, -1 );
}
2010-07-19 22:00:00 +02:00
/*
================
Con_Init
================
*/
void Con_Init( void )
{
int i;
2010-07-20 22:00:00 +02:00
// must be init before startup video subsystem
scr_width = Cvar_Get( "width", "640", 0, "screen width" );
scr_height = Cvar_Get( "height", "480", 0, "screen height" );
scr_conspeed = Cvar_Get( "scr_conspeed", "600", 0, "console moving speed" );
2010-07-19 22:00:00 +02:00
con_notifytime = Cvar_Get( "con_notifytime", "3", 0, "notify time to live" );
2010-07-21 22:00:00 +02:00
con_fontsize = Cvar_Get( "con_fontsize", "1", CVAR_ARCHIVE|CVAR_LATCH, "console font number (0, 1 or 2)" );
2010-07-20 22:00:00 +02:00
Con_CheckResize();
2010-07-19 22:00:00 +02:00
Con_ClearField( &con.input );
con.input.widthInChars = g_console_field_width;
for( i = 0; i < CON_HISTORY; i++ )
{
Con_ClearField( &con.historyLines[i] );
con.historyLines[i].widthInChars = g_console_field_width;
}
Cmd_AddCommand( "toggleconsole", Con_ToggleConsole_f, "opens or closes the console" );
2010-10-23 22:00:00 +02:00
Cmd_AddCommand( "con_color", Con_SetColor_f, "set a custom console color" );
2010-07-19 22:00:00 +02:00
Cmd_AddCommand( "clear", Con_Clear_f, "clear console history" );
MsgDev( D_NOTE, "Console initialized.\n" );
con.initialized = true;
}
/*
===============
Con_Linefeed
===============
*/
2010-10-26 22:00:00 +02:00
void Con_Linefeed( qboolean skipnotify )
2010-07-19 22:00:00 +02:00
{
int i;
// mark time for transparent overlay
if( con.current >= 0 )
{
if( skipnotify ) con.times[con.current % CON_TIMES] = 0;
2010-10-09 22:00:00 +02:00
else con.times[con.current % CON_TIMES] = host.realtime;
2010-07-19 22:00:00 +02:00
}
con.x = 0;
if( con.display == con.current ) con.display++;
con.current++;
for( i = 0; i < con.linewidth; i++ )
2010-07-20 22:00:00 +02:00
con.text[(con.current % con.totallines) * con.linewidth+i] = ( ColorIndex( COLOR_DEFAULT ) << 8 ) | ' ';
2010-07-19 22:00:00 +02:00
}
/*
================
Con_Print
Handles cursor positioning, line wrapping, etc
All console printing must go through this in order to be logged to disk
If no console is visible, the text will appear at the top of the game window
================
*/
void Con_Print( const char *txt )
{
int y, c, l, color;
2010-10-26 22:00:00 +02:00
qboolean skipnotify = false;
2010-07-19 22:00:00 +02:00
int prev;
// client not running
if( host.type == HOST_DEDICATED ) return;
if( !con.initialized ) return;
2011-03-09 22:00:00 +01:00
if( !Q_strncmp( txt, "[skipnotify]", 12 ))
2010-07-19 22:00:00 +02:00
{
skipnotify = true;
txt += 12;
}
2010-07-20 22:00:00 +02:00
color = ColorIndex( COLOR_DEFAULT );
2010-07-19 22:00:00 +02:00
2010-07-21 22:00:00 +02:00
while(( c = *txt ) != 0 )
2010-07-19 22:00:00 +02:00
{
2010-07-21 22:00:00 +02:00
if( IsColorString( txt ))
2010-07-19 22:00:00 +02:00
{
color = ColorIndex(*(txt+1));
txt += 2;
continue;
}
// count word length
for( l = 0; l < con.linewidth; l++ )
{
if( txt[l] <= ' ') break;
}
// word wrap
2010-07-21 22:00:00 +02:00
if( l != con.linewidth && ( con.x + l >= con.linewidth ))
2010-07-19 22:00:00 +02:00
Con_Linefeed( skipnotify);
txt++;
2010-07-20 22:00:00 +02:00
switch( c )
2010-07-19 22:00:00 +02:00
{
case '\n':
Con_Linefeed( skipnotify );
break;
case '\r':
con.x = 0;
break;
default: // display character and advance
y = con.current % con.totallines;
con.text[y*con.linewidth+con.x] = (color << 8) | c;
con.x++;
if( con.x >= con.linewidth )
{
Con_Linefeed( skipnotify );
con.x = 0;
}
break;
}
}
// mark time for transparent overlay
if( con.current >= 0 )
{
if( skipnotify )
{
prev = con.current % CON_TIMES - 1;
if( prev < 0 ) prev = CON_TIMES - 1;
con.times[prev] = 0;
}
2010-10-09 22:00:00 +02:00
else con.times[con.current % CON_TIMES] = host.realtime;
2010-07-19 22:00:00 +02:00
}
}
2010-08-20 22:00:00 +02:00
void Con_NPrintf( int idx, char *fmt, ... )
{
2011-03-07 22:00:00 +01:00
char buffer[2048]; // must support > 1k messages
va_list args;
if( idx < 0 || idx >= MAX_DBG_NOTIFY )
return;
va_start( args, fmt );
2011-03-09 22:00:00 +01:00
Q_vsnprintf( buffer, 2048, fmt, args );
2011-03-07 22:00:00 +01:00
va_end( args );
2011-03-09 22:00:00 +01:00
Q_snprintf( con.notify[idx].szNotify, sizeof( con.notify[idx].szNotify ), buffer );
2011-03-07 22:00:00 +01:00
// reset values
con.notify[idx].expire = host.realtime + 4.0f;
MakeRGBA( con.notify[idx].color, 255, 255, 255, 255 );
con.draw_notify = true;
2010-08-20 22:00:00 +02:00
}
void Con_NXPrintf( con_nprint_t *info, char *fmt, ... )
{
2011-03-07 22:00:00 +01:00
char buffer[2048]; // must support > 1k messages
va_list args;
if( !info ) return;
if( info->index < 0 || info->index >= MAX_DBG_NOTIFY )
return;
va_start( args, fmt );
2011-03-09 22:00:00 +01:00
Q_vsnprintf( buffer, 2048, fmt, args );
2011-03-07 22:00:00 +01:00
va_end( args );
2011-03-09 22:00:00 +01:00
Q_snprintf( con.notify[info->index].szNotify, sizeof( con.notify[info->index].szNotify ), buffer );
2011-03-07 22:00:00 +01:00
// setup values
con.notify[info->index].expire = host.realtime + info->time_to_live;
MakeRGBA( con.notify[info->index].color, info->color[0] * 255, info->color[1] * 255, info->color[2] * 255, 255 );
con.draw_notify = true;
2010-08-20 22:00:00 +02:00
}
2010-07-19 22:00:00 +02:00
/*
=============================================================================
EDIT FIELDS
=============================================================================
*/
/*
===============
Cmd_CheckName
compare first argument with string
===============
*/
2010-10-26 22:00:00 +02:00
static qboolean Cmd_CheckName( const char *name )
2010-07-19 22:00:00 +02:00
{
2011-03-09 22:00:00 +01:00
if( !Q_stricmp( Cmd_Argv( 0 ), name ))
2010-07-19 22:00:00 +02:00
return true;
2011-03-09 22:00:00 +01:00
if( !Q_stricmp( Cmd_Argv( 0 ), va( "\\%s", name )))
2010-07-19 22:00:00 +02:00
return true;
return false;
}
/*
===============
pfnFindMatches
===============
*/
static void pfnFindMatches( const char *s, const char *unused1, const char *unused2, void *unused3 )
{
int i;
if( *s == '@' ) return; // never show system cvars or cmds
2011-03-09 22:00:00 +01:00
if( Q_strnicmp( s, con.completionString, Q_strlen( con.completionString )))
2010-07-19 22:00:00 +02:00
return;
con.matchCount++;
if( con.matchCount == 1 )
{
2011-03-09 22:00:00 +01:00
Q_strncpy( con.shortestMatch, s, sizeof( con.shortestMatch ));
2010-07-19 22:00:00 +02:00
return;
}
// cut shortestMatch to the amount common with s
for( i = 0; s[i]; i++ )
{
2011-03-09 22:00:00 +01:00
if( Q_tolower( con.shortestMatch[i] ) != Q_tolower( s[i] ))
2010-07-19 22:00:00 +02:00
con.shortestMatch[i] = 0;
}
}
/*
===============
pfnPrintMatches
===============
*/
static void pfnPrintMatches( const char *s, const char *unused1, const char *m, void *unused2 )
{
2011-03-09 22:00:00 +01:00
if( !Q_strnicmp( s, con.shortestMatch, Q_strlen( con.shortestMatch )))
2010-07-19 22:00:00 +02:00
{
if( m && *m ) Msg( " %s ^3\"%s\"\n", s, m );
else Msg( " %s\n", s ); // variable or command without description
}
}
static void ConcatRemaining( const char *src, const char *start )
{
char *arg;
int i;
2011-03-09 22:00:00 +01:00
arg = Q_strstr( src, start );
2010-07-19 22:00:00 +02:00
if( !arg )
{
for( i = 1; i < Cmd_Argc(); i++ )
{
2011-03-09 22:00:00 +01:00
Q_strncat( con.completionField->buffer, " ", sizeof( con.completionField->buffer ));
2010-07-19 22:00:00 +02:00
arg = Cmd_Argv( i );
while( *arg )
{
if( *arg == ' ' )
{
2011-03-09 22:00:00 +01:00
Q_strncat( con.completionField->buffer, "\"", sizeof( con.completionField->buffer ));
2010-07-19 22:00:00 +02:00
break;
}
arg++;
}
2011-03-09 22:00:00 +01:00
Q_strncat( con.completionField->buffer, Cmd_Argv( i ), sizeof( con.completionField->buffer ));
if( *arg == ' ' ) Q_strncat( con.completionField->buffer, "\"", sizeof( con.completionField->buffer ));
2010-07-19 22:00:00 +02:00
}
return;
}
2011-03-09 22:00:00 +01:00
arg += Q_strlen( start );
Q_strncat( con.completionField->buffer, arg, sizeof( con.completionField->buffer ));
2010-07-19 22:00:00 +02:00
}
/*
===============
Con_CompleteCommand
perform Tab expansion
===============
*/
void Con_CompleteCommand( field_t *field )
{
field_t temp;
string filename;
autocomplete_list_t *list;
con.completionField = field;
// only look at the first token for completion purposes
Cmd_TokenizeString( con.completionField->buffer );
con.completionString = Cmd_Argv( 0 );
if( con.completionString[0] == '\\' || con.completionString[0] == '/' )
con.completionString++;
con.matchCount = 0;
con.shortestMatch[0] = 0;
2011-03-09 22:00:00 +01:00
if( !Q_strlen( con.completionString ))
2010-07-19 22:00:00 +02:00
return;
Cmd_LookupCmds( NULL, NULL, pfnFindMatches );
Cvar_LookupVars( 0, NULL, NULL, pfnFindMatches );
if( con.matchCount == 0 ) return; // no matches
2011-03-10 22:00:00 +01:00
Q_memcpy( &temp, con.completionField, sizeof( field_t ));
2010-07-19 22:00:00 +02:00
if( Cmd_Argc() == 2 )
{
2010-10-26 22:00:00 +02:00
qboolean result = false;
2010-07-19 22:00:00 +02:00
// autocomplete second arg
for( list = cmd_list; list->name; list++ )
{
if( Cmd_CheckName( list->name ))
{
result = list->func( Cmd_Argv(1), filename, MAX_STRING );
break;
}
}
if( result )
{
2011-03-09 22:00:00 +01:00
Q_sprintf( con.completionField->buffer, "%s %s", Cmd_Argv( 0 ), filename );
con.completionField->cursor = Q_strlen( con.completionField->buffer );
2010-07-19 22:00:00 +02:00
return;
}
}
if( con.matchCount == 1 )
{
2011-03-09 22:00:00 +01:00
Q_sprintf( con.completionField->buffer, "\\%s", con.shortestMatch );
2010-07-19 22:00:00 +02:00
if( Cmd_Argc() == 1 )
2011-03-09 22:00:00 +01:00
Q_strncat( con.completionField->buffer, " ", sizeof( con.completionField->buffer ));
2010-07-19 22:00:00 +02:00
else ConcatRemaining( temp.buffer, con.completionString );
2011-03-09 22:00:00 +01:00
con.completionField->cursor = Q_strlen( con.completionField->buffer );
2010-07-19 22:00:00 +02:00
return;
}
// multiple matches, complete to shortest
2011-03-09 22:00:00 +01:00
Q_sprintf( con.completionField->buffer, "\\%s", con.shortestMatch );
con.completionField->cursor = Q_strlen( con.completionField->buffer );
2010-07-19 22:00:00 +02:00
ConcatRemaining( temp.buffer, con.completionString );
Msg( "]%s\n", con.completionField->buffer );
// run through again, printing matches
Cmd_LookupCmds( NULL, NULL, pfnPrintMatches );
Cvar_LookupVars( 0, NULL, NULL, pfnPrintMatches );
}
/*
================
Field_Paste
================
*/
void Field_Paste( field_t *edit )
{
char *cbd;
2010-07-20 22:00:00 +02:00
int i, pasteLen;
2010-07-19 22:00:00 +02:00
cbd = Sys_GetClipboardData();
2010-07-20 22:00:00 +02:00
if( !cbd ) return;
2010-07-19 22:00:00 +02:00
// send as if typed, so insert / overstrike works properly
2011-03-09 22:00:00 +01:00
pasteLen = Q_strlen( cbd );
2010-07-20 22:00:00 +02:00
for( i = 0; i < pasteLen; i++ )
Field_CharEvent( edit, cbd[i] );
2010-07-19 22:00:00 +02:00
Mem_Free( cbd );
}
/*
=================
Field_KeyDownEvent
Performs the basic line editing functions for the console,
in-game talk, and menu fields
Key events are used for non-printable characters, others are gotten from char events.
=================
*/
void Field_KeyDownEvent( field_t *edit, int key )
{
int len;
// shift-insert is paste
if((( key == K_INS ) || ( key == K_KP_INS )) && Key_IsDown( K_SHIFT ))
{
Field_Paste( edit );
return;
}
2011-03-09 22:00:00 +01:00
len = Q_strlen( edit->buffer );
2010-07-19 22:00:00 +02:00
if ( key == K_DEL )
{
if ( edit->cursor < len )
{
memmove( edit->buffer + edit->cursor, edit->buffer + edit->cursor + 1, len - edit->cursor );
}
return;
}
if ( key == K_BACKSPACE )
{
if ( edit->cursor > 0 )
{
memmove( edit->buffer + edit->cursor - 1, edit->buffer + edit->cursor, len - edit->cursor + 1 );
edit->cursor--;
if ( edit->scroll ) edit->scroll--;
}
return;
}
if ( key == K_RIGHTARROW )
{
if ( edit->cursor < len ) edit->cursor++;
if ( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len )
edit->scroll++;
return;
}
if ( key == K_LEFTARROW )
{
if ( edit->cursor > 0 ) edit->cursor--;
if ( edit->cursor < edit->scroll ) edit->scroll--;
return;
}
2011-03-09 22:00:00 +01:00
if ( key == K_HOME || ( Q_tolower(key) == 'a' && Key_IsDown( K_CTRL )))
2010-07-19 22:00:00 +02:00
{
edit->cursor = 0;
return;
}
2011-03-09 22:00:00 +01:00
if ( key == K_END || ( Q_tolower(key) == 'e' && Key_IsDown( K_CTRL )))
2010-07-19 22:00:00 +02:00
{
edit->cursor = len;
return;
}
if ( key == K_INS )
{
host.key_overstrike = !host.key_overstrike;
return;
}
}
/*
==================
Field_CharEvent
==================
*/
void Field_CharEvent( field_t *edit, int ch )
{
int len;
if( ch == 'v' - 'a' + 1 )
{
// ctrl-v is paste
Field_Paste( edit );
return;
}
if( ch == 'c' - 'a' + 1 )
{
// ctrl-c clears the field
Con_ClearField( edit );
return;
}
2011-03-09 22:00:00 +01:00
len = Q_strlen( edit->buffer );
2010-07-19 22:00:00 +02:00
if( ch == 'a' - 'a' + 1 )
{
// ctrl-a is home
edit->cursor = 0;
edit->scroll = 0;
return;
}
if( ch == 'e' - 'a' + 1 )
{
// ctrl-e is end
edit->cursor = len;
edit->scroll = edit->cursor - edit->widthInChars;
return;
}
// ignore any other non printable chars
if ( ch < 32 ) return;
if ( host.key_overstrike )
{
if ( edit->cursor == MAX_STRING - 1 ) return;
edit->buffer[edit->cursor] = ch;
edit->cursor++;
}
else
{
// insert mode
if ( len == MAX_STRING - 1 ) return; // all full
memmove( edit->buffer + edit->cursor + 1, edit->buffer + edit->cursor, len + 1 - edit->cursor );
edit->buffer[edit->cursor] = ch;
edit->cursor++;
}
if( edit->cursor >= edit->widthInChars ) edit->scroll++;
if( edit->cursor == len + 1) edit->buffer[edit->cursor] = 0;
}
/*
=============================================================================
CONSOLE LINE EDITING
==============================================================================
*/
/*
====================
Key_Console
Handles history and console scrollback
====================
*/
void Key_Console( int key )
{
// ctrl-L clears screen
if( key == 'l' && Key_IsDown( K_CTRL ))
{
Cbuf_AddText( "clear\n" );
return;
}
// enter finishes the line
if ( key == K_ENTER || key == K_KP_ENTER )
{
// if not in the game explicitly prepent a slash if needed
if( cls.state != ca_active && con.input.buffer[0] != '\\' && con.input.buffer[0] != '/' )
{
char temp[MAX_SYSPATH];
2011-03-09 22:00:00 +01:00
Q_strncpy( temp, con.input.buffer, sizeof( temp ));
Q_sprintf( con.input.buffer, "\\%s", temp );
2010-07-19 22:00:00 +02:00
con.input.cursor++;
}
// backslash text are commands, else chat
if( con.input.buffer[0] == '\\' || con.input.buffer[0] == '/' )
Cbuf_AddText( con.input.buffer + 1 ); // skip backslash
else Cbuf_AddText( con.input.buffer ); // valid command
Cbuf_AddText( "\n" );
// echo to console
Msg( ">%s\n", con.input.buffer );
// copy line to history buffer
con.historyLines[con.nextHistoryLine % CON_HISTORY] = con.input;
con.nextHistoryLine++;
con.historyLine = con.nextHistoryLine;
Con_ClearField( &con.input );
con.input.widthInChars = g_console_field_width;
if( cls.state == ca_disconnected )
{
// force an update, because the command may take some time
SCR_UpdateScreen ();
}
return;
}
// command completion
if( key == K_TAB )
{
Con_CompleteCommand( &con.input );
return;
}
// command history (ctrl-p ctrl-n for unix style)
2011-03-09 22:00:00 +01:00
if(( key == K_MWHEELUP && Key_IsDown( K_SHIFT )) || ( key == K_UPARROW ) || (( Q_tolower(key) == 'p' ) && Key_IsDown( K_CTRL )))
2010-07-19 22:00:00 +02:00
{
if( con.nextHistoryLine - con.historyLine < CON_HISTORY && con.historyLine > 0 )
{
con.historyLine--;
}
con.input = con.historyLines[con.historyLine % CON_HISTORY];
return;
}
2011-03-09 22:00:00 +01:00
if(( key == K_MWHEELDOWN && Key_IsDown( K_SHIFT )) || ( key == K_DOWNARROW ) || (( Q_tolower(key) == 'n' ) && Key_IsDown( K_CTRL )))
2010-07-19 22:00:00 +02:00
{
if( con.historyLine == con.nextHistoryLine ) return;
con.historyLine++;
con.input = con.historyLines[con.historyLine % CON_HISTORY];
return;
}
// console scrolling
if( key == K_PGUP )
{
Con_PageUp();
return;
}
if( key == K_PGDN )
{
Con_PageDown();
return;
}
if( key == K_MWHEELUP )
{
Con_PageUp();
if( Key_IsDown( K_CTRL ))
{
Con_PageUp();
Con_PageUp();
}
return;
}
if( key == K_MWHEELDOWN )
{
Con_PageDown();
if( Key_IsDown( K_CTRL ))
{
Con_PageDown();
Con_PageDown();
}
return;
}
// ctrl-home = top of console
if( key == K_HOME && Key_IsDown( K_CTRL ))
{
Con_Top();
return;
}
// ctrl-end = bottom of console
if( key == K_END && Key_IsDown( K_CTRL ))
{
Con_Bottom();
return;
}
// pass to the normal editline routine
Field_KeyDownEvent( &con.input, key );
}
/*
==============================================================================
DRAWING
==============================================================================
*/
/*
================
Con_DrawInput
The input line scrolls horizontally if typing goes beyond the right edge
================
*/
void Con_DrawInput( void )
{
2010-07-20 22:00:00 +02:00
int len;
int drawLen, hideChar = -1;
int prestep, curPos;
int x, y, cursorChar;
char str[MAX_SYSPATH];
byte *colorDefault;
// don't draw anything (always draw if not active)
if( cls.key_dest != key_console ) return;
2010-07-19 22:00:00 +02:00
2010-07-20 22:00:00 +02:00
x = QCHAR_WIDTH; // room for ']'
y = con.vislines - ( con.charHeight * 2 );
drawLen = con.input.widthInChars;
2011-03-09 22:00:00 +01:00
len = Q_strlen( con.input.buffer ) + 1;
2010-07-20 22:00:00 +02:00
colorDefault = g_color_table[ColorIndex( COLOR_DEFAULT )];
// guarantee that cursor will be visible
if( len <= drawLen )
{
prestep = 0;
}
else
{
if( con.input.scroll + drawLen > len )
{
con.input.scroll = len - drawLen;
if( con.input.scroll < 0 )
con.input.scroll = 0;
}
prestep = con.input.scroll;
}
2010-07-19 22:00:00 +02:00
2010-07-20 22:00:00 +02:00
if( prestep + drawLen > len )
drawLen = len - prestep;
// extract <drawLen> characters from the field at <prestep>
if( drawLen >= MAX_SYSPATH ) Host_Error("drawLen >= MAX_SYSPATH\n" );
2011-03-10 22:00:00 +01:00
Q_memcpy( str, con.input.buffer + prestep, drawLen );
2010-07-20 22:00:00 +02:00
str[drawLen] = 0;
// save char for overstrike
cursorChar = str[con.input.cursor - prestep];
2010-10-09 22:00:00 +02:00
if( host.key_overstrike && cursorChar && !((int)( host.realtime * 4 ) & 1 ))
2010-07-20 22:00:00 +02:00
hideChar = con.input.cursor - prestep; // skip this char
// draw it
Con_DrawGenericString( x, y, str, colorDefault, false, hideChar );
Con_DrawCharacter( QCHAR_WIDTH >> 1, y, ']', colorDefault );
// draw the cursor
2010-10-09 22:00:00 +02:00
if((int)( host.realtime * 4 ) & 1 ) return; // off blink
2010-07-20 22:00:00 +02:00
// calc cursor position
str[con.input.cursor - prestep] = 0;
Con_DrawStringLen( str, &curPos, NULL );
if( host.key_overstrike && cursorChar )
{
// overstrike cursor
2010-12-02 22:00:00 +01:00
GL_SetRenderMode( kRenderTransInverse );
2010-07-20 22:00:00 +02:00
Con_DrawGenericChar( x + curPos, y, cursorChar, colorDefault );
}
else Con_DrawCharacter( x + curPos, y, '_', colorDefault );
2010-07-19 22:00:00 +02:00
}
2011-03-07 22:00:00 +01:00
/*
================
Con_DrawDebugLines
Custom debug messages
================
*/
int Con_DrawDebugLines( void )
{
int i, count = 0;
int y = 20;
for( i = 0; i < MAX_DBG_NOTIFY; i++ )
{
if( host.realtime < con.notify[i].expire )
{
int x, len;
int fontTall;
Con_DrawStringLen( con.notify[i].szNotify, &len, &fontTall );
x = scr_width->integer - 10 - len;
fontTall += 1;
if( y + fontTall > (int)scr_height->integer - 20 )
return count;
count++;
y = 20 + fontTall * i;
Con_DrawString( x, y, con.notify[i].szNotify, con.notify[i].color );
}
}
return count;
}
2010-07-19 22:00:00 +02:00
/*
================
Con_DrawNotify
Draws the last few lines of output transparently over the game top
================
*/
void Con_DrawNotify( void )
{
int i, x, v = 0;
2010-10-09 22:00:00 +02:00
int start, currentColor;
2010-07-19 22:00:00 +02:00
short *text;
2010-10-09 22:00:00 +02:00
float time;
2010-07-19 22:00:00 +02:00
2011-02-22 22:00:00 +01:00
if( !host.developer || Cvar_VariableInteger( "sv_background" ))
return;
2010-07-19 22:00:00 +02:00
2011-03-07 22:00:00 +01:00
if( con.draw_notify )
{
if( Con_DrawDebugLines () == 0 )
con.draw_notify = false;
}
2010-07-19 22:00:00 +02:00
currentColor = 7;
2010-12-14 22:00:00 +01:00
pglColor4ubv( g_color_table[currentColor] );
2010-07-19 22:00:00 +02:00
for( i = con.current - CON_TIMES + 1; i <= con.current; i++ )
{
if( i < 0 ) continue;
time = con.times[i % CON_TIMES];
2010-07-23 22:00:00 +02:00
if( time == 0 ) continue;
2010-10-09 22:00:00 +02:00
time = host.realtime - time;
2010-07-23 22:00:00 +02:00
2010-10-09 22:00:00 +02:00
if( time > con_notifytime->value )
2010-07-23 22:00:00 +02:00
continue; // expired
2010-07-19 22:00:00 +02:00
text = con.text + (i % con.totallines) * con.linewidth;
2010-07-20 22:00:00 +02:00
start = con.charWidths[' ']; // offset one space at left screen side
2010-07-19 22:00:00 +02:00
for( x = 0; x < con.linewidth; x++ )
{
2010-07-20 22:00:00 +02:00
if((( text[x] >> 8 ) & 7 ) != currentColor )
2010-07-19 22:00:00 +02:00
currentColor = ( text[x] >> 8 ) & 7;
2010-07-20 22:00:00 +02:00
start += Con_DrawCharacter( start, v, text[x] & 0xFF, g_color_table[currentColor] );
2010-07-19 22:00:00 +02:00
}
2010-07-20 22:00:00 +02:00
v += con.charHeight;
2010-07-19 22:00:00 +02:00
}
2010-12-14 22:00:00 +01:00
pglColor4ub( 255, 255, 255, 255 );
2010-07-19 22:00:00 +02:00
}
/*
================
Con_DrawConsole
Draws the console with the solid background
================
*/
void Con_DrawSolidConsole( float frac )
{
int i, x, y;
int rows;
short *text;
int row;
2010-07-20 22:00:00 +02:00
int lines, start;
2010-07-19 22:00:00 +02:00
int currentColor;
string curbuild;
lines = scr_height->integer * frac;
if( lines <= 0 ) return;
2010-07-20 22:00:00 +02:00
if( lines > scr_height->integer )
lines = scr_height->integer;
2010-07-19 22:00:00 +02:00
// draw the background
2010-07-20 22:00:00 +02:00
y = frac * scr_height->integer;
if( y >= 1 )
{
2010-12-02 22:00:00 +01:00
GL_SetRenderMode( kRenderNormal );
R_DrawStretchPic( 0, y - scr_height->integer, scr_width->integer, scr_height->integer, 0, 0, 1, 1, con.background );
2010-07-20 22:00:00 +02:00
}
else y = 0;
2010-07-19 22:00:00 +02:00
if( host.developer )
{
// draw current version
2010-07-23 22:00:00 +02:00
byte *color = g_color_table[7];
2010-07-20 22:00:00 +02:00
int stringLen, width = 0, charH;
2011-03-10 22:00:00 +01:00
Q_snprintf( curbuild, MAX_STRING, "Xash3D %i/%g (hw build %i)", PROTOCOL_VERSION, XASH_VERSION, com_buildnum( ));
2010-07-20 22:00:00 +02:00
Con_DrawStringLen( curbuild, &stringLen, &charH );
start = scr_width->integer - stringLen;
2011-03-09 22:00:00 +01:00
stringLen = Con_StringLength( curbuild );
2010-07-20 22:00:00 +02:00
for( i = 0; i < stringLen; i++ )
2010-07-23 22:00:00 +02:00
width += Con_DrawCharacter( start + width, 0, curbuild[i], color );
2010-07-19 22:00:00 +02:00
}
// draw the text
con.vislines = lines;
2010-07-20 22:00:00 +02:00
rows = ( lines - QCHAR_WIDTH ) / QCHAR_WIDTH; // rows of text to draw
y = lines - ( con.charHeight * 3 );
2010-07-19 22:00:00 +02:00
// draw from the bottom up
if( con.display != con.current )
{
2010-07-20 22:00:00 +02:00
start = con.charWidths[' ']; // offset one space at left screen side
// draw red arrows to show the buffer is backscrolled
2010-07-19 22:00:00 +02:00
for( x = 0; x < con.linewidth; x += 4 )
2010-07-20 22:00:00 +02:00
Con_DrawCharacter(( x + 1 ) * start, y, '^', g_color_table[1] );
y -= con.charHeight;
2010-07-19 22:00:00 +02:00
rows--;
}
row = con.display;
if( con.x == 0 ) row--;
currentColor = 7;
2010-12-14 22:00:00 +01:00
pglColor4ubv( g_color_table[currentColor] );
2010-07-19 22:00:00 +02:00
2010-07-20 22:00:00 +02:00
for( i = 0; i < rows; i++, y -= con.charHeight, row-- )
2010-07-19 22:00:00 +02:00
{
if( row < 0 ) break;
if( con.current - row >= con.totallines )
{
// past scrollback wrap point
continue;
}
2010-07-20 22:00:00 +02:00
text = con.text + ( row % con.totallines ) * con.linewidth;
start = con.charWidths[' ']; // offset one space at left screen side
2010-07-19 22:00:00 +02:00
for( x = 0; x < con.linewidth; x++ )
{
2010-07-20 22:00:00 +02:00
if((( text[x] >> 8 ) & 7 ) != currentColor )
currentColor = ( text[x] >> 8 ) & 7;
start += Con_DrawCharacter( start, y, text[x] & 0xFF, g_color_table[currentColor] );
2010-07-19 22:00:00 +02:00
}
}
// draw the input prompt, user text, and cursor if desired
Con_DrawInput();
2010-12-14 22:00:00 +01:00
pglColor4ub( 255, 255, 255, 255 );
2010-07-19 22:00:00 +02:00
}
/*
==================
Con_DrawConsole
==================
*/
void Con_DrawConsole( void )
{
// never draw console whel changelevel in-progress
if( cls.changelevel ) return;
// check for console width changes from a vid mode change
Con_CheckResize ();
if( cls.state == ca_connecting || cls.state == ca_connected )
{
2011-02-26 22:00:00 +01:00
if( !cl_allow_levelshots->integer )
2010-07-19 22:00:00 +02:00
{
2011-02-26 22:00:00 +01:00
if( Cvar_VariableInteger( "sv_background" ) && cls.key_dest != key_console )
con.displayFrac = con.finalFrac = 0.0f;
else con.displayFrac = con.finalFrac = 1.0f;
2010-07-19 22:00:00 +02:00
}
else
{
if( host.developer >= 4 )
{
con.displayFrac = 0.5f; // keep console open
}
else
{
con.finalFrac = 0.0f;
Con_RunConsole();
if( host.developer >= 2 )
Con_DrawNotify(); // draw notify lines
}
}
}
// if disconnected, render console full screen
switch( cls.state )
{
case ca_uninitialized:
break;
case ca_disconnected:
if( cls.key_dest != key_menu && host.developer )
{
Con_DrawSolidConsole( 1.0f );
2010-12-27 22:00:00 +01:00
Key_SetKeyDest( key_console );
2010-07-19 22:00:00 +02:00
}
break;
case ca_connected:
case ca_connecting:
// force to show console always for -dev 3 and higher
if( con.displayFrac ) Con_DrawSolidConsole( con.displayFrac );
break;
case ca_active:
case ca_cinematic:
2011-02-22 22:00:00 +01:00
if( Cvar_VariableInteger( "sv_background" ))
{
if( cls.key_dest == key_console )
Con_DrawSolidConsole( 1.0f );
}
else
{
if( con.displayFrac ) Con_DrawSolidConsole( con.displayFrac );
else if( cls.state == ca_active && cls.key_dest == key_game )
Con_DrawNotify(); // draw notify lines
}
2010-07-19 22:00:00 +02:00
break;
}
}
2010-12-28 22:00:00 +01:00
/*
==================
Con_DrawVersion
Used by menu
==================
*/
void Con_DrawVersion( void )
{
// draws the current build
byte *color = g_color_table[7];
int i, stringLen, width = 0, charH;
int start, height = scr_height->integer;
string curbuild;
if( cls.key_dest != key_menu ) return;
2011-03-10 22:00:00 +01:00
Q_snprintf( curbuild, MAX_STRING, "v%i/%g (build %i)", PROTOCOL_VERSION, XASH_VERSION, com_buildnum( ));
2010-12-28 22:00:00 +01:00
Con_DrawStringLen( curbuild, &stringLen, &charH );
start = scr_width->integer - stringLen * 1.05f;
2011-03-09 22:00:00 +01:00
stringLen = Con_StringLength( curbuild );
2010-12-28 22:00:00 +01:00
height -= charH * 1.5f;
for( i = 0; i < stringLen; i++ )
width += Con_DrawCharacter( start + width, height, curbuild[i], color );
}
2010-07-19 22:00:00 +02:00
/*
==================
Con_RunConsole
Scroll it up or down
==================
*/
void Con_RunConsole( void )
{
// decide on the destination height of the console
if( host.developer && cls.key_dest == key_console )
{
if( cls.state == ca_disconnected )
con.finalFrac = 1.0f;// full screen
else con.finalFrac = 0.5f; // half screen
}
else con.finalFrac = 0; // none visible
2010-10-09 22:00:00 +02:00
// when level is loading frametime may be is wrong
2010-07-19 22:00:00 +02:00
if( cls.state == ca_connecting || cls.state == ca_connected )
2010-10-09 22:00:00 +02:00
host.realframetime = ( MAX_FPS / host_maxfps->value ) * MIN_FRAMETIME;
2010-07-19 22:00:00 +02:00
if( con.finalFrac < con.displayFrac )
{
2010-10-09 22:00:00 +02:00
con.displayFrac -= scr_conspeed->value * 0.002 * host.realframetime;
2010-07-19 22:00:00 +02:00
if( con.finalFrac > con.displayFrac )
con.displayFrac = con.finalFrac;
}
else if( con.finalFrac > con.displayFrac )
{
2010-10-09 22:00:00 +02:00
con.displayFrac += scr_conspeed->value * 0.002 * host.realframetime;
2010-07-19 22:00:00 +02:00
if( con.finalFrac < con.displayFrac )
con.displayFrac = con.finalFrac;
}
}
/*
==============================================================================
CONSOLE INTERFACE
==============================================================================
*/
void Con_CharEvent( int key )
{
Field_CharEvent( &con.input, key );
}
void Con_VidInit( void )
{
2010-07-20 22:00:00 +02:00
g_console_field_width = ( scr_width->integer / 8 ) - 2;
2010-07-19 22:00:00 +02:00
con.input.widthInChars = g_console_field_width;
2010-07-20 22:00:00 +02:00
// loading console image
if( host.developer )
2010-10-21 22:00:00 +02:00
{
if( scr_width->integer < 640 )
{
2011-03-08 22:00:00 +01:00
if( FS_FileExists( "cached/conback400", false ))
2010-12-02 22:00:00 +01:00
con.background = GL_LoadTexture( "cached/conback400", NULL, 0, TF_IMAGE );
else con.background = GL_LoadTexture( "cached/conback", NULL, 0, TF_IMAGE );
2010-10-21 22:00:00 +02:00
}
else
{
2011-03-08 22:00:00 +01:00
if( FS_FileExists( "cached/conback640", false ))
2010-12-02 22:00:00 +01:00
con.background = GL_LoadTexture( "cached/conback640", NULL, 0, TF_IMAGE );
else con.background = GL_LoadTexture( "cached/conback", NULL, 0, TF_IMAGE );
2010-10-21 22:00:00 +02:00
}
}
else
{
if( scr_width->integer < 640 )
{
2011-03-08 22:00:00 +01:00
if( FS_FileExists( "cached/loading400", false ))
2010-12-02 22:00:00 +01:00
con.background = GL_LoadTexture( "cached/loading400", NULL, 0, TF_IMAGE );
else con.background = GL_LoadTexture( "cached/loading", NULL, 0, TF_IMAGE );
2010-10-21 22:00:00 +02:00
}
else
{
2011-03-08 22:00:00 +01:00
if( FS_FileExists( "cached/loading640", false ))
2010-12-02 22:00:00 +01:00
con.background = GL_LoadTexture( "cached/loading640", NULL, 0, TF_IMAGE );
else con.background = GL_LoadTexture( "cached/loading", NULL, 0, TF_IMAGE );
2010-10-21 22:00:00 +02:00
}
}
2010-07-20 22:00:00 +02:00
2011-02-05 22:00:00 +01:00
// missed console image will be replaced as white (GoldSrc rules)
if( con.background == tr.defaultTexture )
con.background = tr.whiteTexture;
2010-07-20 22:00:00 +02:00
Con_LoadConchars();
2010-07-19 22:00:00 +02:00
}
/*
=========
Cmd_AutoComplete
NOTE: input string must be equal or longer than MAX_STRING
=========
*/
void Cmd_AutoComplete( char *complete_string )
{
field_t input;
if( !complete_string || !*complete_string )
return;
// setup input
2011-03-09 22:00:00 +01:00
Q_strncpy( input.buffer, complete_string, sizeof( input.buffer ));
2010-07-19 22:00:00 +02:00
input.cursor = input.scroll = 0;
Con_CompleteCommand( &input );
// setup output
if( input.buffer[0] == '\\' || input.buffer[0] == '/' )
2011-03-09 22:00:00 +01:00
Q_strncpy( complete_string, input.buffer + 1, sizeof( input.buffer ));
else Q_strncpy( complete_string, input.buffer, sizeof( input.buffer ));
2010-07-19 22:00:00 +02:00
}
void Con_Close( void )
{
Con_ClearField( &con.input );
Con_ClearNotify();
con.finalFrac = 0; // none visible
con.displayFrac = 0;
}
2010-07-20 22:00:00 +02:00
/*
=========
Con_DefaultColor
2010-11-15 22:00:00 +01:00
called from MainUI
2010-07-20 22:00:00 +02:00
=========
*/
void Con_DefaultColor( int r, int g, int b )
2010-07-19 22:00:00 +02:00
{
2010-07-20 22:00:00 +02:00
r = bound( 0, r, 255 );
g = bound( 0, g, 255 );
b = bound( 0, b, 255 );
MakeRGBA( g_color_table[7], r, g, b, 255 );
2010-07-19 22:00:00 +02:00
}