tactilefeedback/tactileclickplugin/src/tactileclickplugin.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 08:53:38 +0200
changeset 0 d54f32e146dd
child 2 a1cbe1c1d62f
permissions -rw-r--r--
Revision: 200947 Kit: 200951

/*
* 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 click maker plugin, which
*               handles key events at window server process.
* Part of:      Tactile Feedback.
*
*/

#include <e32std.h>
#include <tactilearearegistry.h>
#include <tactilefeedbacktrace.h>

#include "tactileclickplugin.h"
#include "OstTraceDefinitions.h"
#ifdef OST_TRACE_COMPILER_IN_USE
#include "tactileclickpluginTraces.h"
#endif

// ======== MEMBER FUNCTIONS ========

CTactileClickPlugin::CTactileClickPlugin()
    {
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CTactileClickPlugin::ConstructL()
    {
    TRACE( "CTactileClickPlugin::ConstructL - Begin" );
    
    // Notice that we intentionally don't connect to feedback server here. 
    // This is  because some players may try to connect to such servers, 
    // which don't exist yet when click plugin is loaded.
    iAreaRegistry = CTactileAreaRegistry::NewL();
    
    // We need to store ourselves to thead local storage, so that
    // Anim Dll plugins can access the feedback functionality
    Dll::SetTls( this );

    TRACE( "CTactileClickPlugin::ConstructL - End" );
    }

// ---------------------------------------------------------------------------
// We really have to trap ConstructL, because construction of click maker 
// plug-in must not fail (otherwise WSERV will panic, resulting in KERN 4 and
// re-boot of whole device).
// ---------------------------------------------------------------------------
//
CTactileClickPlugin* CTactileClickPlugin::NewL()
    {
    CTactileClickPlugin* self = new( ELeave ) CTactileClickPlugin;
    TRAP_IGNORE( self->ConstructL() );
    return self;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
CTactileClickPlugin::~CTactileClickPlugin()
    {
    delete iAreaRegistry;
    iFeedback.Close();
    Dll::SetTls( NULL );
    }

// ---------------------------------------------------------------------------
// From class CClickMaker.
// No implementation needed
// ---------------------------------------------------------------------------
//
void CTactileClickPlugin::KeyEvent( 
    TEventCode /*aType*/, const TKeyEvent& /*aEvent*/ )
    {    
    }
    
// ---------------------------------------------------------------------------
// From class CClickMaker.
// No implementation needed
// ---------------------------------------------------------------------------
//
void CTactileClickPlugin::PointerEvent( const TPointerEvent& /*aEvent*/ )
    {
    }

// ---------------------------------------------------------------------------
// From class CClickMaker.
//
// Notice that we really have to check iFeedback handle and iAreaRegistry 
// pointer here, because click maker construction must not fail 
// (otherwise WSERV will panic) 
//
// We handle three types of events here:
// 
// 1. Pointer events coming from window server: For these we do hit testing,
//    and use RTactileFeedback to play feedback accordingly.
//
// 2. Window group open events: These ones we pass to area registry for
//    bookkeeping.
//
// 3. Window group close events: These ones we also pass to area registry, so
//    that it can update its bookkeeping.
// ---------------------------------------------------------------------------
//
void CTactileClickPlugin::OtherEvent( TInt aType, TAny* aParam )
    {
    // 1. Pointer events 
    if ( aParam && aType == EEventPointer && iFeedback.Handle() && iAreaRegistry )
        {
        TPointerEventData* p = static_cast<TPointerEventData*> ( aParam );
                 
        // Extract pointer event from the data given by wserv               
        TPointerEvent pointerEvent = p->iPointerEvent;
        
        // Handle button down and button up events.
        if ( pointerEvent.iType == TPointerEvent::EButton1Down ||
             pointerEvent.iType == TPointerEvent::EButton1Up )
            {
            OstTrace0( TACTILE_PERFORMANCE, TACTILE_CLICK_PLUGIN_PEN_EVENT_1, "e_TACTILE_CLICK_PLUGIN_PEN_EVENT 1");
            
            // Adjust position so that it is window -relative
            pointerEvent.iPosition = ( p->iCurrentPos - p->iWindowOrigin );
            
            TInt feedback = 
                iAreaRegistry->HitTestPointerEvent( 
                    pointerEvent, 
                    p->iWindowGroupId,
                    p->iClientHandle );  
                
            // There is extra vibra- and audio information in top two bits of
            // feedback type.    
            TBool playVibra( EFalse );
            TBool playAudio( EFalse );
            
            if ( pointerEvent.iType == TPointerEvent::EButton1Down )
                {
                playVibra  = ( ( feedback & KTactileVibraBitDown ) != 0);
                playAudio  = ( ( feedback & KTactileAudioBitDown ) != 0);
                }
            else
                {
                playVibra  = ( ( feedback & KTactileVibraBitUp ) != 0);
                playAudio  = ( ( feedback & KTactileAudioBitUp ) != 0);
                }
                        
            // Clear upper two bytes completely
            feedback &= 0xffff;
             
            if ( feedback != ETouchFeedbackNone )    
                {
                iFeedback.PlayFeedback( 
                            static_cast<TTouchLogicalFeedback>( feedback ),
                            playVibra, 
                            playAudio );
                }

            OstTrace0( TACTILE_PERFORMANCE, TACTILE_CLICK_PLUGIN_PEN_EVENT_0, "e_TACTILE_CLICK_PLUGIN_PEN_EVENT 0");
            }
        }
    // 2. Window group open events    
    else if ( aType == EEventGroupWindowOpen && iAreaRegistry )
        {
        TGroupWindowOpenData* data = static_cast<TGroupWindowOpenData*>( aParam );
        
        iAreaRegistry->HandleWindowGroupCreated( 
            data->iIdentifier,
            data->iClient );
        }
    // 3. Window group close events.    
    else if ( aType == EEventGroupWindowClose && iAreaRegistry )
        {
        TInt identifier = reinterpret_cast<TInt>( aParam ); 
        
        iAreaRegistry->HandleWindowGroupClosed( 
            identifier );
        }
    }

// ---------------------------------------------------------------------------
// From class CClickMaker.
// This is run when client calls RSoundPlugin::CommandReply -function.
//
// Handling of individual commands has been moved to dedicated functions,
// because data handling requires some effort and relively complex code.
// This is due to the mistake in CClickMaker API design, as the data is
// not delivered in the same format as it was passed to RSoundPlugin on
// client side (client gives data in descriptor, but here the descriptor
// needs to be re-built from TAny* pointer, and in addition the lenght
// of data needs to be known based on command id).
//
// Leaving here causes RSoundPlugin::CommandReply to return with an error
// code on client side. It is thus important that we leave in case 
// command ETactileOpCodeConnect fails, because that way client knows
// that feedback won't work and does not attempt to do updates in vain.
// ---------------------------------------------------------------------------
//
TInt CTactileClickPlugin::CommandReplyL( TInt aOpcode, TAny* aArgs )
    {
    TRACE2( "CTactileClickPlugin::OtherEvent - Begin, opCode = %d", aOpcode );
    
    TInt errCode = KErrNone;
    
    switch ( aOpcode )
        {
        case ETactileOpCodeConnect:
            HandleConnectL( aArgs );
            break;
        case ETactileOpCodeDisconnect:
            HandleDisconnect( aArgs );
            break;
        case ETactileOpCodeImmediateFeedback:
            errCode = HandleImmediateFeedback( aArgs );
            break;
        default:
            break;
        }
       
    TRACE( "CTactileClickPlugin::OtherEvent - End" );    
    
    return errCode;    
    };

// ---------------------------------------------------------------------------
// From class MTactileFeedbackServer
//
// This function handles instant feedback requests originating inside
// window server (i.e. From Anim Dll plugins).
// ---------------------------------------------------------------------------
//
void CTactileClickPlugin::InstantFeedback( TTouchLogicalFeedback aType )
    {
    TRACE( "CTactileClickPlugin::InstantFeedback - Begin" );    
    
    if ( iFeedback.Handle() )
        {
        OstTrace0( TACTILE_PERFORMANCE, TACTILE_CLICK_PLUGIN_INSTANT_FEEDBACK_SERVER_API_1, "e_TACTILE_CLICK_PLUGIN_INSTANT_FEEDBACK_SERVER_API 1");
        
        iFeedback.PlayFeedback( aType, ETrue, ETrue );
        
        OstTrace0( TACTILE_PERFORMANCE, TACTILE_CLICK_PLUGIN_INSTANT_FEEDBACK_SERVER_API_0, "e_TACTILE_CLICK_PLUGIN_INSTANT_FEEDBACK_SERVER_API 0");
        }
    }

// ---------------------------------------------------------------------------
// Handling of connection requests from clients.
//
// #1 Try to connect to Tactile Feedback server in case it is not yet created,
//    and creation has already been attempted (this is an optimization in case
//    there is something wrong in the device so that construction cannot 
//    succeed).
// 
// #2 Convert the data and pass connect .command to Area Registry
//
// #3 In case area registry or server connection does not exist, then leave 
//    so that client knows that feedback is not in use.
// ---------------------------------------------------------------------------
//
void CTactileClickPlugin::HandleConnectL( TAny* aArgs )
    {
    // #1 Try to connct to RTactileFeedback first (if not yet connected)
    if ( !iFeedback.Handle() )
        {
        // Try to connect to Tactile Feedback server now when first client 
        // has connected.
        User::LeaveIfError( iFeedback.Connect() );
        }
    
    // #2
    if ( aArgs && iAreaRegistry && iFeedback.Handle() )
        {
        TInt* tmpInt = reinterpret_cast<TInt*>( aArgs );

        TInt dataSize = sizeof( TTactileFeedbackConnectData );
        
        TPtrC8 tmpConnectData( reinterpret_cast<TUint8*>( tmpInt ), dataSize );
        
        TTactileFeedbackConnectData clientData;
        
        TPckg<TTactileFeedbackConnectData> dataBuf (clientData);
        
        dataBuf.Copy( tmpConnectData );
        
        iAreaRegistry->HandleConnectL( clientData );
        }
    else
        {
        // #3 Fail connection if everything is not in place. This way client
        // will not waste effort doing updates in vain.
        User::Leave( KErrGeneral );
        }
    }
        
// ---------------------------------------------------------------------------
// Handle disconnection requests from clients.
//
// Here we just convert the data and pass command to Area Registry
// ---------------------------------------------------------------------------
//
void CTactileClickPlugin::HandleDisconnect( TAny* aArgs )
    {  
    if ( aArgs && iAreaRegistry )
        {
        TInt* tmpInt = reinterpret_cast<TInt*>( aArgs );

        TInt dataSize = sizeof( TTactileFeedbackDisconnectData );
        
        TPtrC8 tmpDisconnectData( reinterpret_cast<TUint8*>( tmpInt ), dataSize );
        
        TTactileFeedbackDisconnectData clientData;
        
        TPckg<TTactileFeedbackDisconnectData> dataBuf (clientData);
        
        dataBuf.Copy( tmpDisconnectData );
        
        iAreaRegistry->HandleDisconnect( clientData );
        }
    }

// ---------------------------------------------------------------------------
// Handling of immediate feedback, that originates from client application
// (See InstantFeedback function for handling of feedback that originates
//  from inside window server)
//
// We don't need to do any state checks (call in progress) etc. here, 
// because Feedback Server will do that anyway. Notice that we rely
// on the client API so that it does not send direct feedback in case client
// application has disabled feedback from itself.
// ---------------------------------------------------------------------------
//
TInt CTactileClickPlugin::HandleImmediateFeedback( TAny* aArgs )
    {
    if ( aArgs && iFeedback.Handle() )
        {
        TTactileFeedbackImmediateData* data = 
            reinterpret_cast<TTactileFeedbackImmediateData*>( aArgs );
        
        OstTrace0( TACTILE_PERFORMANCE, TACTILE_CLICK_PLUGIN_INSTANT_FEEDBACK_CLIENT_API_1, "e_TACTILE_CLICK_PLUGIN_INSTANT_FEEDBACK_CLIENT_API 1");
        
       iFeedback.PlayFeedback( data->iFeedbackType, 
                                      data->iPlayVibra, 
                                      data->iPlayAudio );  

        OstTrace0( TACTILE_PERFORMANCE, TACTILE_CLICK_PLUGIN_INSTANT_FEEDBACK_CLIENT_API_0, "e_TACTILE_CLICK_PLUGIN_INSTANT_FEEDBACK_CLIENT_API 0");
        }
    
    return KErrNone;
    }

// ======== GLOBAL FUNCTIONS ========

// ---------------------------------------------------------------------------
// Function with this signature needs to be the first exported function
// in click maker plugin DLLs.
//
// Constructs and returns an instance to tactile click maker plugin.
// ---------------------------------------------------------------------------
//
EXPORT_C CClickMaker* CreateClickMakerL()
    {
    TRACE( "CreateClickMakerL" );    
    return CTactileClickPlugin::NewL();
    }