tactilefeedback/tactilearearegistry/src/tactilearearegistry.cpp
changeset 0 d54f32e146dd
child 13 a4df7952b706
child 22 4838b44af342
--- /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;
+    }
+   
+    
+    
+