--- /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 <tactilefeedbacktrace.h>
+#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<TInt>( aWindowHandle ) )
+ {
+ // Set pointer to the first area belonging to this window.
+ TFeedbackChunkAreaEntry* entryPtr =
+ reinterpret_cast<TFeedbackChunkAreaEntry*>( 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<TTouchLogicalFeedback>( 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<TTouchLogicalFeedback>( feedbackUp );
+
+ // Can only match agains same pen down event once
+ iLastPenDown.Reset();
+ }
+ }
+ if ( matchFound )
+ {
+ aFeedback = static_cast<TTouchLogicalFeedback>(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;
+ }
+
+
+
+