You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2525 lines
59 KiB
C++
2525 lines
59 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
// Description : Console implementation for UNIX systems based on curses ncurses
|
|
|
|
|
|
#include "CrySystem_precompiled.h"
|
|
#include "System.h"
|
|
#include "UnixConsole.h"
|
|
|
|
#if defined(USE_DEDICATED_SERVER_CONSOLE)
|
|
|
|
#if defined(USE_UNIXCONSOLE)
|
|
|
|
#if !defined(WIN32)
|
|
#include <sys/types.h>
|
|
#include <sys/select.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#if defined(MAC)
|
|
#include <util.h>
|
|
#include <sys/ioctl.h>
|
|
#else
|
|
#include <pty.h>
|
|
#endif // defined(MAC)
|
|
#if defined(LINUX) || defined(MAC)
|
|
#include <syslog.h>
|
|
#endif
|
|
#endif
|
|
#include <CryThread.h>
|
|
|
|
#define UNIXConsole_MORE_LEFT "<<"
|
|
#define UNIXConsole_MORE_RIGHT ">>"
|
|
#define UNIXConsole_MORE_COLOR (3) // Colorpair index
|
|
#define UNIXConsole_PROMPT "] "
|
|
#define UNIXConsole_PROMPT_COLOR (2) // Colorpair index
|
|
#define UNIXConsole_WRAP_CHAR '\\'
|
|
#define UNIXConsole_WRAP_COLOR (4)
|
|
#define UNIXConsole_MIN_WIDTH (10)
|
|
#define UNIXConsole_MAX_LINES (1000)
|
|
#define UNIXConsole_MAX_HISTORY (100)
|
|
|
|
#if defined(LINUX) || defined(MAC)
|
|
// Should find a better test for ncurses...
|
|
#define NCURSES 1
|
|
#endif
|
|
|
|
#if defined(WIN32)
|
|
#define snprintf _snprintf
|
|
#endif
|
|
|
|
// macro which check if we should process console function
|
|
// used for disable console on Linux
|
|
#define IS_SHOW_CONSOLE if (!m_bShowConsole) {return; }
|
|
#define IS_SHOW_CONSOLE_RET(ret) if (!m_bShowConsole) {return ret; }
|
|
|
|
|
|
CryCriticalSectionNonRecursive CUNIXConsole::m_cleanupLock;
|
|
|
|
class CUNIXConsoleInputThread
|
|
: public CrySimpleThread<>
|
|
{
|
|
CUNIXConsole& m_UNIXConsole;
|
|
#if !defined(WIN32)
|
|
int m_IntrPipe[2];
|
|
#else
|
|
HANDLE m_IntrEvent;
|
|
#endif
|
|
bool m_Cancelled;
|
|
|
|
public:
|
|
CUNIXConsoleInputThread(CUNIXConsole& UNIXConsole)
|
|
: m_UNIXConsole(UNIXConsole)
|
|
, m_Cancelled(false)
|
|
{
|
|
#if !defined(WIN32)
|
|
pipe(m_IntrPipe);
|
|
#else
|
|
m_IntrEvent = CreateEvent(NULL, true, false, NULL);
|
|
#endif
|
|
}
|
|
|
|
~CUNIXConsoleInputThread()
|
|
{
|
|
#if !defined(WIN32)
|
|
close(m_IntrPipe[0]);
|
|
close(m_IntrPipe[1]);
|
|
#else
|
|
CloseHandle(m_IntrEvent);
|
|
#endif
|
|
}
|
|
|
|
virtual void Run();
|
|
virtual void Cancel() { m_Cancelled = true; Interrupt(); }
|
|
void Interrupt()
|
|
{
|
|
#if !defined(WIN32)
|
|
write(m_IntrPipe[1], "", 1);
|
|
#else
|
|
SetEvent(m_IntrEvent);
|
|
#endif
|
|
}
|
|
};
|
|
|
|
class CUNIXConsoleSignalHandler
|
|
{
|
|
friend class CUNIXConsole;
|
|
|
|
static CUNIXConsole* m_pUNIXConsole;
|
|
static void Handler(int signum);
|
|
};
|
|
|
|
CUNIXConsole::CUNIXConsole()
|
|
: m_HistoryIndex(-1)
|
|
, m_PromptResponse(0)
|
|
, m_pSystem(NULL)
|
|
, m_pConsole(NULL)
|
|
, m_pTimer(NULL)
|
|
, m_OnUpdateCalled(false)
|
|
, m_LastUpdateTime(0.0f)
|
|
, m_svMap(NULL)
|
|
, m_svGameRules(NULL)
|
|
, m_Width(~0)
|
|
, m_Height(~0)
|
|
, m_HeaderHeight(1)
|
|
, m_StatusHeight(1)
|
|
, m_CmdHeight(2)
|
|
, m_Color(DEFAULT_COLOR)
|
|
, m_DefaultColorPair(-1)
|
|
, m_EnableColor(true)
|
|
, m_WindowResized(false)
|
|
, m_OnShutdownCalled(false)
|
|
, m_Initialized(false)
|
|
, m_RequireDedicatedServer(false)
|
|
, m_ScrollUp(0)
|
|
, m_InputThread(NULL)
|
|
, m_CursorPosition(0)
|
|
, m_ScrollPosition(0)
|
|
, m_fsMode(false)
|
|
, m_bShowConsole(true)
|
|
{
|
|
}
|
|
|
|
CUNIXConsole::~CUNIXConsole()
|
|
{
|
|
Cleanup();
|
|
m_Lock.Lock();
|
|
}
|
|
|
|
void CUNIXConsole::SetRequireDedicatedServer(bool value)
|
|
{
|
|
assert(!m_Initialized);
|
|
m_RequireDedicatedServer = value;
|
|
}
|
|
|
|
#if defined(WIN32) || defined(WIN64)
|
|
void ResizeConBufAndWindow(HANDLE hConsole, SHORT xSize, SHORT ySize)
|
|
{
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi; /* hold current console buffer info */
|
|
BOOL bSuccess;
|
|
SMALL_RECT srWindowRect; /* hold the new console size */
|
|
COORD coordScreen;
|
|
|
|
bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);
|
|
/* get the largest size we can size the console window to */
|
|
coordScreen = GetLargestConsoleWindowSize(hConsole);
|
|
/* define the new console window size and scroll position */
|
|
srWindowRect.Right = (SHORT) (min(xSize, coordScreen.X) - 1);
|
|
srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
|
|
srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
|
|
/* define the new console buffer size */
|
|
coordScreen.X = xSize;
|
|
coordScreen.Y = ySize;
|
|
/* if the current buffer is larger than what we want, resize the */
|
|
/* console window first, then the buffer */
|
|
if ((DWORD) csbi.dwSize.X * csbi.dwSize.Y > (DWORD) xSize * ySize)
|
|
{
|
|
bSuccess = SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect);
|
|
bSuccess = SetConsoleScreenBufferSize(hConsole, coordScreen);
|
|
bSuccess = SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect);
|
|
bSuccess = SetConsoleScreenBufferSize(hConsole, coordScreen);
|
|
}
|
|
/* if the current buffer is smaller than what we want, resize the */
|
|
/* buffer first, then the console window */
|
|
if ((DWORD) csbi.dwSize.X * csbi.dwSize.Y < (DWORD) xSize * ySize)
|
|
{
|
|
bSuccess = SetConsoleScreenBufferSize(hConsole, coordScreen);
|
|
bSuccess = SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect);
|
|
}
|
|
/* if the current buffer *is* the size we want, don't do anything! */
|
|
return;
|
|
}
|
|
|
|
BOOL WINAPI CtrlHandler(DWORD evt)
|
|
{
|
|
switch (evt)
|
|
{
|
|
case CTRL_C_EVENT:
|
|
case CTRL_BREAK_EVENT:
|
|
return TRUE;
|
|
case CTRL_CLOSE_EVENT:
|
|
gEnv->pSystem->Quit();
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
void CUNIXConsole::Init(const char* headerString)
|
|
{
|
|
assert(!m_Initialized);
|
|
|
|
if (headerString != NULL)
|
|
{
|
|
m_HeaderString = headerString;
|
|
}
|
|
|
|
#if defined(WIN32) || defined(WIN64)
|
|
// Allocate a console for the process. We don't care if this is successful
|
|
// or not, because failure indicates that the process already has a
|
|
// console, which is fine.
|
|
AllocConsole();
|
|
HANDLE hConsOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
ResizeConBufAndWindow(hConsOut, 120, 60);
|
|
SetConsoleCtrlHandler(CtrlHandler, TRUE);
|
|
#endif
|
|
|
|
// Initialize curses.
|
|
initscr();
|
|
cbreak();
|
|
noecho();
|
|
nonl();
|
|
intrflush(stdscr, FALSE);
|
|
keypad(stdscr, TRUE);
|
|
scrollok(stdscr, TRUE);
|
|
idcok(stdscr, TRUE);
|
|
idlok(stdscr, TRUE);
|
|
nodelay(stdscr, TRUE);
|
|
|
|
// Enable color output.
|
|
if (m_EnableColor)
|
|
{
|
|
if (start_color() != OK)
|
|
{
|
|
m_EnableColor = false;
|
|
}
|
|
}
|
|
|
|
if (m_EnableColor)
|
|
{
|
|
#if defined(NCURSES)
|
|
// Setup the color table.
|
|
short colorPair = 0;
|
|
attr_t attr;
|
|
attr_get(&attr, &colorPair, NULL);
|
|
m_DefaultColorPair = colorPair;
|
|
m_ColorPair[0] = m_DefaultColorPair;
|
|
m_ColorPair[1] = m_DefaultColorPair;
|
|
short color_fg = 0, color_bg = 0;
|
|
pair_content(m_DefaultColorPair, &color_fg, &color_bg);
|
|
short pair = 0;
|
|
init_pair(++pair, COLOR_BLUE, color_bg);
|
|
m_ColorPair[2] = pair;
|
|
init_pair(++pair, COLOR_GREEN, color_bg);
|
|
m_ColorPair[3] = pair;
|
|
init_pair(++pair, COLOR_RED, color_bg);
|
|
m_ColorPair[4] = pair;
|
|
init_pair(++pair, COLOR_CYAN, color_bg);
|
|
m_ColorPair[5] = pair;
|
|
init_pair(++pair, COLOR_YELLOW, COLOR_BLACK);
|
|
m_ColorPair[6] = pair;
|
|
init_pair(++pair, COLOR_MAGENTA, color_bg);
|
|
m_ColorPair[7] = pair;
|
|
init_pair(++pair, COLOR_RED, color_bg);
|
|
m_ColorPair[8] = pair;
|
|
init_pair(++pair, COLOR_BLACK, COLOR_WHITE);
|
|
m_ColorPair[9] = pair;
|
|
#else
|
|
// Color output supported only for ncurses.
|
|
m_EnableColor = false;
|
|
m_DefaultColorPair = 0;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
m_DefaultColorPair = 0;
|
|
}
|
|
|
|
// Set the screen size and draw the initial screen.
|
|
SetSize(COLS, LINES);
|
|
|
|
m_syslogStats.Init();
|
|
|
|
m_Initialized = true;
|
|
}
|
|
|
|
void CUNIXConsole::Cleanup()
|
|
{
|
|
m_cleanupLock.Lock();
|
|
if (m_Initialized)
|
|
{
|
|
// Kill the input thread.
|
|
if (m_InputThread != NULL)
|
|
{
|
|
m_InputThread->Cancel();
|
|
m_InputThread->WaitForThread();
|
|
delete m_InputThread;
|
|
m_InputThread = NULL;
|
|
}
|
|
|
|
// Curses cleanup.
|
|
clear();
|
|
endwin();
|
|
|
|
m_Initialized = false;
|
|
}
|
|
m_cleanupLock.Unlock();
|
|
}
|
|
|
|
void CUNIXConsole::SetSize(unsigned width, unsigned height)
|
|
{
|
|
bool repaint = false;
|
|
|
|
assert(IsLocked());
|
|
if (width != m_Width)
|
|
{
|
|
m_Width = width;
|
|
FixCursorPosition();
|
|
repaint = true;
|
|
}
|
|
if (height != m_Height)
|
|
{
|
|
m_Height = height;
|
|
repaint = true;
|
|
}
|
|
if (repaint)
|
|
{
|
|
Repaint();
|
|
}
|
|
}
|
|
|
|
bool CUNIXConsole::IsTooSmall()
|
|
{
|
|
assert(IsLocked());
|
|
if (m_Height < m_HeaderHeight + m_StatusHeight + m_CmdHeight + 1)
|
|
{
|
|
return true;
|
|
}
|
|
if (m_Width < UNIXConsole_MIN_WIDTH)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CUNIXConsole::CheckResize()
|
|
{
|
|
assert(IsLocked());
|
|
#if defined(NCURSES)
|
|
int lines, cols;
|
|
|
|
IS_SHOW_CONSOLE
|
|
|
|
if (m_WindowResized)
|
|
{
|
|
m_WindowResized = false;
|
|
// Get the new window size from the terminal driver.
|
|
winsize ws;
|
|
memset(&ws, 0, sizeof ws);
|
|
ioctl(1, TIOCGWINSZ, &ws);
|
|
lines = ws.ws_row;
|
|
cols = ws.ws_col;
|
|
if (m_Width != lines || m_Height != cols)
|
|
{
|
|
resizeterm(lines, cols);
|
|
SetSize(cols, lines);
|
|
Repaint();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CUNIXConsole::NewLine()
|
|
{
|
|
char lineBuf[3] = "\1X";
|
|
bool doScroll = !m_LineBuffer.empty();
|
|
|
|
assert(IsLocked());
|
|
lineBuf[1] = m_Color + '0';
|
|
|
|
if (m_LineBuffer.size() == UNIXConsole_MAX_LINES)
|
|
{
|
|
m_LineBuffer.pop_front();
|
|
}
|
|
m_LineBuffer.push_back(lineBuf);
|
|
|
|
if (doScroll)
|
|
{
|
|
ScrollLog();
|
|
}
|
|
else
|
|
{
|
|
unsigned row = m_Height - m_CmdHeight - m_StatusHeight - 1;
|
|
move(row, 0);
|
|
}
|
|
if (m_ScrollUp > 0)
|
|
{
|
|
const string& lastDisplayLine
|
|
= m_LineBuffer[m_LineBuffer.size() - m_ScrollUp - 1];
|
|
DrawLogLine(lastDisplayLine);
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::ContinueLine()
|
|
{
|
|
IS_SHOW_CONSOLE
|
|
|
|
if (m_LineBuffer.empty())
|
|
{
|
|
NewLine();
|
|
return;
|
|
}
|
|
const string& lastLine = *m_LineBuffer.rbegin();
|
|
unsigned column = DrawLogLine(lastLine, true /* noOutput */);
|
|
SetColorRaw(m_Color);
|
|
const unsigned row = m_Height - m_CmdHeight - m_StatusHeight - 1;
|
|
move(row, column);
|
|
}
|
|
|
|
void CUNIXConsole::SetColor(int color)
|
|
{
|
|
assert(IsLocked());
|
|
m_Color = color;
|
|
SetColorRaw(color);
|
|
if (m_LineBuffer.empty())
|
|
{
|
|
m_LineBuffer.push_back("");
|
|
}
|
|
const TLineBuffer::const_reverse_iterator it = m_LineBuffer.rbegin();
|
|
string& lastLine = const_cast<string&>(*it);
|
|
lastLine.push_back(1);
|
|
lastLine.push_back('0' + color);
|
|
}
|
|
|
|
void CUNIXConsole::SetColorRaw(int color)
|
|
{
|
|
attr_t colorAttr = 0;
|
|
|
|
assert(IsLocked());
|
|
if (color == DEFAULT_COLOR)
|
|
{
|
|
color = 0;
|
|
}
|
|
if (m_EnableColor)
|
|
{
|
|
colorAttr = COLOR_PAIR(m_ColorPair[color]);
|
|
}
|
|
switch (color)
|
|
{
|
|
case 0:
|
|
attrset(A_NORMAL | colorAttr);
|
|
break;
|
|
case 1:
|
|
attrset(A_REVERSE | colorAttr);
|
|
break;
|
|
case 2:
|
|
case 3:
|
|
attrset(A_NORMAL | colorAttr);
|
|
break;
|
|
case 4:
|
|
attrset(A_BOLD | colorAttr);
|
|
break;
|
|
case 5:
|
|
attrset(A_NORMAL | colorAttr);
|
|
break;
|
|
case 6:
|
|
attrset(A_BOLD | colorAttr);
|
|
break;
|
|
case 7:
|
|
case 8:
|
|
case 9:
|
|
attrset(A_NORMAL | colorAttr);
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::Put(int c)
|
|
{
|
|
assert(IsLocked());
|
|
|
|
// Get the last buffer line.
|
|
if (m_LineBuffer.empty())
|
|
{
|
|
NewLine();
|
|
}
|
|
const TLineBuffer::const_reverse_iterator it = m_LineBuffer.rbegin();
|
|
string& lastLine = const_cast<string&>(*it);
|
|
assert(c >= 0x20);
|
|
|
|
// Wrap the line if required.
|
|
unsigned row = m_Height - m_CmdHeight - m_StatusHeight - 1;
|
|
unsigned column = 0;
|
|
GetLineHeight(lastLine, &column);
|
|
assert(column <= m_Width);
|
|
lastLine.push_back(char(c));
|
|
|
|
if (m_ScrollUp == 0)
|
|
{
|
|
// Output the line wrap character.
|
|
if (column == m_Width - 1)
|
|
{
|
|
move(row, m_Width - 1);
|
|
attr_t colorAttr = 0;
|
|
if (m_EnableColor)
|
|
{
|
|
colorAttr = COLOR_PAIR(UNIXConsole_WRAP_COLOR);
|
|
}
|
|
attrset(A_NORMAL | colorAttr);
|
|
addch(UNIXConsole_WRAP_CHAR);
|
|
ScrollLog();
|
|
move(row, 0);
|
|
}
|
|
|
|
// Output the character.
|
|
SetColorRaw(m_Color);
|
|
addch(c);
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::Put(const char* s)
|
|
{
|
|
assert(IsLocked());
|
|
while (*s)
|
|
{
|
|
if (*s == '\n')
|
|
{
|
|
NewLine();
|
|
}
|
|
else
|
|
{
|
|
Put(*s);
|
|
}
|
|
++s;
|
|
}
|
|
}
|
|
|
|
unsigned CUNIXConsole::GetLineLength(const string& line)
|
|
{
|
|
unsigned length = 0;
|
|
|
|
for (string::const_iterator it = line.begin(), itEnd = line.end();
|
|
it != itEnd;
|
|
++it)
|
|
{
|
|
char c = *it;
|
|
if (c == 1)
|
|
{
|
|
++it;
|
|
assert(it != itEnd);
|
|
continue;
|
|
}
|
|
++length;
|
|
}
|
|
return length;
|
|
}
|
|
|
|
char CUNIXConsole::GetLastCharacter(const string& line, int* color)
|
|
{
|
|
char c0 = 0, c1 = 0;
|
|
char lastChar = 0;
|
|
|
|
if (!line.empty())
|
|
{
|
|
for (int i = line.size() - 1; i >= 0; i--)
|
|
{
|
|
c1 = c0;
|
|
c0 = line[i];
|
|
if (c0 == 1)
|
|
{
|
|
assert(lastChar);
|
|
if (color)
|
|
{
|
|
*color = c1 - '0';
|
|
}
|
|
return lastChar;
|
|
}
|
|
else if (c0 && c1 != 1 && !lastChar)
|
|
{
|
|
lastChar = c0;
|
|
}
|
|
}
|
|
}
|
|
|
|
*color = DEFAULT_COLOR;
|
|
if (lastChar)
|
|
{
|
|
return lastChar;
|
|
}
|
|
assert(c0 && c0 != 1);
|
|
return c0;
|
|
}
|
|
|
|
unsigned CUNIXConsole::GetLineHeight(const string& line, unsigned* column)
|
|
{
|
|
unsigned lineLength = GetLineLength(line);
|
|
unsigned height = 1;
|
|
|
|
assert(IsLocked());
|
|
while (lineLength > m_Width)
|
|
{
|
|
lineLength -= m_Width - 1;
|
|
++height;
|
|
}
|
|
if (column != NULL)
|
|
{
|
|
*column = lineLength;
|
|
}
|
|
return height;
|
|
}
|
|
|
|
void CUNIXConsole::ScrollLog()
|
|
{
|
|
if (m_fsMode)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Scroll the log window. We'll do that by defining a software scrolling
|
|
// region. The constructor has set scrollok() and idlok(), so the output
|
|
// routing will use the hardware scrolling region (if available).
|
|
unsigned top = m_HeaderHeight;
|
|
unsigned bottom = m_Height - m_CmdHeight - m_StatusHeight;
|
|
|
|
assert(IsLocked());
|
|
// Some curses implementations (pdcurses) require the current position to
|
|
// be within the defined scrolling region.
|
|
move(top, 0);
|
|
if (setscrreg(top, bottom) == OK)
|
|
{
|
|
move(bottom, 0);
|
|
addch('\n');
|
|
setscrreg(0, m_Height - 1);
|
|
DrawStatus(1);
|
|
}
|
|
else
|
|
{
|
|
// Scrolling regions not supported. We'll scroll the entire window and
|
|
// the repaint everything except for the log window.
|
|
scroll(stdscr);
|
|
move(bottom - 1, 0);
|
|
attrset(A_NORMAL);
|
|
clrtobot();
|
|
DrawHeader();
|
|
DrawStatus();
|
|
DrawCmd();
|
|
}
|
|
move(bottom - 1, 0);
|
|
}
|
|
|
|
bool CUNIXConsole::FixCursorPosition()
|
|
{
|
|
IS_SHOW_CONSOLE_RET(false)
|
|
|
|
bool repaint = false;
|
|
|
|
assert(IsLocked());
|
|
|
|
// Clip the cursor position.
|
|
if (m_CursorPosition > (int)m_InputLine.size())
|
|
{
|
|
m_CursorPosition = m_InputLine.size();
|
|
}
|
|
|
|
// Trivial scroll position fixes.
|
|
if (m_CursorPosition < (int)m_Width / 2 && m_ScrollPosition > 0)
|
|
{
|
|
m_ScrollPosition = 0;
|
|
repaint = true;
|
|
}
|
|
else if (m_CursorPosition < m_ScrollPosition)
|
|
{
|
|
m_ScrollPosition = m_CursorPosition - m_Width / 4;
|
|
repaint = true;
|
|
}
|
|
|
|
assert(m_ScrollPosition <= m_CursorPosition);
|
|
// The method may be called after the cursor has been moved to the
|
|
// right, so we may have to scroll the input line.
|
|
int displayLenth = m_Width * m_CmdHeight;
|
|
displayLenth -= strlen(UNIXConsole_PROMPT);
|
|
// If the cursor is at the end of the input line, then we only must leave
|
|
// one space for the cursor itself, otherwise we must leave space for the
|
|
// right scroll indicator.
|
|
if (m_CursorPosition == m_InputLine.size())
|
|
{
|
|
displayLenth -= 1;
|
|
}
|
|
else
|
|
{
|
|
displayLenth -= strlen(UNIXConsole_MORE_RIGHT);
|
|
}
|
|
if (m_ScrollPosition < m_CursorPosition - displayLenth)
|
|
{
|
|
m_ScrollPosition = m_CursorPosition - displayLenth;
|
|
repaint = true;
|
|
}
|
|
|
|
return repaint;
|
|
}
|
|
|
|
void CUNIXConsole::OnEdit()
|
|
{
|
|
assert(IsLocked());
|
|
m_SavedInputLine.clear();
|
|
m_HistoryIndex = -1;
|
|
if (m_pConsole != NULL)
|
|
{
|
|
m_pConsole->ResetAutoCompletion();
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyEnter()
|
|
{
|
|
bool redrawAll = false;
|
|
bool pushCommand = false;
|
|
|
|
assert(IsLocked());
|
|
|
|
// Scroll the log window to the bottom.
|
|
if (m_ScrollUp > 0)
|
|
{
|
|
m_ScrollUp = 0;
|
|
redrawAll = true;
|
|
}
|
|
|
|
// Process the input line.
|
|
while (!m_InputLine.empty() && m_InputLine[0] == '\\')
|
|
{
|
|
m_InputLine.erase(0, 1);
|
|
}
|
|
if (!m_InputLine.empty())
|
|
{
|
|
pushCommand = true;
|
|
|
|
#if defined(UC_ENABLE_MAGIC_COMMANDS)
|
|
// Enable some magic commands intercepted by the console. All magic
|
|
// commands start with an '@' character.
|
|
{
|
|
const char* const command = m_InputLine.c_str();
|
|
if (!azstricmp(command, "@quit"))
|
|
{
|
|
// We're called from the input thread, hence we can't join it. We
|
|
// have to prevent Cleanup() (called via atexit()) from trying to
|
|
// join.
|
|
CUNIXConsoleInputThread* inputThread = m_InputThread;
|
|
m_InputThread = NULL;
|
|
Unlock();
|
|
if (m_pSystem != NULL)
|
|
{
|
|
m_pSystem->Quit();
|
|
inputThread->Exit();
|
|
}
|
|
exit(0);
|
|
// Not reached.
|
|
abort();
|
|
}
|
|
// Add other magic commands here.
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (pushCommand)
|
|
{
|
|
CSystem* pSystem = static_cast<CSystem*>(gEnv->pSystem);
|
|
#if defined(CVARS_WHITELIST)
|
|
ICVarsWhitelist* pCVarsWhitelist = pSystem->GetCVarsWhiteList();
|
|
bool execute = (pCVarsWhitelist) ? pCVarsWhitelist->IsWhiteListed(m_InputLine, false) : true;
|
|
if (execute)
|
|
#endif // defined(CVARS_WHITELIST)
|
|
{
|
|
m_CommandQueue.push_back(m_InputLine);
|
|
}
|
|
}
|
|
|
|
if (!m_InputLine.empty())
|
|
{
|
|
m_CommandHistory.push_back(m_InputLine);
|
|
while (m_CommandHistory.size() > UNIXConsole_MAX_HISTORY)
|
|
{
|
|
m_CommandHistory.pop_front();
|
|
}
|
|
m_HistoryIndex = -1;
|
|
m_InputLine.clear();
|
|
m_SavedInputLine.clear();
|
|
m_CursorPosition = 0;
|
|
m_ScrollPosition = 0;
|
|
if (!redrawAll)
|
|
{
|
|
DrawCmd();
|
|
refresh();
|
|
}
|
|
}
|
|
|
|
if (redrawAll)
|
|
{
|
|
Repaint();
|
|
}
|
|
}
|
|
void CUNIXConsole::KeyUp()
|
|
{
|
|
const int historySize = m_CommandHistory.size();
|
|
|
|
assert(IsLocked());
|
|
if (m_HistoryIndex < historySize - 1)
|
|
{
|
|
if (m_HistoryIndex == -1)
|
|
{
|
|
m_SavedInputLine = m_InputLine;
|
|
}
|
|
m_HistoryIndex += 1;
|
|
m_InputLine = m_CommandHistory[historySize - m_HistoryIndex - 1];
|
|
m_CursorPosition = m_InputLine.size();
|
|
FixCursorPosition();
|
|
DrawCmd();
|
|
refresh();
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyDown()
|
|
{
|
|
const int historySize = m_CommandHistory.size();
|
|
|
|
assert(IsLocked());
|
|
if (m_HistoryIndex > -1)
|
|
{
|
|
m_HistoryIndex -= 1;
|
|
if (m_HistoryIndex == -1)
|
|
{
|
|
m_InputLine = m_SavedInputLine;
|
|
m_SavedInputLine.clear();
|
|
}
|
|
else
|
|
{
|
|
m_InputLine = m_CommandHistory[historySize - m_HistoryIndex - 1];
|
|
}
|
|
m_CursorPosition = m_InputLine.size();
|
|
FixCursorPosition();
|
|
DrawCmd();
|
|
refresh();
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyLeft()
|
|
{
|
|
assert(IsLocked());
|
|
if (m_CursorPosition > 0)
|
|
{
|
|
m_CursorPosition -= 1;
|
|
DrawCmd(!FixCursorPosition());
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyRight()
|
|
{
|
|
assert(IsLocked());
|
|
if (m_CursorPosition < (int)m_InputLine.size())
|
|
{
|
|
m_CursorPosition += 1;
|
|
DrawCmd(!FixCursorPosition());
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyHome(bool ctrl)
|
|
{
|
|
assert(IsLocked());
|
|
if (ctrl)
|
|
{
|
|
const int logHeight = GetLogHeight();
|
|
int maxUp = m_LineBuffer.size() - logHeight;
|
|
if (m_ScrollUp != maxUp)
|
|
{
|
|
m_ScrollUp = maxUp;
|
|
Repaint();
|
|
}
|
|
}
|
|
else if (m_CursorPosition != 0)
|
|
{
|
|
m_CursorPosition = 0;
|
|
DrawCmd(!FixCursorPosition());
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyEnd(bool ctrl)
|
|
{
|
|
assert(IsLocked());
|
|
if (ctrl)
|
|
{
|
|
const int logHeight = GetLogHeight();
|
|
int maxUp = m_LineBuffer.size() - logHeight;
|
|
if (m_ScrollUp != 0)
|
|
{
|
|
m_ScrollUp = 0;
|
|
Repaint();
|
|
}
|
|
}
|
|
else if (m_CursorPosition < (int)m_InputLine.size())
|
|
{
|
|
m_CursorPosition = m_InputLine.size();
|
|
DrawCmd(!FixCursorPosition());
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyBackspace()
|
|
{
|
|
assert(IsLocked());
|
|
if (m_CursorPosition > 0)
|
|
{
|
|
m_InputLine.erase(m_CursorPosition - 1, 1);
|
|
m_CursorPosition -= 1;
|
|
FixCursorPosition();
|
|
OnEdit();
|
|
DrawCmd();
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyDelete()
|
|
{
|
|
assert(IsLocked());
|
|
if (m_CursorPosition < (int)m_InputLine.size())
|
|
{
|
|
m_InputLine.erase(m_CursorPosition, 1);
|
|
FixCursorPosition();
|
|
OnEdit();
|
|
DrawCmd();
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyDeleteWord()
|
|
{
|
|
assert(IsLocked());
|
|
if (m_CursorPosition > 0)
|
|
{
|
|
const char* const inputLine = m_InputLine.c_str();
|
|
const char* p = inputLine + m_CursorPosition - 1;
|
|
|
|
while (p > inputLine && *p == ' ')
|
|
{
|
|
--p;
|
|
}
|
|
while (p > inputLine && *p != ' ')
|
|
{
|
|
--p;
|
|
}
|
|
m_InputLine.erase(p - inputLine, m_CursorPosition);
|
|
m_CursorPosition = (uint32)(p - inputLine);
|
|
FixCursorPosition();
|
|
OnEdit();
|
|
DrawCmd();
|
|
|
|
/* Old std::string based code, kept for reference.
|
|
size_t wordIndex;
|
|
wordIndex = m_InputLine.find_last_of(" ", m_CursorPosition - 1);
|
|
if (wordIndex != string::npos)
|
|
{
|
|
wordIndex = m_InputLine.find_last_not_of(" ", wordIndex);
|
|
if (wordIndex != string::npos)
|
|
wordIndex += 1;
|
|
}
|
|
if (wordIndex == string::npos)
|
|
{
|
|
m_InputLine.erase(0, m_CursorPosition);
|
|
m_CursorPosition = 0;
|
|
}
|
|
else
|
|
{
|
|
m_InputLine.erase(wordIndex, m_CursorPosition - wordIndex);
|
|
m_CursorPosition = wordIndex;
|
|
}
|
|
FixCursorPosition();
|
|
OnEdit();
|
|
DrawCmd();
|
|
*/
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyKill()
|
|
{
|
|
assert(IsLocked());
|
|
if (m_CursorPosition < (int)m_InputLine.size())
|
|
{
|
|
m_InputLine.resize(m_CursorPosition);
|
|
FixCursorPosition();
|
|
OnEdit();
|
|
DrawCmd();
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyRepaint()
|
|
{
|
|
assert(IsLocked());
|
|
Repaint();
|
|
}
|
|
|
|
void CUNIXConsole::KeyTab()
|
|
{
|
|
const char* result;
|
|
|
|
assert(IsLocked());
|
|
if (m_OnShutdownCalled)
|
|
{
|
|
return;
|
|
}
|
|
string Tmp(m_InputLine);
|
|
Unlock();
|
|
result = m_pConsole->ProcessCompletion(Tmp.c_str());
|
|
Lock();
|
|
if (result != NULL)
|
|
{
|
|
if (result[0] == '\\')
|
|
{
|
|
++result;
|
|
}
|
|
m_InputLine = result;
|
|
m_CursorPosition = m_InputLine.size();
|
|
FixCursorPosition();
|
|
m_SavedInputLine.clear();
|
|
m_HistoryIndex = -1;
|
|
DrawCmd();
|
|
refresh();
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyPgUp(bool ctrl)
|
|
{
|
|
const int logHeight = GetLogHeight();
|
|
// int logStep = logHeight - 2;
|
|
int logStep = ctrl ? 10 : 1;
|
|
int maxUp = m_LineBuffer.size() - logHeight;
|
|
int prevScrollUp = m_ScrollUp;
|
|
|
|
assert(IsLocked());
|
|
if (logStep < 1)
|
|
{
|
|
logStep = 1;
|
|
}
|
|
if (maxUp < 0)
|
|
{
|
|
maxUp = 0;
|
|
}
|
|
m_ScrollUp += logStep;
|
|
if (m_ScrollUp > maxUp)
|
|
{
|
|
m_ScrollUp = maxUp;
|
|
}
|
|
if (m_ScrollUp != prevScrollUp)
|
|
{
|
|
Repaint();
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyPgDown(bool ctrl)
|
|
{
|
|
const int logHeight = GetLogHeight();
|
|
// int logStep = logHeight - 2;
|
|
int logStep = ctrl ? 10 : 1;
|
|
int prevScrollUp = m_ScrollUp;
|
|
|
|
assert(IsLocked());
|
|
if (logStep < 1)
|
|
{
|
|
logStep = 1;
|
|
}
|
|
if (m_ScrollUp > 0)
|
|
{
|
|
m_ScrollUp -= logStep;
|
|
}
|
|
if (m_ScrollUp < 0)
|
|
{
|
|
m_ScrollUp = 0;
|
|
}
|
|
if (m_ScrollUp != prevScrollUp)
|
|
{
|
|
Repaint();
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::Key(int c)
|
|
{
|
|
assert(IsLocked());
|
|
assert(c >= 0x20 && c <= 0xff);
|
|
assert(m_CursorPosition <= (int)m_InputLine.size());
|
|
m_InputLine.insert(m_CursorPosition, 1, (char)c);
|
|
m_CursorPosition += 1;
|
|
FixCursorPosition();
|
|
OnEdit();
|
|
DrawCmd();
|
|
refresh();
|
|
}
|
|
|
|
void CUNIXConsole::Repaint()
|
|
{
|
|
IS_SHOW_CONSOLE
|
|
|
|
assert(IsLocked());
|
|
clear();
|
|
DrawHeader();
|
|
if (m_fsMode)
|
|
{
|
|
DrawFullscreen();
|
|
}
|
|
else
|
|
{
|
|
DrawLog();
|
|
}
|
|
DrawStatus();
|
|
DrawCmd();
|
|
refresh();
|
|
}
|
|
|
|
void CUNIXConsole::Flush()
|
|
{
|
|
IS_SHOW_CONSOLE
|
|
|
|
assert(IsLocked());
|
|
refresh();
|
|
}
|
|
|
|
void CUNIXConsole::InputIdle()
|
|
{
|
|
IS_SHOW_CONSOLE
|
|
|
|
if (m_pTimer == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Lock();
|
|
|
|
CTimeValue now = m_pTimer->GetAsyncTime();
|
|
float timePassed = (now - m_LastUpdateTime).GetSeconds();
|
|
|
|
// If more than 0.2 sec have passed since the last OnUpdate() call, then
|
|
// we'll start painting dots to the status line.
|
|
if (timePassed > 0.2f)
|
|
{
|
|
int nDots = (int)(timePassed + 0.5) / 3;
|
|
if (nDots > (int) m_Width - 2)
|
|
{
|
|
nDots = (int)m_Width - 2;
|
|
}
|
|
if (m_ProgressStatus.length() != nDots)
|
|
{
|
|
m_ProgressStatus.clear();
|
|
m_ProgressStatus.append(nDots, '.');
|
|
DrawStatus();
|
|
DrawCmd(true);
|
|
refresh();
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
}
|
|
|
|
// get at least n spaces
|
|
static char* GetSpaces(int n)
|
|
{
|
|
static char* spaceBuffer = 0;
|
|
static int spaceBufferSz = 0;
|
|
|
|
if (n > spaceBufferSz)
|
|
{
|
|
spaceBufferSz = MAX(spaceBufferSz * 2, n);
|
|
delete[] spaceBuffer;
|
|
spaceBuffer = new char[spaceBufferSz];
|
|
memset(spaceBuffer, ' ', spaceBufferSz);
|
|
}
|
|
|
|
return spaceBuffer;
|
|
}
|
|
|
|
void CUNIXConsole::DrawHeader()
|
|
{
|
|
IS_SHOW_CONSOLE
|
|
|
|
const char* const headerString = m_HeaderString.c_str();
|
|
int headerLength = m_HeaderString.size();
|
|
int padLeft = 0, padRight = 0;
|
|
const char* term = termname();
|
|
|
|
assert(IsLocked());
|
|
|
|
if (m_HeaderHeight == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (headerLength >= (int)m_Width)
|
|
{
|
|
padLeft = 0;
|
|
padRight = 0;
|
|
headerLength = m_Width;
|
|
}
|
|
else
|
|
{
|
|
padLeft = (m_Width - headerLength) / 2;
|
|
padRight = m_Width - headerLength - padLeft;
|
|
}
|
|
move(m_HeaderHeight - 1, 0);
|
|
#if defined(LINUX)
|
|
// For the Linux console ncurses reports the A_UNDERLINE is supported, even
|
|
// thought it is not. We'll do an explicit test for the terminal type
|
|
// "linux" (i.e. Linux console).
|
|
if ((termattrs() & A_UNDERLINE) && strcasecmp(term, "linux"))
|
|
{
|
|
attrset(A_UNDERLINE);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (m_EnableColor)
|
|
{
|
|
attrset(A_BOLD | COLOR_PAIR(m_ColorPair[2] /* blue */));
|
|
}
|
|
else
|
|
{
|
|
attrset(A_REVERSE);
|
|
}
|
|
}
|
|
scrollok(stdscr, FALSE);
|
|
addnstr(GetSpaces(padLeft), padLeft);
|
|
addnstr(headerString, headerLength);
|
|
addnstr(GetSpaces(padRight), padRight);
|
|
scrollok(stdscr, TRUE);
|
|
attrset(A_NORMAL);
|
|
}
|
|
|
|
// Output a single log line.
|
|
// If the noOutput flag is set to 'true', then no output is written to the
|
|
// screen and no cursor movements are performed.
|
|
// The method returns the current output column (i.e. the output column for
|
|
// the next charater to be written to the log window).
|
|
// Note: Even it the noOutput flag is set, the method will update the m_Color
|
|
// field of the UNIX console.
|
|
unsigned CUNIXConsole::DrawLogLine(const string& line, bool noOutput)
|
|
{
|
|
IS_SHOW_CONSOLE_RET(0)
|
|
|
|
const unsigned row = m_Height - m_CmdHeight - m_StatusHeight - 1;
|
|
unsigned column = 0;
|
|
|
|
if (!noOutput)
|
|
{
|
|
assert(IsLocked());
|
|
move(row, column);
|
|
attrset(A_NORMAL);
|
|
}
|
|
for (string::const_iterator it = line.begin(), itEnd = line.end(); it != itEnd; )
|
|
{
|
|
char c = *it++;
|
|
if (column == m_Width - 1)
|
|
{
|
|
if (!noOutput)
|
|
{
|
|
attr_t colorAttr = 0;
|
|
if (m_EnableColor)
|
|
{
|
|
colorAttr = COLOR_PAIR(UNIXConsole_WRAP_COLOR);
|
|
}
|
|
attrset(A_NORMAL | colorAttr);
|
|
addch(UNIXConsole_WRAP_CHAR);
|
|
ScrollLog();
|
|
move(row, 0);
|
|
SetColorRaw(m_Color);
|
|
}
|
|
column = 0;
|
|
}
|
|
if (c == 1)
|
|
{
|
|
assert(it != itEnd);
|
|
int color = *it - '0';
|
|
m_Color = color;
|
|
++it;
|
|
if (!noOutput)
|
|
{
|
|
SetColorRaw(color);
|
|
}
|
|
continue;
|
|
}
|
|
if (!noOutput)
|
|
{
|
|
addch(c);
|
|
}
|
|
++column;
|
|
}
|
|
return column;
|
|
}
|
|
|
|
// The scrollUp parameter indicates how many log lines to scroll up from the
|
|
// bottom.
|
|
void CUNIXConsole::DrawLog()
|
|
{
|
|
IS_SHOW_CONSOLE
|
|
|
|
unsigned scrollUp = m_ScrollUp;
|
|
|
|
assert(IsLocked());
|
|
|
|
if (IsTooSmall())
|
|
{
|
|
return;
|
|
}
|
|
if (m_LineBuffer.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// DrawLog is called only on refresh and on window resize, so performance is
|
|
// not an issue. We'll simply repaint by re-sending the log lines from the
|
|
// scroll buffer.
|
|
int nLines = m_LineBuffer.size();
|
|
int lastLine = nLines - 1 - (int)scrollUp;
|
|
int firstLine = lastLine - GetLogHeight();
|
|
|
|
if (firstLine < 0)
|
|
{
|
|
firstLine = 0;
|
|
}
|
|
for (int i = firstLine; i <= lastLine; ++i)
|
|
{
|
|
const string& line = m_LineBuffer[i];
|
|
if (i > firstLine)
|
|
{
|
|
ScrollLog();
|
|
}
|
|
DrawLogLine(line);
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::DrawStatus(int maxLines)
|
|
{
|
|
IS_SHOW_CONSOLE
|
|
|
|
unsigned row = m_Height - m_CmdHeight - m_StatusHeight;
|
|
const char* statusLeft = NULL;
|
|
const char* statusRight = NULL;
|
|
char bufferLeft[256];
|
|
char bufferRight[256];
|
|
|
|
assert(IsLocked());
|
|
|
|
if (IsTooSmall() || maxLines == 0 || m_StatusHeight == 0)
|
|
{
|
|
return;
|
|
}
|
|
else if (maxLines == -1)
|
|
{
|
|
maxLines = m_StatusHeight;
|
|
}
|
|
|
|
// If we're scrolled, then the right size shows a scroll indicator.
|
|
if (m_ScrollUp > 0)
|
|
{
|
|
const int logHeight = GetLogHeight();
|
|
int logBottomLine = (int)m_LineBuffer.size() - m_ScrollUp;
|
|
assert(logBottomLine >= 0);
|
|
float percent = 100.f * (float)logBottomLine / m_LineBuffer.size();
|
|
if (m_ScrollUp == m_LineBuffer.size() - logHeight)
|
|
{
|
|
cry_strcpy(bufferRight, "| SCROLL:TOP ");
|
|
}
|
|
else
|
|
{
|
|
snprintf(
|
|
bufferRight,
|
|
sizeof bufferRight,
|
|
"| SCROLL:%.1f%% ",
|
|
percent);
|
|
bufferRight[sizeof(bufferRight) - 1] = 0;
|
|
}
|
|
statusRight = bufferRight;
|
|
}
|
|
|
|
if (!m_Prompt.empty())
|
|
{
|
|
// No status display when a user prompt is active.
|
|
}
|
|
else if (!m_ProgressStatus.empty())
|
|
{
|
|
snprintf(bufferLeft, sizeof bufferLeft, " %s", m_ProgressStatus.c_str());
|
|
bufferLeft[sizeof bufferLeft - 1] = 0;
|
|
statusLeft = bufferLeft;
|
|
}
|
|
else if (m_OnUpdateCalled)
|
|
{
|
|
// Standard status display.
|
|
// Map name and game rules on the left.
|
|
// Current update rate and player count on the right.
|
|
const char* mapName = m_svMap->GetString();
|
|
const char* gameRules = m_svGameRules->GetString();
|
|
snprintf(bufferLeft, sizeof bufferLeft,
|
|
" map:%s rules:%s",
|
|
mapName,
|
|
gameRules);
|
|
bufferLeft[sizeof bufferLeft - 1] = 0;
|
|
statusLeft = bufferLeft;
|
|
float updateRate = 0.f;
|
|
static float displayUpdateRate = 0.f;
|
|
if (m_pTimer != NULL)
|
|
{
|
|
updateRate = m_pTimer->GetFrameRate();
|
|
#if 0
|
|
// Avoid jumping numbers in the update rate display. Per update the
|
|
// displayed update rate changes by at most maxDeltaRate.
|
|
const static float maxDeltaRate = 10.0f;
|
|
if (updateRate - displayUpdateRate > maxDeltaRate)
|
|
{
|
|
displayUpdateRate += maxDeltaRate;
|
|
}
|
|
else if (updateRate - displayUpdateRate < -maxDeltaRate)
|
|
{
|
|
displayUpdateRate -= maxDeltaRate;
|
|
}
|
|
else
|
|
{
|
|
displayUpdateRate = updateRate;
|
|
}
|
|
#else
|
|
// Display the update rate as reported by the timer.
|
|
displayUpdateRate = updateRate;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
displayUpdateRate = 0.f;
|
|
}
|
|
if (statusRight == NULL)
|
|
{
|
|
char* pBufferRight = bufferRight;
|
|
char* const pBufferRightEnd = bufferRight + sizeof bufferRight;
|
|
azstrcpy(pBufferRight, AZ_ARRAY_SIZE(bufferRight), "| ");
|
|
pBufferRight += strlen(pBufferRight);
|
|
int numPlayers = 0;
|
|
|
|
if (pBufferRight < pBufferRightEnd)
|
|
{
|
|
if (m_pConsole != NULL)
|
|
{
|
|
pBufferRight += snprintf(
|
|
pBufferRight,
|
|
pBufferRightEnd - pBufferRight,
|
|
"upd:%.1fms(%.2f..%.2f) " \
|
|
"rate:%.1f/s",
|
|
m_updStats.avgUpdateTime, m_updStats.minUpdateTime, m_updStats.maxUpdateTime,
|
|
displayUpdateRate);
|
|
}
|
|
else
|
|
{
|
|
cry_strcpy(pBufferRight, pBufferRightEnd - pBufferRight, "BUSY ");
|
|
}
|
|
}
|
|
bufferRight[sizeof bufferRight - 1] = 0;
|
|
statusRight = bufferRight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No status display (blank). This branch is taken on the very first draw
|
|
// operation of the UNIX console.
|
|
}
|
|
if (statusLeft == NULL)
|
|
{
|
|
statusLeft = "";
|
|
}
|
|
if (statusRight == NULL)
|
|
{
|
|
statusRight = "";
|
|
}
|
|
|
|
int leftWidth = strlen(statusLeft);
|
|
int rightWidth = strlen(statusRight);
|
|
int pad = 0;
|
|
|
|
if (leftWidth + rightWidth > (int)m_Width)
|
|
{
|
|
pad = 0;
|
|
if (rightWidth > (int)m_Width)
|
|
{
|
|
leftWidth = 0;
|
|
rightWidth = m_Width;
|
|
}
|
|
else
|
|
{
|
|
leftWidth = m_Width - rightWidth;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pad = m_Width - leftWidth - rightWidth;
|
|
}
|
|
|
|
move(row, 0);
|
|
attrset(A_REVERSE | A_BOLD);
|
|
scrollok(stdscr, FALSE);
|
|
for (int i = 0; i < leftWidth; ++i)
|
|
{
|
|
addch(statusLeft[i]);
|
|
}
|
|
for (int i = 0; i < pad; ++i)
|
|
{
|
|
addch(' ');
|
|
}
|
|
for (int i = 0; i < rightWidth; ++i)
|
|
{
|
|
addch(statusRight[i]);
|
|
}
|
|
scrollok(stdscr, TRUE);
|
|
attrset(A_NORMAL);
|
|
}
|
|
|
|
void CUNIXConsole::DrawFullscreen()
|
|
{
|
|
IS_SHOW_CONSOLE
|
|
|
|
scrollok(stdscr, FALSE);
|
|
|
|
int maxy = 1;
|
|
for (DynArray<SConDrawCmd>::iterator iter = m_drawCmds.begin(); iter != m_drawCmds.end(); ++iter)
|
|
{
|
|
maxy = max(maxy, iter->y);
|
|
}
|
|
|
|
int scrolly = min(maxy - 1, m_ScrollUp);
|
|
|
|
for (DynArray<SConDrawCmd>::iterator iter = m_drawCmds.begin(); iter != m_drawCmds.end(); ++iter)
|
|
{
|
|
switch (iter->op)
|
|
{
|
|
case eCDO_PutText:
|
|
{
|
|
int y = iter->y - scrolly;
|
|
if (y < 0 || y > (int)m_Height - 4)
|
|
{
|
|
break;
|
|
}
|
|
if (iter->x < 0 || iter->x > (int)m_Width)
|
|
{
|
|
break;
|
|
}
|
|
int len = strlen(iter->text);
|
|
if (iter->x + len > (int)m_Width)
|
|
{
|
|
len = m_Width - iter->x;
|
|
}
|
|
move(y + 1, iter->x);
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
addch(iter->text[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
scrollok(stdscr, TRUE);
|
|
}
|
|
|
|
void CUNIXConsole::DrawCmd(bool cursorOnly)
|
|
{
|
|
IS_SHOW_CONSOLE
|
|
|
|
unsigned row = m_Height - m_CmdHeight;
|
|
unsigned column = 0;
|
|
attr_t colorAttr = 0;
|
|
const unsigned promptWidth = strlen(UNIXConsole_PROMPT);
|
|
const unsigned moreLeftWidth = strlen(UNIXConsole_MORE_LEFT);
|
|
const unsigned moreRightWidth = strlen(UNIXConsole_MORE_RIGHT);
|
|
|
|
assert(IsLocked());
|
|
|
|
// If the window is too small, then don't draw anything.
|
|
if (IsTooSmall()
|
|
|| m_CmdHeight == 0
|
|
|| m_Width < promptWidth + moreLeftWidth + moreRightWidth)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!m_Prompt.empty())
|
|
{
|
|
DrawCmdPrompt();
|
|
return;
|
|
}
|
|
|
|
if (!cursorOnly)
|
|
{
|
|
scrollok(stdscr, FALSE);
|
|
|
|
// Draw the command prompt.
|
|
if (m_EnableColor)
|
|
{
|
|
colorAttr = COLOR_PAIR(UNIXConsole_PROMPT_COLOR);
|
|
}
|
|
attrset(A_BOLD | colorAttr);
|
|
move(row, 0);
|
|
for (const char* p = UNIXConsole_PROMPT; *p; ++p)
|
|
{
|
|
addch(*p);
|
|
++column;
|
|
}
|
|
|
|
// Draw the left scroll indicator (if scrolled).
|
|
if (m_ScrollPosition > 0)
|
|
{
|
|
if (m_EnableColor)
|
|
{
|
|
colorAttr = COLOR_PAIR(UNIXConsole_MORE_COLOR);
|
|
}
|
|
attrset(A_NORMAL | colorAttr);
|
|
for (const char* p = UNIXConsole_MORE_LEFT; *p; ++p)
|
|
{
|
|
addch(*p);
|
|
++column;
|
|
}
|
|
}
|
|
|
|
// Draw the input line. We'll draw to the end of the command window
|
|
// (leaving the last cell blank) and the overdraw the more indicator (if
|
|
// required).
|
|
string::const_iterator it = m_InputLine.begin();
|
|
string::const_iterator itEnd = m_InputLine.end();
|
|
if (m_ScrollPosition > 0)
|
|
{
|
|
for (int i = m_ScrollPosition + strlen(UNIXConsole_MORE_LEFT);
|
|
i > 0;
|
|
--i, ++it)
|
|
{
|
|
;
|
|
}
|
|
}
|
|
attrset(A_NORMAL);
|
|
bool lineTruncated = false;
|
|
for (; it != itEnd; ++it)
|
|
{
|
|
char c = *it;
|
|
if (row == m_Height - 1 && column == m_Width - 1)
|
|
{
|
|
lineTruncated = true;
|
|
break;
|
|
}
|
|
if (column == m_Width)
|
|
{
|
|
row += 1;
|
|
column = 0;
|
|
assert(row < m_Height);
|
|
move(row, column);
|
|
}
|
|
addch(c);
|
|
++column;
|
|
}
|
|
|
|
// Draw the right scroll indicator (if required).
|
|
if (lineTruncated)
|
|
{
|
|
move(m_Height - 1, m_Width - moreRightWidth);
|
|
if (m_EnableColor)
|
|
{
|
|
colorAttr = COLOR_PAIR(UNIXConsole_MORE_COLOR);
|
|
}
|
|
attrset(A_NORMAL | colorAttr);
|
|
for (const char* p = UNIXConsole_MORE_RIGHT; *p; ++p)
|
|
{
|
|
addch(*p);
|
|
++column;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
attrset(A_NORMAL);
|
|
clrtobot();
|
|
move(m_Height - 1, m_Width - 1);
|
|
addch(' ');
|
|
}
|
|
|
|
scrollok(stdscr, TRUE);
|
|
}
|
|
|
|
// Update the cursor position.
|
|
column = m_CursorPosition - m_ScrollPosition + promptWidth;
|
|
row = m_Height - m_CmdHeight;
|
|
if (column >= m_Width)
|
|
{
|
|
row += column / m_Width;
|
|
column %= m_Width;
|
|
}
|
|
move(row, column);
|
|
refresh();
|
|
}
|
|
|
|
void CUNIXConsole::DrawCmdPrompt()
|
|
{
|
|
IS_SHOW_CONSOLE
|
|
|
|
unsigned row = m_Height - m_CmdHeight;
|
|
unsigned column = 0;
|
|
|
|
string::const_iterator it = m_Prompt.begin();
|
|
string::const_iterator itEnd = m_Prompt.end();
|
|
attrset(A_BOLD);
|
|
clrtobot();
|
|
scrollok(stdscr, FALSE);
|
|
move(row, column);
|
|
for (; it != itEnd; ++it)
|
|
{
|
|
char c = *it;
|
|
if (row == m_Height - 1 && column == m_Width - 1)
|
|
{
|
|
break;
|
|
}
|
|
if (column == m_Width)
|
|
{
|
|
row += 1;
|
|
column = 0;
|
|
move(row, column);
|
|
}
|
|
addch(c);
|
|
++column;
|
|
}
|
|
scrollok(stdscr, TRUE);
|
|
move(row, column);
|
|
refresh();
|
|
}
|
|
|
|
char CUNIXConsole::Prompt(const char* promptString, const char* responseChars)
|
|
{
|
|
char response = 0;
|
|
|
|
Lock();
|
|
|
|
while (m_PromptResponse != 0)
|
|
{
|
|
m_PromptCond.Wait(m_Lock);
|
|
}
|
|
#ifndef _NDEBUG
|
|
// This method is called from __assert_fail, so we better don't put any
|
|
// asserts in here...
|
|
if (!m_Prompt.empty() || !*promptString || !*responseChars)
|
|
{
|
|
abort();
|
|
}
|
|
if (strlen(responseChars) + 1 > sizeof m_PromptResponseChars)
|
|
{
|
|
abort();
|
|
}
|
|
#endif
|
|
m_Prompt = promptString;
|
|
cry_strcpy(m_PromptResponseChars, responseChars);
|
|
DrawCmd();
|
|
while (m_PromptResponse == 0)
|
|
{
|
|
m_PromptCond.Wait(m_Lock);
|
|
}
|
|
response = m_PromptResponse;
|
|
m_PromptResponse = 0;
|
|
m_Prompt.clear();
|
|
m_PromptResponseChars[0] = 0;
|
|
m_PromptCond.Notify();
|
|
DrawCmd();
|
|
|
|
Unlock();
|
|
|
|
return response;
|
|
}
|
|
|
|
bool CUNIXConsole::IsInputThread()
|
|
{
|
|
CrySimpleThread<>* callerThread = CrySimpleThread<>::Self();
|
|
|
|
return callerThread == m_InputThread;
|
|
}
|
|
|
|
void CUNIXConsole::PrintF(const char* format, ...)
|
|
{
|
|
char lineBuffer[1024];
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
vsnprintf(lineBuffer, sizeof lineBuffer, format, ap);
|
|
lineBuffer[sizeof lineBuffer - 1] = 0;
|
|
Print(lineBuffer);
|
|
va_end(ap);
|
|
}
|
|
|
|
void CUNIXConsole::Print(const char* line)
|
|
{
|
|
IS_SHOW_CONSOLE
|
|
|
|
static string lastLine;
|
|
static bool firstCall = true;
|
|
const size_t lineLength = strlen(line);
|
|
size_t lineOffset = 0;
|
|
|
|
Lock();
|
|
|
|
// Check if the last line is a true prefix of the specified text argument.
|
|
// It it is a prefix, then the output is added to the last line sent to the
|
|
// sink.
|
|
if (!firstCall
|
|
&& lineLength > lastLine.size()
|
|
&& !strncmp(line, lastLine.c_str(), lastLine.size()))
|
|
{
|
|
// Line continued.
|
|
lineOffset = lastLine.size();
|
|
ContinueLine(); // Will set the correct color.
|
|
}
|
|
else
|
|
{
|
|
NewLine();
|
|
SetColor();
|
|
}
|
|
lastLine = line;
|
|
firstCall = false;
|
|
|
|
for (size_t i = lineOffset; i < lineLength; ++i)
|
|
{
|
|
char c = line[i];
|
|
switch (c)
|
|
{
|
|
case '\\':
|
|
if (i < lineLength - 1 && line[i + 1] == 'n')
|
|
{
|
|
// Sequence "\\n", treat as "\n".
|
|
NewLine();
|
|
++i;
|
|
continue;
|
|
}
|
|
break;
|
|
case '\n':
|
|
NewLine();
|
|
continue;
|
|
case '\r':
|
|
ClearLine();
|
|
continue;
|
|
case '\t':
|
|
// We'll do it like the graphical console, just add 4 spaces and don't
|
|
// care about TAB stops.
|
|
for (unsigned j = 0; j < 4; ++j)
|
|
{
|
|
Put(' ');
|
|
}
|
|
continue;
|
|
case '$':
|
|
if (i < lineLength - 1)
|
|
{
|
|
++i;
|
|
char colorChar = line[i];
|
|
if (isdigit(colorChar))
|
|
{
|
|
SetColor(colorChar - '0');
|
|
continue;
|
|
}
|
|
if (colorChar == 'o' || colorChar == 'O')
|
|
{
|
|
// Ignore.
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (c < 0x20)
|
|
{
|
|
// Unrecognized control character. Ignore.
|
|
continue;
|
|
}
|
|
Put((unsigned char)c);
|
|
}
|
|
DrawCmd(true);
|
|
// Flush();
|
|
|
|
Unlock();
|
|
}
|
|
|
|
bool CUNIXConsole::OnError(const char* errorString)
|
|
{
|
|
if (!m_Initialized)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CUNIXConsole::OnInitProgress(const char* sProgressMsg)
|
|
{
|
|
if (!m_Initialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Lock();
|
|
m_ProgressStatus = sProgressMsg;
|
|
DrawStatus();
|
|
DrawCmd(true);
|
|
Flush();
|
|
Unlock();
|
|
}
|
|
|
|
void CUNIXConsole::OnInit(ISystem* pSystem)
|
|
{
|
|
if (m_RequireDedicatedServer && !gEnv->IsDedicated())
|
|
{
|
|
return;
|
|
}
|
|
|
|
Lock();
|
|
|
|
if (!m_Initialized)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
assert(m_pSystem == NULL);
|
|
m_pSystem = pSystem;
|
|
assert(m_pConsole == NULL);
|
|
m_pConsole = pSystem->GetIConsole();
|
|
|
|
// Add the output print sink to the system console.
|
|
if (m_pConsole != 0)
|
|
{
|
|
m_pConsole->AddOutputPrintSink(this);
|
|
}
|
|
|
|
// Start the input thread.
|
|
m_InputThread = new CUNIXConsoleInputThread(*this);
|
|
m_InputThread->Start();
|
|
|
|
Unlock();
|
|
|
|
#if defined(NCURSES)
|
|
// Setup the signal handler.
|
|
struct sigaction action;
|
|
memset(&action, 0, sizeof action);
|
|
action.sa_handler = CUNIXConsoleSignalHandler::Handler;
|
|
sigfillset(&action.sa_mask);
|
|
CUNIXConsoleSignalHandler::m_pUNIXConsole = this;
|
|
sigaction(SIGWINCH, &action, NULL);
|
|
sigset_t mask;
|
|
memset(&mask, 0, sizeof mask);
|
|
sigemptyset(&mask);
|
|
sigaddset(&mask, SIGWINCH);
|
|
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
|
#endif
|
|
}
|
|
|
|
void CUNIXConsole::OnShutdown()
|
|
{
|
|
if (!m_Initialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Lock();
|
|
assert(!m_OnShutdownCalled);
|
|
m_pConsole->RemoveOutputPrintSink(this);
|
|
m_OnShutdownCalled = true;
|
|
Unlock();
|
|
|
|
Cleanup();
|
|
}
|
|
|
|
void CUNIXConsole::OnUpdate()
|
|
{
|
|
FUNCTION_PROFILER(gEnv->pSystem, PROFILE_SYSTEM);
|
|
|
|
IS_SHOW_CONSOLE
|
|
|
|
if (!m_Initialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool updateStatus = false;
|
|
static CTimeValue lastStatusUpdate = 0.f;
|
|
|
|
if (m_OnShutdownCalled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Lock();
|
|
|
|
if (!m_OnUpdateCalled)
|
|
{
|
|
m_OnUpdateCalled = true;
|
|
assert(m_svMap == NULL);
|
|
assert(m_svGameRules == NULL);
|
|
m_svMap = m_pConsole->GetCVar("sv_map");
|
|
m_svGameRules = m_pConsole->GetCVar("sv_gamerules");
|
|
assert(m_pTimer == NULL);
|
|
m_pTimer = m_pSystem->GetITimer();
|
|
}
|
|
|
|
if (!m_ProgressStatus.empty())
|
|
{
|
|
m_ProgressStatus.clear();
|
|
updateStatus = true;
|
|
}
|
|
CTimeValue now = m_pTimer->GetAsyncTime();
|
|
if ((now - lastStatusUpdate).GetSeconds() > 0.1f)
|
|
{
|
|
updateStatus = true;
|
|
}
|
|
m_LastUpdateTime = now;
|
|
|
|
if (updateStatus)
|
|
{
|
|
DrawStatus();
|
|
DrawCmd(true);
|
|
Flush();
|
|
lastStatusUpdate = now;
|
|
}
|
|
|
|
while (!m_CommandQueue.empty())
|
|
{
|
|
const string& command = m_CommandQueue[0];
|
|
Unlock();
|
|
if (m_pConsole)
|
|
{
|
|
m_pConsole->ExecuteString(command.c_str());
|
|
|
|
// doing the check again in case m_pConsole was nulled while executing the command
|
|
if (m_pConsole)
|
|
{
|
|
m_pConsole->AddCommandToHistory(command.c_str());
|
|
}
|
|
}
|
|
Lock();
|
|
m_CommandQueue.pop_front();
|
|
}
|
|
|
|
m_pSystem->GetUpdateStats(m_updStats);
|
|
|
|
bool fsMode = !m_drawCmds.empty();
|
|
if (fsMode || fsMode != m_fsMode)
|
|
{
|
|
m_fsMode = fsMode;
|
|
Repaint();
|
|
}
|
|
|
|
Unlock();
|
|
}
|
|
|
|
void CUNIXConsole::GetMemoryUsage(ICrySizer* pSizer)
|
|
{
|
|
size_t size = sizeof *this;
|
|
|
|
Lock();
|
|
|
|
// We're using string (for various reasons), so our best guess of the
|
|
// size is the .size().
|
|
size += m_HeaderString.size();
|
|
size += m_LineBuffer.size() * sizeof(string);
|
|
for (TLineBuffer::const_iterator it = m_LineBuffer.begin(),
|
|
itEnd = m_LineBuffer.end();
|
|
it != itEnd;
|
|
++it)
|
|
{
|
|
size += it->size();
|
|
}
|
|
size += m_CommandQueue.size() * sizeof(string);
|
|
for (TCommandQueue::const_iterator it = m_CommandQueue.begin(),
|
|
itEnd = m_CommandQueue.end();
|
|
it != itEnd;
|
|
++it)
|
|
{
|
|
size += it->size();
|
|
}
|
|
size += m_CommandHistory.size() * sizeof(string);
|
|
for (TCommandHistory::const_iterator it = m_CommandHistory.begin(),
|
|
itEnd = m_CommandHistory.end();
|
|
it != itEnd;
|
|
++it)
|
|
{
|
|
size += it->size();
|
|
}
|
|
if (m_InputThread != NULL)
|
|
{
|
|
size += sizeof *m_InputThread;
|
|
}
|
|
size += m_InputLine.size();
|
|
size += m_SavedInputLine.size();
|
|
size += m_ProgressStatus.size();
|
|
|
|
Unlock();
|
|
|
|
pSizer->AddObject(this, size);
|
|
}
|
|
|
|
Vec2_tpl<int> CUNIXConsole::BeginDraw()
|
|
{
|
|
m_newCmds.resize(0);
|
|
return Vec2_tpl<int>(80, 25 - 3);
|
|
}
|
|
|
|
void CUNIXConsole::PutText(int x, int y, const char* msg)
|
|
{
|
|
IS_SHOW_CONSOLE
|
|
|
|
SConDrawCmd cmd;
|
|
cmd.op = eCDO_PutText;
|
|
cmd.x = x;
|
|
cmd.y = y;
|
|
cry_strcpy(cmd.text, msg);
|
|
m_newCmds.push_back(cmd);
|
|
}
|
|
|
|
void CUNIXConsole::EndDraw()
|
|
{
|
|
IS_SHOW_CONSOLE
|
|
|
|
Lock();
|
|
m_drawCmds.swap(m_newCmds);
|
|
Unlock();
|
|
}
|
|
|
|
void CUNIXConsoleInputThread::Run()
|
|
{
|
|
#if !defined(WIN32)
|
|
fd_set rdfds;
|
|
#endif
|
|
bool interrupted = false;
|
|
|
|
// The input thread selects stdin (0) and the interrupt pipe. The select()
|
|
// call has a timeout (typically 0.5 sec) and will call the InputIdle()
|
|
// method whenever the timer expires.
|
|
while (true)
|
|
{
|
|
interrupted = false;
|
|
#if !defined(WIN32)
|
|
FD_ZERO(&rdfds);
|
|
FD_SET(m_IntrPipe[0], &rdfds);
|
|
FD_SET(0, &rdfds);
|
|
timeval tv;
|
|
memset(&tv, 0, sizeof tv);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 100000; // 0.1 sec.
|
|
if (select(m_IntrPipe[0] + 1, &rdfds, NULL, NULL, &tv) != -1)
|
|
{
|
|
if (FD_ISSET(m_IntrPipe[0], &rdfds))
|
|
{
|
|
char buf;
|
|
read(m_IntrPipe[0], &buf, 1);
|
|
interrupted = true;
|
|
}
|
|
else if (!FD_ISSET(0, &rdfds))
|
|
{
|
|
// Both m_IntrPipe[0] and 0 are not ready, so this must be a timeout.
|
|
m_UNIXConsole.InputIdle();
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Got interrupted by a signal.
|
|
assert(errno == EINTR);
|
|
interrupted = true;
|
|
}
|
|
#else
|
|
HANDLE handles[2];
|
|
handles[0] = m_IntrEvent;
|
|
handles[1] = GetStdHandle(STD_INPUT_HANDLE);
|
|
DWORD result = WaitForMultipleObjects(2, handles, false, 10 /* 0.1 sec */);
|
|
switch (result)
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
interrupted = true;
|
|
ResetEvent(m_IntrEvent);
|
|
break;
|
|
case WAIT_OBJECT_0 + 1:
|
|
break;
|
|
case WAIT_TIMEOUT:
|
|
m_UNIXConsole.InputIdle();
|
|
continue;
|
|
case WAIT_FAILED:
|
|
assert(!"WaitForMultipleObjects() failed");
|
|
}
|
|
#endif
|
|
if (interrupted)
|
|
{
|
|
if (m_Cancelled)
|
|
{
|
|
break;
|
|
}
|
|
m_UNIXConsole.Lock();
|
|
m_UNIXConsole.CheckResize();
|
|
m_UNIXConsole.Unlock();
|
|
continue;
|
|
}
|
|
int c = getch();
|
|
m_UNIXConsole.Lock();
|
|
|
|
// Handle prompt responses.
|
|
if (!m_UNIXConsole.m_Prompt.empty())
|
|
{
|
|
bool acceptAll = false;
|
|
char response = 0;
|
|
if (strchr(m_UNIXConsole.m_PromptResponseChars, '@'))
|
|
{
|
|
acceptAll = true;
|
|
}
|
|
if (c == KEY_ENTER || c == '\r')
|
|
{
|
|
c = '\n';
|
|
}
|
|
if (c == KEY_BACKSPACE || c == 0x7f)
|
|
{
|
|
c = '\010';
|
|
}
|
|
if (c <= 0xff && strchr(m_UNIXConsole.m_PromptResponseChars, (char)c))
|
|
{
|
|
response = (char)c;
|
|
}
|
|
else if (acceptAll && (isprint(c) || c == '\n'))
|
|
{
|
|
response = (char)c;
|
|
}
|
|
else
|
|
{
|
|
beep();
|
|
m_UNIXConsole.Unlock();
|
|
continue;
|
|
}
|
|
m_UNIXConsole.m_PromptResponse = response;
|
|
m_UNIXConsole.m_PromptCond.Notify();
|
|
m_UNIXConsole.Unlock();
|
|
continue;
|
|
}
|
|
|
|
// if console is hided then pass only F10 key
|
|
if (!m_UNIXConsole.m_bShowConsole)
|
|
{
|
|
if (KEY_F(10) == c)
|
|
{
|
|
m_UNIXConsole.KeyF(10);
|
|
}
|
|
m_UNIXConsole.Unlock();
|
|
continue;
|
|
}
|
|
|
|
switch (c)
|
|
{
|
|
case ERR:
|
|
break;
|
|
case KEY_RESIZE:
|
|
// Window size changed. This key is received only if the ncurses
|
|
// library is configured to handle SIGWINCH and if no other SIGWINCH
|
|
// handler has been installed.
|
|
m_UNIXConsole.SetSize(COLS, LINES);
|
|
m_UNIXConsole.Repaint();
|
|
break;
|
|
case KEY_ENTER:
|
|
case PADENTER:
|
|
case '\r':
|
|
case '\n':
|
|
m_UNIXConsole.KeyEnter();
|
|
break;
|
|
case KEY_UP:
|
|
case '\020': // CTRL-P
|
|
m_UNIXConsole.KeyUp();
|
|
break;
|
|
case KEY_DOWN:
|
|
case '\016': // CTRL-N
|
|
m_UNIXConsole.KeyDown();
|
|
break;
|
|
case KEY_LEFT:
|
|
m_UNIXConsole.KeyLeft();
|
|
break;
|
|
case KEY_RIGHT:
|
|
m_UNIXConsole.KeyRight();
|
|
break;
|
|
case KEY_HOME:
|
|
case '\001': // CTRL-A
|
|
m_UNIXConsole.KeyHome(false);
|
|
break;
|
|
case CTL_HOME:
|
|
m_UNIXConsole.KeyHome(true);
|
|
break;
|
|
case KEY_END:
|
|
case '\005': // CTRL-E
|
|
m_UNIXConsole.KeyEnd(false);
|
|
break;
|
|
case CTL_END:
|
|
m_UNIXConsole.KeyEnd(true);
|
|
break;
|
|
case KEY_BACKSPACE:
|
|
#if defined(MAC)
|
|
// Mac OS X returns delete key instead of backspace
|
|
case 0x7f:
|
|
#else
|
|
case '\010': // CTRL-H
|
|
#endif
|
|
m_UNIXConsole.KeyBackspace();
|
|
break;
|
|
case KEY_DC:
|
|
case KEY_SDC:
|
|
case '\004': // CTRL-D
|
|
m_UNIXConsole.KeyDelete();
|
|
break;
|
|
case '\027': // CTRL-W
|
|
m_UNIXConsole.KeyDeleteWord();
|
|
break;
|
|
case '\013': // CTRL-K
|
|
m_UNIXConsole.KeyKill();
|
|
break;
|
|
case '\014': // CTRL-L
|
|
m_UNIXConsole.KeyRepaint();
|
|
break;
|
|
case '\t': // TAB
|
|
m_UNIXConsole.KeyTab();
|
|
break;
|
|
case KEY_NPAGE:
|
|
case '\006': // CTRL-F
|
|
m_UNIXConsole.KeyPgDown(false);
|
|
break;
|
|
case CTL_PGDN:
|
|
m_UNIXConsole.KeyPgDown(true);
|
|
break;
|
|
case KEY_PPAGE:
|
|
case '\002': // CTRL-B
|
|
m_UNIXConsole.KeyPgUp(false);
|
|
break;
|
|
case CTL_PGUP:
|
|
m_UNIXConsole.KeyPgUp(true);
|
|
break;
|
|
case KEY_F(10):
|
|
m_UNIXConsole.KeyF(10);
|
|
break;
|
|
case KEY_F(11):
|
|
m_UNIXConsole.KeyF(11);
|
|
break;
|
|
default:
|
|
if (c >= 0x20 && c <= 0xff)
|
|
{
|
|
m_UNIXConsole.Key(c);
|
|
}
|
|
break;
|
|
}
|
|
m_UNIXConsole.Unlock();
|
|
}
|
|
}
|
|
|
|
void CUNIXConsole::KeyF(int id)
|
|
{
|
|
#ifdef LINUX
|
|
if (11 == id)
|
|
{
|
|
def_prog_mode();
|
|
endwin();
|
|
m_bShowConsole = false;
|
|
system("/bin/bash");
|
|
reset_prog_mode();
|
|
refresh();
|
|
m_bShowConsole = true;
|
|
}
|
|
else if (10 == id)
|
|
{
|
|
if (m_bShowConsole)
|
|
{
|
|
def_prog_mode();
|
|
endwin();
|
|
m_bShowConsole = false;
|
|
}
|
|
else
|
|
{
|
|
reset_prog_mode();
|
|
refresh();
|
|
m_bShowConsole = true;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
CUNIXConsole* CUNIXConsoleSignalHandler::m_pUNIXConsole = NULL;
|
|
|
|
void CUNIXConsoleSignalHandler::Handler(int signum)
|
|
{
|
|
#if defined(NCURSES)
|
|
switch (signum)
|
|
{
|
|
case SIGWINCH:
|
|
m_pUNIXConsole->m_WindowResized = true;
|
|
m_pUNIXConsole->m_InputThread->Interrupt();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endif // USE_UNIXCONSOLE
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// simple light-weight console implementation
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
#if defined(AZ_RESTRICTED_PLATFORM)
|
|
#include AZ_RESTRICTED_FILE(UnixConsole_cpp)
|
|
#endif
|
|
#if defined(AZ_RESTRICTED_SECTION_IMPLEMENTED)
|
|
#undef AZ_RESTRICTED_SECTION_IMPLEMENTED
|
|
#else
|
|
CNULLConsole::CNULLConsole(bool isDaemonMode)
|
|
: m_isDaemon(isDaemonMode)
|
|
{
|
|
}
|
|
|
|
void CNULLConsole::Print(const char* inszText)
|
|
{
|
|
if (m_isDaemon)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if defined(WIN32) || defined(WIN64)
|
|
DWORD written;
|
|
char buf[1024];
|
|
sprintf_s(buf, "%s\n", inszText);
|
|
WriteConsole(m_hOut, buf, strlen(buf), &written, NULL);
|
|
#elif defined(LINUX) || defined(MAC)
|
|
printf("%s\n", inszText);
|
|
#endif
|
|
}
|
|
|
|
void CNULLConsole::OnInit(ISystem* pSystem)
|
|
{
|
|
m_syslogStats.Init();
|
|
|
|
if (m_isDaemon)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IConsole* pConsole = pSystem->GetIConsole();
|
|
pConsole->AddOutputPrintSink(this);
|
|
|
|
#if defined(WIN32) || defined(WIN64)
|
|
AllocConsole();
|
|
m_hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
#endif
|
|
}
|
|
|
|
void CNULLConsole::OnUpdate()
|
|
{
|
|
}
|
|
|
|
void CNULLConsole::PutText([[maybe_unused]] int x, [[maybe_unused]] int y, [[maybe_unused]] const char* msg)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Logging server internal statistics into syslog service
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
CSyslogStats::CSyslogStats()
|
|
: m_syslog_stats(0)
|
|
, m_syslog_period(SYSLOG_DEFAULT_PERIOD)
|
|
{
|
|
}
|
|
|
|
CSyslogStats::~CSyslogStats()
|
|
{
|
|
#if (defined(LINUX) && !defined(ANDROID)) || defined(MAC)
|
|
#if defined(NCURSES)
|
|
closelog();
|
|
#endif
|
|
if (gEnv->pConsole)
|
|
{
|
|
gEnv->pConsole->UnregisterVariable("syslog_stats");
|
|
gEnv->pConsole->UnregisterVariable("syslog_period");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CSyslogStats::Init()
|
|
{
|
|
#if (defined(LINUX) && !defined(ANDROID)) || defined(MAC)
|
|
#if defined(NCURSES)
|
|
# if defined(LINUX)
|
|
openlog("LinuxLauncher", LOG_PID, LOG_USER);
|
|
# elif defined(MAC)
|
|
openlog("MacLauncher", LOG_PID, LOG_USER);
|
|
# endif
|
|
#endif // NCURSES
|
|
|
|
if (gEnv->pConsole)
|
|
{
|
|
REGISTER_CVAR2("syslog_stats", &m_syslog_stats, 0, 0, "Start/Stop logging server info into syslog");
|
|
REGISTER_CVAR2("syslog_period", &m_syslog_period, SYSLOG_DEFAULT_PERIOD, 0, "Syslog logging timeout period");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CSyslogStats::Update([[maybe_unused]] float srvRate, [[maybe_unused]] int numPlayers)
|
|
{
|
|
}
|
|
|
|
#endif // defined(USE_DEDICATED_SERVER_CONSOLE)
|
|
|
|
// vim:ts=2
|
|
|