Wiley.Symbian.OS.Internals.Real.time.Kernel.Programming.Dec.2005.eBook-DDU (779891), страница 95
Текст из файла (страница 95)
This is done because the client needs toknow the return value before any more of its code can execute, while thesecond function will just be stored by WSERV on the client side and sentto the server side later. WSERV calls these functions in response to theclient calls to the RAnim functions CommandReply() and Command(),respectively:void CHandWritingAnim::Command(TInt aOpcode,TAny* aArgs){THandAnimArgUnion pData;pData.any=aArgs;switch (aOpcode){case EHwOpActivate:Activate();break;case EHwOpDeactivate:Deactivate();break;case EHwOpSetDrawData:SetDrawData(pData.DrawData);break;default:iFunctions->Panic();}}The previous function shows three commands that the client can callon the handwriting anim.
These are to turn the handwriting recognitionon and off, and to change some display settings, such as line-width andcolor. The client has to pass a large block of data with this call; this ispassed into the function with an untyped pointer. To avoid a cast, weuse a union, but of course this provides no more type safety that a castwould – it just makes the code more readable.union THandAnimArgUnion{const TAny* Any;const TBool* Bool;const THandwritingDrawData* DrawData;};The CommandReplyL() function also provides two functions that theclient can call:TInt CHandWritingAnim::CommandReplyL(TInt aOpcode, TAny* aArgs){THandAnimArgUnion pData;450THE WINDOW SERVERpData.any=aArgs;switch (aOpcode){case EHwOpSpriteMask:SpriteChangeL(*pData.Bool);break;case EHwOpGetLastChar:return iLastGeneratedCharacter;default:iFunctions->Panic();}return KErrNone;}The first function allows the client to change the bitmaps that are actuallyused to draw the ink.
There is no return value from this function, but itcan fail. In this case it will leave and the leave value will be returned tothe client. The second function returns the last generated character to theclient.11.7.4 Handling pointer events and updating the spriteWhen a pointer down is received, the anim sets a timer. It does thisbecause the pointer event might be a click on a UI feature rather than thestart of the drawing of some digital ink. If the user clicks and holds untilthe timer expires, or clicks and releases without moving the pen, then thisindicates a click on a UI feature.The following routine deals with pointer move events.
It only has todeal with them when the handwriting is active and the pen is down,so most states just return EFalse to say that the event has not beenconsumed:TBool CHandWritingAnim::HandlePointerMove(TPoint aPoint){switch (iState){case EHwStateWaitingMove:{cont TInt KMinMovement=5TPoint moved=aPoint-iCurrentDrawPoint;if (Abs(moved.iX)< KMinMovement && Abs(moved.iY)< KMinMovement)return ETrue;iSpriteFunctions->Activate(ETrue);DrawPoint();iState=EHwStateDrawing;}case EHwStateDrawing:break;default:return EFalse;}DrawLine(aPoint);A SIMPLE HANDWRITING ANIMATION DLL451UpdateSprite();return ETrue;}If we are still waiting for the timer to expire (iState==EHwStateWaitingMove), and the point is still close to the original down, then themove event is ignored – but it is still consumed.
If not, the anim makesthe sprite visible by calling the Activate() function, then draws a pointinto the sprite, and updates the state to indicate that drawing is in progress.It then calls the following function to draw a line into the sprite bitmap:void CHandWritingAnim::DrawLine(TPoint aEndPoint){iSpriteGc->Activate(iBitmapDevice);iSpriteGc->SetPenSize(TSize(iDrawData.iLineWidth,iDrawData.iLineWidth));iSpriteGc->SetPenColor(iDrawData.iLineColor);iSpriteGc->MoveTo(iCurrentDrawPoint);iSpriteGc->DrawLineTo(aEndPoint);if (iMaskBitmapDevice){iSpriteGc->Activate(iMaskBitmapDevice);iSpriteGc->SetPenSize(TSize(iDrawData.iMaskLineWidth,iDrawData.iMaskLineWidth));//Mask must be drawn in blackiSpriteGc->SetPenColor(KRgbBlack);iSpriteGc->MoveTo(iCurrentDrawPoint);iSpriteGc->DrawLineTo(aEndPoint);}iCurrentDrawPoint=aEndPoint;iPointStore->AddPoint(aEndPoint);}The anim uses the same graphics context to draw to the sprite bitmapand to the mask bitmap – it activates that context on the bitmap deviceof the bitmap in which the drawing is to be done.
If there is no mask,then the anim will use the bitmap itself as the mask, and so the ink willhave to be drawn in black. If there is a mask, then the line of the digitalink needs to be drawn into the mask and the bitmap. In this case, itshould be drawn in black in the mask, but any color can be used forthe ink in the bitmap. WSERV stores the end point of the line, so that itcan use it as the starting point of the line the next time this function iscalled. It also stores the point in a buffer so that later on the characterrecognition algorithm can make an analysis of the ink shape. After thebitmaps are updated, the code calls this function to update the screen (seeCHandWritingAnim::HandlePointerMove earlier in the chapter):void CHandWritingAnim::UpdateSprite(){TRect drawTo;452THE WINDOW SERVERiSpriteGc->RectDrawnTo(drawTo);iSpriteFunctions->UpdateMember(0,drawTo,EFalse);}When any drawing is being done, BITGDI keeps track of which pixelshave been drawn to – or at least a bounding rectangle of those pixels.
Thisrectangle is not always pixel perfect, but serves as a good approximation.This rectangle is retrieved by calling RectDrawnTo() and this samefunction also resets the rectangle. Then the function calls the updatemember function. This is a function provided by WSERV to all spriteanims and its purpose is to correct the screen in the area of this rectangle.The normal way to update a sprite is to remove it and then draw itagain to the screen – but of course if this is done, the screen will flicker. InSymbian OS v5u, we added a way to update a sprite without flicker – thisis the function used in the previous code and defined thus:void MAnimSpriteFunctions::UpdateMember(TInt aMember,const TRect& aRect,TBool aFullUpdate);The third parameter to this function tells WSERV whether it shoulddo a full update of the sprite by removing it and redrawing it, orjust do an incremental update by redrawing the sprite to the screen.Obviously, when drawing digital ink, the ink only increases, so if wedo an incremental update we will get the correct screen content.
(It’sworth noting that there is one circumstance in which WSERV will do afull update even if an incremental one is requested. If there is no maskbitmap, then the way the bitmap is drawn to the screen is determinedby the iDrawMode member of the TSpriteMember class. If this is notEDrawModePEN, then WSERV will always do a full update.)11.7.5 Sending eventsThere are two situations in which the anim will need to send (or create)an event. The first is when the ink is converted to a character. The secondis when the timer goes off, without the user having moved the pen fromthe position where it was clicked down. Since the down event itself wasconsumed, the anim will need to resend it so that the client code can actupon it.
This is done in the following code:void CHandWritingAnim::CharacterFinished(){iState=EHwStateInactive;iLastGeneratedCharacter=iPointStore->GetChar();TKeyEvent keyEvent;keyEvent.iCode=iLastGeneratedCharacter;keyEvent.iScanCode=iLastGeneratedCharacter;A SIMPLE HANDWRITING ANIMATION DLL453keyEvent.iModifiers=0;keyEvent.iRepeats=0;iFunctions->PostKeyEvent(keyEvent);iPointStore->ClearPoints();iSpriteFunctions->Activate(EFalse);ClearSprite();}void CHandWritingAnim::SendEatenDownEvent(){TRawEvent rawEvent;rawEvent.Set(TRawEvent::EButton1Down,iCurrentDrawPoint.iX,iCurrentDrawPoint.iY);iFunctions->PostRawEvent(rawEvent);iState=EHwStateInactive;}There are two functions that allow an anim to send events and they areboth illustrated in the previous code and defined in MAnimGeneralFunctions.
This is one of the classes that allows anims to call functionson WSERV:void PostRawEvent(const TRawEvent& aRawEvent) const;void PostKeyEvent(const TKeyEvent& aRawEvent) const;PostRawEvent() is used by an anim to send the down event. It allowsthe sending of any raw event (that is, one of the set of events that thekernel sends to WSERV) into WSERV for processing. It’s worth noting thatthe first thing that WSERV will do with the event is to pass it back to anyanims that are receiving events – so it would be easy for code to create aninfinite loop with bad use of this function! You can send most key eventsusing PostRawEvent(), but you would have to send a key up event,a key down event and in some cases up and down events for modifierkeys too.
This is the reason for the existence of the second function,PostKeyEvent(), which allows the sending of an EEventKey event.11.7.6Client-side code – constructionThe client has to create both the anim and the sprite. A class, CHandWriting, is used to own and manage both of these. I have written thisclass to allow it to be included into any other project that wants to ownthe handwriting anim:class CHandWriting : public CBase{public:CHandWriting(RWsSession& aSession);void ConstructL(TSize aScreenSize, RWindowGroup& aGroup,TBool aUseSeparateMask);454THE WINDOW SERVER∼CHandWriting();void SetMaskL(TBool aUseSeparateMask);void ToggleStatus();private:void CreateSpriteL(TSize aScreenSize, RWindowGroup& aGroup,TBool aUseSeparateMask);void LoadDllL();void FillInSpriteMember(TSpriteMember& aMember);private:RWsSession& iSession;RAnimDll iAnimDll;RHandWritingAnim iAnim;RWsSprite iSprite;CFbsBitmap *iBitmap;CFbsBitmap *iMaskBitmap;TBool iActive;};The sprite has to be created first as this has to be passed to the functionthat constructs the anim.
We do this using the following two routines:void CHandWriting::CreateSpriteL(TSize aScreenSize,RWindowGroup& aGroup,TBool aUseSeparateMask){TInt color,gray; //Unused variablesTDisplayMode mode=iSession .GetDefModeMaxNumColors(color,gray);iBitmap=new(ELeave) CFbsBitmap();User::LeaveIfError(iBitmap->Create(aScreenSize,mode));TSpriteMember member;member.iMaskBitmap=iBitmap;if (aUseSeparateMask){iMaskBitmap=new(ELeave) CFbsBitmap();User::LeaveIfError(iMaskBitmap->Create(aScreenSize,mode));member.iMaskBitmap=iMaskBitmap;}User::LeaveIfError(iSprite.Construct(aGroup,TPoint(),ESpriteNoChildClip|ESpriteNoShadows));FillInSpriteMember(member);iSprite.AppendMember(member);}void CHandWriting::FillInSpriteMember(TSpriteMember& aMember){aMember.iBitmap=iBitmap;aMember.iInvertMask=ETrue;//Must be invertedaMember.iDrawMode=CGraphicsContext::EDrawModePEN;//Ignored when using maskaMember.iOffset=TPoint();//Must be 0,0aMember.iInterval=0;//Not used as only one TSpriteMember in sprite}We construct the sprite by calling iSprite.Construct() in the thirdline from the end of the first routine.