2 * Copyright (c) 2017 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"
22 // Ecore is littered with C style cast
23 #pragma GCC diagnostic push
24 #pragma GCC diagnostic ignored "-Wold-style-cast"
28 #include <Ecore_Wayland.h>
33 #include <sys/types.h>
41 #include <dali/public-api/images/native-image.h>
42 #include <dali/public-api/events/touch-event.h>
43 #include <dali/public-api/events/touch-point.h>
44 #include <dali/public-api/common/stage.h>
45 #include <dali/public-api/images/buffer-image.h>
46 #include <dali/public-api/images/pixel.h>
48 #include <dali/integration-api/debug.h>
51 #include <adaptor-impl.h>
52 #include <accessibility-adaptor-impl.h>
53 #include <native-image-source.h>
57 #if defined(DEBUG_ENABLED)
58 #define STATE_DEBUG_STRING(state) (state==DISCONNECTED?"DISCONNECTED":state==CONNECTED?"CONNECTED":"UNKNOWN")
64 const float SLIDING_ANIMATION_DURATION( 0.2f ); // 200 milli seconds
65 const float AUTO_INDICATOR_STAY_DURATION(3.0f); // 3 seconds
66 const float SHOWING_DISTANCE_HEIGHT_RATE(0.34f); // 20 pixels
74 const int NUM_GRADIENT_INTERVALS(5); // Number of gradient intervals
75 const float GRADIENT_ALPHA[NUM_GRADIENT_INTERVALS+1] = { 0.6f, 0.38f, 0.20f, 0.08f, 0.0f, 0.0f };
77 #define MAKE_SHADER(A)#A
79 const char* BACKGROUND_VERTEX_SHADER = MAKE_SHADER(
80 attribute mediump vec2 aPosition;
81 attribute mediump float aAlpha;
82 varying mediump float vAlpha;
83 uniform mediump mat4 uMvpMatrix;
84 uniform mediump vec3 uSize;
88 mediump vec4 vertexPosition = vec4( aPosition * uSize.xy, 0.0, 1.0 );
89 vertexPosition = uMvpMatrix * vertexPosition;
92 gl_Position = vertexPosition;
96 const char* BACKGROUND_FRAGMENT_SHADER = MAKE_SHADER(
97 uniform lowp vec4 uColor;
98 varying mediump float vAlpha;
102 gl_FragColor = uColor;
103 gl_FragColor.a *= vAlpha;
107 const char* FOREGROUND_VERTEX_SHADER = DALI_COMPOSE_SHADER(
108 attribute mediump vec2 aPosition;\n
109 varying mediump vec2 vTexCoord;\n
110 uniform mediump mat4 uMvpMatrix;\n
111 uniform mediump vec3 uSize;\n
112 uniform mediump vec4 sTextureRect;\n
116 gl_Position = uMvpMatrix * vec4(aPosition * uSize.xy, 0.0, 1.0);\n
117 vTexCoord = aPosition + vec2(0.5);\n
121 const char* FOREGROUND_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
122 varying mediump vec2 vTexCoord;\n
123 uniform sampler2D sTexture;\n
127 gl_FragColor = texture2D( sTexture, vTexCoord );\n // the foreground does not apply actor color
131 Dali::Geometry CreateQuadGeometry()
133 Dali::Property::Map quadVertexFormat;
134 quadVertexFormat["aPosition"] = Dali::Property::VECTOR2;
135 Dali::PropertyBuffer vertexData = Dali::PropertyBuffer::New( quadVertexFormat );
137 const float halfQuadSize = .5f;
138 struct QuadVertex { Dali::Vector2 position; };
139 QuadVertex quadVertexData[4] = {
140 { Dali::Vector2(-halfQuadSize, -halfQuadSize) },
141 { Dali::Vector2(-halfQuadSize, halfQuadSize) },
142 { Dali::Vector2( halfQuadSize, -halfQuadSize) },
143 { Dali::Vector2( halfQuadSize, halfQuadSize) } };
144 vertexData.SetData(quadVertexData, 4);
146 Dali::Geometry quad = Dali::Geometry::New();
147 quad.AddVertexBuffer( vertexData );
148 quad.SetType( Dali::Geometry::TRIANGLE_STRIP );
152 const float OPAQUE_THRESHOLD(0.99f);
153 const float TRANSPARENT_THRESHOLD(0.05f);
155 // indicator service name
156 const char* INDICATOR_SERVICE_NAME("elm_indicator");
158 // Copied from ecore_evas_extn_engine.h
174 OP_PROFILE_CHANGE_REQUEST,
175 OP_PROFILE_CHANGE_DONE,
193 // Copied from elm_conform.c
195 const int MSG_DOMAIN_CONTROL_INDICATOR( 0x10001 );
196 const int MSG_ID_INDICATOR_REPEAT_EVENT( 0x10002 );
197 const int MSG_ID_INDICATOR_ROTATION( 0x10003 );
198 const int MSG_ID_INDICATOR_OPACITY( 0X1004 );
199 const int MSG_ID_INDICATOR_TYPE( 0X1005 );
200 const int MSG_ID_INDICATOR_START_ANIMATION( 0X10006 );
212 struct IpcIndicatorDataAnimation
218 struct IpcDataEvMouseUp
221 Evas_Button_Flags flags;
223 unsigned int timestamp;
224 Evas_Event_Flags event_flags;
226 IpcDataEvMouseUp(unsigned long timestamp)
228 flags(EVAS_BUTTON_NONE),
230 timestamp(static_cast<unsigned int>(timestamp)),
231 event_flags(EVAS_EVENT_FLAG_NONE)
236 struct IpcDataEvMouseDown
239 Evas_Button_Flags flags;
241 unsigned int timestamp;
242 Evas_Event_Flags event_flags;
244 IpcDataEvMouseDown(unsigned long timestamp)
246 flags(EVAS_BUTTON_NONE),
248 timestamp(static_cast<unsigned int>(timestamp)),
249 event_flags(EVAS_EVENT_FLAG_NONE)
254 struct IpcDataEvMouseMove
257 Evas_Button_Flags flags;
259 unsigned int timestamp;
260 Evas_Event_Flags event_flags;
262 IpcDataEvMouseMove(const Dali::Vector2& touchPoint, unsigned long timestamp)
263 : x(static_cast<Evas_Coord>(touchPoint.x)),
264 y(static_cast<Evas_Coord>(touchPoint.y)),
265 flags(EVAS_BUTTON_NONE),
267 timestamp(static_cast<unsigned int>(timestamp)),
268 event_flags(EVAS_EVENT_FLAG_NONE)
273 struct IpcDataEvMouseOut
275 unsigned int timestamp;
277 Evas_Event_Flags event_flags;
279 IpcDataEvMouseOut(unsigned long timestamp)
280 : timestamp(static_cast<unsigned int>(timestamp)),
282 event_flags(EVAS_EVENT_FLAG_NONE)
287 #ifdef ENABLE_INDICATOR_IMAGE_SAVING
289 void SaveIndicatorImage( Dali::NativeImageSourcePtr nativeImageSource )
291 // Save image data to disk in BMP form.
292 static int gFilenameCounter = 0;
293 static const char bmpHeader[] = {
294 0x42, 0x4d, 0x0a, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x7c, 0x00,
296 0xe0, 0x01, 0x00, 0x00, // Width (480)
297 0x1b, 0x00, 0x00, 0x00, // Height ( 27)
298 0x01, 0x00, 0x20, 0x00, 0x03, 0x00,
299 0x00, 0x00, 0x80, 0xca, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
300 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff,
301 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
302 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
303 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
304 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
305 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
308 // This is a BMP header with width & height hard-coded in.
309 // The data was first determined by dumping the raw data and inspecting in GIMP, before creating this header data.
310 std::vector<unsigned char> buffer;
313 Dali::Pixel::Format pixelFormat;
314 if( nativeImageSource->GetPixels( buffer, w, h, pixelFormat ) )
316 int imageSize = w * h * 4;
317 std::stringstream fileName;
318 // Give each file an incremental filename.
319 fileName << "/opt/usr/media/Images/out-" << std::setfill( '0' ) << std::setw( 5 ) << gFilenameCounter << ".bmp";
321 std::ofstream outfile( fileName.str().c_str(), std::ofstream::binary );
322 if( outfile.is_open() )
324 DALI_LOG_WARNING( "Saving Indicator Image w:%d, h:%d, %s\n", w, h, fileName.str().c_str() );
326 outfile.write( bmpHeader, sizeof( bmpHeader ) / sizeof( bmpHeader[0] ) ); // Size of the BMP header.
327 outfile.write( (const char*)buffer.data(), imageSize );
333 DALI_LOG_ERROR( "COULD NOT OPEN FOR SAVING: %s\n", fileName.str().c_str() );
340 } // anonymous namespace
349 #if defined(DEBUG_ENABLED)
350 Debug::Filter* gIndicatorLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_INDICATOR");
353 // Impl to hide EFL implementation.
355 struct Indicator::Impl
357 enum // operation mode
360 INDICATOR_STAY_WITH_DURATION
366 Impl(Indicator* indicator)
367 : mIndicator(indicator),
368 mEcoreEventHandler(NULL)
370 #if defined(DALI_PROFILE_MOBILE)
372 mEcoreEventHandler = ecore_event_handler_add(ECORE_WL_EVENT_INDICATOR_FLICK, EcoreEventIndicator, this);
374 mEcoreEventHandler = ecore_event_handler_add(ECORE_X_EVENT_CLIENT_MESSAGE, EcoreEventClientMessage, this);
376 #endif // WAYLAND && DALI_PROFILE_MOBILE
384 if ( mEcoreEventHandler )
386 ecore_event_handler_del(mEcoreEventHandler);
390 static void SetIndicatorVisibility( void* data, int operation )
392 Indicator::Impl* indicatorImpl((Indicator::Impl*)data);
394 if ( indicatorImpl == NULL || indicatorImpl->mIndicator == NULL)
398 if ( operation == INDICATOR_STAY_WITH_DURATION )
400 // if indicator is not showing, INDICATOR_FLICK_DONE is given
401 if( indicatorImpl->mIndicator->mVisible == Dali::Window::AUTO &&
402 !indicatorImpl->mIndicator->mIsShowing )
404 indicatorImpl->mIndicator->ShowIndicator( AUTO_INDICATOR_STAY_DURATION );
407 else if( operation == INDICATOR_HIDE )
409 if( indicatorImpl->mIndicator->mVisible == Dali::Window::AUTO &&
410 indicatorImpl->mIndicator->mIsShowing )
412 indicatorImpl->mIndicator->ShowIndicator( HIDE_NOW );
416 #if defined(DALI_PROFILE_MOBILE)
419 * Called when the Ecore indicator event is received.
421 static Eina_Bool EcoreEventIndicator( void* data, int type, void* event )
423 SetIndicatorVisibility( data, INDICATOR_STAY_WITH_DURATION );
424 return ECORE_CALLBACK_PASS_ON;
428 * Called when the client messages (i.e. quick panel state) are received.
430 static Eina_Bool EcoreEventClientMessage( void* data, int type, void* event )
432 Ecore_X_Event_Client_Message* clientMessageEvent((Ecore_X_Event_Client_Message*)event);
434 if ( clientMessageEvent != NULL )
436 if (clientMessageEvent->message_type == ECORE_X_ATOM_E_INDICATOR_FLICK_DONE)
438 SetIndicatorVisibility( data, INDICATOR_STAY_WITH_DURATION );
440 else if ( clientMessageEvent->message_type == ECORE_X_ATOM_E_MOVE_QUICKPANEL_STATE )
442 SetIndicatorVisibility( data, INDICATOR_HIDE );
445 return ECORE_CALLBACK_PASS_ON;
448 #endif // WAYLAND && DALI_PROFILE_MOBILE
451 Indicator* mIndicator;
452 Ecore_Event_Handler* mEcoreEventHandler;
455 Indicator::LockFile::LockFile(const std::string filename)
456 : mFilename(filename),
459 mFileDescriptor = open(filename.c_str(), O_RDWR);
460 if( mFileDescriptor == -1 )
464 DALI_LOG_ERROR( "### Cannot open %s for indicator lock ###\n", mFilename.c_str() );
468 Indicator::LockFile::~LockFile()
470 // Closing file descriptor also unlocks file.
471 close( mFileDescriptor );
474 bool Indicator::LockFile::Lock()
476 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
479 if( mFileDescriptor > 0 )
481 struct flock filelock;
483 filelock.l_type = F_RDLCK;
484 filelock.l_whence = SEEK_SET;
485 filelock.l_start = 0;
487 if( fcntl( mFileDescriptor, F_SETLKW, &filelock ) == -1 )
490 DALI_LOG_ERROR( "### Failed to lock with fd : %s ###\n", mFilename.c_str() );
500 DALI_LOG_ERROR( "### Invalid fd ###\n" );
506 void Indicator::LockFile::Unlock()
508 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
510 struct flock filelock;
512 filelock.l_type = F_UNLCK;
513 filelock.l_whence = SEEK_SET;
514 filelock.l_start = 0;
516 if (fcntl(mFileDescriptor, F_SETLKW, &filelock) == -1)
519 DALI_LOG_ERROR( "### Failed to lock with fd : %s ###\n", mFilename.c_str() );
523 bool Indicator::LockFile::RetrieveAndClearErrorStatus()
525 bool error = mErrorThrown;
526 mErrorThrown = false;
530 Indicator::ScopedLock::ScopedLock(LockFile* lockFile)
531 : mLockFile(lockFile),
536 mLocked = mLockFile->Lock();
540 Indicator::ScopedLock::~ScopedLock()
548 bool Indicator::ScopedLock::IsLocked()
553 Indicator::Indicator( Adaptor* adaptor, Dali::Window::WindowOrientation orientation, IndicatorInterface::Observer* observer )
555 mGestureDeltaY( 0.0f ),
556 mGestureDetected( false ),
558 mOpacityMode( Dali::Window::OPAQUE ),
559 mState( DISCONNECTED ),
561 mServerConnection( NULL ),
562 mObserver( observer ),
563 mOrientation( orientation ),
566 mVisible( Dali::Window::INVISIBLE ),
568 mIsAnimationPlaying( false ),
569 mCurrentSharedFile( 0 ),
570 mSharedBufferType( BUFFER_TYPE_SHM ),
572 mBackgroundVisible( false ),
575 mIndicatorContentActor = Dali::Actor::New();
576 mIndicatorContentActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
577 mIndicatorContentActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
579 // Indicator image handles the touch event including "leave"
580 mIndicatorContentActor.SetLeaveRequired( true );
581 mIndicatorContentActor.TouchSignal().Connect( this, &Indicator::OnTouch );
582 mIndicatorContentActor.SetColor( Color::BLACK );
584 mIndicatorActor = Dali::Actor::New();
585 mIndicatorActor.Add( mIndicatorContentActor );
587 // Event handler to find out flick down gesture
588 mEventActor = Dali::Actor::New();
589 mEventActor.SetParentOrigin( ParentOrigin::TOP_CENTER );
590 mEventActor.SetAnchorPoint( AnchorPoint::TOP_CENTER );
591 mIndicatorActor.Add( mEventActor );
593 // Attach pan gesture to find flick down during hiding.
594 // It can prevent the problem that scrollview gets pan gesture even indicator area is touched,
595 // since it consumes the pan gesture in advance.
596 mPanDetector = Dali::PanGestureDetector::New();
597 mPanDetector.DetectedSignal().Connect( this, &Indicator::OnPan );
598 mPanDetector.Attach( mEventActor );
602 // register indicator to accessibility adaptor
603 Dali::AccessibilityAdaptor accessibilityAdaptor = AccessibilityAdaptor::Get();
604 if(accessibilityAdaptor)
606 AccessibilityAdaptor::GetImplementation( accessibilityAdaptor ).SetIndicator( this );
608 // hide the indicator by default
609 mIndicatorActor.SetVisible( false );
611 // create impl to handle ecore event
612 mImpl = new Impl(this);
615 Indicator::~Indicator()
625 mEventActor.TouchSignal().Disconnect( this, &Indicator::OnTouch );
630 void Indicator::SetAdaptor(Adaptor* adaptor)
633 mIndicatorBuffer->SetAdaptor( adaptor );
636 Dali::Actor Indicator::GetActor()
638 return mIndicatorActor;
641 void Indicator::Open( Dali::Window::WindowOrientation orientation )
643 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
645 // Calls from Window should be set up to ensure we are in a
646 // disconnected state before opening a second time.
647 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
649 mOrientation = orientation;
653 // Change background visibility depending on orientation
654 if( mOrientation == Dali::Window::LANDSCAPE || mOrientation == Dali::Window::LANDSCAPE_INVERSE )
656 if( mBackgroundRenderer )
658 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
659 mBackgroundVisible = false;
664 SetOpacityMode( mOpacityMode );
668 void Indicator::Close()
670 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s", STATE_DEBUG_STRING(mState) );
672 if( mState == CONNECTED )
675 if( mObserver != NULL )
677 mObserver->IndicatorClosed( this );
681 Dali::Texture emptyTexture;
682 SetForegroundImage( emptyTexture );
685 void Indicator::SetOpacityMode( Dali::Window::IndicatorBgOpacity mode )
689 Dali::Geometry geometry = CreateBackgroundGeometry();
692 if( mBackgroundRenderer )
694 if( mBackgroundRenderer.GetGeometry() != geometry )
696 mBackgroundRenderer.SetGeometry( geometry );
701 if( !mBackgroundShader )
703 mBackgroundShader = Dali::Shader::New( BACKGROUND_VERTEX_SHADER, BACKGROUND_FRAGMENT_SHADER, Dali::Shader::Hint::OUTPUT_IS_TRANSPARENT );
706 mBackgroundRenderer = Dali::Renderer::New( geometry, mBackgroundShader );
709 if( !mBackgroundVisible )
711 mIndicatorContentActor.AddRenderer( mBackgroundRenderer );
712 mBackgroundVisible = true;
715 else if( mBackgroundRenderer )
717 mIndicatorContentActor.RemoveRenderer( mBackgroundRenderer );
718 mBackgroundVisible = false;
723 void Indicator::SetVisible( Dali::Window::IndicatorVisibleMode visibleMode, bool forceUpdate )
725 if ( visibleMode != mVisible || forceUpdate )
727 // If we were previously hidden, then we should update the image data before we display the indicator
728 if ( mVisible == Dali::Window::INVISIBLE )
730 UpdateImageData( mCurrentSharedFile );
733 if ( visibleMode == Dali::Window::INVISIBLE )
735 if (mServerConnection)
737 mServerConnection->SendEvent( OP_HIDE, NULL, 0 );
742 mIndicatorActor.SetVisible( true );
744 if( mServerConnection )
746 mServerConnection->SendEvent( OP_SHOW, NULL, 0 );
750 mVisible = visibleMode;
753 if( mForegroundRenderer && mForegroundRenderer.GetTextures().GetTexture( 0u ) )
755 if( CheckVisibleState() && mVisible == Dali::Window::AUTO )
758 ShowIndicator( AUTO_INDICATOR_STAY_DURATION /* stay n sec */ );
760 else if( CheckVisibleState() && mVisible == Dali::Window::VISIBLE )
763 ShowIndicator( KEEP_SHOWING );
768 ShowIndicator( HIDE_NOW );
778 bool Indicator::IsConnected()
780 return ( mState == CONNECTED );
783 bool Indicator::SendMessage( int messageDomain, int messageId, const void *data, int size )
787 return mServerConnection->SendEvent( OP_MSG, messageDomain, messageId, data, size );
795 bool Indicator::OnTouch(Dali::Actor indicator, const Dali::TouchData& touchData)
797 if( mServerConnection )
799 // Send touch event to indicator server when indicator is showing
800 if( CheckVisibleState() || mIsShowing )
802 switch( touchData.GetState(0) )
804 case Dali::PointState::DOWN:
806 IpcDataEvMouseMove ipcMove( touchData.GetLocalPosition(0), touchData.GetTime() );
807 IpcDataEvMouseDown ipcDown( touchData.GetTime() );
808 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
809 mServerConnection->SendEvent( OP_EV_MOUSE_DOWN, &ipcDown, sizeof(ipcDown) );
811 if( mVisible == Dali::Window::AUTO )
813 // Stop hiding indicator
814 ShowIndicator( KEEP_SHOWING );
819 case Dali::PointState::MOTION:
821 IpcDataEvMouseMove ipcMove( touchData.GetLocalPosition(0), touchData.GetTime() );
822 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
826 case Dali::PointState::UP:
827 case Dali::PointState::INTERRUPTED:
829 IpcDataEvMouseUp ipcUp( touchData.GetTime() );
830 mServerConnection->SendEvent( OP_EV_MOUSE_UP, &ipcUp, sizeof(ipcUp) );
832 if( mVisible == Dali::Window::AUTO )
835 ShowIndicator( 0.5f /* hide after 0.5 sec */ );
840 case Dali::TouchPoint::Leave:
842 IpcDataEvMouseMove ipcMove( touchData.GetLocalPosition(0), touchData.GetTime() );
843 mServerConnection->SendEvent( OP_EV_MOUSE_MOVE, &ipcMove, sizeof(ipcMove) );
844 IpcDataEvMouseUp ipcOut( touchData.GetTime() );
845 mServerConnection->SendEvent( OP_EV_MOUSE_OUT, &ipcOut, sizeof(ipcOut) );
860 bool Indicator::Connect()
862 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
864 DALI_ASSERT_DEBUG( mState == DISCONNECTED );
866 bool connected = false;
868 mServerConnection = new ServerConnection( INDICATOR_SERVICE_NAME, 0, false, this );
869 if( mServerConnection )
871 connected = mServerConnection->IsConnected();
874 delete mServerConnection;
875 mServerConnection = NULL;
881 StartReconnectionTimer();
891 void Indicator::StartReconnectionTimer()
893 if( ! mReconnectTimer )
895 mReconnectTimer = Dali::Timer::New(1000);
896 mConnection.DisconnectAll();
897 mReconnectTimer.TickSignal().Connect( mConnection, &Indicator::OnReconnectTimer );
899 mReconnectTimer.Start();
902 bool Indicator::OnReconnectTimer()
906 if( mState == DISCONNECTED )
917 void Indicator::Disconnect()
919 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
921 mState = DISCONNECTED;
923 delete mServerConnection;
924 mServerConnection = NULL;
926 ClearSharedFileInfo();
929 void Indicator::Resize( int width, int height )
940 if( mImageWidth != width || mImageHeight != height )
943 mImageHeight = height;
945 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
946 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
947 mEventActor.SetSize(mImageWidth, mImageHeight);
952 void Indicator::SetLockFileInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
954 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
956 // epcEvent->ref == w
957 // epcEvent->ref_to == h
958 // epcEvent->response == buffer num
959 // epcEvent->data = lockfile + nul byte
961 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) && (epcEvent->data) &&
962 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
964 int n = epcEvent->response;
966 if( n >= 0 && n < SHARED_FILE_NUMBER )
968 mCurrentSharedFile = n;
970 mSharedFileInfo[n].mImageWidth = epcEvent->ref;
971 mSharedFileInfo[n].mImageHeight = epcEvent->ref_to;
973 mSharedFileInfo[n].mLockFileName.clear();
975 mSharedFileInfo[n].mLockFileName = static_cast< char* >( epcEvent->data );
977 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetLockFileInfo: buffer num = %d, w = %d, h = %d, lock = %s\n",
978 n, mSharedFileInfo[n].mImageWidth, mSharedFileInfo[n].mImageHeight, mSharedFileInfo[n].mLockFileName.c_str() );
983 void Indicator::SetSharedImageInfo( Ecore_Ipc_Event_Server_Data *epcEvent )
985 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
987 // epcEvent->ref == shm id
988 // epcEvent->ref_to == shm num
989 // epcEvent->response == buffer num
990 // epcEvent->data = shm ref string + nul byte
992 if ( (epcEvent->data) &&
993 (epcEvent->size > 0) &&
994 (((unsigned char *)epcEvent->data)[epcEvent->size - 1] == 0) )
996 int n = epcEvent->response;
998 if( n >= 0 && n < SHARED_FILE_NUMBER )
1000 mCurrentSharedFile = n;
1002 mSharedFileInfo[n].mSharedFileName.clear();
1004 mSharedFileInfo[n].mSharedFileName = static_cast< char* >( epcEvent->data );
1006 mSharedFileInfo[n].mSharedFileID = epcEvent->ref;
1007 mSharedFileInfo[n].mSharedFileNumber = epcEvent->ref_to;
1009 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "SetSharedImageInfo: buffer num %d, shared file = %s, id = %d, num = %d\n",
1010 n, mSharedFileInfo[n].mSharedFileName.c_str(), mSharedFileInfo[n].mSharedFileID, mSharedFileInfo[n].mSharedFileNumber );
1015 void Indicator::LoadSharedImage( Ecore_Ipc_Event_Server_Data *epcEvent )
1017 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1019 // epcEvent->ref == alpha
1020 // epcEvent->ref_to == sys
1021 // epcEvent->response == buffer num
1023 if ( mSharedBufferType != BUFFER_TYPE_SHM )
1028 int n = epcEvent->response;
1030 if( n >= 0 && n < SHARED_FILE_NUMBER )
1032 mCurrentSharedFile = n;
1034 delete mSharedFileInfo[n].mSharedFile;
1035 mSharedFileInfo[n].mSharedFile = NULL;
1037 delete mSharedFileInfo[n].mLock;
1038 mSharedFileInfo[n].mLock = NULL;
1040 std::stringstream sharedFileID;
1041 std::stringstream sharedFileNumber;
1043 sharedFileID << mSharedFileInfo[n].mSharedFileID;
1044 sharedFileNumber << mSharedFileInfo[n].mSharedFileNumber;
1046 std::string sharedFilename = "/" + mSharedFileInfo[n].mSharedFileName + "-" + sharedFileID.str() + "." + sharedFileNumber.str();
1048 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "LoadSharedImage: file name = %s\n", sharedFilename.c_str() );
1050 mSharedFileInfo[n].mSharedFile = SharedFile::New( sharedFilename.c_str(), mSharedFileInfo[n].mImageWidth * mSharedFileInfo[n].mImageWidth * 4, true );
1051 if( mSharedFileInfo[n].mSharedFile != NULL )
1053 mSharedFileInfo[n].mLock = new Indicator::LockFile( mSharedFileInfo[n].mLockFileName );
1054 if( mSharedFileInfo[n].mLock->RetrieveAndClearErrorStatus() )
1056 DALI_LOG_ERROR( "### Indicator error: Cannot open lock file %s ###\n", mSharedFileInfo[n].mLockFileName.c_str() );
1059 CreateNewImage( n );
1065 void Indicator::LoadPixmapImage( Ecore_Ipc_Event_Server_Data *epcEvent )
1067 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1069 // epcEvent->ref == pixmap id
1070 // epcEvent->ref_to == type
1071 // epcEvent->response == buffer num
1073 if( (epcEvent->ref > 0) && (epcEvent->ref_to > 0) )
1075 mSharedBufferType = (BufferType)(epcEvent->ref_to);
1077 ClearSharedFileInfo();
1079 mPixmap = static_cast<PixmapId>(epcEvent->ref);
1080 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "mPixmap [%x]", mPixmap);
1082 CreateNewPixmapImage();
1087 void Indicator::UpdateTopMargin()
1089 int newMargin = (mVisible == Dali::Window::VISIBLE && mOpacityMode == Dali::Window::OPAQUE) ? mImageHeight : 0;
1090 if (mTopMargin != newMargin)
1092 mTopMargin = newMargin;
1093 mAdaptor->IndicatorSizeChanged( mTopMargin );
1097 void Indicator::UpdateVisibility()
1099 if( CheckVisibleState() )
1101 // set default indicator type (enable the quick panel)
1102 OnIndicatorTypeChanged( INDICATOR_TYPE_1 );
1106 // set default indicator type (disable the quick panel)
1107 OnIndicatorTypeChanged( INDICATOR_TYPE_2 );
1112 mIndicatorContentActor.SetPosition( 0.0f, -mImageHeight, 0.0f );
1115 SetVisible(mVisible, true);
1118 void Indicator::UpdateImageData( int bufferNumber )
1120 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "State: %s mVisible: %s", STATE_DEBUG_STRING(mState), mVisible?"T":"F" );
1122 if( mState == CONNECTED && mVisible )
1126 // in case of shm indicator (not pixmap), not sure we can skip it when mIsShowing is false
1127 CopyToBuffer( bufferNumber );
1133 mAdaptor->RequestUpdateOnce();
1139 bool Indicator::CopyToBuffer( int bufferNumber )
1141 bool success = false;
1143 if( mSharedFileInfo[bufferNumber].mLock )
1145 Indicator::ScopedLock scopedLock(mSharedFileInfo[bufferNumber].mLock);
1146 if( mSharedFileInfo[bufferNumber].mLock->RetrieveAndClearErrorStatus() )
1150 else if( scopedLock.IsLocked() )
1152 unsigned char *src = mSharedFileInfo[bufferNumber].mSharedFile->GetAddress();
1153 size_t size = static_cast< size_t >( mSharedFileInfo[bufferNumber].mImageWidth ) * mSharedFileInfo[bufferNumber].mImageHeight * 4;
1155 if( mIndicatorBuffer->UpdatePixels( src, size ) )
1157 mAdaptor->RequestUpdateOnce();
1166 void Indicator::CreateNewPixmapImage()
1168 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mImageWidth, mImageHeight );
1169 Dali::NativeImageSourcePtr nativeImageSource = Dali::NativeImageSource::New( mPixmap );
1171 #ifdef ENABLE_INDICATOR_IMAGE_SAVING
1172 SaveIndicatorImage( nativeImageSource );
1175 if( nativeImageSource )
1177 Dali::Texture texture = Dali::Texture::New( *nativeImageSource );
1178 SetForegroundImage( texture );
1179 mIndicatorContentActor.SetSize( mImageWidth, mImageHeight );
1180 mIndicatorActor.SetSize( mImageWidth, mImageHeight );
1181 mEventActor.SetSize( mImageWidth, mImageHeight );
1186 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1188 if( mObserver != NULL )
1190 mObserver->IndicatorClosed( this );
1192 // Don't do connection in this callback - strange things happen!
1193 StartReconnectionTimer();
1197 void Indicator::CreateNewImage( int bufferNumber )
1199 DALI_LOG_TRACE_METHOD_FMT( gIndicatorLogFilter, "W:%d H:%d", mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight );
1200 mIndicatorBuffer = new IndicatorBuffer( mAdaptor, mSharedFileInfo[bufferNumber].mImageWidth, mSharedFileInfo[bufferNumber].mImageHeight, Pixel::BGRA8888 );
1201 bool success = false;
1203 if( CopyToBuffer( bufferNumber ) ) // Only create images if we have valid image buffer
1205 Dali::Texture texture = Dali::Texture::New( mIndicatorBuffer->GetNativeImage() );
1208 SetForegroundImage( texture );
1215 DALI_LOG_WARNING("### Cannot create indicator image - disconnecting ###\n");
1217 if( mObserver != NULL )
1219 mObserver->IndicatorClosed( this );
1221 // Don't do connection in this callback - strange things happen!
1222 StartReconnectionTimer();
1226 Dali::Geometry Indicator::CreateBackgroundGeometry()
1228 switch( mOpacityMode )
1230 case Dali::Window::TRANSLUCENT:
1231 if( !mTranslucentGeometry )
1233 // Construct 5 interval mesh
1247 struct BackgroundVertex
1253 unsigned int numVertices = 2 * ( NUM_GRADIENT_INTERVALS + 1 );
1254 BackgroundVertex vertices[ numVertices ];
1257 float delta = 1.0f / NUM_GRADIENT_INTERVALS;
1258 BackgroundVertex* currentVertex = vertices;
1259 for( int y = 0; y < NUM_GRADIENT_INTERVALS + 1; ++y, d += delta )
1261 currentVertex->mPosition = Vector2( -0.5f, d );
1262 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1265 currentVertex->mPosition = Vector2( 0.5f, d );
1266 currentVertex->mAlpha = GRADIENT_ALPHA[ y ];
1271 unsigned int numIndices = 2 * 3 * NUM_GRADIENT_INTERVALS;
1272 unsigned short indices[ numIndices ];
1274 unsigned short* currentIndex = indices;
1275 for( int y = 0; y < NUM_GRADIENT_INTERVALS; ++y )
1277 *currentIndex++ = (2 * y);
1278 *currentIndex++ = (2 * y) + 3;
1279 *currentIndex++ = (2 * y) + 1;
1281 *currentIndex++ = (2 * y);
1282 *currentIndex++ = (2 * y) + 2;
1283 *currentIndex++ = (2 * y) + 3;
1286 Dali::Property::Map vertexFormat;
1287 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1288 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1289 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1290 vertexPropertyBuffer.SetData( vertices, numVertices );
1292 // Create the geometry object
1293 mTranslucentGeometry = Dali::Geometry::New();
1294 mTranslucentGeometry.AddVertexBuffer( vertexPropertyBuffer );
1295 mTranslucentGeometry.SetIndexBuffer( &indices[0], numIndices );
1298 return mTranslucentGeometry;
1299 case Dali::Window::OPAQUE:
1301 if( !mSolidGeometry )
1304 struct BackgroundVertex
1310 BackgroundVertex vertices[ 4 ] = { { Vector2( -0.5f, -0.5f ), 1.0f }, { Vector2( 0.5f, -0.5f ), 1.0f },
1311 { Vector2( -0.5f, 0.5f ), 1.0f }, { Vector2( 0.5f, 0.5f ), 1.0f } };
1314 unsigned short indices[ 6 ] = { 0, 3, 1, 0, 2, 3 };
1316 Dali::Property::Map vertexFormat;
1317 vertexFormat[ "aPosition" ] = Dali::Property::VECTOR2;
1318 vertexFormat[ "aAlpha" ] = Dali::Property::FLOAT;
1319 Dali::PropertyBuffer vertexPropertyBuffer = Dali::PropertyBuffer::New( vertexFormat );
1320 vertexPropertyBuffer.SetData( vertices, 4 );
1323 // Create the geometry object
1324 mSolidGeometry = Dali::Geometry::New();
1325 mSolidGeometry.AddVertexBuffer( vertexPropertyBuffer );
1326 mSolidGeometry.SetIndexBuffer( &indices[0], 6 );
1329 return mSolidGeometry;
1330 case Dali::Window::TRANSPARENT:
1334 return Dali::Geometry();
1337 void Indicator::SetForegroundImage( Dali::Texture texture )
1339 if( !mForegroundRenderer && texture )
1342 Dali::Shader shader = Dali::Shader::New( FOREGROUND_VERTEX_SHADER, FOREGROUND_FRAGMENT_SHADER );
1344 // Create renderer from geometry and material
1345 Dali::Geometry quad = CreateQuadGeometry();
1346 mForegroundRenderer = Dali::Renderer::New( quad, shader );
1347 // Make sure the foreground stays in front of the background
1348 mForegroundRenderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, 1.f );
1350 // Set blend function
1351 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_RGB, Dali::BlendFactor::ONE );
1352 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_RGB, Dali::BlendFactor::ONE_MINUS_SRC_ALPHA );
1353 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_SRC_ALPHA, Dali::BlendFactor::ONE );
1354 mForegroundRenderer.SetProperty( Dali::Renderer::Property::BLEND_FACTOR_DEST_ALPHA, Dali::BlendFactor::ONE );
1356 // Create a texture-set and add to renderer
1358 Dali::TextureSet textureSet = Dali::TextureSet::New();
1359 textureSet.SetTexture( 0u, texture );
1360 mForegroundRenderer.SetTextures( textureSet );
1362 mIndicatorContentActor.AddRenderer( mForegroundRenderer );
1364 else if( mForegroundRenderer )
1366 Dali::TextureSet textureSet = mForegroundRenderer.GetTextures();
1367 textureSet.SetTexture( 0u, texture );
1370 if( mImageWidth == 0 && mImageHeight == 0 && texture)
1372 Resize( texture.GetWidth(), texture.GetHeight() );
1376 void Indicator::OnIndicatorTypeChanged( Type indicatorType )
1378 if( mObserver != NULL )
1380 mObserver->IndicatorTypeChanged( indicatorType );
1384 void Indicator::DataReceived( void* event )
1386 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1387 Ecore_Ipc_Event_Server_Data *epcEvent = static_cast<Ecore_Ipc_Event_Server_Data *>( event );
1389 switch( epcEvent->minor )
1393 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE\n" );
1396 mAdaptor->RequestUpdateOnce();
1400 case OP_UPDATE_DONE:
1402 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_UPDATE_DONE [%d]\n", epcEvent->response );
1403 // epcEvent->response == display buffer #
1404 UpdateImageData( epcEvent->response );
1409 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF0\n" );
1410 SetSharedImageInfo( epcEvent );
1415 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF1\n" );
1416 SetLockFileInfo( epcEvent );
1421 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_SHM_REF2\n" );
1422 LoadSharedImage( epcEvent );
1427 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_PIXMAP_REF\n" );
1428 LoadPixmapImage( epcEvent );
1433 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_RESIZE\n" );
1435 if( (epcEvent->data) && (epcEvent->size >= (int)sizeof(IpcDataResize)) )
1437 IpcDataResize *newSize = static_cast<IpcDataResize*>( epcEvent->data );
1438 Resize( newSize->w, newSize->h );
1444 int msgDomain = epcEvent->ref;
1445 int msgId = epcEvent->ref_to;
1447 void *msgData = NULL;
1448 int msgDataSize = 0;
1449 msgData = epcEvent->data;
1450 msgDataSize = epcEvent->size;
1452 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT. msgDomain = %d\n", msgDomain );
1454 if( msgDomain == MSG_DOMAIN_CONTROL_INDICATOR )
1458 case MSG_ID_INDICATOR_TYPE:
1460 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: OP_MSG_PARENT, INDICATOR_TYPE\n" );
1461 Type* indicatorType = static_cast<Type*>( epcEvent->data );
1462 OnIndicatorTypeChanged( *indicatorType );
1466 case MSG_ID_INDICATOR_START_ANIMATION:
1468 DALI_LOG_INFO( gIndicatorLogFilter, Debug::General, "Indicator client received: MSG_ID_INDICATOR_START_ANIMATION\n" );
1470 if (msgDataSize != (int)sizeof(IpcIndicatorDataAnimation))
1472 DALI_LOG_ERROR("Message data is incorrect\n");
1476 IpcIndicatorDataAnimation *animData = static_cast<IpcIndicatorDataAnimation*>(msgData);
1478 if(!CheckVisibleState())
1480 ShowIndicator( animData->duration /* n sec */ );
1491 void Indicator::ConnectionClosed()
1493 DALI_LOG_TRACE_METHOD( gIndicatorLogFilter );
1495 // Will get this callback if the server connection failed to start up.
1496 delete mServerConnection;
1497 mServerConnection = NULL;
1498 mState = DISCONNECTED;
1500 // Attempt to re-connect
1504 bool Indicator::CheckVisibleState()
1506 if( mOrientation == Dali::Window::LANDSCAPE
1507 || mOrientation == Dali::Window::LANDSCAPE_INVERSE
1508 || (mVisible == Dali::Window::INVISIBLE)
1509 || (mVisible == Dali::Window::AUTO && !mIsShowing) )
1517 void Indicator::ClearSharedFileInfo()
1519 for( int i = 0; i < SHARED_FILE_NUMBER; i++ )
1521 delete mSharedFileInfo[i].mLock;
1522 mSharedFileInfo[i].mLock = NULL;
1524 delete mSharedFileInfo[i].mSharedFile;
1525 mSharedFileInfo[i].mSharedFile = NULL;
1527 mSharedFileInfo[i].mLockFileName.clear();
1528 mSharedFileInfo[i].mSharedFileName.clear();
1533 * duration can be this
1537 * KEEP_SHOWING = -1,
1541 void Indicator::ShowIndicator(float duration)
1543 if( !mIndicatorAnimation )
1545 mIndicatorAnimation = Dali::Animation::New(SLIDING_ANIMATION_DURATION);
1546 mIndicatorAnimation.FinishedSignal().Connect(this, &Indicator::OnAnimationFinished);
1549 if(mIsShowing && !EqualsZero(duration))
1551 // If need to show during showing, do nothing.
1552 // In 2nd phase (below) will update timer
1554 else if(!mIsShowing && mIsAnimationPlaying && EqualsZero(duration))
1556 // If need to hide during hiding or hidden already, do nothing
1560 mIndicatorAnimation.Clear();
1562 if( EqualsZero(duration) )
1564 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, -mImageHeight, 0), Dali::AlphaFunction::EASE_OUT );
1568 OnIndicatorTypeChanged( INDICATOR_TYPE_2 ); // un-toucable
1572 mIndicatorAnimation.AnimateTo( Property( mIndicatorContentActor, Dali::Actor::Property::POSITION ), Vector3(0, 0, 0), Dali::AlphaFunction::EASE_OUT );
1576 OnIndicatorTypeChanged( INDICATOR_TYPE_1 ); // touchable
1579 mIndicatorAnimation.Play();
1580 mIsAnimationPlaying = true;
1587 mShowTimer = Dali::Timer::New(1000 * duration);
1588 mShowTimer.TickSignal().Connect(this, &Indicator::OnShowTimer);
1590 mShowTimer.SetInterval(1000* duration);
1593 if( mVisible == Dali::Window::AUTO )
1595 // check the stage touch
1596 Dali::Stage::GetCurrent().TouchSignal().Connect( this, &Indicator::OnStageTouch );
1601 if(mShowTimer && mShowTimer.IsRunning())
1606 if( mVisible == Dali::Window::AUTO )
1608 // check the stage touch
1609 Dali::Stage::GetCurrent().TouchSignal().Disconnect( this, &Indicator::OnStageTouch );
1614 bool Indicator::OnShowTimer()
1616 // after time up, hide indicator
1617 ShowIndicator( HIDE_NOW );
1622 void Indicator::OnAnimationFinished(Dali::Animation& animation)
1624 mIsAnimationPlaying = false;
1625 // once animation is finished and indicator is hidden, take it off stage
1626 if( mObserver != NULL )
1628 mObserver->IndicatorVisibilityChanged( mIsShowing ); // is showing?
1632 void Indicator::OnPan( Dali::Actor actor, const Dali::PanGesture& gesture )
1634 // Nothing to do, but we still want to consume pan
1637 void Indicator::OnStageTouch(const Dali::TouchData& touchData)
1639 // when stage is touched while indicator is showing temporary, hide it
1640 if( mIsShowing && ( CheckVisibleState() == false || mVisible == Dali::Window::AUTO ) )
1642 switch( touchData.GetState(0) )
1644 case Dali::PointState::DOWN:
1646 // if touch point is inside the indicator, indicator is not hidden
1647 if( mImageHeight < int( touchData.GetScreenPosition(0).y ) )
1649 ShowIndicator( HIDE_NOW );
1666 #pragma GCC diagnostic pop