diff -r 000000000000 -r 7f656887cf89 plugins/consoles/vt100cons/src/vt100/vtc_base.cpp --- /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 + +_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 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(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(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; + } +