plugins/consoles/vt100cons/src/vt100/vtc_base.cpp
changeset 0 7f656887cf89
child 27 17e35ffe449b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/consoles/vt100cons/src/vt100/vtc_base.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,413 @@
+// vtc_base.cpp
+// 
+// Copyright (c) 2009 - 2010 Accenture. All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+// 
+// Initial Contributors:
+// Accenture - Initial contribution
+//
+
+#include "vtc_base.h"
+#include <fshell/common.mmh>
+
+_LIT(KIniFileName, "\\system\\console\\vt100.ini");
+_LIT(KIniDesciptionFile, "\\resource\\vt100.idf");
+_LIT(KAttConsoleSizeDetect, "console_size_detect");
+
+_LIT(KNewLine, "\r\n");
+
+class TOverflowTruncate : public TDes16Overflow
+	{
+public:
+	virtual void Overflow(TDes16&) {}
+	};
+	
+EXPORT_C CVtcConsoleBase::CVtcConsoleBase()
+	{
+	//SetDebug(ETrue); // debug
+	}
+
+EXPORT_C CVtcConsoleBase::~CVtcConsoleBase()
+	{
+	delete iIniFile;
+	delete iInputController;
+	delete iOutputController;
+	delete iUnderlyingConsole; // In case of leave during construction, this might still be non-null
+	}
+
+EXPORT_C TInt CVtcConsoleBase::Create(const TDesC& aTitle, TSize /*aSize*/)
+	{
+	TRAPD(err, ConstructL(aTitle));
+	if (err)
+		{
+		TBuf<512> message;
+		message.Format(_L("Failed to create console (%d)."), err);
+			
+		if (iUnderlyingConsole && (LazyConsole::IsConstructed(iUnderlyingConsole) || !LazyConsole::IsLazy(iUnderlyingConsole)))
+		// if we have an underlyconsole, which is either not lazy or is lazy but already constructed, then print the error to it.
+			{
+			iUnderlyingConsole->Write(message);
+			iUnderlyingConsole->Write(KNewLine);
+			}
+		else
+		// else display a dialog
+			{
+			RNotifier notifier;
+			if (notifier.Connect() == KErrNone)
+				{
+				TInt buttonVal;
+				TRequestStatus notifierStatus;
+				notifier.Notify(_L("vt100"), message, _L("OK"), KNullDesC, buttonVal, notifierStatus);
+				User::WaitForRequest(notifierStatus);
+				notifier.Close();
+				}
+			}
+		}
+	Message(EDebug, _L("VT100 console create completed with err=%d"), err);
+	return err;
+	}
+
+EXPORT_C void CVtcConsoleBase::ConstructL(const TDesC&)
+	{
+	iIniFile = LtkUtils::CIniFile::NewL(KIniFileName, KIniDesciptionFile);
+
+	TSize screenSize(80, 24); // If sizeDetect is not specified, we default to (and only support) 80x24
+	if (iIniFile->GetBool(KAttConsoleSizeDetect))
+		{
+		DetectScreenSizeL(screenSize);
+		}
+	iOutputController = CVtConsoleOutputController::NewL(*this, *iIniFile, screenSize);
+	iInputController = CVtConsoleInputController::NewL(*this, *iIniFile);
+	ClearScreen();
+	delete iUnderlyingConsole;
+	iUnderlyingConsole = NULL;
+	}
+	
+EXPORT_C TInt CVtcConsoleBase::Extension_(TUint aExtensionId, TAny*& a0, TAny* a1)
+	{
+	if (aExtensionId == ConsoleMode::KSetConsoleModeExtension)
+		{
+		ConsoleMode::TMode mode = (ConsoleMode::TMode)(TInt)a1;
+		iInputController->SetMode(mode);
+		iOutputController->SetMode(mode);
+		return KErrNone;
+		}
+	else if (aExtensionId == UnderlyingConsole::KSetUnderlyingConsoleExtension)
+		{
+		iUnderlyingConsole = (CConsoleBase*)a1;
+		return KErrNone;
+		}
+	else if (aExtensionId == ConsoleAttributes::KSetConsoleAttributesExtension)
+		{
+		ConsoleAttributes::TAttributes* attributes = (ConsoleAttributes::TAttributes*)a1;
+		return iOutputController->SetAttributes(attributes->iAttributes, attributes->iForegroundColor, attributes->iBackgroundColor);
+		}
+	else
+		{
+		return CConsoleBase::Extension_(aExtensionId, a0, a1);
+		}
+	}
+
+EXPORT_C void CVtcConsoleBase::Message(TVerbosity aVerbosity, TRefByValue<const TDesC> aFmt, ...)
+	{
+	if (Debug() || (aVerbosity == EInformation) || (aVerbosity == EError))
+		{
+		TOverflowTruncate overflow;
+		VA_LIST list;
+		VA_START(list, aFmt);
+		TBuf<0x100> buf;
+		buf.AppendFormatList(aFmt, list, &overflow);
+		
+		if (iUnderlyingConsole)
+			{
+			iUnderlyingConsole->Write(buf);
+			iUnderlyingConsole->Write(KNewLine);
+			}
+		else
+			{
+			User::InfoPrint(buf);
+			}
+		}
+	}
+	
+EXPORT_C TBool CVtcConsoleBase::Debug()
+	{
+	return iDebug;
+	}
+	
+EXPORT_C void CVtcConsoleBase::SetDebug(TBool aDebug)
+	{
+	iDebug = aDebug;
+	}
+		
+EXPORT_C void CVtcConsoleBase::Read(TRequestStatus& aStatus)
+	{
+	iInputController->GetKeyPress(iKeyPress, aStatus);
+	}
+
+EXPORT_C void CVtcConsoleBase::ReadCancel()
+	{
+	iInputController->CancelGetKeyPress();
+	}
+
+EXPORT_C void CVtcConsoleBase::Write(const TDesC& aDes)
+	{
+	iOutputController->Write(aDes);
+	}
+	
+EXPORT_C TPoint CVtcConsoleBase::CursorPos() const
+	{
+	TPoint pos;
+	iOutputController->GetCursorPos(pos);
+	return pos;
+	}
+
+EXPORT_C void CVtcConsoleBase::SetCursorPosAbs(const TPoint& aPoint)
+	{
+	iOutputController->SetCursorPosAbs(aPoint);
+	}
+
+EXPORT_C void CVtcConsoleBase::SetCursorPosRel(const TPoint& aPoint)
+	{
+	iOutputController->SetCursorPosRel(aPoint);
+	}
+
+EXPORT_C void CVtcConsoleBase::SetCursorHeight(TInt aPercentage)
+	{
+	iOutputController->SetCursorHeight(aPercentage);
+	}
+
+EXPORT_C void CVtcConsoleBase::SetTitle(const TDesC& aDes)
+	{
+	iOutputController->SetTitle(aDes);
+	}
+
+EXPORT_C void CVtcConsoleBase::ClearScreen()
+	{
+	iOutputController->ClearScreen();
+	}
+
+EXPORT_C void CVtcConsoleBase::ClearToEndOfLine()
+	{
+	iOutputController->ClearToEndOfLine();
+	}
+
+EXPORT_C TSize CVtcConsoleBase::ScreenSize() const
+	{
+	TSize size;
+	iOutputController->GetScreenSize(size);
+	return size;
+	}
+
+EXPORT_C TKeyCode CVtcConsoleBase::KeyCode() const
+	{
+	return iKeyPress.iCode;
+	}
+
+EXPORT_C TUint CVtcConsoleBase::KeyModifiers() const
+	{
+	return iKeyPress.iModifiers;
+	}
+
+void CVtcConsoleBase::DetectScreenSizeL(TSize& aSize)
+	{
+	TInt err;
+	do
+		{
+		// If we get a KErrCorrupt error during the detect, we restart it from the top
+		// This is because KErrCorrupt is (hopefully) only ever returned as a result of the DSR in ReadCursorPos failing, in which case it is worth retrying.
+		// If it's any other error, the console is actually dead so we should bail
+		err = DoDetectScreenSize(aSize);
+		Message(EDebug, _L("DoDetectScreenSize returned %d"), err);
+		}
+#ifdef FSHELL_PLATFORM_OPP // Temporary OPP specific change - the drivers for the mid-sized prototype currently complete requests with KErrAbort just before power management sends the device to sleep.
+	while ((err == KErrCorrupt) || (err == KErrAbort));
+#else
+	while (err == KErrCorrupt);
+#endif
+	User::LeaveIfError(err);
+	}
+
+TInt CVtcConsoleBase::DoDetectScreenSize(TSize& aSize)
+	{
+	Message(EDebug, _L("Beginning VT100 console size detect"));
+	_LIT8(KSpace, " ");
+	_LIT8(KNewLine, "\r\n");
+	_LIT8(KResetCursorPosAbs, "\x1b[1;1H");
+	TInt err = Output(KResetCursorPosAbs);
+	if (err) return err;
+
+#ifdef FSHELL_VT100_WORK_AROUND_TERATERM_CURSOR_BUG
+	// Note, at the point where the cursor has just wrap to the next line, TeraTerm reports
+	// the same cursor position has just before is wrapped. When the cursor is moved to the
+	// right again, TeraTerm corrects its reckoning of the cursor position. The following
+	// code works around this bug by keeping track of the previous cursor position and noticing
+	// if it doesn't increment. This is treated as though the cursor had wrapped.
+
+	TInt previousCursorPosX = -1;
+	for (TInt x = 0; ; ++x)
+		{
+		err = Output(KSpace);
+		if (err) return err;
+		TPoint pos;
+		TInt err = ReadCursorPos(pos);
+		if (err) return err;
+		TInt cursorPosX = pos.iX;
+		if ((cursorPosX == 0) || (cursorPosX == previousCursorPosX))
+			{
+			aSize.iWidth = x + 1;
+			break;
+			}
+		previousCursorPosX = cursorPosX;
+		}
+#else
+	for (TInt x = 0; ; ++x)
+		{
+		err = Output(KSpace);
+		if (err) return err;
+		TPoint pos;
+		err = ReadCursorPos(pos);
+		if (err) return err;
+		if (pos.iX == 0)
+			{
+			aSize.iWidth = x + 1;
+			break;
+			}
+		}
+#endif
+	err = Output(KResetCursorPosAbs);
+	if (err) return err;
+	TInt prevYPos = 0;
+	for (TInt y = 0; ; ++y)
+		{
+		err = Output(KNewLine);
+		if (err) return err;
+		TPoint pos;
+		TInt err = ReadCursorPos(pos);
+		if (err) return err;
+		if (pos.iY == prevYPos)
+			{
+			aSize.iHeight = y;
+			break;
+			}
+		else
+			{
+			prevYPos = y;
+			}
+		}
+	err = Output(KResetCursorPosAbs);
+	Message(EDebug, _L("Completed VT100 console size detect = %dx%d"), aSize.iWidth, aSize.iHeight);
+	return err;
+	}
+
+TInt CVtcConsoleBase::ReadCursorPos(TPoint& aPosition)
+	{
+	// Ideally this functionality should be moved into the input and output controllers, but currently
+	// this is problematic because:
+	//
+	// 1) TeraTerm contains bugs in the way it reports cursor position that need to be worked around.
+	//    See FSHELL_VT100_WORK_AROUND_TERATERM_CURSOR_BUG.
+	//
+	// 2) The CVtConsoleInputController state machine would need some fairly heavy changes to be able
+	//    to cope with ANSI Device Status Report escape sequences.
+	//
+	// 3) The fact that CVtConsoleInputController and CVtConsoleOutputController run in the same thread
+	//    means that implementing CVtcConsoleBase::CursorPos (which is synchronous) would be tricky.
+	//    Possible solutions are a) using CActiveSchedulerWait (which is almost always a bad idea) and
+	//    b) putting CVtConsoleInputController in a separate thread (which would be a fair bit of work,
+	//       and we've been there before...).
+	//
+	// For the time being then, vt100.dll has its own console size detection code (a duplication of
+	// the version in iosrv) and uses this function only during construction to initialize the size of
+	// the output controller's cursor tracker. From that point on, the cursor position is always retrieved
+	// via the cursor tracker (the horrible thing that it is).
+
+	// Note, this function can only safely be used before the input and output controllers are created
+	// because in uses the MConsoleInput and MConsoleOutput interfaces directly.
+	ASSERT((iOutputController == NULL) && (iInputController == NULL));
+
+	_LIT8(KDeviceStatusReport, "\x1b[6n");
+	//Message(EDebug, _L("Sending VT100 DSR"));
+	TInt err = Output(KDeviceStatusReport);
+	if (err < 0)
+		{
+		Message(EDebug, _L("Error %d sending DSR"), err);
+		return err;
+		}
+	
+	TPoint pos;
+	TBuf8<32> buf;
+	TPtr8 ptr(const_cast<TUint8*>(buf.Ptr()), 0, buf.MaxLength());
+	FOREVER
+		{
+		TRequestStatus status;
+		//Message(EDebug, _L("Waiting for input"));
+		Input(ptr, status);
+		User::WaitForRequest(status);
+		err = status.Int();
+		if (err < 0)
+			{
+			Message(EDebug, _L("Error getting input %d"), err);
+			return err;
+			}
+
+		buf.SetLength(buf.Length() + ptr.Length());
+		ptr.Set(const_cast<TUint8*>(buf.Ptr()) + buf.Length(), 0, buf.MaxLength() - buf.Length());
+		if (ptr.MaxLength() == 0) return KErrOverflow; // Not sure how this could happen, maybe if the terminal returned really really long but otherwise valid numbers for the x and y pos
+
+		// The sequence we're looking for is \x1b[nnn;nnnR
+		TLex8 lex(buf);
+		if (lex.Eos()) continue;
+		if (lex.Get() != 0x1b) return KErrCorrupt;
+		if (lex.Eos()) continue;
+		if (lex.Get() != '[') return KErrCorrupt;
+		if (lex.Eos()) continue;
+		err = lex.Val(pos.iY);
+		if (err) return err;
+		if (lex.Eos()) continue;
+		if (lex.Get() != ';') return KErrCorrupt;
+		if (lex.Eos()) continue;
+		err = lex.Val(pos.iX);
+		if (err) return err;
+		if (lex.Eos()) continue;
+		if (lex.Get() != 'R') return KErrCorrupt;
+		// If we reach here, we've successfully read the whole sequence
+
+		// I assume we subtract one here because the VT100 indexes are 1-based and we use zero-based? -TomS
+		pos.iX--;
+		pos.iY--;
+		aPosition = pos;
+		return KErrNone;
+		}
+	}
+
+EXPORT_C TInt CVtcConsoleBase::ReadKeywordValuePair(TLex& aLex, TPtrC& aKeyword, TPtrC& aValue)
+	{
+	TLexMark mark;
+	aLex.SkipSpaceAndMark(mark);
+	while (!aLex.Eos() && !aLex.Peek().IsSpace() && (aLex.Peek() != '='))
+		{
+		aLex.Get();
+		}
+	aKeyword.Set(aLex.MarkedToken(mark));
+	aLex.SkipSpace();
+	if (aLex.Get() != '=')
+		{
+		return KErrArgument;
+		}
+	aLex.SkipSpaceAndMark(mark);
+	while (!aLex.Eos() && (aLex.Peek() != ','))
+		{
+		aLex.Get();
+		}
+	aValue.Set(aLex.MarkedToken(mark));
+	if (aLex.Peek() == ',')
+		{
+		aLex.Get();
+		}
+	return KErrNone;
+	}
+