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 )
562 mIndicatorContentActor = Dali::Actor::New();
563 mIndicatorContentActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
564 mIndicatorContentActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
566 // Indicator image handles the touch event including "leave"
567 mIndicatorContentActor.SetLeaveRequired( true );
568 mIndicatorContentActor.TouchedSignal().Connect( this, &Indicator::OnTouched );
569 mIndicatorContentActor.SetColor( Color::BLACK );
571 mIndicatorActor = Dali::Actor::New();
572 mIndicatorActor.Add( mIndicatorContentActor );
574 // Event handler to find out flick down gesture
575 mEventActor = Dali::Actor::New();
576 mEventActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
577 mEventActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
578 mIndicatorActor.Add( mEventActor );
580 // Attach pan gesture to find flick down during hiding.
581 // It can prevent the problem that scrollview gets pan gesture even indicator area is touched,
582 // since it consumes the pan gesture in advance.
583 mPanDetector = Dali::PanGestureDetector::New();
584 mPanDetector.DetectedSignal().Connect( this, &Indicator::OnPan );
585 mPanDetector.Attach( mEventActor );
589 // register indicator to accessibility adaptor
590 Dali::AccessibilityAdaptor accessibilityAdaptor = AccessibilityAdaptor::Get();
591 if(accessibilityAdaptor)
593 AccessibilityAdaptor::GetImplementation( accessibilityAdaptor ).SetIndicator( this );
595 // hide the indicator by default
596 mIndicatorActor.SetVisible( false );
598 // create impl to handle ecore event
599 mImpl = new Impl(this);
602 Indicator::~Indicator()
612 mEventActor.TouchedSignal().Disconnect( this, &Indicator::OnTouched );
617 void Indicator::SetAdaptor(Adaptor* adaptor)
620 mIndicatorBuffer->SetAdaptor( adaptor );
623 Dali::Actor Indicator::GetActor()
625 return mIndicatorActor;
628 void Indicator::Open( Dali::Window::WindowOrientation orientation )
630 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
632 // Calls from Window should be set up to ensure we are in a
633 // disconnected state before opening a second time.
634 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
636 mOrientation = orientation;
640 // Change background visibility depending on orientation
641 if( mOrientation == Dali::Window::LANDSCAPE || mOrientation == Dali::Window::LANDSCAPE_INVERSE )
643 if( mBackgroundRenderer )
645 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
646 mBackgroundVisible = false;
651 SetOpacityMode( mOpacityMode );
655 void Indicator::Close()
657 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s", STATE_DEBUG_STRING(mState) );
659 if( mState == CONNECTED )
662 if( mObserver != NULL )
664 mObserver->IndicatorClosed( this );
668 Dali::Texture emptyTexture;
669 SetForegroundImage( emptyTexture );
672 void Indicator::SetOpacityMode( Dali::Window::IndicatorBgOpacity mode )
676 Dali::Geometry geometry = CreateBackgroundGeometry();
679 if( mBackgroundRenderer )
681 if( mBackgroundRenderer.GetGeometry() != geometry )
683 mBackgroundRenderer.SetGeometry( geometry );
688 if( !mBackgroundShader )
690 mBackgroundShader = Dali::Shader::New( BACKGROUND_VERTEX_SHADER, BACKGROUND_FRAGMENT_SHADER, Dali::Shader::Hint::OUTPUT_IS_TRANSPARENT );
693 mBackgroundRenderer = Dali::Renderer::New( geometry, mBackgroundShader );
696 if( !mBackgroundVisible )
698 mIndicatorContentActor.AddRenderer( mBackgroundRenderer );
699 mBackgroundVisible = true;
702 else if( mBackgroundRenderer )
704 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
705 mBackgroundVisible = false;
709 void Indicator::SetVisible( Dali::Window::IndicatorVisibleMode visibleMode, bool forceUpdate )
711 if ( visibleMode != mVisible || forceUpdate )
713 // If we were previously hidden, then we should update the image data before we display the indicator
714 if ( mVisible == Dali::Window::INVISIBLE )
716 UpdateImageData( mCurrentSharedFile );
719 if ( visibleMode == Dali::Window::INVISIBLE )
721 if (mServerConnection)
723 mServerConnection->SendEvent( OP_HIDE, NULL, 0 );
728 mIndicatorActor.SetVisible( true );
730 if( mServerConnection )
732 mServerConnection->SendEvent( OP_SHOW, NULL, 0 );
736 mVisible = visibleMode;
738 if( mForegroundRenderer && mForegroundRenderer.GetTextures().GetTexture( 0u ) )
740 if( CheckVisibleState() && mVisible == Dali::Window::AUTO )
743 ShowIndicator( AUTO_INDICATOR_STAY_DURATION /* stay n sec */ );
745 else if( CheckVisibleState() && mVisible == Dali::Window::VISIBLE )
748 ShowIndicator( KEEP_SHOWING );
753 ShowIndicator( HIDE_NOW );
763 bool Indicator::IsConnected()
765 return ( mState == CONNECTED );
768 bool Indicator::SendMessage( int messageDomain, int messageId, const void *data, int size )
772 return mServerConnection->SendEvent( OP_MSG, messageDomain, messageId, data, size );
780 bool Indicator::OnTouched(Dali::Actor indicator, const Dali::TouchEvent& touchEvent)
782 if( mServerConnection )
784 const TouchPoint& touchPoint = touchEvent.GetPoint( 0 );
786 // Send touch event to indicator server when indicator is showing
787 if( CheckVisibleState() || mIsShowing )
789 switch( touchPoint.state )
791 case Dali::PointState::DOWN:
793 IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
794 IpcDataEvMouseDown ipcDown( touchEvent.time );
795 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
796 mServerConnection->SendEvent( OP_EV_MOUSE_DOWN, &ipcDown, sizeof(ipcDown) );
798 if( mVisible == Dali::Window::AUTO )
800 // Stop hiding indicator
801 ShowIndicator( KEEP_SHOWING );
806 case Dali::PointState::MOTION:
808 IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
809 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
813 case Dali::PointState::UP:
814 case Dali::PointState::INTERRUPTED:
816 IpcDataEvMouseUp ipcUp( touchEvent.time );
817 mServerConnection->SendEvent( OP_EV_MOUSE_UP, &ipcUp, sizeof(ipcUp) );
819 if( mVisible == Dali::Window::AUTO )
822 ShowIndicator( 0.5f /* hide after 0.5 sec */ );
827 case Dali::TouchPoint::Leave:
829 IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
830 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
831 IpcDataEvMouseUp ipcOut( touchEvent.time );
832 mServerConnection->SendEvent( OP_EV_MOUSE_OUT, &ipcOut, sizeof(ipcOut) );
847 bool Indicator::Connect()
849 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
851 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
853 bool connected = false;
855 mServerConnection = new ServerConnection( INDICATOR_SERVICE_NAME, 0, false, this );
856 if( mServerConnection )
858 connected = mServerConnection->IsConnected();
861 delete mServerConnection;
862 mServerConnection = NULL;
868 StartReconnectionTimer();
878 void Indicator::StartReconnectionTimer()
880 if( ! mReconnectTimer )
882 mReconnectTimer = Dali::Timer::New(1000);
883 mConnection.DisconnectAll();
884 mReconnectTimer.TickSignal().Connect( mConnection, &Indicator::OnReconnectTimer );
886 mReconnectTimer.Start();
889 bool Indicator::OnReconnectTimer()
893 if( mState == DISCONNECTED )
904 void Indicator::Disconnect()
906 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
908 mState = DISCONNECTED;
910 delete mServerConnection;
911 mServerConnection = NULL;
913 ClearSharedFileInfo();
916 void Indicator::Resize( int width, int height )
927 if( mImageWidth != width || mImageHeight != height )
930 mImageHeight = height;
932 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
933 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
934 mEventActor.SetSize(mImageWidth, mImageHeight);
938 void Indicator::SetLockFileInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
940 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
942 // epcEvent->ref == w
943 // epcEvent->ref_to == h
944 // epcEvent->response == buffer num
945 // epcEvent->data = lockfile + nul byte
947 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) && (epcEvent->data) &&
948 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
950 int n = epcEvent->response;
952 if( n >= 0 && n < SHARED_FILE_NUMBER )
954 mCurrentSharedFile = n;
956 mSharedFileInfo[n].mImageWidth = epcEvent->ref;
957 mSharedFileInfo[n].mImageHeight = epcEvent->ref_to;
959 mSharedFileInfo[n].mLockFileName.clear();
961 mSharedFileInfo[n].mLockFileName = static_cast< char* >( epcEvent->data );
963 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetLockFileInfo: buffer num = %d, w = %d, h = %d, lock = %s\n",
964 n, mSharedFileInfo[n].mImageWidth, mSharedFileInfo[n].mImageHeight, mSharedFileInfo[n].mLockFileName.c_str() );
969 void Indicator::SetSharedImageInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
971 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
973 // epcEvent->ref == shm id
974 // epcEvent->ref_to == shm num
975 // epcEvent->response == buffer num
976 // epcEvent->data = shm ref string + nul byte
978 if ( (epcEvent->data) &&
979 (epcEvent->size > 0) &&
980 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
982 int n = epcEvent->response;
984 if( n >= 0 && n < SHARED_FILE_NUMBER )
986 mCurrentSharedFile = n;
988 mSharedFileInfo[n].mSharedFileName.clear();
990 mSharedFileInfo[n].mSharedFileName = static_cast< char* >( epcEvent->data );
992 mSharedFileInfo[n].mSharedFileID = epcEvent->ref;
993 mSharedFileInfo[n].mSharedFileNumber = epcEvent->ref_to;
995 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetSharedImageInfo: buffer num %d, shared file = %s, id = %d, num = %d\n",
996 n, mSharedFileInfo[n].mSharedFileName.c_str(), mSharedFileInfo[n].mSharedFileID, mSharedFileInfo[n].mSharedFileNumber );
1001 void Indicator::LoadSharedImage( Ecore_Ipc_Event_Server_Data *epcEvent )
1003 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1005 // epcEvent->ref == alpha
1006 // epcEvent->ref_to == sys
1007 // epcEvent->response == buffer num
1009 if ( mSharedBufferType != BUFFER_TYPE_SHM )
1014 int n = epcEvent->response;
1016 if( n >= 0 && n < SHARED_FILE_NUMBER )
1018 mCurrentSharedFile = n;
1020 delete mSharedFileInfo[n].mSharedFile;
1021 mSharedFileInfo[n].mSharedFile = NULL;
1023 delete mSharedFileInfo[n].mLock;
1024 mSharedFileInfo[n].mLock = NULL;
1026 std::stringstream sharedFileID;
1027 std::stringstream sharedFileNumber;
1029 sharedFileID << mSharedFileInfo[n].mSharedFileID;
1030 sharedFileNumber << mSharedFileInfo[n].mSharedFileNumber;
1032 std::string sharedFilename = "/" + mSharedFileInfo[n].mSharedFileName + "-" + sharedFileID.str() + "." + sharedFileNumber.str();
1034 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "LoadSharedImage: file name = %s\n", sharedFilename.c_str() );
1036 mSharedFileInfo[n].mSharedFile = SharedFile::New( sharedFilename.c_str(), mSharedFileInfo[n].mImageWidth * mSharedFileInfo[n].mImageWidth * 4, true );
1037 if( mSharedFileInfo[n].mSharedFile != NULL )
1039 mSharedFileInfo[n].mLock = new Indicator::LockFile( mSharedFileInfo[n].mLockFileName );
1040 if( mSharedFileInfo[n].mLock->RetrieveAndClearErrorStatus() )
1042 DALI_LOG_ERROR( "### Indicator error: Cannot open lock file %s ###\n", mSharedFileInfo[n].mLockFileName.c_str() );
1045 CreateNewImage( n );
1051 void Indicator::LoadPixmapImage( Ecore_Ipc_Event_Server_Data *epcEvent )
1053 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1055 // epcEvent->ref == pixmap id
1056 // epcEvent->ref_to == type
1057 // epcEvent->response == buffer num
1059 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) )
1061 mSharedBufferType = (BufferType)(epcEvent->ref_to);
1063 ClearSharedFileInfo();
1065 mPixmap = static_cast<PixmapId>(epcEvent->ref);
1066 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "mPixmap [%x]", mPixmap);
1068 CreateNewPixmapImage();
1073 void Indicator::UpdateVisibility()
1075 if( CheckVisibleState() )
1077 // set default indicator type (enable the quick panel)
1078 OnIndicatorTypeChanged( INDICATOR_TYPE_1 );
1082 // set default indicator type (disable the quick panel)
1083 OnIndicatorTypeChanged( INDICATOR_TYPE_2 );
1088 mIndicatorContentActor.SetPosition( 0.0f, -mImageHeight, 0.0f );
1091 SetVisible(mVisible, true);
1094 void Indicator::UpdateImageData( int bufferNumber )
1096 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s mVisible: %s", STATE_DEBUG_STRING(mState), mVisible?"T":"F" );
1098 if( mState == CONNECTED && mVisible )
1102 // in case of shm indicator (not pixmap), not sure we can skip it when mIsShowing is false
1103 CopyToBuffer( bufferNumber );
1109 mAdaptor->RequestUpdateOnce();
1115 bool Indicator::CopyToBuffer( int bufferNumber )
1117 bool success = false;
1119 if( mSharedFileInfo[bufferNumber].mLock )
1121 Indicator::ScopedLock scopedLock(mSharedFileInfo[bufferNumber].mLock);
1122 if( mSharedFileInfo[bufferNumber].mLock->RetrieveAndClearErrorStatus() )
1126 else if( scopedLock.IsLocked() )
1128 unsigned char *src = mSharedFileInfo[bufferNumber].mSharedFile->GetAddress();
1129 size_t size = mSharedFileInfo[bufferNumber].mImageWidth * mSharedFileInfo[bufferNumber].mImageHeight * 4;
1131 if( mIndicatorBuffer->UpdatePixels( src, size ) )
1133 mAdaptor->RequestUpdateOnce();
1142 void Indicator::CreateNewPixmapImage()
1144 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mImageWidth, mImageHeight );
1145 Dali::NativeImageSourcePtr nativeImageSource = Dali::NativeImageSource::New( mPixmap );
1147 #ifdef ENABLE_INDICATOR_IMAGE_SAVING
1148 SaveIndicatorImage( nativeImageSource );
1151 if( nativeImageSource )
1153 Dali::Texture texture = Dali::Texture::New( *nativeImageSource );
1154 SetForegroundImage( texture );
1155 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
1156 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
1157 mEventActor.SetSize( mImageWidth, mImageHeight );
1161 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1163 if( mObserver != NULL )
1165 mObserver->IndicatorClosed( this );
1167 // Don't do connection in this callback - strange things happen!
1168 StartReconnectionTimer();
1172 void Indicator::CreateNewImage( int bufferNumber )
1174 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight );
1175 mIndicatorBuffer = new IndicatorBuffer( mAdaptor, mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight, Pixel::BGRA8888 );
1176 bool success = false;
1178 if( CopyToBuffer( bufferNumber ) ) // Only create images if we have valid image buffer
1180 Dali::Texture texture = Dali::Texture::New( mIndicatorBuffer->GetNativeImage() );
1183 SetForegroundImage( texture );
1190 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1192 if( mObserver != NULL )
1194 mObserver->IndicatorClosed( this );
1196 // Don't do connection in this callback - strange things happen!
1197 StartReconnectionTimer();
1201 Dali::Geometry Indicator::CreateBackgroundGeometry()
1203 switch( mOpacityMode )
1205 case Dali::Window::TRANSLUCENT:
1206 if( !mTranslucentGeometry )
1208 // Construct 5 interval mesh
1222 struct BackgroundVertex
1228 unsigned int numVertices = 2 * ( NUM_GRADIENT_INTERVALS + 1 );
1229 BackgroundVertex vertices[ numVertices ];
1232 float delta = 1.0f / NUM_GRADIENT_INTERVALS;
1233 BackgroundVertex* currentVertex = vertices;
1234 for( int y = 0; y < NUM_GRADIENT_INTERVALS + 1; ++y, d += delta )
1236 currentVertex->mPosition = Vector2( -0.5f, d );
1237 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1240 currentVertex->mPosition = Vector2( 0.5f, d );
1241 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1246 unsigned int numIndices = 2 * 3 * NUM_GRADIENT_INTERVALS;
1247 unsigned short indices[ numIndices ];
1249 unsigned short* currentIndex = indices;
1250 for( int y = 0; y < NUM_GRADIENT_INTERVALS; ++y )
1252 *currentIndex++ = (2 * y);
1253 *currentIndex++ = (2 * y) + 3;
1254 *currentIndex++ = (2 * y) + 1;
1256 *currentIndex++ = (2 * y);
1257 *currentIndex++ = (2 * y) + 2;
1258 *currentIndex++ = (2 * y) + 3;
1261 Dali::Property::Map vertexFormat;
1262 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1263 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1264 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1265 vertexPropertyBuffer.SetData( vertices, numVertices );
1267 // Create the geometry object
1268 mTranslucentGeometry = Dali::Geometry::New();
1269 mTranslucentGeometry.AddVertexBuffer( vertexPropertyBuffer );
1270 mTranslucentGeometry.SetIndexBuffer( &indices[0], numIndices );
1273 return mTranslucentGeometry;
1274 case Dali::Window::OPAQUE:
1276 if( !mSolidGeometry )
1279 struct BackgroundVertex
1285 BackgroundVertex vertices[ 4 ] = { { Vector2( -0.5f, -0.5f ), 1.0f }, { Vector2( 0.5f, -0.5f ), 1.0f },
1286 { Vector2( -0.5f, 0.5f ), 1.0f }, { Vector2( 0.5f, 0.5f ), 1.0f } };
1289 unsigned short indices[ 6 ] = { 0, 3, 1, 0, 2, 3 };
1291 Dali::Property::Map vertexFormat;
1292 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1293 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1294 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1295 vertexPropertyBuffer.SetData( vertices, 4 );
1298 // Create the geometry object
1299 mSolidGeometry = Dali::Geometry::New();
1300 mSolidGeometry.AddVertexBuffer( vertexPropertyBuffer );
1301 mSolidGeometry.SetIndexBuffer( &indices[0], 6 );
1304 return mSolidGeometry;
1305 case Dali::Window::TRANSPARENT:
1309 return Dali::Geometry();
1312 void Indicator::SetForegroundImage( Dali::Texture texture )
1314 if( !mForegroundRenderer && texture )
1317 Dali::Shader shader = Dali::Shader::New( FOREGROUND_VERTEX_SHADER, FOREGROUND_FRAGMENT_SHADER );
1319 // Create renderer from geometry and material
1320 Dali::Geometry quad = CreateQuadGeometry();
1321 mForegroundRenderer = Dali::Renderer::New( quad, shader );
1322 // Make sure the foreground stays in front of the background
1323 mForegroundRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, 1.f );
1325 // Set blend function
1326 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_RGB, Dali::BlendFactor::ONE );
1327 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_RGB, Dali::BlendFactor::ONE_MINUS_SRC_ALPHA );
1328 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_ALPHA, Dali::BlendFactor::ONE );
1329 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_ALPHA, Dali::BlendFactor::ONE );
1331 // Create a texture-set and add to renderer
1333 Dali::TextureSet textureSet = Dali::TextureSet::New();
1334 textureSet.SetTexture( 0u, texture );
1335 mForegroundRenderer.SetTextures( textureSet );
1337 mIndicatorContentActor.AddRenderer( mForegroundRenderer );
1339 else if( mForegroundRenderer )
1341 Dali::TextureSet textureSet = mForegroundRenderer.GetTextures();
1342 textureSet.SetTexture( 0u, texture );
1345 if( mImageWidth == 0 && mImageHeight == 0 && texture)
1347 Resize( texture.GetWidth(), texture.GetHeight() );
1351 void Indicator::OnIndicatorTypeChanged( Type indicatorType )
1353 if( mObserver != NULL )
1355 mObserver->IndicatorTypeChanged( indicatorType );
1359 void Indicator::DataReceived( void* event )
1361 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1362 Ecore_Ipc_Event_Server_Data *epcEvent = static_cast<Ecore_Ipc_Event_Server_Data *>( event );
1364 switch( epcEvent->minor )
1368 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE\n" );
1371 //mAdaptor->RequestUpdateOnce();
1375 case OP_UPDATE_DONE:
1377 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE_DONE [%d]\n", epcEvent->response );
1378 // epcEvent->response == display buffer #
1379 //UpdateImageData( epcEvent->response );
1384 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF0\n" );
1385 SetSharedImageInfo( epcEvent );
1390 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF1\n" );
1391 SetLockFileInfo( epcEvent );
1396 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF2\n" );
1397 LoadSharedImage( epcEvent );
1402 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_PIXMAP_REF\n" );
1403 LoadPixmapImage( epcEvent );
1408 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_RESIZE\n" );
1410 if( (epcEvent->data) && (epcEvent->size >= (int)sizeof(IpcDataResize)) )
1412 IpcDataResize *newSize = static_cast<IpcDataResize*>( epcEvent->data );
1413 Resize( newSize->w, newSize->h );
1419 int msgDomain = epcEvent->ref;
1420 int msgId = epcEvent->ref_to;
1422 void *msgData = NULL;
1423 int msgDataSize = 0;
1424 msgData = epcEvent->data;
1425 msgDataSize = epcEvent->size;
1427 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT. msgDomain = %d\n", msgDomain );
1429 if( msgDomain == MSG_DOMAIN_CONTROL_INDICATOR )
1433 case MSG_ID_INDICATOR_TYPE:
1435 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT, INDICATOR_TYPE\n" );
1436 Type* indicatorType = static_cast<Type*>( epcEvent->data );
1437 OnIndicatorTypeChanged( *indicatorType );
1441 case MSG_ID_INDICATOR_START_ANIMATION:
1443 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: MSG_ID_INDICATOR_START_ANIMATION\n" );
1445 if (msgDataSize != (int)sizeof(IpcIndicatorDataAnimation))
1447 DALI_LOG_ERROR("Message data is incorrect\n");
1451 IpcIndicatorDataAnimation *animData = static_cast<IpcIndicatorDataAnimation*>(msgData);
1453 if(!CheckVisibleState())
1455 ShowIndicator( animData->duration /* n sec */ );
1466 void Indicator::ConnectionClosed()
1468 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1470 // Will get this callback if the server connection failed to start up.
1471 delete mServerConnection;
1472 mServerConnection = NULL;
1473 mState = DISCONNECTED;
1475 // Attempt to re-connect
1479 bool Indicator::CheckVisibleState()
1481 if( mOrientation == Dali::Window::LANDSCAPE
1482 || mOrientation == Dali::Window::LANDSCAPE_INVERSE
1483 || (mVisible == Dali::Window::INVISIBLE)
1484 || (mVisible == Dali::Window::AUTO && !mIsShowing) )
1492 void Indicator::ClearSharedFileInfo()
1494 for( int i = 0; i < SHARED_FILE_NUMBER; i++ )
1496 delete mSharedFileInfo[i].mLock;
1497 mSharedFileInfo[i].mLock = NULL;
1499 delete mSharedFileInfo[i].mSharedFile;
1500 mSharedFileInfo[i].mSharedFile = NULL;
1502 mSharedFileInfo[i].mLockFileName.clear();
1503 mSharedFileInfo[i].mSharedFileName.clear();
1508 * duration can be this
1512 * KEEP_SHOWING = -1,
1516 void Indicator::ShowIndicator(float duration)
1518 if( !mIndicatorAnimation )
1520 mIndicatorAnimation = Dali::Animation::New(SLIDING_ANIMATION_DURATION);
1521 mIndicatorAnimation.FinishedSignal().Connect(this, &Indicator::OnAnimationFinished);
1524 if(mIsShowing && !EqualsZero(duration))
1526 // If need to show during showing, do nothing.
1527 // In 2nd phase (below) will update timer
1529 else if(!mIsShowing && mIsAnimationPlaying && EqualsZero(duration))
1531 // If need to hide during hiding or hidden already, do nothing
1535 mIndicatorAnimation.Clear();
1537 if( EqualsZero(duration) )
1539 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, -mImageHeight, 0), Dali::AlphaFunction::EASE_OUT );
1543 OnIndicatorTypeChanged( INDICATOR_TYPE_2 ); // un-toucable
1547 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, 0, 0), Dali::AlphaFunction::EASE_OUT );
1551 OnIndicatorTypeChanged( INDICATOR_TYPE_1 ); // touchable
1554 mIndicatorAnimation.Play();
1555 mIsAnimationPlaying = true;
1562 mShowTimer = Dali::Timer::New(1000 * duration);
1563 mShowTimer.TickSignal().Connect(this, &Indicator::OnShowTimer);
1565 mShowTimer.SetInterval(1000* duration);
1568 if( mVisible == Dali::Window::AUTO )
1570 // check the stage touch
1571 Dali::Stage::GetCurrent().TouchedSignal().Connect( this, &Indicator::OnStageTouched );
1576 if(mShowTimer && mShowTimer.IsRunning())
1581 if( mVisible == Dali::Window::AUTO )
1583 // check the stage touch
1584 Dali::Stage::GetCurrent().TouchedSignal().Disconnect( this, &Indicator::OnStageTouched );
1589 bool Indicator::OnShowTimer()
1591 // after time up, hide indicator
1592 ShowIndicator( HIDE_NOW );
1597 void Indicator::OnAnimationFinished(Dali::Animation& animation)
1599 mIsAnimationPlaying = false;
1600 // once animation is finished and indicator is hidden, take it off stage
1601 if( mObserver != NULL )
1603 mObserver->IndicatorVisibilityChanged( mIsShowing ); // is showing?
1607 void Indicator::OnPan( Dali::Actor actor, const Dali::PanGesture& gesture )
1611 if( mServerConnection )
1613 switch( gesture.state )
1615 case Gesture::Started:
1617 mGestureDetected = false;
1619 // The gesture position is the current position after it has moved by the displacement.
1620 // We want to reference the original position.
1621 mGestureDeltaY = gesture.position.y - gesture.displacement.y;
1624 // No break, Fall through
1625 case Gesture::Continuing:
1627 if( mVisible == Dali::Window::AUTO && !mIsShowing )
1629 // Only take one touch point
1630 if( gesture.numberOfTouches == 1 && mGestureDetected == false )
1632 mGestureDeltaY += gesture.displacement.y;
1634 if( mGestureDeltaY >= mImageHeight * SHOWING_DISTANCE_HEIGHT_RATE )
1636 ShowIndicator( AUTO_INDICATOR_STAY_DURATION );
1637 mGestureDetected = true;
1645 case Gesture::Finished:
1646 case Gesture::Cancelled:
1648 // if indicator is showing, hide again when touching is finished (Since touch leave is activated, checking it in gesture::finish instead of touch::up)
1649 if( mVisible == Dali::Window::AUTO && mIsShowing )
1651 ShowIndicator( AUTO_INDICATOR_STAY_DURATION );
1663 void Indicator::OnStageTouched(const Dali::TouchEvent& touchEvent)
1665 const TouchPoint& touchPoint = touchEvent.GetPoint( 0 );
1667 // when stage is touched while indicator is showing temporary, hide it
1668 if( mIsShowing && ( CheckVisibleState() == false || mVisible == Dali::Window::AUTO ) )
1670 switch( touchPoint.state )
1672 case Dali::PointState::DOWN:
1674 // if touch point is inside the indicator, indicator is not hidden
1675 if( mImageHeight < int(touchPoint.screen.y) )
1677 ShowIndicator( HIDE_NOW );