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>
38 #include <dali/public-api/images/native-image.h>
39 #include <dali/public-api/events/touch-event.h>
40 #include <dali/public-api/events/touch-point.h>
41 #include <dali/public-api/common/stage.h>
42 #include <dali/public-api/images/buffer-image.h>
43 #include <dali/public-api/images/pixel.h>
45 #include <dali/integration-api/debug.h>
48 #include <adaptor-impl.h>
49 #include <accessibility-adaptor-impl.h>
50 #include <native-image-source.h>
54 #if defined(DEBUG_ENABLED)
55 #define STATE_DEBUG_STRING(state) (state==DISCONNECTED?"DISCONNECTED":state==CONNECTED?"CONNECTED":"UNKNOWN")
61 const float SLIDING_ANIMATION_DURATION( 0.2f ); // 200 milli seconds
62 const float AUTO_INDICATOR_STAY_DURATION(3.0f); // 3 seconds
63 const float SHOWING_DISTANCE_HEIGHT_RATE(0.34f); // 20 pixels
71 const int NUM_GRADIENT_INTERVALS(5); // Number of gradient intervals
72 const float GRADIENT_ALPHA[NUM_GRADIENT_INTERVALS+1] = { 0.6f, 0.38f, 0.20f, 0.08f, 0.0f, 0.0f };
74 #define MAKE_SHADER(A)#A
76 const char* BACKGROUND_VERTEX_SHADER = MAKE_SHADER(
77 attribute mediump vec2 aPosition;
78 attribute mediump float aAlpha;
79 varying mediump float vAlpha;
80 uniform mediump mat4 uMvpMatrix;
81 uniform mediump vec3 uSize;
85 mediump vec4 vertexPosition = vec4( aPosition * uSize.xy, 0.0, 1.0 );
86 vertexPosition = uMvpMatrix * vertexPosition;
89 gl_Position = vertexPosition;
93 const char* BACKGROUND_FRAGMENT_SHADER = MAKE_SHADER(
94 uniform lowp vec4 uColor;
95 varying mediump float vAlpha;
99 gl_FragColor = uColor;
100 gl_FragColor.a *= vAlpha;
104 const char* FOREGROUND_VERTEX_SHADER = DALI_COMPOSE_SHADER(
105 attribute mediump vec2 aPosition;\n
106 varying mediump vec2 vTexCoord;\n
107 uniform mediump mat4 uMvpMatrix;\n
108 uniform mediump vec3 uSize;\n
109 uniform mediump vec4 sTextureRect;\n
113 gl_Position = uMvpMatrix * vec4(aPosition * uSize.xy, 0.0, 1.0);\n
114 vTexCoord = aPosition + vec2(0.5);\n
118 const char* FOREGROUND_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
119 varying mediump vec2 vTexCoord;\n
120 uniform sampler2D sTexture;\n
124 gl_FragColor = texture2D( sTexture, vTexCoord );\n // the foreground does not apply actor color
128 Dali::Geometry CreateQuadGeometry()
130 Dali::Property::Map quadVertexFormat;
131 quadVertexFormat["aPosition"] = Dali::Property::VECTOR2;
132 Dali::PropertyBuffer vertexData = Dali::PropertyBuffer::New( quadVertexFormat );
134 const float halfQuadSize = .5f;
135 struct QuadVertex { Dali::Vector2 position; };
136 QuadVertex quadVertexData[4] = {
137 { Dali::Vector2(-halfQuadSize, -halfQuadSize) },
138 { Dali::Vector2(-halfQuadSize, halfQuadSize) },
139 { Dali::Vector2( halfQuadSize, -halfQuadSize) },
140 { Dali::Vector2( halfQuadSize, halfQuadSize) } };
141 vertexData.SetData(quadVertexData, 4);
143 Dali::Geometry quad = Dali::Geometry::New();
144 quad.AddVertexBuffer( vertexData );
145 quad.SetType( Dali::Geometry::TRIANGLE_STRIP );
149 const float OPAQUE_THRESHOLD(0.99f);
150 const float TRANSPARENT_THRESHOLD(0.05f);
152 // indicator service name
153 const char* INDICATOR_SERVICE_NAME("elm_indicator");
155 // Copied from ecore_evas_extn_engine.h
171 OP_PROFILE_CHANGE_REQUEST,
172 OP_PROFILE_CHANGE_DONE,
190 // Copied from elm_conform.c
192 const int MSG_DOMAIN_CONTROL_INDICATOR( 0x10001 );
193 const int MSG_ID_INDICATOR_REPEAT_EVENT( 0x10002 );
194 const int MSG_ID_INDICATOR_ROTATION( 0x10003 );
195 const int MSG_ID_INDICATOR_OPACITY( 0X1004 );
196 const int MSG_ID_INDICATOR_TYPE( 0X1005 );
197 const int MSG_ID_INDICATOR_START_ANIMATION( 0X10006 );
209 struct IpcIndicatorDataAnimation
215 struct IpcDataEvMouseUp
218 Evas_Button_Flags flags;
220 unsigned int timestamp;
221 Evas_Event_Flags event_flags;
223 IpcDataEvMouseUp(unsigned long timestamp)
225 flags(EVAS_BUTTON_NONE),
227 timestamp(static_cast<unsigned int>(timestamp)),
228 event_flags(EVAS_EVENT_FLAG_NONE)
233 struct IpcDataEvMouseDown
236 Evas_Button_Flags flags;
238 unsigned int timestamp;
239 Evas_Event_Flags event_flags;
241 IpcDataEvMouseDown(unsigned long timestamp)
243 flags(EVAS_BUTTON_NONE),
245 timestamp(static_cast<unsigned int>(timestamp)),
246 event_flags(EVAS_EVENT_FLAG_NONE)
251 struct IpcDataEvMouseMove
254 Evas_Button_Flags flags;
256 unsigned int timestamp;
257 Evas_Event_Flags event_flags;
259 IpcDataEvMouseMove(const Dali::TouchPoint& touchPoint, unsigned long timestamp)
260 : x(static_cast<Evas_Coord>(touchPoint.local.x)),
261 y(static_cast<Evas_Coord>(touchPoint.local.y)),
262 flags(EVAS_BUTTON_NONE),
264 timestamp(static_cast<unsigned int>(timestamp)),
265 event_flags(EVAS_EVENT_FLAG_NONE)
270 struct IpcDataEvMouseOut
272 unsigned int timestamp;
274 Evas_Event_Flags event_flags;
276 IpcDataEvMouseOut(unsigned long timestamp)
277 : timestamp(static_cast<unsigned int>(timestamp)),
279 event_flags(EVAS_EVENT_FLAG_NONE)
284 #ifdef ENABLE_INDICATOR_IMAGE_SAVING
286 void SaveIndicatorImage( Dali::NativeImageSourcePtr nativeImageSource )
288 // Save image data to disk in BMP form.
289 static int gFilenameCounter = 0;
290 static const char bmpHeader[] = {
291 0x42, 0x4d, 0x0a, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x7c, 0x00,
293 0xe0, 0x01, 0x00, 0x00, // Width (480)
294 0x1b, 0x00, 0x00, 0x00, // Height ( 27)
295 0x01, 0x00, 0x20, 0x00, 0x03, 0x00,
296 0x00, 0x00, 0x80, 0xca, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
297 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff,
298 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
299 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
300 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
301 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
302 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
305 // This is a BMP header with width & height hard-coded in.
306 // The data was first determined by dumping the raw data and inspecting in GIMP, before creating this header data.
307 std::vector<unsigned char> buffer;
310 Dali::Pixel::Format pixelFormat;
311 if( nativeImageSource->GetPixels( buffer, w, h, pixelFormat ) )
313 int imageSize = w * h * 4;
314 std::stringstream fileName;
315 // Give each file an incremental filename.
316 fileName << "/opt/usr/media/Images/out-" << std::setfill( '0' ) << std::setw( 5 ) << gFilenameCounter << ".bmp";
318 std::ofstream outfile( fileName.str().c_str(), std::ofstream::binary );
319 if( outfile.is_open() )
321 DALI_LOG_WARNING( "Saving Indicator Image w:%d, h:%d, %s\n", w, h, fileName.str().c_str() );
323 outfile.write( bmpHeader, sizeof( bmpHeader ) / sizeof( bmpHeader[0] ) ); // Size of the BMP header.
324 outfile.write( (const char*)buffer.data(), imageSize );
330 DALI_LOG_ERROR( "COULD NOT OPEN FOR SAVING: %s\n", fileName.str().c_str() );
337 } // anonymous namespace
346 #if defined(DEBUG_ENABLED)
347 Debug::Filter* gIndicatorLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_INDICATOR");
350 // Impl to hide EFL implementation.
352 struct Indicator::Impl
354 enum // operation mode
357 INDICATOR_STAY_WITH_DURATION
363 Impl(Indicator* indicator)
364 : mIndicator(indicator),
365 mEcoreEventHandler(NULL)
367 #if defined(DALI_PROFILE_MOBILE)
369 mEcoreEventHandler = ecore_event_handler_add(ECORE_WL_EVENT_INDICATOR_FLICK, EcoreEventIndicator, this);
371 mEcoreEventHandler = ecore_event_handler_add(ECORE_X_EVENT_CLIENT_MESSAGE, EcoreEventClientMessage, this);
373 #endif // WAYLAND && DALI_PROFILE_MOBILE
381 if ( mEcoreEventHandler )
383 ecore_event_handler_del(mEcoreEventHandler);
387 static void SetIndicatorVisibility( void* data, int operation )
389 Indicator::Impl* indicatorImpl((Indicator::Impl*)data);
391 if ( indicatorImpl == NULL || indicatorImpl->mIndicator == NULL)
395 if ( operation == INDICATOR_STAY_WITH_DURATION )
397 // if indicator is not showing, INDICATOR_FLICK_DONE is given
398 if( indicatorImpl->mIndicator->mVisible == Dali::Window::AUTO &&
399 !indicatorImpl->mIndicator->mIsShowing )
401 indicatorImpl->mIndicator->ShowIndicator( AUTO_INDICATOR_STAY_DURATION );
404 else if( operation == INDICATOR_HIDE )
406 if( indicatorImpl->mIndicator->mVisible == Dali::Window::AUTO &&
407 indicatorImpl->mIndicator->mIsShowing )
409 indicatorImpl->mIndicator->ShowIndicator( HIDE_NOW );
413 #if defined(DALI_PROFILE_MOBILE)
416 * Called when the Ecore indicator event is received.
418 static Eina_Bool EcoreEventIndicator( void* data, int type, void* event )
420 SetIndicatorVisibility( data, INDICATOR_STAY_WITH_DURATION );
421 return ECORE_CALLBACK_PASS_ON;
425 * Called when the client messages (i.e. quick panel state) are received.
427 static Eina_Bool EcoreEventClientMessage( void* data, int type, void* event )
429 Ecore_X_Event_Client_Message* clientMessageEvent((Ecore_X_Event_Client_Message*)event);
431 if ( clientMessageEvent != NULL )
433 if (clientMessageEvent->message_type == ECORE_X_ATOM_E_INDICATOR_FLICK_DONE)
435 SetIndicatorVisibility( data, INDICATOR_STAY_WITH_DURATION );
437 else if ( clientMessageEvent->message_type == ECORE_X_ATOM_E_MOVE_QUICKPANEL_STATE )
439 SetIndicatorVisibility( data, INDICATOR_HIDE );
442 return ECORE_CALLBACK_PASS_ON;
445 #endif // WAYLAND && DALI_PROFILE_MOBILE
448 Indicator* mIndicator;
449 Ecore_Event_Handler* mEcoreEventHandler;
452 Indicator::LockFile::LockFile(const std::string filename)
453 : mFilename(filename),
456 mFileDescriptor = open(filename.c_str(), O_RDWR);
457 if( mFileDescriptor == -1 )
461 DALI_LOG_ERROR( "### Cannot open %s for indicator lock ###\n", mFilename.c_str() );
465 Indicator::LockFile::~LockFile()
467 // Closing file descriptor also unlocks file.
468 close( mFileDescriptor );
471 bool Indicator::LockFile::Lock()
473 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
476 if( mFileDescriptor > 0 )
478 if( lockf( mFileDescriptor, F_LOCK, 0 ) == 0 ) // Note, operation may block.
486 // file descriptor is no longer valid or not writable
489 DALI_LOG_ERROR( "### Cannot lock indicator: bad file descriptor for %s ###\n", mFilename.c_str() );
497 void Indicator::LockFile::Unlock()
499 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
500 if( lockf( mFileDescriptor, F_ULOCK, 0 ) != 0 )
504 // file descriptor is no longer valid or not writable
507 DALI_LOG_ERROR( "### Cannot unlock indicator: bad file descriptor for %s\n", mFilename.c_str() );
512 bool Indicator::LockFile::RetrieveAndClearErrorStatus()
514 bool error = mErrorThrown;
515 mErrorThrown = false;
519 Indicator::ScopedLock::ScopedLock(LockFile* lockFile)
520 : mLockFile(lockFile),
525 mLocked = mLockFile->Lock();
529 Indicator::ScopedLock::~ScopedLock()
537 bool Indicator::ScopedLock::IsLocked()
542 Indicator::Indicator( Adaptor* adaptor, Dali::Window::WindowOrientation orientation, IndicatorInterface::Observer* observer )
544 mGestureDetected( false ),
546 mOpacityMode( Dali::Window::OPAQUE ),
547 mState( DISCONNECTED ),
549 mServerConnection( NULL ),
550 mObserver( observer ),
551 mOrientation( orientation ),
554 mVisible( Dali::Window::INVISIBLE ),
556 mIsAnimationPlaying( false ),
557 mCurrentSharedFile( 0 ),
558 mSharedBufferType( BUFFER_TYPE_SHM ),
560 mBackgroundVisible( false ),
563 mIndicatorContentActor = Dali::Actor::New();
564 mIndicatorContentActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
565 mIndicatorContentActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
567 // Indicator image handles the touch event including "leave"
568 mIndicatorContentActor.SetLeaveRequired( true );
569 mIndicatorContentActor.TouchedSignal().Connect( this, &Indicator::OnTouched );
570 mIndicatorContentActor.SetColor( Color::BLACK );
572 mIndicatorActor = Dali::Actor::New();
573 mIndicatorActor.Add( mIndicatorContentActor );
575 // Event handler to find out flick down gesture
576 mEventActor = Dali::Actor::New();
577 mEventActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
578 mEventActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
579 mIndicatorActor.Add( mEventActor );
581 // Attach pan gesture to find flick down during hiding.
582 // It can prevent the problem that scrollview gets pan gesture even indicator area is touched,
583 // since it consumes the pan gesture in advance.
584 mPanDetector = Dali::PanGestureDetector::New();
585 mPanDetector.DetectedSignal().Connect( this, &Indicator::OnPan );
586 mPanDetector.Attach( mEventActor );
590 // register indicator to accessibility adaptor
591 Dali::AccessibilityAdaptor accessibilityAdaptor = AccessibilityAdaptor::Get();
592 if(accessibilityAdaptor)
594 AccessibilityAdaptor::GetImplementation( accessibilityAdaptor ).SetIndicator( this );
596 // hide the indicator by default
597 mIndicatorActor.SetVisible( false );
599 // create impl to handle ecore event
600 mImpl = new Impl(this);
603 Indicator::~Indicator()
613 mEventActor.TouchedSignal().Disconnect( this, &Indicator::OnTouched );
618 void Indicator::SetAdaptor(Adaptor* adaptor)
621 mIndicatorBuffer->SetAdaptor( adaptor );
624 Dali::Actor Indicator::GetActor()
626 return mIndicatorActor;
629 void Indicator::Open( Dali::Window::WindowOrientation orientation )
631 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
633 // Calls from Window should be set up to ensure we are in a
634 // disconnected state before opening a second time.
635 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
637 mOrientation = orientation;
641 // Change background visibility depending on orientation
642 if( mOrientation == Dali::Window::LANDSCAPE || mOrientation == Dali::Window::LANDSCAPE_INVERSE )
644 if( mBackgroundRenderer )
646 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
647 mBackgroundVisible = false;
652 SetOpacityMode( mOpacityMode );
656 void Indicator::Close()
658 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s", STATE_DEBUG_STRING(mState) );
660 if( mState == CONNECTED )
663 if( mObserver != NULL )
665 mObserver->IndicatorClosed( this );
669 Dali::Texture emptyTexture;
670 SetForegroundImage( emptyTexture );
673 void Indicator::SetOpacityMode( Dali::Window::IndicatorBgOpacity mode )
677 Dali::Geometry geometry = CreateBackgroundGeometry();
680 if( mBackgroundRenderer )
682 if( mBackgroundRenderer.GetGeometry() != geometry )
684 mBackgroundRenderer.SetGeometry( geometry );
689 if( !mBackgroundShader )
691 mBackgroundShader = Dali::Shader::New( BACKGROUND_VERTEX_SHADER, BACKGROUND_FRAGMENT_SHADER, Dali::Shader::Hint::OUTPUT_IS_TRANSPARENT );
694 mBackgroundRenderer = Dali::Renderer::New( geometry, mBackgroundShader );
697 if( !mBackgroundVisible )
699 mIndicatorContentActor.AddRenderer( mBackgroundRenderer );
700 mBackgroundVisible = true;
703 else if( mBackgroundRenderer )
705 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
706 mBackgroundVisible = false;
711 void Indicator::SetVisible( Dali::Window::IndicatorVisibleMode visibleMode, bool forceUpdate )
713 if ( visibleMode != mVisible || forceUpdate )
715 // If we were previously hidden, then we should update the image data before we display the indicator
716 if ( mVisible == Dali::Window::INVISIBLE )
718 UpdateImageData( mCurrentSharedFile );
721 if ( visibleMode == Dali::Window::INVISIBLE )
723 if (mServerConnection)
725 mServerConnection->SendEvent( OP_HIDE, NULL, 0 );
730 mIndicatorActor.SetVisible( true );
732 if( mServerConnection )
734 mServerConnection->SendEvent( OP_SHOW, NULL, 0 );
738 mVisible = visibleMode;
741 if( mForegroundRenderer && mForegroundRenderer.GetTextures().GetTexture( 0u ) )
743 if( CheckVisibleState() && mVisible == Dali::Window::AUTO )
746 ShowIndicator( AUTO_INDICATOR_STAY_DURATION /* stay n sec */ );
748 else if( CheckVisibleState() && mVisible == Dali::Window::VISIBLE )
751 ShowIndicator( KEEP_SHOWING );
756 ShowIndicator( HIDE_NOW );
766 bool Indicator::IsConnected()
768 return ( mState == CONNECTED );
771 bool Indicator::SendMessage( int messageDomain, int messageId, const void *data, int size )
775 return mServerConnection->SendEvent( OP_MSG, messageDomain, messageId, data, size );
783 bool Indicator::OnTouched(Dali::Actor indicator, const Dali::TouchEvent& touchEvent)
785 if( mServerConnection )
787 const TouchPoint& touchPoint = touchEvent.GetPoint( 0 );
789 // Send touch event to indicator server when indicator is showing
790 if( CheckVisibleState() || mIsShowing )
792 switch( touchPoint.state )
794 case Dali::PointState::DOWN:
796 IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
797 IpcDataEvMouseDown ipcDown( touchEvent.time );
798 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
799 mServerConnection->SendEvent( OP_EV_MOUSE_DOWN, &ipcDown, sizeof(ipcDown) );
801 if( mVisible == Dali::Window::AUTO )
803 // Stop hiding indicator
804 ShowIndicator( KEEP_SHOWING );
809 case Dali::PointState::MOTION:
811 IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
812 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
816 case Dali::PointState::UP:
817 case Dali::PointState::INTERRUPTED:
819 IpcDataEvMouseUp ipcUp( touchEvent.time );
820 mServerConnection->SendEvent( OP_EV_MOUSE_UP, &ipcUp, sizeof(ipcUp) );
822 if( mVisible == Dali::Window::AUTO )
825 ShowIndicator( 0.5f /* hide after 0.5 sec */ );
830 case Dali::TouchPoint::Leave:
832 IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
833 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
834 IpcDataEvMouseUp ipcOut( touchEvent.time );
835 mServerConnection->SendEvent( OP_EV_MOUSE_OUT, &ipcOut, sizeof(ipcOut) );
850 bool Indicator::Connect()
852 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
854 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
856 bool connected = false;
858 mServerConnection = new ServerConnection( INDICATOR_SERVICE_NAME, 0, false, this );
859 if( mServerConnection )
861 connected = mServerConnection->IsConnected();
864 delete mServerConnection;
865 mServerConnection = NULL;
871 StartReconnectionTimer();
881 void Indicator::StartReconnectionTimer()
883 if( ! mReconnectTimer )
885 mReconnectTimer = Dali::Timer::New(1000);
886 mConnection.DisconnectAll();
887 mReconnectTimer.TickSignal().Connect( mConnection, &Indicator::OnReconnectTimer );
889 mReconnectTimer.Start();
892 bool Indicator::OnReconnectTimer()
896 if( mState == DISCONNECTED )
907 void Indicator::Disconnect()
909 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
911 mState = DISCONNECTED;
913 delete mServerConnection;
914 mServerConnection = NULL;
916 ClearSharedFileInfo();
919 void Indicator::Resize( int width, int height )
930 if( mImageWidth != width || mImageHeight != height )
933 mImageHeight = height;
935 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
936 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
937 mEventActor.SetSize(mImageWidth, mImageHeight);
942 void Indicator::SetLockFileInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
944 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
946 // epcEvent->ref == w
947 // epcEvent->ref_to == h
948 // epcEvent->response == buffer num
949 // epcEvent->data = lockfile + nul byte
951 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) && (epcEvent->data) &&
952 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
954 int n = epcEvent->response;
956 if( n >= 0 && n < SHARED_FILE_NUMBER )
958 mCurrentSharedFile = n;
960 mSharedFileInfo[n].mImageWidth = epcEvent->ref;
961 mSharedFileInfo[n].mImageHeight = epcEvent->ref_to;
963 mSharedFileInfo[n].mLockFileName.clear();
965 mSharedFileInfo[n].mLockFileName = static_cast< char* >( epcEvent->data );
967 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetLockFileInfo: buffer num = %d, w = %d, h = %d, lock = %s\n",
968 n, mSharedFileInfo[n].mImageWidth, mSharedFileInfo[n].mImageHeight, mSharedFileInfo[n].mLockFileName.c_str() );
973 void Indicator::SetSharedImageInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
975 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
977 // epcEvent->ref == shm id
978 // epcEvent->ref_to == shm num
979 // epcEvent->response == buffer num
980 // epcEvent->data = shm ref string + nul byte
982 if ( (epcEvent->data) &&
983 (epcEvent->size > 0) &&
984 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
986 int n = epcEvent->response;
988 if( n >= 0 && n < SHARED_FILE_NUMBER )
990 mCurrentSharedFile = n;
992 mSharedFileInfo[n].mSharedFileName.clear();
994 mSharedFileInfo[n].mSharedFileName = static_cast< char* >( epcEvent->data );
996 mSharedFileInfo[n].mSharedFileID = epcEvent->ref;
997 mSharedFileInfo[n].mSharedFileNumber = epcEvent->ref_to;
999 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetSharedImageInfo: buffer num %d, shared file = %s, id = %d, num = %d\n",
1000 n, mSharedFileInfo[n].mSharedFileName.c_str(), mSharedFileInfo[n].mSharedFileID, mSharedFileInfo[n].mSharedFileNumber );
1005 void Indicator::LoadSharedImage( Ecore_Ipc_Event_Server_Data *epcEvent )
1007 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1009 // epcEvent->ref == alpha
1010 // epcEvent->ref_to == sys
1011 // epcEvent->response == buffer num
1013 if ( mSharedBufferType != BUFFER_TYPE_SHM )
1018 int n = epcEvent->response;
1020 if( n >= 0 && n < SHARED_FILE_NUMBER )
1022 mCurrentSharedFile = n;
1024 delete mSharedFileInfo[n].mSharedFile;
1025 mSharedFileInfo[n].mSharedFile = NULL;
1027 delete mSharedFileInfo[n].mLock;
1028 mSharedFileInfo[n].mLock = NULL;
1030 std::stringstream sharedFileID;
1031 std::stringstream sharedFileNumber;
1033 sharedFileID << mSharedFileInfo[n].mSharedFileID;
1034 sharedFileNumber << mSharedFileInfo[n].mSharedFileNumber;
1036 std::string sharedFilename = "/" + mSharedFileInfo[n].mSharedFileName + "-" + sharedFileID.str() + "." + sharedFileNumber.str();
1038 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "LoadSharedImage: file name = %s\n", sharedFilename.c_str() );
1040 mSharedFileInfo[n].mSharedFile = SharedFile::New( sharedFilename.c_str(), mSharedFileInfo[n].mImageWidth * mSharedFileInfo[n].mImageWidth * 4, true );
1041 if( mSharedFileInfo[n].mSharedFile != NULL )
1043 mSharedFileInfo[n].mLock = new Indicator::LockFile( mSharedFileInfo[n].mLockFileName );
1044 if( mSharedFileInfo[n].mLock->RetrieveAndClearErrorStatus() )
1046 DALI_LOG_ERROR( "### Indicator error: Cannot open lock file %s ###\n", mSharedFileInfo[n].mLockFileName.c_str() );
1049 CreateNewImage( n );
1055 void Indicator::LoadPixmapImage( Ecore_Ipc_Event_Server_Data *epcEvent )
1057 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1059 // epcEvent->ref == pixmap id
1060 // epcEvent->ref_to == type
1061 // epcEvent->response == buffer num
1063 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) )
1065 mSharedBufferType = (BufferType)(epcEvent->ref_to);
1067 ClearSharedFileInfo();
1069 mPixmap = static_cast<PixmapId>(epcEvent->ref);
1070 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "mPixmap [%x]", mPixmap);
1072 CreateNewPixmapImage();
1077 void Indicator::UpdateTopMargin()
1079 int newMargin = (mVisible == Dali::Window::VISIBLE && mOpacityMode == Dali::Window::OPAQUE) ? mImageHeight : 0;
1080 if (mTopMargin != newMargin)
1082 mTopMargin = newMargin;
1083 mAdaptor->IndicatorSizeChanged( mTopMargin );
1087 void Indicator::UpdateVisibility()
1089 if( CheckVisibleState() )
1091 // set default indicator type (enable the quick panel)
1092 OnIndicatorTypeChanged( INDICATOR_TYPE_1 );
1096 // set default indicator type (disable the quick panel)
1097 OnIndicatorTypeChanged( INDICATOR_TYPE_2 );
1102 mIndicatorContentActor.SetPosition( 0.0f, -mImageHeight, 0.0f );
1105 SetVisible(mVisible, true);
1108 void Indicator::UpdateImageData( int bufferNumber )
1110 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s mVisible: %s", STATE_DEBUG_STRING(mState), mVisible?"T":"F" );
1112 if( mState == CONNECTED && mVisible )
1116 // in case of shm indicator (not pixmap), not sure we can skip it when mIsShowing is false
1117 CopyToBuffer( bufferNumber );
1123 mAdaptor->RequestUpdateOnce();
1129 bool Indicator::CopyToBuffer( int bufferNumber )
1131 bool success = false;
1133 if( mSharedFileInfo[bufferNumber].mLock )
1135 Indicator::ScopedLock scopedLock(mSharedFileInfo[bufferNumber].mLock);
1136 if( mSharedFileInfo[bufferNumber].mLock->RetrieveAndClearErrorStatus() )
1140 else if( scopedLock.IsLocked() )
1142 unsigned char *src = mSharedFileInfo[bufferNumber].mSharedFile->GetAddress();
1143 size_t size = mSharedFileInfo[bufferNumber].mImageWidth * mSharedFileInfo[bufferNumber].mImageHeight * 4;
1145 if( mIndicatorBuffer->UpdatePixels( src, size ) )
1147 mAdaptor->RequestUpdateOnce();
1156 void Indicator::CreateNewPixmapImage()
1158 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mImageWidth, mImageHeight );
1159 Dali::NativeImageSourcePtr nativeImageSource = Dali::NativeImageSource::New( mPixmap );
1161 #ifdef ENABLE_INDICATOR_IMAGE_SAVING
1162 SaveIndicatorImage( nativeImageSource );
1165 if( nativeImageSource )
1167 Dali::Texture texture = Dali::Texture::New( *nativeImageSource );
1168 SetForegroundImage( texture );
1169 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
1170 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
1171 mEventActor.SetSize( mImageWidth, mImageHeight );
1176 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1178 if( mObserver != NULL )
1180 mObserver->IndicatorClosed( this );
1182 // Don't do connection in this callback - strange things happen!
1183 StartReconnectionTimer();
1187 void Indicator::CreateNewImage( int bufferNumber )
1189 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight );
1190 mIndicatorBuffer = new IndicatorBuffer( mAdaptor, mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight, Pixel::BGRA8888 );
1191 bool success = false;
1193 if( CopyToBuffer( bufferNumber ) ) // Only create images if we have valid image buffer
1195 Dali::Texture texture = Dali::Texture::New( mIndicatorBuffer->GetNativeImage() );
1198 SetForegroundImage( texture );
1205 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1207 if( mObserver != NULL )
1209 mObserver->IndicatorClosed( this );
1211 // Don't do connection in this callback - strange things happen!
1212 StartReconnectionTimer();
1216 Dali::Geometry Indicator::CreateBackgroundGeometry()
1218 switch( mOpacityMode )
1220 case Dali::Window::TRANSLUCENT:
1221 if( !mTranslucentGeometry )
1223 // Construct 5 interval mesh
1237 struct BackgroundVertex
1243 unsigned int numVertices = 2 * ( NUM_GRADIENT_INTERVALS + 1 );
1244 BackgroundVertex vertices[ numVertices ];
1247 float delta = 1.0f / NUM_GRADIENT_INTERVALS;
1248 BackgroundVertex* currentVertex = vertices;
1249 for( int y = 0; y < NUM_GRADIENT_INTERVALS + 1; ++y, d += delta )
1251 currentVertex->mPosition = Vector2( -0.5f, d );
1252 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1255 currentVertex->mPosition = Vector2( 0.5f, d );
1256 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1261 unsigned int numIndices = 2 * 3 * NUM_GRADIENT_INTERVALS;
1262 unsigned short indices[ numIndices ];
1264 unsigned short* currentIndex = indices;
1265 for( int y = 0; y < NUM_GRADIENT_INTERVALS; ++y )
1267 *currentIndex++ = (2 * y);
1268 *currentIndex++ = (2 * y) + 3;
1269 *currentIndex++ = (2 * y) + 1;
1271 *currentIndex++ = (2 * y);
1272 *currentIndex++ = (2 * y) + 2;
1273 *currentIndex++ = (2 * y) + 3;
1276 Dali::Property::Map vertexFormat;
1277 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1278 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1279 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1280 vertexPropertyBuffer.SetData( vertices, numVertices );
1282 // Create the geometry object
1283 mTranslucentGeometry = Dali::Geometry::New();
1284 mTranslucentGeometry.AddVertexBuffer( vertexPropertyBuffer );
1285 mTranslucentGeometry.SetIndexBuffer( &indices[0], numIndices );
1288 return mTranslucentGeometry;
1289 case Dali::Window::OPAQUE:
1291 if( !mSolidGeometry )
1294 struct BackgroundVertex
1300 BackgroundVertex vertices[ 4 ] = { { Vector2( -0.5f, -0.5f ), 1.0f }, { Vector2( 0.5f, -0.5f ), 1.0f },
1301 { Vector2( -0.5f, 0.5f ), 1.0f }, { Vector2( 0.5f, 0.5f ), 1.0f } };
1304 unsigned short indices[ 6 ] = { 0, 3, 1, 0, 2, 3 };
1306 Dali::Property::Map vertexFormat;
1307 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1308 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1309 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1310 vertexPropertyBuffer.SetData( vertices, 4 );
1313 // Create the geometry object
1314 mSolidGeometry = Dali::Geometry::New();
1315 mSolidGeometry.AddVertexBuffer( vertexPropertyBuffer );
1316 mSolidGeometry.SetIndexBuffer( &indices[0], 6 );
1319 return mSolidGeometry;
1320 case Dali::Window::TRANSPARENT:
1324 return Dali::Geometry();
1327 void Indicator::SetForegroundImage( Dali::Texture texture )
1329 if( !mForegroundRenderer && texture )
1332 Dali::Shader shader = Dali::Shader::New( FOREGROUND_VERTEX_SHADER, FOREGROUND_FRAGMENT_SHADER );
1334 // Create renderer from geometry and material
1335 Dali::Geometry quad = CreateQuadGeometry();
1336 mForegroundRenderer = Dali::Renderer::New( quad, shader );
1337 // Make sure the foreground stays in front of the background
1338 mForegroundRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, 1.f );
1340 // Set blend function
1341 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_RGB, Dali::BlendFactor::ONE );
1342 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_RGB, Dali::BlendFactor::ONE_MINUS_SRC_ALPHA );
1343 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_ALPHA, Dali::BlendFactor::ONE );
1344 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_ALPHA, Dali::BlendFactor::ONE );
1346 // Create a texture-set and add to renderer
1348 Dali::TextureSet textureSet = Dali::TextureSet::New();
1349 textureSet.SetTexture( 0u, texture );
1350 mForegroundRenderer.SetTextures( textureSet );
1352 mIndicatorContentActor.AddRenderer( mForegroundRenderer );
1354 else if( mForegroundRenderer )
1356 Dali::TextureSet textureSet = mForegroundRenderer.GetTextures();
1357 textureSet.SetTexture( 0u, texture );
1360 if( mImageWidth == 0 && mImageHeight == 0 && texture)
1362 Resize( texture.GetWidth(), texture.GetHeight() );
1366 void Indicator::OnIndicatorTypeChanged( Type indicatorType )
1368 if( mObserver != NULL )
1370 mObserver->IndicatorTypeChanged( indicatorType );
1374 void Indicator::DataReceived( void* event )
1376 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1377 Ecore_Ipc_Event_Server_Data *epcEvent = static_cast<Ecore_Ipc_Event_Server_Data *>( event );
1379 switch( epcEvent->minor )
1383 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE\n" );
1386 //mAdaptor->RequestUpdateOnce();
1390 case OP_UPDATE_DONE:
1392 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE_DONE [%d]\n", epcEvent->response );
1393 // epcEvent->response == display buffer #
1394 //UpdateImageData( epcEvent->response );
1399 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF0\n" );
1400 SetSharedImageInfo( epcEvent );
1405 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF1\n" );
1406 SetLockFileInfo( epcEvent );
1411 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF2\n" );
1412 LoadSharedImage( epcEvent );
1417 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_PIXMAP_REF\n" );
1418 LoadPixmapImage( epcEvent );
1423 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_RESIZE\n" );
1425 if( (epcEvent->data) && (epcEvent->size >= (int)sizeof(IpcDataResize)) )
1427 IpcDataResize *newSize = static_cast<IpcDataResize*>( epcEvent->data );
1428 Resize( newSize->w, newSize->h );
1434 int msgDomain = epcEvent->ref;
1435 int msgId = epcEvent->ref_to;
1437 void *msgData = NULL;
1438 int msgDataSize = 0;
1439 msgData = epcEvent->data;
1440 msgDataSize = epcEvent->size;
1442 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT. msgDomain = %d\n", msgDomain );
1444 if( msgDomain == MSG_DOMAIN_CONTROL_INDICATOR )
1448 case MSG_ID_INDICATOR_TYPE:
1450 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT, INDICATOR_TYPE\n" );
1451 Type* indicatorType = static_cast<Type*>( epcEvent->data );
1452 OnIndicatorTypeChanged( *indicatorType );
1456 case MSG_ID_INDICATOR_START_ANIMATION:
1458 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: MSG_ID_INDICATOR_START_ANIMATION\n" );
1460 if (msgDataSize != (int)sizeof(IpcIndicatorDataAnimation))
1462 DALI_LOG_ERROR("Message data is incorrect\n");
1466 IpcIndicatorDataAnimation *animData = static_cast<IpcIndicatorDataAnimation*>(msgData);
1468 if(!CheckVisibleState())
1470 ShowIndicator( animData->duration /* n sec */ );
1481 void Indicator::ConnectionClosed()
1483 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1485 // Will get this callback if the server connection failed to start up.
1486 delete mServerConnection;
1487 mServerConnection = NULL;
1488 mState = DISCONNECTED;
1490 // Attempt to re-connect
1494 bool Indicator::CheckVisibleState()
1496 if( mOrientation == Dali::Window::LANDSCAPE
1497 || mOrientation == Dali::Window::LANDSCAPE_INVERSE
1498 || (mVisible == Dali::Window::INVISIBLE)
1499 || (mVisible == Dali::Window::AUTO && !mIsShowing) )
1507 void Indicator::ClearSharedFileInfo()
1509 for( int i = 0; i < SHARED_FILE_NUMBER; i++ )
1511 delete mSharedFileInfo[i].mLock;
1512 mSharedFileInfo[i].mLock = NULL;
1514 delete mSharedFileInfo[i].mSharedFile;
1515 mSharedFileInfo[i].mSharedFile = NULL;
1517 mSharedFileInfo[i].mLockFileName.clear();
1518 mSharedFileInfo[i].mSharedFileName.clear();
1523 * duration can be this
1527 * KEEP_SHOWING = -1,
1531 void Indicator::ShowIndicator(float duration)
1533 if( !mIndicatorAnimation )
1535 mIndicatorAnimation = Dali::Animation::New(SLIDING_ANIMATION_DURATION);
1536 mIndicatorAnimation.FinishedSignal().Connect(this, &Indicator::OnAnimationFinished);
1539 if(mIsShowing && !EqualsZero(duration))
1541 // If need to show during showing, do nothing.
1542 // In 2nd phase (below) will update timer
1544 else if(!mIsShowing && mIsAnimationPlaying && EqualsZero(duration))
1546 // If need to hide during hiding or hidden already, do nothing
1550 mIndicatorAnimation.Clear();
1552 if( EqualsZero(duration) )
1554 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, -mImageHeight, 0), Dali::AlphaFunction::EASE_OUT );
1558 OnIndicatorTypeChanged( INDICATOR_TYPE_2 ); // un-toucable
1562 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, 0, 0), Dali::AlphaFunction::EASE_OUT );
1566 OnIndicatorTypeChanged( INDICATOR_TYPE_1 ); // touchable
1569 mIndicatorAnimation.Play();
1570 mIsAnimationPlaying = true;
1577 mShowTimer = Dali::Timer::New(1000 * duration);
1578 mShowTimer.TickSignal().Connect(this, &Indicator::OnShowTimer);
1580 mShowTimer.SetInterval(1000* duration);
1583 if( mVisible == Dali::Window::AUTO )
1585 // check the stage touch
1586 Dali::Stage::GetCurrent().TouchedSignal().Connect( this, &Indicator::OnStageTouched );
1591 if(mShowTimer && mShowTimer.IsRunning())
1596 if( mVisible == Dali::Window::AUTO )
1598 // check the stage touch
1599 Dali::Stage::GetCurrent().TouchedSignal().Disconnect( this, &Indicator::OnStageTouched );
1604 bool Indicator::OnShowTimer()
1606 // after time up, hide indicator
1607 ShowIndicator( HIDE_NOW );
1612 void Indicator::OnAnimationFinished(Dali::Animation& animation)
1614 mIsAnimationPlaying = false;
1615 // once animation is finished and indicator is hidden, take it off stage
1616 if( mObserver != NULL )
1618 mObserver->IndicatorVisibilityChanged( mIsShowing ); // is showing?
1622 void Indicator::OnPan( Dali::Actor actor, const Dali::PanGesture& gesture )
1626 if( mServerConnection )
1628 switch( gesture.state )
1630 case Gesture::Started:
1632 mGestureDetected = false;
1634 // The gesture position is the current position after it has moved by the displacement.
1635 // We want to reference the original position.
1636 mGestureDeltaY = gesture.position.y - gesture.displacement.y;
1639 // No break, Fall through
1640 case Gesture::Continuing:
1642 if( mVisible == Dali::Window::AUTO && !mIsShowing )
1644 // Only take one touch point
1645 if( gesture.numberOfTouches == 1 && mGestureDetected == false )
1647 mGestureDeltaY += gesture.displacement.y;
1649 if( mGestureDeltaY >= mImageHeight * SHOWING_DISTANCE_HEIGHT_RATE )
1651 ShowIndicator( AUTO_INDICATOR_STAY_DURATION );
1652 mGestureDetected = true;
1660 case Gesture::Finished:
1661 case Gesture::Cancelled:
1663 // if indicator is showing, hide again when touching is finished (Since touch leave is activated, checking it in gesture::finish instead of touch::up)
1664 if( mVisible == Dali::Window::AUTO && mIsShowing )
1666 ShowIndicator( AUTO_INDICATOR_STAY_DURATION );
1678 void Indicator::OnStageTouched(const Dali::TouchEvent& touchEvent)
1680 const TouchPoint& touchPoint = touchEvent.GetPoint( 0 );
1682 // when stage is touched while indicator is showing temporary, hide it
1683 if( mIsShowing && ( CheckVisibleState() == false || mVisible == Dali::Window::AUTO ) )
1685 switch( touchPoint.state )
1687 case Dali::PointState::DOWN:
1689 // if touch point is inside the indicator, indicator is not hidden
1690 if( mImageHeight < int(touchPoint.screen.y) )
1692 ShowIndicator( HIDE_NOW );