tactilefeedback/tactilearearegistry/src/tactilearearegistry.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:23:25 +0100
branchRCL_3
changeset 37 09b094b73eb8
parent 36 39d4c97df8cb
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201033 Kit: 201035

/*
* 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
    iTactileSemaphore.Wait();
    
    for ( TInt i=0; i < iChunkArray.Count(); i++ )
        {
        if ( iChunkArray[i].iWindowGroupId == aData.iWindowGroupId )
            {
            // #2
            iChunkArray[i].iChunk.Close();
            
            // #3
            iChunkArray.Remove( i );            
            break;
            }
        }  
    iTactileSemaphore.Signal();
    }
    

// ---------------------------------------------------------------------------
// 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;
    }