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::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 mGestureDeltaY( 0.0f ),
545 mGestureDetected( false ),
547 mOpacityMode( Dali::Window::OPAQUE ),
548 mState( DISCONNECTED ),
550 mServerConnection( NULL ),
551 mObserver( observer ),
552 mOrientation( orientation ),
555 mVisible( Dali::Window::INVISIBLE ),
557 mIsAnimationPlaying( false ),
558 mCurrentSharedFile( 0 ),
559 mSharedBufferType( BUFFER_TYPE_SHM ),
561 mBackgroundVisible( false ),
564 mIndicatorContentActor = Dali::Actor::New();
565 mIndicatorContentActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
566 mIndicatorContentActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
568 // Indicator image handles the touch event including "leave"
569 mIndicatorContentActor.SetLeaveRequired( true );
570 mIndicatorContentActor.TouchedSignal().Connect( this, &Indicator::OnTouched );
571 mIndicatorContentActor.SetColor( Color::BLACK );
573 mIndicatorActor = Dali::Actor::New();
574 mIndicatorActor.Add( mIndicatorContentActor );
576 // Event handler to find out flick down gesture
577 mEventActor = Dali::Actor::New();
578 mEventActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
579 mEventActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
580 mIndicatorActor.Add( mEventActor );
582 // Attach pan gesture to find flick down during hiding.
583 // It can prevent the problem that scrollview gets pan gesture even indicator area is touched,
584 // since it consumes the pan gesture in advance.
585 mPanDetector = Dali::PanGestureDetector::New();
586 mPanDetector.DetectedSignal().Connect( this, &Indicator::OnPan );
587 mPanDetector.Attach( mEventActor );
591 // register indicator to accessibility adaptor
592 Dali::AccessibilityAdaptor accessibilityAdaptor = AccessibilityAdaptor::Get();
593 if(accessibilityAdaptor)
595 AccessibilityAdaptor::GetImplementation( accessibilityAdaptor ).SetIndicator( this );
597 // hide the indicator by default
598 mIndicatorActor.SetVisible( false );
600 // create impl to handle ecore event
601 mImpl = new Impl(this);
604 Indicator::~Indicator()
614 mEventActor.TouchedSignal().Disconnect( this, &Indicator::OnTouched );
619 void Indicator::SetAdaptor(Adaptor* adaptor)
622 mIndicatorBuffer->SetAdaptor( adaptor );
625 Dali::Actor Indicator::GetActor()
627 return mIndicatorActor;
630 void Indicator::Open( Dali::Window::WindowOrientation orientation )
632 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
634 // Calls from Window should be set up to ensure we are in a
635 // disconnected state before opening a second time.
636 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
638 mOrientation = orientation;
642 // Change background visibility depending on orientation
643 if( mOrientation == Dali::Window::LANDSCAPE || mOrientation == Dali::Window::LANDSCAPE_INVERSE )
645 if( mBackgroundRenderer )
647 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
648 mBackgroundVisible = false;
653 SetOpacityMode( mOpacityMode );
657 void Indicator::Close()
659 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s", STATE_DEBUG_STRING(mState) );
661 if( mState == CONNECTED )
664 if( mObserver != NULL )
666 mObserver->IndicatorClosed( this );
670 Dali::Texture emptyTexture;
671 SetForegroundImage( emptyTexture );
674 void Indicator::SetOpacityMode( Dali::Window::IndicatorBgOpacity mode )
678 Dali::Geometry geometry = CreateBackgroundGeometry();
681 if( mBackgroundRenderer )
683 if( mBackgroundRenderer.GetGeometry() != geometry )
685 mBackgroundRenderer.SetGeometry( geometry );
690 if( !mBackgroundShader )
692 mBackgroundShader = Dali::Shader::New( BACKGROUND_VERTEX_SHADER, BACKGROUND_FRAGMENT_SHADER, Dali::Shader::Hint::OUTPUT_IS_TRANSPARENT );
695 mBackgroundRenderer = Dali::Renderer::New( geometry, mBackgroundShader );
698 if( !mBackgroundVisible )
700 mIndicatorContentActor.AddRenderer( mBackgroundRenderer );
701 mBackgroundVisible = true;
704 else if( mBackgroundRenderer )
706 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
707 mBackgroundVisible = false;
712 void Indicator::SetVisible( Dali::Window::IndicatorVisibleMode visibleMode, bool forceUpdate )
714 if ( visibleMode != mVisible || forceUpdate )
716 // If we were previously hidden, then we should update the image data before we display the indicator
717 if ( mVisible == Dali::Window::INVISIBLE )
719 UpdateImageData( mCurrentSharedFile );
722 if ( visibleMode == Dali::Window::INVISIBLE )
724 if (mServerConnection)
726 mServerConnection->SendEvent( OP_HIDE, NULL, 0 );
731 mIndicatorActor.SetVisible( true );
733 if( mServerConnection )
735 mServerConnection->SendEvent( OP_SHOW, NULL, 0 );
739 mVisible = visibleMode;
742 if( mForegroundRenderer && mForegroundRenderer.GetTextures().GetTexture( 0u ) )
744 if( CheckVisibleState() && mVisible == Dali::Window::AUTO )
747 ShowIndicator( AUTO_INDICATOR_STAY_DURATION /* stay n sec */ );
749 else if( CheckVisibleState() && mVisible == Dali::Window::VISIBLE )
752 ShowIndicator( KEEP_SHOWING );
757 ShowIndicator( HIDE_NOW );
767 bool Indicator::IsConnected()
769 return ( mState == CONNECTED );
772 bool Indicator::SendMessage( int messageDomain, int messageId, const void *data, int size )
776 return mServerConnection->SendEvent( OP_MSG, messageDomain, messageId, data, size );
784 bool Indicator::OnTouched(Dali::Actor indicator, const Dali::TouchEvent& touchEvent)
786 if( mServerConnection )
788 const TouchPoint& touchPoint = touchEvent.GetPoint( 0 );
790 // Send touch event to indicator server when indicator is showing
791 if( CheckVisibleState() || mIsShowing )
793 switch( touchPoint.state )
795 case Dali::PointState::DOWN:
797 IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
798 IpcDataEvMouseDown ipcDown( touchEvent.time );
799 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
800 mServerConnection->SendEvent( OP_EV_MOUSE_DOWN, &ipcDown, sizeof(ipcDown) );
802 if( mVisible == Dali::Window::AUTO )
804 // Stop hiding indicator
805 ShowIndicator( KEEP_SHOWING );
810 case Dali::PointState::MOTION:
812 IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
813 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
817 case Dali::PointState::UP:
818 case Dali::PointState::INTERRUPTED:
820 IpcDataEvMouseUp ipcUp( touchEvent.time );
821 mServerConnection->SendEvent( OP_EV_MOUSE_UP, &ipcUp, sizeof(ipcUp) );
823 if( mVisible == Dali::Window::AUTO )
826 ShowIndicator( 0.5f /* hide after 0.5 sec */ );
831 case Dali::TouchPoint::Leave:
833 IpcDataEvMouseMove ipcMove( touchPoint, touchEvent.time );
834 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
835 IpcDataEvMouseUp ipcOut( touchEvent.time );
836 mServerConnection->SendEvent( OP_EV_MOUSE_OUT, &ipcOut, sizeof(ipcOut) );
851 bool Indicator::Connect()
853 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
855 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
857 bool connected = false;
859 mServerConnection = new ServerConnection( INDICATOR_SERVICE_NAME, 0, false, this );
860 if( mServerConnection )
862 connected = mServerConnection->IsConnected();
865 delete mServerConnection;
866 mServerConnection = NULL;
872 StartReconnectionTimer();
882 void Indicator::StartReconnectionTimer()
884 if( ! mReconnectTimer )
886 mReconnectTimer = Dali::Timer::New(1000);
887 mConnection.DisconnectAll();
888 mReconnectTimer.TickSignal().Connect( mConnection, &Indicator::OnReconnectTimer );
890 mReconnectTimer.Start();
893 bool Indicator::OnReconnectTimer()
897 if( mState == DISCONNECTED )
908 void Indicator::Disconnect()
910 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
912 mState = DISCONNECTED;
914 delete mServerConnection;
915 mServerConnection = NULL;
917 ClearSharedFileInfo();
920 void Indicator::Resize( int width, int height )
931 if( mImageWidth != width || mImageHeight != height )
934 mImageHeight = height;
936 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
937 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
938 mEventActor.SetSize(mImageWidth, mImageHeight);
943 void Indicator::SetLockFileInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
945 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
947 // epcEvent->ref == w
948 // epcEvent->ref_to == h
949 // epcEvent->response == buffer num
950 // epcEvent->data = lockfile + nul byte
952 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) && (epcEvent->data) &&
953 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
955 int n = epcEvent->response;
957 if( n >= 0 && n < SHARED_FILE_NUMBER )
959 mCurrentSharedFile = n;
961 mSharedFileInfo[n].mImageWidth = epcEvent->ref;
962 mSharedFileInfo[n].mImageHeight = epcEvent->ref_to;
964 mSharedFileInfo[n].mLockFileName.clear();
966 mSharedFileInfo[n].mLockFileName = static_cast< char* >( epcEvent->data );
968 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetLockFileInfo: buffer num = %d, w = %d, h = %d, lock = %s\n",
969 n, mSharedFileInfo[n].mImageWidth, mSharedFileInfo[n].mImageHeight, mSharedFileInfo[n].mLockFileName.c_str() );
974 void Indicator::SetSharedImageInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
976 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
978 // epcEvent->ref == shm id
979 // epcEvent->ref_to == shm num
980 // epcEvent->response == buffer num
981 // epcEvent->data = shm ref string + nul byte
983 if ( (epcEvent->data) &&
984 (epcEvent->size > 0) &&
985 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
987 int n = epcEvent->response;
989 if( n >= 0 && n < SHARED_FILE_NUMBER )
991 mCurrentSharedFile = n;
993 mSharedFileInfo[n].mSharedFileName.clear();
995 mSharedFileInfo[n].mSharedFileName = static_cast< char* >( epcEvent->data );
997 mSharedFileInfo[n].mSharedFileID = epcEvent->ref;
998 mSharedFileInfo[n].mSharedFileNumber = epcEvent->ref_to;
1000 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetSharedImageInfo: buffer num %d, shared file = %s, id = %d, num = %d\n",
1001 n, mSharedFileInfo[n].mSharedFileName.c_str(), mSharedFileInfo[n].mSharedFileID, mSharedFileInfo[n].mSharedFileNumber );
1006 void Indicator::LoadSharedImage( Ecore_Ipc_Event_Server_Data *epcEvent )
1008 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1010 // epcEvent->ref == alpha
1011 // epcEvent->ref_to == sys
1012 // epcEvent->response == buffer num
1014 if ( mSharedBufferType != BUFFER_TYPE_SHM )
1019 int n = epcEvent->response;
1021 if( n >= 0 && n < SHARED_FILE_NUMBER )
1023 mCurrentSharedFile = n;
1025 delete mSharedFileInfo[n].mSharedFile;
1026 mSharedFileInfo[n].mSharedFile = NULL;
1028 delete mSharedFileInfo[n].mLock;
1029 mSharedFileInfo[n].mLock = NULL;
1031 std::stringstream sharedFileID;
1032 std::stringstream sharedFileNumber;
1034 sharedFileID << mSharedFileInfo[n].mSharedFileID;
1035 sharedFileNumber << mSharedFileInfo[n].mSharedFileNumber;
1037 std::string sharedFilename = "/" + mSharedFileInfo[n].mSharedFileName + "-" + sharedFileID.str() + "." + sharedFileNumber.str();
1039 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "LoadSharedImage: file name = %s\n", sharedFilename.c_str() );
1041 mSharedFileInfo[n].mSharedFile = SharedFile::New( sharedFilename.c_str(), mSharedFileInfo[n].mImageWidth * mSharedFileInfo[n].mImageWidth * 4, true );
1042 if( mSharedFileInfo[n].mSharedFile != NULL )
1044 mSharedFileInfo[n].mLock = new Indicator::LockFile( mSharedFileInfo[n].mLockFileName );
1045 if( mSharedFileInfo[n].mLock->RetrieveAndClearErrorStatus() )
1047 DALI_LOG_ERROR( "### Indicator error: Cannot open lock file %s ###\n", mSharedFileInfo[n].mLockFileName.c_str() );
1050 CreateNewImage( n );
1056 void Indicator::LoadPixmapImage( Ecore_Ipc_Event_Server_Data *epcEvent )
1058 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1060 // epcEvent->ref == pixmap id
1061 // epcEvent->ref_to == type
1062 // epcEvent->response == buffer num
1064 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) )
1066 mSharedBufferType = (BufferType)(epcEvent->ref_to);
1068 ClearSharedFileInfo();
1070 mPixmap = static_cast<PixmapId>(epcEvent->ref);
1071 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "mPixmap [%x]", mPixmap);
1073 CreateNewPixmapImage();
1078 void Indicator::UpdateTopMargin()
1080 int newMargin = (mVisible == Dali::Window::VISIBLE && mOpacityMode == Dali::Window::OPAQUE) ? mImageHeight : 0;
1081 if (mTopMargin != newMargin)
1083 mTopMargin = newMargin;
1084 mAdaptor->IndicatorSizeChanged( mTopMargin );
1088 void Indicator::UpdateVisibility()
1090 if( CheckVisibleState() )
1092 // set default indicator type (enable the quick panel)
1093 OnIndicatorTypeChanged( INDICATOR_TYPE_1 );
1097 // set default indicator type (disable the quick panel)
1098 OnIndicatorTypeChanged( INDICATOR_TYPE_2 );
1103 mIndicatorContentActor.SetPosition( 0.0f, -mImageHeight, 0.0f );
1106 SetVisible(mVisible, true);
1109 void Indicator::UpdateImageData( int bufferNumber )
1111 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s mVisible: %s", STATE_DEBUG_STRING(mState), mVisible?"T":"F" );
1113 if( mState == CONNECTED && mVisible )
1117 // in case of shm indicator (not pixmap), not sure we can skip it when mIsShowing is false
1118 CopyToBuffer( bufferNumber );
1124 mAdaptor->RequestUpdateOnce();
1130 bool Indicator::CopyToBuffer( int bufferNumber )
1132 bool success = false;
1134 if( mSharedFileInfo[bufferNumber].mLock )
1136 Indicator::ScopedLock scopedLock(mSharedFileInfo[bufferNumber].mLock);
1137 if( mSharedFileInfo[bufferNumber].mLock->RetrieveAndClearErrorStatus() )
1141 else if( scopedLock.IsLocked() )
1143 unsigned char *src = mSharedFileInfo[bufferNumber].mSharedFile->GetAddress();
1144 size_t size = static_cast< size_t >( mSharedFileInfo[bufferNumber].mImageWidth ) * mSharedFileInfo[bufferNumber].mImageHeight * 4;
1146 if( mIndicatorBuffer->UpdatePixels( src, size ) )
1148 mAdaptor->RequestUpdateOnce();
1157 void Indicator::CreateNewPixmapImage()
1159 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mImageWidth, mImageHeight );
1160 Dali::NativeImageSourcePtr nativeImageSource = Dali::NativeImageSource::New( mPixmap );
1162 #ifdef ENABLE_INDICATOR_IMAGE_SAVING
1163 SaveIndicatorImage( nativeImageSource );
1166 if( nativeImageSource )
1168 Dali::Texture texture = Dali::Texture::New( *nativeImageSource );
1169 SetForegroundImage( texture );
1170 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
1171 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
1172 mEventActor.SetSize( mImageWidth, mImageHeight );
1177 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1179 if( mObserver != NULL )
1181 mObserver->IndicatorClosed( this );
1183 // Don't do connection in this callback - strange things happen!
1184 StartReconnectionTimer();
1188 void Indicator::CreateNewImage( int bufferNumber )
1190 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight );
1191 mIndicatorBuffer = new IndicatorBuffer( mAdaptor, mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight, Pixel::BGRA8888 );
1192 bool success = false;
1194 if( CopyToBuffer( bufferNumber ) ) // Only create images if we have valid image buffer
1196 Dali::Texture texture = Dali::Texture::New( mIndicatorBuffer->GetNativeImage() );
1199 SetForegroundImage( texture );
1206 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1208 if( mObserver != NULL )
1210 mObserver->IndicatorClosed( this );
1212 // Don't do connection in this callback - strange things happen!
1213 StartReconnectionTimer();
1217 Dali::Geometry Indicator::CreateBackgroundGeometry()
1219 switch( mOpacityMode )
1221 case Dali::Window::TRANSLUCENT:
1222 if( !mTranslucentGeometry )
1224 // Construct 5 interval mesh
1238 struct BackgroundVertex
1244 unsigned int numVertices = 2 * ( NUM_GRADIENT_INTERVALS + 1 );
1245 BackgroundVertex vertices[ numVertices ];
1248 float delta = 1.0f / NUM_GRADIENT_INTERVALS;
1249 BackgroundVertex* currentVertex = vertices;
1250 for( int y = 0; y < NUM_GRADIENT_INTERVALS + 1; ++y, d += delta )
1252 currentVertex->mPosition = Vector2( -0.5f, d );
1253 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1256 currentVertex->mPosition = Vector2( 0.5f, d );
1257 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1262 unsigned int numIndices = 2 * 3 * NUM_GRADIENT_INTERVALS;
1263 unsigned short indices[ numIndices ];
1265 unsigned short* currentIndex = indices;
1266 for( int y = 0; y < NUM_GRADIENT_INTERVALS; ++y )
1268 *currentIndex++ = (2 * y);
1269 *currentIndex++ = (2 * y) + 3;
1270 *currentIndex++ = (2 * y) + 1;
1272 *currentIndex++ = (2 * y);
1273 *currentIndex++ = (2 * y) + 2;
1274 *currentIndex++ = (2 * y) + 3;
1277 Dali::Property::Map vertexFormat;
1278 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1279 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1280 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1281 vertexPropertyBuffer.SetData( vertices, numVertices );
1283 // Create the geometry object
1284 mTranslucentGeometry = Dali::Geometry::New();
1285 mTranslucentGeometry.AddVertexBuffer( vertexPropertyBuffer );
1286 mTranslucentGeometry.SetIndexBuffer( &indices[0], numIndices );
1289 return mTranslucentGeometry;
1290 case Dali::Window::OPAQUE:
1292 if( !mSolidGeometry )
1295 struct BackgroundVertex
1301 BackgroundVertex vertices[ 4 ] = { { Vector2( -0.5f, -0.5f ), 1.0f }, { Vector2( 0.5f, -0.5f ), 1.0f },
1302 { Vector2( -0.5f, 0.5f ), 1.0f }, { Vector2( 0.5f, 0.5f ), 1.0f } };
1305 unsigned short indices[ 6 ] = { 0, 3, 1, 0, 2, 3 };
1307 Dali::Property::Map vertexFormat;
1308 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1309 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1310 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1311 vertexPropertyBuffer.SetData( vertices, 4 );
1314 // Create the geometry object
1315 mSolidGeometry = Dali::Geometry::New();
1316 mSolidGeometry.AddVertexBuffer( vertexPropertyBuffer );
1317 mSolidGeometry.SetIndexBuffer( &indices[0], 6 );
1320 return mSolidGeometry;
1321 case Dali::Window::TRANSPARENT:
1325 return Dali::Geometry();
1328 void Indicator::SetForegroundImage( Dali::Texture texture )
1330 if( !mForegroundRenderer && texture )
1333 Dali::Shader shader = Dali::Shader::New( FOREGROUND_VERTEX_SHADER, FOREGROUND_FRAGMENT_SHADER );
1335 // Create renderer from geometry and material
1336 Dali::Geometry quad = CreateQuadGeometry();
1337 mForegroundRenderer = Dali::Renderer::New( quad, shader );
1338 // Make sure the foreground stays in front of the background
1339 mForegroundRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, 1.f );
1341 // Set blend function
1342 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_RGB, Dali::BlendFactor::ONE );
1343 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_RGB, Dali::BlendFactor::ONE_MINUS_SRC_ALPHA );
1344 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_ALPHA, Dali::BlendFactor::ONE );
1345 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_ALPHA, Dali::BlendFactor::ONE );
1347 // Create a texture-set and add to renderer
1349 Dali::TextureSet textureSet = Dali::TextureSet::New();
1350 textureSet.SetTexture( 0u, texture );
1351 mForegroundRenderer.SetTextures( textureSet );
1353 mIndicatorContentActor.AddRenderer( mForegroundRenderer );
1355 else if( mForegroundRenderer )
1357 Dali::TextureSet textureSet = mForegroundRenderer.GetTextures();
1358 textureSet.SetTexture( 0u, texture );
1361 if( mImageWidth == 0 && mImageHeight == 0 && texture)
1363 Resize( texture.GetWidth(), texture.GetHeight() );
1367 void Indicator::OnIndicatorTypeChanged( Type indicatorType )
1369 if( mObserver != NULL )
1371 mObserver->IndicatorTypeChanged( indicatorType );
1375 void Indicator::DataReceived( void* event )
1377 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1378 Ecore_Ipc_Event_Server_Data *epcEvent = static_cast<Ecore_Ipc_Event_Server_Data *>( event );
1380 switch( epcEvent->minor )
1384 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE\n" );
1387 mAdaptor->RequestUpdateOnce();
1391 case OP_UPDATE_DONE:
1393 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE_DONE [%d]\n", epcEvent->response );
1394 // epcEvent->response == display buffer #
1395 UpdateImageData( epcEvent->response );
1400 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF0\n" );
1401 SetSharedImageInfo( epcEvent );
1406 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF1\n" );
1407 SetLockFileInfo( epcEvent );
1412 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF2\n" );
1413 LoadSharedImage( epcEvent );
1418 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_PIXMAP_REF\n" );
1419 LoadPixmapImage( epcEvent );
1424 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_RESIZE\n" );
1426 if( (epcEvent->data) && (epcEvent->size >= (int)sizeof(IpcDataResize)) )
1428 IpcDataResize *newSize = static_cast<IpcDataResize*>( epcEvent->data );
1429 Resize( newSize->w, newSize->h );
1435 int msgDomain = epcEvent->ref;
1436 int msgId = epcEvent->ref_to;
1438 void *msgData = NULL;
1439 int msgDataSize = 0;
1440 msgData = epcEvent->data;
1441 msgDataSize = epcEvent->size;
1443 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT. msgDomain = %d\n", msgDomain );
1445 if( msgDomain == MSG_DOMAIN_CONTROL_INDICATOR )
1449 case MSG_ID_INDICATOR_TYPE:
1451 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT, INDICATOR_TYPE\n" );
1452 Type* indicatorType = static_cast<Type*>( epcEvent->data );
1453 OnIndicatorTypeChanged( *indicatorType );
1457 case MSG_ID_INDICATOR_START_ANIMATION:
1459 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: MSG_ID_INDICATOR_START_ANIMATION\n" );
1461 if (msgDataSize != (int)sizeof(IpcIndicatorDataAnimation))
1463 DALI_LOG_ERROR("Message data is incorrect\n");
1467 IpcIndicatorDataAnimation *animData = static_cast<IpcIndicatorDataAnimation*>(msgData);
1469 if(!CheckVisibleState())
1471 ShowIndicator( animData->duration /* n sec */ );
1482 void Indicator::ConnectionClosed()
1484 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1486 // Will get this callback if the server connection failed to start up.
1487 delete mServerConnection;
1488 mServerConnection = NULL;
1489 mState = DISCONNECTED;
1491 // Attempt to re-connect
1495 bool Indicator::CheckVisibleState()
1497 if( mOrientation == Dali::Window::LANDSCAPE
1498 || mOrientation == Dali::Window::LANDSCAPE_INVERSE
1499 || (mVisible == Dali::Window::INVISIBLE)
1500 || (mVisible == Dali::Window::AUTO && !mIsShowing) )
1508 void Indicator::ClearSharedFileInfo()
1510 for( int i = 0; i < SHARED_FILE_NUMBER; i++ )
1512 delete mSharedFileInfo[i].mLock;
1513 mSharedFileInfo[i].mLock = NULL;
1515 delete mSharedFileInfo[i].mSharedFile;
1516 mSharedFileInfo[i].mSharedFile = NULL;
1518 mSharedFileInfo[i].mLockFileName.clear();
1519 mSharedFileInfo[i].mSharedFileName.clear();
1524 * duration can be this
1528 * KEEP_SHOWING = -1,
1532 void Indicator::ShowIndicator(float duration)
1534 if( !mIndicatorAnimation )
1536 mIndicatorAnimation = Dali::Animation::New(SLIDING_ANIMATION_DURATION);
1537 mIndicatorAnimation.FinishedSignal().Connect(this, &Indicator::OnAnimationFinished);
1540 if(mIsShowing && !EqualsZero(duration))
1542 // If need to show during showing, do nothing.
1543 // In 2nd phase (below) will update timer
1545 else if(!mIsShowing && mIsAnimationPlaying && EqualsZero(duration))
1547 // If need to hide during hiding or hidden already, do nothing
1551 mIndicatorAnimation.Clear();
1553 if( EqualsZero(duration) )
1555 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, -mImageHeight, 0), Dali::AlphaFunction::EASE_OUT );
1559 OnIndicatorTypeChanged( INDICATOR_TYPE_2 ); // un-toucable
1563 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, 0, 0), Dali::AlphaFunction::EASE_OUT );
1567 OnIndicatorTypeChanged( INDICATOR_TYPE_1 ); // touchable
1570 mIndicatorAnimation.Play();
1571 mIsAnimationPlaying = true;
1578 mShowTimer = Dali::Timer::New(1000 * duration);
1579 mShowTimer.TickSignal().Connect(this, &Indicator::OnShowTimer);
1581 mShowTimer.SetInterval(1000* duration);
1584 if( mVisible == Dali::Window::AUTO )
1586 // check the stage touch
1587 Dali::Stage::GetCurrent().TouchedSignal().Connect( this, &Indicator::OnStageTouched );
1592 if(mShowTimer && mShowTimer.IsRunning())
1597 if( mVisible == Dali::Window::AUTO )
1599 // check the stage touch
1600 Dali::Stage::GetCurrent().TouchedSignal().Disconnect( this, &Indicator::OnStageTouched );
1605 bool Indicator::OnShowTimer()
1607 // after time up, hide indicator
1608 ShowIndicator( HIDE_NOW );
1613 void Indicator::OnAnimationFinished(Dali::Animation& animation)
1615 mIsAnimationPlaying = false;
1616 // once animation is finished and indicator is hidden, take it off stage
1617 if( mObserver != NULL )
1619 mObserver->IndicatorVisibilityChanged( mIsShowing ); // is showing?
1623 void Indicator::OnPan( Dali::Actor actor, const Dali::PanGesture& gesture )
1625 // Nothing to do, but we still want to consume pan
1628 void Indicator::OnStageTouched(const Dali::TouchEvent& touchEvent)
1630 const TouchPoint& touchPoint = touchEvent.GetPoint( 0 );
1632 // when stage is touched while indicator is showing temporary, hide it
1633 if( mIsShowing && ( CheckVisibleState() == false || mVisible == Dali::Window::AUTO ) )
1635 switch( touchPoint.state )
1637 case Dali::PointState::DOWN:
1639 // if touch point is inside the indicator, indicator is not hidden
1640 if( mImageHeight < int(touchPoint.screen.y) )
1642 ShowIndicator( HIDE_NOW );