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.
// SensibleClient.cpp
//
// Copyright (c) 2006 - 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 "SensibleClient.h"
#include "SensibleClient_internals.h"
#include "cliserv.h"
#include "SensibleCompat.h"
_LIT(KClientPanic, "SensibleClient");
#define DebugPanic() User::Panic(KClientPanic, - __LINE__)
#define AssertPanic(x) User::Panic(KClientPanic, x)
enum TPanic
{
EReadOffEndOfCallback, // A call to TCallbackParser::Next when there's nothing more in the callback buffer
EBadType, // A call to TCallbackParser::NextX where X doesn't match the type of the next thing in the buffer
};
#ifdef EKA2
static TInt StartServer()
//
// Start the server process. Simultaneous launching
// of two such processes should be detected when the second one attempts to
// create the server object, failing with KErrAlreadyExists.
//
{
const TUidType serverUid(KNullUid,KNullUid,KServerUid3);
RProcess server;
TInt r=server.Create(KMyServerImg,KNullDesC,serverUid);
if (r!=KErrNone)
return r;
TRequestStatus stat;
server.Rendezvous(stat);
if (stat!=KRequestPending)
server.Kill(0); // abort startup
else
server.Resume(); // logon OK - start the server
User::WaitForRequest(stat); // wait for start or death
// we are expected to return a negative error code from this function so if the server died
// with a positive (or zero) code, map it to KErrServerTerminated
r = (server.ExitType() != EExitPending && stat.Int() >= 0) ? KErrServerTerminated : stat.Int();
server.Close();
return r;
}
#else
#include <e32math.h>
inline TServerStart::TServerStart(TRequestStatus& aStatus)
:iId(RThread().Id()),iStatus(&aStatus)
{aStatus=KRequestPending;}
inline TPtrC TServerStart::AsCommand() const
{return TPtrC(reinterpret_cast<const TText*>(this),sizeof(TServerStart)/sizeof(TText));}
static TInt StartServer()
//
// Start the server process/thread which lives in an EPOCEXE object
//
{
TRequestStatus started;
TServerStart start(started);
const TUidType serverUid(KNullUid,KNullUid,KServerUid3);
#ifdef __WINS__
//
// In WINS the server is a DLL, the exported entrypoint returns a TInt
// which represents the real entry-point for the server thread
//
RLibrary lib;
TInt r=lib.Load(KMyServerImg,serverUid);
if (r!=KErrNone)
return r;
TLibraryFunction ordinal1=lib.Lookup(1);
TThreadFunction serverFunc=reinterpret_cast<TThreadFunction>(ordinal1());
//
// To deal with the unique thread (+semaphore!) naming in EPOC, and that we may
// be trying to restart a server that has just exited we attempt to create a
// unique thread name for the server.
// This uses Math::Random() to generate a 32-bit random number for the name
//
TName name(KMyServerName);
name.AppendNum(Math::Random(),EHex);
RThread server;
r=server.Create(name,serverFunc,
KMyServerStackSize,
&start,&lib,NULL,
KMyServerInitHeapSize,KMyServerMaxHeapSize,EOwnerProcess);
lib.Close(); // if successful, server thread has handle to library now
#else
//
// EPOC is easy, we just create a new server process. Simultaneous launching
// of two such processes should be detected when the second one attempts to
// create the server object, failing with KErrAlreadyExists.
//
RProcess server;
TInt r=server.Create(KMyServerImg,start.AsCommand(),serverUid);
#endif
if (r!=KErrNone)
return r;
TRequestStatus died;
server.Logon(died);
if (died!=KRequestPending)
{
// logon failed - server is not yet running, so cannot have terminated
User::WaitForRequest(died); // eat signal
server.Kill(0); // abort startup
server.Close();
return died.Int();
}
//
// logon OK - start the server
server.Resume();
User::WaitForRequest(started,died); // wait for start or death
if (started==KRequestPending)
{
// server has died, never made it to the startup signal
server.Close();
return died.Int();
}
//
// server started (at last). Cancel and consume the death-notification
// before reporting success
server.LogonCancel(died);
server.Close();
User::WaitForRequest(died); // eat the signal (from the cancel)
return KErrNone;
}
TInt E32Dll(TDllReason)
{
return 0;
}
#endif
RSensibleSessionBody::RSensibleSessionBody()
: iDispatcher(NULL)
{}
TInt RSensibleSessionBody::Connect(TInt aMessageSlots, TBool aStartCallbackDispatcher)
//
// Connect to the server, attempting to start it if necessary
//
{
TInt r = KErrNone;
TInt retry=2;
for (;;)
{
r=DoCreateSession(KMyServerName, TVersion(0,0,0), aMessageSlots);
if (r!=KErrNotFound && r!=KErrServerTerminated)
break;
if (--retry==0)
break;
r=StartServer();
if (r!=KErrNone && r!=KErrAlreadyExists)
break;
}
if (r == KErrNone && aStartCallbackDispatcher)
{
if (StartCallbackDispatcher() != KErrNone)
{
Close();
return KErrNoMemory;
}
}
return r;
}
TInt RSensibleSessionBody::StartCallbackDispatcher()
{
if (!iDispatcher)
{
iDispatcher = new CServerCallbackDispatcher(*this);
}
else if (!iDispatcher->IsActive())
{
iDispatcher->Register();
}
return iDispatcher == NULL ? KErrNoMemory : KErrNone;
}
void RSensibleSessionBody::StopCallbackDispatcher()
{
if (iDispatcher) iDispatcher->Cancel();
}
void RSensibleSessionBody::DispatchCallbackL(TServerCallback& /*aCallback*/, TCallbackParser& /*aParser*/)
{
// Implemented by subclass
}
void RSensibleSessionBody::ServerDiedL()
{
// Implemented by subclass
}
void RSensibleSessionBody::Close()
{
delete iDispatcher;
iDispatcher = NULL;
}
// Not happy this actually works or is worth it
/*
void CCleanupAndComplete::RunL()
{
if (iClientStatus)
{
// We use this class with a null pointer to allow non-asynchronous (ie blind) APIs to still have the ability to cleanup resource when they are completed
User::RequestComplete(iClientStatus, iStatus.Int());
}
delete this;
}
void CCleanupAndComplete::DoCancel()
{
//TODO what? Probably delete this in some way
//delete this;
}
CCleanupAndComplete::CCleanupAndComplete(TRequestStatus* aClientStatus)
: CActive(EPriorityStandard), iClientStatus(aClientStatus)
{
CActiveScheduler::Add(this);
if (iClientStatus)
{
*iClientStatus = KRequestPending;
}
}
CCleanupAndComplete* CCleanupAndComplete::NewLC(TRequestStatus* aClientStatus)
{
CCleanupAndComplete* self = new(ELeave) CCleanupAndComplete(aClientStatus);
CleanupStack::PushL(self);
return self;
}
TInt CCleanupAndComplete::Create(TRequestStatus*& aStatus, const TDesC8& aDes, TInt& aIpcArg)
{
CCleanupAndComplete* self = new CCleanupAndComplete(aStatus);
TInt err = KErrNone;
if (!self) err = KErrNoMemory;
HBufC8* buf = NULL;
if (!err)
{
buf = aDes.Alloc();
}
if (!buf) err = KErrNoMemory;
if (!err)
{
err = self->iWithoutDestructor.Append(buf);
}
if (err)
{
delete buf;
delete self;
return KErrNoMemory;
}
aIpcArg = (TInt)buf;
aStatus = &self->iStatus;
self->SetActive();
return KErrNone;
}
TInt CCleanupAndComplete::Create(TRequestStatus*& aStatus, const TDesC16& aDes, TInt& aIpcArg)
{
CCleanupAndComplete* self = new CCleanupAndComplete(aStatus);
TInt err = KErrNone;
if (!self) err = KErrNoMemory;
HBufC16* buf = NULL;
if (!err)
{
buf = aDes.Alloc();
}
if (!buf) err = KErrNoMemory;
if (!err)
{
err = self->iWithoutDestructor.Append(buf);
}
if (err)
{
delete buf;
delete self;
return KErrNoMemory;
}
aIpcArg = (TInt)buf;
aStatus = &self->iStatus;
self->SetActive();
return KErrNone;
}
void CCleanupAndComplete::AddL(CBase* aWithDestructor)
{
User::LeaveIfError(iWithDestructor.Append(aWithDestructor));
}
void CCleanupAndComplete::AddL(TAny* aWithoutDestructor)
{
User::LeaveIfError(iWithoutDestructor.Append(aWithoutDestructor));
}
CCleanupAndComplete::~CCleanupAndComplete()
{
Cancel();
iWithDestructor.ResetAndDestroy();
for (TInt i = 0; i < iWithoutDestructor.Count(); i++)
{
delete iWithoutDestructor[i];
}
}
void CCleanupAndComplete::SetActive()
{
CActive::SetActive();
}
*/
CServerCallbackDispatcher::CServerCallbackDispatcher(RSensibleSessionBody& aSession)
: CActive(EPriorityStandard), iSession(aSession), iContextPtr(NULL, 0)
{
CActiveScheduler::Add(this);
Register();
}
void CServerCallbackDispatcher::Register()
{
iState = EWaitingForCallback;
IPC(args, &iNextCallback, 0,0,0);
iSession.DoSendReceive(ERegisterCallbackNotifier, args, iStatus);
SetActive();
}
void CServerCallbackDispatcher::RunL()
{
DISOWN(iCachedCallbackResult8);
DISOWN(iCachedCallbackResult16);
if (iStatus == KErrServerTerminated)
{
iSession.ServerDiedL();
return;
}
else if (iStatus != KErrNone)
{
//TODO Do something...
__DEBUGGER();
return;
}
TServerCallback cb = iNextCallback();
TCallbackParser p(cb);
if (iState == EWaitingForCallback && cb.iContextLength != 0)
{
iState = EWaitingForContext;
DISOWN(iContext);
iContext = HBufC8::NewL(cb.iContextLength);
iContextPtr = iContext->Des();
IPC(args, &iContextPtr, 0,0,0);
iSession.DoSendReceive(EGetCallbackContext, args, iStatus);
SetActive();
return;
}
if (iState == EWaitingForContext)
{
p.SetContext(*iContext);
}
Register();
iSession.DispatchCallbackL(cb, p);
}
void CServerCallbackDispatcher::DoCancel()
{
IPC(args, 0,0,0,0);
iSession.DoSendReceive(ECancelCallbackNotifier, args);
}
CServerCallbackDispatcher::~CServerCallbackDispatcher()
{
Cancel();
DISOWN(iCachedCallbackResult8);
DISOWN(iCachedCallbackResult16);
DISOWN(iContext);
}
TCallbackParser::TCallbackParser(const TServerCallback& aCallback)
: iCallback(aCallback), iContext(NULL), iNextPtr(aCallback.iData.Ptr()), iInContext(EFalse)
{}
void TCallbackParser::SetContext(TDesC8& aContext)
{
iContext = &aContext;
}
#define DOGET(T, type, name) T name; TAny* res = Next(sizeof(T), #type); Mem::Copy(&name, res, sizeof(T));
#define GET(T, type) DOGET(T, type, __result); return __result;
TInt TCallbackParser::NextInt()
{
GET(TInt, i);
}
TUint TCallbackParser::NextUint()
{
GET(TUint, u);
}
TPoint TCallbackParser::NextPoint()
{
GET(TPoint, P);
}
TSize TCallbackParser::NextSize()
{
GET(TSize, S);
}
TRgb TCallbackParser::NextRgb()
{
GET(TRgb, G);
}
TRect TCallbackParser::NextRect()
{
GET(TRect, R);
}
TPtrC TCallbackParser::NextDesC()
{
DOGET(TInt, i, len);
const TUint16* ptr = reinterpret_cast<const TUint16*>(Next(len*2, "D"));
if (TUint(ptr)&1)
{
// Need to allow for padding
ptr = (TUint16*)(((TUint8*)ptr)+1);
iNextPtr++; // And also move iNextPtr to the right place for the next read
}
return TPtrC(ptr, len);
}
TPtrC8 TCallbackParser::NextDesC8()
{
DOGET(TInt, i, len);
const TUint8* ptr = reinterpret_cast<const TUint8*>(Next(len, "8"));
return TPtrC8(ptr, len);
}
void* TCallbackParser::Next(TInt aSize, char* aType)
{
const TUint8* endPtr = iInContext ? iContext->Ptr() + iContext->Size() : iCallback.iData.Ptr() + iCallback.iData.Size();
if (iNextPtr + aSize + 1 > endPtr)
{
// No room for arg and type
if (!iInContext && iContext)
{
// try moving to context
iNextPtr = (TUint8*)iContext->Ptr();
iInContext = ETrue;
}
else
{
// Either there's no context, or we're already in it and reading off the end
AssertPanic(EReadOffEndOfCallback);
}
}
TUint8 nextType = *iNextPtr; //iInContext ? iCallback.iContextTypes[iIdx] : iCallback.iDataTypes[iIdx];
iNextPtr++;
void* result = (void*)iNextPtr;
__ASSERT_ALWAYS(nextType == *aType, AssertPanic(EBadType)); // Types must match
iNextPtr = iNextPtr + aSize;
return result;
}