libraries/qr3/src/keycapture.cpp
changeset 0 7f656887cf89
child 86 56b6ee983610
--- /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 = &cap;
+					}
+				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;
+			}
+		}
+	}