diff -r 000000000000 -r 7f656887cf89 libraries/qr3/src/keycapture.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libraries/qr3/src/keycapture.cpp Wed Jun 23 15:52:26 2010 +0100 @@ -0,0 +1,503 @@ +// keycapture.cpp +// +// Copyright (c) 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 + +class RWsSession; +class RWindowGroup; + +#include +#ifdef FSHELL_WSERV_SUPPORT +#include +#endif + +//#define KEYCAPTURE_DEBUG(args...) RDebug::Printf(args) +#define KEYCAPTURE_DEBUG(args...) + +enum + { + ELongCapture = 1, + ECaptureIsDummy = 2, + ECaptureIsKeyUpDown = 4, + }; + +struct SCapture + { + MCaptureInterface* iClient; + TInt iIdentifier; + TInt iOriginalCode; // This is the code we were actually passed in. + TInt iCaptureCode; // This is the key code we call capture on. This will be the same as iOriginalCode for a normal capture. For a long capture, it will be a virtual key. For an up-and-down, it will be the scan code equivalent of iOriginalCode + TInt32 iCaptureHandle; + TUint32 iFlags; + TInt iPriority; + }; + +#define iWs (*reinterpret_cast(iOpaque1)) +#define iWg (*reinterpret_cast(iOpaque2)) +#define iCaptures (*reinterpret_cast**>(&iOpaque3)) +#define iPushList (*reinterpret_cast >**>(&iOpaque4)) +#define KSpareKeysMask 0xF + +#define KSpareKeyRange EKeyF9 // We use the range F9-F12 inclusive as that seems to be about the only range that I can find not being used by UIQ or licensees + +CKeyCapturer::CKeyCapturer() + : CActive(CActive::EPriorityUserInput) + , iUsedSpareKeys(0) + { + } + +void CKeyCapturer::ConstructL() + { +#ifdef FSHELL_WSERV_SUPPORT + __ASSERT_COMPILE(sizeof(RWsSession) == EOpaquePadding1); + __ASSERT_COMPILE(sizeof(RWindowGroup) == EOpaquePadding2); + User::LeaveIfError(iWs.Connect()); + iWg = RWindowGroup(iWs); + User::LeaveIfError(iWg.Construct((TUint32)this, EFalse)); + iWg.SetOrdinalPosition(-1,-1); // This appears to be needed to solve a race condition whereby someone creates a new window (or window group, or something) at roughly the same time as we do, which prevents the other window from coming to the foreground + User::LeaveIfError(iWg.SetName(_L("CKeyCapturerWindowGroup"))); // To ease debugging in wserv + + iCaptures = new(ELeave) CArrayFixFlat(8); + iPushList = new(ELeave) CArrayPtrFlat >(8); + + CActiveScheduler::Add(this); + iWs.EventReady(&iStatus); + SetActive(); +#else + User::Leave(KErrNotSupported); +#endif + } + +EXPORT_C void CKeyCapturer::AddCaptureL(MCaptureInterface* aClient, TInt aIdentifier, TInt aKeyCode) + { + AddCaptureL(aClient, aIdentifier, aKeyCode, 0, KMaxTInt); + } + +EXPORT_C void CKeyCapturer::AddCaptureL(MCaptureInterface* aClient, TInt aIdentifier, TInt aKeyCode, TInt aScanCode, TInt aCapturePriority) + { + if (aKeyCode == 0 || !aClient) User::Leave(KErrArgument); + RemoveCapture(aClient, aIdentifier); // Just in case it's already been set + + SCapture cap; + cap.iClient = aClient; + cap.iIdentifier = aIdentifier; + cap.iCaptureCode = aKeyCode; + cap.iOriginalCode = aKeyCode; + cap.iFlags = 0; + cap.iPriority = aCapturePriority; + + AddCaptureL(cap); + + if (aScanCode) + { + // If we've been passed a scan code, use it to capture the keyup and keydown events too + SCapture upanddownCap; + upanddownCap.iClient = NULL; + upanddownCap.iIdentifier = -1; + upanddownCap.iCaptureCode = aScanCode; + upanddownCap.iOriginalCode = aScanCode; + upanddownCap.iFlags = ECaptureIsDummy | ECaptureIsKeyUpDown; + upanddownCap.iPriority = aCapturePriority; + + TRAPD(err, AddCaptureL(upanddownCap)); + if (err) + { + CancelCapture(cap); + iCaptures->Delete(iCaptures->Count()-1); + User::Leave(err); + } + } + } + +EXPORT_C void CKeyCapturer::AddLongCaptureL(MCaptureInterface* aClient, TInt aIdentifier, TInt aKeyCode, TInt aScanCode) + { + AddLongCaptureL(aClient, aIdentifier, aKeyCode, aScanCode, KMaxTInt); + } + +EXPORT_C void CKeyCapturer::AddLongCaptureL(MCaptureInterface* aClient, TInt aIdentifier, TInt aKeyCode, TInt aScanCode, TInt aCapturePriority) + { + if (aKeyCode == 0 || aScanCode == 0 || !aClient) User::Leave(KErrArgument); + RemoveCapture(aClient, aIdentifier); // Just in case it's already been set + + SCapture cap; + cap.iClient = aClient; + cap.iIdentifier = aIdentifier; + cap.iCaptureCode = 0; // Is filled in later + cap.iOriginalCode = aKeyCode; + cap.iFlags = ELongCapture; + cap.iPriority = aCapturePriority; + + AddCaptureL(cap); + + // In order to get repeat events (and, by extension, long key presses) the key events + // must be going to the window group that's capturing the long press. Therefore, + // we also need to capture the key events even if we don't have any client for that + // key. + // + // Of course, this isn't documented anywhere. + SCapture keyCap; + keyCap.iClient = NULL; // So that no action is taken when a short press occurs + keyCap.iIdentifier = -1; + keyCap.iCaptureCode = aKeyCode; + keyCap.iOriginalCode = aKeyCode; + keyCap.iFlags = ECaptureIsDummy; + keyCap.iPriority = aCapturePriority; + + TRAPD(err, AddCaptureL(keyCap)); + if (err) + { + CancelCapture(cap); + iCaptures->Delete(iCaptures->Count()-1); + User::Leave(err); + } + + // And finally, do a upanddown capture as well, for good measure (TODO do this for normal captures as well, but need the scancode... grr. + SCapture upanddownCap; + upanddownCap.iClient = NULL; + upanddownCap.iIdentifier = -1; + upanddownCap.iCaptureCode = aScanCode; + upanddownCap.iOriginalCode = aScanCode; + upanddownCap.iFlags = ECaptureIsDummy | ECaptureIsKeyUpDown; + upanddownCap.iPriority = aCapturePriority; + + TRAP(err, AddCaptureL(upanddownCap)); + if (err) + { + CancelCapture(keyCap); + iCaptures->Delete(iCaptures->Count()-1); + CancelCapture(cap); + iCaptures->Delete(iCaptures->Count()-1); + User::Leave(err); + } + } + +void CKeyCapturer::AddCaptureL(SCapture& cap) + { +#ifdef FSHELL_WSERV_SUPPORT + TBool isLong = (cap.iFlags & ELongCapture); + TBool isUpDown = (cap.iFlags & ECaptureIsKeyUpDown); + //ASSERT(iFlags & (ELongCapture | ECaptureIsKeyUpDown) == 0); // Can't be both a long capture and an up-down + + if (isLong && iUsedSpareKeys == KSpareKeysMask) + { + // No more keys available to remap as long key presses + User::Leave(KErrOverflow); + } + + if (cap.iPriority == KMaxTInt) cap.iPriority = KMaxTInt-1; // Internally somewhere wserv adds one to the priority, and KMaxTInt+1 is KMinTInt... + + KEYCAPTURE_DEBUG("KC: AddCapture ident=%d on key=%d, long=%d, updown=%d, dummy=%d)", cap.iIdentifier, cap.iOriginalCode, isLong, isUpDown, cap.iFlags&ECaptureIsDummy); + + iCaptures->SetReserveL(iCaptures->Count() + 1); // So that the AppendL later cannot fail + if (isLong) + { + TInt spareKey = -1; // There's a cleaner way to work this out, but since there's only 4, what's the point? + if ((iUsedSpareKeys & 1) == 0) spareKey = 0; + else if ((iUsedSpareKeys & 2) == 0) spareKey = 1; + else if ((iUsedSpareKeys & 4) == 0) spareKey = 2; + else if ((iUsedSpareKeys & 8) == 0) spareKey = 3; + ASSERT(spareKey != -1); // Otherwise the check at the top should have caught this case + cap.iCaptureCode = KSpareKeyRange + spareKey; + + cap.iCaptureHandle = iWg.CaptureLongKey(cap.iOriginalCode, cap.iCaptureCode, 0, 0, cap.iPriority, ELongCaptureWaitShort); + User::LeaveIfError(cap.iCaptureHandle); + iUsedSpareKeys |= 1 << spareKey; + } + else if (isUpDown) + { + cap.iCaptureHandle = iWg.CaptureKeyUpAndDowns(cap.iCaptureCode, 0, 0, cap.iPriority); + User::LeaveIfError(cap.iCaptureHandle); + } + else + { + cap.iCaptureHandle = iWg.CaptureKey(cap.iCaptureCode, 0, 0, cap.iPriority); + User::LeaveIfError(cap.iCaptureHandle); + } + iCaptures->AppendL(cap); +#else + User::Leave(KErrNotSupported); +#endif + } + +EXPORT_C void CKeyCapturer::RemoveCapture(MCaptureInterface* aClient, TInt aIdentifier) + { + // Linear search thru iCaptures + for (TInt i = 0; i < iCaptures->Count(); i++) + { + SCapture cap = iCaptures->operator[](i); // Copy cap so we can use it after it's been deleted below + if (cap.iIdentifier == aIdentifier) + { + ASSERT(cap.iClient == aClient); + (void)aClient; // For urel aClient is unused + + CancelCapture(cap); + iCaptures->Delete(i); + RemoveDummyForCapture(cap); + break; + } + } + } + +void CKeyCapturer::CancelCapture(SCapture& cap) + { +#ifdef FSHELL_WSERV_SUPPORT + KEYCAPTURE_DEBUG("KC: CancelCapture ident=%d, captureCode=%d, flags=%d", cap.iIdentifier, cap.iCaptureCode, cap.iFlags); + if (cap.iFlags & ELongCapture) + { + iWg.CancelCaptureLongKey(cap.iCaptureHandle); + TInt bit = cap.iCaptureCode - KSpareKeyRange; + iUsedSpareKeys &= ~(1<Count(); i++) + { + SCapture& cap = iCaptures->operator[](i); + CancelCapture(cap); + } + delete iCaptures; + iPushList->ResetAndDestroy(); + delete iPushList; +#ifdef FSHELL_WSERV_SUPPORT + iWg.Close(); + iWs.Close(); +#endif + } + +void CKeyCapturer::RunL() + { +#ifdef FSHELL_WSERV_SUPPORT + TWsEvent e; + iWs.GetEvent(e); + + iWs.EventReady(&iStatus); + SetActive(); + + if (e.Type() == EEventKey || e.Type() == EEventKeyDown || e.Type() == EEventKeyUp) + { + TInt code = e.Key()->iCode; + TInt scanCode = e.Key()->iScanCode; + KEYCAPTURE_DEBUG("KC: Received key event (type=%d, key=%d, scan=%d, rep=%d)", e.Type(), code, scanCode, e.Key()->iRepeats); + + SCapture* foundDummyCapture = NULL; + + // Linear search thru iCaptures + for (TInt i = 0; i < iCaptures->Count(); i++) + { + SCapture& cap = iCaptures->operator[](i); + + TBool match; + if (e.Type() == EEventKeyDown || e.Type() == EEventKeyUp) + { + match = (scanCode == cap.iCaptureCode) && (cap.iFlags & ECaptureIsKeyUpDown); + } + else + { + match = (code == cap.iCaptureCode) && !(cap.iFlags & ECaptureIsKeyUpDown); + } + + if (match) + { + TBool dummy = cap.iFlags & ECaptureIsDummy; + if (cap.iClient && !dummy) + { + iLastEventScanCode = e.Key()->iScanCode; + KEYCAPTURE_DEBUG("KC: Sending event ident=%d to client", cap.iIdentifier); + cap.iClient->CapturedKeyPressedL(cap.iIdentifier); + iLastEventScanCode = 0; + } + if (dummy) + { + // If it's a dummy capture tied to a particular long key press, then we want to keep looking just in case there's something else that has genuinely captured the key + // Also we remember the dummy in case we don't find any other capture, because we need to forward on uncaptured dummies + foundDummyCapture = ∩ + } + else + { + foundDummyCapture = NULL; + break; + } + } + } + if (foundDummyCapture) + { + KEYCAPTURE_DEBUG("KC: Capture is dummy, passing on to frontmost windowgroup"); + TInt foc = iWs.GetFocusWindowGroup(); + if (foc != iWg.Identifier()) + { + // Send the event on being sure not to pass it back to ourselves! + iWs.SendEventToWindowGroup(foc, e); + } + } + } +#endif + } + +TInt CKeyCapturer::RunError(TInt /*aError*/) + { + return KErrNone; // Don't care if our client left from its call to CapturedKeyPressedL + } + +void CKeyCapturer::DoCancel() + { +#ifdef FSHELL_WSERV_SUPPORT + iWs.EventReadyCancel(); +#endif + } + +#ifdef __EXE__ + CKeyCapturer* gCapturer = NULL; +#endif + +EXPORT_C CKeyCapturer* CKeyCapturer::GetCapturerL() + { + CKeyCapturer* ref = NULL; + +#ifdef __EXE__ + if (!gCapturer) + { + gCapturer = CKeyCapturer::NewLC(); + CleanupStack::Pop(); + } + ref = gCapturer; +#else + ref = (CKeyCapturer*)Dll::Tls(); + if (!ref) + { + ref = CKeyCapturer::NewLC(); + User::LeaveIfError(Dll::SetTls(ref)); + CleanupStack::Pop(ref); + } +#endif + return ref; + } + +EXPORT_C void CKeyCapturer::DeleteCapturer() + { +#ifdef __EXE__ + delete gCapturer; + gCapturer = NULL; +#else + CKeyCapturer* ref = (CKeyCapturer*)Dll::Tls(); + delete ref; + Dll::SetTls(NULL); + //Dll::FreeTls(); +#endif + } + +CKeyCapturer* CKeyCapturer::NewLC() + { + CKeyCapturer* res = new(ELeave) CKeyCapturer; + CleanupStack::PushL(res); + res->ConstructL(); + return res; + } + +EXPORT_C void CKeyCapturer::PushCurrentCapturesL() + { + CArrayFixFlat* newList = new(ELeave) CArrayFixFlat(8); + iPushList->AppendL(iCaptures); + // Now cancel everything in currentList to leave a clean slate + for (TInt i = 0; i < iCaptures->Count(); i++) + { + SCapture& cap = iCaptures->operator[](i); + CancelCapture(cap); + } + iCaptures = newList; + } + +EXPORT_C void CKeyCapturer::PopCapturesL() + { + //__ASSERT_ALWAYS(iPushList->Count() >= 1, User::Invariant()); + KEYCAPTURE_DEBUG("KC: Popping captures"); + + // Cancel everything in the current list + for (TInt i = 0; i < iCaptures->Count(); i++) + { + SCapture& cap = iCaptures->operator[](i); + CancelCapture(cap); + } + iCaptures->Reset(); // Transfer everything to iCaptures one by one, to allow for the (admittedly unlikely) possibility that any individual capture could fail + + CArrayFixFlat* newList = (*iPushList)[iPushList->Count()-1]; + for (TInt i = newList->Count() - 1; i >= 0 ; i--) + { + SCapture& cap = newList->operator[](i); + AddCaptureL(cap); + newList->Delete(i); // When it's been sucessfully captured into iCaptures, remove if from newList + } + iPushList->Delete(iPushList->Count() - 1); + delete newList; + KEYCAPTURE_DEBUG("KC: Finished restoring previous captures"); + } + +EXPORT_C TInt CKeyCapturer::IsCaptured(TInt aIdentifier) + { + TBool dontCare; + return IsCaptured(aIdentifier, dontCare); + } + +EXPORT_C TInt CKeyCapturer::IsCaptured(TInt aIdentifier, TBool& aIsLong) + { + for (TInt i = 0; i < iCaptures->Count(); i++) + { + SCapture& cap = iCaptures->operator[](i); + if (cap.iIdentifier == aIdentifier && !(cap.iFlags & ECaptureIsDummy)) + { + aIsLong = cap.iFlags & ELongCapture; + return cap.iOriginalCode; + } + } + return 0; + } + +EXPORT_C TInt CKeyCapturer::GetScanCodeOfLastEvent() + { + return iLastEventScanCode; + } + +void CKeyCapturer::RemoveDummyForCapture(const SCapture& aCapture) + { + for (TInt i = 0; i < iCaptures->Count(); i++) + { + SCapture cap = iCaptures->operator[](i); + TUint32 matchFlags = ECaptureIsDummy; + if (aCapture.iFlags & ELongCapture) + { + // Nothing to do - the match flags for a long press are ECaptureIsDummy and that's it + } + else + { + // A dummy for a short press is an upAndDown one + matchFlags |= ECaptureIsKeyUpDown; + } + if ((cap.iFlags & matchFlags) && (cap.iOriginalCode == aCapture.iOriginalCode)) + { + CancelCapture(cap); + iCaptures->Delete(i); + RemoveDummyForCapture(cap); // Just in case the long press had a dummy (being the short press) which would then ALSO have a dummy (the upanddown) + break; + } + } + }