mirror of https://github.com/FWGS/xash3d-fwgs
652 lines
14 KiB
C
652 lines
14 KiB
C
/*
|
|
sys_con.c - win32 dedicated and developer console
|
|
Copyright (C) 2007 Uncle Mike
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "xash3d_mathlib.h"
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
WIN32 CONSOLE
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
// console defines
|
|
#define COMMAND_HISTORY 64 // system console keep more commands than game console
|
|
|
|
typedef struct
|
|
{
|
|
char title[64];
|
|
HWND hWnd;
|
|
HANDLE hInput;
|
|
HANDLE hOutput;
|
|
int inputLine;
|
|
int browseLine;
|
|
int cursorPosition;
|
|
int totalLines;
|
|
int savedConsoleTextLen;
|
|
int consoleTextLen;
|
|
string consoleText;
|
|
string savedConsoleText;
|
|
string statusLine;
|
|
string lineBuffer[COMMAND_HISTORY];
|
|
qboolean inputEnabled;
|
|
qboolean consoleVisible;
|
|
|
|
// log stuff
|
|
qboolean log_active;
|
|
char log_path[MAX_SYSPATH];
|
|
} WinConData;
|
|
|
|
static WinConData s_wcd;
|
|
static WORD g_color_table[8] =
|
|
{
|
|
FOREGROUND_INTENSITY, // black
|
|
FOREGROUND_RED, // red
|
|
FOREGROUND_GREEN, // green
|
|
FOREGROUND_RED | FOREGROUND_GREEN, // yellow
|
|
FOREGROUND_BLUE | FOREGROUND_INTENSITY, // blue
|
|
FOREGROUND_GREEN | FOREGROUND_BLUE, // cyan
|
|
FOREGROUND_RED | FOREGROUND_BLUE, // magenta
|
|
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, // default color (white)
|
|
};
|
|
|
|
static BOOL WINAPI Wcon_HandleConsole(DWORD CtrlType)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static void Wcon_PrintInternal( const char *msg, int length )
|
|
{
|
|
char *pTemp;
|
|
DWORD cbWritten;
|
|
const char *pMsgString;
|
|
static char tmpBuf[2048];
|
|
static char szOutput[2048];
|
|
|
|
Q_strncpy( szOutput, msg, length ? (length + 1) : ( sizeof( szOutput ) - 1 ));
|
|
if( length )
|
|
szOutput[length + 1] = '\0';
|
|
else
|
|
szOutput[sizeof( szOutput ) - 1] = '\0';
|
|
|
|
pTemp = tmpBuf;
|
|
pMsgString = szOutput;
|
|
while( pMsgString && *pMsgString )
|
|
{
|
|
if( IsColorString( pMsgString ))
|
|
{
|
|
if (( pTemp - tmpBuf ) > 0 )
|
|
{
|
|
// dump accumulated text before change color
|
|
*pTemp = 0; // terminate string
|
|
WriteFile( s_wcd.hOutput, tmpBuf, Q_strlen(tmpBuf), &cbWritten, 0 );
|
|
pTemp = tmpBuf;
|
|
}
|
|
|
|
// set new color
|
|
SetConsoleTextAttribute( s_wcd.hOutput, g_color_table[ColorIndex(*(pMsgString + 1))] );
|
|
pMsgString += 2; // skip color info
|
|
}
|
|
else if(( pTemp - tmpBuf ) < sizeof( tmpBuf ) - 1 )
|
|
{
|
|
*pTemp++ = *pMsgString++;
|
|
}
|
|
else
|
|
{
|
|
// temp buffer is full, dump it now
|
|
*pTemp = 0; // terminate string
|
|
WriteFile( s_wcd.hOutput, tmpBuf, Q_strlen(tmpBuf), &cbWritten, 0 );
|
|
pTemp = tmpBuf;
|
|
}
|
|
}
|
|
|
|
// check for last portion
|
|
if (( pTemp - tmpBuf ) > 0 )
|
|
{
|
|
// dump accumulated text
|
|
*pTemp = 0; // terminate string
|
|
WriteFile( s_wcd.hOutput, tmpBuf, Q_strlen(tmpBuf), &cbWritten, 0 );
|
|
pTemp = tmpBuf;
|
|
}
|
|
|
|
// restore white color
|
|
SetConsoleTextAttribute( s_wcd.hOutput, g_color_table[7] );
|
|
}
|
|
|
|
void Wcon_ShowConsole( qboolean show )
|
|
{
|
|
if( !s_wcd.hWnd || show == s_wcd.consoleVisible )
|
|
return;
|
|
|
|
s_wcd.consoleVisible = show;
|
|
if( show )
|
|
ShowWindow( s_wcd.hWnd, SW_SHOW );
|
|
else
|
|
ShowWindow( s_wcd.hWnd, SW_HIDE );
|
|
}
|
|
|
|
void Wcon_DisableInput( void )
|
|
{
|
|
if( host.type != HOST_DEDICATED )
|
|
return;
|
|
|
|
s_wcd.inputEnabled = false;
|
|
}
|
|
|
|
static void Wcon_SetInputText( const char *inputText )
|
|
{
|
|
if( host.type != HOST_DEDICATED )
|
|
return;
|
|
|
|
while( s_wcd.consoleTextLen-- )
|
|
{
|
|
Wcon_PrintInternal( "\b \b", 0 );
|
|
}
|
|
Wcon_PrintInternal( inputText, 0 );
|
|
Q_strncpy( s_wcd.consoleText, inputText, sizeof(s_wcd.consoleText) - 1 );
|
|
s_wcd.consoleTextLen = Q_strlen( inputText );
|
|
s_wcd.cursorPosition = s_wcd.consoleTextLen;
|
|
s_wcd.browseLine = s_wcd.inputLine;
|
|
}
|
|
|
|
static void Wcon_Clear_f( void )
|
|
{
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
SMALL_RECT scrollRect;
|
|
COORD scrollTarget;
|
|
CHAR_INFO fill;
|
|
|
|
if( host.type != HOST_DEDICATED )
|
|
return;
|
|
|
|
if( !GetConsoleScreenBufferInfo( s_wcd.hOutput, &csbi ))
|
|
{
|
|
return;
|
|
}
|
|
|
|
scrollRect.Left = 0;
|
|
scrollRect.Top = 0;
|
|
scrollRect.Right = csbi.dwSize.X;
|
|
scrollRect.Bottom = csbi.dwSize.Y;
|
|
scrollTarget.X = 0;
|
|
scrollTarget.Y = (SHORT)(0 - csbi.dwSize.Y);
|
|
fill.Char.UnicodeChar = TEXT(' ');
|
|
fill.Attributes = csbi.wAttributes;
|
|
ScrollConsoleScreenBuffer( s_wcd.hOutput, &scrollRect, NULL, scrollTarget, &fill );
|
|
|
|
csbi.dwCursorPosition.X = 0;
|
|
csbi.dwCursorPosition.Y = 0;
|
|
SetConsoleCursorPosition( s_wcd.hOutput, csbi.dwCursorPosition );
|
|
|
|
s_wcd.consoleText[0] = '\0';
|
|
s_wcd.consoleTextLen = 0;
|
|
s_wcd.cursorPosition = 0;
|
|
s_wcd.inputLine = 0;
|
|
s_wcd.browseLine = 0;
|
|
s_wcd.totalLines = 0;
|
|
Wcon_PrintInternal( "\n", 0 );
|
|
}
|
|
|
|
static void Wcon_EventUpArrow()
|
|
{
|
|
int nLastCommandInHistory = s_wcd.inputLine + 1;
|
|
if( nLastCommandInHistory > s_wcd.totalLines )
|
|
nLastCommandInHistory = 0;
|
|
|
|
if( s_wcd.browseLine == nLastCommandInHistory )
|
|
return;
|
|
|
|
if( s_wcd.browseLine == s_wcd.inputLine )
|
|
{
|
|
if( s_wcd.consoleTextLen > 0 )
|
|
{
|
|
Q_strncpy( s_wcd.savedConsoleText, s_wcd.consoleText, s_wcd.consoleTextLen );
|
|
}
|
|
s_wcd.savedConsoleTextLen = s_wcd.consoleTextLen;
|
|
}
|
|
|
|
s_wcd.browseLine--;
|
|
if( s_wcd.browseLine < 0 )
|
|
{
|
|
s_wcd.browseLine = s_wcd.totalLines - 1;
|
|
}
|
|
|
|
while( s_wcd.consoleTextLen-- )
|
|
{
|
|
Wcon_PrintInternal( "\b \b", 0 );
|
|
}
|
|
|
|
Wcon_PrintInternal( s_wcd.lineBuffer[s_wcd.browseLine], 0 );
|
|
Q_strncpy( s_wcd.consoleText, s_wcd.lineBuffer[s_wcd.browseLine], sizeof( s_wcd.consoleText ));
|
|
s_wcd.consoleTextLen = Q_strlen( s_wcd.lineBuffer[s_wcd.browseLine] );
|
|
s_wcd.cursorPosition = s_wcd.consoleTextLen;
|
|
}
|
|
|
|
static void Wcon_EventDownArrow()
|
|
{
|
|
if( s_wcd.browseLine == s_wcd.inputLine )
|
|
return;
|
|
|
|
if( ++s_wcd.browseLine > s_wcd.totalLines )
|
|
s_wcd.browseLine = 0;
|
|
|
|
while( s_wcd.consoleTextLen-- )
|
|
{
|
|
Wcon_PrintInternal( "\b \b", 0 );
|
|
}
|
|
|
|
if( s_wcd.browseLine == s_wcd.inputLine )
|
|
{
|
|
if( s_wcd.savedConsoleTextLen > 0 )
|
|
{
|
|
Q_strncpy( s_wcd.consoleText, s_wcd.savedConsoleText, s_wcd.savedConsoleTextLen );
|
|
Wcon_PrintInternal( s_wcd.consoleText, s_wcd.savedConsoleTextLen );
|
|
}
|
|
s_wcd.consoleTextLen = s_wcd.savedConsoleTextLen;
|
|
}
|
|
else
|
|
{
|
|
Wcon_PrintInternal( s_wcd.lineBuffer[s_wcd.browseLine], 0 );
|
|
Q_strncpy( s_wcd.consoleText, s_wcd.lineBuffer[s_wcd.browseLine], sizeof( s_wcd.consoleText ));
|
|
s_wcd.consoleTextLen = Q_strlen( s_wcd.lineBuffer[s_wcd.browseLine] );
|
|
}
|
|
s_wcd.cursorPosition = s_wcd.consoleTextLen;
|
|
}
|
|
|
|
static void Wcon_EventLeftArrow()
|
|
{
|
|
if( s_wcd.cursorPosition == 0 )
|
|
return;
|
|
|
|
Wcon_PrintInternal( "\b", 0 );
|
|
s_wcd.cursorPosition--;
|
|
}
|
|
|
|
static void Wcon_EventRightArrow()
|
|
{
|
|
if( s_wcd.cursorPosition == s_wcd.consoleTextLen )
|
|
return;
|
|
|
|
Wcon_PrintInternal( s_wcd.consoleText + s_wcd.cursorPosition, 1 );
|
|
s_wcd.cursorPosition++;
|
|
}
|
|
|
|
static int Wcon_EventNewline()
|
|
{
|
|
int nLen;
|
|
|
|
nLen = 0;
|
|
Wcon_PrintInternal( "\n", 0 );
|
|
if( s_wcd.consoleTextLen )
|
|
{
|
|
nLen = s_wcd.consoleTextLen;
|
|
|
|
s_wcd.consoleText[s_wcd.consoleTextLen] = '\0';
|
|
s_wcd.consoleTextLen = 0;
|
|
s_wcd.cursorPosition = 0;
|
|
|
|
if (( s_wcd.inputLine == 0 ) || ( Q_strcmp( s_wcd.lineBuffer[s_wcd.inputLine - 1], s_wcd.consoleText )))
|
|
{
|
|
Q_strncpy( s_wcd.lineBuffer[s_wcd.inputLine], s_wcd.consoleText, sizeof( s_wcd.consoleText ));
|
|
s_wcd.inputLine++;
|
|
|
|
if( s_wcd.inputLine > s_wcd.totalLines )
|
|
s_wcd.totalLines = s_wcd.inputLine;
|
|
|
|
if( s_wcd.inputLine >= COMMAND_HISTORY )
|
|
s_wcd.inputLine = 0;
|
|
}
|
|
s_wcd.browseLine = s_wcd.inputLine;
|
|
}
|
|
return nLen;
|
|
}
|
|
|
|
static void Wcon_EventBackspace()
|
|
{
|
|
int nCount;
|
|
|
|
if( s_wcd.cursorPosition < 1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
s_wcd.consoleTextLen--;
|
|
s_wcd.cursorPosition--;
|
|
|
|
Wcon_PrintInternal( "\b", 0 );
|
|
|
|
for( nCount = s_wcd.cursorPosition; nCount < s_wcd.consoleTextLen; ++nCount )
|
|
{
|
|
s_wcd.consoleText[nCount] = s_wcd.consoleText[nCount + 1];
|
|
Wcon_PrintInternal( s_wcd.consoleText + nCount, 1 );
|
|
}
|
|
|
|
Wcon_PrintInternal( " ", 0 );
|
|
|
|
nCount = s_wcd.consoleTextLen;
|
|
while( nCount >= s_wcd.cursorPosition )
|
|
{
|
|
Wcon_PrintInternal( "\b", 0 );
|
|
nCount--;
|
|
}
|
|
|
|
s_wcd.browseLine = s_wcd.inputLine;
|
|
}
|
|
|
|
static void Wcon_EventTab()
|
|
{
|
|
s_wcd.consoleText[s_wcd.consoleTextLen] = '\0';
|
|
Cmd_AutoComplete( s_wcd.consoleText );
|
|
Wcon_SetInputText( s_wcd.consoleText );
|
|
}
|
|
|
|
static void Wcon_EventCharacter(char c)
|
|
{
|
|
int nCount;
|
|
|
|
if( s_wcd.consoleTextLen >= ( sizeof( s_wcd.consoleText ) - 2 ))
|
|
{
|
|
return;
|
|
}
|
|
|
|
nCount = s_wcd.consoleTextLen;
|
|
while( nCount > s_wcd.cursorPosition )
|
|
{
|
|
s_wcd.consoleText[nCount] = s_wcd.consoleText[nCount - 1];
|
|
nCount--;
|
|
}
|
|
|
|
s_wcd.consoleText[s_wcd.cursorPosition] = c;
|
|
Wcon_PrintInternal( s_wcd.consoleText + s_wcd.cursorPosition, s_wcd.consoleTextLen - s_wcd.cursorPosition + 1 );
|
|
s_wcd.consoleTextLen++;
|
|
s_wcd.cursorPosition++;
|
|
|
|
nCount = s_wcd.consoleTextLen;
|
|
while( nCount > s_wcd.cursorPosition )
|
|
{
|
|
Wcon_PrintInternal( "\b", 0 );
|
|
nCount--;
|
|
}
|
|
|
|
s_wcd.browseLine = s_wcd.inputLine;
|
|
}
|
|
|
|
static void Wcon_UpdateStatusLine()
|
|
{
|
|
COORD coord;
|
|
WORD wAttrib;
|
|
DWORD dwWritten;
|
|
|
|
coord.X = 0;
|
|
coord.Y = 0;
|
|
wAttrib = g_color_table[5] | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY;
|
|
|
|
FillConsoleOutputCharacter( s_wcd.hOutput, ' ', 80, coord, &dwWritten );
|
|
FillConsoleOutputAttribute( s_wcd.hOutput, wAttrib, 80, coord, &dwWritten );
|
|
WriteConsoleOutputCharacter( s_wcd.hOutput, s_wcd.statusLine, Q_strlen(s_wcd.statusLine), coord, &dwWritten );
|
|
}
|
|
|
|
static char *Wcon_KeyEvent( int key, WCHAR character )
|
|
{
|
|
int nLen;
|
|
char inputBuffer[1024];
|
|
|
|
switch( key )
|
|
{
|
|
case VK_UP:
|
|
Wcon_EventUpArrow();
|
|
return NULL;
|
|
case VK_DOWN:
|
|
Wcon_EventDownArrow();
|
|
return NULL;
|
|
case VK_LEFT:
|
|
Wcon_EventLeftArrow();
|
|
return NULL;
|
|
case VK_RIGHT:
|
|
Wcon_EventRightArrow();
|
|
return NULL;
|
|
}
|
|
|
|
switch( character )
|
|
{
|
|
case '\r': // Enter
|
|
nLen = Wcon_EventNewline();
|
|
if (nLen)
|
|
{
|
|
return s_wcd.consoleText;
|
|
}
|
|
break;
|
|
case '\b': // Backspace
|
|
Wcon_EventBackspace();
|
|
break;
|
|
case '\t': // TAB
|
|
Wcon_EventTab();
|
|
break;
|
|
default:
|
|
// TODO implement converting wide chars to UTF-8 and properly handling it
|
|
if (( character >= ' ' ) && ( character <= '~' ))
|
|
{
|
|
Wcon_EventCharacter(character);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
WIN32 IO
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
/*
|
|
================
|
|
Con_WinPrint
|
|
|
|
print into window console
|
|
================
|
|
*/
|
|
void Wcon_WinPrint( const char *pMsg )
|
|
{
|
|
int nLen;
|
|
if( s_wcd.consoleTextLen )
|
|
{
|
|
nLen = s_wcd.consoleTextLen;
|
|
while (nLen--)
|
|
{
|
|
Wcon_PrintInternal( "\b \b", 0 );
|
|
}
|
|
}
|
|
|
|
Wcon_PrintInternal( pMsg, 0 );
|
|
|
|
if( s_wcd.consoleTextLen )
|
|
{
|
|
Wcon_PrintInternal( s_wcd.consoleText, s_wcd.consoleTextLen );
|
|
}
|
|
|
|
Wcon_UpdateStatusLine();
|
|
}
|
|
|
|
/*
|
|
================
|
|
Con_CreateConsole
|
|
|
|
create win32 console
|
|
================
|
|
*/
|
|
void Wcon_CreateConsole( void )
|
|
{
|
|
if( Sys_CheckParm( "-log" ))
|
|
s_wcd.log_active = true;
|
|
|
|
if( host.type == HOST_NORMAL )
|
|
{
|
|
Q_strncpy( s_wcd.title, va( "Xash3D %s", XASH_VERSION ), sizeof( s_wcd.title ));
|
|
Q_strncpy( s_wcd.log_path, "engine.log", sizeof( s_wcd.log_path ));
|
|
}
|
|
else // dedicated console
|
|
{
|
|
Q_strncpy( s_wcd.title, va( "XashDS %s", XASH_VERSION ), sizeof( s_wcd.title ));
|
|
Q_strncpy( s_wcd.log_path, "dedicated.log", sizeof( s_wcd.log_path ));
|
|
s_wcd.log_active = true; // always make log
|
|
}
|
|
|
|
AllocConsole();
|
|
SetConsoleTitle( s_wcd.title );
|
|
SetConsoleCP( CP_UTF8 );
|
|
SetConsoleOutputCP( CP_UTF8 );
|
|
|
|
s_wcd.hWnd = GetConsoleWindow();
|
|
s_wcd.hInput = GetStdHandle( STD_INPUT_HANDLE );
|
|
s_wcd.hOutput = GetStdHandle( STD_OUTPUT_HANDLE );
|
|
s_wcd.inputEnabled = true;
|
|
|
|
if( !SetConsoleCtrlHandler( &Wcon_HandleConsole, TRUE ))
|
|
{
|
|
Con_Reportf( S_ERROR "Couldn't attach console handler function\n" );
|
|
return;
|
|
}
|
|
|
|
SetWindowPos( s_wcd.hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOREPOSITION | SWP_SHOWWINDOW );
|
|
|
|
// show console if needed
|
|
if( host.con_showalways )
|
|
{
|
|
// make console visible
|
|
ShowWindow( s_wcd.hWnd, SW_SHOWDEFAULT );
|
|
UpdateWindow( s_wcd.hWnd );
|
|
SetForegroundWindow( s_wcd.hWnd );
|
|
SetFocus( s_wcd.hWnd );
|
|
s_wcd.consoleVisible = true;
|
|
}
|
|
else
|
|
{
|
|
s_wcd.consoleVisible = false;
|
|
ShowWindow( s_wcd.hWnd, SW_HIDE );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
Con_InitConsoleCommands
|
|
|
|
register console commands (dedicated only)
|
|
================
|
|
*/
|
|
void Wcon_InitConsoleCommands( void )
|
|
{
|
|
if( host.type != HOST_DEDICATED )
|
|
return;
|
|
|
|
Cmd_AddCommand( "clear", Wcon_Clear_f, "clear console history" );
|
|
}
|
|
|
|
/*
|
|
================
|
|
Con_DestroyConsole
|
|
|
|
destroy win32 console
|
|
================
|
|
*/
|
|
void Wcon_DestroyConsole( void )
|
|
{
|
|
// last text message into console or log
|
|
Con_Reportf( "Sys_FreeLibrary: Unloading xash.dll\n" );
|
|
|
|
Sys_CloseLog();
|
|
|
|
if( s_wcd.hWnd )
|
|
{
|
|
ShowWindow( s_wcd.hWnd, SW_HIDE );
|
|
s_wcd.hWnd = 0;
|
|
}
|
|
|
|
FreeConsole();
|
|
|
|
// place it here in case Sys_Crash working properly
|
|
if( host.hMutex )
|
|
CloseHandle( host.hMutex );
|
|
}
|
|
|
|
/*
|
|
================
|
|
Con_Input
|
|
|
|
returned input text
|
|
================
|
|
*/
|
|
char *Wcon_Input( void )
|
|
{
|
|
int i;
|
|
int eventsCount;
|
|
static INPUT_RECORD events[1024];
|
|
|
|
if( !s_wcd.inputEnabled )
|
|
return NULL;
|
|
|
|
while( true )
|
|
{
|
|
if( !GetNumberOfConsoleInputEvents( s_wcd.hInput, &eventsCount ))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if( eventsCount <= 0 )
|
|
break;
|
|
|
|
if( !ReadConsoleInputW( s_wcd.hInput, events, ARRAYSIZE( events ), &eventsCount ))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if( eventsCount == 0 )
|
|
return NULL;
|
|
|
|
for( i = 0; i < eventsCount; i++ )
|
|
{
|
|
INPUT_RECORD *pRec = &events[i];
|
|
if( pRec->EventType != KEY_EVENT )
|
|
continue;
|
|
|
|
if( pRec->Event.KeyEvent.bKeyDown )
|
|
return Wcon_KeyEvent( pRec->Event.KeyEvent.wVirtualKeyCode, pRec->Event.KeyEvent.uChar.UnicodeChar );
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
================
|
|
Wcon_SetStatus
|
|
|
|
set server status string in console
|
|
================
|
|
*/
|
|
void Wcon_SetStatus( const char *pStatus )
|
|
{
|
|
if( host.type != HOST_DEDICATED )
|
|
return;
|
|
|
|
Q_strncpy( s_wcd.statusLine, pStatus, sizeof( s_wcd.statusLine ) - 1 );
|
|
s_wcd.statusLine[sizeof( s_wcd.statusLine ) - 2] = '\0';
|
|
Wcon_UpdateStatusLine();
|
|
}
|