diff -r 000000000000 -r 7f656887cf89 plugins/consoles/vt100cons/src/vt100/vtc_controller.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/consoles/vt100cons/src/vt100/vtc_controller.cpp Wed Jun 23 15:52:26 2010 +0100 @@ -0,0 +1,883 @@ +// vtc_controller.cpp +// +// Copyright (c) 2008 - 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 +#include +#include "vtc_controller.h" +#include "vtc_cursor_tracker.h" + + +// +// Constants. +// + +_LIT(KAttSupportAttributes, "support_attributes"); +_LIT(KAttForegroundColor, "foreground_color"); +_LIT(KAttBackgroundColor, "background_color"); + +class TEscapeMapping + { +public: + TUint8 iEscapeChar; + TKeyCode iKeyCode; + }; + +class TLongerEscapeMapping + { +public: + TUint8 iEscapeChar1; + TUint8 iEscapeChar2; + TKeyCode iKeyCode; + }; + + +const TEscapeMapping KEscapeMappings[] = + { + { 'A', EKeyUpArrow }, + { 'B', EKeyDownArrow }, + { 'C', EKeyRightArrow }, + { 'D', EKeyLeftArrow }, + { 'H', EKeyHome }, + { 'K', EKeyEnd }, + }; +const TInt KNumEscapeMappings(sizeof(KEscapeMappings) / sizeof(TEscapeMapping)); + +const TEscapeMapping KFunctionKeyMappings[] = + { + { 'P', EKeyF1 }, + { 'Q', EKeyF2 }, + { 'R', EKeyF3 }, + { 'S', EKeyF4 }, + }; +const TInt KNumEscapeFunctionMappings(sizeof(KFunctionKeyMappings) / sizeof(TEscapeMapping)); + +// The following are for VT220 support, which is needed to get function keys understood from teraterm. They use ESC [xx~ where xx is given by the below table +// See http://aperiodic.net/phil/archives/Geekery/term-function-keys.html for the reference I used +const TLongerEscapeMapping KExtendedEscapeMappings[] = + { + { '1', '1', EKeyF1 }, + { '1', '2', EKeyF2 }, + { '1', '3', EKeyF3 }, + { '1', '4', EKeyF4 }, + { '1', '5', EKeyF5 }, + // '1' '6' isn't used + { '1', '7', EKeyF6 }, + { '1', '8', EKeyF7 }, + { '1', '9', EKeyF8 }, + { '2', '0', EKeyF9 }, + { '2', '1', EKeyF10 }, + // '2' '2' isn't used + { '2', '3', EKeyF11 }, + { '2', '4', EKeyF12 }, + + // ESC [1~ is Home key according to http://www.zaik.uni-koeln.de/~ftp/elfi/etc/telnet.key + { '1', 0, EKeyHome }, + { '4', 0, EKeyEnd }, + { '5', 0, EKeyPageUp }, + { '6', 0, EKeyPageDown }, + { '2', 0, EKeyInsert }, + { '3', 0, EKeyDelete }, + }; +const TInt KNumExtendedEscapeMappings = sizeof(KExtendedEscapeMappings) / sizeof(TLongerEscapeMapping); + +static const TUint8 KEscapeChar1 = 0x1b; +static const TUint8 KEscapeChar2 = '['; +static const TUint8 KEscapeChar2Func = 'O'; + +static const TInt KEscapeTimeoutMicros = 200000; // 1/5th second + + +//______________________________________________________________________________ +// TKeyPress +EXPORT_C TKeyPress::TKeyPress() + : iCode(EKeyNull), iModifiers(0) + { + } + +EXPORT_C TKeyPress::TKeyPress(TKeyCode aCode, TUint aModifiers) + : iCode(aCode), iModifiers(aModifiers) + { + } + +//______________________________________________________________________________ +// CVtConsoleOutputController +EXPORT_C CVtConsoleOutputController* CVtConsoleOutputController::NewL(MConsoleOutput& aOutput, LtkUtils::CIniFile& aIniFile, const TSize& aScreenSize) + { + CVtConsoleOutputController* self = new(ELeave)CVtConsoleOutputController(aOutput, aIniFile); + CleanupStack::PushL(self); + User::LeaveIfError(self->Construct(aScreenSize)); + CleanupStack::Pop(self); + return self; + } + +EXPORT_C CVtConsoleOutputController* CVtConsoleOutputController::New(MConsoleOutput& aOutput, LtkUtils::CIniFile& aIniFile, const TSize& aScreenSize) + { + CVtConsoleOutputController* self = new CVtConsoleOutputController(aOutput, aIniFile); + TInt err = self->Construct(aScreenSize); + if (err!=KErrNone) + { + delete self; + self = NULL; + } + return self; + } + +class TAttributeBuf : public TBuf8<64> + { +public: + enum TColorType + { + EForeground, + EBackground + }; +public: + TAttributeBuf(LtkUtils::CIniFile& aIniFile); + void Add(TUint aAttributes); + void Add(ConsoleAttributes::TColor aColor, TColorType aType); + void Finalize(); + TBool SomethingAdded() const; +private: + void Add(const TDesC8& aAttribute); +private: + LtkUtils::CIniFile& iIniFile; + TBool iSomethingAdded; + }; + +_LIT8(KPrefix, "\x1b["); +TAttributeBuf::TAttributeBuf(LtkUtils::CIniFile& aIniFile) + : TBuf8<64>(KPrefix), iIniFile(aIniFile), iSomethingAdded(EFalse) + { + } + +void TAttributeBuf::Add(const TDesC8& aAttribute) + { + if (iSomethingAdded) + { + Append(';'); + } + Append(aAttribute); + iSomethingAdded = ETrue; + } + +void TAttributeBuf::Finalize() + { + ASSERT(iSomethingAdded); + Append('m'); + } + +TBool TAttributeBuf::SomethingAdded() const + { + return iSomethingAdded; + } + +void TAttributeBuf::Add(TUint aAttributes) + { + _LIT8(KAttReset, "0"); + _LIT8(KAttBold, "1"); + _LIT8(KAttUnderscore, "4"); + _LIT8(KAttBlink, "5"); + _LIT8(KAttInverse, "7"); + _LIT8(KAttConceal, "8"); + + if (aAttributes & ConsoleAttributes::ENone) + { + Add(KAttReset); + Add((ConsoleAttributes::TColor)iIniFile.GetInt(KAttForegroundColor), TAttributeBuf::EForeground); + Add((ConsoleAttributes::TColor)iIniFile.GetInt(KAttBackgroundColor), TAttributeBuf::EBackground); + } + if (aAttributes & ConsoleAttributes::EBold) + { + Add(KAttBold); + } + if (aAttributes & ConsoleAttributes::EUnderscore) + { + Add(KAttUnderscore); + } + if (aAttributes & ConsoleAttributes::EBlink) + { + Add(KAttBlink); + } + if (aAttributes & ConsoleAttributes::EInverse) + { + Add(KAttInverse); + } + if (aAttributes & ConsoleAttributes::EConceal) + { + Add(KAttConceal); + } + } + +void TAttributeBuf::Add(ConsoleAttributes::TColor aColor, TColorType aType) + { + _LIT8(KFgBlack, "30"); + _LIT8(KFgRed, "31"); + _LIT8(KFgGreen, "32"); + _LIT8(KFgYellow, "33"); + _LIT8(KFgBlue, "34"); + _LIT8(KFgMagenta, "35"); + _LIT8(KFgCyan, "36"); + _LIT8(KFgWhite, "37"); + _LIT8(KFgReset, "39"); + _LIT8(KBgBlack, "40"); + _LIT8(KBgRed, "41"); + _LIT8(KBgGreen, "42"); + _LIT8(KBgYellow, "43"); + _LIT8(KBgBlue, "44"); + _LIT8(KBgMagenta, "45"); + _LIT8(KBgCyan, "46"); + _LIT8(KBgWhite, "47"); + _LIT8(KBgReset, "49"); + + if (aType == EForeground) + { + switch (aColor) + { + default: + case ConsoleAttributes::EUnchanged: + { + // Do nothing. + break; + } + case ConsoleAttributes::EBlack: + { + Add(KFgBlack); + break; + } + case ConsoleAttributes::ERed: + { + Add(KFgRed); + break; + } + case ConsoleAttributes::EGreen: + { + Add(KFgGreen); + break; + } + case ConsoleAttributes::EYellow: + { + Add(KFgYellow); + break; + } + case ConsoleAttributes::EBlue: + { + Add(KFgBlue); + break; + } + case ConsoleAttributes::EMagenta: + { + Add(KFgMagenta); + break; + } + case ConsoleAttributes::ECyan: + { + Add(KFgCyan); + break; + } + case ConsoleAttributes::EWhite: + { + Add(KFgWhite); + break; + } + case ConsoleAttributes::EReset: + { + Add(KFgReset); + break; + } + } + } + else + { + switch (aColor) + { + default: + case ConsoleAttributes::EUnchanged: + { + // Do nothing. + break; + } + case ConsoleAttributes::EBlack: + { + Add(KBgBlack); + break; + } + case ConsoleAttributes::ERed: + { + Add(KBgRed); + break; + } + case ConsoleAttributes::EGreen: + { + Add(KBgGreen); + break; + } + case ConsoleAttributes::EYellow: + { + Add(KBgYellow); + break; + } + case ConsoleAttributes::EBlue: + { + Add(KBgBlue); + break; + } + case ConsoleAttributes::EMagenta: + { + Add(KBgMagenta); + break; + } + case ConsoleAttributes::ECyan: + { + Add(KBgCyan); + break; + } + case ConsoleAttributes::EWhite: + { + Add(KBgWhite); + break; + } + case ConsoleAttributes::EReset: + { + Add(KBgReset); + break; + } + } + } + } + +EXPORT_C TInt CVtConsoleOutputController::ResetAttributes() + { + if (iIniFile.GetBool(KAttSupportAttributes)) + { + TAttributeBuf buf(iIniFile); + buf.Add(ConsoleAttributes::ENone); + buf.Finalize(); + return iOutput.Output(buf); + } + + return KErrNone; + } + +EXPORT_C TInt CVtConsoleOutputController::SetAttributes(TUint aAttributes, ConsoleAttributes::TColor aForegroundColor, ConsoleAttributes::TColor aBackgroundColor) + { + if (iIniFile.GetBool(KAttSupportAttributes)) + { + TAttributeBuf buf(iIniFile); + buf.Add(aAttributes); + buf.Add(aForegroundColor, TAttributeBuf::EForeground); + buf.Add(aBackgroundColor, TAttributeBuf::EBackground); + + if (buf.SomethingAdded()) + { + buf.Finalize(); + return iOutput.Output(buf); + } + + return KErrNone; + } + + return KErrNotSupported; + } + +CVtConsoleOutputController::CVtConsoleOutputController(MConsoleOutput& aOutput, LtkUtils::CIniFile& aIniFile) + : iOutput(aOutput), iIniFile(aIniFile), iMode(ConsoleMode::EText) + { + } + +TInt CVtConsoleOutputController::Construct(const TSize& aScreenSize) + { + iCursorTracker = new TCursorTracker(aScreenSize); + if (!iCursorTracker) return KErrNoMemory; + return KErrNone; + } + +EXPORT_C CVtConsoleOutputController::~CVtConsoleOutputController() + { + delete iCursorTracker; + iOutputBuf.Close(); + } + +TInt CVtConsoleOutputController::GetCursorPos(TPoint& aPos) const + { + aPos = iCursorTracker->CursorPos(); + return KErrNone; + } + + +TInt CVtConsoleOutputController::SetCursorPosAbs(const TPoint& aPos) + { + TInt err = KErrInUse; + if (iMode == ConsoleMode::EText) + { + _LIT8(KEscapeSetCursorPosAbs, "\x1b[%u;%uH"); + TBuf8<32> buf; + buf.Format(KEscapeSetCursorPosAbs, aPos.iY + 1, aPos.iX + 1); + err = iOutput.Output(buf); + if (err == KErrNone) + { + iCursorTracker->SetCursorPosAbs(aPos); + } + } + return err; + } + +TInt CVtConsoleOutputController::SetCursorPosRel(const TPoint& aPoint) + { + TInt err = KErrInUse; + if (iMode == ConsoleMode::EText) + { + TBuf8<32> buf; + + if (aPoint.iY == 0) + { + // Do nothing. + } + else if (aPoint.iY < 0) + { + // Move cursor up. + _LIT8(KEscapeCursorUp, "\x1b[%uA"); + buf.AppendFormat(KEscapeCursorUp, -aPoint.iY); + } + else if (aPoint.iY > 0) + { + // Move cursor down. + _LIT8(KEscapeCursorDown, "\x1b[%uB"); + buf.AppendFormat(KEscapeCursorDown, aPoint.iY); + } + + if (aPoint.iX == 0) + { + // Do nothing. + } + else if (aPoint.iX < 0) + { + // Move cursor left. + _LIT8(KEscapeCursorLeft, "\x1b[%uD"); + buf.AppendFormat(KEscapeCursorLeft, -aPoint.iY); + } + else if (aPoint.iX > 0) + { + // Move cursor right. + _LIT8(KEscapeCursorRight, "\x1b[%uC"); + buf.AppendFormat(KEscapeCursorRight, aPoint.iY); + } + + err = KErrNone; + if (buf.Length() > 0) + { + err = iOutput.Output(buf); + if (err == KErrNone) + { + iCursorTracker->SetCursorPosRel(aPoint); + } + } + } + return err; + } + +TInt CVtConsoleOutputController::SetCursorHeight(TInt aPercentage) + { + TInt err = KErrInUse; + if (iMode == ConsoleMode::EText) + { + _LIT8(KEscapeHideCursor, "\x1b[?25l"); + _LIT8(KEscapeSeeCursor, "\x1b[?25h"); + if (aPercentage == 0) + { + err = iOutput.Output(KEscapeHideCursor); + } + else + { + err = iOutput.Output(KEscapeSeeCursor); + } + } + return err; + } + +TInt CVtConsoleOutputController::SetTitle(const TDesC&) + { + return KErrNone; + } + +TInt CVtConsoleOutputController::ClearScreen() + { + TInt err = KErrInUse; + if (iMode == ConsoleMode::EText) + { + _LIT8(KResetTerminal, "\x1b" "c" "\x1b" "[?7h"); // reset console, then enable line wrapping + err = iOutput.Output(KResetTerminal); + if (err == KErrNone) + { + User::After(100000); // It seems that TeraTerm doesn't like receiving attribute changes too soon after a terminal reset (tends to ignore the attributes). + err = ResetAttributes(); + if (err == KErrNone) + { + _LIT8(KEscapeClearScreen, "\x1b[2J\x1b[01;01H"); + err = iOutput.Output(KEscapeClearScreen); + if (err == KErrNone) + { + iCursorTracker->Reset(); + } + } + } + } + return err; + } + +TInt CVtConsoleOutputController::ClearToEndOfLine() + { + TInt err = KErrInUse; + if (iMode == ConsoleMode::EText) + { + _LIT8(KEscapeClearToEndOfLine, "\x1b[K"); + err = iOutput.Output(KEscapeClearToEndOfLine); + } + return err; + } + +TInt CVtConsoleOutputController::GetScreenSize(TSize& aSize) const + { + aSize = iCursorTracker->ConsoleSize(); + return KErrNone; + } + +TInt CVtConsoleOutputController::Write(const TDesC& aDes) + { + TInt err = KErrNone; + if (iMode == ConsoleMode::EBinary) + { + // staight collapse to 8 bit, no cleverness + TBuf8<256> buf; + TInt offset = 0; + while ((offset < aDes.Length()) && (err == KErrNone)) + { + buf.Copy(aDes.Mid(offset, Min(aDes.Length() - offset, buf.MaxLength()))); + offset += buf.Length(); + err = iOutput.Output(buf); + } + } + else + { + // In text mode we do a UTF-16 -> UTF-8 conversion + TRAP(err, iOutputBuf.CopyAsUtf8L(aDes)); + if (err == KErrNone) + { + err = iOutput.Output(iOutputBuf); + if (err == KErrNone) + { + iCursorTracker->Write(aDes); + } + } + } + return err; + } + +TInt CVtConsoleOutputController::Write(const TDesC8& aDes) + { + TInt err = iOutput.Output(aDes); + if ((err == KErrNone) && (iMode == ConsoleMode::EText)) + { + iCursorTracker->Write(aDes); + } + return err; + } + +void CVtConsoleOutputController::SetMode(ConsoleMode::TMode aMode) + { + iMode = aMode; + } + +//______________________________________________________________________________ +// CVtConsoleInputController + +EXPORT_C CVtConsoleInputController* CVtConsoleInputController::New(MConsoleInput& aConsoleInput, LtkUtils::CIniFile& aIniFile) + { + CVtConsoleInputController* self = NULL; + TRAPD(err, self = NewL(aConsoleInput, aIniFile)); + if (err == KErrNone) + { + return self; + } + return NULL; + } + +EXPORT_C CVtConsoleInputController* CVtConsoleInputController::NewL(MConsoleInput& aConsoleInput, LtkUtils::CIniFile& aIniFile) + { + CVtConsoleInputController* self = NewLC(aConsoleInput, aIniFile); + CleanupStack::Pop(self); + return self; + } + +EXPORT_C CVtConsoleInputController* CVtConsoleInputController::NewLC(MConsoleInput& aConsoleInput, LtkUtils::CIniFile& aIniFile) + { + CVtConsoleInputController* self = new(ELeave)CVtConsoleInputController(aConsoleInput, aIniFile); + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + +CVtConsoleInputController::CVtConsoleInputController(MConsoleInput& aConsoleInput, LtkUtils::CIniFile& aIniFile) + : CActive(CActive::EPriorityStandard), iConsoleInput(aConsoleInput), iIniFile(aIniFile), iMode(ConsoleMode::EText) + { + CActiveScheduler::Add(this); + } + +void CVtConsoleInputController::ConstructL() + { + iEscapeTimer = CPeriodic::NewL(CActive::EPriorityLow); + } + +CVtConsoleInputController::~CVtConsoleInputController() + { + Reset(); + delete iEscapeTimer; + } + +EXPORT_C void CVtConsoleInputController::GetKeyPress(TKeyPress& aKeyPress, TRequestStatus& aStatus) + { + ASSERT((iClientKeyPress == NULL) && (iClientRequestStatus == NULL)); + iClientKeyPress = &aKeyPress; + iClientRequestStatus = &aStatus; + *iClientRequestStatus = KRequestPending; + if (iKeyCodePending) + { + iKeyCodePending = EFalse; + CompleteKeyPressRequest(iPendingKeyCode); + } + else if (iInputError) + { + CompleteKeyPressRequest(iInputError); + iInputError = KErrNone; + } + else + { + ReadKeyPress(); + } + } + +EXPORT_C void CVtConsoleInputController::CancelGetKeyPress() + { + Cancel(); + if (iClientRequestStatus) + { + CompleteKeyPressRequest(KErrCancel); + } + } + +EXPORT_C void CVtConsoleInputController::SetMode(ConsoleMode::TMode aMode) + { + Reset(); + iMode = aMode; + } + +void CVtConsoleInputController::ReadKeyPress() + { + iEscapeTimer->Cancel(); + + while (iClientRequestStatus && (iBufPos < iBuf.Length())) + { + TUint8 c = iBuf[iBufPos++]; + if (iMode == ConsoleMode::EBinary) + { + CompleteKeyPressRequest((TKeyCode)c); + } + else + { + switch (iState) + { + case ENormal: + if (c == KEscapeChar1) + { + iState = EWaitingForEscapeChar2; + if (iBufPos == iBuf.Length()) + { + iEscapeTimer->Start(KEscapeTimeoutMicros, KEscapeTimeoutMicros, TCallBack(EscapeTimeoutS, this)); + } + } + else + { + CompleteKeyPressRequest((TKeyCode)c); + } + break; + case EWaitingForEscapeChar2: + if (c == KEscapeChar2) + { + iState = EWaitingForEscapeChar3; + } + else if (c == KEscapeChar2Func) + { + iState = EWaitingForEscapeChar3Func; + } + else + { + CompleteKeyPressRequest(EKeyEscape, (TKeyCode)c); + iState = ENormal; + } + break; + case EWaitingForEscapeChar3: + if (c >= '0' && c <= '9') + { + iState = EWaitingForExtendedFunc; + iExtendedEscapeBuf.Zero(); + iExtendedEscapeBuf.Append(c); + } + else + { + DoEscapeKeyL(c, KEscapeMappings, KNumEscapeMappings); + } + break; + case EWaitingForEscapeChar3Func: + DoEscapeKeyL(c, KFunctionKeyMappings, KNumEscapeFunctionMappings); + break; + case EWaitingForExtendedFunc: + if (iExtendedEscapeBuf.Length() < iExtendedEscapeBuf.MaxLength() && ((c >= '0' && c <= '9') || c == ';')) + { + iExtendedEscapeBuf.Append(c); + // Stay in this state until you see a '~' + } + else if (c == '~') + { + DoExtendedEscapeKey(); + } + else + { + // Gone off the rails, bail + iState = ENormal; + CompleteKeyPressRequest((TKeyCode)c); + } + break; + } + } + } + + ReadInput(); + } + +void CVtConsoleInputController::CompleteKeyPressRequest(TInt aError) + { + ASSERT(iClientKeyPress && iClientRequestStatus); + iClientKeyPress = NULL; + User::RequestComplete(iClientRequestStatus, aError); + } + +void CVtConsoleInputController::CompleteKeyPressRequest(TKeyCode aKeyCode) + { + ASSERT(iClientKeyPress && iClientRequestStatus); + iClientKeyPress->iCode = (TKeyCode)aKeyCode; + iClientKeyPress->iModifiers = 0; + iClientKeyPress = NULL; + User::RequestComplete(iClientRequestStatus, KErrNone); + } + +void CVtConsoleInputController::CompleteKeyPressRequest(TKeyCode aKeyCode1, TKeyCode aKeyCode2) + { + ASSERT(!iKeyCodePending); + // Store the second key-code in a member variable to be used the next time GetKeyPress is called. + iKeyCodePending = ETrue; + iPendingKeyCode = aKeyCode2; + CompleteKeyPressRequest(aKeyCode1); + } + +void CVtConsoleInputController::Reset() + { + Cancel(); + iEscapeTimer->Cancel(); + iState = ENormal; + iKeyCodePending = EFalse; + } + +void CVtConsoleInputController::ReadInput() + { + if (iClientRequestStatus && !IsActive()) // Note, if the escape timer expired we could already be active. + { + ASSERT(iBufPos == iBuf.Length()); + iBufPos = 0; + iBuf.Zero(); + iConsoleInput.Input(iBuf, iStatus); + SetActive(); + } + } + +void CVtConsoleInputController::RunL() + { + TInt err = iStatus.Int(); +#ifdef FSHELL_PLATFORM_OPP + if (err == KErrAbort) + { + ReadInput(); + return; + } +#endif + if (err == KErrNone) + { + ReadKeyPress(); + } + else if (iClientRequestStatus) + { + CompleteKeyPressRequest(err); + } + else + { + // Report the error next time the client requests a key. + iInputError = err; + } + } + +void CVtConsoleInputController::DoCancel() + { + iConsoleInput.CancelInput(iStatus); + } + +void CVtConsoleInputController::DoEscapeKeyL(TUint8 aChar, const TEscapeMapping* aMappings, TInt aMappingCount) + { + iState = ENormal; + for (TInt j = 0; j < aMappingCount; ++j) + { + if (aChar == aMappings[j].iEscapeChar) + { + CompleteKeyPressRequest(aMappings[j].iKeyCode); + return; + } + } + } + +TInt CVtConsoleInputController::EscapeTimeout() + { + ASSERT(iState == EWaitingForEscapeChar2); + iState = ENormal; + CompleteKeyPressRequest(EKeyEscape); + return KErrNone; + } + +TInt CVtConsoleInputController::EscapeTimeoutS(TAny* aSelf) + { + return ((CVtConsoleInputController*)aSelf)->EscapeTimeout(); + } + + +void CVtConsoleInputController::DoExtendedEscapeKey() + { + iState = ENormal; + TUint8 escape1 = 0; + TUint8 escape2 = 0; + if (iExtendedEscapeBuf.Length()) escape1 = iExtendedEscapeBuf[0]; + if (iExtendedEscapeBuf.Length() > 1) escape2 = iExtendedEscapeBuf[1]; + + for (TInt j = 0; j < KNumExtendedEscapeMappings; ++j) + { + const TLongerEscapeMapping& mapping = KExtendedEscapeMappings[j]; + if (escape1 == mapping.iEscapeChar1 && escape2 == mapping.iEscapeChar2) + { + CompleteKeyPressRequest(mapping.iKeyCode); + return; + } + } + }