commands/grabscreen/grabscreen.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Thu, 26 Aug 2010 00:49:35 +0100
changeset 45 534b01198c2d
parent 30 35cb3fe43f60
permissions -rw-r--r--
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.

// grabscreen.cpp
// 
// Copyright (c) 2008 - 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/ioutils.h>
#include <w32std.h>
#include <fbs.h>
#include <bitdev.h>
#include <imageconversion.h>

using namespace IoUtils;


class CCmdGrabscreen : public CCommandBase
	{
public:
	static CCommandBase* NewLC();
	~CCmdGrabscreen();
private:
	enum TFormat
		{
		EJpeg,
		EGif,
		EBmp,
		EPng,
		ERaw
		};
private:
	CCmdGrabscreen();
	void WriteEncodedL(TUid aEncodingUid);
	void WriteRawL();
private: // From CCommandBase.
	virtual const TDesC& Name() const;
	virtual void DoRunL();
	virtual void ArgumentsL(RCommandArgumentList& aArguments);
	virtual void OptionsL(RCommandOptionList& aOptions);
	
	virtual void RunL();
private:
	TFileName2 iFileName;
	CImageEncoder* iImageEncoder;
	HBufC8* iImageData;
	CFrameImageData* iFrameImageData;
	RFile iFile;
	TFormat iFormat;
	RArray<TInt> iRect;
	TInt iScreen;
	CFbsBitmap* iBitMap;
	CWsScreenDevice* iDev;
	RWsSession iWsSession;
	TInt iQuality;
	TBool iGreyscale;
	};


CCommandBase* CCmdGrabscreen::NewLC()
	{
	CCmdGrabscreen* self = new(ELeave) CCmdGrabscreen();
	CleanupStack::PushL(self);
	self->BaseConstructL();
	return self;
	}

CCmdGrabscreen::~CCmdGrabscreen()
	{
	delete iImageEncoder;
	delete iFrameImageData;
	delete iDev;
	delete iBitMap;
	delete iImageData;
	iFile.Close();
	iRect.Close();
	iWsSession.Close();
	REComSession::FinalClose();
	}

CCmdGrabscreen::CCmdGrabscreen() 
	: CCommandBase(EManualComplete), iQuality(100)
	{
	}

const TDesC& CCmdGrabscreen::Name() const
	{
	_LIT(KName, "grabscreen");	
	return KName;
	}

void CCmdGrabscreen::DoRunL()
	{
	if ((iFormat == ERaw) && (!iArguments.IsPresent(0)))
		{
		PrintError(KErrArgument, _L("STDOUT writing is not supported for raw format; please specify a filename"));
		DisplayHelp();
		User::Leave(KErrArgument);
		}
	
	LeaveIfErr(iWsSession.Connect(), _L("Couldn't connect to windowserver"));
	iDev = new(ELeave) CWsScreenDevice(iWsSession);
#ifdef SYMBIAN_WSERV_AND_CONE_MULTIPLE_SCREENS
	const TInt numScreens = iWsSession.NumberOfScreens();
	if (iScreen >= numScreens)
		{
		if (numScreens == 1)
			{
			LeaveIfErr(KErrArgument, _L("Invalid screen number - there is only 1 screen"));
			}
		else
			{
			LeaveIfErr(KErrArgument, _L("Invalid screen number - there are only %d screens"));
			}
		}
#endif
	LeaveIfErr(iDev->Construct(iScreen), _L("Couldn't construct CWsScreenDevice for screen %d"), iScreen);
	iBitMap = new(ELeave) CFbsBitmap();

	TRect rect;
	if (iOptions.IsPresent(&iRect))
		{
		if (iRect.Count() != 4)
			{
			LeaveIfErr(KErrArgument, _L("Invalid --rect option - four integers must be specifed"));
			}
		rect.SetRect(iRect[0], iRect[1], iRect[2], iRect[3]);
		}
	else
		{
		rect.SetRect(TPoint(0, 0), iDev->SizeInPixels());
		}
	
	LeaveIfErr(iBitMap->Create(rect.Size(), iDev->DisplayMode()), _L("Couldn't create fbsbitmap of size (%d,%d) for display mode %d"), rect.Width(), rect.Height(), iDev->DisplayMode());
	User::LeaveIfError(iDev->CopyScreenToBitmap(iBitMap, rect));

	switch (iFormat)
		{
		case EJpeg:
			WriteEncodedL(KImageTypeJPGUid);
			break;
		case EGif:
			WriteEncodedL(KImageTypeGIFUid);
			break;
		case EBmp:
			WriteEncodedL(KImageTypeBMPUid);
			break;
		case EPng:
			WriteEncodedL(KImageTypePNGUid);
			break;
		case ERaw:
			WriteRawL();
			break;
		}
	}

