--- /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;
+ }
+