2 * Copyright (c) 2016 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::Vector2& touchPoint, unsigned long timestamp)
260 : x(static_cast<Evas_Coord>(touchPoint.x)),
261 y(static_cast<Evas_Coord>(touchPoint.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 struct flock filelock;
480 filelock.l_type = F_RDLCK;
481 filelock.l_whence = SEEK_SET;
482 filelock.l_start = 0;
484 if( fcntl( mFileDescriptor, F_SETLKW, &filelock ) == -1 )
487 DALI_LOG_ERROR( "### Failed to lock with fd : %s ###\n", mFilename.c_str() );
497 DALI_LOG_ERROR( "### Invalid fd ###\n" );
503 void Indicator::LockFile::Unlock()
505 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
507 struct flock filelock;
509 filelock.l_type = F_UNLCK;
510 filelock.l_whence = SEEK_SET;
511 filelock.l_start = 0;
513 if (fcntl(mFileDescriptor, F_SETLKW, &filelock) == -1)
516 DALI_LOG_ERROR( "### Failed to lock with fd : %s ###\n", mFilename.c_str() );
520 bool Indicator::LockFile::RetrieveAndClearErrorStatus()
522 bool error = mErrorThrown;
523 mErrorThrown = false;
527 Indicator::ScopedLock::ScopedLock(LockFile* lockFile)
528 : mLockFile(lockFile),
533 mLocked = mLockFile->Lock();
537 Indicator::ScopedLock::~ScopedLock()
545 bool Indicator::ScopedLock::IsLocked()
550 Indicator::Indicator( Adaptor* adaptor, Dali::Window::WindowOrientation orientation, IndicatorInterface::Observer* observer )
552 mGestureDeltaY( 0.0f ),
553 mGestureDetected( false ),
555 mOpacityMode( Dali::Window::OPAQUE ),
556 mState( DISCONNECTED ),
558 mServerConnection( NULL ),
559 mObserver( observer ),
560 mOrientation( orientation ),
563 mVisible( Dali::Window::INVISIBLE ),
565 mIsAnimationPlaying( false ),
566 mCurrentSharedFile( 0 ),
567 mSharedBufferType( BUFFER_TYPE_SHM ),
569 mBackgroundVisible( false ),
572 mIndicatorContentActor = Dali::Actor::New();
573 mIndicatorContentActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
574 mIndicatorContentActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
576 // Indicator image handles the touch event including "leave"
577 mIndicatorContentActor.SetLeaveRequired( true );
578 mIndicatorContentActor.TouchSignal().Connect( this, &Indicator::OnTouch );
579 mIndicatorContentActor.SetColor( Color::BLACK );
581 mIndicatorActor = Dali::Actor::New();
582 mIndicatorActor.Add( mIndicatorContentActor );
584 // Event handler to find out flick down gesture
585 mEventActor = Dali::Actor::New();
586 mEventActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
587 mEventActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
588 mIndicatorActor.Add( mEventActor );
590 // Attach pan gesture to find flick down during hiding.
591 // It can prevent the problem that scrollview gets pan gesture even indicator area is touched,
592 // since it consumes the pan gesture in advance.
593 mPanDetector = Dali::PanGestureDetector::New();
594 mPanDetector.DetectedSignal().Connect( this, &Indicator::OnPan );
595 mPanDetector.Attach( mEventActor );
599 // register indicator to accessibility adaptor
600 Dali::AccessibilityAdaptor accessibilityAdaptor = AccessibilityAdaptor::Get();
601 if(accessibilityAdaptor)
603 AccessibilityAdaptor::GetImplementation( accessibilityAdaptor ).SetIndicator( this );
605 // hide the indicator by default
606 mIndicatorActor.SetVisible( false );
608 // create impl to handle ecore event
609 mImpl = new Impl(this);
612 Indicator::~Indicator()
622 mEventActor.TouchSignal().Disconnect( this, &Indicator::OnTouch );
627 void Indicator::SetAdaptor(Adaptor* adaptor)
630 mIndicatorBuffer->SetAdaptor( adaptor );
633 Dali::Actor Indicator::GetActor()
635 return mIndicatorActor;
638 void Indicator::Open( Dali::Window::WindowOrientation orientation )
640 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
642 // Calls from Window should be set up to ensure we are in a
643 // disconnected state before opening a second time.
644 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
646 mOrientation = orientation;
650 // Change background visibility depending on orientation
651 if( mOrientation == Dali::Window::LANDSCAPE || mOrientation == Dali::Window::LANDSCAPE_INVERSE )
653 if( mBackgroundRenderer )
655 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
656 mBackgroundVisible = false;
661 SetOpacityMode( mOpacityMode );
665 void Indicator::Close()
667 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s", STATE_DEBUG_STRING(mState) );
669 if( mState == CONNECTED )
672 if( mObserver != NULL )
674 mObserver->IndicatorClosed( this );
678 Dali::Texture emptyTexture;
679 SetForegroundImage( emptyTexture );
682 void Indicator::SetOpacityMode( Dali::Window::IndicatorBgOpacity mode )
686 Dali::Geometry geometry = CreateBackgroundGeometry();
689 if( mBackgroundRenderer )
691 if( mBackgroundRenderer.GetGeometry() != geometry )
693 mBackgroundRenderer.SetGeometry( geometry );
698 if( !mBackgroundShader )
700 mBackgroundShader = Dali::Shader::New( BACKGROUND_VERTEX_SHADER, BACKGROUND_FRAGMENT_SHADER, Dali::Shader::Hint::OUTPUT_IS_TRANSPARENT );
703 mBackgroundRenderer = Dali::Renderer::New( geometry, mBackgroundShader );
706 if( !mBackgroundVisible )
708 mIndicatorContentActor.AddRenderer( mBackgroundRenderer );
709 mBackgroundVisible = true;
712 else if( mBackgroundRenderer )
714 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
715 mBackgroundVisible = false;
720 void Indicator::SetVisible( Dali::Window::IndicatorVisibleMode visibleMode, bool forceUpdate )
722 if ( visibleMode != mVisible || forceUpdate )
724 // If we were previously hidden, then we should update the image data before we display the indicator
725 if ( mVisible == Dali::Window::INVISIBLE )
727 UpdateImageData( mCurrentSharedFile );
730 if ( visibleMode == Dali::Window::INVISIBLE )
732 if (mServerConnection)
734 mServerConnection->SendEvent( OP_HIDE, NULL, 0 );
739 mIndicatorActor.SetVisible( true );
741 if( mServerConnection )
743 mServerConnection->SendEvent( OP_SHOW, NULL, 0 );
747 mVisible = visibleMode;
750 if( mForegroundRenderer && mForegroundRenderer.GetTextures().GetTexture( 0u ) )
752 if( CheckVisibleState() && mVisible == Dali::Window::AUTO )
755 ShowIndicator( AUTO_INDICATOR_STAY_DURATION /* stay n sec */ );
757 else if( CheckVisibleState() && mVisible == Dali::Window::VISIBLE )
760 ShowIndicator( KEEP_SHOWING );
765 ShowIndicator( HIDE_NOW );
775 bool Indicator::IsConnected()
777 return ( mState == CONNECTED );
780 bool Indicator::SendMessage( int messageDomain, int messageId, const void *data, int size )
784 return mServerConnection->SendEvent( OP_MSG, messageDomain, messageId, data, size );
792 bool Indicator::OnTouch(Dali::Actor indicator, const Dali::TouchData& touchData)
794 if( mServerConnection )
796 // Send touch event to indicator server when indicator is showing
797 if( CheckVisibleState() || mIsShowing )
799 switch( touchData.GetState(0) )
801 case Dali::PointState::DOWN:
803 IpcDataEvMouseMove ipcMove( touchData.GetLocalPosition(0), touchData.GetTime() );
804 IpcDataEvMouseDown ipcDown( touchData.GetTime() );
805 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
806 mServerConnection->SendEvent( OP_EV_MOUSE_DOWN, &ipcDown, sizeof(ipcDown) );
808 if( mVisible == Dali::Window::AUTO )
810 // Stop hiding indicator
811 ShowIndicator( KEEP_SHOWING );
816 case Dali::PointState::MOTION:
818 IpcDataEvMouseMove ipcMove( touchData.GetLocalPosition(0), touchData.GetTime() );
819 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
823 case Dali::PointState::UP:
824 case Dali::PointState::INTERRUPTED:
826 IpcDataEvMouseUp ipcUp( touchData.GetTime() );
827 mServerConnection->SendEvent( OP_EV_MOUSE_UP, &ipcUp, sizeof(ipcUp) );
829 if( mVisible == Dali::Window::AUTO )
832 ShowIndicator( 0.5f /* hide after 0.5 sec */ );
837 case Dali::TouchPoint::Leave:
839 IpcDataEvMouseMove ipcMove( touchData.GetLocalPosition(0), touchData.GetTime() );
840 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
841 IpcDataEvMouseUp ipcOut( touchData.GetTime() );
842 mServerConnection->SendEvent( OP_EV_MOUSE_OUT, &ipcOut, sizeof(ipcOut) );
857 bool Indicator::Connect()
859 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
861 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
863 bool connected = false;
865 mServerConnection = new ServerConnection( INDICATOR_SERVICE_NAME, 0, false, this );
866 if( mServerConnection )
868 connected = mServerConnection->IsConnected();
871 delete mServerConnection;
872 mServerConnection = NULL;
878 StartReconnectionTimer();
888 void Indicator::StartReconnectionTimer()
890 if( ! mReconnectTimer )
892 mReconnectTimer = Dali::Timer::New(1000);
893 mConnection.DisconnectAll();
894 mReconnectTimer.TickSignal().Connect( mConnection, &Indicator::OnReconnectTimer );
896 mReconnectTimer.Start();
899 bool Indicator::OnReconnectTimer()
903 if( mState == DISCONNECTED )
914 void Indicator::Disconnect()
916 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
918 mState = DISCONNECTED;
920 delete mServerConnection;
921 mServerConnection = NULL;
923 ClearSharedFileInfo();
926 void Indicator::Resize( int width, int height )
937 if( mImageWidth != width || mImageHeight != height )
940 mImageHeight = height;
942 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
943 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
944 mEventActor.SetSize(mImageWidth, mImageHeight);
949 void Indicator::SetLockFileInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
951 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
953 // epcEvent->ref == w
954 // epcEvent->ref_to == h
955 // epcEvent->response == buffer num
956 // epcEvent->data = lockfile + nul byte
958 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) && (epcEvent->data) &&
959 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
961 int n = epcEvent->response;
963 if( n >= 0 && n < SHARED_FILE_NUMBER )
965 mCurrentSharedFile = n;
967 mSharedFileInfo[n].mImageWidth = epcEvent->ref;
968 mSharedFileInfo[n].mImageHeight = epcEvent->ref_to;
970 mSharedFileInfo[n].mLockFileName.clear();
972 mSharedFileInfo[n].mLockFileName = static_cast< char* >( epcEvent->data );
974 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetLockFileInfo: buffer num = %d, w = %d, h = %d, lock = %s\n",
975 n, mSharedFileInfo[n].mImageWidth, mSharedFileInfo[n].mImageHeight, mSharedFileInfo[n].mLockFileName.c_str() );
980 void Indicator::SetSharedImageInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
982 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
984 // epcEvent->ref == shm id
985 // epcEvent->ref_to == shm num
986 // epcEvent->response == buffer num
987 // epcEvent->data = shm ref string + nul byte
989 if ( (epcEvent->data) &&
990 (epcEvent->size > 0) &&
991 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
993 int n = epcEvent->response;
995 if( n >= 0 && n < SHARED_FILE_NUMBER )
997 mCurrentSharedFile = n;
999 mSharedFileInfo[n].mSharedFileName.clear();
1001 mSharedFileInfo[n].mSharedFileName = static_cast< char* >( epcEvent->data );
1003 mSharedFileInfo[n].mSharedFileID = epcEvent->ref;
1004 mSharedFileInfo[n].mSharedFileNumber = epcEvent->ref_to;
1006 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetSharedImageInfo: buffer num %d, shared file = %s, id = %d, num = %d\n",
1007 n, mSharedFileInfo[n].mSharedFileName.c_str(), mSharedFileInfo[n].mSharedFileID, mSharedFileInfo[n].mSharedFileNumber );
1012 void Indicator::LoadSharedImage( Ecore_Ipc_Event_Server_Data *epcEvent )
1014 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1016 // epcEvent->ref == alpha
1017 // epcEvent->ref_to == sys
1018 // epcEvent->response == buffer num
1020 if ( mSharedBufferType != BUFFER_TYPE_SHM )
1025 int n = epcEvent->response;
1027 if( n >= 0 && n < SHARED_FILE_NUMBER )
1029 mCurrentSharedFile = n;
1031 delete mSharedFileInfo[n].mSharedFile;
1032 mSharedFileInfo[n].mSharedFile = NULL;
1034 delete mSharedFileInfo[n].mLock;
1035 mSharedFileInfo[n].mLock = NULL;
1037 std::stringstream sharedFileID;
1038 std::stringstream sharedFileNumber;
1040 sharedFileID << mSharedFileInfo[n].mSharedFileID;
1041 sharedFileNumber << mSharedFileInfo[n].mSharedFileNumber;
1043 std::string sharedFilename = "/" + mSharedFileInfo[n].mSharedFileName + "-" + sharedFileID.str() + "." + sharedFileNumber.str();
1045 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "LoadSharedImage: file name = %s\n", sharedFilename.c_str() );
1047 mSharedFileInfo[n].mSharedFile = SharedFile::New( sharedFilename.c_str(), mSharedFileInfo[n].mImageWidth * mSharedFileInfo[n].mImageWidth * 4, true );
1048 if( mSharedFileInfo[n].mSharedFile != NULL )
1050 mSharedFileInfo[n].mLock = new Indicator::LockFile( mSharedFileInfo[n].mLockFileName );
1051 if( mSharedFileInfo[n].mLock->RetrieveAndClearErrorStatus() )
1053 DALI_LOG_ERROR( "### Indicator error: Cannot open lock file %s ###\n", mSharedFileInfo[n].mLockFileName.c_str() );
1056 CreateNewImage( n );
1062 void Indicator::LoadPixmapImage( Ecore_Ipc_Event_Server_Data *epcEvent )
1064 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1066 // epcEvent->ref == pixmap id
1067 // epcEvent->ref_to == type
1068 // epcEvent->response == buffer num
1070 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) )
1072 mSharedBufferType = (BufferType)(epcEvent->ref_to);
1074 ClearSharedFileInfo();
1076 mPixmap = static_cast<PixmapId>(epcEvent->ref);
1077 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "mPixmap [%x]", mPixmap);
1079 CreateNewPixmapImage();
1084 void Indicator::UpdateTopMargin()
1086 int newMargin = (mVisible == Dali::Window::VISIBLE && mOpacityMode == Dali::Window::OPAQUE) ? mImageHeight : 0;
1087 if (mTopMargin != newMargin)
1089 mTopMargin = newMargin;
1090 mAdaptor->IndicatorSizeChanged( mTopMargin );
1094 void Indicator::UpdateVisibility()
1096 if( CheckVisibleState() )
1098 // set default indicator type (enable the quick panel)
1099 OnIndicatorTypeChanged( INDICATOR_TYPE_1 );
1103 // set default indicator type (disable the quick panel)
1104 OnIndicatorTypeChanged( INDICATOR_TYPE_2 );
1109 mIndicatorContentActor.SetPosition( 0.0f, -mImageHeight, 0.0f );
1112 SetVisible(mVisible, true);
1115 void Indicator::UpdateImageData( int bufferNumber )
1117 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s mVisible: %s", STATE_DEBUG_STRING(mState), mVisible?"T":"F" );
1119 if( mState == CONNECTED && mVisible )
1123 // in case of shm indicator (not pixmap), not sure we can skip it when mIsShowing is false
1124 CopyToBuffer( bufferNumber );
1130 mAdaptor->RequestUpdateOnce();
1136 bool Indicator::CopyToBuffer( int bufferNumber )
1138 bool success = false;
1140 if( mSharedFileInfo[bufferNumber].mLock )
1142 Indicator::ScopedLock scopedLock(mSharedFileInfo[bufferNumber].mLock);
1143 if( mSharedFileInfo[bufferNumber].mLock->RetrieveAndClearErrorStatus() )
1147 else if( scopedLock.IsLocked() )
1149 unsigned char *src = mSharedFileInfo[bufferNumber].mSharedFile->GetAddress();
1150 size_t size = static_cast< size_t >( mSharedFileInfo[bufferNumber].mImageWidth ) * mSharedFileInfo[bufferNumber].mImageHeight * 4;
1152 if( mIndicatorBuffer->UpdatePixels( src, size ) )
1154 mAdaptor->RequestUpdateOnce();
1163 void Indicator::CreateNewPixmapImage()
1165 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mImageWidth, mImageHeight );
1166 Dali::NativeImageSourcePtr nativeImageSource = Dali::NativeImageSource::New( mPixmap );
1168 #ifdef ENABLE_INDICATOR_IMAGE_SAVING
1169 SaveIndicatorImage( nativeImageSource );
1172 if( nativeImageSource )
1174 Dali::Texture texture = Dali::Texture::New( *nativeImageSource );
1175 SetForegroundImage( texture );
1176 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
1177 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
1178 mEventActor.SetSize( mImageWidth, mImageHeight );
1183 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1185 if( mObserver != NULL )
1187 mObserver->IndicatorClosed( this );
1189 // Don't do connection in this callback - strange things happen!
1190 StartReconnectionTimer();
1194 void Indicator::CreateNewImage( int bufferNumber )
1196 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight );
1197 mIndicatorBuffer = new IndicatorBuffer( mAdaptor, mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight, Pixel::BGRA8888 );
1198 bool success = false;
1200 if( CopyToBuffer( bufferNumber ) ) // Only create images if we have valid image buffer
1202 Dali::Texture texture = Dali::Texture::New( mIndicatorBuffer->GetNativeImage() );
1205 SetForegroundImage( texture );
1212 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1214 if( mObserver != NULL )
1216 mObserver->IndicatorClosed( this );
1218 // Don't do connection in this callback - strange things happen!
1219 StartReconnectionTimer();
1223 Dali::Geometry Indicator::CreateBackgroundGeometry()
1225 switch( mOpacityMode )
1227 case Dali::Window::TRANSLUCENT:
1228 if( !mTranslucentGeometry )
1230 // Construct 5 interval mesh
1244 struct BackgroundVertex
1250 unsigned int numVertices = 2 * ( NUM_GRADIENT_INTERVALS + 1 );
1251 BackgroundVertex vertices[ numVertices ];
1254 float delta = 1.0f / NUM_GRADIENT_INTERVALS;
1255 BackgroundVertex* currentVertex = vertices;
1256 for( int y = 0; y < NUM_GRADIENT_INTERVALS + 1; ++y, d += delta )
1258 currentVertex->mPosition = Vector2( -0.5f, d );
1259 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1262 currentVertex->mPosition = Vector2( 0.5f, d );
1263 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1268 unsigned int numIndices = 2 * 3 * NUM_GRADIENT_INTERVALS;
1269 unsigned short indices[ numIndices ];
1271 unsigned short* currentIndex = indices;
1272 for( int y = 0; y < NUM_GRADIENT_INTERVALS; ++y )
1274 *currentIndex++ = (2 * y);
1275 *currentIndex++ = (2 * y) + 3;
1276 *currentIndex++ = (2 * y) + 1;
1278 *currentIndex++ = (2 * y);
1279 *currentIndex++ = (2 * y) + 2;
1280 *currentIndex++ = (2 * y) + 3;
1283 Dali::Property::Map vertexFormat;
1284 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1285 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1286 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1287 vertexPropertyBuffer.SetData( vertices, numVertices );
1289 // Create the geometry object
1290 mTranslucentGeometry = Dali::Geometry::New();
1291 mTranslucentGeometry.AddVertexBuffer( vertexPropertyBuffer );
1292 mTranslucentGeometry.SetIndexBuffer( &indices[0], numIndices );
1295 return mTranslucentGeometry;
1296 case Dali::Window::OPAQUE:
1298 if( !mSolidGeometry )
1301 struct BackgroundVertex
1307 BackgroundVertex vertices[ 4 ] = { { Vector2( -0.5f, -0.5f ), 1.0f }, { Vector2( 0.5f, -0.5f ), 1.0f },
1308 { Vector2( -0.5f, 0.5f ), 1.0f }, { Vector2( 0.5f, 0.5f ), 1.0f } };
1311 unsigned short indices[ 6 ] = { 0, 3, 1, 0, 2, 3 };
1313 Dali::Property::Map vertexFormat;
1314 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1315 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1316 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1317 vertexPropertyBuffer.SetData( vertices, 4 );
1320 // Create the geometry object
1321 mSolidGeometry = Dali::Geometry::New();
1322 mSolidGeometry.AddVertexBuffer( vertexPropertyBuffer );
1323 mSolidGeometry.SetIndexBuffer( &indices[0], 6 );
1326 return mSolidGeometry;
1327 case Dali::Window::TRANSPARENT:
1331 return Dali::Geometry();
1334 void Indicator::SetForegroundImage( Dali::Texture texture )
1336 if( !mForegroundRenderer && texture )
1339 Dali::Shader shader = Dali::Shader::New( FOREGROUND_VERTEX_SHADER, FOREGROUND_FRAGMENT_SHADER );
1341 // Create renderer from geometry and material
1342 Dali::Geometry quad = CreateQuadGeometry();
1343 mForegroundRenderer = Dali::Renderer::New( quad, shader );
1344 // Make sure the foreground stays in front of the background
1345 mForegroundRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, 1.f );
1347 // Set blend function
1348 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_RGB, Dali::BlendFactor::ONE );
1349 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_RGB, Dali::BlendFactor::ONE_MINUS_SRC_ALPHA );
1350 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_ALPHA, Dali::BlendFactor::ONE );
1351 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_ALPHA, Dali::BlendFactor::ONE );
1353 // Create a texture-set and add to renderer
1355 Dali::TextureSet textureSet = Dali::TextureSet::New();
1356 textureSet.SetTexture( 0u, texture );
1357 mForegroundRenderer.SetTextures( textureSet );
1359 mIndicatorContentActor.AddRenderer( mForegroundRenderer );
1361 else if( mForegroundRenderer )
1363 Dali::TextureSet textureSet = mForegroundRenderer.GetTextures();
1364 textureSet.SetTexture( 0u, texture );
1367 if( mImageWidth == 0 && mImageHeight == 0 && texture)
1369 Resize( texture.GetWidth(), texture.GetHeight() );
1373 void Indicator::OnIndicatorTypeChanged( Type indicatorType )
1375 if( mObserver != NULL )
1377 mObserver->IndicatorTypeChanged( indicatorType );
1381 void Indicator::DataReceived( void* event )
1383 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1384 Ecore_Ipc_Event_Server_Data *epcEvent = static_cast<Ecore_Ipc_Event_Server_Data *>( event );
1386 switch( epcEvent->minor )
1390 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE\n" );
1393 mAdaptor->RequestUpdateOnce();
1397 case OP_UPDATE_DONE:
1399 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE_DONE [%d]\n", epcEvent->response );
1400 // epcEvent->response == display buffer #
1401 UpdateImageData( epcEvent->response );
1406 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF0\n" );
1407 SetSharedImageInfo( epcEvent );
1412 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF1\n" );
1413 SetLockFileInfo( epcEvent );
1418 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF2\n" );
1419 LoadSharedImage( epcEvent );
1424 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_PIXMAP_REF\n" );
1425 LoadPixmapImage( epcEvent );
1430 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_RESIZE\n" );
1432 if( (epcEvent->data) && (epcEvent->size >= (int)sizeof(IpcDataResize)) )
1434 IpcDataResize *newSize = static_cast<IpcDataResize*>( epcEvent->data );
1435 Resize( newSize->w, newSize->h );
1441 int msgDomain = epcEvent->ref;
1442 int msgId = epcEvent->ref_to;
1444 void *msgData = NULL;
1445 int msgDataSize = 0;
1446 msgData = epcEvent->data;
1447 msgDataSize = epcEvent->size;
1449 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT. msgDomain = %d\n", msgDomain );
1451 if( msgDomain == MSG_DOMAIN_CONTROL_INDICATOR )
1455 case MSG_ID_INDICATOR_TYPE:
1457 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT, INDICATOR_TYPE\n" );
1458 Type* indicatorType = static_cast<Type*>( epcEvent->data );
1459 OnIndicatorTypeChanged( *indicatorType );
1463 case MSG_ID_INDICATOR_START_ANIMATION:
1465 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: MSG_ID_INDICATOR_START_ANIMATION\n" );
1467 if (msgDataSize != (int)sizeof(IpcIndicatorDataAnimation))
1469 DALI_LOG_ERROR("Message data is incorrect\n");
1473 IpcIndicatorDataAnimation *animData = static_cast<IpcIndicatorDataAnimation*>(msgData);
1475 if(!CheckVisibleState())
1477 ShowIndicator( animData->duration /* n sec */ );
1488 void Indicator::ConnectionClosed()
1490 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1492 // Will get this callback if the server connection failed to start up.
1493 delete mServerConnection;
1494 mServerConnection = NULL;
1495 mState = DISCONNECTED;
1497 // Attempt to re-connect
1501 bool Indicator::CheckVisibleState()
1503 if( mOrientation == Dali::Window::LANDSCAPE
1504 || mOrientation == Dali::Window::LANDSCAPE_INVERSE
1505 || (mVisible == Dali::Window::INVISIBLE)
1506 || (mVisible == Dali::Window::AUTO && !mIsShowing) )
1514 void Indicator::ClearSharedFileInfo()
1516 for( int i = 0; i < SHARED_FILE_NUMBER; i++ )
1518 delete mSharedFileInfo[i].mLock;
1519 mSharedFileInfo[i].mLock = NULL;
1521 delete mSharedFileInfo[i].mSharedFile;
1522 mSharedFileInfo[i].mSharedFile = NULL;
1524 mSharedFileInfo[i].mLockFileName.clear();
1525 mSharedFileInfo[i].mSharedFileName.clear();
1530 * duration can be this
1534 * KEEP_SHOWING = -1,
1538 void Indicator::ShowIndicator(float duration)
1540 if( !mIndicatorAnimation )
1542 mIndicatorAnimation = Dali::Animation::New(SLIDING_ANIMATION_DURATION);
1543 mIndicatorAnimation.FinishedSignal().Connect(this, &Indicator::OnAnimationFinished);
1546 if(mIsShowing && !EqualsZero(duration))
1548 // If need to show during showing, do nothing.
1549 // In 2nd phase (below) will update timer
1551 else if(!mIsShowing && mIsAnimationPlaying && EqualsZero(duration))
1553 // If need to hide during hiding or hidden already, do nothing
1557 mIndicatorAnimation.Clear();
1559 if( EqualsZero(duration) )
1561 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, -mImageHeight, 0), Dali::AlphaFunction::EASE_OUT );
1565 OnIndicatorTypeChanged( INDICATOR_TYPE_2 ); // un-toucable
1569 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, 0, 0), Dali::AlphaFunction::EASE_OUT );
1573 OnIndicatorTypeChanged( INDICATOR_TYPE_1 ); // touchable
1576 mIndicatorAnimation.Play();
1577 mIsAnimationPlaying = true;
1584 mShowTimer = Dali::Timer::New(1000 * duration);
1585 mShowTimer.TickSignal().Connect(this, &Indicator::OnShowTimer);
1587 mShowTimer.SetInterval(1000* duration);
1590 if( mVisible == Dali::Window::AUTO )
1592 // check the stage touch
1593 Dali::Stage::GetCurrent().TouchSignal().Connect( this, &Indicator::OnStageTouch );
1598 if(mShowTimer && mShowTimer.IsRunning())
1603 if( mVisible == Dali::Window::AUTO )
1605 // check the stage touch
1606 Dali::Stage::GetCurrent().TouchSignal().Disconnect( this, &Indicator::OnStageTouch );
1611 bool Indicator::OnShowTimer()
1613 // after time up, hide indicator
1614 ShowIndicator( HIDE_NOW );
1619 void Indicator::OnAnimationFinished(Dali::Animation& animation)
1621 mIsAnimationPlaying = false;
1622 // once animation is finished and indicator is hidden, take it off stage
1623 if( mObserver != NULL )
1625 mObserver->IndicatorVisibilityChanged( mIsShowing ); // is showing?
1629 void Indicator::OnPan( Dali::Actor actor, const Dali::PanGesture& gesture )
1631 // Nothing to do, but we still want to consume pan
1634 void Indicator::OnStageTouch(const Dali::TouchData& touchData)
1636 // when stage is touched while indicator is showing temporary, hide it
1637 if( mIsShowing && ( CheckVisibleState() == false || mVisible == Dali::Window::AUTO ) )
1639 switch( touchData.GetState(0) )
1641 case Dali::PointState::DOWN:
1643 // if touch point is inside the indicator, indicator is not hidden
1644 if( mImageHeight < int( touchData.GetScreenPosition(0).y ) )
1646 ShowIndicator( HIDE_NOW );