void CCmdGrabscreen::WriteEncodedL(TUid aEncodingUid)
	{
	if (aEncodingUid == KImageTypeJPGUid)
		{
		TJpegImageData* imageData = new(ELeave) TJpegImageData();
		CleanupStack::PushL(imageData);
		imageData->iSampleScheme = iGreyscale ? TJpegImageData::EMonochrome : TJpegImageData::EColor444;
		imageData->iQualityFactor = iQuality;
		TRAPL(iFrameImageData = CFrameImageData::NewL(), _L("Couldn't create CFrameImageData"));
		LeaveIfErr(iFrameImageData->AppendImageData(imageData), _L("Failed to write image data to TJpegImageData"));
		CleanupStack::Pop(imageData);
		}
	else if (aEncodingUid == KImageTypeGIFUid)
		{
		if (iGreyscale)
			{
			PrintWarning(_L("Greyscale not supported for GIF images."));
			}
		if (iOptions.IsPresent(&iQuality))
			{
			PrintWarning(_L("Quality control not supported for GIF images."));
			}
		}
	else if (aEncodingUid == KImageTypeBMPUid)
		{
		if (iGreyscale)
			{
			PrintWarning(_L("Greyscale not supported for BMP images."));
			}
		if (iOptions.IsPresent(&iQuality))
			{
			PrintWarning(_L("Quality control not supported for BMP images."));
			}
		}
	else if (aEncodingUid == KImageTypePNGUid)
		{
		if (iOptions.IsPresent(&iQuality))
			{
			PrintWarning(_L("Quality control not supported for PNG images."));
			}

		TPngEncodeData* imageData = new(ELeave) TPngEncodeData();
		CleanupStack::PushL(imageData);
		imageData->iBitsPerPixel = iGreyscale ? 4 : 8;
		imageData->iColor = !iGreyscale;
		imageData->iLevel = TPngEncodeData::EBestCompression;
		imageData->iPaletted = EFalse;
		iFrameImageData = CFrameImageData::NewL();
		User::LeaveIfError(iFrameImageData->AppendFrameData(imageData));
		CleanupStack::Pop(imageData);
		}
		
	if (iArguments.IsPresent(0))
		{
		LeaveIfErr(iFile.Replace(FsL(), iFileName, EFileWrite), _L("Unable to open \"%S\" for writing"), &iFileName);
		iImageEncoder = CImageEncoder::FileNewL(iFile, CImageEncoder::EOptionNone, aEncodingUid);
		}
	else
		{
		iImageEncoder = CImageEncoder::DataNewL(iImageData, CImageEncoder::EOptionNone, aEncodingUid);
		}
	iImageEncoder->Convert(&iStatus, *iBitMap, iFrameImageData);
	SetActive();
	}
	
void CCmdGrabscreen::RunL()
	{
	LeaveIfErr(iStatus.Int(), _L("Error returned from CImageEncoder::Convert"));
	if (!iArguments.IsPresent(0))
		{
		User::LeaveIfNull(iImageData);
		
		Stdout().SetModeL(RIoReadWriteHandle::EBinary);
		
		// we need to expand the 8 bit data when writing to stdout
		static const TInt KBufferSize = 0x100;
		TBuf<KBufferSize> buf;
		TPtr8 imageData(iImageData->Des());
		while (imageData.Length())
			{
			TInt thisCopyLen = Min(KBufferSize, imageData.Length());
			buf.Copy(imageData.Left(thisCopyLen));
			imageData.Set(imageData.MidTPtr(thisCopyLen));
			Stdout().WriteL(buf);
			}
		}
	Complete(KErrNone);
	}

void CCmdGrabscreen::WriteRawL()
	{
	if (iGreyscale)
		{
		PrintWarning(_L("Greyscale not supported for raw images."));
		}
	if (iOptions.IsPresent(&iQuality))
		{
		PrintWarning(_L("Quality control not supported for raw images."));
		}
	LeaveIfErr(iBitMap->Save(iFileName), _L("Unable to write \"%S\""), &iFileName);
	Complete(KErrNone);
	}

void CCmdGrabscreen::ArgumentsL(RCommandArgumentList& aArguments)
	{
	_LIT(KArg, "file_name");
	aArguments.AppendFileNameL(iFileName, KArg);
	}

void CCmdGrabscreen::OptionsL(RCommandOptionList& aOptions)
	{
	_LIT(KOptFormat, "format");
	aOptions.AppendEnumL((TInt&)iFormat, KOptFormat);

	_LIT(KOptRect, "rect");
	aOptions.AppendIntL(iRect, KOptRect);

	_LIT(KOptScreen, "screen");
	aOptions.AppendIntL(iScreen, KOptScreen);

	_LIT(KOptQuality, "quality");
	aOptions.AppendIntL(iQuality, KOptQuality);

	_LIT(KOptGreyscale, "greyscale");
	aOptions.AppendBoolL(iGreyscale, KOptGreyscale);
	}


EXE_BOILER_PLATE(CCmdGrabscreen)