diff -r 000000000000 -r d54f32e146dd tactilefeedback/tactilearearegistry/src/tactilearearegistry.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tactilefeedback/tactilearearegistry/src/tactilearearegistry.cpp Thu Dec 17 08:53:38 2009 +0200 @@ -0,0 +1,483 @@ +/* +* Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "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: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: The class to be used from server side for accessing registry. +* Hit testing is done using this API. +* Part of: Tactile Feedback. +* +*/ + + +#include +#include "tactilearearegistry.h" +#include "OstTraceDefinitions.h" +#ifdef OST_TRACE_COMPILER_IN_USE +#include "tactilearearegistryTraces.h" +#endif + +// ======== MEMBER FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +CTactileAreaRegistry::TTactilePenDownEvent::TTactilePenDownEvent(): + iWindowGroupId( -1 ), iWindowHandle( 0 ) + { + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +CTactileAreaRegistry::TTactilePenDownEvent::TTactilePenDownEvent( + TInt aWindowGroupId, + TUint32 aWindowHandle, + TRect aFeedbackArea ): + iWindowGroupId( aWindowGroupId ), + iWindowHandle( aWindowHandle ), + iFeedbackArea( aFeedbackArea ) + { + } + +// --------------------------------------------------------------------------- +// +// --------------------------------------------------------------------------- +// +void CTactileAreaRegistry::TTactilePenDownEvent::Reset() + { + iWindowGroupId = -1 ; + iWindowHandle = 0; + } + +// --------------------------------------------------------------------------- +// Constructor. +// --------------------------------------------------------------------------- +// +CTactileAreaRegistry::CTactileAreaRegistry() + { + } + +// --------------------------------------------------------------------------- +// 2-phased constructor. +// --------------------------------------------------------------------------- +// +EXPORT_C CTactileAreaRegistry* CTactileAreaRegistry::NewL() + { + CTactileAreaRegistry* self = new ( ELeave ) CTactileAreaRegistry; + + // We don't need ConstructL in this class for the moment, so we + // can just return the created instance right away. + return self; + } + +// --------------------------------------------------------------------------- +// Destructor. +// --------------------------------------------------------------------------- +// +CTactileAreaRegistry::~CTactileAreaRegistry() + { + iChunkArray.Close(); + iWgArray.Close(); + iTactileSemaphore.Close(); + } + + +// --------------------------------------------------------------------------- +// #1 Find the shared chunk created by client +// #2 Open handle to the found chunk +// #3 Store window group id and connection handle with the chunk, +// and add chunk to array +// #4 Open global semaphore in case it is not yet open +// --------------------------------------------------------------------------- +// +EXPORT_C void CTactileAreaRegistry::HandleConnectL( + const TTactileFeedbackConnectData& aData ) + { + TRACE3("CTactileAreaRegistry::HandleConnectL - wgId = %d, Chunk = %S", + aData.iWindowGroupId, + &aData.iChunkName ); + + // #1 + TFindChunk findChunk( aData.iChunkName ); + + TFullName chunkFullName; + + TInt err = findChunk.Next( chunkFullName ); + + User::LeaveIfError( err ); + + // #2 + TTactileAreaChunk newChunk; + + err = newChunk.iChunk.OpenGlobal( chunkFullName, ETrue ); + + User::LeaveIfError( err ); + + CleanupClosePushL( newChunk.iChunk ); + + // #3 + newChunk.iWindowGroupId = aData.iWindowGroupId; + + newChunk.iConnectionHandle = + ConnectionHandleByWgId( aData.iWindowGroupId ); + + iChunkArray.AppendL( newChunk ); + + CleanupStack::Pop(); // newChunk.iChunk + + // #4 Open global semaphore in case it is not yet open + if ( !iTactileSemaphore.Handle() ) + { + err = iTactileSemaphore.OpenGlobal( KTouchFeedbackSemaphore ); + User::LeaveIfError( err ); + } + } + + +// --------------------------------------------------------------------------- +// We handle diconnect by finding client's chunk, closing it and then +// removing the entry from chunk array. +// +// #1 Find the correct entry +// #2 Close chunk +// #3 Remove entry from chunk array +// --------------------------------------------------------------------------- +// +EXPORT_C void CTactileAreaRegistry::HandleDisconnect( + const TTactileFeedbackDisconnectData& aData ) + { + // #1 + for ( TInt i=0; i < iChunkArray.Count(); i++ ) + { + if ( iChunkArray[i].iWindowGroupId == aData.iWindowGroupId ) + { + // #2 + iChunkArray[i].iChunk.Close(); + + // #3 + iChunkArray.Remove( i ); + break; + } + } + } + + +// --------------------------------------------------------------------------- +// Here we just store information (e.g. connection handle) about the +// newly created window group. +// +// Notice that we ignore possible error on purpose when adding new item to +// the array. Firstly a failure should be almost impossible, and secondly +// it would only cause feedback to be disabled for the newly created +// window group. +// --------------------------------------------------------------------------- +// +EXPORT_C void CTactileAreaRegistry::HandleWindowGroupCreated( + TInt aIdentifier, TUint aConnectionHandle ) + { + TTactileWgroupItem newItem; + + newItem.iWindowGroupId = aIdentifier; + newItem.iConnectionHandle = aConnectionHandle; + + iWgArray.Append( newItem ); + } + + +// --------------------------------------------------------------------------- +// When window group is closed, we remove all information about it from +// the array (we don't destroy chunks because it has been done based +// on the disconnect request already). +// --------------------------------------------------------------------------- +// +EXPORT_C void CTactileAreaRegistry::HandleWindowGroupClosed( + TInt aIdentifier ) + { + // Loop down so that deleting of items won't mix up the array + for ( TInt i = iWgArray.Count()-1; i >= 0; i-- ) + { + if ( iWgArray[i].iWindowGroupId == aIdentifier ) + { + iWgArray.Remove( i ); + } + } + } + + +// --------------------------------------------------------------------------- +// This is the implementation of hit testing. +// +// It is assumed that this function is not called unless there is at least +// one connected application, and unless iTactileSemaphore has been opened +// succesfully. +// +// #1 Check that we have valid handle to global semaphore. +// #2 Find correct chunk based on window group id. +// #3 Do actual hit testing in a separate function. +// #4 Return feedback type based on hit testing results. +// --------------------------------------------------------------------------- +// +EXPORT_C TTouchLogicalFeedback CTactileAreaRegistry::HitTestPointerEvent( + const TPointerEvent& aPointerEvent, + TInt aWgIdentifier, + TUint32 aWindowHandle ) + { + TTouchLogicalFeedback feedback(ETouchFeedbackNone); + + OstTrace0( TACTILE_PERFORMANCE, TACTILE_REGISTRY_HIT_TEST_1, "e_TACTILE_REGISTRY_HIT_TEST 1"); + + // #1 + if ( iTactileSemaphore.Handle() && + ( aPointerEvent.iType == TPointerEvent::EButton1Down || + aPointerEvent.iType == TPointerEvent::EButton1Up ) ) + { + // We keep performance trace here instead of beginning of function + // so that drag events won't confuse performance measurements. + + // #2 + TInt chunkIndex = ChunkIndexByWindowGroupId( aWgIdentifier ); + + // #3 If we found the window group where pointer event is going to land, + // then search for correct window (and area) in its shared chunk. + if ( chunkIndex >= 0 && chunkIndex < iChunkArray.Count() ) + { + feedback = HitTestChunk( + iChunkArray[chunkIndex].iChunk, + aPointerEvent, + aWgIdentifier, + aWindowHandle ); + } + } + + OstTrace0( TACTILE_PERFORMANCE, TACTILE_REGISTRY_HIT_TEST_0, "e_TACTILE_REGISTRY_HIT_TEST 0"); + + // #4 + return feedback; + } + +// --------------------------------------------------------------------------- +// In this function we do hit testing for one chunk. +// +// There is currently support for feedback on both +// pen down and up -events. +// +// #1 Reset last pen down information in case this was a pen down -event. +// #2 Call wait on global semaphore for mutual exlusion of shared memory. +// #3 Start from the beginning of chunk, and read number of windows first. +// #4 Go through all windows in a loop +// --------------------------------------------------------------------------- +// +TTouchLogicalFeedback CTactileAreaRegistry::HitTestChunk( + RChunk& aChunk, + const TPointerEvent& aPointerEvent, + TInt aWgIdentifier, + TUint32 aWindowHandle ) + { + // #1 + if ( aPointerEvent.iType == TPointerEvent::EButton1Down ) + { + iLastPenDown.Reset(); + } + + TTouchLogicalFeedback feedback = ETouchFeedbackNone; + + // #2 Protect shared memory chunks so that nobody can modify + // them in the middle of hit testing. + iTactileSemaphore.Wait(); + + // #3 + TInt* base = (TInt*) aChunk.Base(); + + TInt windowCount = *base; + base++; + + TBool windowFound(EFalse); + + // #4 Iterate though windows + for ( TInt i=0; i < windowCount && !windowFound; i++ ) + { + // Read handle identifier of this window + TInt wsHandle = *base; + base++; + // Read number of areas registered to this window + TInt areaCount = *base; + base++; + // Read offset to the area data of this window + TInt offset = *base; + base++; + + // Check if this window is the one where pointer event hit. + if ( wsHandle == static_cast( aWindowHandle ) ) + { + // Set pointer to the first area belonging to this window. + TFeedbackChunkAreaEntry* entryPtr = + reinterpret_cast( aChunk.Base() + offset ); + + TBool matchFound = EFalse; + + // Go through all areas and test each one against the pointer + // event as long as a match is found. + for ( TInt j = 0; j < areaCount && !matchFound; j++ ) + { + matchFound = HitTestRegistryEntry( aPointerEvent, + *entryPtr, + aWgIdentifier, + aWindowHandle, + feedback ); + + entryPtr++; + } + + // No need to continue in the loop, + // because window found already + windowFound = ETrue; + } + } + + // Release the semaphore so that applications can do updates to + // registry again. + iTactileSemaphore.Signal(); + + return feedback; + } + + +// --------------------------------------------------------------------------- +// Here we analyse the pointer event type against feedback area entry, that +// is located in same window where the pointer event hit. +// +// This functionality is encapsulated into separate function mainly +// because this is the only part of the implementation that needs +// to be modified in case we'll have different kinds of down- and up event +// combinations (there could be different types for e.g. in situations were +// up event is / is not required to match down event for it to trigger +// feedback). +// +// Here we also record details of pen down events (that trigger feedback), so +// that we can check on pen up event in case it matches the same feedback +// area where down -event landed. +// +// #1 First see if pointer event even hit the area +// +// #2 Currently pen down event always generates feedback +// +// #3 We trigger feedback on pen up event only if it matches the same +// feedback area, where corresponding down event hit. +// +// --------------------------------------------------------------------------- +// +TBool CTactileAreaRegistry::HitTestRegistryEntry( + const TPointerEvent& aPointerEvent, + const TFeedbackChunkAreaEntry& aEntry, + TInt aWgIdentifier, + TUint32 aWindowHandle, + TTouchLogicalFeedback& aFeedback ) + { + TBool matchFound = EFalse; + + // #1 + if ( aEntry.iRect.Contains( aPointerEvent.iPosition) ) + { + TInt enablers = aEntry.iFeedbackType & ( KTactileVibraBitDown | + KTactileAudioBitDown | + KTactileVibraBitUp | + KTactileAudioBitUp ); + TInt feedbackDown = aEntry.iFeedbackType & 0x3FF; // the first 10 bits + TInt feedbackUp = aEntry.iFeedbackType & (0x3FF << 10); // next 10 bits + feedbackUp = feedbackUp >> 10; + // #2 Pointer down on the area always triggers feedback + if ( ( aPointerEvent.iType == TPointerEvent::EButton1Down )) + { + matchFound = ETrue; + aFeedback = static_cast( feedbackDown ); + iLastPenDown = + TTactilePenDownEvent( + aWgIdentifier, aWindowHandle, aEntry.iRect ); + } + // #3 + else if ( ( aPointerEvent.iType == TPointerEvent::EButton1Up )) + { + if ( iLastPenDown.iWindowGroupId == aWgIdentifier && + iLastPenDown.iWindowHandle == aWindowHandle && + iLastPenDown.iFeedbackArea == aEntry.iRect ) + { + matchFound = ETrue; + aFeedback = static_cast( feedbackUp ); + + // Can only match agains same pen down event once + iLastPenDown.Reset(); + } + } + if ( matchFound ) + { + aFeedback = static_cast(aFeedback | enablers); + } + } + return matchFound; + } + + +// --------------------------------------------------------------------------- +// Find the chunk of that application, where pointer event hit. +// +// #1 First determine the window server client connection handle (we use +// this to indentify chunk instead of window group id, because this way +// resolving works the same way also for additional window groups) +// +// #2 Go though the chunk array for finding out the correct one. +// --------------------------------------------------------------------------- +// +TInt CTactileAreaRegistry::ChunkIndexByWindowGroupId( TInt aWgIdentifier ) const + { + TInt chunkIndex = KErrNotFound; + + // #1 + TUint connectionHandle = ConnectionHandleByWgId( aWgIdentifier ); + + // #2 Iterate though chunks to find the correct one + for ( TInt chunk = 0; chunk < iChunkArray.Count() && chunkIndex == KErrNotFound; chunk++ ) + { + if ( iChunkArray[chunk].iConnectionHandle == connectionHandle ) + { + chunkIndex = chunk; + } + } + + return chunkIndex; + } + + +// --------------------------------------------------------------------------- +// Here we scan through the window group array, and return the corresponding +// connection handle in case a match is found. +// --------------------------------------------------------------------------- +// +TUint CTactileAreaRegistry::ConnectionHandleByWgId( TInt aWgIdentifier ) const + { + TUint connectionHandle = 0; + + for ( TInt i=0; i < iWgArray.Count() && !connectionHandle; i++ ) + { + if ( iWgArray[i].iWindowGroupId == aWgIdentifier ) + { + connectionHandle = iWgArray[i].iConnectionHandle; + } + } + + return connectionHandle; + } + + + +