summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTim Angus <tim@ngus.net>2006-01-24 05:04:21 +0000
committerTim Angus <tim@ngus.net>2006-01-24 05:04:21 +0000
commit608a95f42c08146d7e85fa876476d98365339a32 (patch)
treebb413c295ce51aa6648090e9856c399989a5e2f4 /src
parent470f5cc7203a148289d6ad6f0f5c7b4d80a48d50 (diff)
* Merged ioq3-r522
- i586 is now default -march - Couple of OpenAL "bug" fixes - Fancy autocompletion - Delete key on *nix fixed - Client now sleeps when inactive - Persistent console history
Diffstat (limited to 'src')
-rw-r--r--src/client/cl_console.c1
-rw-r--r--src/client/cl_keys.c119
-rw-r--r--src/client/client.h2
-rw-r--r--src/client/snd_codec.c2
-rw-r--r--src/client/snd_codec_ogg.c2
-rw-r--r--src/client/snd_openal.c43
-rw-r--r--src/qcommon/cmd.c23
-rw-r--r--src/qcommon/common.c275
-rw-r--r--src/qcommon/cvar.c25
-rw-r--r--src/qcommon/files.c23
-rw-r--r--src/qcommon/q_math.c22
-rw-r--r--src/qcommon/q_shared.c99
-rw-r--r--src/qcommon/q_shared.h6
-rw-r--r--src/qcommon/qcommon.h5
-rw-r--r--src/unix/unix_main.c44
15 files changed, 587 insertions, 104 deletions
diff --git a/src/client/cl_console.c b/src/client/cl_console.c
index efbf2d17..22a148a1 100644
--- a/src/client/cl_console.c
+++ b/src/client/cl_console.c
@@ -312,6 +312,7 @@ void Con_Init (void) {
Field_Clear( &historyEditLines[i] );
historyEditLines[i].widthInChars = g_console_field_width;
}
+ CL_LoadConsoleHistory( );
Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
Cmd_AddCommand ("messagemode", Con_MessageMode_f);
diff --git a/src/client/cl_keys.c b/src/client/cl_keys.c
index f80c24c3..232f04a4 100644
--- a/src/client/cl_keys.c
+++ b/src/client/cl_keys.c
@@ -484,10 +484,10 @@ void Console_Key (int key) {
// enter finishes the line
if ( key == K_ENTER || key == K_KP_ENTER ) {
- // if not in the game explicitly prepent a slash if needed
+ // if not in the game explicitly prepend a slash if needed
if ( cls.state != CA_ACTIVE && g_consoleField.buffer[0] != '\\'
&& g_consoleField.buffer[0] != '/' ) {
- char temp[MAX_STRING_CHARS];
+ char temp[MAX_EDIT_LINE-1];
Q_strncpyz( temp, g_consoleField.buffer, sizeof( temp ) );
Com_sprintf( g_consoleField.buffer, sizeof( g_consoleField.buffer ), "\\%s", temp );
@@ -520,6 +520,8 @@ void Console_Key (int key) {
g_consoleField.widthInChars = g_console_field_width;
+ CL_SaveConsoleHistory( );
+
if ( cls.state == CA_DISCONNECTED ) {
SCR_UpdateScreen (); // force an update, because the command
} // may take some time
@@ -529,7 +531,7 @@ void Console_Key (int key) {
// command completion
if (key == K_TAB) {
- Field_CompleteCommand(&g_consoleField);
+ Field_AutoComplete(&g_consoleField);
return;
}
@@ -1210,6 +1212,12 @@ void CL_CharEvent( int key ) {
return;
}
+ // delete is not a printable character and is
+ // otherwise handled by Field_KeyDownEvent
+ if ( key == 127 ) {
+ return;
+ }
+
// distribute the key down event to the apropriate handler
if ( cls.keyCatchers & KEYCATCH_CONSOLE )
{
@@ -1294,3 +1302,108 @@ Ket_SetCatcher
void Key_SetCatcher( int catcher ) {
cls.keyCatchers = catcher;
}
+
+// This must not exceed MAX_CMD_LINE
+#define MAX_CONSOLE_SAVE_BUFFER 1024
+static char consoleSaveBuffer[ MAX_CONSOLE_SAVE_BUFFER ];
+
+/*
+================
+CL_LoadConsoleHistory
+
+Load the console history from cl_consoleHistory
+================
+*/
+void CL_LoadConsoleHistory( void )
+{
+ char *token, *text_p;
+ int i, numChars, numLines = 0;
+ cvar_t *cv;
+
+ cv = Cvar_Get( "cl_consoleHistory", "", CVAR_ARCHIVE|CVAR_ROM );
+ Q_strncpyz( consoleSaveBuffer, cv->string, MAX_CONSOLE_SAVE_BUFFER );
+
+ text_p = consoleSaveBuffer;
+
+ for( i = COMMAND_HISTORY - 1; i >= 0; i-- )
+ {
+ if( !*( token = COM_Parse( &text_p ) ) )
+ break;
+
+ historyEditLines[ i ].cursor = atoi( token );
+
+ if( !*( token = COM_Parse( &text_p ) ) )
+ break;
+
+ historyEditLines[ i ].scroll = atoi( token );
+
+ if( !*( token = COM_Parse( &text_p ) ) )
+ break;
+
+ numChars = atoi( token );
+ text_p++;
+ if( numChars > ( strlen( consoleSaveBuffer ) - ( text_p - consoleSaveBuffer ) ) )
+ {
+ Com_DPrintf( S_COLOR_YELLOW "WARNING: probable corrupt history\n" );
+ break;
+ }
+ Com_Memcpy( historyEditLines[ i ].buffer,
+ text_p, numChars );
+ historyEditLines[ i ].buffer[ numChars ] = '\0';
+ text_p += numChars;
+
+ numLines++;
+ }
+
+ memmove( &historyEditLines[ 0 ], &historyEditLines[ i + 1 ],
+ numLines * sizeof( field_t ) );
+ for( i = numLines; i < COMMAND_HISTORY; i++ )
+ Field_Clear( &historyEditLines[ i ] );
+
+ historyLine = nextHistoryLine = numLines;
+}
+
+/*
+================
+CL_SaveConsoleHistory
+
+Save the console history into the cvar cl_consoleHistory
+so that it persists across invocations of q3
+================
+*/
+void CL_SaveConsoleHistory( void )
+{
+ int i;
+ int lineLength, saveBufferLength, additionalLength;
+
+ consoleSaveBuffer[ 0 ] = '\0';
+
+ i = ( nextHistoryLine - 1 ) % COMMAND_HISTORY;
+ do
+ {
+ if( historyEditLines[ i ].buffer[ 0 ] )
+ {
+ lineLength = strlen( historyEditLines[ i ].buffer );
+ saveBufferLength = strlen( consoleSaveBuffer );
+
+ //ICK "seta cl_consoleHistory " + "%d %d %d " = 23 + 13 = 36
+ additionalLength = lineLength + 36;
+
+ if( saveBufferLength + additionalLength < MAX_CONSOLE_SAVE_BUFFER )
+ {
+ Q_strcat( consoleSaveBuffer, MAX_CONSOLE_SAVE_BUFFER,
+ va( "%d %d %d %s ",
+ historyEditLines[ i ].cursor,
+ historyEditLines[ i ].scroll,
+ lineLength,
+ historyEditLines[ i ].buffer ) );
+ }
+ else
+ break;
+ }
+ i = ( i - 1 + COMMAND_HISTORY ) % COMMAND_HISTORY;
+ }
+ while( i != ( nextHistoryLine - 1 ) % COMMAND_HISTORY );
+
+ Cvar_Set( "cl_consoleHistory", consoleSaveBuffer );
+}
diff --git a/src/client/client.h b/src/client/client.h
index fb1ecea1..f0aa6e8e 100644
--- a/src/client/client.h
+++ b/src/client/client.h
@@ -454,6 +454,8 @@ void Con_Top( void );
void Con_Bottom( void );
void Con_Close( void );
+void CL_LoadConsoleHistory( void );
+void CL_SaveConsoleHistory( void );
//
// cl_scrn.c
diff --git a/src/client/snd_codec.c b/src/client/snd_codec.c
index e1c6bb47..98fae65f 100644
--- a/src/client/snd_codec.c
+++ b/src/client/snd_codec.c
@@ -98,7 +98,7 @@ void S_CodecInit()
{
codecs = NULL;
S_CodecRegister(&wav_codec);
-#ifdef USE_CODEC_VORBIS
+#if USE_CODEC_VORBIS
S_CodecRegister(&ogg_codec);
#endif
}
diff --git a/src/client/snd_codec_ogg.c b/src/client/snd_codec_ogg.c
index 20462fcb..464d8d38 100644
--- a/src/client/snd_codec_ogg.c
+++ b/src/client/snd_codec_ogg.c
@@ -23,7 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// OGG support is enabled by this define
-#ifdef USE_CODEC_VORBIS
+#if USE_CODEC_VORBIS
// includes for the Q3 sound system
#include "client.h"
diff --git a/src/client/snd_openal.c b/src/client/snd_openal.c
index e8b0915d..01b965ae 100644
--- a/src/client/snd_openal.c
+++ b/src/client/snd_openal.c
@@ -39,6 +39,7 @@ cvar_t *s_alDopplerSpeed;
cvar_t *s_alMinDistance;
cvar_t *s_alRolloff;
cvar_t *s_alDriver;
+cvar_t *s_alMaxSpeakerDistance;
/*
=================
@@ -469,6 +470,7 @@ typedef struct src_s
static src_t srcList[MAX_SRC];
static int srcCount = 0;
static qboolean alSourcesInitialised = qfalse;
+static vec3_t lastListenerOrigin = { 0.0f, 0.0f, 0.0f };
typedef struct sentity_s
{
@@ -487,6 +489,22 @@ static sentity_t entityList[MAX_GENTITIES];
/*
=================
+S_AL_SanitiseVector
+=================
+*/
+#define S_AL_SanitiseVector(v) _S_AL_SanitiseVector(v,__LINE__)
+static void _S_AL_SanitiseVector( vec3_t v, int line )
+{
+ if( Q_isnan( v[ 0 ] ) || Q_isnan( v[ 1 ] ) || Q_isnan( v[ 2 ] ) )
+ {
+ Com_DPrintf( S_COLOR_YELLOW "WARNING: vector with one or more NaN components "
+ "being passed to OpenAL at %s:%d -- zeroing\n", __FILE__, line );
+ VectorClear( v );
+ }
+}
+
+/*
+=================
S_AL_SrcInit
=================
*/
@@ -762,6 +780,7 @@ S_AL_UpdateEntityPosition
static
void S_AL_UpdateEntityPosition( int entityNum, const vec3_t origin )
{
+ S_AL_SanitiseVector( (vec_t *)origin );
if ( entityNum < 0 || entityNum > MAX_GENTITIES )
Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum );
VectorCopy( origin, entityList[entityNum].origin );
@@ -816,6 +835,7 @@ void S_AL_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx
}
else
VectorCopy( origin, sorigin );
+ S_AL_SanitiseVector( sorigin );
qalSourcefv(srcList[src].alSource, AL_POSITION, sorigin);
// Start it playing
@@ -896,7 +916,9 @@ S_AL_AddLoopingSound
static
void S_AL_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx )
{
- S_AL_SrcLoop(SRCPRI_AMBIENT, sfx, origin, velocity, entityNum);
+ S_AL_SanitiseVector( (vec_t *)origin );
+ S_AL_SanitiseVector( (vec_t *)velocity );
+ S_AL_SrcLoop(SRCPRI_ENTITY, sfx, origin, velocity, entityNum);
}
/*
@@ -907,7 +929,17 @@ S_AL_AddRealLoopingSound
static
void S_AL_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx )
{
- S_AL_SrcLoop(SRCPRI_ENTITY, sfx, origin, velocity, entityNum);
+ S_AL_SanitiseVector( (vec_t *)origin );
+ S_AL_SanitiseVector( (vec_t *)velocity );
+
+ // There are certain maps (*cough* Q3:TA mpterra*) that have large quantities
+ // of ET_SPEAKERS in the PVS at any given time. OpenAL can't cope with mixing
+ // large numbers of sounds, so this culls them by distance
+ if( DistanceSquared( origin, lastListenerOrigin ) >
+ s_alMaxSpeakerDistance->value * s_alMaxSpeakerDistance->value )
+ return;
+
+ S_AL_SrcLoop(SRCPRI_AMBIENT, sfx, origin, velocity, entityNum);
}
/*
@@ -1429,12 +1461,18 @@ S_AL_Respatialize
static
void S_AL_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater )
{
+ S_AL_SanitiseVector( (vec_t *)origin );
+ S_AL_SanitiseVector( axis[ 0 ] );
+ S_AL_SanitiseVector( axis[ 1 ] );
+ S_AL_SanitiseVector( axis[ 2 ] );
// Axis[0] = Forward
// Axis[2] = Up
float velocity[] = {0.0f, 0.0f, 0.0f};
float orientation[] = {axis[0][0], axis[0][1], axis[0][2],
axis[2][0], axis[2][1], axis[2][2]};
+ VectorCopy( origin, lastListenerOrigin );
+
// Set OpenAL listener paramaters
qalListenerfv(AL_POSITION, (ALfloat *)origin);
qalListenerfv(AL_VELOCITY, velocity);
@@ -1592,6 +1630,7 @@ qboolean S_AL_Init( soundInterface_t *si )
s_alDopplerSpeed = Cvar_Get( "s_alDopplerSpeed", "2200", CVAR_ARCHIVE );
s_alMinDistance = Cvar_Get( "s_alMinDistance", "120", CVAR_CHEAT );
s_alRolloff = Cvar_Get( "s_alRolloff", "0.8", CVAR_CHEAT );
+ s_alMaxSpeakerDistance = Cvar_Get( "s_alMaxSpeakerDistance", "1024", CVAR_ARCHIVE );
s_alDriver = Cvar_Get( "s_alDriver", ALDRIVER_DEFAULT, CVAR_ARCHIVE );
diff --git a/src/qcommon/cmd.c b/src/qcommon/cmd.c
index a3053c89..7495bf1c 100644
--- a/src/qcommon/cmd.c
+++ b/src/qcommon/cmd.c
@@ -477,7 +477,7 @@ will point into this temporary buffer.
*/
// NOTE TTimo define that to track tokenization issues
//#define TKN_DBG
-void Cmd_TokenizeString( const char *text_in ) {
+static void Cmd_TokenizeString2( const char *text_in, qboolean ignoreQuotes ) {
const char *text;
char *textOut;
@@ -534,7 +534,7 @@ void Cmd_TokenizeString( const char *text_in ) {
// handle quoted strings
// NOTE TTimo this doesn't handle \" escaping
- if ( *text == '"' ) {
+ if ( !ignoreQuotes && *text == '"' ) {
cmd.argv[cmd.argc] = textOut;
cmd.argc++;
text++;
@@ -555,7 +555,7 @@ void Cmd_TokenizeString( const char *text_in ) {
// skip until whitespace, quote, or command
while ( *text > ' ' ) {
- if ( text[0] == '"' ) {
+ if ( !ignoreQuotes && text[0] == '"' ) {
break;
}
@@ -580,6 +580,23 @@ void Cmd_TokenizeString( const char *text_in ) {
}
+/*
+============
+Cmd_TokenizeString
+============
+*/
+void Cmd_TokenizeString( const char *text_in ) {
+ Cmd_TokenizeString2( text_in, qfalse );
+}
+
+/*
+============
+Cmd_TokenizeStringIgnoreQuotes
+============
+*/
+void Cmd_TokenizeStringIgnoreQuotes( const char *text_in ) {
+ Cmd_TokenizeString2( text_in, qtrue );
+}
/*
============
diff --git a/src/qcommon/common.c b/src/qcommon/common.c
index a0a66ae7..85cef699 100644
--- a/src/qcommon/common.c
+++ b/src/qcommon/common.c
@@ -2774,7 +2774,7 @@ void Field_Clear( field_t *edit ) {
static const char *completionString;
static char shortestMatch[MAX_TOKEN_CHARS];
static int matchCount;
-// field we are working on, passed to Field_CompleteCommand (&g_consoleCommand for instance)
+// field we are working on, passed to Field_AutoComplete(&g_consoleCommand for instance)
static field_t *completionField;
/*
@@ -2796,7 +2796,12 @@ static void FindMatches( const char *s ) {
}
// cut shortestMatch to the amount common with s
- for ( i = 0 ; s[i] ; i++ ) {
+ for ( i = 0 ; shortestMatch[i] ; i++ ) {
+ if ( i >= strlen( s ) ) {
+ shortestMatch[i] = 0;
+ break;
+ }
+
if ( tolower(shortestMatch[i]) != tolower(s[i]) ) {
shortestMatch[i] = 0;
}
@@ -2805,11 +2810,11 @@ static void FindMatches( const char *s ) {
/*
===============
-PrintCmdMatches
+PrintMatches
===============
*/
-static void PrintCmdMatches( const char *s ) {
+static void PrintMatches( const char *s ) {
if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
Com_Printf( " %s\n", s );
}
@@ -2822,101 +2827,239 @@ PrintCvarMatches
===============
*/
static void PrintCvarMatches( const char *s ) {
+ char value[ TRUNCATE_LENGTH ];
+
if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
- Com_Printf( " %s = \"%s\"\n", s, Cvar_VariableString( s ) );
+ Com_TruncateLongString( value, Cvar_VariableString( s ) );
+ Com_Printf( " %s = \"%s\"\n", s, value );
}
}
-static void keyConcatArgs( void ) {
- int i;
- char *arg;
+/*
+===============
+Field_FindFirstSeparator
+===============
+*/
+static char *Field_FindFirstSeparator( char *s )
+{
+ int i;
- for ( i = 1 ; i < Cmd_Argc() ; i++ ) {
- Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
- arg = Cmd_Argv( i );
- while (*arg) {
- if (*arg == ' ') {
- Q_strcat( completionField->buffer, sizeof( completionField->buffer ), "\"");
- break;
- }
- arg++;
- }
- Q_strcat( completionField->buffer, sizeof( completionField->buffer ), Cmd_Argv( i ) );
- if (*arg == ' ') {
- Q_strcat( completionField->buffer, sizeof( completionField->buffer ), "\"");
- }
+ for( i = 0; i < strlen( s ); i++ )
+ {
+ if( s[ i ] == ';' )
+ return &s[ i ];
}
+
+ return NULL;
}
-static void ConcatRemaining( const char *src, const char *start ) {
- char *str;
+/*
+===============
+Field_CompleteFilename
+===============
+*/
+static void Field_CompleteFilename( const char *dir,
+ const char *ext, qboolean stripExt )
+{
+ matchCount = 0;
+ shortestMatch[ 0 ] = 0;
- str = strstr(src, start);
- if (!str) {
- keyConcatArgs();
+ FS_FilenameCompletion( dir, ext, stripExt, FindMatches );
+
+ if( matchCount == 0 )
+ return;
+
+ Q_strcat( completionField->buffer, sizeof( completionField->buffer ),
+ shortestMatch + strlen( completionString ) );
+ completionField->cursor = strlen( completionField->buffer );
+
+ if( matchCount == 1 )
+ {
+ Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
+ completionField->cursor++;
return;
}
- str += strlen(start);
- Q_strcat( completionField->buffer, sizeof( completionField->buffer ), str);
+ Com_Printf( "]%s\n", completionField->buffer );
+
+ FS_FilenameCompletion( dir, ext, stripExt, PrintMatches );
}
/*
===============
Field_CompleteCommand
-
-perform Tab expansion
-NOTE TTimo this was originally client code only
- moved to common code when writing tty console for *nix dedicated server
===============
*/
-void Field_CompleteCommand( field_t *field ) {
- field_t temp;
+static void Field_CompleteCommand( char *cmd,
+ qboolean doCommands, qboolean doCvars )
+{
+ int completionArgument = 0;
+ char *p;
- completionField = field;
+ // Skip leading whitespace and quotes
+ cmd = Com_SkipCharset( cmd, " \"" );
- // only look at the first token for completion purposes
- Cmd_TokenizeString( completionField->buffer );
+ Cmd_TokenizeStringIgnoreQuotes( cmd );
+ completionArgument = Cmd_Argc( );
- completionString = Cmd_Argv(0);
- if ( completionString[0] == '\\' || completionString[0] == '/' ) {
- completionString++;
+ // If there is trailing whitespace on the cmd
+ if( *( cmd + strlen( cmd ) - 1 ) == ' ' )
+ {
+ completionString = "";
+ completionArgument++;
}
- matchCount = 0;
- shortestMatch[0] = 0;
+ else
+ completionString = Cmd_Argv( completionArgument - 1 );
- if ( strlen( completionString ) == 0 ) {
- return;
- }
+ if( completionArgument > 1 )
+ {
+ const char *baseCmd = Cmd_Argv( 0 );
+
+#ifndef DEDICATED
+ // If the very first token does not have a leading \ or /,
+ // refuse to autocomplete
+ if( cmd == completionField->buffer )
+ {
+ if( baseCmd[ 0 ] != '\\' && baseCmd[ 0 ] != '/' )
+ return;
- Cmd_CommandCompletion( FindMatches );
- Cvar_CommandCompletion( FindMatches );
+ baseCmd++;
+ }
+#endif
- if ( matchCount == 0 ) {
- return; // no matches
+ if( ( p = Field_FindFirstSeparator( cmd ) ) )
+ {
+ // Compound command
+ Field_CompleteCommand( p + 1, qtrue, qtrue );
+ }
+ else
+ {
+ // FIXME: all this junk should really be associated with the respective
+ // commands, instead of being hard coded here
+ if( ( !Q_stricmp( baseCmd, "map" ) ||
+ !Q_stricmp( baseCmd, "devmap" ) ||
+ !Q_stricmp( baseCmd, "spmap" ) ||
+ !Q_stricmp( baseCmd, "spdevmap" ) ) &&
+ completionArgument == 2 )
+ {
+ Field_CompleteFilename( "maps", "bsp", qtrue );
+ }
+ else if( ( !Q_stricmp( baseCmd, "exec" ) ||
+ !Q_stricmp( baseCmd, "writeconfig" ) ) &&
+ completionArgument == 2 )
+ {
+ Field_CompleteFilename( "", "cfg", qfalse );
+ }
+ else if( !Q_stricmp( baseCmd, "condump" ) &&
+ completionArgument == 2 )
+ {
+ Field_CompleteFilename( "", "txt", qfalse );
+ }
+ else if( !Q_stricmp( baseCmd, "demo" ) && completionArgument == 2 )
+ {
+ char demoExt[ 16 ];
+
+ Com_sprintf( demoExt, sizeof( demoExt ), ".dm_%d", PROTOCOL_VERSION );
+ Field_CompleteFilename( "demos", demoExt, qtrue );
+ }
+ else if( ( !Q_stricmp( baseCmd, "toggle" ) ||
+ !Q_stricmp( baseCmd, "vstr" ) ||
+ !Q_stricmp( baseCmd, "set" ) ||
+ !Q_stricmp( baseCmd, "seta" ) ||
+ !Q_stricmp( baseCmd, "setu" ) ||
+ !Q_stricmp( baseCmd, "sets" ) ) &&
+ completionArgument == 2 )
+ {
+ // Skip "<cmd> "
+ p = Com_SkipTokens( cmd, 1, " " );
+
+ if( p > cmd )
+ Field_CompleteCommand( p, qfalse, qtrue );
+ }
+ else if( !Q_stricmp( baseCmd, "rcon" ) && completionArgument == 2 )
+ {
+ // Skip "rcon "
+ p = Com_SkipTokens( cmd, 1, " " );
+
+ if( p > cmd )
+ Field_CompleteCommand( p, qtrue, qtrue );
+ }
+ else if( !Q_stricmp( baseCmd, "bind" ) && completionArgument >= 3 )
+ {
+ // Skip "bind <key> "
+ p = Com_SkipTokens( cmd, 2, " " );
+
+ if( p > cmd )
+ Field_CompleteCommand( p, qtrue, qtrue );
+ }
+ }
}
+ else
+ {
+ if( completionString[0] == '\\' || completionString[0] == '/' )
+ completionString++;
- Com_Memcpy(&temp, completionField, sizeof(field_t));
+ matchCount = 0;
+ shortestMatch[ 0 ] = 0;
- if ( matchCount == 1 ) {
- Com_sprintf( completionField->buffer, sizeof( completionField->buffer ), "\\%s", shortestMatch );
- if ( Cmd_Argc() == 1 ) {
- Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
- } else {
- ConcatRemaining( temp.buffer, completionString );
+ if( strlen( completionString ) == 0 )
+ return;
+
+ if( doCommands )
+ Cmd_CommandCompletion( FindMatches );
+
+ if( doCvars )
+ Cvar_CommandCompletion( FindMatches );
+
+ if( matchCount == 0 )
+ return; // no matches
+
+ if( cmd == completionField->buffer )
+ {
+#ifndef DEDICATED
+ Com_sprintf( completionField->buffer,
+ sizeof( completionField->buffer ), "\\%s", shortestMatch );
+#else
+ Com_sprintf( completionField->buffer,
+ sizeof( completionField->buffer ), "%s", shortestMatch );
+#endif
+ }
+ else
+ {
+ Q_strcat( completionField->buffer, sizeof( completionField->buffer ),
+ shortestMatch + strlen( completionString ) );
}
+
completionField->cursor = strlen( completionField->buffer );
- return;
+
+ if( matchCount == 1 )
+ {
+ Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
+ completionField->cursor++;
+ return;
+ }
+
+ Com_Printf( "]%s\n", completionField->buffer );
+
+ // run through again, printing matches
+ if( doCommands )
+ Cmd_CommandCompletion( PrintMatches );
+
+ if( doCvars )
+ Cvar_CommandCompletion( PrintCvarMatches );
}
+}
- // multiple matches, complete to shortest
- Com_sprintf( completionField->buffer, sizeof( completionField->buffer ), "\\%s", shortestMatch );
- completionField->cursor = strlen( completionField->buffer );
- ConcatRemaining( temp.buffer, completionString );
+/*
+===============
+Field_AutoComplete
- Com_Printf( "]%s\n", completionField->buffer );
+Perform Tab expansion
+===============
+*/
+void Field_AutoComplete( field_t *field )
+{
+ completionField = field;
- // run through again, printing matches
- Cmd_CommandCompletion( PrintCmdMatches );
- Cvar_CommandCompletion( PrintCvarMatches );
+ Field_CompleteCommand( completionField->buffer, qtrue, qtrue );
}
diff --git a/src/qcommon/cvar.c b/src/qcommon/cvar.c
index 839f41ce..a306d887 100644
--- a/src/qcommon/cvar.c
+++ b/src/qcommon/cvar.c
@@ -470,7 +470,10 @@ Handles variable inspection and changing from the console
============
*/
qboolean Cvar_Command( void ) {
- cvar_t *v;
+ cvar_t *v;
+ char string[ TRUNCATE_LENGTH ];
+ char resetString[ TRUNCATE_LENGTH ];
+ char latchedString[ TRUNCATE_LENGTH ];
// check variables
v = Cvar_FindVar (Cmd_Argv(0));
@@ -480,9 +483,13 @@ qboolean Cvar_Command( void ) {
// perform a variable print or set
if ( Cmd_Argc() == 1 ) {
- Com_Printf ("\"%s\" is:\"%s" S_COLOR_WHITE "\" default:\"%s" S_COLOR_WHITE "\"\n", v->name, v->string, v->resetString );
+ Com_TruncateLongString( string, v->string );
+ Com_TruncateLongString( resetString, v->resetString );
+ Com_Printf ("\"%s\" is:\"%s" S_COLOR_WHITE "\" default:\"%s" S_COLOR_WHITE "\"\n",
+ v->name, string, resetString );
if ( v->latchedString ) {
- Com_Printf( "latched: \"%s\"\n", v->latchedString );
+ Com_TruncateLongString( latchedString, v->latchedString );
+ Com_Printf( "latched: \"%s\"\n", latchedString );
}
return qtrue;
}
@@ -643,11 +650,21 @@ void Cvar_WriteVariables( fileHandle_t f ) {
if( var->flags & CVAR_ARCHIVE ) {
// write the latched value, even if it hasn't taken effect yet
if ( var->latchedString ) {
+ if( strlen( var->name ) + strlen( var->latchedString ) + 10 > sizeof( buffer ) ) {
+ Com_Printf( S_COLOR_YELLOW "WARNING: value of variable "
+ "\"%s\" too long to write to file\n", var->name );
+ continue;
+ }
Com_sprintf (buffer, sizeof(buffer), "seta %s \"%s\"\n", var->name, var->latchedString);
} else {
+ if( strlen( var->name ) + strlen( var->string ) + 10 > sizeof( buffer ) ) {
+ Com_Printf( S_COLOR_YELLOW "WARNING: value of variable "
+ "\"%s\" too long to write to file\n", var->name );
+ continue;
+ }
Com_sprintf (buffer, sizeof(buffer), "seta %s \"%s\"\n", var->name, var->string);
}
- FS_Printf (f, "%s", buffer);
+ FS_Write( buffer, strlen( buffer ), f );
}
}
}
diff --git a/src/qcommon/files.c b/src/qcommon/files.c
index 14db75c7..b24b36fd 100644
--- a/src/qcommon/files.c
+++ b/src/qcommon/files.c
@@ -3361,3 +3361,26 @@ void FS_Flush( fileHandle_t f ) {
fflush(fsh[f].handleFiles.file.o);
}
+void FS_FilenameCompletion( const char *dir, const char *ext,
+ qboolean stripExt, void(*callback)(const char *s) ) {
+ char **filenames;
+ int nfiles;
+ int i;
+ char filename[ MAX_STRING_CHARS ];
+
+ filenames = FS_ListFilteredFiles( dir, ext, NULL, &nfiles );
+
+ FS_SortFileList( filenames, nfiles );
+
+ for( i = 0; i < nfiles; i++ ) {
+ FS_ConvertPath( filenames[ i ] );
+ Q_strncpyz( filename, filenames[ i ], MAX_STRING_CHARS );
+
+ if( stripExt ) {
+ COM_StripExtension( filename, filename );
+ }
+
+ callback( filename );
+ }
+ FS_FreeFileList( filenames );
+}
diff --git a/src/qcommon/q_math.c b/src/qcommon/q_math.c
index bf209562..196d2f55 100644
--- a/src/qcommon/q_math.c
+++ b/src/qcommon/q_math.c
@@ -1538,3 +1538,25 @@ vec_t DistanceBetweenLineSegments(
return (vec_t)sqrt( DistanceBetweenLineSegmentsSquared(
sP0, sP1, tP0, tP1, s, t ) );
}
+
+/*
+=================
+Q_isnan
+
+Don't pass doubles to this
+================
+*/
+int Q_isnan( float x )
+{
+ union
+ {
+ float f;
+ unsigned int i;
+ } t;
+
+ t.f = x;
+ t.i &= 0x7FFFFFFF;
+ t.i = 0x7F800000 - t.i;
+
+ return (int)( (unsigned int)t.i >> 31 );
+}
diff --git a/src/qcommon/q_shared.c b/src/qcommon/q_shared.c
index 091847ca..9f152d62 100644
--- a/src/qcommon/q_shared.c
+++ b/src/qcommon/q_shared.c
@@ -60,10 +60,19 @@ COM_StripExtension
============
*/
void COM_StripExtension( const char *in, char *out ) {
- while ( *in && *in != '.' ) {
- *out++ = *in++;
+ int length;
+
+ strcpy( out, in );
+
+ length = strlen(out)-1;
+ while (length > 0 && out[length] != '.')
+ {
+ length--;
+ if (out[length] == '/')
+ return; // no extension
}
- *out = 0;
+ if (length)
+ out[length] = 0;
}
@@ -910,6 +919,26 @@ char * QDECL va( char *format, ... ) {
return buf;
}
+/*
+============
+Com_TruncateLongString
+
+Assumes buffer is atleast TRUNCATE_LENGTH big
+============
+*/
+void Com_TruncateLongString( char *buffer, const char *s )
+{
+ int length = strlen( s );
+
+ if( length <= TRUNCATE_LENGTH )
+ Q_strncpyz( buffer, s, TRUNCATE_LENGTH );
+ else
+ {
+ Q_strncpyz( buffer, s, ( TRUNCATE_LENGTH / 2 ) - 3 );
+ Q_strcat( buffer, TRUNCATE_LENGTH, " ... " );
+ Q_strcat( buffer, TRUNCATE_LENGTH, s + length - ( TRUNCATE_LENGTH / 2 ) + 3 );
+ }
+}
/*
=====================================================================
@@ -1250,4 +1279,68 @@ void Info_SetValueForKey_Big( char *s, const char *key, const char *value ) {
//====================================================================
+/*
+==================
+Com_CharIsOneOfCharset
+==================
+*/
+static qboolean Com_CharIsOneOfCharset( char c, char *set )
+{
+ int i;
+
+ for( i = 0; i < strlen( set ); i++ )
+ {
+ if( set[ i ] == c )
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+==================
+Com_SkipCharset
+==================
+*/
+char *Com_SkipCharset( char *s, char *sep )
+{
+ char *p = s;
+
+ while( p )
+ {
+ if( Com_CharIsOneOfCharset( *p, sep ) )
+ p++;
+ else
+ break;
+ }
+
+ return p;
+}
+
+/*
+==================
+Com_SkipTokens
+==================
+*/
+char *Com_SkipTokens( char *s, int numTokens, char *sep )
+{
+ int sepCount = 0;
+ char *p = s;
+ while( sepCount < numTokens )
+ {
+ if( Com_CharIsOneOfCharset( *p++, sep ) )
+ {
+ sepCount++;
+ while( Com_CharIsOneOfCharset( *p, sep ) )
+ p++;
+ }
+ else if( *p == '\0' )
+ break;
+ }
+
+ if( sepCount == numTokens )
+ return p;
+ else
+ return s;
+}
diff --git a/src/qcommon/q_shared.h b/src/qcommon/q_shared.h
index 3955e2c4..a55689e7 100644
--- a/src/qcommon/q_shared.h
+++ b/src/qcommon/q_shared.h
@@ -585,6 +585,7 @@ void MatrixMultiply(float in1[3][3], float in2[3][3], float out[3][3]);
void VectorMatrixMultiply( const vec3_t p, vec3_t m[ 3 ], vec3_t out );
void AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up);
void PerpendicularVector( vec3_t dst, const vec3_t src );
+int Q_isnan( float x );
void GetPerpendicularViewVector( const vec3_t point, const vec3_t p1,
const vec3_t p2, vec3_t up );
@@ -663,6 +664,8 @@ void Parse3DMatrix (char **buf_p, int z, int y, int x, float *m);
void QDECL Com_sprintf (char *dest, int size, const char *fmt, ...);
+char *Com_SkipTokens( char *s, int numTokens, char *sep );
+char *Com_SkipCharset( char *s, char *sep );
// mode parm for FS_FOpenFile
typedef enum {
@@ -733,6 +736,9 @@ void Swap_Init (void);
*/
char * QDECL va(char *format, ...);
+#define TRUNCATE_LENGTH 64
+void Com_TruncateLongString( char *buffer, const char *s );
+
//=============================================
//
diff --git a/src/qcommon/qcommon.h b/src/qcommon/qcommon.h
index 7e14ad11..e2382cc6 100644
--- a/src/qcommon/qcommon.h
+++ b/src/qcommon/qcommon.h
@@ -410,6 +410,7 @@ char *Cmd_Cmd (void);
// if arg > argc, so string operations are allways safe.
void Cmd_TokenizeString( const char *text );
+void Cmd_TokenizeStringIgnoreQuotes( const char *text_in );
// Takes a null terminated string. Does not need to be /n terminated.
// breaks the string up into arg tokens.
@@ -654,6 +655,8 @@ void FS_Rename( const char *from, const char *to );
void FS_Remove( const char *osPath );
void FS_HomeRemove( const char *homePath );
+void FS_FilenameCompletion( const char *dir, const char *ext,
+ qboolean stripExt, void(*callback)(const char *s) );
/*
==============================================================
@@ -671,7 +674,7 @@ typedef struct {
} field_t;
void Field_Clear( field_t *edit );
-void Field_CompleteCommand( field_t *edit );
+void Field_AutoComplete( field_t *edit );
/*
==============================================================
diff --git a/src/unix/unix_main.c b/src/unix/unix_main.c
index d5b69ffb..156407e0 100644
--- a/src/unix/unix_main.c
+++ b/src/unix/unix_main.c
@@ -550,7 +550,6 @@ char *Sys_ConsoleInput(void)
{
// we use this when sending back commands
static char text[256];
- int i;
int avail;
char key;
field_t *history;
@@ -589,22 +588,7 @@ char *Sys_ConsoleInput(void)
if (key == '\t')
{
tty_Hide();
- Field_CompleteCommand( &tty_con );
- // Field_CompleteCommand does weird things to the string, do a cleanup
- // it adds a '\' at the beginning of the string
- // cursor doesn't reflect actual length of the string that's sent back
- tty_con.cursor = strlen(tty_con.buffer);
- if (tty_con.cursor>0)
- {
- if (tty_con.buffer[0] == '\\')
- {
- for (i=0; i<=tty_con.cursor; i++)
- {
- tty_con.buffer[i] = tty_con.buffer[i+1];
- }
- tty_con.cursor--;
- }
- }
+ Field_AutoComplete( &tty_con );
tty_Show();
return NULL;
}
@@ -1238,6 +1222,7 @@ void Sys_ANSIColorify( const char *msg, char *buffer, int bufferSize )
int msgLength, pos;
int i, j;
char *escapeCode;
+ char tempBuffer[ 7 ];
if( !msg || !buffer )
return;
@@ -1251,7 +1236,8 @@ void Sys_ANSIColorify( const char *msg, char *buffer, int bufferSize )
{
if( msg[ i ] == '\n' )
{
- strncat( buffer, va( "%c[0m\n", 0x1B ), bufferSize );
+ Com_sprintf( tempBuffer, 7, "%c[0m\n", 0x1B );
+ strncat( buffer, tempBuffer, bufferSize );
i++;
}
else if( msg[ i ] == Q_COLOR_ESCAPE )
@@ -1271,13 +1257,19 @@ void Sys_ANSIColorify( const char *msg, char *buffer, int bufferSize )
}
if( escapeCode )
- strncat( buffer, va( "%c[%sm", 0x1B, escapeCode ), bufferSize );
+ {
+ Com_sprintf( tempBuffer, 7, "%c[%sm", 0x1B, escapeCode );
+ strncat( buffer, tempBuffer, bufferSize );
+ }
i++;
}
}
else
- strncat( buffer, va( "%c", msg[ i++ ] ), bufferSize );
+ {
+ Com_sprintf( tempBuffer, 7, "%c", msg[ i++ ] );
+ strncat( buffer, tempBuffer, bufferSize );
+ }
}
}
@@ -1409,6 +1401,18 @@ int main ( int argc, char* argv[] )
while (1)
{
+#if !defined( DEDICATED ) && USE_SDL_VIDEO
+ int appState = SDL_GetAppState( );
+
+ // If we have no input focus at all, sleep a bit
+ if( !( appState & ( SDL_APPMOUSEFOCUS | SDL_APPINPUTFOCUS ) ) )
+ usleep( 16000 );
+
+ // If we're minimised, sleep a bit more
+ if( !( appState & SDL_APPACTIVE ) )
+ usleep( 32000 );
+#endif
+
#ifdef __linux__
Sys_ConfigureFPU();
#endif