--- /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 <fshell/qr3dll.h>
+
+class RWsSession;
+class RWindowGroup;
+
+#include <fshell/common.mmh>
+#ifdef FSHELL_WSERV_SUPPORT
+#include <w32std.h>
+#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<RWsSession*>(iOpaque1))
+#define iWg (*reinterpret_cast<RWindowGroup*>(iOpaque2))
+#define iCaptures (*reinterpret_cast<CArrayFixFlat<SCapture>**>(&iOpaque3))
+#define iPushList (*reinterpret_cast<CArrayPtrFlat<CArrayFixFlat<SCapture> >**>(&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<SCapture>(8);
+ iPushList = new(ELeave) CArrayPtrFlat<CArrayFixFlat<SCapture> >(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<<bit);
+ }
+ else if (cap.iFlags & ECaptureIsKeyUpDown)
+ {
+ iWg.CancelCaptureKeyUpAndDowns(cap.iCaptureHandle);
+ }
+ else
+ {
+ iWg.CancelCaptureKey(cap.iCaptureHandle);
+ }
+#endif
+ }
+
+CKeyCapturer::~CKeyCapturer()
+ {
+ Cancel();
+
+ for (TInt i = 0; i < iCaptures->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<SCapture>* newList = new(ELeave) CArrayFixFlat<SCapture>(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<SCapture>* 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;
+ }
+ }
+ }