Added ENotifyKeypresses and ECaptureCtrlC flags to CCommandBase.
Commands can now get keypresses and handle ctrl-C via callbacks instead of having to implement custom active objects. As part of this extended the CCommandBase extension interface to MCommandExtensionsV2 for the new virtual functions KeyPressed(TUint aKeyCode, TUint aModifiers) and CtrlCPressed(). sudo now cleans up correctly by using ECaptureCtrlC.
// 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;
}
}
}