2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "ecore-indicator-impl.h"
25 #include <Ecore_Wayland.h>
30 #include <sys/types.h>
36 #include <dali/public-api/images/native-image.h>
37 #include <dali/public-api/events/touch-data.h>
38 #include <dali/public-api/common/stage.h>
39 #include <dali/public-api/images/buffer-image.h>
41 #include <dali/integration-api/debug.h>
44 #include <adaptor-impl.h>
45 #include <accessibility-adaptor-impl.h>
46 #include <native-image-source.h>
50 #if defined(DEBUG_ENABLED)
51 #define STATE_DEBUG_STRING(state) (state==DISCONNECTED?"DISCONNECTED":state==CONNECTED?"CONNECTED":"UNKNOWN")
57 const float SLIDING_ANIMATION_DURATION( 0.2f ); // 200 milli seconds
58 const float AUTO_INDICATOR_STAY_DURATION(3.0f); // 3 seconds
59 const float SHOWING_DISTANCE_HEIGHT_RATE(0.34f); // 20 pixels
67 const int NUM_GRADIENT_INTERVALS(5); // Number of gradient intervals
68 const float GRADIENT_ALPHA[NUM_GRADIENT_INTERVALS+1] = { 0.6f, 0.38f, 0.20f, 0.08f, 0.0f, 0.0f };
70 #define MAKE_SHADER(A)#A
72 const char* BACKGROUND_VERTEX_SHADER = MAKE_SHADER(
73 attribute mediump vec2 aPosition;
74 attribute mediump float aAlpha;
75 varying mediump float vAlpha;
76 uniform mediump mat4 uMvpMatrix;
77 uniform mediump vec3 uSize;
81 mediump vec4 vertexPosition = vec4( aPosition * uSize.xy, 0.0, 1.0 );
82 vertexPosition = uMvpMatrix * vertexPosition;
85 gl_Position = vertexPosition;
89 const char* BACKGROUND_FRAGMENT_SHADER = MAKE_SHADER(
90 uniform lowp vec4 uColor;
91 varying mediump float vAlpha;
95 gl_FragColor = uColor;
96 gl_FragColor.a *= vAlpha;
100 const char* FOREGROUND_VERTEX_SHADER = DALI_COMPOSE_SHADER(
101 attribute mediump vec2 aPosition;\n
102 varying mediump vec2 vTexCoord;\n
103 uniform mediump mat4 uMvpMatrix;\n
104 uniform mediump vec3 uSize;\n
105 uniform mediump vec4 sTextureRect;\n
109 gl_Position = uMvpMatrix * vec4(aPosition * uSize.xy, 0.0, 1.0);\n
110 vTexCoord = aPosition + vec2(0.5);\n
114 const char* FOREGROUND_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
115 varying mediump vec2 vTexCoord;\n
116 uniform sampler2D sTexture;\n
120 gl_FragColor = texture2D( sTexture, vTexCoord );\n // the foreground does not apply actor color
124 Dali::Geometry CreateQuadGeometry()
126 Dali::Property::Map quadVertexFormat;
127 quadVertexFormat["aPosition"] = Dali::Property::VECTOR2;
128 Dali::PropertyBuffer vertexData = Dali::PropertyBuffer::New( quadVertexFormat );
130 const float halfQuadSize = .5f;
131 struct QuadVertex { Dali::Vector2 position; };
132 QuadVertex quadVertexData[4] = {
133 { Dali::Vector2(-halfQuadSize, -halfQuadSize) },
134 { Dali::Vector2(-halfQuadSize, halfQuadSize) },
135 { Dali::Vector2( halfQuadSize, -halfQuadSize) },
136 { Dali::Vector2( halfQuadSize, halfQuadSize) } };
137 vertexData.SetData(quadVertexData, 4);
139 Dali::Geometry quad = Dali::Geometry::New();
140 quad.AddVertexBuffer( vertexData );
141 quad.SetType( Dali::Geometry::TRIANGLE_STRIP );
145 const float OPAQUE_THRESHOLD(0.99f);
146 const float TRANSPARENT_THRESHOLD(0.05f);
148 // indicator service name
149 const char* INDICATOR_SERVICE_NAME("elm_indicator");
151 // Copied from ecore_evas_extn_engine.h
167 OP_PROFILE_CHANGE_REQUEST,
168 OP_PROFILE_CHANGE_DONE,
186 // Copied from elm_conform.c
188 const int MSG_DOMAIN_CONTROL_INDICATOR( 0x10001 );
189 const int MSG_ID_INDICATOR_REPEAT_EVENT( 0x10002 );
190 const int MSG_ID_INDICATOR_ROTATION( 0x10003 );
191 const int MSG_ID_INDICATOR_OPACITY( 0X1004 );
192 const int MSG_ID_INDICATOR_TYPE( 0X1005 );
193 const int MSG_ID_INDICATOR_START_ANIMATION( 0X10006 );
205 struct IpcIndicatorDataAnimation
211 struct IpcDataEvMouseUp
214 Evas_Button_Flags flags;
216 unsigned int timestamp;
217 Evas_Event_Flags event_flags;
219 IpcDataEvMouseUp(unsigned long timestamp)
221 flags(EVAS_BUTTON_NONE),
223 timestamp(static_cast<unsigned int>(timestamp)),
224 event_flags(EVAS_EVENT_FLAG_NONE)
229 struct IpcDataEvMouseDown
232 Evas_Button_Flags flags;
234 unsigned int timestamp;
235 Evas_Event_Flags event_flags;
237 IpcDataEvMouseDown(unsigned long timestamp)
239 flags(EVAS_BUTTON_NONE),
241 timestamp(static_cast<unsigned int>(timestamp)),
242 event_flags(EVAS_EVENT_FLAG_NONE)
247 struct IpcDataEvMouseMove
250 Evas_Button_Flags flags;
252 unsigned int timestamp;
253 Evas_Event_Flags event_flags;
255 IpcDataEvMouseMove(const Dali::TouchData& touchData, unsigned long timestamp)
256 : x(static_cast<Evas_Coord>(touchData.GetLocalPosition( 0 ).x)),
257 y(static_cast<Evas_Coord>(touchData.GetLocalPosition( 0 ).y)),
258 flags(EVAS_BUTTON_NONE),
260 timestamp(static_cast<unsigned int>(timestamp)),
261 event_flags(EVAS_EVENT_FLAG_NONE)
266 struct IpcDataEvMouseOut
268 unsigned int timestamp;
270 Evas_Event_Flags event_flags;
272 IpcDataEvMouseOut(unsigned long timestamp)
273 : timestamp(static_cast<unsigned int>(timestamp)),
275 event_flags(EVAS_EVENT_FLAG_NONE)
280 } // anonymous namespace
289 #if defined(DEBUG_ENABLED)
290 Debug::Filter* gIndicatorLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_INDICATOR");
293 // Impl to hide EFL implementation.
295 struct Indicator::Impl
297 enum // operation mode
300 INDICATOR_STAY_WITH_DURATION
306 Impl(Indicator* indicator)
307 : mIndicator(indicator),
308 mEcoreEventHandler(NULL)
310 #if defined(DALI_PROFILE_MOBILE)
312 mEcoreEventHandler = ecore_event_handler_add(ECORE_WL_EVENT_INDICATOR_FLICK, EcoreEventIndicator, this);
314 mEcoreEventHandler = ecore_event_handler_add(ECORE_X_EVENT_CLIENT_MESSAGE, EcoreEventClientMessage, this);
316 #endif // WAYLAND && DALI_PROFILE_MOBILE
324 if ( mEcoreEventHandler )
326 ecore_event_handler_del(mEcoreEventHandler);
330 static void SetIndicatorVisibility( void* data, int operation )
332 Indicator::Impl* indicatorImpl((Indicator::Impl*)data);
334 if ( indicatorImpl == NULL || indicatorImpl->mIndicator == NULL)
338 if ( operation == INDICATOR_STAY_WITH_DURATION )
340 // if indicator is not showing, INDICATOR_FLICK_DONE is given
341 if( indicatorImpl->mIndicator->mVisible == Dali::Window::AUTO &&
342 !indicatorImpl->mIndicator->mIsShowing )
344 indicatorImpl->mIndicator->ShowIndicator( AUTO_INDICATOR_STAY_DURATION );
347 else if( operation == INDICATOR_HIDE )
349 if( indicatorImpl->mIndicator->mVisible == Dali::Window::AUTO &&
350 indicatorImpl->mIndicator->mIsShowing )
352 indicatorImpl->mIndicator->ShowIndicator( HIDE_NOW );
356 #if defined(DALI_PROFILE_MOBILE)
359 * Called when the Ecore indicator event is received.
361 static Eina_Bool EcoreEventIndicator( void* data, int type, void* event )
363 SetIndicatorVisibility( data, INDICATOR_STAY_WITH_DURATION );
364 return ECORE_CALLBACK_PASS_ON;
368 * Called when the client messages (i.e. quick panel state) are received.
370 static Eina_Bool EcoreEventClientMessage( void* data, int type, void* event )
372 Ecore_X_Event_Client_Message* clientMessageEvent((Ecore_X_Event_Client_Message*)event);
374 if ( clientMessageEvent != NULL )
376 if (clientMessageEvent->message_type == ECORE_X_ATOM_E_INDICATOR_FLICK_DONE)
378 SetIndicatorVisibility( data, INDICATOR_STAY_WITH_DURATION );
380 else if ( clientMessageEvent->message_type == ECORE_X_ATOM_E_MOVE_QUICKPANEL_STATE )
382 SetIndicatorVisibility( data, INDICATOR_HIDE );
385 return ECORE_CALLBACK_PASS_ON;
388 #endif // WAYLAND && DALI_PROFILE_MOBILE
391 Indicator* mIndicator;
392 Ecore_Event_Handler* mEcoreEventHandler;
395 Indicator::LockFile::LockFile(const std::string filename)
396 : mFilename(filename),
399 mFileDescriptor = open(filename.c_str(), O_RDWR);
400 if( mFileDescriptor == -1 )
404 DALI_LOG_ERROR( "### Cannot open %s for indicator lock ###\n", mFilename.c_str() );
408 Indicator::LockFile::~LockFile()
410 // Closing file descriptor also unlocks file.
411 close( mFileDescriptor );
414 bool Indicator::LockFile::Lock()
416 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
419 if( mFileDescriptor > 0 )
421 struct flock filelock;
423 filelock.l_type = F_RDLCK;
424 filelock.l_whence = SEEK_SET;
425 filelock.l_start = 0;
427 if( fcntl( mFileDescriptor, F_SETLKW, &filelock ) == -1 )
430 DALI_LOG_ERROR( "### Failed to lock with fd : %s ###\n", mFilename.c_str() );
440 DALI_LOG_ERROR( "### Invalid fd ###\n" );
446 void Indicator::LockFile::Unlock()
448 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
450 struct flock filelock;
452 filelock.l_type = F_UNLCK;
453 filelock.l_whence = SEEK_SET;
454 filelock.l_start = 0;
456 if (fcntl(mFileDescriptor, F_SETLKW, &filelock) == -1)
459 DALI_LOG_ERROR( "### Failed to lock with fd : %s ###\n", mFilename.c_str() );
463 bool Indicator::LockFile::RetrieveAndClearErrorStatus()
465 bool error = mErrorThrown;
466 mErrorThrown = false;
470 Indicator::ScopedLock::ScopedLock(LockFile* lockFile)
471 : mLockFile(lockFile),
476 mLocked = mLockFile->Lock();
480 Indicator::ScopedLock::~ScopedLock()
488 bool Indicator::ScopedLock::IsLocked()
493 Indicator::Indicator( Adaptor* adaptor, Dali::Window::WindowOrientation orientation, IndicatorInterface::Observer* observer )
495 mGestureDetected( false ),
497 mOpacityMode( Dali::Window::OPAQUE ),
498 mState( DISCONNECTED ),
500 mServerConnection( NULL ),
501 mObserver( observer ),
502 mOrientation( orientation ),
505 mVisible( Dali::Window::INVISIBLE ),
507 mIsAnimationPlaying( false ),
508 mCurrentSharedFile( 0 ),
509 mSharedBufferType( BUFFER_TYPE_SHM ),
511 mBackgroundVisible( false ),
514 mIndicatorContentActor = Dali::Actor::New();
515 mIndicatorContentActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
516 mIndicatorContentActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
518 // Indicator image handles the touch event including "leave"
519 mIndicatorContentActor.SetLeaveRequired( true );
520 mIndicatorContentActor.TouchSignal().Connect( this, &Indicator::OnTouched );
521 mIndicatorContentActor.SetColor( Color::BLACK );
523 mIndicatorActor = Dali::Actor::New();
524 mIndicatorActor.Add( mIndicatorContentActor );
526 // Event handler to find out flick down gesture
527 mEventActor = Dali::Actor::New();
528 mEventActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
529 mEventActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
530 mIndicatorActor.Add( mEventActor );
532 // Attach pan gesture to find flick down during hiding.
533 // It can prevent the problem that scrollview gets pan gesture even indicator area is touched,
534 // since it consumes the pan gesture in advance.
535 mPanDetector = Dali::PanGestureDetector::New();
536 mPanDetector.DetectedSignal().Connect( this, &Indicator::OnPan );
537 mPanDetector.Attach( mEventActor );
541 // register indicator to accessibility adaptor
542 Dali::AccessibilityAdaptor accessibilityAdaptor = AccessibilityAdaptor::Get();
543 if(accessibilityAdaptor)
545 AccessibilityAdaptor::GetImplementation( accessibilityAdaptor ).SetIndicator( this );
547 // hide the indicator by default
548 mIndicatorActor.SetVisible( false );
550 // create impl to handle ecore event
551 mImpl = new Impl(this);
554 Indicator::~Indicator()
564 mEventActor.TouchSignal().Disconnect( this, &Indicator::OnTouched );
569 void Indicator::SetAdaptor(Adaptor* adaptor)
572 mIndicatorBuffer->SetAdaptor( adaptor );
575 Dali::Actor Indicator::GetActor()
577 return mIndicatorActor;
580 void Indicator::Open( Dali::Window::WindowOrientation orientation )
582 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
584 // Calls from Window should be set up to ensure we are in a
585 // disconnected state before opening a second time.
586 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
588 mOrientation = orientation;
592 // Change background visibility depending on orientation
593 if( mOrientation == Dali::Window::LANDSCAPE || mOrientation == Dali::Window::LANDSCAPE_INVERSE )
595 if( mBackgroundRenderer )
597 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
598 mBackgroundVisible = false;
603 SetOpacityMode( mOpacityMode );
607 void Indicator::Close()
609 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s", STATE_DEBUG_STRING(mState) );
611 if( mState == CONNECTED )
614 if( mObserver != NULL )
616 mObserver->IndicatorClosed( this );
620 Dali::Texture emptyTexture;
621 SetForegroundImage( emptyTexture );
624 void Indicator::SetOpacityMode( Dali::Window::IndicatorBgOpacity mode )
628 Dali::Geometry geometry = CreateBackgroundGeometry();
631 if( mBackgroundRenderer )
633 if( mBackgroundRenderer.GetGeometry() != geometry )
635 mBackgroundRenderer.SetGeometry( geometry );
640 if( !mBackgroundShader )
642 mBackgroundShader = Dali::Shader::New( BACKGROUND_VERTEX_SHADER, BACKGROUND_FRAGMENT_SHADER, Dali::Shader::Hint::OUTPUT_IS_TRANSPARENT );
645 mBackgroundRenderer = Dali::Renderer::New( geometry, mBackgroundShader );
648 if( !mBackgroundVisible )
650 mIndicatorContentActor.AddRenderer( mBackgroundRenderer );
651 mBackgroundVisible = true;
654 else if( mBackgroundRenderer )
656 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
657 mBackgroundVisible = false;
662 void Indicator::SetVisible( Dali::Window::IndicatorVisibleMode visibleMode, bool forceUpdate )
664 if ( visibleMode != mVisible || forceUpdate )
666 // If we were previously hidden, then we should update the image data before we display the indicator
667 if ( mVisible == Dali::Window::INVISIBLE )
669 UpdateImageData( mCurrentSharedFile );
672 if ( visibleMode == Dali::Window::INVISIBLE )
674 if (mServerConnection)
676 mServerConnection->SendEvent( OP_HIDE, NULL, 0 );
681 mIndicatorActor.SetVisible( true );
683 if( mServerConnection )
685 mServerConnection->SendEvent( OP_SHOW, NULL, 0 );
689 mVisible = visibleMode;
692 if( mForegroundRenderer && mForegroundRenderer.GetTextures().GetTexture( 0u ) )
694 if( CheckVisibleState() && mVisible == Dali::Window::AUTO )
697 ShowIndicator( AUTO_INDICATOR_STAY_DURATION /* stay n sec */ );
699 else if( CheckVisibleState() && mVisible == Dali::Window::VISIBLE )
702 ShowIndicator( KEEP_SHOWING );
707 ShowIndicator( HIDE_NOW );
717 bool Indicator::IsConnected()
719 return ( mState == CONNECTED );
722 bool Indicator::SendMessage( int messageDomain, int messageId, const void *data, int size )
726 return mServerConnection->SendEvent( OP_MSG, messageDomain, messageId, data, size );
734 bool Indicator::OnTouched(Dali::Actor indicator, const Dali::TouchData& touchData)
736 if( mServerConnection )
738 // Send touch event to indicator server when indicator is showing
739 if( CheckVisibleState() || mIsShowing )
741 switch( touchData.GetState( 0 ) )
743 case Dali::PointState::DOWN:
745 IpcDataEvMouseMove ipcMove( touchData, touchData.GetTime() );
746 IpcDataEvMouseDown ipcDown( touchData.GetTime() );
747 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
748 mServerConnection->SendEvent( OP_EV_MOUSE_DOWN, &ipcDown, sizeof(ipcDown) );
750 if( mVisible == Dali::Window::AUTO )
752 // Stop hiding indicator
753 ShowIndicator( KEEP_SHOWING );
758 case Dali::PointState::MOTION:
760 IpcDataEvMouseMove ipcMove( touchData, touchData.GetTime() );
761 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
765 case Dali::PointState::UP:
766 case Dali::PointState::INTERRUPTED:
768 IpcDataEvMouseUp ipcUp( touchData.GetTime() );
769 mServerConnection->SendEvent( OP_EV_MOUSE_UP, &ipcUp, sizeof(ipcUp) );
771 if( mVisible == Dali::Window::AUTO )
774 ShowIndicator( 0.5f /* hide after 0.5 sec */ );
779 case Dali::PointState::LEAVE:
781 IpcDataEvMouseMove ipcMove( touchData, touchData.GetTime() );
782 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
783 IpcDataEvMouseUp ipcOut( touchData.GetTime() );
784 mServerConnection->SendEvent( OP_EV_MOUSE_OUT, &ipcOut, sizeof(ipcOut) );
799 bool Indicator::Connect()
801 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
803 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
805 bool connected = false;
807 mServerConnection = new ServerConnection( INDICATOR_SERVICE_NAME, 0, false, this );
808 if( mServerConnection )
810 connected = mServerConnection->IsConnected();
813 delete mServerConnection;
814 mServerConnection = NULL;
820 StartReconnectionTimer();
830 void Indicator::StartReconnectionTimer()
832 if( ! mReconnectTimer )
834 mReconnectTimer = Dali::Timer::New(1000);
835 mConnection.DisconnectAll();
836 mReconnectTimer.TickSignal().Connect( mConnection, &Indicator::OnReconnectTimer );
838 mReconnectTimer.Start();
841 bool Indicator::OnReconnectTimer()
845 if( mState == DISCONNECTED )
856 void Indicator::Disconnect()
858 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
860 mState = DISCONNECTED;
862 delete mServerConnection;
863 mServerConnection = NULL;
865 ClearSharedFileInfo();
868 void Indicator::Resize( int width, int height )
879 if( mImageWidth != width || mImageHeight != height )
882 mImageHeight = height;
884 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
885 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
886 mEventActor.SetSize(mImageWidth, mImageHeight);
891 void Indicator::SetLockFileInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
893 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
895 // epcEvent->ref == w
896 // epcEvent->ref_to == h
897 // epcEvent->response == buffer num
898 // epcEvent->data = lockfile + nul byte
900 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) && (epcEvent->data) &&
901 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
903 int n = epcEvent->response;
905 if( n >= 0 && n < SHARED_FILE_NUMBER )
907 mCurrentSharedFile = n;
909 mSharedFileInfo[n].mImageWidth = epcEvent->ref;
910 mSharedFileInfo[n].mImageHeight = epcEvent->ref_to;
912 mSharedFileInfo[n].mLockFileName.clear();
914 mSharedFileInfo[n].mLockFileName = static_cast< char* >( epcEvent->data );
916 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetLockFileInfo: buffer num = %d, w = %d, h = %d, lock = %s\n",
917 n, mSharedFileInfo[n].mImageWidth, mSharedFileInfo[n].mImageHeight, mSharedFileInfo[n].mLockFileName.c_str() );
922 void Indicator::SetSharedImageInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
924 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
926 // epcEvent->ref == shm id
927 // epcEvent->ref_to == shm num
928 // epcEvent->response == buffer num
929 // epcEvent->data = shm ref string + nul byte
931 if ( (epcEvent->data) &&
932 (epcEvent->size > 0) &&
933 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
935 int n = epcEvent->response;
937 if( n >= 0 && n < SHARED_FILE_NUMBER )
939 mCurrentSharedFile = n;
941 mSharedFileInfo[n].mSharedFileName.clear();
943 mSharedFileInfo[n].mSharedFileName = static_cast< char* >( epcEvent->data );
945 mSharedFileInfo[n].mSharedFileID = epcEvent->ref;
946 mSharedFileInfo[n].mSharedFileNumber = epcEvent->ref_to;
948 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetSharedImageInfo: buffer num %d, shared file = %s, id = %d, num = %d\n",
949 n, mSharedFileInfo[n].mSharedFileName.c_str(), mSharedFileInfo[n].mSharedFileID, mSharedFileInfo[n].mSharedFileNumber );
954 void Indicator::LoadSharedImage( Ecore_Ipc_Event_Server_Data *epcEvent )
956 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
958 // epcEvent->ref == alpha
959 // epcEvent->ref_to == sys
960 // epcEvent->response == buffer num
962 if ( mSharedBufferType != BUFFER_TYPE_SHM )
967 int n = epcEvent->response;
969 if( n >= 0 && n < SHARED_FILE_NUMBER )
971 mCurrentSharedFile = n;
973 delete mSharedFileInfo[n].mSharedFile;
974 mSharedFileInfo[n].mSharedFile = NULL;
976 delete mSharedFileInfo[n].mLock;
977 mSharedFileInfo[n].mLock = NULL;
979 std::stringstream sharedFileID;
980 std::stringstream sharedFileNumber;
982 sharedFileID << mSharedFileInfo[n].mSharedFileID;
983 sharedFileNumber << mSharedFileInfo[n].mSharedFileNumber;
985 std::string sharedFilename = "/" + mSharedFileInfo[n].mSharedFileName + "-" + sharedFileID.str() + "." + sharedFileNumber.str();
987 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "LoadSharedImage: file name = %s\n", sharedFilename.c_str() );
989 mSharedFileInfo[n].mSharedFile = SharedFile::New( sharedFilename.c_str(), mSharedFileInfo[n].mImageWidth * mSharedFileInfo[n].mImageWidth * 4, true );
990 if( mSharedFileInfo[n].mSharedFile != NULL )
992 mSharedFileInfo[n].mLock = new Indicator::LockFile( mSharedFileInfo[n].mLockFileName );
993 if( mSharedFileInfo[n].mLock->RetrieveAndClearErrorStatus() )
995 DALI_LOG_ERROR( "### Indicator error: Cannot open lock file %s ###\n", mSharedFileInfo[n].mLockFileName.c_str() );
1000 CreateNewImage( n );
1006 void Indicator::LoadPixmapImage( Ecore_Ipc_Event_Server_Data *epcEvent )
1008 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1010 // epcEvent->ref == pixmap id
1011 // epcEvent->ref_to == type
1012 // epcEvent->response == buffer num
1014 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) )
1016 mSharedBufferType = (BufferType)(epcEvent->ref_to);
1018 ClearSharedFileInfo();
1020 mPixmap = static_cast<PixmapId>(epcEvent->ref);
1021 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "mPixmap [%x]", mPixmap);
1023 CreateNewPixmapImage();
1028 void Indicator::UpdateTopMargin()
1030 int newMargin = (mVisible == Dali::Window::VISIBLE && mOpacityMode == Dali::Window::OPAQUE) ? mImageHeight : 0;
1031 if (mTopMargin != newMargin)
1033 mTopMargin = newMargin;
1034 mAdaptor->IndicatorSizeChanged( mTopMargin );
1038 void Indicator::UpdateVisibility()
1040 if( CheckVisibleState() )
1042 // set default indicator type (enable the quick panel)
1043 OnIndicatorTypeChanged( INDICATOR_TYPE_1 );
1047 // set default indicator type (disable the quick panel)
1048 OnIndicatorTypeChanged( INDICATOR_TYPE_2 );
1053 mIndicatorContentActor.SetPosition( 0.0f, -mImageHeight, 0.0f );
1056 SetVisible(mVisible, true);
1059 void Indicator::UpdateImageData( int bufferNumber )
1061 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s mVisible: %s", STATE_DEBUG_STRING(mState), mVisible?"T":"F" );
1063 if( mState == CONNECTED && mVisible )
1067 // in case of shm indicator (not pixmap), not sure we can skip it when mIsShowing is false
1068 CopyToBuffer( bufferNumber );
1074 mAdaptor->RequestUpdateOnce();
1080 bool Indicator::CopyToBuffer( int bufferNumber )
1082 bool success = false;
1084 if( mSharedFileInfo[bufferNumber].mLock )
1086 Indicator::ScopedLock scopedLock(mSharedFileInfo[bufferNumber].mLock);
1087 if( mSharedFileInfo[bufferNumber].mLock->RetrieveAndClearErrorStatus() )
1091 else if( scopedLock.IsLocked() )
1093 unsigned char *src = mSharedFileInfo[bufferNumber].mSharedFile->GetAddress();
1094 size_t size = mSharedFileInfo[bufferNumber].mImageWidth * mSharedFileInfo[bufferNumber].mImageHeight * 4;
1096 if( mIndicatorBuffer->UpdatePixels( src, size ) )
1098 mAdaptor->RequestUpdateOnce();
1107 void Indicator::CreateNewPixmapImage()
1109 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mImageWidth, mImageHeight );
1110 Dali::NativeImageSourcePtr nativeImageSource = Dali::NativeImageSource::New( mPixmap );
1112 if( nativeImageSource )
1114 Dali::Texture texture = Dali::Texture::New( *nativeImageSource );
1115 SetForegroundImage( texture );
1116 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
1117 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
1118 mEventActor.SetSize( mImageWidth, mImageHeight );
1123 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1125 if( mObserver != NULL )
1127 mObserver->IndicatorClosed( this );
1129 // Don't do connection in this callback - strange things happen!
1130 StartReconnectionTimer();
1134 void Indicator::CreateNewImage( int bufferNumber )
1136 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight );
1137 mIndicatorBuffer = new IndicatorBuffer( mAdaptor, mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight, Pixel::BGRA8888 );
1138 bool success = false;
1140 if( CopyToBuffer( bufferNumber ) ) // Only create images if we have valid image buffer
1142 Dali::Texture texture = Dali::Texture::New( mIndicatorBuffer->GetNativeImage() );
1145 SetForegroundImage( texture );
1152 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1154 if( mObserver != NULL )
1156 mObserver->IndicatorClosed( this );
1158 // Don't do connection in this callback - strange things happen!
1159 StartReconnectionTimer();
1163 Dali::Geometry Indicator::CreateBackgroundGeometry()
1165 switch( mOpacityMode )
1167 case Dali::Window::TRANSLUCENT:
1168 if( !mTranslucentGeometry )
1170 // Construct 5 interval mesh
1184 struct BackgroundVertex
1190 unsigned int numVertices = 2 * ( NUM_GRADIENT_INTERVALS + 1 );
1191 BackgroundVertex vertices[ numVertices ];
1194 float delta = 1.0f / NUM_GRADIENT_INTERVALS;
1195 BackgroundVertex* currentVertex = vertices;
1196 for( int y = 0; y < NUM_GRADIENT_INTERVALS + 1; ++y, d += delta )
1198 currentVertex->mPosition = Vector2( -0.5f, d );
1199 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1202 currentVertex->mPosition = Vector2( 0.5f, d );
1203 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1208 unsigned int numIndices = 2 * 3 * NUM_GRADIENT_INTERVALS;
1209 unsigned short indices[ numIndices ];
1211 unsigned short* currentIndex = indices;
1212 for( int y = 0; y < NUM_GRADIENT_INTERVALS; ++y )
1214 *currentIndex++ = (2 * y);
1215 *currentIndex++ = (2 * y) + 3;
1216 *currentIndex++ = (2 * y) + 1;
1218 *currentIndex++ = (2 * y);
1219 *currentIndex++ = (2 * y) + 2;
1220 *currentIndex++ = (2 * y) + 3;
1223 Dali::Property::Map vertexFormat;
1224 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1225 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1226 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1227 vertexPropertyBuffer.SetData( vertices, numVertices );
1229 // Create the geometry object
1230 mTranslucentGeometry = Dali::Geometry::New();
1231 mTranslucentGeometry.AddVertexBuffer( vertexPropertyBuffer );
1232 mTranslucentGeometry.SetIndexBuffer( &indices[0], numIndices );
1235 return mTranslucentGeometry;
1236 case Dali::Window::OPAQUE:
1238 if( !mSolidGeometry )
1241 struct BackgroundVertex
1247 BackgroundVertex vertices[ 4 ] = { { Vector2( -0.5f, -0.5f ), 1.0f }, { Vector2( 0.5f, -0.5f ), 1.0f },
1248 { Vector2( -0.5f, 0.5f ), 1.0f }, { Vector2( 0.5f, 0.5f ), 1.0f } };
1251 unsigned short indices[ 6 ] = { 0, 3, 1, 0, 2, 3 };
1253 Dali::Property::Map vertexFormat;
1254 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1255 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1256 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1257 vertexPropertyBuffer.SetData( vertices, 4 );
1260 // Create the geometry object
1261 mSolidGeometry = Dali::Geometry::New();
1262 mSolidGeometry.AddVertexBuffer( vertexPropertyBuffer );
1263 mSolidGeometry.SetIndexBuffer( &indices[0], 6 );
1266 return mSolidGeometry;
1267 case Dali::Window::TRANSPARENT:
1271 return Dali::Geometry();
1274 void Indicator::SetForegroundImage( Dali::Texture texture )
1276 if( !mForegroundRenderer && texture )
1279 Dali::Shader shader = Dali::Shader::New( FOREGROUND_VERTEX_SHADER, FOREGROUND_FRAGMENT_SHADER );
1281 // Create renderer from geometry and material
1282 Dali::Geometry quad = CreateQuadGeometry();
1283 mForegroundRenderer = Dali::Renderer::New( quad, shader );
1284 // Make sure the foreground stays in front of the background
1285 mForegroundRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, 1.f );
1287 // Set blend function
1288 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_RGB, Dali::BlendFactor::ONE );
1289 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_RGB, Dali::BlendFactor::ONE_MINUS_SRC_ALPHA );
1290 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_ALPHA, Dali::BlendFactor::ONE );
1291 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_ALPHA, Dali::BlendFactor::ONE );
1293 // Create a texture-set and add to renderer
1295 Dali::TextureSet textureSet = Dali::TextureSet::New();
1296 textureSet.SetTexture( 0u, texture );
1297 mForegroundRenderer.SetTextures( textureSet );
1299 mIndicatorContentActor.AddRenderer( mForegroundRenderer );
1301 else if( mForegroundRenderer )
1303 Dali::TextureSet textureSet = mForegroundRenderer.GetTextures();
1304 textureSet.SetTexture( 0u, texture );
1307 if( mImageWidth == 0 && mImageHeight == 0 && texture)
1309 Resize( texture.GetWidth(), texture.GetHeight() );
1313 void Indicator::OnIndicatorTypeChanged( Type indicatorType )
1315 if( mObserver != NULL )
1317 mObserver->IndicatorTypeChanged( indicatorType );
1321 void Indicator::DataReceived( void* event )
1323 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1324 Ecore_Ipc_Event_Server_Data *epcEvent = static_cast<Ecore_Ipc_Event_Server_Data *>( event );
1326 switch( epcEvent->minor )
1330 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE\n" );
1333 mAdaptor->RequestUpdateOnce();
1337 case OP_UPDATE_DONE:
1339 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE_DONE [%d]\n", epcEvent->response );
1340 // epcEvent->response == display buffer #
1341 UpdateImageData( epcEvent->response );
1346 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF0\n" );
1347 SetSharedImageInfo( epcEvent );
1352 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF1\n" );
1353 SetLockFileInfo( epcEvent );
1358 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF2\n" );
1359 LoadSharedImage( epcEvent );
1364 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_PIXMAP_REF\n" );
1365 LoadPixmapImage( epcEvent );
1370 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_RESIZE\n" );
1372 if( (epcEvent->data) && (epcEvent->size >= (int)sizeof(IpcDataResize)) )
1374 IpcDataResize *newSize = static_cast<IpcDataResize*>( epcEvent->data );
1375 Resize( newSize->w, newSize->h );
1381 int msgDomain = epcEvent->ref;
1382 int msgId = epcEvent->ref_to;
1384 void *msgData = NULL;
1385 int msgDataSize = 0;
1386 msgData = epcEvent->data;
1387 msgDataSize = epcEvent->size;
1389 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT. msgDomain = %d\n", msgDomain );
1391 if( msgDomain == MSG_DOMAIN_CONTROL_INDICATOR )
1395 case MSG_ID_INDICATOR_TYPE:
1397 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT, INDICATOR_TYPE\n" );
1398 Type* indicatorType = static_cast<Type*>( epcEvent->data );
1399 OnIndicatorTypeChanged( *indicatorType );
1403 case MSG_ID_INDICATOR_START_ANIMATION:
1405 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: MSG_ID_INDICATOR_START_ANIMATION\n" );
1407 if (msgDataSize != (int)sizeof(IpcIndicatorDataAnimation))
1409 DALI_LOG_ERROR("Message data is incorrect\n");
1413 IpcIndicatorDataAnimation *animData = static_cast<IpcIndicatorDataAnimation*>(msgData);
1415 if(!CheckVisibleState())
1417 ShowIndicator( animData->duration /* n sec */ );
1428 void Indicator::ConnectionClosed()
1430 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1432 // Will get this callback if the server connection failed to start up.
1433 delete mServerConnection;
1434 mServerConnection = NULL;
1435 mState = DISCONNECTED;
1437 // Attempt to re-connect
1441 bool Indicator::CheckVisibleState()
1443 if( mOrientation == Dali::Window::LANDSCAPE
1444 || mOrientation == Dali::Window::LANDSCAPE_INVERSE
1445 || (mVisible == Dali::Window::INVISIBLE)
1446 || (mVisible == Dali::Window::AUTO && !mIsShowing) )
1454 void Indicator::ClearSharedFileInfo()
1456 for( int i = 0; i < SHARED_FILE_NUMBER; i++ )
1458 delete mSharedFileInfo[i].mLock;
1459 mSharedFileInfo[i].mLock = NULL;
1461 delete mSharedFileInfo[i].mSharedFile;
1462 mSharedFileInfo[i].mSharedFile = NULL;
1464 mSharedFileInfo[i].mLockFileName.clear();
1465 mSharedFileInfo[i].mSharedFileName.clear();
1470 * duration can be this
1474 * KEEP_SHOWING = -1,
1478 void Indicator::ShowIndicator(float duration)
1480 if( !mIndicatorAnimation )
1482 mIndicatorAnimation = Dali::Animation::New(SLIDING_ANIMATION_DURATION);
1483 mIndicatorAnimation.FinishedSignal().Connect(this, &Indicator::OnAnimationFinished);
1486 if(mIsShowing && !EqualsZero(duration))
1488 // If need to show during showing, do nothing.
1489 // In 2nd phase (below) will update timer
1491 else if(!mIsShowing && mIsAnimationPlaying && EqualsZero(duration))
1493 // If need to hide during hiding or hidden already, do nothing
1497 mIndicatorAnimation.Clear();
1499 if( EqualsZero(duration) )
1501 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, -mImageHeight, 0), Dali::AlphaFunction::EASE_OUT );
1505 OnIndicatorTypeChanged( INDICATOR_TYPE_2 ); // un-toucable
1509 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, 0, 0), Dali::AlphaFunction::EASE_OUT );
1513 OnIndicatorTypeChanged( INDICATOR_TYPE_1 ); // touchable
1516 mIndicatorAnimation.Play();
1517 mIsAnimationPlaying = true;
1524 mShowTimer = Dali::Timer::New(1000 * duration);
1525 mShowTimer.TickSignal().Connect(this, &Indicator::OnShowTimer);
1527 mShowTimer.SetInterval(1000* duration);
1530 if( mVisible == Dali::Window::AUTO )
1532 // check the stage touch
1533 Dali::Stage::GetCurrent().TouchSignal().Connect( this, &Indicator::OnStageTouched );
1538 if(mShowTimer && mShowTimer.IsRunning())
1543 if( mVisible == Dali::Window::AUTO )
1545 // check the stage touch
1546 Dali::Stage::GetCurrent().TouchSignal().Disconnect( this, &Indicator::OnStageTouched );
1551 bool Indicator::OnShowTimer()
1553 // after time up, hide indicator
1554 ShowIndicator( HIDE_NOW );
1559 void Indicator::OnAnimationFinished(Dali::Animation& animation)
1561 mIsAnimationPlaying = false;
1562 // once animation is finished and indicator is hidden, take it off stage
1563 if( mObserver != NULL )
1565 mObserver->IndicatorVisibilityChanged( mIsShowing ); // is showing?
1569 void Indicator::OnPan( Dali::Actor actor, const Dali::PanGesture& gesture )
1573 if( mServerConnection )
1575 switch( gesture.state )
1577 case Gesture::Started:
1579 mGestureDetected = false;
1581 // The gesture position is the current position after it has moved by the displacement.
1582 // We want to reference the original position.
1583 mGestureDeltaY = gesture.position.y - gesture.displacement.y;
1586 // No break, Fall through
1587 case Gesture::Continuing:
1589 if( mVisible == Dali::Window::AUTO && !mIsShowing )
1591 // Only take one touch point
1592 if( gesture.numberOfTouches == 1 && mGestureDetected == false )
1594 mGestureDeltaY += gesture.displacement.y;
1596 if( mGestureDeltaY >= mImageHeight * SHOWING_DISTANCE_HEIGHT_RATE )
1598 ShowIndicator( AUTO_INDICATOR_STAY_DURATION );
1599 mGestureDetected = true;
1607 case Gesture::Finished:
1608 case Gesture::Cancelled:
1610 // if indicator is showing, hide again when touching is finished (Since touch leave is activated, checking it in gesture::finish instead of touch::up)
1611 if( mVisible == Dali::Window::AUTO && mIsShowing )
1613 ShowIndicator( AUTO_INDICATOR_STAY_DURATION );
1625 void Indicator::OnStageTouched(const Dali::TouchData& touchData)
1627 // when stage is touched while indicator is showing temporary, hide it
1628 if( mIsShowing && ( CheckVisibleState() == false || mVisible == Dali::Window::AUTO ) )
1630 switch( touchData.GetState( 0 ) )
1632 case Dali::PointState::DOWN:
1634 // if touch point is inside the indicator, indicator is not hidden
1635 if( mImageHeight < int(touchData.GetScreenPosition( 0 ).y) )
1637 ShowIndicator( HIDE_NOW );