2007-05-07 18:33:24 +02:00
/**
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* File : CConsole . cpp
* Project : 0 A . D .
* Description : Implements the in - game console with scripting support .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2004-06-03 20:38:14 +02:00
# include "precompiled.h"
2004-07-10 20:57:13 +02:00
# include <wctype.h>
2004-06-03 20:38:14 +02:00
2004-06-02 22:40:07 +02:00
# include "CConsole.h"
2006-06-09 18:44:16 +02:00
# include "lib/ogl.h"
# include "lib/res/file/vfs.h"
# include "lib/res/graphics/unifont.h"
2007-09-23 12:15:28 +02:00
# include "lib/sysdep/clipboard.h"
2006-06-02 04:10:27 +02:00
# include "maths/MathUtil.h"
2007-01-24 21:17:28 +01:00
# include "network/Client.h"
# include "network/Server.h"
2006-06-09 18:44:16 +02:00
# include "ps/CLogger.h"
# include "ps/Globals.h"
# include "ps/Hotkey.h"
# include "ps/Interact.h"
# include "ps/Pyrogenesis.h"
# include "scripting/ScriptingHost.h"
2006-06-09 20:32:00 +02:00
# include "simulation/Entity.h"
2004-08-03 01:14:54 +02:00
2005-07-03 18:25:48 +02:00
CConsole * g_Console = 0 ;
2004-07-11 18:03:26 +02:00
CConsole : : CConsole ( )
2004-06-03 02:19:22 +02:00
{
2004-06-16 17:42:48 +02:00
2004-06-02 22:40:07 +02:00
m_bToggle = false ;
m_bVisible = false ;
2004-06-03 03:42:40 +02:00
m_fVisibleFrac = 0.0f ;
2004-06-03 02:19:22 +02:00
2004-08-14 13:40:40 +02:00
m_szBuffer = new wchar_t [ CONSOLE_BUFFER_SIZE ] ;
2004-06-02 22:40:07 +02:00
FlushBuffer ( ) ;
m_iMsgHistPos = 1 ;
2006-07-06 05:17:44 +02:00
m_charsPerPage = 0 ;
2007-06-17 00:07:40 +02:00
m_ScriptObject = NULL ; // scripting host isn't initialised yet - we'll set this later
2004-06-02 22:40:07 +02:00
2004-07-11 17:07:38 +02:00
InsertMessage ( L " [ 0 A.D. Console v0.12 ] type \" \\ info \" for help " ) ;
2004-06-26 14:58:35 +02:00
InsertMessage ( L " " ) ;
2004-06-02 22:40:07 +02:00
2006-07-06 05:17:44 +02:00
if ( vfs_exists ( " gui/text/help.txt " ) )
{
FileIOBuf buf ;
size_t size ;
if ( vfs_load ( " gui/text/help.txt " , buf , size ) < 0 )
{
LOG ( ERROR , " Console " , " Help file not found for console " ) ;
file_buf_free ( buf ) ;
return ;
}
2006-09-02 23:20:25 +02:00
// TODO: read in text mode, or at least get rid of the \r\n somehow
// TODO: maybe the help file should be UTF-8 - we assume it's iso-8859-1 here
m_helpText = CStrW ( CStr ( ( const char * ) buf ) ) ;
2006-07-06 05:17:44 +02:00
file_buf_free ( buf ) ;
}
else
2006-09-02 23:20:25 +02:00
{
2006-07-06 05:17:44 +02:00
InsertMessage ( L " No help file found. " ) ;
2006-09-02 23:20:25 +02:00
}
2006-07-06 05:17:44 +02:00
}
2004-06-02 22:40:07 +02:00
CConsole : : ~ CConsole ( )
{
m_mapFuncList . clear ( ) ;
m_deqMsgHistory . clear ( ) ;
m_deqBufHistory . clear ( ) ;
delete [ ] m_szBuffer ;
2007-06-17 00:07:40 +02:00
if ( m_ScriptObject )
JS_RemoveRoot ( g_ScriptingHost . GetContext ( ) , & m_ScriptObject ) ;
2004-06-02 22:40:07 +02:00
}
2004-07-11 18:03:26 +02:00
void CConsole : : SetSize ( float X , float Y , float W , float H )
{
m_fX = X ;
m_fY = Y ;
m_fWidth = W ;
m_fHeight = H ;
}
2005-07-03 18:25:48 +02:00
void CConsole : : UpdateScreenSize ( int w , int h )
{
float height = h * 0.6f ;
SetSize ( 0 , h - height , ( float ) w , height ) ;
}
2004-07-11 18:03:26 +02:00
2004-07-13 23:09:55 +02:00
void CConsole : : ToggleVisible ( )
{
m_bToggle = true ;
m_bVisible = ! m_bVisible ;
}
2004-08-03 01:14:54 +02:00
void CConsole : : SetVisible ( bool visible )
{
if ( visible ! = m_bVisible )
m_bToggle = true ;
m_bVisible = visible ;
}
2004-07-13 23:09:55 +02:00
2004-06-02 22:40:07 +02:00
void CConsole : : FlushBuffer ( void )
{
2005-01-23 18:45:25 +01:00
// Clear the buffer and set the cursor and length to 0
2004-08-14 13:40:40 +02:00
memset ( m_szBuffer , ' \0 ' , sizeof ( wchar_t ) * CONSOLE_BUFFER_SIZE ) ;
2004-06-02 22:40:07 +02:00
m_iBufferPos = m_iBufferLength = 0 ;
}
2004-06-16 17:42:48 +02:00
void CConsole : : ToLower ( wchar_t * szMessage , uint iSize )
2004-06-02 22:40:07 +02:00
{
2004-06-16 17:42:48 +02:00
uint L = ( uint ) wcslen ( szMessage ) ;
2004-06-02 22:40:07 +02:00
2004-06-09 15:35:20 +02:00
if ( L < = 0 ) return ;
2004-06-02 22:40:07 +02:00
if ( iSize & & iSize < L ) L = iSize ;
2004-08-06 17:07:01 +02:00
for ( uint i = 0 ; i < L ; i + + )
2004-06-19 23:35:00 +02:00
szMessage [ i ] = towlower ( szMessage [ i ] ) ;
2004-06-02 22:40:07 +02:00
}
2004-06-16 17:42:48 +02:00
void CConsole : : Trim ( wchar_t * szMessage , const wchar_t cChar , uint iSize )
2004-06-02 22:40:07 +02:00
{
2004-06-16 17:42:48 +02:00
size_t L = wcslen ( szMessage ) ;
2004-06-09 15:35:20 +02:00
if ( ! L )
return ;
2004-06-02 22:40:07 +02:00
if ( iSize & & iSize < L ) L = iSize ;
2004-06-16 17:42:48 +02:00
wchar_t szChar [ 2 ] = { cChar , 0 } ;
2004-06-02 22:40:07 +02:00
2005-01-23 18:45:25 +01:00
// Find the first point at which szChar does not
// exist in the message
2004-06-16 17:42:48 +02:00
size_t ofs = wcsspn ( szMessage , szChar ) ;
2004-06-09 15:35:20 +02:00
if ( ofs = = 0 ) // no leading <cChar> chars - we're done
return ;
2004-06-02 22:40:07 +02:00
2004-06-09 15:35:20 +02:00
// move everything <ofs> chars left, replacing leading cChar chars
L - = ofs ;
2004-06-26 14:58:35 +02:00
memmove ( szMessage , szMessage + ofs , L * sizeof ( wchar_t ) ) ;
2004-06-02 22:40:07 +02:00
2004-06-09 15:35:20 +02:00
for ( ssize_t i = ( ssize_t ) L ; i > = 0 ; i - - )
2004-06-02 22:40:07 +02:00
{
2004-06-09 15:35:20 +02:00
szMessage [ i ] = ' \0 ' ;
if ( szMessage [ i - 1 ] ! = cChar ) break ;
2004-06-02 22:40:07 +02:00
}
}
2004-06-16 17:42:48 +02:00
void CConsole : : RegisterFunc ( fptr F , const wchar_t * szName )
2004-06-02 22:40:07 +02:00
{
2004-06-09 15:35:20 +02:00
// need to allocate a copy - szName may be a const string literal
// (we'll change it - stripping out spaces and converting to lowercase).
2004-08-14 13:40:40 +02:00
wchar_t copy [ CONSOLE_BUFFER_SIZE ] ;
copy [ CONSOLE_BUFFER_SIZE - 1 ] = ' \0 ' ;
wcsncpy ( copy , szName , CONSOLE_BUFFER_SIZE - 1 ) ;
2004-06-02 22:40:07 +02:00
2004-06-09 15:35:20 +02:00
Trim ( copy ) ;
ToLower ( copy ) ;
2004-06-02 22:40:07 +02:00
2004-06-16 17:42:48 +02:00
m_mapFuncList . insert ( std : : pair < std : : wstring , fptr > ( copy , F ) ) ;
2004-06-02 22:40:07 +02:00
}
2004-07-17 08:14:23 +02:00
2004-06-02 22:40:07 +02:00
void CConsole : : Update ( const float DeltaTime )
{
2004-06-03 02:19:22 +02:00
if ( m_bToggle )
2004-06-02 22:40:07 +02:00
{
2004-06-03 02:19:22 +02:00
const float AnimateTime = .30f ;
const float Delta = DeltaTime / AnimateTime ;
if ( m_bVisible )
{
2004-06-03 03:42:40 +02:00
m_fVisibleFrac + = Delta ;
if ( m_fVisibleFrac > 1.0f )
2004-06-03 02:19:22 +02:00
{
2004-06-03 03:42:40 +02:00
m_fVisibleFrac = 1.0f ;
2004-06-03 02:19:22 +02:00
m_bToggle = false ;
}
}
2004-06-02 22:40:07 +02:00
else
2004-06-03 02:19:22 +02:00
{
2004-06-03 03:42:40 +02:00
m_fVisibleFrac - = Delta ;
if ( m_fVisibleFrac < 0.0f )
2004-06-03 02:19:22 +02:00
{
2004-06-03 03:42:40 +02:00
m_fVisibleFrac = 0.0f ;
2004-06-03 02:19:22 +02:00
m_bToggle = false ;
}
}
2004-06-02 22:40:07 +02:00
}
}
//Render Manager.
void CConsole : : Render ( )
{
2004-06-26 14:58:35 +02:00
if ( ! ( m_bVisible | | m_bToggle ) ) return ;
2004-06-02 22:40:07 +02:00
2004-06-03 02:19:22 +02:00
// animation: slide in from top of screen
const float MaxY = m_fHeight ;
2004-06-03 03:42:40 +02:00
const float DeltaY = ( 1.0f - m_fVisibleFrac ) * MaxY ;
2004-06-03 02:19:22 +02:00
glTranslatef ( m_fX , m_fY + DeltaY , 0.0f ) ; //Move to window position
2004-06-02 22:40:07 +02:00
2006-02-11 19:53:32 +01:00
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
2004-08-06 17:07:01 +02:00
DrawWindow ( ) ;
DrawHistory ( ) ;
2004-06-02 22:40:07 +02:00
DrawBuffer ( ) ;
2006-02-11 19:53:32 +01:00
glDisable ( GL_BLEND ) ;
2004-06-02 22:40:07 +02:00
}
void CConsole : : DrawWindow ( void )
{
2005-01-23 18:45:25 +01:00
// TODO: Add texturing
2004-06-02 22:40:07 +02:00
glDisable ( GL_TEXTURE_2D ) ;
glPushMatrix ( ) ;
2005-01-23 18:45:25 +01:00
// Draw Background
// Set the color to a translucent blue
2004-06-02 22:40:07 +02:00
glColor4f ( 0.0f , 0.0f , 0.5f , 0.6f ) ;
glBegin ( GL_QUADS ) ;
glVertex2f ( 0.0f , 0.0f ) ;
2004-08-14 13:40:40 +02:00
glVertex2f ( m_fWidth - 1.0f , 0.0f ) ;
glVertex2f ( m_fWidth - 1.0f , m_fHeight - 1.0f ) ;
glVertex2f ( 0.0f , m_fHeight - 1.0f ) ;
2004-06-02 22:40:07 +02:00
glEnd ( ) ;
2005-01-23 18:45:25 +01:00
// Draw Border
// Set the color to a translucent yellow
2004-06-02 22:40:07 +02:00
glColor4f ( 0.5f , 0.5f , 0.0f , 0.6f ) ;
2004-08-14 13:40:40 +02:00
glBegin ( GL_LINE_LOOP ) ;
2004-06-02 22:40:07 +02:00
glVertex2f ( 0.0f , 0.0f ) ;
2004-08-14 13:40:40 +02:00
glVertex2f ( m_fWidth - 1.0f , 0.0f ) ;
glVertex2f ( m_fWidth - 1.0f , m_fHeight - 1.0f ) ;
glVertex2f ( 0.0f , m_fHeight - 1.0f ) ;
glEnd ( ) ;
2004-06-02 22:40:07 +02:00
2004-08-14 13:40:40 +02:00
if ( m_fHeight > m_iFontHeight + 4 )
{
glBegin ( GL_LINES ) ;
2004-06-16 17:42:48 +02:00
glVertex2f ( 0.0f , ( GLfloat ) ( m_iFontHeight + 4 ) ) ;
glVertex2f ( m_fWidth , ( GLfloat ) ( m_iFontHeight + 4 ) ) ;
2004-08-14 13:40:40 +02:00
glEnd ( ) ;
}
2004-06-02 22:40:07 +02:00
glPopMatrix ( ) ;
glEnable ( GL_TEXTURE_2D ) ;
}
void CConsole : : DrawHistory ( void ) {
int i = 1 ;
2006-02-11 19:53:32 +01:00
2004-06-16 17:42:48 +02:00
std : : deque < std : : wstring > : : iterator Iter ; //History iterator
2004-06-02 22:40:07 +02:00
glPushMatrix ( ) ;
glColor3f ( 1.0f , 1.0f , 1.0f ) ; //Set color of text
2004-06-16 17:42:48 +02:00
glTranslatef ( 9.0f , ( float ) m_iFontOffset , 0.0f ) ; //move away from the border
2004-06-02 22:40:07 +02:00
2004-07-14 00:44:20 +02:00
// Draw the text upside-down, because it's aligned with
// the GUI (which uses the top-left as (0,0))
glScalef ( 1.0f , - 1.0f , 1.0f ) ;
2004-06-02 22:40:07 +02:00
for ( Iter = m_deqMsgHistory . begin ( ) ;
Iter ! = m_deqMsgHistory . end ( )
2004-08-14 13:40:40 +02:00
& & ( ( ( i - m_iMsgHistPos + 1 ) * m_iFontHeight ) < m_fHeight ) ;
2004-06-02 22:40:07 +02:00
Iter + + )
{
if ( i > = m_iMsgHistPos ) {
2004-07-14 00:44:20 +02:00
glTranslatef ( 0.0f , - ( float ) m_iFontHeight , 0.0f ) ;
2004-06-02 22:40:07 +02:00
glPushMatrix ( ) ;
2004-07-12 00:30:08 +02:00
glwprintf ( L " %ls " , Iter - > data ( ) ) ;
2004-06-02 22:40:07 +02:00
glPopMatrix ( ) ;
}
i + + ;
}
glPopMatrix ( ) ;
}
//Renders the buffer to the screen.
void CConsole : : DrawBuffer ( void )
{
2004-06-16 17:42:48 +02:00
if ( m_fHeight < m_iFontHeight ) return ;
2004-06-02 22:40:07 +02:00
glPushMatrix ( ) ;
glColor3f ( 1.0f , 1.0f , 0.0f ) ;
2004-06-16 17:42:48 +02:00
glTranslatef ( 2.0f , ( float ) m_iFontOffset , 0 ) ;
2004-07-14 00:44:20 +02:00
glScalef ( 1.0f , - 1.0f , 1.0f ) ;
2004-06-16 17:42:48 +02:00
glwprintf ( L " ] " ) ;
2004-06-02 22:40:07 +02:00
glColor3f ( 1.0f , 1.0f , 1.0f ) ;
if ( m_iBufferPos = = 0 ) DrawCursor ( ) ;
for ( int i = 0 ; i < m_iBufferLength ; i + + ) {
2004-07-12 00:30:08 +02:00
glwprintf ( L " %lc " , m_szBuffer [ i ] ) ;
2004-06-02 22:40:07 +02:00
if ( m_iBufferPos - 1 = = i ) DrawCursor ( ) ;
}
glPopMatrix ( ) ;
}
void CConsole : : DrawCursor ( void )
{
2005-10-31 19:36:36 +01:00
// (glPushMatrix is necessary because glwprintf does glTranslatef)
2004-06-02 22:40:07 +02:00
glPushMatrix ( ) ;
2004-08-11 16:48:36 +02:00
// Slightly translucent yellow
2004-08-10 19:16:46 +02:00
glColor4f ( 1.0f , 1.0f , 0.0f , 0.8f ) ;
2004-08-11 16:48:36 +02:00
// U+FE33: PRESENTATION FORM FOR VERTICAL LOW LINE
// (sort of like a | which is aligned to the left of most characters)
2004-08-10 19:16:46 +02:00
glwprintf ( L " %lc " , 0xFE33 ) ;
2004-08-11 16:48:36 +02:00
// Revert to the standard text colour
2004-08-10 19:16:46 +02:00
glColor3f ( 1.0f , 1.0f , 1.0f ) ;
2004-06-02 22:40:07 +02:00
glPopMatrix ( ) ;
}
//Inserts a character into the buffer.
2004-06-21 18:29:47 +02:00
void CConsole : : InsertChar ( const int szChar , const wchar_t cooked )
2004-06-02 22:40:07 +02:00
{
static int iHistoryPos = - 1 ;
if ( ! m_bVisible ) return ;
switch ( szChar ) {
2006-03-15 20:19:03 +01:00
case SDLK_RETURN :
2004-06-02 22:40:07 +02:00
iHistoryPos = - 1 ;
m_iMsgHistPos = 1 ;
2004-08-06 17:07:01 +02:00
ProcessBuffer ( m_szBuffer ) ;
2004-06-02 22:40:07 +02:00
FlushBuffer ( ) ;
return ;
2006-03-15 20:19:03 +01:00
case SDLK_TAB :
2005-01-23 18:45:25 +01:00
// Auto Complete
2004-06-02 22:40:07 +02:00
return ;
2006-03-15 20:19:03 +01:00
case SDLK_BACKSPACE :
2004-06-02 22:40:07 +02:00
if ( IsEmpty ( ) | | IsBOB ( ) ) return ;
if ( m_iBufferPos = = m_iBufferLength )
m_szBuffer [ m_iBufferPos - 1 ] = ' \0 ' ;
else {
2004-08-06 17:07:01 +02:00
for ( int j = m_iBufferPos - 1 ; j < m_iBufferLength - 1 ; j + + )
2004-06-02 22:40:07 +02:00
m_szBuffer [ j ] = m_szBuffer [ j + 1 ] ; // move chars to left
2004-06-16 17:42:48 +02:00
m_szBuffer [ m_iBufferLength - 1 ] = ' \0 ' ;
2004-08-06 17:07:01 +02:00
}
2004-06-02 22:40:07 +02:00
m_iBufferPos - - ;
m_iBufferLength - - ;
return ;
2004-06-26 14:45:54 +02:00
case SDLK_DELETE :
if ( IsEmpty ( ) | | IsEOB ( ) ) return ;
if ( m_iBufferPos = = m_iBufferLength - 1 )
2006-05-16 06:41:37 +02:00
{
2004-06-26 14:45:54 +02:00
m_szBuffer [ m_iBufferPos ] = ' \0 ' ;
2006-05-16 06:41:37 +02:00
m_iBufferLength - - ;
}
else
{
if ( g_keys [ SDLK_RCTRL ] | | g_keys [ SDLK_LCTRL ] )
{
// Make Ctrl-Delete delete up to end of line
m_szBuffer [ m_iBufferPos ] = ' \0 ' ;
m_iBufferLength = m_iBufferPos ;
}
else
{
// Delete just one char and move the others left
for ( int j = m_iBufferPos ; j < m_iBufferLength - 1 ; j + + )
m_szBuffer [ j ] = m_szBuffer [ j + 1 ] ;
m_szBuffer [ m_iBufferLength - 1 ] = ' \0 ' ;
m_iBufferLength - - ;
}
2004-06-26 14:45:54 +02:00
}
return ;
case SDLK_HOME :
2005-10-31 19:36:36 +01:00
if ( g_keys [ SDLK_RCTRL ] | | g_keys [ SDLK_LCTRL ] )
2005-01-01 19:12:23 +01:00
{
int linesShown = ( int ) m_fHeight / m_iFontHeight - 4 ;
m_iMsgHistPos = clamp ( ( int ) m_deqMsgHistory . size ( ) - linesShown , 1 , ( int ) m_deqMsgHistory . size ( ) ) ;
}
else
{
m_iBufferPos = 0 ;
}
2004-06-26 14:45:54 +02:00
return ;
case SDLK_END :
2005-10-31 19:36:36 +01:00
if ( g_keys [ SDLK_RCTRL ] | | g_keys [ SDLK_LCTRL ] )
2005-01-01 19:12:23 +01:00
{
m_iMsgHistPos = 1 ;
}
else
{
m_iBufferPos = m_iBufferLength ;
}
2004-06-26 14:45:54 +02:00
return ;
2004-06-02 22:40:07 +02:00
case SDLK_LEFT :
if ( m_iBufferPos ) m_iBufferPos - - ;
return ;
case SDLK_RIGHT :
if ( m_iBufferPos ! = m_iBufferLength ) m_iBufferPos + + ;
return ;
2005-01-23 18:45:25 +01:00
// BEGIN: Buffer History Lookup
2004-06-02 22:40:07 +02:00
case SDLK_UP :
2006-02-11 19:53:32 +01:00
if ( m_deqBufHistory . size ( ) )
2004-06-02 22:40:07 +02:00
{
2006-01-22 10:56:12 +01:00
int oldHistoryPos = iHistoryPos ;
while ( iHistoryPos ! = ( int ) m_deqBufHistory . size ( ) - 1 )
{
iHistoryPos + + ;
std : : wstring & histString = m_deqBufHistory . at ( iHistoryPos ) ;
2006-02-19 22:16:54 +01:00
if ( ( int ) histString . length ( ) > = m_iBufferPos )
2006-01-22 10:56:12 +01:00
{
bool bad = false ;
for ( int i = 0 ; i < m_iBufferPos ; i + + )
{
if ( histString [ i ] ! = m_szBuffer [ i ] )
{
bad = true ; break ;
}
}
if ( ! bad )
{
SetBuffer ( m_deqBufHistory . at ( iHistoryPos ) . data ( ) ) ;
return ;
}
}
}
// if we got here, failed to find a string with the right prefix;
// revert to the old position in case the user types more stuff
iHistoryPos = oldHistoryPos ;
2004-06-02 22:40:07 +02:00
}
return ;
case SDLK_DOWN :
2006-02-19 01:33:50 +01:00
if ( m_deqBufHistory . size ( ) & & iHistoryPos > 0 )
2006-01-22 10:56:12 +01:00
{
int oldHistoryPos = iHistoryPos ;
while ( iHistoryPos ! = 0 )
{
iHistoryPos - - ;
std : : wstring & histString = m_deqBufHistory . at ( iHistoryPos ) ;
2006-02-19 22:16:54 +01:00
if ( ( int ) histString . length ( ) > = m_iBufferPos )
2006-01-22 10:56:12 +01:00
{
bool bad = false ;
for ( int i = 0 ; i < m_iBufferPos ; i + + )
{
if ( histString [ i ] ! = m_szBuffer [ i ] )
{
bad = true ; break ;
}
}
if ( ! bad )
{
SetBuffer ( m_deqBufHistory . at ( iHistoryPos ) . data ( ) ) ;
return ;
}
}
}
// if we got here, failed to find a string with the right prefix;
// revert to the old position in case the user types more stuff,
// and also clear any complietion we might've added going up
iHistoryPos = oldHistoryPos ;
m_szBuffer [ m_iBufferPos ] = 0 ;
m_iBufferLength = m_iBufferPos ;
}
2004-06-02 22:40:07 +02:00
return ;
2005-01-23 18:45:25 +01:00
// END: Buffer History Lookup
2004-06-02 22:40:07 +02:00
2005-01-23 18:45:25 +01:00
// BEGIN: Message History Lookup
2004-06-02 22:40:07 +02:00
case SDLK_PAGEUP :
2004-06-21 18:29:47 +02:00
if ( m_iMsgHistPos ! = ( int ) m_deqMsgHistory . size ( ) ) m_iMsgHistPos + + ;
2004-06-02 22:40:07 +02:00
return ;
case SDLK_PAGEDOWN :
if ( m_iMsgHistPos ! = 1 ) m_iMsgHistPos - - ;
return ;
2005-01-23 18:45:25 +01:00
// END: Message History Lookup
2004-06-02 22:40:07 +02:00
default : //Insert a character
if ( IsFull ( ) ) return ;
2004-06-16 17:42:48 +02:00
if ( cooked = = 0 ) return ;
2004-06-02 22:40:07 +02:00
if ( IsEOB ( ) ) //are we at the end of the buffer?
2004-06-11 00:24:03 +02:00
m_szBuffer [ m_iBufferPos ] = cooked ; //cat char onto end
2004-06-02 22:40:07 +02:00
else { //we need to insert
2004-06-09 15:55:01 +02:00
int i ;
for ( i = m_iBufferLength ; i > m_iBufferPos ; i - - )
2004-06-02 22:40:07 +02:00
m_szBuffer [ i ] = m_szBuffer [ i - 1 ] ; // move chars to right
2004-06-11 00:24:03 +02:00
m_szBuffer [ i ] = cooked ;
2004-06-02 22:40:07 +02:00
}
m_iBufferPos + + ;
m_iBufferLength + + ;
return ;
}
}
2004-06-16 17:42:48 +02:00
void CConsole : : InsertMessage ( const wchar_t * szMessage , . . . )
{
va_list args ;
2004-08-14 13:40:40 +02:00
wchar_t szBuffer [ CONSOLE_MESSAGE_SIZE ] ;
2004-06-16 17:42:48 +02:00
va_start ( args , szMessage ) ;
2004-08-14 13:40:40 +02:00
if ( vswprintf ( szBuffer , CONSOLE_MESSAGE_SIZE , szMessage , args ) = = - 1 )
2004-07-11 17:07:38 +02:00
{
2005-05-11 20:56:30 +02:00
debug_printf ( " Error printfing console message (buffer size exceeded?) \n " ) ;
2004-07-11 17:07:38 +02:00
// Make it obvious that the text was trimmed (assuming it was)
2004-08-14 13:40:40 +02:00
wcscpy ( szBuffer + CONSOLE_MESSAGE_SIZE - 4 , L " ... " ) ;
2004-07-11 17:07:38 +02:00
}
2004-06-16 17:42:48 +02:00
va_end ( args ) ;
2006-09-02 23:20:25 +02:00
InsertMessageRaw ( CStrW ( szBuffer ) ) ;
}
2006-07-06 05:17:44 +02:00
2006-09-02 23:20:25 +02:00
void CConsole : : InsertMessageRaw ( const CStrW & message )
{
// (TODO: this text-wrapping is rubbish since we now use variable-width fonts)
2006-07-06 05:17:44 +02:00
//Insert newlines to wraparound text where needed
2006-09-02 23:20:25 +02:00
CStrW wrapAround ( message ) ;
CStrW newline ( L ' \n ' ) ;
2006-07-06 05:17:44 +02:00
size_t oldNewline = 0 ;
size_t distance ;
//make sure everything has been initialized
if ( m_charsPerPage ! = 0 )
{
while ( oldNewline + m_charsPerPage < wrapAround . length ( ) )
{
distance = wrapAround . find ( newline , oldNewline ) - oldNewline ;
if ( distance > m_charsPerPage )
{
oldNewline + = m_charsPerPage ;
wrapAround . insert ( oldNewline + + , newline ) ;
}
else
oldNewline + = distance + 1 ;
}
}
2004-07-11 17:07:38 +02:00
// Split into lines and add each one individually
2006-07-06 05:17:44 +02:00
oldNewline = 0 ;
while ( ( distance = wrapAround . find ( newline , oldNewline ) ) ! = wrapAround . npos )
2004-07-11 17:07:38 +02:00
{
2006-07-06 05:17:44 +02:00
distance - = oldNewline ;
m_deqMsgHistory . push_front ( wrapAround . substr ( oldNewline , distance ) ) ;
oldNewline + = distance + 1 ;
2004-07-11 17:07:38 +02:00
}
2006-07-06 05:17:44 +02:00
m_deqMsgHistory . push_front ( wrapAround . substr ( oldNewline ) ) ;
2004-06-02 22:40:07 +02:00
}
2004-08-03 01:14:54 +02:00
const wchar_t * CConsole : : GetBuffer ( )
{
m_szBuffer [ m_iBufferLength ] = 0 ;
return ( m_szBuffer ) ;
}
2004-06-02 22:40:07 +02:00
2004-06-16 17:42:48 +02:00
void CConsole : : SetBuffer ( const wchar_t * szMessage , . . . )
2004-06-02 22:40:07 +02:00
{
2006-01-22 10:56:12 +01:00
int oldBufferPos = m_iBufferPos ; // remember since FlushBuffer will set it to 0
2004-06-02 22:40:07 +02:00
va_list args ;
2004-08-14 13:40:40 +02:00
wchar_t szBuffer [ CONSOLE_BUFFER_SIZE ] ;
2004-06-02 22:40:07 +02:00
va_start ( args , szMessage ) ;
2004-08-14 13:40:40 +02:00
vswprintf ( szBuffer , CONSOLE_BUFFER_SIZE , szMessage , args ) ;
2004-06-02 22:40:07 +02:00
va_end ( args ) ;
FlushBuffer ( ) ;
2004-08-14 13:40:40 +02:00
wcsncpy ( m_szBuffer , szMessage , CONSOLE_BUFFER_SIZE ) ;
2006-01-22 10:56:12 +01:00
m_iBufferLength = ( int ) wcslen ( m_szBuffer ) ;
m_iBufferPos = std : : min ( oldBufferPos , m_iBufferLength ) ;
2004-06-02 22:40:07 +02:00
}
2006-11-07 22:03:13 +01:00
void CConsole : : UseHistoryFile ( const CStr & filename , int max_history_lines )
2005-05-10 09:13:25 +02:00
{
2005-08-09 17:55:44 +02:00
m_MaxHistoryLines = max_history_lines ;
2005-05-10 09:13:25 +02:00
m_sHistoryFile = filename ;
LoadHistory ( ) ;
}
2004-06-02 22:40:07 +02:00
2006-11-07 22:03:13 +01:00
void CConsole : : ProcessBuffer ( const wchar_t * szLine )
{
2004-06-02 22:40:07 +02:00
if ( szLine = = NULL ) return ;
2004-06-16 17:42:48 +02:00
if ( wcslen ( szLine ) < = 0 ) return ;
2006-02-11 19:53:32 +01:00
2005-06-28 06:06:25 +02:00
debug_assert ( wcslen ( szLine ) < CONSOLE_BUFFER_SIZE ) ;
2004-06-02 22:40:07 +02:00
m_deqBufHistory . push_front ( szLine ) ;
2005-05-10 09:13:25 +02:00
SaveHistory ( ) ; // Do this each line for the moment; if a script causes
// a crash it's a useful record.
2004-06-02 22:40:07 +02:00
2006-11-07 22:03:13 +01:00
wchar_t szCommand [ CONSOLE_BUFFER_SIZE ] = { 0 } ;
2004-06-02 22:40:07 +02:00
2004-06-16 17:42:48 +02:00
std : : map < std : : wstring , fptr > : : iterator Iter ;
2004-06-02 22:40:07 +02:00
2004-08-24 13:07:50 +02:00
if ( szLine [ 0 ] = = ' \\ ' )
{
2006-11-07 22:03:13 +01:00
if ( swscanf ( szLine , L " \\ %ls " , szCommand ) ! = 1 )
return ;
2004-06-09 15:35:20 +02:00
Trim ( szCommand ) ;
ToLower ( szCommand ) ;
2004-06-02 22:40:07 +02:00
2004-08-24 13:07:50 +02:00
if ( ! wcscmp ( szCommand , L " info " ) )
{
2004-06-26 14:58:35 +02:00
InsertMessage ( L " " ) ;
2004-06-16 17:42:48 +02:00
InsertMessage ( L " [Information] " ) ;
InsertMessage ( L " -View commands \" \\ commands \" " ) ;
InsertMessage ( L " -Call command \" \\ <command> \" " ) ;
InsertMessage ( L " -Say \" <string> \" " ) ;
2006-07-06 05:17:44 +02:00
InsertMessage ( L " -Help - Lists functions usable from console " ) ;
2004-06-26 14:58:35 +02:00
InsertMessage ( L " " ) ;
2004-08-24 13:07:50 +02:00
}
else if ( ! wcscmp ( szCommand , L " commands " ) )
{
2004-06-26 14:58:35 +02:00
InsertMessage ( L " " ) ;
2004-06-16 17:42:48 +02:00
InsertMessage ( L " [Commands] " ) ;
2004-06-02 22:40:07 +02:00
2004-06-16 17:42:48 +02:00
if ( ! m_mapFuncList . size ( ) ) InsertMessage ( L " (none registered) " ) ;
2004-06-02 22:40:07 +02:00
for ( Iter = m_mapFuncList . begin ( ) ; Iter ! = m_mapFuncList . end ( ) ; Iter + + )
2004-07-12 00:30:08 +02:00
InsertMessage ( L " \\ %ls " , Iter - > first . data ( ) ) ;
2004-06-02 22:40:07 +02:00
2004-06-26 14:58:35 +02:00
InsertMessage ( L " " ) ;
2004-08-24 13:07:50 +02:00
}
2006-07-06 05:17:44 +02:00
else if ( ! ( wcscmp ( szCommand , L " Help " ) & & wcscmp ( szCommand , L " help " ) ) )
{
InsertMessage ( L " " ) ;
InsertMessage ( L " [Help] " ) ;
2006-09-02 23:20:25 +02:00
InsertMessageRaw ( m_helpText ) ;
2006-07-06 05:17:44 +02:00
}
2004-08-24 13:07:50 +02:00
else
{
2004-06-16 17:42:48 +02:00
Iter = m_mapFuncList . find ( szCommand ) ;
if ( Iter = = m_mapFuncList . end ( ) )
2004-07-12 00:30:08 +02:00
InsertMessage ( L " unknown command <%ls> " , szCommand ) ;
2004-06-16 17:42:48 +02:00
else
Iter - > second ( ) ;
2004-06-02 22:40:07 +02:00
}
}
2007-06-17 00:07:40 +02:00
else if ( szLine [ 0 ] = = ' : ' | | szLine [ 0 ] = = ' ? ' )
2004-06-11 00:24:03 +02:00
{
// Process it as JavaScript
2004-06-16 17:42:48 +02:00
2007-06-17 00:07:40 +02:00
// Run the script inside the first selected entity, if there is one.
// (Actually do it by using a separate object with the entity as its parent, so the script
// can read the entities variables but will define new variables in a private scope)
// (NOTE: this doesn't actually work, because the entities don't really have properties
// since they aren't sufficiently like real JS objects, which makes them get ignored in
// this situation. But this code is here so that it will work when the entities get fixed.)
if ( ! m_ScriptObject )
{
m_ScriptObject = JS_NewObject ( g_ScriptingHost . GetContext ( ) , NULL , NULL , NULL ) ;
JS_AddRoot ( g_ScriptingHost . GetContext ( ) , & m_ScriptObject ) ; // gets unrooted in ~CConsole
}
if ( ! g_Selection . m_selected . empty ( ) )
{
JS_SetParent ( g_ScriptingHost . GetContext ( ) , m_ScriptObject , g_Selection . m_selected [ 0 ] - > GetScript ( ) ) ;
}
2004-11-11 08:09:32 +01:00
2007-06-17 00:07:40 +02:00
jsval rval = g_ScriptingHost . ExecuteScript ( CStrW ( szLine + 1 ) , L " Console " , m_ScriptObject ) ;
if ( szLine [ 0 ] = = ' ? ' & & rval )
2004-08-24 13:07:50 +02:00
{
try {
InsertMessage ( L " %ls " , g_ScriptingHost . ValueToUCString ( rval ) . c_str ( ) ) ;
} catch ( PSERROR_Scripting_ConversionFailed ) {
InsertMessage ( L " %hs " , " <error converting return value to string> " ) ;
}
}
2007-06-17 00:07:40 +02:00
2007-07-24 20:04:38 +02:00
JS_SetParent ( g_ScriptingHost . GetContext ( ) , m_ScriptObject , JS_GetGlobalObject ( g_ScriptingHost . GetContext ( ) ) ) ; // so the previous parent can get garbage-collected
2004-06-11 00:24:03 +02:00
}
2004-08-14 13:40:40 +02:00
else
2007-06-17 00:07:40 +02:00
{
2004-08-16 17:19:17 +02:00
SendChatMessage ( szLine ) ;
2007-06-17 00:07:40 +02:00
}
2004-06-02 22:40:07 +02:00
}
2005-05-10 09:13:25 +02:00
void CConsole : : LoadHistory ( )
{
2006-05-04 17:27:06 +02:00
// note: we don't care if this file doesn't exist or can't be read;
// just don't load anything in that case.
// do this before vfs_load to avoid an error message if file not found.
2007-02-01 15:46:14 +01:00
if ( ! vfs_exists ( m_sHistoryFile ) )
2006-05-04 17:27:06 +02:00
return ;
FileIOBuf buf ; size_t buflen ;
2007-02-01 15:46:14 +01:00
if ( vfs_load ( m_sHistoryFile , buf , buflen ) < 0 )
2006-01-24 09:16:29 +01:00
return ;
2007-02-01 15:46:14 +01:00
CStr bytes ( ( char * ) buf , buflen ) ;
2006-01-24 09:16:29 +01:00
( void ) file_buf_free ( buf ) ;
2007-02-01 15:46:14 +01:00
CStrW str ( bytes . FromUTF8 ( ) ) ;
2006-01-24 09:16:29 +01:00
size_t pos = 0 ;
2007-02-01 15:46:14 +01:00
while ( pos ! = CStrW : : npos )
2005-05-10 09:13:25 +02:00
{
2007-02-01 15:46:14 +01:00
pos = str . find ( ' \n ' ) ;
if ( pos ! = CStrW : : npos )
2005-05-10 09:13:25 +02:00
{
2007-02-01 15:46:14 +01:00
if ( pos > 0 )
m_deqBufHistory . push_front ( str . Left ( str [ pos - 1 ] = = ' \r ' ? pos - 1 : pos ) ) ;
str = str . substr ( pos + 1 ) ;
2005-05-10 09:13:25 +02:00
}
2007-02-01 15:46:14 +01:00
else if ( str . length ( ) > 0 )
m_deqBufHistory . push_front ( str ) ;
2005-05-10 09:13:25 +02:00
}
}
void CConsole : : SaveHistory ( )
{
CStr buffer ;
std : : deque < std : : wstring > : : iterator it ;
2005-08-09 17:55:44 +02:00
int line_count = 0 ;
2007-02-01 15:46:14 +01:00
for ( it = m_deqBufHistory . begin ( ) ; it ! = m_deqBufHistory . end ( ) ; + + it )
2005-05-10 09:13:25 +02:00
{
2007-02-01 15:46:14 +01:00
if ( line_count + + > = m_MaxHistoryLines )
2005-08-09 17:55:44 +02:00
break ;
2007-02-01 15:46:14 +01:00
buffer = CStrW ( * it ) . ToUTF8 ( ) + " \n " + buffer ;
2005-05-10 09:13:25 +02:00
}
2007-09-25 11:39:20 +02:00
vfs_store ( m_sHistoryFile , ( const u8 * ) buffer . c_str ( ) , buffer . length ( ) , FILE_NO_AIO ) ;
2005-05-10 09:13:25 +02:00
}
2004-08-16 17:19:17 +02:00
void CConsole : : SendChatMessage ( const wchar_t * szMessage )
{
if ( g_NetClient | | g_NetServer )
{
CChatMessage * msg = new CChatMessage ( ) ;
msg - > m_Recipient = PS_CHAT_RCP_ALL ;
msg - > m_Message = szMessage ;
if ( g_NetClient )
g_NetClient - > Push ( msg ) ;
else
{
2006-11-12 05:02:36 +01:00
msg - > m_Sender = 0 ;
ReceivedChatMessage ( g_NetServer - > GetServerPlayerName ( ) , msg - > m_Message . c_str ( ) ) ;
2004-08-16 17:19:17 +02:00
g_NetServer - > Broadcast ( msg ) ;
}
}
}
void CConsole : : ReceivedChatMessage ( const wchar_t * szSender , const wchar_t * szMessage )
{
InsertMessage ( L " %ls: %ls " , szSender , szMessage ) ;
}
2004-06-02 22:40:07 +02:00
2006-03-15 20:19:03 +01:00
static bool isUnprintableChar ( SDL_keysym key )
{
// U+0000 to U+001F are control characters
if ( key . unicode < 0x20 )
{
switch ( key . sym )
{
// We want to allow some, which are handled specially
case SDLK_RETURN : case SDLK_TAB :
case SDLK_BACKSPACE : case SDLK_DELETE :
case SDLK_HOME : case SDLK_END :
case SDLK_LEFT : case SDLK_RIGHT :
case SDLK_UP : case SDLK_DOWN :
case SDLK_PAGEUP : case SDLK_PAGEDOWN :
return false ;
// Ignore the others
default :
return true ;
}
}
return false ;
}
2006-08-26 23:52:18 +02:00
InReaction conInputHandler ( const SDL_Event_ * ev )
2004-06-02 22:40:07 +02:00
{
2006-08-26 23:52:18 +02:00
if ( ev - > ev . type = = SDL_HOTKEYDOWN )
2004-07-13 23:09:55 +02:00
{
2006-08-26 23:52:18 +02:00
if ( ev - > ev . user . code = = HOTKEY_CONSOLE_TOGGLE )
2004-07-21 18:34:07 +02:00
{
g_Console - > ToggleVisible ( ) ;
2006-03-15 20:19:03 +01:00
return IN_HANDLED ;
2004-07-21 18:34:07 +02:00
}
2006-08-26 23:52:18 +02:00
else if ( ev - > ev . user . code = = HOTKEY_CONSOLE_COPY )
2004-08-03 01:14:54 +02:00
{
2005-12-07 04:44:17 +01:00
sys_clipboard_set ( g_Console - > GetBuffer ( ) ) ;
2006-03-15 20:19:03 +01:00
return IN_HANDLED ;
2004-08-03 01:14:54 +02:00
}
2006-08-26 23:52:18 +02:00
else if ( ev - > ev . user . code = = HOTKEY_CONSOLE_PASTE )
2004-08-03 01:14:54 +02:00
{
2005-12-07 04:44:17 +01:00
wchar_t * text = sys_clipboard_get ( ) ;
2004-08-03 01:14:54 +02:00
if ( text )
{
for ( wchar_t * c = text ; * c ; c + + )
g_Console - > InsertChar ( 0 , * c ) ;
2005-12-07 04:44:17 +01:00
sys_clipboard_free ( text ) ;
2004-08-03 01:14:54 +02:00
}
2006-03-15 20:19:03 +01:00
return IN_HANDLED ;
2004-08-03 01:14:54 +02:00
}
2004-07-13 23:09:55 +02:00
}
2006-08-26 23:52:18 +02:00
if ( ev - > ev . type ! = SDL_KEYDOWN )
2005-10-20 19:44:56 +02:00
return IN_PASS ;
2004-07-21 18:34:07 +02:00
2006-08-26 23:52:18 +02:00
SDLKey sym = ev - > ev . key . keysym . sym ;
2006-02-11 19:53:32 +01:00
2004-07-13 23:09:55 +02:00
if ( ! g_Console - > IsActive ( ) )
2005-10-20 19:44:56 +02:00
return IN_PASS ;
2004-07-13 23:09:55 +02:00
2006-02-11 19:53:32 +01:00
// Stop unprintable characters (ctrl+, alt+ and escape),
2004-08-03 01:14:54 +02:00
// also prevent ` and/or ~ appearing in console every time it's toggled.
2006-08-26 23:52:18 +02:00
if ( ! isUnprintableChar ( ev - > ev . key . keysym ) & &
2006-02-11 19:53:32 +01:00
! hotkeys [ HOTKEY_CONSOLE_TOGGLE ] )
2006-08-26 23:52:18 +02:00
g_Console - > InsertChar ( sym , ( wchar_t ) ev - > ev . key . keysym . unicode ) ;
2004-07-13 23:09:55 +02:00
2005-10-20 19:44:56 +02:00
return IN_PASS ;
2004-06-02 22:40:07 +02:00
}